improved ACN protocol layer separation and robustness
This commit is contained in:
parent
43c0e61465
commit
796b8a5993
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
#include "sdt.h"
|
||||
|
||||
// ACN EPI 17. Operation of SDT on UDP Networks
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
|
||||
// ANSI E1.17- 2015, Architecture for Control Networks–
|
||||
// Session Data Transport Protocol
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue