/* packet.h Copyright (c) 2022 Kevin Matz (kevin.matz@gmail.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "artnet.h" #include "config.h" #include "bufferstream.h" #include "../../esta/rdm/uid.h" #include #include namespace ARTNET { /** * @brief The packet_data struct */ struct packet_data : public streamable { uint16_t version = VERSION; //!< Protocol Version }; /** * @brief The artpoll_data struct * * \cite ARTNET This packet is used to discover the presence of other * Controllers, Nodes and Media Servers. The ArtPoll packet is only sent by * a Controller. Both Controllers and Nodes respond to the packet. */ struct poll_data : public packet_data { poll_data() : diagnostic_level(DpLow) {}; TalkToMe talk_to_me; //!< Set behaviour of Node Priority diagnostic_level; //!< The lowest priority of diagnostics message virtual size_t streamSize() const override { return 4; }; virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; }; /** * @brief The artpollreply_data struct * * A device, in response to a Controller’s ArtPoll, sends the ArtPollReply. * This packet is also broadcast to the Directed Broadcast address by all * Art-Net devices on power up. */ struct pollreply_data : public packet_data { pollreply_data() : udp_port(UDP_PORT) , oem(MY_ARTNET_MANUFACTUER_ID) , esta_manufacturer(MY_ESTA_MANUFACTURER_ID) {}; uint32_t my_ip = 0; //!< the Node’s IPv4 address uint16_t udp_port; //!< The Port is always 0x1936 PortAddress net_sub_switch; //!< Net and Subnet switch positions OEM oem; //!< the equipment vendor and the feature set uint8_t ubea_version = 0; //!< the firmware version of the User Bios Extension Area (UBEA) GeneralStatus status; //!< general status register uint16_t esta_manufacturer; //!< manufacturer ID std::string short_name = ""; //!< short name for the Node std::string long_name = ""; //!< long name for the Node uint16_t num_ports = 0; //!< the number of input or output ports. Max=4 PortTypes port_types[4]; //!< operation and protocol of each port. GoodInput good_input[4]; //!< input status of the node. GoodOutput good_output[4]; //!< output status of the node. uint8_t SwIn[4]; //!< Bits 3-0 of the 15 bit Port-Address for each input port uint8_t SwOut[4]; //!< Bits 3-0 of the 15 bit Port-Address for each output port uint8_t SwVideo = 0; //!< This field is now deprecated. ActivityReport SwMacro; //!< used for remote event triggering or cueing. ActivityReport SwRemote; //!< used for remote event triggering or cueing. Style style; //!< the equipment style of the device. uint8_t mac_address[6] = {0}; //!< MAC Address uint32_t bind_ip = 0; //!< the IP of the root device. uint8_t bind_index = 1; //!< the order of bound devices. /** * @brief textual report of the Node’s operating status or operational errors. * @return */ const std::string report() const { return _node_report; }; void set_report(NodeReport, uint, std::string); virtual size_t streamSize() const override { return 226; }; virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; private: std::string _node_report = ""; static const size_t _filler_length = 21; //!< length of filler at end of packet }; /** * @brief The artipprog_data struct * * Program the IPv4 address. */ struct ipprog_data : public packet_data { ipprog_data() : udp_port(UDP_PORT) {}; union { uint8_t _raw = 0; struct { bool program_ip : 1; bool program_netmask : 1; bool program_port : 1; bool reset_ip_mask_port : 1; uint8_t reserved : 2; bool enable_dhcp : 1; bool enable_programming : 1; }; } command; //!< Action of this packet uint32_t ip_address = 0; //!< IP Address to be programmed into Node uint32_t subnet_mask = 0; //!< Subnet Mask to be programmed into Node uint16_t udp_port; //!< Depreciated virtual size_t streamSize() const override { return 24; }; virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; private: static const size_t _spare_length = 8; }; /** * @brief The artipprogreply_data struct */ struct ipprogreply_data : public packet_data { ipprogreply_data() : udp_port(UDP_PORT) {}; uint32_t ip_address = 0; //!< IP Address of the Node uint32_t subnet_mask = 0; //!< Subnet Mask of the Node uint16_t udp_port; //!< Depreciated union { uint8_t _raw = 0; struct { uint8_t reserved1 : 6; bool dhcp_enabled : 1; bool reserved2 : 1; }; } status; //!< Status flags virtual size_t streamSize() const override { return 24; }; virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; private: static const size_t _spare_length = 7; }; /** * @brief The address_data struct */ struct address_data : public packet_data { PortAddress net_sub_switch; //!< Net and Subnet switch positions uint8_t bind_index = 1; //!< the order of bound devices. std::string short_name = ""; //!< short name for the Node std::string long_name = ""; //!< long name for the Node uint8_t SwIn[4]; //!< Bits 3-0 of the 15 bit Port-Address for each input port uint8_t SwOut[4]; //!< Bits 3-0 of the 15 bit Port-Address for each output port uint8_t SwVideo = 0; //!< This field is now deprecated. AddressCommand command; //!< Node configuration command. virtual size_t streamSize() const override { return 97; }; virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; }; /** * @brief The diagdata_data struct */ struct diagdata_data : public packet_data { Priority priority; //!< priority of the diagnostic data std::string data = ""; //!< ASCII text virtual size_t streamSize() const override { return 8 + data.length() + 1; }; virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; private: static const uint16_t _max_data_length = 512; //!< max data length, including null terminator }; /** * @brief The timecode_data struct */ struct timecode_data : public packet_data { Timecode time; //!< time code virtual size_t streamSize() const override { return 9; }; virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; }; /** * @brief The command_data struct */ struct command_data : public packet_data { uint16_t esta_manufacturer; //!< manufacturer ID std::string data = ""; //!< ASCII text virtual size_t streamSize() const override { return 6 + data.length() + 1; }; virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; private: static const uint16_t _max_data_length = 512; //!< max data length, including null terminator }; /** * @brief The trigger_data struct */ struct trigger_data : public packet_data { OEM oem; //!< the equipment vendor and the feature set uint8_t key; //!< the trigger key uint8_t subkey; //!< the trigger subkey uint8_t data[512] = {0}; //!< data array virtual size_t streamSize() const override { return 520; }; virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; }; /** * @brief The dmx_data struct */ struct dmx_data : public packet_data { uint8_t sequence; //!< to ensure that ArtDmx packets are used in the correct order. uint8_t physical; //!< the physical input port from which DMX was input PortAddress universe; //!< Net, Subnet, and universe number std::vector data = {0}; //!< data array, initialize null start virtual size_t streamSize() const override { return 7 + data.size(); }; virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; }; /** * @brief The sync_data struct */ struct sync_data : public packet_data { virtual size_t streamSize() const override { return 4; }; virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; }; /** * @brief The nzs_data struct */ struct nzs_data : public packet_data { /** * @brief nzs_data * @param startcode */ nzs_data(uint8_t startcode = 0) { data = {startcode}; } uint8_t sequence; //!< to ensure that ArtDmx packets are used in the correct order. PortAddress universe; //!< Net, Subnet, and universe number std::vector data; //!< data array with startcode virtual size_t streamSize() const override { return 7 + data.size(); }; virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; }; /** * @brief The vlc_data struct */ struct vlc_data : public nzs_data { /** * @brief vlc_data * @param other */ vlc_data(const nzs_data * other = nullptr) : nzs_data(VLC::START_CODE) { if (other) _from_nzs(other); } union { uint8_t _raw = 0; struct { uint8_t padding : 5; //!< reserved bool beacon : 1; //!< the transmitter should continuously repeat transmission of this packet until another is received bool reply : 1; //!< is a reply packet that is in response to the request sent with matching number in the transaction number bool IEEE : 1; //!< data in the payload area shall be interpreted as IEEE VLC data. }; } flags; //!< flags for this packet uint16_t transaction = 0; //!< transaction number which allows VLC transactions to be synchronised uint16_t address = 0; //!< The slot number, range 1-512, of the device to which this packet is directed. uint8_t depth = 0; //!< modulation depth expressed as a percentage in the range 1 to 100 uint16_t frequency = 0; //!< modulation frequency of the VLC transmitter expressed in Hz. uint16_t modulation = 0; //!< modulation type number that the transmitter should use to transmit VLC. uint16_t language = 0; //!< payload language code uint16_t beacon_frequency; //!< the frequency in Hertz at which the VLC packet should be repeated. std::vector payload; //!< The actual VLC payload. virtual size_t streamSize() const override { return 30 + data.size(); }; /// The ArtVlc packet is a specially formed ArtNzs packet. /// compile() MUST BE CALLED prior to streaming. void compile(); private: const static size_t _payload_max_length = 480; uint16_t _checksum() const; bool _from_nzs(const nzs_data *); }; /** * @brief The input_data struct */ struct input_data : public packet_data { uint8_t bind_index = 1; //!< the order of bound devices. uint16_t num_ports = 0; //!< the number of input or output ports. Max=4 union InputFlags { uint8_t _raw = 0; //!< raw byte struct { bool disabled : 1; //!< Set to disable this input. uint8_t reserved : 7; }; }; //!< flags for this packet InputFlags status[4]; //!< input disable status of each port. virtual size_t streamSize() const override { return 10; }; virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; }; /** * @brief The firmwaremaster_data struct */ struct firmwaremaster_data : public packet_data { FIRMWARE::MasterType type; //!< Defines the packet contents uint8_t block_id; //!< Counts the consecutive blocks of firmware upload. uint64_t length; //!< the file size (in words) of the file to be uploaded. uint16_t block[256] = {0}; //!< the firmware or UBEA data block. virtual size_t streamSize() const override { return 542; }; virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; private: const size_t _spare_length = 20; }; /** * @brief The firmwarereply_data struct */ struct firmwarereply_data : public packet_data { FIRMWARE::ResponseType type; //!< Defines the packet contents virtual size_t streamSize() const override { return 26; }; virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; private: const size_t _spare_length = 21; }; /** * @brief The todrequest_data struct */ struct todrequest_data : public packet_data { std::vector universes; //!< universes that must respond virtual size_t streamSize() const override { return 14 + universes.size(); }; virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; private: const size_t _max_count = 32; }; /** * @brief The toddata_data struct */ struct toddata_data : public packet_data { RDM::Version rdm_version = RDM::Standard; //!< supports Draft or Standard RDM uint8_t port = 1; //!< physical port index uint8_t bind_index = 1; //!< sub-device PortAddress universe; //!< responding universe RDM::TodCommandResponse type = RDM::ResponseTodFull; //!< response type uint16_t total_count = 0; //!< total device count uint8_t block_count = 0; //!< index of these devices in total std::vector> devices; //!< block of up to 200 devices virtual size_t streamSize() const override { return 18 + devices.size() * 6; } virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; }; /** * @brief The todcontrol_data struct */ struct todcontrol_data : public packet_data { PortAddress universe; //!< unverse that should action command RDM::TodControlCommand command = RDM::AtcNone; //!< control command virtual size_t streamSize() const override { return 14; }; virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; }; /** * @brief The rdm_data struct */ struct rdm_data : public packet_data { RDM::Version rdm_version = RDM::Standard; //!< supports Draft or Standard RDM PortAddress universe; //!< responding universe RDM::RdmCommand command = RDM::ArProcess; //!< response type std::vector data = {::RDM::SC_RDM}; //!< data array, initialize RDM start virtual size_t streamSize() const override { return 13 + data.size(); }; virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; }; /** * @brief The rdmsub_data struct */ struct rdmsub_data : public packet_data { RDM::Version rdm_version = RDM::Standard; //!< supports Draft or Standard RDM ::RDM::UID uid; //!< UID of target RDM device. uint8_t command_class; //!< Get, Set, GetResponse, SetResponse ::RDM::PID pid; //!< type of parameter uint16_t subdevice = 0; //!< the first device information contained in packet std::vector data; //!< packed data array virtual size_t streamSize() const override { return 20 + (data.size() * 2); }; virtual void iStream(std::shared_ptr) override; virtual void oStream(std::shared_ptr) const override; }; /** * @brief The ARTNET packet * * All UDP packets accepted by the Node conform to the Art-Net protocol * specification as defined... Any other packets are ignored. */ struct Packet : public streamable { /** * @brief Packet * @param opcode * @param data */ Packet(OpCode opcode = OpNull, std::shared_ptr data = nullptr) : _opcode(opcode) , _data(data) {} /** * @brief opcode * @return */ OpCode opcode() const { return _opcode; } /** * @brief data * @return */ template auto data() const { static_assert(std::is_base_of::value, "type parameter of data must derive from ARTNET::packet_data"); return _data ? std::optional>{std::static_pointer_cast(_data)} : std::nullopt; } size_t streamSize() const override; void iStream(std::shared_ptr) override; void oStream(std::shared_ptr) const override; protected: OpCode _opcode; //!< Op Code std::shared_ptr _data; //!< packet data }; /** * @brief The ArtPoll Packet */ struct ArtPoll : public Packet { /** * @brief ArtPoll * @param data */ ArtPoll(std::shared_ptr data = std::make_shared()) : Packet(OpPoll, data) {} }; /** * @brief The ArtPollReply Packet */ struct ArtPollReply : public Packet { /** * @brief ArtPollReply * @param data */ ArtPollReply(std::shared_ptr data = std::make_shared()) : Packet(OpPollReply, data) {} }; /** * @brief The ArtIpProg Packet */ struct ArtIpProg : public Packet { /** * @brief ArtIpProg * @param data */ ArtIpProg(std::shared_ptr data = std::make_shared()) : Packet(OpIpProg, data) {} }; /** * @brief The ArtIpProgReply Packet */ struct ArtIpProgReply : public Packet { /** * @brief ArtIpProgReply * @param data */ ArtIpProgReply(std::shared_ptr data = std::make_shared()) : Packet(OpIpProgReply, data) {} }; /** * @brief The ArtAddress Packet */ struct ArtAddress : public Packet { /** * @brief ArtAddress * @param data */ ArtAddress(std::shared_ptr data = std::make_shared()) : Packet(OpAddress, data) {} }; /** * @brief The ArtDiagData Packet */ struct ArtDiagData : public Packet { /** * @brief ArtDiagData * @param data */ ArtDiagData(std::shared_ptr data = std::make_shared()) : Packet(OpDiagData, data) {} }; /** * @brief The ArtDirectory class */ struct ArtDirectory : public Packet { ArtDirectory() : Packet(OpDirectory) /// \todo I/O data for Directory {} }; /** * @brief The ArtDirectoryReply class */ struct ArtDirectoryReply : public Packet { ArtDirectoryReply() : Packet(OpDirectoryReply) /// \todo I/O data for DirectoryReply {} }; /** * @brief The ArtFileTnMaster class */ struct ArtFileTnMaster : public Packet { ArtFileTnMaster() : Packet(OpFileTnMaster) /// \todo I/O data for FileTnMaster {} }; /** * @brief The ArtFileFnMaster class */ struct ArtFileFnMaster : public Packet { ArtFileFnMaster() : Packet(OpFileFnMaster) /// \todo I/O data for FileFnMaster {} }; /** * @brief The ArtFileFnReply class */ struct ArtFileFnReply : public Packet { ArtFileFnReply() : Packet(OpFileFnReply) /// \todo I/O data for FileFnReply {} }; /** * @brief The ArtMedia class */ struct ArtMedia : public Packet { ArtMedia() : Packet(OpMedia) /// \todo I/O data for Media {} }; /** * @brief The ArtMediaPatch class */ struct ArtMediaPatch : public Packet { ArtMediaPatch() : Packet(OpMediaPatch) /// \todo I/O data for MediaPatch {} }; /** * @brief The ArtMediaControl class */ struct ArtMediaControl : public Packet { ArtMediaControl() : Packet(OpMediaControl) /// \todo I/O data for MediaControl {} }; /** * @brief The ArtMediaControlReply class */ struct ArtMediaControlReply : public Packet { ArtMediaControlReply() : Packet(OpMediaContrlReply) /// \todo I/O data for MediaControlReply {} }; /** * @brief The ArtTimeCode Packet */ struct ArtTimeCode : public Packet { /** * @brief ArtTimeCode * @param data */ ArtTimeCode(std::shared_ptr data = std::make_shared()) : Packet(OpTimeCode, data) {} }; /** * @brief The ArtTimeSync class */ struct ArtTimeSync : public Packet { ArtTimeSync() : Packet(OpTimeSync) /// \todo I/O data for TimeSync {} }; /** * @brief The ArtCommand class */ struct ArtCommand : public Packet { /** * @brief ArtCommand * @param data */ ArtCommand(std::shared_ptr data = std::make_shared()) : Packet(OpCommand, data) {} }; /** * @brief The ArtTrigger Packet */ struct ArtTrigger : public Packet { /** * @brief ArtTrigger * @param data */ ArtTrigger(std::shared_ptr data = std::make_shared()) : Packet(OpTrigger, data) {} }; /** * @brief The ArtDmx Packet */ struct ArtDmx : public Packet { /** * @brief ArtDmx * @param data */ ArtDmx(std::shared_ptr data = std::make_shared()) : Packet(OpDmx, data) {} }; /** * @brief The ArtSync Packet */ struct ArtSync : public Packet { /** * @brief ArtSync * @param data */ ArtSync(std::shared_ptr data = std::make_shared()) : Packet(OpSync, data) {} }; /** * @brief The ArtNzs Packet */ struct ArtNzs : public Packet { /** * @brief ArtNzs * @param data */ ArtNzs(std::shared_ptr data = std::make_shared()) : Packet(OpNzs, data) {} /** * @brief ArtNzs and ArtVlc share the same OpCode. * @return */ bool isVLC() const { if (!_data) return false; auto data = std::static_pointer_cast(_data); return data->data[0] == VLC::START_CODE; }; }; /** * @brief The ArtVlc Packet */ struct ArtVlc : public Packet { /** * @brief ArtVlc * @param data */ ArtVlc(std::shared_ptr data = std::make_shared()) : Packet(OpNzs, data) {} }; /** * @brief The ArtInput Packet */ struct ArtInput : public Packet { /** * @brief ArtInput * @param data */ ArtInput(std::shared_ptr data = std::make_shared()) : Packet(OpInput, data) {} }; /** * @brief The ArtFirmwareMaster Packet */ struct ArtFirmwareMaster : public Packet { /** * @brief ArtFirmwareMaster * @param data */ ArtFirmwareMaster(std::shared_ptr data = std::make_shared()) : Packet(OpFirmwareMaster, data) {} }; /** * @brief The ArtFirmwareReply Packet */ struct ArtFirmwareReply : public Packet { /** * @brief ArtFirmwareReply * @param data */ ArtFirmwareReply(std::shared_ptr data = std::make_shared()) : Packet(OpFirmwareReply, data) {} }; /** * @brief The ArtTodRequest Packet */ struct ArtTodRequest : public Packet { /** * @brief ArtTodRequest * @param data */ ArtTodRequest(std::shared_ptr data = std::make_shared()) : Packet(OpTodRequest, data) {} }; /** * @brief The ArtTodData Packet */ struct ArtTodData : public Packet { /** * @brief ArtTodData * @param data */ ArtTodData(std::shared_ptr data = std::make_shared()) : Packet(OpTodData, data) {} }; /** * @brief The ArtTodControl Packet */ struct ArtTodControl : public Packet { /** * @brief ArtTodControl * @param data */ ArtTodControl(std::shared_ptr data = std::make_shared()) : Packet(OpTodControl, data) {} }; /** * @brief The ArtRdm Packet */ struct ArtRdm : public Packet { /** * @brief ArtRdm * @param data */ ArtRdm(std::shared_ptr data = std::make_shared()) : Packet(OpRdm, data) {} }; /** * @brief The ArtRdmSub Packet */ struct ArtRdmSub : public Packet { /** * @brief ArtRdmSub * @param data */ ArtRdmSub(std::shared_ptr data = std::make_shared()) : Packet(OpRdmSub, data) {} }; /** * @brief The ArtVideoSetup class */ struct ArtVideoSetup : public Packet { ArtVideoSetup() : Packet(OpVideoSetup) /// \todo I/O data for VideoSetup {} }; /** * @brief The ArtVideoPalette class */ struct ArtVideoPalette : public Packet { ArtVideoPalette() : Packet(OpVideoPalette) /// \todo I/O data for VideoPalette {} }; /** * @brief The ArtVideoData class */ struct ArtVideoData : public Packet { ArtVideoData() : Packet(OpVideoData) /// \todo I/O data for VideoData {} }; } // namespace ARTNET