1
0
Fork 0
OpenLCP/protocol/rdm/message.cpp

328 lines
9.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
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()
: failure_mode(0)
{
}
/**
* @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)
, parameterId(obj.parameterId)
, failure_mode(0)
{
data_.insert(data_.end(), obj.data()->begin(), obj.data()->end());
}
/**
* @brief Message::Message
* @param data
*/
Message::Message(const std::vector<uint8_t> &data)
: failure_mode(0)
{
read(data);
}
/**
* @brief Message::read
* @param data
*/
void Message::read(const std::vector<uint8_t> &data)
{
if (data.size() < 9) // SC + SC_SUB + LENGTH + DESTINATION
{
short_message = true;
return;
}
/// \cite RDM 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)
{
incorrect_sc = true;
return;
}
/// \cite RDM 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)
{
incorrect_sub_sc = true;
return;
}
/// \cite RDM 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 + 2 != data.size())
{
length_mismatch = true;
return;
}
/// \cite RDM 6.2.4 Destination UID
/// The Destination UID is the UID of the target device(s).
destination.manufacturer = readType<uint16_t>(data, 3);
destination.device = readType<uint32_t>(data, 5);
/// \cite RDM 6.2.5 Source UID
/// The Source UID is the UID of the device originating this packet.
source.manufacturer = readType<uint16_t>(data, 9);
source.device = readType<uint32_t>(data, 11);
/// \cite RDM 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];
/// \cite RDM 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];
/// \cite RDM 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];
/// \cite RDM 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.
subDevice = readType<uint16_t>(data, 18);
/// \cite RDM 6.2.10.1 Command Class (CC)
commandClass = data[20];
/// \cite RDM 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.
parameterId = readType<uint16_t>(data, 21);
/// \cite RDM 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];
/// \cite RDM 6.2.10.4 Parameter Data (PD)
/// The Parameter Data is of variable length.
for (int i = 0; i < pdl; i++)
appendData(data[24+i]);
/// \cite RDM 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 chksum = readType<uint16_t>(data, data.size() - 2);
if (chksum != checksum())
{
checksum_fail = true;
return;
}
}
/**
* @brief Message::write
* @param data
*/
void Message::write(std::vector<uint8_t> &data) const
{
if (commandClass == DISCOVERY_COMMAND_RESPONSE &&
parameterId == DISC_UNIQUE_BRANCH)
{
writeDiscBranch(data);
return;
}
data.push_back(SC_RDM);
data.push_back(SC_SUB_MESSAGE);
data.push_back(24 + length());
writeType(data, destination.manufacturer);
writeType(data, destination.device);
writeType(data, source.manufacturer);
writeType(data, source.device);
data.push_back(transaction);
data.push_back(portID);
data.push_back(messageCount);
writeType(data, subDevice);
data.push_back(commandClass);
writeType(data, parameterId);
data.push_back(length());
data.insert(data.end(), data_.begin(), data_.end());
writeType(data, checksum());
}
/**
* @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_)
sum = addSum_(sum, v);
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);
}
}
/**
* @brief Message::nak
* @param reason
*/
void Message::nak(uint16_t reason)
{
data_.clear();
responseType = RESPONSE_TYPE_NACK_REASON;
appendData(reason);
}
/**
* @brief Message::data
* @return
*/
const std::vector<uint8_t>* Message::data() const
{
return &data_;
}
/**
* @brief Message::length
* @return
*/
uint8_t Message::length() const
{
return data_.size();
}
/**
* @brief Message::checksum
* @return
*/
uint16_t Message::checksum() const
{
uint16_t sum = 0;
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);
sum = addSum_(sum, parameterId);
sum = addSum_(sum, length());
for (uint8_t val : data_)
sum = addSum_(sum, val);
return sum;
}
/**
* @brief Message::requiredLength
* @param length
* @param response
* @return
*/
bool Message::requiredLength(const size_t length, MsgPtr response) const
{
if (data_.size() != length)
{
response->nak(NR_FORMAT_ERROR);
return false;
}
return true;
}
} // namespace RDM