diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 554baa985f..296da8211b 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -21,7 +21,7 @@ jobs: pkg: "gcc g++" - id: "distcheck-debian-stable-amd64-gcc" task: "distcheck" - configure-args: "--enable-ja-rule --enable-e133 --enable-rdm-tests --enable-java-libs" + configure-args: "--enable-ja-rule --enable-e133 --enable-rdm-tests" # TODO(Perry): Fix Debian 12 OOM issue on GitHub Actions container: "debian:stable" compiler: @@ -30,7 +30,7 @@ jobs: pkg: "gcc g++" - id: "distcheck-debian-stable-amd64-clang" task: "distcheck" - configure-args: "--enable-ja-rule --enable-e133 --enable-rdm-tests --enable-java-libs" + configure-args: "--enable-ja-rule --enable-e133 --enable-rdm-tests" # TODO(Perry): Fix Debian 12 OOM issue on GitHub Actions container: "debian:stable" compiler: @@ -50,13 +50,13 @@ jobs: run: apt-get update -y # See comments beginning at # https://github.com/actions/runner/issues/763#issuecomment-1435474884 - # Without Git, actions/checkout@v3 will resort to REST and will not + # Without Git, actions/checkout@v4 will resort to REST and will not # create a .git folder or .git.config. The Problem Matcher looks for # .git/config to find where the root of the repo is, so it must be # present. - name: Install Git run: apt-get -y install git - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install build tools shell: bash run: | @@ -90,17 +90,18 @@ jobs: - name: Autoreconf run: sudo --preserve-env -u builduser env "PATH=$PATH" autoreconf -i - name: Set configure arguments + # Env var name DISTCHECK_CONFIGURE_FLAGS must be used, see #1881 and #1883 run: | - echo "GH_OLA_CONFIGURE_ARGS=${{ matrix.configure-args }}" >> $GITHUB_ENV + echo "DISTCHECK_CONFIGURE_FLAGS=${{ matrix.configure-args }}" >> $GITHUB_ENV - name: Set additional Linux configure arguments if: runner.os == 'Linux' # Silence all deprecated declarations on Linux due to auto_ptr making the build log too long run: | - echo "GH_OLA_CONFIGURE_ARGS=$GH_OLA_CONFIGURE_ARGS CPPFLAGS=-Wno-deprecated-declarations" >> $GITHUB_ENV + echo "DISTCHECK_CONFIGURE_FLAGS=$DISTCHECK_CONFIGURE_FLAGS CPPFLAGS=-Wno-deprecated-declarations" >> $GITHUB_ENV - name: Print configure command - run: echo "./configure $GH_OLA_CONFIGURE_ARGS" + run: echo "./configure $DISTCHECK_CONFIGURE_FLAGS" - name: Configure - run: sudo --preserve-env -u builduser env "PATH=$PATH" ./configure $GH_OLA_CONFIGURE_ARGS + run: sudo --preserve-env -u builduser env "PATH=$PATH" ./configure $DISTCHECK_CONFIGURE_FLAGS - name: ${{ matrix.task }} run: sudo --preserve-env -u builduser env "PATH=$PATH" make ${{ matrix.task }} -j${{ steps.num-cpu-cores.outputs.NUM_CPU_CORES }} VERBOSE=1 - name: Display structure of the built files @@ -118,14 +119,14 @@ jobs: if: always() run: sha256sum ola-*.tar.gz - name: Upload source tree artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: always() with: name: ola-${{ matrix.id }}-source-tree path: ola-${{ matrix.id }}-source-tree.tar.gz - name: Upload built artifact if: matrix.task == 'distcheck' || matrix.task == 'dist' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ola-${{ matrix.id }}-dist path: | @@ -146,7 +147,7 @@ jobs: flag-name: ${{ matrix.id }} - name: Upload coverage artifacts if: always() && matrix.task == 'coverage' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ola-${{ matrix.id }}-coverage path: coverage/ @@ -164,7 +165,7 @@ jobs: - id: "distcheck-debian-stable-amd64-clang" steps: - name: Download built source tree archive - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ola-${{ matrix.id }}-source-tree path: . diff --git a/.github/workflows/debian.yml b/.github/workflows/debian.yml index dbf285ba85..7fff3d801f 100644 --- a/.github/workflows/debian.yml +++ b/.github/workflows/debian.yml @@ -21,13 +21,13 @@ jobs: run: apt-get update -y # See comments beginning at # https://github.com/actions/runner/issues/763#issuecomment-1435474884 - # Without Git, actions/checkout@v3 will resort to REST and will not + # Without Git, actions/checkout@v4 will resort to REST and will not # create a .git folder or .git.config. The Problem Matcher looks for # .git/config to find where the root of the repo is, so it must be # present. - name: Install Git run: apt-get -y install git - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install build tools run: apt-get -y install devscripts adduser fakeroot sudo - name: Install build dependencies @@ -53,7 +53,7 @@ jobs: shell: bash run: find . -type f -exec sha256sum {} \; working-directory: built - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: always() with: name: ola-built-debian-${{ matrix.image_tag }}-${{ matrix.architecture }} @@ -72,7 +72,7 @@ jobs: steps: - uses: actions/checkout@master - name: Download build artifact - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ola-built-debian-${{ matrix.image_tag }}-${{ matrix.architecture }} path: built @@ -85,7 +85,7 @@ jobs: run: apt-get -y install autopkgtest - name: Test run: autopkgtest --output-dir=test-output built/*ges -- null - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: always() # Always upload the test output, even on failed tests with: name: ola-test-output-debian-${{ matrix.image_tag }}-${{ matrix.architecture }} diff --git a/.github/workflows/isort.yml b/.github/workflows/isort.yml index f314e5b555..9aff6b16d1 100644 --- a/.github/workflows/isort.yml +++ b/.github/workflows/isort.yml @@ -8,5 +8,5 @@ jobs: isort: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: isort/isort-action@v1 diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 731f617f5c..2aeeb8ca77 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -10,13 +10,13 @@ jobs: run: apt-get update -y # See comments beginning at # https://github.com/actions/runner/issues/763#issuecomment-1435474884 - # Without Git, actions/checkout@v3 will resort to REST and will not + # Without Git, actions/checkout@v4 will resort to REST and will not # create a .git folder or .git.config. The Problem Matcher looks for # .git/config to find where the root of the repo is, so it must be # present. - name: Install Git run: apt-get -y install git - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install build tools shell: bash run: | @@ -56,7 +56,7 @@ jobs: tar --exclude=ola-debian-stable-built-source-tree.tar.gz -cvzf ola-debian-stable-built-source-tree.tar.gz . - name: SHA256 artifact archive run: sha256sum ola-debian-stable-built-source-tree.tar.gz - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ola-debian-stable-built-source-tree path: ola-debian-stable-built-source-tree.tar.gz @@ -67,7 +67,7 @@ jobs: needs: build steps: - name: Download built source tree archive - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ola-debian-stable-built-source-tree path: . @@ -97,7 +97,7 @@ jobs: needs: build steps: - name: Download built source tree archive - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ola-debian-stable-built-source-tree path: . @@ -123,7 +123,7 @@ jobs: needs: build steps: - name: Download built source tree archive - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ola-debian-stable-built-source-tree path: . @@ -160,7 +160,7 @@ jobs: needs: build steps: - name: Download built source tree archive - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ola-debian-stable-built-source-tree path: . @@ -197,7 +197,7 @@ jobs: needs: build steps: - name: Download built source tree archive - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ola-debian-stable-built-source-tree path: . @@ -226,7 +226,7 @@ jobs: needs: build steps: - name: Download built source tree archive - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ola-debian-stable-built-source-tree path: . @@ -260,9 +260,9 @@ jobs: name: weblint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Node.js v18 - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 18 # LTS cache: 'npm' diff --git a/.travis-ci.sh b/.travis-ci.sh index 3d1e0c758b..94a93939a7 100755 --- a/.travis-ci.sh +++ b/.travis-ci.sh @@ -291,6 +291,7 @@ elif [[ $TASK = 'pychecker-wip' ]]; then pychecker --quiet --limit 500 --blacklist $PYCHECKER_BLACKLIST $(find ./ -name "*.py" -and ! \( -name "*_pb2.py" -or -name "OlaClient.py" -or -name "ola_candidate_ports.py" \) | xargs) else # Otherwise compile and check as normal + # Env var name DISTCHECK_CONFIGURE_FLAGS must be used, see #1881 and #1883 if [[ "$TRAVIS_OS_NAME" = "linux" ]]; then # Silence all deprecated declarations on Linux due to auto_ptr making the build log too long export DISTCHECK_CONFIGURE_FLAGS='--enable-rdm-tests --enable-java-libs --enable-ja-rule --enable-e133 CPPFLAGS=-Wno-deprecated-declarations' diff --git a/Makefile.am b/Makefile.am index 64dff2c0f4..3ba22470bd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -272,8 +272,10 @@ COVERAGE_OUTPUTS = --txt coverage/coverage.txt \ --cobertura coverage/coverage.cobertura.xml \ --html-details coverage/details.html/coverage.details.html \ --coveralls coverage/coverage.coveralls.json -COVERAGE_GCOV_EXE=--gcov-executable /usr/bin/gcov -COVERAGE_FILTERS=-e '.*Test\.cpp$$' \ +# See https://gcovr.com/en/stable/guide/gcov_parser.html#negative-hit-counts +COVERAGE_FLAGS = --gcov-ignore-parse-errors=negative_hits.warn_once_per_file +COVERAGE_GCOV_EXE = --gcov-executable /usr/bin/gcov +COVERAGE_FILTERS = -e '.*Test\.cpp$$' \ -e '.*\.pb\.cc$$' \ -e '.*\.pb\.cpp$$' \ -e '.*\.pb\.h$$' \ @@ -288,7 +290,7 @@ if !BUILD_GCOV else if FOUND_GCOVR mkdir -p coverage/details.html/ - gcovr --print-summary $(COVERAGE_OUTPUTS) $(COVERAGE_GCOV_EXE) --root . $(COVERAGE_FILTERS) + gcovr $(COVERAGE_FLAGS) --print-summary $(COVERAGE_OUTPUTS) $(COVERAGE_GCOV_EXE) --root . $(COVERAGE_FILTERS) else $(error gcovr not found. Install gcovr (e.g. via pip for the latest version) and re-run configure.) endif diff --git a/NEWS b/NEWS index 7609e7de5e..6a58b4a0d9 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -x/y/2023 ola-0.10.10 +x/y/2024 ola-0.10.10 Features: * diff --git a/README.developer b/README.developer index e989e800df..309ad07a92 100644 --- a/README.developer +++ b/README.developer @@ -55,7 +55,7 @@ The release lifecycle is: - New feature work occurs on the master branch. - Once the new features are considered stable or enough time has passed, a new minor release branch will be created, e.g. 0.10. -- The minor release branch will be stablized with bugfixes, these bug fixes +- The minor release branch will be stabilized with bugfixes, these bug fixes will also be merged back into master. - Once declared stable, a new patch branch 0 will be created e.g. 0.10.0 - Release specific changes like the version number, debian files etc. will be diff --git a/common/io/IOStackTest.cpp b/common/io/IOStackTest.cpp index c2ce9935b0..8563f9892a 100644 --- a/common/io/IOStackTest.cpp +++ b/common/io/IOStackTest.cpp @@ -207,7 +207,7 @@ void IOStackTest::testAppendToQueue() { } /** - * Confirm we re-use blocks + * Confirm we reuse blocks */ void IOStackTest::testBlockReuse() { MemoryBlockPool pool(4); diff --git a/common/io/PollerInterface.h b/common/io/PollerInterface.h index 003106f3b3..cde0f2c39b 100644 --- a/common/io/PollerInterface.h +++ b/common/io/PollerInterface.h @@ -51,7 +51,7 @@ namespace io { * reentrant. Calling any of the read / write / close actions may in turn add / * remove descriptors, including the descriptor the method was itself called * on. There are tests in SelectServerTest.cpp to exercise some of these cases - * but implementors need to be careful. + * but implementers need to be careful. * * @warning * For example, if Poll() iterates over a set of Descriptors and calls diff --git a/common/messaging/DescriptorTest.cpp b/common/messaging/DescriptorTest.cpp index eb7fcdbcc0..37cce39c97 100644 --- a/common/messaging/DescriptorTest.cpp +++ b/common/messaging/DescriptorTest.cpp @@ -34,6 +34,8 @@ using ola::messaging::BoolFieldDescriptor; using ola::messaging::FieldDescriptor; using ola::messaging::FieldDescriptorGroup; using ola::messaging::IPV4FieldDescriptor; +using ola::messaging::IPV6FieldDescriptor; +using ola::messaging::MACFieldDescriptor; using ola::messaging::StringFieldDescriptor; using ola::messaging::UIDFieldDescriptor; using ola::messaging::UInt16FieldDescriptor; @@ -76,6 +78,20 @@ void DescriptorTest::testFieldDescriptors() { OLA_ASSERT_TRUE(ipv4_descriptor.LimitedSize()); OLA_ASSERT_EQ(4u, ipv4_descriptor.MaxSize()); + // IPv6 address + IPV6FieldDescriptor ipv6_descriptor("ipv6"); + OLA_ASSERT_EQ(string("ipv6"), ipv6_descriptor.Name()); + OLA_ASSERT_TRUE(ipv6_descriptor.FixedSize()); + OLA_ASSERT_TRUE(ipv6_descriptor.LimitedSize()); + OLA_ASSERT_EQ(16u, ipv6_descriptor.MaxSize()); + + // MAC address + MACFieldDescriptor mac_descriptor("mac"); + OLA_ASSERT_EQ(string("mac"), mac_descriptor.Name()); + OLA_ASSERT_TRUE(mac_descriptor.FixedSize()); + OLA_ASSERT_TRUE(mac_descriptor.LimitedSize()); + OLA_ASSERT_EQ(6u, mac_descriptor.MaxSize()); + // UID UIDFieldDescriptor uid_descriptor("uid"); OLA_ASSERT_EQ(string("uid"), uid_descriptor.Name()); diff --git a/common/messaging/MessagePrinter.cpp b/common/messaging/MessagePrinter.cpp index 07226b96fe..7123c15782 100644 --- a/common/messaging/MessagePrinter.cpp +++ b/common/messaging/MessagePrinter.cpp @@ -45,37 +45,44 @@ string MessagePrinter::AsString(const Message *message) { void GenericMessagePrinter::Visit(const BoolMessageField *message) { - Stream() << string(m_indent, ' ') << - TransformLabel(message->GetDescriptor()->Name()) << ": " - << (message->Value() ? "true" : "false") << endl; + Stream() << string(m_indent, ' ') + << TransformLabel(message->GetDescriptor()->Name()) << ": " + << (message->Value() ? "true" : "false") << endl; } void GenericMessagePrinter::Visit(const IPV4MessageField *message) { - Stream() << string(m_indent, ' ') << - TransformLabel(message->GetDescriptor()->Name()) << ": " - << message->Value() << endl; + Stream() << string(m_indent, ' ') + << TransformLabel(message->GetDescriptor()->Name()) << ": " + << message->Value() << endl; +} + + +void GenericMessagePrinter::Visit(const IPV6MessageField *message) { + Stream() << string(m_indent, ' ') + << TransformLabel(message->GetDescriptor()->Name()) << ": " + << message->Value() << endl; } void GenericMessagePrinter::Visit(const MACMessageField *message) { - Stream() << string(m_indent, ' ') << - TransformLabel(message->GetDescriptor()->Name()) << ": " - << message->Value().ToString() << endl; + Stream() << string(m_indent, ' ') + << TransformLabel(message->GetDescriptor()->Name()) << ": " + << message->Value().ToString() << endl; } void GenericMessagePrinter::Visit(const UIDMessageField *message) { - Stream() << string(m_indent, ' ') << - TransformLabel(message->GetDescriptor()->Name()) << ": " - << message->Value().ToString() << endl; + Stream() << string(m_indent, ' ') + << TransformLabel(message->GetDescriptor()->Name()) << ": " + << message->Value().ToString() << endl; } void GenericMessagePrinter::Visit(const StringMessageField *message) { - Stream() << string(m_indent, ' ') << - TransformLabel(message->GetDescriptor()->Name()) << ": " - << EncodeString(message->Value()) << endl; + Stream() << string(m_indent, ' ') + << TransformLabel(message->GetDescriptor()->Name()) << ": " + << EncodeString(message->Value()) << endl; } @@ -134,8 +141,8 @@ void GenericMessagePrinter::Visit(const BasicMessageField *message) { void GenericMessagePrinter::Visit(const GroupMessageField *message) { - Stream() << string(m_indent, ' ') << - TransformLabel(message->GetDescriptor()->Name()) << " {" << endl; + Stream() << string(m_indent, ' ') + << TransformLabel(message->GetDescriptor()->Name()) << " {" << endl; m_indent += m_indent_size; } @@ -178,8 +185,9 @@ void GenericMessagePrinter::AppendInt(const string &name, void GenericMessagePrinter::AppendMultiplier(int8_t multiplier) { - if (multiplier) + if (multiplier) { Stream() << " x 10 ^ " << static_cast(multiplier); + } } } // namespace messaging } // namespace ola diff --git a/common/messaging/MessagePrinterTest.cpp b/common/messaging/MessagePrinterTest.cpp index a096ab628d..5484370aa0 100644 --- a/common/messaging/MessagePrinterTest.cpp +++ b/common/messaging/MessagePrinterTest.cpp @@ -32,6 +32,8 @@ using std::string; using std::vector; +using ola::network::IPV6Address; +using ola::network::MACAddress; using ola::rdm::UID; @@ -39,10 +41,14 @@ using ola::messaging::BoolFieldDescriptor; using ola::messaging::BoolMessageField; using ola::messaging::FieldDescriptor; using ola::messaging::FieldDescriptorGroup; +using ola::messaging::GenericMessagePrinter; using ola::messaging::GroupMessageField; using ola::messaging::IPV4FieldDescriptor; -using ola::messaging::GenericMessagePrinter; using ola::messaging::IPV4MessageField; +using ola::messaging::IPV6FieldDescriptor; +using ola::messaging::IPV6MessageField; +using ola::messaging::MACFieldDescriptor; +using ola::messaging::MACMessageField; using ola::messaging::Int16FieldDescriptor; using ola::messaging::Int16MessageField; using ola::messaging::Int8FieldDescriptor; @@ -87,6 +93,8 @@ void GenericMessagePrinterTest::testSimplePrinter() { // setup some fields BoolFieldDescriptor bool_descriptor("On/Off"); IPV4FieldDescriptor ipv4_descriptor("ip"); + IPV6FieldDescriptor ipv6_descriptor("ipv6"); + MACFieldDescriptor mac_descriptor("mac"); UIDFieldDescriptor uid_descriptor("uid"); StringFieldDescriptor string_descriptor("Name", 0, 32); UInt32FieldDescriptor uint32_descriptor("Id"); @@ -100,6 +108,12 @@ void GenericMessagePrinterTest::testSimplePrinter() { fields.push_back( new IPV4MessageField(&ipv4_descriptor, ola::network::HostToNetwork(0x0a000001))); + fields.push_back( + new IPV6MessageField(&ipv6_descriptor, + IPV6Address::FromStringOrDie("::ffff:192.168.0.1"))); + fields.push_back( + new MACMessageField(&mac_descriptor, + MACAddress::FromStringOrDie("01:23:45:67:89:ab"))); fields.push_back(new UIDMessageField(&uid_descriptor, UID(0x7a70, 1))); fields.push_back(new StringMessageField(&string_descriptor, "foobar")); fields.push_back(new UInt32MessageField(&uint32_descriptor, 42)); @@ -109,7 +123,8 @@ void GenericMessagePrinterTest::testSimplePrinter() { Message message(fields); string expected = ( - "On/Off: false\nip: 10.0.0.1\nuid: 7a70:00000001\nName: foobar\nId: 42\n" + "On/Off: false\nip: 10.0.0.1\nipv6: ::ffff:192.168.0.1\n" + "mac: 01:23:45:67:89:ab\nuid: 7a70:00000001\nName: foobar\nId: 42\n" "Count: 4 x 10 ^ -3\nDelta: 10 x 10 ^ 1\nRate: 10 x 10 ^ -1\n"); OLA_ASSERT_EQ(expected, m_printer.AsString(&message)); } diff --git a/common/messaging/SchemaPrinter.cpp b/common/messaging/SchemaPrinter.cpp index ba8258ef92..62b07146b2 100644 --- a/common/messaging/SchemaPrinter.cpp +++ b/common/messaging/SchemaPrinter.cpp @@ -43,6 +43,12 @@ void SchemaPrinter::Visit(const IPV4FieldDescriptor *descriptor) { } +void SchemaPrinter::Visit(const IPV6FieldDescriptor *descriptor) { + m_str << string(m_indent, ' ') << descriptor->Name() << ": IPv6 address" + << endl; +} + + void SchemaPrinter::Visit(const MACFieldDescriptor *descriptor) { m_str << string(m_indent, ' ') << descriptor->Name() << ": MAC" << endl; } diff --git a/common/messaging/SchemaPrinterTest.cpp b/common/messaging/SchemaPrinterTest.cpp index dbbe83be67..0eda1f5a36 100644 --- a/common/messaging/SchemaPrinterTest.cpp +++ b/common/messaging/SchemaPrinterTest.cpp @@ -33,6 +33,7 @@ using std::vector; using ola::messaging::BoolFieldDescriptor; using ola::messaging::IPV4FieldDescriptor; +using ola::messaging::IPV6FieldDescriptor; using ola::messaging::MACFieldDescriptor; using ola::messaging::Descriptor; using ola::messaging::FieldDescriptor; @@ -85,6 +86,8 @@ void SchemaPrinterTest::testPrinter() { "Count", false, 10); IPV4FieldDescriptor *ipv4_descriptor = new IPV4FieldDescriptor( "Address"); + IPV6FieldDescriptor *ipv6_descriptor = new IPV6FieldDescriptor( + "v6 Address"); MACFieldDescriptor *mac_descriptor = new MACFieldDescriptor( "MAC Address"); UIDFieldDescriptor *uid_descriptor = new UIDFieldDescriptor("Device"); @@ -95,6 +98,7 @@ void SchemaPrinterTest::testPrinter() { fields.push_back(string_descriptor); fields.push_back(uint8_descriptor); fields.push_back(ipv4_descriptor); + fields.push_back(ipv6_descriptor); fields.push_back(mac_descriptor); fields.push_back(uid_descriptor); @@ -104,7 +108,8 @@ void SchemaPrinterTest::testPrinter() { string expected = ( "On/Off: bool\nName: string [0, 32]\nCount: uint8\n" - "Address: IPv4 address\nMAC Address: MAC\nDevice: UID\n"); + "Address: IPv4 address\nv6 Address: IPv6 address\nMAC Address: MAC\n" + "Device: UID\n"); OLA_ASSERT_EQ(expected, printer.AsString()); } diff --git a/common/network/HealthCheckedConnection.cpp b/common/network/HealthCheckedConnection.cpp index 6f5cc741de..b39008a608 100644 --- a/common/network/HealthCheckedConnection.cpp +++ b/common/network/HealthCheckedConnection.cpp @@ -26,14 +26,25 @@ namespace network { HealthCheckedConnection::HealthCheckedConnection( ola::thread::SchedulerInterface *scheduler, + const ola::TimeInterval heartbeat_interval, const ola::TimeInterval timeout_interval) : m_scheduler(scheduler), - m_heartbeat_interval(timeout_interval), + m_heartbeat_interval(heartbeat_interval), + m_timeout_interval(timeout_interval), m_send_timeout_id(ola::thread::INVALID_TIMEOUT), m_receive_timeout_id(ola::thread::INVALID_TIMEOUT) { } +HealthCheckedConnection::HealthCheckedConnection( + ola::thread::SchedulerInterface *scheduler, + const ola::TimeInterval heartbeat_interval) + : HealthCheckedConnection(scheduler, + heartbeat_interval, + ola::TimeInterval(static_cast( + 2.5 * heartbeat_interval.AsInt()))) { +} + HealthCheckedConnection::~HealthCheckedConnection() { if (m_send_timeout_id != ola::thread::INVALID_TIMEOUT) m_scheduler->RemoveTimeout(m_send_timeout_id); @@ -101,10 +112,8 @@ bool HealthCheckedConnection::SendNextHeartbeat() { void HealthCheckedConnection::UpdateReceiveTimer() { - TimeInterval timeout_interval(static_cast( - 2.5 * m_heartbeat_interval.AsInt())); m_receive_timeout_id = m_scheduler->RegisterSingleTimeout( - timeout_interval, + m_timeout_interval, NewSingleCallback( this, &HealthCheckedConnection::InternalHeartbeatTimeout)); } diff --git a/common/network/HealthCheckedConnectionTest.cpp b/common/network/HealthCheckedConnectionTest.cpp index 4a6a09d9de..9f4a1618c7 100644 --- a/common/network/HealthCheckedConnectionTest.cpp +++ b/common/network/HealthCheckedConnectionTest.cpp @@ -56,10 +56,28 @@ class MockHealthCheckedConnection: public HealthCheckedConnection { MockHealthCheckedConnection(ola::io::ConnectedDescriptor *descriptor, SelectServer *scheduler, + const ola::TimeInterval heartbeat_interval, const ola::TimeInterval timeout_interval, const Options &options, MockClock *clock) - : HealthCheckedConnection(scheduler, timeout_interval), + : HealthCheckedConnection(scheduler, + heartbeat_interval, + timeout_interval), + m_descriptor(descriptor), + m_ss(scheduler), + m_options(options), + m_next_heartbeat(0), + m_expected_heartbeat(0), + m_channel_ok(true), + m_clock(clock) { + } + + MockHealthCheckedConnection(ola::io::ConnectedDescriptor *descriptor, + SelectServer *scheduler, + const ola::TimeInterval heartbeat_interval, + const Options &options, + MockClock *clock) + : HealthCheckedConnection(scheduler, heartbeat_interval), m_descriptor(descriptor), m_ss(scheduler), m_options(options), @@ -70,8 +88,10 @@ class MockHealthCheckedConnection: public HealthCheckedConnection { } void SendHeartbeat() { + OLA_DEBUG << "Maybe send heartbeat"; if (m_options.send_every == 0 || m_next_heartbeat % m_options.send_every == 0) { + OLA_DEBUG << "Sending heartbeat"; m_descriptor->Send(&m_next_heartbeat, sizeof(m_next_heartbeat)); } m_clock->AdvanceTime(0, 180000); @@ -115,13 +135,18 @@ class HealthCheckedConnectionTest: public CppUnit::TestFixture { HealthCheckedConnectionTest() : CppUnit::TestFixture(), m_ss(NULL, &m_clock), - heartbeat_interval(0, 200000) { + heartbeat_interval(0, 200000), + // Allow a little bit of wiggle room so we don't hit timing issues + // when running the tests + timeout_interval(0, 650000) { } CPPUNIT_TEST_SUITE(HealthCheckedConnectionTest); CPPUNIT_TEST(testSimpleChannel); CPPUNIT_TEST(testChannelWithPacketLoss); CPPUNIT_TEST(testChannelWithHeavyPacketLoss); + CPPUNIT_TEST(testChannelWithHeavyPacketLossLongerTimeout); + CPPUNIT_TEST(testChannelWithVeryHeavyPacketLossLongerTimeout); CPPUNIT_TEST(testPauseAndResume); CPPUNIT_TEST_SUITE_END(); @@ -131,6 +156,8 @@ class HealthCheckedConnectionTest: public CppUnit::TestFixture { void testSimpleChannel(); void testChannelWithPacketLoss(); void testChannelWithHeavyPacketLoss(); + void testChannelWithHeavyPacketLossLongerTimeout(); + void testChannelWithVeryHeavyPacketLossLongerTimeout(); void testPauseAndResume(); void PauseReading(MockHealthCheckedConnection *connection) { @@ -148,6 +175,7 @@ class HealthCheckedConnectionTest: public CppUnit::TestFixture { SelectServer m_ss; LoopbackDescriptor socket; TimeInterval heartbeat_interval; + TimeInterval timeout_interval; MockHealthCheckedConnection::Options options; }; @@ -206,7 +234,7 @@ void HealthCheckedConnectionTest::testChannelWithPacketLoss() { /** - * Check the channel works when every 2nd heartbeat is lost + * Check the channel fails when 2 of every 3 heartbeats are lost */ void HealthCheckedConnectionTest::testChannelWithHeavyPacketLoss() { options.send_every = 3; @@ -228,6 +256,57 @@ void HealthCheckedConnectionTest::testChannelWithHeavyPacketLoss() { } +/** + * Check the channel works when 2 of every 3 heartbeats are lost but the + * timeout interval is 3 * heartbeat_interval rather than the default + */ +void HealthCheckedConnectionTest:: + testChannelWithHeavyPacketLossLongerTimeout() { + options.send_every = 3; + MockHealthCheckedConnection connection(&socket, + &m_ss, + heartbeat_interval, + timeout_interval, + options, + &m_clock); + + socket.SetOnData( + NewCallback(&connection, &MockHealthCheckedConnection::ReadData)); + connection.Setup(); + m_ss.AddReadDescriptor(&socket); + connection.Setup(); + + m_ss.Run(); + OLA_ASSERT_TRUE(connection.ChannelOk()); +} + + +/** + * Check the channel fails when 3 of every 4 heartbeats are lost even though + * the timeout interval is 3 * heartbeat_interval + */ +void HealthCheckedConnectionTest:: + testChannelWithVeryHeavyPacketLossLongerTimeout() { + options.send_every = 4; + options.abort_on_failure = false; + MockHealthCheckedConnection connection(&socket, + &m_ss, + heartbeat_interval, + timeout_interval, + options, + &m_clock); + + socket.SetOnData( + NewCallback(&connection, &MockHealthCheckedConnection::ReadData)); + connection.Setup(); + m_ss.AddReadDescriptor(&socket); + connection.Setup(); + + m_ss.Run(); + OLA_ASSERT_FALSE(connection.ChannelOk()); +} + + /** * Check pausing doesn't mark the channel as bad. */ diff --git a/common/network/IPV4Address.cpp b/common/network/IPV4Address.cpp index 3dba9a8fb8..ba619ad0e8 100644 --- a/common/network/IPV4Address.cpp +++ b/common/network/IPV4Address.cpp @@ -14,7 +14,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * IPV4Address.cpp - * A IPV4 address + * An IPV4 address * Copyright (C) 2011 Simon Newton */ diff --git a/common/network/IPV6Address.cpp b/common/network/IPV6Address.cpp new file mode 100644 index 0000000000..5a46d953cc --- /dev/null +++ b/common/network/IPV6Address.cpp @@ -0,0 +1,170 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * IPV6Address.cpp + * An IPV6 address + * Copyright (C) 2023 Peter Newman + */ + +#if HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#ifdef HAVE_SYS_SOCKET_H +#include // Required by FreeBSD +#endif // HAVE_SYS_SOCKET_H +#ifdef HAVE_ARPA_INET_H +#include +#endif // HAVE_ARPA_INET_H +#ifdef HAVE_NETINET_IN_H +#include // Required by FreeBSD +#endif // HAVE_NETINET_IN_H + +#include +#include +#include +#include +#include + +#include "common/network/NetworkUtilsInternal.h" +#include "ola/Logging.h" +#include "ola/network/IPV6Address.h" +#include "ola/network/NetworkUtils.h" + +namespace ola { +namespace network { + +using std::string; + +IPV6Address::IPV6Address(const uint8_t *address) { + // TODO(Peter): Deal with any network byte order conversion? + memcpy(&m_address.s6_addr[0], address, sizeof (struct in6_addr)); +} + +bool IPV6Address::operator<(const IPV6Address &other) const { + // TODO(Peter): Deal with any network byte order conversion? + return (memcmp(&m_address.s6_addr[0], + &other.m_address.s6_addr[0], + sizeof (struct in6_addr)) < 0); +} + +bool IPV6Address::operator>(const IPV6Address &other) const { + // TODO(Peter): Deal with any network byte order conversion? + return (memcmp(&m_address.s6_addr[0], + &other.m_address.s6_addr[0], + sizeof (struct in6_addr)) > 0); +} + +bool IPV6StringToAddress(const string &address, struct in6_addr *addr) { + bool ok; +//// TODO(Peter): This currently allows some rather quirky values as per +//// inet_pton, we may want to restrict that in future to match IPV6Validator +//// if that deviates + + if (address.empty()) { + // Don't bother trying to extract an address if we weren't given one + return false; + } + +#ifdef HAVE_INET_PTON + ok = (1 == inet_pton(AF_INET6, address.data(), addr)); +#else + OLA_FATAL << "Failed to convert string to address, inet_pton unavailable"; + return false; +#endif // HAVE_INET_PTON + + if (!ok) { + OLA_WARN << "Could not convert address " << address; + } + return ok; +} + +bool IPV6Address::IsWildcard() const { + return IN6_IS_ADDR_UNSPECIFIED(&m_address); +} + +string IPV6Address::ToString() const { + struct in6_addr addr; + addr = m_address; +#ifdef HAVE_INET_NTOP + char str[INET6_ADDRSTRLEN]; + if (inet_ntop(AF_INET6, &addr, str, INET6_ADDRSTRLEN) == NULL) { + OLA_FATAL << "Failed to convert address to string using inet_ntop"; + return NULL; + } + return str; +#else + OLA_FATAL << "Failed to convert address to string, inet_ntop unavailable"; + return NULL; +#endif // HAVE_INET_NTOP +} + +IPV6Address* IPV6Address::FromString(const string &address) { + struct in6_addr addr; + if (!IPV6StringToAddress(address, &addr)) { + return NULL; + } + + return new IPV6Address(addr); +} + +bool IPV6Address::FromString(const string &address, IPV6Address *target) { + struct in6_addr addr; + if (!IPV6StringToAddress(address, &addr)) { + return false; + } + *target = IPV6Address(addr); + return true; +} + +IPV6Address IPV6Address::FromStringOrDie(const string &address) { + struct in6_addr addr; + assert(IPV6StringToAddress(address, &addr)); + return IPV6Address(addr); +} + +/*bool IPV6Address::ToCIDRMask(IPV6Address address, uint8_t *mask) { + uint32_t netmask = NetworkToHost(address.AsInt()); + uint8_t bits = 0; + bool seen_one = false; + for (uint8_t i = 0; i < std::numeric_limits::digits; i++) { + if (netmask & 1) { + bits++; + seen_one = true; + } else { + if (seen_one) { + return false; + } + } + netmask = netmask >> 1; + } + *mask = bits; + return true; +}*/ + +IPV6Address IPV6Address::WildCard() { + in6_addr wildCard = IN6ADDR_ANY_INIT; + // TODO(Peter): Deal with any host to network conversion... + return IPV6Address(wildCard); +} + +IPV6Address IPV6Address::Loopback() { + in6_addr loopback = IN6ADDR_LOOPBACK_INIT; + // TODO(Peter): Deal with any host to network conversion... + // return IPV6Address(HostToNetwork(IN6ADDR_LOOPBACK_INIT)); + return IPV6Address(loopback); +} +} // namespace network +} // namespace ola diff --git a/common/network/IPV6AddressTest.cpp b/common/network/IPV6AddressTest.cpp new file mode 100644 index 0000000000..05400cb82a --- /dev/null +++ b/common/network/IPV6AddressTest.cpp @@ -0,0 +1,209 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * IPV6AddressTest.cpp + * Test fixture for the IPV6Address class + * Copyright (C) 2023 Peter Newman + */ + +#include + +#include +#include +#include +#include +#include +#include +#include "common/network/NetworkUtilsInternal.h" +#include "ola/network/IPV6Address.h" +#include "ola/network/NetworkUtils.h" +#include "ola/testing/TestUtils.h" + + +using ola::network::IPV6Address; +using ola::network::HostToNetwork; +using std::auto_ptr; +using std::string; +using std::vector; + +class IPV6AddressTest: public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(IPV6AddressTest); + CPPUNIT_TEST(testIPV6Address); + CPPUNIT_TEST(testWildcard); + CPPUNIT_TEST(testLoopback); + CPPUNIT_TEST_SUITE_END(); + + public: + void testIPV6Address(); + void testWildcard(); + // TODO(Peter): Test the all-nodes link-local multicast group if we add it + void testLoopback(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(IPV6AddressTest); + + +/* + * Test the IPV6 Address class works + */ +void IPV6AddressTest::testIPV6Address() { + IPV6Address wildcard_address; + OLA_ASSERT_EQ(string("::"), wildcard_address.ToString()); +// OLA_ASSERT_EQ(static_cast(0), wildcard_address.AsInt()); + OLA_ASSERT_TRUE(wildcard_address.IsWildcard()); + + IPV6Address address1 = IPV6Address::FromStringOrDie("::ffff:c0a8:101"); +// int ip_as_int = address1.AsInt(); + OLA_ASSERT_NE(wildcard_address, address1); +// OLA_ASSERT_NE(HostToNetwork(0xc0a811), ip_as_int); +// OLA_ASSERT_EQ(HostToNetwork(0xc0a80101), static_cast(ip_as_int)); + OLA_ASSERT_EQ(string("::ffff:192.168.1.1"), address1.ToString()); + + IPV6Address address2 = IPV6Address::FromStringOrDie( + "2001:db8:1234:5678:90ab:cdef:feed:face"); +// int ip_as_int = address2.AsInt(); + OLA_ASSERT_NE(wildcard_address, address2); +// OLA_ASSERT_NE(HostToNetwork(0xc0a811), ip_as_int); +// OLA_ASSERT_EQ(HostToNetwork(0xc0a80101), static_cast(ip_as_int)); + + const uint8_t big_endian_address_data[] = + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 10, 0, 0, 1}; + IPV6Address binary_address(big_endian_address_data); + OLA_ASSERT_EQ(string("::ffff:10.0.0.1"), binary_address.ToString()); + + // Test Get() + uint8_t address_data[] = {32, 1, 13, 184, 18, 52, 86, 120, + 144, 171, 205, 239, 254, 237, 250, 206}; + uint8_t addr[IPV6Address::LENGTH]; + address2.Get(addr); + OLA_ASSERT_DATA_EQUALS(addr, + sizeof(addr), + reinterpret_cast(&address_data), + sizeof(address_data)); + + // test copy and assignment + IPV6Address address3(address1); + OLA_ASSERT_EQ(address1, address3); + IPV6Address address4 = address1; + OLA_ASSERT_EQ(address1, address4); + + // test stringification + OLA_ASSERT_EQ(string("::ffff:192.168.1.1"), address1.ToString()); + std::ostringstream str; + str << address1; + OLA_ASSERT_EQ(string("::ffff:192.168.1.1"), str.str()); + + // test from string + auto_ptr string_address( + IPV6Address::FromString("::ffff:10.0.0.1")); + OLA_ASSERT_NOT_NULL(string_address.get()); + OLA_ASSERT_EQ(string("::ffff:10.0.0.1"), string_address->ToString()); + + auto_ptr string_address2(IPV6Address::FromString("foo")); + OLA_ASSERT_NULL(string_address2.get()); + + // and the second form + IPV6Address string_address3; + OLA_ASSERT_TRUE(IPV6Address::FromString( + "::ffff:172.16.4.1", &string_address3)); + OLA_ASSERT_EQ(string("::ffff:172.16.4.1"), string_address3.ToString()); + + IPV6Address string_address4; + // Add the leading zero to the second group + OLA_ASSERT_TRUE(IPV6Address::FromString( + "2001:0db8:1234:5678:90ab:cdef:feed:face", &string_address4)); + // Confirm it's not rendered when we convert to a string + OLA_ASSERT_EQ(string("2001:db8:1234:5678:90ab:cdef:feed:face"), + string_address4.ToString()); + + IPV6Address string_address5; + OLA_ASSERT_TRUE(IPV6Address::FromString( + "2001:db8:dead:beef:dead:beef:dead:beef", &string_address5)); + OLA_ASSERT_EQ(string("2001:db8:dead:beef:dead:beef:dead:beef"), + string_address5.ToString()); + + IPV6Address string_address6; + OLA_ASSERT_FALSE(IPV6Address::FromString("", &string_address6)); + + // make sure sorting works + vector addresses; + addresses.push_back(address1); + addresses.push_back(*string_address); + addresses.push_back(string_address3); + addresses.push_back(string_address4); + addresses.push_back(string_address5); + std::sort(addresses.begin(), addresses.end()); + + // The comparisons take into account network byte order automagically. + OLA_ASSERT_EQ(string("::ffff:10.0.0.1"), addresses[0].ToString()); + OLA_ASSERT_EQ(string("::ffff:172.16.4.1"), addresses[1].ToString()); + OLA_ASSERT_EQ(string("::ffff:192.168.1.1"), addresses[2].ToString()); + OLA_ASSERT_EQ(string("2001:db8:1234:5678:90ab:cdef:feed:face"), + addresses[3].ToString()); + OLA_ASSERT_EQ(string("2001:db8:dead:beef:dead:beef:dead:beef"), + addresses[4].ToString()); + +/* uint8_t mask = 255; // UINT8_MAX; + OLA_ASSERT_TRUE( + IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("0.0.0.0"), &mask)); + OLA_ASSERT_EQ(0, static_cast(mask)); + + OLA_ASSERT_TRUE( + IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.0.0.0"), + &mask)); + OLA_ASSERT_EQ(8, static_cast(mask)); + + OLA_ASSERT_TRUE( + IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.255.255.0"), + &mask)); + OLA_ASSERT_EQ(24, static_cast(mask)); + + OLA_ASSERT_TRUE( + IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.255.255.252"), + &mask)); + OLA_ASSERT_EQ(30, static_cast(mask)); + + OLA_ASSERT_TRUE( + IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.255.255.255"), + &mask)); + OLA_ASSERT_EQ(32, static_cast(mask)); + + OLA_ASSERT_FALSE( + IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.0.0.255"), + &mask));*/ +} + + +/* + * Test the wildcard address works. + */ +void IPV6AddressTest::testWildcard() { + IPV6Address wildcard_address; + OLA_ASSERT_EQ(string("::"), wildcard_address.ToString()); +// OLA_ASSERT_EQ(static_cast(0), wildcard_address.AsInt()); + OLA_ASSERT_TRUE(wildcard_address.IsWildcard()); + + IPV6Address wildcard_address2 = IPV6Address::WildCard(); + OLA_ASSERT_EQ(wildcard_address, wildcard_address2); +} + + +/* + * Test the loopback address works. + */ +void IPV6AddressTest::testLoopback() { + IPV6Address loopback_address = IPV6Address::Loopback(); + OLA_ASSERT_EQ(string("::1"), loopback_address.ToString()); +} diff --git a/common/network/Makefile.mk b/common/network/Makefile.mk index 44ef81f57d..fc9327e677 100644 --- a/common/network/Makefile.mk +++ b/common/network/Makefile.mk @@ -5,6 +5,7 @@ common_libolacommon_la_SOURCES += \ common/network/FakeInterfacePicker.h \ common/network/HealthCheckedConnection.cpp \ common/network/IPV4Address.cpp \ + common/network/IPV6Address.cpp \ common/network/Interface.cpp \ common/network/InterfacePicker.cpp \ common/network/MACAddress.cpp \ @@ -44,6 +45,7 @@ common_network_HealthCheckedConnectionTester_LDADD = $(COMMON_TESTING_LIBS) common_network_NetworkTester_SOURCES = \ common/network/IPV4AddressTest.cpp \ + common/network/IPV6AddressTest.cpp \ common/network/InterfacePickerTest.cpp \ common/network/InterfaceTest.cpp \ common/network/MACAddressTest.cpp \ diff --git a/common/protocol/Ola.proto b/common/protocol/Ola.proto index 02896effa4..a1190add51 100644 --- a/common/protocol/Ola.proto +++ b/common/protocol/Ola.proto @@ -68,6 +68,7 @@ enum PluginIds { OLA_PLUGIN_GPIO = 22; OLA_PLUGIN_SPIDMX = 23; OLA_PLUGIN_NANOLEAF = 24; + OLA_PLUGIN_E133 = 25; /* * To obtain a new plugin ID, open a ticket at diff --git a/common/rdm/DescriptorConsistencyChecker.cpp b/common/rdm/DescriptorConsistencyChecker.cpp index ed224e4955..e947d6d14e 100644 --- a/common/rdm/DescriptorConsistencyChecker.cpp +++ b/common/rdm/DescriptorConsistencyChecker.cpp @@ -41,6 +41,11 @@ void DescriptorConsistencyChecker::Visit( } +void DescriptorConsistencyChecker::Visit( + const ola::messaging::IPV6FieldDescriptor*) { +} + + void DescriptorConsistencyChecker::Visit( const ola::messaging::MACFieldDescriptor*) { } diff --git a/common/rdm/DescriptorConsistencyChecker.h b/common/rdm/DescriptorConsistencyChecker.h index 294debf3f3..a1ed804841 100644 --- a/common/rdm/DescriptorConsistencyChecker.h +++ b/common/rdm/DescriptorConsistencyChecker.h @@ -52,6 +52,7 @@ class DescriptorConsistencyChecker void Visit(const ola::messaging::BoolFieldDescriptor*); void Visit(const ola::messaging::IPV4FieldDescriptor*); + void Visit(const ola::messaging::IPV6FieldDescriptor*); void Visit(const ola::messaging::MACFieldDescriptor*); void Visit(const ola::messaging::UIDFieldDescriptor*); void Visit(const ola::messaging::StringFieldDescriptor*); diff --git a/common/rdm/GroupSizeCalculator.cpp b/common/rdm/GroupSizeCalculator.cpp index d4287b5725..18eb334a0f 100644 --- a/common/rdm/GroupSizeCalculator.cpp +++ b/common/rdm/GroupSizeCalculator.cpp @@ -131,6 +131,12 @@ void GroupSizeCalculator::Visit( } +void GroupSizeCalculator::Visit( + const ola::messaging::IPV6FieldDescriptor *descriptor) { + m_non_groups.push_back(descriptor); +} + + void GroupSizeCalculator::Visit( const ola::messaging::MACFieldDescriptor *descriptor) { m_non_groups.push_back(descriptor); @@ -246,6 +252,12 @@ void StaticGroupTokenCalculator::Visit( } +void StaticGroupTokenCalculator::Visit( + OLA_UNUSED const ola::messaging::IPV6FieldDescriptor *descriptor) { + m_token_count.top()++; +} + + void StaticGroupTokenCalculator::Visit( OLA_UNUSED const ola::messaging::MACFieldDescriptor *descriptor) { m_token_count.top()++; diff --git a/common/rdm/GroupSizeCalculator.h b/common/rdm/GroupSizeCalculator.h index f2c8b4e8e4..c15ec07cda 100644 --- a/common/rdm/GroupSizeCalculator.h +++ b/common/rdm/GroupSizeCalculator.h @@ -53,6 +53,7 @@ class StaticGroupTokenCalculator void Visit(const ola::messaging::BoolFieldDescriptor*); void Visit(const ola::messaging::IPV4FieldDescriptor*); + void Visit(const ola::messaging::IPV6FieldDescriptor*); void Visit(const ola::messaging::MACFieldDescriptor*); void Visit(const ola::messaging::UIDFieldDescriptor*); void Visit(const ola::messaging::StringFieldDescriptor*); @@ -98,6 +99,7 @@ class GroupSizeCalculator: public ola::messaging::FieldDescriptorVisitor { void Visit(const ola::messaging::BoolFieldDescriptor*); void Visit(const ola::messaging::IPV4FieldDescriptor*); + void Visit(const ola::messaging::IPV6FieldDescriptor*); void Visit(const ola::messaging::MACFieldDescriptor*); void Visit(const ola::messaging::UIDFieldDescriptor*); void Visit(const ola::messaging::StringFieldDescriptor*); diff --git a/common/rdm/GroupSizeCalculatorTest.cpp b/common/rdm/GroupSizeCalculatorTest.cpp index a47b80d6c0..c86dd1dc18 100644 --- a/common/rdm/GroupSizeCalculatorTest.cpp +++ b/common/rdm/GroupSizeCalculatorTest.cpp @@ -34,9 +34,11 @@ using ola::messaging::Descriptor; using ola::messaging::FieldDescriptor; using ola::messaging::FieldDescriptorGroup; using ola::messaging::IPV4FieldDescriptor; +using ola::messaging::IPV6FieldDescriptor; using ola::messaging::Int16FieldDescriptor; using ola::messaging::Int32FieldDescriptor; using ola::messaging::Int8FieldDescriptor; +using ola::messaging::MACFieldDescriptor; using ola::messaging::StringFieldDescriptor; using ola::messaging::UIDFieldDescriptor; using ola::messaging::UInt16FieldDescriptor; @@ -85,13 +87,16 @@ void GroupSizeCalculatorTest::testSimpleCases() { fields.push_back(new Int32FieldDescriptor("int32")); fields.push_back(new StringFieldDescriptor("string", 0, 32)); fields.push_back(new IPV4FieldDescriptor("address")); + fields.push_back(new IPV6FieldDescriptor("addressv6")); + fields.push_back(new MACFieldDescriptor("mac")); fields.push_back(new UIDFieldDescriptor("uid")); Descriptor descriptor("Test Descriptor", fields); unsigned int token_count, group_repeat_count; OLA_ASSERT_TRUE( m_static_calculator.CalculateTokensRequired(&descriptor, &token_count)); - OLA_ASSERT_EQ(10u, token_count); + OLA_ASSERT_EQ(12u, token_count); // Actual token count + OLA_ASSERT_EQ( GroupSizeCalculator::INSUFFICIENT_TOKENS, @@ -103,21 +108,21 @@ void GroupSizeCalculatorTest::testSimpleCases() { OLA_ASSERT_EQ( GroupSizeCalculator::INSUFFICIENT_TOKENS, m_calculator.CalculateGroupSize( - 9, + 11, // Actual token count - 1 &descriptor, &group_repeat_count)); OLA_ASSERT_EQ( GroupSizeCalculator::NO_VARIABLE_GROUPS, m_calculator.CalculateGroupSize( - 10, + 12, // Actual token count &descriptor, &group_repeat_count)); OLA_ASSERT_EQ( GroupSizeCalculator::EXTRA_TOKENS, m_calculator.CalculateGroupSize( - 11, + 13, // Actual token count + 1 &descriptor, &group_repeat_count)); } diff --git a/common/rdm/MessageDeserializer.cpp b/common/rdm/MessageDeserializer.cpp index 28480da6e5..a3e6ec3360 100644 --- a/common/rdm/MessageDeserializer.cpp +++ b/common/rdm/MessageDeserializer.cpp @@ -114,7 +114,7 @@ void MessageDeserializer::Visit( } m_message_stack.top().push_back( - new ola::messaging::BoolMessageField(descriptor, m_data[m_offset++])); + new ola::messaging::BoolMessageField(descriptor, m_data[m_offset++])); } @@ -128,9 +128,22 @@ void MessageDeserializer::Visit( memcpy(&data, m_data + m_offset, sizeof(data)); m_offset += sizeof(data); m_message_stack.top().push_back( - new ola::messaging::IPV4MessageField( - descriptor, - ola::network::IPV4Address(data))); + new ola::messaging::IPV4MessageField( + descriptor, + ola::network::IPV4Address(data))); +} + + +void MessageDeserializer::Visit( + const ola::messaging::IPV6FieldDescriptor *descriptor) { + if (!CheckForData(descriptor->MaxSize())) { + return; + } + + ola::network::IPV6Address ipv6_address(m_data + m_offset); + m_offset += descriptor->MaxSize(); + m_message_stack.top().push_back( + new ola::messaging::IPV6MessageField(descriptor, ipv6_address)); } @@ -143,7 +156,7 @@ void MessageDeserializer::Visit( ola::network::MACAddress mac_address(m_data + m_offset); m_offset += descriptor->MaxSize(); m_message_stack.top().push_back( - new ola::messaging::MACMessageField(descriptor, mac_address)); + new ola::messaging::MACMessageField(descriptor, mac_address)); } @@ -156,7 +169,7 @@ void MessageDeserializer::Visit( ola::rdm::UID uid(m_data + m_offset); m_offset += descriptor->MaxSize(); m_message_stack.top().push_back( - new ola::messaging::UIDMessageField(descriptor, uid)); + new ola::messaging::UIDMessageField(descriptor, uid)); } @@ -179,7 +192,7 @@ void MessageDeserializer::Visit( ShortenString(&value); m_offset += string_size; m_message_stack.top().push_back( - new ola::messaging::StringMessageField(descriptor, value)); + new ola::messaging::StringMessageField(descriptor, value)); } @@ -226,7 +239,7 @@ void MessageDeserializer::Visit( const ola::messaging::FieldDescriptorGroup *descriptor) { unsigned int iterations = descriptor->FixedSize() ? descriptor->MinBlocks() : - m_variable_field_size; + m_variable_field_size; for (unsigned int i = 0; i < iterations; ++i) { vector fields; @@ -298,7 +311,7 @@ void MessageDeserializer::IntVisit( } m_message_stack.top().push_back( - new ola::messaging::BasicMessageField(descriptor, value)); + new ola::messaging::BasicMessageField(descriptor, value)); } } // namespace rdm } // namespace ola diff --git a/common/rdm/MessageDeserializerTest.cpp b/common/rdm/MessageDeserializerTest.cpp index b1660e5178..d674b2c2f8 100644 --- a/common/rdm/MessageDeserializerTest.cpp +++ b/common/rdm/MessageDeserializerTest.cpp @@ -34,7 +34,6 @@ using ola::messaging::BoolFieldDescriptor; -using ola::messaging::IPV4FieldDescriptor; using ola::messaging::Descriptor; using ola::messaging::FieldDescriptor; using ola::messaging::FieldDescriptorGroup; @@ -42,6 +41,7 @@ using ola::messaging::Int16FieldDescriptor; using ola::messaging::Int32FieldDescriptor; using ola::messaging::Int8FieldDescriptor; using ola::messaging::IPV4FieldDescriptor; +using ola::messaging::IPV6FieldDescriptor; using ola::messaging::MACFieldDescriptor; using ola::messaging::Message; using ola::messaging::GenericMessagePrinter; @@ -62,6 +62,7 @@ class MessageDeserializerTest: public CppUnit::TestFixture { CPPUNIT_TEST(testSimpleBigEndian); CPPUNIT_TEST(testSimpleLittleEndian); CPPUNIT_TEST(testIPV4); + CPPUNIT_TEST(testIPV6); CPPUNIT_TEST(testMAC); CPPUNIT_TEST(testString); CPPUNIT_TEST(testUID); @@ -75,6 +76,7 @@ class MessageDeserializerTest: public CppUnit::TestFixture { void testSimpleBigEndian(); void testSimpleLittleEndian(); void testIPV4(); + void testIPV6(); void testMAC(); void testString(); void testUID(); @@ -246,6 +248,32 @@ void MessageDeserializerTest::testIPV4() { } +/** + * Test IPV6 inflation. + */ +void MessageDeserializerTest::testIPV6() { + // build the descriptor + vector fields; + fields.push_back(new IPV6FieldDescriptor("Addressv6")); + Descriptor descriptor("Test Descriptor", fields); + + // now setup the data + const uint8_t big_endian_data[] = {0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 10, 0, 0, 1}; + + // now the correct amount & verify + auto_ptr message(m_deserializer.InflateMessage( + &descriptor, + big_endian_data, + sizeof(big_endian_data))); + OLA_ASSERT_NOT_NULL(message.get()); + OLA_ASSERT_EQ(1u, message->FieldCount()); + + const string expected = "Addressv6: ::ffff:10.0.0.1\n"; + OLA_ASSERT_EQ(expected, m_printer.AsString(message.get())); +} + + /** * Test MAC inflation. */ diff --git a/common/rdm/MessageSerializer.cpp b/common/rdm/MessageSerializer.cpp index 8fe5198d2f..8181157642 100644 --- a/common/rdm/MessageSerializer.cpp +++ b/common/rdm/MessageSerializer.cpp @@ -77,6 +77,15 @@ void MessageSerializer::Visit( } +void MessageSerializer::Visit( + const ola::messaging::IPV6MessageField *message) { + unsigned int size = message->GetDescriptor()->MaxSize(); + CheckForFreeSpace(size); + message->Value().Pack(m_data + m_offset, size); + m_offset += size; +} + + void MessageSerializer::Visit( const ola::messaging::MACMessageField *message) { unsigned int size = message->GetDescriptor()->MaxSize(); diff --git a/common/rdm/MessageSerializerTest.cpp b/common/rdm/MessageSerializerTest.cpp index 9d6163fa7d..2eca076471 100644 --- a/common/rdm/MessageSerializerTest.cpp +++ b/common/rdm/MessageSerializerTest.cpp @@ -41,6 +41,7 @@ using ola::messaging::Int16FieldDescriptor; using ola::messaging::Int32FieldDescriptor; using ola::messaging::Int8FieldDescriptor; using ola::messaging::IPV4FieldDescriptor; +using ola::messaging::IPV6FieldDescriptor; using ola::messaging::MACFieldDescriptor; using ola::messaging::Message; using ola::messaging::StringFieldDescriptor; @@ -111,6 +112,7 @@ void MessageSerializerTest::testSimple() { fields.push_back(new UInt32FieldDescriptor("uint32")); fields.push_back(new Int32FieldDescriptor("int32")); fields.push_back(new IPV4FieldDescriptor("ip")); + fields.push_back(new IPV6FieldDescriptor("ipv6")); fields.push_back(new MACFieldDescriptor("mac")); fields.push_back(new StringFieldDescriptor("string", 0, 32)); Descriptor descriptor("Test Descriptor", fields); @@ -125,6 +127,7 @@ void MessageSerializerTest::testSimple() { inputs.push_back("66000"); inputs.push_back("-66000"); inputs.push_back("10.0.0.1"); + inputs.push_back("::ffff:192.168.0.1"); inputs.push_back("01:23:45:67:89:ab"); inputs.push_back("foo"); @@ -137,12 +140,13 @@ void MessageSerializerTest::testSimple() { const uint8_t *data = serializer.SerializeMessage(message.get(), &packed_length); OLA_ASSERT_NOT_NULL(data); - OLA_ASSERT_EQ(28u, packed_length); + OLA_ASSERT_EQ(44u, packed_length); uint8_t expected[] = { 1, 1, 253, 1, 44, 254, 112, 0, 1, 1, 208, 255, 254, 254, 48, 10, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 192, 168, 0, 1, 1, 35, 69, 103, 137, 171, 'f', 'o', 'o'}; diff --git a/common/rdm/PidStoreLoader.cpp b/common/rdm/PidStoreLoader.cpp index 70665f2852..adf5594e6b 100644 --- a/common/rdm/PidStoreLoader.cpp +++ b/common/rdm/PidStoreLoader.cpp @@ -478,6 +478,9 @@ const FieldDescriptor *PidStoreLoader::FieldToFieldDescriptor( case ola::rdm::pid::UID: descriptor = new ola::messaging::UIDFieldDescriptor(field.name()); break; + case ola::rdm::pid::IPV6: + descriptor = new ola::messaging::IPV6FieldDescriptor(field.name()); + break; default: OLA_WARN << "Unknown field type: " << field.type(); } diff --git a/common/rdm/Pids.proto b/common/rdm/Pids.proto index 841e1d7931..19010f7ad8 100644 --- a/common/rdm/Pids.proto +++ b/common/rdm/Pids.proto @@ -42,6 +42,7 @@ enum FieldType { IPV4 = 10; UID = 11; MAC = 12; + IPV6 = 13; } // A value which has a label applied diff --git a/common/rdm/QueueingRDMController.cpp b/common/rdm/QueueingRDMController.cpp index c8c4a8b332..22b5814267 100644 --- a/common/rdm/QueueingRDMController.cpp +++ b/common/rdm/QueueingRDMController.cpp @@ -14,7 +14,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * QueueingRDMController.cpp - * The Jese DMX TRI device. + * An RDM Controller that sends a single message at a time. * Copyright (C) 2010 Simon Newton */ diff --git a/common/rdm/QueueingRDMControllerTest.cpp b/common/rdm/QueueingRDMControllerTest.cpp index 28a22ee0ba..fbdd09a16a 100644 --- a/common/rdm/QueueingRDMControllerTest.cpp +++ b/common/rdm/QueueingRDMControllerTest.cpp @@ -14,7 +14,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * QueueingRDMControllerTest.cpp - * Test fixture for the UID classes + * Test fixture for the QueueingRDMController * Copyright (C) 2005 Simon Newton */ diff --git a/common/rdm/StringMessageBuilder.cpp b/common/rdm/StringMessageBuilder.cpp index 63840d3198..da6fad314e 100644 --- a/common/rdm/StringMessageBuilder.cpp +++ b/common/rdm/StringMessageBuilder.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -127,8 +128,9 @@ const ola::messaging::Message *StringMessageBuilder::GetMessage( */ void StringMessageBuilder::Visit( const ola::messaging::BoolFieldDescriptor *descriptor) { - if (StopParsing()) + if (StopParsing()) { return; + } bool value = false; bool valid = false; @@ -170,8 +172,9 @@ void StringMessageBuilder::Visit( */ void StringMessageBuilder::Visit( const ola::messaging::IPV4FieldDescriptor *descriptor) { - if (StopParsing()) + if (StopParsing()) { return; + } string token = m_inputs[m_offset++]; ola::network::IPV4Address ip_address; @@ -185,13 +188,35 @@ void StringMessageBuilder::Visit( } +/** + * IPV6 Addresses + */ +void StringMessageBuilder::Visit( + const ola::messaging::IPV6FieldDescriptor *descriptor) { + if (StopParsing()) { + return; + } + + string token = m_inputs[m_offset++]; + ola::network::IPV6Address ipv6_address; + if (!ola::network::IPV6Address::FromString(token, &ipv6_address)) { + SetError(descriptor->Name()); + return; + } + + m_groups.top().push_back( + new ola::messaging::IPV6MessageField(descriptor, ipv6_address)); +} + + /** * MAC Addresses */ void StringMessageBuilder::Visit( const ola::messaging::MACFieldDescriptor *descriptor) { - if (StopParsing()) + if (StopParsing()) { return; + } string token = m_inputs[m_offset++]; ola::network::MACAddress mac_address; @@ -210,8 +235,9 @@ void StringMessageBuilder::Visit( */ void StringMessageBuilder::Visit( const ola::messaging::UIDFieldDescriptor *descriptor) { - if (StopParsing()) + if (StopParsing()) { return; + } string token = m_inputs[m_offset++]; auto_ptr uid(UID::FromString(token)); @@ -231,8 +257,9 @@ void StringMessageBuilder::Visit( */ void StringMessageBuilder::Visit( const ola::messaging::StringFieldDescriptor *descriptor) { - if (StopParsing()) + if (StopParsing()) { return; + } const string &token = m_inputs[m_offset++]; if (descriptor->MaxSize() != 0 && @@ -335,8 +362,9 @@ void StringMessageBuilder::SetError(const string &error) { template void StringMessageBuilder::VisitInt( const ola::messaging::IntegerFieldDescriptor *descriptor) { - if (StopParsing()) + if (StopParsing()) { return; + } type int_value; string input = m_inputs[m_offset++]; diff --git a/common/rdm/StringMessageBuilderTest.cpp b/common/rdm/StringMessageBuilderTest.cpp index de09265147..6ee21f16e8 100644 --- a/common/rdm/StringMessageBuilderTest.cpp +++ b/common/rdm/StringMessageBuilderTest.cpp @@ -38,6 +38,7 @@ using ola::messaging::Descriptor; using ola::messaging::FieldDescriptor; using ola::messaging::FieldDescriptorGroup; using ola::messaging::IPV4FieldDescriptor; +using ola::messaging::IPV6FieldDescriptor; using ola::messaging::Int16FieldDescriptor; using ola::messaging::Int32FieldDescriptor; using ola::messaging::Int8FieldDescriptor; @@ -136,6 +137,7 @@ void StringBuilderTest::testSimpleBuilder() { fields.push_back(new BoolFieldDescriptor("bool5")); fields.push_back(new BoolFieldDescriptor("bool6")); fields.push_back(new IPV4FieldDescriptor("ip1")); + fields.push_back(new IPV6FieldDescriptor("ipv61")); fields.push_back(new MACFieldDescriptor("mac1")); fields.push_back(new UInt8FieldDescriptor("uint8")); fields.push_back(new UInt16FieldDescriptor("uint16")); @@ -156,6 +158,7 @@ void StringBuilderTest::testSimpleBuilder() { inputs.push_back("TRUE"); inputs.push_back("FALSE"); inputs.push_back("10.0.0.1"); + inputs.push_back("::ffff:192.168.0.1"); inputs.push_back("01:23:45:67:89:ab"); inputs.push_back("255"); inputs.push_back("300"); @@ -171,13 +174,14 @@ void StringBuilderTest::testSimpleBuilder() { // verify OLA_ASSERT_TRUE(message.get()); OLA_ASSERT_EQ(static_cast(fields.size()), - message->FieldCount()); + message->FieldCount()); string expected = ( "bool1: true\nbool2: false\nbool3: true\nbool4: false\nbool5: true\n" - "bool6: false\nip1: 10.0.0.1\nmac1: 01:23:45:67:89:ab\nuint8: 255\n" - "uint16: 300\nuint32: 66000\nint8: -128\nint16: -300\nint32: -66000\n" - "string: foo\nhex uint16: 1024\n"); + "bool6: false\nip1: 10.0.0.1\nipv61: ::ffff:192.168.0.1\n" + "mac1: 01:23:45:67:89:ab\nuint8: 255\nuint16: 300\nuint32: 66000\n" + "int8: -128\nint16: -300\nint32: -66000\nstring: foo\n" + "hex uint16: 1024\n"); OLA_ASSERT_EQ(expected, m_printer.AsString(message.get())); } diff --git a/common/rdm/UIDTest.cpp b/common/rdm/UIDTest.cpp index 3a114150d3..9191119a15 100644 --- a/common/rdm/UIDTest.cpp +++ b/common/rdm/UIDTest.cpp @@ -34,6 +34,7 @@ using ola::rdm::UIDSet; class UIDTest: public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(UIDTest); CPPUNIT_TEST(testUID); + CPPUNIT_TEST(testRPTUID); CPPUNIT_TEST(testUIDInequalities); CPPUNIT_TEST(testUIDSet); CPPUNIT_TEST(testUIDSetUnion); @@ -43,6 +44,7 @@ class UIDTest: public CppUnit::TestFixture { public: void testUID(); + void testRPTUID(); void testUIDInequalities(); void testUIDSet(); void testUIDSetUnion(); @@ -58,6 +60,8 @@ CPPUNIT_TEST_SUITE_REGISTRATION(UIDTest); */ void UIDTest::testUID() { UID uid(1, 2); + OLA_ASSERT_FALSE(uid.IsBroadcast()); + OLA_ASSERT_FALSE(uid.IsVendorcast()); UID uid2 = uid; OLA_ASSERT_EQ(uid, uid2); OLA_ASSERT_FALSE(uid != uid2); @@ -84,9 +88,12 @@ void UIDTest::testUID() { UID all_devices = UID::AllDevices(); UID manufacturer_devices = UID::VendorcastAddress(0x52); + UID manufacturer_devices2 = UID::VendorcastAddress(uid); OLA_ASSERT_EQ(string("ffff:ffffffff"), all_devices.ToString()); OLA_ASSERT_EQ(string("0052:ffffffff"), manufacturer_devices.ToString()); + OLA_ASSERT_EQ(string("0001:ffffffff"), + manufacturer_devices2.ToString()); OLA_ASSERT_EQ(all_devices.ManufacturerId(), static_cast(0xffff)); OLA_ASSERT_EQ(all_devices.DeviceId(), @@ -95,8 +102,16 @@ void UIDTest::testUID() { static_cast(0x0052)); OLA_ASSERT_EQ(manufacturer_devices.DeviceId(), static_cast(0xffffffff)); + OLA_ASSERT_EQ(manufacturer_devices2.ManufacturerId(), + static_cast(0x0001)); + OLA_ASSERT_EQ(manufacturer_devices2.DeviceId(), + static_cast(0xffffffff)); OLA_ASSERT_TRUE(all_devices.IsBroadcast()); + OLA_ASSERT_FALSE(all_devices.IsVendorcast()); OLA_ASSERT_TRUE(manufacturer_devices.IsBroadcast()); + OLA_ASSERT_TRUE(manufacturer_devices.IsVendorcast()); + OLA_ASSERT_TRUE(manufacturer_devices2.IsBroadcast()); + OLA_ASSERT_TRUE(manufacturer_devices2.IsVendorcast()); // now test the packing & unpacking unsigned int buffer_size = UID::UID_SIZE; @@ -118,6 +133,49 @@ void UIDTest::testUID() { } +/* + * Test the RPT UIDs work. + */ +void UIDTest::testRPTUID() { + UID uid(1, 2); + UID rpt_all_controllers = UID::RPTAllControllers(); + UID rpt_all_devices = UID::RPTAllDevices(); + UID rpt_manufacturer_devices = UID::RPTVendorcastAddressDevices(0x52); + UID rpt_manufacturer_devices2 = UID::RPTVendorcastAddressDevices(uid); + OLA_ASSERT_EQ(string("fffc:ffffffff"), rpt_all_controllers.ToString()); + OLA_ASSERT_EQ(string("fffd:ffffffff"), rpt_all_devices.ToString()); + OLA_ASSERT_EQ(string("fffd:0052ffff"), + rpt_manufacturer_devices.ToString()); + OLA_ASSERT_EQ(string("fffd:0001ffff"), + rpt_manufacturer_devices2.ToString()); + OLA_ASSERT_EQ(rpt_all_controllers.ManufacturerId(), + static_cast(0xfffc)); + OLA_ASSERT_EQ(rpt_all_controllers.DeviceId(), + static_cast(0xffffffff)); + OLA_ASSERT_EQ(rpt_all_devices.ManufacturerId(), + static_cast(0xfffd)); + OLA_ASSERT_EQ(rpt_all_devices.DeviceId(), + static_cast(0xffffffff)); + OLA_ASSERT_EQ(rpt_manufacturer_devices.ManufacturerId(), + static_cast(0xfffd)); + OLA_ASSERT_EQ(rpt_manufacturer_devices.DeviceId(), + static_cast(0x0052ffff)); + OLA_ASSERT_EQ(rpt_manufacturer_devices2.ManufacturerId(), + static_cast(0xfffd)); + OLA_ASSERT_EQ(rpt_manufacturer_devices2.DeviceId(), + static_cast(0x0001ffff)); + // TODO(Peter): Handle the more complicated RPT vendorcast tests + OLA_ASSERT_TRUE(rpt_all_controllers.IsBroadcast()); + OLA_ASSERT_FALSE(rpt_all_controllers.IsVendorcast()); + OLA_ASSERT_TRUE(rpt_all_devices.IsBroadcast()); + OLA_ASSERT_FALSE(rpt_all_devices.IsVendorcast()); + OLA_ASSERT_TRUE(rpt_manufacturer_devices.IsBroadcast()); + OLA_ASSERT_TRUE(rpt_manufacturer_devices.IsVendorcast()); + OLA_ASSERT_TRUE(rpt_manufacturer_devices2.IsBroadcast()); + OLA_ASSERT_TRUE(rpt_manufacturer_devices2.IsVendorcast()); +} + + /* * Test the UIDs inequalities work */ diff --git a/common/rdm/VariableFieldSizeCalculator.cpp b/common/rdm/VariableFieldSizeCalculator.cpp index b054a38589..f30c55f4ed 100644 --- a/common/rdm/VariableFieldSizeCalculator.cpp +++ b/common/rdm/VariableFieldSizeCalculator.cpp @@ -69,7 +69,7 @@ VariableFieldSizeCalculator::calculator_state return data_size > m_fixed_size_sum ? TOO_LARGE : FIXED_SIZE; // we know there is only one, now we need to work out the number of - // repeatitions or length if it's a string + // repetitions or length if it's a string unsigned int bytes_remaining = data_size - m_fixed_size_sum; if (variable_string_field_count) { // variable string @@ -124,6 +124,12 @@ void VariableFieldSizeCalculator::Visit( } +void VariableFieldSizeCalculator::Visit( + const ola::messaging::IPV6FieldDescriptor *descriptor) { + m_fixed_size_sum += descriptor->MaxSize(); +} + + void VariableFieldSizeCalculator::Visit( const ola::messaging::MACFieldDescriptor *descriptor) { m_fixed_size_sum += descriptor->MaxSize(); diff --git a/common/rdm/VariableFieldSizeCalculator.h b/common/rdm/VariableFieldSizeCalculator.h index d6208b323c..4e224b0afa 100644 --- a/common/rdm/VariableFieldSizeCalculator.h +++ b/common/rdm/VariableFieldSizeCalculator.h @@ -63,6 +63,7 @@ class VariableFieldSizeCalculator void Visit(const ola::messaging::BoolFieldDescriptor*); void Visit(const ola::messaging::IPV4FieldDescriptor*); + void Visit(const ola::messaging::IPV6FieldDescriptor*); void Visit(const ola::messaging::MACFieldDescriptor*); void Visit(const ola::messaging::UIDFieldDescriptor*); void Visit(const ola::messaging::StringFieldDescriptor*); diff --git a/common/rpc/RpcChannel.cpp b/common/rpc/RpcChannel.cpp index 7d1c81d6fd..10b5211e93 100644 --- a/common/rpc/RpcChannel.cpp +++ b/common/rpc/RpcChannel.cpp @@ -166,7 +166,7 @@ void RpcChannel::DescriptorReady() { m_buffer_size = AllocateMsgBuffer(m_expected_size); if (m_buffer_size < m_expected_size) { - OLA_WARN << "buffer size to small " << m_buffer_size << " < " << + OLA_WARN << "buffer size too small: " << m_buffer_size << " < " << m_expected_size; return; } diff --git a/common/web/SchemaParser.h b/common/web/SchemaParser.h index dfbd34f44a..ba49dba29e 100644 --- a/common/web/SchemaParser.h +++ b/common/web/SchemaParser.h @@ -91,7 +91,7 @@ class SchemaParser : public JsonParserInterface { /** * @brief Claim the RootValidator that was created by parsing the schema. * @returns A new Validator, or NULL if the schema wasn't valid. Ownership of - * the validtor is transferred to the caller. + * the validator is transferred to the caller. */ ValidatorInterface* ClaimRootValidator(); diff --git a/config/ola.m4 b/config/ola.m4 index af7b0bc3a5..f767788c87 100644 --- a/config/ola.m4 +++ b/config/ola.m4 @@ -49,7 +49,14 @@ if test -z "$PROTOC" ; then AC_MSG_ERROR([cannot find 'protoc' program]); elif test -n "$1" ; then AC_MSG_CHECKING([protoc version]) - [protoc_version=`$PROTOC --version 2>&1 | grep 'libprotoc' | sed 's/.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/g'`] + # Since v20.x we only get effectively the minor and patch versions out of protoc. + # Treat them as major and minor and everything should keep working indefinitely. + # See https://protobuf.dev/support/version-support/ + # So we've got either of these: + # libprotoc 2.4.1 + # libprotoc 23.3 + # The first sed ensures all versions have major, minor, patch, by adding a .0 on the end of ones missing it + [protoc_version=`$PROTOC --version 2>&1 | grep 'libprotoc' | sed 's/\([^\.0-9][0-9][0-9]*\.[0-9][0-9]*\)$/\1\.0/g' | sed 's/[^0-9]*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/g'`] [required=$1] [required_major=`echo $required | sed 's/[^0-9].*//'`] [required_minor=`echo $required | sed 's/[0-9][0-9]*\.\([0-9][0-9]*\)\.[0-9][0-9]*/\1/'`] diff --git a/configure.ac b/configure.ac index 3e9e809afe..c54571a452 100644 --- a/configure.ac +++ b/configure.ac @@ -894,6 +894,7 @@ PLUGIN_SUPPORT(artnet, USE_ARTNET) PLUGIN_SUPPORT(dmx4linux, USE_DMX4LINUX, [$have_dmx4linux]) PLUGIN_SUPPORT(dummy, USE_DUMMY) PLUGIN_SUPPORT(e131, USE_E131) +PLUGIN_SUPPORT(e133, USE_E133) PLUGIN_SUPPORT(espnet, USE_ESPNET) PLUGIN_SUPPORT(ftdidmx, USE_FTDI, [$have_libftdi]) PLUGIN_SUPPORT(gpio, USE_GPIO) diff --git a/data/rdm/pids.proto b/data/rdm/pids.proto index cebb8ef20e..50548ae6a0 100644 --- a/data/rdm/pids.proto +++ b/data/rdm/pids.proto @@ -4010,6 +4010,144 @@ pid { } set_sub_device_range: ROOT_OR_ALL_SUBDEVICE } +pid { + name: "COMPONENT_SCOPE" + value: 2048 + get_request { + field { + type: UINT16 + name: "scope_slot" + range { + min: 1 + max: 65535 + } + } + } + get_response { + field { + type: UINT16 + name: "scope_slot" + range { + min: 1 + max: 65535 + } + } + field { + type: STRING + name: "scope_string" + min_size: 63 + max_size: 63 + } + field { + type: UINT8 + name: "static_config_type" + label { + value: 0 + label: "No static config" + } + label { + value: 1 + label: "Static config IPv4" + } + label { + value: 1 + label: "Static config IPv6" + } + range { + min: 0 + max: 2 + } + } + field { + type: IPV4 + name: "static_broker_ipv4_address" + label { + value: 0 + label: "No static broker IPv4 address" + } + } + field { + type: IPV6 + name: "static_broker_ipv6_address" + label { + value: 0 + label: "No static broker IPv6 address" + } + } + field { + type: UINT16 + name: "static_broker_port" + label { + value: 0 + label: "No static broker port" + } + } + } + get_sub_device_range: ROOT_DEVICE + set_request { + field { + type: UINT16 + name: "scope_slot" + range { + min: 1 + max: 65535 + } + } + field { + type: STRING + name: "scope_string" + min_size: 63 + max_size: 63 + } + field { + type: UINT8 + name: "static_config_type" + label { + value: 0 + label: "No static config" + } + label { + value: 1 + label: "Static config IPv4" + } + label { + value: 1 + label: "Static config IPv6" + } + range { + min: 0 + max: 2 + } + } + field { + type: IPV4 + name: "static_broker_ipv4_address" + label { + value: 0 + label: "No static broker IPv4 address" + } + } + field { + type: IPV6 + name: "static_broker_ipv6_address" + label { + value: 0 + label: "No static broker IPv6 address" + } + } + field { + type: UINT16 + name: "static_broker_port" + label { + value: 0 + label: "No static broker port" + } + } + } + set_response { + } + set_sub_device_range: ROOT_DEVICE +} pid { name: "SEARCH_DOMAIN" value: 2049 @@ -4036,6 +4174,60 @@ pid { } set_sub_device_range: ROOT_DEVICE } +pid { + name: "TCP_COMMS_STATUS" + value: 2050 + get_request { + } + get_response { + field { + type: GROUP + name: "comms_statuses" + field { + type: STRING + name: "scope_string" + min_size: 63 + max_size: 63 + } + field { + type: IPV4 + name: "broker_ipv4_address" + label { + value: 0 + label: "No IPv4 Connection" + } + } + field { + type: IPV6 + name: "broker_ipv6_address" + label { + value: 0 + label: "No IPv6 Connection" + } + } + field { + type: UINT16 + name: "broker_port" + } + field { + type: UINT16 + name: "unhealthy_tcp_events" + } + } + } + get_sub_device_range: ROOT_DEVICE + set_request { + field { + type: STRING + name: "scope_string" + min_size: 63 + max_size: 63 + } + } + set_response { + } + set_sub_device_range: ROOT_DEVICE +} pid { name: "BROKER_STATUS" value: 2051 diff --git a/examples/ola-client.cpp b/examples/ola-client.cpp index f6eeab53c9..c639889efd 100644 --- a/examples/ola-client.cpp +++ b/examples/ola-client.cpp @@ -84,6 +84,7 @@ typedef struct { string cmd; // argv[0] string uni_name; // universe name string dmx; // DMX string + bool blackout; ola::port_priority_mode priority_mode; // port priority mode uint8_t priority_value; // port priority value bool list_plugin_ids; @@ -304,6 +305,7 @@ void InitOptions(options *opts) { opts->port_direction = ola::client::OUTPUT_PORT; opts->device_id = INVALID_VALUE; opts->merge_mode = OlaUniverse::MERGE_HTP; + opts->blackout = false; opts->priority_mode = ola::PRIORITY_MODE_INHERIT; opts->priority_value = 0; } @@ -358,6 +360,7 @@ void ParseOptions(int argc, char *argv[], options *opts) { static struct option long_options[] = { {"dmx", required_argument, 0, 'd'}, + {"blackout", no_argument, 0, 'b'}, {"help", no_argument, 0, 'h'}, {"ltp", no_argument, 0, 'l'}, {"name", required_argument, 0, 'n'}, @@ -373,7 +376,7 @@ void ParseOptions(int argc, char *argv[], options *opts) { int option_index = 0; while (1) { - c = getopt_long(argc, argv, "ld:n:u:p:s:hv", long_options, &option_index); + c = getopt_long(argc, argv, "ld:bn:u:p:s:hv", long_options, &option_index); if (c == -1) break; @@ -384,6 +387,9 @@ void ParseOptions(int argc, char *argv[], options *opts) { case 'd': opts->dmx = optarg; break; + case 'b': + opts->blackout = true; + break; case 'h': opts->help = true; break; @@ -656,7 +662,8 @@ void DisplayUniverseMergeHelp(const options &opts) { * Help message for set dmx */ void DisplaySetDmxHelp(const options &opts) { - cout << "Usage: " << opts.cmd << " --universe --dmx \n" + cout << "Usage: " << opts.cmd << " --universe [ --dmx ] " + "[ --blackout ]\n" "\n" "Sets the DMX values for a universe.\n" "\n" @@ -665,6 +672,7 @@ void DisplaySetDmxHelp(const options &opts) { " -d, --dmx Comma separated DMX values, e.g. " "0,255,128 sets first channel to 0, second channel to 255" " and third channel to 128.\n" + " -b, --blackout Send a universe to blackout instead.\n" << endl; } @@ -857,9 +865,16 @@ int SendDmx(OlaClientWrapper *wrapper, const options &opts) { SelectServer *ss = wrapper->GetSelectServer(); OlaClient *client = wrapper->GetClient(); ola::DmxBuffer buffer; - bool status = buffer.SetFromString(opts.dmx); + bool status = false; + if (opts.blackout) { + status = buffer.Blackout(); + } else { + status = buffer.SetFromString(opts.dmx); + } - if (opts.uni < 0 || !status || buffer.Size() == 0) { + // A dmx string and blackout are mutually exclusive + if (opts.uni < 0 || !status || (opts.blackout && !opts.dmx.empty()) || + buffer.Size() == 0) { DisplaySetDmxHelp(opts); exit(1); } diff --git a/include/ola/acn/ACNVectors.h b/include/ola/acn/ACNVectors.h index 72c050ea91..b058beb810 100644 --- a/include/ola/acn/ACNVectors.h +++ b/include/ola/acn/ACNVectors.h @@ -43,13 +43,14 @@ namespace acn { * @brief ACN vectors used at the root layer. */ enum RootVector { - VECTOR_ROOT_E131_REV2 = 3, /**< Draft E1.31, used by some old gear. */ - VECTOR_ROOT_E131 = 4, /**< E1.31 (sACN) */ - VECTOR_ROOT_RPT = 5, /**< E1.33 (RPT) */ - VECTOR_ROOT_NULL = 6, /**< NULL (empty) root */ - VECTOR_ROOT_BROKER = 9, /**< E1.33 (Broker) */ - VECTOR_ROOT_LLRP = 0x0A, /**< E1.33 (LLRP) */ - VECTOR_ROOT_EPT = 0x0B, /**< E1.33 (EPT) */ + /** @brief Draft E1.31, used by some old gear. */ + VECTOR_ROOT_E131_REV2 = 0x00000003, + VECTOR_ROOT_E131 = 0x00000004, /**< E1.31 (sACN) */ + VECTOR_ROOT_RPT = 0x00000005, /**< E1.33 (RPT) */ + VECTOR_ROOT_NULL = 0x00000006, /**< NULL (empty) root */ + VECTOR_ROOT_BROKER = 0x00000009, /**< E1.33 (Broker) */ + VECTOR_ROOT_LLRP = 0x0000000A, /**< E1.33 (LLRP) */ + VECTOR_ROOT_EPT = 0x0000000B, /**< E1.33 (EPT) */ }; /** @@ -94,9 +95,123 @@ enum E133ControllerVector { * @brief Vectors used at the E1.33 LLRP layer. */ enum LLRPVector { - VECTOR_LLRP_PROBE_REQUEST = 1, /**< LLRP Probe Request */ - VECTOR_LLRP_PROBE_REPLY = 2, /**< LLRP Probe Reply */ - VECTOR_LLRP_RDM_CMD = 3, /**< LLRP RDM Command */ + VECTOR_LLRP_PROBE_REQUEST = 0x00000001, /**< LLRP Probe Request */ + VECTOR_LLRP_PROBE_REPLY = 0x00000002, /**< LLRP Probe Reply */ + VECTOR_LLRP_RDM_CMD = 0x00000003, /**< LLRP RDM Command */ +}; + +/** + * @brief Vectors used at the E1.33 Broker layer. + */ +enum BrokerVector { + VECTOR_BROKER_CONNECT = 0x0001, /**< Broker Client Connect */ + VECTOR_BROKER_CONNECT_REPLY = 0x0002, /**< Broker Connect Reply */ + + /** @brief Broker Client Entry Update */ + VECTOR_BROKER_CLIENT_ENTRY_UPDATE = 0x0003, + + VECTOR_BROKER_REDIRECT_V4 = 0x0004, /**< Broker Client Redirect IPv4 */ + VECTOR_BROKER_REDIRECT_V6 = 0x0005, /**< Broker Client Redirect IPv6 */ + VECTOR_BROKER_FETCH_CLIENT_LIST = 0x0006, /**< Broker Fetch Client List */ + + /** @brief Broker Connected Client List */ + VECTOR_BROKER_CONNECTED_CLIENT_LIST = 0x0007, + + /** @brief Broker Client Incremental Addition */ + VECTOR_BROKER_CLIENT_ADD = 0x0008, + + /** @brief Broker Client Incremental Deletion */ + VECTOR_BROKER_CLIENT_REMOVE = 0x0009, + + /** @brief Broker Client Entry Change */ + VECTOR_BROKER_CLIENT_ENTRY_CHANGE = 0x000A, + + /** @brief Broker Request Dynamic UID Assignment */ + VECTOR_BROKER_REQUEST_DYNAMIC_UIDS = 0x000B, + + /** @brief Broker Dynamic UID Assignment List */ + VECTOR_BROKER_ASSIGNED_DYNAMIC_UIDS = 0x000C, + + /** @brief Broker Fetch Dynamic UID Assignment List */ + VECTOR_BROKER_FETCH_DYNAMIC_UID_LIST = 0x000D, + + VECTOR_BROKER_DISCONNECT = 0x000E, /**< Broker Client Disconnect */ + VECTOR_BROKER_NULL = 0x000F, /**< Broker Client Null */ +}; + +// Table A-8, RPT PDU Vector +/** + * @brief Vectors used at the E1.33 RPT layer. + */ +enum RPTVector { + VECTOR_RPT_REQUEST = 0x00000001, /**< RPT Request */ + VECTOR_RPT_STATUS = 0x00000002, /**< RPT Status */ + VECTOR_RPT_NOTIFICATION = 0x00000003, /**< RPT Notification */ +}; + +// Table A-9, RPT Request PDU Vector +/** + * @brief Vectors used at the E1.33 RPT Request layer. + */ +enum RPTRequestVector { + VECTOR_REQUEST_RDM_CMD = 0x01, /**< RPT Request RDM Command */ +}; + +// Table A-10, RPT Status PDU Vector +/** + * @brief Vectors used at the E1.33 RPT Status layer. + */ +enum RPTStatusVector { + /** @brief RPT Status Unknown RPT UID */ + VECTOR_RPT_STATUS_UNKNOWN_RPT_UID = 0x0001, + + VECTOR_RPT_STATUS_RDM_TIMEOUT = 0x0002, /**< RPT Status RDM Timeout */ + + /** @brief RPT Status RDM Invalid Response */ + VECTOR_RPT_STATUS_RDM_INVALID_RESPONSE = 0x0003, + + /** @brief RPT Status Unknown RDM UID */ + VECTOR_RPT_STATUS_UNKNOWN_RDM_UID = 0x0004, + + /** @brief RPT Status Unknown Endpoint */ + VECTOR_RPT_STATUS_UNKNOWN_ENDPOINT = 0x0005, + + /** @brief RPT Status Broadcast Complete */ + VECTOR_RPT_STATUS_BROADCAST_COMPLETE = 0x0006, + + VECTOR_RPT_STATUS_UNKNOWN_VECTOR = 0x0007, /**< RPT Status Unknown Vector */ + + /** @brief RPT Status Invalid Message */ + VECTOR_RPT_STATUS_INVALID_MESSAGE = 0x0008, + + /** @brief RPT Status Invalid Command Class */ + VECTOR_RPT_STATUS_INVALID_COMMAND_CLASS = 0x0009, +}; + +// Table A-11, RPT Notification PDU Vector +/** + * @brief Vectors used at the E1.33 RPT Notification layer. + */ +enum RPTNotificationVector { + VECTOR_NOTIFICATION_RDM_CMD = 0x01, /**< RPT Notification RDM Command */ +}; + +/** + * @brief Vectors used at the E1.33 RDM Command layer. + */ +enum RDMCommandVector { + VECTOR_RDM_CMD_RDM_DATA = 0xCC, /**< E1.33 RDM Command */ +}; + +// Table A-21, Client Protocol Codes +// These aren't fully named as vectors in the standard, but are used as such so +// we put them in here +/** + * @brief Vectors used at the E1.33 Broker Client Entry layer. + */ +enum ClientProtocolCode { + CLIENT_PROTOCOL_RPT = 0x00000005, /**< Broker RPT Client Entry */ + CLIENT_PROTOCOL_EPT = 0x0000000B, /**< Broker EPT Client Entry */ }; /** diff --git a/include/ola/e133/DeviceManager.h b/include/ola/e133/DeviceManager.h index 9fada52606..023e8d9f1b 100644 --- a/include/ola/e133/DeviceManager.h +++ b/include/ola/e133/DeviceManager.h @@ -53,7 +53,7 @@ class DeviceManager { * @returns true if the data should be acknowledged, false otherwise. */ typedef ola::Callback3 RDMMesssageCallback; + const string&> RDMMessageCallback; // Run when we acquire designated controller status for a device. typedef ola::Callback1 AcquireDeviceCallback; @@ -66,7 +66,7 @@ class DeviceManager { ~DeviceManager(); // Ownership of the callbacks is transferred. - void SetRDMMessageCallback(RDMMesssageCallback *callback); + void SetRDMMessageCallback(RDMMessageCallback *callback); void SetAcquireDeviceCallback(AcquireDeviceCallback *callback); void SetReleaseDeviceCallback(ReleaseDeviceCallback *callback); diff --git a/include/ola/e133/E133Enums.h b/include/ola/e133/E133Enums.h index f4c2b4ac06..f90697779f 100644 --- a/include/ola/e133/E133Enums.h +++ b/include/ola/e133/E133Enums.h @@ -21,9 +21,17 @@ #ifndef INCLUDE_OLA_E133_E133ENUMS_H_ #define INCLUDE_OLA_E133_E133ENUMS_H_ +#include + namespace ola { namespace e133 { +// Appendix A - Endpoints and Table 3-1: Endpoint ID Allocation +static const uint16_t NULL_ENDPOINT = 0x0000; +static const uint16_t MIN_DEVICE_ENDPOINT = 0x0001; +static const uint16_t MAX_DEVICE_ENDPOINT = 0xF9FF; +static const uint16_t BROADCAST_ENDPOINT = 0xFFFF; + // Table A-6, Discovery Stats enum DiscoveryState { DISCOVERY_INCOMPLETE = 0, @@ -39,6 +47,7 @@ enum EndpointMode { ENDPOINT_MODE_OUTPUT = 2, }; +// TODO(Peter): Check that this no longer exists // Table A-9 E1.33 Status Codes enum E133StatusCode { SC_E133_ACK = 0x0000, @@ -53,10 +62,53 @@ enum E133StatusCode { SC_E133_BROADCAST_COMPLETE = 0x0009, }; +// Table A-19 E1.33 Connection Status Codes for Broker Connect +enum E133ConnectStatusCode { + CONNECT_OK = 0x0000, + CONNECT_SCOPE_MISMATCH = 0x0001, + CONNECT_CAPACITY_EXCEEDED = 0x0002, + CONNECT_DUPLICATE_UID = 0x0003, + CONNECT_INVALID_CLIENT_ENTRY = 0x0004, + CONNECT_INVALID_UID = 0x0005, +}; + +// Table A-20 E1.33 Status Codes for Dynamic UID Mapping +enum E133DynamicUIDStatusCode { + DYNAMIC_UID_STATUS_OK = 0x0000, + DYNAMIC_UID_STATUS_INVALID_REQUEST = 0x0001, + DYNAMIC_UID_STATUS_UID_NOT_FOUND = 0x0002, + DYNAMIC_UID_STATUS_DUPLICATE_RID = 0x0003, + DYNAMIC_UID_STATUS_CAPACITY_EXHAUSTED = 0x0004, +}; + +// Table A-22 E1.33 RPT Client Type Codes +enum E133RPTClientTypeCode { + RPT_CLIENT_TYPE_DEVICE = 0x00, + RPT_CLIENT_TYPE_CONTROLLER = 0x01, +}; + +// Table A-24 E1.33 Client Disconnect Reason Codes +enum E133DisconnectStatusCode { + DISCONNECT_SHUTDOWN = 0x0000, + DISCONNECT_CAPACITY_EXHAUSTED = 0x0001, + DISCONNECT_HARDWARE_FAULT = 0x0002, + DISCONNECT_SOFTWARE_FAULT = 0x0003, + DISCONNECT_SOFTWARE_RESET = 0x0004, + DISCONNECT_INCORRECT_SCOPE = 0x0005, + DISCONNECT_RPT_RECONFIGURE = 0x0006, + DISCONNECT_LLRP_RECONFIGURE = 0x0007, + DISCONNECT_USER_RECONFIGURE = 0x0008, +}; + // The max size of an E1.33 Status string. enum { MAX_E133_STATUS_STRING_SIZE = 64 }; + +// The E1.33 version. +enum { + E133_VERSION = 1 +}; } // namespace e133 } // namespace ola #endif // INCLUDE_OLA_E133_E133ENUMS_H_ diff --git a/include/ola/e133/E133Helper.h b/include/ola/e133/E133Helper.h new file mode 100644 index 0000000000..4fad7608ae --- /dev/null +++ b/include/ola/e133/E133Helper.h @@ -0,0 +1,36 @@ +/* + * 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. + * + * E133Helper.h + * Various misc E1.33 functions. + * Copyright (C) 2024 Peter Newman + */ + +#ifndef INCLUDE_OLA_E133_E133HELPER_H_ +#define INCLUDE_OLA_E133_E133HELPER_H_ + +#include +#include +#include + +namespace ola { +namespace e133 { + +bool IntToRPTClientType(uint8_t input, + ola::e133::E133RPTClientTypeCode *client_type); +std::string RPTClientTypeToString(uint8_t type); +} // namespace e133 +} // namespace ola +#endif // INCLUDE_OLA_E133_E133HELPER_H_ diff --git a/include/ola/e133/E133StatusHelper.h b/include/ola/e133/E133StatusHelper.h index cd9680b029..62c4f223d0 100644 --- a/include/ola/e133/E133StatusHelper.h +++ b/include/ola/e133/E133StatusHelper.h @@ -22,17 +22,28 @@ #define INCLUDE_OLA_E133_E133STATUSHELPER_H_ #include +#include #include +#include #include namespace ola { namespace e133 { -using std::string; -using ola::e133::E133StatusCode; +bool IntToStatusCode(uint16_t input, ola::e133::E133StatusCode *status_code); +std::string StatusCodeToString(ola::e133::E133StatusCode status_code); -bool IntToStatusCode(uint16_t input, E133StatusCode *status_code); -string StatusCodeToString(E133StatusCode status_code); +bool IntToConnectStatusCode( + uint16_t input, + ola::e133::E133ConnectStatusCode *connect_status_code); +std::string ConnectStatusCodeToString( + E133ConnectStatusCode connect_status_code); + +bool IntToRPTStatusCode(uint16_t input, + ola::acn::RPTStatusVector *rpt_status_code); +std::string RPTStatusCodeToString(ola::acn::RPTStatusVector rpt_status_code); +bool RPTStatusCodeToRDMStatusCode(ola::acn::RPTStatusVector rpt_status_code, + ola::rdm::RDMStatusCode *rdm_status_code); } // namespace e133 } // namespace ola #endif // INCLUDE_OLA_E133_E133STATUSHELPER_H_ diff --git a/include/ola/e133/Makefile.mk b/include/ola/e133/Makefile.mk index d9f7c11c2f..d8f72c9728 100644 --- a/include/ola/e133/Makefile.mk +++ b/include/ola/e133/Makefile.mk @@ -4,6 +4,7 @@ if INSTALL_E133 olae133include_HEADERS = \ include/ola/e133/DeviceManager.h \ include/ola/e133/E133Enums.h \ + include/ola/e133/E133Helper.h \ include/ola/e133/E133Receiver.h \ include/ola/e133/E133StatusHelper.h \ include/ola/e133/E133URLParser.h \ diff --git a/include/ola/e133/MessageBuilder.h b/include/ola/e133/MessageBuilder.h index 272a56339a..ace419b40f 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/include/ola/io/MemoryBlock.h b/include/ola/io/MemoryBlock.h index 464fdfd77b..2a053d52bb 100644 --- a/include/ola/io/MemoryBlock.h +++ b/include/ola/io/MemoryBlock.h @@ -63,7 +63,7 @@ class MemoryBlock { /** * @brief Move the insertation point to the end of the block. - * This is useful if you want to use the block in pre-pend mode. + * This is useful if you want to use the block in prepend mode. */ void SeekBack() { m_first = m_data_end; diff --git a/include/ola/messaging/Descriptor.h b/include/ola/messaging/Descriptor.h index 3f287141ea..f997a9a00d 100644 --- a/include/ola/messaging/Descriptor.h +++ b/include/ola/messaging/Descriptor.h @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -116,6 +117,25 @@ class IPV4FieldDescriptor: public FieldDescriptor { }; +/** + * A FieldDescriptor that represents a IPv6 Address + */ +class IPV6FieldDescriptor: public FieldDescriptor { + public: + explicit IPV6FieldDescriptor(const std::string &name) + : FieldDescriptor(name) { + } + + bool FixedSize() const { return true; } + bool LimitedSize() const { return true; } + unsigned int MaxSize() const { return ola::network::IPV6Address::LENGTH; } + + void Accept(FieldDescriptorVisitor *visitor) const { + visitor->Visit(this); + } +}; + + /** * A FieldDescriptor that represents a MAC Address */ diff --git a/include/ola/messaging/DescriptorVisitor.h b/include/ola/messaging/DescriptorVisitor.h index 43a64f572a..5b5820d588 100644 --- a/include/ola/messaging/DescriptorVisitor.h +++ b/include/ola/messaging/DescriptorVisitor.h @@ -30,6 +30,7 @@ namespace messaging { class BoolFieldDescriptor; class FieldDescriptorGroup; class IPV4FieldDescriptor; +class IPV6FieldDescriptor; class MACFieldDescriptor; class StringFieldDescriptor; class UIDFieldDescriptor; @@ -49,6 +50,7 @@ class FieldDescriptorVisitor { virtual void Visit(const BoolFieldDescriptor*) = 0; virtual void Visit(const IPV4FieldDescriptor*) = 0; + virtual void Visit(const IPV6FieldDescriptor*) = 0; virtual void Visit(const MACFieldDescriptor*) = 0; virtual void Visit(const UIDFieldDescriptor*) = 0; virtual void Visit(const StringFieldDescriptor*) = 0; diff --git a/include/ola/messaging/Message.h b/include/ola/messaging/Message.h index b8062f57c1..ecc56bd2a2 100644 --- a/include/ola/messaging/Message.h +++ b/include/ola/messaging/Message.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -125,6 +126,32 @@ class IPV4MessageField: public MessageFieldInterface { }; +/** + * A MessageField that represents a IPv6 Address + */ +class IPV6MessageField: public MessageFieldInterface { + public: + IPV6MessageField(const IPV6FieldDescriptor *descriptor, + const ola::network::IPV6Address &value) + : m_descriptor(descriptor), + m_value(value) { + } + + const IPV6FieldDescriptor *GetDescriptor() const { + return m_descriptor; + } + const ola::network::IPV6Address& Value() const { return m_value; } + + void Accept(MessageVisitor *visitor) const { + visitor->Visit(this); + } + + private: + const IPV6FieldDescriptor *m_descriptor; + ola::network::IPV6Address m_value; +}; + + /** * A MessageField that represents a MAC Address */ diff --git a/include/ola/messaging/MessagePrinter.h b/include/ola/messaging/MessagePrinter.h index 0053d16618..2f2f363664 100644 --- a/include/ola/messaging/MessagePrinter.h +++ b/include/ola/messaging/MessagePrinter.h @@ -40,6 +40,7 @@ class MessagePrinter: public MessageVisitor { virtual void Visit(const BoolMessageField*) {} virtual void Visit(const IPV4MessageField*) {} + virtual void Visit(const IPV6MessageField*) {} virtual void Visit(const MACMessageField*) {} virtual void Visit(const UIDMessageField*) {} virtual void Visit(const StringMessageField*) {} @@ -78,6 +79,7 @@ class GenericMessagePrinter: public MessagePrinter { virtual void Visit(const BoolMessageField*); virtual void Visit(const IPV4MessageField*); + virtual void Visit(const IPV6MessageField*); virtual void Visit(const MACMessageField*); virtual void Visit(const UIDMessageField*); virtual void Visit(const StringMessageField*); diff --git a/include/ola/messaging/MessageVisitor.h b/include/ola/messaging/MessageVisitor.h index 00812f8e56..81b55ed965 100644 --- a/include/ola/messaging/MessageVisitor.h +++ b/include/ola/messaging/MessageVisitor.h @@ -30,6 +30,7 @@ namespace messaging { class BoolMessageField; class GroupMessageField; class IPV4MessageField; +class IPV6MessageField; class MACMessageField; class StringMessageField; class UIDMessageField; @@ -46,6 +47,7 @@ class MessageVisitor { virtual void Visit(const BoolMessageField*) = 0; virtual void Visit(const IPV4MessageField*) = 0; + virtual void Visit(const IPV6MessageField*) = 0; virtual void Visit(const MACMessageField*) = 0; virtual void Visit(const UIDMessageField*) = 0; virtual void Visit(const StringMessageField*) = 0; diff --git a/include/ola/messaging/SchemaPrinter.h b/include/ola/messaging/SchemaPrinter.h index 21feb46090..95aa7c9e6b 100644 --- a/include/ola/messaging/SchemaPrinter.h +++ b/include/ola/messaging/SchemaPrinter.h @@ -50,6 +50,7 @@ class SchemaPrinter: public FieldDescriptorVisitor { void Visit(const BoolFieldDescriptor*); void Visit(const IPV4FieldDescriptor*); + void Visit(const IPV6FieldDescriptor*); void Visit(const MACFieldDescriptor*); void Visit(const UIDFieldDescriptor*); void Visit(const StringFieldDescriptor*); diff --git a/include/ola/messaging/StringMessageBuilder.h b/include/ola/messaging/StringMessageBuilder.h index 4bb9011c4b..12b7273fd3 100644 --- a/include/ola/messaging/StringMessageBuilder.h +++ b/include/ola/messaging/StringMessageBuilder.h @@ -42,6 +42,7 @@ class StringMessageBuilder: public FieldDescriptorVisitor { void Visit(const BoolFieldDescriptor*); void Visit(const IPV4FieldDescriptor*); + void Visit(const IPV6FieldDescriptor*); void Visit(const MACFieldDescriptor*); void Visit(const UIDFieldDescriptor*); void Visit(const StringFieldDescriptor*); diff --git a/include/ola/network/AdvancedTCPConnector.h b/include/ola/network/AdvancedTCPConnector.h index fdc57c7b04..c6e3d61f26 100644 --- a/include/ola/network/AdvancedTCPConnector.h +++ b/include/ola/network/AdvancedTCPConnector.h @@ -84,7 +84,9 @@ class AdvancedTCPConnector { /** * @brief Return the number of connections tracked by this connector. */ - unsigned int EndpointCount() const { return m_connections.size(); } + unsigned int EndpointCount() const { + return static_cast(m_connections.size()); + } /** * @brief The state of a connection. diff --git a/include/ola/network/HealthCheckedConnection.h b/include/ola/network/HealthCheckedConnection.h index a4311f51eb..8195c7ea70 100644 --- a/include/ola/network/HealthCheckedConnection.h +++ b/include/ola/network/HealthCheckedConnection.h @@ -18,16 +18,17 @@ * * This class adds health checking to a connection, which ensures that the * connection is able to transfer data in a timely manner. The implementation - * is pretty simple: we define a heart beat interval I, which *must* be the + * is pretty simple: we define a heartbeat interval I, which *must* be the * same at both ends of the connection. Every I seconds, both ends send a - * heart beat message and if either end doesn't receive a heart beat in - * 2.5 * I, the connection is deemed dead, and the connection is closed. + * heartbeat message and if either end doesn't receive a heartbeat within the + * timeout interval (which defaults to 2.5 * I if not specified), the + * connection is deemed dead, and the connection is closed. * * This class provides the basic health check mechanism, the sub class is left * to define the format of the heartbeat message. * * To use this health checked channel, subclass HealthCheckedConnection, and - * provide the SendHeartbeat() and HeartbeatTimeout methods. + * provide the SendHeartbeat() and HeartbeatTimeout() methods. * * There are some additional features: * - Some receivers may want to stop reading from a connection under some @@ -57,7 +58,10 @@ namespace network { class HealthCheckedConnection { public: HealthCheckedConnection(ola::thread::SchedulerInterface *scheduler, + const ola::TimeInterval heartbeat_interval, const ola::TimeInterval timeout_interval); + HealthCheckedConnection(ola::thread::SchedulerInterface *scheduler, + const ola::TimeInterval heartbeat_interval); virtual ~HealthCheckedConnection(); /** @@ -75,7 +79,7 @@ class HealthCheckedConnection { /** * Call this when a heartbeat is piggybacked on another message. This - * prevents sending heatbeats unless necessary. + * prevents sending heartbeats unless necessary. */ void HeartbeatSent(); @@ -106,6 +110,7 @@ class HealthCheckedConnection { private: ola::thread::SchedulerInterface *m_scheduler; ola::TimeInterval m_heartbeat_interval; + ola::TimeInterval m_timeout_interval; ola::thread::timeout_id m_send_timeout_id; ola::thread::timeout_id m_receive_timeout_id; diff --git a/include/ola/network/IPV4Address.h b/include/ola/network/IPV4Address.h index 06857af6f8..06139393d5 100644 --- a/include/ola/network/IPV4Address.h +++ b/include/ola/network/IPV4Address.h @@ -14,7 +14,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * IPV4Address.h - * Represents a IPv4 Address + * Represents an IPv4 Address * Copyright (C) 2011 Simon Newton */ diff --git a/include/ola/network/IPV6Address.h b/include/ola/network/IPV6Address.h new file mode 100644 index 0000000000..23f6000c25 --- /dev/null +++ b/include/ola/network/IPV6Address.h @@ -0,0 +1,240 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * IPV6Address.h + * Represents an IPv6 Address + * Copyright (C) 2023 Peter Newman + */ + +/** + * @addtogroup network + * @{ + * @file IPV6Address.h + * @brief Represents an IPv6 Address. + * @} + */ + +#ifndef INCLUDE_OLA_NETWORK_IPV6ADDRESS_H_ +#define INCLUDE_OLA_NETWORK_IPV6ADDRESS_H_ + +#include // Required by FreeBSD +#include // Required by FreeBSD +#include +#include +#include +#include + +namespace ola { +namespace network { + +/** + * @addtogroup network + * @{ + */ + +/** + * @brief Represents an IPv6 Address. + * + * All methods use network byte order unless otherwise mentioned. + */ +class IPV6Address { + public: + /** + * @brief The length in bytes of an IPv6 address. + */ + enum { LENGTH = 16 }; + + /** + * @brief Create a new IPv6 Address set to IN6ADDR_ANY_INIT (::). + */ + IPV6Address() { + m_address = IN6ADDR_ANY_INIT; + } + +// TODO(Peter): From uint128_t? + + /** + * @brief Construct a new IPv6 address from binary data. + * @param address a pointer to the memory containing the IPv6 address data. The + * data should be most significant byte first. + */ + explicit IPV6Address(const uint8_t *address); + + /** + * @brief Create a new IPv6 Address from a in6_addr struct. + * @param address the ip address, in network byte order. + */ + explicit IPV6Address(in6_addr address) { + m_address = address; + } + + /** + * @brief Copy constructor. + * @param other the IPV6Address to copy. + */ + IPV6Address(const IPV6Address &other) + : m_address(other.m_address) { + } + + /** + * @brief Assignment operator. + * @param other the IPV6Address to assign to this object. + */ + IPV6Address& operator=(const IPV6Address &other) { + if (this != &other) { + m_address = other.m_address; + } + return *this; + } + + /** + * @brief Equals operator. + * @param other the IPV6Address to compare. + * @returns true if both IPV6Addresses are equal. + */ + bool operator==(const IPV6Address &other) const { + return IN6_ARE_ADDR_EQUAL(&m_address, &other.m_address); + } + + /** + * @brief Not equals operator. + * @param other the IPV6Address to compare. + * @returns false if both IPV6Addresses are equal. + */ + bool operator!=(const IPV6Address &other) const { + return !(*this == other); + } + + /** + * @brief Less than operator for partial ordering. + */ + bool operator<(const IPV6Address &other) const; + + /** + * @brief Greater than operator. + */ + bool operator>(const IPV6Address &other) const; + + /** +// * @brief Return the IPV6Address as an int in network-byte order. +// * @returns An uint32 representing the IP address. + */ +// uint32_t AsInt() const { return m_address; } + + /** + * @brief Checks if this address is the wildcard address (::). + * @returns true if this address is the wildcard address. + */ + bool IsWildcard() const; + + /** + * @brief Copy the IPV6Address to a memory location. + * @param ptr the memory location to copy the address to. The location + * should be at least LENGTH bytes. + * @note The address is copied in network byte order. + */ + void Get(uint8_t ptr[LENGTH]) const { + memcpy(ptr, + reinterpret_cast(&m_address.s6_addr[0]), + LENGTH); + } + + /** + * @brief Write the binary representation of the IPv6 address to memory. + * @param buffer a pointer to memory to write the IPv6 address to + * @param length the size of the memory block, should be at least LENGTH. + * @returns true if length was >= LENGTH, false otherwise. + */ + bool Pack(uint8_t *buffer, unsigned int length) const { + if (length < LENGTH) + return false; + Get(buffer); + return true; + } + + /** + * @brief Convert the IPV6Address to a string. + * @returns the string representation of this IPV6Address. + */ + std::string ToString() const; + + /** + * @brief Write the string representation of this IPV6Address to an + * ostream. + * @param out the ostream to write to. + * @param address the address to write. + */ + friend std::ostream& operator<<(std::ostream &out, + const IPV6Address &address) { + return out << address.ToString(); + } + + /** + * @brief Convert a string to an IPV6Address. + * @param address the IP address string to convert. + * @returns a new IPV6Address or NULL if the string was invalid. The caller + * is responsible for deleting the IPV6Address object. + */ + static IPV6Address* FromString(const std::string &address); + + /** + * @brief Convert a string to an IPV6Address. + * @param address the IP address string to convert. + * @param[out] target the converted IPV6Address. + * @returns true if the string was a valid IPv6 address, false otherwise. + */ + static bool FromString(const std::string &address, IPV6Address *target); + + /** + * @brief Convert a string to an IPV6Address or abort. + * @note This should only be used within tests. + * @param address the IP address to convert. + * @return an IPV6Address matching the string. + */ + static IPV6Address FromStringOrDie(const std::string &address); + + /** +// * @brief Convert a subnet mask to its CIDR format value +// * @param address the subnet mask as an IPV6Address object + * @param mask the mask variable to populate + * @return true if we managed to convert the address to a CIDR value, false + otherwise + */ +// static bool ToCIDRMask(IPV6Address address, uint8_t *mask); + + /** + * @brief Returns the wildcard address IN6ADDR_ANY_INIT (::). + * @return an IPV6Address representing the wildcard address. + */ + static IPV6Address WildCard(); + + // TODO(Peter): Add support for the all-nodes link-local multicast group + + /** + * @brief Returns the loopback address (::1/128). + * @return an IPV6Address representing the loopback address. + */ + static IPV6Address Loopback(); + + private: + // TODO(Peter): Decide how to store the address internally... + in6_addr m_address; +}; +/** + * @} + */ +} // namespace network +} // namespace ola +#endif // INCLUDE_OLA_NETWORK_IPV6ADDRESS_H_ diff --git a/include/ola/network/Makefile.mk b/include/ola/network/Makefile.mk index fce6c1435d..f21c3533b7 100644 --- a/include/ola/network/Makefile.mk +++ b/include/ola/network/Makefile.mk @@ -3,6 +3,7 @@ olanetworkinclude_HEADERS = \ include/ola/network/AdvancedTCPConnector.h\ include/ola/network/HealthCheckedConnection.h \ include/ola/network/IPV4Address.h \ + include/ola/network/IPV6Address.h \ include/ola/network/Interface.h \ include/ola/network/InterfacePicker.h \ include/ola/network/MACAddress.h \ diff --git a/include/ola/network/TCPConnector.h b/include/ola/network/TCPConnector.h index 4b74c71f9a..866392b1a2 100644 --- a/include/ola/network/TCPConnector.h +++ b/include/ola/network/TCPConnector.h @@ -98,7 +98,9 @@ class TCPConnector { /** * @brief Return the number of pending connections */ - unsigned int ConnectionsPending() const { return m_connections.size(); } + unsigned int ConnectionsPending() const { + return static_cast(m_connections.size()); + } /** * @brief Called when the TCP socket connects. diff --git a/include/ola/rdm/MessageDeserializer.h b/include/ola/rdm/MessageDeserializer.h index 4d228125e2..5e320653af 100644 --- a/include/ola/rdm/MessageDeserializer.h +++ b/include/ola/rdm/MessageDeserializer.h @@ -57,6 +57,7 @@ class MessageDeserializer: public ola::messaging::FieldDescriptorVisitor { void Visit(const ola::messaging::BoolFieldDescriptor*); void Visit(const ola::messaging::IPV4FieldDescriptor*); + void Visit(const ola::messaging::IPV6FieldDescriptor*); void Visit(const ola::messaging::MACFieldDescriptor*); void Visit(const ola::messaging::UIDFieldDescriptor*); void Visit(const ola::messaging::StringFieldDescriptor*); diff --git a/include/ola/rdm/MessageSerializer.h b/include/ola/rdm/MessageSerializer.h index 821f2097ea..cf6f51f86e 100644 --- a/include/ola/rdm/MessageSerializer.h +++ b/include/ola/rdm/MessageSerializer.h @@ -48,6 +48,7 @@ class MessageSerializer: public ola::messaging::MessageVisitor { void Visit(const ola::messaging::BoolMessageField*); void Visit(const ola::messaging::IPV4MessageField*); + void Visit(const ola::messaging::IPV6MessageField*); void Visit(const ola::messaging::MACMessageField*); void Visit(const ola::messaging::UIDMessageField*); void Visit(const ola::messaging::StringMessageField*); diff --git a/include/ola/rdm/QueueingRDMController.h b/include/ola/rdm/QueueingRDMController.h index acb6527eca..8311b7122a 100644 --- a/include/ola/rdm/QueueingRDMController.h +++ b/include/ola/rdm/QueueingRDMController.h @@ -14,7 +14,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * QueueingRDMController.h - * A RDM Controller that sends a single message at a time. + * An RDM Controller that sends a single message at a time. * Copyright (C) 2010 Simon Newton */ diff --git a/include/ola/rdm/RDMControllerInterface.h b/include/ola/rdm/RDMControllerInterface.h index 8ddff1e049..564c20ea88 100644 --- a/include/ola/rdm/RDMControllerInterface.h +++ b/include/ola/rdm/RDMControllerInterface.h @@ -79,12 +79,12 @@ class RDMControllerInterface { * @param request the RDMRequest, ownership is transferred. * @param on_complete The callback to run when the request completes. * - * Implementors much ensure that the callback is always run at some point. + * Implementers much ensure that the callback is always run at some point. * In other words, there must be no way that a request can be dropped in such * a way that the callback is never run. Doing so will either block all * subsequent requests, or leak memory depending on the implementation. * - * Also the implementor of this class may want to re-write the transaction #, + * Also the implementer of this class may want to re-write the transaction #, * and possibly the UID (changing src UIDs isn't addressed by the RDM * spec). * diff --git a/include/ola/rdm/RDMEnums.h b/include/ola/rdm/RDMEnums.h index c57e8f7e97..b4c7230c0e 100644 --- a/include/ola/rdm/RDMEnums.h +++ b/include/ola/rdm/RDMEnums.h @@ -159,28 +159,6 @@ typedef enum { PID_PRESET_MERGEMODE = 0x1043, PID_POWER_ON_SELF_TEST = 0x1044, - // Draft E1.33 PIDs - DO NOT USE - PID_ENDPOINT_LIST = 0x7FE0, - PID_ENDPOINT_TO_UNIVERSE = 0x7FE1, - PID_RDM_TRAFFIC_ENABLE = 0x7FE2, - PID_ENDPOINT_MODE = 0x7FE3, - PID_ENDPOINT_LABEL = 0x7FE4, - PID_DISCOVERY_STATE = 0x7FE5, - PID_ENDPOINT_TIMING = 0x7FE6, - PID_ENDPOINT_TIMING_DESCRIPTION = 0x7FE7, - PID_BINDING_CONTROL_FIELDS = 0x7FE8, - PID_ENDPOINT_IDENTIFY = 0x7FE9, - PID_BACKGROUND_DISCOVERY = 0x7FEA, - PID_ENDPOINT_DEVICE_LIST_CHANGE = 0x7FEB, - PID_ENDPOINT_DEVICES = 0x7FEC, - PID_TCP_COMMS_STATUS = 0x7FED, - PID_ENDPOINT_LIST_CHANGE = 0x7FEE, - PID_BACKGROUND_QUEUED_STATUS_POLICY = 0x7FD0, - PID_BACKGROUND_QUEUED_STATUS_POLICY_DESCRIPTION = 0x7FD1, - PID_BACKGROUND_STATUS_TYPE = 0x7FD2, - PID_QUEUED_STATUS_ENDPOINT_COLLECTION = 0x7FD3, - PID_QUEUED_STATUS_UID_COLLECTION = 0x7FD4, - // E1.37-2 PIDs PID_LIST_INTERFACES = 0x0700, PID_INTERFACE_LABEL = 0x0701, @@ -196,6 +174,30 @@ typedef enum { PID_DNS_NAME_SERVER = 0x070B, PID_DNS_HOSTNAME = 0x070C, PID_DNS_DOMAIN_NAME = 0x070D, + + // E1.33 PIDs + PID_COMPONENT_SCOPE = 0x0800, + PID_SEARCH_DOMAIN = 0x0801, + PID_TCP_COMMS_STATUS = 0x0802, + PID_BROKER_STATUS = 0x0803, + + // E1.37-7 PIDs + PID_ENDPOINT_LIST = 0x0900, + PID_ENDPOINT_LIST_CHANGE = 0x0901, + PID_IDENTIFY_ENDPOINT = 0x0902, + PID_ENDPOINT_TO_UNIVERSE = 0x0903, + PID_ENDPOINT_MODE = 0x0904, + PID_ENDPOINT_LABEL = 0x0905, + PID_RDM_TRAFFIC_ENABLE = 0x0906, + PID_DISCOVERY_STATE = 0x0907, + PID_BACKGROUND_DISCOVERY = 0x908, + PID_ENDPOINT_TIMING = 0x0909, + PID_ENDPOINT_TIMING_DESCRIPTION = 0x090A, + PID_ENDPOINT_RESPONDERS = 0x090B, + PID_ENDPOINT_RESPONDER_LIST_CHANGE = 0x090C, + PID_BINDING_CONTROL_FIELDS = 0x090D, + PID_BACKGROUND_QUEUED_STATUS_POLICY = 0x090E, + PID_BACKGROUND_QUEUED_STATUS_POLICY_DESCRIPTION = 0x090F, } rdm_pid; @@ -718,6 +720,9 @@ static const uint8_t MAX_RDM_HOSTNAME_LENGTH = 63; static const uint8_t MAX_RDM_DOMAIN_NAME_LENGTH = 231; static const uint8_t DNS_NAME_SERVER_MAX_INDEX = 2; + +// Excluding the mandatory NULL terminator +static const uint8_t MAX_RDM_SCOPE_STRING_LENGTH = 62; } // namespace rdm } // namespace ola #endif // INCLUDE_OLA_RDM_RDMENUMS_H_ diff --git a/include/ola/rdm/StringMessageBuilder.h b/include/ola/rdm/StringMessageBuilder.h index 4f08afc85e..ed2bfbdc49 100644 --- a/include/ola/rdm/StringMessageBuilder.h +++ b/include/ola/rdm/StringMessageBuilder.h @@ -22,7 +22,7 @@ * @addtogroup rdm_helpers * @{ * @file include/ola/rdm/StringMessageBuilder.h - * @brief Builds a Messagse object from a list of strings and a Descriptor. + * @brief Builds a Message object from a list of strings and a Descriptor. * @} */ @@ -62,6 +62,7 @@ class StringMessageBuilder: public ola::messaging::FieldDescriptorVisitor { void Visit(const ola::messaging::BoolFieldDescriptor*); void Visit(const ola::messaging::IPV4FieldDescriptor*); + void Visit(const ola::messaging::IPV6FieldDescriptor*); void Visit(const ola::messaging::MACFieldDescriptor*); void Visit(const ola::messaging::UIDFieldDescriptor*); void Visit(const ola::messaging::StringFieldDescriptor*); diff --git a/include/ola/rdm/UID.h b/include/ola/rdm/UID.h index dc1ff4c982..fb2defd87b 100644 --- a/include/ola/rdm/UID.h +++ b/include/ola/rdm/UID.h @@ -174,6 +174,16 @@ class UID { */ bool IsBroadcast() const { return m_uid.device_id == ALL_DEVICES; } + /** + * @brief Check if this UID is a vendorcast UID. + * @returns true if the manufacturer id is not 0xffff and the device id is + * 0xffffffff. + */ + bool IsVendorcast() const { + return ((m_uid.esta_id != ALL_MANUFACTURERS) && + (m_uid.device_id == ALL_DEVICES)); + } + /** * @brief Check if this UID matches against another. * @param uid the UID to check against @@ -260,7 +270,7 @@ class UID { /** * @brief Returns a UID that matches all devices for a particular - * manufacturer. + * manufacturer. * @param esta_id the manufacturer id of the devices to match. * @returns a UID(X, 0xffffffff). */ @@ -278,6 +288,44 @@ class UID { return UID(uid.ManufacturerId(), ALL_DEVICES); } + /** + * @brief Returns a UID that matches all RPT controllers (fffc:ffffffff). + * @returns a UID(0xfffc, 0xffffffff). + */ + static UID RPTAllControllers() { + return UID(RPT_ALL_CONTROLLERS_MANUFACTURER, ALL_DEVICES); + } + + /** + * @brief Returns a UID that matches all RPT devices (fffd:ffffffff). + * @returns a UID(0xfffd, 0xffffffff). + */ + static UID RPTAllDevices() { + return UID(RPT_ALL_DEVICES_MANUFACTURER, ALL_DEVICES); + } + + /** + * @brief Returns a UID that matches all RPT devices for a particular + * manufacturer. + * @param esta_id the manufacturer id of the devices to match. + * @returns a UID(0xfffd, 0xXXXXffff). + */ + static UID RPTVendorcastAddressDevices(uint16_t esta_id) { + return UID(RPT_ALL_DEVICES_MANUFACTURER, + RPTVendorcastDevicesDeviceId(esta_id)); + } + + /** + * @brief Returns a UID that matches all RPT devices for a particular + * manufacturer. + * @param uid a UID whose manufacturer id you want to match. + * @returns a UID(0xfffd, 0xXXXXffff). + */ + static UID RPTVendorcastAddressDevices(UID uid) { + return UID(RPT_ALL_DEVICES_MANUFACTURER, + RPTVendorcastDevicesDeviceId(uid.ManufacturerId())); + } + /** * @brief Return a new UID from a string. * @param uid the UID as a string i.e. XXXX:YYYYYYYY. @@ -300,9 +348,21 @@ class UID { /** * @brief The value for the 'all devices' id. + * + * This is also the value for RPT all controllers and all devices. */ static const uint32_t ALL_DEVICES = 0xffffffff; + /** + * @brief The value for the RPT 'all controllers' manufacturer. + */ + static const uint16_t RPT_ALL_CONTROLLERS_MANUFACTURER = 0xfffc; + + /** + * @brief The value for the RPT 'all devices' manufacturer. + */ + static const uint16_t RPT_ALL_DEVICES_MANUFACTURER = 0xfffd; + private: struct rdm_uid { uint16_t esta_id; @@ -324,6 +384,10 @@ class UID { } return a < b ? -1 : 1; } + + static uint32_t RPTVendorcastDevicesDeviceId(uint16_t esta_id) { + return ((static_cast(esta_id) << 16) | 0xffff); + } }; } // namespace rdm } // namespace ola diff --git a/include/ola/web/JsonTypes.h b/include/ola/web/JsonTypes.h index 2477fbecdd..b05fd0e1f9 100644 --- a/include/ola/web/JsonTypes.h +++ b/include/ola/web/JsonTypes.h @@ -23,7 +23,7 @@ * @addtogroup json * @{ * @file JsonTypes.h - * @brief Enums used to identfy JSON types. + * @brief Enums used to identify JSON types. * @} */ diff --git a/include/olad/PluginAdaptor.h b/include/olad/PluginAdaptor.h index 222a56d157..59a33bbc7b 100644 --- a/include/olad/PluginAdaptor.h +++ b/include/olad/PluginAdaptor.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -48,13 +49,15 @@ class PluginAdaptor: public ola::io::SelectServerInterface { * @param preferences_factory pointer to the PreferencesFactory object * @param port_broker pointer to the PortBroker object * @param instance_name the instance name of this OlaServer + * @param uid the default ola::rdm::UID of this OlaServer */ PluginAdaptor(class DeviceManager *device_manager, ola::io::SelectServerInterface *select_server, ExportMap *export_map, class PreferencesFactory *preferences_factory, class PortBrokerInterface *port_broker, - const std::string *instance_name); + const std::string *instance_name, + const ola::rdm::UID *default_uid); // The following methods are part of the SelectServerInterface bool AddReadDescriptor(ola::io::ReadFileDescriptor *descriptor); @@ -98,6 +101,12 @@ class PluginAdaptor: public ola::io::SelectServerInterface { */ const std::string InstanceName() const; + /** + * @brief Return the default UID for the OLA server + * @return an ola::rdm::UID which is the UID for the server + */ + const ola::rdm::UID DefaultUID() const; + ExportMap *GetExportMap() const { return m_export_map; } @@ -134,6 +143,7 @@ class PluginAdaptor: public ola::io::SelectServerInterface { class PreferencesFactory *m_preferences_factory; class PortBrokerInterface *m_port_broker; const std::string *m_instance_name; + const ola::rdm::UID *m_default_uid; DISALLOW_COPY_AND_ASSIGN(PluginAdaptor); }; diff --git a/include/olad/Preferences.h b/include/olad/Preferences.h index 9c7ff4fa6b..65c921dca2 100644 --- a/include/olad/Preferences.h +++ b/include/olad/Preferences.h @@ -141,6 +141,22 @@ class IPv4Validator: public Validator { }; +/* + * Check an IPv6 address is valid + */ +class IPv6Validator: public Validator { + public: + explicit IPv6Validator(bool empty_ok = true): + m_empty_ok(empty_ok) {} + + bool IsValid(const std::string &value) const; + private: + bool m_empty_ok; + + DISALLOW_COPY_AND_ASSIGN(IPv6Validator); +}; + + /* * The abstract Preferences class */ diff --git a/libs/acn/BrokerClientAddInflator.h b/libs/acn/BrokerClientAddInflator.h new file mode 100644 index 0000000000..14af53fe16 --- /dev/null +++ b/libs/acn/BrokerClientAddInflator.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. + * + * BrokerClientAddInflator.h + * Interface for the BrokerClientAddInflator class. + * Copyright (C) 2023 Peter Newman + */ + +#ifndef LIBS_ACN_BROKERCLIENTADDINFLATOR_H_ +#define LIBS_ACN_BROKERCLIENTADDINFLATOR_H_ + +#include "ola/acn/ACNVectors.h" +#include "libs/acn/BaseInflator.h" + +namespace ola { +namespace acn { + +class BrokerClientAddInflator: public BaseInflator { + friend class BrokerClientAddInflatorTest; + + public: + BrokerClientAddInflator() + : BaseInflator() { + } + ~BrokerClientAddInflator() {} + + uint32_t Id() const { return ola::acn::VECTOR_BROKER_CLIENT_ADD; } + + 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_BROKERCLIENTADDINFLATOR_H_ diff --git a/libs/acn/BrokerClientEntryChangeInflator.h b/libs/acn/BrokerClientEntryChangeInflator.h new file mode 100644 index 0000000000..9827a60618 --- /dev/null +++ b/libs/acn/BrokerClientEntryChangeInflator.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. + * + * BrokerClientEntryChangeInflator.h + * Interface for the BrokerClientEntryChangeInflator class. + * Copyright (C) 2023 Peter Newman + */ + +#ifndef LIBS_ACN_BROKERCLIENTENTRYCHANGEINFLATOR_H_ +#define LIBS_ACN_BROKERCLIENTENTRYCHANGEINFLATOR_H_ + +#include "ola/acn/ACNVectors.h" +#include "libs/acn/BaseInflator.h" + +namespace ola { +namespace acn { + +class BrokerClientEntryChangeInflator: public BaseInflator { + friend class BrokerClientEntryChangeInflatorTest; + + public: + BrokerClientEntryChangeInflator() + : BaseInflator() { + } + ~BrokerClientEntryChangeInflator() {} + + uint32_t Id() const { return ola::acn::VECTOR_BROKER_CLIENT_ENTRY_CHANGE; } + + 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_BROKERCLIENTENTRYCHANGEINFLATOR_H_ diff --git a/libs/acn/BrokerClientEntryHeader.h b/libs/acn/BrokerClientEntryHeader.h new file mode 100644 index 0000000000..1a0bf86e92 --- /dev/null +++ b/libs/acn/BrokerClientEntryHeader.h @@ -0,0 +1,64 @@ +/* + * 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. + * + * BrokerClientEntryHeader.h + * The E1.33 Broker Client Entry Header + * Copyright (C) 2023 Peter Newman + */ + +#ifndef LIBS_ACN_BROKERCLIENTENTRYHEADER_H_ +#define LIBS_ACN_BROKERCLIENTENTRYHEADER_H_ + +#include +#include + +#include + +namespace ola { +namespace acn { + +// TODO(Peter): I think technically this probably shouldn't be a header and +// instead is just data at this level! +/* + * Header for the Broker Client Entry level + */ +class BrokerClientEntryHeader { + public: + BrokerClientEntryHeader() {} + + explicit BrokerClientEntryHeader(const ola::acn::CID &client_cid) + : m_client_cid(client_cid) { + } + ~BrokerClientEntryHeader() {} + + const ola::acn::CID ClientCid() const { return m_client_cid; } + + bool operator==(const BrokerClientEntryHeader &other) const { + return m_client_cid == other.m_client_cid; + } + + PACK( + struct broker_client_entry_pdu_header_s { + uint8_t client_cid[CID::CID_LENGTH]; + }); + typedef struct broker_client_entry_pdu_header_s + broker_client_entry_pdu_header; + + private: + ola::acn::CID m_client_cid; +}; +} // namespace acn +} // namespace ola +#endif // LIBS_ACN_BROKERCLIENTENTRYHEADER_H_ diff --git a/libs/acn/BrokerClientEntryPDU.cpp b/libs/acn/BrokerClientEntryPDU.cpp new file mode 100644 index 0000000000..dc850e3316 --- /dev/null +++ b/libs/acn/BrokerClientEntryPDU.cpp @@ -0,0 +1,118 @@ +/* + * 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. + * + * BrokerClientEntryPDU.cpp + * The BrokerClientEntryPDU + * Copyright (C) 2023 Peter Newman + */ + + +#include "ola/Logging.h" +#include "ola/base/Array.h" +#include "ola/network/NetworkUtils.h" +#include "libs/acn/BrokerClientEntryPDU.h" + +namespace ola { +namespace acn { + +using ola::io::OutputStream; +using ola::network::HostToNetwork; + +/* + * Size of the header portion. + */ +unsigned int BrokerClientEntryPDU::HeaderSize() const { + return sizeof(BrokerClientEntryHeader::broker_client_entry_pdu_header); +} + + +/* + * Size of the data portion + */ +unsigned int BrokerClientEntryPDU::DataSize() const { + return m_pdu ? m_pdu->Size() : 0; +} + + +/* + * Pack the header portion. + */ +bool BrokerClientEntryPDU::PackHeader(uint8_t *data, + unsigned int *length) const { + unsigned int header_size = HeaderSize(); + + if (*length < header_size) { + OLA_WARN << "BrokerClientEntryPDU::PackHeader: buffer too small, got " + << *length << " required " << header_size; + *length = 0; + return false; + } + + BrokerClientEntryHeader::broker_client_entry_pdu_header header; + m_header.ClientCid().Pack(header.client_cid); + *length = sizeof(BrokerClientEntryHeader::broker_client_entry_pdu_header); + memcpy(data, &header, *length); + return true; +} + + +/* + * Pack the data portion. + */ +bool BrokerClientEntryPDU::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 BrokerClientEntryPDU::PackHeader(OutputStream *stream) const { + BrokerClientEntryHeader::broker_client_entry_pdu_header header; + m_header.ClientCid().Pack(header.client_cid); + stream->Write( + reinterpret_cast(&header), + sizeof(BrokerClientEntryHeader::broker_client_entry_pdu_header)); +} + + +/* + * Pack the data into a buffer + */ +void BrokerClientEntryPDU::PackData(OutputStream *stream) const { + if (m_pdu) + m_pdu->Write(stream); +} + + +void BrokerClientEntryPDU::PrependPDU(ola::io::IOStack *stack, + uint32_t vector, + const ola::acn::CID &client_cid) { + BrokerClientEntryHeader::broker_client_entry_pdu_header header; + client_cid.Pack(header.client_cid); + stack->Write( + reinterpret_cast(&header), + sizeof(BrokerClientEntryHeader::broker_client_entry_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/BrokerClientEntryPDU.h b/libs/acn/BrokerClientEntryPDU.h new file mode 100644 index 0000000000..cd6c83b4eb --- /dev/null +++ b/libs/acn/BrokerClientEntryPDU.h @@ -0,0 +1,61 @@ +/* + * 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. + * + * BrokerClientEntryPDU.h + * Interface for the BrokerClientEntryPDU class + * Copyright (C) 2023 Peter Newman + */ + +#ifndef LIBS_ACN_BROKERCLIENTENTRYPDU_H_ +#define LIBS_ACN_BROKERCLIENTENTRYPDU_H_ + +#include +#include + +#include "libs/acn/PDU.h" +#include "libs/acn/BrokerClientEntryHeader.h" + +namespace ola { +namespace acn { + +class BrokerClientEntryPDU: public PDU { + public: + BrokerClientEntryPDU(unsigned int vector, + const BrokerClientEntryHeader &header, + const PDU *pdu): + PDU(vector, FOUR_BYTES, true), + m_header(header), + m_pdu(pdu) {} + ~BrokerClientEntryPDU() {} + + 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::acn::CID &client_cid); + + private: + BrokerClientEntryHeader m_header; + const PDU *m_pdu; +}; +} // namespace acn +} // namespace ola +#endif // LIBS_ACN_BROKERCLIENTENTRYPDU_H_ diff --git a/libs/acn/BrokerClientEntryPDUTest.cpp b/libs/acn/BrokerClientEntryPDUTest.cpp new file mode 100644 index 0000000000..3c8a1d41ab --- /dev/null +++ b/libs/acn/BrokerClientEntryPDUTest.cpp @@ -0,0 +1,168 @@ +/* + * 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. + * + * BrokerClientEntryPDUTest.cpp + * Test fixture for the BrokerClientEntryPDU 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/BrokerClientEntryPDU.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; + +class BrokerClientEntryPDUTest: public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(BrokerClientEntryPDUTest); + CPPUNIT_TEST(testSimpleBrokerClientEntryPDU); + CPPUNIT_TEST(testSimpleBrokerClientEntryPDUToOutputStream); + CPPUNIT_TEST(testPrepend); + CPPUNIT_TEST_SUITE_END(); + + public: + void testSimpleBrokerClientEntryPDU(); + void testSimpleBrokerClientEntryPDUToOutputStream(); + void testPrepend(); + + void setUp() { + ola::InitLogging(ola::OLA_LOG_DEBUG, ola::OLA_LOG_STDERR); + } + + private: + static const unsigned int TEST_VECTOR; + static const uint8_t TEST_DATA[]; +}; + +const uint8_t BrokerClientEntryPDUTest::TEST_DATA[] = {0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15}; + +CPPUNIT_TEST_SUITE_REGISTRATION(BrokerClientEntryPDUTest); + +const unsigned int BrokerClientEntryPDUTest::TEST_VECTOR = 39; + + +/* + * Test that packing a BrokerClientEntryPDU without data works. + */ +void BrokerClientEntryPDUTest::testSimpleBrokerClientEntryPDU() { + const CID client_cid = CID::FromData(TEST_DATA); + BrokerClientEntryHeader header(client_cid); + BrokerClientEntryPDU pdu(TEST_VECTOR, header, NULL); + + OLA_ASSERT_EQ(16u, pdu.HeaderSize()); + OLA_ASSERT_EQ(0u, pdu.DataSize()); + OLA_ASSERT_EQ(23u, 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[CID::CID_LENGTH]; + client_cid.Pack(buffer); + OLA_ASSERT_DATA_EQUALS(&data[7], CID::CID_LENGTH, buffer, sizeof(buffer)); + + // 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 BrokerClientEntryPDUTest::testSimpleBrokerClientEntryPDUToOutputStream() { + const ola::acn::CID client_cid = CID::FromData(TEST_DATA); + BrokerClientEntryHeader header(client_cid); + BrokerClientEntryPDU pdu(TEST_VECTOR, header, NULL); + + OLA_ASSERT_EQ(16u, pdu.HeaderSize()); + OLA_ASSERT_EQ(0u, pdu.DataSize()); + OLA_ASSERT_EQ(23u, pdu.Size()); + + IOQueue output; + OutputStream stream(&output); + pdu.Write(&stream); + OLA_ASSERT_EQ(23u, 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, 0x17, + 0, 0, 0, 39, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15 + }; + OLA_ASSERT_DATA_EQUALS(EXPECTED, sizeof(EXPECTED), pdu_data, pdu_size); + output.Pop(output.Size()); + delete[] pdu_data; +} + + +void BrokerClientEntryPDUTest::testPrepend() { + const ola::acn::CID client_cid = CID::FromData(TEST_DATA); + IOStack stack; + BrokerClientEntryPDU::PrependPDU(&stack, + TEST_VECTOR, + client_cid); + + 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, 0x17, + 0, 0, 0, 39, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15 + }; + OLA_ASSERT_DATA_EQUALS(expected_data, sizeof(expected_data), buffer, length); + delete[] buffer; +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/BrokerClientEntryRPTInflator.cpp b/libs/acn/BrokerClientEntryRPTInflator.cpp new file mode 100644 index 0000000000..6b1b2258ba --- /dev/null +++ b/libs/acn/BrokerClientEntryRPTInflator.cpp @@ -0,0 +1,89 @@ +/* + * 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. + * + * BrokerClientEntryRPTInflator.cpp + * The Inflator for BrokerClientEntryRPT PDU + * Copyright (C) 2023 Peter Newman + */ + +#include +#include + +#include "ola/Logging.h" +#include "ola/acn/CID.h" +#include "ola/e133/E133Helper.h" +#include "ola/network/NetworkUtils.h" +#include "ola/rdm/UID.h" +#include "include/ola/strings/Format.h" +#include "libs/acn/BrokerClientEntryRPTInflator.h" + +namespace ola { +namespace acn { + +using ola::acn::CID; +using ola::network::NetworkToHost; +using ola::rdm::UID; +using ola::strings::IntToString; + +/** + * Set a BrokerClientEntryRPTHandler to run when receiving a Broker Client + * Entry RPT message. + * @param handler the callback to invoke when there is a Broker Client Entry + * RPT. + */ +void BrokerClientEntryRPTInflator::SetBrokerClientEntryRPTHandler( + BrokerClientEntryRPTHandler *handler) { + m_broker_client_entry_rpt_handler.reset(handler); +} + + +unsigned int BrokerClientEntryRPTInflator::InflatePDUBlock( + OLA_UNUSED HeaderSet *headers, + const uint8_t *data, + unsigned int len) { + broker_client_entry_rpt_pdu_data pdu_data; + if (len > sizeof(pdu_data)) { + OLA_WARN << "Got too much data, received " << len << " only expecting " + << sizeof(pdu_data); + return 0; + } + + memcpy(reinterpret_cast(&pdu_data), data, sizeof(pdu_data)); + + OLA_DEBUG << "Client Entry RPT from " << CID::FromData(pdu_data.client_cid) + << " (" << UID(pdu_data.client_uid) << ") of RPT Client Type " + << IntToString(pdu_data.rpt_client_type); + + ola::e133::E133RPTClientTypeCode client_type; + if (!ola::e133::IntToRPTClientType(pdu_data.rpt_client_type, &client_type)) { + OLA_WARN << "Unknown E1.33 RPT Client Type code " + << IntToString(pdu_data.rpt_client_type); + } + + BrokerClientEntryRPT client_entry(CID::FromData(pdu_data.client_cid), + UID(pdu_data.client_uid), + client_type, + CID::FromData(pdu_data.binding_cid)); + + if (m_broker_client_entry_rpt_handler.get()) { + m_broker_client_entry_rpt_handler->Run(headers, + client_entry); + } else { + OLA_WARN << "No Broker Client Entry RPT handler defined!"; + } + return sizeof(pdu_data); +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/BrokerClientEntryRPTInflator.h b/libs/acn/BrokerClientEntryRPTInflator.h new file mode 100644 index 0000000000..c7db73c4fa --- /dev/null +++ b/libs/acn/BrokerClientEntryRPTInflator.h @@ -0,0 +1,100 @@ +/* + * 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. + * + * BrokerClientEntryRPTInflator.h + * Interface for the BrokerClientEntryRPTInflator class. + * Copyright (C) 2023 Peter Newman + */ + +#ifndef LIBS_ACN_BROKERCLIENTENTRYRPTINFLATOR_H_ +#define LIBS_ACN_BROKERCLIENTENTRYRPTINFLATOR_H_ + +#include "ola/Callback.h" +#include "ola/acn/ACNVectors.h" +#include "ola/e133/E133Enums.h" +#include "ola/rdm/UID.h" +#include "libs/acn/BaseInflator.h" + +namespace ola { +namespace acn { + +class BrokerClientEntryRPTInflator: public BaseInflator { + friend class BrokerClientEntryRPTInflatorTest; + + public: + struct BrokerClientEntryRPT { + BrokerClientEntryRPT(const ola::acn::CID &_client_cid, + const ola::rdm::UID &_client_uid, + ola::e133::E133RPTClientTypeCode _client_type_code, + const ola::acn::CID &_binding_cid) + : client_cid(_client_cid), + client_uid(_client_uid), + client_type_code(_client_type_code), + binding_cid(_binding_cid) { + } + ola::acn::CID client_cid; + ola::rdm::UID client_uid; + ola::e133::E133RPTClientTypeCode client_type_code; + ola::acn::CID binding_cid; + }; + + + // These are pointers so the callers don't have to pull in all the headers. + typedef ola::Callback2 BrokerClientEntryRPTHandler; + + BrokerClientEntryRPTInflator() + : BaseInflator() { + } + ~BrokerClientEntryRPTInflator() {} + + uint32_t Id() const { return ola::acn::CLIENT_PROTOCOL_RPT; } + + PACK( + struct broker_client_entry_rpt_pdu_data_s { + uint8_t client_cid[ola::acn::CID::CID_LENGTH]; + uint8_t client_uid[ola::rdm::UID::LENGTH]; + uint8_t rpt_client_type; + uint8_t binding_cid[ola::acn::CID::CID_LENGTH]; + }); + typedef struct broker_client_entry_rpt_pdu_data_s + broker_client_entry_rpt_pdu_data; + + void SetBrokerClientEntryRPTHandler(BrokerClientEntryRPTHandler *handler); + + 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 + + unsigned int InflatePDUBlock(HeaderSet *headers, + const uint8_t *data, + unsigned int len); + + private: + std::auto_ptr m_broker_client_entry_rpt_handler; +}; +} // namespace acn +} // namespace ola +#endif // LIBS_ACN_BROKERCLIENTENTRYRPTINFLATOR_H_ diff --git a/libs/acn/BrokerClientEntryRPTPDU.cpp b/libs/acn/BrokerClientEntryRPTPDU.cpp new file mode 100644 index 0000000000..0937f5df8e --- /dev/null +++ b/libs/acn/BrokerClientEntryRPTPDU.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. + * + * BrokerClientEntryRPTPDU.cpp + * The BrokerClientEntryRPTPDU + * Copyright (C) 2023 Peter Newman + */ + + +#include "ola/Logging.h" +#include "ola/acn/CID.h" +#include "ola/base/Array.h" +#include "ola/network/NetworkUtils.h" +#include "ola/rdm/UID.h" +#include "libs/acn/BrokerClientEntryRPTPDU.h" +#include "libs/acn/BrokerClientEntryPDU.h" + +namespace ola { +namespace acn { + +using ola::acn::CID; +using ola::io::OutputStream; +using ola::network::HostToNetwork; +using ola::rdm::UID; + +/* + * Size of the header portion. + */ +unsigned int BrokerClientEntryRPTPDU::HeaderSize() const { + return sizeof(BrokerClientEntryHeader::broker_client_entry_pdu_header); +} + + +/* + * Size of the data portion + */ +unsigned int BrokerClientEntryRPTPDU::DataSize() const { + return sizeof(broker_client_entry_rpt_pdu_data); +} + + +/* + * Pack the header portion. + */ +bool BrokerClientEntryRPTPDU::PackHeader(uint8_t *data, + unsigned int *length) const { + unsigned int header_size = HeaderSize(); + + if (*length < header_size) { + OLA_WARN << "BrokerClientEntryRPTPDU::PackHeader: buffer too small, got " + << *length << " required " << header_size; + *length = 0; + return false; + } + + BrokerClientEntryHeader::broker_client_entry_pdu_header header; + m_header.ClientCid().Pack(header.client_cid); + *length = sizeof(BrokerClientEntryHeader::broker_client_entry_pdu_header); + memcpy(data, &header, *length); + return true; +} + + +/* + * Pack the data portion. + */ +bool BrokerClientEntryRPTPDU::PackData(uint8_t *data, + unsigned int *length) const { + unsigned int data_size = DataSize(); + + if (*length < data_size) { + OLA_WARN << "BrokerClientEntryRPTPDU::PackData: buffer too small, got " + << *length << " required " << data_size; + *length = 0; + return false; + } + + broker_client_entry_rpt_pdu_data pdu_data; + + m_client_uid.Pack(pdu_data.client_uid, sizeof(pdu_data.client_uid)); + pdu_data.rpt_client_type = m_rpt_client_type; + m_binding_cid.Pack(pdu_data.binding_cid); + + *length = sizeof(broker_client_entry_rpt_pdu_data); + memcpy(data, &pdu_data, *length); + return true; +} + + +/* + * Pack the header into a buffer. + */ +void BrokerClientEntryRPTPDU::PackHeader(OutputStream *stream) const { + BrokerClientEntryHeader::broker_client_entry_pdu_header header; + m_header.ClientCid().Pack(header.client_cid); + stream->Write( + reinterpret_cast(&header), + sizeof(BrokerClientEntryHeader::broker_client_entry_pdu_header)); +} + + +/* + * Pack the data into a buffer + */ +void BrokerClientEntryRPTPDU::PackData(OutputStream *stream) const { + broker_client_entry_rpt_pdu_data pdu_data; + + m_client_uid.Pack(pdu_data.client_uid, sizeof(pdu_data.client_uid)); + pdu_data.rpt_client_type = m_rpt_client_type; + m_binding_cid.Pack(pdu_data.binding_cid); + + stream->Write( + reinterpret_cast(&pdu_data), + static_cast(sizeof(broker_client_entry_rpt_pdu_data))); +} + + +void BrokerClientEntryRPTPDU::PrependPDU(ola::io::IOStack *stack, + uint32_t vector, + const ola::acn::CID &client_cid, + const ola::rdm::UID &client_uid, + uint8_t rpt_client_type, + const ola::acn::CID &binding_cid) { + broker_client_entry_rpt_pdu_data pdu_data; + + client_uid.Pack(pdu_data.client_uid, sizeof(pdu_data.client_uid)); + pdu_data.rpt_client_type = rpt_client_type; + binding_cid.Pack(pdu_data.binding_cid); + + stack->Write( + reinterpret_cast(&pdu_data), + static_cast(sizeof(broker_client_entry_rpt_pdu_data))); + + BrokerClientEntryPDU::PrependPDU(stack, vector, client_cid); +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/BrokerClientEntryRPTPDU.h b/libs/acn/BrokerClientEntryRPTPDU.h new file mode 100644 index 0000000000..277bab5146 --- /dev/null +++ b/libs/acn/BrokerClientEntryRPTPDU.h @@ -0,0 +1,80 @@ +/* + * 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. + * + * BrokerClientEntryRPTPDU.h + * Interface for the BrokerClientEntryRPTPDU class + * Copyright (C) 2023 Peter Newman + */ + +#ifndef LIBS_ACN_BROKERCLIENTENTRYRPTPDU_H_ +#define LIBS_ACN_BROKERCLIENTENTRYRPTPDU_H_ + +#include +#include +#include + +#include "libs/acn/PDU.h" +#include "libs/acn/BrokerClientEntryHeader.h" + +namespace ola { +namespace acn { + +class BrokerClientEntryRPTPDU: public PDU { + public: + BrokerClientEntryRPTPDU(unsigned int vector, + const BrokerClientEntryHeader &header, + const ola::rdm::UID &client_uid, + uint8_t rpt_client_type, + const ola::acn::CID &binding_cid): + PDU(vector, FOUR_BYTES, true), + m_header(header), + m_client_uid(client_uid), + m_rpt_client_type(rpt_client_type), + m_binding_cid(binding_cid) {} + ~BrokerClientEntryRPTPDU() {} + + 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::acn::CID &client_cid, + const ola::rdm::UID &client_uid, + uint8_t rpt_client_type, + const ola::acn::CID &binding_cid); + + PACK( + struct broker_client_entry_rpt_pdu_data_s { + uint8_t client_uid[ola::rdm::UID::LENGTH]; + uint8_t rpt_client_type; + uint8_t binding_cid[ola::acn::CID::CID_LENGTH]; + }); + typedef struct broker_client_entry_rpt_pdu_data_s + broker_client_entry_rpt_pdu_data; + + private: + BrokerClientEntryHeader m_header; + ola::rdm::UID m_client_uid; + uint8_t m_rpt_client_type; + ola::acn::CID m_binding_cid; +}; +} // namespace acn +} // namespace ola +#endif // LIBS_ACN_BROKERCLIENTENTRYRPTPDU_H_ diff --git a/libs/acn/BrokerClientEntryRPTPDUTest.cpp b/libs/acn/BrokerClientEntryRPTPDUTest.cpp new file mode 100644 index 0000000000..6390eb09ca --- /dev/null +++ b/libs/acn/BrokerClientEntryRPTPDUTest.cpp @@ -0,0 +1,192 @@ +/* + * 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. + * + * BrokerClientEntryRPTPDUTest.cpp + * Test fixture for the BrokerClientEntryRPTPDU 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/BrokerClientEntryRPTPDU.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 BrokerClientEntryRPTPDUTest: public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(BrokerClientEntryRPTPDUTest); + CPPUNIT_TEST(testSimpleBrokerClientEntryRPTPDU); + CPPUNIT_TEST(testSimpleBrokerClientEntryRPTPDUToOutputStream); + CPPUNIT_TEST(testPrepend); + CPPUNIT_TEST_SUITE_END(); + + public: + void testSimpleBrokerClientEntryRPTPDU(); + void testSimpleBrokerClientEntryRPTPDUToOutputStream(); + void testPrepend(); + + void setUp() { + ola::InitLogging(ola::OLA_LOG_DEBUG, ola::OLA_LOG_STDERR); + } + + private: + static const unsigned int TEST_VECTOR; + static const uint8_t TEST_DATA_1[]; + static const uint8_t TEST_DATA_2[]; +}; + +const uint8_t BrokerClientEntryRPTPDUTest::TEST_DATA_1[] = {0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15}; + +const uint8_t BrokerClientEntryRPTPDUTest::TEST_DATA_2[] = {16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, + 26, 27, 28, 29, + 30, 31}; + +CPPUNIT_TEST_SUITE_REGISTRATION(BrokerClientEntryRPTPDUTest); + +const unsigned int BrokerClientEntryRPTPDUTest::TEST_VECTOR = 39; + + +/* + * Test that packing a BrokerClientEntryRPTPDU without data works. + */ +void BrokerClientEntryRPTPDUTest::testSimpleBrokerClientEntryRPTPDU() { + const CID client_cid = CID::FromData(TEST_DATA_1); + const UID client_uid = UID(0x4321, 0x12345678); + const CID bounding_cid = CID::FromData(TEST_DATA_2); + BrokerClientEntryHeader header(client_cid); + BrokerClientEntryRPTPDU pdu(TEST_VECTOR, header, client_uid, 3, bounding_cid); + + OLA_ASSERT_EQ(16u, pdu.HeaderSize()); + OLA_ASSERT_EQ(23u, pdu.DataSize()); + OLA_ASSERT_EQ(46u, 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[CID::CID_LENGTH]; + client_cid.Pack(buffer); + OLA_ASSERT_DATA_EQUALS(&data[7], CID::CID_LENGTH, buffer, sizeof(buffer)); + + // 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 BrokerClientEntryRPTPDUTest:: + testSimpleBrokerClientEntryRPTPDUToOutputStream() { + const ola::acn::CID client_cid = CID::FromData(TEST_DATA_1); + const UID client_uid = UID(0x4321, 0x12345678); + const CID bounding_cid = CID::FromData(TEST_DATA_2); + BrokerClientEntryHeader header(client_cid); + BrokerClientEntryRPTPDU pdu(TEST_VECTOR, header, client_uid, 3, bounding_cid); + + OLA_ASSERT_EQ(16u, pdu.HeaderSize()); + OLA_ASSERT_EQ(23u, pdu.DataSize()); + OLA_ASSERT_EQ(46u, pdu.Size()); + + IOQueue output; + OutputStream stream(&output); + pdu.Write(&stream); + OLA_ASSERT_EQ(46u, 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, 0x2e, + 0, 0, 0, 39, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 0x43, 0x21, 0x12, 0x34, 0x56, 0x78, + 3, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31 + }; + OLA_ASSERT_DATA_EQUALS(EXPECTED, sizeof(EXPECTED), pdu_data, pdu_size); + output.Pop(output.Size()); + delete[] pdu_data; +} + +void BrokerClientEntryRPTPDUTest::testPrepend() { + const CID client_cid = CID::FromData(TEST_DATA_1); + const UID client_uid = UID(0x4321, 0x12345678); + const CID bounding_cid = CID::FromData(TEST_DATA_2); + IOStack stack; + BrokerClientEntryRPTPDU::PrependPDU(&stack, + TEST_VECTOR, + client_cid, + client_uid, + 3, + bounding_cid); + + 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, 0x2e, + 0, 0, 0, 39, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 0x43, 0x21, 0x12, 0x34, 0x56, 0x78, + 3, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31 + }; + OLA_ASSERT_DATA_EQUALS(expected_data, sizeof(expected_data), buffer, length); + delete[] buffer; +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/BrokerClientEntryUpdateInflator.h b/libs/acn/BrokerClientEntryUpdateInflator.h new file mode 100644 index 0000000000..497fedd3be --- /dev/null +++ b/libs/acn/BrokerClientEntryUpdateInflator.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. + * + * BrokerClientEntryUpdateInflator.h + * Interface for the BrokerClientEntryUpdateInflator class. + * Copyright (C) 2023 Peter Newman + */ + +#ifndef LIBS_ACN_BROKERCLIENTENTRYUPDATEINFLATOR_H_ +#define LIBS_ACN_BROKERCLIENTENTRYUPDATEINFLATOR_H_ + +#include "ola/acn/ACNVectors.h" +#include "libs/acn/BaseInflator.h" + +namespace ola { +namespace acn { + +class BrokerClientEntryUpdateInflator: public BaseInflator { + friend class BrokerClientEntryUpdateInflatorTest; + + public: + BrokerClientEntryUpdateInflator() + : BaseInflator() { + } + ~BrokerClientEntryUpdateInflator() {} + + uint32_t Id() const { return ola::acn::VECTOR_BROKER_CLIENT_ENTRY_UPDATE; } + + 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_BROKERCLIENTENTRYUPDATEINFLATOR_H_ diff --git a/libs/acn/BrokerClientRemoveInflator.h b/libs/acn/BrokerClientRemoveInflator.h new file mode 100644 index 0000000000..4117139350 --- /dev/null +++ b/libs/acn/BrokerClientRemoveInflator.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. + * + * BrokerClientRemoveInflator.h + * Interface for the BrokerClientRemoveInflator class. + * Copyright (C) 2023 Peter Newman + */ + +#ifndef LIBS_ACN_BROKERCLIENTREMOVEINFLATOR_H_ +#define LIBS_ACN_BROKERCLIENTREMOVEINFLATOR_H_ + +#include "ola/acn/ACNVectors.h" +#include "libs/acn/BaseInflator.h" + +namespace ola { +namespace acn { + +class BrokerClientRemoveInflator: public BaseInflator { + friend class BrokerClientRemoveInflatorTest; + + public: + BrokerClientRemoveInflator() + : BaseInflator() { + } + ~BrokerClientRemoveInflator() {} + + uint32_t Id() const { return ola::acn::VECTOR_BROKER_CLIENT_REMOVE; } + + 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_BROKERCLIENTREMOVEINFLATOR_H_ diff --git a/libs/acn/BrokerConnectPDU.cpp b/libs/acn/BrokerConnectPDU.cpp new file mode 100644 index 0000000000..a93811c1cd --- /dev/null +++ b/libs/acn/BrokerConnectPDU.cpp @@ -0,0 +1,133 @@ +/* + * 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. + * + * BrokerConnectPDU.cpp + * The BrokerConnectPDU + * Copyright (C) 2023 Peter Newman + */ + +#include "libs/acn/BrokerConnectPDU.h" + +#include +#include + +#include "ola/acn/ACNVectors.h" +#include "ola/network/NetworkUtils.h" + +namespace ola { +namespace acn { + +using ola::io::OutputStream; +using ola::network::HostToNetwork; +using std::min; +using std::string; + +unsigned int BrokerConnectPDU::DataSize() const { + return static_cast(sizeof(broker_connect_pdu_data)); +} + +bool BrokerConnectPDU::PackData(uint8_t *data, unsigned int *length) const { + broker_connect_pdu_data pdu_data; + + size_t client_scope_str_len = min(m_client_scope.size(), + sizeof(pdu_data.client_scope)); + strncpy(pdu_data.client_scope, m_client_scope.c_str(), client_scope_str_len); + memset(pdu_data.client_scope + client_scope_str_len, 0, + (sizeof(pdu_data.client_scope) - client_scope_str_len)); + + pdu_data.e133_version = HostToNetwork(m_e133_version); + + size_t search_domain_str_len = min(m_search_domain.size(), + sizeof(pdu_data.search_domain)); + strncpy(pdu_data.search_domain, m_search_domain.c_str(), + search_domain_str_len); + memset(pdu_data.search_domain + search_domain_str_len, 0, + (sizeof(pdu_data.search_domain) - search_domain_str_len)); + + uint8_t connection = 0; + if (m_incremental_updates) { + connection |= CONNECTION_INCREMENTAL_UPDATES; + } + pdu_data.connection = HostToNetwork(connection); + *length = static_cast(sizeof(broker_connect_pdu_data)); + + memcpy(data, &pdu_data, *length); + return true; +} + +void BrokerConnectPDU::PackData(ola::io::OutputStream *stream) const { + broker_connect_pdu_data pdu_data; + + size_t client_scope_str_len = min(m_client_scope.size(), + sizeof(pdu_data.client_scope)); + strncpy(pdu_data.client_scope, m_client_scope.c_str(), client_scope_str_len); + memset(pdu_data.client_scope + client_scope_str_len, 0, + (sizeof(pdu_data.client_scope) - client_scope_str_len)); + + pdu_data.e133_version = HostToNetwork(m_e133_version); + + size_t search_domain_str_len = min(m_search_domain.size(), + sizeof(pdu_data.search_domain)); + strncpy(pdu_data.search_domain, m_search_domain.c_str(), + search_domain_str_len); + memset(pdu_data.search_domain + search_domain_str_len, 0, + (sizeof(pdu_data.search_domain) - search_domain_str_len)); + + uint8_t connection = 0; + if (m_incremental_updates) { + connection |= CONNECTION_INCREMENTAL_UPDATES; + } + pdu_data.connection = HostToNetwork(connection); + + stream->Write(reinterpret_cast(&pdu_data), + static_cast(sizeof(broker_connect_pdu_data))); +} + +void BrokerConnectPDU::PrependPDU(ola::io::IOStack *stack, + const string &client_scope, + uint16_t e133_version, + const string &search_domain, + bool incremental_updates) { + broker_connect_pdu_data pdu_data; + + size_t client_scope_str_len = min(client_scope.size(), + sizeof(pdu_data.client_scope)); + strncpy(pdu_data.client_scope, client_scope.c_str(), client_scope_str_len); + memset(pdu_data.client_scope + client_scope_str_len, 0, + (sizeof(pdu_data.client_scope) - client_scope_str_len)); + + pdu_data.e133_version = HostToNetwork(e133_version); + + size_t search_domain_str_len = min(search_domain.size(), + sizeof(pdu_data.search_domain)); + strncpy(pdu_data.search_domain, search_domain.c_str(), + search_domain_str_len); + memset(pdu_data.search_domain + search_domain_str_len, 0, + (sizeof(pdu_data.search_domain) - search_domain_str_len)); + + uint8_t connection = 0; + if (incremental_updates) { + connection |= CONNECTION_INCREMENTAL_UPDATES; + } + pdu_data.connection = HostToNetwork(connection); + stack->Write(reinterpret_cast(&pdu_data), + static_cast(sizeof(broker_connect_pdu_data))); + uint16_t vector = HostToNetwork( + static_cast(VECTOR_BROKER_CONNECT)); + 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/BrokerConnectPDU.h b/libs/acn/BrokerConnectPDU.h new file mode 100644 index 0000000000..9712ce4e02 --- /dev/null +++ b/libs/acn/BrokerConnectPDU.h @@ -0,0 +1,85 @@ +/* + * 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. + * + * BrokerConnectPDU.h + * The BrokerConnectPDU class + * Copyright (C) 2023 Peter Newman + */ + +#ifndef LIBS_ACN_BROKERCONNECTPDU_H_ +#define LIBS_ACN_BROKERCONNECTPDU_H_ + +#include + +#include "libs/acn/PDU.h" +#include "ola/io/IOStack.h" +#include "ola/rdm/RDMEnums.h" + +namespace ola { +namespace acn { + +class BrokerConnectPDU : public PDU { + public: + explicit BrokerConnectPDU(unsigned int vector, + const std::string &client_scope, + uint16_t e133_version, + const std::string &search_domain, + bool incremental_updates): + PDU(vector, TWO_BYTES, true), + m_client_scope(client_scope), + m_e133_version(e133_version), + m_search_domain(search_domain), + m_incremental_updates(incremental_updates) {} + + 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; + bool PackData(uint8_t *data, unsigned int *length) const; + void PackData(ola::io::OutputStream *stream) const; + + static void PrependPDU(ola::io::IOStack *stack, + const std::string &client_scope, + uint16_t e133_version, + const std::string &search_domain, + bool incremental_updates); + + // bit masks for connection + static const uint8_t CONNECTION_INCREMENTAL_UPDATES = 0x01; + + PACK( + struct broker_connect_pdu_data_s { + // Plus one to allow for the mandatory null + char client_scope[ola::rdm::MAX_RDM_SCOPE_STRING_LENGTH + 1]; + uint16_t e133_version; + char search_domain[ola::rdm::MAX_RDM_DOMAIN_NAME_LENGTH]; + uint8_t connection; + }); + typedef struct broker_connect_pdu_data_s broker_connect_pdu_data; + + private: + const std::string m_client_scope; + uint16_t m_e133_version; + const std::string m_search_domain; + uint8_t m_incremental_updates; +}; +} // namespace acn +} // namespace ola +#endif // LIBS_ACN_BROKERCONNECTPDU_H_ diff --git a/libs/acn/BrokerConnectPDUTest.cpp b/libs/acn/BrokerConnectPDUTest.cpp new file mode 100644 index 0000000000..e9d1052e34 --- /dev/null +++ b/libs/acn/BrokerConnectPDUTest.cpp @@ -0,0 +1,199 @@ +/* + * 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. + * + * BrokerConnectPDUTest.cpp + * Test fixture for the BrokerConnectPDU class + * Copyright (C) 2023 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/BrokerConnectPDU.h" + +namespace ola { +namespace acn { + +using ola::acn::BrokerConnectPDU; +using ola::io::IOQueue; +using ola::io::IOStack; +using ola::io::OutputStream; +using ola::network::HostToNetwork; + +class BrokerConnectPDUTest: public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(BrokerConnectPDUTest); + CPPUNIT_TEST(testSimpleBrokerConnectPDU); + CPPUNIT_TEST(testSimpleBrokerConnectPDUToOutputStream); + CPPUNIT_TEST(testPrepend); + CPPUNIT_TEST_SUITE_END(); + + public: + void testSimpleBrokerConnectPDU(); + void testSimpleBrokerConnectPDUToOutputStream(); + void testPrepend(); + + private: + static const uint16_t TEST_VECTOR; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(BrokerConnectPDUTest); + +const uint16_t BrokerConnectPDUTest::TEST_VECTOR = 39; + + +/* + * Test that packing a BrokerConnectPDU works. + */ +void BrokerConnectPDUTest::testSimpleBrokerConnectPDU() { + BrokerConnectPDU pdu( + TEST_VECTOR, + "default", + 1, + "local.", + true); + + OLA_ASSERT_EQ(0u, pdu.HeaderSize()); + OLA_ASSERT_EQ(297u, pdu.DataSize()); + OLA_ASSERT_EQ(302u, 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); + + // TODO(Peter): Better spot check the data! + + // 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 BrokerConnectPDUTest::testSimpleBrokerConnectPDUToOutputStream() { + BrokerConnectPDU pdu( + TEST_VECTOR, + "default", + 1, + "local.", + true); + + OLA_ASSERT_EQ(0u, pdu.HeaderSize()); + OLA_ASSERT_EQ(297u, pdu.DataSize()); + OLA_ASSERT_EQ(302u, pdu.Size()); + + IOQueue output; + OutputStream stream(&output); + pdu.Write(&stream); + OLA_ASSERT_EQ(302u, 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, 0x01, 0x2e, + 0, 39, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, // default + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0x01, + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x2e, // local. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1 + }; + OLA_ASSERT_DATA_EQUALS(EXPECTED, sizeof(EXPECTED), pdu_data, pdu_size); + output.Pop(output.Size()); + delete[] pdu_data; +} + + +void BrokerConnectPDUTest::testPrepend() { + IOStack stack; + BrokerConnectPDU::PrependPDU(&stack, + "default", + 1, + "local.", + true); + + unsigned int length = stack.Size(); + uint8_t *buffer = new uint8_t[length]; + OLA_ASSERT(stack.Read(buffer, length)); + + const uint8_t expected_data[] = { + 0xf0, 0x01, 0x2e, + 0, 1, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, // default + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0x01, + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x2e, // local. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1 + }; + OLA_ASSERT_DATA_EQUALS(expected_data, sizeof(expected_data), buffer, length); + delete[] buffer; +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/BrokerConnectReplyInflator.cpp b/libs/acn/BrokerConnectReplyInflator.cpp new file mode 100644 index 0000000000..313f4958b1 --- /dev/null +++ b/libs/acn/BrokerConnectReplyInflator.cpp @@ -0,0 +1,75 @@ +/* + * 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. + * + * BrokerConnectReplyInflator.cpp + * The Inflator for BrokerConnectReply PDU + * Copyright (C) 2023 Peter Newman + */ + +#include +#include + +#include "ola/Logging.h" +#include "ola/network/NetworkUtils.h" +#include "ola/rdm/UID.h" +#include "libs/acn/BrokerConnectReplyInflator.h" + +namespace ola { +namespace acn { + +using ola::network::NetworkToHost; +using ola::rdm::UID; + +/** + * Set a BrokerConnectReplyHandler to run when receiving a Broker Connect Reply + * message. + * @param handler the callback to invoke when there is a Broker Connect Reply. + */ +void BrokerConnectReplyInflator::SetBrokerConnectReplyHandler( + BrokerConnectReplyHandler *handler) { + m_broker_connect_reply_handler.reset(handler); +} + +unsigned int BrokerConnectReplyInflator::InflatePDUBlock(HeaderSet *headers, + const uint8_t *data, + unsigned int len) { + broker_connect_reply_pdu_data pdu_data; + if (len > sizeof(pdu_data)) { + OLA_WARN << "Got too much data, received " << len << " only expecting " + << sizeof(pdu_data); + return 0; + } + + memcpy(reinterpret_cast(&pdu_data), data, sizeof(pdu_data)); + + OLA_DEBUG << "Connect reply from " << UID(pdu_data.broker_uid) << " for " + << UID(pdu_data.client_uid) << " with connection code " + << pdu_data.connection_code << " using E1.33 version " + << NetworkToHost(pdu_data.e133_version); + + BrokerConnectReply reply(pdu_data.connection_code, + NetworkToHost(pdu_data.e133_version), + UID(pdu_data.broker_uid), + UID(pdu_data.client_uid)); + + if (m_broker_connect_reply_handler.get()) { + m_broker_connect_reply_handler->Run(headers, reply); + } else { + OLA_WARN << "No Broker Connect Reply handler defined!"; + } + return sizeof(pdu_data); +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/BrokerConnectReplyInflator.h b/libs/acn/BrokerConnectReplyInflator.h new file mode 100644 index 0000000000..ae8c647b35 --- /dev/null +++ b/libs/acn/BrokerConnectReplyInflator.h @@ -0,0 +1,98 @@ +/* + * 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. + * + * BrokerConnectReplyInflator.h + * Interface for the BrokerConnectReplyInflator class. + * Copyright (C) 2023 Peter Newman + */ + +#ifndef LIBS_ACN_BROKERCONNECTREPLYINFLATOR_H_ +#define LIBS_ACN_BROKERCONNECTREPLYINFLATOR_H_ + +#include "ola/Callback.h" +#include "ola/acn/ACNVectors.h" +#include "ola/rdm/UID.h" +#include "libs/acn/BaseInflator.h" + +namespace ola { +namespace acn { + +class BrokerConnectReplyInflator: public BaseInflator { + friend class BrokerConnectReplyInflatorTest; + + public: + struct BrokerConnectReply { + BrokerConnectReply(uint16_t _connection_code, + uint16_t _e133_version, + const ola::rdm::UID &_broker_uid, + const ola::rdm::UID &_client_uid) + : connection_code(_connection_code), + e133_version(_e133_version), + broker_uid(_broker_uid), + client_uid(_client_uid) { + } + uint16_t connection_code; + uint16_t e133_version; + ola::rdm::UID broker_uid; + ola::rdm::UID client_uid; + }; + + + // These are pointers so the callers don't have to pull in all the headers. + typedef ola::Callback2 BrokerConnectReplyHandler; + + BrokerConnectReplyInflator() + : BaseInflator() { + } + ~BrokerConnectReplyInflator() {} + + uint32_t Id() const { return ola::acn::VECTOR_BROKER_CONNECT_REPLY; } + + PACK( + struct broker_connect_reply_pdu_data_s { + uint16_t connection_code; + uint16_t e133_version; + uint8_t broker_uid[ola::rdm::UID::LENGTH]; + uint8_t client_uid[ola::rdm::UID::LENGTH]; + }); + typedef struct broker_connect_reply_pdu_data_s broker_connect_reply_pdu_data; + + void SetBrokerConnectReplyHandler(BrokerConnectReplyHandler *handler); + + 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 + + unsigned int InflatePDUBlock(HeaderSet *headers, + const uint8_t *data, + unsigned int len); + + private: + std::auto_ptr m_broker_connect_reply_handler; +}; +} // namespace acn +} // namespace ola +#endif // LIBS_ACN_BROKERCONNECTREPLYINFLATOR_H_ diff --git a/libs/acn/BrokerConnectReplyInflatorTest.cpp b/libs/acn/BrokerConnectReplyInflatorTest.cpp new file mode 100644 index 0000000000..b042e67f13 --- /dev/null +++ b/libs/acn/BrokerConnectReplyInflatorTest.cpp @@ -0,0 +1,115 @@ +/* + * 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. + * + * BrokerConnectReplyInflatorTest.cpp + * Test fixture for the BrokerConnectReplyInflator class + * Copyright (C) 2023 Peter Newman + */ + +#include + +#include "ola/Logging.h" +#include "ola/network/NetworkUtils.h" +#include "libs/acn/HeaderSet.h" +#include "libs/acn/PDUTestCommon.h" +#include "libs/acn/BrokerInflator.h" +#include "libs/acn/BrokerPDU.h" +#include "ola/testing/TestUtils.h" + + +namespace ola { +namespace acn { + +using ola::network::HostToNetwork; + +class BrokerConnectReplyInflatorTest: public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(BrokerConnectReplyInflatorTest); + 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 BrokerConnectReplyInflatorTest::TEST_DATA[] = {0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, + 11, 12, 13, 14, + 15}; +const uint8_t BrokerConnectReplyInflatorTest::TEST_DATA2[] = {10, 11, 12, 13, + 14, 15, 16, 17, + 18, 19, 20, 21, + 22, 23, 24, 25}; +CPPUNIT_TEST_SUITE_REGISTRATION(BrokerConnectReplyInflatorTest); + + +/* + * Check that we can decode headers properly + */ +void BrokerConnectReplyInflatorTest::testDecodeHeader() { + OLA_ASSERT(inflator.DecodeHeader(&header_set, + reinterpret_cast(&header), + sizeof(header), + &bytes_used)); + OLA_ASSERT_EQ((unsigned int) sizeof(header), bytes_used); + BrokerHeader decoded_header = header_set.GetBrokerHeader(); + + // 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.GetBrokerHeader(); + OLA_ASSERT(destination_cid == decoded_header.DestinationCid()); + OLA_ASSERT_EQ((uint32_t) 72650, decoded_header.TransactionNumber()); + + 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 BrokerConnectReply PDU that contains other PDUs + */ +void BrokerConnectReplyInflatorTest::testInflatePDU() { + // TODO(Peter): pass a different type of msg here as well + BrokerConnectReplyPDU pdu(3, NULL); + OLA_ASSERT_EQ((unsigned int) 23, 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); + + BrokerConnectReplyInflator inflator; + HeaderSet header_set; + OLA_ASSERT(inflator.InflatePDUBlock(&header_set, data, size)); + // TODO(Peter): Test that the callback has the right data + delete[] data; +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/BrokerFetchClientListPDU.cpp b/libs/acn/BrokerFetchClientListPDU.cpp new file mode 100644 index 0000000000..99e7f866e3 --- /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 0000000000..df4a40e4f0 --- /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 0000000000..00a5ad09cf --- /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/BrokerInflator.h b/libs/acn/BrokerInflator.h new file mode 100644 index 0000000000..58da325542 --- /dev/null +++ b/libs/acn/BrokerInflator.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. + * + * BrokerInflator.h + * Interface for the BrokerInflator class. + * Copyright (C) 2023 Peter Newman + */ + +#ifndef LIBS_ACN_BROKERINFLATOR_H_ +#define LIBS_ACN_BROKERINFLATOR_H_ + +#include "ola/acn/ACNVectors.h" +#include "libs/acn/BaseInflator.h" + +namespace ola { +namespace acn { + +class BrokerInflator: public BaseInflator { + friend class BrokerInflatorTest; + + public: + BrokerInflator() + : BaseInflator(PDU::TWO_BYTES) { + } + ~BrokerInflator() {} + + uint32_t Id() const { return ola::acn::VECTOR_ROOT_BROKER; } + + 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_BROKERINFLATOR_H_ diff --git a/libs/acn/BrokerNullInflator.h b/libs/acn/BrokerNullInflator.h new file mode 100644 index 0000000000..28a641c722 --- /dev/null +++ b/libs/acn/BrokerNullInflator.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. + * + * BrokerNullInflator.h + * Interface for the BrokerNullInflator class. + * Copyright (C) 2023 Peter Newman + */ + +#ifndef LIBS_ACN_BROKERNULLINFLATOR_H_ +#define LIBS_ACN_BROKERNULLINFLATOR_H_ + +#include "ola/acn/ACNVectors.h" +#include "libs/acn/BaseInflator.h" + +namespace ola { +namespace acn { + +class BrokerNullInflator: public BaseInflator { + friend class BrokerNullInflatorTest; + + public: + BrokerNullInflator() + : BaseInflator() { + } + ~BrokerNullInflator() {} + + uint32_t Id() const { return ola::acn::VECTOR_BROKER_NULL; } + + 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_BROKERNULLINFLATOR_H_ diff --git a/libs/acn/BrokerNullInflatorTest.cpp b/libs/acn/BrokerNullInflatorTest.cpp new file mode 100644 index 0000000000..8f3ada3589 --- /dev/null +++ b/libs/acn/BrokerNullInflatorTest.cpp @@ -0,0 +1,71 @@ +/* + * 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. + * + * BrokerNullInflatorTest.cpp + * Test fixture for the BrokerNullInflator class + * Copyright (C) 2023 Peter Newman + */ + +#include + +#include "ola/Logging.h" +#include "ola/network/NetworkUtils.h" +#include "libs/acn/HeaderSet.h" +#include "libs/acn/PDUTestCommon.h" +#include "libs/acn/BrokerNullInflator.h" +#include "libs/acn/BrokerNullPDU.h" +#include "ola/testing/TestUtils.h" + + +namespace ola { +namespace acn { + +using ola::network::HostToNetwork; + +class BrokerNullInflatorTest: public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(BrokerNullInflatorTest); +// CPPUNIT_TEST(testDecodeHeader); + CPPUNIT_TEST(testInflatePDU); + CPPUNIT_TEST_SUITE_END(); + + public: + void testInflatePDU(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(BrokerNullInflatorTest); + + +/* + * Check that we can inflate a BrokerNull PDU that contains other PDUs + */ +void BrokerNullInflatorTest::testInflatePDU() { + // TODO(Peter): pass a different type of msg here as well + BrokerNullPDU pdu(3); + OLA_ASSERT_EQ((unsigned int) 5, 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); + + BrokerNullInflator inflator; + HeaderSet header_set; + OLA_ASSERT(inflator.InflatePDUBlock(&header_set, data, size)); +// OLA_ASSERT(header == header_set.GetBrokerHeader()); + delete[] data; +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/BrokerNullPDU.cpp b/libs/acn/BrokerNullPDU.cpp new file mode 100644 index 0000000000..6a4b08741b --- /dev/null +++ b/libs/acn/BrokerNullPDU.cpp @@ -0,0 +1,37 @@ +/* + * 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. + * + * BrokerNullPDU.cpp + * The BrokerNullPDU + * Copyright (C) 2023 Peter Newman + */ + +#include "libs/acn/BrokerNullPDU.h" + +#include +#include + +namespace ola { +namespace acn { + +using ola::network::HostToNetwork; + +void BrokerNullPDU::PrependPDU(ola::io::IOStack *stack) { + uint16_t vector = HostToNetwork(static_cast(VECTOR_BROKER_NULL)); + 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/BrokerNullPDU.h b/libs/acn/BrokerNullPDU.h new file mode 100644 index 0000000000..0bd2a768a4 --- /dev/null +++ b/libs/acn/BrokerNullPDU.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. + * + * BrokerNullPDU.h + * The BrokerNullPDU class + * Copyright (C) 2023 Peter Newman + */ + +#ifndef LIBS_ACN_BROKERNULLPDU_H_ +#define LIBS_ACN_BROKERNULLPDU_H_ + +#include + +#include "libs/acn/PDU.h" + +namespace ola { +namespace acn { + +class BrokerNullPDU : public PDU { + public: + explicit BrokerNullPDU(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_BROKERNULLPDU_H_ diff --git a/libs/acn/BrokerNullPDUTest.cpp b/libs/acn/BrokerNullPDUTest.cpp new file mode 100644 index 0000000000..6c90c4d907 --- /dev/null +++ b/libs/acn/BrokerNullPDUTest.cpp @@ -0,0 +1,147 @@ +/* + * 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. + * + * BrokerNullPDUTest.cpp + * Test fixture for the BrokerNullPDU 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/BrokerNullPDU.h" + +namespace ola { +namespace acn { + +using ola::acn::BrokerNullPDU; +using ola::io::IOQueue; +using ola::io::IOStack; +using ola::io::OutputStream; +using ola::network::HostToNetwork; + +class BrokerNullPDUTest: public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(BrokerNullPDUTest); + CPPUNIT_TEST(testSimpleBrokerNullPDU); + CPPUNIT_TEST(testSimpleBrokerNullPDUToOutputStream); + CPPUNIT_TEST(testPrepend); + CPPUNIT_TEST_SUITE_END(); + + public: + void testSimpleBrokerNullPDU(); + void testSimpleBrokerNullPDUToOutputStream(); + 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(BrokerNullPDUTest); + +const uint16_t BrokerNullPDUTest::TEST_VECTOR = 39; + + +/* + * Test that packing a BrokerNullPDU works. + */ +void BrokerNullPDUTest::testSimpleBrokerNullPDU() { + BrokerNullPDU 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 BrokerNullPDUTest::testSimpleBrokerNullPDUToOutputStream() { + BrokerNullPDU 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 BrokerNullPDUTest::testPrepend() { + IOStack stack; + BrokerNullPDU::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, 0x0f + }; + OLA_ASSERT_DATA_EQUALS(expected_data, sizeof(expected_data), buffer, length); + delete[] buffer; +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/BrokerPDU.cpp b/libs/acn/BrokerPDU.cpp new file mode 100644 index 0000000000..b01f5d834c --- /dev/null +++ b/libs/acn/BrokerPDU.cpp @@ -0,0 +1,70 @@ +/* + * 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. + * + * BrokerPDU.cpp + * The BrokerPDU + * Copyright (C) 2023 Peter Newman + */ + + +#include "ola/Logging.h" +#include "ola/base/Array.h" +#include "ola/network/NetworkUtils.h" +#include "libs/acn/BrokerPDU.h" + +namespace ola { +namespace acn { + +using ola::io::OutputStream; +using ola::network::HostToNetwork; + +/* + * Size of the data portion + */ +unsigned int BrokerPDU::DataSize() const { + return m_pdu ? m_pdu->Size() : 0; +} + + +/* + * Pack the data portion. + */ +bool BrokerPDU::PackData(uint8_t *data, unsigned int *length) const { + if (m_pdu) { + return m_pdu->Pack(data, length); + } + *length = 0; + return true; +} + + +/* + * Pack the data into a buffer + */ +void BrokerPDU::PackData(OutputStream *stream) const { + if (m_pdu) { + m_pdu->Write(stream); + } +} + + +void BrokerPDU::PrependPDU(ola::io::IOStack *stack, + uint16_t vector) { + 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/BrokerPDU.h b/libs/acn/BrokerPDU.h new file mode 100644 index 0000000000..f77bfe6452 --- /dev/null +++ b/libs/acn/BrokerPDU.h @@ -0,0 +1,61 @@ +/* + * 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. + * + * BrokerPDU.h + * Interface for the BrokerPDU class + * Copyright (C) 2023 Peter Newman + */ + +#ifndef LIBS_ACN_BROKERPDU_H_ +#define LIBS_ACN_BROKERPDU_H_ + +#include +#include + +#include "libs/acn/PDU.h" + +namespace ola { +namespace acn { + +class BrokerPDU: public PDU { + public: + BrokerPDU(unsigned int vector, + const PDU *pdu): + PDU(vector, FOUR_BYTES, true), + m_pdu(pdu) {} + ~BrokerPDU() {} + + 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; + bool PackData(uint8_t *data, unsigned int *length) const; + + void PackData(ola::io::OutputStream *stream) const; + + static void PrependPDU(ola::io::IOStack *stack, + uint16_t vector); + + private: + const PDU *m_pdu; +}; +} // namespace acn +} // namespace ola +#endif // LIBS_ACN_BROKERPDU_H_ diff --git a/libs/acn/BrokerPDUTest.cpp b/libs/acn/BrokerPDUTest.cpp new file mode 100644 index 0000000000..e4c2c6ef60 --- /dev/null +++ b/libs/acn/BrokerPDUTest.cpp @@ -0,0 +1,127 @@ +/* + * 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. + * + * BrokerPDUTest.cpp + * Test fixture for the BrokerPDU class + * Copyright (C) 2023 Peter Newman + */ + +#include + +#include "ola/Logging.h" +#include "ola/io/IOQueue.h" +#include "ola/io/OutputStream.h" +#include "ola/network/NetworkUtils.h" +#include "ola/testing/TestUtils.h" +#include "libs/acn/BrokerPDU.h" +#include "libs/acn/PDUTestCommon.h" + + +namespace ola { +namespace acn { + +using ola::io::IOQueue; +using ola::io::OutputStream; +using ola::network::HostToNetwork; + +class BrokerPDUTest: public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(BrokerPDUTest); + CPPUNIT_TEST(testSimpleBrokerPDU); + CPPUNIT_TEST(testSimpleBrokerPDUToOutputStream); + CPPUNIT_TEST_SUITE_END(); + + public: + void testSimpleBrokerPDU(); + void testSimpleBrokerPDUToOutputStream(); + + void setUp() { + ola::InitLogging(ola::OLA_LOG_DEBUG, ola::OLA_LOG_STDERR); + } + + private: + static const unsigned int TEST_VECTOR; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(BrokerPDUTest); + +const unsigned int BrokerPDUTest::TEST_VECTOR = 39; + + +/* + * Test that packing a BrokerPDU without data works. + */ +void BrokerPDUTest::testSimpleBrokerPDU() { + BrokerPDU pdu(TEST_VECTOR, NULL); + + 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 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); + + // 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 BrokerPDUTest::testSimpleBrokerPDUToOutputStream() { + BrokerPDU pdu(TEST_VECTOR, NULL); + + 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; +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/DMPE131Inflator.cpp b/libs/acn/DMPE131Inflator.cpp index 3e11c3e4b2..ed011161d8 100644 --- a/libs/acn/DMPE131Inflator.cpp +++ b/libs/acn/DMPE131Inflator.cpp @@ -71,8 +71,9 @@ bool DMPE131Inflator::HandlePDUData(uint32_t vector, return true; } - if (universe_iter == m_handlers.end()) + if (universe_iter == m_handlers.end()) { return true; + } DMPHeader dmp_header = headers.GetDMPHeader(); @@ -104,16 +105,17 @@ bool DMPE131Inflator::HandlePDUData(uint32_t vector, if (address->Increment() != 1) { OLA_INFO << "E1.31 DMP packet with increment " << address->Increment() - << ", disarding"; + << ", disarding"; return true; } unsigned int length_remaining = pdu_len - available_length; int start_code = -1; - if (e131_header.UsingRev2()) + if (e131_header.UsingRev2()) { start_code = static_cast(address->Start()); - else if (length_remaining && address->Number()) + } else if (length_remaining && address->Number()) { start_code = *(data + available_length); + } // The only time we want to continue processing a non-0 start code is if it // contains a Terminate message. @@ -132,14 +134,16 @@ bool DMPE131Inflator::HandlePDUData(uint32_t vector, // Reaching here means that we actually have new data and we should merge. if (target_buffer && start_code == 0) { unsigned int channels = std::min(length_remaining, address->Number()); - if (e131_header.UsingRev2()) + if (e131_header.UsingRev2()) { target_buffer->Set(data + available_length, channels); - else - target_buffer->Set(data + available_length + 1, channels - 1); + } else { + target_buffer->Set(data + available_length + 1, channels - 1); + } } - if (universe_iter->second.priority) + if (universe_iter->second.priority) { *universe_iter->second.priority = universe_iter->second.active_priority; + } // merge the sources switch (universe_iter->second.sources.size()) { @@ -155,9 +159,11 @@ bool DMPE131Inflator::HandlePDUData(uint32_t vector, // HTP Merge universe_iter->second.buffer->Reset(); std::vector::const_iterator source_iter = - universe_iter->second.sources.begin(); - for (; source_iter != universe_iter->second.sources.end(); ++source_iter) + universe_iter->second.sources.begin(); + for (; source_iter != universe_iter->second.sources.end(); + ++source_iter) { universe_iter->second.buffer->HTPMerge(source_iter->buffer); + } universe_iter->second.closure->Run(); } return true; @@ -175,8 +181,9 @@ bool DMPE131Inflator::SetHandler(uint16_t universe, ola::DmxBuffer *buffer, uint8_t *priority, ola::Callback0 *closure) { - if (!closure || !buffer) + if (!closure || !buffer) { return false; + } UniverseHandlers::iterator iter = m_handlers.find(universe); @@ -265,38 +272,41 @@ bool DMPE131Inflator::TrackSourceIfRequired( iter++; } - if (sources.empty()) + if (sources.empty()) { universe_data->active_priority = 0; + } for (iter = sources.begin(); iter != sources.end(); ++iter) { - if (iter->cid == headers.GetRootHeader().GetCid()) + if (iter->cid == headers.GetRootHeader().GetCid()) { break; + } } if (iter == sources.end()) { // This is an untracked source if (e131_header.StreamTerminated() || - priority < universe_data->active_priority) + priority < universe_data->active_priority) { return false; + } if (priority > universe_data->active_priority) { - OLA_INFO << "Raising priority for universe " << - e131_header.Universe() << " from " << - static_cast(universe_data->active_priority) << " to " << - static_cast(priority); + OLA_INFO << "Raising priority for universe " << e131_header.Universe() + << " from " << static_cast(universe_data->active_priority) + << " to " << static_cast(priority); sources.clear(); universe_data->active_priority = priority; } if (sources.size() == MAX_MERGE_SOURCES) { // TODO(simon): flag this in the export map - OLA_WARN << "Max merge sources reached for universe " << - e131_header.Universe() << ", " << - headers.GetRootHeader().GetCid().ToString() << " won't be tracked"; + OLA_WARN << "Max merge sources reached for universe " + << e131_header.Universe() << ", " + << headers.GetRootHeader().GetCid().ToString() + << " won't be tracked"; return false; } else { - OLA_INFO << "Added new E1.31 source: " << - headers.GetRootHeader().GetCid().ToString(); + OLA_INFO << "Added new E1.31 source: " + << headers.GetRootHeader().GetCid().ToString(); dmx_source new_source; new_source.cid = headers.GetRootHeader().GetCid(); new_source.sequence = e131_header.Sequence(); @@ -311,19 +321,21 @@ bool DMPE131Inflator::TrackSourceIfRequired( int8_t seq_diff = static_cast(e131_header.Sequence() - iter->sequence); if (seq_diff <= 0 && seq_diff > SEQUENCE_DIFF_THRESHOLD) { - OLA_INFO << "Old packet received, ignoring, this # " << - static_cast(e131_header.Sequence()) << ", last " << - static_cast(iter->sequence); + OLA_INFO << "Old packet received, ignoring, this # " + << static_cast(e131_header.Sequence()) << ", last " + << static_cast(iter->sequence); return false; } iter->sequence = e131_header.Sequence(); if (e131_header.StreamTerminated()) { - OLA_INFO << "CID " << headers.GetRootHeader().GetCid().ToString() << - " sent a termination for universe " << e131_header.Universe(); + OLA_INFO << "CID " << headers.GetRootHeader().GetCid().ToString() + << " sent a termination for universe " + << e131_header.Universe(); sources.erase(iter); - if (sources.empty()) + if (sources.empty()) { universe_data->active_priority = 0; + } // We need to trigger a merge here else the buffer will be stale, we keep // the buffer as NULL though so we don't use the data. return true; diff --git a/libs/acn/DMPE131Inflator.h b/libs/acn/DMPE131Inflator.h index 35b62d845b..e1185b653b 100644 --- a/libs/acn/DMPE131Inflator.h +++ b/libs/acn/DMPE131Inflator.h @@ -36,58 +36,58 @@ class DMPE131Inflator: public DMPInflator { friend class DMPE131InflatorTest; public: - explicit DMPE131Inflator(bool ignore_preview): - DMPInflator(), - m_ignore_preview(ignore_preview) { - } - ~DMPE131Inflator(); + explicit DMPE131Inflator(bool ignore_preview): + DMPInflator(), + m_ignore_preview(ignore_preview) { + } + ~DMPE131Inflator(); - bool SetHandler(uint16_t universe, ola::DmxBuffer *buffer, - uint8_t *priority, ola::Callback0 *handler); - bool RemoveHandler(uint16_t universe); + bool SetHandler(uint16_t universe, ola::DmxBuffer *buffer, + uint8_t *priority, ola::Callback0 *handler); + bool RemoveHandler(uint16_t universe); - void RegisteredUniverses(std::vector *universes); + void RegisteredUniverses(std::vector *universes); protected: - virtual bool HandlePDUData(uint32_t vector, - const HeaderSet &headers, - const uint8_t *data, - unsigned int pdu_len); + virtual bool HandlePDUData(uint32_t vector, + const HeaderSet &headers, + const uint8_t *data, + unsigned int pdu_len); private: - typedef struct { - ola::acn::CID cid; - uint8_t sequence; - TimeStamp last_heard_from; - DmxBuffer buffer; - } dmx_source; + typedef struct { + ola::acn::CID cid; + uint8_t sequence; + TimeStamp last_heard_from; + DmxBuffer buffer; + } dmx_source; - typedef struct { - DmxBuffer *buffer; - Callback0 *closure; - uint8_t active_priority; - uint8_t *priority; - std::vector sources; - } universe_handler; + typedef struct { + DmxBuffer *buffer; + Callback0 *closure; + uint8_t active_priority; + uint8_t *priority; + std::vector sources; + } universe_handler; - typedef std::map UniverseHandlers; + typedef std::map UniverseHandlers; - UniverseHandlers m_handlers; - bool m_ignore_preview; - ola::Clock m_clock; + UniverseHandlers m_handlers; + bool m_ignore_preview; + ola::Clock m_clock; - bool TrackSourceIfRequired(universe_handler *universe_data, - const HeaderSet &headers, - DmxBuffer **buffer); + bool TrackSourceIfRequired(universe_handler *universe_data, + const HeaderSet &headers, + DmxBuffer **buffer); - // The max number of sources we'll track per universe. - static const uint8_t MAX_MERGE_SOURCES = 6; - // The max merge priority. - static const uint8_t MAX_E131_PRIORITY = 200; - // ignore packets that differ by less than this amount from the last one - static const int8_t SEQUENCE_DIFF_THRESHOLD = -20; - // expire sources after 2.5s - static const TimeInterval EXPIRY_INTERVAL; + // The max number of sources we'll track per universe. + static const uint8_t MAX_MERGE_SOURCES = 6; + // The max merge priority. + static const uint8_t MAX_E131_PRIORITY = 200; + // ignore packets that differ by less than this amount from the last one + static const int8_t SEQUENCE_DIFF_THRESHOLD = -20; + // expire sources after 2.5s + static const TimeInterval EXPIRY_INTERVAL; }; } // namespace acn } // namespace ola diff --git a/tools/e133/E133HealthCheckedConnection.cpp b/libs/acn/E133HealthCheckedConnection.cpp similarity index 87% rename from tools/e133/E133HealthCheckedConnection.cpp rename to libs/acn/E133HealthCheckedConnection.cpp index f03e7e5dc7..798728e3d1 100644 --- a/tools/e133/E133HealthCheckedConnection.cpp +++ b/libs/acn/E133HealthCheckedConnection.cpp @@ -21,8 +21,8 @@ #include #include +#include "libs/acn/E133HealthCheckedConnection.h" #include "libs/acn/RootSender.h" -#include "tools/e133/E133HealthCheckedConnection.h" using ola::io::IOStack; @@ -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/libs/acn/E133HealthCheckedConnection.h similarity index 71% rename from tools/e133/E133HealthCheckedConnection.h rename to libs/acn/E133HealthCheckedConnection.h index 5e7b61453a..1008edfcaf 100644 --- a/tools/e133/E133HealthCheckedConnection.h +++ b/libs/acn/E133HealthCheckedConnection.h @@ -21,13 +21,16 @@ * 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_ -#define TOOLS_E133_E133HEALTHCHECKEDCONNECTION_H_ +#ifndef LIBS_ACN_E133HEALTHCHECKEDCONNECTION_H_ +#define LIBS_ACN_E133HEALTHCHECKEDCONNECTION_H_ #include #include @@ -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_ +#endif // LIBS_ACN_E133HEALTHCHECKEDCONNECTION_H_ diff --git a/libs/acn/E133Helper.cpp b/libs/acn/E133Helper.cpp new file mode 100644 index 0000000000..61d0d053e6 --- /dev/null +++ b/libs/acn/E133Helper.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. + * + * E133Helper.cpp + * Various misc E1.33 functions. + * Copyright (C) 2024 Peter Newman + * + * At some point we may want to localize this file. + */ + +#include +#include +#include +#include "ola/e133/E133Enums.h" +#include "ola/e133/E133Helper.h" +#include "ola/StringUtils.h" + +namespace ola { +namespace e133 { + +using std::ostringstream; +using std::string; +using std::vector; + + +/** + * Verify that the int is a valid E1.33 RPT Client Type. + */ +bool IntToRPTClientType(uint8_t input, + ola::e133::E133RPTClientTypeCode *client_type) { + switch (input) { + case ola::e133::RPT_CLIENT_TYPE_DEVICE: + *client_type = ola::e133::RPT_CLIENT_TYPE_DEVICE; + return true; + case ola::e133::RPT_CLIENT_TYPE_CONTROLLER: + *client_type = ola::e133::RPT_CLIENT_TYPE_CONTROLLER; + return true; + default: + return false; + } +} + + +/** + * Convert a uint8_t representing an RPT client type to a human-readable string. + * @param type the RPT client type value + */ +string RPTClientTypeToString(uint8_t type) { + switch (type) { + case RPT_CLIENT_TYPE_DEVICE: + return "Device"; + case RPT_CLIENT_TYPE_CONTROLLER: + return "Controller"; + default: + ostringstream str; + str << "Unknown, was " << static_cast(type); + return str.str(); + } +} +} // namespace e133 +} // namespace ola diff --git a/libs/acn/E133StatusHelper.cpp b/libs/acn/E133StatusHelper.cpp new file mode 100644 index 0000000000..5345255216 --- /dev/null +++ b/libs/acn/E133StatusHelper.cpp @@ -0,0 +1,272 @@ +/* + * 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. + * + * E133StatusHelper.cpp + * Functions for dealing with E1.33 Status Codes. + * Copyright (C) 2013 Simon Newton + */ + +#include +#include +#include "ola/Logging.h" +#include "ola/e133/E133StatusHelper.h" + +namespace ola { +namespace e133 { + +using std::string; +using ola::acn::RPTStatusVector; +using ola::e133::E133StatusCode; +using ola::e133::E133ConnectStatusCode; +using ola::rdm::RDMStatusCode; + +/** + * Verify that the int is a valid E1.33 Status Code. + */ +bool IntToStatusCode(uint16_t input, E133StatusCode *status_code) { + if (!status_code) { + OLA_WARN << "ola:e133::IntToStatusCode: missing status_code"; + return false; + } + + switch (input) { + case ola::e133::SC_E133_ACK: + *status_code = ola::e133::SC_E133_ACK; + return true; + case ola::e133::SC_E133_RDM_TIMEOUT: + *status_code = ola::e133::SC_E133_RDM_TIMEOUT; + return true; + case ola::e133::SC_E133_RDM_INVALID_RESPONSE: + *status_code = ola::e133::SC_E133_RDM_INVALID_RESPONSE; + return true; + case ola::e133::SC_E133_BUFFER_FULL: + *status_code = ola::e133::SC_E133_BUFFER_FULL; + return true; + case ola::e133::SC_E133_UNKNOWN_UID: + *status_code = ola::e133::SC_E133_UNKNOWN_UID; + return true; + case ola::e133::SC_E133_NONEXISTENT_ENDPOINT: + *status_code = ola::e133::SC_E133_NONEXISTENT_ENDPOINT; + return true; + case ola::e133::SC_E133_WRONG_ENDPOINT: + *status_code = ola::e133::SC_E133_WRONG_ENDPOINT; + return true; + case ola::e133::SC_E133_ACK_OVERFLOW_CACHE_EXPIRED: + *status_code = ola::e133::SC_E133_ACK_OVERFLOW_CACHE_EXPIRED; + return true; + case ola::e133::SC_E133_ACK_OVERFLOW_IN_PROGRESS: + *status_code = ola::e133::SC_E133_ACK_OVERFLOW_IN_PROGRESS; + return true; + case ola::e133::SC_E133_BROADCAST_COMPLETE: + *status_code = ola::e133::SC_E133_BROADCAST_COMPLETE; + return true; + default: + return false; + } +} + + +/** + * Return a text string describing this status code. + */ +string StatusCodeToString(E133StatusCode status_code) { + switch (status_code) { + case ola::e133::SC_E133_ACK: + return "Acknowledged"; + case ola::e133::SC_E133_RDM_TIMEOUT: + return "Response Timeout"; + case ola::e133::SC_E133_RDM_INVALID_RESPONSE: + return "Invalid Response"; + case ola::e133::SC_E133_BUFFER_FULL: + return "Buffer Full"; + case ola::e133::SC_E133_UNKNOWN_UID: + return "Unknown UID"; + case ola::e133::SC_E133_NONEXISTENT_ENDPOINT: + return "Endpoint doesn't exist"; + case ola::e133::SC_E133_WRONG_ENDPOINT: + return "Wrong endpoint"; + case ola::e133::SC_E133_ACK_OVERFLOW_CACHE_EXPIRED: + return "Ack overflow cache expired"; + case ola::e133::SC_E133_ACK_OVERFLOW_IN_PROGRESS: + return "Ack overflow in progress"; + case ola::e133::SC_E133_BROADCAST_COMPLETE: + return "Request was broadcast"; + } + return "Unknown E1.33 Status Code"; +} + + +bool IntToConnectStatusCode(uint16_t input, + E133ConnectStatusCode *connect_status_code) { + if (!connect_status_code) { + OLA_WARN << "ola:e133::IntToConnectStatusCode: missing " + << "connect_status_code"; + return false; + } + + switch (input) { + case ola::e133::CONNECT_OK: + *connect_status_code = ola::e133::CONNECT_OK; + return true; + case ola::e133::CONNECT_SCOPE_MISMATCH: + *connect_status_code = ola::e133::CONNECT_SCOPE_MISMATCH; + return true; + case ola::e133::CONNECT_CAPACITY_EXCEEDED: + *connect_status_code = ola::e133::CONNECT_CAPACITY_EXCEEDED; + return true; + case ola::e133::CONNECT_DUPLICATE_UID: + *connect_status_code = ola::e133::CONNECT_DUPLICATE_UID; + return true; + case ola::e133::CONNECT_INVALID_CLIENT_ENTRY: + *connect_status_code = ola::e133::CONNECT_INVALID_CLIENT_ENTRY; + return true; + case ola::e133::CONNECT_INVALID_UID: + *connect_status_code = ola::e133::CONNECT_INVALID_UID; + return true; + default: + return false; + } +} + + +string ConnectStatusCodeToString(E133ConnectStatusCode connect_status_code) { + switch (connect_status_code) { + case ola::e133::CONNECT_OK: + return "Ok"; + case ola::e133::CONNECT_SCOPE_MISMATCH: + return "Scope mismatch"; + case ola::e133::CONNECT_CAPACITY_EXCEEDED: + return "Capacity exceeded"; + case ola::e133::CONNECT_DUPLICATE_UID: + return "Duplicate UID"; + case ola::e133::CONNECT_INVALID_CLIENT_ENTRY: + return "Invalid client entry"; + case ola::e133::CONNECT_INVALID_UID: + return "Invalid UID"; + } + return "Unknown E1.33 Connect Status Code"; +} + + +bool IntToRPTStatusCode(uint16_t input, + RPTStatusVector *rpt_status_code) { + if (!rpt_status_code) { + OLA_WARN << "ola:e133::IntToRPTStatusCode: missing rpt_status_code"; + return false; + } + + switch (input) { + case ola::acn::VECTOR_RPT_STATUS_UNKNOWN_RPT_UID: + *rpt_status_code = ola::acn::VECTOR_RPT_STATUS_UNKNOWN_RPT_UID; + return true; + case ola::acn::VECTOR_RPT_STATUS_RDM_TIMEOUT: + *rpt_status_code = ola::acn::VECTOR_RPT_STATUS_RDM_TIMEOUT; + return true; + case ola::acn::VECTOR_RPT_STATUS_RDM_INVALID_RESPONSE: + *rpt_status_code = ola::acn::VECTOR_RPT_STATUS_RDM_INVALID_RESPONSE; + return true; + case ola::acn::VECTOR_RPT_STATUS_UNKNOWN_RDM_UID: + *rpt_status_code = ola::acn::VECTOR_RPT_STATUS_UNKNOWN_RDM_UID; + return true; + case ola::acn::VECTOR_RPT_STATUS_UNKNOWN_ENDPOINT: + *rpt_status_code = ola::acn::VECTOR_RPT_STATUS_UNKNOWN_ENDPOINT; + return true; + case ola::acn::VECTOR_RPT_STATUS_BROADCAST_COMPLETE: + *rpt_status_code = ola::acn::VECTOR_RPT_STATUS_BROADCAST_COMPLETE; + return true; + case ola::acn::VECTOR_RPT_STATUS_UNKNOWN_VECTOR: + *rpt_status_code = ola::acn::VECTOR_RPT_STATUS_UNKNOWN_VECTOR; + return true; + case ola::acn::VECTOR_RPT_STATUS_INVALID_MESSAGE: + *rpt_status_code = ola::acn::VECTOR_RPT_STATUS_INVALID_MESSAGE; + return true; + case ola::acn::VECTOR_RPT_STATUS_INVALID_COMMAND_CLASS: + *rpt_status_code = ola::acn::VECTOR_RPT_STATUS_INVALID_COMMAND_CLASS; + return true; + default: + return false; + } +} + + +string RPTStatusCodeToString(RPTStatusVector rpt_status_code) { + switch (rpt_status_code) { + case ola::acn::VECTOR_RPT_STATUS_UNKNOWN_RPT_UID: + return "Unknown RPT UID"; + case ola::acn::VECTOR_RPT_STATUS_RDM_TIMEOUT: + return "RDM Timeout"; + case ola::acn::VECTOR_RPT_STATUS_RDM_INVALID_RESPONSE: + return "RDM Invalid Response"; + case ola::acn::VECTOR_RPT_STATUS_UNKNOWN_RDM_UID: + return "Unknown RDM UID"; + case ola::acn::VECTOR_RPT_STATUS_UNKNOWN_ENDPOINT: + return "Unknown Endpoint"; + case ola::acn::VECTOR_RPT_STATUS_BROADCAST_COMPLETE: + return "Broadcast Complete"; + case ola::acn::VECTOR_RPT_STATUS_UNKNOWN_VECTOR: + return "Unknown Vector"; + case ola::acn::VECTOR_RPT_STATUS_INVALID_MESSAGE: + return "Invalid Message"; + case ola::acn::VECTOR_RPT_STATUS_INVALID_COMMAND_CLASS: + return "Invalid Command Class"; + } + return "Unknown E1.33 RPT Status Code"; +} + + +bool RPTStatusCodeToRDMStatusCode(RPTStatusVector rpt_status_code, + RDMStatusCode *rdm_status_code) { + if (!rdm_status_code) { + OLA_WARN << "ola:e133::RPTStatusCodeToRDMStatusCode: missing " + << "rdm_status_code"; + return false; + } + + // TODO(Peter): Fill in the gaps, possibly adding additional RDMStatusCodes + // if required + switch (rpt_status_code) { +// case ola::acn::VECTOR_RPT_STATUS_UNKNOWN_RPT_UID: +// *rdm_status_code = ola::rdm::; +// return true; + case ola::acn::VECTOR_RPT_STATUS_RDM_TIMEOUT: + *rdm_status_code = ola::rdm::RDM_TIMEOUT; + return true; + case ola::acn::VECTOR_RPT_STATUS_RDM_INVALID_RESPONSE: + *rdm_status_code = ola::rdm::RDM_INVALID_RESPONSE; + return true; + case ola::acn::VECTOR_RPT_STATUS_UNKNOWN_RDM_UID: + *rdm_status_code = ola::rdm::RDM_UNKNOWN_UID; + return true; +// case ola::acn::VECTOR_RPT_STATUS_UNKNOWN_ENDPOINT: +// *rdm_status_code = ola::rdm::; +// return true; + case ola::acn::VECTOR_RPT_STATUS_BROADCAST_COMPLETE: + *rdm_status_code = ola::rdm::RDM_WAS_BROADCAST; + return true; +// case ola::acn::VECTOR_RPT_STATUS_UNKNOWN_VECTOR: +// *rdm_status_code = ola::rdm::; +// return true; +// case ola::acn::VECTOR_RPT_STATUS_INVALID_MESSAGE: +// *rdm_status_code = ola::rdm::; +// return true; + case ola::acn::VECTOR_RPT_STATUS_INVALID_COMMAND_CLASS: + *rdm_status_code = ola::rdm::RDM_INVALID_COMMAND_CLASS; + return true; + default: + return false; + } +} +} // namespace e133 +} // namespace ola diff --git a/libs/acn/E133StatusHelperTest.cpp b/libs/acn/E133StatusHelperTest.cpp new file mode 100644 index 0000000000..058feaa42d --- /dev/null +++ b/libs/acn/E133StatusHelperTest.cpp @@ -0,0 +1,133 @@ +/* + * 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. + * + * E133StatusHelperTest.cpp + * Test fixture for the E133 Status Helper code + * Copyright (C) 2024 Peter Newman + */ + +#include +#include +#include + +#include "ola/e133/E133Enums.h" +#include "ola/e133/E133StatusHelper.h" +#include "ola/testing/TestUtils.h" + +using std::string; +using ola::e133::IntToStatusCode; +using ola::e133::IntToConnectStatusCode; +using ola::e133::IntToRPTStatusCode; +using ola::e133::RPTStatusCodeToRDMStatusCode; + +class E133StatusHelperTest: public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(E133StatusHelperTest); + CPPUNIT_TEST(testIntToStatusCode); + CPPUNIT_TEST(testIntToConnectStatusCode); + CPPUNIT_TEST(testIntToRPTStatusCode); + CPPUNIT_TEST(testRPTStatusCodeToRDMStatusCode); + CPPUNIT_TEST_SUITE_END(); + + public: + void testIntToStatusCode(); + void testIntToConnectStatusCode(); + void testIntToRPTStatusCode(); + void testRPTStatusCodeToRDMStatusCode(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(E133StatusHelperTest); + + +/* + * Test the IntToStatusCode function. + */ +void E133StatusHelperTest::testIntToStatusCode() { + ola::e133::E133StatusCode value; + OLA_ASSERT_TRUE(IntToStatusCode(0, &value)); + OLA_ASSERT_EQ(value, ola::e133::SC_E133_ACK); + OLA_ASSERT_TRUE(IntToStatusCode(1, &value)); + OLA_ASSERT_EQ(value, ola::e133::SC_E133_RDM_TIMEOUT); + OLA_ASSERT_TRUE(IntToStatusCode(9, &value)); + OLA_ASSERT_EQ(value, ola::e133::SC_E133_BROADCAST_COMPLETE); + // Update this if additional entries are added to the enum + OLA_ASSERT_FALSE(IntToStatusCode((ola::e133::SC_E133_BROADCAST_COMPLETE + 1), + &value)); + // test null status code + OLA_ASSERT_FALSE(IntToStatusCode(0, NULL)); +} + + +/* + * Test the IntToConnectStatusCode function. + */ +void E133StatusHelperTest::testIntToConnectStatusCode() { + ola::e133::E133ConnectStatusCode value; + OLA_ASSERT_TRUE(IntToConnectStatusCode(0, &value)); + OLA_ASSERT_EQ(value, ola::e133::CONNECT_OK); + OLA_ASSERT_TRUE(IntToConnectStatusCode(1, &value)); + OLA_ASSERT_EQ(value, ola::e133::CONNECT_SCOPE_MISMATCH); + OLA_ASSERT_TRUE(IntToConnectStatusCode(5, &value)); + OLA_ASSERT_EQ(value, ola::e133::CONNECT_INVALID_UID); + // Update this if additional entries are added to the enum + OLA_ASSERT_FALSE(IntToConnectStatusCode((ola::e133::CONNECT_INVALID_UID + 1), + &value)); + // test null status code + OLA_ASSERT_FALSE(IntToConnectStatusCode(0, NULL)); +} + + +/* + * Test the IntToRPTStatusCode function. + */ +void E133StatusHelperTest::testIntToRPTStatusCode() { + ola::acn::RPTStatusVector value; + OLA_ASSERT_TRUE(IntToRPTStatusCode(1, &value)); + OLA_ASSERT_EQ(value, ola::acn::VECTOR_RPT_STATUS_UNKNOWN_RPT_UID); + OLA_ASSERT_TRUE(IntToRPTStatusCode(2, &value)); + OLA_ASSERT_EQ(value, ola::acn::VECTOR_RPT_STATUS_RDM_TIMEOUT); + OLA_ASSERT_TRUE(IntToRPTStatusCode(9, &value)); + OLA_ASSERT_EQ(value, ola::acn::VECTOR_RPT_STATUS_INVALID_COMMAND_CLASS); + // Update this if additional entries are added to the enum + OLA_ASSERT_FALSE(IntToRPTStatusCode( + (ola::acn::VECTOR_RPT_STATUS_INVALID_COMMAND_CLASS + 1), &value)); + // test null status code + OLA_ASSERT_FALSE(IntToRPTStatusCode(1, NULL)); +} + + +/* + * Test the RPTStatusCodeToRDMStatusCode function. + */ +void E133StatusHelperTest::testRPTStatusCodeToRDMStatusCode() { + ola::rdm::RDMStatusCode value; + // Update this if earlier entries are added to the conversion + OLA_ASSERT_TRUE(RPTStatusCodeToRDMStatusCode( + ola::acn::VECTOR_RPT_STATUS_RDM_TIMEOUT, &value)); + OLA_ASSERT_EQ(value, ola::rdm::RDM_TIMEOUT); + OLA_ASSERT_TRUE(RPTStatusCodeToRDMStatusCode( + ola::acn::VECTOR_RPT_STATUS_RDM_INVALID_RESPONSE, &value)); + OLA_ASSERT_EQ(value, ola::rdm::RDM_INVALID_RESPONSE); + OLA_ASSERT_TRUE(RPTStatusCodeToRDMStatusCode( + ola::acn::VECTOR_RPT_STATUS_INVALID_COMMAND_CLASS, &value)); + OLA_ASSERT_EQ(value, ola::rdm::RDM_INVALID_COMMAND_CLASS); + // Because the function accepts an enum as input, the compile traps any out + // of range source entry + // Remove this once we provide suitable mapping for all statuses + OLA_ASSERT_FALSE(RPTStatusCodeToRDMStatusCode( + ola::acn::VECTOR_RPT_STATUS_UNKNOWN_RPT_UID, &value)); + // test null status code + OLA_ASSERT_FALSE(RPTStatusCodeToRDMStatusCode( + ola::acn::VECTOR_RPT_STATUS_RDM_TIMEOUT, NULL)); +} diff --git a/libs/acn/HeaderSet.h b/libs/acn/HeaderSet.h index 06a56c26aa..ae4925e1b4 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 18fa7141f5..26623b0c1f 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/LLRPHeader.h b/libs/acn/LLRPHeader.h index 40a5098c42..8b51c0d749 100644 --- a/libs/acn/LLRPHeader.h +++ b/libs/acn/LLRPHeader.h @@ -51,8 +51,8 @@ class LLRPHeader { uint32_t TransactionNumber() const { return m_transaction_number; } bool operator==(const LLRPHeader &other) const { - return m_destination_cid == other.m_destination_cid && - m_transaction_number == other.m_transaction_number; + return ((m_destination_cid == other.m_destination_cid) && + (m_transaction_number == other.m_transaction_number)); } PACK( diff --git a/libs/acn/LLRPProbeReplyInflator.h b/libs/acn/LLRPProbeReplyInflator.h index 3aab45f7b1..51f77d6cae 100644 --- a/libs/acn/LLRPProbeReplyInflator.h +++ b/libs/acn/LLRPProbeReplyInflator.h @@ -36,7 +36,7 @@ class LLRPProbeReplyInflator: public BaseInflator { public: struct LLRPProbeReply { - LLRPProbeReply(const ola::rdm::UID &_uid) + explicit LLRPProbeReply(const ola::rdm::UID &_uid) : uid(_uid) { } ola::rdm::UID uid; diff --git a/libs/acn/LLRPProbeReplyPDU.cpp b/libs/acn/LLRPProbeReplyPDU.cpp index a7822e7dc9..b0f8419028 100644 --- a/libs/acn/LLRPProbeReplyPDU.cpp +++ b/libs/acn/LLRPProbeReplyPDU.cpp @@ -35,7 +35,8 @@ using ola::rdm::UID; bool LLRPProbeReplyPDU::PackData(uint8_t *data, unsigned int *length) const { llrp_probe_reply_pdu_data pdu_data; m_target_uid.Pack(pdu_data.target_uid, sizeof(pdu_data.target_uid)); - m_hardware_address.Pack(pdu_data.hardware_address, sizeof(pdu_data.hardware_address)); + m_hardware_address.Pack(pdu_data.hardware_address, + sizeof(pdu_data.hardware_address)); pdu_data.type = HostToNetwork(static_cast(m_type)); *length = sizeof(llrp_probe_reply_pdu_data); @@ -56,6 +57,11 @@ void LLRPProbeReplyPDU::PrependPDU(ola::io::IOStack *stack, const UID &target_uid, const MACAddress &hardware_address, const LLRPComponentType type) { + if (!stack) { + OLA_WARN << "LLRPProbeReplyPDU::PrependPDU: missing stack"; + return; + } + llrp_probe_reply_pdu_data data; target_uid.Pack(data.target_uid, sizeof(data.target_uid)); hardware_address.Pack(data.hardware_address, sizeof(data.hardware_address)); diff --git a/libs/acn/LLRPProbeReplyPDUTest.cpp b/libs/acn/LLRPProbeReplyPDUTest.cpp index ae25918683..6c27667475 100644 --- a/libs/acn/LLRPProbeReplyPDUTest.cpp +++ b/libs/acn/LLRPProbeReplyPDUTest.cpp @@ -99,7 +99,8 @@ void LLRPProbeReplyPDUTest::testSimpleLLRPProbeReplyPDU() { OLA_ASSERT_DATA_EQUALS(&data[4], UID::LENGTH, buffer, sizeof(buffer)); uint8_t buffer2[MACAddress::LENGTH]; hardware_address.Pack(buffer2, sizeof(buffer2)); - OLA_ASSERT_DATA_EQUALS(&data[10], MACAddress::LENGTH, buffer2, sizeof(buffer2)); + OLA_ASSERT_DATA_EQUALS(&data[10], MACAddress::LENGTH, + buffer2, sizeof(buffer2)); // test undersized buffer bytes_used = size - 1; @@ -175,6 +176,14 @@ void LLRPProbeReplyPDUTest::testPrepend() { 0xff }; OLA_ASSERT_DATA_EQUALS(expected_data, sizeof(expected_data), buffer, length); + + // test null stack + LLRPProbeReplyPDU::PrependPDU( + NULL, + target_uid, + hardware_address, + LLRPProbeReplyPDU::LLRP_COMPONENT_TYPE_NON_RDMNET); + delete[] buffer; } } // namespace acn diff --git a/libs/acn/LLRPProbeRequestInflator.cpp b/libs/acn/LLRPProbeRequestInflator.cpp index adf7d63fdd..d160fa10fe 100644 --- a/libs/acn/LLRPProbeRequestInflator.cpp +++ b/libs/acn/LLRPProbeRequestInflator.cpp @@ -93,17 +93,22 @@ bool LLRPProbeRequestInflator::HandlePDUData(uint32_t vector, pdu_len - (sizeof(pdu_data) - sizeof(pdu_data.known_uids))); if (known_uids_size % UID::UID_SIZE != 0) { - OLA_WARN << "Got a partial known UID, received " << known_uids_size << " bytes"; + OLA_WARN << "Got a partial known UID, received " << known_uids_size + << " bytes"; return false; } memcpy(reinterpret_cast(&pdu_data), data, sizeof(pdu_data)); - OLA_DEBUG << "Probe from " << UID(pdu_data.lower_uid) << " to " << UID(pdu_data.upper_uid); + OLA_DEBUG << "Probe from " << UID(pdu_data.lower_uid) << " to " + << UID(pdu_data.upper_uid); LLRPProbeRequest request(UID(pdu_data.lower_uid), UID(pdu_data.upper_uid)); - request.client_tcp_connection_inactive = (pdu_data.filter & LLRPProbeRequestPDU::FILTER_CLIENT_TCP_CONNECTION_INACTIVE); - request.brokers_only = (pdu_data.filter & LLRPProbeRequestPDU::FILTER_BROKERS_ONLY); + request.client_tcp_connection_inactive = + (pdu_data.filter & + LLRPProbeRequestPDU::FILTER_CLIENT_TCP_CONNECTION_INACTIVE); + request.brokers_only = (pdu_data.filter & + LLRPProbeRequestPDU::FILTER_BROKERS_ONLY); unsigned int known_uids_used_size = known_uids_size; request.known_uids = UIDSet(pdu_data.known_uids, &known_uids_used_size); diff --git a/libs/acn/LLRPProbeRequestPDU.cpp b/libs/acn/LLRPProbeRequestPDU.cpp index ee8a021ec7..ecb9c9b21f 100644 --- a/libs/acn/LLRPProbeRequestPDU.cpp +++ b/libs/acn/LLRPProbeRequestPDU.cpp @@ -36,7 +36,6 @@ unsigned int LLRPProbeRequestPDU::DataSize() const { return static_cast(sizeof(llrp_probe_request_pdu_data) - sizeof(data.known_uids) + (m_known_uids.Size() * UID::LENGTH)); - } bool LLRPProbeRequestPDU::PackData(uint8_t *data, unsigned int *length) const { @@ -87,6 +86,11 @@ void LLRPProbeRequestPDU::PrependPDU(ola::io::IOStack *stack, bool client_tcp_connection_inactive, bool brokers_only, const ola::rdm::UIDSet &known_uids) { + if (!stack) { + OLA_WARN << "LLRPProbeRequestPDU::PrependPDU: missing stack"; + return; + } + llrp_probe_request_pdu_data data; lower_uid.Pack(data.lower_uid, sizeof(data.lower_uid)); upper_uid.Pack(data.upper_uid, sizeof(data.upper_uid)); diff --git a/libs/acn/LLRPProbeRequestPDUTest.cpp b/libs/acn/LLRPProbeRequestPDUTest.cpp index 88f39d587c..23e4e61a37 100644 --- a/libs/acn/LLRPProbeRequestPDUTest.cpp +++ b/libs/acn/LLRPProbeRequestPDUTest.cpp @@ -191,6 +191,15 @@ void LLRPProbeRequestPDUTest::testPrepend() { 0x56, 0x78, 0, 0, 0, 2, }; OLA_ASSERT_DATA_EQUALS(expected_data, sizeof(expected_data), buffer, length); + + // test null stack + LLRPProbeRequestPDU::PrependPDU(NULL, + UID(0x0000, 0x00000000), + UID(0xffff, 0xffffffff), + false, + false, + known_uids); + delete[] buffer; } } // namespace acn diff --git a/libs/acn/Makefile.mk b/libs/acn/Makefile.mk index a036a3f55a..de2432be19 100644 --- a/libs/acn/Makefile.mk +++ b/libs/acn/Makefile.mk @@ -1,4 +1,5 @@ COMMON_E131_CXXFLAGS = $(COMMON_CXXFLAGS) -Wconversion +COMMON_E133_CXXFLAGS = $(COMMON_CXXFLAGS) -Wconversion # pkg-config ################################################## @@ -15,6 +16,12 @@ else noinst_LTLIBRARIES += libs/acn/libolaacn.la endif +if INSTALL_E133 + lib_LTLIBRARIES += libs/acn/libolae133core.la +else + noinst_LTLIBRARIES += libs/acn/libolae133core.la +endif + # libolaacn.la libs_acn_libolaacn_la_SOURCES = \ libs/acn/CID.cpp \ @@ -51,16 +58,78 @@ libs_acn_libolae131core_la_SOURCES = \ libs/acn/E131PDU.h \ libs/acn/E131Sender.cpp \ libs/acn/E131Sender.h \ + libs/acn/HeaderSet.h \ + libs/acn/PDU.cpp \ + libs/acn/PDU.h \ + libs/acn/PDUTestCommon.h \ + libs/acn/PreamblePacker.cpp \ + libs/acn/PreamblePacker.h \ + libs/acn/RootHeader.h \ + libs/acn/RootInflator.cpp \ + libs/acn/RootInflator.h \ + libs/acn/RootPDU.cpp \ + libs/acn/RootPDU.h \ + libs/acn/RootSender.cpp \ + libs/acn/RootSender.h \ + libs/acn/TCPTransport.cpp \ + libs/acn/TCPTransport.h \ + libs/acn/Transport.h \ + libs/acn/TransportHeader.h \ + libs/acn/UDPTransport.cpp \ + libs/acn/UDPTransport.h + +libs_acn_libolae131core_la_CXXFLAGS = \ + $(COMMON_E131_CXXFLAGS) $(uuid_CFLAGS) +libs_acn_libolae131core_la_LIBADD = $(uuid_LIBS) \ + common/libolacommon.la \ + libs/acn/libolaacn.la + +# libolae133core.la +# This needs to be after libolaacn.la and libolae131core.la since it depends on +# them. Otherwise it breaks the freeBSD build +libs_acn_libolae133core_la_SOURCES = \ + libs/acn/BrokerClientAddInflator.h \ + libs/acn/BrokerClientEntryChangeInflator.h \ + libs/acn/BrokerClientEntryHeader.h \ + libs/acn/BrokerClientEntryPDU.cpp \ + libs/acn/BrokerClientEntryPDU.h \ + libs/acn/BrokerClientEntryRPTInflator.cpp \ + libs/acn/BrokerClientEntryRPTInflator.h \ + libs/acn/BrokerClientEntryRPTPDU.cpp \ + libs/acn/BrokerClientEntryRPTPDU.h \ + libs/acn/BrokerClientEntryUpdateInflator.h \ + libs/acn/BrokerClientRemoveInflator.h \ + libs/acn/BrokerConnectedClientListInflator.cpp \ + libs/acn/BrokerConnectedClientListInflator.h \ + libs/acn/BrokerConnectPDU.cpp \ + libs/acn/BrokerConnectPDU.h \ + libs/acn/BrokerConnectReplyInflator.cpp \ + libs/acn/BrokerConnectReplyInflator.h \ + libs/acn/BrokerFetchClientListPDU.cpp \ + libs/acn/BrokerFetchClientListPDU.h \ + libs/acn/BrokerHeader.h \ + libs/acn/BrokerInflator.h \ + libs/acn/BrokerManager.cpp \ + libs/acn/BrokerManagerImpl.cpp \ + libs/acn/BrokerManagerImpl.h \ + libs/acn/BrokerNullInflator.h \ + libs/acn/BrokerNullPDU.cpp \ + libs/acn/BrokerNullPDU.h \ + libs/acn/BrokerPDU.cpp \ + libs/acn/BrokerPDU.h \ libs/acn/E133Header.h \ + libs/acn/E133HealthCheckedConnection.cpp \ + libs/acn/E133HealthCheckedConnection.h \ + libs/acn/E133Helper.cpp \ libs/acn/E133Inflator.cpp \ libs/acn/E133Inflator.h \ libs/acn/E133PDU.cpp \ libs/acn/E133PDU.h \ + libs/acn/E133StatusHelper.cpp \ libs/acn/E133StatusInflator.cpp \ libs/acn/E133StatusInflator.h \ libs/acn/E133StatusPDU.cpp \ libs/acn/E133StatusPDU.h \ - libs/acn/HeaderSet.h \ libs/acn/LLRPHeader.h \ libs/acn/LLRPInflator.cpp \ libs/acn/LLRPInflator.h \ @@ -74,34 +143,29 @@ libs_acn_libolae131core_la_SOURCES = \ libs/acn/LLRPProbeRequestPDU.h \ libs/acn/LLRPPDU.cpp \ libs/acn/LLRPPDU.h \ - libs/acn/PDU.cpp \ - libs/acn/PDU.h \ - libs/acn/PDUTestCommon.h \ - libs/acn/PreamblePacker.cpp \ - libs/acn/PreamblePacker.h \ + libs/acn/MessageBuilder.cpp \ libs/acn/RDMInflator.cpp \ libs/acn/RDMInflator.h \ libs/acn/RDMPDU.cpp \ libs/acn/RDMPDU.h \ - libs/acn/RootHeader.h \ - libs/acn/RootInflator.cpp \ - libs/acn/RootInflator.h \ - libs/acn/RootPDU.cpp \ - libs/acn/RootPDU.h \ - libs/acn/RootSender.cpp \ - libs/acn/RootSender.h \ - libs/acn/TCPTransport.cpp \ - libs/acn/TCPTransport.h \ - libs/acn/Transport.h \ - libs/acn/TransportHeader.h \ - libs/acn/UDPTransport.cpp \ - libs/acn/UDPTransport.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/RPTStatusInflator.cpp \ + libs/acn/RPTStatusInflator.h -libs_acn_libolae131core_la_CXXFLAGS = \ - $(COMMON_E131_CXXFLAGS) $(uuid_CFLAGS) -libs_acn_libolae131core_la_LIBADD = $(uuid_LIBS) \ +libs_acn_libolae133core_la_CXXFLAGS = \ + $(COMMON_E133_CXXFLAGS) $(uuid_CFLAGS) +libs_acn_libolae133core_la_LIBADD = $(uuid_LIBS) \ common/libolacommon.la \ - libs/acn/libolaacn.la + libs/acn/libolaacn.la \ + libs/acn/libolae131core.la # PROGRAMS ################################################## @@ -145,13 +209,27 @@ libs_acn_E131Tester_LDADD = \ libs/acn/libolae131core.la \ $(COMMON_TESTING_LIBS) +# libs/acn/BrokerClientEntryRPTInflatorTest.cpp +# libs/acn/BrokerInflatorTest.cpp libs_acn_E133Tester_SOURCES = \ + libs/acn/BrokerClientEntryPDUTest.cpp \ + libs/acn/BrokerClientEntryRPTPDUTest.cpp \ + libs/acn/BrokerConnectPDUTest.cpp \ + libs/acn/BrokerFetchClientListPDUTest.cpp \ + libs/acn/BrokerNullInflatorTest.cpp \ + libs/acn/BrokerNullPDUTest.cpp \ + libs/acn/BrokerPDUTest.cpp \ + libs/acn/E133StatusHelperTest.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 \ + libs/acn/libolae133core.la \ $(COMMON_TESTING_LIBS) libs_acn_LLRPTester_SOURCES = \ @@ -162,6 +240,7 @@ libs_acn_LLRPTester_SOURCES = \ libs_acn_LLRPTester_CPPFLAGS = $(COMMON_TESTING_FLAGS) libs_acn_LLRPTester_LDADD = \ libs/acn/libolae131core.la \ + libs/acn/libolae133core.la \ $(COMMON_TESTING_LIBS) libs_acn_TransportTester_SOURCES = \ diff --git a/tools/e133/MessageBuilder.cpp b/libs/acn/MessageBuilder.cpp similarity index 64% rename from tools/e133/MessageBuilder.cpp rename to libs/acn/MessageBuilder.cpp index a192b45bf0..0216cfed64 100644 --- a/tools/e133/MessageBuilder.cpp +++ b/libs/acn/MessageBuilder.cpp @@ -24,10 +24,15 @@ #include "ola/acn/CID.h" #include "ola/e133/MessageBuilder.h" #include "ola/io/IOStack.h" +#include "ola/rdm/RDMCommandSerializer.h" +#include "ola/rdm/UID.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 +41,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 +65,41 @@ 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) { + // TODO(Peter): Potentially need some future way to handle controller + // messages here + ola::rdm::UID rpt_destination_uid = request->DestinationUID(); + if (rpt_destination_uid.IsBroadcast()) { + if (rpt_destination_uid.IsVendorcast()) { + rpt_destination_uid = ola::rdm::UID::RPTVendorcastAddressDevices( + rpt_destination_uid); + } else { + rpt_destination_uid = ola::rdm::UID::RPTAllDevices(); + } + if (destination_endpoint_id != NULL_ENDPOINT) { + // TODO(Peter): Should we handle the reserved endpoints now? + destination_endpoint_id = BROADCAST_ENDPOINT; + } + } + 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, + rpt_destination_uid, 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 +109,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. diff --git a/libs/acn/PDU.cpp b/libs/acn/PDU.cpp index 0f256eca13..0fa3ef744e 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 2be285dead..18193c2044 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 a5d86cc5d1..cdeec18320 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/PreamblePacker.cpp b/libs/acn/PreamblePacker.cpp index 1803b38e69..520b064f3c 100644 --- a/libs/acn/PreamblePacker.cpp +++ b/libs/acn/PreamblePacker.cpp @@ -47,8 +47,7 @@ const uint8_t PreamblePacker::ACN_HEADER[] = { const unsigned int PreamblePacker::ACN_HEADER_SIZE = sizeof(ACN_HEADER); const uint8_t PreamblePacker::TCP_ACN_HEADER[] = { - 0x00, 0x14, // preamble size - 0x00, 0x00, // post amble size + // No pre or post amble size for TCP 0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00 diff --git a/libs/acn/RDMInflator.cpp b/libs/acn/RDMInflator.cpp index a4e32166e1..9dba40ba45 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 4c37cf6c0c..4d4e5517da 100644 --- a/libs/acn/RDMInflator.h +++ b/libs/acn/RDMInflator.h @@ -34,45 +34,45 @@ class RDMInflator: public BaseInflator { friend class RDMInflatorTest; public: - // These are pointers so the callers don't have to pull in all the headers. - typedef ola::Callback3 RDMMessageHandler; + // These are pointers so the callers don't have to pull in all the headers. + typedef ola::Callback3 RDMMessageHandler; - typedef ola::Callback2 GenericRDMMessageHandler; + typedef ola::Callback2 GenericRDMMessageHandler; - RDMInflator(unsigned int vector = ola::acn::VECTOR_FRAMING_RDMNET); - ~RDMInflator() {} + // 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() {} - uint32_t Id() const { return m_vector; } + uint32_t Id() const { return m_vector; } - void SetRDMHandler(RDMMessageHandler *handler); - void SetGenericRDMHandler(GenericRDMMessageHandler *handler); - - static const unsigned int VECTOR_RDMNET_DATA = 0xcc; + void SetRDMHandler(RDMMessageHandler *handler); + void SetGenericRDMHandler(GenericRDMMessageHandler *handler); protected: - bool DecodeHeader(HeaderSet *headers, - const uint8_t *data, - unsigned int len, - unsigned int *bytes_used); + bool DecodeHeader(HeaderSet *headers, + const uint8_t *data, + unsigned int len, + unsigned int *bytes_used); - void ResetHeaderField() {} // namespace noop + void ResetHeaderField() {} // namespace noop - virtual bool HandlePDUData(uint32_t vector, - const HeaderSet &headers, - const uint8_t *data, - unsigned int pdu_len); + virtual bool HandlePDUData(uint32_t vector, + const HeaderSet &headers, + const uint8_t *data, + unsigned int pdu_len); private: - std::auto_ptr m_rdm_handler; - std::auto_ptr m_generic_rdm_handler; - unsigned int m_vector; + std::auto_ptr m_rdm_handler; + std::auto_ptr m_generic_rdm_handler; + unsigned int m_vector; }; } // namespace acn } // namespace ola diff --git a/libs/acn/RDMPDU.cpp b/libs/acn/RDMPDU.cpp index cab7d20a5c..a816592680 100644 --- a/libs/acn/RDMPDU.cpp +++ b/libs/acn/RDMPDU.cpp @@ -20,8 +20,8 @@ #include "libs/acn/RDMPDU.h" -#include -#include +#include "ola/network/NetworkUtils.h" +#include "ola/rdm/RDMPacket.h" namespace ola { namespace acn { @@ -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/RDMPDU.h b/libs/acn/RDMPDU.h index c51d9a140f..b81afb623a 100644 --- a/libs/acn/RDMPDU.h +++ b/libs/acn/RDMPDU.h @@ -22,11 +22,11 @@ #define LIBS_ACN_RDMPDU_H_ #include -#include -#include -#include #include "libs/acn/PDU.h" +#include "ola/io/ByteString.h" +#include "ola/io/IOStack.h" +#include "ola/rdm/RDMPacket.h" namespace ola { namespace acn { diff --git a/libs/acn/RDMPDUTest.cpp b/libs/acn/RDMPDUTest.cpp index ff6489d0fb..f11160f6ba 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 0000000000..00a26bc349 --- /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 0000000000..5d9cde6f50 --- /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 0000000000..1fd4eedaaf --- /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 0000000000..f349e53eef --- /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 0000000000..532fb45a54 --- /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 0000000000..3a3330e023 --- /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 0000000000..c359855402 --- /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 0000000000..422f6a2152 --- /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 0000000000..ec2cf38de3 --- /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 0000000000..db61797554 --- /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 0000000000..97a490cd44 --- /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 0000000000..f40b30b7ac --- /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/libs/acn/RPTStatusInflator.cpp b/libs/acn/RPTStatusInflator.cpp new file mode 100644 index 0000000000..83cb2d9eb6 --- /dev/null +++ b/libs/acn/RPTStatusInflator.cpp @@ -0,0 +1,94 @@ +/* + * 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. + * + * RPTStatusInflator.cpp + * The Inflator for the RPT Status PDUs + * Copyright (C) 2024 Peter Newman + */ + +#include +#include +#include "ola/Logging.h" +#include "ola/e133/E133StatusHelper.h" +#include "libs/acn/RPTStatusInflator.h" + +namespace ola { +namespace acn { + +using std::string; + +/** + * Create a new RPT Status inflator + */ +RPTStatusInflator::RPTStatusInflator() + : BaseInflator(PDU::TWO_BYTES) { +} + +/** + * Set an RPTStatusHandler to run when receiving an RPT Status message. + * @param handler the callback to invoke when there is an RPT status message. + */ +void RPTStatusInflator::SetRPTStatusHandler(RPTStatusHandler *handler) { + m_rpt_status_handler.reset(handler); +} + + +/* + * Decode the RPT Status 'header', which is 0 bytes in length. + * @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 RPTStatusInflator::DecodeHeader(HeaderSet *, + const uint8_t*, + unsigned int, + unsigned int *bytes_used) { + *bytes_used = 0; + return true; +} + + +/* + * Handle an RPT Status PDU for E1.33. + */ +bool RPTStatusInflator::HandlePDUData(uint32_t vector, + OLA_UNUSED const HeaderSet &headers, + OLA_UNUSED const uint8_t *data, + OLA_UNUSED unsigned int pdu_len) { + ola::acn::RPTStatusVector status_vector; + if (!ola::e133::IntToRPTStatusCode(static_cast(vector), + &status_vector)) { + OLA_WARN << "Unknown RPT status message vector was " << vector; + return true; + } else { + OLA_INFO << "RPT status message vector was " + << ola::e133::RPTStatusCodeToString(status_vector); + } + // TODO(Peter): Handle the optional string message for the vectors that can + // have it/alert for those that can't if they do + + if (m_rpt_status_handler.get()) { + E133Header e133_header = headers.GetE133Header(); + + m_rpt_status_handler->Run(&headers.GetTransportHeader(), &e133_header, + status_vector, ""); + } else { + OLA_WARN << "No RPT Status handler defined!"; + } + return true; +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/RPTStatusInflator.h b/libs/acn/RPTStatusInflator.h new file mode 100644 index 0000000000..85c0946030 --- /dev/null +++ b/libs/acn/RPTStatusInflator.h @@ -0,0 +1,72 @@ +/* + * 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. + * + * RPTStatusInflator.h + * Interface for the RPTStatusInflator class. + * Copyright (C) 2024 Peter Newman + */ + +#ifndef LIBS_ACN_RPTSTATUSINFLATOR_H_ +#define LIBS_ACN_RPTSTATUSINFLATOR_H_ + +#include +#include "ola/Callback.h" +#include "ola/acn/ACNVectors.h" +#include "libs/acn/BaseInflator.h" +#include "libs/acn/TransportHeader.h" +#include "libs/acn/E133Header.h" + +namespace ola { +namespace acn { + +class RPTStatusInflator: public BaseInflator { + friend class RPTStatusInflatorTest; + + public: + // These are pointers so the callers don't have to pull in all the headers. + // TODO(Peter): Should this be the E133Header or the RPTHeader? + typedef ola::Callback4 RPTStatusHandler; + + RPTStatusInflator(); + ~RPTStatusInflator() {} + + uint32_t Id() const { return ola::acn::VECTOR_RPT_STATUS; } + + void SetRPTStatusHandler(RPTStatusHandler *handler); + + protected: + bool DecodeHeader(HeaderSet *headers, + const uint8_t *data, + unsigned int len, + unsigned int *bytes_used); + + void ResetHeaderField() {} // namespace noop + + virtual bool HandlePDUData(uint32_t vector, + const HeaderSet &headers, + const uint8_t *data, + unsigned int pdu_len); + + private: + std::auto_ptr m_rpt_status_handler; +}; +} // namespace acn +} // namespace ola +#endif // LIBS_ACN_RPTSTATUSINFLATOR_H_ diff --git a/libs/acn/RootPDU.cpp b/libs/acn/RootPDU.cpp index cec65ed7bb..e5bf9c0dad 100644 --- a/libs/acn/RootPDU.cpp +++ b/libs/acn/RootPDU.cpp @@ -86,12 +86,16 @@ void RootPDU::SetBlock(const PDUBlock *block) { /* * Prepend a Root Layer flags, length, vector & header */ -void RootPDU::PrependPDU(IOStack *stack, uint32_t vector, const CID &cid, bool force_length_flag) { +void RootPDU::PrependPDU(IOStack *stack, + uint32_t vector, + const CID &cid, + bool force_length_flag) { cid.Write(stack); vector = HostToNetwork(vector); stack->Write(reinterpret_cast(&vector), sizeof(vector)); - PrependFlagsAndLength(stack, VFLAG_MASK | HFLAG_MASK | DFLAG_MASK, force_length_flag); + PrependFlagsAndLength(stack, VFLAG_MASK | HFLAG_MASK | DFLAG_MASK, + force_length_flag); } } // namespace acn } // namespace ola diff --git a/libs/acn/RootPDU.h b/libs/acn/RootPDU.h index 6d66ebc178..b46d9e601c 100644 --- a/libs/acn/RootPDU.h +++ b/libs/acn/RootPDU.h @@ -59,8 +59,10 @@ class RootPDU: public PDU { const ola::acn::CID &Cid(const ola::acn::CID &cid) { return m_cid = cid; } void SetBlock(const PDUBlock *block); - static void PrependPDU(ola::io::IOStack *stack, uint32_t vector, - const ola::acn::CID &cid, bool force_length_flag = false); + static void PrependPDU(ola::io::IOStack *stack, + uint32_t vector, + const ola::acn::CID &cid, + bool force_length_flag = false); private: ola::acn::CID m_cid; diff --git a/libs/acn/RootSender.h b/libs/acn/RootSender.h index a09bbf6199..8f6f237687 100644 --- a/libs/acn/RootSender.h +++ b/libs/acn/RootSender.h @@ -32,7 +32,8 @@ namespace acn { class RootSender { public: - explicit RootSender(const ola::acn::CID &cid, bool force_length_flag = false); + explicit RootSender(const ola::acn::CID &cid, + bool force_length_flag = false); ~RootSender() {} // Convenience method to encapsulate & send a single PDU diff --git a/libs/acn/TCPTransport.cpp b/libs/acn/TCPTransport.cpp index 5890f0c0ff..4060df25fa 100644 --- a/libs/acn/TCPTransport.cpp +++ b/libs/acn/TCPTransport.cpp @@ -31,8 +31,7 @@ namespace ola { namespace acn { const uint8_t ACN_HEADER[] = { - 0x00, 0x14, // preamble size - 0x00, 0x00, // post amble size + // No pre or post amble size for TCP 0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00 diff --git a/man/ola_set_dmx.1 b/man/ola_set_dmx.1 index 0de61b95f7..89f95379bb 100644 --- a/man/ola_set_dmx.1 +++ b/man/ola_set_dmx.1 @@ -4,7 +4,7 @@ ola_set_dmx \- Sets the DMX values for a universe. .SH SYNOPSIS .B ola_set_dmx -\fI--universe --dmx \fR +\fI--universe [ --dmx ] [ --blackout ]\fR .SH DESCRIPTION Sets the DMX values for a universe. .TP @@ -15,3 +15,7 @@ Display this help message and exit. .TP \fB\-d\fR, \fB\-\-dmx\fR Comma separated DMX values, e.g. 0,255,128 sets first channel to 0, second channel to 255 and third channel to 128. +.HP +\fB\-b\fR, \fB\-\-blackout\fR +Send a universe to blackout instead. +.TP diff --git a/olad/DynamicPluginLoader.cpp b/olad/DynamicPluginLoader.cpp index d5b11e60ec..233df1eb69 100644 --- a/olad/DynamicPluginLoader.cpp +++ b/olad/DynamicPluginLoader.cpp @@ -39,6 +39,10 @@ #include "plugins/e131/E131Plugin.h" #endif // USE_E131 +#ifdef USE_E133 +#include "plugins/e133/E133Plugin.h" +#endif // USE_E133 + #ifdef USE_ESPNET #include "plugins/espnet/EspNetPlugin.h" #endif // USE_ESPNET @@ -159,6 +163,10 @@ void DynamicPluginLoader::PopulatePlugins() { m_plugins.push_back(new ola::plugin::e131::E131Plugin(m_plugin_adaptor)); #endif // USE_E131 +#ifdef USE_E133 + m_plugins.push_back(new ola::plugin::e133::E133Plugin(m_plugin_adaptor)); +#endif // USE_E133 + #ifdef USE_ESPNET m_plugins.push_back(new ola::plugin::espnet::EspNetPlugin(m_plugin_adaptor)); #endif // USE_ESPNET diff --git a/olad/OlaServer.cpp b/olad/OlaServer.cpp index 2921a6f996..1050a0bc69 100644 --- a/olad/OlaServer.cpp +++ b/olad/OlaServer.cpp @@ -225,7 +225,7 @@ bool OlaServer::Init() { auto_ptr plugin_adaptor( new PluginAdaptor(device_manager.get(), m_ss, m_export_map, m_preferences_factory, port_broker.get(), - &m_instance_name)); + &m_instance_name, &m_default_uid)); auto_ptr plugin_manager( new PluginManager(m_plugin_loaders, plugin_adaptor.get())); diff --git a/olad/PluginManagerTest.cpp b/olad/PluginManagerTest.cpp index a769f66926..2045547555 100644 --- a/olad/PluginManagerTest.cpp +++ b/olad/PluginManagerTest.cpp @@ -103,7 +103,7 @@ class MockLoader: public ola::PluginLoader { */ void PluginManagerTest::testPluginManager() { ola::MemoryPreferencesFactory factory; - ola::PluginAdaptor adaptor(NULL, NULL, NULL, &factory, NULL, NULL); + ola::PluginAdaptor adaptor(NULL, NULL, NULL, &factory, NULL, NULL, NULL); TestMockPlugin plugin1(&adaptor, ola::OLA_PLUGIN_ARTNET); TestMockPlugin plugin2(&adaptor, ola::OLA_PLUGIN_ESPNET, false); @@ -133,7 +133,7 @@ void PluginManagerTest::testPluginManager() { */ void PluginManagerTest::testConflictingPlugins() { ola::MemoryPreferencesFactory factory; - ola::PluginAdaptor adaptor(NULL, NULL, NULL, &factory, NULL, NULL); + ola::PluginAdaptor adaptor(NULL, NULL, NULL, &factory, NULL, NULL, NULL); set conflict_set1, conflict_set2, conflict_set3; conflict_set1.insert(ola::OLA_PLUGIN_ARTNET); diff --git a/olad/plugin_api/PluginAdaptor.cpp b/olad/plugin_api/PluginAdaptor.cpp index da50565cf9..7061050590 100644 --- a/olad/plugin_api/PluginAdaptor.cpp +++ b/olad/plugin_api/PluginAdaptor.cpp @@ -37,13 +37,15 @@ PluginAdaptor::PluginAdaptor(DeviceManager *device_manager, ExportMap *export_map, PreferencesFactory *preferences_factory, PortBrokerInterface *port_broker, - const std::string *instance_name): + const std::string *instance_name, + const ola::rdm::UID *default_uid): m_device_manager(device_manager), m_ss(select_server), m_export_map(export_map), m_preferences_factory(preferences_factory), m_port_broker(port_broker), - m_instance_name(instance_name) { + m_instance_name(instance_name), + m_default_uid(default_uid) { } bool PluginAdaptor::AddReadDescriptor( @@ -136,4 +138,12 @@ const std::string PluginAdaptor::InstanceName() const { return ""; } } + +const ola::rdm::UID PluginAdaptor::DefaultUID() const { + if (m_default_uid) { + return *m_default_uid; + } else { + return ola::rdm::UID(0, 0); + } +} } // namespace ola diff --git a/olad/plugin_api/PortTest.cpp b/olad/plugin_api/PortTest.cpp index f7379507c5..42849d71d3 100644 --- a/olad/plugin_api/PortTest.cpp +++ b/olad/plugin_api/PortTest.cpp @@ -91,7 +91,7 @@ void PortTest::testInputPortPriorities() { MockDevice device(NULL, "foo"); TimeStamp time_stamp; MockSelectServer ss(&time_stamp); - ola::PluginAdaptor plugin_adaptor(NULL, &ss, NULL, NULL, NULL, NULL); + ola::PluginAdaptor plugin_adaptor(NULL, &ss, NULL, NULL, NULL, NULL, NULL); // This port operates in static priority mode TestMockInputPort input_port(&device, 1, &plugin_adaptor); port_manager.PatchPort(&input_port, universe_id); diff --git a/olad/plugin_api/Preferences.cpp b/olad/plugin_api/Preferences.cpp index a2378f0a3d..32441b40d4 100644 --- a/olad/plugin_api/Preferences.cpp +++ b/olad/plugin_api/Preferences.cpp @@ -53,6 +53,7 @@ #include "ola/StringUtils.h" #include "ola/file/Util.h" #include "ola/network/IPV4Address.h" +#include "ola/network/IPV6Address.h" #include "ola/stl/STLUtils.h" #include "ola/thread/Thread.h" @@ -186,6 +187,34 @@ bool IPv4Validator::IsValid(const string &value) const { } +bool IPv6Validator::IsValid(const string &value) const { + if (value.empty()) { + return m_empty_ok; + } + + /*vector tokens; + // TODO(Peter): Deal with stuff like ::ffff:1.2.3.4 + // Maybe just fall back to the IPv6 string parsing for now...? + StringSplit(value, &tokens, ":"); + if (tokens.size() != ola::network::IPV6Address::LENGTH) { + return false; + } + + for (unsigned int i = 0 ; i < 4; i++) { + unsigned int octet; + if (!StringToInt(tokens[i], &octet)) { + return false; + } + if (octet > UINT8_MAX) { + return false; + } + } + return true;*/ + ola::network::IPV6Address validator_address; + return ola::network::IPV6Address::FromString(value, &validator_address); +} + + // Prefs Factory //----------------------------------------------------------------------------- diff --git a/olad/plugin_api/PreferencesTest.cpp b/olad/plugin_api/PreferencesTest.cpp index 9064444e19..812863c57e 100644 --- a/olad/plugin_api/PreferencesTest.cpp +++ b/olad/plugin_api/PreferencesTest.cpp @@ -40,6 +40,7 @@ using ola::Preferences; using ola::SetValidator; using ola::StringValidator; using ola::IPv4Validator; +using ola::IPv6Validator; using std::string; using std::vector; @@ -131,6 +132,41 @@ void PreferencesTest::testValidators() { IPv4Validator ipv4_validator2(false); // empty not ok OLA_ASSERT_FALSE(ipv4_validator2.IsValid("")); + OLA_ASSERT(ipv4_validator2.IsValid("1.2.3.4")); + OLA_ASSERT(ipv4_validator2.IsValid("10.0.255.1")); + OLA_ASSERT_FALSE(ipv4_validator2.IsValid("foo")); + OLA_ASSERT_FALSE(ipv4_validator2.IsValid("1.2.3")); + OLA_ASSERT_FALSE(ipv4_validator2.IsValid("1.2.3.4.5")); + OLA_ASSERT_FALSE(ipv4_validator2.IsValid("1.f00.3.4")); + + IPv6Validator ipv6_validator; // empty ok + OLA_ASSERT(ipv6_validator.IsValid("")); + OLA_ASSERT(ipv6_validator.IsValid("2001:db8:1234:5678:90ab:cdef:feed:face")); + OLA_ASSERT(ipv6_validator.IsValid("::ffff:1.2.3.4")); + OLA_ASSERT(ipv6_validator.IsValid("::ffff:10.0.255.1")); + OLA_ASSERT_FALSE(ipv6_validator.IsValid("foo")); + OLA_ASSERT_FALSE(ipv6_validator.IsValid( + "2001:db8:1234:5678:90ab:cdef:feed:face:0000")); + OLA_ASSERT_FALSE(ipv6_validator.IsValid( + "2001:db8:1234:5678:90ab:cdef:feed:gggg")); + OLA_ASSERT_FALSE(ipv6_validator.IsValid("::ffff:1.2.3")); + OLA_ASSERT_FALSE(ipv6_validator.IsValid("::ffff:1.2.3.4.5")); + OLA_ASSERT_FALSE(ipv6_validator.IsValid("::ffff:1.f00.3.4")); + + IPv6Validator ipv6_validator2(false); // empty not ok + OLA_ASSERT_FALSE(ipv6_validator2.IsValid("")); + OLA_ASSERT(ipv6_validator2.IsValid( + "2001:db8:1234:5678:90ab:cdef:feed:face")); + OLA_ASSERT(ipv6_validator2.IsValid("::ffff:1.2.3.4")); + OLA_ASSERT(ipv6_validator2.IsValid("::ffff:10.0.255.1")); + OLA_ASSERT_FALSE(ipv6_validator2.IsValid("foo")); + OLA_ASSERT_FALSE(ipv6_validator2.IsValid( + "2001:db8:1234:5678:90ab:cdef:feed:face:0000")); + OLA_ASSERT_FALSE(ipv6_validator2.IsValid( + "2001:db8:1234:5678:90ab:cdef:feed:gggg")); + OLA_ASSERT_FALSE(ipv6_validator2.IsValid("::ffff:1.2.3")); + OLA_ASSERT_FALSE(ipv6_validator2.IsValid("::ffff:1.2.3.4.5")); + OLA_ASSERT_FALSE(ipv6_validator2.IsValid("::ffff:1.f00.3.4")); } diff --git a/olad/plugin_api/UniverseTest.cpp b/olad/plugin_api/UniverseTest.cpp index 7b5d2d5b5b..69f0904fd7 100644 --- a/olad/plugin_api/UniverseTest.cpp +++ b/olad/plugin_api/UniverseTest.cpp @@ -241,7 +241,7 @@ void UniverseTest::testReceiveDmx() { ola::PortManager port_manager(m_store, &broker); TimeStamp time_stamp; MockSelectServer ss(&time_stamp); - ola::PluginAdaptor plugin_adaptor(NULL, &ss, NULL, NULL, NULL, NULL); + ola::PluginAdaptor plugin_adaptor(NULL, &ss, NULL, NULL, NULL, NULL, NULL); MockDevice device(NULL, "foo"); TestMockInputPort port(&device, 1, &plugin_adaptor); // input port @@ -366,7 +366,7 @@ void UniverseTest::testLtpMerging() { TimeStamp time_stamp; MockSelectServer ss(&time_stamp); - ola::PluginAdaptor plugin_adaptor(NULL, &ss, NULL, NULL, NULL, NULL); + ola::PluginAdaptor plugin_adaptor(NULL, &ss, NULL, NULL, NULL, NULL, NULL); MockDevice device(NULL, "foo"); MockDevice device2(NULL, "bar"); TestMockInputPort port(&device, 1, &plugin_adaptor); // input port @@ -446,7 +446,7 @@ void UniverseTest::testHtpMerging() { TimeStamp time_stamp; MockSelectServer ss(&time_stamp); - ola::PluginAdaptor plugin_adaptor(NULL, &ss, NULL, NULL, NULL, NULL); + ola::PluginAdaptor plugin_adaptor(NULL, &ss, NULL, NULL, NULL, NULL, NULL); MockDevice device(NULL, "foo"); MockDevice device2(NULL, "bar"); TestMockInputPort port(&device, 1, &plugin_adaptor); // input port diff --git a/olad/www/mobile.html b/olad/www/mobile.html index d52d754af1..2e32fc3d00 100644 --- a/olad/www/mobile.html +++ b/olad/www/mobile.html @@ -463,7 +463,7 @@ diff --git a/olad/www/ola.html b/olad/www/ola.html index 757131aea8..7dfc34187a 100644 --- a/olad/www/ola.html +++ b/olad/www/ola.html @@ -1035,7 +1035,7 @@

diff --git a/plugins/Makefile.mk b/plugins/Makefile.mk index b298128a1e..67e3e56ff9 100644 --- a/plugins/Makefile.mk +++ b/plugins/Makefile.mk @@ -23,6 +23,7 @@ if !USING_WIN32 include plugins/usbpro/Makefile.mk include plugins/dmx4linux/Makefile.mk include plugins/e131/Makefile.mk +include plugins/e133/Makefile.mk include plugins/uartdmx/Makefile.mk endif diff --git a/plugins/artnet/ArtNetNodeTest.cpp b/plugins/artnet/ArtNetNodeTest.cpp index 65e5ecb5c2..85954801a7 100644 --- a/plugins/artnet/ArtNetNodeTest.cpp +++ b/plugins/artnet/ArtNetNodeTest.cpp @@ -211,7 +211,7 @@ class ArtNetNodeTest: public CppUnit::TestFixture { // This sends a tod data so 7s70:00000000 is insert into the tod void PopulateTod() { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t art_tod[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x81, @@ -646,7 +646,7 @@ void ArtNetNodeTest::testBroadcastSendDMX() { m_socket->SetDiscardMode(false); { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t DMX_MESSAGE[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x50, @@ -666,7 +666,7 @@ void ArtNetNodeTest::testBroadcastSendDMX() { // send an odd sized dmx frame, we should pad this to a multiple of two { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t DMX_MESSAGE2[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x50, @@ -684,14 +684,14 @@ void ArtNetNodeTest::testBroadcastSendDMX() { } { // attempt to send on a invalid port - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); DmxBuffer dmx; dmx.SetFromString("0,1,2,3,4"); OLA_ASSERT_FALSE(node.SendDMX(4, dmx)); } { // attempt to send an empty frame - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); DmxBuffer empty_buffer; OLA_ASSERT(node.SendDMX(m_port_id, empty_buffer)); } @@ -717,7 +717,7 @@ void ArtNetNodeTest::testBroadcastSendDMXZeroUniverse() { m_socket->SetDiscardMode(false); { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t DMX_MESSAGE[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x50, @@ -781,7 +781,7 @@ void ArtNetNodeTest::testBroadcastSendDMXZeroUniverse() { } { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t DMX_MESSAGE[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x50, @@ -818,7 +818,7 @@ void ArtNetNodeTest::testLimitedBroadcastDMX() { m_socket->SetDiscardMode(false); { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t DMX_MESSAGE[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x50, @@ -861,7 +861,7 @@ void ArtNetNodeTest::testNonBroadcastSendDMX() { vector node_addresses; { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t poll_reply_message[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x21, @@ -926,7 +926,7 @@ void ArtNetNodeTest::testNonBroadcastSendDMX() { // add another peer { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t poll_reply_message2[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x21, @@ -980,7 +980,7 @@ void ArtNetNodeTest::testNonBroadcastSendDMX() { // send another DMX frame, this should get unicast twice { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t DMX_MESSAGE2[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x50, @@ -999,7 +999,7 @@ void ArtNetNodeTest::testNonBroadcastSendDMX() { // adjust the broadcast threshold { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); node.SetBroadcastThreshold(2); // send another DMX frame, this should get broadcast @@ -1050,7 +1050,7 @@ void ArtNetNodeTest::testReceiveDMX() { // 'receive' a DMX message { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); OLA_ASSERT_FALSE(m_got_dmx); ReceiveFromPeer(DMX_MESSAGE, sizeof(DMX_MESSAGE), peer_ip); OLA_ASSERT(m_got_dmx); @@ -1059,7 +1059,7 @@ void ArtNetNodeTest::testReceiveDMX() { // send a second frame { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); uint8_t DMX_MESSAGE2[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x50, @@ -1079,7 +1079,7 @@ void ArtNetNodeTest::testReceiveDMX() { // advance the clock by more than the merge timeout (10s) { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); m_clock.AdvanceTime(11, 0); // send another message, but first update the seq # @@ -1128,7 +1128,7 @@ void ArtNetNodeTest::testReceiveDMXZeroUniverse() { 0, 6, // dmx length 0, 1, 2, 3, 4, 5 }; - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); OLA_ASSERT_FALSE(m_got_dmx); ReceiveFromPeer(DMX_MESSAGE, sizeof(DMX_MESSAGE), peer_ip); OLA_ASSERT(m_got_dmx); @@ -1158,7 +1158,7 @@ void ArtNetNodeTest::testReceiveDMXZeroUniverse() { 0, 4, // dmx length 10, 11, 12, 13 }; - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); OLA_ASSERT_FALSE(m_got_dmx); ReceiveFromPeer(DMX_MESSAGE, sizeof(DMX_MESSAGE), peer_ip); OLA_ASSERT(m_got_dmx); @@ -1186,7 +1186,7 @@ void ArtNetNodeTest::testHTPMerge() { // 'receive' a DMX message from the first peer { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); uint8_t source1_message1[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x50, @@ -1206,7 +1206,7 @@ void ArtNetNodeTest::testHTPMerge() { // receive a message from a second peer { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); uint8_t source2_message1[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x50, @@ -1267,7 +1267,7 @@ void ArtNetNodeTest::testHTPMerge() { // send a packet from a third source, this shouldn't result in any new dmx { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t source3_message1[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x50, @@ -1287,7 +1287,7 @@ void ArtNetNodeTest::testHTPMerge() { // send another packet from the first source { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); uint8_t source1_message2[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x50, @@ -1312,7 +1312,7 @@ void ArtNetNodeTest::testHTPMerge() { // send another packet from the first source { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); uint8_t source1_message3[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x50, @@ -1337,7 +1337,7 @@ void ArtNetNodeTest::testHTPMerge() { // send another packet from the first source { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); uint8_t source1_message4[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x50, @@ -1379,7 +1379,7 @@ void ArtNetNodeTest::testLTPMerge() { // switch to LTP merge mode, this will trigger an art poll reply { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); uint8_t poll_reply_message[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x21, @@ -1422,7 +1422,7 @@ void ArtNetNodeTest::testLTPMerge() { // 'receive' a DMX message from the first peer { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); uint8_t source1_message1[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x50, @@ -1442,7 +1442,7 @@ void ArtNetNodeTest::testLTPMerge() { // receive a message from a second peer { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); uint8_t source2_message1[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x50, @@ -1506,7 +1506,7 @@ void ArtNetNodeTest::testLTPMerge() { // send another packet from the first source { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); uint8_t source1_message2[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x50, @@ -1547,7 +1547,7 @@ void ArtNetNodeTest::testControllerDiscovery() { // send a tod control { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); ExpectedBroadcast(TOD_CONTROL, sizeof(TOD_CONTROL)); node.RunFullDiscovery( m_port_id, @@ -1557,7 +1557,7 @@ void ArtNetNodeTest::testControllerDiscovery() { // advance the clock and run the select server { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); m_clock.AdvanceTime(5, 0); // tod timeout is 4s ss.RunOnce(); // update the wake up time OLA_ASSERT(m_discovery_done); @@ -1568,7 +1568,7 @@ void ArtNetNodeTest::testControllerDiscovery() { // run discovery again, this time returning a ArtTod from a peer { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); m_discovery_done = false; ExpectedBroadcast(TOD_CONTROL, sizeof(TOD_CONTROL)); @@ -1602,7 +1602,7 @@ void ArtNetNodeTest::testControllerDiscovery() { // advance the clock and run the select server { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); m_clock.AdvanceTime(5, 0); // tod timeout is 4s ss.RunOnce(); // update the wake up time OLA_ASSERT(m_discovery_done); @@ -1617,7 +1617,7 @@ void ArtNetNodeTest::testControllerDiscovery() { // run discovery again, removing one UID, and moving another from peer1 // to peer2 { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); m_discovery_done = false; ExpectedBroadcast(TOD_CONTROL, sizeof(TOD_CONTROL)); @@ -1668,7 +1668,7 @@ void ArtNetNodeTest::testControllerDiscovery() { // advance the clock and run the select server { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); m_clock.AdvanceTime(5, 0); // tod timeout is 4s ss.RunOnce(); // update the wake up time OLA_ASSERT(m_discovery_done); @@ -1681,7 +1681,7 @@ void ArtNetNodeTest::testControllerDiscovery() { // try running discovery for a invalid port id { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); m_discovery_done = false; node.RunFullDiscovery( 4, @@ -1708,7 +1708,7 @@ void ArtNetNodeTest::testControllerIncrementalDiscovery() { // send a tod request { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t tod_request[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x80, @@ -1733,7 +1733,7 @@ void ArtNetNodeTest::testControllerIncrementalDiscovery() { // respond with a tod { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t art_tod1[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x81, @@ -1766,7 +1766,7 @@ void ArtNetNodeTest::testControllerIncrementalDiscovery() { // try running discovery for a invalid port id { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); m_discovery_done = false; node.RunIncrementalDiscovery( 4, @@ -1797,7 +1797,7 @@ void ArtNetNodeTest::testUnsolicitedTod() { // receive a tod { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); OLA_ASSERT_FALSE(m_discovery_done); // receive a ArtTod @@ -1848,7 +1848,7 @@ void ArtNetNodeTest::testResponderDiscovery() { // receive a tod request { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t tod_request[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x80, @@ -1870,7 +1870,7 @@ void ArtNetNodeTest::testResponderDiscovery() { // respond with a Tod { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t art_tod1[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x81, @@ -1897,7 +1897,7 @@ void ArtNetNodeTest::testResponderDiscovery() { // try a tod request a universe that doesn't match ours { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); m_tod_request = false; const uint8_t tod_request2[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, @@ -1920,7 +1920,7 @@ void ArtNetNodeTest::testResponderDiscovery() { // check TodControl { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); OLA_ASSERT_FALSE(m_tod_flush); const uint8_t tod_control[] = { @@ -1940,7 +1940,7 @@ void ArtNetNodeTest::testResponderDiscovery() { // try a tod control a universe that doesn't match ours { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); m_tod_flush = false; OLA_ASSERT_FALSE(m_tod_flush); const uint8_t tod_control2[] = { @@ -1980,7 +1980,7 @@ void ArtNetNodeTest::testRDMResponder() { ola::NewCallback(this, &ArtNetNodeTest::HandleRDM))); { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t rdm_request[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x83, @@ -2026,7 +2026,7 @@ void ArtNetNodeTest::testRDMResponder() { // run the RDM callback, triggering the response { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t rdm_response[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x83, @@ -2079,7 +2079,7 @@ void ArtNetNodeTest::testRDMRequest() { // create a new RDM request { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); SendRDMRequest( &node, ola::NewSingleCallback(this, &ArtNetNodeTest::FinalizeRDM)); @@ -2087,7 +2087,7 @@ void ArtNetNodeTest::testRDMRequest() { // send a response { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t rdm_response[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x83, @@ -2133,7 +2133,7 @@ void ArtNetNodeTest::testRDMRequestTimeout() { // create a new RDM request { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); SendRDMRequest( &node, ola::NewSingleCallback(this, &ArtNetNodeTest::ExpectTimeout)); @@ -2163,7 +2163,7 @@ void ArtNetNodeTest::testRDMRequestIPMismatch() { // create a new RDM request { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); SendRDMRequest( &node, ola::NewSingleCallback(this, &ArtNetNodeTest::ExpectTimeout)); @@ -2171,7 +2171,7 @@ void ArtNetNodeTest::testRDMRequestIPMismatch() { // send a response from a different IP { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t rdm_response[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x83, @@ -2220,7 +2220,7 @@ void ArtNetNodeTest::testRDMRequestUIDMismatch() { // create a new RDM request { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); SendRDMRequest( &node, ola::NewSingleCallback(this, &ArtNetNodeTest::ExpectTimeout)); @@ -2228,7 +2228,7 @@ void ArtNetNodeTest::testRDMRequestUIDMismatch() { // send a response from a different IP { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t rdm_response[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x83, @@ -2273,7 +2273,7 @@ void ArtNetNodeTest::testTimeCode() { m_socket->SetDiscardMode(false); { - SocketVerifier verifer(m_socket); + SocketVerifier verifier(m_socket); const uint8_t timecode_message[] = { 'A', 'r', 't', '-', 'N', 'e', 't', 0x00, 0x00, 0x97, diff --git a/plugins/e131/E131Plugin.h b/plugins/e131/E131Plugin.h index fed52781b3..1bcfced735 100644 --- a/plugins/e131/E131Plugin.h +++ b/plugins/e131/E131Plugin.h @@ -14,7 +14,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * E131Plugin.h - * Interface for the E1.131 plugin class + * Interface for the E1.31 plugin class * Copyright (C) 2007 Simon Newton */ diff --git a/plugins/e133/E133Plugin.cpp b/plugins/e133/E133Plugin.cpp new file mode 100644 index 0000000000..02018a957f --- /dev/null +++ b/plugins/e133/E133Plugin.cpp @@ -0,0 +1,185 @@ +/* + * 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. + * + * E133Plugin.cpp + * The E1.33 plugin for ola + * Copyright (C) 2024 Peter Newman + */ + +#include +#include + +#include "ola/Logging.h" +#include "ola/network/NetworkUtils.h" +#include "ola/network/SocketAddress.h" +#include "ola/StringUtils.h" +#include "ola/acn/CID.h" +#include "olad/PluginAdaptor.h" +#include "olad/Preferences.h" +#include "plugins/e133/E133Device.h" +#include "plugins/e133/E133Plugin.h" +#include "plugins/e133/E133PluginDescription.h" + +namespace ola { +namespace plugin { +namespace e133 { + +using ola::acn::CID; +using ola::network::IPV4SocketAddress; +using std::string; + +const char E133Plugin::CID_KEY[] = "cid"; +const unsigned int E133Plugin::DEFAULT_DSCP_VALUE = 0; +const char E133Plugin::DSCP_KEY[] = "dscp"; +const char E133Plugin::INPUT_PORT_COUNT_KEY[] = "input_ports"; +const char E133Plugin::IP_KEY[] = "ip"; +const char E133Plugin::OUTPUT_PORT_COUNT_KEY[] = "output_ports"; +const char E133Plugin::PLUGIN_NAME[] = "E1.33 (RDMNet)"; +const char E133Plugin::PLUGIN_PREFIX[] = "e133"; +const char E133Plugin::PREPEND_HOSTNAME_KEY[] = "prepend_hostname"; +const char E133Plugin::TARGET_SOCKET_KEY[] = "target_socket"; +const unsigned int E133Plugin::DEFAULT_PORT_COUNT = 5; + + +/* + * Start the plugin + */ +bool E133Plugin::StartHook() { + CID cid = CID::FromString(m_preferences->GetValue(CID_KEY)); + string ip_addr = m_preferences->GetValue(IP_KEY); + + E133Device::E133DeviceOptions options; + if (m_preferences->GetValueAsBool(PREPEND_HOSTNAME_KEY)) { + std::ostringstream str; + str << ola::network::Hostname() << "-" << m_plugin_adaptor->InstanceName(); +// options.source_name = str.str(); + } else { +// options.source_name = m_plugin_adaptor->InstanceName(); + } + + unsigned int dscp; + if (!StringToInt(m_preferences->GetValue(DSCP_KEY), &dscp)) { + OLA_WARN << "Can't convert dscp value " << + m_preferences->GetValue(DSCP_KEY) << " to int"; +// options.dscp = 0; + } else { + // shift 2 bits left +// options.dscp = dscp << 2; + } + + if (!StringToInt(m_preferences->GetValue(INPUT_PORT_COUNT_KEY), + &options.input_ports)) { + OLA_WARN << "Invalid value for input_ports"; + } + + if (!StringToInt(m_preferences->GetValue(OUTPUT_PORT_COUNT_KEY), + &options.output_ports)) { + OLA_WARN << "Invalid value for output_ports"; + } + + IPV4SocketAddress socket_address; + if (!IPV4SocketAddress::FromString(m_preferences->GetValue(TARGET_SOCKET_KEY), &socket_address)) { + OLA_WARN << "Invalid value for " << TARGET_SOCKET_KEY; + } + + m_device = new E133Device(this, cid, ip_addr, socket_address, m_plugin_adaptor, options); + + if (!m_device->Start()) { + delete m_device; + return false; + } + + m_plugin_adaptor->RegisterDevice(m_device); + return true; +} + + +/* + * Stop the plugin + * @return true on success, false on failure + */ +bool E133Plugin::StopHook() { + if (m_device) { + m_plugin_adaptor->UnregisterDevice(m_device); + bool ret = m_device->Stop(); + delete m_device; + return ret; + } + return true; +} + + +/* + * Return the description for this plugin + */ +string E133Plugin::Description() const { + return plugin_description; +} + + +/* + * Load the plugin prefs and default to sensible values + * + */ +bool E133Plugin::SetDefaultPreferences() { + if (!m_preferences) + return false; + + bool save = false; + + CID cid = CID::FromString(m_preferences->GetValue(CID_KEY)); + if (cid.IsNil()) { + cid = CID::Generate(); + m_preferences->SetValue(CID_KEY, cid.ToString()); + save = true; + } + + save |= m_preferences->SetDefaultValue( + DSCP_KEY, + UIntValidator(0, 63), + DEFAULT_DSCP_VALUE); + + save |= m_preferences->SetDefaultValue( + INPUT_PORT_COUNT_KEY, + UIntValidator(0, 512), + DEFAULT_PORT_COUNT); + + save |= m_preferences->SetDefaultValue( + OUTPUT_PORT_COUNT_KEY, + UIntValidator(0, 512), + DEFAULT_PORT_COUNT); + + save |= m_preferences->SetDefaultValue(IP_KEY, StringValidator(true), ""); + + save |= m_preferences->SetDefaultValue( + PREPEND_HOSTNAME_KEY, + BoolValidator(), + true); + + IPV4SocketAddress socket_address; + if (!IPV4SocketAddress::FromString(m_preferences->GetValue(TARGET_SOCKET_KEY), &socket_address)) { + m_preferences->SetValue(TARGET_SOCKET_KEY, ""); + save = true; + } + + if (save) { + m_preferences->Save(); + } + + return true; +} +} // namespace e133 +} // namespace plugin +} // namespace ola diff --git a/plugins/e133/E133Plugin.h b/plugins/e133/E133Plugin.h new file mode 100644 index 0000000000..68059f302d --- /dev/null +++ b/plugins/e133/E133Plugin.h @@ -0,0 +1,67 @@ +/* + * 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. + * + * E133Plugin.h + * Interface for the E1.33 plugin class + * Copyright (C) 2024 Peter Newman + */ + +#ifndef PLUGINS_E133_E133PLUGIN_H_ +#define PLUGINS_E133_E133PLUGIN_H_ + +#include +#include "olad/Plugin.h" +#include "ola/plugin_id.h" + +namespace ola { +namespace plugin { +namespace e133 { + +class E133Device; + +class E133Plugin: public ola::Plugin { + public: + explicit E133Plugin(ola::PluginAdaptor *plugin_adaptor): + ola::Plugin(plugin_adaptor), + m_device(NULL) {} + ~E133Plugin() {} + + std::string Name() const { return PLUGIN_NAME; } + ola_plugin_id Id() const { return OLA_PLUGIN_E133; } + std::string Description() const; + std::string PluginPrefix() const { return PLUGIN_PREFIX; } + + private: + bool StartHook(); + bool StopHook(); + bool SetDefaultPreferences(); + + E133Device *m_device; + static const char CID_KEY[]; + static const unsigned int DEFAULT_DSCP_VALUE; + static const unsigned int DEFAULT_PORT_COUNT; + static const char DSCP_KEY[]; + static const char INPUT_PORT_COUNT_KEY[]; + static const char IP_KEY[]; + static const char OUTPUT_PORT_COUNT_KEY[]; + static const char PLUGIN_NAME[]; + static const char PLUGIN_PREFIX[]; + static const char PREPEND_HOSTNAME_KEY[]; + static const char TARGET_SOCKET_KEY[]; +}; +} // namespace e133 +} // namespace plugin +} // namespace ola +#endif // PLUGINS_E133_E133PLUGIN_H_ diff --git a/plugins/e133/Makefile.mk b/plugins/e133/Makefile.mk new file mode 100644 index 0000000000..d64378a62c --- /dev/null +++ b/plugins/e133/Makefile.mk @@ -0,0 +1,35 @@ +# LIBRARIES +################################################## + +if USE_E133 +lib_LTLIBRARIES += plugins/e133/libolae133.la + +# Plugin description is generated from README.md +built_sources += plugins/e133/E133PluginDescription.h +nodist_plugins_e133_libolae133_la_SOURCES = \ + plugins/e133/E133PluginDescription.h +plugins/e133/E133PluginDescription.h: plugins/e133/README.md plugins/e133/Makefile.mk plugins/convert_README_to_header.sh + sh $(top_srcdir)/plugins/convert_README_to_header.sh $(top_srcdir)/plugins/e133 $(top_builddir)/plugins/e133/E133PluginDescription.h + +plugins_e133_libolae133_la_SOURCES = \ + plugins/e133/E133Device.cpp \ + plugins/e133/E133Device.h \ + plugins/e133/E133Plugin.cpp \ + plugins/e133/E133Plugin.h \ + plugins/e133/E133Port.cpp \ + plugins/e133/E133Port.h \ + plugins/e133/E133PortImpl.cpp \ + plugins/e133/E133PortImpl.h +plugins_e133_libolae133_la_CXXFLAGS = $(COMMON_PROTOBUF_CXXFLAGS) $(uuid_CFLAGS) + +# plugins/e133/messages/libolae133conf.la +plugins_e133_libolae133_la_LIBADD = \ + $(uuid_LIBS) \ + common/libolacommon.la \ + olad/plugin_api/libolaserverplugininterface.la \ + libs/acn/libolaacn.la \ + libs/acn/libolae131core.la \ + libs/acn/libolae133core.la +endif + +EXTRA_DIST += plugins/e133/README.md diff --git a/plugins/e133/README.md b/plugins/e133/README.md new file mode 100644 index 0000000000..56ca954d07 --- /dev/null +++ b/plugins/e133/README.md @@ -0,0 +1,26 @@ +E1.33 (RDMNet) Plugin +===================================== + +This plugin creates a single device with a configurable number of input and +output ports. + +Each port can be assigned to a different E1.33 Universe. + + +## Config file: `ola-e133.conf` + +`cid = 00010203-0405-0607-0809-0A0B0C0D0E0F` +The CID to use for this device. + +#`dscp = [int]` +#The DSCP value to tag the packets with, range is 0 to 63. + +`input_ports = [int]` +The number of input ports to create up to an arbitrary max of 512. + +`ip = [a.b.c.d|]` +The IP address or interface name to bind to. If not specified it will use +the first non-loopback interface. + +`output_ports = [int]` +The number of output ports to create up to an arbitrary max of 512. diff --git a/plugins/kinet/README.md b/plugins/kinet/README.md index 68bca96c5f..e2872ec783 100644 --- a/plugins/kinet/README.md +++ b/plugins/kinet/README.md @@ -8,13 +8,13 @@ the Port Out (V2) modes of the KiNET protocol. ## Config file: `ola-kinet.conf` -`power_supply = ` +`power_supply = ` The IP of the power supply to send to. You can communicate with more than one power supply by adding multiple `power_supply =` lines. ### Per Power Supply Settings -`-mode = [dmxout|portout]` +`-mode = [dmxout|portout]` The mode of KiNET to send to the power supply. DMX Out is sometimes known as V1 and Port Out as V2. @@ -32,7 +32,7 @@ for the wildcard universe on each device. Instead, the universe for each device may be patched by assigning this output port to the intended universe in OLA. -`-ports = ` +`-ports = ` The number of physical ports available on the power supply in Port Out mode. Each physical port will create an OLA port that may be assigned to any universe. This setting is ignored in DMX Out mode. The default and maximum diff --git a/plugins/spi/README.md b/plugins/spi/README.md index 41de19056b..f5aac524c5 100644 --- a/plugins/spi/README.md +++ b/plugins/spi/README.md @@ -44,7 +44,7 @@ pin. The number of ports will be 2 ^ (# of pins). If the software backend is used, this defines the number of ports which will be created. -`-sync-ports = ` +`-sync-port = ` Controls which port triggers a flush (write) of the SPI data. If set to -1 the SPI data is written when any port changes. This can result in a lot of data writes (slow) and partial frames. If set to -2, the last port is used. diff --git a/plugins/spi/SPIOutput.h b/plugins/spi/SPIOutput.h index 745e6c7c07..ed37e9b3e7 100644 --- a/plugins/spi/SPIOutput.h +++ b/plugins/spi/SPIOutput.h @@ -94,7 +94,7 @@ class SPIOutput: public ola::rdm::DiscoverableRDMControllerInterface { private: /** - * The RDM Operations for the MovingLightResponder. + * The RDM Operations for the SPIOutput. */ class RDMOps : public ola::rdm::ResponderOps { public: diff --git a/plugins/uartdmx/README.md b/plugins/uartdmx/README.md index 409b532d3f..79e1a7139a 100644 --- a/plugins/uartdmx/README.md +++ b/plugins/uartdmx/README.md @@ -12,10 +12,10 @@ http://eastertrail.blogspot.co.uk/2014/04/command-and-control-ii.html ## Config file: `ola-uartdmx.conf` -`enabled = true` +`enabled = true` Enable this plugin (DISABLED by default). -`device = /dev/ttyAMA0` +`device = /dev/ttyAMA0` The device to use for DMX output (optional). Multiple devices are supported if the hardware exists. On later software it may also be /dev/serial0. Using USB-serial adapters is not supported (try the @@ -24,8 +24,8 @@ Using USB-serial adapters is not supported (try the ### Per Device Settings (using above device name) -`-break = 100` +`-break = 100` The DMX break time in microseconds for this device (optional). -`-malf = 100` +`-malf = 100` The Mark After Last Frame time in microseconds for this device (optional). diff --git a/plugins/usbdmx/AVLdiyD512Factory.cpp b/plugins/usbdmx/AVLdiyD512Factory.cpp index 0f965ab7f7..2e0481636f 100644 --- a/plugins/usbdmx/AVLdiyD512Factory.cpp +++ b/plugins/usbdmx/AVLdiyD512Factory.cpp @@ -63,6 +63,9 @@ bool AVLdiyD512Factory::DeviceAdded( // Some AVLdiy devices don't have serial numbers. Since there isn't another // good way to uniquely identify a USB device, we only support one of these // types of devices per host. + // TODO(Peter): We could instead use the device & bus number (like the + // Eurolite plugin). You could use more than one device, but the patch + // wouldn't follow if you plugged it into a different port if (info.serial.empty()) { if (m_missing_serial_number) { OLA_WARN << "Failed to read serial number or serial number empty. " diff --git a/plugins/usbdmx/AnymauDMXFactory.cpp b/plugins/usbdmx/AnymauDMXFactory.cpp index 1995fd87b1..86df54f2d9 100644 --- a/plugins/usbdmx/AnymauDMXFactory.cpp +++ b/plugins/usbdmx/AnymauDMXFactory.cpp @@ -63,6 +63,9 @@ bool AnymauDMXFactory::DeviceAdded( // Some Anyma devices don't have serial numbers. Since there isn't another // good way to uniquely identify a USB device, we only support one of these // types of devices per host. + // TODO(Peter): We could instead use the device & bus number (like the + // Eurolite plugin). You could use more than one device, but the patch + // wouldn't follow if you plugged it into a different port if (info.serial.empty()) { if (m_missing_serial_number) { OLA_WARN << "Failed to read serial number or serial number empty. " diff --git a/plugins/usbdmx/DMXCreator512BasicFactory.cpp b/plugins/usbdmx/DMXCreator512BasicFactory.cpp index 0728e7a183..caa6a0de5d 100644 --- a/plugins/usbdmx/DMXCreator512BasicFactory.cpp +++ b/plugins/usbdmx/DMXCreator512BasicFactory.cpp @@ -55,6 +55,9 @@ bool DMXCreator512BasicFactory::DeviceAdded( // vendor and product ids. Also, since DMXCreator 512 Basic devices don't have // serial numbers and there is no other good way to uniquely identify a USB // device, we only support one of these types of devices per host. + // TODO(Peter): We could instead use the device & bus number (like the + // Eurolite plugin). You could use more than one device, but the patch + // wouldn't follow if you plugged it into a different port if (info.serial.empty()) { if (m_missing_serial_number) { OLA_WARN << "We can only support one device without a serial number."; diff --git a/plugins/usbdmx/EuroliteProFactory.cpp b/plugins/usbdmx/EuroliteProFactory.cpp index ce4a0b5e10..42efe7bea6 100644 --- a/plugins/usbdmx/EuroliteProFactory.cpp +++ b/plugins/usbdmx/EuroliteProFactory.cpp @@ -20,6 +20,8 @@ #include "plugins/usbdmx/EuroliteProFactory.h" +#include + #include "libs/usb/LibUsbAdaptor.h" #include "ola/Logging.h" #include "ola/base/Flags.h" @@ -46,12 +48,34 @@ const uint16_t EuroliteProFactory::VENDOR_ID_MK2 = 0x0403; const char EuroliteProFactory::ENABLE_EUROLITE_MK2_KEY[] = "enable_eurolite_mk2"; +const char EuroliteProFactory::EUROLITE_MK2_SERIAL_KEY[] = + "eurolite_mk2_serial"; EuroliteProFactory::EuroliteProFactory(ola::usb::LibUsbAdaptor *adaptor, Preferences *preferences) : BaseWidgetFactory("EuroliteProFactory"), m_adaptor(adaptor), m_enable_eurolite_mk2(IsEuroliteMk2Enabled(preferences)) { + const std::vector serials = + preferences->GetMultipleValue(EUROLITE_MK2_SERIAL_KEY); + // A single empty string is considered the same as specifying + // no serial numbers. This is useful as a default value. + const bool has_default_value = + serials.size() == 1 && serials[0].empty(); + if (!has_default_value) { + for (std::vector::const_iterator iter = serials.begin(); + iter != serials.end(); ++iter) { + if (iter->empty()) { + OLA_WARN << EUROLITE_MK2_SERIAL_KEY + << " requires a serial number, but it is empty."; + } else if (STLContains(m_expected_eurolite_mk2_serials, *iter)) { + OLA_WARN << EUROLITE_MK2_SERIAL_KEY << " lists serial " + << *iter << " more than once."; + } else { + m_expected_eurolite_mk2_serials.insert(*iter); + } + } + } } bool EuroliteProFactory::IsEuroliteMk2Enabled(Preferences *preferences) { @@ -68,11 +92,11 @@ bool EuroliteProFactory::DeviceAdded( libusb_device *usb_device, const struct libusb_device_descriptor &descriptor) { bool is_mk2 = false; + LibUsbAdaptor::DeviceInformation info; // Eurolite USB-DMX512-PRO? if (descriptor.idVendor == VENDOR_ID && descriptor.idProduct == PRODUCT_ID) { OLA_INFO << "Found a new Eurolite USB-DMX512-PRO device"; - LibUsbAdaptor::DeviceInformation info; if (!m_adaptor->GetDeviceInfo(usb_device, descriptor, &info)) { return false; } @@ -88,13 +112,21 @@ bool EuroliteProFactory::DeviceAdded( // Eurolite USB-DMX512-PRO MK2? } else if (descriptor.idVendor == VENDOR_ID_MK2 && descriptor.idProduct == PRODUCT_ID_MK2) { - if (m_enable_eurolite_mk2) { - OLA_INFO << "Found a possible new Eurolite USB-DMX512-PRO MK2 device"; - LibUsbAdaptor::DeviceInformation info; - if (!m_adaptor->GetDeviceInfo(usb_device, descriptor, &info)) { - return false; - } + if (!m_adaptor->GetDeviceInfo(usb_device, descriptor, &info)) { + return false; + } + + const bool serial_matches = + STLContains(m_expected_eurolite_mk2_serials, info.serial); + if (m_enable_eurolite_mk2 || serial_matches) { + if (serial_matches) { + OLA_INFO << "Found a probable new Eurolite USB-DMX512-PRO MK2 device " + << "with matching serial " << info.serial; + } else { + OLA_INFO << "Found a probable new Eurolite USB-DMX512-PRO MK2 device " + << "with serial " << info.serial; + } if (!m_adaptor->CheckManufacturer(EXPECTED_MANUFACTURER_MK2, info)) { return false; } @@ -104,9 +136,12 @@ bool EuroliteProFactory::DeviceAdded( } is_mk2 = true; } else { - OLA_INFO << "Connected FTDI device could be a Eurolite " - << "USB-DMX512-PRO MK2 but was ignored, because " - << ENABLE_EUROLITE_MK2_KEY << " was false."; + OLA_INFO << "Connected FTDI device with serial " << info.serial + << " could be a Eurolite USB-DMX512-PRO MK2 but was " + << "ignored, because " + << ENABLE_EUROLITE_MK2_KEY << " was false and " + << "its serial number was not listed specifically in " + << EUROLITE_MK2_SERIAL_KEY; return false; } } else { @@ -114,17 +149,21 @@ bool EuroliteProFactory::DeviceAdded( return false; } - // The Eurolite doesn't have a serial number, so instead we use the device & - // bus number. + // The original Eurolite doesn't have a serial number, so instead we use the + // device & bus number. The MK2 does, so we use that where available. // TODO(simon): check if this supports the SERIAL NUMBER label and use that // instead. - // There is no Serialnumber--> work around: bus+device number - int bus_number = libusb_get_bus_number(usb_device); - int device_address = libusb_get_device_address(usb_device); - std::ostringstream serial_str; - serial_str << bus_number << "-" << device_address; + if (is_mk2 && !info.serial.empty()) { + serial_str << info.serial; + } else { + // Original, there is no Serialnumber--> work around: bus+device number + int bus_number = libusb_get_bus_number(usb_device); + int device_address = libusb_get_device_address(usb_device); + + serial_str << bus_number << "-" << device_address; + } EurolitePro *widget = NULL; if (FLAGS_use_async_libusb) { diff --git a/plugins/usbdmx/EuroliteProFactory.h b/plugins/usbdmx/EuroliteProFactory.h index 23ebdc3f86..249735dd75 100644 --- a/plugins/usbdmx/EuroliteProFactory.h +++ b/plugins/usbdmx/EuroliteProFactory.h @@ -21,6 +21,9 @@ #ifndef PLUGINS_USBDMX_EUROLITEPROFACTORY_H_ #define PLUGINS_USBDMX_EUROLITEPROFACTORY_H_ +#include +#include + #include "libs/usb/LibUsbAdaptor.h" #include "ola/base/Macro.h" #include "olad/Preferences.h" @@ -46,10 +49,12 @@ class EuroliteProFactory : public BaseWidgetFactory { static bool IsEuroliteMk2Enabled(Preferences *preferences); static const char ENABLE_EUROLITE_MK2_KEY[]; + static const char EUROLITE_MK2_SERIAL_KEY[]; private: ola::usb::LibUsbAdaptor *m_adaptor; bool m_enable_eurolite_mk2; + std::set m_expected_eurolite_mk2_serials; static const uint16_t PRODUCT_ID; static const uint16_t VENDOR_ID; diff --git a/plugins/usbdmx/README.md b/plugins/usbdmx/README.md index 79d0eb8b88..04696f1c38 100644 --- a/plugins/usbdmx/README.md +++ b/plugins/usbdmx/README.md @@ -9,7 +9,7 @@ This plugin supports various USB DMX devices including: * DMXControl Projects e.V. Nodle U1 * DMXCreator 512 Basic * Eurolite USB-DMX512 PRO -* Eurolite USB-DMX512 PRO MK2 (when `enable_eurolite_mk2 = true`) +* Eurolite USB-DMX512 PRO MK2 (see notes below) * Eurolite freeDMX Wi-Fi * Fadecandy * FX5 DMX @@ -20,25 +20,36 @@ This plugin supports various USB DMX devices including: ## Config file : `ola-usbdmx.conf` -`libusb_debug_level = {0,1,2,3,4}` -The debug level for libusb, see http://libusb.sourceforge.net/api-1.0/ +`libusb_debug_level = {0,1,2,3,4}` +The debug level for libusb, see http://libusb.sourceforge.net/api-1.0/ 0 = No logging, 4 = Verbose debug. `enable_eurolite_mk2 = {false,true}` Whether to enable detection of the Eurolite USB-DMX512 PRO MK2. -Default = false. This device is indistinguishable from other devices +Default = `false`. This device is indistinguishable from other devices with an FTDI chip, and is therefore disabled by default. When enabled, this plugin will conflict with the usbserial, StageProfi and FTDI USB DMX -plugins. +plugins. If this is undesirable, the `eurolite_mk2_serial` setting can be +used instead, which manually marks a specific USB device as a Eurolite +USB-DMX512 PRO MK2. -`nodle--mode = {0,1,2,3,4,5,6,7}` +`eurolite_mk2_serial = ` +Claim the USB device with the given serial number as a Eurolite USB-DMX512 +PRO MK2 even when `enable_eurolite_mk2 = false`. This makes it possible +to use the Eurolite USB-DMX512 PRO MK2 together with other devices that +can not be distinguished otherwise. This setting has no effect when +`enable_eurolite_mk2 = true` or if no device is connected with the given +serial. The setting may be specified multiple times to use multiple Eurolite +USB-DMX512 PRO MK2 devices. + +`nodle--mode = {0,1,2,3,4,5,6,7}` The mode for the Nodle U1 interface with serial number `` to operate -in. Default = 6 -0 - Standby -1 - DMX In -> DMX Out -2 - PC Out -> DMX Out -3 - DMX In + PC Out -> DMX Out -4 - DMX In -> PC In -5 - DMX In -> DMX Out & DMX In -> PC In -6 - PC Out -> DMX Out & DMX In -> PC In +in. Default = 6 +0 - Standby +1 - DMX In -> DMX Out +2 - PC Out -> DMX Out +3 - DMX In + PC Out -> DMX Out +4 - DMX In -> PC In +5 - DMX In -> DMX Out & DMX In -> PC In +6 - PC Out -> DMX Out & DMX In -> PC In 7 - DMX In + PC Out -> DMX Out & DMX In -> PC In diff --git a/plugins/usbdmx/ScanlimeFadecandyFactory.cpp b/plugins/usbdmx/ScanlimeFadecandyFactory.cpp index 4f1420a8b6..60f28d5dd9 100644 --- a/plugins/usbdmx/ScanlimeFadecandyFactory.cpp +++ b/plugins/usbdmx/ScanlimeFadecandyFactory.cpp @@ -64,6 +64,9 @@ bool ScanlimeFadecandyFactory::DeviceAdded( // Fadecandy devices may be missing serial numbers. Since there isn't another // good way to uniquely identify a USB device, we only support one of these // types of devices per host. + // TODO(Peter): We could instead use the device & bus number (like the + // Eurolite plugin). You could use more than one device, but the patch + // wouldn't follow if you plugged it into a different port if (info.serial.empty()) { if (m_missing_serial_number) { OLA_WARN << "Failed to read serial number or serial number empty. " diff --git a/plugins/usbdmx/UsbDmxPlugin.cpp b/plugins/usbdmx/UsbDmxPlugin.cpp index d1760aea96..4aa3f7f887 100644 --- a/plugins/usbdmx/UsbDmxPlugin.cpp +++ b/plugins/usbdmx/UsbDmxPlugin.cpp @@ -107,6 +107,11 @@ bool UsbDmxPlugin::SetDefaultPreferences() { BoolValidator(), false); + save |= m_preferences->SetDefaultValue( + EuroliteProFactory::EUROLITE_MK2_SERIAL_KEY, + StringValidator(), + ""); + if (save) { m_preferences->Save(); } diff --git a/plugins/usbpro/DmxterWidget.cpp b/plugins/usbpro/DmxterWidget.cpp index aae5f95645..5158bd0288 100644 --- a/plugins/usbpro/DmxterWidget.cpp +++ b/plugins/usbpro/DmxterWidget.cpp @@ -50,7 +50,7 @@ const uint8_t DmxterWidgetImpl::TOD_LABEL = 0x82; const uint8_t DmxterWidgetImpl::DISCOVERY_BRANCH_LABEL = 0x83; const uint8_t DmxterWidgetImpl::FULL_DISCOVERY_LABEL = 0x84; const uint8_t DmxterWidgetImpl::INCREMENTAL_DISCOVERY_LABEL = 0x85; -const uint8_t DmxterWidgetImpl::SHUTDOWN_LABAEL = 0xf0; +const uint8_t DmxterWidgetImpl::SHUTDOWN_LABEL = 0xf0; /* @@ -195,7 +195,7 @@ void DmxterWidgetImpl::HandleMessage(uint8_t label, case RDM_BCAST_REQUEST_LABEL: HandleBroadcastRDMResponse(data, length); break; - case SHUTDOWN_LABAEL: + case SHUTDOWN_LABEL: HandleShutdown(data, length); break; default: @@ -325,7 +325,7 @@ void DmxterWidgetImpl::HandleRDMResponse(const uint8_t *data, case RC_NACK_FORMAT_ERROR: case RC_NACK_HARDWARE_FAULT: case RC_NACK_PROXY_REJECT: - case RC_NACK_WRITE_PROECT: + case RC_NACK_WRITE_PROTECT: case RC_NACK_COMMAND_CLASS: case RC_NACK_DATA_RANGE: case RC_NACK_BUFFER_FULL: diff --git a/plugins/usbpro/DmxterWidget.h b/plugins/usbpro/DmxterWidget.h index 44cde56723..8a2355f4d9 100644 --- a/plugins/usbpro/DmxterWidget.h +++ b/plugins/usbpro/DmxterWidget.h @@ -77,7 +77,7 @@ class DmxterWidgetImpl: public BaseUsbProWidget, static const uint8_t DISCOVERY_BRANCH_LABEL; static const uint8_t FULL_DISCOVERY_LABEL; static const uint8_t INCREMENTAL_DISCOVERY_LABEL; - static const uint8_t SHUTDOWN_LABAEL; + static const uint8_t SHUTDOWN_LABEL; typedef enum { RC_CHECKSUM_ERROR = 1, @@ -107,7 +107,7 @@ class DmxterWidgetImpl: public BaseUsbProWidget, RC_NACK_FORMAT_ERROR = 25, RC_NACK_HARDWARE_FAULT = 26, RC_NACK_PROXY_REJECT = 27, - RC_NACK_WRITE_PROECT = 28, + RC_NACK_WRITE_PROTECT = 28, RC_NACK_COMMAND_CLASS = 29, RC_NACK_DATA_RANGE = 30, RC_NACK_BUFFER_FULL = 31, diff --git a/python/ola/OlaClient.py b/python/ola/OlaClient.py index 47e8dfa41c..f10eb4f0db 100644 --- a/python/ola/OlaClient.py +++ b/python/ola/OlaClient.py @@ -1345,7 +1345,7 @@ def GetCandidatePorts(self, callback, universe=None): request.universe = universe try: - # GetCandidatePorts works very much like GetDeviceInfo, so we can re-use + # GetCandidatePorts works very much like GetDeviceInfo, so we can reuse # its complete method. self._stub.GetCandidatePorts( controller, request, diff --git a/python/ola/PidStore.py b/python/ola/PidStore.py index 38fb200562..5742b35e92 100644 --- a/python/ola/PidStore.py +++ b/python/ola/PidStore.py @@ -549,6 +549,29 @@ def Pack(self, args): return super(IntAtom, self).Pack(value) +class IPV6Atom(FixedSizeAtom): + """A sixteen-byte IPV6 address.""" + def __init__(self, name, **kwargs): + super(IPV6Atom, self).__init__(name, 'BBBBBBBBBBBBBBBB') + + def Unpack(self, data): + try: + return socket.inet_ntop(socket.AF_INET6, data) + except socket.error as e: + raise ArgsValidationError("Can't unpack data: %s" % e) + + def Pack(self, args): + # TODO(Peter): This currently allows some rather quirky values as per + # inet_pton, we may want to restrict that in future + format_string = self._FormatString() + try: + data = struct.pack(format_string, + socket.inet_pton(socket.AF_INET6, args[0])) + except socket.error as e: + raise ArgsValidationError("Can't pack data: %s" % e) + return data, 1 + + class MACAtom(FixedSizeAtom): """A MAC address.""" def __init__(self, name, **kwargs): @@ -1044,7 +1067,7 @@ def LoadFile(self, pid_file_name, validate, override=False): if validate: if ((pid_pb.value >= RDMConstants.RDM_MANUFACTURER_PID_MIN) and (pid_pb.value <= RDMConstants.RDM_MANUFACTURER_PID_MAX)): - raise InvalidPidFormat('%0x04hx between %0x04hx and %0x04hx in %s' % + raise InvalidPidFormat('0x%04hx between 0x%04hx and 0x%04hx in %s' % (pid_pb.value, RDMConstants.RDM_MANUFACTURER_PID_MIN, RDMConstants.RDM_MANUFACTURER_PID_MAX, @@ -1080,7 +1103,7 @@ def LoadFile(self, pid_file_name, validate, override=False): if ((pid_pb.value < RDMConstants.RDM_MANUFACTURER_PID_MIN) or (pid_pb.value > RDMConstants.RDM_MANUFACTURER_PID_MAX)): raise InvalidPidFormat( - 'Manufacturer pid 0x%04hx not between %0x04hx and %0x04hx' % + 'Manufacturer pid 0x%04hx not between 0x%04hx and 0x%04hx' % (pid_pb.value, RDMConstants.RDM_MANUFACTURER_PID_MIN, RDMConstants.RDM_MANUFACTURER_PID_MAX)) @@ -1271,6 +1294,8 @@ def _FieldToAtom(self, field): return UInt32(field_name, **args) elif field.type == Pids_pb2.IPV4: return IPV4(field_name, **args) + elif field.type == Pids_pb2.IPV6: + return IPV6Atom(field_name, **args) elif field.type == Pids_pb2.MAC: return MACAtom(field_name, **args) elif field.type == Pids_pb2.UID: diff --git a/python/ola/PidStoreTest.py b/python/ola/PidStoreTest.py index 600f1dd389..e09226f1fd 100755 --- a/python/ola/PidStoreTest.py +++ b/python/ola/PidStoreTest.py @@ -174,6 +174,8 @@ def testCmp(self): g1getreq = PidStore.Group("grg", [PidStore.UInt16("ui16"), PidStore.Int32("i32")]) g1setreq = PidStore.Group("srg", [PidStore.MACAtom("mac"), + PidStore.IPV4("ipv4"), + PidStore.IPV6Atom("ipv6"), PidStore.Int8("i32")]) p1b = PidStore.Pid("base", 42, None, None, g1getreq, g1setreq) diff --git a/python/ola/StringUtils.py b/python/ola/StringUtils.py index a0f51cfafd..4a4d7dd7de 100644 --- a/python/ola/StringUtils.py +++ b/python/ola/StringUtils.py @@ -32,11 +32,11 @@ def StringEscape(s): """Escape unprintable characters in a string.""" # TODO(Peter): How does this interact with the E1.20 Unicode flag? # We don't use sys.version_info.major to support Python 2.6. - if sys.version_info[0] == 2 and type(s) == str: + if sys.version_info[0] == 2 and isinstance(s, str): return s.encode('string-escape') - elif sys.version_info[0] == 2 and type(s) == unicode: + elif sys.version_info[0] == 2 and isinstance(s, unicode): return s.encode('unicode-escape') - elif type(s) == str: + elif isinstance(s, str): # All strings in Python 3 are unicode # This encode/decode pair gets us an escaped string return s.encode('unicode-escape').decode(encoding="ascii", diff --git a/scripts/spelling.sh b/scripts/spelling.sh index bcc82af8ac..c20c9c2c30 100755 --- a/scripts/spelling.sh +++ b/scripts/spelling.sh @@ -70,11 +70,14 @@ SPELLINGBLACKLIST=$(cat <<-BLACKLIST -wholename "./olad/www/new/js/app.min.js" -or \ -wholename "./olad/www/new/js/app.min.js.map" -or \ -wholename "./olad/www/new/libs/angular/js/angular.min.js" -or \ + -wholename "./olad/www/new/libs/bootstrap/js/bootstrap.min.js" -or \ + -wholename "./olad/www/new/libs/jquery/js/jquery.min.js" -or \ -wholename "./olad/www/new/libs/marked/js/marked.min.js" -or \ -wholename "./olad/www/ola.js" -or \ -wholename "./plugins/artnet/messages/ArtNetConfigMessages.pb.*" -or \ -wholename "./tools/ola_trigger/config.tab.*" -or \ - -wholename "./tools/ola_trigger/lex.yy.cpp" + -wholename "./tools/ola_trigger/lex.yy.cpp" -or \ + -wholename "./tools/rdm/static/jquery-1.7.2.min.js" BLACKLIST ) diff --git a/tools/e133/DesignatedControllerConnection.cpp b/tools/e133/DesignatedControllerConnection.cpp index 5daae899fa..0683712e91 100644 --- a/tools/e133/DesignatedControllerConnection.cpp +++ b/tools/e133/DesignatedControllerConnection.cpp @@ -33,10 +33,10 @@ #include "ola/rdm/RDMCommand.h" #include "ola/rdm/RDMCommandSerializer.h" #include "libs/acn/E133Header.h" +#include "libs/acn/E133HealthCheckedConnection.h" #include "libs/acn/E133StatusInflator.h" #include "libs/acn/RDMPDU.h" #include "tools/e133/DesignatedControllerConnection.h" -#include "tools/e133/E133HealthCheckedConnection.h" #include "tools/e133/TCPConnectionStats.h" using ola::NewCallback; diff --git a/tools/e133/DesignatedControllerConnection.h b/tools/e133/DesignatedControllerConnection.h index 7993b70e12..25820594bc 100644 --- a/tools/e133/DesignatedControllerConnection.h +++ b/tools/e133/DesignatedControllerConnection.h @@ -33,11 +33,11 @@ #include "ola/network/TCPSocket.h" #include "ola/network/TCPSocketFactory.h" #include "ola/util/SequenceNumber.h" +#include "libs/acn/E133HealthCheckedConnection.h" #include "libs/acn/E133Inflator.h" #include "libs/acn/E133StatusInflator.h" #include "libs/acn/RootInflator.h" #include "libs/acn/TCPTransport.h" -#include "tools/e133/E133HealthCheckedConnection.h" #include "tools/e133/TCPConnectionStats.h" using std::string; diff --git a/tools/e133/DeviceManager.cpp b/tools/e133/DeviceManager.cpp index b91879eddd..3fe1f38874 100644 --- a/tools/e133/DeviceManager.cpp +++ b/tools/e133/DeviceManager.cpp @@ -54,9 +54,9 @@ DeviceManager::~DeviceManager() {} /** * Set the callback to be run when RDMNet data is received from a device. - * @param callback the RDMMesssageCallback to run when data is received. + * @param callback the RDMMessageCallback to run when data is received. */ -void DeviceManager::SetRDMMessageCallback(RDMMesssageCallback *callback) { +void DeviceManager::SetRDMMessageCallback(RDMMessageCallback *callback) { m_impl->SetRDMMessageCallback(callback); } diff --git a/tools/e133/DeviceManagerImpl.cpp b/tools/e133/DeviceManagerImpl.cpp index 86ace1d99c..f063ae441a 100644 --- a/tools/e133/DeviceManagerImpl.cpp +++ b/tools/e133/DeviceManagerImpl.cpp @@ -36,13 +36,13 @@ #include #include +#include "libs/acn/E133HealthCheckedConnection.h" #include "libs/acn/E133Inflator.h" #include "libs/acn/E133StatusPDU.h" #include "libs/acn/TCPTransport.h" #include "tools/e133/DeviceManagerImpl.h" #include "tools/e133/E133Endpoint.h" -#include "tools/e133/E133HealthCheckedConnection.h" namespace ola { namespace e133 { @@ -132,9 +132,9 @@ DeviceManagerImpl::~DeviceManagerImpl() { /** * Set the callback to be run when RDMNet data is received from a device. - * @param callback the RDMMesssageCallback to run when data is received. + * @param callback the RDMMessageCallback to run when data is received. */ -void DeviceManagerImpl::SetRDMMessageCallback(RDMMesssageCallback *callback) { +void DeviceManagerImpl::SetRDMMessageCallback(RDMMessageCallback *callback) { m_rdm_callback.reset(callback); } diff --git a/tools/e133/DeviceManagerImpl.h b/tools/e133/DeviceManagerImpl.h index 90bee905ef..5f0c7ad1c4 100644 --- a/tools/e133/DeviceManagerImpl.h +++ b/tools/e133/DeviceManagerImpl.h @@ -70,7 +70,7 @@ class DeviceManagerImpl { * @returns true if the data should be acknowledged, false otherwise. */ typedef ola::Callback3 RDMMesssageCallback; + const string&> RDMMessageCallback; // Run when we acquire designated controller status for a device. typedef ola::Callback1 AcquireDeviceCallback; @@ -83,7 +83,7 @@ class DeviceManagerImpl { ~DeviceManagerImpl(); // Ownership of the callbacks is transferred. - void SetRDMMessageCallback(RDMMesssageCallback *callback); + void SetRDMMessageCallback(RDMMessageCallback *callback); void SetAcquireDeviceCallback(AcquireDeviceCallback *callback); void SetReleaseDeviceCallback(ReleaseDeviceCallback *callback); @@ -98,7 +98,7 @@ class DeviceManagerImpl { DeviceMap; DeviceMap m_device_map; - auto_ptr m_rdm_callback; + auto_ptr m_rdm_callback; auto_ptr m_acquire_device_cb_; auto_ptr m_release_device_cb_; diff --git a/tools/e133/E133Device.cpp b/tools/e133/E133Device.cpp index ca2a4a69f9..4411d035cd 100644 --- a/tools/e133/E133Device.cpp +++ b/tools/e133/E133Device.cpp @@ -42,7 +42,7 @@ #include "tools/e133/E133Device.h" #include "tools/e133/E133Endpoint.h" -#include "tools/e133/E133HealthCheckedConnection.h" +#include "libs/acn/E133HealthCheckedConnection.h" #include "tools/e133/EndpointManager.h" #include "tools/e133/TCPConnectionStats.h" diff --git a/tools/e133/E133Endpoint.cpp b/tools/e133/E133Endpoint.cpp index f0fbd44927..58efa583fa 100644 --- a/tools/e133/E133Endpoint.cpp +++ b/tools/e133/E133Endpoint.cpp @@ -44,7 +44,7 @@ E133Endpoint::E133Endpoint(DiscoverableRDMControllerInterface *controller, m_is_physical(properties.is_physical), m_universe(UNPATCHED_UNIVERSE), m_endpoint_label(""), - m_device_list_change(0), + m_responder_list_change(0), m_controller(controller) { } diff --git a/tools/e133/E133Endpoint.h b/tools/e133/E133Endpoint.h index cbcc1cb3d6..f1d9e174e5 100644 --- a/tools/e133/E133Endpoint.h +++ b/tools/e133/E133Endpoint.h @@ -29,7 +29,7 @@ using ola::rdm::UIDSet; using std::string; - +// TODO(Peter): Can this become NULL_ENDPOINT? static const uint16_t ROOT_E133_ENDPOINT = 0; /** @@ -81,11 +81,11 @@ class E133EndpointInterface // ENDPOINT_TIMING_DESCRIPTION - // ENDPOINT_DEVICE_LIST_CHANGE - virtual uint32_t device_list_change() const = 0; + // ENDPOINT_RESPONDER_LIST_CHANGE + virtual uint32_t responder_list_change() const = 0; - // ENDPOINT_DEVICES - virtual void EndpointDevices(UIDSet *uids) const = 0; + // ENDPOINT_RESPONDERS + virtual void EndpointResponders(UIDSet *uids) const = 0; // BINDING_AND_CONTROL_FIELDS @@ -96,7 +96,7 @@ class E133EndpointInterface /** * An E133Endpoint which wraps another RDM controller. This just passes - * everything through the to controller. + * everything through to the controller. */ class E133Endpoint: public E133EndpointInterface { public: @@ -141,8 +141,8 @@ class E133Endpoint: public E133EndpointInterface { m_endpoint_label = endpoint_label; } - uint32_t device_list_change() const { return m_device_list_change; } - void EndpointDevices(UIDSet *uids) const { *uids = m_uids; } + uint32_t responder_list_change() const { return m_responder_list_change; } + void EndpointResponders(UIDSet *uids) const { *uids = m_uids; } virtual void RunFullDiscovery(ola::rdm::RDMDiscoveryCallback *callback); virtual void RunIncrementalDiscovery( @@ -156,7 +156,7 @@ class E133Endpoint: public E133EndpointInterface { const bool m_is_physical; uint16_t m_universe; string m_endpoint_label; - uint32_t m_device_list_change; + uint32_t m_responder_list_change; UIDSet m_uids; DiscoverableRDMControllerInterface *m_controller; }; diff --git a/tools/e133/E133StatusHelper.cpp b/tools/e133/E133StatusHelper.cpp deleted file mode 100644 index a87376425a..0000000000 --- a/tools/e133/E133StatusHelper.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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. - * - * E133StatusHelper.cpp - * Functions for dealing with E1.33 Status Codes. - * Copyright (C) 2013 Simon Newton - */ - -#include -#include -#include "ola/e133/E133StatusHelper.h" - -namespace ola { -namespace e133 { - -using std::string; - -/** - * Verify that the int is a valid E1.33 Status Code. - */ -bool IntToStatusCode(uint16_t input, E133StatusCode *status_code) { - switch (input) { - case ola::e133::SC_E133_ACK: - *status_code = ola::e133::SC_E133_ACK; - return true; - case ola::e133::SC_E133_RDM_TIMEOUT: - *status_code = ola::e133::SC_E133_RDM_TIMEOUT; - return true; - case ola::e133::SC_E133_RDM_INVALID_RESPONSE: - *status_code = ola::e133::SC_E133_RDM_INVALID_RESPONSE; - return true; - case ola::e133::SC_E133_BUFFER_FULL: - *status_code = ola::e133::SC_E133_BUFFER_FULL; - return true; - case ola::e133::SC_E133_UNKNOWN_UID: - *status_code = ola::e133::SC_E133_UNKNOWN_UID; - return true; - case ola::e133::SC_E133_NONEXISTENT_ENDPOINT: - *status_code = ola::e133::SC_E133_NONEXISTENT_ENDPOINT; - return true; - case ola::e133::SC_E133_WRONG_ENDPOINT: - *status_code = ola::e133::SC_E133_WRONG_ENDPOINT; - return true; - case ola::e133::SC_E133_ACK_OVERFLOW_CACHE_EXPIRED: - *status_code = ola::e133::SC_E133_ACK_OVERFLOW_CACHE_EXPIRED; - return true; - case ola::e133::SC_E133_ACK_OVERFLOW_IN_PROGRESS: - *status_code = ola::e133::SC_E133_ACK_OVERFLOW_IN_PROGRESS; - return true; - case ola::e133::SC_E133_BROADCAST_COMPLETE: - *status_code = ola::e133::SC_E133_BROADCAST_COMPLETE; - return true; - default: - return false; - } -} - - -/** - * Return a text string describing this status code. - */ -string StatusCodeToString(E133StatusCode status_code) { - switch (status_code) { - case ola::e133::SC_E133_ACK: - return "Acknowledged"; - case ola::e133::SC_E133_RDM_TIMEOUT: - return "Response Timeout"; - case ola::e133::SC_E133_RDM_INVALID_RESPONSE: - return "Invalid Response"; - case ola::e133::SC_E133_BUFFER_FULL: - return "Buffer Full"; - case ola::e133::SC_E133_UNKNOWN_UID: - return "Unknown UID"; - case ola::e133::SC_E133_NONEXISTENT_ENDPOINT: - return "Endpoint doesn't exist"; - case ola::e133::SC_E133_WRONG_ENDPOINT: - return "Wrong endpoint"; - case ola::e133::SC_E133_ACK_OVERFLOW_CACHE_EXPIRED: - return "Ack overflow cache expired"; - case ola::e133::SC_E133_ACK_OVERFLOW_IN_PROGRESS: - return "Ack overflow in progress"; - case ola::e133::SC_E133_BROADCAST_COMPLETE: - return "Request was broadcast"; - } - return "Unknown E1.33 Status Code"; -} -} // namespace e133 -} // namespace ola diff --git a/tools/e133/Makefile.mk b/tools/e133/Makefile.mk index 76f23cdf21..76377e1add 100644 --- a/tools/e133/Makefile.mk +++ b/tools/e133/Makefile.mk @@ -22,12 +22,10 @@ endif # libolae133common # Code required by both the controller and device. tools_e133_libolae133common_la_SOURCES = \ - tools/e133/E133HealthCheckedConnection.cpp \ - tools/e133/E133HealthCheckedConnection.h \ - tools/e133/E133Receiver.cpp \ - tools/e133/E133StatusHelper.cpp \ - tools/e133/MessageBuilder.cpp -tools_e133_libolae133common_la_LIBADD = libs/acn/libolae131core.la + tools/e133/E133Receiver.cpp +tools_e133_libolae133common_la_LIBADD = \ + libs/acn/libolae131core.la \ + libs/acn/libolae133core.la # libolae133controller # Controller side. @@ -38,6 +36,7 @@ tools_e133_libolae133controller_la_SOURCES = \ tools_e133_libolae133controller_la_LIBADD = \ common/libolacommon.la \ libs/acn/libolae131core.la \ + libs/acn/libolae133core.la \ tools/e133/libolae133common.la # libolae133device @@ -60,6 +59,7 @@ tools_e133_libolae133device_la_SOURCES = \ tools_e133_libolae133device_la_LIBADD = \ common/libolacommon.la \ libs/acn/libolae131core.la \ + libs/acn/libolae133core.la \ tools/e133/libolae133common.la @@ -77,6 +77,7 @@ noinst_PROGRAMS += \ tools_e133_e133_receiver_SOURCES = tools/e133/e133-receiver.cpp tools_e133_e133_receiver_LDADD = common/libolacommon.la \ libs/acn/libolaacn.la \ + libs/acn/libolae133core.la \ plugins/usbpro/libolausbprowidget.la \ tools/e133/libolae133device.la @@ -87,6 +88,7 @@ endif tools_e133_e133_monitor_SOURCES = tools/e133/e133-monitor.cpp tools_e133_e133_monitor_LDADD = common/libolacommon.la \ libs/acn/libolaacn.la \ + libs/acn/libolae133core.la \ tools/e133/libolae133common.la \ tools/e133/libolae133controller.la @@ -94,25 +96,30 @@ tools_e133_e133_controller_SOURCES = tools/e133/e133-controller.cpp # required for PID_DATA_FILE tools_e133_e133_controller_LDADD = common/libolacommon.la \ libs/acn/libolae131core.la \ + libs/acn/libolae133core.la \ tools/e133/libolae133common.la \ tools/e133/libolae133controller.la tools_e133_basic_controller_SOURCES = tools/e133/basic-controller.cpp tools_e133_basic_controller_LDADD = common/libolacommon.la \ libs/acn/libolaacn.la \ + libs/acn/libolae133core.la \ tools/e133/libolae133common.la tools_e133_basic_device_SOURCES = tools/e133/basic-device.cpp tools_e133_basic_device_LDADD = common/libolacommon.la \ libs/acn/libolaacn.la \ + libs/acn/libolae133core.la \ tools/e133/libolae133common.la tools_e133_llrp_manager_SOURCES = tools/e133/llrp-manager.cpp tools_e133_llrp_manager_LDADD = common/libolacommon.la \ libs/acn/libolaacn.la \ + libs/acn/libolae133core.la \ tools/e133/libolae133common.la tools_e133_llrp_target_SOURCES = tools/e133/llrp-target.cpp tools_e133_llrp_target_LDADD = common/libolacommon.la \ libs/acn/libolaacn.la \ + libs/acn/libolae133core.la \ tools/e133/libolae133common.la diff --git a/tools/e133/ManagementEndpoint.cpp b/tools/e133/ManagementEndpoint.cpp index af67274a5c..4c1ca18ed6 100644 --- a/tools/e133/ManagementEndpoint.cpp +++ b/tools/e133/ManagementEndpoint.cpp @@ -59,9 +59,9 @@ const ola::rdm::ResponderOps::ParamHandler { ola::rdm::PID_ENDPOINT_LIST_CHANGE, &ManagementEndpoint::GetEndpointListChange, NULL}, - { ola::rdm::PID_ENDPOINT_IDENTIFY, - &ManagementEndpoint::GetEndpointIdentify, - &ManagementEndpoint::SetEndpointIdentify}, + { ola::rdm::PID_IDENTIFY_ENDPOINT, + &ManagementEndpoint::GetIdentifyEndpoint, + &ManagementEndpoint::SetIdentifyEndpoint}, { ola::rdm::PID_ENDPOINT_TO_UNIVERSE, &ManagementEndpoint::GetEndpointToUniverse, &ManagementEndpoint::SetEndpointToUniverse}, @@ -76,11 +76,11 @@ const ola::rdm::ResponderOps::ParamHandler // PID_BACKGROUND_DISCOVERY // PID_ENDPOINT_TIMING // PID_ENDPOINT_TIMING_DESCRIPTION - { ola::rdm::PID_ENDPOINT_LIST_CHANGE, - &ManagementEndpoint::GetEndpointDeviceListChange, + { ola::rdm::PID_ENDPOINT_RESPONDER_LIST_CHANGE, + &ManagementEndpoint::GetEndpointResponderListChange, NULL}, - { ola::rdm::PID_ENDPOINT_DEVICES, - &ManagementEndpoint::GetEndpointDevices, + { ola::rdm::PID_ENDPOINT_RESPONDERS, + &ManagementEndpoint::GetEndpointResponders, NULL}, // PID_BINDING_AND_CONTROL_FIELDS { ola::rdm::PID_TCP_COMMS_STATUS, @@ -222,9 +222,9 @@ RDMResponse *ManagementEndpoint::GetEndpointListChange( /** - * Handle PID_ENDPOINT_IDENTIFY + * Handle PID_IDENTIFY_ENDPOINT */ -RDMResponse *ManagementEndpoint::GetEndpointIdentify( +RDMResponse *ManagementEndpoint::GetIdentifyEndpoint( const RDMRequest *request) { uint16_t endpoint_id; if (!ResponderHelper::ExtractUInt16(request, &endpoint_id)) { @@ -243,41 +243,41 @@ RDMResponse *ManagementEndpoint::GetEndpointIdentify( uint16_t endpoint_number; uint8_t identify_mode; }); - IdentifyEndpointParamData endpoint_identify_message = { + IdentifyEndpointParamData identify_endpoint_message = { HostToNetwork(endpoint_id), endpoint->identify_mode() }; return GetResponseFromData( request, - reinterpret_cast(&endpoint_identify_message), - sizeof(endpoint_identify_message)); + reinterpret_cast(&identify_endpoint_message), + sizeof(identify_endpoint_message)); } -RDMResponse *ManagementEndpoint::SetEndpointIdentify( +RDMResponse *ManagementEndpoint::SetIdentifyEndpoint( const RDMRequest *request) { PACK( struct IdentifyEndpointParamData { uint16_t endpoint_number; uint8_t identify_mode; }); - IdentifyEndpointParamData endpoint_identify_message; + IdentifyEndpointParamData identify_endpoint_message; - if (request->ParamDataSize() != sizeof(endpoint_identify_message)) { + if (request->ParamDataSize() != sizeof(identify_endpoint_message)) { return NackWithReason(request, NR_FORMAT_ERROR); } - memcpy(reinterpret_cast(&endpoint_identify_message), + memcpy(reinterpret_cast(&identify_endpoint_message), request->ParamData(), - sizeof(endpoint_identify_message)); + sizeof(identify_endpoint_message)); E133Endpoint *endpoint = m_endpoint_manager->GetEndpoint( - ola::network::NetworkToHost(endpoint_identify_message.endpoint_number)); + ola::network::NetworkToHost(identify_endpoint_message.endpoint_number)); // endpoint not found if (!endpoint) { return NackWithReason(request, ola::rdm::NR_ENDPOINT_NUMBER_INVALID); } - endpoint->set_identify_mode(endpoint_identify_message.identify_mode); + endpoint->set_identify_mode(identify_endpoint_message.identify_mode); return GetResponseFromData(request, NULL, 0); } @@ -324,9 +324,9 @@ RDMResponse *ManagementEndpoint::SetEndpointLabel(const RDMRequest *request) { } /** - * Handle PID_ENDPOINT_DEVICE_LIST_CHANGE + * Handle PID_ENDPOINT_RESPONDER_LIST_CHANGE */ -RDMResponse *ManagementEndpoint::GetEndpointDeviceListChange( +RDMResponse *ManagementEndpoint::GetEndpointResponderListChange( const RDMRequest *request) { uint16_t endpoint_id; if (!ResponderHelper::ExtractUInt16(request, &endpoint_id)) { @@ -340,7 +340,7 @@ RDMResponse *ManagementEndpoint::GetEndpointDeviceListChange( return NackWithReason(request, ola::rdm::NR_ENDPOINT_NUMBER_INVALID); } - uint32_t list_change_id = HostToNetwork(endpoint->device_list_change()); + uint32_t list_change_id = HostToNetwork(endpoint->responder_list_change()); return GetResponseFromData( request, reinterpret_cast(&list_change_id), @@ -348,9 +348,10 @@ RDMResponse *ManagementEndpoint::GetEndpointDeviceListChange( } /** - * Handle PID_ENDPOINT_DEVICES + * Handle PID_ENDPOINT_RESPONDERS */ -RDMResponse *ManagementEndpoint::GetEndpointDevices(const RDMRequest *request) { +RDMResponse *ManagementEndpoint::GetEndpointResponders( + const RDMRequest *request) { uint16_t endpoint_id; if (!ResponderHelper::ExtractUInt16(request, &endpoint_id)) { return NackWithReason(request, NR_FORMAT_ERROR); @@ -369,21 +370,21 @@ RDMResponse *ManagementEndpoint::GetEndpointDevices(const RDMRequest *request) { } UIDSet uids; - uint32_t list_change_id = HostToNetwork(endpoint->device_list_change()); - endpoint->EndpointDevices(&uids); + uint32_t list_change_id = HostToNetwork(endpoint->responder_list_change()); + endpoint->EndpointResponders(&uids); - struct DeviceListParamData { + struct ResponderListParamData { uint16_t endpoint; uint32_t list_change; uint8_t data[0]; }; // TODO(simon): fix this - we can overflow an RDM packet if there are too - // many devices! + // many responders! unsigned int param_data_size = 2 + 4 + uids.Size() * UID::UID_SIZE; uint8_t *raw_data = new uint8_t[param_data_size]; - DeviceListParamData *param_data = reinterpret_cast( - raw_data); + ResponderListParamData *param_data = + reinterpret_cast(raw_data); // TODO(simon): fix this to track changes. param_data->endpoint = HostToNetwork(endpoint_id); diff --git a/tools/e133/ManagementEndpoint.h b/tools/e133/ManagementEndpoint.h index d54b508eb4..79ccc80cfe 100644 --- a/tools/e133/ManagementEndpoint.h +++ b/tools/e133/ManagementEndpoint.h @@ -30,7 +30,7 @@ using ola::rdm::RDMResponse; using ola::rdm::RDMCallback; /** - * The ManagementEndpoint handles RDMCommands directed at this E1.33 device. It + * The ManagementEndpoint handles RDMCommands directed at this E1.33 Component. It * can also pass through commands to another controller if there is one * supplied. */ @@ -50,7 +50,7 @@ class ManagementEndpoint: public E133Endpoint { private: /** - * The RDM Operations for the MovingLightResponder. + * The RDM Operations for the ManagementEndpoint. */ class RDMOps : public ola::rdm::ResponderOps { public: @@ -74,16 +74,16 @@ class ManagementEndpoint: public E133Endpoint { // RDM PID handlers. RDMResponse *GetEndpointList(const RDMRequest *request); RDMResponse *GetEndpointListChange(const RDMRequest *request); - RDMResponse *GetEndpointIdentify(const RDMRequest *request); - RDMResponse *SetEndpointIdentify(const RDMRequest *request); + RDMResponse *GetIdentifyEndpoint(const RDMRequest *request); + RDMResponse *SetIdentifyEndpoint(const RDMRequest *request); RDMResponse *GetEndpointToUniverse(const RDMRequest *request); RDMResponse *SetEndpointToUniverse(const RDMRequest *request); RDMResponse *GetEndpointMode(const RDMRequest *request); RDMResponse *SetEndpointMode(const RDMRequest *request); RDMResponse *GetEndpointLabel(const RDMRequest *request); RDMResponse *SetEndpointLabel(const RDMRequest *request); - RDMResponse *GetEndpointDeviceListChange(const RDMRequest *request); - RDMResponse *GetEndpointDevices(const RDMRequest *request); + RDMResponse *GetEndpointResponderListChange(const RDMRequest *request); + RDMResponse *GetEndpointResponders(const RDMRequest *request); RDMResponse *GetTCPCommsStatus(const RDMRequest *request); RDMResponse *SetTCPCommsStatus(const RDMRequest *request); diff --git a/tools/e133/basic-controller.cpp b/tools/e133/basic-controller.cpp index 4df2ddcfb3..02bfd3825e 100644 --- a/tools/e133/basic-controller.cpp +++ b/tools/e133/basic-controller.cpp @@ -42,9 +42,9 @@ #include #include +#include "libs/acn/E133HealthCheckedConnection.h" #include "libs/acn/RootInflator.h" #include "libs/acn/TCPTransport.h" -#include "tools/e133/E133HealthCheckedConnection.h" DEFINE_string(listen_ip, "", "The IP Address to listen on"); DEFINE_uint16(listen_port, 5569, "The port to listen on"); diff --git a/tools/e133/basic-device.cpp b/tools/e133/basic-device.cpp index bdc87afc22..3d30c99a9a 100644 --- a/tools/e133/basic-device.cpp +++ b/tools/e133/basic-device.cpp @@ -39,9 +39,9 @@ #include #include +#include "libs/acn/E133HealthCheckedConnection.h" #include "libs/acn/RootInflator.h" #include "libs/acn/TCPTransport.h" -#include "tools/e133/E133HealthCheckedConnection.h" DEFINE_string(controller_ip, "", "The IP Address of the Controller"); DEFINE_uint16(controller_port, 5569, "The port on the controller"); diff --git a/tools/rdm/ResponderTest.py b/tools/rdm/ResponderTest.py index c83388f19a..2ef001d352 100644 --- a/tools/rdm/ResponderTest.py +++ b/tools/rdm/ResponderTest.py @@ -694,15 +694,15 @@ def _CheckForAckOrNack(self, response, unpacked_data, unpack_exception): @staticmethod def _EscapeData(data): - if type(data) == list: + if isinstance(data, list): return [ResponderTestFixture._EscapeData(i) for i in data] - elif type(data) == dict: + elif isinstance(data, dict): d = {} for k, v in data.items(): # We can't escape the key as then it may become a new key d[k] = ResponderTestFixture._EscapeData(v) return d - elif type(data) == str or type(data) == unicode: + elif isinstance(data, str) or isinstance(data, unicode): return StringEscape(data) else: return data diff --git a/tools/rdm/TestDefinitions.py b/tools/rdm/TestDefinitions.py index 696d0d05b1..68069ea354 100644 --- a/tools/rdm/TestDefinitions.py +++ b/tools/rdm/TestDefinitions.py @@ -8059,7 +8059,7 @@ class SetEndpointModeWithExtraData(TestMixins.SetWithDataMixin, OptionalParameterTestFixture): """Send a SET ENDPOINT_MODE command with extra data.""" PID = 'ENDPOINT_MODE' - DATA = 'foobar' + DATA = b'foobar' class AllSubDevicesGetEndpointLabel(TestMixins.AllSubDevicesGetMixin, @@ -8178,7 +8178,7 @@ class SetEndpointTimingWithExtraData(TestMixins.SetWithDataMixin, OptionalParameterTestFixture): """Send a SET ENDPOINT_TIMING command with extra data.""" PID = 'ENDPOINT_TIMING' - DATA = 'foobar' + DATA = b'foobar' class AllSubDevicesGetEndpointTimingDescription( @@ -8438,67 +8438,67 @@ class SetEndpointToUniverseWithExtraData(TestMixins.SetWithDataMixin, OptionalParameterTestFixture): """Send a SET ENDPOINT_TO_UNIVERSE command with extra data.""" PID = 'ENDPOINT_TO_UNIVERSE' - DATA = 'foobar' + DATA = b'foobar' -class AllSubDevicesGetRdmTrafficEnable(TestMixins.AllSubDevicesGetMixin, +class AllSubDevicesGetRDMTrafficEnable(TestMixins.AllSubDevicesGetMixin, OptionalParameterTestFixture): """Send a get RDM_TRAFFIC_ENABLE to ALL_SUB_DEVICES.""" PID = 'RDM_TRAFFIC_ENABLE' DATA = [0x0001] -# class GetRdmTrafficEnable(TestMixins., +# class GetRDMTrafficEnable(TestMixins., # OptionalParameterTestFixture): # CATEGORY = TestCategory. # PID = 'RDM_TRAFFIC_ENABLE' # TODO(peter): Test get -class GetZeroRdmTrafficEnable(TestMixins.GetZeroUInt16Mixin, +class GetZeroRDMTrafficEnable(TestMixins.GetZeroUInt16Mixin, OptionalParameterTestFixture): """GET RDM_TRAFFIC_ENABLE for endpoint id 0.""" PID = 'RDM_TRAFFIC_ENABLE' OVERRIDE_NACKS = [RDMNack.NR_ENDPOINT_NUMBER_INVALID] -class GetRdmTrafficEnableWithNoData(TestMixins.GetWithNoDataMixin, +class GetRDMTrafficEnableWithNoData(TestMixins.GetWithNoDataMixin, OptionalParameterTestFixture): """GET RDM_TRAFFIC_ENABLE with no argument given.""" PID = 'RDM_TRAFFIC_ENABLE' -class GetRdmTrafficEnableWithExtraData(TestMixins.GetWithDataMixin, +class GetRDMTrafficEnableWithExtraData(TestMixins.GetWithDataMixin, OptionalParameterTestFixture): """GET RDM_TRAFFIC_ENABLE with more than 2 bytes of data.""" PID = 'RDM_TRAFFIC_ENABLE' -# class SetRdmTrafficEnable(TestMixins., +# class SetRDMTrafficEnable(TestMixins., # OptionalParameterTestFixture): # CATEGORY = TestCategory. # PID = 'RDM_TRAFFIC_ENABLE' # TODO(peter): Test set -# class SetZeroRdmTrafficEnable(TestMixins.SetZero, +# class SetZeroRDMTrafficEnable(TestMixins.SetZero, # OptionalParameterTestFixture): # """SET RDM_TRAFFIC_ENABLE for endpoint id 0.""" # PID = 'RDM_TRAFFIC_ENABLE' # TODO(peter): Test set zero -class SetRdmTrafficEnableWithNoData(TestMixins.SetWithNoDataMixin, +class SetRDMTrafficEnableWithNoData(TestMixins.SetWithNoDataMixin, OptionalParameterTestFixture): """Set RDM_TRAFFIC_ENABLE command with no data.""" PID = 'RDM_TRAFFIC_ENABLE' -class SetRdmTrafficEnableWithExtraData(TestMixins.SetWithDataMixin, +class SetRDMTrafficEnableWithExtraData(TestMixins.SetWithDataMixin, OptionalParameterTestFixture): """Send a SET RDM_TRAFFIC_ENABLE command with extra data.""" PID = 'RDM_TRAFFIC_ENABLE' - DATA = 'foobar' + DATA = b'foobar' class AllSubDevicesGetDiscoveryState(TestMixins.AllSubDevicesGetMixin, @@ -8558,7 +8558,7 @@ class SetDiscoveryStateWithExtraData(TestMixins.SetWithDataMixin, OptionalParameterTestFixture): """Send a SET DISCOVERY_STATE command with extra data.""" PID = 'DISCOVERY_STATE' - DATA = 'foobar' + DATA = b'foobar' class AllSubDevicesGetBackgroundDiscovery(TestMixins.AllSubDevicesGetMixin, @@ -8618,7 +8618,7 @@ class SetBackgroundDiscoveryWithExtraData(TestMixins.SetWithDataMixin, OptionalParameterTestFixture): """Send a SET BACKGROUND_DISCOVERY command with extra data.""" PID = 'BACKGROUND_DISCOVERY' - DATA = 'foobar' + DATA = b'foobar' class AllSubDevicesGetBackgroundQueuedStatusPolicy( @@ -8654,8 +8654,9 @@ class SetBackgroundQueuedStatusPolicyWithNoData(TestMixins.SetWithNoDataMixin, PID = 'BACKGROUND_QUEUED_STATUS_POLICY' -class SetBackgroundQueuedStatusPolicyWithExtraData(TestMixins.SetWithDataMixin, - OptionalParameterTestFixture): +class SetBackgroundQueuedStatusPolicyWithExtraData( + TestMixins.SetWithDataMixin, + OptionalParameterTestFixture): """Send a SET BACKGROUND_QUEUED_STATUS_POLICY command with extra data.""" PID = 'BACKGROUND_QUEUED_STATUS_POLICY' @@ -8663,13 +8664,16 @@ class SetBackgroundQueuedStatusPolicyWithExtraData(TestMixins.SetWithDataMixin, class AllSubDevicesGetBackgroundQueuedStatusPolicyDescription( TestMixins.AllSubDevicesGetMixin, OptionalParameterTestFixture): - """Send a get BACKGROUND_QUEUED_STATUS_POLICY_DESCRIPTION to ALL_SUB_DEVICES.""" + """Send a get BACKGROUND_QUEUED_STATUS_POLICY_DESCRIPTION to + ALL_SUB_DEVICES. + """ PID = 'BACKGROUND_QUEUED_STATUS_POLICY_DESCRIPTION' DATA = [0x00] -# class GetBackgroundQueuedStatusPolicyDescription(TestMixins., -# OptionalParameterTestFixture): +# class GetBackgroundQueuedStatusPolicyDescription( +# TestMixins., +# OptionalParameterTestFixture): # CATEGORY = TestCategory. # PID = 'BACKGROUND_QUEUED_STATUS_POLICY_DESCRIPTION' # TODO(peter): Test get @@ -8685,7 +8689,9 @@ class GetBackgroundQueuedStatusPolicyDescriptionWithNoData( class GetBackgroundQueuedStatusPolicyDescriptionWithExtraData( TestMixins.GetWithDataMixin, OptionalParameterTestFixture): - """GET BACKGROUND_QUEUED_STATUS_POLICY_DESCRIPTION with more than 1 byte of data.""" + """GET BACKGROUND_QUEUED_STATUS_POLICY_DESCRIPTION with more than 1 byte of + data. + """ PID = 'BACKGROUND_QUEUED_STATUS_POLICY_DESCRIPTION' diff --git a/tools/rdm/TestHelpers.py b/tools/rdm/TestHelpers.py index 51eb606de7..4a157bcf63 100644 --- a/tools/rdm/TestHelpers.py +++ b/tools/rdm/TestHelpers.py @@ -31,7 +31,7 @@ def ContainsUnprintable(s): """Check if a string s contain unprintable characters.""" # TODO(Peter): How does this interact with the E1.20 Unicode flag? - if type(s) == str or type(s) == unicode: + if isinstance(s, str) or isinstance(s, unicode): # All strings in Python 3 are unicode, Python 2 ones might not be return s != StringEscape(s) else: diff --git a/tools/rdm/list_rdm_tests.py b/tools/rdm/list_rdm_tests.py index 000666fa59..fc7a818469 100755 --- a/tools/rdm/list_rdm_tests.py +++ b/tools/rdm/list_rdm_tests.py @@ -101,7 +101,7 @@ def AllSubDevicesGet(names, pid, pid_test_base_name, get_size): (pid.name)) print(' PID = \'%s\'' % (pid.name)) if get_size > 0: - print(' #DATA = [] # TODO(%s): Specify some suitable data, %d byte%s' % + print(' # DATA = [] # TODO(%s): Specify some suitable data, %d byte%s' % (getpass.getuser(), get_size, 's' if get_size > 1 else '')) print('') print('') @@ -153,15 +153,18 @@ def GetWithExtraData(names, pid, pid_test_base_name, get_size): print(' PID = \'%s\'' % (pid.name)) dummy_data = GenerateDummyData(get_size) if dummy_data is None: - print((" #DATA = 'foo' # TODO(%s): Specify extra data if this isn't " - "enough. Ensure the first %d bytes are sane/valid.") % (getpass.getuser(), get_size)) + print((" # DATA = b'foo' # TODO(%s): Specify extra data if this isn't " + "enough.") % (getpass.getuser())) + print(" # Ensure the first %d bytes are sane/valid." % (get_size)) elif dummy_data != 'foo': # Doesn't match default, explicitly set value - print((" DATA = '%s' # TODO(%s): Specify extra data if this isn't " - "enough. Ensure the first %d bytes are sane/valid.") % (dummy_data, getpass.getuser(), get_size)) + print((" DATA = b'%s' # TODO(%s): Specify extra data if this isn't " + "enough.") % (dummy_data, getpass.getuser())) + print(" # Ensure the first %d bytes are sane/valid." % (get_size)) else: - print((" #DATA = '%s' # TODO(%s): Specify extra data if this isn't " - "enough. Ensure the first %d bytes are sane/valid.") % (dummy_data, getpass.getuser(), get_size)) + print((" # DATA = b'%s' # TODO(%s): Specify extra data if this isn't " + "enough.") % (dummy_data, getpass.getuser())) + print(" # Ensure the first %d bytes are sane/valid." % (get_size)) print('') print('') @@ -276,15 +279,18 @@ def SetWithExtraData(names, pid, pid_test_base_name, set_size): print(' PID = \'%s\'' % (pid.name)) dummy_data = GenerateDummyData(set_size) if dummy_data is None: - print((" #DATA = 'foo' # TODO(%s): Specify extra data if this isn't " - "enough. Ensure the first %d bytes are sane/valid.") % (getpass.getuser(), set_size)) + print((" # DATA = b'foo' # TODO(%s): Specify extra data if this isn't " + "enough.") % (getpass.getuser())) + print(" # Ensure the first %d bytes are sane/valid." % (set_size)) elif dummy_data != 'foo': # Doesn't match default, explicitly set value - print((" DATA = '%s' # TODO(%s): Specify extra data if this isn't " - "enough. Ensure the first %d bytes are sane/valid.") % (dummy_data, getpass.getuser(), set_size)) + print((" DATA = b'%s' # TODO(%s): Specify extra data if this isn't " + "enough.") % (dummy_data, getpass.getuser())) + print(" # Ensure the first %d bytes are sane/valid." % (set_size)) else: - print((" #DATA = '%s' # TODO(%s): Specify extra data if this isn't " - "enough. Ensure the first %d bytes are sane/valid.") % (dummy_data, getpass.getuser(), set_size)) + print((" # DATA = b'%s' # TODO(%s): Specify extra data if this isn't " + "enough.") % (dummy_data, getpass.getuser())) + print(" # Ensure the first %d bytes are sane/valid." % (set_size)) print('') print('') @@ -353,7 +359,7 @@ def main(): (pid.GetRequest(PidStore.RDM_GET).HasAtoms())): for atom in pid.GetRequest(PidStore.RDM_GET).GetAtoms(): get_size += atom.size - #TODO(Peter): Should we just print this total all the time? + # TODO(Peter): Should we just print this total all the time? if get_size != pid.GetRequest(PidStore.RDM_GET).GetAtoms()[0].size: print('# Get requires %d bytes' % (get_size)) diff --git a/tools/rdm/rdm_test_server.py b/tools/rdm/rdm_test_server.py index 80158c4c2e..62588be6ec 100755 --- a/tools/rdm/rdm_test_server.py +++ b/tools/rdm/rdm_test_server.py @@ -846,7 +846,7 @@ def _CheckValidUniverse(self, request): request: the HTTPRequest object. Returns: - The santitized universe id. + The sanitized universe id. Raises: ServerException if the universe isn't valid or doesn't exist.