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.io.IOException;
16 import java.net.DatagramPacket;
17 import java.net.DatagramSocket;
18 import java.net.SocketException;
19 import java.util.ArrayList;
20 import java.util.List;
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.dmx.internal.DmxBridgeHandler;
25 import org.openhab.core.thing.Bridge;
26 import org.openhab.core.thing.ThingStatus;
27 import org.openhab.core.thing.ThingStatusDetail;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
32 * The {@link DmxOverEthernetHandler} is an abstract class with base functions
33 * for DMX over Ethernet Bridges (ArtNet, sACN)
35 * @author Jan N. Klug - Initial contribution
38 public abstract class DmxOverEthernetHandler extends DmxBridgeHandler {
39 private final Logger logger = LoggerFactory.getLogger(DmxOverEthernetHandler.class);
41 protected @Nullable DmxOverEthernetPacket packetTemplate;
42 protected IpNode senderNode = new IpNode();
43 protected List<IpNode> receiverNodes = new ArrayList<>();
45 protected boolean refreshAlways = false;
47 protected @Nullable DatagramSocket socket = null;
48 private long lastSend = 0;
49 private int repeatCounter = 0;
50 private int sequenceNo = 0;
53 protected void openConnection() {
54 if (getThing().getStatus() != ThingStatus.ONLINE) {
56 if (senderNode.getAddress() == null) {
57 DatagramSocket socket;
58 if (senderNode.getPort() == 0) {
59 socket = new DatagramSocket();
60 senderNode.setInetAddress(socket.getLocalAddress());
61 senderNode.setPort(socket.getLocalPort());
63 socket = new DatagramSocket(senderNode.getPort());
64 senderNode.setInetAddress(socket.getLocalAddress());
68 socket = new DatagramSocket(senderNode.getPort(), senderNode.getAddress());
70 updateStatus(ThingStatus.ONLINE);
71 logger.debug("opened socket {} in bridge {}", senderNode, this.thing.getUID());
72 } catch (SocketException e) {
73 logger.debug("could not open socket {} in bridge {}: {}", senderNode, this.thing.getUID(),
75 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "opening UDP socket failed");
81 protected void closeConnection() {
82 DatagramSocket socket = this.socket;
84 logger.debug("closing socket {} in bridge {}", senderNode, this.thing.getUID());
88 logger.debug("socket was already closed when calling closeConnection in bridge {}", this.thing.getUID());
90 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "UDP socket closed");
94 protected void sendDmxData() {
95 if (getThing().getStatus() == ThingStatus.ONLINE) {
96 boolean needsSending = false;
97 long now = System.currentTimeMillis();
98 universe.calculateBuffer(now);
99 if ((universe.getLastBufferChanged() > lastSend) || refreshAlways) {
102 } else if (now - lastSend > 800) {
104 } else if (repeatCounter < 3) {
109 DmxOverEthernetPacket packetTemplate = this.packetTemplate;
110 if (packetTemplate == null) {
111 logger.warn("Packet template missing when trying to send data for '{}'. This is a bug.",
115 packetTemplate.setPayload(universe.getBuffer(), universe.getBufferSize());
116 packetTemplate.setSequence(sequenceNo);
117 DatagramPacket sendPacket = new DatagramPacket(packetTemplate.getRawPacket(),
118 packetTemplate.getPacketLength());
119 for (IpNode receiverNode : receiverNodes) {
120 sendPacket.setAddress(receiverNode.getAddress());
121 sendPacket.setPort(receiverNode.getPort());
122 logger.trace("sending packet with length {} to {}", packetTemplate.getPacketLength(),
123 receiverNode.toString());
125 DatagramSocket socket = this.socket;
126 if (socket != null) {
127 socket.send(sendPacket);
129 throw new IOException("Socket for sending not set.");
131 } catch (IOException e) {
132 logger.debug("Could not send to {} in {}: {}", receiverNode, this.thing.getUID(),
134 closeConnection(ThingStatusDetail.COMMUNICATION_ERROR, "could not send DMX data");
138 sequenceNo = (sequenceNo + 1) % 256;
145 public DmxOverEthernetHandler(Bridge sacnBridge) {