Author Topic: Crossroads  (Read 4102 times)

Cube

  • Administrator
  • Contributor
  • *****
  • Posts: 86
    • View Profile
    • The Venturii Adventure
Crossroads
« on: February 08, 2018, 11:06:52 AM »
Now it comes to that time. Decision time. A Fork In The Road. Buscom appears to be working nicely, and is nearing the point where I would be cloning it into a new project to begin implementing the Mosbus connectivity. But herein lies the crossroad: Is there any benefit to making that an external module versus just incorporating the modbus functionality into buscom? Obviously it need not be utilized if not needed, but what reasons could be contrived to have them separate? Perhaps there is an argument to be made here for using the codebase as it is as a plug-in host for all manner of device-specific processing, since all the other code largely remains the same. I'm thinking of revitalizing the DSC integration module, but again - it would essentially be based on buscom with all it's established code and functionality. I wonder if there might be a way we could standardize on the buscom code and somehow create "plug-in" modules that interact with specific hardware, building on what we've already created. This would simplify the maintenance of code, bugfixes and enhancements to the core would need to be applied to each and every module. What does a modularized system even look like? I've never built "kernel modules" (well, built - yes. Designed / coded: no.) We'd need some demarcation point, probably after the channel had been connected, to turn processing control within each thread over to a function within the module space for "doing something" with that connection, whether that be querying one or more devices, listening for data, or what have you. The module space code would need to be able to allocate it's own memory and Venturii Value Interpreters, generate it's own events, etc. Perhaps this may even be a good place to introduce Lua to the mix, and code the module-specific stuff in Lua script (if that's even what you call it.)

Within the Modbus module, for example, there are dozens of registers, each one holding 16 bits of data. My plan was to create the modbus module such that when you created a channel, you would then add a device (similar to the vdac system) and tell that module how many registers to allocate and the starting address within the PLC. This would then create n * sizeof(register) Value Interpreters (since for us to be able to do things intelligently within Venturii, we pretty much need access to each individual register as a separate entity that can be read and written to independently of all the others. While not the most efficient use of space, (multiplying storage by at least a factor of 8) the functionality requirement outweighs the "waste of bits". But then from there, we haven't much to do. Read all the registers, set the values of each register if any of our VINTs have dvals set, and sleep between poll cycles. The module code would retain control until or unless an error condition occurred by which it became apparent (or desirable / necessary) that the underlying channel connection be reconnected. The module space code need only return (or maybe call some "disconnect()" function and then return) to pass control of the thread back to the buscom code base, which would try to re-establish the connection.

Hmm, the more I think of it, the more I like this paradigm. In fact, i can see all the Venturii modules unifying under this architecture, which would simplify a LOT of code, partially by actually removing most of it, and then only incorporating the device or hardware-specific code itself within these modules. You could even have a single Venturii module talking to many different device types, all within the same process. Arguments could be made on both sides of the table for or against this approach from a single-point-of-failure, redundancy, division of labour or many other basket-to-egg-ratio perspectives, but having the option to hold all the eggs would not mean you couldn't then run multiple "baskets", even having a separate basket for each egg.

This is why I love writing these things down and working them out in text; great ideas form this way and I'm sure down the road it may even be interesting to have a record of the thought process as it formulated within my mind! Haha  The inner workings of my brain on display. Viva Venturii!
Venturii - Integrate Everything

Cube

  • Administrator
  • Contributor
  • *****
  • Posts: 86
    • View Profile
    • The Venturii Adventure
Re: Crossroads
« Reply #1 on: February 08, 2018, 04:55:57 PM »
Of course, no sooner did I get to creating the first plugin module (Modbus) than I hit a hurdle: The libmodbus library takes care of all it's own communication. One of the first examples it provides reads:

Code: [Select]
modbus_t *ctx;
ctx = modbus_new_tcp("127.0.0.1", 1502);

Thinking about it deeper though, this may not be a problem at all. CLI handling will need to be modified somewhat, but a series of hooks and callback functions ought to modularize this aspect nicely. For example, I could have the base command handler function check to see if we have a module type installed matching the value of arg[0], and pass off cmd processing to that plugin's command parser.

Another problem I encountered is that under the present system the channels run in their own threads, talking to whatever devices are associated with them. If we create a Modbus RTU device, it will not be associated with any channel, and therefore will not have any thread to work in. We have a couple of options then, we could:
  • Make it a plugin option to create a thread for the module, passing control to a module worker function tasked with looking after the communication of all that module's devices.
  • Make an option per device to have a dedicated worker function managing that device's communication, each of which runs in it's own thread.
  • Something else?

I'm leaning towards option #2 personally, since this gives the most flexibility. This way, a device can be put on a communication channel, or it can do it's own thing. Of course, each module could define it's own communication requirements as part of the API. A module for DSC panels may specify that it's devi ... BREAKING THOUGHT We could add a parameter to the channel structure so that it designates what each channel is for. Channels may be unreserved, IE: Are not linked or tied down to any one module (such as they presently are.) They could also be reserved for a specific module type (this would help clarify their purpose / function when listing the channels and would also prevent the user from inadvertently placing devices from two or more different modules on the same channel. We now return you to your previously scheduled programming thoughts... Back to the DSC panel example, we could create a Plugin for DSC panels. (Maxsys, Power Series and Neo). This plugin would indicate that it's communication is handled at the channel level, and would provide a function pointer to it's main worker thread, one worker for each channel thread. The panel structures would be shared within the plugin code, but protected from multi-threaded access by rwlocks as we have gotten very good at writing, Thus each worker thread would scan the plugin's array of devices, acting only on devices registered for communication on that specific channel.

That approach would then be complimented by the modbus mode, whereby the plugin's devices do not need to associate with any channels on their own (maybe we could even prohibit this (or at least ensure it does not become and option for selection) and enabling a modbus plugin device would create a new thread specifically for handling that device. I think this makes sense, it's becoming clear in my mind -- now to try to implement it!
Venturii - Integrate Everything