1
0
Fork 0

the Art-Net packet definitions

This commit is contained in:
Kevin Matz 2022-06-08 16:05:06 -04:00
parent 7826d17d40
commit 5710631724
4 changed files with 2455 additions and 1 deletions

View File

@ -1,4 +1,6 @@
target_sources(${PROJECT_NAME}
PRIVATE
artnet/artnet.h
artnet/packet.h
artnet/packet.cpp
)

556
protocol/artnet/artnet.h Normal file
View File

@ -0,0 +1,556 @@
/*
artnet.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 <cstdint>
/**
* @brief \cite ARTNET Art-Net is an Ethernet protocol based on the TCP/IP protocol
* suite.
*
* Its purpose is to allow transfer of large amounts of DMX512 data
* over a wide area using standard networking technology.
*/
namespace ARTNET {
/// @brief \cite ARTNET Universe Addressing
///
/// > The Port-Address of each DMX512 Universe is encoded as a 15-bit number
struct PortAddress {
/**
* @brief PortAddress
* @param v
*/
PortAddress(uint16_t v = 0) : value(v) {};
/**
* @brief PortAddress
* @param n
* @param s
*/
PortAddress(uint8_t n, uint8_t s) : subuni(s), net(n) {}
union {
uint16_t value;
struct {
union {
uint8_t subuni;
struct {
uint8_t universe : 4; //!< A single DMX512 frame of 512 channels is referred to as a Universe.
uint8_t subnet : 4; //!< A group of 16 consecutive universes is referred to as a sub-net.
};
};
union{
uint8_t _net;
struct {
uint8_t net : 7; //!< A group of 16 consecutive Sub-Nets or 256 consecutive Universes is referred to as a net. There are 128 Nets in total.
bool reserved : 1;
};
};
};
};
};
/// @brief \cite ARTNET Protocol Operation
///
/// > The UDP port used as both source and destination is 0x1936.
static const uint16_t UDP_PORT = 6454;
/// @brief Packet ID
///
/// > Array of 8 characters, the final character is a null termination.
/// > Value = A r t - N e t 0x00
static const uint8_t PACKET_IDENTIFIER[] = { 0x41, 0x72, 0x74, 0x2D,
0x4E, 0x65, 0x74, 0x00};
/// @brief \cite ARTNET Art-Net protocol revision number.
///
/// Current value 14. Controllers should ignore communication with nodes
/// using a protocol version lower than 14.
static const uint16_t VERSION = 14;
/// @brief \cite ARTNET Table 1 - OpCodes
///
/// The OpCode defines the class of data following within the UDP packet.
/// The following table details the legal OpCode values used in Art-Net packets
enum OpCode : uint16_t {
OpNull = 0x0000, //!< no-op. Ignore this packet.
OpPoll = 0x2000, //!< an ArtPoll packet, no other data is contained in this UDP packet.
OpPollReply = 0x2100, //!< an ArtPollReply Packet. It contains device status information.
OpDiagData = 0x2300, //!< Diagnostics and data logging packet.
OpCommand = 0x2400, //!< Used to send text based parameter commands.
// OpOutput = 0x5000, //!< an ArtDmx data packet. It contains zero start code DMX512 information for a single Universe.
OpDmx = 0x5000, //!< an ArtDmx data packet. It contains zero start code DMX512 information for a single Universe.
OpNzs = 0x5100, //!< an ArtNzs data packet. It contains non-zero start code (except RDM) DMX512 information for a single Universe.
OpSync = 0x5200, //!< an ArtSync data packet. It is used to force synchronous transfer of ArtDmx packets to a nodes output.
OpAddress = 0x6000, //!< an ArtAddress packet. It contains remote programming information for a Node.
OpInput = 0x7000, //!< an ArtInput packet. It contains enable disable data for DMX inputs.
OpTodRequest = 0x8000, //!< an ArtTodRequest packet. It is used to request a Table of Devices (ToD) for RDM discovery.
OpTodData = 0x8100, //!< an ArtTodData packet. It is used to send a Table of Devices (ToD) for RDM discovery.
OpTodControl = 0x8200, //!< an ArtTodControl packet. It is used to send RDM discovery control messages.
OpRdm = 0x8300, //!< an ArtRdm packet. It is used to send all non discovery RDM messages.
OpRdmSub = 0x8400, //!< an ArtRdmSub packet. It is used to send compressed, RDM Sub-Device data.
OpVideoSetup = 0xA010, //!< an ArtVideoSetup packet. It contains video screen setup information for nodes that implement the extended video features.
OpVideoPalette = 0xA020, //!< an ArtVideoPalette packet. It contains colour palette setup information for nodes that implement the extended video features.
OpVideoData = 0xA040, //!< an ArtVideoData packet. It contains display data for nodes that implement the extended video features.
OpMacMaster = 0xF000, //!< depreciated
OpMacSlave = 0xF100, //!< depreciated
OpFirmwareMaster = 0xF200, //!< an ArtFirmwareMaster packet. It is used to upload new firmware or firmware extensions to the Node.
OpFirmwareReply = 0xF300, //!< an ArtFirmwareReply packet. It is returned by the node to acknowledge receipt of an ArtFirmwareMaster packet or ArtFileTnMaster packet.
OpFileTnMaster = 0xF400, //!< Uploads user file to node.
OpFileFnMaster = 0xF500, //!< Downloads user file from node.
OpFileFnReply = 0xF600, //!< Server to Node acknowledge for download packets.
OpIpProg = 0xF800, //!< an ArtIpProg packet. It is used to re- programme the IP, Mask and Port address of the Node.
OpIpProgReply = 0xF900, //!< an ArtIpProgReply packet. It is returned by the node to acknowledge receipt of an ArtIpProg packet.
OpMedia = 0x9000, //!< an ArtMedia packet. It is Unicast by a Media Server and acted upon by a Controller.
OpMediaPatch = 0x9100, //!< an ArtMediaPatch packet. It is Unicast by a Controller and acted upon by a Media Server.
OpMediaControl = 0x9200, //!< an ArtMediaControl packet. It is Unicast by a Controller and acted upon by a Media Server.
OpMediaContrlReply = 0x9300, //!< an ArtMediaControlReply packet. It is Unicast by a Media Server and acted upon by a Controller.
OpTimeCode = 0x9700, //!< an ArtTimeCode packet. It is used to transport time code over the network.
OpTimeSync = 0x9800, //!< Used to synchronise real time date and clock
OpTrigger = 0x9900, //!< Used to send trigger macros
OpDirectory = 0x9A00, //!< Requests a node's file list
OpDirectoryReply = 0x9B00 //!< Replies to OpDirectory with file list
};
/// OEM is unknown or non-registered type
static const uint16_t oem_unknown = 0x00ff;
/// Used by ArtTrigger for general purpose codes
static const uint16_t oem_gobal = 0xffff;
/// @brief \cite ARTNET Table 3 - NodeReport Codes
enum NodeReport : uint16_t {
RcDebug = 0x0000, //!< Booted in debug mode (Only used in development)
RcPowerOk = 0x0001, //!< Power On Tests successful
RcPowerFail = 0x0002, //!< Hardware tests failed at Power On
RcSocketWr1 = 0x0003, //!< Last UDP from Node failed due to truncated length, Most likely caused by a collision.
RcParseFail = 0x0004, //!< Unable to identify last UDP transmission. Check OpCode and packet length.
RcUdpFail = 0x0005, //!< Unable to open Udp Socket in last transmission attempt
RcShNameOk = 0x0006, //!< Confirms that Short Name programming via ArtAddress, was successful.
RcLoNameOk = 0x0007, //!< Confirms that Long Name programming via ArtAddress, was successful.
RcDmxError = 0x0008, //!< DMX512 receive errors detected.
RcDmxUdpFull = 0x0009, //!< Ran out of internal DMX transmit buffers.
RcDmxRxFull = 0x000A, //!< Ran out of internal DMX Rx buffers.
RcSwitchErr = 0x000B, //!< Rx Universe switches conflict.
RcConfigErr = 0x000C, //!< Product configuration does not match firmware.
RcDmxShort = 0x000D, //!< DMX output short detected. See GoodOutput field.
RcFirmwareFail = 0x000E, //!< Last attempt to upload new firmware failed.
RcUserFail = 0x000F, //!< User changed switch settings when address locked by remote programming. User changes ignored.
RcFactoryRes = 0x0010 //!< Factory reset has occurred.
};
/// \cite ARTNET Table 4 - Style Codes
enum Style : uint8_t {
StNode = 0x00, //!< A DMX to / from Art-Net device
StController = 0x01, //!< A lighting console.
StMedia = 0x02, //!< A Media Server.
StRoute = 0x03, //!< A network routing device.
StBackup = 0x04, //!< A backup device.
StConfig = 0x05, //!< A configuration or diagnostic tool
StVisual = 0x06 //!< A visualiser
};
/// \cite ARTNET Table 5 - Priority Codes
enum Priority : uint8_t {
DpLow = 0x10, //!< Low priority message.
DpMed = 0x40, //!< Medium priority message.
DpHigh = 0x80, //!< High priority message.
DpCritial = 0xE0, //!< Critical priority message.
DpVolatile = 0xF0 //!< Volatile message.
};
/// Set the communication behavior during ArtPoll
struct TalkToMe {
union {
uint8_t _raw = 0;
struct {
bool depreciated : 1;
bool reply_on_change : 1; //!< Send ArtPollReply whenever Node conditions change.
bool diag_enable : 1; //!< Send me diagnostics messages.
bool diag_unicast : 1; //!< Diagnostics messages are unicast. (if bit 2).
bool VLC_disable : 1; //!< Disable VLC transmission.
uint8_t reserved : 3;
};
};
};
/**
* @brief The OEM word
*
* Describes the equipment vendor and the feature set available.
* Bit 15 high indicates extended features available.
*/
struct OEM {
union {
uint16_t word = oem_unknown;
struct {
uint16_t manufacturer : 15;
bool extended_features : 1;
};
};
};
/**
* @brief The Authority enum
*/
enum Authority : uint8_t {
AuthorityUnkown = 0b00, //!< Port-Address Programming Authority unknown.
AuthorityLocal = 0b01, //!< Port-Address set by front panel controls.
AuthorityRemote = 0b10, //!< All or part of Port-Address programmed by network
AuthorityUnused = 0b11, //!< not used
};
/**
* @brief The Indicator enum
*/
enum Indicator : uint8_t {
IndicatorUnknown = 0b00, //!< Indicator state unknown.
IndicatorIdentify = 0b01, //!< Indicators in Locate / Identify Mode.
IndicatorMute = 0b10, //!< Indicators in Mute Mode.
IndicatorNormal = 0b11, //!< Indicators in Normal Mode.
};
/**
* @brief The FailoverMode enum
*/
enum FailoverMode : uint8_t {
FailoverHoldLast = 0b00, //!< Hold last state.
FailoverAllZeros = 0b01, //!< All output to zero.
FailoverAllFull = 0b10, //!< All output to full.
FailoverScenePlayback = 0b11, //!< Playback failover scene.
};
/**
* @brief The General_Status register
*/
struct GeneralStatus {
union {
uint8_t _raw1 = 0;
struct {
bool ubea_valid : 1; //!< UBEA present and not corrupt.
bool rdm_capable : 1; //!< Capable of Remote Device Management (RDM).
bool boot_failsafe : 1; //!< Booted from ROM
bool reserved1 : 1; //!< not implimented
Authority authority : 2; //!< Port-Address Programming Authority
Indicator indicator : 2; //!< Indicator State
};
};
union {
uint8_t _raw2 = 0;
struct {
bool web_config_available : 1; //!< supports web browser configuration.
bool DHCP_active : 1; //!< Nodes IP is DHCP configured.
bool DHCP_capable : 1; //!< Node is DHCP capable.
bool port_address_15b : 1; //!< Node supports 15 bit Port-Address
bool sACN_capable : 1; //!< Node is able to switch between Art-Net and sACN.
bool squawking : 1; //!< is sqawking
bool io_remote_set : 1; //!> Node supports switching of output style using ArtCommand.
bool remote_RDM : 1; //!> Node supports control of RDM using ArtCommand.
};
};
union {
uint8_t _raw3 = 0;
struct {
uint8_t reserved3 : 5; //!< Not used, set to zero
bool failover_capable : 1; //!< Node supports fail-over.
FailoverMode failover_mode : 2; //!< network data loss behavior.
};
};
};
/**
* @brief The KnownProtocols enum
*
* Protocols elegible to be reported in the PortTypes array
*/
enum KnownProtocols : uint8_t {
Protocol_DMX = 0b000000,
Protocol_MIDI = 0b000001,
Protocol_Avab = 0b000010,
Protocol_CMX = 0b000011,
Protocol_ADB = 0b000100,
Protocol_ArtNet = 0b000101,
};
/**
* @brief The PortTypes struct
*
* An array of 4 PortTypes are reported in the ArtPollReply packet.
*/
struct PortTypes {
union {
uint8_t _raw = 0;
struct {
KnownProtocols protocol : 6;
bool can_input : 1;
bool can_output : 1;
};
};
};
/**
* @brief The GoodInput struct
*
* An array of 4 GoodInput are reported in the ArtPollReply packet.
*/
struct GoodInput {
union {
uint8_t _raw = 0;
struct {
uint8_t reserved : 2; //!< Unused and transmitted as zero.
bool rx_errors : 1; //!< Receive errors detected.
bool disabled : 1; //!< Input is disabled.
bool DMX_text : 1; //!< includes DMX512 text packets.
bool DMX_SIP : 1; //!< includes DMX512 SIPs.
bool DMX_test : 1; //!< includes DMX512 test packets.
bool active : 1; //!< Data received.
};
};
};
/**
* @brief The GoodOutput struct
*
* An array of 4 GoodOutput are reported in the ArtPollReply packet.
*/
struct GoodOutput {
union {
uint8_t _rawA = 0;
struct {
bool sACN_enabled : 1; //!< Output is selected to transmit sACN.
bool merge_LTP : 1; //!< Merge Mode is LTP.
bool output_short : 1; //!< DMX output short detected on power up.
bool merge_ArtNet : 1; //!< Output is merging ArtNet data.
bool DMX_text : 1; //!< includes DMX512 text packets.
bool DMX_SIP : 1; //!< includes DMX512 SIPs.
bool DMX_test : 1; //!< includes DMX512 test packets.
bool active : 1; //!< Data received.
};
};
union {
uint8_t _rawB = 0;
struct {
uint8_t reserved : 6; //!< Not used, set to zero
bool output_continuous : 1; //!< oposite of changes-only
bool rdm_disabled : 1; //!< RDM disabled
};
};
};
/**
* @brief The ActivityReport struct
*
* reported in the ArtPollReply packet as SwMacro and SwRemote
*/
struct ActivityReport {
union {
uint8_t _raw = 0;
struct {
bool active_1 : 1;
bool active_2 : 1;
bool active_3 : 1;
bool active_4 : 1;
bool active_5 : 1;
bool active_6 : 1;
bool active_7 : 1;
bool active_8 : 1;
};
};
};
/**
* @brief The AddressCommand enum
*/
enum AddressCommand : uint8_t {
// Node configuration commands:
AcNone = 0x00, //!< No action
AcCancelMerge = 0x01, //!< if in merge mode, cancel
AcLedNormal = 0x02, //!< set front panel indicators to normal
AcLedMute = 0x03, //!< switch off front panel indicators
AcLedLocate = 0x04, //!< flash front panel indicators
AcResetRxFlags = 0x05, //!< reset SIP, Text, Test and Error flags
// Fail-over configuration commands:
AcFailHold = 0x08, //!< hold last look
AcFailZero = 0x09, //!< output zeros on data loss
AcFailFull = 0x0a, //!< output full on data loss
AcFailScene = 0x0b, //!< output scene on data loss
AcFailRecord = 0x0c, //!< record current output state as failover scene
// Port configuration commands:
AcMergeLtp0 = 0x10, //!< set port 0 to merge LTP
AcMergeLtp1 = 0x11, //!< set port 1 to merge LTP
AcMergeLtp2 = 0x12, //!< set port 2 to merge LTP
AcMergeLtp3 = 0x13, //!< set port 3 to merge LTP
AcMergeHtp0 = 0x50, //!< set port 0 to merge HTP
AcMergeHtp1 = 0x51, //!< set port 1 to merge HTP
AcMergeHtp2 = 0x52, //!< set port 2 to merge HTP
AcMergeHtp3 = 0x53, //!< set port 3 to merge HTP
AcArtNetSel0 = 0x60, //!< set port 0 protocol to Art-Net
AcArtNetSel1 = 0x61, //!< set port 1 protocol to Art-Net
AcArtNetSel2 = 0x62, //!< set port 2 protocol to Art-Net
AcArtNetSel3 = 0x63, //!< set port 3 protocol to Art-Net
AcAcnSel0 = 0x70, //!< set port 0 protocol to sACN
AcAcnSel1 = 0x71, //!< set port 1 protocol to sACN
AcAcnSel2 = 0x72, //!< set port 2 protocol to sACN
AcAcnSel3 = 0x73, //!< set port 3 protocol to sACN
AcClearOp0 = 0x90, //!< clear output buffer for port 0
AcClearOp1 = 0x91, //!< clear output buffer for port 1
AcClearOp2 = 0x92, //!< clear output buffer for port 2
AcClearOp3 = 0x93, //!< clear output buffer for port 3
AcStyleDelta0 = 0xa0, //!< set port 0 to changes-only
AcStyleDelta1 = 0xa1, //!< set port 1 to changes-only
AcStyleDelta2 = 0xa2, //!< set port 2 to changes-only
AcStyleDelta3 = 0xa3, //!< set port 3 to changes-only
AcStyleConst0 = 0xb0, //!< set port 0 to constant output
AcStyleConst1 = 0xb1, //!< set port 1 to constant output
AcStyleConst2 = 0xb2, //!< set port 2 to constant output
AcStyleConst3 = 0xb3, //!< set port 3 to constant output
AcRdEnable0 = 0xc0, //!< enable RDM for port 0
AcRdEnable1 = 0xc1, //!< enable RDM for port 1
AcRdEnable2 = 0xc2, //!< enable RDM for port 2
AcRdEnable3 = 0xc3, //!< enable RDM for port 3
AcRdmDisable0 = 0xd0, //!< disable RDM for port 0
AcRdmDisable1 = 0xd1, //!< disable RDM for port 1
AcRdmDisable2 = 0xd2, //!< disable RDM for port 2
AcRdmDisable3 = 0xd3, //!< disable RDM for port 3
};
/**
* @brief The TimecodeType enum
*/
enum TimecodeType : uint8_t {
Film = 0, //!< 24 fps
EBU = 1, //!< 25 fps
DF = 3, //!< 29.97 fps
SMPTE = 4, //!< 30 fps
};
/**
* @brief The Timecode struct
*/
struct Timecode {
uint8_t frame; //!< 24-30, depending on type
uint8_t seconds; //!< 0-59
uint8_t minutes; //!< 0-59
uint8_t hours; //!< 0-24
TimecodeType type; //!< framerate
};
// Table 7 ArtTrigger Key Values.
/// The SubKey field contains an ASCII character which the receiving device
/// should process as if it were a keyboard press.
static const uint8_t KeyAscii = 0;
/// The SubKey field contains the number of a Macro which the receiving
/// device should execute.
static const uint8_t KeyMacro = 1;
/// The SubKey field contains a soft-key number which the receiving device
/// should process as if it were a soft-key keyboard press.
static const uint8_t KeySoft = 2;
/// The SubKey field contains the number of a Show which the receiving
/// device should run.
static const uint8_t KeyShow = 3;
static const std::size_t Short_Name_Length = 18; //!< string length
static const std::size_t Long_Name_Length = 64; //!< string length
static const std::size_t Node_Report_Length = 64; //!< string length
/// \cite ARTNET
/// ...to allow transition between synchronous and non-synchronous modes, a
/// node shall time out to non-synchronous operation if an ArtSync is not
/// received for 4 seconds or more.
static const uint16_t SYNC_LOSS_TIMEOUT = 4000;
namespace VLC {
/// The DMX512 start code of this (ArtVlc) packet is set to 0x91.
static const uint8_t START_CODE = 0x91;
/// Magic number used to identify this packet.
static const uint8_t MagicNumber[] = {0x41, 0x4c, 0x45};
/// Payload contains a simple text string representing a URL.
static const uint16_t BeaconURL = 0x0000;
/// Payload contains a simple ASCII text message
static const uint16_t BeaconText = 0x0001;
} // namespace ARTNET::VLC
namespace FIRMWARE {
/// Type of firmware master packet
enum MasterType : uint8_t {
FirmFirst = 0x00, //!< The first packet of a firmware upload.
FirmCont = 0x01, //!< A consecutive continuation packet of a firmware upload.
FirmLast = 0x02, //!< The last packet of a firmware upload.
UbeaFirst = 0x03, //!< The first packet of a UBEA upload.
UbeaCont = 0x04, //!< A consecutive continuation packet of a UBEA upload.
UbeaLast = 0x05, //!< The last packet of a UBEA upload.
};
/// Type of firmware reply packet
enum ResponseType : uint8_t {
FirmBlockGood = 0x00, //!< Last packet recieved successfully.
FirmAllGood = 0x01, //!< All firmware receieved successfully.
FirmFail = 0xff, //!< Firmware upload failed.
};
} // namespace ARTNET::FIRMWARE
namespace RDM {
enum Version : uint8_t {
Draft = 0x00, //!< RDM DRAFT V1.0
Standard = 0x01, //!< RDM V1.0
};
enum TodRequestCommand : uint8_t {
TodFull = 0x00, //!< Send the Entire TOD
};
enum TodCommandResponse : uint8_t {
ResponseTodFull = 0x00, //!< entire or packet in sequence of the entire TOD.
ResponseTodNak = 0xff, //!< TOD is not available or discovery is incomplete.
};
enum TodControlCommand : uint8_t {
AtcNone = 0x00, //!< No action.
AtcFlush = 0x01, //!< The node flushes its TOD and instigates full discovery.
};
enum RdmCommand : uint8_t {
ArProcess = 0x00, //!< Process RDM Packet.
};
} // namespace ARTNET::RDM
/// @brief Communication Activity
///
/// On if any Art-Net packets detected on network, timeout after 6 seconds.
static const uint16_t DATA_LOSS_TIMEOUT = 6000;
} // namespace ARTNET

