Modules, what good are they?
vining
Posts: 4,368
I was thinking the other while driving that modules aren't really necssarry and might actually cause more problems then they fix.
As far as reusing code for multiple devices by instantiating multiple instances of the code that's easily accomplished using arrays and to me the idea of a module really only makes sense for systems that can run multiple threads at the same time. Since Netlinx is a single thread system you can accomplish the same thing with arrays or structures so that your code can control multiple devices of the same type and just simply track your TPs and what they should be controlling. You have to do that anyway to determine which instance of a module to talk to.
It seems passing stuff to the module, not having persistence, unable to reference other code unless passed in as a paremeter are reasons not to use modules. It often feels like I'm doing things twice, once in the module and then again in the main (.axi) when it could be done just once. Basically instead of a module you can create a manufacturer specific function for your com and not use modules at all.
I'm beginning to think the logic of using modules really makes no sense since we can accomplish the same things with structures, arrays, function and keep our code organized by simply using includes file which we most already do.
As far as reusing code for multiple devices by instantiating multiple instances of the code that's easily accomplished using arrays and to me the idea of a module really only makes sense for systems that can run multiple threads at the same time. Since Netlinx is a single thread system you can accomplish the same thing with arrays or structures so that your code can control multiple devices of the same type and just simply track your TPs and what they should be controlling. You have to do that anyway to determine which instance of a module to talk to.
It seems passing stuff to the module, not having persistence, unable to reference other code unless passed in as a paremeter are reasons not to use modules. It often feels like I'm doing things twice, once in the module and then again in the main (.axi) when it could be done just once. Basically instead of a module you can create a manufacturer specific function for your com and not use modules at all.
I'm beginning to think the logic of using modules really makes no sense since we can accomplish the same things with structures, arrays, function and keep our code organized by simply using includes file which we most already do.
0
Comments
First of all, you shouldn't do that or you'll get in an accident.
You can control any number of UIs and any number of devices from one module. There is no need to instantiate one per device. The entire program is a module, so without modules, nothing would work.
You don't need multiple threads to run modules so I don't see the relevance there. Unless you have multiple cores or CPUs, its not true multithreading. You should be grateful for this. Multithreaded applications bring with them all sorts of nasty problems. For robotic control, a single threaded system is much superior in my opinion.
Its funny that the reasons you state to not use modules are precisely the nice features of modules. A module encapsulates functionality, and this reduces complexity, a tenet of good software engineering. I don't want the module code to interact with any other part of the program other than the things I pass into it. I like this guarantee. Modules shouldn't be able to use persistent memory since the module itself isn't persistent. You can write to a file from a module so you can safely store long term data if need be.
.I don't understand what you mean about doing things twice. Sounds like you might be doing something wrong there. Using modules allows a write once and reuse paradigm.
Ugh. I use a few includes to organize functionality, but I prefer to let the modules do most of the work. The last shop I worked at used includes all the time and I hated it. Since all the variable names have global scope, you end up coming up with stupid names just so they don't confiict with other variables with similar function. For instance, the names that I give the real and virtual devices in the module that are passed in are dvDevice and vdvDevice. Its the same in every module, so I never have to think about naming schemes and simplifies module creation since I can copy an existing module and the only things that I need to change are the commands, parsing and a few other things. Everything else stays the same. You can't do that with includes. If Netlinx didn't have modules I would seriously consider getting out of this line of work.
Paul
If you want code isolated from the main that's fine, if you want control of the source code that's fine also but if the reason for creating modules is to "re-use" code for multiples of the same device then that's not a legitimate reason since you can run as many devices you want from code that's structure to do so with out modules. Then you don't have to deal with the negative aspects of using modules and IMHO the negatives do out weight the positives most of the time.
1) I can provide my module to anyone, without providing them source code.
2) When I assist them with implementing my module I am guaranteed that they haven't modified the source/behavior, since they don't have the code. This makes it easier for me to support them.
3) When debugging a room it can be tempting to chop-n-change a variety of code just to get the room working. By "locking away" code that is tried and tested, you reduce the risk of modifying code which was behaving fine, allowing you to focus on the actual cause.
4) Including a .tko in your project rather than the .axs reduces compile time, since the .tko does not need compiling, only linking.
Yes, lack of persistence is a minor pain but it makes sense. However, if I don't want to expose the code in my module, how will an implementer know what variable needs to be set? Configuring the module through send_commands makes a lot more sense. Besides, persistent variables are only stored as long as the timekeeper battery lasts. Once it goes flat you'll lose your persistent values on reboot, and wish you had loaded your variables from file instead.
Roger McLean
Swinburne University
I don't understand. For instance, if you use the AMX iPort module, you have a comm module and a UI module. You instantiate both and are finished writing code. Same with the Kaleidescape module. What events are you writing in these cases?
Devs, vdevs are only ever defined once, no matter whether you use modules or not. The fact that you say they are done twice makes me wonder if you are using modules incorrectly or in a unorthodox manner leading to problems.
The whole point of software engineering principles is to determine the best way to do something, given an unlimited choice. You can write the same program in an infinite number of ways. Are all these ways going to be equally efficient, maintainable, modifiable, readable, etc? No. Just because you can do something doesn't mean its good practice to do so. Limiting scope is a valuable software engineering pinciple and modules allow for this. Just because there are built in limitations to AMX modules, like not being able to pass structures to them, or having only commands, strings, and events to communicate with them rather than being able to use tradtional OO syntax like dvBluray.play(), doesn't negate the principle behind using them, at least to me.
Paul
I agree completely! Just cuz you can write a module doesn't mean you should. Everything you spoke of can be done with out modules. I can see writing modules for reasons annuello mentioned which are also some of the same reasons you mentioned but as far being better style I don't buy it and to gt back to original point which is if the reason you're writng modules is to "re-use" code which you agree isn't necessary for connecting to multiple devices or TPs then the only reason for modules are basically because we were brain washed into thinking we needed to or for some of the reason annuello mentiond which is really just for locking the code.
I've been writng module to "re-use" the code for multiple instance of devices and for me it would be easier and cleaner not too but I just thought it made sense because that's what's been said over the years. I don't care about locking code anymore and I like includes and if done right it's just as clean as using modules. I found with modules I spend more time marshalling data in and out or sending levels, channels, command & strings. If I keep it simple I can create stack structure or var and pass them to the function that need them and have those functions accessible through the code.
In that case I would strongly suggest you don't use modules.
Paul
I would advise you to look in to design patterns such as MVC (Model-View-Controller) or a simplification of that practice. see http://www.leepoint.net/notes-java/GUI/structure/ui-model-communication.html for a short explanation. Perhaps wikipedia will help as well: http://en.wikipedia.org/wiki/Model-view-controller
Are you doing weird things in your modules or something? Because I never run in to the issues you're describing and I make modules for every device I encounter. I build a small framework on which I plug-in these modules. I do everything that's purely device-related in the modules themselves and handle system-wide commands etc in the framework. So in case of a kaleidescape system I pass around the framework (using the extra port on the touchpanel for that deivce) which is more of a blessing really, I don't want to dig in to the kaleidescape module.
Granted I don't use modules written by AMX/manufacturers unless I really need to (like with kaleidescape).
Or you can approach it like I do and build a framework on which you can plug-in your modules. Another thing with modules is that you can debug purely on that code and don't have to touch code you previously tested as running and bug-less.
You should read a book or two on software engineering, at first I thought it would be a pain in the arse as well. But as it turns out it has saved me a lot of time. It's not something you see returned on just one project, you see it on all of them. Because you know your framework is running correctly and if a certain device is messing up you know it's either in the module of that device or in the device itself.
Books I suggest:
http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882
http://www.amazon.com/exec/obidos/ASIN/0201633612/ref=nosim/cboard-20
And my favorite:
http://www.amazon.com/Code-Complete-Practical-Handbook-Construction/dp/0735619670/ref=sr_1_1?s=books&ie=UTF8&qid=1301906967&sr=1-1
The only time modules would be required to controll multiple devices is if it were possible to run multiple threads. Since netlinx can't spawn or fork a child processes (excuse the terminology) then the only real purpose of modules is ultimate isolation which allows you to define names with out regard to the names already being used and it can also be locked. Whether you see modules as being better than includes or functions that may be inpart due to languages that actully need module instances that can run simultaneously and the guys making the rules, writing the books are using these languages.
As for the need to marshal data in and out of a module ... I've never had much of a need for that since I handle all that internal to the module. I think it comes down to a way of thinking. To my mind, a module is the nearest thing to a fully encapsulated object that the NetLinx language allows. So, I really work as hard as I can to do just that ... encapsulate it. It should be a plug-in with the minimal amount of hooks to make it run and to allow the rest of the program to know what it's doing. Granted, NetLinx doesn't go far enough with that, but I rarely have the need to send anything more to the module than the device number, a virtual interface, and some variables holding the UI devices and channels. Everything else stays inside as much as possible.
You can have one include file running six idenctical rojectors too, it's not really any different.
Here's a good scenario that I run into all the time that an include seems to work best (if they'd make the super_inlcude)
Situation:
I quite often find myself programming installations that have the following things.
TVs, Whole House Audio systems and a plethora of sources.
Quite often our designers will do things like use the whole house audio system to pump TV audio to a room that has a big TV but no speakers for all the sources they want. Visa-Vera, they might put a TV in a room and feed the whole house media player to that TV so the owner can listen to music on their TV.
So, I have a routine to handle general house audio stuff that works with any touchcpanel, keypad, remote, etc... I also have a routine that handles TVs from any TP, keypad, remote, etc... I also have a routine for each source and its control.
So, let's say part of my routine is an "All House Off' button for whole house audio. However, If the current audio zone is being used for TV audio, then don't turn it off.
or, let say you don't want other users in the house to be able to set up a party scene on the whole-house audio if someone's watching TV...
The idea is there are states and variables that need to go back and forth from one routine to another. This is doable in modules but is a bit clunky.
If I always have a global variable called House_Audio_Zone_source[32] that tracks the source selected, then I can use that variable at any time to track the status of the HA system easily. Not to mention, I can make it persistent so it stays true after reboots.
So, the reusable code in this example is the global variable. In essence it's functioning like a module in that it can be used again and again all over the program. But, the difference is that I don't have to do a lot of hook handling in my main code to cause changes in state to traverse the entire program.
The advantage of a module is that you can declare variables within the module that stay there and work every time. The disadvantage of the Include is that where you declare your variables gets a bit sticky. For any of use who've tried using the above mentioned method, we know that the order in which or include statements occur and where they are placed can be a bit of a juggling act.
That's what my idea for the super_include comes from. It's just something to tell the compiler, "Look, I know you don't see this variable declared yet, but hold on, you'll see it once you pass through all the includes..."
Ugh. Order shouldn't matter in a program. I had to redo so much of a predecessors code because he relied on order/timing so much that if you changed the order or timing of something, then a variable wouldn't be set in time, so a device wouldn't turn on, etc and includes are part and parcel of this for the reason you mention. It was a total nightmare. Sure it mostly worked on small systems, most of the time, but on large systems, if you wanted to do anything interesting, forget it.
The way I do thngs now, order/timing doesn't matter, turn on any source from any TP in any zone at any time, and it will work. Since Netlinx is single threaded you can always be sure of the order of execution unless you are dealing with waits, or non-blocking I/O. Since I use modules rather than includes, I never have to worry about any interaction between global variables or the timing of when some variable gets read or written to. It cuts way down on gnarly, hard to reproduce, hard to solve timing/order type bugs.
Paul
Working to a 'real world' framework is awesome, unfortunately though as NetLinx is half way between a scripting language and an OOP language this isn't always possible. You can however do a lot of funky things with a combination of effective use of structures and adhering to some non compiler enforced rules. For example, if you've got a nice little neat include for a device prefix all the function names with something relevant to that device (that way when NS auto fills for you it makes things a little easier) and prefix all 'private' functions with and underscore or similar. If you (and anyone else you are developing with) can adhere to a single ruleset things stay a lot more structured and maintainable.
The absolute major advantage of modules, for me at least, which seems to have been left out of the conversation so far is abstraction. You have the ability to define a set API for handling all communication with that module. As long as it remains the same (or at least doesn't remove anything) you can completely change anything happening inside to improve performance, add functionality, or remove bugs then push this out to *anywhere* using that module without breaking things. Make the change in one place and instantly improve/fix everywhere it is used.
I wasn't referring to program order. H I was referring to what order the includes are declared. Using my same example I may have an Whole House Audio include that uses a global variable from the TV include to determine if an Tv using the Whole House Audio for Tv Sound. And visa versatile: a global variable from the WHA include sed in the TV include.
If I put the WHA include before the TV include I'll get a 'undefined variable' error for the variable From the TV include
What I'm referring to is a 'module-like' include. I never have to modify a line of code either. I have a WHA include for Précis, Élan s12, etc that can be interchanges without a problem.
What you are asking for should not be a difficult compiler change to implement. You are asking for the AMX engineers to add 1 additional pass at the scanner/parser level of the compiler. The first pass would collect and store constants,variables,calls, and functions of all includes. Then the second pass processes everything else.
I have long since given up on trying to understand how AMX decides to allocate their engineering departments brain power. Don't ask me to explore that road again.
I think this argument of "why not use an include file?" is kind of silly to be honest. If you look at everything as a nail, you'll use a hammer all the time. Just read what the documentation says on the subject:
The main difference I see between the two are that one is meant to be integrated *with* your program, while the other is meant to be it's own *separate* program (in a sense.) And yes, NetLinx is single threaded, so you do have to be careful how you define them as explained in the help file.
My point is: how you use them is up to you. A hockey stick was meant to be used with a puck on ice not a ball on grass but kids get enjoyment out of it either way.
Besides productivity and being able to "put away" whole sections of code that doesn't need to be touched anymore, I have created a module template that I use to build all of our comm modules with. It already has queuing and input/output processes built. No need to change the variable names because it is encapsulated in the module package. This makes troubleshooting comm modules easier because they all work the same way.
Persistence inside the module can be simply handled by passing a persistent variable in to the module and using the parameter name on the inside. I know this doesn't sound ideal at first, but sometime AMX doesn't provide all of the tools you might need to make things perfect. (Timeline arrays would probably change my life)
I have included a couple of examples of how I apply it if you are interested. The Include example has been changed to .axs due to file type limitations.
Interesting. I don't use the define_program like you do. If you end up with lots of modules does this slow things down at all? I would think that with chatty devices like Kaleidescape players, lighting, security, etc, it might but perhaps you haven't found this.
Paul
This has to be combined with multi-processor usage. The include format that I have also helps me to quickly and easily move a whole category of programming off to another processor without causing major amounts of work trying to untangle it from the main code. For example, I like to run the KScape module on a local processor that is just providing ports somewhere in the system. It is Ethernet typically so who cares where it is located. This moves all but the touch panel command and channel data from the passing through the main processor.
I always make my comm modules support IP and 232 since now a days it's just so easy to add a $100 serial server when you have a long distance ot simpluy run out of 232 ports. I actually wrote one recently that would do IR too, the TriFecta.
When I made my AutoPatch module I made the single module actually manage up to 3 18x18, 3 dev ports, 3 com buffers so Icould seemlessly control up to 54 zones although I limited myself to 18 sources which have to cascade from one AP to the others with Y's. If you select zones 2, 24, 30 & 50 to switch to XM radio the module will build the string and route to the appropriate Devs.
I like modules I just don't see them as being as essential for coding in AMX as most of you folks. Maybe that's cuz I ain't been properly skooled. That said I don't know if I can get out of the habit of writing them but I sort wish I had module to write now that would normally require instances so I could try it with out actually using modules. A non module, module (include). I did recently take code that required 6 instances of one of my modules and made a simple include that handled everything even more elegantly than instantiating the modules did. I guess different strokes for different folks applies here. I've always been somewhat abnormal anyway.
Yup. Being heavily invested in Duet and RMS, any and all device communication modules I write strictly conform to SNAPI - makes my life incredibly easy when it comes to deployment.