1
0
Fork 0

status collection PIDs

This commit is contained in:
Kevin Matz 2021-08-12 00:31:00 -04:00
parent a18501f607
commit 33476e0ceb
8 changed files with 447 additions and 37 deletions

View File

@ -63,6 +63,7 @@ set(SOURCE_FILES
rdm/responder.cpp
rdm/sensor.h
rdm/sensor.cpp
rdm/status.h
rdm/uid.h
rdmnet/rdmnet.h
sacn/data.cpp

View File

@ -36,9 +36,14 @@ Device::Device(Device* parent)
, deviceModelDescription("Basic RDM Device")
, deviceProductCategory(PRODUCT_CATEGORY_NOT_DECLARED)
, parent_(parent)
, status_reporting_threshold_(STATUS_ADVISORY)
{
id.manufacturer = MY_ESTA_MANUFACTURER_ID;
queued_statuses_.emplace(STATUS_ADVISORY, std::queue<StatusPtr>());
queued_statuses_.emplace(STATUS_WARNING, std::queue<StatusPtr>());
queued_statuses_.emplace(STATUS_ERROR, std::queue<StatusPtr>());
/// 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
@ -48,6 +53,26 @@ Device::Device(Device* parent)
&Device::actionGetSupportedParameters,
this, std::placeholders::_1,
std::placeholders::_2));
/// 10.3.4 Clear Status ID (CLEAR_STATUS_ID)
/// This parameter is used to clear the status message queue.
parameters_.try_emplace(CLEAR_STATUS_ID, new Parameter());
parameters_.at(SUPPORTED_PARAMETERS)->setAction(std::bind(
&Device::actionSetClearStatusId,
this, std::placeholders::_1,
std::placeholders::_2));
///10.3.5 Get/Set Sub-Device Status Reporting Threshold
/// (SUB_DEVICE_STATUS_REPORT_THRESHOLD)
/// This parameter is used to set the verbosity of Sub-Device reporting using
/// the Status Type codes as enumerated in Table A-4 .
parameters_.try_emplace(SUB_DEVICE_STATUS_REPORT_THRESHOLD, new Parameter());
parameters_.at(SUB_DEVICE_STATUS_REPORT_THRESHOLD)->getAction(std::bind(
&Device::actionGetSubdeviceThreshold,
this, std::placeholders::_1,
std::placeholders::_2));
parameters_.at(SUB_DEVICE_STATUS_REPORT_THRESHOLD)->setAction(std::bind(
&Device::actionSetSubdeviceThreshold,
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.
@ -284,6 +309,63 @@ void Device::set(const MsgPtr message, MsgPtr response)
}
/**
* @brief Device::enqueueMessage
* @param message
* @param urgent
*/
void Device::enqueueMessage(MsgPtr message, bool urgent)
{
/// only root devices have a message queue
if (parent_)
return;
message->destination = controller_uid_;
if (urgent)
queued_messages_.push_front(message);
else
queued_messages_.push_back(message);
}
/**
* @brief Device::enqueueStatus
* @param status
*/
void Device::enqueueStatus(StatusPtr status)
{
switch (status->status_type) {
case STATUS_ERROR:
if (status_reporting_threshold_ != STATUS_ADVISORY &&
status_reporting_threshold_ != STATUS_WARNING)
queued_statuses_.at(STATUS_ERROR).push(status);
break;
case STATUS_ERROR_CLEARED:
if (status_reporting_threshold_ != STATUS_ADVISORY &&
status_reporting_threshold_ != STATUS_WARNING)
queued_statuses_.at(STATUS_ERROR).push(status);
break;
case STATUS_WARNING:
if (status_reporting_threshold_ != STATUS_ADVISORY)
queued_statuses_.at(STATUS_WARNING).push(status);
break;
case STATUS_WARNING_CLEARED:
if (status_reporting_threshold_ != STATUS_ADVISORY)
queued_statuses_.at(STATUS_WARNING).push(status);
break;
case STATUS_ADVISORY:
queued_statuses_.at(STATUS_ADVISORY).push(status);
break;
case STATUS_ADVISORY_CLEARED:
queued_statuses_.at(STATUS_ADVISORY).push(status);
break;
default:
break;
}
}
/**
* @brief Device::actionPrep
* @return
@ -365,6 +447,76 @@ void Device::actionGetDeviceInfo(const MsgPtr message, MsgPtr response)
}
/**
* @brief Device::actionSetClearStatusId
* @param message
* @param response
*/
void Device::actionSetClearStatusId(const MsgPtr message, MsgPtr response)
{
if (!message->requiredLength(0, response))
return;
for (auto& [_, queue] : queued_statuses_)
while (!queue.empty())
queue.pop();
response->responseType = RESPONSE_TYPE_ACK;
}
/**
* @brief Device::actionGetSubdeviceThreshold
* @param message
* @param response
*/
void Device::actionGetSubdeviceThreshold(const MsgPtr message, MsgPtr response)
{
if (!message->requiredLength(0, response))
return;
response->responseType = RESPONSE_TYPE_ACK;
response->appendData(status_reporting_threshold_);
}
/**
* @brief Device::actionSetSubdeviceThreshold
* @param message
* @param response
*/
void Device::actionSetSubdeviceThreshold(const MsgPtr message, MsgPtr response)
{
if (!message->requiredLength(1, response))
return;
uint8_t threshold = message->data()->front();
switch (threshold) {
case STATUS_ERROR:
status_reporting_threshold_ = threshold;
while (!queued_statuses_.at(STATUS_WARNING).empty())
queued_statuses_.at(STATUS_WARNING).pop();
while (!queued_statuses_.at(STATUS_ADVISORY).empty())
queued_statuses_.at(STATUS_ADVISORY).pop();
break;
case STATUS_WARNING:
status_reporting_threshold_ = threshold;
while (!queued_statuses_.at(STATUS_ADVISORY).empty())
queued_statuses_.at(STATUS_ADVISORY).pop();
break;
case STATUS_ADVISORY:
status_reporting_threshold_ = threshold;
break;
default:
response->nak(NR_DATA_OUT_OF_RANGE);
return;
}
response->responseType = RESPONSE_TYPE_ACK;
}
/**
* @brief Device::actionGetProductDetailIdList
* @param message
@ -682,8 +834,9 @@ void Device::actionSetIdentifyDevice(const MsgPtr message, MsgPtr response)
if (!message->requiredLength(1, response))
return;
response->responseType = RESPONSE_TYPE_ACK;
identify(message->data()->front());
response->responseType = RESPONSE_TYPE_ACK;
}

View File

@ -27,9 +27,11 @@
#include "parameter.h"
#include "rdm.h"
#include "sensor.h"
#include "status.h"
#include "uid.h"
#include <list>
#include <queue>
#include <string>
#include <unordered_map>
@ -59,14 +61,25 @@ public:
uint16_t deviceProductCategory;
protected:
friend class Responder;
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_;
std::deque<MsgPtr> queued_messages_;
void enqueueMessage(MsgPtr message, bool urgent = false);
UID controller_uid_;
std::unordered_map<uint8_t, std::queue<StatusPtr>> queued_statuses_;
void enqueueStatus(StatusPtr status);
bool actionPrep_(const MsgPtr message, MsgPtr response);
void actionGetSupportedParameters (const MsgPtr message, MsgPtr response);
void actionSetClearStatusId (const MsgPtr message, MsgPtr response);
void actionGetSubdeviceThreshold (const MsgPtr message, MsgPtr response);
void actionSetSubdeviceThreshold (const MsgPtr message, MsgPtr response);
void actionGetDeviceInfo (const MsgPtr message, MsgPtr response);
void actionGetProductDetailIdList (const MsgPtr message, MsgPtr response);
void actionGetDevModelDescription (const MsgPtr message, MsgPtr response);
@ -88,6 +101,7 @@ private:
Device* parent_;
PID last_rx_pid_ = 0;
uint ack_overflow_page = 0;
uint8_t status_reporting_threshold_;
bool identifying_ = false;
};

View File

@ -404,4 +404,66 @@ std::string NackReasonDescription(const uint16_t NR)
}
}
/**
* @brief StatusMessageDescription
* @param status
* @return
*/
std::string StatusMessageDescription(uint16_t status)
{
switch (status) {
case STS_CAL_FAIL:
return "%L failed calibration";
case STS_SENS_NOT_FOUND:
return "%L sensor not found";
case STS_SENS_ALWAYS_ON:
return "%L sensor always on";
case STS_LAMP_DOUSED:
return "Lamp doused";
case STS_LAMP_STRIKE:
return "Lamp failed to strike";
case STS_OVERTEMP:
return "Sensor %d over temp at %d degrees C";
case STS_UNDERTEMP:
return "Sensor %d under temp at %d degrees C";
case STS_SENS_OUT_RANGE:
return "Sensor %d out of range";
case STS_OVERVOLTAGE_PHASE:
return "Phase %d over voltage at %d V.";
case STS_UNDERVOLTAGE_PHASE:
return "Phase %d under voltage at %d V.";
case STS_OVERCURRENT:
return "Phase %d over current at %d A.";
case STS_UNDERCURRENT:
return "Phase %d under current at %d A.";
case STS_PHASE:
return "Phase %d is at %d degrees";
case STS_PHASE_ERROR:
return "Phase %d Error.";
case STS_AMPS:
return "%d Amps";
case STS_VOLTS:
return "%d Volts";
case STS_DIMSLOT_OCCUPIED:
return "No Dimmer";
case STS_BREAKER_TRIP:
return "Tripped Breaker";
case STS_WATTS:
return "%d Watts";
case STS_DIM_FAILURE:
return "Dimmer Failure";
case STS_DIM_PANIC:
return "Panic Mode";
case STS_READY:
return "%L ready";
case STS_NOT_READY:
return "%L not ready";
case STS_LOW_FLUID:
return "%L low fluid";
default:
return std::string();
}
}
} // namespace RDM

