]> git.basschouten.com Git - openhab-addons.git/blob
3255789116205869187f8b9ab0db981f701298de
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.dmx.internal.dmxoverethernet;
14
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;
21
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;
30
31 /**
32  * The {@link DmxOverEthernetHandler} is an abstract class with base functions
33  * for DMX over Ethernet Bridges (ArtNet, sACN)
34  *
35  * @author Jan N. Klug - Initial contribution
36  */
37 @NonNullByDefault
38 public abstract class DmxOverEthernetHandler extends DmxBridgeHandler {
39     private final Logger logger = LoggerFactory.getLogger(DmxOverEthernetHandler.class);
40
41     protected @Nullable DmxOverEthernetPacket packetTemplate;
42     protected IpNode senderNode = new IpNode();
43     protected List<IpNode> receiverNodes = new ArrayList<>();
44
45     protected boolean refreshAlways = false;
46
47     protected @Nullable DatagramSocket socket = null;
48     private long lastSend = 0;
49     private int repeatCounter = 0;
50     private int sequenceNo = 0;
51
52     @Override
53     protected void openConnection() {
54         if (getThing().getStatus() != ThingStatus.ONLINE) {
55             try {
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());
62                     } else {
63                         socket = new DatagramSocket(senderNode.getPort());
64                         senderNode.setInetAddress(socket.getLocalAddress());
65                     }
66                     this.socket = socket;
67                 } else {
68                     socket = new DatagramSocket(senderNode.getPort(), senderNode.getAddress());
69                 }
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(),
74                         e.getMessage());
75                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "opening UDP socket failed");
76             }
77         }
78     }
79
80     @Override
81     protected void closeConnection() {
82         DatagramSocket socket = this.socket;
83         if (socket != null) {
84             logger.debug("closing socket {} in bridge {}", senderNode, this.thing.getUID());
85             socket.close();
86             this.socket = null;
87         } else {
88             logger.debug("socket was already closed when calling closeConnection in bridge {}", this.thing.getUID());
89         }
90         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "UDP socket closed");
91     }
92
93     @Override
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) {
100                 needsSending = true;
101                 repeatCounter = 0;
102             } else if (now - lastSend > 800) {
103                 needsSending = true;
104             } else if (repeatCounter < 3) {
105                 needsSending = true;
106                 repeatCounter++;
107             }
108             if (needsSending) {
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.",
112                             thing.getUID());
113                     return;
114                 }
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());
124                     try {
125                         DatagramSocket socket = this.socket;
126                         if (socket != null) {
127                             socket.send(sendPacket);
128                         } else {
129                             throw new IOException("Socket for sending not set.");
130                         }
131                     } catch (IOException e) {
132                         logger.debug("Could not send to {} in {}: {}", receiverNode, this.thing.getUID(),
133                                 e.getMessage());
134                         closeConnection(ThingStatusDetail.COMMUNICATION_ERROR, "could not send DMX data");
135                     }
136                 }
137                 lastSend = now;
138                 sequenceNo = (sequenceNo + 1) % 256;
139             }
140         } else {
141             openConnection();
142         }
143     }
144
145     public DmxOverEthernetHandler(Bridge sacnBridge) {
146         super(sacnBridge);
147     }
148 }