improved ACN protocol layer separation and robustness

This commit is contained in:
Kevin Matz 2021-01-08 02:22:41 -05:00
parent 43c0e61465
commit 796b8a5993
17 changed files with 333 additions and 205 deletions

View File

@ -35,59 +35,81 @@ address_type::address_type(uint8_t val) {
width = (address_length)(val & 0b11);
}
range::range(PDU::stream_ptr stream, data_type type, address_length length) {
address = read(stream, length);
if (type == SINGLE)
return;
incriment = read(stream, length);
count = read(stream, length);
range::range(PDU::stream_ptr stream, data_type t, address_length l) {
address = read(stream, l);
if (t == SINGLE) return;
incriment = read(stream, l);
count = read(stream, l);
}
uint32_t range::read(PDU::stream_ptr stream, address_length length) {
switch (length) {
case ONE:
return PDU::read8(stream);
return stream->read8();
case TWO:
return PDU::read16(stream);
return stream->read16();
case FOUR:
return PDU::read32(stream);
return stream->read32();
default:
return 0;
}
}
void readHeader(PDU::pdu_ptr pdu) {
if (pdu->flags().hasHeader) {
address_type * header = new address_type(pdu->buffer()->get());
pdu->setHeader(header);
Pdu::Pdu(PDU::stream_ptr stream)
: ACN::PDU::Pdu(stream, 1) // vectors are 1 octet
{
if (stream->fail()) return;
if (!buffer_->good()) return;
if (flags_.hasHeader)
setHeader(new address_type(buffer_->read8()));
if (flags_.hasData) {
switch(vector()) {
case SET_PROPERTY:
readSetData();
break;
default:
break;
}
}
}
void readSet(PDU::pdu_ptr pdu) {
const address_type *header = static_cast<const address_type*>(pdu->header());
void Pdu::readSetData() {
// header may be inherited. check that one exists
if (!header()) {
buffer_->setstate(buffer_->rdstate() | std::ios_base::failbit);
return;
}
PDU::stream_ptr stream = pdu->buffer();
auto header = (address_type*)this->header();
auto data = new dmp_set_data();
dmp_set_data * data = new dmp_set_data();
while(stream->good()) {
while(buffer_->good()) {
// Property Address
range pr(stream, header->type, header->width);
range pr(buffer_, header->type, header->width);
if (!buffer_->good() || pr.count * pr.incriment > buffer_->available()) {
buffer_->setstate(buffer_->rdstate() | std::ios_base::failbit);
break;
}
// Property Data
std::vector<uint8_t> pd;
pd.resize(pr.address, 0);
for (uint32_t i = 0; i < pr.count * pr.incriment; i ++)
pd.push_back(stream->get());
pd.push_back(buffer_->read8());
// Property Fields
set_property fields(pr, pd);
data->properties.push_back(fields);
// set EOF if buffer is drained
if (stream->available() == 0)
stream->setstate(stream->rdstate() | std::ios_base::eofbit);
//set EOF if buffer insufficient for another property range
if (buffer_->available() < header->width * (header->type == SINGLE? 1 : 3))
buffer_->setstate(buffer_->rdstate() | std::ios_base::eofbit);
}
pdu->setData(data);
setData(data);
}
} // DMP

View File

@ -23,7 +23,7 @@
*/
#pragma once
#include <stdint.h>
#include <cstdint>
#include <vector>
#include "pdu.h"
@ -100,11 +100,16 @@ static const uint8_t SUBSCRIBE_ACCEPT = 12;
static const uint8_t SUBSCRIBE_REJECT = 13;
static const uint8_t SYNC_EVENT = 17;
void readHeader(PDU::pdu_ptr);
void readSet(PDU::pdu_ptr);
inline PDU::block_ptr readBlock(PDU::stream_ptr s, uint8_t vl = 1) {
return PDU::readBlock(s, vl);
}
class Pdu
: public ACN::PDU::Pdu
{
public:
Pdu(PDU::stream_ptr);
private:
void readSetData();
};
typedef std::shared_ptr<Pdu> pdu_ptr;
} // DMP
} // ACN

View File

@ -23,18 +23,11 @@
*/
#include "pdu.h"
#include <cmath>
namespace ACN {
namespace PDU {
pdu_flags::pdu_flags(uint8_t val) {
hasLength = (val >> 7) & 0b1;
hasVector = (val >> 6) & 0b1;
hasHeader = (val >> 5) & 0b1;
hasData = (val >> 4) & 0b1;
};
Pdu::Pdu(stream_ptr stream, size_t vector_size)
: flags_(stream->peek())
{
@ -44,30 +37,49 @@ Pdu::Pdu(stream_ptr stream, size_t vector_size)
inherit_ = 0; // pointer to previous PDU in block
// read length and vector off of the stream
length_ = readLength(stream, flags_.hasLength);
if (flags_.hasVector)
vector_ = readVector(stream, vector_size);
// ESP_LOGD(TAG, "length: %d vector: %d", length_, vector_);
// abort if the remaining PDU length isn't available
if (stream->available() < (flags_.hasLength ? 3 : 2)) {
stream->setstate(stream->rdstate() | std::ios_base::failbit);
return;
}
readLength(stream);
if (!length_ || length_ > std::pow(2, (8 * (flags_.hasLength ? 3 : 2)) - 4)) {
stream->setstate(stream->rdstate() | std::ios_base::failbit);
return;
}
// length includes the flags, length, and vector
// calculate the remaining lenght of the PDU
int len = length_;
len -= flags_.hasLength ? 3 : 2;
len -= vector_size;
// calculate the remaining length of the PDU
int len = length_ - (flags_.hasLength ? 3 : 2);
if (len < (int)vector_size) {
stream->setstate(stream->rdstate() | std::ios_base::failbit);
return;
}
// abort if the remaining PDU length isn't available
if (!stream->good() || len > stream->available()) {
if (!stream->good() || stream->available() < len) {
stream->setstate(stream->rdstate() | std::ios_base::failbit);
return;
}
// create a stream buffer for the header and data
uint8_t buf[len];
stream->read(buf, len);
buffer_ = stream_ptr(new pdu_stream(buf, len));
buffer_ = stream_ptr(new pdu_stream(stream->cur_ptr(), len));
if (buffer_->available() != len) {
stream->setstate(stream->rdstate() | std::ios_base::failbit);
return;
}
// fast-forward the input stream
for (int i = 0; i < len; i++)
stream->get();
if (!stream->available())
stream->setstate(stream->rdstate() | std::ios_base::eofbit);
if (flags_.hasVector)
readVector(vector_size);
}
Pdu::~Pdu() {
if (header_) delete header_;
if (data_) delete data_;
@ -88,7 +100,7 @@ pdu_header * Pdu::header() {
return header_;
if (inherit_ != nullptr)
return inherit_->header();
return nullptr;
return 0;
}
@ -97,66 +109,71 @@ pdu_data * Pdu::data() {
return data_;
if (inherit_ != nullptr)
return inherit_->data();
return nullptr;
return 0;
}
block_ptr readBlock(stream_ptr stream, uint8_t vector_size) {
block_ptr block(new pdu_block());
while(stream->good()) {
// create a PDU off of the stream
pdu_ptr pdu(new Pdu(stream, vector_size));
if (stream->fail()) break; // OK if eofbit is set
void Pdu::readLength(stream_ptr stream) {
length_ = stream->read16() & 0x0fff; // high 4 bytes are flags
if (flags_.hasLength)
length_ = (length_ << 8 ) | stream->read8();
}
// set inheritor pointer
if (!block->empty()) pdu->setInherit(block->back());
// add the PDU to the block list
block->push_back(pdu);
void Pdu::readVector(uint8_t vector_size) {
vector_ = 0;
for (int o = vector_size - 1; o >= 0; o--)
vector_ |= (buffer_->read8() << (8 * o));
}
// set EOF if buffer is drained
if (stream->available() == 0)
stream->setstate(stream->rdstate() | std::ios_base::eofbit);
pdu_flags::pdu_flags(uint8_t val) {
hasLength = (val >> 7) & 0b1;
hasVector = (val >> 6) & 0b1;
hasHeader = (val >> 5) & 0b1;
hasData = (val >> 4) & 0b1;
};
uint8_t pdu_stream::read8() {
uint8_t ret = 0;
if (available() < sizeof(ret)) {
setstate(rdstate() | std::ios_base::failbit);
return 0;
}
return block;
}
uint32_t readLength(stream_ptr stream, bool hasLength) {
uint32_t length = 0;
length |= (stream->get() & 0x0f) << 16;
length |= stream->get() << 8;
if (hasLength)
length |= stream->get();
else
length = length >> 8;
return length;
}
uint32_t readVector(stream_ptr stream, uint8_t vector_size) {
uint32_t vect = 0;
for (int o = vector_size - 1; o >= 0; o--) {
vect |= (stream->get() << (8 * o));
}
return vect;
}
uint8_t read8(stream_ptr stream) {
return stream->get();
}
uint16_t read16(stream_ptr stream) {
uint16_t ret = 0;
ret |= stream->get() << 8;
ret |= stream->get();
ret = get();
if (!available())
setstate(rdstate() | std::ios_base::eofbit);
return ret;
}
uint32_t read32(stream_ptr stream) {
uint16_t pdu_stream::read16() {
uint16_t ret = 0;
if (available() < sizeof(ret)) {
setstate(rdstate() | std::ios_base::failbit);
return 0;
}
ret |= get() << 8;
ret |= get();
if (!available())
setstate(rdstate() | std::ios_base::eofbit);
return ret;
}
uint32_t pdu_stream::read32() {
uint32_t ret = 0;
ret |= stream->get() << 24;
ret |= stream->get() << 16;
ret |= stream->get() << 6;
ret |= stream->get();
if (available() < sizeof(ret)) {
setstate(rdstate() | std::ios_base::failbit);
return 0;
}
ret |= get() << 24;
ret |= get() << 16;
ret |= get() << 6;
ret |= get();
if (!available())
setstate(rdstate() | std::ios_base::eofbit);
return ret;
}

View File

@ -23,10 +23,10 @@
*/
#pragma once
#include <stdint.h>
#include <vector>
#include <cstdint>
#include <istream>
#include <memory>
#include <vector>
namespace ACN {
namespace PDU {
@ -53,16 +53,20 @@ typedef std::shared_ptr<pdu_block> block_ptr;
struct pdu_header { virtual ~pdu_header() {} };
struct pdu_data { virtual ~pdu_data() {} };
/*
pdu_buffer and pdu_stream hold the raw data of the packet
/**
Memory buffer of uint8_t data.
*/
class pdu_buffer
: public std::basic_streambuf<uint8_t>
{
public:
pdu_buffer(uint8_t * p, size_t l) { setg(p, p, p + l); }
pdu_buffer(uint8_t * p, size_t l) { setg(p, p, p + l); };
uint8_t * cur_ptr() { return gptr(); };
};
/**
Input stream of nested PDU
*/
class pdu_stream
: public std::basic_istream<uint8_t>
{
@ -71,10 +75,26 @@ public:
: std::basic_istream<uint8_t>(&_buffer)
, _buffer(p, l) { rdbuf(&_buffer); }
std::streamsize available() { return _buffer.in_avail(); }
uint8_t * cur_ptr() { return _buffer.cur_ptr(); };
uint8_t read8 ();
uint16_t read16();
uint32_t read32();
private:
pdu_buffer _buffer;
};
/**
Base class PDU
All PDU share common structure of:
flags, length, vector,
and protocol specific header/data.
Flag values indicate if lenght, vector, header or data
are present in the PDU, or if they should be inherited from the
preceding PDU.
*/
class Pdu {
public:
Pdu(stream_ptr, size_t vector_size);
@ -90,30 +110,53 @@ public:
stream_ptr buffer() {return buffer_;}
// setters
void setHeader (pdu_header * h) {header_ = h;}
void setData (pdu_data * d) {data_ = d;}
void setParent (pdu_ptr pdu) {parent_ = pdu;}
void setInherit(pdu_ptr pdu) {inherit_ = pdu;}
void setParent (pdu_ptr pdu) {parent_ = pdu;}
void setInherit(pdu_ptr pdu) {inherit_ = pdu;}
private:
protected:
pdu_flags flags_;
uint32_t length_;
uint32_t vector_;
pdu_header * header_;
pdu_data * data_;
pdu_ptr parent_;
pdu_ptr inherit_;
stream_ptr buffer_;
// private setters
void setHeader (pdu_header * h) {header_ = h;}
void setData (pdu_data * d) {data_ = d;}
private:
pdu_header * header_;
pdu_data * data_;
void readLength(stream_ptr);
void readVector(uint8_t);
};
// utility functions to constuct the PDU from input stream
block_ptr readBlock (stream_ptr, uint8_t vector_length = 4);
uint32_t readLength(stream_ptr, bool hasLength);
uint32_t readVector(stream_ptr, uint8_t);
uint8_t read8 (stream_ptr);
uint16_t read16(stream_ptr);
uint32_t read32(stream_ptr);
/**
Template creator of a PDU Block.
@param std::shared_ptr<PDU::pdu_stream> The stream to read from.
@return std::shared_ptr<std::vector<std::shared_ptr<T>>> A block of PDU
*/
template<typename T>
std::shared_ptr<std::vector<std::shared_ptr<T>>> readBlock(stream_ptr buffer) {
auto block = std::shared_ptr<std::vector<std::shared_ptr<T>>>
(new std::vector<std::shared_ptr<T>>);
while(buffer->good()) {
std::shared_ptr<T> pdu(new T(buffer));
if (buffer->fail()) // stream failed during pdu constructor
break;
if (pdu->buffer()->fail()) // pdu buffer errors
continue;
if (!block->empty()) // set inheritee
pdu->setInherit(block->back());
block->push_back(pdu); // add to block
}
return block;
}
} // PDU
} // ACN

View File

@ -30,9 +30,11 @@ namespace RLP {
namespace UDP {
preamble_t::preamble_t(PDU::stream_ptr stream) {
length = PDU::read16(stream);
postamble_size = PDU::read16(stream);
length = stream->read16();
postamble_size = stream->read16();
stream->read(acn_id, 12);
if (stream->gcount() != 12)
stream->setstate(stream->rdstate() | std::ios_base::failbit);
}
@ -54,22 +56,6 @@ preamble_t::operator bool () {
return true;
}
/*
Process ACN packet
*/
PDU::block_ptr readBlock(PDU::stream_ptr packet) {
// std::streamoff origin = packet->tellg();
preamble_t preamble(packet);
if (!preamble)
packet->setstate(std::ios_base::failbit);
// Preamble length has a standard min, but no max. Discard any extra.
std::streamoff extra_preamble = preamble.length - PREAMBLE_MINIMUM_SIZE;
for (int i = 0; i < extra_preamble; i++)
packet->get();
return PDU::readBlock(packet);
}
} // UDP
} // RLP

View File

@ -23,7 +23,7 @@
*/
#pragma once
#include <stdint.h>
#include <cstdint>
#include "rlp.h"
#include "pdu.h"
@ -51,8 +51,6 @@ struct preamble_t {
};
PDU::block_ptr readBlock(PDU::stream_ptr);
} // UDP
} // RLP
} // ACN

View File

@ -28,17 +28,21 @@ namespace ACN {
namespace RLP {
rlp_header::rlp_header(PDU::stream_ptr stream)
: pdu_header()
: PDU::pdu_header()
{
if (stream->available() > (long int)sizeof(cid))
stream->read(cid, sizeof(cid));
stream->read(cid, 16);
if (stream->gcount() != 16)
stream->setstate(stream->rdstate() | std::ios_base::failbit);
}
void readHeader(PDU::pdu_ptr pdu) {
if (pdu->flags().hasHeader) {
rlp_header * header = new rlp_header(pdu->buffer());
pdu->setHeader(header);
}
Pdu::Pdu(PDU::stream_ptr stream)
: ACN::PDU::Pdu(stream, 4)
{
if (stream->fail()) return;
if (!buffer_->good()) return;
if (flags_.hasHeader)
setHeader(new rlp_header(buffer_));
}
} // RLP

View File

@ -23,8 +23,8 @@
*/
#pragma once
#include <cstdint>
#include "pdu.h"
#include <stdint.h>
namespace ACN {
namespace RLP {
@ -37,7 +37,13 @@ struct rlp_header : PDU::pdu_header {
rlp_header(PDU::stream_ptr);
};
void readHeader(PDU::pdu_ptr);
class Pdu
: public PDU::Pdu
{
public:
Pdu(PDU::stream_ptr);
};
typedef std::shared_ptr<Pdu> pdu_ptr;
} // RLP
} // ACN

View File

@ -23,7 +23,7 @@
*/
#pragma once
#include <stdint.h>
#include <cstdint>
#include "sdt.h"
// ACN EPI 17. Operation of SDT on UDP Networks

View File

@ -23,7 +23,7 @@
*/
#pragma once
#include <stdint.h>
#include <cstdint>
// ANSI E1.17- 2015, Architecture for Control Networks
// Session Data Transport Protocol

View File

@ -30,18 +30,23 @@ using namespace ACN;
frame_header::frame_header(PDU::stream_ptr stream) {
stream->read(source_name, 64);
priority = PDU::read8(stream);
reserved = PDU::read16(stream);
sequence_number = PDU::read8(stream);
options = PDU::read8(stream);
universe = PDU::read16(stream);
if (stream->gcount() != 64)
stream->setstate(stream->rdstate() | std::ios_base::failbit);
priority = stream->read8();
sync_address = stream->read16();
sequence_number = stream->read8();
options = stream->read8();
universe = stream->read16();
}
void readHeader(PDU::pdu_ptr pdu) {
if (pdu->flags().hasHeader) {
frame_header * header = new frame_header(pdu->buffer());
pdu->setHeader(header);
}
Pdu::Pdu(PDU::stream_ptr stream)
: ACN::PDU::Pdu(stream, 4) // vectors are 4 octets
{
if (stream->fail()) return;
if (!buffer_->good()) return;
if (flags_.hasHeader)
setHeader(new frame_header(buffer_));
}
} // DATA

View File

@ -33,7 +33,7 @@ using namespace ACN;
struct frame_header : PDU::pdu_header {
uint8_t source_name[64];
uint8_t priority;
uint16_t reserved;
uint16_t sync_address;
uint8_t sequence_number;
uint8_t options;
uint16_t universe;
@ -47,7 +47,14 @@ enum options_t : uint8_t {
FORCE_SYNCHRONIZATION = 0b00100000, // Bit 5 = Force_Synchronization
};
void readHeader(PDU::pdu_ptr);
class Pdu
: public PDU::Pdu
{
public:
Pdu(PDU::stream_ptr);
};
typedef std::shared_ptr<Pdu> pdu_ptr;
} // DATA
} // SACN

View File

@ -25,7 +25,8 @@
#include "receiver-esp.h"
// ESP logging module
#include "esp_log.h"
#include <Arduino.h>
#include <esp_log.h>
static const char* TAG = "EspReceiver";
using namespace SACN;

View File

@ -34,25 +34,43 @@ void Receiver::subscribe(const uint16_t universe) {
universes_.emplace(universe, new SACN::Universe());
}
SACN::Universe * Receiver::universe(uint16_t universe) {
if (universes_.count(universe))
return universes_.at(universe);
return nullptr;
return 0;
}
void Receiver::packetHandler(PDU::stream_ptr packet) {
PDU::block_ptr block = RLP::UDP::readBlock(packet);
for(PDU::pdu_ptr pdu : *block) {
// read RLP header
RLP::readHeader(pdu);
/**
Parse the received UDP data.
Layer 4 only. Verificiton of layer 3 (UDP port) must be handled prior to calling.
@param packet is a shared pointer to the UDP data buffer.
*/
void Receiver::packetHandler(PDU::stream_ptr packet) {
// verify the UDP preamble
RLP::UDP::preamble_t preamble(packet);
if (!preamble)
packet->setstate(packet->rdstate() | std::ios_base::failbit);
if (!packet->good()) return;
// Preamble length has a standard min, but no max. Discard any extra.
int extra = preamble.length - RLP::UDP::PREAMBLE_MINIMUM_SIZE;
for (int i = 0; i < extra; i++)
packet->read8();
auto block = PDU::readBlock<RLP::Pdu>(packet);
if (packet->fail())
return;
for(auto root : *block) {
// 5.5 Vector
// Receivers shall discard the packet if the received value is not
// VECTOR_ROOT_E131_DATA or VECTOR_ROOT_E131_EXTENDED.
switch (pdu->vector()) {
switch (root->vector()) {
case VECTOR_ROOT_E131_DATA:
rootDataHandler(pdu);
rootDataHandler(root);
break;
case VECTOR_ROOT_E131_EXTENDED:
break;
@ -62,19 +80,25 @@ void Receiver::packetHandler(PDU::stream_ptr packet) {
}
}
void Receiver::rootDataHandler(PDU::pdu_ptr pdu) {
PDU::block_ptr block = PDU::readBlock(pdu->buffer());
// PDU data will be a block of one E1.31 frame
for(PDU::pdu_ptr child : *block) {
child->setParent(pdu);
/**
Receive VECTOR_ROOT_E131_DATA vector'd packets.
@param pdu is a shared pointer to the PDU
*/
void Receiver::rootDataHandler(RLP::pdu_ptr root) {
auto block = PDU::readBlock<DATA::Pdu>(root->buffer());
if (root->buffer()->fail())
return;
for(auto frame : *block) {
frame->setParent(root);
// 6.2.1 E1.31 Data Packet: Vector
// Sources sending an E1.31 Data Packet shall set the E1.31 Layer's Vector
// to VECTOR_E131_DATA_PACKET. This value indicates that the E1.31 framing
// layer is wrapping a DMP PDU.
switch(child->vector()) {
switch(frame->vector()) {
case VECTOR_E131_DATA_PACKET:
dataPacketHandler(child);
dataPacketHandler(frame);
break;
default:
break;
@ -82,33 +106,38 @@ void Receiver::rootDataHandler(PDU::pdu_ptr pdu) {
}
}
void Receiver::dataPacketHandler(PDU::pdu_ptr pdu) {
DATA::readHeader(pdu);
DATA::frame_header * header = (DATA::frame_header*)pdu->header();
if (!universes_.count(header->universe))
/**
Receive `VECTOR_ROOT_E131_DATA -> VECTOR_E131_DATA_PACKET` vector'd packets.
Merging will be based on frame header. PDU data will be read as a block of DMP PDUs and passed to the universe.
@param pdu is a shared pointer to the PDU
*/
void Receiver::dataPacketHandler(DATA::pdu_ptr frame) {
// header may be inherited. check that one exists
if (!frame->header())
return;
auto header = (DATA::frame_header *)frame->header();
if (universes_.count(header->universe) == 0)
return;
Universe * universe = universes_.at(header->universe);
// TODO: do something with merging/priorty
// RLP::rlp_header * root_header = (RLP::rlp_header*)pdu->parent()->header();
// uint8_t * cid = root_header->cid;
// PDU data will be a block of one DMP PDU
PDU::block_ptr block = DMP::readBlock(pdu->buffer());
for (PDU::pdu_ptr dmp : *block) {
dmp->setParent(pdu);
// all DMP messages have the same header format
DMP::readHeader(dmp);
auto block = PDU::readBlock<DMP::Pdu>(frame->buffer());
if (frame->buffer()->fail())
return;
for (auto dmp : *block) {
dmp->setParent(frame);
// 7.2 DMP Layer: Vector
// The DMP Layer's Vector shall be set to VECTOR_DMP_SET_PROPERTY, which
// indicates a DMP Set Property message by sources. Receivers shall discard
// the packet if the received value is not VECTOR_DMP_SET_PROPERTY.
switch(dmp->vector()) {
case DMP::SET_PROPERTY:
DMP::readSet(dmp);
universe->set(dmp);
break;
default:

View File

@ -23,6 +23,7 @@
*/
#pragma once
#include "data.h"
#include "sacn.h"
#include "universe.h"
#include <unordered_map>
@ -40,8 +41,8 @@ public:
protected:
void packetHandler(PDU::stream_ptr);
void rootDataHandler(PDU::pdu_ptr);
void dataPacketHandler(PDU::pdu_ptr);
void rootDataHandler(RLP::pdu_ptr);
void dataPacketHandler(DATA::pdu_ptr);
private:
std::unordered_map <uint16_t, SACN::Universe *> universes_;

View File

@ -23,7 +23,7 @@
*/
#pragma once
#include <stdint.h>
#include <cstdint>
#include "../acn/acn.h"
// E1.31 Lightweight streaming protocol for transport of DMX512 using ACN

View File

@ -36,18 +36,22 @@ void Universe::set(PDU::pdu_ptr pdu) {
// 7.3 Address Type and Data Type
// Sources shall set the DMP Layer's Address Type and Data Type to 0xa1.
// Receivers shall discard the packet if the received value is not 0xa1.
DMP::address_type * type = (DMP::address_type*)pdu->header();
if (!pdu->header())
return;
auto type = (DMP::address_type*)pdu->header();
if (!type->z_reserved) return; // needs to be true, but why?
if (type->relative) return;
if (type->type != DMP::ARRAY) return;
if (type->x_reserved != 0) return;
if (type->width != DMP::TWO) return;
// only accept the first property pair in the data
DMP::dmp_set_data * data = (DMP::dmp_set_data*)pdu->data();
DMP::set_property property = data->properties.front();
// only act on the first property pair in the data
if (!pdu->data())
return;
auto data = (DMP::dmp_set_data*)pdu->data();
auto prop = data->properties.front();
auto pr = prop.first;
DMP::range pr = property.first;
// 7.4 First Property Address
// Sources shall set the DMP Layer's First Property Address to 0x0000.
// Receivers shall discard the packet if the received value is not 0x0000.
@ -69,7 +73,7 @@ void Universe::set(PDU::pdu_ptr pdu) {
// 7.7 Property Values (DMX512-A Data)
/// The DMP Layer's Property values field is used to encode the
// DMX512-A [DMX] START Code and data.
DMX::Universe::set(property.second);
DMX::Universe::set(prop.second);
}
};