1
0
Fork 0

more rdm support

This commit is contained in:
Kevin Matz 2021-08-08 17:25:41 -04:00
parent 970617b9cd
commit 1d996f46a4
17 changed files with 1404 additions and 1 deletions

View File

@ -41,8 +41,22 @@ set(SOURCE_FILES
dmx/universe.cpp
dmx/universe.h
otp/opt.h
rdm/controller.h
rdm/controller.cpp
rdm/device.h
rdm/device.cpp
rdm/E1.37-1.h
rdm/E1.37-2.h
rdm/E1.37-7.h
rdm/message.h
rdm/message.cpp
rdm/parameter.h
rdm/parameter.cpp
rdm/rdm.h
rdm/rdm.cpp
rdm/responder.h
rdm/responder.cpp
rdm/uid.h
rdmnet/rdmnet.h
sacn/data.cpp
sacn/data.h

79
rdm/E1.37-1.h Normal file
View File

@ -0,0 +1,79 @@
/*
E1.37-1.h.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
#include <cstdint>
/**
* @brief ANSI E1.37-1 2012 (R2017)
* Additional Message Sets for ANSI E1.20 (RDM) Part 1,
* Dimmer Message Sets
*/
namespace RDM {
using PID = uint16_t;
/// Appendix A: Defined Parameters (Normative)
/// Table A-1: RDM Parameter ID Defines
/// Category DMX512 Setup
static const PID DMX_BLOCK_ADDRESS = 0x0140;
static const PID DMX_FAIL_MODE = 0x0141;
static const PID DMX_STARTUP_MODE = 0x0142;
/// Category Dimmer Settings
static const PID DIMMER_INFO = 0x0340;
static const PID MINIMUM_LEVEL = 0x0341;
static const PID MAXIMUM_LEVEL = 0x0342;
static const PID CURVE = 0x0343;
static const PID CURVE_DESCRIPTION = 0x0344;
static const PID OUTPUT_RESPONSE_TIME = 0x0345;
static const PID OUTPUT_RESPONSE_TIME_DESCRIPTION = 0x0346;
static const PID MODULATION_FREQUENCY = 0x0347;
static const PID MODULATION_FREQUENCY_DESCRIPTION = 0x0348;
/// Category Power/Lamp Settings
static const PID BURN_IN = 0x0440;
/// Category Configuration
static const PID LOCK_PIN = 0x0640;
static const PID LOCK_STATE = 0x0641;
static const PID LOCK_STATE_DESCRIPTION = 0x0642;
/// Category Control
static const PID IDENTIFY_MODE = 0x1040;
static const PID PRESET_INFO = 0x1041;
static const PID PRESET_STATUS = 0x1042;
static const PID PRESET_MERGEMODE = 0x1043;
static const PID POWER_ON_SELF_TEST = 0x1044;
/// Preset Programmed Defines
static const uint8_t PRESET_NOT_PROGRAMMED = 0x00;
static const uint8_t PRESET_PROGRAMMED = 0x01;
static const uint8_t PRESET_PROGRAMMED_READ_ONLY = 0x02;
/// Merge Mode Defines
static const uint8_t MERGEMODE_DEFAULT = 0x00;
static const uint8_t MERGEMODE_HTP = 0x01;
static const uint8_t MERGEMODE_LTP = 0x02;
static const uint8_t MERGEMODE_DMX_ONLY = 0x03;
static const uint8_t MERGEMODE_OTHER = 0xFF;
} // namespace RDM

66
rdm/E1.37-2.h Normal file
View File

@ -0,0 +1,66 @@
/*
E1.37-2.h.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
#include <cstdint>
/**
* @brief ANSI E1.37-2 2015 Entertainment Technology
* Additional Message Sets for ANSI E1.20 (RDM) Part 2,
* IPv4 & DNS Configuration Messages
*/
namespace RDM {
using PID = uint16_t;
/// Appendix A: Defined Parameters (Normative)
static const uint32_t IPV4_UNCONFIGURED = 0x00000000;
static const uint32_t NO_DEFAULT_ROUTE = 0x00000000;
/// Table A-1: RDM Parameter ID
static const PID LIST_INTERFACES = 0x0700;
static const PID INTERFACE_LABEL = 0x0701;
static const PID INTERFACE_HARDWARE_ADDRESS_TYPE1 = 0x0702;
static const PID IPV4_DHCP_MODE = 0x0703;
static const PID IPV4_ZEROCONF_MODE = 0x0704;
static const PID IPV4_CURRENT_ADDRESS = 0x0705;
static const PID IPV4_STATIC_ADDRESS = 0x0706;
static const PID INTERFACE_RENEW_DHCP = 0x0707;
static const PID INTERFACE_RELEASE_DHCP = 0x0708;
static const PID INTERFACE_APPLY_CONFIGURATION = 0x0709;
static const PID IPV4_DEFAULT_ROUTE = 0x070A;
static const PID DNS_IPV4_NAME_SERVER = 0x070B;
static const PID DNS_HOSTNAME = 0x070C;
static const PID DNS_DOMAIN_NAME = 0x070D;
/// Table A-2: Additional NACK Reason Codes
// superseded in E1.37-7
// static const uint16_t NR_ACTION_NOT_SUPPORTED = 0x000B; //!< The parameter data is valid but the SET operation cannot be performed with the current configuration.
/// Table A-3: DHCP Mode
static const uint8_t DHCP_STATUS_INACTIVE = 0x00;
static const uint8_t DHCP_STATUS_ACTIVE = 0x01; //!< The IP address was not obtained via DHCP.
static const uint8_t DHCP_STATUS_UNKNOWN = 0x02; //!< The IP address was obtained via DHCP.
} // namespace RDM

81
rdm/E1.37-7.h Normal file
View File

@ -0,0 +1,81 @@
/*
E1.37-7.h.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
#include <cstdint>
/**
* @brief ANSI E1.37-7
* Additional Message Sets for ANSI E1.20 (RDM) -
* Gateway & Splitter Configuration Messages
*/
namespace RDM {
using PID = uint16_t;
/// Appendix A: Defined Parameters (Normative)
/// Table A-1: RDM Parameter ID
static const PID ENDPOINT_LIST = 0x0900;
static const PID ENDPOINT_LIST_CHANGE = 0x0901;
static const PID IDENTIFY_ENDPOINT = 0x0902;
static const PID ENDPOINT_TO_UNIVERSE = 0x0903;
static const PID ENDPOINT_MODE = 0x0904;
static const PID ENDPOINT_LABEL = 0x0905;
static const PID RDM_TRAFFIC_ENABLE = 0x0906;
static const PID DISCOVERY_STATE = 0x0907;
static const PID BACKGROUND_DISCOVERY = 0x0908;
static const PID ENDPOINT_TIMING = 0x0909;
static const PID ENDPOINT_TIMING_DESCRIPTION = 0x090A;
static const PID ENDPOINT_RESPONDERS = 0x090B;
static const PID ENDPOINT_RESPONDER_LIST_CHANGE = 0x090C;
static const PID BINDING_CONTROL_FIELDS = 0x090D;
static const PID BACKGROUND_QUEUED_STATUS_POLICY = 0x090E;
static const PID BACKGROUND_QUEUED_STATUS_POLICY_DESCRIPTION = 0x090F;
/// Table A-2: Discovery State
static const uint8_t DISCOVERY_INCOMPLETE = 0x00;
static const uint8_t DISCOVERY_INCREMENTAL = 0x01;
static const uint8_t DISCOVERY_FULL = 0x02;
static const uint8_t DISCOVERY_NOT_ACTIVE = 0x04;
/// Table A-3: Discovery Status
static const uint16_t DISCOVERY_COUNT_INCOMPLETE = 0x0000;
static const uint16_t DISCOVERY_COUNT_UNKNOWN = 0xFFFF;
/// Table A-4: Endpoint Mode
static const uint8_t ENDPOINT_MODE_DISABLED = 0x00;
static const uint8_t ENDPOINT_MODE_INPUT = 0x01;
static const uint8_t ENDPOINT_MODE_OUTPUT = 0x02;
/// Table A-5: Endpoint Types
static const uint8_t ENDPOINT_TYPE_VIRTUAL = 0x00;
static const uint8_t ENDPOINT_TYPE_PHYSICAL = 0x01;
/// Table A-6: Additional Response NACK Reason Codes
static const uint16_t NR_ACTION_NOT_SUPPORTED = 0x000B; // also defined in E1.37-2
static const uint16_t NR_ENDPOINT_NUMBER_INVALID = 0x000C;
static const uint16_t NR_INVALID_ENDPOINT_MODE = 0x000D;
static const uint16_t NR_UNKNOWN_UID = 0x000E;
} // namespace RDM

79
rdm/controller.cpp Normal file
View File

@ -0,0 +1,79 @@
/*
controller.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 "controller.h"
namespace RDM {
/**
* @brief Controller::Controller
*/
Controller::Controller()
: Responder()
, next_transaction_(0)
{
}
/**
* @brief Controller::~Controller
*/
Controller::~Controller()
{
}
/**
* @brief Controller::rxDiscoveryResponse
* @param message
*/
void Controller::rxDiscoveryResponse(__attribute__((unused)) const Message* message)
{
}
/**
* @brief Controller::rxGetResponse
* @param message
*/
void Controller::rxGetResponse(__attribute__((unused)) const Message* message)
{
}
/**
* @brief Controller::rxSetResponse
* @param message
*/
void Controller::rxSetResponse(__attribute__((unused)) const Message* message)
{
}
} // namespace RDM

