1
0
Fork 0

action-able basic PIDs

This commit is contained in:
Kevin Matz 2021-08-09 15:04:49 -04:00
parent e4fb0736fc
commit af47a4cfb7
11 changed files with 473 additions and 69 deletions

View File

@ -61,6 +61,8 @@ set(SOURCE_FILES
rdm/rdm.cpp
rdm/responder.h
rdm/responder.cpp
rdm/sensor.h
rdm/sensor.cpp
rdm/uid.h
rdmnet/rdmnet.h
sacn/data.cpp

View File

@ -33,6 +33,9 @@ Controller::Controller()
: Responder()
, next_transaction_(0)
{
deviceModelID = 2;
deviceModelDescription = "Basic RDM Controller";
deviceProductCategory = PRODUCT_CATEGORY_CONTROL_CONTROLLER;
}

View File

@ -21,8 +21,8 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include "config.h"
#include "device.h"
namespace RDM {
@ -30,13 +30,76 @@ namespace RDM {
/**
* @brief Device::Device
*/
Device::Device()
Device::Device(Device* parent)
: DMX::Device()
, deviceModelID(0)
, deviceModelDescription("Basic RDM Device")
, deviceProductCategory(PRODUCT_CATEGORY_NOT_DECLARED)
, parent_(parent)
{
// 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.
parameters_[SUPPORTED_PARAMETERS] = new Parameter;
id.manufacturer = MY_ESTA_MANUFACTURER_ID;
/// 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.
parameters_.try_emplace(SUPPORTED_PARAMETERS, new Parameter());
parameters_.at(SUPPORTED_PARAMETERS)->getAction(std::bind(
&Device::actionGetSupportedParameters,
this, std::placeholders::_1,
std::placeholders::_2));
/// 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.
parameters_.try_emplace(DEVICE_INFO, new Parameter());
parameters_.at(DEVICE_INFO)->getAction(std::bind(
&Device::actionGetDeviceInfo,
this, std::placeholders::_1,
std::placeholders::_2));
/// 10.5.2 Get Product Detail ID List (PRODUCT_DETAIL_ID_LIST)
/// This parameter shall be used for requesting technology details for a
/// device.
parameters_.try_emplace(PRODUCT_DETAIL_ID_LIST, new Parameter());
parameters_.at(PRODUCT_DETAIL_ID_LIST)->getAction(std::bind(
&Device::actionGetProductDetailIdList,
this, std::placeholders::_1,
std::placeholders::_2));
/// 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.
parameters_.try_emplace(DEVICE_MODEL_DESCRIPTION, new Parameter());
parameters_.at(DEVICE_MODEL_DESCRIPTION)->getAction(std::bind(
&Device::actionGetDevModelDescription,
this, std::placeholders::_1,
std::placeholders::_2));
/// 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.
parameters_.try_emplace(MANUFACTURER_LABEL, new Parameter());
parameters_.at(MANUFACTURER_LABEL)->getAction(std::bind(
&Device::actionGetManufacturerLabel,
this, std::placeholders::_1,
std::placeholders::_2));
/// 10.5.9 Get Software Version Label (SOFTWARE_VERSION_LABEL)
/// This parameter is used to get a descriptive ASCII text label for the
/// devices operating software version.
parameters_.try_emplace(SOFTWARE_VERSION_LABEL, new Parameter());
parameters_.at(SOFTWARE_VERSION_LABEL)->getAction(std::bind(
&Device::actionGetSoftwareVersionLabel,
this, std::placeholders::_1,
std::placeholders::_2));
/// 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.
parameters_.try_emplace(IDENTIFY_DEVICE, new Parameter());
parameters_.at(IDENTIFY_DEVICE)->getAction(std::bind(
&Device::actionGetIdentifyDevice,
this, std::placeholders::_1,
std::placeholders::_2));
parameters_.at(IDENTIFY_DEVICE)->setAction(std::bind(
&Device::actionSetIdentifyDevice,
this, std::placeholders::_1,
std::placeholders::_2));
}
@ -45,8 +108,70 @@ 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();
}
@ -58,12 +183,8 @@ Device::~Device()
void Device::get(const Message *message, Message *response)
{
response->commandClass = GET_COMMAND_RESPONSE;
if (!parameters_.count(message->propertyID))
{
response->responseType = RESPONSE_TYPE_NACK_REASON;
response->appendData<uint16_t>(NR_UNKNOWN_PID);
return;
}
if (!actionPrep_(message, response))
return;
parameters_.at(message->propertyID)->get(message, response);
}
@ -76,13 +197,193 @@ void Device::get(const Message *message, Message *response)
void Device::set(const Message *message, Message *response)
{
response->commandClass = SET_COMMAND_RESPONSE;
if (!actionPrep_(message, response))
return;
parameters_.at(message->propertyID)->set(message, response);
}
/**
* @brief Device::actionPrep
* @return
*/
bool Device::actionPrep_(const Message *message, Message *response)
{
if (!parameters_.count(message->propertyID))
{
response->responseType = RESPONSE_TYPE_NACK_REASON;
response->appendData<uint16_t>(NR_UNKNOWN_PID);
return false;
}
if (message->propertyID == last_rx_pid_)
++ack_overflow_page;
else {
ack_overflow_page = 0;
last_rx_pid_ = message->propertyID;
}
return true;
}
/**
* @brief Device::actionGetSupportedParameters
* @param message
* @param response
*/
void Device::actionGetSupportedParameters(
__attribute__((unused)) const Message *message, Message *response)
{
uint count = parameters_.size();
uint length = count * sizeof(PID);
uint lastPage = length / 0xfe;
uint first = ack_overflow_page * ( 0xfe / sizeof(PID) );
if (first >= count) {
ack_overflow_page = 0;
first = 0;
}
if (length > 0xfe && ack_overflow_page != lastPage)
response->responseType = RESPONSE_TYPE_ACK_OVERFLOW;
else
response->responseType = RESPONSE_TYPE_ACK;
auto pid = parameters_.begin();
if (first != 0)
std::advance(pid, first);
while (pid != parameters_.end() && response->length() < 0xfe)
{
response->appendData<PID>(pid->first);
pid++;
}
}
/**
* @brief Device::actionGetDeviceInfo
* @param message
* @param response
*/
void Device::actionGetDeviceInfo(
__attribute__((unused)) const Message *message, Message *response)
{
response->responseType = RESPONSE_TYPE_ACK;
response->appendData<uint16_t>(RDM_PROTOCOL_VERSION);
response->appendData<uint16_t>(deviceModelID);
response->appendData<uint16_t>(deviceProductCategory);
response->appendData<uint32_t>(LIB_VERSION);
response->appendData<uint16_t>(DMX::Device::footprint());
response->appendData<uint8_t> (DMX::Device::personality());
response->appendData<uint8_t> (DMX::Device::personalityCount());
response->appendData<uint16_t>(DMX::Device::address());
response->appendData<uint16_t>(subDeviceCount());
response->appendData<uint8_t> (sensors_.size());
}
/**
* @brief Device::actionGetProductDetailIdList
* @param message
* @param response
*/
void Device::actionGetProductDetailIdList(
__attribute__((unused)) const Message *message, Message *response)
{
response->responseType = RESPONSE_TYPE_ACK;
if (product_detail_list_.empty())
{
response->appendData<uint16_t>(PRODUCT_DETAIL_NOT_DECLARED);
return;
}
parameters_.at(message->propertyID)->set(message, response);
for ( uint16_t id : product_detail_list_ )
response->appendData<uint16_t>(id);
}
/**
* @brief Device::actionGetDevModelDescription
* @param message
* @param response
*/
void Device::actionGetDevModelDescription(
__attribute__((unused)) const Message *message, Message *response)
{
response->responseType = RESPONSE_TYPE_ACK;
for (size_t i = 0; i < deviceModelDescription.size(); i++)
{
if (i > 32)
break;
response->appendData<char>(deviceModelDescription.at(i));
}
}
/**
* @brief Device::actionGetManufacturerLabel
* @param message
* @param response
*/
void Device::actionGetManufacturerLabel(
__attribute__((unused)) const Message *message, Message *response)
{
response->responseType = RESPONSE_TYPE_ACK;
std::string label = std::string(MY_ESTA_MANUFACTURER_LABEL);
for (size_t i = 0; i < label.size(); i++)
{
if (i > 32)
break;
response->appendData<char>(label.at(i));
}
}
/**
* @brief Device::actionGetSoftwareVersionLabel
* @param message
* @param response
*/
void Device::actionGetSoftwareVersionLabel(
__attribute__((unused)) const Message *message, Message *response)
{
response->responseType = RESPONSE_TYPE_ACK;
std::string label = std::string(LIB_VERSION_LABEL);
for (size_t i = 0; i < label.size(); i++)
{
if (i > 32)
break;
response->appendData<char>(label.at(i));
}
}
/**
* @brief Device::actionGetIdentifyDevice
* @param message
* @param response
*/
void Device::actionGetIdentifyDevice(
__attribute__((unused)) const Message *message, Message *response)
{
response->responseType = RESPONSE_TYPE_ACK;
response->appendData<uint8_t>(identifying_);
}
/**
* @brief Device::actionSetIdentifyDevice
* @param message
* @param response
*/
void Device::actionSetIdentifyDevice(const Message *message, Message *response)
{
if (message->data()->size() != 1)
{
response->responseType = RESPONSE_TYPE_NACK_REASON;
response->appendData<uint16_t>(NR_FORMAT_ERROR);
return;
}
response->responseType = RESPONSE_TYPE_ACK;
identify(message->data()->front());
}
} // namespace RDM

View File

@ -23,34 +23,62 @@
*/
#pragma once
#include "dmx/device.h"
#include "parameter.h"
#include "rdm.h"
#include "sensor.h"
#include "uid.h"
#include <list>
#include <string>
#include <unordered_map>
namespace RDM {
class Device
: public DMX::Device
{
public:
Device();
Device(Device* parent = nullptr);
virtual ~Device();
const UID id() const { return id_; }
void addSubDevice(uint16_t number, Device* dev);
Device* subDevice(uint16_t number);
uint16_t subDeviceCount() const;
void addProductDetailId(uint16_t);
virtual void get(const Message* message, Message* response);
virtual void set(const Message* message, Message* response);
void get(const Message* message, Message* response);
void set(const Message* message, Message* response);
void setManufacturerId(uint16_t man) { id_.manufacturer = man; }
void setDeviceId(uint32_t dev) { id_.device = dev; }
virtual void identify(bool state) { identifying_ = state; }
UID id;
uint16_t deviceModelID;
std::string deviceModelDescription;
uint16_t deviceProductCategory;
protected:
std::unordered_map<uint16_t, Device*> sub_devices_;
std::unordered_map<PID, Parameter*> parameters_;
std::vector<Sensor*> sensors_;
std::list<uint16_t> product_detail_list_;
bool actionPrep_(const Message *message, Message *response);
void actionGetSupportedParameters (const Message *message, Message *response);
void actionGetDeviceInfo (const Message *message, Message *response);
void actionGetProductDetailIdList (const Message *message, Message *response);
void actionGetDevModelDescription (const Message *message, Message *response);
void actionGetManufacturerLabel (const Message *message, Message *response);
void actionGetSoftwareVersionLabel(const Message *message, Message *response);
void actionGetIdentifyDevice (const Message *message, Message *response);
void actionSetIdentifyDevice (const Message *message, Message *response);
private:
UID id_;
Device* parent_;
PID last_rx_pid_ = 0;
uint ack_overflow_page = 0;
bool identifying_ = false;
};

View File

@ -42,8 +42,13 @@ Parameter::Parameter()
void Parameter::get(__attribute__((unused)) const Message* message,
Message* response) const
{
response->responseType = RESPONSE_TYPE_NACK_REASON;
response->appendData<uint16_t>(NR_UNSUPPORTED_COMMAND_CLASS);
if (!getter_){
response->responseType = RESPONSE_TYPE_NACK_REASON;
response->appendData<uint16_t>(NR_UNSUPPORTED_COMMAND_CLASS);
return;
}
getter_(message, response);
}
@ -54,11 +59,16 @@ void Parameter::get(__attribute__((unused)) const Message* message,
* @param nak
*/
void Parameter::set(__attribute__((unused)) const Message* message,
Message* response)
Message* response) const
{
response->responseType = RESPONSE_TYPE_NACK_REASON;
response->appendData<uint16_t>(NR_UNSUPPORTED_COMMAND_CLASS);
if (!setter_)
{
response->responseType = RESPONSE_TYPE_NACK_REASON;
response->appendData<uint16_t>(NR_UNSUPPORTED_COMMAND_CLASS);
return;
}
setter_(message, response);
}
} // namespace RDM

View File

@ -26,16 +26,30 @@
#include "rdm.h"
#include "message.h"
#include <functional>
namespace RDM {
using PidAction = std::function<void(const Message* message, Message* response)>;
/**
* @brief The Parameter class
*/
class Parameter
{
public:
Parameter();
virtual ~Parameter();
~Parameter();
virtual void get(const Message* message, Message* response) const;
virtual void set(const Message* message, Message* response);
void get(const Message* message, Message* response) const;
void set(const Message* message, Message* response) const;
void getAction(const PidAction action) { getter_ = action; };
void setAction(const PidAction action) { setter_ = action; };
private:
PidAction getter_;
PidAction setter_;
};

View File

@ -35,6 +35,11 @@ using namespace DMX;
using PID = uint16_t;
/// 10.5.1 Get Device Info (DEVICE_INFO)
/// RDM Protocol Version:
/// The version of this standard is 1.0
static const uint16_t RDM_PROTOCOL_VERSION = 0x0100;
/// Appendix A: Defined Parameters (Normative)
/// START Codes (Slot 0)

View File

@ -31,13 +31,17 @@ namespace RDM {
Responder::Responder()
: Device()
{
deviceModelID = 1;
deviceModelDescription = "Basic RDM Responder";
// E1.20 required parameters
parameters_[DISC_UNIQUE_BRANCH] = new Parameter;
parameters_[DISC_MUTE] = new Parameter;
parameters_[DISC_UN_MUTE] = new Parameter;
parameters_[DEVICE_INFO] = new Parameter;
parameters_[SOFTWARE_VERSION_LABEL] = new Parameter;
parameters_[IDENTIFY_DEVICE] = new Parameter;
// DISC_UNIQUE_BRANCH
// DISC_MUTE
// DISC_UN_MUTE
// addt'l parameters
// COMMS_STATUS
// QUEUED_MESSAGE
}
@ -46,34 +50,6 @@ Responder::Responder()
*/
Responder::~Responder()
{
for( auto& [_, dev] : sub_devices_)
delete dev;
}
/**
* @brief Responder::addSubDevice
* @param number
* @param dev
*/
void Responder::addSubDevice(uint16_t number, Device *dev)
{
if (sub_devices_.count(number))
delete sub_devices_.at(number);
sub_devices_[number] = dev;
}
/**
* @brief Responder::subDevice
* @param number
* @return
*/
Device* Responder::subDevice(uint16_t number)
{
if (sub_devices_.count(number))
return sub_devices_.at(number);
return nullptr;
}
@ -273,7 +249,7 @@ void Responder::receive(const Message *message)
return;
response = new Message();
response->source = id();
response->source = id;
response->destination = message->source;
response->subDevice = message->subDevice;
response->propertyID = message->propertyID;

View File

@ -40,8 +40,6 @@ public:
virtual ~Responder();
void receive(const std::vector<uint8_t>& data);
void addSubDevice(uint16_t number, Device* dev);
Device* subDevice(uint16_t number);
protected:
virtual void send();
@ -59,7 +57,6 @@ protected:
std::queue<Message*> queued_messages_;
private:
std::unordered_map<uint16_t, Device*> sub_devices_;
};

34
rdm/sensor.cpp Normal file
View File

@ -0,0 +1,34 @@
/*
sensor.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 "sensor.h"
namespace RDM {
Sensor::Sensor()
{
}
} // namespace RDM

34
rdm/sensor.h Normal file
View File

@ -0,0 +1,34 @@
/*
sensor.h
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.
*/
#pragma once
namespace RDM {
class Sensor
{
public:
Sensor();
};
} // namespace RDM