2023-05-02 13:00:19 -04:00
|
|
|
|
/*
|
|
|
|
|
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 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);
|
2023-05-03 15:56:13 -04:00
|
|
|
|
response->appendParameterData((uint16_t)0); // DMX512 Footprint
|
|
|
|
|
response->appendParameterData((uint16_t)0); // DMX512 Personality
|
|
|
|
|
response->appendParameterData((uint16_t)0); // DMX512 START address 1
|
|
|
|
|
response->appendParameterData((uint16_t)0); // DMX512 START address 2
|
|
|
|
|
response->appendParameterData((uint16_t)0); // Sub-Device count
|
2023-05-02 13:00:19 -04:00
|
|
|
|
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
|