52
rdm/controller.h Normal file
View File

@ -0,0 +1,52 @@
/*
controller.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
#include "responder.h"
namespace RDM {
/**
* @brief The Controller class
*/
class Controller
: public Responder
{
public:
Controller();
~Controller();
protected:
virtual void rxDiscoveryResponse(const Message* message) override;
virtual void rxGetResponse(const Message* message) override;
virtual void rxSetResponse(const Message* message) override;
private:
uint8_t next_transaction_;
};
} // namespace RDM

88
rdm/device.cpp Normal file
View File

@ -0,0 +1,88 @@
/*
device.cpp
Copyright (c) 2021 Kevin Matz (kevin.matz@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include "device.h"
namespace RDM {
/**
* @brief Device::Device
*/
Device::Device()
{
// 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;
}
/**
* @brief Device::~Device
*/
Device::~Device()
{
for (auto& [_, parameter] : parameters_)
delete parameter;
}
/**
* @brief Device::get
* @param message
* @param response
*/
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;
}
parameters_.at(message->propertyID)->get(message, response);
}
/**
* @brief Device::set
* @param message
* @param response
*/
void Device::set(const Message *message, Message *response)
{
response->commandClass = SET_COMMAND_RESPONSE;
if (!parameters_.count(message->propertyID))
{
response->responseType = RESPONSE_TYPE_NACK_REASON;
response->appendData<uint16_t>(NR_UNKNOWN_PID);
return;
}
parameters_.at(message->propertyID)->set(message, response);
}
} // namespace RDM

