split device types
This commit is contained in:
parent
887ce0f5a4
commit
9985f21cb2
|
@ -7,6 +7,7 @@ configure_file(../../config.h.in config.h)
|
|||
|
||||
target_sources(${PROJECT_NAME}
|
||||
PUBLIC
|
||||
basicdevice.h
|
||||
rdm_controller.h
|
||||
device.h
|
||||
message.h
|
||||
|
@ -14,8 +15,10 @@ target_sources(${PROJECT_NAME}
|
|||
parameterdescription.h
|
||||
responder.h
|
||||
sensor.h
|
||||
subdevice.h
|
||||
status.h
|
||||
PRIVATE
|
||||
basicdevice.cpp
|
||||
rdm_controller.cpp
|
||||
device.cpp
|
||||
E1.37-1.h
|
||||
|
@ -27,6 +30,7 @@ target_sources(${PROJECT_NAME}
|
|||
rdm.h
|
||||
responder.cpp
|
||||
sensor.cpp
|
||||
subdevice.cpp
|
||||
uid.h
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
rdm/basicdevice.cpp
|
||||
|
||||
Copyright (c) 2023 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 "E1.37-1.h"
|
||||
#include "config.h"
|
||||
#include "basicdevice.h"
|
||||
|
||||
namespace RDM {
|
||||
|
||||
BasicDevice::BasicDevice()
|
||||
: deviceManufacturerLabel(MY_ESTA_MANUFACTURER_LABEL)
|
||||
, deviceModelDescription("Basic RDM Sub-Device")
|
||||
, deviceModelID(0)
|
||||
, deviceProductCategory(PRODUCT_CATEGORY_NOT_DECLARED)
|
||||
, status_reporting_threshold_(STATUS_ADVISORY)
|
||||
{
|
||||
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 *parameter;
|
||||
/// \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.
|
||||
parameter = addParameter(SUPPORTED_PARAMETERS);
|
||||
parameter->onGet([this](MsgPair msg){actionGetSupportedParameters(msg);});
|
||||
parameter = addParameter(PARAMETER_DESCRIPTION);
|
||||
parameter->onGet([this](MsgPair msg){actionGetParameterDescription(msg);});
|
||||
/// \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.
|
||||
parameter = addParameter(DEVICE_INFO);
|
||||
parameter->onGet([this](MsgPair msg){actionGetDeviceInfo(msg);});
|
||||
/// \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.
|
||||
parameter = addParameter(SOFTWARE_VERSION_LABEL);
|
||||
parameter->onGet([this](MsgPair msg){actionGetSoftwareVersionLabel(msg);});
|
||||
/// \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.
|
||||
parameter = addParameter(IDENTIFY_DEVICE);
|
||||
parameter->onGet([this](MsgPair msg){actionGetIdentifyDevice(msg);});
|
||||
parameter->onSet([this](MsgPair msg){actionSetIdentifyDevice(msg);});
|
||||
/// \cite RDM 10.11.2 Reset Device (RESET_DEVICE)
|
||||
/// This parameter is used to instruct the responder to reset itself.
|
||||
parameter = addParameter(RESET_DEVICE);
|
||||
parameter->onSet([this](MsgPair msg){actionSetResetDevice(msg);});
|
||||
}
|
||||
|
||||
|
||||
BasicDevice::~BasicDevice()
|
||||
{
|
||||
for (auto& [_, parameter] : parameters_)
|
||||
delete parameter;
|
||||
for (auto sensor : sensors_)
|
||||
delete sensor;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief BasicDevice::identify
|
||||
* @param state
|
||||
*/
|
||||
void BasicDevice::identify(bool state)
|
||||
{
|
||||
identifying_ = state;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief BasicDevice::reset
|
||||
* @param hard
|
||||
*/
|
||||
void BasicDevice::reset(bool hard)
|
||||
{
|
||||
(void)hard;
|
||||
}
|
||||
|
||||
|
||||
bool BasicDevice::hasManufacturerPIDs() const
|
||||
{
|
||||
auto [_, parameter] = *parameters_.cend()--;
|
||||
return parameter->isManufacturer();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief BasicDevice::dispatch
|
||||
* @param msg
|
||||
*/
|
||||
void BasicDevice::dispatch(MsgPair msg)
|
||||
{
|
||||
auto [call, response] = msg;
|
||||
if (!parameters_.count(call->mdb.pid))
|
||||
return response->nak(NR_UNKNOWN_PID);
|
||||
|
||||
if (call->mdb.pid == last_rx_pid_)
|
||||
++ack_overflow_page_;
|
||||
else
|
||||
{
|
||||
ack_overflow_page_ = 0;
|
||||
last_rx_pid_ = call->mdb.pid;
|
||||
}
|
||||
|
||||
parameters_.at(call->mdb.pid)->dispatch(msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief BasicDevice::enqueueStatus
|
||||
* @param status
|
||||
*/
|
||||
void BasicDevice::enqueueStatus(StatusPtr status)
|
||||
{
|
||||
auto queue = status->status_type & 0xf;
|
||||
|
||||
if (!queued_statuses.count(queue))
|
||||
return;
|
||||
|
||||
if (queue >= status_reporting_threshold_)
|
||||
queued_statuses.at(queue).push(status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief BasicDevice::addParameter
|
||||
* @param metadata
|
||||
* @return
|
||||
*/
|
||||
Parameter *BasicDevice::addParameter(const ParameterDescription metadata)
|
||||
{
|
||||
if (parameters_.count(metadata.pid))
|
||||
return parameters_.at(metadata.pid);
|
||||
|
||||
auto [it, _] = parameters_.emplace(metadata.pid, new Parameter(metadata));
|
||||
return it->second;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief BasicDevice::actionGetSupportedParameters
|
||||
* @param msg
|
||||
*/
|
||||
void BasicDevice::actionGetSupportedParameters(MsgPair msg)
|
||||
{
|
||||
auto [_, response] = msg;
|
||||
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 it = parameters_.cbegin();
|
||||
if (first != 0)
|
||||
std::advance(it, first);
|
||||
while (it != parameters_.cend() && response->mdb.pdl() < 0xfe)
|
||||
{
|
||||
auto [pid, _] = *it;
|
||||
switch (pid) {
|
||||
case DMX_PERSONALITY.pid:
|
||||
case DMX_PERSONALITY_DESCRIPTION.pid:
|
||||
case DMX_START_ADDRESS.pid:
|
||||
case DMX_BLOCK_ADDRESS.pid:
|
||||
case DMX_STARTUP_MODE.pid:
|
||||
case DMX_FAIL_MODE.pid:
|
||||
if (!hasDMXaddress())
|
||||
continue;
|
||||
break;
|
||||
case PARAMETER_DESCRIPTION.pid:
|
||||
if (!hasManufacturerPIDs())
|
||||
continue;
|
||||
break;
|
||||
case SENSOR_VALUE.pid:
|
||||
case SENSOR_DEFINITION.pid:
|
||||
case RECORD_SENSORS.pid:
|
||||
if (!hasSensors())
|
||||
continue;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
response->appendParameterData(pid);
|
||||
it++;
|
||||
}
|
||||
|
||||
if (length > 0xfe && ack_overflow_page_ != lastPage)
|
||||
response->responseType = RESPONSE_TYPE_ACK_OVERFLOW;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief BasicDevice::actionGetParameterDescription
|
||||
* @param msg
|
||||
*/
|
||||
void BasicDevice::actionGetParameterDescription(MsgPair msg)
|
||||
{
|
||||
auto [command, response] = msg;
|
||||
/// \todo return a Parameter Description
|
||||
response->nak(NR_UNSUPPORTED_COMMAND_CLASS);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief BasicDevice::actionGetDeviceInfo
|
||||
* @param msg
|
||||
*/
|
||||
void BasicDevice::actionGetDeviceInfo(MsgPair msg)
|
||||
{
|
||||
auto [_, response] = msg;
|
||||
response->appendParameterData(RDM_PROTOCOL_VERSION);
|
||||
response->appendParameterData(deviceModelID);
|
||||
response->appendParameterData(deviceProductCategory);
|
||||
response->appendParameterData(LIB_VERSION);
|
||||
response->appendParameterData((uint16_t)0);
|
||||
response->appendParameterData((uint16_t)0);
|
||||
response->appendParameterData((uint16_t)0);
|
||||
response->appendParameterData((uint16_t)0);
|
||||
response->appendParameterData((uint16_t)0);
|
||||
response->appendParameterData<uint8_t>(sensors_.size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief BasicDevice::actionGetSoftwareVersionLabel
|
||||
* @param msg
|
||||
*/
|
||||
void BasicDevice::actionGetSoftwareVersionLabel(MsgPair msg)
|
||||
{
|
||||
auto [_, response] = msg;
|
||||
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 BasicDevice::actionGetIdentifyDevice
|
||||
* @param msg
|
||||
*/
|
||||
void BasicDevice::actionGetIdentifyDevice(MsgPair msg)
|
||||
{
|
||||
auto [_, response] = msg;
|
||||
response->appendParameterData<uint8_t>(identifying_);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief BasicDevice::actionSetIdentifyDevice
|
||||
* @param msg
|
||||
*/
|
||||
void BasicDevice::actionSetIdentifyDevice(MsgPair msg)
|
||||
{
|
||||
auto [command, _] = msg;
|
||||
identify(command->mdb.pd.front());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief BasicDevice::actionSetResetDevice
|
||||
* @param msg
|
||||
*/
|
||||
void BasicDevice::actionSetResetDevice(MsgPair msg)
|
||||
{
|
||||
auto [command, response] = msg;
|
||||
switch (command->mdb.pd.front()) {
|
||||
case 0x01:
|
||||
reset(false);
|
||||
break;
|
||||
case 0xff:
|
||||
reset(true);
|
||||
break;
|
||||
default:
|
||||
return response->nak(NR_DATA_OUT_OF_RANGE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace RDM
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
rdm/basicdevice.h
|
||||
|
||||
Copyright (c) 2023 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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "message.h"
|
||||
#include "parameter.h"
|
||||
#include "sensor.h"
|
||||
#include "status.h"
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
|
||||
namespace RDM {
|
||||
|
||||
/**
|
||||
* @brief The BasicDevice class
|
||||
*/
|
||||
class BasicDevice
|
||||
{
|
||||
public:
|
||||
explicit BasicDevice();
|
||||
virtual ~BasicDevice();
|
||||
|
||||
std::string deviceManufacturerLabel; //!< manufacturer label
|
||||
std::string deviceModelDescription; //!< model description
|
||||
uint16_t deviceModelID; //!< model ID number
|
||||
uint16_t deviceProductCategory; //!< device category
|
||||
|
||||
virtual void identify(bool state);
|
||||
virtual void reset(bool hard);
|
||||
|
||||
protected:
|
||||
friend class Device;
|
||||
friend class Responder;
|
||||
|
||||
virtual bool hasManufacturerPIDs() const; //!< device has non-standard PIDs @return
|
||||
virtual bool hasDMXaddress() const { return false; } //!< device has a DMX address @return
|
||||
virtual bool hasSubDevices() const { return false; } //!< device has subdevices @return
|
||||
virtual bool hasSensors() const { return !sensors_.empty(); } //!< device as sensors @return
|
||||
|
||||
virtual void dispatch(MsgPair msg);
|
||||
|
||||
void enqueueStatus(StatusPtr status);
|
||||
|
||||
Parameter *addParameter(const ParameterDescription metadata);
|
||||
|
||||
virtual void actionGetSupportedParameters(MsgPair msg);
|
||||
virtual void actionGetParameterDescription(MsgPair msg);
|
||||
virtual void actionGetDeviceInfo(MsgPair msg);
|
||||
virtual void actionGetSoftwareVersionLabel(MsgPair msg);
|
||||
virtual void actionGetIdentifyDevice(MsgPair msg);
|
||||
virtual void actionSetIdentifyDevice(MsgPair msg);
|
||||
virtual void actionSetResetDevice(MsgPair msg);
|
||||
|
||||
/// \cite RDM 10.3.2 The responder shall maintain reported status information until it has
|
||||
/// been successfully delivered to the controller.
|
||||
std::unordered_map<uint8_t, std::queue<StatusPtr>> queued_statuses; //!< outbound status queue
|
||||
|
||||
private:
|
||||
bool identifying_ = false;
|
||||
uint8_t status_reporting_threshold_;
|
||||
uint16_t last_rx_pid_ = 0;
|
||||
uint32_t ack_overflow_page_ = 0;
|
||||
|
||||
std::map<PID, Parameter*> parameters_; //!< parameters
|
||||
std::vector<Sensor*> sensors_; //!< sensors
|
||||
};
|
||||
|
||||
} // namespace RDM
|
||||
|
|
@ -22,148 +22,89 @@
|
|||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "device.h"
|
||||
#include "config.h"
|
||||
|
||||
namespace RDM {
|
||||
|
||||
/**
|
||||
* @brief Device::Device
|
||||
* @param id
|
||||
* @param parent
|
||||
*/
|
||||
Device::Device(UID id, Device* parent)
|
||||
: DMX::Device()
|
||||
Device::Device(UID id)
|
||||
: RDM::BasicDevice()
|
||||
, DMX::Device()
|
||||
, uid(id)
|
||||
, deviceManufacturerLabel(MY_ESTA_MANUFACTURER_LABEL)
|
||||
, deviceModelDescription("Basic RDM Device")
|
||||
, deviceModelID(0)
|
||||
, deviceProductCategory(PRODUCT_CATEGORY_NOT_DECLARED)
|
||||
, parent_(parent)
|
||||
, status_reporting_threshold_(STATUS_ADVISORY)
|
||||
{
|
||||
queued_statuses_.emplace(STATUS_ADVISORY, std::queue<StatusPtr>());
|
||||
queued_statuses_.emplace(STATUS_WARNING, std::queue<StatusPtr>());
|
||||
queued_statuses_.emplace(STATUS_ERROR, std::queue<StatusPtr>());
|
||||
deviceModelDescription = "Basic RDM Device";
|
||||
|
||||
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));
|
||||
Parameter *parameter;
|
||||
/// \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));
|
||||
parameter = addParameter(CLEAR_STATUS_ID);
|
||||
parameter->onGet([this](MsgPair msg){actionSetClearStatusId(msg);});
|
||||
/// \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));
|
||||
parameter = addParameter(SUB_DEVICE_STATUS_REPORT_THRESHOLD);
|
||||
parameter->onGet([this](MsgPair msg){actionGetSubdeviceThreshold(msg);});
|
||||
parameter->onSet([this](MsgPair msg){actionSetSubdeviceThreshold(msg);});
|
||||
/// \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));
|
||||
parameter = addParameter(PRODUCT_DETAIL_ID_LIST);
|
||||
parameter->onGet([this](MsgPair msg){actionGetProductDetailIdList(msg);});
|
||||
/// \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));
|
||||
parameter = addParameter(DEVICE_MODEL_DESCRIPTION);
|
||||
parameter->onGet([this](MsgPair msg){actionGetDevModelDescription(msg);});
|
||||
/// \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));
|
||||
parameter = addParameter(MANUFACTURER_LABEL);
|
||||
parameter->onGet([this](MsgPair msg){actionGetManufacturerLabel(msg);});
|
||||
/// \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));
|
||||
parameter = addParameter(LANGUAGE_CAPABILITIES);
|
||||
parameter->onGet([this](MsgPair msg){actionGetLanguage(msg);});
|
||||
/// \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));
|
||||
parameter = addParameter(LANGUAGE);
|
||||
parameter->onGet([this](MsgPair msg){actionGetLanguage(msg);});
|
||||
parameter->onSet([this](MsgPair msg){actionSetLanguage(msg);});
|
||||
/// \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));
|
||||
parameter = addParameter(DMX_PERSONALITY);
|
||||
parameter->onGet([this](MsgPair msg){actionGetDmxPersonality(msg);});
|
||||
parameter->onSet([this](MsgPair msg){actionSetDmxPersonality(msg);});
|
||||
/// \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));
|
||||
parameter = addParameter(DMX_PERSONALITY_DESCRIPTION);
|
||||
parameter->onGet([this](MsgPair msg){actionGetDmxPersonalityDesc(msg);});
|
||||
/// \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));
|
||||
parameter = addParameter(DMX_START_ADDRESS);
|
||||
parameter->onGet([this](MsgPair msg){actionGetDmxStartAddress(msg);});
|
||||
parameter->onSet([this](MsgPair msg){actionSetDmxStartAddress(msg);});
|
||||
/// \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));
|
||||
parameter = addParameter(SENSOR_DEFINITION);
|
||||
parameter->onGet([this](MsgPair msg){actionSensorDispatch(msg);});
|
||||
/// \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));
|
||||
parameter = addParameter(SENSOR_VALUE);
|
||||
parameter->onGet([this](MsgPair msg){actionSensorDispatch(msg);});
|
||||
parameter->onSet([this](MsgPair msg){actionSensorDispatch(msg);});
|
||||
/// \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));
|
||||
parameter = addParameter(RECORD_SENSORS);
|
||||
parameter->onSet([this](MsgPair msg){actionSensorDispatch(msg);});
|
||||
}
|
||||
|
||||
|
||||
|
@ -172,12 +113,6 @@ Device::Device(UID id, Device* parent)
|
|||
*/
|
||||
Device::~Device()
|
||||
{
|
||||
for( auto& [_, device] : sub_devices_)
|
||||
delete device;
|
||||
for (auto& [_, parameter] : parameters_)
|
||||
delete parameter;
|
||||
for (auto sensor : sensors_)
|
||||
delete sensor;
|
||||
}
|
||||
|
||||
|
||||
|
@ -185,17 +120,23 @@ Device::~Device()
|
|||
* @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)
|
||||
void Device::addSubDevice(uint16_t number, std::shared_ptr<SubDevice> dev)
|
||||
{
|
||||
if (parent_)
|
||||
return;
|
||||
|
||||
if (sub_devices_.count(number))
|
||||
delete sub_devices_.at(number);
|
||||
sub_devices_[number] = dev;
|
||||
sub_devices_.at(number) = dev;
|
||||
else
|
||||
sub_devices_.emplace(number, dev);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Device::deleteSubDevice
|
||||
* @param number
|
||||
*/
|
||||
void Device::deleteSubDevice(uint16_t number)
|
||||
{
|
||||
sub_devices_.erase(number);
|
||||
}
|
||||
|
||||
|
||||
|
@ -204,11 +145,8 @@ void Device::addSubDevice(uint16_t number, Device *dev)
|
|||
* @param number
|
||||
* @return
|
||||
*/
|
||||
Device* Device::subDevice(uint16_t number)
|
||||
std::shared_ptr<SubDevice> Device::subDevice(uint16_t number)
|
||||
{
|
||||
if (parent_)
|
||||
return nullptr;
|
||||
|
||||
if (sub_devices_.count(number))
|
||||
return sub_devices_.at(number);
|
||||
return nullptr;
|
||||
|
@ -221,17 +159,51 @@ Device* Device::subDevice(uint16_t number)
|
|||
*/
|
||||
uint16_t Device::subDeviceCount() const
|
||||
{
|
||||
if (parent_)
|
||||
return parent_->subDeviceCount();
|
||||
return sub_devices_.size();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Device::addProductDetailId
|
||||
* @brief Device::dispatch
|
||||
* @param msg
|
||||
*/
|
||||
void Device::dispatch(MsgPair msg)
|
||||
{
|
||||
auto [call, response] = msg;
|
||||
|
||||
if (call->subDevice == 0)
|
||||
return BasicDevice::dispatch(msg);
|
||||
else if (call->mdb.cc == DISCOVERY_COMMAND)
|
||||
return response->nak(NR_SUB_DEVICE_OUT_OF_RANGE);
|
||||
|
||||
if (call->subDevice == SUB_DEVICE_ALL_CALL)
|
||||
{
|
||||
/// \cite RDM 9.2.2 Using Sub-Devices
|
||||
/// Broadcast GET commands sent to the SUB_DEVICE_ALL_CALL Sub-Device ID are
|
||||
/// not allowed. Any responder receiving a GET command sent to this Sub-Device
|
||||
/// ID shall respond with a NACK with a NACK Reason Code of
|
||||
/// NR_SUB_DEVICE_OUT_OF_RANGE.
|
||||
if (call->mdb.cc == GET_COMMAND)
|
||||
return response->nak(NR_SUB_DEVICE_OUT_OF_RANGE);
|
||||
|
||||
response->do_not_send = true;
|
||||
for (auto& [_, dev] : sub_devices_)
|
||||
dev->dispatch(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub_devices_.count(call->subDevice))
|
||||
return sub_devices_.at(call->subDevice)->dispatch(msg);
|
||||
|
||||
response->nak(NR_SUB_DEVICE_OUT_OF_RANGE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Device::addProductDetail
|
||||
* @param id
|
||||
*/
|
||||
void Device::addProductDetailId(uint16_t id)
|
||||
void Device::addProductDetail(uint16_t id)
|
||||
{
|
||||
product_detail_list_.push_back(id);
|
||||
while (product_detail_list_.size() > 6)
|
||||
|
@ -239,67 +211,6 @@ void Device::addProductDetailId(uint16_t id)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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
|
||||
|
@ -307,10 +218,6 @@ void Device::reset(bool hard)
|
|||
*/
|
||||
void Device::enqueueMessage(MsgPtr message, bool urgent)
|
||||
{
|
||||
// only root devices have a message queue
|
||||
if (parent_)
|
||||
return;
|
||||
|
||||
message->destination = controller_uid_;
|
||||
|
||||
if (urgent)
|
||||
|
@ -320,112 +227,9 @@ void Device::enqueueMessage(MsgPtr message, bool urgent)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Device::enqueueStatus
|
||||
* @param status
|
||||
*/
|
||||
void Device::enqueueStatus(StatusPtr status)
|
||||
void Device::actionGetDeviceInfo(MsgPair msg)
|
||||
{
|
||||
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);
|
||||
|
||||
auto [_, response] = msg;
|
||||
response->appendParameterData(RDM_PROTOCOL_VERSION);
|
||||
response->appendParameterData(deviceModelID);
|
||||
response->appendParameterData(deviceProductCategory);
|
||||
|
@ -436,69 +240,52 @@ void Device::actionGetDeviceInfo(const MsgPtr message, MsgPtr response)
|
|||
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
|
||||
* @param msg
|
||||
*/
|
||||
void Device::actionSetClearStatusId(const MsgPtr message, MsgPtr response)
|
||||
void Device::actionSetClearStatusId([[maybe_unused]]MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 0)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
for (auto& [_, queue] : queued_statuses_)
|
||||
for (auto& [_, queue] : queued_statuses)
|
||||
while (!queue.empty())
|
||||
queue.pop();
|
||||
|
||||
response->responseType = RESPONSE_TYPE_ACK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Device::actionGetSubdeviceThreshold
|
||||
* @param message
|
||||
* @param response
|
||||
* @param msg
|
||||
*/
|
||||
void Device::actionGetSubdeviceThreshold(const MsgPtr message, MsgPtr response)
|
||||
void Device::actionGetSubdeviceThreshold(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 0)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
auto [_, response] = msg;
|
||||
response->appendParameterData(status_reporting_threshold_);
|
||||
|
||||
response->responseType = RESPONSE_TYPE_ACK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Device::actionSetSubdeviceThreshold
|
||||
* @param message
|
||||
* @param response
|
||||
* @param msg
|
||||
*/
|
||||
void Device::actionSetSubdeviceThreshold(const MsgPtr message, MsgPtr response)
|
||||
void Device::actionSetSubdeviceThreshold(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 1)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
uint8_t threshold = message->mdb.pd.front();
|
||||
|
||||
auto [command, response] = msg;
|
||||
uint8_t threshold = command->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();
|
||||
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();
|
||||
while (!queued_statuses.at(STATUS_ADVISORY).empty())
|
||||
queued_statuses.at(STATUS_ADVISORY).pop();
|
||||
break;
|
||||
case STATUS_ADVISORY:
|
||||
status_reporting_threshold_ = threshold;
|
||||
|
@ -507,128 +294,80 @@ void Device::actionSetSubdeviceThreshold(const MsgPtr message, MsgPtr response)
|
|||
response->nak(NR_DATA_OUT_OF_RANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
response->responseType = RESPONSE_TYPE_ACK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Device::actionGetProductDetailIdList
|
||||
* @param message
|
||||
* @param response
|
||||
* @param msg
|
||||
*/
|
||||
void Device::actionGetProductDetailIdList(const MsgPtr message, MsgPtr response)
|
||||
void Device::actionGetProductDetailIdList(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 0)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
auto [_, response] = msg;
|
||||
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
|
||||
* @param msg
|
||||
*/
|
||||
void Device::actionGetDevModelDescription(const MsgPtr message, MsgPtr response)
|
||||
void Device::actionGetDevModelDescription(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 0)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
auto [_, response] = msg;
|
||||
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
|
||||
* @param msg
|
||||
*/
|
||||
void Device::actionGetManufacturerLabel(const MsgPtr message, MsgPtr response)
|
||||
void Device::actionGetManufacturerLabel(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 0)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
auto [_, response] = msg;
|
||||
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
|
||||
* @param msg
|
||||
*/
|
||||
void Device::actionGetLanguage(const MsgPtr message, MsgPtr response)
|
||||
void Device::actionGetLanguage(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 0)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
auto [_, response] = msg;
|
||||
response->appendParameterData('e');
|
||||
response->appendParameterData('n');
|
||||
|
||||
response->responseType = RESPONSE_TYPE_ACK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Device::actionSetLanguage
|
||||
* @param message
|
||||
* @param response
|
||||
* @param msg
|
||||
*/
|
||||
void Device::actionSetLanguage(const MsgPtr message, MsgPtr response)
|
||||
void Device::actionSetLanguage(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 2)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
if (message->mdb.pd[0] != 'e' ||
|
||||
message->mdb.pd[1] != 'n')
|
||||
auto [command, response] = msg;
|
||||
if (command->mdb.pd[0] != 'e' ||
|
||||
command->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
|
||||
* @param msg
|
||||
*/
|
||||
void Device::actionGetDmxPersonality(const MsgPtr message, MsgPtr response)
|
||||
void Device::actionGetDmxPersonality(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 0)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
response->responseType = RESPONSE_TYPE_ACK;
|
||||
auto [_, response] = msg;
|
||||
response->appendParameterData(DMX::Device::personality());
|
||||
response->appendParameterData(DMX::Device::personalityCount());
|
||||
}
|
||||
|
@ -636,41 +375,30 @@ void Device::actionGetDmxPersonality(const MsgPtr message, MsgPtr response)
|
|||
|
||||
/**
|
||||
* @brief Device::actionSetDmxPersonality
|
||||
* @param message
|
||||
* @param response
|
||||
* @param msg
|
||||
*/
|
||||
void Device::actionSetDmxPersonality(const MsgPtr message, MsgPtr response)
|
||||
void Device::actionSetDmxPersonality(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 1)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
uint8_t mode = message->mdb.pd.front();
|
||||
|
||||
auto [command, response] = msg;
|
||||
uint8_t mode = command->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
|
||||
* @param msg
|
||||
*/
|
||||
void Device::actionGetDmxPersonalityDesc(const MsgPtr message, MsgPtr response)
|
||||
void Device::actionGetDmxPersonalityDesc(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 1)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
uint8_t mode = message->mdb.pd.front();
|
||||
auto [command, response] = msg;
|
||||
uint8_t mode = command->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++)
|
||||
|
@ -684,15 +412,11 @@ void Device::actionGetDmxPersonalityDesc(const MsgPtr message, MsgPtr response)
|
|||
|
||||
/**
|
||||
* @brief Device::actionGetDmxStartAddress
|
||||
* @param message
|
||||
* @param response
|
||||
* @param msg
|
||||
*/
|
||||
void Device::actionGetDmxStartAddress(const MsgPtr message, MsgPtr response)
|
||||
void Device::actionGetDmxStartAddress(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 0)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
response->responseType = RESPONSE_TYPE_ACK;
|
||||
auto [_, response] = msg;
|
||||
if (footprint() == 0)
|
||||
response->appendParameterData<uint16_t>(0xFFFF);
|
||||
else
|
||||
|
@ -702,36 +426,27 @@ void Device::actionGetDmxStartAddress(const MsgPtr message, MsgPtr response)
|
|||
|
||||
/**
|
||||
* @brief Device::actionSetDmxStartAddress
|
||||
* @param message
|
||||
* @param response
|
||||
* @param msg
|
||||
*/
|
||||
void Device::actionSetDmxStartAddress(const MsgPtr message, MsgPtr response)
|
||||
void Device::actionSetDmxStartAddress(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 2)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
uint16_t addr = Message::readType<uint16_t>(message->mdb.pd, 0);
|
||||
|
||||
auto [command, response] = msg;
|
||||
uint16_t addr = Message::readType<uint16_t>(command->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
|
||||
* @param msg
|
||||
*/
|
||||
void Device::actionSensorDispatch(const MsgPtr message, MsgPtr response)
|
||||
void Device::actionSensorDispatch(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 1)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
auto [command, response] = msg;
|
||||
uint8_t index = command->mdb.pd.front();
|
||||
|
||||
uint8_t index = message->mdb.pd.front();
|
||||
|
||||
switch (message->mdb.cc) {
|
||||
switch (command->mdb.cc) {
|
||||
case GET_COMMAND:
|
||||
{
|
||||
if (index == 0xFF || index >= sensors_.size())
|
||||
|
@ -740,11 +455,11 @@ void Device::actionSensorDispatch(const MsgPtr message, MsgPtr response)
|
|||
return;
|
||||
}
|
||||
auto sensor = sensors_.at(index);
|
||||
switch (message->mdb.pid) {
|
||||
case SENSOR_DEFINITION:
|
||||
switch (command->mdb.pid) {
|
||||
case SENSOR_DEFINITION.pid:
|
||||
sensor->actionGetSensorDefinition(index, response);
|
||||
break;
|
||||
case SENSOR_VALUE:
|
||||
case SENSOR_VALUE.pid:
|
||||
sensor->actionGetSensorValue(index, response);
|
||||
break;
|
||||
}
|
||||
|
@ -757,13 +472,14 @@ void Device::actionSensorDispatch(const MsgPtr message, MsgPtr response)
|
|||
response->nak(NR_DATA_OUT_OF_RANGE);
|
||||
return;
|
||||
}
|
||||
auto setSensor = [index, message, response](Sensor * sensor)
|
||||
auto setSensor = [index, msg](Sensor * sensor)
|
||||
{
|
||||
switch (message->mdb.pid) {
|
||||
case SENSOR_VALUE:
|
||||
auto [command, response] = msg;
|
||||
switch (command->mdb.pid) {
|
||||
case SENSOR_VALUE.pid:
|
||||
sensor->actionSetSensorValue(index, response);
|
||||
break;
|
||||
case RECORD_SENSORS:
|
||||
case RECORD_SENSORS.pid:
|
||||
sensor->actionSetRecordSensors(response);
|
||||
break;
|
||||
}
|
||||
|
@ -778,60 +494,4 @@ void Device::actionSensorDispatch(const MsgPtr message, MsgPtr response)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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
|
||||
|
|
|
@ -24,10 +24,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "../dmx/device.h"
|
||||
#include "parameter.h"
|
||||
#include "rdm.h"
|
||||
#include "sensor.h"
|
||||
#include "status.h"
|
||||
#include "basicdevice.h"
|
||||
#include "subdevice.h"
|
||||
#include "uid.h"
|
||||
|
||||
#include <list>
|
||||
|
@ -41,76 +39,51 @@ namespace RDM {
|
|||
* @brief The RDM::Device class
|
||||
*/
|
||||
class Device
|
||||
: public DMX::Device
|
||||
: public RDM::BasicDevice
|
||||
, public DMX::Device
|
||||
{
|
||||
public:
|
||||
explicit Device(UID id = UID(), Device* parent = nullptr);
|
||||
explicit Device(UID id = UID());
|
||||
virtual ~Device();
|
||||
|
||||
const UID uid; //!< This devices UID
|
||||
|
||||
void addSubDevice(uint16_t number, Device* dev);
|
||||
Device* subDevice(uint16_t number);
|
||||
uint16_t subDeviceCount() const;
|
||||
void addProductDetailId(uint16_t);
|
||||
std::shared_ptr<SubDevice> subDevice(uint16_t number);
|
||||
virtual uint16_t subDeviceCount() const;
|
||||
|
||||
Parameter* addParameter(const PID id);
|
||||
|
||||
void get(const MsgPtr message, MsgPtr response);
|
||||
void set(const MsgPtr message, MsgPtr response);
|
||||
|
||||
virtual void identify(bool state);
|
||||
virtual void reset(bool hard);
|
||||
|
||||
std::string deviceManufacturerLabel; //!< manufacturer label
|
||||
std::string deviceModelDescription; //!< model description
|
||||
uint16_t deviceModelID; //!< model ID number
|
||||
uint16_t deviceProductCategory; //!< device category
|
||||
void addProductDetail(uint16_t id);
|
||||
|
||||
protected:
|
||||
friend class Responder;
|
||||
std::unordered_map<uint16_t, Device*> sub_devices_; //!< sub devices
|
||||
std::unordered_map<PID, Parameter*> parameters_; //!< parameters
|
||||
std::vector<Sensor*> sensors_; //!< sensors
|
||||
std::list<uint16_t> product_detail_list_; //!< product detail list
|
||||
|
||||
std::deque<MsgPtr> queued_messages_; //!< outbound message queue
|
||||
virtual void dispatch(MsgPair msg) override;
|
||||
|
||||
void addSubDevice(uint16_t number, std::shared_ptr<SubDevice> dev);
|
||||
void deleteSubDevice(uint16_t number);
|
||||
|
||||
void enqueueMessage(MsgPtr message, bool urgent = false);
|
||||
UID controller_uid_; //!< controller UID
|
||||
|
||||
std::unordered_map<uint8_t, std::queue<StatusPtr>> queued_statuses_; //!< outbound status queue
|
||||
void enqueueStatus(StatusPtr status);
|
||||
|
||||
bool actionPrep_(const MsgPtr message, MsgPtr response);
|
||||
|
||||
void actionGetSupportedParameters (const MsgPtr message, MsgPtr response);
|
||||
void actionSetClearStatusId (const MsgPtr message, MsgPtr response);
|
||||
void actionGetSubdeviceThreshold (const MsgPtr message, MsgPtr response);
|
||||
void actionSetSubdeviceThreshold (const MsgPtr message, MsgPtr response);
|
||||
void actionGetDeviceInfo (const MsgPtr message, MsgPtr response);
|
||||
void actionGetProductDetailIdList (const MsgPtr message, MsgPtr response);
|
||||
void actionGetDevModelDescription (const MsgPtr message, MsgPtr response);
|
||||
void actionGetManufacturerLabel (const MsgPtr message, MsgPtr response);
|
||||
void actionGetLanguage (const MsgPtr message, MsgPtr response);
|
||||
void actionSetLanguage (const MsgPtr message, MsgPtr response);
|
||||
void actionGetSoftwareVersionLabel(const MsgPtr message, MsgPtr response);
|
||||
void actionGetDmxPersonality (const MsgPtr message, MsgPtr response);
|
||||
void actionSetDmxPersonality (const MsgPtr message, MsgPtr response);
|
||||
void actionGetDmxPersonalityDesc (const MsgPtr message, MsgPtr response);
|
||||
void actionGetDmxStartAddress (const MsgPtr message, MsgPtr response);
|
||||
void actionSetDmxStartAddress (const MsgPtr message, MsgPtr response);
|
||||
void actionSensorDispatch (const MsgPtr message, MsgPtr response);
|
||||
void actionGetIdentifyDevice (const MsgPtr message, MsgPtr response);
|
||||
void actionSetIdentifyDevice (const MsgPtr message, MsgPtr response);
|
||||
void actionSetResetDevice (const MsgPtr message, MsgPtr response);
|
||||
virtual void actionSetClearStatusId(MsgPair msg);
|
||||
virtual void actionGetSubdeviceThreshold(MsgPair msg);
|
||||
virtual void actionSetSubdeviceThreshold(MsgPair msg);
|
||||
virtual void actionGetDeviceInfo(MsgPair msg) override;
|
||||
virtual void actionGetProductDetailIdList(MsgPair msg);
|
||||
virtual void actionGetDevModelDescription(MsgPair msg);
|
||||
virtual void actionGetManufacturerLabel(MsgPair msg);
|
||||
virtual void actionGetLanguage(MsgPair msg);
|
||||
virtual void actionSetLanguage(MsgPair msg);
|
||||
virtual void actionGetDmxPersonality(MsgPair msg);
|
||||
virtual void actionSetDmxPersonality(MsgPair msg);
|
||||
virtual void actionGetDmxPersonalityDesc(MsgPair msg);
|
||||
virtual void actionGetDmxStartAddress(MsgPair msg);
|
||||
virtual void actionSetDmxStartAddress(MsgPair msg);
|
||||
virtual void actionSensorDispatch(MsgPair msg);
|
||||
|
||||
private:
|
||||
Device* parent_;
|
||||
PID last_rx_pid_ = 0;
|
||||
unsigned int ack_overflow_page = 0;
|
||||
uint8_t status_reporting_threshold_;
|
||||
bool identifying_ = false;
|
||||
|
||||
std::list<uint16_t> product_detail_list_; //!< product detail list
|
||||
std::unordered_map<uint16_t, std::shared_ptr<SubDevice>> sub_devices_; //!< sub devices
|
||||
std::deque<MsgPtr> queued_messages_; //!< outbound message queue
|
||||
UID controller_uid_; //!< controller UID
|
||||
};
|
||||
|
||||
} // namespace RDM
|
||||
|
|
|
@ -31,62 +31,52 @@ namespace RDM {
|
|||
/**
|
||||
* @brief Responder::Responder
|
||||
* @param id
|
||||
* @param parent
|
||||
*/
|
||||
Responder::Responder(UID id, Device* parent)
|
||||
: Device(id, parent)
|
||||
Responder::Responder(UID id)
|
||||
: Device(id)
|
||||
, control_field(0)
|
||||
{
|
||||
deviceModelID = 1;
|
||||
deviceModelDescription = "Basic RDM Responder";
|
||||
subdevice_flag = true;
|
||||
|
||||
Parameter *pid;
|
||||
|
||||
Parameter *parameter;
|
||||
/// \cite RDM 7.5 Discovery Unique Branch Message (DISC_UNIQUE_BRANCH)
|
||||
pid = addParameter(DISC_UNIQUE_BRANCH);
|
||||
pid->discAction(std::bind(&Responder::actionDiscoverUniqueBranch, this,
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
parameter = addParameter(DISC_UNIQUE_BRANCH);
|
||||
parameter->onDisc([this](MsgPair msg){actionDiscoverUniqueBranch(msg);});
|
||||
/// \cite RDM 7.6.3 Discovery Mute Message (DISC_MUTE)
|
||||
/// A responder port shall set its Mute flag when it receives this message
|
||||
/// containing its UID, or a broadcast address.
|
||||
pid = addParameter(DISC_MUTE);
|
||||
pid->discAction(std::bind(&Responder::actionDiscoveryMute, this,
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
parameter = addParameter(DISC_MUTE);
|
||||
parameter->onDisc([this](MsgPair msg){actionDiscoveryMute(msg);});
|
||||
/// \cite RDM 7.6.4 Discovery Un-Mute Message (DISC_UN_MUTE)
|
||||
/// A responder port shall clear its Mute flag when it receives this message
|
||||
/// containing its UID, or a broadcast address.
|
||||
pid = addParameter(DISC_UN_MUTE);
|
||||
pid->discAction(std::bind(&Responder::actionDiscoveryUnmute, this,
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
parameter = addParameter(DISC_UN_MUTE);
|
||||
parameter->onDisc([this](MsgPair msg){actionDiscoveryUnmute(msg);});
|
||||
/// \cite RDM 10.2.1 Communication Status (COMMS_STATUS)
|
||||
/// The COMMS_STATUS parameter is used to collect information that may be
|
||||
/// useful in analyzing the integrity of the communication system.
|
||||
pid = addParameter(COMMS_STATUS);
|
||||
pid->getAction(std::bind(&Responder::actionGetCommsStatus, this,
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
pid->setAction(std::bind(&Responder::actionSetCommsStatus, this,
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
parameter = addParameter(COMMS_STATUS);
|
||||
parameter->onGet([this](MsgPair msg){actionGetCommsStatus(msg);});
|
||||
parameter->onSet([this](MsgPair msg){actionSetCommsStatus(msg);});
|
||||
/// \cite RDM 10.3.1 Get Queued Message (QUEUED_MESSAGE)
|
||||
/// The QUEUED_MESSAGE parameter shall be used to retrieve a message from the
|
||||
/// responder’s message queue. The Message Count field of all response
|
||||
/// messages defines the number of messages that are queued in the responder.
|
||||
/// Each QUEUED_MESSAGE response shall be composed of a single message response.
|
||||
pid = addParameter(QUEUED_MESSAGE);
|
||||
pid->getAction(std::bind(&Responder::actionGetQueuedMessage, this,
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
parameter = addParameter(QUEUED_MESSAGE);
|
||||
parameter->onGet([this](MsgPair msg){actionGetQueuedMessage(msg);});
|
||||
/// \cite RDM 10.3.2 Get Status Messages (STATUS_MESSAGES)
|
||||
/// This parameter is used to collect Status or Error information
|
||||
/// from a device.
|
||||
pid = addParameter(STATUS_MESSAGES);
|
||||
pid->getAction(std::bind(&Responder::actionGetStatusMessages, this,
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
parameter = addParameter(STATUS_MESSAGES);
|
||||
parameter->onGet([this](MsgPair msg){actionGetStatusMessages(msg);});
|
||||
/// \cite RDM 10.3.3 Get Status ID Description (STATUS_ID_DESCRIPTION)
|
||||
/// This parameter is used to request an ASCII text description of a given
|
||||
/// Status ID. The description may be up to 32 characters.
|
||||
pid = addParameter(STATUS_ID_DESCRIPTION);
|
||||
pid->getAction(std::bind(&Responder::actionGetStatusIdDescription, this,
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
parameter = addParameter(STATUS_ID_DESCRIPTION);
|
||||
parameter->onGet([this](MsgPair msg){actionGetStatusIdDescription(msg);});
|
||||
}
|
||||
|
||||
|
||||
|
@ -97,12 +87,12 @@ Responder::~Responder()
|
|||
|
||||
/**
|
||||
* @brief Responder::send
|
||||
* @param data
|
||||
* @param buffer
|
||||
*/
|
||||
void Responder::send(const std::vector<uint8_t> &data)
|
||||
void Responder::send(const std::vector<uint8_t> &buffer)
|
||||
{
|
||||
if (auto sp = sender_.lock())
|
||||
(*sp)(data); // sender still exists
|
||||
(*sp)(buffer); // sender still exists
|
||||
}
|
||||
|
||||
|
||||
|
@ -159,12 +149,12 @@ std::shared_ptr<void> Responder::setSender(std::function<void(const std::vector<
|
|||
|
||||
/**
|
||||
* @brief Responder::receive
|
||||
* @param data
|
||||
* @param buffer
|
||||
*/
|
||||
void Responder::receive(const std::vector<uint8_t> &data)
|
||||
void Responder::receive(const std::vector<uint8_t> &buffer)
|
||||
{
|
||||
auto msg = std::make_shared<Message>();
|
||||
msg->read(data);
|
||||
msg->read(buffer);
|
||||
receive(msg);
|
||||
}
|
||||
|
||||
|
@ -223,6 +213,7 @@ void Responder::receive(const MsgPtr message)
|
|||
response->subDevice = message->subDevice; // copy sub-device
|
||||
response->mdb.pid = message->mdb.pid; // copy PID
|
||||
response->tn = message->tn; // this reply is a response to the received message
|
||||
response->responseType = RESPONSE_TYPE_ACK; // preset type to ack
|
||||
response->mdb.cc = response_cc; // appropriate command class
|
||||
|
||||
/// \cite RDM 5.3 Broadcast Message Addressing
|
||||
|
@ -235,21 +226,7 @@ void Responder::receive(const MsgPtr message)
|
|||
if (message->failure_mode)
|
||||
response->nak(NR_FORMAT_ERROR); // nak on any failure modes
|
||||
else
|
||||
{
|
||||
switch (message->mdb.cc) { // dispatch valid messages
|
||||
case DISCOVERY_COMMAND:
|
||||
rxDiscovery(message, response);
|
||||
break;
|
||||
case GET_COMMAND:
|
||||
rxGet(message, response);
|
||||
break;
|
||||
case SET_COMMAND:
|
||||
rxSet(message, response);
|
||||
break;
|
||||
default:
|
||||
response->nak(NR_UNSUPPORTED_COMMAND_CLASS);
|
||||
}
|
||||
}
|
||||
dispatch({message, response});
|
||||
|
||||
send(response); // send the reply
|
||||
}
|
||||
|
@ -261,7 +238,7 @@ void Responder::receive(const MsgPtr message)
|
|||
*/
|
||||
void Responder::reset(bool hard)
|
||||
{
|
||||
(void)hard;
|
||||
Device::reset(hard);
|
||||
|
||||
/// \cite RDM 10.11.2 Reset Device (RESET_DEVICE)
|
||||
/// This parameter shall also clear the Discovery Mute flag.
|
||||
|
@ -269,106 +246,13 @@ void Responder::reset(bool hard)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Responder::rxDiscovery
|
||||
* @param message
|
||||
* @param response
|
||||
*/
|
||||
void Responder::rxDiscovery(const MsgPtr message, MsgPtr response)
|
||||
{
|
||||
if (message->subDevice != 0)
|
||||
{
|
||||
response->nak(NR_SUB_DEVICE_OUT_OF_RANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!actionPrep_(message, response))
|
||||
return;
|
||||
|
||||
parameters_.at(message->mdb.pid)->disc(message, response);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Responder::rxGet
|
||||
* @param message
|
||||
* @param response
|
||||
*/
|
||||
void Responder::rxGet(const MsgPtr message, MsgPtr response)
|
||||
{
|
||||
/// \cite RDM 9.2.2 Using Sub-Devices
|
||||
/// Broadcast GET commands sent to the SUB_DEVICE_ALL_CALL Sub-Device ID are
|
||||
/// not allowed. Any responder receiving a GET command sent to this Sub-Device
|
||||
/// ID shall respond with a NACK with a NACK Reason Code of
|
||||
/// NR_SUB_DEVICE_OUT_OF_RANGE.
|
||||
if (message->subDevice == SUB_DEVICE_ALL_CALL)
|
||||
{
|
||||
response->nak(NR_SUB_DEVICE_OUT_OF_RANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (message->subDevice == 0)
|
||||
{
|
||||
get(message, response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sub_devices_.count(message->subDevice))
|
||||
{
|
||||
response->nak(NR_SUB_DEVICE_OUT_OF_RANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
sub_devices_.at(message->subDevice)->get(message, response);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Responder::rxSet
|
||||
* @param message
|
||||
* @param response
|
||||
*/
|
||||
void Responder::rxSet(const MsgPtr message, MsgPtr response)
|
||||
{
|
||||
if (message->subDevice == 0)
|
||||
{
|
||||
set(message, response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub_devices_.empty())
|
||||
{
|
||||
response->nak(NR_SUB_DEVICE_OUT_OF_RANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (message->subDevice == SUB_DEVICE_ALL_CALL)
|
||||
{
|
||||
for (auto& [num, dev] : sub_devices_)
|
||||
dev->set(message, response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sub_devices_.count(message->subDevice))
|
||||
{
|
||||
response->nak(NR_SUB_DEVICE_OUT_OF_RANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
sub_devices_.at(message->subDevice)->set(message, response);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Responder::actionDiscoverUniqueBranch
|
||||
* @param message
|
||||
* @param response
|
||||
* @param msg
|
||||
*/
|
||||
void Responder::actionDiscoverUniqueBranch(const MsgPtr message, MsgPtr response)
|
||||
void Responder::actionDiscoverUniqueBranch(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 12)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
auto [command, response] = msg;
|
||||
if (discovery_mute_flag_)
|
||||
{
|
||||
response->do_not_send = true;
|
||||
|
@ -376,10 +260,10 @@ void Responder::actionDiscoverUniqueBranch(const MsgPtr message, MsgPtr response
|
|||
}
|
||||
|
||||
UID lower, upper;
|
||||
lower.manufacturer = Message::readType<uint16_t>(message->mdb.pd, 0);
|
||||
lower.device = Message::readType<uint32_t>(message->mdb.pd, 2);
|
||||
upper.manufacturer = Message::readType<uint16_t>(message->mdb.pd, 6);
|
||||
upper.device = Message::readType<uint32_t>(message->mdb.pd, 8);
|
||||
lower.manufacturer = Message::readType<uint16_t>(command->mdb.pd, 0);
|
||||
lower.device = Message::readType<uint32_t>(command->mdb.pd, 2);
|
||||
upper.manufacturer = Message::readType<uint16_t>(command->mdb.pd, 6);
|
||||
upper.device = Message::readType<uint32_t>(command->mdb.pd, 8);
|
||||
|
||||
if (uid.uid() < lower.uid())
|
||||
{
|
||||
|
@ -399,184 +283,149 @@ void Responder::actionDiscoverUniqueBranch(const MsgPtr message, MsgPtr response
|
|||
|
||||
/**
|
||||
* @brief Responder::actionDiscoveryMute
|
||||
* @param message
|
||||
* @param response
|
||||
* @param msg
|
||||
*/
|
||||
void Responder::actionDiscoveryMute(const MsgPtr message, MsgPtr response)
|
||||
void Responder::actionDiscoveryMute(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 0)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
auto [command, response] = msg;
|
||||
discovery_mute_flag_ = true;
|
||||
controller_uid_ = message->source;
|
||||
controller_uid_ = command->source;
|
||||
response->appendParameterData(control_field);
|
||||
|
||||
response->responseType = RESPONSE_TYPE_ACK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Responder::actionDiscoveryUnmute
|
||||
* @param message
|
||||
* @param response
|
||||
* @param msg
|
||||
*/
|
||||
void Responder::actionDiscoveryUnmute(const MsgPtr message, MsgPtr response)
|
||||
void Responder::actionDiscoveryUnmute(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 0)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
auto [_, response] = msg;
|
||||
discovery_mute_flag_ = false;
|
||||
response->appendParameterData(control_field);
|
||||
|
||||
response->responseType = RESPONSE_TYPE_ACK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Responder::actionGetCommsStatus
|
||||
* @param message
|
||||
* @param response
|
||||
* @param msg
|
||||
*/
|
||||
void Responder::actionGetCommsStatus(const MsgPtr message, MsgPtr response)
|
||||
void Responder::actionGetCommsStatus(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 0)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
auto [_, response] = msg;
|
||||
response->appendParameterData(short_message_counter_);
|
||||
response->appendParameterData(length_mismatch_counter_);
|
||||
response->appendParameterData(checksum_fail_counter_);
|
||||
|
||||
response->responseType = RESPONSE_TYPE_ACK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Responder::actionSetCommsStatus
|
||||
* @param message
|
||||
* @param response
|
||||
* @param msg
|
||||
*/
|
||||
void Responder::actionSetCommsStatus(const MsgPtr message, MsgPtr response)
|
||||
void Responder::actionSetCommsStatus(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 0)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
auto [_, response] = msg;
|
||||
short_message_counter_ = 0;
|
||||
length_mismatch_counter_ = 0;
|
||||
checksum_fail_counter_ = 0;
|
||||
|
||||
response->responseType = RESPONSE_TYPE_ACK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Responder::actionGetQueuedMessage
|
||||
* @param message
|
||||
* @param response
|
||||
* @param msg
|
||||
*/
|
||||
void Responder::actionGetQueuedMessage(const MsgPtr message, MsgPtr response)
|
||||
void Responder::actionGetQueuedMessage(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 1)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
auto [_, response] = msg;
|
||||
if (queued_messages_.empty())
|
||||
{
|
||||
response->mdb.pid = STATUS_MESSAGES;
|
||||
actionGetStatusMessages(message, response);
|
||||
response->mdb.pid = STATUS_MESSAGES.pid;
|
||||
actionGetStatusMessages(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
response->do_not_send = true;
|
||||
auto msg = queued_messages_.front();
|
||||
response = queued_messages_.front();
|
||||
queued_messages_.pop_front();
|
||||
last_status_message_ = msg;
|
||||
send(msg);
|
||||
last_status_message_ = response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Responder::actionGetStatusMessages
|
||||
* @param message
|
||||
* @param response
|
||||
* @param msg
|
||||
*/
|
||||
void Responder::actionGetStatusMessages(const MsgPtr message, MsgPtr response)
|
||||
void Responder::actionGetStatusMessages(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 1)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
auto [command, response] = msg;
|
||||
uint8_t queue = command->mdb.pd.front();
|
||||
|
||||
uint8_t type = message->mdb.pd.front();
|
||||
/// Due to the maximum packet length limitation, the total number of status messages sent
|
||||
/// within a single message cannot exceed 25.
|
||||
int count = 25;
|
||||
|
||||
if (type != STATUS_GET_LAST_MESSAGE &&
|
||||
type != STATUS_ERROR &&
|
||||
type != STATUS_WARNING &&
|
||||
type != STATUS_ADVISORY)
|
||||
{
|
||||
response->nak(NR_DATA_OUT_OF_RANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == STATUS_GET_LAST_MESSAGE)
|
||||
{
|
||||
response->do_not_send = true;
|
||||
send(last_status_message_);
|
||||
return;
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
auto reportStatusQueue = [response, counter] (std::queue<StatusPtr> q) mutable
|
||||
/// \cite RDM 10.3.2 The Status Type STATUS_GET_LAST_MESSAGE shall be used by the Controller
|
||||
/// to request the retransmission of the last sent Status Message or Queued Message.
|
||||
if (queue == STATUS_GET_LAST_MESSAGE)
|
||||
{
|
||||
while(!q.empty() && counter < 25)
|
||||
if (last_status_message_)
|
||||
response = last_status_message_;
|
||||
else
|
||||
response->nak(NR_DATA_OUT_OF_RANGE);
|
||||
return;
|
||||
} else {
|
||||
/// Status is considered successfully delivered when the responder receives a
|
||||
/// Status Type Requested other than STATUS_GET_LAST_MESSAGE.
|
||||
last_status_message_ = nullptr;
|
||||
}
|
||||
|
||||
// _CLEARED responses sit in the same queue as their status setting counterparts
|
||||
queue &= 0xf;
|
||||
|
||||
auto reportStatusQueue = [msg, &count] (std::queue<StatusPtr> &q)
|
||||
{
|
||||
auto [_, response] = msg;
|
||||
while(!q.empty() && count >= 0)
|
||||
{
|
||||
for (uint8_t& b : q.front()->bytes)
|
||||
response->appendParameterData(b);
|
||||
counter++;
|
||||
count--;
|
||||
if (count < 0) break;
|
||||
|
||||
auto status = q.front();
|
||||
q.pop();
|
||||
|
||||
response->mdb.pd.insert(response->mdb.pd.end(),
|
||||
std::begin(status->bytes), std::end(status->bytes));
|
||||
}
|
||||
};
|
||||
|
||||
if (type == STATUS_ERROR ||
|
||||
type == STATUS_WARNING ||
|
||||
type == STATUS_ADVISORY)
|
||||
{
|
||||
reportStatusQueue(queued_statuses_.at(STATUS_ERROR));
|
||||
for (auto& [_, dev] : sub_devices_)
|
||||
reportStatusQueue(dev->queued_statuses_.at(STATUS_ERROR));
|
||||
}
|
||||
while (queue > STATUS_NONE && count >= 0)
|
||||
{
|
||||
if (queued_statuses.count(queue))
|
||||
reportStatusQueue(queued_statuses.at(queue));
|
||||
for (auto& [_, dev] : sub_devices_)
|
||||
if (dev->queued_statuses.count(queue))
|
||||
reportStatusQueue(dev->queued_statuses.at(queue));
|
||||
queue--;
|
||||
}
|
||||
|
||||
if (type == STATUS_WARNING ||
|
||||
type == STATUS_ADVISORY)
|
||||
{
|
||||
reportStatusQueue(queued_statuses_.at(STATUS_WARNING));
|
||||
for (auto& [_, dev] : sub_devices_)
|
||||
reportStatusQueue(dev->queued_statuses_.at(STATUS_WARNING));
|
||||
}
|
||||
|
||||
if (type == STATUS_ADVISORY)
|
||||
{
|
||||
reportStatusQueue(queued_statuses_.at(STATUS_ADVISORY));
|
||||
for (auto& [_, dev] : sub_devices_)
|
||||
reportStatusQueue(dev->queued_statuses_.at(STATUS_ADVISORY));
|
||||
}
|
||||
|
||||
if (counter == 25)
|
||||
if (count < 0)
|
||||
response->responseType = RESPONSE_TYPE_ACK_OVERFLOW;
|
||||
else
|
||||
response->responseType = RESPONSE_TYPE_ACK;
|
||||
|
||||
/// The responder shall maintain reported status information until it has been successfully
|
||||
/// delivered to the controller.
|
||||
last_status_message_ = response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Responder::actionGetStatusIdDescription
|
||||
* @param message
|
||||
* @param response
|
||||
* @param msg
|
||||
*/
|
||||
void Responder::actionGetStatusIdDescription(const MsgPtr message, MsgPtr response)
|
||||
void Responder::actionGetStatusIdDescription(MsgPair msg)
|
||||
{
|
||||
if (message->mdb.pdl() != 2)
|
||||
return response->nak(NR_FORMAT_ERROR);
|
||||
|
||||
uint16_t status = message->readType<uint16_t>(message->mdb.pd, 0);
|
||||
auto [command, response] = msg;
|
||||
uint16_t status = Message::readType<uint16_t>(command->mdb.pd, 0);
|
||||
|
||||
std::string label = RDM::StatusMessageDescription(status);
|
||||
for (size_t i = 0; i < label.size(); i++)
|
||||
|
@ -585,8 +434,6 @@ void Responder::actionGetStatusIdDescription(const MsgPtr message, MsgPtr respon
|
|||
break;
|
||||
response->appendParameterData(label.at(i));
|
||||
}
|
||||
|
||||
response->responseType = RESPONSE_TYPE_ACK;
|
||||
}
|
||||
|
||||
} // namespace RDM
|
||||
|
|
|
@ -37,7 +37,7 @@ class Responder
|
|||
: public Device
|
||||
{
|
||||
public:
|
||||
explicit Responder(UID id, Device* parent = nullptr);
|
||||
explicit Responder(UID id);
|
||||
virtual ~Responder();
|
||||
|
||||
std::shared_ptr<void> setSender(std::function<void(const std::vector<uint8_t>&)> cb);
|
||||
|
@ -52,25 +52,22 @@ public:
|
|||
};
|
||||
};
|
||||
|
||||
virtual void send(const std::vector<uint8_t> &data);
|
||||
virtual void send(const std::vector<uint8_t> &buffer);
|
||||
virtual void send(const MsgPtr message);
|
||||
virtual void receive(const std::vector<uint8_t> &data);
|
||||
virtual void receive(const std::vector<uint8_t> &buffer);
|
||||
virtual void receive(const MsgPtr message);
|
||||
|
||||
virtual void reset(bool hard) override;
|
||||
|
||||
protected:
|
||||
virtual void rxDiscovery(const MsgPtr message, MsgPtr response);
|
||||
virtual void rxGet(const MsgPtr message, MsgPtr response);
|
||||
virtual void rxSet(const MsgPtr message, MsgPtr response);
|
||||
|
||||
void actionDiscoverUniqueBranch (const MsgPtr message, MsgPtr response);
|
||||
void actionDiscoveryMute (const MsgPtr message, MsgPtr response);
|
||||
void actionDiscoveryUnmute (const MsgPtr message, MsgPtr response);
|
||||
void actionGetCommsStatus (const MsgPtr message, MsgPtr response);
|
||||
void actionSetCommsStatus (const MsgPtr message, MsgPtr response);
|
||||
void actionGetQueuedMessage (const MsgPtr message, MsgPtr response);
|
||||
void actionGetStatusMessages (const MsgPtr message, MsgPtr response);
|
||||
void actionGetStatusIdDescription (const MsgPtr message, MsgPtr response);
|
||||
virtual void actionDiscoverUniqueBranch (MsgPair msg);
|
||||
virtual void actionDiscoveryMute (MsgPair msg);
|
||||
virtual void actionDiscoveryUnmute (MsgPair msg);
|
||||
virtual void actionGetCommsStatus (MsgPair msg);
|
||||
virtual void actionSetCommsStatus (MsgPair msg);
|
||||
virtual void actionGetQueuedMessage (MsgPair msg);
|
||||
virtual void actionGetStatusMessages (MsgPair msg);
|
||||
virtual void actionGetStatusIdDescription (MsgPair msg);
|
||||
|
||||
private:
|
||||
bool discovery_mute_flag_ = false;
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
rdm/subdevice.cpp
|
||||
|
||||
Copyright (c) 2023 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 "subdevice.h"
|
||||
|
||||
namespace RDM {
|
||||
|
||||
SubDevice::SubDevice()
|
||||
: BasicDevice()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
SubDevice::~SubDevice()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace RDM
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
rdm/subdevice.h
|
||||
|
||||
Copyright (c) 2023 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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "basicdevice.h"
|
||||
|
||||
namespace RDM {
|
||||
|
||||
/**
|
||||
* @brief The SubDevice class
|
||||
*/
|
||||
class SubDevice
|
||||
: public BasicDevice
|
||||
{
|
||||
public:
|
||||
explicit SubDevice();
|
||||
virtual ~SubDevice();
|
||||
};
|
||||
|
||||
|
||||
} // namespace RDM
|
Loading…
Reference in New Issue