OpenLCP/rdm/responder.cpp

420 lines
12 KiB
C++
Raw Normal View History

2021-08-08 17:25:41 -04:00
/*
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()
{
2021-08-09 15:04:49 -04:00
deviceModelID = 1;
deviceModelDescription = "Basic RDM Responder";
2021-08-08 17:25:41 -04:00
// E1.20 required parameters
2021-08-09 15:04:49 -04:00
// DISC_UNIQUE_BRANCH
// DISC_MUTE
// DISC_UN_MUTE
// addt'l parameters
// COMMS_STATUS
// QUEUED_MESSAGE
2021-08-08 17:25:41 -04:00
}
/**
* @brief Responder::~Responder
*/
Responder::~Responder()
{
}
/**
* @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);
2021-08-10 01:24:52 -04:00
// RDM::UID::operator== also returns true for broadcast messages
if (message.destination != id)
return;
2021-08-08 17:25:41 -04:00
// 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)
{
2021-08-10 00:57:09 -04:00
if (!message)
{
if (queued_messages_.empty())
return;
message = queued_messages_.front();
queued_messages_.pop();
}
2021-08-08 17:25:41 -04:00
// 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();
2021-08-09 15:04:49 -04:00
response->source = id;
2021-08-08 17:25:41 -04:00
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