Module Creation - Best Practices
jabramson
Posts: 106
I am creating some modules and I was wondering about best practices in handling/passing variables.
For example, I've made a module for a Biamp Nexia unit. I am controlling and tracking the unit through the module (I use a send_string to a virtual device with something like "Single Fader Mute <Off> <1>". The module receives this, processes it, and then sends the appropriate string to the Nexia unit and the Nexia unit sends the appropriate response back to the module where the Module processes the response.
Currently, in the define_module section I pass all the variables to be updated such as mute status, gain status, etc. Each grouping is a separate array (Mutes, Gains, Matrix, Other) and the module then updates those values based on return strings. As my module gets more complex I'm having to send quite a few different sets of arrays and I was wondering, is there an easier way to do this?
For example, I've made a module for a Biamp Nexia unit. I am controlling and tracking the unit through the module (I use a send_string to a virtual device with something like "Single Fader Mute <Off> <1>". The module receives this, processes it, and then sends the appropriate string to the Nexia unit and the Nexia unit sends the appropriate response back to the module where the Module processes the response.
Currently, in the define_module section I pass all the variables to be updated such as mute status, gain status, etc. Each grouping is a separate array (Mutes, Gains, Matrix, Other) and the module then updates those values based on return strings. As my module gets more complex I'm having to send quite a few different sets of arrays and I was wondering, is there an easier way to do this?
0
Comments
Of course, you can do whatever you want and design your modules any way you want, but there are good reasons to write your modules to function in the "standard" way.
Any variable initiatin you need should be passed in as a command. You will find after doing his for a while you will come up with some standards on commands.
An easy example of this is the baud rate of a serial device. I never hard code the baud rate in he module because I don't know how many times I've seen a manufacturer change baud rates thus breaking my module.
Another thing too that will make your UI code better is two things:
1). Make sure your UI code can handle multiple panels with he possibility of differing ports on ech panel and
2) the ability of the UI code to NOT send feedback to panels not currently controlling the device in question. (Which, of course, implies the UI having the ability to trck when a panel is in control and even a hierarchy in case you allow multiple simultaneous controllers or require only one at a time.
Those are my suggestions.
For setting string values (or numerical values not suited to levels) have a look at the ^TXT-type methods used for setting button strings in touch panels. The exact format varies between modules but schemes like these seem to be popular:
SEND_COMMAND dv,"opcode,address,value";
(When sending a numerical "address" be very careful to define whether you're sending it as a binary byte 0-255 or as an ASCII integer itoa(address) '0', '123', '1024' etc.)
For setting more complex values you can use commands and strings again, except load them into a structure and use VARIABLE_TO_STRING to convert it to a form that can be sent to the module. It can then turn it back into a structure using STRING_TO_VARIABLE.
I never use send_string to communicate with a module. For comm modules I usually only have 2 arguments, the device and the virtual device, but for UI modules there can be 3 to 8 arguments. Any feedback goes to the virtual and main code listens for changes on that virtual. Since I author most of my own modules, I often break my own rules depending on the situation. For instance, I'll hard code the baud rate and just change it in the module if it needs to change rather than passing it in, sometime I pass in the button channels, other times hard code them. Best to flexible and not force yourself into corners.
Paul
Then all you need is a loop wherever you prefer to do feedback.
This is the method I use in my nexia/audia module. I have a common structure between my comm module and an include. All the addresses of the nexia blocks I want to communicate with are stored in the include. At startup I generate and store all the block addresses' variable to string value, any command sent to the module for controlling a nexia block has its variable to string appended to the end. The module decodes the string and has all the neccessary info to assemble the address portion of the command. When I first wrote the code it was an experiment to see if it would be fast enough to be usefull, suprisingly it is. When any information is recevied by the module from the nexia the address is variable to string encoded and the data and address are sent back to the include, where the address is decoded and checked against the database and stored in a "values" database if a match is found. All the values for any audia/nexia block (excluding text) can be stored in 8 integers, 4 signed integers, a long, and a float.
I agree with your dislike of UI modules. I tend to like keeping them as Includes and what I'd call semi-customizable code. For one, there always seems to be Something that needs to be customized in one UI to another. So, I really try and keep the UI functions fairly standardized in regards to how they communicate to the Comm modules. but they're fairly free to be customized for how that then gets to and from the UI.
Paul
I really wish someone had actually told me SNAPI existed when I was at the programmer courses (or maybe they did and I missed it). By the time I found about it I had already developed my own standard and written many modules to it. I have thus far not been able to convince myself that the time/effort to convert all my modules to SNAPI would be in any way worth it.
There are only two circumstances where I pass any variables to a COM module other than the physical and virtual devices. One, if the module talks to other modules it'll have additional parameters for those virtual devices, and two, the ClearOne ConvergePro module I wrote. There was no way I was going to create a device with over 1900 levels or a device with 43 ports each with 45 levels to provide feedback from the matrix in those boxes. :eek: Instead I pass in a couple of arrays, one for mute state and one for level value.
It's always interesting to see all the different ways people solve the same problems. When I sat down to build a module for Nexia/Audia control I couldn't come up with any solution for dealing with the flexible architecture that I liked in a single module (probably because I never remember that things like string_to_variable exist ), so I wound up dispersing it into multiple modules. One central module handles all the communications back and forth, and then sub-modules for the different types of blocks you want to control. The main module keeps track of what sub-modules you've declared and sorts feedback from the Nexia/Audia to the appropriate sub-module.
Like most standards its just agreed arbitrariness, so your standard is probably just as "good" as SNAPIs.
Paul
SNAPI is a mess, because it has to be comprehensive, and most of the time, we don't need comprehensive control. For example, if I'm doing a surround receiver, I need source selection, volume, and surround mode ... I don't need, except in special cases, graphic EQ or individual channel levels, because I do that all when I set it up and don't touch it later. As a result, I've kind of created my own API rules, because I don't need every device to have UI access to every available function. I want it streamlined.
I absolutely cannot stand putting running code in an INCLUDE (just a personal preference). I almost never have only one of a device in a system, and the built-in instancing of module code is far too convenient to ignore when you have half a dozen cable boxes, or whatever, on a system.