Personal tools
The Open Lighting Project has moved!

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 Client API"

From wiki.openlighting.org

Jump to: navigation, search
(Example III: Receiving DMX data)
m (Add link to doxygen tutorial)
 
(31 intermediate revisions by 4 users not shown)
Line 1: Line 1:
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, helper classes as provided which can be used to do most of the client setup / management.
+
__TOC__
  
 +
'''Warning''': This documentation may be out of date. If in doubt check the code in the [http://docs.openlighting.org/doc/client_tutorial.html Doxygen documentation] or the ola-examples package.
  
==Example I: Sending an DMX update==
+
OLA provides a C++ client library which allows other applications to send/receive DMX as well as control the OLA server. When writing a client application, you have a choice between two client APIs. There was a third API, the OlaClient, but that was deprecated in Feb 2013. It will be removed in a future release and should not be used in new code.
  
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>
+
== Compiler & Linker Flags ==
 +
 
 +
To correctly build and link against libola you'll need to pass the right options to your build system. OLA installs a [http://en.wikipedia.org/wiki/Pkg-config pkg-config] file with the right flags. To get the flags run:
 +
 
 +
<pre>
 +
$ pkg-config --cflags --libs libola
 +
-I/usr/local/include  -L/usr/local/lib -lola -lolacommon -lprotobuf
 +
</pre>
 +
 
 +
If you're using autotools, you can add
 +
 
 +
<pre>
 +
PKG_PROG_PKG_CONFIG
 +
PKG_CHECK_MODULES(libola, [libola >= 0.8.26])
 +
</pre>
 +
 
 +
to configure.ac
 +
 
 +
== Different Client APIs ==
 +
 
 +
If you want to:
 +
; Only send DMX
 +
: Use StreamingClient.h . This sets up a unidirectional channel between the client and the OLA server.
 +
 
 +
; Do any of the following send DMX, receive DMX, patch ports, control RDM devices
 +
: Use the OlaCallbackClient which can be setup using the OlaCallbackClientWrapper
 +
 
 +
=== OlaClient - DEPRECATED, DO NOT USE ===
 +
The OlaClient class provides a SetObserver() method which takes an object of type OlaClientObserver. When calls to the OlaClient object complete, the appropriate method is call on the OlaClientObserver object. The following diagrams attempts to show this (the red parts are the bit the client application provides).
 +
 
 +
[[Image:OlaClientFlow.png|center]]
 +
 
 +
  class Observer: public ola::OlaClientObserver {
 +
    public
 +
      Observer() {}:
 +
      void ForceRDMDiscoveryComplete(unsigned int universe,
 +
                                    const string &error) {
 +
          // something here
 +
      }
 +
  };
 +
 
 +
  Observer observer();
 +
  unsigned int universe = 1;
 +
  client->SetObserver(&observer);
 +
  client->ForceDiscovery(universe);
 +
 
 +
=== OlaCallbackClient ===
 +
On the other hand, the OlaCallbackClient takes a [[OLA_developer_info#Closures_.26_Callbacks| Callback]] object whenever a method is called.
 +
 
 +
  void ForceRDMDiscoveryComplete(unsigned int universe, string &error) {
 +
    // fill this in
 +
  }
 +
 
 +
  unsigned int universe = 1;
 +
  client->ForceDiscovery(
 +
      universe,
 +
      ola::NewSingleCallback(&ForceRDMDiscoveryComplete, universe));
 +
 
 +
The Callbacks used in OlaCallbackClient are much more flexible than the Observer model in OlaClient. It's recommended that all new code use OlaCallbackClient.
 +
 
 +
=== Wrapper Classes ===
 +
 
 +
The constructors of both OlaClient and OlaCallbackClient take a pointer to a [[OLA_developer_info#SelectServer_.26_Sockets| Connected Socket]] object. While this is useful for testing, most clients don't care about the details of creating a socket, connecting to the OLA server etc. This is where the client wrapper classes come in. They take care of setting up the connection to the server and provide a way to access the OlaClient* object and the underlying SelectServer object.
 +
 
 +
  #include <ola/OlaClientWrapper.h>
 +
  #include <ola/OlaCallbackClient.h>
 +
 
 +
  int main() {
 +
    ola::OlaCallbackClientWrapper wrapper;
 +
    if (!wrapper.Setup())
 +
      //setup failed
 +
      return -1;
 +
 
 +
    // Get the underlying OlaCallbackClient object
 +
    ola::OlaCallbackClient *client = wrapper.GetClient();
 +
 
 +
    // Get the underlying SelectServer object
 +
    ola::io::SelectServer *ss = client->GetSelectServer();
 +
 
 +
    // do stuff here
 +
  }
 +
 
 +
Note that for historical reasons, the OlaClientWrapper class is also referred to as SimpleClient.
 +
 
 +
==Example I:  Sending an DMX update using the StreamingClient==
 +
 
 +
The simplest interface to OLA is the StreamingClient which just allows an application to send DMX data to the OLA server
 +
 
 +
#include <stdlib.h>
 +
#include <ola/DmxBuffer.h>
 +
#include <ola/Logging.h>
 +
#include <ola/StreamingClient.h>
 +
 +
  #include <iostream>                                                                                                         
 +
 +
using std::cout;
 +
using std::endl;
 +
 +
 +
int main(int argc, char *argv[]) {
 +
  unsigned int universe = 1;  // universe to use for sending data
 +
  unsigned int i;
 +
 +
  // turn on OLA logging
 +
  ola::InitLogging(ola::OLA_LOG_WARN, ola::OLA_LOG_STDERR);
 +
 +
  // Create a new DmxBuffer to hold the data
 +
  ola::DmxBuffer buffer;
 +
  // set all channels to 0
 +
  buffer.Blackout();
 +
 +
  // create a new client and set the Error Closure
 +
  ola::StreamingClient ola_client;
 +
 +
  // Setup the client, this connects to the server
 +
  if (!ola_client.Setup()) {
 +
    cout << "Setup failed" << endl;
 +
    exit(1);
 +
  }
 +
 
 +
  // send the data to the ola server
 +
  for (i = 0; i < 100; i++) {
 +
    buffer.SetChannel(0, i);
 +
    if (!ola_client.SendDmx(universe, buffer)) {
 +
      cout << "Send DMX failed" << endl;
 +
      exit(1);
 +
    }
 +
    usleep(20000);  // sleep for 20ms between updates                                                                           
 +
  }
 +
 +
  // close the connection
 +
  ola_client.Stop();
 +
  return 0;
 +
}
 +
 
 +
Note there isn't any need to poll for updates from the server in this case because the communication is all one way (client -> server). This also means that you can't receive DMX data or modify state on the server (like port patching and device parameters) when using the StreamingClient.
 +
 
 +
Also take note of the call to usleep(). Without this you're likely to fill the network socket buffer at which point the client code will detect an error and close the socket (you may need to increase the number of loop iterations to trigger this).
 +
 
 +
==Example II: Sending an DMX update using the SimpleClient==
 +
 
 +
This example connects to the OLA Server and sends one DMX update just like the example above but instead uses OlaClient & OlaClientWrapper.  It's about as basic as you can get because it requires no callbacks and exits immediately.
 +
 
 +
#include <ola/DmxBuffer.h>
 +
#include <ola/Logging.h>
 +
#include <ola/OlaClientWrapper.h>
 +
#include <ola/OlaClient.h>
 
    
 
    
 
  int main() {
 
  int main() {
 +
  ola::InitLogging(ola::OLA_LOG_WARN, ola::OLA_LOG_STDERR);
 +
  ola::DmxBuffer buffer;
 +
  buffer.Blackout();
 
   // some dummy dmx data
 
   // some dummy dmx data
   dmx_t dmx_data[] = {0, 128, 255};
+
   buffer.SetChannel(0, 255);
+
 
   lla::SimpleClient simple_client;
+
   ola::OlaClientWrapper wrapper;
   if (!simple_client.Setup())
+
   if (!wrapper.Setup())
 
     //setup failed
 
     //setup failed
 
     return -1;
 
     return -1;
 
   
 
   
   // Get the underlying LlaClient
+
   // Get the underlying OlaClient object
   lla::LlaClient *client = simple_client.GetClient();
+
   ola::OlaClient *client = wrapper.GetClient();
 
   
 
   
 
   // Send the DMX data  
 
   // Send the DMX data  
   client->SendDmx(1, dmx_data, sizeof(dmx_data));
+
   client->SendDmx(1, buffer);
 
  }
 
  }
  
 +
==Example III: Sending multiple updates==
  
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''.
+
The previous example only sent a single DMX update before exiting. This next example adds a timeout which sends DMX every 50ms. This introduces a new class ''SelectServer'' which is used for registering timeouts.
  
 +
  #include <ola/DmxBuffer.h>
 +
  #include <ola/io/SelectServer.h>
 +
  #include <ola/Logging.h>
 +
  #include <ola/OlaClient.h>
 +
  #include <ola/OlaClientWrapper.h>
 +
  #include <ola/Callback.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(ola::OlaClient *client): m_tick(0),
 +
                                          m_client(client) {
 +
        m_buffer.Blackout();
 +
        m_buffer.SetChannel(0, MAX_DMX);
 +
      }
 +
 
 +
      // Called on timeout
 +
      bool SendDmx() {
 +
        m_buffer.SetChannel(1, m_tick);
 +
        m_buffer.SetChannel(2, MAX_DMX - m_tick);
 +
        m_client->SendDmx(1, m_buffer);
 +
        m_tick++;
 +
        m_tick %= MAX_DMX + 1;
 +
        // we must return true else we'll get canceled
 +
        return true;
 +
      }
 +
    private:
 +
      unsigned int m_tick;
 +
      ola::DmxBuffer m_buffer;
 +
      ola::OlaClient *m_client;
 +
  };
 +
 
 +
  int main() {
 +
    ola::InitLogging(ola::OLA_LOG_WARN, ola::OLA_LOG_STDERR);
 +
    ola::OlaClientWrapper wrapper;
 +
 
 +
    if (!wrapper.Setup())
 +
      return -1;
 +
 
 +
    // Create a timeout and register it
 +
    DmxTimeout timeout(wrapper.GetClient());
 +
    ola::io::SelectServer *ss = wrapper.GetSelectServer();
 +
    ss->RegisterRepeatingTimeout(TIMEOUT_MS,
 +
                                ola::NewCallback(&timeout, &DmxTimeout::SendDmx));
 +
 
 +
    // Start the main loop
 +
    ss->Run();
 +
  }
  
==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.
+
In this example we create ''DmxTimeout'' class whose ''SendDmx()'' method is called every time the timer expires.
  
  #include <lla/SimpleClient.h>
+
The other important part here is the ''SelectServer''. As well as the ''RegisterRepeatingTimeout'' 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 IV: Receiving DMX data==
 +
 
 +
The forth example shows how to listen and respond to event from the OLA server.
 +
 
 +
  #include <ola/Logging.h>
 +
#include <ola/OlaClientWrapper.h>
 
   
 
   
// Maximum value of a dmx channel
+
  static const unsigned int UNIVERSE = 1;
  static const unsigned int MAX_DMX = 255;
 
// How often to send updates
 
static const unsigned int TIMEOUT_MS = 50;
 
 
   
 
   
  class DmxTimeout: public lla::select_server::TimeoutListener {
+
  void NewDmx(unsigned int universe,
  public:
+
            const DmxBuffer &data,
    DmxTimeout(lla::LlaClient *client):
+
            const std::string &error) {
      TimeoutListener(), m_tick(0), m_client(client) {}
+
  OLA_INFO << "Received " << (int) data.Size() <<
+
     " channels for universe " << (int) universe;
    // Called on timeout
+
  }
     int Timeout() {
 
      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;
 
    }
 
  private:
 
    unsigned int m_tick;
 
    dmx_t m_dmx_data[3];
 
    lla::LlaClient *m_client;
 
  };
 
 
   
 
   
 
  int main() {
 
  int main() {
   lla::SimpleClient simple_client;
+
   ola::InitLogging(ola::OLA_LOG_INFO, ola::OLA_LOG_STDERR);
 +
  ola::OlaCallbackClient wrapper;
 
   
 
   
   if (!simple_client.Setup())
+
   if (!wrapper.Setup())
 
     return -1;
 
     return -1;
                                                                                                                                               
+
   lla::select_server::SelectServer *ss = simple_client.GetSelectServer();
+
   ola::OlaClient *client = simple_client.GetClient();
   // Create a timeout and register it
+
   // Set the observer and register our interest in this universe
   DmxTimeout timeout(simple_client.GetClient());
+
   client->SetDmxCallback(UNIVERSE, NewCallback(&NewDmx));
   ss->RegisterTimeout(TIMEOUT_MS, &timeout);
+
   client->RegisterUniverse(UNIVERSE, ola::REGISTER);
    
+
   simple_client.GetSelectServer()->Run();
  // Start the main loop
 
  ss->Run();
 
 
  }
 
  }
  
 +
==Example V: More complex client==
  
In this example we create ''DmxTimeout'' class whose ''Timeout()'' method is called every time the timer expires. We use the ''Timeout()'' method to send DMX data.
+
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.
  
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.
+
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.
  
==Example III: Receiving DMX data==
+
Bypassing the SimpleClient and using OlaClient directly addresses both these problems.
  
The third example shows how to listen and respond to event from the LLA server.
+
#include <ola/OlaClient.h>
 
+
#include <ola/io/SelectServer.h>
  #include <lla/SimpleClient.h>
+
  #include <ola/network/Socket.h>
 +
 +
using ola::io::PipeSocket;
 
   
 
   
  static const unsigned int UNIVERSE = 1;
+
  int main() {
 +
  // Create the select server
 +
  ola::io::SelectServer ss;
 
   
 
   
class OurObserver: public lla::LlaClientObserver {
+
   // Create the pipe socket to talk to the server on
   public:
+
  PipeSocket *pipe_socket = new PipeSocket();
    // Called when new DMX values arrive
+
  if (!pipe_socket->Init())
    void NewDmx(unsigned int universe, unsigned int length,
+
     return -1;
                dmx_t *data, const std::string &error) {
 
      printf("received %d channels for universe %d\n",
 
              length, universe);
 
     }
 
};
 
 
   
 
   
int main() {
+
   // Remember to add this socket to the SelectServer
   lla::SimpleClient simple_client;
+
   ss.AddSocket(pipe_socket);
   OurObserver observer;
 
 
   
 
   
   if (!simple_client.Setup())
+
  // Setup the OlaClient
 +
  ola::OlaClient client(pipe_socket);
 +
   if (!client.Setup())
 
     return -1;
 
     return -1;
 
   
 
   
   lla::LlaClient *client = simple_client.GetClient();
+
   // At this point the client is setup. We then need to setup the LLAServer
   // Set the observer and register our interest in this universe
+
  // ...
   client->SetObserver(&observer);
+
  client->RegisterUniverse(UNIVERSE, lla::REGISTER);
+
   // Once that is done we add the pipe as a new connection
  simple_client.GetSelectServer()->Run();
+
   ola_server->NewConnection(m_pipe_socket->OppositeEnd());
 
  }
 
  }
  
  
Here we inherit from the ''LlaClientObserver'' class and override the methods we're interested in receiving notification for.
+
This example shows how we can create our own instance of a ''ConnectedSocket'' (''PipeSocket'' is a subclass of ''ConnectedSocket'') and pass it to the ''OlaClient'' to use. This code is very similar to what ''SimpleClient'' does under the hood.
 
 
In the main function we set the observer object and register our interest in a universe.
 
 
 
==Example IV: More complex client==
 

Latest revision as of 03:40, 12 October 2013

Warning: This documentation may be out of date. If in doubt check the code in the Doxygen documentation or the ola-examples package.

OLA provides a C++ client library which allows other applications to send/receive DMX as well as control the OLA server. When writing a client application, you have a choice between two client APIs. There was a third API, the OlaClient, but that was deprecated in Feb 2013. It will be removed in a future release and should not be used in new code.


Compiler & Linker Flags

To correctly build and link against libola you'll need to pass the right options to your build system. OLA installs a pkg-config file with the right flags. To get the flags run:

$ pkg-config --cflags --libs libola
-I/usr/local/include  -L/usr/local/lib -lola -lolacommon -lprotobuf 

If you're using autotools, you can add

PKG_PROG_PKG_CONFIG
PKG_CHECK_MODULES(libola, [libola >= 0.8.26]) 

to configure.ac

Different Client APIs

If you want to:

Only send DMX
Use StreamingClient.h . This sets up a unidirectional channel between the client and the OLA server.
Do any of the following send DMX, receive DMX, patch ports, control RDM devices
Use the OlaCallbackClient which can be setup using the OlaCallbackClientWrapper

OlaClient - DEPRECATED, DO NOT USE

The OlaClient class provides a SetObserver() method which takes an object of type OlaClientObserver. When calls to the OlaClient object complete, the appropriate method is call on the OlaClientObserver object. The following diagrams attempts to show this (the red parts are the bit the client application provides).

OlaClientFlow.png
 class Observer: public ola::OlaClientObserver {
   public
     Observer() {}:
     void ForceRDMDiscoveryComplete(unsigned int universe,
                                    const string &error) {
         // something here
     }
 };
 
 Observer observer();
 unsigned int universe = 1;
 client->SetObserver(&observer);
 client->ForceDiscovery(universe);

OlaCallbackClient

On the other hand, the OlaCallbackClient takes a Callback object whenever a method is called.

 void ForceRDMDiscoveryComplete(unsigned int universe, string &error) {
   // fill this in
 }
 
 unsigned int universe = 1;
 client->ForceDiscovery(
     universe,
     ola::NewSingleCallback(&ForceRDMDiscoveryComplete, universe));

The Callbacks used in OlaCallbackClient are much more flexible than the Observer model in OlaClient. It's recommended that all new code use OlaCallbackClient.

Wrapper Classes

The constructors of both OlaClient and OlaCallbackClient take a pointer to a Connected Socket object. While this is useful for testing, most clients don't care about the details of creating a socket, connecting to the OLA server etc. This is where the client wrapper classes come in. They take care of setting up the connection to the server and provide a way to access the OlaClient* object and the underlying SelectServer object.

 #include <ola/OlaClientWrapper.h>
 #include <ola/OlaCallbackClient.h>
 
 int main() {
   ola::OlaCallbackClientWrapper wrapper;
   if (!wrapper.Setup())
     //setup failed
     return -1;
 
   // Get the underlying OlaCallbackClient object
   ola::OlaCallbackClient *client = wrapper.GetClient();
 
   // Get the underlying SelectServer object
   ola::io::SelectServer *ss = client->GetSelectServer();
 
   // do stuff here
 }

Note that for historical reasons, the OlaClientWrapper class is also referred to as SimpleClient.

Example I: Sending an DMX update using the StreamingClient

The simplest interface to OLA is the StreamingClient which just allows an application to send DMX data to the OLA server

#include <stdlib.h>
#include <ola/DmxBuffer.h>
#include <ola/Logging.h>
#include <ola/StreamingClient.h>

#include <iostream>                                                                                                          

using std::cout;
using std::endl;


int main(int argc, char *argv[]) {
  unsigned int universe = 1;  // universe to use for sending data
  unsigned int i;

  // turn on OLA logging
  ola::InitLogging(ola::OLA_LOG_WARN, ola::OLA_LOG_STDERR);

  // Create a new DmxBuffer to hold the data
  ola::DmxBuffer buffer;
  // set all channels to 0
  buffer.Blackout();

  // create a new client and set the Error Closure
  ola::StreamingClient ola_client;

  // Setup the client, this connects to the server
  if (!ola_client.Setup()) {
    cout << "Setup failed" << endl;
    exit(1);
  }
 
  // send the data to the ola server 
  for (i = 0; i < 100; i++) {
    buffer.SetChannel(0, i);
    if (!ola_client.SendDmx(universe, buffer)) {
      cout << "Send DMX failed" << endl;
      exit(1);
    }
    usleep(20000);   // sleep for 20ms between updates                                                                            
  }

  // close the connection
  ola_client.Stop();
  return 0;
}

Note there isn't any need to poll for updates from the server in this case because the communication is all one way (client -> server). This also means that you can't receive DMX data or modify state on the server (like port patching and device parameters) when using the StreamingClient.

Also take note of the call to usleep(). Without this you're likely to fill the network socket buffer at which point the client code will detect an error and close the socket (you may need to increase the number of loop iterations to trigger this).

Example II: Sending an DMX update using the SimpleClient

This example connects to the OLA Server and sends one DMX update just like the example above but instead uses OlaClient & OlaClientWrapper. It's about as basic as you can get because it requires no callbacks and exits immediately.

#include <ola/DmxBuffer.h>
#include <ola/Logging.h>
#include <ola/OlaClientWrapper.h>
#include <ola/OlaClient.h>
 
int main() {
  ola::InitLogging(ola::OLA_LOG_WARN, ola::OLA_LOG_STDERR);
  ola::DmxBuffer buffer;
  buffer.Blackout();
  // some dummy dmx data
  buffer.SetChannel(0, 255);
 
  ola::OlaClientWrapper wrapper;
  if (!wrapper.Setup())
    //setup failed
    return -1;

  // Get the underlying OlaClient object
  ola::OlaClient *client = wrapper.GetClient();

  // Send the DMX data 
  client->SendDmx(1, buffer);
}

Example III: Sending multiple updates

The previous example only sent a single DMX update before exiting. This next example adds a timeout which sends DMX every 50ms. This introduces a new class SelectServer which is used for registering timeouts.

 #include <ola/DmxBuffer.h>
 #include <ola/io/SelectServer.h>
 #include <ola/Logging.h>
 #include <ola/OlaClient.h> 
 #include <ola/OlaClientWrapper.h>
 #include <ola/Callback.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(ola::OlaClient *client): m_tick(0),
                                         m_client(client) {
       m_buffer.Blackout();
       m_buffer.SetChannel(0, MAX_DMX);
     }
 
     // Called on timeout
     bool SendDmx() {
       m_buffer.SetChannel(1, m_tick);
       m_buffer.SetChannel(2, MAX_DMX - m_tick);
       m_client->SendDmx(1, m_buffer);
       m_tick++;
       m_tick %= MAX_DMX + 1;
       // we must return true else we'll get canceled
       return true;
     }
   private:
     unsigned int m_tick;
     ola::DmxBuffer m_buffer;
     ola::OlaClient *m_client;
 };
 
 int main() {
   ola::InitLogging(ola::OLA_LOG_WARN, ola::OLA_LOG_STDERR);
   ola::OlaClientWrapper wrapper;
 
   if (!wrapper.Setup())
     return -1;
 
   // Create a timeout and register it
   DmxTimeout timeout(wrapper.GetClient());
   ola::io::SelectServer *ss = wrapper.GetSelectServer();
   ss->RegisterRepeatingTimeout(TIMEOUT_MS,
                                ola::NewCallback(&timeout, &DmxTimeout::SendDmx));
 
   // Start the main loop
   ss->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 RegisterRepeatingTimeout 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 IV: Receiving DMX data

The forth example shows how to listen and respond to event from the OLA server.

#include <ola/Logging.h>
#include <ola/OlaClientWrapper.h>

static const unsigned int UNIVERSE = 1;

void NewDmx(unsigned int universe,
            const DmxBuffer &data, 
            const std::string &error) {
  OLA_INFO << "Received " << (int) data.Size() <<
    " channels for universe " << (int) universe;
}

int main() {
  ola::InitLogging(ola::OLA_LOG_INFO, ola::OLA_LOG_STDERR);
  ola::OlaCallbackClient wrapper;

  if (!wrapper.Setup())
    return -1;

  ola::OlaClient *client = simple_client.GetClient();
  // Set the observer and register our interest in this universe
  client->SetDmxCallback(UNIVERSE, NewCallback(&NewDmx));
  client->RegisterUniverse(UNIVERSE, ola::REGISTER);
  simple_client.GetSelectServer()->Run();
}

Example V: 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 OlaClient directly addresses both these problems.

#include <ola/OlaClient.h>
#include <ola/io/SelectServer.h>
#include <ola/network/Socket.h>

using ola::io::PipeSocket;

int main() {
  // Create the select server
  ola::io::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 OlaClient
  ola::OlaClient 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
  ola_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 OlaClient to use. This code is very similar to what SimpleClient does under the hood.