We've launched our new site at www.openlighting.org. This wiki will remain and be updated with more technical information.
Difference between revisions of "OLA developer info"
From wiki.openlighting.org
Line 8: | Line 8: | ||
''Peperoni and usbdmx are probably easy to implement. The specs and source code examples are available.'' | ''Peperoni and usbdmx are probably easy to implement. The specs and source code examples are available.'' | ||
− | == | + | == Core Classes == |
− | |||
− | + | === DmxBuffer === | |
+ | |||
+ | The DmxBuffer class allows DMX data to be passed around the code, while avoiding unnecessary copying. DmxBuffer is defined in include/ola/DmxBuffer.h | ||
+ | |||
+ | === Closures & Callbacks === | ||
+ | |||
+ | Closures & Callbacks are similar to function pointers, they allow both functions and methods to be invoked at a later time with data from either/both the time the Closure is constructed and the time the Closure is executed. | ||
+ | |||
+ | All Closures/Callbacks have a Run() method, which is how the Closure is called. | ||
+ | Closures take no arguments at exec time, Callbacks take one or more arguments at exec time. | ||
+ | |||
+ | === SelectServer & Sockets === | ||
+ | |||
+ | The SelectServer is the dispatcher at the core of OLA. It waits for events, and when an action occurs calls the specified method. | ||
+ | |||
+ | |||
+ | == Plugin System == | ||
+ | |||
+ | We'll use plugin to refer to the entire module (Plugin, Devices & Ports), and Plugin to refer to the class that inherits from Plugin. | ||
− | Plugins create and register Devices, which each consist of 0 or more Ports. A Plugin generally does a bit of work when it starts, then leaves all work to the individual Devices and Ports. | + | Plugins create and register Devices, which each consist of 0 (obviously not useful) or more Ports. A Plugin generally does a bit of work when it starts to detect devices, then leaves all work to the individual Devices and Ports. |
+ | |||
+ | === Plugins === | ||
+ | |||
+ | The AbstractPlugin interface is defined in include/olad/Plugin.h. The Plugin class implements most of this interface, and leaves Id(), PluginPrefix(), StartHook(), StopHook(), SetDefaultPreferences() and Description() to be implemented by the child classes. | ||
+ | |||
+ | The startup sequence for a Plugin object is: | ||
+ | * From within DynamicPluginLoader::LoadPlugins an instance of the plugin is created | ||
+ | * If the ShouldStart() method returns False, nothing else happened the Start() method is called. | ||
+ | * The Start() method calls LoadPreferences() which in turn calls SetDefaultPreferences(), this last method gives the Plugin the opportunity to setup the Preferences object. | ||
+ | * if SetDefaultPreferences() doesn't fail, StartHook() is called where new Devices are created. m_plugin_adaptor->RegisterDevice() should be called to add new Devices. | ||
+ | Plugin classes | ||
+ | During the shutdown sequence: | ||
+ | * Stop() is called, which in turn calls StopHook() | ||
+ | * StopHook should call m_plugin_adaptor->UnregisterDevice() for any devices registered during the start phase. | ||
+ | * delete is then called on the Plugin object | ||
− | |||
The create() function is called with a pointer to a PluginAdaptor, this returns a new instance of the Plugin. | The create() function is called with a pointer to a PluginAdaptor, this returns a new instance of the Plugin. | ||
Obviously the constructor is called - use this to init variables but any real work should be done later. | Obviously the constructor is called - use this to init variables but any real work should be done later. |
Revision as of 14:18, 2 July 2010
Contents
Unsupported USB devices
- Peperoni Rodin1 (but this is supported directly by QLC)
- Peperoni USBDMX21
- USB DMX from usbdmx.com
- Sandsys UMX2
- Sandsys UMX4
Peperoni and usbdmx are probably easy to implement. The specs and source code examples are available.
Core Classes
DmxBuffer
The DmxBuffer class allows DMX data to be passed around the code, while avoiding unnecessary copying. DmxBuffer is defined in include/ola/DmxBuffer.h
Closures & Callbacks
Closures & Callbacks are similar to function pointers, they allow both functions and methods to be invoked at a later time with data from either/both the time the Closure is constructed and the time the Closure is executed.
All Closures/Callbacks have a Run() method, which is how the Closure is called. Closures take no arguments at exec time, Callbacks take one or more arguments at exec time.
SelectServer & Sockets
The SelectServer is the dispatcher at the core of OLA. It waits for events, and when an action occurs calls the specified method.
Plugin System
We'll use plugin to refer to the entire module (Plugin, Devices & Ports), and Plugin to refer to the class that inherits from Plugin.
Plugins create and register Devices, which each consist of 0 (obviously not useful) or more Ports. A Plugin generally does a bit of work when it starts to detect devices, then leaves all work to the individual Devices and Ports.
Plugins
The AbstractPlugin interface is defined in include/olad/Plugin.h. The Plugin class implements most of this interface, and leaves Id(), PluginPrefix(), StartHook(), StopHook(), SetDefaultPreferences() and Description() to be implemented by the child classes.
The startup sequence for a Plugin object is:
- From within DynamicPluginLoader::LoadPlugins an instance of the plugin is created
* If the ShouldStart() method returns False, nothing else happened the Start() method is called. * The Start() method calls LoadPreferences() which in turn calls SetDefaultPreferences(), this last method gives the Plugin the opportunity to setup the Preferences object.
- if SetDefaultPreferences() doesn't fail, StartHook() is called where new Devices are created. m_plugin_adaptor->RegisterDevice() should be called to add new Devices.
Plugin classes During the shutdown sequence:
- Stop() is called, which in turn calls StopHook()
- StopHook should call m_plugin_adaptor->UnregisterDevice() for any devices registered during the start phase.
- delete is then called on the Plugin object
The create() function is called with a pointer to a PluginAdaptor, this returns a new instance of the Plugin. Obviously the constructor is called - use this to init variables but any real work should be done later. Plugin.start() is called, which calls start_hook() Within start_hook, the Plugin should create Devices and register them by calling plugin_adaptor->register_device(device). ... At any time we may call get_id(), get_name(), get_desc() on the Plugin. ... Plugin->stop() is called, which calls stop_hook(). The Plugin should unregister any devices created in start_hook() The destroy() function is called. This should delete the Plugin created in create().
- So how do devices / ports work?
Devices have one or more ports (accessible by port_count() and get_port(i)). Ports are where the action happens. They need to provide the following methods:
read(uint8_t *, unsigned int) write(uint8_t *, unsigned int)
Universes will then use these methods to read/write data from a port. Write is self explanatory, a call to read is triggered by port->dmx_changed(). These calls need to be non-blocking, blocking here will delay the main processing loop. To satisfy this, most ports use this sequence of events:
- register a file descriptor for reading // some time later - receive notification that there is new data - read the data and copy it to a buffer - call dmx_changed() to notify the Universe we have new data - then Universe then calls read()
Often more than one port will use the same file descriptor which is owned by the Device. This means the device is responsible for reading the data and dispatching to the right port. See below for an example.
- Here's an example of how dmx data is received from the UsbPro Device.
The UsbProDevice will have been registered using plugin_adaptor->register_fd(). When input becomes available the following sequence happens:
device->action() // signals the device that new data is available widget->recv() // tells the widget to read more data widget->do_recv() // reads the data from the fd widget->handle_cos() // handles the change-of-state message from the widget device->new_dmx() // signal the device that new dmx data has arrived port->dmx_changed() // signal the port that new dmx data has arrived // if this port is bound to a universe, the universe will then call port->read() device->get_dmx() widget->get_dmx()
Of course, the plugin authors are free to implement this however they like.
- I have difficulties to track the flow of data from it is fetched from the USB Pro until it is printed to the console by the app lla_usbpro. Some diagrams over the structure and data flow diagrams, or text that gives a good overview would be nice
Config messages are handled a little differently for two reasons:
* The configure() method in a plugin has to return a response immediately. We don't want to block because we'll delay all lla processing. The new RPC subsystem removes this limitation. * Sending a PARAMETER_REQUEST to the widget doesn't generate a response immediately (in fact it may not generate one at all).
To work around this, we send a parameter_request when we start the device, and then anytime we set parameters. In the meantime we store the parameters in the widget object and return those. The sequence looks like:
device->configure() device->config_get_params() widget->get_parms()
- What is the interface between the LLA core and LLA plugins?
See above and the files plugin.h, device.h and port.h. The create() call will be passed a PluginAdaptor object which can then be used to register/unregister file descriptors, loop functions, timeouts and devices.
- What is the interface between the LLA core and other apps/clients to LLA like QLC?
All clients should use the LlaClient library. This needs better documentation.
- What is it with the hidden web server?
That's coming in the next version. It's not checked in yet.
- How is functionality split between the usbpro plugin and the example program?
The example program constructs configuration request messages and sends them (using LlaClient) to the Lla Core. The core routes this message to the plugin, which then returns a response message. This response is passed back to the client.
- When llad is started it loads all available plugins. Always?
Ideas for easy configuration
For some users, it will be useful to have a "auto-connect" feature. When a attached device is discovered (either when LLA i started or when a new device is attached), the user could be asked if the available ports (input as well as output) should be patched to the lowest available universes.
- Enable auto-connect ( OFF|connect whatever comes first|connect by stored patch layout)
- Save a given combination of devices (just by type or with unique ID's from serial numbers, USB device ID's etc)
Which devices cannot be autodetected?
Ideas for QLC integration
Here is room for YOUR writing :-)
About devince config messages
We need a way to tune settings on a port/device that the LLA Core doesn't know about. To enable this, the LlaClient provides a method dev_config(unsigned int dev, LlaDevConfigMsg *msg)
The LlaDevConfigMsg is an interface which declares one method: pack(uint8_t buffer, unsigned int length). On the device side, we declare a method configure(uint8_t *request, int length)
So to use this:
On the client
MyObserver::dev_config(unsigned int dev, uin8_t *res, unsigned int length) { MyLlaDevConfigMsg msg = parse_message(data, length); // do something with the result } int main() { // all the setup code MyObserver observer; // the observer gets the dev_config() callback lla_client->set_observer(&observer); MyLlaDevConfigMsg msg; // set some fields msg.foo = 1 lla_client->dev_config(device_id, &msg); //calls pack() on the message }
In the device:
MyDevice::configure(data, length) { MyLlaDevConfigMsg msg = parse_message(data, length); // do something with the message MyLlaDevConfigMsg *response = new MyLlaDevConfigMsg(); // response is deleted by the lla core return response; }
The tool app "lla-usbpro"
The purpose is to set and get the settings that reside in the USB Pro box.
The communication with USB Pro's seems to go via the LLA core, and lla-usbpro registers as a LLA client, and uses some event handlers.
As defined in the device spec. (PDF from Enttec):
label=3 response
- 1. data byte= Firmware version LSB. Valid range is 0 to 255.
- 2. data byte=Firmware version MSB. Valid range is 0 to 255.
- 3. data byte=DMX output break time in 10.67 microsecond units. range=[9-127] (96.03 - 1355.09 micro seconds)
- 4. data byte=DMX output Mark After Break time in 10.67 microsecond units. range=[1-127] (10.67 - 1355.09 micro seconds)
- 5. data byte=DMX output rate in packets per second. range=[1-40]
- x. data byte= some user configuration of the requested size
The serial number is is decoded (from 4 bit Binary Coded Decimal) in lla-usbpro, not the plugin.