We've launched our new site at www.openlighting.org. This wiki will remain and be updated with more technical information.
OLA Client API
From wiki.openlighting.org
Note: This refers to the most recent version of LLA [>= 0.3.0]
LLA provides a client library which allows other applications to send/receive DMX as well as control the LLA server. To cater for different application requirements, a SimpleClient helper class is provided which can be used to perform the client setup / management.
Contents
Example I: Sending an DMX update
This first client simply connects to the LLA Server and sends one DMX update. It's about as simple as you can get because it requires no callbacks and exits immediately. We use two classes in this example, lla::SimpleClient, and lla::LlaClient.
#include <lla/SimpleClient.h> int main() { // some dummy dmx data dmx_t dmx_data[] = {0, 128, 255}; lla::SimpleClient simple_client; if (!simple_client.Setup()) //setup failed return -1; // Get the underlying LlaClient lla::LlaClient *client = simple_client.GetClient(); // Send the DMX data client->SendDmx(1, dmx_data, sizeof(dmx_data)); }
The main class used to communicate with the LLA Server is LlaClient. For ease of use, the SimpleClient class sets up LlaClient with a connection to an LLA Server running on the localhost:LLA_DEFAULT_PORT. You can call GetClient() on a SimpleClient instance to get a pointer to the underlying LlaClient.
Example II: Sending multiple updates
The previous example only sent a single DMX update before quitting. This next example adds a timeout which sends DMX every 50ms. This introduces a new class SelectServer which is used for registering timeouts.
#include <lla/SimpleClient.h> #include <lla/Closure.h> // Maximum value of a dmx channel static const unsigned int MAX_DMX = 255; // How often to send updates static const unsigned int TIMEOUT_MS = 50; class DmxTimeout { public: DmxTimeout(lla::LlaClient *client): m_tick(0), m_client(client) {} // Called on timeout int SendDmx() { m_dmx_data[0] = MAX_DMX; m_dmx_data[1] = m_tick; m_dmx_data[2] = MAX_DMX - m_tick; m_client->SendDmx(1, m_dmx_data, sizeof(m_dmx_data)); m_tick++; m_tick %= MAX_DMX + 1; // we must return 0 else we get canceled return 0; } private: unsigned int m_tick; dmx_t m_dmx_data[3]; lla::LlaClient *m_client; }; int main() { lla::SimpleClient simple_client; if (!simple_client.Setup()) return -1; // Create a timeout and register it DmxTimeout timeout(simple_client.GetClient()); ss->RegisterTimeout(TIMEOUT_MS, lla::NewClosure(&timeout, &DmxTimeout::SendDmx), true); // we want this to repeat // Start the main loop simple_client.GetSelectServer()->Run(); }
In this example we create DmxTimeout class whose SendDmx() method is called every time the timer expires.
The other important part here is the SelectServer. As well as the RegisterTimeout method we've used above, this can also be used to register sockets so we can respond to network activity. The Run() method starts the main event processing loop which will halt if an error occurs or Terminate() is called.
Example III: Receiving DMX data
The third example shows how to listen and respond to event from the LLA server.
#include <lla/SimpleClient.h> static const unsigned int UNIVERSE = 1; class OurObserver: public lla::LlaClientObserver { public: // Called when new DMX values arrive void NewDmx(unsigned int universe, unsigned int length, dmx_t *data, const std::string &error) { printf("received %d channels for universe %d\n", length, universe); } }; int main() { lla::SimpleClient simple_client; OurObserver observer; if (!simple_client.Setup()) return -1; lla::LlaClient *client = simple_client.GetClient(); // Set the observer and register our interest in this universe client->SetObserver(&observer); client->RegisterUniverse(UNIVERSE, lla::REGISTER); simple_client.GetSelectServer()->Run(); }
Here we inherit from the LlaClientObserver class and override the methods we're interested in receiving notification for.
In the main function we set the observer object and register our interest in a universe.
Example IV: More complex client
The above is all well and good but what if the main application has it's own event processing loop? An example of this is a GTK/Glib application which uses GMainLoop.
On the other hand, what if you're not connecting to the LLA Server over TCP? Sometimes it may be desirable to embed the LLA server within the main application.
Bypassing the SimpleClient and using LlaClient directly addresses both these problems.
#include <lla/LlaClient.h> #include <lla/select_server/SelectServer.h> #include <lla/select_server/Socket.h> using lla::select_server::PipeSocket; int main() { // Create the select server lla::select_server::SelectServer ss; // Create the pipe socket to talk to the server on PipeSocket *pipe_socket = new PipeSocket(); if (!pipe_socket->Init()) return -1; // Remember to add this socket to the SelectServer ss.AddSocket(pipe_socket); // Setup the LlaClient lla::LlaClient client(pipe_socket); if (!client.Setup()) return -1; // At this point the client is setup. We then need to setup the LLAServer // ... // Once that is done we add the pipe as a new connection lla_server->NewConnection(m_pipe_socket->OppositeEnd()); }
This example shows how we can create our own instance of a ConnectedSocket (PipeSocket is a subclass of ConnectedSocket) and pass it to the LlaClient to use. This code is very similar to what SimpleClient does under the hood.