syndilights/open-lighting-architecture/ola-0.8.4/olad/OlaServerServiceImpl.cpp

848 lines
24 KiB
C++

/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* OlaServerServiceImpl.cpp
* Implemtation of the OlaServerService interface. This is the class that
* handles all the RPCs on the server side.
* Copyright (C) 2005 - 2008 Simon Newton
*/
#include <algorithm>
#include <string>
#include <vector>
#include "common/protocol/Ola.pb.h"
#include "ola/Callback.h"
#include "ola/DmxBuffer.h"
#include "ola/ExportMap.h"
#include "ola/Logging.h"
#include "ola/rdm/UIDSet.h"
#include "olad/Client.h"
#include "olad/Device.h"
#include "olad/DeviceManager.h"
#include "olad/DmxSource.h"
#include "olad/InternalRDMController.h"
#include "olad/OlaServerServiceImpl.h"
#include "olad/Plugin.h"
#include "olad/PluginManager.h"
#include "olad/Port.h"
#include "olad/PortManager.h"
#include "olad/Universe.h"
#include "olad/UniverseStore.h"
namespace ola {
using google::protobuf::RpcController;
using ola::proto::Ack;
using ola::proto::DeviceConfigReply;
using ola::proto::DeviceConfigRequest;
using ola::proto::DeviceInfo;
using ola::proto::DeviceInfoReply;
using ola::proto::DeviceInfoRequest;
using ola::proto::DmxData;
using ola::proto::MergeModeRequest;
using ola::proto::OptionalUniverseRequest;
using ola::proto::PatchPortRequest;
using ola::proto::PluginDescriptionReply;
using ola::proto::PluginDescriptionRequest;
using ola::proto::PluginInfo;
using ola::proto::PluginListReply;
using ola::proto::PluginListRequest;
using ola::proto::PortInfo;
using ola::proto::RegisterDmxRequest;
using ola::proto::UniverseInfo;
using ola::proto::UniverseInfoReply;
using ola::proto::UniverseNameRequest;
using ola::proto::UniverseRequest;
using ola::rdm::UIDSet;
OlaServerServiceImpl::~OlaServerServiceImpl() {
if (m_uid)
delete m_uid;
}
/*
* Returns the current DMX values for a particular universe
*/
void OlaServerServiceImpl::GetDmx(
RpcController* controller,
const UniverseRequest* request,
DmxData* response,
google::protobuf::Closure* done) {
Universe *universe = m_universe_store->GetUniverse(request->universe());
if (!universe)
return MissingUniverseError(controller, done);
const DmxBuffer buffer = universe->GetDMX();
response->set_data(buffer.Get());
response->set_universe(request->universe());
done->Run();
}
/*
* Register a client to receive DMX data.
*/
void OlaServerServiceImpl::RegisterForDmx(
RpcController* controller,
const RegisterDmxRequest* request,
Ack* response,
google::protobuf::Closure* done) {
Universe *universe = m_universe_store->GetUniverseOrCreate(
request->universe());
if (!universe)
return MissingUniverseError(controller, done);
if (request->action() == ola::proto::REGISTER) {
universe->AddSinkClient(m_client);
} else {
universe->RemoveSinkClient(m_client);
}
done->Run();
(void) response;
}
/*
* Update the DMX values for a particular universe
*/
void OlaServerServiceImpl::UpdateDmxData(
RpcController* controller,
const DmxData* request,
Ack* response,
google::protobuf::Closure* done) {
Universe *universe = m_universe_store->GetUniverse(request->universe());
if (!universe)
return MissingUniverseError(controller, done);
if (m_client) {
DmxBuffer buffer;
buffer.Set(request->data());
uint8_t priority = DmxSource::PRIORITY_DEFAULT;
if (request->has_priority()) {
priority = request->priority();
priority = std::max(DmxSource::PRIORITY_MIN, priority);
priority = std::min(DmxSource::PRIORITY_MAX, priority);
}
DmxSource source(buffer, *m_wake_up_time, priority);
m_client->DMXRecieved(request->universe(), source);
universe->SourceClientDataChanged(m_client);
}
done->Run();
(void) response;
}
/*
* Handle a streaming DMX update, we don't send responses for this
*/
void OlaServerServiceImpl::StreamDmxData(
RpcController* controller,
const ::ola::proto::DmxData* request,
::ola::proto::STREAMING_NO_RESPONSE* response,
::google::protobuf::Closure* done) {
Universe *universe = m_universe_store->GetUniverse(request->universe());
if (!universe)
return;
if (m_client) {
DmxBuffer buffer;
buffer.Set(request->data());
uint8_t priority = DmxSource::PRIORITY_DEFAULT;
if (request->has_priority()) {
priority = request->priority();
priority = std::max(DmxSource::PRIORITY_MIN, priority);
priority = std::min(DmxSource::PRIORITY_MAX, priority);
}
DmxSource source(buffer, *m_wake_up_time, priority);
m_client->DMXRecieved(request->universe(), source);
universe->SourceClientDataChanged(m_client);
}
(void) controller;
(void) response;
(void) done;
}
/*
* Sets the name of a universe
*/
void OlaServerServiceImpl::SetUniverseName(
RpcController* controller,
const UniverseNameRequest* request,
Ack* response,
google::protobuf::Closure* done) {
Universe *universe = m_universe_store->GetUniverse(request->universe());
if (!universe)
return MissingUniverseError(controller, done);
universe->SetName(request->name());
done->Run();
(void) response;
}
/*
* Set the merge mode for a universe
*/
void OlaServerServiceImpl::SetMergeMode(
RpcController* controller,
const MergeModeRequest* request,
Ack* response,
google::protobuf::Closure* done) {
Universe *universe = m_universe_store->GetUniverse(request->universe());
if (!universe)
return MissingUniverseError(controller, done);
Universe::merge_mode mode = request->merge_mode() == ola::proto::HTP ?
Universe::MERGE_HTP : Universe::MERGE_LTP;
universe->SetMergeMode(mode);
done->Run();
(void) response;
}
/*
* Patch a port to a universe
*/
void OlaServerServiceImpl::PatchPort(
RpcController* controller,
const PatchPortRequest* request,
Ack* response,
google::protobuf::Closure* done) {
AbstractDevice *device =
m_device_manager->GetDevice(request->device_alias());
if (!device)
return MissingDeviceError(controller, done);
bool result;
if (request->is_output()) {
OutputPort *port = device->GetOutputPort(request->port_id());
if (!port)
return MissingPortError(controller, done);
if (request->action() == ola::proto::PATCH)
result = m_port_manager->PatchPort(port, request->universe());
else
result = m_port_manager->UnPatchPort(port);
} else {
InputPort *port = device->GetInputPort(request->port_id());
if (!port)
return MissingPortError(controller, done);
if (request->action() == ola::proto::PATCH)
result = m_port_manager->PatchPort(port, request->universe());
else
result = m_port_manager->UnPatchPort(port);
}
if (!result)
controller->SetFailed("Patch port request failed");
(void) response;
done->Run();
}
/*
* Set the priority of a set of ports
*/
void OlaServerServiceImpl::SetPortPriority(
RpcController* controller,
const ola::proto::PortPriorityRequest* request,
Ack* response,
google::protobuf::Closure* done) {
AbstractDevice *device =
m_device_manager->GetDevice(request->device_alias());
if (!device)
return MissingDeviceError(controller, done);
bool status;
bool inherit_mode = true;
uint8_t value = 0;
if (request->priority_mode() == PRIORITY_MODE_OVERRIDE) {
if (request->has_priority()) {
inherit_mode = false;
value = request->priority();
} else {
OLA_INFO << "In Set Port Priority, override mode was set but the value "
"wasn't specified";
controller->SetFailed(
"Invalid SetPortPriority request, see logs for more info");
done->Run();
return;
}
}
if (request->is_output()) {
OutputPort *port = device->GetOutputPort(request->port_id());
if (!port)
return MissingPortError(controller, done);
if (inherit_mode)
status = m_port_manager->SetPriorityInherit(port);
else
status = m_port_manager->SetPriorityOverride(port, value);
} else {
InputPort *port = device->GetInputPort(request->port_id());
if (!port)
return MissingPortError(controller, done);
if (inherit_mode)
status = m_port_manager->SetPriorityInherit(port);
else
status = m_port_manager->SetPriorityOverride(port, value);
}
if (!status)
controller->SetFailed(
"Invalid SetPortPriority request, see logs for more info");
done->Run();
(void) response;
}
/*
* Returns information on the active universes.
*/
void OlaServerServiceImpl::GetUniverseInfo(
RpcController* controller,
const OptionalUniverseRequest* request,
UniverseInfoReply* response,
google::protobuf::Closure* done) {
UniverseInfo *universe_info;
if (request->has_universe()) {
// return info for a single universe
Universe *universe = m_universe_store->GetUniverse(request->universe());
if (!universe)
return MissingUniverseError(controller, done);
universe_info = response->add_universe();
universe_info->set_universe(universe->UniverseId());
universe_info->set_name(universe->Name());
universe_info->set_merge_mode(universe->MergeMode() == Universe::MERGE_HTP
? ola::proto::HTP: ola::proto::LTP);
universe_info->set_input_port_count(universe->InputPortCount());
universe_info->set_output_port_count(universe->OutputPortCount());
universe_info->set_rdm_devices(universe->UIDCount());
} else {
// return all
vector<Universe*> uni_list;
m_universe_store->GetList(&uni_list);
vector<Universe*>::const_iterator iter;
for (iter = uni_list.begin(); iter != uni_list.end(); ++iter) {
universe_info = response->add_universe();
universe_info->set_universe((*iter)->UniverseId());
universe_info->set_name((*iter)->Name());
universe_info->set_merge_mode((*iter)->MergeMode() == Universe::MERGE_HTP
? ola::proto::HTP: ola::proto::LTP);
universe_info->set_input_port_count((*iter)->InputPortCount());
universe_info->set_output_port_count((*iter)->OutputPortCount());
universe_info->set_rdm_devices((*iter)->UIDCount());
}
}
done->Run();
}
/*
* Return info on available plugins
*/
void OlaServerServiceImpl::GetPlugins(
RpcController* controller,
const PluginListRequest* request,
PluginListReply* response,
google::protobuf::Closure* done) {
vector<AbstractPlugin*> plugin_list;
vector<AbstractPlugin*>::const_iterator iter;
m_plugin_manager->Plugins(&plugin_list);
for (iter = plugin_list.begin(); iter != plugin_list.end(); ++iter)
AddPlugin(*iter, response);
done->Run();
(void) request;
(void) controller;
}
/*
* Return the description for a plugin.
*/
void OlaServerServiceImpl::GetPluginDescription(
RpcController* controller,
const ola::proto::PluginDescriptionRequest* request,
ola::proto::PluginDescriptionReply* response,
google::protobuf::Closure* done) {
AbstractPlugin *plugin =
m_plugin_manager->GetPlugin((ola_plugin_id) request->plugin_id());
if (plugin) {
response->set_name(plugin->Name());
response->set_description(plugin->Description());
} else {
controller->SetFailed("Plugin not loaded");
}
done->Run();
}
/*
* Return information on available devices
*/
void OlaServerServiceImpl::GetDeviceInfo(RpcController* controller,
const DeviceInfoRequest* request,
DeviceInfoReply* response,
google::protobuf::Closure* done) {
vector<device_alias_pair> device_list = m_device_manager->Devices();
vector<device_alias_pair>::const_iterator iter;
for (iter = device_list.begin(); iter != device_list.end(); ++iter) {
if (request->has_plugin_id()) {
if (iter->device->Owner()->Id() == request->plugin_id() ||
request->plugin_id() == ola::OLA_PLUGIN_ALL)
AddDevice(iter->device, iter->alias, response);
} else {
AddDevice(iter->device, iter->alias, response);
}
}
(void) controller;
done->Run();
}
/*
* Handle a GetCandidatePorts request
*/
void OlaServerServiceImpl::GetCandidatePorts(
RpcController* controller,
const ola::proto::OptionalUniverseRequest* request,
ola::proto::DeviceInfoReply* response,
google::protobuf::Closure* done) {
vector<device_alias_pair> device_list = m_device_manager->Devices();
vector<device_alias_pair>::const_iterator iter;
Universe *universe = NULL;
if (request->has_universe()) {
universe = m_universe_store->GetUniverse(request->universe());
if (!universe)
return MissingUniverseError(controller, done);
}
vector<InputPort*> input_ports;
vector<OutputPort*> output_ports;
vector<InputPort*>::const_iterator input_iter;
vector<OutputPort*>::const_iterator output_iter;
for (iter = device_list.begin(); iter != device_list.end(); ++iter) {
AbstractDevice *device = iter->device;
input_ports.clear();
output_ports.clear();
device->InputPorts(&input_ports);
device->OutputPorts(&output_ports);
bool seen_input_port = false;
bool seen_output_port = false;
unsigned int unpatched_input_ports = 0;
unsigned int unpatched_output_ports = 0;
if (universe) {
for (input_iter = input_ports.begin(); input_iter != input_ports.end();
input_iter++) {
if ((*input_iter)->GetUniverse() == universe)
seen_input_port = true;
else if (!(*input_iter)->GetUniverse())
unpatched_input_ports++;
}
for (output_iter = output_ports.begin();
output_iter != output_ports.end(); output_iter++) {
if ((*output_iter)->GetUniverse() == universe)
seen_output_port = true;
else if (!(*output_iter)->GetUniverse())
unpatched_output_ports++;
}
} else {
unpatched_input_ports = input_ports.size();
unpatched_output_ports = output_ports.size();
}
bool can_bind_more_input_ports = (
(!seen_output_port || device->AllowLooping()) &&
(!seen_input_port || device->AllowMultiPortPatching()));
bool can_bind_more_output_ports = (
(!seen_input_port || device->AllowLooping()) &&
(!seen_output_port || device->AllowMultiPortPatching()));
if ((unpatched_input_ports == 0 || !can_bind_more_input_ports) &&
(unpatched_output_ports == 0 || !can_bind_more_output_ports))
continue;
// go ahead and create the device at this point
DeviceInfo *device_info = response->add_device();
device_info->set_device_alias(iter->alias);
device_info->set_device_name(device->Name());
device_info->set_device_id(device->UniqueId());
if (device->Owner())
device_info->set_plugin_id(device->Owner()->Id());
for (input_iter = input_ports.begin(); input_iter != input_ports.end();
++input_iter) {
if ((*input_iter)->GetUniverse())
continue;
if (!can_bind_more_input_ports)
break;
PortInfo *port_info = device_info->add_input_port();
PopulatePort(**input_iter, port_info);
if (!device->AllowMultiPortPatching())
break;
}
for (output_iter = output_ports.begin(); output_iter != output_ports.end();
++output_iter) {
if ((*output_iter)->GetUniverse())
continue;
if (!can_bind_more_output_ports)
break;
PortInfo *port_info = device_info->add_output_port();
PopulatePort(**output_iter, port_info);
if (!device->AllowMultiPortPatching())
break;
}
}
done->Run();
}
/*
* Handle a ConfigureDevice request
*/
void OlaServerServiceImpl::ConfigureDevice(RpcController* controller,
const DeviceConfigRequest* request,
DeviceConfigReply* response,
google::protobuf::Closure* done) {
AbstractDevice *device =
m_device_manager->GetDevice(request->device_alias());
if (!device)
return MissingDeviceError(controller, done);
device->Configure(controller,
request->data(),
response->mutable_data(), done);
}
/*
* Fetch the UID list for a universe
*/
void OlaServerServiceImpl::GetUIDs(RpcController* controller,
const ola::proto::UniverseRequest* request,
ola::proto::UIDListReply* response,
google::protobuf::Closure* done) {
Universe *universe = m_universe_store->GetUniverse(request->universe());
if (!universe)
return MissingUniverseError(controller, done);
response->set_universe(request->universe());
UIDSet uid_set;
universe->GetUIDs(&uid_set);
UIDSet::Iterator iter = uid_set.Begin();
for (; iter != uid_set.End(); ++iter) {
ola::proto::UID *uid = response->add_uid();
uid->set_esta_id(iter->ManufacturerId());
uid->set_device_id(iter->DeviceId());
}
done->Run();
}
/*
* Force RDM discovery for a universe
*/
void OlaServerServiceImpl::ForceDiscovery(
RpcController* controller,
const ola::proto::UniverseRequest* request,
ola::proto::Ack* response,
google::protobuf::Closure* done) {
Universe *universe = m_universe_store->GetUniverse(request->universe());
if (!universe)
return MissingUniverseError(controller, done);
universe->RunRDMDiscovery();
done->Run();
(void) response;
}
/*
* Handle an RDM Command
*/
void OlaServerServiceImpl::RDMCommand(
RpcController* controller,
const ::ola::proto::RDMRequest* request,
ola::proto::RDMResponse* response,
google::protobuf::Closure* done) {
Universe *universe = m_universe_store->GetUniverse(request->universe());
if (!universe)
return MissingUniverseError(controller, done);
UID destination(request->uid().esta_id(),
request->uid().device_id());
SingleUseCallback1<void, const rdm_response_data&> *callback =
NewSingleCallback(
this,
&OlaServerServiceImpl::HandleRDMResponse,
controller,
response,
done);
bool r = m_rdm_controller->SendRDMRequest(
universe,
destination,
request->sub_device(),
request->param_id(),
request->data(),
request->is_set(),
callback,
m_uid);
if (!r) {
controller->SetFailed("Failed to send RDM command");
delete callback;
done->Run();
}
}
/*
* Set this client's source UID
*/
void OlaServerServiceImpl::SetSourceUID(
RpcController* controller,
const ::ola::proto::UID* request,
ola::proto::Ack* response,
google::protobuf::Closure* done) {
UID source_uid(request->esta_id(), request->device_id());
if (!m_uid)
m_uid = new UID(source_uid);
else
*m_uid = source_uid;
done->Run();
(void) controller;
(void) response;
}
/*
* Handle an RDM Response, this includes broadcast messages, messages that
* timed out and normal response messages.
*/
void OlaServerServiceImpl::HandleRDMResponse(
RpcController* controller,
ola::proto::RDMResponse* response,
google::protobuf::Closure* done,
const rdm_response_data &status) {
// check for time out errors
if (status.status == RDM_RESPONSE_TIMED_OUT) {
controller->SetFailed("RDM command timed out");
done->Run();
return;
}
if (status.status == RDM_RESPONSE_OK) {
if (status.response) {
response->set_response_code(status.response->ResponseType());
response->set_message_count(status.response->MessageCount());
response->set_was_broadcast(false);
if (status.response->ParamData() && status.response->ParamDataSize()) {
const string data(
reinterpret_cast<const char*>(status.response->ParamData()),
status.response->ParamDataSize());
response->set_data(data);
} else {
response->set_data("");
}
} else {
OLA_WARN << "RDM state was ok but response was NULL";
controller->SetFailed("Missing Response");
}
} else if (status.status == RDM_RESPONSE_BROADCAST) {
response->set_was_broadcast(true);
// fill these in with dummy values
response->set_response_code(0);
response->set_message_count(0);
response->set_data("");
} else {
OLA_WARN << "unknown response status " << status.status;
controller->SetFailed("Unknown status, see the logs");
}
done->Run();
}
// Private methods
//-----------------------------------------------------------------------------
void OlaServerServiceImpl::MissingUniverseError(
RpcController* controller,
google::protobuf::Closure* done) {
controller->SetFailed("Universe doesn't exist");
done->Run();
}
void OlaServerServiceImpl::MissingDeviceError(
RpcController* controller,
google::protobuf::Closure* done) {
controller->SetFailed("Device doesn't exist");
done->Run();
}
void OlaServerServiceImpl::MissingPluginError(
RpcController* controller,
google::protobuf::Closure* done) {
controller->SetFailed("Plugin doesn't exist");
done->Run();
}
void OlaServerServiceImpl::MissingPortError(RpcController* controller,
google::protobuf::Closure* done) {
controller->SetFailed("Port doesn't exist");
done->Run();
}
/*
* Add this device to the DeviceInfo response
*/
void OlaServerServiceImpl::AddPlugin(AbstractPlugin *plugin,
PluginListReply* response) const {
PluginInfo *plugin_info = response->add_plugin();
plugin_info->set_plugin_id(plugin->Id());
plugin_info->set_name(plugin->Name());
}
/*
* Add this device to the DeviceInfo response
*/
void OlaServerServiceImpl::AddDevice(AbstractDevice *device,
unsigned int alias,
DeviceInfoReply* response) const {
DeviceInfo *device_info = response->add_device();
device_info->set_device_alias(alias);
device_info->set_device_name(device->Name());
device_info->set_device_id(device->UniqueId());
if (device->Owner())
device_info->set_plugin_id(device->Owner()->Id());
vector<InputPort*> input_ports;
device->InputPorts(&input_ports);
vector<InputPort*>::const_iterator input_iter;
for (input_iter = input_ports.begin(); input_iter != input_ports.end();
++input_iter) {
PortInfo *port_info = device_info->add_input_port();
PopulatePort(**input_iter, port_info);
}
vector<OutputPort*> output_ports;
device->OutputPorts(&output_ports);
vector<OutputPort*>::const_iterator output_iter;
for (output_iter = output_ports.begin(); output_iter != output_ports.end();
++output_iter) {
PortInfo *port_info = device_info->add_output_port();
PopulatePort(**output_iter, port_info);
}
}
template <class PortClass>
void OlaServerServiceImpl::PopulatePort(const PortClass &port,
PortInfo *port_info) const {
port_info->set_port_id(port.PortId());
port_info->set_priority_capability(port.PriorityCapability());
port_info->set_description(port.Description());
if (port.GetUniverse()) {
port_info->set_active(true);
port_info->set_universe(port.GetUniverse()->UniverseId());
} else {
port_info->set_active(false);
}
if (port.PriorityCapability() != CAPABILITY_NONE)
port_info->set_priority(port.GetPriority());
if (port.PriorityCapability() == CAPABILITY_FULL)
port_info->set_priority_mode(port.GetPriorityMode());
}
// OlaServerServiceImplFactory
// ----------------------------------------------------------------------------
OlaServerServiceImpl *OlaServerServiceImplFactory::New(
UniverseStore *universe_store,
DeviceManager *device_manager,
PluginManager *plugin_manager,
Client *client,
ExportMap *export_map,
PortManager *port_manager,
InternalRDMController *rdm_controller,
const TimeStamp *wake_up_time) {
return new OlaServerServiceImpl(universe_store,
device_manager,
plugin_manager,
client,
export_map,
port_manager,
rdm_controller,
wake_up_time);
};
} // ola