58
rdm/device.h Normal file
View File

@ -0,0 +1,58 @@
/*
device.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
#include "parameter.h"
#include "rdm.h"
#include "uid.h"
#include <unordered_map>
namespace RDM {
class Device
{
public:
Device();
virtual ~Device();
const UID id() const { return id_; }
virtual void get(const Message* message, Message* response);
virtual void set(const Message* message, Message* response);
void setManufacturerId(uint16_t man) { id_.manufacturer = man; }
void setDeviceId(uint32_t dev) { id_.device = dev; }
protected:
std::unordered_map<PID, Parameter*> parameters_;
private:
UID id_;
};
} // namespace RDM

92
rdm/message.cpp Normal file
View File

@ -0,0 +1,92 @@
/*
message.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 "message.h"
namespace RDM {
/**
* @brief Message::Message
*/
Message::Message()
{
}
/**
* @brief Message::Message
* @param obj
*/
Message::Message(const Message &obj)
: source(obj.source)
, destination(obj.destination)
, transaction(obj.transaction)
, portID(obj.portID)
, messageCount(obj.messageCount)
, subDevice(obj.subDevice)
, propertyID(obj.propertyID)
{
data_.insert(data_.end(), obj.data()->begin(), obj.data()->end());
}
/**
* @brief Message::~Message
*/
Message::~Message()
{
}
/**
* @brief Message::checksum
* @return
*/
uint16_t Message::checksum()
{
uint16_t sum = 0;
sum = addSum_<uint8_t> (sum, SC_RDM);
sum = addSum_<uint8_t> (sum, SC_SUB_MESSAGE);
sum = addSum_<uint8_t> (sum, 24 + length());
sum = addSum_<uint16_t>(sum, source.manufacturer);
sum = addSum_<uint32_t>(sum, source.device);
sum = addSum_<uint16_t>(sum, destination.manufacturer);
sum = addSum_<uint32_t>(sum, destination.device);
sum = addSum_<uint8_t> (sum, transaction);
sum = addSum_<uint8_t> (sum, portID);
sum = addSum_<uint8_t> (sum, messageCount);
sum = addSum_<uint16_t>(sum, subDevice);
sum = addSum_<uint8_t> (sum, commandClass);
sum = addSum_<uint16_t>(sum, propertyID);
sum = addSum_<uint8_t> (sum, length());
for (uint8_t val : data_)
sum = addSum_<uint8_t>(sum, val);
return sum;
}
} // namespace RDM

