1
0
Fork 0

LLRP wire format

This commit is contained in:
Kevin Matz 2021-08-13 12:03:11 -04:00
parent 2c304859b9
commit fa13e03ffa
4 changed files with 310 additions and 5 deletions

View File

@ -23,10 +23,44 @@
*/
#include "llrp.h"
#include "rdmnet.h"
namespace RDMnet {
namespace LLRP {
/**
* @brief llrp_data::iStream
*/
void llrp_data::iStream(ACN::PDU::Stream stream)
{
/// Destination CID - The Destination CID indicates the CID of the intended
/// recipient of this PDU.
uint8_t * buffer = new uint8_t[UUID_LENGTH];
stream->read(buffer, UUID_LENGTH);
if (stream->gcount() != UUID_LENGTH)
stream->setstate(std::ios_base::failbit);
destination = UUID::uuid(buffer);
delete[] buffer;
/// Transaction Number - The Transaction Number allows an LLRP Manager to
/// associate reply messages with requests.
*stream >> transaction;
}
/**
* @brief llrp_data::oStream
* @param stream
*/
void llrp_data::oStream(ACN::PDU::Stream stream) const
{
stream->write(destination.bytes(), UUID_LENGTH);
*stream << transaction;
if (child)
child->oStream(stream);
}
/**
* @brief LLRP::Pdu::Pdu
*/
@ -40,13 +74,95 @@ Pdu::Pdu()
* @brief LLRP::Pdu::iStream
* @param stream
*/
void Pdu::iStream([[maybe_unused]] ACN::PDU::Stream stream)
void Pdu::iStream(ACN::PDU::Stream stream)
{
RDMnet::Pdu::iStream(stream); // do base class first
if (stream->fail()) return;
if (!stream_->good()) return;
/// has no header
auto payload = new llrp_data();
payload->iStream(stream_);
data_ = payload;
switch (vector_) {
case VECTOR_LLRP_PROBE_REQUEST:
{
auto block = ACN::PDU::Block<ProbeRequest::Pdu>();
block.readBlock(stream_, std::shared_ptr<ACN::PDU::Pdu>(this));
if (!block.pdu->empty())
payload->child = block.pdu->front();
}
break;
case VECTOR_LLRP_PROBE_REPLY:
{
auto block = ACN::PDU::Block<ProbeReply::Pdu>();
block.readBlock(stream_, std::shared_ptr<ACN::PDU::Pdu>(this));
if (!block.pdu->empty())
payload->child = block.pdu->front();
}
break;
case VECTOR_LLRP_RDM_CMD:
{
auto block = ACN::PDU::Block<RdmCmd::Pdu>();
block.readBlock(stream_, std::shared_ptr<ACN::PDU::Pdu>(this));
if (!block.pdu->empty())
payload->child = block.pdu->front();
}
break;
default:
stream_->setstate(std::ios_base::failbit);
return;
}
if (!payload->child)
stream_->setstate(std::ios_base::failbit);
}
namespace ProbeRequest {
/**
* @brief request_data::iStream
* @param stream
*/
void request_data::iStream(ACN::PDU::Stream stream)
{
auto readUID = [stream] (RDM::UID& uid) {
*stream >> uid.device;
*stream >> uid.manufacturer;
};
readUID(lower);
readUID(upper);
*stream >> filter.value;
while (stream->good()) {
known.emplace_back(RDM::UID());
readUID(known.back());
}
}
/**
* @brief request_data::oStream
* @param stream
*/
void request_data::oStream(ACN::PDU::Stream stream) const
{
auto writeUID = [stream] (const RDM::UID& uid) {
*stream << uid.device;
*stream << uid.manufacturer;
};
writeUID(lower);
writeUID(upper);
*stream << filter.value;
for ( auto& id : known )
writeUID(id);
}
/**
* @brief LLRP::ProbeRequest::Pdu::Pdu
*/
@ -60,8 +176,16 @@ Pdu::Pdu()
* @brief LLRP::ProbeRequest::Pdu::iStream
* @param stream
*/
void Pdu::iStream([[maybe_unused]] ACN::PDU::Stream stream)
void Pdu::iStream(ACN::PDU::Stream stream)
{
RDMnet::Pdu::iStream(stream); // do base class first
if (stream->fail()) return;
if (!stream_->good()) return;
/// LLRP::PDU has no header
data_ = new request_data();
data_->iStream(stream_);
}
} // namespace ProbeRequest
@ -69,6 +193,31 @@ void Pdu::iStream([[maybe_unused]] ACN::PDU::Stream stream)
namespace ProbeReply {
/**
* @brief reply_data::iStream
*/
void reply_data::iStream(ACN::PDU::Stream stream)
{
*stream >> id.device;
*stream >> id.manufacturer;
stream->read(address, sizeof(address));
if (stream->gcount() != sizeof(address))
stream->setstate(std::ios_base::failbit);
*stream >> type;
}
/**
* @brief reply_data::oStream
*/
void reply_data::oStream(ACN::PDU::Stream stream) const
{
*stream << id.device;
*stream << id.manufacturer;
stream->write(address, sizeof(address));
*stream << type;
}
/**
* @brief LLRP::ProbeReply::Pdu::Pdu
*/
@ -82,12 +231,71 @@ Pdu::Pdu()
* @brief LLRP::ProbeReply::Pdu::iStream
* @param stream
*/
void Pdu::iStream([[maybe_unused]] ACN::PDU::Stream stream)
void Pdu::iStream(ACN::PDU::Stream stream)
{
RDMnet::Pdu::iStream(stream); // do base class first
if (stream->fail()) return;
if (!stream_->good()) return;
/// has no header
data_ = new reply_data();
data_->iStream(stream_);
}
} // namespace ProbeReply
namespace RdmCmd {
/**
* @brief rdm_data::iStream
*/
void rdm_data::iStream(ACN::PDU::Stream stream)
{
data.push_back(VECTOR_RDM_CMD_RDM_DATA);
while (stream->good())
data.push_back(stream->readType<uint8_t>());
}
/**
* @brief rdm_data::oStream
*/
void rdm_data::oStream(ACN::PDU::Stream stream) const
{
for (size_t i = 1; i < data.size(); i++ )
*stream << data.at(i);
}
/**
* @brief LLRP::RdmCmd::Pdu::Pdu
*/
Pdu::Pdu()
: RDMnet::Pdu(1)
{
}
/**
* @brief LLRP::ProbeReply::Pdu::iStream
* @param stream
*/
void Pdu::iStream(ACN::PDU::Stream stream)
{
RDMnet::Pdu::iStream(stream); // do base class first
if (stream->fail()) return;
if (!stream_->good()) return;
/// has no header
data_ = new rdm_data();
data_->iStream(stream_);
}
} // namespace RdmCmd
} // namespace LLRP
} // namespace RDMnet

