2021-08-08 17:25:41 -04:00
|
|
|
|
/*
|
|
|
|
|
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()
|
2021-08-10 15:35:27 -04:00
|
|
|
|
: failure_mode(0)
|
2021-08-08 17:25:41 -04:00
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @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)
|
2021-08-12 00:44:41 -04:00
|
|
|
|
, parameterId(obj.parameterId)
|
2021-08-10 15:35:27 -04:00
|
|
|
|
, failure_mode(0)
|
2021-08-08 17:25:41 -04:00
|
|
|
|
{
|
|
|
|
|
data_.insert(data_.end(), obj.data()->begin(), obj.data()->end());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-08-10 13:33:24 -04:00
|
|
|
|
/**
|
|
|
|
|
* @brief Message::Message
|
|
|
|
|
* @param data
|
|
|
|
|
*/
|
|
|
|
|
Message::Message(const std::vector<u_int8_t> &data)
|
2021-08-10 15:35:27 -04:00
|
|
|
|
: failure_mode(0)
|
2021-08-10 13:33:24 -04:00
|
|
|
|
{
|
|
|
|
|
read(data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-08-08 17:25:41 -04:00
|
|
|
|
/**
|
|
|
|
|
* @brief Message::~Message
|
|
|
|
|
*/
|
|
|
|
|
Message::~Message()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-08-10 13:33:24 -04:00
|
|
|
|
/**
|
|
|
|
|
* @brief Message::read
|
|
|
|
|
* @param data
|
|
|
|
|
*/
|
|
|
|
|
void Message::read(const std::vector<uint8_t> &data)
|
|
|
|
|
{
|
2021-08-10 15:35:27 -04:00
|
|
|
|
if (data.size() < 9) // SC + SC_SUB + LENGTH + DESTINATION
|
|
|
|
|
{
|
|
|
|
|
short_message = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-08-10 13:33:24 -04:00
|
|
|
|
|
|
|
|
|
// 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)
|
2021-08-10 15:35:27 -04:00
|
|
|
|
{
|
|
|
|
|
incorrect_sc = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-08-10 13:33:24 -04:00
|
|
|
|
|
|
|
|
|
// 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)
|
2021-08-10 15:35:27 -04:00
|
|
|
|
{
|
|
|
|
|
incorrect_sub_sc = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-08-10 13:33:24 -04:00
|
|
|
|
|
|
|
|
|
// 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];
|
2021-08-10 15:35:27 -04:00
|
|
|
|
if (length + 2 != data.size())
|
|
|
|
|
{
|
|
|
|
|
length_mismatch = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-08-10 13:33:24 -04:00
|
|
|
|
|
|
|
|
|
// 6.2.4 Destination UID
|
|
|
|
|
// The Destination UID is the UID of the target device(s).
|
2021-08-11 09:42:43 -04:00
|
|
|
|
destination.manufacturer = readType<uint16_t>(data, 3);
|
|
|
|
|
destination.device = readType<uint32_t>(data, 5);
|
2021-08-10 13:33:24 -04:00
|
|
|
|
|
|
|
|
|
// 6.2.5 Source UID
|
|
|
|
|
// The Source UID is the UID of the device originating this packet.
|
2021-08-11 09:42:43 -04:00
|
|
|
|
source.manufacturer = readType<uint16_t>(data, 9);
|
|
|
|
|
source.device = readType<uint32_t>(data, 11);
|
2021-08-10 13:33:24 -04:00
|
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
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.
|
|
|
|
|
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.
|
|
|
|
|
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.
|
2021-08-11 09:42:43 -04:00
|
|
|
|
subDevice = readType<uint16_t>(data, 18);
|
2021-08-10 13:33:24 -04:00
|
|
|
|
|
|
|
|
|
// 6.2.10.1 Command Class (CC)
|
|
|
|
|
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 Manufacturer’s
|
|
|
|
|
// own use.
|
2021-08-12 00:44:41 -04:00
|
|
|
|
parameterId = readType<uint16_t>(data, 21);
|
2021-08-10 13:33:24 -04:00
|
|
|
|
|
|
|
|
|
// 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)
|
2021-08-10 15:35:27 -04:00
|
|
|
|
// The Parameter Data is of variable length.
|
2021-08-10 13:33:24 -04:00
|
|
|
|
for (int i = 0; i < pdl; i++)
|
2021-08-11 09:42:43 -04:00
|
|
|
|
appendData(data[24+i]);
|
2021-08-10 15:35:27 -04:00
|
|
|
|
|
|
|
|
|
// 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.
|
2021-08-11 09:42:43 -04:00
|
|
|
|
auto chksum = readType<uint16_t>(data, data.size() - 2);
|
2021-08-10 15:35:27 -04:00
|
|
|
|
if (chksum != checksum())
|
|
|
|
|
{
|
|
|
|
|
checksum_fail = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-08-10 13:33:24 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Message::write
|
|
|
|
|
* @param data
|
|
|
|
|
*/
|
|
|
|
|
void Message::write(std::vector<uint8_t> &data) const
|
|
|
|
|
{
|
2021-08-11 01:26:51 -04:00
|
|
|
|
if (commandClass == DISCOVERY_COMMAND_RESPONSE &&
|
2021-08-12 00:44:41 -04:00
|
|
|
|
parameterId == DISC_UNIQUE_BRANCH)
|
2021-08-11 01:26:51 -04:00
|
|
|
|
{
|
|
|
|
|
writeDiscBranch(data);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-08-10 13:33:24 -04:00
|
|
|
|
data.push_back(SC_RDM);
|
|
|
|
|
data.push_back(SC_SUB_MESSAGE);
|
|
|
|
|
data.push_back(24 + length());
|
2021-08-11 09:42:43 -04:00
|
|
|
|
writeType(data, destination.manufacturer);
|
|
|
|
|
writeType(data, destination.device);
|
|
|
|
|
writeType(data, source.manufacturer);
|
|
|
|
|
writeType(data, source.device);
|
2021-08-10 13:33:24 -04:00
|
|
|
|
data.push_back(transaction);
|
|
|
|
|
data.push_back(portID);
|
|
|
|
|
data.push_back(messageCount);
|
2021-08-11 09:42:43 -04:00
|
|
|
|
writeType(data, subDevice);
|
2021-08-10 13:33:24 -04:00
|
|
|
|
data.push_back(commandClass);
|
2021-08-12 00:44:41 -04:00
|
|
|
|
writeType(data, parameterId);
|
2021-08-10 13:33:24 -04:00
|
|
|
|
data.push_back(length());
|
|
|
|
|
data.insert(data.end(), data_.begin(), data_.end());
|
2021-08-11 09:42:43 -04:00
|
|
|
|
writeType(data, checksum());
|
2021-08-11 01:26:51 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Message::writeDiscBranch
|
|
|
|
|
* @param data
|
|
|
|
|
*/
|
|
|
|
|
void Message::writeDiscBranch(std::vector<uint8_t> &data) const
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < 7; i++)
|
|
|
|
|
data.push_back(0xfe); //!< Response Preamble bytes
|
|
|
|
|
data.push_back(0xaa); //!< Preamble separator byte
|
|
|
|
|
|
|
|
|
|
uint16_t sum = 0;
|
|
|
|
|
for ( const uint8_t& v : data_)
|
2021-08-11 09:42:43 -04:00
|
|
|
|
sum = addSum_(sum, v);
|
2021-08-11 01:26:51 -04:00
|
|
|
|
|
|
|
|
|
std::vector<uint8_t> d = data_;
|
|
|
|
|
writeType<uint16_t>(d, sum);
|
|
|
|
|
|
|
|
|
|
for ( uint8_t& v : d)
|
|
|
|
|
{
|
|
|
|
|
data.push_back(v | 0xaa);
|
|
|
|
|
data.push_back(v | 0x55);
|
|
|
|
|
}
|
2021-08-10 13:33:24 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-08-10 16:08:09 -04:00
|
|
|
|
/**
|
|
|
|
|
* @brief Message::nak
|
|
|
|
|
* @param reason
|
|
|
|
|
*/
|
|
|
|
|
void Message::nak(uint16_t reason)
|
|
|
|
|
{
|
|
|
|
|
data_.clear();
|
|
|
|
|
responseType = RESPONSE_TYPE_NACK_REASON;
|
2021-08-11 09:42:43 -04:00
|
|
|
|
appendData(reason);
|
2021-08-10 16:08:09 -04:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-08-08 17:25:41 -04:00
|
|
|
|
/**
|
|
|
|
|
* @brief Message::checksum
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
2021-08-10 13:33:24 -04:00
|
|
|
|
uint16_t Message::checksum() const
|
2021-08-08 17:25:41 -04:00
|
|
|
|
{
|
|
|
|
|
uint16_t sum = 0;
|
|
|
|
|
|
2021-08-11 09:42:43 -04:00
|
|
|
|
sum = addSum_(sum, SC_RDM);
|
|
|
|
|
sum = addSum_(sum, SC_SUB_MESSAGE);
|
|
|
|
|
sum = addSum_(sum, 24 + length());
|
|
|
|
|
sum = addSum_(sum, source.manufacturer);
|
|
|
|
|
sum = addSum_(sum, source.device);
|
|
|
|
|
sum = addSum_(sum, destination.manufacturer);
|
|
|
|
|
sum = addSum_(sum, destination.device);
|
|
|
|
|
sum = addSum_(sum, transaction);
|
|
|
|
|
sum = addSum_(sum, portID);
|
|
|
|
|
sum = addSum_(sum, messageCount);
|
|
|
|
|
sum = addSum_(sum, subDevice);
|
|
|
|
|
sum = addSum_(sum, commandClass);
|
2021-08-12 00:44:41 -04:00
|
|
|
|
sum = addSum_(sum, parameterId);
|
2021-08-11 09:42:43 -04:00
|
|
|
|
sum = addSum_(sum, length());
|
2021-08-08 17:25:41 -04:00
|
|
|
|
for (uint8_t val : data_)
|
2021-08-11 09:42:43 -04:00
|
|
|
|
sum = addSum_(sum, val);
|
2021-08-08 17:25:41 -04:00
|
|
|
|
|
|
|
|
|
return sum;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-10 16:08:09 -04:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Message::requiredLength
|
|
|
|
|
* @param length
|
|
|
|
|
* @param response
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
2021-08-11 10:28:03 -04:00
|
|
|
|
bool Message::requiredLength(const size_t length, MsgPtr response) const
|
2021-08-10 16:08:09 -04:00
|
|
|
|
{
|
|
|
|
|
if (data_.size() != length)
|
|
|
|
|
{
|
|
|
|
|
response->nak(NR_FORMAT_ERROR);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-08 17:25:41 -04:00
|
|
|
|
} // namespace RDM
|