104
rdm/message.h Normal file
View File

@ -0,0 +1,104 @@
/*
message.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
#include <cstdint>
#include <vector>
#include "rdm.h"
#include "uid.h"
namespace RDM {
struct Message
{
UID source;
UID destination;
uint8_t transaction;
union {
uint8_t portID;
uint8_t responseType;
};
uint8_t messageCount;
uint16_t subDevice;
uint8_t commandClass;
PID propertyID;
Message();
Message(const Message &obj);
~Message();
const std::vector<uint8_t>* data() const { return &data_; }
uint8_t length() const { return data_.size(); }
uint16_t checksum();
template<typename T>
void appendData(const T & val)
{
Message::writeType<T>(data_, val);
}
template<typename T>
static T readType(const std::vector<uint8_t>& vect, size_t start)
{
if (vect.size() < sizeof(T))
return 0;
T ret = 0;
auto data = reinterpret_cast<uint8_t*>(&ret);
for (int i = sizeof(T); --i >= 0; )
data[i] = vect[start + i];
return ret;
}
template<typename T>
static void writeType(std::vector<uint8_t>& data, T val)
{
auto raw = reinterpret_cast<uint8_t*>(val);
for (int i = sizeof(T); --i >= 0; )
data.push_back(raw[i]);
}
private:
std::vector<uint8_t> data_;
template<typename T>
uint16_t addSum_(uint16_t sum, T val) const
{
uint16_t carry;
auto raw = reinterpret_cast<uint8_t*>(val);
for (int i = sizeof(T); --i >= 0; )
{
uint8_t num = raw[i];
while (num != 0)
{
carry = sum & num;
sum = sum ^ num;
num = carry << 1;
}
}
return sum;
}
};
} // namespace RDM

64
rdm/parameter.cpp Normal file
View File

@ -0,0 +1,64 @@
/*
property.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 "parameter.h"
namespace RDM {
/**
* @brief Property::Property
*/
Parameter::Parameter()
{
}
/**
* @brief Property::get
* @param message
* @param ack
* @param nak
*/
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);
}
/**
* @brief Property::set
* @param message
* @param ack
* @param nak
*/
void Parameter::set(__attribute__((unused)) const Message* message,
Message* response)
{
response->responseType = RESPONSE_TYPE_NACK_REASON;
response->appendData<uint16_t>(NR_UNSUPPORTED_COMMAND_CLASS);
}
} // namespace RDM

42
rdm/parameter.h Normal file
View File

