841 lines
24 KiB
C++
841 lines
24 KiB
C++
/*
|
||
device.cpp
|
||
|
||
Copyright (c) 2021 Kevin Matz (kevin.matz@gmail.com)
|
||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
of this software and associated documentation files (the "Software"), to deal
|
||
in the Software without restriction, including without limitation the rights
|
||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
copies of the Software, and to permit persons to whom the Software is
|
||
furnished to do so, subject to the following conditions:
|
||
|
||
The above copyright notice and this permission notice shall be included in all
|
||
copies or substantial portions of the Software.
|
||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
SOFTWARE.
|
||
*/
|
||
|
||
#include "config.h"
|
||
#include "device.h"
|
||
|
||
namespace RDM {
|
||
|
||
/**
|
||
* @brief Device::Device
|
||
* @param id
|
||
* @param parent
|
||
*/
|
||
Device::Device(UID id, Device* parent)
|
||
: DMX::Device()
|
||
, deviceManufacturerLabel(MY_ESTA_MANUFACTURER_LABEL)
|
||
, deviceModelDescription("Basic RDM Device")
|
||
, deviceModelID(0)
|
||
, deviceProductCategory(PRODUCT_CATEGORY_NOT_DECLARED)
|
||
, id_(id)
|
||
, parent_(parent)
|
||
, status_reporting_threshold_(STATUS_ADVISORY)
|
||
{
|
||
if (!id_.manufacturer)
|
||
id_.manufacturer = MY_ESTA_MANUFACTURER_ID;
|
||
|
||
queued_statuses_.emplace(STATUS_ADVISORY, std::queue<StatusPtr>());
|
||
queued_statuses_.emplace(STATUS_WARNING, std::queue<StatusPtr>());
|
||
queued_statuses_.emplace(STATUS_ERROR, std::queue<StatusPtr>());
|
||
|
||
Parameter *pid;
|
||
|
||
/// \cite RDM 9.2.3 Required Sub-Device Messages
|
||
/// Devices supporting the use of sub-devices shall support the
|
||
/// SUPPORTED_PARAMETERS message in order for the controller to determine
|
||
/// which additional messages are supported by the sub-devices.
|
||
pid = addParameter(SUPPORTED_PARAMETERS);
|
||
pid->getAction(std::bind(&Device::actionGetSupportedParameters, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
/// \cite RDM 10.3.4 Clear Status ID (CLEAR_STATUS_ID)
|
||
/// This parameter is used to clear the status message queue.
|
||
pid = addParameter(CLEAR_STATUS_ID);
|
||
pid->getAction(std::bind(&Device::actionSetClearStatusId, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
/// \cite RDM 10.3.5 Get/Set Sub-Device Status Reporting Threshold
|
||
/// (SUB_DEVICE_STATUS_REPORT_THRESHOLD)
|
||
/// This parameter is used to set the verbosity of Sub-Device reporting using
|
||
/// the Status Type codes as enumerated in Table A-4 .
|
||
pid = addParameter(SUB_DEVICE_STATUS_REPORT_THRESHOLD);
|
||
pid->getAction(std::bind(&Device::actionGetSubdeviceThreshold, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
pid->setAction(std::bind(&Device::actionSetSubdeviceThreshold, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
/// \cite RDM 10.5.1 Get Device Info (DEVICE_INFO)
|
||
/// This parameter is used to retrieve a variety of information about the
|
||
/// device that is normally required by a controller.
|
||
pid = addParameter(DEVICE_INFO);
|
||
pid->getAction(std::bind(&Device::actionGetDeviceInfo, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
/// \cite RDM 10.5.2 Get Product Detail ID List (PRODUCT_DETAIL_ID_LIST)
|
||
/// This parameter shall be used for requesting technology details for a
|
||
/// device.
|
||
pid = addParameter(PRODUCT_DETAIL_ID_LIST);
|
||
pid->getAction(std::bind(&Device::actionGetProductDetailIdList, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
/// \cite RDM 10.5.3 Get Device Model Description (DEVICE_MODEL_DESCRIPTION)
|
||
/// This parameter provides a text description of up to 32 characters for the
|
||
/// device model type.
|
||
pid = addParameter(DEVICE_MODEL_DESCRIPTION);
|
||
pid->getAction(std::bind(&Device::actionGetDevModelDescription, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
/// \cite RDM 10.5.4 Get Manufacturer Label (MANUFACTURER_LABEL)
|
||
/// This parameter provides an ASCII text response with the Manufacturer name
|
||
/// for the device of up to 32 characters.
|
||
pid = addParameter(MANUFACTURER_LABEL);
|
||
pid->getAction(std::bind(&Device::actionGetManufacturerLabel, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
/// \cite RDM 10.5.7 Get Language Capabilities (LANGUAGE_CAPABILITIES)
|
||
/// This parameter is used to identify languages that the device supports for
|
||
/// using the LANGUAGE parameter.
|
||
pid = addParameter(LANGUAGE_CAPABILITIES);
|
||
pid->getAction(std::bind(&Device::actionGetLanguage, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
/// \cite RDM 10.5.8 Get/Set Language (LANGUAGE)
|
||
/// This parameter is used to change the language of the messages from
|
||
/// the device.
|
||
pid = addParameter(LANGUAGE);
|
||
pid->getAction(std::bind(&Device::actionGetLanguage, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
pid->setAction(std::bind(&Device::actionSetLanguage, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
/// \cite RDM 10.5.9 Get Software Version Label (SOFTWARE_VERSION_LABEL)
|
||
/// This parameter is used to get a descriptive ASCII text label for the
|
||
/// device’s operating software version.
|
||
pid = addParameter(SOFTWARE_VERSION_LABEL);
|
||
pid->getAction(std::bind(&Device::actionGetSoftwareVersionLabel, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
/// \cite RDM 10.6.1 Get/Set DMX512 Personality (DMX_PERSONALITY)
|
||
/// This parameter is used to set the responder’s DMX512 Personality.
|
||
pid = addParameter(DMX_PERSONALITY);
|
||
pid->getAction(std::bind(&Device::actionGetDmxPersonality, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
pid->setAction(std::bind(&Device::actionSetDmxPersonality, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
/// \cite RDM 10.6.2 Get DMX512 Personality Description (DMX_PERSONALITY_DESCRIPTION)
|
||
/// This parameter is used to get a descriptive ASCII text label for a given
|
||
/// DMX512 Personality.
|
||
pid = addParameter(DMX_PERSONALITY_DESCRIPTION);
|
||
pid->getAction(std::bind(&Device::actionGetDmxPersonalityDesc, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
/// \cite RDM 10.6.3 Get/Set DMX512 Starting Address (DMX_START_ADDRESS)
|
||
/// This parameter is used to set or get the DMX512 start address.
|
||
pid = addParameter(DMX_START_ADDRESS);
|
||
pid->getAction(std::bind(&Device::actionGetDmxStartAddress, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
pid->setAction(std::bind(&Device::actionSetDmxStartAddress, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
/// \cite RDM 10.7.1 Get Sensor Definition (SENSOR_DEFINITION)
|
||
/// This parameter is used to retrieve the definition of a specific sensor.
|
||
pid = addParameter(SENSOR_DEFINITION);
|
||
pid->getAction(std::bind(&Device::actionSensorDispatch, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
/// \cite RDM 10.7.2 Get/Set Sensor (SENSOR_VALUE)
|
||
/// This parameter shall be used to retrieve or reset sensor data.
|
||
pid = addParameter(SENSOR_VALUE);
|
||
pid->getAction(std::bind(&Device::actionSensorDispatch, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
pid->setAction(std::bind(&Device::actionSensorDispatch, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
/// \cite RDM 10.7.3 Record Sensors (RECORD_SENSORS)
|
||
/// This parameter instructs devices such as dimming racks that monitor load
|
||
/// changes to store the current value for monitoring sensor changes.
|
||
pid = addParameter(RECORD_SENSORS);
|
||
pid->setAction(std::bind(&Device::actionSensorDispatch, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
/// \cite RDM 10.11.1 Get/Set Identify Device (IDENTIFY_DEVICE)
|
||
/// This parameter is used for the user to physically identify the device
|
||
/// represented by the UID.
|
||
pid = addParameter(IDENTIFY_DEVICE);
|
||
pid->getAction(std::bind(&Device::actionGetIdentifyDevice, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
pid->setAction(std::bind(&Device::actionSetIdentifyDevice, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
/// \cite RDM 10.11.2 Reset Device (RESET_DEVICE)
|
||
/// This parameter is used to instruct the responder to reset itself.
|
||
pid = addParameter(RESET_DEVICE);
|
||
pid->setAction(std::bind(&Device::actionSetResetDevice, this,
|
||
std::placeholders::_1, std::placeholders::_2));
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::~Device
|
||
*/
|
||
Device::~Device()
|
||
{
|
||
for( auto& [_, device] : sub_devices_)
|
||
delete device;
|
||
for (auto& [_, parameter] : parameters_)
|
||
delete parameter;
|
||
for (auto sensor : sensors_)
|
||
delete sensor;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::addSubDevice
|
||
* @param number
|
||
* @param dev
|
||
*
|
||
* Root devices (ie. Respoders) shall override with a meaningful implimentation
|
||
*/
|
||
void Device::addSubDevice(uint16_t number, Device *dev)
|
||
{
|
||
if (parent_)
|
||
return;
|
||
|
||
if (sub_devices_.count(number))
|
||
delete sub_devices_.at(number);
|
||
sub_devices_[number] = dev;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::subDevice
|
||
* @param number
|
||
* @return
|
||
*/
|
||
Device* Device::subDevice(uint16_t number)
|
||
{
|
||
if (parent_)
|
||
return nullptr;
|
||
|
||
if (sub_devices_.count(number))
|
||
return sub_devices_.at(number);
|
||
return nullptr;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::subDeviceCount
|
||
* @return
|
||
*/
|
||
uint16_t Device::subDeviceCount() const
|
||
{
|
||
if (parent_)
|
||
return parent_->subDeviceCount();
|
||
return sub_devices_.size();
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::addProductDetailId
|
||
* @param id
|
||
*/
|
||
void Device::addProductDetailId(uint16_t id)
|
||
{
|
||
product_detail_list_.push_back(id);
|
||
while (product_detail_list_.size() > 6)
|
||
product_detail_list_.pop_front();
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::addParameter
|
||
* @param id
|
||
* @return
|
||
*/
|
||
Parameter *Device::addParameter(const PID id)
|
||
{
|
||
if (parameters_.count(id))
|
||
return parameters_.at(id);
|
||
|
||
auto [it, _] = parameters_.emplace(id, new Parameter(id));
|
||
return it->second;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::get
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::get(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (!actionPrep_(message, response))
|
||
return;
|
||
parameters_.at(message->mdb.pid)->get(message, response);
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::set
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::set(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (!actionPrep_(message, response))
|
||
return;
|
||
parameters_.at(message->mdb.pid)->set(message, response);
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::identify
|
||
* @param state
|
||
*/
|
||
void Device::identify(bool state)
|
||
{
|
||
identifying_ = state;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::reset
|
||
* @param hard
|
||
*/
|
||
void Device::reset(bool hard)
|
||
{
|
||
(void)hard;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::enqueueMessage
|
||
* @param message
|
||
* @param urgent
|
||
*/
|
||
void Device::enqueueMessage(MsgPtr message, bool urgent)
|
||
{
|
||
// only root devices have a message queue
|
||
if (parent_)
|
||
return;
|
||
|
||
message->destination = controller_uid_;
|
||
|
||
if (urgent)
|
||
queued_messages_.push_front(message);
|
||
else
|
||
queued_messages_.push_back(message);
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::enqueueStatus
|
||
* @param status
|
||
*/
|
||
void Device::enqueueStatus(StatusPtr status)
|
||
{
|
||
switch (status->status_type) {
|
||
case STATUS_ERROR:
|
||
if (status_reporting_threshold_ != STATUS_ADVISORY &&
|
||
status_reporting_threshold_ != STATUS_WARNING)
|
||
queued_statuses_.at(STATUS_ERROR).push(status);
|
||
break;
|
||
case STATUS_ERROR_CLEARED:
|
||
if (status_reporting_threshold_ != STATUS_ADVISORY &&
|
||
status_reporting_threshold_ != STATUS_WARNING)
|
||
queued_statuses_.at(STATUS_ERROR).push(status);
|
||
break;
|
||
case STATUS_WARNING:
|
||
if (status_reporting_threshold_ != STATUS_ADVISORY)
|
||
queued_statuses_.at(STATUS_WARNING).push(status);
|
||
break;
|
||
case STATUS_WARNING_CLEARED:
|
||
if (status_reporting_threshold_ != STATUS_ADVISORY)
|
||
queued_statuses_.at(STATUS_WARNING).push(status);
|
||
break;
|
||
case STATUS_ADVISORY:
|
||
queued_statuses_.at(STATUS_ADVISORY).push(status);
|
||
break;
|
||
case STATUS_ADVISORY_CLEARED:
|
||
queued_statuses_.at(STATUS_ADVISORY).push(status);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionPrep_
|
||
* @param message
|
||
* @param response
|
||
* @return
|
||
*/
|
||
bool Device::actionPrep_(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (!parameters_.count(message->mdb.pid))
|
||
{
|
||
response->nak(NR_UNKNOWN_PID);
|
||
return false;
|
||
}
|
||
|
||
if (message->mdb.pid == last_rx_pid_)
|
||
++ack_overflow_page;
|
||
else {
|
||
ack_overflow_page = 0;
|
||
last_rx_pid_ = message->mdb.pid;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionGetSupportedParameters
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionGetSupportedParameters(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 0)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
unsigned int count = parameters_.size();
|
||
unsigned int length = count * sizeof(PID);
|
||
unsigned int lastPage = length / 0xfe;
|
||
unsigned int first = ack_overflow_page * ( 0xfe / sizeof(PID) );
|
||
if (first >= count) {
|
||
ack_overflow_page = 0;
|
||
first = 0;
|
||
}
|
||
|
||
auto pid = parameters_.begin();
|
||
if (first != 0)
|
||
std::advance(pid, first);
|
||
while (pid != parameters_.end() && response->mdb.pdl() < 0xfe)
|
||
{
|
||
response->appendParameterData(pid->first);
|
||
pid++;
|
||
}
|
||
|
||
if (length > 0xfe && ack_overflow_page != lastPage)
|
||
response->responseType = RESPONSE_TYPE_ACK_OVERFLOW;
|
||
else
|
||
response->responseType = RESPONSE_TYPE_ACK;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionGetDeviceInfo
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionGetDeviceInfo(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 0)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
response->appendParameterData(RDM_PROTOCOL_VERSION);
|
||
response->appendParameterData(deviceModelID);
|
||
response->appendParameterData(deviceProductCategory);
|
||
response->appendParameterData(LIB_VERSION);
|
||
response->appendParameterData(DMX::Device::footprint());
|
||
response->appendParameterData(DMX::Device::personality());
|
||
response->appendParameterData(DMX::Device::personalityCount());
|
||
response->appendParameterData(DMX::Device::address());
|
||
response->appendParameterData(subDeviceCount());
|
||
response->appendParameterData<uint8_t>(sensors_.size());
|
||
|
||
response->responseType = RESPONSE_TYPE_ACK;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionSetClearStatusId
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionSetClearStatusId(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 0)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
for (auto& [_, queue] : queued_statuses_)
|
||
while (!queue.empty())
|
||
queue.pop();
|
||
|
||
response->responseType = RESPONSE_TYPE_ACK;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionGetSubdeviceThreshold
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionGetSubdeviceThreshold(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 0)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
response->appendParameterData(status_reporting_threshold_);
|
||
|
||
response->responseType = RESPONSE_TYPE_ACK;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionSetSubdeviceThreshold
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionSetSubdeviceThreshold(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 1)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
uint8_t threshold = message->mdb.pd.front();
|
||
|
||
switch (threshold) {
|
||
case STATUS_ERROR:
|
||
status_reporting_threshold_ = threshold;
|
||
while (!queued_statuses_.at(STATUS_WARNING).empty())
|
||
queued_statuses_.at(STATUS_WARNING).pop();
|
||
while (!queued_statuses_.at(STATUS_ADVISORY).empty())
|
||
queued_statuses_.at(STATUS_ADVISORY).pop();
|
||
break;
|
||
case STATUS_WARNING:
|
||
status_reporting_threshold_ = threshold;
|
||
while (!queued_statuses_.at(STATUS_ADVISORY).empty())
|
||
queued_statuses_.at(STATUS_ADVISORY).pop();
|
||
break;
|
||
case STATUS_ADVISORY:
|
||
status_reporting_threshold_ = threshold;
|
||
break;
|
||
default:
|
||
response->nak(NR_DATA_OUT_OF_RANGE);
|
||
return;
|
||
}
|
||
|
||
response->responseType = RESPONSE_TYPE_ACK;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionGetProductDetailIdList
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionGetProductDetailIdList(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 0)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
if (product_detail_list_.empty())
|
||
response->appendParameterData(PRODUCT_DETAIL_NOT_DECLARED);
|
||
else
|
||
for (const auto detail : product_detail_list_)
|
||
response->appendParameterData(detail);
|
||
|
||
response->responseType = RESPONSE_TYPE_ACK;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionGetDevModelDescription
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionGetDevModelDescription(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 0)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
for (size_t i = 0; i < deviceModelDescription.size() && i <= 32; i++)
|
||
response->appendParameterData(deviceModelDescription.at(i));
|
||
|
||
response->responseType = RESPONSE_TYPE_ACK;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionGetManufacturerLabel
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionGetManufacturerLabel(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 0)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
for (size_t i = 0; i < deviceManufacturerLabel.size() && i <= 32; i++)
|
||
response->appendParameterData(deviceManufacturerLabel.at(i));
|
||
|
||
response->responseType = RESPONSE_TYPE_ACK;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionGetLanguage
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionGetLanguage(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 0)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
response->appendParameterData('e');
|
||
response->appendParameterData('n');
|
||
|
||
response->responseType = RESPONSE_TYPE_ACK;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionSetLanguage
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionSetLanguage(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 2)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
if (message->mdb.pd[0] != 'e' ||
|
||
message->mdb.pd[1] != 'n')
|
||
return response->nak(NR_DATA_OUT_OF_RANGE);
|
||
|
||
response->responseType = RESPONSE_TYPE_ACK;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionGetSoftwareVersionLabel
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionGetSoftwareVersionLabel(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 0)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
response->responseType = RESPONSE_TYPE_ACK;
|
||
std::string label = std::string(LIB_VERSION_LABEL);
|
||
for (size_t i = 0; i < label.size() && i <= 32; i++)
|
||
response->appendParameterData(label.at(i));
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionGetDmxPersonality
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionGetDmxPersonality(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 0)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
response->responseType = RESPONSE_TYPE_ACK;
|
||
response->appendParameterData(DMX::Device::personality());
|
||
response->appendParameterData(DMX::Device::personalityCount());
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionSetDmxPersonality
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionSetDmxPersonality(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 1)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
uint8_t mode = message->mdb.pd.front();
|
||
|
||
if ( mode == 0 || mode > DMX::Device::personalityCount())
|
||
return response->nak(NR_DATA_OUT_OF_RANGE);
|
||
|
||
setPersonality(mode);
|
||
|
||
response->responseType = RESPONSE_TYPE_ACK;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionGetDmxPersonalityDesc
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionGetDmxPersonalityDesc(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 1)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
uint8_t mode = message->mdb.pd.front();
|
||
|
||
if ( mode == 0 || mode > DMX::Device::personalityCount())
|
||
return response->nak(NR_DATA_OUT_OF_RANGE);
|
||
|
||
response->responseType = RESPONSE_TYPE_ACK;
|
||
response->appendParameterData(mode);
|
||
response->appendParameterData(personalities_.at(mode)->footprint());
|
||
for (size_t i = 0; i < personalities_.at(mode)->description().size(); i++)
|
||
{
|
||
if (i > 32)
|
||
break;
|
||
response->appendParameterData(personalities_.at(mode)->description().at(i));
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionGetDmxStartAddress
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionGetDmxStartAddress(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 0)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
response->responseType = RESPONSE_TYPE_ACK;
|
||
if (footprint() == 0)
|
||
response->appendParameterData<uint16_t>(0xFFFF);
|
||
else
|
||
response->appendParameterData(address());
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionSetDmxStartAddress
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionSetDmxStartAddress(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 2)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
uint16_t addr = Message::readType<uint16_t>(message->mdb.pd, 0);
|
||
|
||
if (!setAddress(addr))
|
||
return response->nak(NR_DATA_OUT_OF_RANGE);
|
||
|
||
response->responseType = RESPONSE_TYPE_ACK;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionSensorDispatch
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionSensorDispatch(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 1)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
uint8_t index = message->mdb.pd.front();
|
||
|
||
switch (message->mdb.cc) {
|
||
case GET_COMMAND:
|
||
{
|
||
if (index == 0xFF || index >= sensors_.size())
|
||
{
|
||
response->nak(NR_DATA_OUT_OF_RANGE);
|
||
return;
|
||
}
|
||
auto sensor = sensors_.at(index);
|
||
switch (message->mdb.pid) {
|
||
case SENSOR_DEFINITION:
|
||
sensor->actionGetSensorDefinition(index, response);
|
||
break;
|
||
case SENSOR_VALUE:
|
||
sensor->actionGetSensorValue(index, response);
|
||
break;
|
||
}
|
||
}
|
||
break;
|
||
case SET_COMMAND:
|
||
{
|
||
if (index >= sensors_.size() && index != 0xFF)
|
||
{
|
||
response->nak(NR_DATA_OUT_OF_RANGE);
|
||
return;
|
||
}
|
||
auto setSensor = [index, message, response](Sensor * sensor)
|
||
{
|
||
switch (message->mdb.pid) {
|
||
case SENSOR_VALUE:
|
||
sensor->actionSetSensorValue(index, response);
|
||
break;
|
||
case RECORD_SENSORS:
|
||
sensor->actionSetRecordSensors(response);
|
||
break;
|
||
}
|
||
};
|
||
if (index == 0xff)
|
||
for (Sensor* s : sensors_)
|
||
setSensor(s);
|
||
else
|
||
setSensor(sensors_.at(index));
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionGetIdentifyDevice
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionGetIdentifyDevice(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 0)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
response->responseType = RESPONSE_TYPE_ACK;
|
||
response->appendParameterData<uint8_t>(identifying_);
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionSetIdentifyDevice
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionSetIdentifyDevice(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 1)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
identify(message->mdb.pd.front());
|
||
|
||
response->responseType = RESPONSE_TYPE_ACK;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Device::actionSetResetDevice
|
||
* @param message
|
||
* @param response
|
||
*/
|
||
void Device::actionSetResetDevice(const MsgPtr message, MsgPtr response)
|
||
{
|
||
if (message->mdb.pdl() != 1)
|
||
return response->nak(NR_FORMAT_ERROR);
|
||
|
||
switch (message->mdb.pd.front()) {
|
||
case 0x01:
|
||
reset(false);
|
||
break;
|
||
case 0xff:
|
||
reset(true);
|
||
break;
|
||
default:
|
||
return response->nak(NR_DATA_OUT_OF_RANGE);
|
||
}
|
||
|
||
response->responseType = RESPONSE_TYPE_ACK;
|
||
}
|
||
|
||
} // namespace RDM
|