]> git.basschouten.com Git - openhab-addons.git/blob
1c0271d17fd635dfcba76b45e232b50c2b5b064c
[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.plugwise.internal;
14
15 import java.io.IOException;
16 import java.io.InputStream;
17 import java.io.OutputStream;
18 import java.util.Comparator;
19 import java.util.concurrent.ArrayBlockingQueue;
20 import java.util.concurrent.BlockingQueue;
21 import java.util.concurrent.PriorityBlockingQueue;
22 import java.util.concurrent.locks.ReentrantLock;
23 import java.util.function.Supplier;
24 import java.util.stream.Collectors;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.openhab.binding.plugwise.internal.config.PlugwiseStickConfig;
29 import org.openhab.binding.plugwise.internal.protocol.AcknowledgementMessage;
30 import org.openhab.binding.plugwise.internal.protocol.Message;
31 import org.openhab.core.io.transport.serial.PortInUseException;
32 import org.openhab.core.io.transport.serial.SerialPort;
33 import org.openhab.core.io.transport.serial.SerialPortIdentifier;
34 import org.openhab.core.io.transport.serial.SerialPortManager;
35 import org.openhab.core.io.transport.serial.UnsupportedCommOperationException;
36 import org.openhab.core.thing.ThingUID;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41  * The communication context used by the {@link PlugwiseMessageSender} and {@link PlugwiseMessageProcessor} for sending
42  * and receiving messages.
43  *
44  * @author Wouter Born, Karel Goderis - Initial contribution
45  */
46 @NonNullByDefault
47 public class PlugwiseCommunicationContext {
48
49     /** Plugwise protocol header code (hex) */
50     public static final String PROTOCOL_HEADER = "\u0005\u0005\u0003\u0003";
51
52     /** Carriage return */
53     public static final char CR = '\r';
54
55     /** Line feed */
56     public static final char LF = '\n';
57
58     /** Plugwise protocol trailer code (hex) */
59     public static final String PROTOCOL_TRAILER = new String(new char[] { CR, LF });
60
61     public static final int MAX_BUFFER_SIZE = 1024;
62
63     private static final Comparator<? super @Nullable PlugwiseQueuedMessage> QUEUED_MESSAGE_COMPARATOR = (o1, o2) -> {
64         if (o1 == null || o2 == null) {
65             return -1;
66         }
67         int result = o1.getPriority().compareTo(o2.getPriority());
68         if (result == 0) {
69             result = o1.getDateTime().compareTo(o2.getDateTime());
70         }
71         return result;
72     };
73
74     private final Logger logger = LoggerFactory.getLogger(PlugwiseCommunicationContext.class);
75     private final BlockingQueue<@Nullable AcknowledgementMessage> acknowledgedQueue = new ArrayBlockingQueue<>(
76             MAX_BUFFER_SIZE, true);
77     private final BlockingQueue<@Nullable Message> receivedQueue = new ArrayBlockingQueue<>(MAX_BUFFER_SIZE, true);
78     private final PriorityBlockingQueue<@Nullable PlugwiseQueuedMessage> sendQueue = new PriorityBlockingQueue<>(
79             MAX_BUFFER_SIZE, QUEUED_MESSAGE_COMPARATOR);
80     private final BlockingQueue<@Nullable PlugwiseQueuedMessage> sentQueue = new ArrayBlockingQueue<>(MAX_BUFFER_SIZE,
81             true);
82     private final ReentrantLock sentQueueLock = new ReentrantLock();
83     private final PlugwiseFilteredMessageListenerList filteredListeners = new PlugwiseFilteredMessageListenerList();
84
85     private final ThingUID bridgeUID;
86     private final Supplier<PlugwiseStickConfig> configurationSupplier;
87     private final SerialPortManager serialPortManager;
88     private @Nullable SerialPort serialPort;
89
90     public PlugwiseCommunicationContext(ThingUID bridgeUID, Supplier<PlugwiseStickConfig> configurationSupplier,
91             SerialPortManager serialPortManager) {
92         this.bridgeUID = bridgeUID;
93         this.configurationSupplier = configurationSupplier;
94         this.serialPortManager = serialPortManager;
95     }
96
97     public void clearQueues() {
98         acknowledgedQueue.clear();
99         receivedQueue.clear();
100         sendQueue.clear();
101         sentQueue.clear();
102     }
103
104     public void closeSerialPort() {
105         SerialPort localSerialPort = serialPort;
106         if (localSerialPort != null) {
107             try {
108                 InputStream inputStream = localSerialPort.getInputStream();
109                 if (inputStream != null) {
110                     try {
111                         inputStream.close();
112                     } catch (IOException e) {
113                         logger.debug("Error while closing the input stream: {}", e.getMessage());
114                     }
115                 }
116                 OutputStream outputStream = localSerialPort.getOutputStream();
117                 if (outputStream != null) {
118                     try {
119                         outputStream.close();
120                     } catch (IOException e) {
121                         logger.debug("Error while closing the output stream: {}", e.getMessage());
122                     }
123                 }
124                 localSerialPort.close();
125                 serialPort = null;
126             } catch (IOException e) {
127                 logger.warn("An exception occurred while closing the serial port {} ({})", localSerialPort,
128                         e.getMessage());
129             }
130         }
131     }
132
133     private SerialPortIdentifier findSerialPortIdentifier() throws PlugwiseInitializationException {
134         SerialPortIdentifier identifier = serialPortManager.getIdentifier(getConfiguration().getSerialPort());
135         if (identifier != null) {
136             logger.debug("Serial port '{}' has been found", getConfiguration().getSerialPort());
137             return identifier;
138         }
139
140         // Build exception message when port not found
141         String availablePorts = serialPortManager.getIdentifiers().map(SerialPortIdentifier::getName)
142                 .collect(Collectors.joining(System.lineSeparator()));
143
144         throw new PlugwiseInitializationException(
145                 String.format("Serial port '%s' could not be found. Available ports are:%n%s",
146                         getConfiguration().getSerialPort(), availablePorts));
147     }
148
149     public BlockingQueue<@Nullable AcknowledgementMessage> getAcknowledgedQueue() {
150         return acknowledgedQueue;
151     }
152
153     public ThingUID getBridgeUID() {
154         return bridgeUID;
155     }
156
157     public PlugwiseStickConfig getConfiguration() {
158         return configurationSupplier.get();
159     }
160
161     public PlugwiseFilteredMessageListenerList getFilteredListeners() {
162         return filteredListeners;
163     }
164
165     public BlockingQueue<@Nullable Message> getReceivedQueue() {
166         return receivedQueue;
167     }
168
169     public PriorityBlockingQueue<@Nullable PlugwiseQueuedMessage> getSendQueue() {
170         return sendQueue;
171     }
172
173     public BlockingQueue<@Nullable PlugwiseQueuedMessage> getSentQueue() {
174         return sentQueue;
175     }
176
177     public ReentrantLock getSentQueueLock() {
178         return sentQueueLock;
179     }
180
181     public @Nullable SerialPort getSerialPort() {
182         return serialPort;
183     }
184
185     /**
186      * Initialize this device and open the serial port
187      *
188      * @throws PlugwiseInitializationException if port can not be found or opened
189      */
190     public void initializeSerialPort() throws PlugwiseInitializationException {
191         try {
192             SerialPort localSerialPort = findSerialPortIdentifier().open(getClass().getName(), 2000);
193             localSerialPort.notifyOnDataAvailable(true);
194             localSerialPort.setSerialPortParams(115200, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
195                     SerialPort.PARITY_NONE);
196             serialPort = localSerialPort;
197         } catch (PortInUseException e) {
198             throw new PlugwiseInitializationException("Serial port already in use", e);
199         } catch (UnsupportedCommOperationException e) {
200             throw new PlugwiseInitializationException("Failed to set serial port parameters", e);
201         }
202     }
203 }