@ -0,0 +1,42 @@
/*
property.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
#include "rdm.h"
#include "message.h"
namespace RDM {
class Parameter
{
public:
Parameter();
virtual ~Parameter();
virtual void get(const Message* message, Message* response) const;
virtual void set(const Message* message, Message* response);
};
} // namespace RDM

View File

@ -355,11 +355,53 @@ std::string ProductDetailDescription(const u_int16_t PRODUCT_DETAIL)
/// for where the Manufacturer believes that none of the defined details apply.
case PRODUCT_DETAIL_OTHER:
return std::string("Other");
default:
return std::string("Unknown");
}
}
/**
* @brief NackReasonDescription
* @param NR
* @return
*/
std::string NackReasonDescription(const uint16_t NR)
{
switch (NR) {
case NR_UNKNOWN_PID:
return std::string("The responder cannot comply with request because the message is not implemented in responder.");
case NR_FORMAT_ERROR:
return std::string("The responder cannot interpret request as controller data was not formatted correctly.");
case NR_HARDWARE_FAULT:
return std::string("The responder cannot comply due to an internal hardware fault.");
case NR_PROXY_REJECT:
return std::string("Proxy is not the RDM line master and cannot comply with message.");
case NR_WRITE_PROTECT:
return std::string("SET Command normally allowed but being blocked currently.");
case NR_UNSUPPORTED_COMMAND_CLASS:
return std::string("Not valid for Command Class attempted.");
case NR_DATA_OUT_OF_RANGE:
return std::string("Value for given Parameter out of allowable range or not supported.");
case NR_BUFFER_FULL:
return std::string("Buffer or Queue space currently has no free space to store data.");
case NR_PACKET_SIZE_UNSUPPORTED:
return std::string("Incoming message exceeds buffer capacity.");
case NR_SUB_DEVICE_OUT_OF_RANGE:
return std::string("Sub-Device is out of range or unknown.");
case NR_PROXY_BUFFER_FULL:
return std::string("The proxy buffer is full and can not store any more Queued Message or Status Message responses.");
case NR_ACTION_NOT_SUPPORTED: //!< from E1.37-7
return std::string("The specified action is not supported.");
case NR_ENDPOINT_NUMBER_INVALID: //!< from E1.37-7
return std::string("The specified endpoint is invalid.");
case NR_INVALID_ENDPOINT_MODE: //!< from E1.37-7
return std::string("The specified endpoint is in an invalid Endpoint Mode for the requested action.");
case NR_UNKNOWN_UID: //!< from E1.37-7
return std::string("The specified UID is not recognized.");
default:
return std::string("Unknown");
}
}
} // namespace RDM

View File

