diff --git a/include/ola/e133/MessageBuilder.h b/include/ola/e133/MessageBuilder.h index 272a56339..ace419b40 100644 --- a/include/ola/e133/MessageBuilder.h +++ b/include/ola/e133/MessageBuilder.h @@ -27,6 +27,7 @@ #include #include #include +#include #include namespace ola { @@ -46,8 +47,18 @@ class MessageBuilder { void PrependRDMHeader(IOStack *packet); + void BuildTCPRDMCommandPDU(IOStack *packet, + ola::rdm::RDMRequest *request, + uint16_t source_endpoint_id, + uint16_t destination_endpoint_id, + uint32_t sequence_number); + void BuildNullTCPPacket(IOStack *packet); + void BuildBrokerFetchClientListTCPPacket(IOStack *packet); + + void BuildBrokerNullTCPPacket(IOStack *packet); + void BuildTCPE133StatusPDU(IOStack *packet, uint32_t sequence_number, uint16_t endpoint_id, ola::e133::E133StatusCode status_code, diff --git a/libs/acn/BrokerFetchClientListPDU.cpp b/libs/acn/BrokerFetchClientListPDU.cpp new file mode 100644 index 000000000..99e7f866e --- /dev/null +++ b/libs/acn/BrokerFetchClientListPDU.cpp @@ -0,0 +1,38 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * BrokerFetchClientListPDU.cpp + * The BrokerFetchClientListPDU + * Copyright (C) 2023 Peter Newman + */ + +#include "libs/acn/BrokerFetchClientListPDU.h" + +#include +#include + +namespace ola { +namespace acn { + +using ola::network::HostToNetwork; + +void BrokerFetchClientListPDU::PrependPDU(ola::io::IOStack *stack) { + uint16_t vector = HostToNetwork(static_cast( + VECTOR_BROKER_FETCH_CLIENT_LIST)); + stack->Write(reinterpret_cast(&vector), sizeof(vector)); + PrependFlagsAndLength(stack, VFLAG_MASK | HFLAG_MASK | DFLAG_MASK, true); +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/BrokerFetchClientListPDU.h b/libs/acn/BrokerFetchClientListPDU.h new file mode 100644 index 000000000..df4a40e4f --- /dev/null +++ b/libs/acn/BrokerFetchClientListPDU.h @@ -0,0 +1,56 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * BrokerFetchClientListPDU.h + * The BrokerFetchClientListPDU class + * Copyright (C) 2023 Peter Newman + */ + +#ifndef LIBS_ACN_BROKERFETCHCLIENTLISTPDU_H_ +#define LIBS_ACN_BROKERFETCHCLIENTLISTPDU_H_ + +#include + +#include "libs/acn/PDU.h" + +namespace ola { +namespace acn { + +class BrokerFetchClientListPDU : public PDU { + public: + explicit BrokerFetchClientListPDU(unsigned int vector): + PDU(vector, TWO_BYTES, true) {} + + unsigned int HeaderSize() const { return 0; } + bool PackHeader(OLA_UNUSED uint8_t *data, + unsigned int *length) const { + *length = 0; + return true; + } + void PackHeader(OLA_UNUSED ola::io::OutputStream *stream) const {} + + unsigned int DataSize() const { return 0; } + bool PackData(OLA_UNUSED uint8_t *data, + unsigned int *length) const { + *length = 0; + return true; + } + void PackData(OLA_UNUSED ola::io::OutputStream *stream) const {} + + static void PrependPDU(ola::io::IOStack *stack); +}; +} // namespace acn +} // namespace ola +#endif // LIBS_ACN_BROKERFETCHCLIENTLISTPDU_H_ diff --git a/libs/acn/BrokerFetchClientListPDUTest.cpp b/libs/acn/BrokerFetchClientListPDUTest.cpp new file mode 100644 index 000000000..00a5ad09c --- /dev/null +++ b/libs/acn/BrokerFetchClientListPDUTest.cpp @@ -0,0 +1,148 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * BrokerFetchClientListPDUTest.cpp + * Test fixture for the BrokerFetchClientListPDU class + * Copyright (C) 2023 Peter Newman + */ + +#include + +#include "ola/Logging.h" +#include "ola/io/IOQueue.h" +#include "ola/io/IOStack.h" +#include "ola/io/OutputStream.h" +#include "ola/network/NetworkUtils.h" +#include "ola/testing/TestUtils.h" +#include "libs/acn/PDUTestCommon.h" +#include "libs/acn/BrokerFetchClientListPDU.h" + +namespace ola { +namespace acn { + +using ola::acn::BrokerFetchClientListPDU; +using ola::io::IOQueue; +using ola::io::IOStack; +using ola::io::OutputStream; +using ola::network::HostToNetwork; + +class BrokerFetchClientListPDUTest: public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(BrokerFetchClientListPDUTest); + CPPUNIT_TEST(testSimpleBrokerFetchClientListPDU); + CPPUNIT_TEST(testSimpleBrokerFetchClientListPDUToOutputStream); + CPPUNIT_TEST(testPrepend); + CPPUNIT_TEST_SUITE_END(); + + public: + void testSimpleBrokerFetchClientListPDU(); + void testSimpleBrokerFetchClientListPDUToOutputStream(); + void testPrepend(); + + void setUp() { + ola::InitLogging(ola::OLA_LOG_DEBUG, ola::OLA_LOG_STDERR); + } + + private: + static const uint16_t TEST_VECTOR; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(BrokerFetchClientListPDUTest); + +const uint16_t BrokerFetchClientListPDUTest::TEST_VECTOR = 39; + + +/* + * Test that packing a BrokerFetchClientListPDU works. + */ +void BrokerFetchClientListPDUTest::testSimpleBrokerFetchClientListPDU() { + BrokerFetchClientListPDU pdu(TEST_VECTOR); + + OLA_ASSERT_EQ(0u, pdu.HeaderSize()); + OLA_ASSERT_EQ(0u, pdu.DataSize()); + OLA_ASSERT_EQ(5u, pdu.Size()); + + unsigned int size = pdu.Size(); + uint8_t *data = new uint8_t[size]; + unsigned int bytes_used = size; + OLA_ASSERT(pdu.Pack(data, &bytes_used)); + OLA_ASSERT_EQ(size, bytes_used); + + // spot check the data + OLA_ASSERT_EQ((uint8_t) 0xf0, data[0]); + // bytes_used is technically data[1] and data[2] if > 255 + OLA_ASSERT_EQ((uint8_t) bytes_used, data[2]); + uint16_t actual_value; + memcpy(&actual_value, data + 3, sizeof(actual_value)); + OLA_ASSERT_EQ(HostToNetwork(TEST_VECTOR), actual_value); + + // test undersized buffer + bytes_used = size - 1; + OLA_ASSERT_FALSE(pdu.Pack(data, &bytes_used)); + OLA_ASSERT_EQ(0u, bytes_used); + + // test oversized buffer + bytes_used = size + 1; + OLA_ASSERT(pdu.Pack(data, &bytes_used)); + OLA_ASSERT_EQ(size, bytes_used); + delete[] data; +} + +/* + * Test that writing to an output stream works. + */ +void BrokerFetchClientListPDUTest:: + testSimpleBrokerFetchClientListPDUToOutputStream() { + BrokerFetchClientListPDU pdu(TEST_VECTOR); + + OLA_ASSERT_EQ(0u, pdu.HeaderSize()); + OLA_ASSERT_EQ(0u, pdu.DataSize()); + OLA_ASSERT_EQ(5u, pdu.Size()); + + IOQueue output; + OutputStream stream(&output); + pdu.Write(&stream); + OLA_ASSERT_EQ(5u, output.Size()); + + uint8_t *pdu_data = new uint8_t[output.Size()]; + unsigned int pdu_size = output.Peek(pdu_data, output.Size()); + OLA_ASSERT_EQ(output.Size(), pdu_size); + + uint8_t EXPECTED[] = { + 0xf0, 0x00, 0x05, + 0, 39 + }; + OLA_ASSERT_DATA_EQUALS(EXPECTED, sizeof(EXPECTED), pdu_data, pdu_size); + output.Pop(output.Size()); + delete[] pdu_data; +} + + +void BrokerFetchClientListPDUTest::testPrepend() { + IOStack stack; + BrokerFetchClientListPDU::PrependPDU(&stack); + + unsigned int length = stack.Size(); + uint8_t *buffer = new uint8_t[length]; + OLA_ASSERT(stack.Read(buffer, length)); + + const uint8_t expected_data[] = { + 0xf0, 0x00, 0x05, + 0, 0x06 + }; + OLA_ASSERT_DATA_EQUALS(expected_data, sizeof(expected_data), buffer, length); + delete[] buffer; +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/HeaderSet.h b/libs/acn/HeaderSet.h index 06a56c26a..ae4925e1b 100644 --- a/libs/acn/HeaderSet.h +++ b/libs/acn/HeaderSet.h @@ -28,6 +28,7 @@ #include "libs/acn/E133Header.h" #include "libs/acn/LLRPHeader.h" #include "libs/acn/RootHeader.h" +#include "libs/acn/RPTHeader.h" #include "libs/acn/TransportHeader.h" namespace ola { @@ -60,6 +61,9 @@ class HeaderSet { const LLRPHeader &GetLLRPHeader() const { return m_llrp_header; } void SetLLRPHeader(const LLRPHeader &header) { m_llrp_header = header; } + const RPTHeader &GetRPTHeader() const { return m_rpt_header; } + void SetRPTHeader(const RPTHeader &header) { m_rpt_header = header; } + bool operator==(const HeaderSet &other) const { return ( m_transport_header == other.m_transport_header && @@ -67,7 +71,8 @@ class HeaderSet { m_e131_header == other.m_e131_header && m_e133_header == other.m_e133_header && m_dmp_header == other.m_dmp_header && - m_llrp_header == other.m_llrp_header); + m_llrp_header == other.m_llrp_header && + m_rpt_header == other.m_rpt_header); } private: @@ -77,6 +82,7 @@ class HeaderSet { E133Header m_e133_header; DMPHeader m_dmp_header; LLRPHeader m_llrp_header; + RPTHeader m_rpt_header; }; } // namespace acn } // namespace ola diff --git a/libs/acn/HeaderSetTest.cpp b/libs/acn/HeaderSetTest.cpp index 18fa7141f..26623b0c1 100644 --- a/libs/acn/HeaderSetTest.cpp +++ b/libs/acn/HeaderSetTest.cpp @@ -25,6 +25,7 @@ #include "ola/acn/CID.h" #include "ola/network/SocketAddress.h" #include "ola/network/NetworkUtils.h" +#include "ola/rdm/UID.h" #include "libs/acn/HeaderSet.h" #include "ola/testing/TestUtils.h" @@ -36,11 +37,14 @@ using ola::acn::E131Rev2Header; using ola::acn::E133Header; using ola::acn::FOUR_BYTES; using ola::acn::HeaderSet; +using ola::acn::LLRPHeader; using ola::acn::NON_RANGE; using ola::acn::ONE_BYTES; using ola::acn::RANGE_EQUAL; using ola::acn::RootHeader; +using ola::acn::RPTHeader; using ola::acn::TransportHeader; +using ola::rdm::UID; class HeaderSetTest: public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(HeaderSetTest); @@ -49,6 +53,8 @@ class HeaderSetTest: public CppUnit::TestFixture { CPPUNIT_TEST(testE131Header); CPPUNIT_TEST(testE133Header); CPPUNIT_TEST(testDMPHeader); + CPPUNIT_TEST(testLLRPHeader); + CPPUNIT_TEST(testRPTHeader); CPPUNIT_TEST(testHeaderSet); CPPUNIT_TEST_SUITE_END(); @@ -58,6 +64,8 @@ class HeaderSetTest: public CppUnit::TestFixture { void testE131Header(); void testE133Header(); void testDMPHeader(); + void testLLRPHeader(); + void testRPTHeader(); void testHeaderSet(); }; @@ -213,6 +221,59 @@ void HeaderSetTest::testDMPHeader() { } +/* + * test the E1.33 LLRP Header + */ +void HeaderSetTest::testLLRPHeader() { + CID cid = CID::Generate(); + + LLRPHeader header(cid, 9840); + OLA_ASSERT(cid == header.DestinationCid()); + OLA_ASSERT_EQ((uint32_t) 9840, header.TransactionNumber()); + + // test copy and assign + LLRPHeader header2 = header; + OLA_ASSERT(header.DestinationCid() == header2.DestinationCid()); + OLA_ASSERT_EQ(header.TransactionNumber(), header2.TransactionNumber()); + + LLRPHeader header3(header); + OLA_ASSERT(header.DestinationCid() == header3.DestinationCid()); + OLA_ASSERT_EQ(header.TransactionNumber(), header3.TransactionNumber()); + OLA_ASSERT(header == header3); +} + + +/* + * test the RPT Header + */ +void HeaderSetTest::testRPTHeader() { + UID src(1, 2); + UID dest(4, 10); + RPTHeader header(src, 3, dest, 5, 9840); + OLA_ASSERT(src == header.SourceUID()); + OLA_ASSERT_EQ((uint16_t) 3, header.SourceEndpoint()); + OLA_ASSERT(dest == header.DestinationUID()); + OLA_ASSERT_EQ((uint16_t) 5, header.DestinationEndpoint()); + OLA_ASSERT_EQ((uint32_t) 9840, header.Sequence()); + + // test copy and assign + RPTHeader header2 = header; + OLA_ASSERT(header.SourceUID() == header2.SourceUID()); + OLA_ASSERT_EQ(header.SourceEndpoint(), header2.SourceEndpoint()); + OLA_ASSERT(header.DestinationUID() == header2.DestinationUID()); + OLA_ASSERT_EQ(header.DestinationEndpoint(), header2.DestinationEndpoint()); + OLA_ASSERT_EQ(header.Sequence(), header2.Sequence()); + + RPTHeader header3(header); + OLA_ASSERT(header.SourceUID() == header3.SourceUID()); + OLA_ASSERT_EQ(header.SourceEndpoint(), header3.SourceEndpoint()); + OLA_ASSERT(header.DestinationUID() == header3.DestinationUID()); + OLA_ASSERT_EQ(header.DestinationEndpoint(), header3.DestinationEndpoint()); + OLA_ASSERT_EQ(header.Sequence(), header3.Sequence()); + OLA_ASSERT(header == header3); +} + + /* * Check that the header set works */ @@ -222,6 +283,9 @@ void HeaderSetTest::testHeaderSet() { E131Header e131_header("e131", 1, 2, 6001); E133Header e133_header("foo", 1, 2050); DMPHeader dmp_header(false, false, NON_RANGE, ONE_BYTES); + CID destination_cid = CID::Generate(); + LLRPHeader llrp_header(destination_cid, 9840); + RPTHeader rpt_header(UID(1, 2), 3, UID(4, 10), 5, 9840); // test the root header component CID cid = CID::Generate(); @@ -241,12 +305,22 @@ void HeaderSetTest::testHeaderSet() { headers.SetDMPHeader(dmp_header); OLA_ASSERT(dmp_header == headers.GetDMPHeader()); + // test the LLRP headers component + headers.SetLLRPHeader(llrp_header); + OLA_ASSERT(llrp_header == headers.GetLLRPHeader()); + + // test the RPT headers component + headers.SetRPTHeader(rpt_header); + OLA_ASSERT(rpt_header == headers.GetRPTHeader()); + // test assign HeaderSet headers2 = headers; OLA_ASSERT(root_header == headers2.GetRootHeader()); OLA_ASSERT(e131_header == headers2.GetE131Header()); OLA_ASSERT(e133_header == headers2.GetE133Header()); OLA_ASSERT(dmp_header == headers2.GetDMPHeader()); + OLA_ASSERT(llrp_header == headers2.GetLLRPHeader()); + OLA_ASSERT(rpt_header == headers2.GetRPTHeader()); OLA_ASSERT(headers2 == headers); // test copy @@ -255,5 +329,7 @@ void HeaderSetTest::testHeaderSet() { OLA_ASSERT(e131_header == headers3.GetE131Header()); OLA_ASSERT(e133_header == headers3.GetE133Header()); OLA_ASSERT(dmp_header == headers3.GetDMPHeader()); + OLA_ASSERT(llrp_header == headers3.GetLLRPHeader()); + OLA_ASSERT(rpt_header == headers3.GetRPTHeader()); OLA_ASSERT(headers3 == headers); } diff --git a/libs/acn/Makefile.mk b/libs/acn/Makefile.mk index 7c02570ed..37c8d999f 100644 --- a/libs/acn/Makefile.mk +++ b/libs/acn/Makefile.mk @@ -39,6 +39,8 @@ libs_acn_libolae131core_la_SOURCES = \ libs/acn/BrokerClientRemoveInflator.h \ libs/acn/BrokerConnectPDU.cpp \ libs/acn/BrokerConnectPDU.h \ + libs/acn/BrokerFetchClientListPDU.cpp \ + libs/acn/BrokerFetchClientListPDU.h \ libs/acn/BrokerInflator.h \ libs/acn/BrokerNullInflator.h \ libs/acn/BrokerNullPDU.cpp \ @@ -104,6 +106,15 @@ libs_acn_libolae131core_la_SOURCES = \ libs/acn/RootPDU.h \ libs/acn/RootSender.cpp \ libs/acn/RootSender.h \ + libs/acn/RPTHeader.h \ + libs/acn/RPTInflator.cpp \ + libs/acn/RPTInflator.h \ + libs/acn/RPTNotificationInflator.h \ + libs/acn/RPTPDU.cpp \ + libs/acn/RPTPDU.h \ + libs/acn/RPTRequestInflator.h \ + libs/acn/RPTRequestPDU.cpp \ + libs/acn/RPTRequestPDU.h \ libs/acn/TCPTransport.cpp \ libs/acn/TCPTransport.h \ libs/acn/Transport.h \ @@ -162,11 +173,15 @@ libs_acn_E131Tester_LDADD = \ libs_acn_E133Tester_SOURCES = \ libs/acn/BrokerClientEntryPDUTest.cpp \ libs/acn/BrokerConnectPDUTest.cpp \ + libs/acn/BrokerFetchClientListPDUTest.cpp \ libs/acn/BrokerNullPDUTest.cpp \ libs/acn/BrokerPDUTest.cpp \ libs/acn/E133InflatorTest.cpp \ libs/acn/E133PDUTest.cpp \ - libs/acn/RDMPDUTest.cpp + libs/acn/RDMPDUTest.cpp \ + libs/acn/RPTInflatorTest.cpp \ + libs/acn/RPTPDUTest.cpp \ + libs/acn/RPTRequestPDUTest.cpp libs_acn_E133Tester_CPPFLAGS = $(COMMON_TESTING_FLAGS) libs_acn_E133Tester_LDADD = \ libs/acn/libolae131core.la \ diff --git a/libs/acn/PDU.cpp b/libs/acn/PDU.cpp index 0f256eca1..0fa3ef744 100644 --- a/libs/acn/PDU.cpp +++ b/libs/acn/PDU.cpp @@ -50,6 +50,16 @@ unsigned int PDU::Size() const { * @return false on error, true otherwise */ bool PDU::Pack(uint8_t *buffer, unsigned int *length) const { + if (!buffer) { + OLA_WARN << "PDU::Pack: missing buffer"; + return false; + } + + if (!length) { + OLA_WARN << "PDU::Pack: missing length"; + return false; + } + unsigned int size = Size(); unsigned int offset = 0; @@ -121,6 +131,11 @@ bool PDU::Pack(uint8_t *buffer, unsigned int *length) const { * Write this PDU to an OutputStream. */ void PDU::Write(OutputStream *stream) const { + if (!stream) { + OLA_WARN << "PDU::Write: missing stream"; + return; + } + unsigned int size = Size(); if (size <= TWOB_LENGTH_LIMIT && !m_force_length_flag) { @@ -163,6 +178,11 @@ void PDU::Write(OutputStream *stream) const { void PDU::PrependFlagsAndLength(ola::io::OutputBufferInterface *output, uint8_t flags, bool force_length_flag) { + if (!output) { + OLA_WARN << "PDU::PrependFlagsAndLength: missing output"; + return; + } + PrependFlagsAndLength(output, output->Size(), flags, force_length_flag); } @@ -174,6 +194,11 @@ void PDU::PrependFlagsAndLength(ola::io::OutputBufferInterface *output, unsigned int size, uint8_t flags, bool force_length_flag) { + if (!output) { + OLA_WARN << "PDU::PrependFlagsAndLength: missing output"; + return; + } + if (size + 2 <= TWOB_LENGTH_LIMIT && !force_length_flag) { size += 2; uint16_t flags_and_length = static_cast(size); diff --git a/libs/acn/PDU.h b/libs/acn/PDU.h index 2be285dea..18193c204 100644 --- a/libs/acn/PDU.h +++ b/libs/acn/PDU.h @@ -22,6 +22,7 @@ #define LIBS_ACN_PDU_H_ #include +#include #include #include #include @@ -160,6 +161,16 @@ class PDUBlock { */ template bool PDUBlock::Pack(uint8_t *data, unsigned int *length) const { + if (!data) { + OLA_WARN << "PDUBlock::Pack: missing buffer"; + return false; + } + + if (!length) { + OLA_WARN << "PDUBock::Pack: missing length"; + return false; + } + bool status = true; unsigned int i = 0; typename std::vector::const_iterator iter; @@ -181,6 +192,11 @@ bool PDUBlock::Pack(uint8_t *data, unsigned int *length) const { */ template void PDUBlock::Write(ola::io::OutputStream *stream) const { + if (!stream) { + OLA_WARN << "PDUBlock::Write: missing stream"; + return; + } + typename std::vector::const_iterator iter; for (iter = m_pdus.begin(); iter != m_pdus.end(); ++iter) { // TODO(simon): optimize repeated headers & vectors here diff --git a/libs/acn/PDUTest.cpp b/libs/acn/PDUTest.cpp index a5d86cc5d..cdeec1832 100644 --- a/libs/acn/PDUTest.cpp +++ b/libs/acn/PDUTest.cpp @@ -35,12 +35,16 @@ using ola::io::OutputStream; class PDUTest: public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(PDUTest); + CPPUNIT_TEST(testPDU); CPPUNIT_TEST(testPDUBlock); + CPPUNIT_TEST(testPDUToOutputStream); CPPUNIT_TEST(testBlockToOutputStream); CPPUNIT_TEST_SUITE_END(); public: + void testPDU(); void testPDUBlock(); + void testPDUToOutputStream(); void testBlockToOutputStream(); void setUp() { @@ -51,6 +55,41 @@ class PDUTest: public CppUnit::TestFixture { CPPUNIT_TEST_SUITE_REGISTRATION(PDUTest); +/* + * Test that packing a PDUBlock works. + */ +void PDUTest::testPDU() { + MockPDU pdu(0x1234, 0x2468); + + OLA_ASSERT_EQ(4u, pdu.HeaderSize()); + OLA_ASSERT_EQ(4u, pdu.DataSize()); + OLA_ASSERT_EQ(14u, pdu.Size()); + + unsigned int size = pdu.Size(); + uint8_t *data = new uint8_t[size]; + unsigned int bytes_used = size; + OLA_ASSERT(pdu.Pack(data, &bytes_used)); + OLA_ASSERT_EQ(size, bytes_used); + + // test null data + OLA_ASSERT_FALSE(pdu.Pack(NULL, &bytes_used)); + + // test a null length + OLA_ASSERT_FALSE(pdu.Pack(data, NULL)); + + // test undersized buffer + bytes_used = size - 1; + OLA_ASSERT_FALSE(pdu.Pack(data, &bytes_used)); + OLA_ASSERT_EQ(0u, bytes_used); + + // test oversized buffer + bytes_used = size + 1; + OLA_ASSERT(pdu.Pack(data, &bytes_used)); + OLA_ASSERT_EQ(size, bytes_used); + delete[] data; +} + + /* * Test that packing a PDUBlock works. */ @@ -74,6 +113,24 @@ void PDUTest::testPDUBlock() { OLA_ASSERT_EQ(1u, *test++); OLA_ASSERT_EQ(2u, *test++); OLA_ASSERT_EQ(42u, *test); + + // test null data + OLA_ASSERT_FALSE(block.Pack(NULL, &bytes_used)); + + // test a null length + OLA_ASSERT_FALSE(block.Pack(data, NULL)); + + // test undersized buffer + bytes_used = block_size - 1; + OLA_ASSERT_FALSE(block.Pack(data, &bytes_used)); + // TODO(Peter): Work out what behaviour we want for the bytes used, it's + // currently the actual total used, not zero like the PDU::Pack returns + // OLA_ASSERT_EQ(0u, bytes_used); + + // test oversized buffer + bytes_used = block_size + 1; + OLA_ASSERT(block.Pack(data, &bytes_used)); + OLA_ASSERT_EQ(block_size, bytes_used); delete[] data; block.Clear(); @@ -81,6 +138,36 @@ void PDUTest::testPDUBlock() { } +/* + * Test that writing a PDU to an OutputStream works. + */ +void PDUTest::testPDUToOutputStream() { + MockPDU pdu(0x1234, 0x2468); + + IOQueue output; + OutputStream stream(&output); + pdu.Write(&stream); + OLA_ASSERT_EQ(14u, output.Size()); + + uint8_t *data = new uint8_t[output.Size()]; + unsigned int data_size = output.Peek(data, output.Size()); + OLA_ASSERT_EQ(output.Size(), data_size); + + uint8_t EXPECTED[] = { + 0x70, 0x0e, 0, 0, + 0, 0x2b, + 0x34, 0x12, 0, 0, + 0x68, 0x24, 0, 0 + }; + OLA_ASSERT_DATA_EQUALS(EXPECTED, sizeof(EXPECTED), data, data_size); + output.Pop(output.Size()); + + // test null stream + pdu.Write(NULL); + delete[] data; +} + + /* * Test that writing to an OutputStream works. */ @@ -109,6 +196,9 @@ void PDUTest::testBlockToOutputStream() { }; OLA_ASSERT_DATA_EQUALS(EXPECTED, sizeof(EXPECTED), block_data, block_size); output.Pop(output.Size()); + + // test null stream + block.Write(NULL); delete[] block_data; } } // namespace acn diff --git a/libs/acn/RDMInflator.cpp b/libs/acn/RDMInflator.cpp index a4e32166e..9dba40ba4 100644 --- a/libs/acn/RDMInflator.cpp +++ b/libs/acn/RDMInflator.cpp @@ -74,13 +74,13 @@ bool RDMInflator::DecodeHeader(HeaderSet *, /* - * Handle a DMP PDU for E1.33. + * Handle an RDM PDU for E1.33. */ bool RDMInflator::HandlePDUData(uint32_t vector, const HeaderSet &headers, const uint8_t *data, unsigned int pdu_len) { - if (vector != VECTOR_RDMNET_DATA) { + if (vector != VECTOR_RDM_CMD_RDM_DATA) { OLA_INFO << "Not a RDM message, vector was " << vector; return true; } diff --git a/libs/acn/RDMInflator.h b/libs/acn/RDMInflator.h index 41f29ae67..aa2cb73ad 100644 --- a/libs/acn/RDMInflator.h +++ b/libs/acn/RDMInflator.h @@ -46,6 +46,8 @@ class RDMInflator: public BaseInflator { const std::string& // rdm data > GenericRDMMessageHandler; + // TODO(Peter): Set a better default vector for RDM use (possibly the RPT + // one) explicit RDMInflator(unsigned int vector = ola::acn::VECTOR_FRAMING_RDMNET); ~RDMInflator() {} @@ -54,8 +56,6 @@ class RDMInflator: public BaseInflator { void SetRDMHandler(RDMMessageHandler *handler); void SetGenericRDMHandler(GenericRDMMessageHandler *handler); - static const unsigned int VECTOR_RDMNET_DATA = 0xcc; - protected: bool DecodeHeader(HeaderSet *headers, const uint8_t *data, diff --git a/libs/acn/RDMPDU.cpp b/libs/acn/RDMPDU.cpp index f99779ad1..a81659268 100644 --- a/libs/acn/RDMPDU.cpp +++ b/libs/acn/RDMPDU.cpp @@ -47,7 +47,7 @@ void RDMPDU::PackData(ola::io::OutputStream *stream) const { void RDMPDU::PrependPDU(ola::io::IOStack *stack) { uint8_t vector = HostToNetwork(ola::rdm::START_CODE); stack->Write(reinterpret_cast(&vector), sizeof(vector)); - PrependFlagsAndLength(stack); + PrependFlagsAndLength(stack, VFLAG_MASK | HFLAG_MASK | DFLAG_MASK, true); } } // namespace acn } // namespace ola diff --git a/libs/acn/RDMPDUTest.cpp b/libs/acn/RDMPDUTest.cpp index ff6489d0f..f11160f6b 100644 --- a/libs/acn/RDMPDUTest.cpp +++ b/libs/acn/RDMPDUTest.cpp @@ -161,7 +161,7 @@ void RDMPDUTest::testPrepend() { uint8_t *buffer = new uint8_t[length]; OLA_ASSERT(stack.Read(buffer, length)); - const uint8_t expected_data[] = {0x70, 3, TEST_VECTOR}; + const uint8_t expected_data[] = {0xf0, 0, 4, TEST_VECTOR}; OLA_ASSERT_DATA_EQUALS(expected_data, sizeof(expected_data), buffer, length); delete[] buffer; } diff --git a/libs/acn/RPTHeader.h b/libs/acn/RPTHeader.h new file mode 100644 index 000000000..00a26bc34 --- /dev/null +++ b/libs/acn/RPTHeader.h @@ -0,0 +1,96 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * RPTHeader.h + * The E1.33 RPT Header + * Copyright (C) 2023 Peter Newman + */ + +#ifndef LIBS_ACN_RPTHEADER_H_ +#define LIBS_ACN_RPTHEADER_H_ + +#include +#include + +#include +#include + +namespace ola { +namespace acn { + +/* + * Header for the E133 RPT layer + */ +class RPTHeader { + public: + RPTHeader() + : m_source_uid(0, 0), + m_source_endpoint(0), + m_destination_uid(0, 0), + m_destination_endpoint(0), + m_sequence(0) { + } + + RPTHeader(ola::rdm::UID source_uid, + uint16_t source_endpoint, + ola::rdm::UID destination_uid, + uint16_t destination_endpoint, + uint32_t sequence) + : m_source_uid(source_uid), + m_source_endpoint(source_endpoint), + m_destination_uid(destination_uid), + m_destination_endpoint(destination_endpoint), + m_sequence(sequence) { + } + ~RPTHeader() {} + + ola::rdm::UID SourceUID() const { return m_source_uid; } + uint16_t SourceEndpoint() const { return m_source_endpoint; } + ola::rdm::UID DestinationUID() const { return m_destination_uid; } + uint16_t DestinationEndpoint() const { return m_destination_endpoint; } + // TODO(Peter): Should this be SequenceNumber? + // TODO(Peter): Should the sequence number really be part of the header? + uint32_t Sequence() const { return m_sequence; } + + bool operator==(const RPTHeader &other) const { + return m_source_uid == other.m_source_uid && + m_source_endpoint == other.m_source_endpoint && + m_destination_uid == other.m_destination_uid && + m_destination_endpoint == other.m_destination_endpoint && + m_sequence == other.m_sequence; + } + + PACK( + struct rpt_pdu_header_s { + rpt_pdu_header_s() : reserved(0) {} + uint8_t source_uid[ola::rdm::UID::LENGTH]; + uint16_t source_endpoint; + uint8_t destination_uid[ola::rdm::UID::LENGTH]; + uint16_t destination_endpoint; + uint32_t sequence; + uint8_t reserved; + }); + typedef struct rpt_pdu_header_s rpt_pdu_header; + + private: + ola::rdm::UID m_source_uid; + uint16_t m_source_endpoint; + ola::rdm::UID m_destination_uid; + uint16_t m_destination_endpoint; + uint32_t m_sequence; +}; +} // namespace acn +} // namespace ola +#endif // LIBS_ACN_RPTHEADER_H_ diff --git a/libs/acn/RPTInflator.cpp b/libs/acn/RPTInflator.cpp new file mode 100644 index 000000000..5d9cde6f5 --- /dev/null +++ b/libs/acn/RPTInflator.cpp @@ -0,0 +1,73 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * RPTInflator.cpp + * The Inflator for E1.33 RPT + * Copyright (C) 2023 Peter Newman + */ + +#include "ola/Logging.h" +#include "ola/network/NetworkUtils.h" +#include "libs/acn/RPTInflator.h" + +namespace ola { +namespace acn { + +using ola::network::NetworkToHost; + +/* + * Decode the E1.33 RPT headers. If data is null we're expected to use the + * last header we got. + * @param headers the HeaderSet to add to + * @param data a pointer to the data + * @param length length of the data + * @returns true if successful, false otherwise + */ +bool RPTInflator::DecodeHeader(HeaderSet *headers, + const uint8_t *data, + unsigned int length, + unsigned int *bytes_used) { + if (data) { + // the header bit was set, decode it + if (length >= sizeof(RPTHeader::rpt_pdu_header)) { + RPTHeader::rpt_pdu_header raw_header; + memcpy(&raw_header, data, sizeof(RPTHeader::rpt_pdu_header)); + RPTHeader header( + ola::rdm::UID(raw_header.source_uid), + NetworkToHost(raw_header.source_endpoint), + ola::rdm::UID(raw_header.destination_uid), + NetworkToHost(raw_header.destination_endpoint), + NetworkToHost(raw_header.sequence)); + m_last_header = header; + m_last_header_valid = true; + headers->SetRPTHeader(header); + *bytes_used = sizeof(RPTHeader::rpt_pdu_header); + return true; + } + *bytes_used = 0; + return false; + } + + // use the last header if it exists + *bytes_used = 0; + if (!m_last_header_valid) { + OLA_WARN << "Missing E1.33 RPT Header data"; + return false; + } + headers->SetRPTHeader(m_last_header); + return true; +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/RPTInflator.h b/libs/acn/RPTInflator.h new file mode 100644 index 000000000..1fd4eedaa --- /dev/null +++ b/libs/acn/RPTInflator.h @@ -0,0 +1,58 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * RPTInflator.h + * Interface for the RPTInflator class. + * Copyright (C) 2023 Peter Newman + */ + +#ifndef LIBS_ACN_RPTINFLATOR_H_ +#define LIBS_ACN_RPTINFLATOR_H_ + +#include "ola/acn/ACNVectors.h" +#include "libs/acn/BaseInflator.h" +#include "libs/acn/RPTHeader.h" + +namespace ola { +namespace acn { + +class RPTInflator: public BaseInflator { + friend class RPTInflatorTest; + + public: + RPTInflator() + : BaseInflator(), + m_last_header_valid(false) { + } + ~RPTInflator() {} + + uint32_t Id() const { return ola::acn::VECTOR_ROOT_RPT; } + + protected: + bool DecodeHeader(HeaderSet *headers, + const uint8_t *data, + unsigned int len, + unsigned int *bytes_used); + + void ResetHeaderField() { + m_last_header_valid = false; + } + private: + RPTHeader m_last_header; + bool m_last_header_valid; +}; +} // namespace acn +} // namespace ola +#endif // LIBS_ACN_RPTINFLATOR_H_ diff --git a/libs/acn/RPTInflatorTest.cpp b/libs/acn/RPTInflatorTest.cpp new file mode 100644 index 000000000..f349e53ee --- /dev/null +++ b/libs/acn/RPTInflatorTest.cpp @@ -0,0 +1,131 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * RPTInflatorTest.cpp + * Test fixture for the RPTInflator class + * Copyright (C) 2024 Peter Newman + */ + +#include + +#include "ola/Logging.h" +#include "ola/network/NetworkUtils.h" +#include "ola/rdm/UID.h" +#include "libs/acn/HeaderSet.h" +#include "libs/acn/PDUTestCommon.h" +#include "libs/acn/RPTInflator.h" +#include "libs/acn/RPTPDU.h" +#include "ola/testing/TestUtils.h" + + +namespace ola { +namespace acn { + +using ola::network::HostToNetwork; +using ola::rdm::UID; + +class RPTInflatorTest: public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(RPTInflatorTest); + CPPUNIT_TEST(testDecodeHeader); + CPPUNIT_TEST(testInflatePDU); + CPPUNIT_TEST_SUITE_END(); + + public: + void testDecodeHeader(); + void testInflatePDU(); + private: + static const uint8_t TEST_DATA[]; + static const uint8_t TEST_DATA2[]; +}; + +const uint8_t RPTInflatorTest::TEST_DATA[] = {0, 1, 2, 3, 4, 5}; +const uint8_t RPTInflatorTest::TEST_DATA2[] = {10, 11, 12, 13, 14, 15}; +CPPUNIT_TEST_SUITE_REGISTRATION(RPTInflatorTest); + + +/* + * Check that we can decode headers properly + */ +void RPTInflatorTest::testDecodeHeader() { + RPTHeader::rpt_pdu_header header; + // Need to cast the header before we memset as it's got a constructor to + // ensure the reserved value is set to zero by default + memset(reinterpret_cast(&header), 0, sizeof(header)); + RPTInflator inflator; + HeaderSet header_set, header_set2; + unsigned int bytes_used; + const ola::rdm::UID source_uid = UID(TEST_DATA); + const ola::rdm::UID destination_uid = UID(TEST_DATA2); + + source_uid.Pack(header.source_uid, sizeof(header.source_uid)); + header.source_endpoint = HostToNetwork((uint16_t) 1234u); + destination_uid.Pack(header.destination_uid, sizeof(header.destination_uid)); + header.destination_endpoint = HostToNetwork((uint16_t) 5678u); + header.sequence = HostToNetwork(72650u); + + OLA_ASSERT(inflator.DecodeHeader(&header_set, + reinterpret_cast(&header), + sizeof(header), + &bytes_used)); + OLA_ASSERT_EQ((unsigned int) sizeof(header), bytes_used); + RPTHeader decoded_header = header_set.GetRPTHeader(); + + // try an undersized header + OLA_ASSERT_FALSE(inflator.DecodeHeader( + &header_set, + reinterpret_cast(&header), + static_cast(sizeof(header) - 1), + &bytes_used)); + OLA_ASSERT_EQ((unsigned int) 0, bytes_used); + + // test inheriting the header from the prev call + OLA_ASSERT(inflator.DecodeHeader(&header_set2, NULL, 0, &bytes_used)); + OLA_ASSERT_EQ((unsigned int) 0, bytes_used); + decoded_header = header_set2.GetRPTHeader(); + OLA_ASSERT(source_uid == decoded_header.SourceUID()); + OLA_ASSERT_EQ((uint16_t) 1234, decoded_header.SourceEndpoint()); + OLA_ASSERT(destination_uid == decoded_header.DestinationUID()); + OLA_ASSERT_EQ((uint16_t) 5678, decoded_header.DestinationEndpoint()); + OLA_ASSERT_EQ((uint32_t) 72650, decoded_header.Sequence()); + + inflator.ResetHeaderField(); + OLA_ASSERT_FALSE(inflator.DecodeHeader(&header_set2, NULL, 0, &bytes_used)); + OLA_ASSERT_EQ((unsigned int) 0, bytes_used); +} + + +/* + * Check that we can inflate a RPT PDU that contains other PDUs + */ +void RPTInflatorTest::testInflatePDU() { + RPTHeader header(UID(1, 2), 42, UID(10, 20), 99, 2370); + // TODO(Peter): pass a different type of msg here as well + RPTPDU pdu(3, header, NULL); + OLA_ASSERT_EQ((unsigned int) 28, pdu.Size()); + + unsigned int size = pdu.Size(); + uint8_t *data = new uint8_t[size]; + unsigned int bytes_used = size; + OLA_ASSERT(pdu.Pack(data, &bytes_used)); + OLA_ASSERT_EQ((unsigned int) size, bytes_used); + + RPTInflator inflator; + HeaderSet header_set; + OLA_ASSERT(inflator.InflatePDUBlock(&header_set, data, size)); + OLA_ASSERT(header == header_set.GetRPTHeader()); + delete[] data; +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/RPTNotificationInflator.h b/libs/acn/RPTNotificationInflator.h new file mode 100644 index 000000000..532fb45a5 --- /dev/null +++ b/libs/acn/RPTNotificationInflator.h @@ -0,0 +1,55 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * RPTNotificationInflator.h + * Interface for the RPTNotificationInflator class. + * Copyright (C) 2023 Peter Newman + */ + +#ifndef LIBS_ACN_RPTNOTIFICATIONINFLATOR_H_ +#define LIBS_ACN_RPTNOTIFICATIONINFLATOR_H_ + +#include "ola/acn/ACNVectors.h" +#include "libs/acn/BaseInflator.h" + +namespace ola { +namespace acn { + +class RPTNotificationInflator: public BaseInflator { + friend class RPTNotificationInflatorTest; + + public: + RPTNotificationInflator() + : BaseInflator() { + } + ~RPTNotificationInflator() {} + + uint32_t Id() const { return ola::acn::VECTOR_RPT_NOTIFICATION; } + + protected: + // The 'header' is 0 bytes in length. + bool DecodeHeader(HeaderSet*, + const uint8_t*, + unsigned int, + unsigned int *bytes_used) { + *bytes_used = 0; + return true; + } + + void ResetHeaderField() {} // namespace noop +}; +} // namespace acn +} // namespace ola +#endif // LIBS_ACN_RPTNOTIFICATIONINFLATOR_H_ diff --git a/libs/acn/RPTPDU.cpp b/libs/acn/RPTPDU.cpp new file mode 100644 index 000000000..3a3330e02 --- /dev/null +++ b/libs/acn/RPTPDU.cpp @@ -0,0 +1,139 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * RPTPDU.cpp + * The E1.33 RPT PDU + * Copyright (C) 2024 Peter Newman + */ + + +#include "ola/Logging.h" +#include "ola/base/Array.h" +#include "ola/network/NetworkUtils.h" +#include "libs/acn/RPTPDU.h" + +namespace ola { +namespace acn { + +using ola::io::OutputStream; +using ola::network::HostToNetwork; + +/* + * Size of the header portion. + */ +unsigned int RPTPDU::HeaderSize() const { + return sizeof(RPTHeader::rpt_pdu_header); +} + + +/* + * Size of the data portion + */ +unsigned int RPTPDU::DataSize() const { + return m_pdu ? m_pdu->Size() : 0; +} + + +/* + * Pack the header portion. + */ +bool RPTPDU::PackHeader(uint8_t *data, unsigned int *length) const { + unsigned int header_size = HeaderSize(); + + if (*length < header_size) { + OLA_WARN << "RPTPDU::PackHeader: buffer too small, got " << *length + << " required " << header_size; + *length = 0; + return false; + } + + RPTHeader::rpt_pdu_header header; + m_header.SourceUID().Pack(header.source_uid, sizeof(header.source_uid)); + header.source_endpoint = HostToNetwork(m_header.SourceEndpoint()); + m_header.DestinationUID().Pack(header.destination_uid, + sizeof(header.destination_uid)); + header.destination_endpoint = HostToNetwork(m_header.DestinationEndpoint()); + header.sequence = HostToNetwork(m_header.Sequence()); + *length = sizeof(RPTHeader::rpt_pdu_header); + memcpy(data, &header, *length); + return true; +} + + +/* + * Pack the data portion. + */ +bool RPTPDU::PackData(uint8_t *data, unsigned int *length) const { + if (m_pdu) { + return m_pdu->Pack(data, length); + } + *length = 0; + return true; +} + + +/* + * Pack the header into a buffer. + */ +void RPTPDU::PackHeader(OutputStream *stream) const { + RPTHeader::rpt_pdu_header header; + m_header.SourceUID().Pack(header.source_uid, sizeof(header.source_uid)); + header.source_endpoint = HostToNetwork(m_header.SourceEndpoint()); + m_header.DestinationUID().Pack(header.destination_uid, + sizeof(header.destination_uid)); + header.destination_endpoint = HostToNetwork(m_header.DestinationEndpoint()); + header.sequence = HostToNetwork(m_header.Sequence()); + stream->Write(reinterpret_cast(&header), + sizeof(RPTHeader::rpt_pdu_header)); +} + + +/* + * Pack the data into a buffer + */ +void RPTPDU::PackData(OutputStream *stream) const { + if (m_pdu) { + m_pdu->Write(stream); + } +} + + +void RPTPDU::PrependPDU(ola::io::IOStack *stack, + uint32_t vector, + const ola::rdm::UID &source_uid, + uint16_t source_endpoint, + const ola::rdm::UID &destination_uid, + uint16_t destination_endpoint, + uint32_t sequence_number) { + if (!stack) { + OLA_WARN << "RPTPDU::PrependPDU: missing stack"; + return; + } + + RPTHeader::rpt_pdu_header header; + source_uid.Pack(header.source_uid, sizeof(header.source_uid)); + header.source_endpoint = HostToNetwork(source_endpoint); + destination_uid.Pack(header.destination_uid, sizeof(header.destination_uid)); + header.destination_endpoint = HostToNetwork(destination_endpoint); + header.sequence = HostToNetwork(sequence_number); + stack->Write(reinterpret_cast(&header), + sizeof(RPTHeader::rpt_pdu_header)); + + vector = HostToNetwork(vector); + stack->Write(reinterpret_cast(&vector), sizeof(vector)); + PrependFlagsAndLength(stack, VFLAG_MASK | HFLAG_MASK | DFLAG_MASK, true); +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/RPTPDU.h b/libs/acn/RPTPDU.h new file mode 100644 index 000000000..c35985540 --- /dev/null +++ b/libs/acn/RPTPDU.h @@ -0,0 +1,65 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * RPTPDU.h + * Interface for the E1.33 RPTPDU class + * Copyright (C) 2024 Peter Newman + */ + +#ifndef LIBS_ACN_RPTPDU_H_ +#define LIBS_ACN_RPTPDU_H_ + +#include +#include + +#include "libs/acn/PDU.h" +#include "libs/acn/RPTHeader.h" + +namespace ola { +namespace acn { + +class RPTPDU: public PDU { + public: + RPTPDU(unsigned int vector, + const RPTHeader &header, + const PDU *pdu): + PDU(vector, FOUR_BYTES, true), + m_header(header), + m_pdu(pdu) {} + ~RPTPDU() {} + + unsigned int HeaderSize() const; + unsigned int DataSize() const; + bool PackHeader(uint8_t *data, unsigned int *length) const; + bool PackData(uint8_t *data, unsigned int *length) const; + + void PackHeader(ola::io::OutputStream *stream) const; + void PackData(ola::io::OutputStream *stream) const; + + static void PrependPDU(ola::io::IOStack *stack, + uint32_t vector, + const ola::rdm::UID &source_uid, + uint16_t source_endpoint, + const ola::rdm::UID &destination_uid, + uint16_t destination_endpoint, + uint32_t sequence_number); + + private: + RPTHeader m_header; + const PDU *m_pdu; +}; +} // namespace acn +} // namespace ola +#endif // LIBS_ACN_RPTPDU_H_ diff --git a/libs/acn/RPTPDUTest.cpp b/libs/acn/RPTPDUTest.cpp new file mode 100644 index 000000000..422f6a215 --- /dev/null +++ b/libs/acn/RPTPDUTest.cpp @@ -0,0 +1,191 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * RPTPDUTest.cpp + * Test fixture for the E1.33 RPTPDU class + * Copyright (C) 2024 Peter Newman + */ + +#include + +#include "ola/Logging.h" +#include "ola/io/IOQueue.h" +#include "ola/io/IOStack.h" +#include "ola/io/OutputStream.h" +#include "ola/network/NetworkUtils.h" +#include "ola/rdm/UID.h" +#include "ola/testing/TestUtils.h" +#include "libs/acn/RPTPDU.h" +#include "libs/acn/PDUTestCommon.h" + + +namespace ola { +namespace acn { + +using ola::io::IOQueue; +using ola::io::IOStack; +using ola::io::OutputStream; +using ola::network::HostToNetwork; +using ola::rdm::UID; + +class RPTPDUTest: public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(RPTPDUTest); + CPPUNIT_TEST(testSimpleRPTPDU); + CPPUNIT_TEST(testSimpleRPTPDUToOutputStream); + CPPUNIT_TEST(testPrepend); + CPPUNIT_TEST_SUITE_END(); + + public: + void testSimpleRPTPDU(); + void testSimpleRPTPDUToOutputStream(); + void testPrepend(); + + void setUp() { + ola::InitLogging(ola::OLA_LOG_DEBUG, ola::OLA_LOG_STDERR); + } + + private: + static const unsigned int TEST_VECTOR; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(RPTPDUTest); + +const unsigned int RPTPDUTest::TEST_VECTOR = 39; + + +/* + * Test that packing a RPTPDU without data works. + */ +void RPTPDUTest::testSimpleRPTPDU() { + UID source_uid(1, 2); + UID destination_uid(10, 20); + RPTHeader header(source_uid, 42, destination_uid, 99, 2370); + RPTPDU pdu(TEST_VECTOR, header, NULL); + + OLA_ASSERT_EQ(21u, pdu.HeaderSize()); + OLA_ASSERT_EQ(0u, pdu.DataSize()); + OLA_ASSERT_EQ(28u, pdu.Size()); + + unsigned int size = pdu.Size(); + uint8_t *data = new uint8_t[size]; + unsigned int bytes_used = size; + OLA_ASSERT(pdu.Pack(data, &bytes_used)); + OLA_ASSERT_EQ(size, bytes_used); + + // spot check the data + OLA_ASSERT_EQ((uint8_t) 0xf0, data[0]); + // bytes_used is technically data[1] and data[2] if > 255 + OLA_ASSERT_EQ((uint8_t) bytes_used, data[2]); + unsigned int actual_value; + memcpy(&actual_value, data + 3, sizeof(actual_value)); + OLA_ASSERT_EQ(HostToNetwork(TEST_VECTOR), actual_value); + + uint8_t buffer[UID::LENGTH]; + source_uid.Pack(buffer, sizeof(buffer)); + OLA_ASSERT_DATA_EQUALS(&data[7], UID::LENGTH, buffer, sizeof(buffer)); + destination_uid.Pack(buffer, sizeof(buffer)); + OLA_ASSERT_DATA_EQUALS(&data[15], UID::LENGTH, buffer, sizeof(buffer)); + // sequence number + OLA_ASSERT_EQ((uint8_t) 0, data[23]); + OLA_ASSERT_EQ((uint8_t) 0, data[23 + 1]); + OLA_ASSERT_EQ((unsigned int) 9, (unsigned int) data[23 + 2]); + OLA_ASSERT_EQ((unsigned int) 66, (unsigned int) data[23 + 3]); + + // test undersized buffer + bytes_used = size - 1; + OLA_ASSERT_FALSE(pdu.Pack(data, &bytes_used)); + OLA_ASSERT_EQ(0u, bytes_used); + + // test oversized buffer + bytes_used = size + 1; + OLA_ASSERT(pdu.Pack(data, &bytes_used)); + OLA_ASSERT_EQ(size, bytes_used); + delete[] data; +} + + +/* + * Test that writing to an output stream works. + */ +void RPTPDUTest::testSimpleRPTPDUToOutputStream() { + RPTHeader header(UID(0x0102, 0x03040506), 3456, + UID(0x4050, 0x60708090), 7890, + 2370); + RPTPDU pdu(TEST_VECTOR, header, NULL); + + OLA_ASSERT_EQ(21u, pdu.HeaderSize()); + OLA_ASSERT_EQ(0u, pdu.DataSize()); + OLA_ASSERT_EQ(28u, pdu.Size()); + + IOQueue output; + OutputStream stream(&output); + pdu.Write(&stream); + OLA_ASSERT_EQ(28u, output.Size()); + + uint8_t *pdu_data = new uint8_t[output.Size()]; + unsigned int pdu_size = output.Peek(pdu_data, output.Size()); + OLA_ASSERT_EQ(output.Size(), pdu_size); + + uint8_t EXPECTED[] = { + 0xf0, 0x00, 0x1c, + 0, 0, 0, 39, + 1, 2, 3, 4, 5, 6, // source UID + 13, 128, // source endpoint + 64, 80, 96, 112, 128, 144, // dest UID + 30, 210, // dest endpoint + 0, 0, 9, 66, // sequence number + 0 // reserved + }; + OLA_ASSERT_DATA_EQUALS(EXPECTED, sizeof(EXPECTED), pdu_data, pdu_size); + output.Pop(output.Size()); + delete[] pdu_data; +} + + +void RPTPDUTest::testPrepend() { + IOStack stack; + RPTPDU::PrependPDU(&stack, + TEST_VECTOR, + UID(0x0102, 0x03040506), 3456, + UID(0x4050, 0x60708090), 7890, + 2370); + + unsigned int length = stack.Size(); + uint8_t *buffer = new uint8_t[length]; + OLA_ASSERT(stack.Read(buffer, length)); + + const uint8_t expected_data[] = { + 0xf0, 0x00, 0x1c, + 0, 0, 0, 39, + 1, 2, 3, 4, 5, 6, // source UID + 13, 128, // source endpoint + 64, 80, 96, 112, 128, 144, // dest UID + 30, 210, // dest endpoint + 0, 0, 9, 66, // sequence number + 0 // reserved + }; + OLA_ASSERT_DATA_EQUALS(expected_data, sizeof(expected_data), buffer, length); + + // test null stack + RPTPDU::PrependPDU(NULL, + TEST_VECTOR, + UID(0x0102, 0x03040506), 3456, + UID(0x4050, 0x60708090), 7890, + 2370); + + delete[] buffer; +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/RPTRequestInflator.h b/libs/acn/RPTRequestInflator.h new file mode 100644 index 000000000..ec2cf38de --- /dev/null +++ b/libs/acn/RPTRequestInflator.h @@ -0,0 +1,55 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * RPTRequestInflator.h + * Interface for the RPTRequestInflator class. + * Copyright (C) 2023 Peter Newman + */ + +#ifndef LIBS_ACN_RPTREQUESTINFLATOR_H_ +#define LIBS_ACN_RPTREQUESTINFLATOR_H_ + +#include "ola/acn/ACNVectors.h" +#include "libs/acn/BaseInflator.h" + +namespace ola { +namespace acn { + +class RPTRequestInflator: public BaseInflator { + friend class RPTRequestInflatorTest; + + public: + RPTRequestInflator() + : BaseInflator() { + } + ~RPTRequestInflator() {} + + uint32_t Id() const { return ola::acn::VECTOR_RPT_REQUEST; } + + protected: + // The 'header' is 0 bytes in length. + bool DecodeHeader(HeaderSet*, + const uint8_t*, + unsigned int, + unsigned int *bytes_used) { + *bytes_used = 0; + return true; + } + + void ResetHeaderField() {} // namespace noop +}; +} // namespace acn +} // namespace ola +#endif // LIBS_ACN_RPTREQUESTINFLATOR_H_ diff --git a/libs/acn/RPTRequestPDU.cpp b/libs/acn/RPTRequestPDU.cpp new file mode 100644 index 000000000..db6179755 --- /dev/null +++ b/libs/acn/RPTRequestPDU.cpp @@ -0,0 +1,44 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * RPTRequestPDU.cpp + * The RPTRequestPDU + * Copyright (C) 2024 Peter Newman + */ + +#include "libs/acn/RPTRequestPDU.h" + +#include +#include + +namespace ola { +namespace acn { + +using ola::io::OutputStream; +using ola::network::HostToNetwork; + +void RPTRequestPDU::PrependPDU(ola::io::IOStack *stack) { + if (!stack) { + OLA_WARN << "RPTRequestPDU::PrependPDU: missing stack"; + return; + } + + uint32_t vector = HostToNetwork( + static_cast(VECTOR_REQUEST_RDM_CMD)); + stack->Write(reinterpret_cast(&vector), sizeof(vector)); + PrependFlagsAndLength(stack, VFLAG_MASK | HFLAG_MASK | DFLAG_MASK, true); +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/RPTRequestPDU.h b/libs/acn/RPTRequestPDU.h new file mode 100644 index 000000000..97a490cd4 --- /dev/null +++ b/libs/acn/RPTRequestPDU.h @@ -0,0 +1,56 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * RPTRequestPDU.h + * The RPTRequestPDU class + * Copyright (C) 2024 Peter Newman + */ + +#ifndef LIBS_ACN_RPTREQUESTPDU_H_ +#define LIBS_ACN_RPTREQUESTPDU_H_ + +#include + +#include "libs/acn/PDU.h" + +namespace ola { +namespace acn { + +class RPTRequestPDU : public PDU { + public: + explicit RPTRequestPDU(unsigned int vector): + PDU(vector, FOUR_BYTES, true) {} + + unsigned int HeaderSize() const { return 0; } + bool PackHeader(OLA_UNUSED uint8_t *data, + unsigned int *length) const { + *length = 0; + return true; + } + void PackHeader(OLA_UNUSED ola::io::OutputStream *stream) const {} + + unsigned int DataSize() const { return 0; } + bool PackData(OLA_UNUSED uint8_t *data, + unsigned int *length) const { + *length = 0; + return true; + } + void PackData(OLA_UNUSED ola::io::OutputStream *stream) const {} + + static void PrependPDU(ola::io::IOStack *stack); +}; +} // namespace acn +} // namespace ola +#endif // LIBS_ACN_RPTREQUESTPDU_H_ diff --git a/libs/acn/RPTRequestPDUTest.cpp b/libs/acn/RPTRequestPDUTest.cpp new file mode 100644 index 000000000..f40b30b7a --- /dev/null +++ b/libs/acn/RPTRequestPDUTest.cpp @@ -0,0 +1,149 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * RPTRequestPDUTest.cpp + * Test fixture for the RPTRequestPDU class + * Copyright (C) 2024 Peter Newman + */ + +#include +#include +#include + +#include "ola/Logging.h" +#include "ola/io/IOQueue.h" +#include "ola/io/IOStack.h" +#include "ola/io/OutputStream.h" +#include "ola/network/NetworkUtils.h" +#include "ola/testing/TestUtils.h" +#include "libs/acn/PDUTestCommon.h" +#include "libs/acn/RPTRequestPDU.h" + +namespace ola { +namespace acn { + +using ola::acn::RPTRequestPDU; +using ola::io::IOQueue; +using ola::io::IOStack; +using ola::io::OutputStream; +using ola::network::HostToNetwork; + +class RPTRequestPDUTest: public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(RPTRequestPDUTest); + CPPUNIT_TEST(testSimpleRPTRequestPDU); + CPPUNIT_TEST(testSimpleRPTRequestPDUToOutputStream); + CPPUNIT_TEST(testPrepend); + CPPUNIT_TEST_SUITE_END(); + + public: + void testSimpleRPTRequestPDU(); + void testSimpleRPTRequestPDUToOutputStream(); + void testPrepend(); + + private: + static const uint32_t TEST_VECTOR; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(RPTRequestPDUTest); + +const uint32_t RPTRequestPDUTest::TEST_VECTOR = 39; + + +/* + * Test that packing a RPTRequestPDU works. + */ +void RPTRequestPDUTest::testSimpleRPTRequestPDU() { + RPTRequestPDU pdu(TEST_VECTOR); + + OLA_ASSERT_EQ(0u, pdu.HeaderSize()); + OLA_ASSERT_EQ(0u, pdu.DataSize()); + OLA_ASSERT_EQ(7u, pdu.Size()); + + unsigned int size = pdu.Size(); + uint8_t *data = new uint8_t[size]; + unsigned int bytes_used = size; + OLA_ASSERT(pdu.Pack(data, &bytes_used)); + OLA_ASSERT_EQ(size, bytes_used); + + // spot check the data + OLA_ASSERT_EQ((uint8_t) 0xf0, data[0]); + // bytes_used is technically part of data[0-2] if > 255 + OLA_ASSERT_EQ((uint8_t) bytes_used, data[2]); + uint32_t actual_value; + memcpy(&actual_value, data + 3, sizeof(actual_value)); + OLA_ASSERT_EQ(HostToNetwork(TEST_VECTOR), actual_value); + + // test undersized buffer + bytes_used = size - 1; + OLA_ASSERT_FALSE(pdu.Pack(data, &bytes_used)); + OLA_ASSERT_EQ(0u, bytes_used); + + // test oversized buffer + bytes_used = size + 1; + OLA_ASSERT(pdu.Pack(data, &bytes_used)); + OLA_ASSERT_EQ(size, bytes_used); + delete[] data; +} + +/* + * Test that writing to an output stream works. + */ +void RPTRequestPDUTest::testSimpleRPTRequestPDUToOutputStream() { + RPTRequestPDU pdu(TEST_VECTOR); + + OLA_ASSERT_EQ(0u, pdu.HeaderSize()); + OLA_ASSERT_EQ(0u, pdu.DataSize()); + OLA_ASSERT_EQ(7u, pdu.Size()); + + IOQueue output; + OutputStream stream(&output); + pdu.Write(&stream); + OLA_ASSERT_EQ(7u, output.Size()); + + uint8_t *pdu_data = new uint8_t[output.Size()]; + unsigned int pdu_size = output.Peek(pdu_data, output.Size()); + OLA_ASSERT_EQ(output.Size(), pdu_size); + + uint8_t EXPECTED[] = { + 0xf0, 0x00, 0x07, + 0, 0, 0, 39 + }; + OLA_ASSERT_DATA_EQUALS(EXPECTED, sizeof(EXPECTED), pdu_data, pdu_size); + output.Pop(output.Size()); + delete[] pdu_data; +} + + +void RPTRequestPDUTest::testPrepend() { + IOStack stack; + RPTRequestPDU::PrependPDU(&stack); + + unsigned int length = stack.Size(); + uint8_t *buffer = new uint8_t[length]; + OLA_ASSERT(stack.Read(buffer, length)); + + const uint8_t expected_data[] = { + 0xf0, 0x00, 0x07, + 0, 0, 0, 0x01 + }; + OLA_ASSERT_DATA_EQUALS(expected_data, sizeof(expected_data), buffer, length); + + // test null stack + RPTRequestPDU::PrependPDU(NULL); + + delete[] buffer; +} +} // namespace acn +} // namespace ola diff --git a/tools/e133/E133HealthCheckedConnection.cpp b/tools/e133/E133HealthCheckedConnection.cpp index f03e7e5dc..c6dd17f66 100644 --- a/tools/e133/E133HealthCheckedConnection.cpp +++ b/tools/e133/E133HealthCheckedConnection.cpp @@ -39,8 +39,9 @@ E133HealthCheckedConnection::E133HealthCheckedConnection( ola::io::NonBlockingSender *message_queue, ola::SingleUseCallback0 *on_timeout, ola::thread::SchedulingExecutorInterface *scheduler, - const ola::TimeInterval heartbeat_interval) - : HealthCheckedConnection(scheduler, heartbeat_interval), + const ola::TimeInterval heartbeat_interval, + const ola::TimeInterval timeout_interval) + : HealthCheckedConnection(scheduler, heartbeat_interval, timeout_interval), m_message_builder(message_builder), m_message_queue(message_queue), m_on_timeout(on_timeout), @@ -52,8 +53,9 @@ E133HealthCheckedConnection::E133HealthCheckedConnection( * Send a E1.33 heartbeat */ void E133HealthCheckedConnection::SendHeartbeat() { + OLA_DEBUG << "Sending heartbeat"; IOStack packet(m_message_builder->pool()); - m_message_builder->BuildNullTCPPacket(&packet); + m_message_builder->BuildBrokerNullTCPPacket(&packet); m_message_queue->SendMessage(&packet); } diff --git a/tools/e133/E133HealthCheckedConnection.h b/tools/e133/E133HealthCheckedConnection.h index 5e7b61453..3e6712cb3 100644 --- a/tools/e133/E133HealthCheckedConnection.h +++ b/tools/e133/E133HealthCheckedConnection.h @@ -21,9 +21,12 @@ * same health checking logic (and agree on heartbeat intervals) for this to * work correctly. * - * Even though this is called a E1.33 Health Checked Connection, it doesn't - * actually rely on E1.33 at all. You can use it with any ACN based protocol - * since it just sends PDUs with a ROOT_VECTOR_NULL as heartbeat messages. + * This is a E1.33 Health Checked Connection as it sends E1.33 Broker NULL PDUs + * using VECTOR_BROKER_NULL, but it also accepts any ACN root layer PDUs as a + * positive indication the connection is healthy. + * + * You could use it with any ACN based protocol by subclassing it and sending + * heartbeat messages of ROOT_VECTOR_NULL via SendHeartbeat instead. */ #ifndef TOOLS_E133_E133HEALTHCHECKEDCONNECTION_H_ @@ -52,7 +55,9 @@ class E133HealthCheckedConnection ola::SingleUseCallback0 *on_timeout, ola::thread::SchedulingExecutorInterface *scheduler, const ola::TimeInterval heartbeat_interval = - ola::TimeInterval(E133_TCP_HEARTBEAT_INTERVAL, 0)); + ola::TimeInterval(E133_TCP_HEARTBEAT_INTERVAL, 0), + const ola::TimeInterval timeout_interval = + ola::TimeInterval(E133_HEARTBEAT_TIMEOUT, 0)); void SendHeartbeat(); void HeartbeatTimeout(); @@ -64,6 +69,8 @@ class E133HealthCheckedConnection ola::thread::SchedulingExecutorInterface *m_executor; // The default interval in seconds for sending heartbeat messages. - static const unsigned int E133_TCP_HEARTBEAT_INTERVAL = 5; + static const unsigned int E133_TCP_HEARTBEAT_INTERVAL = 15; + // The default interval in seconds before timing out. + static const unsigned int E133_HEARTBEAT_TIMEOUT = 45; }; #endif // TOOLS_E133_E133HEALTHCHECKEDCONNECTION_H_ diff --git a/tools/e133/MessageBuilder.cpp b/tools/e133/MessageBuilder.cpp index a192b45bf..41d352539 100644 --- a/tools/e133/MessageBuilder.cpp +++ b/tools/e133/MessageBuilder.cpp @@ -24,10 +24,14 @@ #include "ola/acn/CID.h" #include "ola/e133/MessageBuilder.h" #include "ola/io/IOStack.h" +#include "ola/rdm/RDMCommandSerializer.h" +#include "libs/acn/BrokerPDU.h" #include "libs/acn/E133PDU.h" #include "libs/acn/RDMPDU.h" #include "libs/acn/RootPDU.h" +#include "libs/acn/RPTPDU.h" +#include "libs/acn/RPTRequestPDU.h" #include "libs/acn/E133StatusPDU.h" #include "libs/acn/PreamblePacker.h" @@ -36,9 +40,11 @@ namespace e133 { using ola::acn::CID; using ola::io::IOStack; +using ola::acn::BrokerPDU; using ola::acn::E133PDU; using ola::acn::PreamblePacker; using ola::acn::RootPDU; +using ola::acn::RPTPDU; MessageBuilder::MessageBuilder(const CID &cid, const string &source_name) @@ -58,6 +64,26 @@ void MessageBuilder::PrependRDMHeader(IOStack *packet) { } +/** + * Build a TCP E1.33 RDM Command PDU response. + */ +void MessageBuilder::BuildTCPRDMCommandPDU(IOStack *packet, + ola::rdm::RDMRequest *request, + uint16_t source_endpoint_id, + uint16_t destination_endpoint_id, + uint32_t sequence_number) { + ola::rdm::RDMCommandSerializer::Write(*request, packet); + ola::acn::RDMPDU::PrependPDU(packet); + ola::acn::RPTRequestPDU::PrependPDU(packet); + RPTPDU::PrependPDU(packet, ola::acn::VECTOR_RPT_REQUEST, + request->SourceUID(), source_endpoint_id, + request->DestinationUID(), destination_endpoint_id, + sequence_number); + RootPDU::PrependPDU(packet, ola::acn::VECTOR_ROOT_RPT, m_cid, true); + PreamblePacker::AddTCPPreamble(packet); +} + + /** * Build a NULL TCP packet. These packets can be used for heartbeats. */ @@ -67,6 +93,26 @@ void MessageBuilder::BuildNullTCPPacket(IOStack *packet) { } +/** + * Build a Broker Fetch Client List TCP packet. + */ +void MessageBuilder::BuildBrokerFetchClientListTCPPacket(IOStack *packet) { + BrokerPDU::PrependPDU(packet, ola::acn::VECTOR_BROKER_FETCH_CLIENT_LIST); + RootPDU::PrependPDU(packet, ola::acn::VECTOR_ROOT_BROKER, m_cid, true); + PreamblePacker::AddTCPPreamble(packet); +} + + +/** + * Build a Broker NULL TCP packet. These packets can be used for broker heartbeats. + */ +void MessageBuilder::BuildBrokerNullTCPPacket(IOStack *packet) { + BrokerPDU::PrependPDU(packet, ola::acn::VECTOR_BROKER_NULL); + RootPDU::PrependPDU(packet, ola::acn::VECTOR_ROOT_BROKER, m_cid, true); + PreamblePacker::AddTCPPreamble(packet); +} + + /** * Build a TCP E1.33 Status PDU response. This should really only be used with * SC_E133_ACK.