View File

@ -461,7 +461,7 @@ using namespace DMX;
static const uint16_t NR_SUB_DEVICE_OUT_OF_RANGE = 0x0009;
static const uint16_t NR_PROXY_BUFFER_FULL = 0x000A;
std::string StatusMessageDescription(const uint16_t status);
/// Appendix B: Status Message IDs (Normative)
/// %d Decimal Number -- as decimal number
/// %x Hexadecimal Number -- as hexadecimal number

View File

@ -39,14 +39,6 @@ Responder::Responder()
deviceModelDescription = "Basic RDM Responder";
subdevice_flag = true;
queued_statuses_.emplace(STATUS_GET_LAST_MESSAGE, std::vector<MsgPtr>());
queued_statuses_.emplace(STATUS_ADVISORY, std::vector<MsgPtr>());
queued_statuses_.emplace(STATUS_WARNING, std::vector<MsgPtr>());
queued_statuses_.emplace(STATUS_ERROR, std::vector<MsgPtr>());
queued_statuses_.emplace(STATUS_ADVISORY_CLEARED, std::vector<MsgPtr>());
queued_statuses_.emplace(STATUS_WARNING_CLEARED, std::vector<MsgPtr>());
queued_statuses_.emplace(STATUS_ERROR_CLEARED, std::vector<MsgPtr>());
/// 7.5 Discovery Unique Branch Message (DISC_UNIQUE_BRANCH)
parameters_.try_emplace(DISC_UNIQUE_BRANCH, new Parameter());
parameters_.at(DISC_UNIQUE_BRANCH)->discAction(std::bind(
@ -81,13 +73,32 @@ Responder::Responder()
&Responder::actionSetCommsStatus,
this, std::placeholders::_1,
std::placeholders::_2));
// Category - Status Collection
// QUEUED_MESSAGE
// STATUS_MESSAGES
// STATUS_ID_DESCRIPTION
// CLEAR_STATUS_ID
// SUB_DEVICE_STATUS_REPORT_THRESHOLD
/// 10.3.1 Get Queued Message (QUEUED_MESSAGE)
/// The QUEUED_MESSAGE parameter shall be used to retrieve a message from the
/// responders message queue. The Message Count field of all response
/// messages defines the number of messages that are queued in the responder.
/// Each QUEUED_MESSAGE response shall be composed of a single message response.
parameters_.try_emplace(QUEUED_MESSAGE, new Parameter());
parameters_.at(QUEUED_MESSAGE)->getAction(std::bind(
&Responder::actionGetQueuedMessage,
this, std::placeholders::_1,
std::placeholders::_2));
/// 10.3.2 Get Status Messages (STATUS_MESSAGES)
/// This parameter is used to collect Status or Error information
/// from a device.
parameters_.try_emplace(STATUS_MESSAGES, new Parameter());
parameters_.at(STATUS_MESSAGES)->getAction(std::bind(
&Responder::actionGetStatusMessages,
this, std::placeholders::_1,
std::placeholders::_2));
/// 10.3.3 Get Status ID Description (STATUS_ID_DESCRIPTION)
/// This parameter is used to request an ASCII text description of a given
/// Status ID. The description may be up to 32 characters.
parameters_.try_emplace(STATUS_ID_DESCRIPTION, new Parameter());
parameters_.at(STATUS_ID_DESCRIPTION)->getAction(std::bind(
&Responder::actionGetStatusIdDescription,
this, std::placeholders::_1,
std::placeholders::_2));
}
@ -194,6 +205,13 @@ void Responder::receive(const MsgPtr message)
response->propertyID = message->propertyID;
response->transaction = message->transaction;
/// 5.3 Broadcast Message Addressing
/// When Broadcast Addressing is used for non-Discovery messages, the
/// responders shall not send a response.
if (message->destination.isBroadcast() &&
message->commandClass != DISCOVERY_COMMAND)
response->do_no_send = true;
switch (message->commandClass) {
case DISCOVERY_COMMAND:
response->commandClass = DISCOVERY_COMMAND_RESPONSE;
@ -214,13 +232,6 @@ void Responder::receive(const MsgPtr message)
if (!response)
return;
/// 5.3 Broadcast Message Addressing
/// When Broadcast Addressing is used for non-Discovery messages, the
/// responders shall not send a response.
if (message->destination.isBroadcast() &&
message->commandClass != DISCOVERY_COMMAND)
return;
if (response->do_no_send)
return;
@ -306,17 +317,16 @@ void Responder::rxSet(const MsgPtr message, MsgPtr response)
return;
}
if (sub_devices_.empty())
{
response->nak(NR_SUB_DEVICE_OUT_OF_RANGE);
return;
}
if (message->subDevice == SUB_DEVICE_ALL_CALL)
{
for (auto& [num, dev] : sub_devices_)
{
auto rsp = MsgPtr(new Message());
rsp->subDevice = num;
dev->set(message, rsp);
if (!message->destination.isBroadcast())
send(rsp);
}
response->do_no_send = true;
dev->set(message, response);
return;
}
@ -379,6 +389,7 @@ void Responder::actionDiscoveryMute(const MsgPtr message, MsgPtr response)
return;
discovery_mute_flag_ = true;
controller_uid_ = message->source;
response->responseType = RESPONSE_TYPE_ACK;
response->appendData(control_field);
@ -436,4 +447,125 @@ void Responder::actionSetCommsStatus(const MsgPtr message, MsgPtr response)
response->responseType = RESPONSE_TYPE_ACK;
}
/**
* @brief Responder::actionGetQueuedMessage
* @param message
* @param response
*/
void Responder::actionGetQueuedMessage(const MsgPtr message, MsgPtr response)
{
if (!message->requiredLength(1, response))
return;
if (queued_messages_.empty())
{
response->propertyID = STATUS_MESSAGES;
actionGetStatusMessages(message, response);
return;
}
response->do_no_send = true;
auto msg = queued_messages_.front();
queued_messages_.pop_front();
last_status_message_ = msg;
send(msg);
}
/**
* @brief Responder::actionGetStatusMessages
* @param message
* @param response
*/
void Responder::actionGetStatusMessages(const MsgPtr message, MsgPtr response)
{
if (!message->requiredLength(1, response))
return;
uint8_t type = message->data()->front();
if (type != STATUS_GET_LAST_MESSAGE &&
type != STATUS_ERROR &&
type != STATUS_WARNING &&
type != STATUS_ADVISORY)
{
response->nak(NR_DATA_OUT_OF_RANGE);
return;
}
if (type == STATUS_GET_LAST_MESSAGE)
{
response->do_no_send = true;
send(last_status_message_);
return;
}
int counter = 0;
auto reportStatusQueue = [response, counter] (std::queue<StatusPtr> q) mutable
{
while(!q.empty() && counter < 25)
{
for (uint8_t& b : q.front()->bytes)
response->appendData(b);
counter++;
q.pop();
}
};
if (type == STATUS_ERROR ||
type == STATUS_WARNING ||
type == STATUS_ADVISORY)
{
reportStatusQueue(queued_statuses_.at(STATUS_ERROR));
for (auto& [_, dev] : sub_devices_)
reportStatusQueue(dev->queued_statuses_.at(STATUS_ERROR));
}
if (type == STATUS_WARNING ||
type == STATUS_ADVISORY)
{
reportStatusQueue(queued_statuses_.at(STATUS_WARNING));
for (auto& [_, dev] : sub_devices_)
reportStatusQueue(dev->queued_statuses_.at(STATUS_WARNING));
}
if (type == STATUS_ADVISORY)
{
reportStatusQueue(queued_statuses_.at(STATUS_ADVISORY));
for (auto& [_, dev] : sub_devices_)
reportStatusQueue(dev->queued_statuses_.at(STATUS_ADVISORY));
}
if (counter == 25)
response->responseType = RESPONSE_TYPE_ACK_OVERFLOW;
else
response->responseType = RESPONSE_TYPE_ACK;
last_status_message_ = response;
}
/**
* @brief Responder::actionGetStatusIdDescription
* @param message
* @param response
*/
void Responder::actionGetStatusIdDescription(const MsgPtr message, MsgPtr response)
{
if (!message->requiredLength(2, response))
return;
uint16_t status = message->readType<uint16_t>(*message->data(), 0);
response->responseType = RESPONSE_TYPE_ACK;
std::string label = RDM::StatusMessageDescription(status);
for (size_t i = 0; i < label.size(); i++)
{
if (i > 32)
break;
response->appendData(label.at(i));
}
}
} // namespace RDM