@ -24,6 +24,10 @@
#pragma once
#include "dmx/dmx.h"
#include "E1.37-1.h"
#include "E1.37-2.h"
#include "E1.37-7.h"
#include <string>
namespace RDM {
@ -438,6 +442,7 @@ using namespace DMX;
static const uint8_t CC_SET = 0x02; //!< PID supports SET only
static const uint8_t CC_GET_SET = 0x03;
std::string NackReasonDescription(const uint16_t NR);
/// Table A-17: Response NACK Reason Code
static const uint16_t NR_UNKNOWN_PID = 0x0000;
static const uint16_t NR_FORMAT_ERROR = 0x0001;

431
rdm/responder.cpp Normal file
View File

@ -0,0 +1,431 @@
/*
responder.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 "responder.h"
namespace RDM {
/**
* @brief Responder::Responder
*/
Responder::Responder()
: Device()
{
// 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;
}
/**
* @brief 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;
}
/**
* @brief Responder::receive
* @param data
*/
void Responder::receive(const std::vector<uint8_t> &data)
{
if (data.size() < 3) // SC + SC_SUB + LENGTH
return;
Message message;
// 6.2.1 START Code
// This field shall contain the defined RDM START Code (SC_RDM). Controllers
// and Responders shall always send SC_RDM in this slot, and any packet
// containing a value other than SC_RDM is outside the scope of this standard.
if (data[0] != SC_RDM)
return;
// 6.2.2 Sub START Code
// This field shall contain the Sub-START Code within RDM that defines this
// packet structure (SC_SUB_MESSAGE). Future versions of this standard which
// may have additional or different packet structures would use this field to
// identify the packet structure being used.
// Controllers shall always send SC_SUB_MESSAGE in this slot, and Responders
// shall ignore any packets containing other values.
if (data[1] != SC_SUB_MESSAGE)
return;
// 6.2.3 Message Length
// The Message Length value is defined as the number of slots in the RDM
// Packet including the START Code and excluding the Checksum. Each slot is
// an 8-bit value.
// The Message Length field points to the Checksum High Slot.
uint8_t length = data[2];
if (length != data.size() - 2)
return;
// 6.2.4 Destination UID
// The Destination UID is the UID of the target device(s).
message.destination.manufacturer = Message::readType<uint16_t>(data, 3);
message.destination.device = Message::readType<uint32_t>(data, 5);
// 6.2.5 Source UID
// The Source UID is the UID of the device originating this packet.
message.source.manufacturer = Message::readType<uint16_t>(data, 9);
message.source.device = Message::readType<uint32_t>(data, 11);
// 6.2.6 Transaction Number (TN)
// Controller generated packets increment this field every time an RDM packet
// is transmitted.
// Responders shall reply with their Transaction Number set to the Transaction
// Number contained in the controller packet to which they are responding.
message.transaction = data[15];
// 6.2.7 Port ID / Response Type
// This field serves different functions depending on whether the message is
// being generated by the controller or the responder.
message.portID = data[16];
// 6.2.8 Message Count
// The message count field is used by a responder to indicate that additional
// data is now available for collection by a controller. This data (which
// might be unrelated to the current message transaction) should be collected
// by the controller using the GET:QUEUED_MESSAGE command.
message.messageCount = data[17];
// 6.2.9 Sub-Device Field
// Sub-devices should be used in devices containing a repetitive number of
// similar modules, such as a dimmer rack. The Sub-Device field allows
// Parameter messages to be addressed to a specific module within the device
// to set or get properties of that module.
// The 16-bit sub-device field provides a range of 512 valid sub-devices,
// addressed from 1 - 512.
// The value of 0xFFFF is reserved as a SUB_DEVICE_ALL_CALL. A value of 0x0000
// shall be used to address the root or base properties of the device that do
// not belong to any sub-device module.
// The Parameter ID designates which parameter on the sub-device is being
// addressed. The use of Sub-Devices is described further in Section 9.
message.subDevice = Message::readType<uint16_t>(data, 18);
// 6.2.10.1 Command Class (CC)
message.commandClass = data[20];
// 6.2.10.2 Parameter ID (PID)
// The Parameter ID is a 16-bit number that identifies a specific type of
// Parameter Data. The Parameter ID (PID) may represent either a well known
// Parameter such as those defined in this document, or a
// Manufacturer-specific parameter whose details are either published by the
// Manufacturer for third-party support or proprietary for the Manufacturers
// own use.
message.propertyID = Message::readType<uint16_t>(data, 21);
// 6.2.10.3 Parameter Data Length (PDL)
// The Parameter Data Length (PDL) is the number of slots included in the
// Parameter Data area that it precedes. When this field is set to 0x00 it
// indicates that there is no Parameter Data following.
uint8_t pdl = data[23];
// 6.2.10.4 Parameter Data (PD)
// The Parameter Data is of variable length. The content format is PID dependent.
if (pdl > 0)
for (int i = 0; i < pdl; i++)
message.appendData(data[24+i]);
if (message.length() != pdl)
return;
// 6.2.11 Checksum
// If the checksum field in the packet does not match the calculated checksum,
// then the packet shall be discarded and no response sent.
auto checksum = Message::readType<uint16_t>(data, length);
if (checksum != message.checksum())
return;
receive(&message);
}
/**
* @brief Responder::send
*/
void Responder::send()
{
if (queued_messages_.empty())
return;
auto message = queued_messages_.front();
queued_messages_.pop();
send(message);
}
/**
* @brief Responder::send
* @param data
*/
void Responder::send(__attribute__((unused)) const std::vector<uint8_t> &data)
{
}
/**
* @brief Responder::send
* @param message
*/
void Responder::send(Message *message)
{
// 6.2.8.2 Message Count field for Responder Generated Messages
// If a responder has more than 255 messages queued, then the Message Count
// field shall remain at 255 until the number of queued messages is reduced
// below that number.
if (queued_messages_.size() > 255)
message->messageCount = 255;
else
message->messageCount = queued_messages_.size();
std::vector<uint8_t> data;
data.push_back(SC_RDM);
data.push_back(SC_SUB_MESSAGE);
data.push_back(24 + message->length());
Message::writeType<uint16_t>(data, message->destination.manufacturer);
Message::writeType<uint32_t>(data, message->destination.device);
Message::writeType<uint16_t>(data, message->source.manufacturer);
Message::writeType<uint32_t>(data, message->source.device);
data.push_back(message->transaction);
data.push_back(message->portID);
data.push_back(message->messageCount);
Message::writeType<uint16_t>(data, message->subDevice);
data.push_back(message->commandClass);
Message::writeType<uint16_t>(data, message->propertyID);
data.push_back(message->length());
data.insert(data.end(), message->data()->begin(), message->data()->end());
Message::writeType<uint16_t>(data, message->checksum());
send(data);
delete message;
}
/**
* @brief Responder::receive
* @param message
*/
void Responder::receive(const Message *message)
{
Message * response = nullptr;
if (message->commandClass == DISCOVERY_COMMAND ||
message->commandClass == GET_COMMAND ||
message->commandClass == SET_COMMAND)
{
// 6.2.8.1 Message Count field for Controller Generated Messages
// The Message Count shall be set to 0x00 in all controller generated requests.
if (message->messageCount != 0)
return;
response = new Message();
response->source = id();
response->destination = message->source;
response->subDevice = message->subDevice;
response->propertyID = message->propertyID;
response->transaction = message->transaction;
}
switch (message->commandClass) {
case DISCOVERY_COMMAND:
rxDiscovery(message, response);
return;
case DISCOVERY_COMMAND_RESPONSE:
rxDiscoveryResponse(message);
return;
case GET_COMMAND:
rxGet(message, response);
return;
case GET_COMMAND_RESPONSE:
rxGetResponse(message);
return;
case SET_COMMAND:
rxSet(message, response);
return;
case SET_COMMAND_RESPONSE:
rxSetResponse(message);
return;
default:
return;
}
}
/**
* @brief Responder::rxDiscovery
* @param message
*/
void Responder::rxDiscovery(__attribute__((unused)) const Message *message,
Message* response)
{
response->commandClass = DISCOVERY_COMMAND_RESPONSE;
send(response);
}
/**
* @brief Responder::rxDiscoveryResponse
* @param message
*/
void Responder::rxDiscoveryResponse(__attribute__((unused)) const Message *message)
{
}
/**
* @brief Responder::rxGet
* @param message
*/
void Responder::rxGet(const Message *message,
__attribute__((unused)) Message* response)
{
// 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->responseType = RESPONSE_TYPE_NACK_REASON;
response->appendData<uint16_t>(NR_SUB_DEVICE_OUT_OF_RANGE);
send(response);
return;
}
if (message->subDevice == 0)
{
get(message, response);
send(response);
return;
}
if (!sub_devices_.count(message->subDevice))
{
response->responseType = RESPONSE_TYPE_NACK_REASON;
response->appendData<uint16_t>(NR_SUB_DEVICE_OUT_OF_RANGE);
send(response);
return;
}
sub_devices_.at(message->subDevice)->get(message, response);
send(response);
}
/**
* @brief Responder::rxGetResponse
* @param message
*/
void Responder::rxGetResponse(__attribute__((unused)) const Message *message)
{
}
/**
* @brief Responder::rxSet
* @param message
*/
void Responder::rxSet(const Message *message,
__attribute__((unused)) Message* response)
{
if (message->subDevice == 0)
{
set(message, response);
send(response);
return;
}
if (message->subDevice == SUB_DEVICE_ALL_CALL)
{
for (auto& [num, dev] : sub_devices_)
{
Message * rsp = new Message(*response);
rsp->subDevice = num;
dev->set(message, rsp);
queued_messages_.push(rsp);
}
delete response;
send();
return;
}
if (!sub_devices_.count(message->subDevice))
{
response->responseType = RESPONSE_TYPE_NACK_REASON;
response->appendData<uint16_t>(NR_SUB_DEVICE_OUT_OF_RANGE);
send(response);
return;
}
sub_devices_.at(message->subDevice)->set(message, response);
send(response);
}
/**
* @brief Responder::rxSetResponse
* @param message
*/
void Responder::rxSetResponse(__attribute__((unused)) const Message *message)
{
}
} // namespace RDM

66
rdm/responder.h Normal file
View File

@ -0,0 +1,66 @@
/*
responder.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
#include "device.h"
#include "message.h"
#include "uid.h"
#include <unordered_map>
#include <queue>
namespace RDM {
class Responder
: public Device
{
public:
Responder();
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();
virtual void send(const std::vector<uint8_t>& data);
virtual void send(Message* message);
virtual void receive(const Message* message);
virtual void rxDiscovery(const Message* message, Message* response);
virtual void rxDiscoveryResponse(const Message* message);
virtual void rxGet(const Message* message, Message* response);
virtual void rxGetResponse(const Message* message);
virtual void rxSet(const Message* message, Message* response);
virtual void rxSetResponse(const Message* message);
std::queue<Message*> queued_messages_;
private:
std::unordered_map<uint16_t, Device*> sub_devices_;
};
} // namespace RDM

40
rdm/uid.h Normal file
View File

@ -0,0 +1,40 @@
/*
uid.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
#include <cstdint>
namespace RDM {
struct UID {
union {
uint64_t uid : 48;
struct {
uint16_t manufacturer;
uint32_t device;
};
};
};
} // namespace RDM