Open Lighting Architecture 0.10.9
Loading...
Searching...
No Matches
Advanced C++ Client API

Advanced C++ Client API Tutorial.

Overview

This page covers some of the more advanced aspects of the OLA C++ Client API including using callbacks, handling a server-side shutdown and running the client in a separate thread. For an introduction on using the OLA C++ Client see C++ DMX Client API Tutorial.

Using Callbacks

Almost all of the ola::client::OlaClient methods take a Callback object. Each callback is executed when the server responds with the results of the method invocation.

Lets look at the ola::client::OlaClient::FetchPluginList method. This method is used to fetch a list of available plugins from the olad server. The client may choose to present the list of plugins to the user so they know what Devices / Protocols are supported.

The code below calls FetchPluginList() and then waits for the results from the olad server. When the list of plugins is received, the name of each plugin is printed to stdout and the program exits.

#include <ola/io/SelectServer.h>
#include <ola/Logging.h>
#include <ola/Callback.h>
#include <vector>
using std::cout;
using std::endl;
using std::vector;
// Called when plugin information is available.
void ShowPluginList(ola::io::SelectServer *ss,
const ola::client::Result &result,
const vector<ola::client::OlaPlugin> &plugins) {
if (!result.Success()) {
std::cerr << result.Error() << endl;
} else {
vector<ola::client::OlaPlugin>::const_iterator iter = plugins.begin();
for (; iter != plugins.end(); ++iter) {
cout << "Plugin: " << iter->Name() << endl;
}
}
ss->Terminate(); // terminate the program.
}
int main(int, char *[]) {
if (!wrapper.Setup()) {
std::cerr << "Setup failed" << endl;
exit(1);
}
// Fetch the list of plugins, a pointer to the SelectServer is passed to the
// callback so we can use it to terminate the program.
wrapper.GetClient()->FetchPluginList(
ola::NewSingleCallback(ShowPluginList, ss));
// Start the main loop
ss->Run();
}
Helper classes for managing OLA clients.
Header file for OLA Logging.
ola::io::SelectServer * GetSelectServer()
Get the SelectServer used by this client.
Definition ClientWrapper.h:74
bool Setup()
Setup the client.
Definition OlaClientWrapper.cpp:43
A templatized ClientWrapper.
Definition ClientWrapper.h:111
ClientClass * GetClient() const
Return the underlying client object.
Definition ClientWrapper.h:122
Indicates the result of a OLA API call.
Definition Result.h:52
const std::string & Error() const
Returns the error message if the action failed.
Definition Result.h:73
bool Success() const
Indicates the status of the action. If the action failed Error() can be used to fetch the error messa...
Definition Result.h:67
A single threaded I/O event management system.
Definition SelectServer.h:63
void Terminate()
Exit from the Run() loop.
Definition SelectServer.cpp:116
void Run()
Enter the event loop.
Definition SelectServer.cpp:126
SingleUseCallback0< ReturnType > * NewSingleCallback(ReturnType(*callback)())
A helper function to create a new SingleUseCallback with 0 create-time arguments and 0 execution time...
Definition Callback.h:194
@ OLA_LOG_WARN
Definition Logging.h:101
@ OLA_LOG_STDERR
Definition Logging.h:117
bool InitLogging(log_level level, log_output output)
Initialize the OLA logging system.
Definition Logging.cpp:118

Handling Server Shutdown.

To write a robust program, you'll need to handle the case of olad shutting down. This can be done by setting a callback to be executed when the connection to the olad server is closed.

The following example builds on the OlaClient TX example. Instead of exiting after 100 DMX frames have been sent, the program runs until the connection to olad is closed.

