Modules for Dummies 101
gregr
Posts: 54
The Dummy is me. I'm trying to graduate from one-off hard-coded jobs that I have been cranking out the last few years. Oh, they work, but as we are getting into larger jobs and more frequent jobs I am finding the need for more a more efficient and modular way to assemble these projects.
I think I want to keep on writing the drivers for the devices, but want to package that code in a way that allows me to:
(1) Efficiently plug it into another project.
(2) Control multiple devices with multiple UI's without repeating the same chunks of code throughout.
Are modules the way to go? I cannot seem to quite wrap my head around them, although I have studied almost every post in here that talks about them.
Specifically:
What parameters get passed to what, and in do they pass in both directions?
A lot of you pass arrays to the module to talk to it, but the Duet modules seem to respond to text-based commands, which seems simpler to me. (The idea of getting the index of a button array instead of the button channel throws me for a loop).
It looks like multiple module instances deals with the multiple identical devices, but how do you keep track of the feedback data for each one?
I have looked at a lot of examples, but it is hard to tell what is happening when the module comes as a .tko or .jar. Is there a simple example which has viewable source code I can examine? Also, I have been looking at examples for single devices only. Your complete jobs with many devices must contain tens of modules if you have a comm and UI module for each device or type of device.
Feel free to direct me to any post that already has the answers!
Thanks for your time.
Greg
I think I want to keep on writing the drivers for the devices, but want to package that code in a way that allows me to:
(1) Efficiently plug it into another project.
(2) Control multiple devices with multiple UI's without repeating the same chunks of code throughout.
Are modules the way to go? I cannot seem to quite wrap my head around them, although I have studied almost every post in here that talks about them.
Specifically:
What parameters get passed to what, and in do they pass in both directions?
A lot of you pass arrays to the module to talk to it, but the Duet modules seem to respond to text-based commands, which seems simpler to me. (The idea of getting the index of a button array instead of the button channel throws me for a loop).
It looks like multiple module instances deals with the multiple identical devices, but how do you keep track of the feedback data for each one?
I have looked at a lot of examples, but it is hard to tell what is happening when the module comes as a .tko or .jar. Is there a simple example which has viewable source code I can examine? Also, I have been looking at examples for single devices only. Your complete jobs with many devices must contain tens of modules if you have a comm and UI module for each device or type of device.
Feel free to direct me to any post that already has the answers!
Thanks for your time.
Greg
0
Comments
As for device communications, modules work well and I use them often. Here is an example of a device module:
I added the channel_event activation to demonstrate how you can use channels to do things as well. The reason I prefer channel events is that you don't have to employee string functions which can be processor intensive. There are some situations where string commands make sense, but normally you are doing a lot of extra work.
Jeff
Both of you are saying the same thing, and it makes sense to me for the most part. I can see where channels are more processor efficient, but seem to be harder to keep track of. I'd like to avoid 50 minute re-boots if possible!
As to channels, Ken mentions monitoring channels on the real device, but these would not exist as such on non-AMX devices, no? I assume you would have to monitor the data_event for the real device, then parse and turn that into channels?
And Jeff, the channels on your virtual device puzzled me for a bit. I guess any defined device can be considered to have channels, even though the actual device has no idea what channels are?
I reckon I'll just have to slog through my first module until it does what I need. There's probably no such thing as writing it perfect the first time.
When I say real device, I mean the actual AMX device that is controlling the MFG device. For example, a projector is connected to 232 port 1. The real device if the device is addressed 5001, 5001:1:0. the feedback is reported to this device. So in the module you might do this...
if (find_string(data.text,'PWR',1)) //response is either PWR1 or PWR0
{
[dvDevice,1] = atoi(data.text)
}
This is reporting the outcome of the find_string to the channel of the real device.
The device that I am using channels on is a virtual device. There are limits, but by default the device will have 255 channels. The modules job is to act on channel changes by the main program and change the state of the channels based on feedback from the device (generally speaking of course ). The virtual device is sort of the gateway between your main code and the actual device. Ideally, you want all of the same type of device to work off of the same "language" on the virtual device.
For example, channel 27 is always power on for my devices. Inside the module, the module converts that channel 27 into the appropriate command and sends the command to the actual device. I highly recommend following AMX standards when possible, then you can just substitute their comm module when one is available and you won't have to do a lot of recoding.
Maybe this will help...Think of a channel as a binary shared variable. If you need more than a binary variable, you can use a level for integer sharing, but I tend to avoid levels for most things. I like to use channels because it is easy to get their current state and act accordingly. ie: [dvTP,TvPower] = [vdvTvModule,255].
Jeff
I can't see how this can be. 50 minutes to reboot a controller? Sounds like you have an infinite while loop or something else going on. Its possible to get loops between the controller and devices as well. I pass touch panel references to modules all the time and haven't seen this. Are you sure there wasn't something else going on there?
Paul
I am almost positive it was caused by an array of touch panels. I had an array of 60 touch panels and I had something like 8 instances of the module. The processor would eventually boot up and run properly, so it was not an infinite loop. I figured out it might be the touch panel array as I had long boot times in the past, but they were in the 20-25 minute range. It just so happened that the job with the really long boot time had less going on than previous jobs, (fewer other modules and code) so I was really confused when it took that long to boot. I took the code out of the module and just ran it in the main program (with an array for the variables as I recall) and boot time dropped to a couple minutes. (Duet modules ).
This was a few firmwares ago, so I am not sure if it is still an issue, but if I get a chance, I will load the old modules and verify.
Jeff
When I write modules for devices, I have a device communications module; for those devices that interface directly with a user, in addition to this module I'll have UI modules which communicate with the comms module. I've got my own text-based command scheme that is similar between all of my modules, and use similar channel and level feedback between modules. (such as for when a receiver input turns on, I can, with a channel_event, then tell that input's device to turn on if not on, etc.) So should I want to swap, say, a Sony receiver for a Denon, all I have to do is change my comm and UI module defines and be done with it. Or if I want to add a touchpanel, I load another module for that new touchpanel and I'm done. That's really the advantage to well-written UI modularization and standardization
Remember that variables passed into modules are by reference, so if it helps with your design, you can use a common variable to share data between modules or between main code and modules. I use this with my old 422 Viewstat modules so I can, say, use the UI module for touchpanel control of one tstat at a time, and have a very simple loop in my main program send current temperatures of all thermostats each minute. If I wanted to, I could write another UI module that allows me to modify all thermostats at the same time without touching any of the other code. Keeping UI seperate from everything else can really help with code reuse, and doing it with modules is easy.
Regarding string functions and parsing, I wrote a function that splits a string on a delimiter (such as a space or a comma) and puts it in a passed multidimensional array. So when working with protocols that have a fixed delimiter from devices, or when working with my simple protocol ('power off' or 'input dvd' or 'volume mute off'), it's as easy as looking at values in an array. (arr[1] == 'volume', arr[2] == 'mute', arr[3] == 'off') It works similar to PHP's explode(), with a couple of key differences. If anyone thinks they could use this function for easier module writing I'll post it, or if you want an example simple module/UI pair I could post that too...
edit: NetLinx uses 1-based arrays. oops. fixed to help against confusion.
Uh...DME??
So would you have a separate Comm and UI module for every different type of controlled device? I'm trying to envision what a completed job would look like. Seems like for a typical job, you could have 20+ modules?
That'd be great if you could.
Thanks all-
Greg
define_mutually_exclusive - I was managing groups manually.
It could. It really depends on panel count and different device types. The modules are used for talking to devices and tying UI to devices, and the main code is used mostly for tying devices together. However, some simple device modules I have written either have no UI component and I write it per job or they accept a tp array (typically modules that only have channel feedback may do this).
EDIT: Misread what you were confirming you wanted... I'll get it for you soon.