switch to unmarshal marshal

This commit is contained in:
Joel Wetzell
2025-12-23 14:10:31 -06:00
parent 4326f99776
commit 5097901aa9
6 changed files with 143 additions and 135 deletions
+18 -15
View File
@@ -1,6 +1,10 @@
package artnet
import "fmt"
import (
"encoding/binary"
"errors"
"fmt"
)
var (
OpPoll uint16 = 0x2000
@@ -43,20 +47,19 @@ var (
OpDirectoryReply uint16 = 0x9b00
)
func Decode(bytes []byte) (ArtNetPacket, error) {
header, err := NewHeader(bytes)
if err != nil {
return nil, err
}
switch header.OpCode {
case OpPoll:
return NewArtPoll(header, bytes)
case OpCommand:
return NewArtCommand(header, bytes)
case OpDmx:
return NewArtDmx(header, bytes)
default:
return nil, fmt.Errorf("unhandled opcode: %#x", header.OpCode)
var ArtNetID []uint8 = []uint8{'A', 'r', 't', '-', 'N', 'e', 't', 0x00}
func Decode(bytes []byte) (ArtNetPacket, error) {
if len(bytes) < 12 {
return nil, errors.New("ArtNet packet must be at least 12 bytes")
}
opCode := binary.LittleEndian.Uint16(bytes[8:10])
switch opCode {
case OpPoll:
return NewArtPoll(bytes)
case OpDmx:
return NewArtDmx(bytes)
default:
return nil, fmt.Errorf("unhandled opcode: %#x", opCode)
}
}
-43
View File
@@ -1,43 +0,0 @@
package artnet
import (
"errors"
)
type ArtCommand struct {
Header *ArtNetHeader
EstaManHi uint8
EstaManLo uint8
Length uint16
Data string
}
func NewArtCommand(header *ArtNetHeader, data []byte) (*ArtCommand, error) {
artCommand := ArtCommand{
Header: header,
}
if len(data) < 18 {
return nil, errors.New("ArtCommand packet must be at least 14 bytes long")
}
offset := 12
artCommand.EstaManHi = data[offset]
artCommand.EstaManLo = data[offset+1]
artCommand.Length = uint16(data[offset+2])<<8 + uint16(data[offset+3])
commandDataOffset := offset + 4
if len(data[commandDataOffset:]) < int(artCommand.Length) {
return nil, errors.New("ArtCommand packet length mismatch")
}
artCommand.Data = string(data[commandDataOffset : commandDataOffset+int(artCommand.Length)-1])
return &artCommand, nil
}
func (ap *ArtCommand) GetOpCode() uint16 {
return ap.Header.OpCode
}
+70 -34
View File
@@ -1,49 +1,85 @@
package artnet
import (
"encoding/binary"
"errors"
"slices"
)
type ArtDmx struct {
Header *ArtNetHeader
Sequence uint8
Physical uint8
SubUni uint8
Net uint8
Length uint16
Data []uint8
ID []uint8
OpCode uint16
ProtVerHi uint8
ProtVerLo uint8
Sequence uint8
Physical uint8
SubUni uint8
Net uint8
Length uint16
Data []uint8
}
func NewArtDmx(header *ArtNetHeader, data []byte) (*ArtDmx, error) {
artDmx := ArtDmx{
Header: header,
func NewArtDmx(data []byte) (*ArtDmx, error) {
artDmx := ArtDmx{}
err := artDmx.UnmarshalBinary(data)
if err != nil {
return nil, err
}
if len(data) < 18 {
return nil, errors.New("ArtDmx packet must be at least 14 bytes long")
}
offset := 12
artDmx.Sequence = data[offset]
artDmx.Physical = data[offset+1]
artDmx.SubUni = data[offset+2]
artDmx.Net = data[offset+3]
artDmx.Length = uint16(data[offset+4])<<8 + uint16(data[offset+5])
dmxDataOffset := offset + 6
if len(data[dmxDataOffset:]) < int(artDmx.Length) {
return nil, errors.New("ArtDmx packet length mismatch")
}
artDmx.Data = make([]uint8, artDmx.Length)
copy(artDmx.Data, data[dmxDataOffset:dmxDataOffset+int(artDmx.Length)])
return &artDmx, nil
}
func (ap *ArtDmx) GetOpCode() uint16 {
return ap.Header.OpCode
func (ad *ArtDmx) GetOpCode() uint16 {
return ad.OpCode
}
func (ad *ArtDmx) GetProtVer() uint16 {
return uint16(ad.ProtVerHi)<<8 + uint16(ad.ProtVerLo)
}
func (ad *ArtDmx) GetID() []uint8 {
return ad.ID
}
func (ad *ArtDmx) UnmarshalBinary(data []byte) error {
if len(data) < 18 {
return errors.New("ArtDmx packet must be at least 18 bytes long")
}
if !slices.Equal(ArtNetID, data[0:8]) {
return errors.New("ID does not match Art-Net ID")
}
ad.OpCode = binary.LittleEndian.Uint16(data[8:10])
ad.ProtVerHi = data[10]
ad.ProtVerLo = data[11]
offset := 12
ad.Sequence = data[offset]
ad.Physical = data[offset+1]
ad.SubUni = data[offset+2]
ad.Net = data[offset+3]
ad.Length = uint16(data[offset+4])<<8 + uint16(data[offset+5])
dmxDataOffset := offset + 6
if len(data[dmxDataOffset:]) < int(ad.Length) {
return errors.New("ArtDmx packet length mismatch")
}
ad.Data = make([]uint8, ad.Length)
copy(ad.Data, data[dmxDataOffset:dmxDataOffset+int(ad.Length)])
return nil
}
func (ad *ArtDmx) MarshalBinary() ([]byte, error) {
data := []byte(ArtNetID)
data = append(data, byte(ad.OpCode), byte(ad.OpCode>>8), ad.ProtVerHi, ad.ProtVerLo, ad.Sequence, ad.Physical, ad.SubUni, ad.Net, byte(ad.Length>>8), byte(ad.Length))
data = append(data, ad.Data...)
return data, nil
}
+1 -1
View File
@@ -1,3 +1,3 @@
module github.com/jwetzell/artnet-go
go 1.23.1
go 1.25.5
+5 -29
View File
@@ -1,37 +1,13 @@
package artnet
import (
"encoding/binary"
"errors"
"slices"
"encoding"
)
type ArtNetPacket interface {
encoding.BinaryUnmarshaler
encoding.BinaryMarshaler
GetOpCode() uint16
}
var ArtNetID []uint8 = []uint8{'A', 'r', 't', '-', 'N', 'e', 't', 0x00}
type ArtNetHeader struct {
ID []uint8
OpCode uint16
ProtVerHi uint8
ProtVerLo uint8
}
func NewHeader(data []byte) (*ArtNetHeader, error) {
if len(data) < 12 {
return nil, errors.New("header must be at least 12 bytes")
}
if !slices.Equal(ArtNetID, data[0:8]) {
return nil, errors.New("header id does not match Art-Net ID")
}
return &ArtNetHeader{
OpCode: binary.LittleEndian.Uint16(data[8:10]),
ProtVerHi: data[10],
ProtVerLo: data[11],
}, nil
GetProtVer() uint16
GetID() []uint8
}
+49 -13
View File
@@ -1,11 +1,16 @@
package artnet
import (
"encoding/binary"
"errors"
"slices"
)
type ArtPoll struct {
Header *ArtNetHeader
ID []uint8
OpCode uint16
ProtVerHi uint8
ProtVerLo uint8
Flags uint8
DiagPriority uint8
AddressTopHi uint8
@@ -18,23 +23,54 @@ type ArtPoll struct {
OemLo uint8
}
func NewArtPoll(header *ArtNetHeader, data []byte) (*ArtPoll, error) {
artPoll := ArtPoll{
Header: header,
}
func NewArtPoll(data []byte) (*ArtPoll, error) {
artPoll := ArtPoll{}
if len(data) < 14 {
return nil, errors.New("ArtPoll packet must be at least 14 bytes long")
}
offset := 12
artPoll.Flags = data[offset]
artPoll.DiagPriority = data[offset+1]
err := artPoll.UnmarshalBinary(data)
//TODO(jwetzell): unpack extended poll fields
if err != nil {
return nil, err
}
return &artPoll, nil
}
func (ap *ArtPoll) GetOpCode() uint16 {
return ap.Header.OpCode
return ap.OpCode
}
func (ap *ArtPoll) GetProtVer() uint16 {
return uint16(ap.ProtVerHi)<<8 + uint16(ap.ProtVerLo)
}
func (ap *ArtPoll) GetID() []uint8 {
return ap.ID
}
func (ap *ArtPoll) UnmarshalBinary(data []byte) error {
if len(data) < 14 {
return errors.New("ArtPoll packet must be at least 14 bytes long")
}
if !slices.Equal(ArtNetID, data[0:8]) {
return errors.New("ID does not match Art-Net ID")
}
ap.OpCode = binary.LittleEndian.Uint16(data[8:10])
ap.ProtVerHi = data[10]
ap.ProtVerLo = data[11]
offset := 12
ap.Flags = data[offset]
ap.DiagPriority = data[offset+1]
//TODO(jwetzell): unpack extended poll fields
return nil
}
func (ap *ArtPoll) MarshalBinary() ([]byte, error) {
data := []byte(ArtNetID)
data = append(data, byte(ap.OpCode), byte(ap.OpCode>>8), ap.ProtVerHi, ap.ProtVerLo, ap.Flags, ap.DiagPriority)
//TODO(jwetzell): pack extended poll fields
return data, nil
}