From 90e9c66eb71cb4c6786437908712f351f1db1e2b Mon Sep 17 00:00:00 2001 From: Jan-Philipp Date: Sun, 16 Jun 2024 15:12:29 +0200 Subject: [PATCH] feat: add ArtNzs opcode --- Artnet/ArtNzs.h | 88 +++++++++++++++++++++++++++++++++++++++++++++++ Artnet/Receiver.h | 40 ++++++++++++++++++++- Artnet/Sender.h | 80 +++++++++++++++++++++++++++++++++++++++--- README.md | 1 + 4 files changed, 203 insertions(+), 6 deletions(-) create mode 100644 Artnet/ArtNzs.h diff --git a/Artnet/ArtNzs.h b/Artnet/ArtNzs.h new file mode 100644 index 0000000..688c8ed --- /dev/null +++ b/Artnet/ArtNzs.h @@ -0,0 +1,88 @@ +#pragma once +#ifndef ARTNET_ARTNZS_H +#define ARTNET_ARTNZS_H + +#include "Common.h" +#include +#include + +namespace art_net { +namespace art_nzs { + +enum Index : uint16_t +{ + ID = 0, + OP_CODE_L = 8, + OP_CODE_H = 9, + PROTOCOL_VER_H = 10, + PROTOCOL_VER_L = 11, + SEQUENCE = 12, + START_CODE = 13, + SUBUNI = 14, + NET = 15, + LENGTH_H = 16, + LENGTH_L = 17, + DATA = 18 +}; + +struct Metadata +{ + uint8_t sequence; + uint8_t start_code; + uint8_t net; + uint8_t subnet; + uint8_t universe; +}; + +using CallbackType = std::function; +#if ARX_HAVE_LIBSTDCPLUSPLUS >= 201103L // Have libstdc++11 +// receiver +using CallbackMap = std::map; +#else +// receiver +using CallbackMap = arx::stdx::map; +#endif + +inline Metadata generateMetadataFrom(const uint8_t *packet) +{ + Metadata metadata; + metadata.sequence = packet[SEQUENCE]; + metadata.start_code = packet[START_CODE]; + metadata.net = packet[NET]; + metadata.subnet = (packet[SUBUNI] >> 4) & 0x0F; + metadata.universe = (packet[SUBUNI] >> 0) & 0x0F; + return metadata; +} + +inline void setMetadataTo(uint8_t *packet, uint8_t sequence, uint8_t start_code, uint8_t net, uint8_t subnet, uint8_t universe) +{ + for (size_t i = 0; i < ID_LENGTH; i++) { + packet[i] = static_cast(ARTNET_ID[i]); + } + packet[OP_CODE_L] = (static_cast(OpCode::Dmx) >> 0) & 0x00FF; + packet[OP_CODE_H] = (static_cast(OpCode::Dmx) >> 8) & 0x00FF; + packet[PROTOCOL_VER_H] = (PROTOCOL_VER >> 8) & 0x00FF; + packet[PROTOCOL_VER_L] = (PROTOCOL_VER >> 0) & 0x00FF; + packet[SEQUENCE] = sequence; + packet[START_CODE] = start_code & 0x03; + packet[NET] = net & 0x7F; + packet[SUBUNI] = ((subnet & 0x0F) << 4) | (universe & 0x0F); + packet[LENGTH_H] = (512 >> 8) & 0xFF; + packet[LENGTH_L] = (512 >> 0) & 0xFF; +} + +inline void setDataTo(uint8_t *packet, const uint8_t* const data, uint16_t size) +{ + memcpy(packet + art_nzs::DATA, data, size); +} +inline void setDataTo(uint8_t *packet, const uint16_t ch, const uint8_t data) +{ + packet[art_nzs::DATA + ch] = data; +} + +} // namespace art_nzs +} // namespace art_net + +using ArtNzsMetadata = art_net::art_nzs::Metadata; + +#endif // ARTNET_ARTNZS_H diff --git a/Artnet/Receiver.h b/Artnet/Receiver.h index 2457013..4abd265 100644 --- a/Artnet/Receiver.h +++ b/Artnet/Receiver.h @@ -4,6 +4,7 @@ #include "Common.h" #include "ArtDmx.h" +#include "ArtNzs.h" #include "ArtPollReply.h" #include "ArtTrigger.h" #include "ArtSync.h" @@ -18,6 +19,7 @@ class Receiver_ art_dmx::CallbackMap callback_art_dmx_universes; art_dmx::CallbackType callback_art_dmx; + art_nzs::CallbackMap callback_art_nzs_universes; art_sync::CallbackType callback_art_sync; art_trigger::CallbackType callback_art_trigger; ArtPollReplyConfig art_poll_reply_config; @@ -81,6 +83,16 @@ class Receiver_ op_code = OpCode::Dmx; break; } + case OpCode::Nzs: { + art_nzs::Metadata metadata = art_nzs::generateMetadataFrom(this->packet.data()); + for (auto& cb : this->callback_art_nzs_universes) { + if (this->getArtDmxUniverse15bit() == cb.first) { + cb.second(this->getArtDmxData(), size - HEADER_SIZE, metadata, remote_info); + } + } + op_code = OpCode::Nzs; + break; + } case OpCode::Poll: { this->sendArtPollReply(remote_info); op_code = OpCode::Poll; @@ -156,6 +168,14 @@ class Receiver_ this->callback_art_dmx_universes.insert(std::make_pair(universe, arx::function_traits::cast(func))); } + // subscribe artnzs packet for specified universe (15 bit) + template + auto subscribeArtNzsUniverse(uint16_t universe, const Fn &func) + -> std::enable_if_t::value> + { + this->callback_art_nzs_universes.insert(std::make_pair(universe, arx::function_traits::cast(func))); + } + // subscribe artdmx packet for all universes template auto subscribeArtDmx(const Fn &func) @@ -200,6 +220,14 @@ class Receiver_ this->callback_art_dmx = nullptr; } + void unsubscribeArtNzsUniverse(uint16_t universe) + { + auto it = this->callback_art_nzs_universes.find(universe); + if (it != this->callback_art_nzs_universes.end()) { + this->callback_art_nzs_universes.erase(it); + } + } + void unsubscribeArtSync() { this->callback_art_sync = nullptr; @@ -299,8 +327,18 @@ class Receiver_ uint8_t my_mac[6]; this->macAddress(my_mac); + arx::stdx::map universes; + for (const auto &cb_pair : this->callback_art_dmx_universes) { - art_poll_reply::Packet reply = art_poll_reply::generatePacketFrom(my_ip, my_mac, cb_pair.first, this->art_poll_reply_config); + universes[cb_pair.first] = true; + } + + for (const auto &cb_pair : this->callback_art_nzs_universes) { + universes[cb_pair.first] = true; + } + + for (const auto &u_pair : universes) { + art_poll_reply::Packet reply = art_poll_reply::generatePacketFrom(my_ip, my_mac, u_pair.first, this->art_poll_reply_config); this->stream->beginPacket(remote.ip, DEFAULT_PORT); this->stream->write(reply.b, sizeof(art_poll_reply::Packet)); this->stream->endPacket(); diff --git a/Artnet/Sender.h b/Artnet/Sender.h index 0ffb5b9..9185c63 100644 --- a/Artnet/Sender.h +++ b/Artnet/Sender.h @@ -4,6 +4,7 @@ #include "Common.h" #include "ArtDmx.h" +#include "ArtNzs.h" #include "ArtTrigger.h" #include "ArtSync.h" @@ -15,7 +16,8 @@ class Sender_ S* stream; Array packet; art_dmx::LastSendTimeMsMap last_send_times; - art_dmx::SequenceMap sequences; + art_dmx::SequenceMap dmx_sequences; + art_dmx::SequenceMap nzs_sequences; public: #if ARX_HAVE_LIBSTDCPLUSPLUS >= 201103L // Have libstdc++11 @@ -60,6 +62,40 @@ class Sender_ } } + // streaming artnzs packet + void setArtNzsData(const uint8_t* const data, uint16_t size) + { + art_nzs::setDataTo(this->packet.data(), data, size); + } + void setArtNzsData(uint16_t ch, uint8_t data) + { + art_nzs::setDataTo(this->packet.data(), ch, data); + } + + void streamArtNzsTo(const String& ip, uint16_t universe15bit) + { + uint8_t net = (universe15bit >> 8) & 0x7F; + uint8_t subnet = (universe15bit >> 4) & 0x0F; + uint8_t universe = (universe15bit >> 0) & 0x0F; + this->streamArtNzsTo(ip, net, subnet, universe, 0); + } + void streamArtNzsTo(const String& ip, uint8_t net, uint8_t subnet, uint8_t universe) + { + this->streamArtNzsTo(ip, net, subnet, universe, 0, 0); + } + void streamArtNzsTo(const String& ip, uint8_t net, uint8_t subnet, uint8_t universe, uint8_t start_code) + { + art_dmx::Destination dest {ip, net, subnet, universe}; + uint32_t now = millis(); + if (this->last_send_times.find(dest) == this->last_send_times.end()) { + this->last_send_times.insert(std::make_pair(dest, uint32_t(0))); + } + if (now >= this->last_send_times[dest] + DEFAULT_INTERVAL_MS) { + this->sendArxNzsInternal(dest, start_code); + this->last_send_times[dest] = now; + } + } + // one-line artdmx sender void sendArtDmx(const String& ip, uint16_t universe15bit, const uint8_t* const data, uint16_t size) { @@ -79,6 +115,25 @@ class Sender_ this->sendArxDmxInternal(dest, physical); } + // one-line artnzs sender + void sendArtNzs(const String& ip, uint16_t universe15bit, const uint8_t* const data, uint16_t size) + { + uint8_t net = (universe15bit >> 8) & 0x7F; + uint8_t subnet = (universe15bit >> 4) & 0x0F; + uint8_t universe = (universe15bit >> 0) & 0x0F; + this->sendArtNzs(ip, net, subnet, universe, data, size); + } + void sendArtNzs(const String& ip, uint8_t net, uint8_t subnet, uint8_t universe, const uint8_t* const data, uint16_t size) + { + this->sendArtNzs(ip, net, subnet, universe, 0, data, size); + } + void sendArtNzs(const String& ip, uint8_t net, uint8_t subnet, uint8_t universe, uint8_t start_code, const uint8_t *data, uint16_t size) + { + art_dmx::Destination dest {ip, net, subnet, universe}; + this->setArtNzsData(data, size); + this->sendArxNzsInternal(dest, start_code); + } + void sendArtTrigger(const String& ip, uint16_t oem = 0, uint8_t key = 0, uint8_t subkey = 0, const uint8_t *payload = nullptr, uint16_t size = 512) { art_trigger::setDataTo(packet.data(), oem, key, subkey, payload, size); @@ -104,12 +159,27 @@ class Sender_ return; } #endif - if (this->sequences.find(dest) == this->sequences.end()) { - this->sequences.insert(std::make_pair(dest, uint8_t(0))); + if (this->dmx_sequences.find(dest) == this->dmx_sequences.end()) { + this->dmx_sequences.insert(std::make_pair(dest, uint8_t(0))); + } + art_dmx::setMetadataTo(this->packet.data(), this->dmx_sequences[dest], physical, dest.net, dest.subnet, dest.universe); + this->sendRawData(dest.ip, DEFAULT_PORT, this->packet.data(), this->packet.size()); + this->dmx_sequences[dest] = (this->dmx_sequences[dest] + 1) % 256; + } + + void sendArxNzsInternal(const art_dmx::Destination &dest, uint8_t start_code) + { +#ifdef ARTNET_ENABLE_WIFI + if (!isNetworkReady()) { + return; + } +#endif + if (this->nzs_sequences.find(dest) == this->nzs_sequences.end()) { + this->nzs_sequences.insert(std::make_pair(dest, uint8_t(0))); } - art_dmx::setMetadataTo(this->packet.data(), this->sequences[dest], physical, dest.net, dest.subnet, dest.universe); + art_nzs::setMetadataTo(this->packet.data(), this->nzs_sequences[dest], start_code, dest.net, dest.subnet, dest.universe); this->sendRawData(dest.ip, DEFAULT_PORT, this->packet.data(), this->packet.size()); - this->sequences[dest] = (this->sequences[dest] + 1) % 256; + this->nzs_sequences[dest] = (this->nzs_sequences[dest] + 1) % 256; } void sendRawData(const String& ip, uint16_t port, const uint8_t* const data, size_t size) diff --git a/README.md b/README.md index 4131a6d..edb55d9 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ If you have already installed this library before v0.3.0, please follow: - Supports following protocols: - ArtDmx + - ArtNzs - ArtPoll/ArtPollReply - ArtTrigger - ArtSync