1057
protocol/artnet/packet.cpp Normal file

File diff suppressed because it is too large Load Diff

839
protocol/artnet/packet.h Normal file
View File

@ -0,0 +1,839 @@
/*
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 "acn/pdu-stream.h"
#include "rdm/uid.h"
#include <vector>
namespace ARTNET {
/**
* @brief The packet_data struct
*/
struct packet_data
: public ACN::PDU::pdu_stream_object
{
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(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) const override;
};
/**
* @brief The artpollreply_data struct
*
* A device, in response to a Controllers 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)
, esta_manufacturer(MY_ESTA_MANUFACTURER_ID)
{};
uint32_t my_ip = 0; //!< the Nodes 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 Nodes 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(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) 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(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) 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(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) 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(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) 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(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) 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(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) 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(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) 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(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) 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<uint8_t> data = {0}; //!< data array, initialize null start
virtual size_t streamSize() const override { return 7 + data.size(); };
virtual void iStream(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) const override;
};
/**
* @brief The sync_data struct
*/
struct sync_data
: public packet_data
{
virtual size_t streamSize() const override { return 4; };
virtual void iStream(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) 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<uint8_t> data; //!< data array with startcode
virtual size_t streamSize() const override { return 7 + data.size(); };
virtual void iStream(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) 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<uint8_t> 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(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) 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(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) 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(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) const override;
private:
const size_t _spare_length = 21;
};
/**
* @brief The todrequest_data struct
*/
struct todrequest_data
: public packet_data
{
std::vector<PortAddress> universes; //!< universes that must respond
virtual size_t streamSize() const override { return 14 + universes.size(); };
virtual void iStream(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) 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<std::shared_ptr<::RDM::UID>> devices; //!< block of up to 200 devices
virtual size_t streamSize() const override { return 18 + devices.size() * 6; }
virtual void iStream(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) 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(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) 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<uint8_t> data = {::RDM::SC_RDM}; //!< data array, initialize RDM start
virtual size_t streamSize() const override { return 13 + data.size(); };
virtual void iStream(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) 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<uint16_t> data; //!< packed data array
virtual size_t streamSize() const override { return 20 + (data.size() * 2); };
virtual void iStream(ACN::PDU::Stream) override;
virtual void oStream(ACN::PDU::Stream) 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 ACN::PDU::pdu_stream_object
{
/**
* @brief Packet
* @param data
*/
Packet(std::shared_ptr<packet_data> data = nullptr)
: _opcode(OpNull)
, _data(data)
{}
/**
* @brief opcode
* @return
*/
OpCode opcode() { return _opcode; }
/**
* @brief data
* @return
*/
template<class T>
std::shared_ptr<T> data()
{
#ifdef RTTI_ENABLED
return std::dynamic_pointer_cast<T>(_data);
#else
return std::static_pointer_cast<T>(_data);
#endif
}
/**
* @brief createData
* @param stream
*/
template<class T>
void createData(ACN::PDU::Stream stream = nullptr)
{
if (_data)
return; // already has a data segment
_data = std::make_shared<T>();
if (stream && stream->good())
_data->iStream(stream);
}
size_t streamSize() const override;
void iStream(ACN::PDU::Stream) override;
void oStream(ACN::PDU::Stream) const override;
protected:
OpCode _opcode; //!< Op Code
std::shared_ptr<packet_data> _data; //!< packet data
};
/**
* @brief The ArtPoll Packet
*/
struct ArtPoll
: public Packet
{
ArtPoll()
: Packet(std::make_shared<poll_data>())
{ _opcode = OpPoll; }
};
/**
* @brief The ArtPollReply Packet
*/
struct ArtPollReply
: public Packet
{
ArtPollReply()
: Packet(std::make_shared<pollreply_data>())
{ _opcode = OpPollReply; }
};
/**
* @brief The ArtIpProg Packet
*/
struct ArtIpProg
: public Packet
{
ArtIpProg()
: Packet(std::make_shared<ipprog_data>())
{ _opcode = OpIpProg; }
};
/**
* @brief The ArtIpProgReply Packet
*/
struct ArtIpProgReply
: public Packet
{
ArtIpProgReply()
: Packet(std::make_shared<ipprogreply_data>())
{ _opcode = OpIpProgReply; }
};
/**
* @brief The ArtAddress Packet
*/
struct ArtAddress
: public Packet
{
ArtAddress()
: Packet(std::make_shared<address_data>())
{ _opcode = OpAddress; }
};
/**
* @brief The ArtDiagData Packet
*/
struct ArtDiagData
: public Packet
{
ArtDiagData()
: Packet(std::make_shared<diagdata_data>())
{ _opcode = OpDiagData; }
};
/**
* @brief The ArtTimeCode Packet
*/
struct ArtTimeCode
: public Packet
{
ArtTimeCode()
: Packet(std::make_shared<timecode_data>())
{ _opcode = OpTimeCode; }
};
/**
* @brief The ArtCommand Packet
*/
struct ArtCommand
: public Packet
{
ArtCommand()
: Packet(std::make_shared<command_data>())
{ _opcode = OpCommand; }
};
/**
* @brief The ArtTrigger Packet
*/
struct ArtTrigger
: public Packet
{
ArtTrigger()
: Packet(std::make_shared<trigger_data>())
{ _opcode = OpTrigger; }
};
/**
* @brief The ArtDmx Packet
*/
struct ArtDmx
: public Packet
{
ArtDmx()
: Packet(std::make_shared<dmx_data>())
{ _opcode = OpDmx; }
};
/**
* @brief The ArtSync Packet
*/
struct ArtSync
: public Packet
{
ArtSync()
: Packet(std::make_shared<sync_data>())
{ _opcode = OpSync; }
};
/**
* @brief The ArtNzs Packet
*/
struct ArtNzs
: public Packet
{
ArtNzs()
: Packet(std::make_shared<nzs_data>())
{ _opcode = OpNzs; }
};
/**
* @brief The ArtVlc Packet
*/
struct ArtVlc
: public Packet
{
ArtVlc()
: Packet(std::make_shared<vlc_data>())
{ _opcode = OpNzs; }
};
/**
* @brief The ArtInput Packet
*/
struct ArtInput
: public Packet
{
ArtInput()
: Packet(std::make_shared<input_data>())
{ _opcode = OpInput; }
};
/**
* @brief The ArtFirmwareMaster Packet
*/
struct ArtFirmwareMaster
: public Packet
{
ArtFirmwareMaster()
: Packet(std::make_shared<firmwaremaster_data>())
{ _opcode = OpFirmwareMaster; }
};
/**
* @brief The ArtFirmwareReply Packet
*/
struct ArtFirmwareReply
: public Packet
{
ArtFirmwareReply()
: Packet(std::make_shared<firmwarereply_data>())
{ _opcode = OpFirmwareReply; }
};
/**
* @brief The ArtTodRequest Packet
*/
struct ArtTodRequest
: public Packet
{
ArtTodRequest()
: Packet(std::make_shared<todrequest_data>())
{ _opcode = OpTodRequest; }
};
/**
* @brief The ArtTodData Packet
*/
struct ArtTodData
: public Packet
{
ArtTodData()
: Packet(std::make_shared<toddata_data>())
{ _opcode = OpTodData; }
};
/**
* @brief The ArtTodControl Packet
*/
struct ArtTodControl
: public Packet
{
ArtTodControl()
: Packet(std::make_shared<todcontrol_data>())
{ _opcode = OpTodControl; }
};
/**
* @brief The ArtRdm Packet
*/
struct ArtRdm
: public Packet
{
ArtRdm()
: Packet(std::make_shared<rdm_data>())
{ _opcode = OpRdm; }
};
/**
* @brief The Art Packet
*/
struct ArtRdmSub
: public Packet
{
ArtRdmSub()
: Packet(std::make_shared<rdmsub_data>())
{ _opcode = OpRdmSub; }
};
} // namespace ARTNET