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.eclipse.jdt.annotation.NonNullByDefault;
20 import org.openhab.binding.dmx.internal.Util;
21 import org.openhab.binding.dmx.internal.multiverse.Universe;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
26 * The {@link SacnPacket} is responsible for handling commands, which are
29 * @author Jan N. Klug - Initial contribution
32 public class SacnPacket extends DmxOverEthernetPacket {
33 public static final int SACN_MAX_PACKET_LEN = 638;
34 public static final int SACN_MAX_PAYLOAD_SIZE = 512;
36 private final Logger logger = LoggerFactory.getLogger(SacnPacket.class);
39 * default constructor, creates a packet
41 * @param uuid UUID is mandatory
44 public SacnPacket(UUID uuid) {
45 payloadSize = SACN_MAX_PAYLOAD_SIZE;
46 rawPacket = new byte[SACN_MAX_PACKET_LEN];
48 /* init E1.31 root layer, total length 38 bytes */
49 rawPacket[0] = 0x00; // preamble size, 2 bytes
51 rawPacket[2] = 0x00; // postamble size, 2 bytes
53 rawPacket[4] = 0x41; // packet identifier, 12 bytes
65 rawPacket[16] = 0x72; // flags & length, 2 bytes
67 rawPacket[18] = 0x00; // vector, 4 bytes;
73 ByteBuffer uuidBytes = ByteBuffer.wrap(new byte[16]);
74 uuidBytes.putLong(uuid.getMostSignificantBits());
75 uuidBytes.putLong(uuid.getLeastSignificantBits());
76 System.arraycopy(uuidBytes.array(), 0, rawPacket, 22, 16);
78 /* init sACN/E1.31 framing layer, total length 77 bytes */
79 rawPacket[38] = 0x72; // flags & length, 2 bytes
81 rawPacket[40] = 0x00; // vector, 4 bytes;
85 for (int i = 44; i < 108; i++) { // senderName, 64 bytes
88 rawPacket[108] = 0x64; // priority (default 100), 1 byte
89 rawPacket[109] = 0x00; // reserved, 2 bytes
90 rawPacket[110] = 0x00;
91 rawPacket[111] = 0x00; // sequence number, 1 byte
92 rawPacket[112] = 0x00; // options, 1 byte
93 rawPacket[113] = 0x00; // universe, 2 bytes
94 rawPacket[114] = 0x00;
96 /* sACN/E1.31 DMP layer, total length 11 + channel count */
97 rawPacket[115] = 0x72; // flags & length, 2 bytes
98 rawPacket[116] = 0x0b;
99 rawPacket[117] = 0x02; // vector, 1 byte
100 rawPacket[118] = (byte) 0xa1; // address type, 1 byte
101 rawPacket[119] = 0x00; // start address, 2 bytes
102 rawPacket[120] = 0x00;
103 rawPacket[121] = 0x00; // address increment, 2 bytes
104 rawPacket[122] = 0x01;
105 rawPacket[123] = 0x02; // payload size, 2 bytes (including start code)
106 rawPacket[123] = 0x01;
107 rawPacket[125] = 0x00; // DMX start code, 1 byte
111 public void setPayloadSize(int payloadSize) throws IllegalArgumentException {
112 if (payloadSize < Universe.MIN_UNIVERSE_SIZE) {
113 throw new IllegalArgumentException(
114 String.format("payload minimum size is %d slots (>%d)", Universe.MIN_UNIVERSE_SIZE, payloadSize));
115 } else if (payloadSize > Universe.MAX_UNIVERSE_SIZE) {
116 throw new IllegalArgumentException(
117 String.format("payload maximum size is %d slots (<%d)", Universe.MAX_UNIVERSE_SIZE, payloadSize));
121 rawPacket[16] = (byte) ((28672 + 110 + payloadSize) / 256);
122 rawPacket[17] = (byte) ((28672 + 110 + payloadSize) % 256);
125 rawPacket[38] = (byte) ((28672 + 88 + payloadSize) / 256);
126 rawPacket[39] = (byte) ((28672 + 88 + payloadSize) % 256);
129 rawPacket[115] = (byte) ((28672 + 11 + payloadSize) / 256);
130 rawPacket[116] = (byte) ((28672 + 11 + payloadSize) % 256);
131 rawPacket[123] = (byte) ((payloadSize + 1) / 256);
132 rawPacket[124] = (byte) ((payloadSize + 1) % 256);
134 this.payloadSize = payloadSize;
138 public void setUniverse(int universeId) {
139 this.universeId = universeId;
141 /* set universe in packet */
142 rawPacket[113] = (byte) (this.universeId / 256);
143 rawPacket[114] = (byte) (this.universeId % 256);
145 /* set sender name in packet */
146 String senderName = new String("openHAB DMX binding (sACN) <" + String.format("%05d", this.universeId) + ">");
147 byte[] senderNameBytes = senderName.getBytes(StandardCharsets.UTF_8);
148 System.arraycopy(senderNameBytes, 0, rawPacket, 44, senderName.length());
150 logger.trace("set packet universe to {}", this.universeId);
154 public void setSequence(int sequenceNo) {
155 rawPacket[111] = (byte) (sequenceNo % 256);
161 * @param priority data priority (for multiple senders), allowed values are 0-200, default 100
163 public void setPriority(int priority) {
164 /* observe limits (coerce to range) */
165 rawPacket[108] = (byte) Util.coerceToRange(priority, 0, 200, logger, "packet priority");
166 logger.debug("set packet priority to {}", priority);
170 public void setPayload(byte[] payload) {
171 System.arraycopy(payload, 0, rawPacket, 126, payloadSize);
175 public void setPayload(byte[] payload, int payloadSize) {
176 if (payloadSize != this.payloadSize) {
177 setPayloadSize(payloadSize);
183 public int getPacketLength() {
184 return (126 + this.payloadSize);