View File

@ -25,10 +25,8 @@
#include "device.h"
#include "message.h"
#include "uid.h"
#include <unordered_map>
#include <queue>
#include <deque>
namespace RDM {
@ -66,14 +64,16 @@ protected:
void actionDiscoveryUnmute (const MsgPtr message, MsgPtr response);
void actionGetCommsStatus (const MsgPtr message, MsgPtr response);
void actionSetCommsStatus (const MsgPtr message, MsgPtr response);
std::unordered_map<uint8_t, std::queue<MsgPtr>> queued_statuses_;
void actionGetQueuedMessage (const MsgPtr message, MsgPtr response);
void actionGetStatusMessages (const MsgPtr message, MsgPtr response);
void actionGetStatusIdDescription (const MsgPtr message, MsgPtr response);
private:
bool discovery_mute_flag_ = false;
uint16_t short_message_counter_ = 0;
uint16_t length_mismatch_counter_ = 0;
uint16_t checksum_fail_counter_ = 0;
MsgPtr last_status_message_;
};

48
rdm/status.h Normal file
View File

@ -0,0 +1,48 @@
/*
status.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 <memory>
namespace RDM {
struct Status
{
union {
uint8_t bytes[9];
struct __attribute__ ((packed)) {
uint16_t subdevice_id;
uint8_t status_type;
uint16_t status_message;
int16_t data1;
int16_t data2;
};
};
}; // struct Status
using StatusPtr = std::shared_ptr<Status>;
} // namespace RDM