View File

@ -24,6 +24,8 @@
#pragma once
#include "pdu.h"
#include "rdm/uid.h"
#include "uuid/uuid.h"
namespace RDMnet {
namespace LLRP {
@ -38,6 +40,20 @@ namespace LLRP {
* large network.
*/
/// 5.4 Packet Structure
struct llrp_data
: public ACN::PDU::pdu_data
{
UUID::uuid destination; //!< The receiver's unique CID or LLRP_BROADCAST_CID,
uint32_t transaction; //!< Used to match request / response messages.
std::shared_ptr<ACN::PDU::Pdu> child; //!< Probe Request PDU, Probe Reply PDU or RDM Command PDU...
size_t streamSize() const override { return 20 + child->streamSize(); }
void iStream(ACN::PDU::Stream) override;
void oStream(ACN::PDU::Stream) const override;
};
/**
* @brief The LLRP::Pdu class
*/
@ -52,6 +68,40 @@ public:
namespace ProbeRequest {
/**
* @brief The filter_t struct
* This bit field indicates a filter to be checked against the state of an
* LLRP Target.
*/
struct filter_t {
union {
uint16_t value = 0;
struct __attribute__((packed)) {
bool client_tcp_inactive : 1;
bool brokers_only : 1;
uint16_t reserved : 14;
};
};
};
/**
* @brief The request_data struct
* Table 5-2: Probe Request PDU
*/
struct request_data
: public ACN::PDU::pdu_data
{
RDM::UID lower;
RDM::UID upper;
filter_t filter;
std::vector<RDM::UID> known;
size_t streamSize() const override { return 14 + (6 * known.size()); }
void iStream(ACN::PDU::Stream) override;
void oStream(ACN::PDU::Stream) const override;
};
/**
* @brief The LLRP::ProbeRequest::Pdu class
*/
@ -67,6 +117,18 @@ public:
namespace ProbeReply {
struct reply_data
: public ACN::PDU::pdu_data
{
RDM::UID id;
uint8_t address[6];
uint8_t type;
size_t streamSize() const override { return 13; }
void iStream(ACN::PDU::Stream) override;
void oStream(ACN::PDU::Stream) const override;
};
/**
* @brief The LLRP::ProbeReply::Pdu class
*/
@ -80,7 +142,29 @@ public:
} // namespace ProbeReply
namespace RdmCmd {
struct rdm_data
: public ACN::PDU::pdu_data
{
std::vector<uint8_t> data;
size_t streamSize() const override { return (data.size() > 0) ? data.size() - 1 : 0; }
void iStream(ACN::PDU::Stream) override;
void oStream(ACN::PDU::Stream) const override;
};
/**
* @brief The LLRP::RdmCmd::Pdu class
*/
class Pdu
: public RDMnet::Pdu
{
public:
Pdu();
void iStream(ACN::PDU::Stream) override;
};
} // namespace RdmCmd
} // namespace LLRP

View File

@ -30,13 +30,19 @@ namespace RDMnet {
namespace LLRP {
/**
* @brief The LLRP::Manager class
* @brief 5.1.1 LLRP Manager
* LLRP Managers issue LLRP discovery probes and issue configuration change
* commands. LLRP Managers might be found on lighting consoles or other
* configuration tool equipment.
*/
class Manager
{
public:
Manager();
virtual ~Manager();
virtual void sendProbeRequest() {};
virtual void receiveProbeReply() {};
};
} // namespace LLRP

View File

@ -30,13 +30,20 @@ namespace RDMnet {
namespace LLRP {
/**
* @brief The LLRP::Target class
* @brief 5.1.2 LLRP Target
* LLRP Targets receive and act on LLRP discovery and configuration commands.
* LLRP Targets enable remote administration of network configuration. All
* Brokers, Controllers, and Devices also operate as LLRP Targets.
*/
class Target
{
public:
Target();
virtual ~Target();
virtual void setup() = 0; //!< listens on LLRP_PORT
virtual void receiveProbeRequest() {};
virtual void sendProbeReply() {};
};
} // namespace LLRP