#include <stdint.h>
#include <ola/DmxBuffer.h>
#include <ola/io/SelectServer.h>
#include <ola/Logging.h>
#include <ola/Callback.h>
using std::cout;
using std::endl;
// Called when the connection to olad is closed.
void ConnectionClosed(ola::io::SelectServer *ss) {
std::cerr << "Connection to olad was closed" << endl;
ss->Terminate(); // terminate the program.
}
bool SendData(ola::client::OlaClientWrapper *wrapper) {
static unsigned int universe = 1;
static uint8_t i = 0;
buffer.Blackout();
buffer.SetChannel(0, i++);
wrapper->GetClient()->SendDMX(universe, buffer, ola::client::SendDMXArgs());
return true;
}
int main(int, char *[]) {
if (!wrapper.Setup()) {
std::cerr << "Setup failed" << endl;
exit(1);
}
// Create a timeout and register it with the SelectServer
ss->RegisterRepeatingTimeout(25, ola::NewCallback(&SendData, &wrapper));
// Register the on-close handler
wrapper.GetClient()->SetCloseHandler(
ola::NewSingleCallback(ConnectionClosed, ss));
// Start the main loop
ss->Run();
}
void SetChannel(unsigned int channel, uint8_t data)
Set a single channel. Calling this on an uninitialized buffer will call Blackout() first....
Definition DmxBuffer.cpp:232
bool Blackout()
Set the buffer to all zeros.
Definition DmxBuffer.cpp:294
ola::thread::timeout_id RegisterRepeatingTimeout(unsigned int ms, ola::Callback0< bool > *callback)
Execute a callback periodically.
Definition SelectServer.cpp:217
Callback0< ReturnType > * NewCallback(ReturnType(*callback)())
A helper function to create a new Callback with 0 create-time arguments and 0 execution time argument...
Definition Callback.h:211
Arguments passed to the SendDMX() method.
Definition ClientArgs.h:71

Instead of exiting, a better approach would be to try and reconnect to the olad server. Clients that do this should use a ola::BackOffPolicy to avoid spinning in a loop trying to reconnect.

Running the OlaClient within a thread.

Sometimes it can be difficult to integrate OLA's Event Driven programming model with the main event loop of your program. The OlaClient is not thread safe, so a workaround is to run a separate thread for the OlaClient.

We can use ola::io::SelectServer::Execute to run a callback on a SelectServer running in a different thread. This allows us to schedule the calls to OlaClient on the thread where the SelectServer is running.

#include <ola/io/SelectServer.h>
#include <ola/Logging.h>
#include <ola/thread/Thread.h>
#include <ola/Callback.h>
#include <vector>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <ola/win/CleanWindows.h>
#endif // _WIN32
using std::cout;
using std::endl;
using std::vector;
public:
bool Start() {
if (!m_wrapper.Setup()) {
return false;
}
}
void Stop() {
m_wrapper.GetSelectServer()->Terminate();
}
void FetchPluginList(ola::client::PluginListCallback *callback) {
m_wrapper.GetSelectServer()->Execute(
NewSingleCallback(this, &OlaThread::InternalFetchPluginList, callback));
}
ola::io::SelectServer* GetSelectServer() {
return m_wrapper.GetSelectServer();
}
protected:
void *Run() {
m_wrapper.GetSelectServer()->Run();
return NULL;
}
private:
void InternalFetchPluginList(ola::client::PluginListCallback *callback) {
m_wrapper.GetClient()->FetchPluginList(callback);
}
};
// Called when plugin information is available.
// This function is run from the OLA Thread, if you use variables in the main
// program then you'll need to add locking.
void ShowPluginList(ola::io::SelectServer *ss,
const ola::client::Result &result,
const vector<ola::client::OlaPlugin> &plugins) {
if (!result.Success()) {
std::cerr << result.Error() << endl;
} else {
vector<ola::client::OlaPlugin>::const_iterator iter = plugins.begin();
for (; iter != plugins.end(); ++iter) {
cout << "Plugin: " << iter->Name() << endl;
}
}
ss->Terminate(); // terminate the program.
}
int main(int, char *[]) {
OlaThread ola_thread;
if (!ola_thread.Start()) {
std::cerr << "Failed to start OLA thread" << endl;
exit(1);
}
// Control is returned to the main program here.
// To fetch a list of plugins
ola::io::SelectServer *ss = ola_thread.GetSelectServer();
ola_thread.FetchPluginList(
ola::NewSingleCallback(ShowPluginList, ss));
// The main program continues...
#ifdef _WIN32
Sleep(1000);
#else
sleep(1);
#endif // _WIN32
// When it's time to exit, Stop the OLA thread.
ola_thread.Stop();
ola_thread.Join();
}
Definition client_thread.cpp:40
void * Run()
The entry point for the new thread.
Definition client_thread.cpp:63
bool Start()
Start the thread and wait for the thread to be running.
Definition client_thread.cpp:42
A 2 argument callback which deletes itself after it's run.
Definition Callback.h:1907
void Execute(ola::BaseCallback0< void > *callback)
Execute the supplied callback at some point in the future.
Definition SelectServer.cpp:253
Definition Thread.h:52
virtual bool Start()
Start the thread and wait for the thread to be running.
Definition Thread.cpp:90
virtual bool Join(void *ptr=NULL)
Join this thread.
Definition Thread.cpp:158

It's important to realize that the ShowPluginList function will be run from the OLA thread. If this function uses any variables from the main program (such as UI widgets), you must use locking, or some other thread synchronization technique.