2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.dmx.internal.dmxoverethernet;
15 import java.nio.ByteBuffer;
16 import java.nio.charset.StandardCharsets;
17 import java.util.UUID;
19 import org.openhab.binding.dmx.internal.Util;
20 import org.openhab.binding.dmx.internal.multiverse.Universe;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
25 * The {@link SacnPacket} is responsible for handling commands, which are
28 * @author Jan N. Klug - Initial contribution
30 public class SacnPacket extends DmxOverEthernetPacket {
31 public static final int SACN_MAX_PACKET_LEN = 638;
32 public static final int SACN_MAX_PAYLOAD_SIZE = 512;
34 private final Logger logger = LoggerFactory.getLogger(SacnPacket.class);
37 * default constructor, creates a packet
39 * @param uuid UUID is mandatory
42 public SacnPacket(UUID uuid) {
43 payloadSize = SACN_MAX_PAYLOAD_SIZE;
44 rawPacket = new byte[SACN_MAX_PACKET_LEN];
46 /* init E1.31 root layer, total length 38 bytes */
47 rawPacket[0] = 0x00; // preamble size, 2 bytes
49 rawPacket[2] = 0x00; // postamble size, 2 bytes
51 rawPacket[4] = 0x41; // packet identifier, 12 bytes
63 rawPacket[16] = 0x72; // flags & length, 2 bytes
65 rawPacket[18] = 0x00; // vector, 4 bytes;
71 ByteBuffer uuidBytes = ByteBuffer.wrap(new byte[16]);
72 uuidBytes.putLong(uuid.getMostSignificantBits());
73 uuidBytes.putLong(uuid.getLeastSignificantBits());
74 System.arraycopy(uuidBytes.array(), 0, rawPacket, 22, 16);
76 /* init sACN/E1.31 framing layer, total length 77 bytes */
77 rawPacket[38] = 0x72; // flags & length, 2 bytes
79 rawPacket[40] = 0x00; // vector, 4 bytes;
83 for (int i = 44; i < 108; i++) { // senderName, 64 bytes
86 rawPacket[108] = 0x64; // priority (default 100), 1 byte
87 rawPacket[109] = 0x00; // reserved, 2 bytes
88 rawPacket[110] = 0x00;
89 rawPacket[111] = 0x00; // sequence number, 1 byte
90 rawPacket[112] = 0x00; // options, 1 byte
91 rawPacket[113] = 0x00; // universe, 2 bytes
92 rawPacket[114] = 0x00;
94 /* sACN/E1.31 DMP layer, total length 11 + channel count */
95 rawPacket[115] = 0x72; // flags & length, 2 bytes
96 rawPacket[116] = 0x0b;
97 rawPacket[117] = 0x02; // vector, 1 byte
98 rawPacket[118] = (byte) 0xa1; // address type, 1 byte
99 rawPacket[119] = 0x00; // start address, 2 bytes
100 rawPacket[120] = 0x00;
101 rawPacket[121] = 0x00; // address increment, 2 bytes
102 rawPacket[122] = 0x01;
103 rawPacket[123] = 0x02; // payload size, 2 bytes (including start code)
104 rawPacket[123] = 0x01;
105 rawPacket[125] = 0x00; // DMX start code, 1 byte
109 public void setPayloadSize(int payloadSize) throws IllegalArgumentException {
110 if (payloadSize < Universe.MIN_UNIVERSE_SIZE) {
111 throw new IllegalArgumentException(
112 String.format("payload minimum size is %d slots (>%d)", Universe.MIN_UNIVERSE_SIZE, payloadSize));
113 } else if (payloadSize > Universe.MAX_UNIVERSE_SIZE) {
114 throw new IllegalArgumentException(
115 String.format("payload maximum size is %d slots (<%d)", Universe.MAX_UNIVERSE_SIZE, payloadSize));
119 rawPacket[16] = (byte) ((28672 + 110 + payloadSize) / 256);
120 rawPacket[17] = (byte) ((28672 + 110 + payloadSize) % 256);
123 rawPacket[38] = (byte) ((28672 + 88 + payloadSize) / 256);
124 rawPacket[39] = (byte) ((28672 + 88 + payloadSize) % 256);
127 rawPacket[115] = (byte) ((28672 + 11 + payloadSize) / 256);
128 rawPacket[116] = (byte) ((28672 + 11 + payloadSize) % 256);
129 rawPacket[123] = (byte) ((payloadSize + 1) / 256);
130 rawPacket[124] = (byte) ((payloadSize + 1) % 256);
132 this.payloadSize = payloadSize;
136 public void setUniverse(int universeId) {
137 this.universeId = universeId;
139 /* set universe in packet */
140 rawPacket[113] = (byte) (this.universeId / 256);
141 rawPacket[114] = (byte) (this.universeId % 256);
143 /* set sender name in packet */
144 String senderName = new String("openHAB DMX binding (sACN) <" + String.format("%05d", this.universeId) + ">");
145 byte[] senderNameBytes = senderName.getBytes(StandardCharsets.UTF_8);
146 System.arraycopy(senderNameBytes, 0, rawPacket, 44, senderName.length());
148 logger.trace("set packet universe to {}", this.universeId);
152 public void setSequence(int sequenceNo) {
153 rawPacket[111] = (byte) (sequenceNo % 256);
159 * @param priority data priority (for multiple senders), allowed values are 0-200, default 100
161 public void setPriority(int priority) {
162 /* observe limits (coerce to range) */
163 rawPacket[108] = (byte) Util.coerceToRange(priority, 0, 200, logger, "packet priority");
164 logger.debug("set packet priority to {}", priority);
168 public void setPayload(byte[] payload) {
169 System.arraycopy(payload, 0, rawPacket, 126, payloadSize);
173 public void setPayload(byte[] payload, int payloadSize) {
174 if (payloadSize != this.payloadSize) {
175 setPayloadSize(payloadSize);
181 public int getPacketLength() {
182 return (126 + this.payloadSize);