From 06ec1acea1eeee6c09ee38fdcf9d66a80c3b3f73 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Tue, 26 May 2026 17:56:28 -0500 Subject: [PATCH] add ArtAddress --- address.go | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 address.go diff --git a/address.go b/address.go new file mode 100644 index 0000000..d65dc94 --- /dev/null +++ b/address.go @@ -0,0 +1,160 @@ +package artnet + +import ( + "encoding/binary" + "errors" + "slices" +) + +type AddressCommand uint8 + +const ( + AcNone AddressCommand = 0x00 + AcCancelMerge AddressCommand = 0x01 + AcLedNormal AddressCommand = 0x02 + AcLedMute AddressCommand = 0x03 + AcLedLocate AddressCommand = 0x04 + AcResetRxFlags AddressCommand = 0x05 + AcAnalysisOn AddressCommand = 0x06 + AcAnalysisOff AddressCommand = 0x07 + AcFailHold AddressCommand = 0x08 + AcFailZero AddressCommand = 0x09 + AcFailFull AddressCommand = 0x0A + AcFailScene AddressCommand = 0x0B + AcFailRecord AddressCommand = 0x0C + AcMergeLtp0 AddressCommand = 0x10 + AcMergeLtp1 AddressCommand = 0x11 + AcMergeLtp2 AddressCommand = 0x12 + AcMergeLtp3 AddressCommand = 0x13 + AcDirectionTx0 AddressCommand = 0x20 + AcDirectionTx1 AddressCommand = 0x21 + AcDirectionTx2 AddressCommand = 0x22 + AcDirectionTx3 AddressCommand = 0x23 + AcDirectionRx0 AddressCommand = 0x30 + AcDirectionRx1 AddressCommand = 0x31 + AcDirectionRx2 AddressCommand = 0x32 + AcDirectionRx3 AddressCommand = 0x33 + AcMergeHtp0 AddressCommand = 0x50 + AcMergeHtp1 AddressCommand = 0x51 + AcMergeHtp2 AddressCommand = 0x52 + AcMergeHtp3 AddressCommand = 0x53 + AcArtNetSel0 AddressCommand = 0x60 + AcArtNetSel1 AddressCommand = 0x61 + AcArtNetSel2 AddressCommand = 0x62 + AcArtNetSel3 AddressCommand = 0x63 + AcAcnSel0 AddressCommand = 0x70 + AcAcnSel1 AddressCommand = 0x71 + AcAcnSel2 AddressCommand = 0x72 + AcAcnSel3 AddressCommand = 0x73 + AcClearOp0 AddressCommand = 0x90 + AcClearOp1 AddressCommand = 0x91 + AcClearOp2 AddressCommand = 0x92 + AcClearOp3 AddressCommand = 0x93 + AcStyleDelta0 AddressCommand = 0xa0 + AcStyleDelta1 AddressCommand = 0xa1 + AcStyleDelta2 AddressCommand = 0xa2 + AcStyleDelta3 AddressCommand = 0xa3 + AcStyleConst0 AddressCommand = 0xb0 + AcStyleConst1 AddressCommand = 0xb1 + AcStyleConst2 AddressCommand = 0xb2 + AcStyleConst3 AddressCommand = 0xb3 + AcRdmEnable0 AddressCommand = 0xc0 + AcRdmEnable1 AddressCommand = 0xc1 + AcRdmEnable2 AddressCommand = 0xc2 + AcRdmEnable3 AddressCommand = 0xc3 + AcRdmDisable0 AddressCommand = 0xd0 + AcRdmDisable1 AddressCommand = 0xd1 + AcRdmDisable2 AddressCommand = 0xd2 + AcRdmDisable3 AddressCommand = 0xd3 + AcBqp0 AddressCommand = 0xe0 + AcBqp1 AddressCommand = 0xe1 + AcBqp2 AddressCommand = 0xe2 + AcBqp3 AddressCommand = 0xe3 + AcBqp4 AddressCommand = 0xe4 + AcBqp5 AddressCommand = 0xe5 + AcBqp6 AddressCommand = 0xe6 + AcBqp7 AddressCommand = 0xe7 + AcBqp8 AddressCommand = 0xe8 + AcBqp9 AddressCommand = 0xe9 + AcBqp10 AddressCommand = 0xea + AcBqp11 AddressCommand = 0xeb + AcBqp12 AddressCommand = 0xec + AcBqp13 AddressCommand = 0xed + AcBqp14 AddressCommand = 0xee + AcBqp15 AddressCommand = 0xef +) + +type ArtAddress struct { + ID [8]uint8 + OpCode uint16 + ProtVerHi uint8 + ProtVerLo uint8 + NetSwitch uint8 + BindIndex uint8 + PortName [18]uint8 + LongName [64]uint8 + SwIn [4]uint8 + SwOut [4]uint8 + SubSwitch uint8 + AcnPriority uint8 + Command uint8 +} + +func (aa *ArtAddress) GetOpCode() uint16 { + return aa.OpCode +} + +func (aa *ArtAddress) GetProtVer() uint16 { + return uint16(aa.ProtVerHi)<<8 + uint16(aa.ProtVerLo) +} + +func (aa *ArtAddress) GetID() [8]uint8 { + return aa.ID +} + +func (aa *ArtAddress) UnmarshalBinary(data []byte) error { + if len(data) < 107 { + return errors.New("ArtAddress packet must be at least 107 bytes long") + } + + copy(aa.ID[:], data[0:8]) + + if !slices.Equal(ArtNetID[:], aa.ID[:]) { + return errors.New("ID does not match Art-Net ID") + } + + aa.OpCode = binary.LittleEndian.Uint16(data[8:10]) + aa.ProtVerHi = data[10] + aa.ProtVerLo = data[11] + + offset := 12 + aa.NetSwitch = data[offset] + aa.BindIndex = data[offset+1] + copy(aa.PortName[:], data[offset+2:offset+20]) + copy(aa.LongName[:], data[offset+20:offset+84]) + copy(aa.SwIn[:], data[offset+84:offset+88]) + copy(aa.SwOut[:], data[offset+88:offset+92]) + aa.SubSwitch = data[offset+92] + aa.AcnPriority = data[offset+93] + aa.Command = data[offset+94] + return nil +} + +func (aa *ArtAddress) MarshalBinary() ([]byte, error) { + data := make([]byte, 8+11) + copy(data[0:8], aa.ID[:]) + binary.LittleEndian.PutUint16(data[8:10], aa.OpCode) + data[10] = aa.ProtVerHi + data[11] = aa.ProtVerLo + offset := 12 + data[offset] = aa.NetSwitch + data[offset+1] = aa.BindIndex + copy(data[offset+2:offset+20], aa.PortName[:]) + copy(data[offset+20:offset+84], aa.LongName[:]) + copy(data[offset+84:offset+88], aa.SwIn[:]) + copy(data[offset+88:offset+92], aa.SwOut[:]) + data[offset+92] = aa.SubSwitch + data[offset+93] = aa.AcnPriority + data[offset+94] = aa.Command + return data, nil +}