2 * Copyright (c) 2010-2022 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.plugwise.internal;
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;
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;
41 * The communication context used by the {@link PlugwiseMessageSender} and {@link PlugwiseMessageProcessor} for sending
42 * and receiving messages.
44 * @author Wouter Born, Karel Goderis - Initial contribution
47 public class PlugwiseCommunicationContext {
49 /** Plugwise protocol header code (hex) */
50 public static final String PROTOCOL_HEADER = "\u0005\u0005\u0003\u0003";
52 /** Carriage return */
53 public static final char CR = '\r';
56 public static final char LF = '\n';
58 /** Plugwise protocol trailer code (hex) */
59 public static final String PROTOCOL_TRAILER = new String(new char[] { CR, LF });
61 public static final int MAX_BUFFER_SIZE = 1024;
63 private static final Comparator<? super @Nullable PlugwiseQueuedMessage> QUEUED_MESSAGE_COMPERATOR = new Comparator<@Nullable PlugwiseQueuedMessage>() {
65 public int compare(@Nullable PlugwiseQueuedMessage o1, @Nullable PlugwiseQueuedMessage o2) {
66 if (o1 == null || o2 == null) {
69 int result = o1.getPriority().compareTo(o2.getPriority());
71 result = o1.getDateTime().compareTo(o2.getDateTime());
77 private final Logger logger = LoggerFactory.getLogger(PlugwiseCommunicationContext.class);
78 private final BlockingQueue<@Nullable AcknowledgementMessage> acknowledgedQueue = new ArrayBlockingQueue<>(
79 MAX_BUFFER_SIZE, true);
80 private final BlockingQueue<@Nullable Message> receivedQueue = new ArrayBlockingQueue<>(MAX_BUFFER_SIZE, true);
81 private final PriorityBlockingQueue<@Nullable PlugwiseQueuedMessage> sendQueue = new PriorityBlockingQueue<>(
82 MAX_BUFFER_SIZE, QUEUED_MESSAGE_COMPERATOR);
83 private final BlockingQueue<@Nullable PlugwiseQueuedMessage> sentQueue = new ArrayBlockingQueue<>(MAX_BUFFER_SIZE,
85 private final ReentrantLock sentQueueLock = new ReentrantLock();
86 private final PlugwiseFilteredMessageListenerList filteredListeners = new PlugwiseFilteredMessageListenerList();
88 private final ThingUID bridgeUID;
89 private final Supplier<PlugwiseStickConfig> configurationSupplier;
90 private final SerialPortManager serialPortManager;
91 private @Nullable SerialPort serialPort;
93 public PlugwiseCommunicationContext(ThingUID bridgeUID, Supplier<PlugwiseStickConfig> configurationSupplier,
94 SerialPortManager serialPortManager) {
95 this.bridgeUID = bridgeUID;
96 this.configurationSupplier = configurationSupplier;
97 this.serialPortManager = serialPortManager;
100 public void clearQueues() {
101 acknowledgedQueue.clear();
102 receivedQueue.clear();
107 public void closeSerialPort() {
108 SerialPort localSerialPort = serialPort;
109 if (localSerialPort != null) {
111 InputStream inputStream = localSerialPort.getInputStream();
112 if (inputStream != null) {
115 } catch (IOException e) {
116 logger.debug("Error while closing the input stream: {}", e.getMessage());
119 OutputStream outputStream = localSerialPort.getOutputStream();
120 if (outputStream != null) {
122 outputStream.close();
123 } catch (IOException e) {
124 logger.debug("Error while closing the output stream: {}", e.getMessage());
127 localSerialPort.close();
129 } catch (IOException e) {
130 logger.warn("An exception occurred while closing the serial port {} ({})", localSerialPort,
136 private SerialPortIdentifier findSerialPortIdentifier() throws PlugwiseInitializationException {
137 SerialPortIdentifier identifier = serialPortManager.getIdentifier(getConfiguration().getSerialPort());
138 if (identifier != null) {
139 logger.debug("Serial port '{}' has been found", getConfiguration().getSerialPort());
143 // Build exception message when port not found
144 String availablePorts = serialPortManager.getIdentifiers().map(id -> id.getName())
145 .collect(Collectors.joining(System.lineSeparator()));
147 throw new PlugwiseInitializationException(
148 String.format("Serial port '%s' could not be found. Available ports are:%n%s",
149 getConfiguration().getSerialPort(), availablePorts));
152 public BlockingQueue<@Nullable AcknowledgementMessage> getAcknowledgedQueue() {
153 return acknowledgedQueue;
156 public ThingUID getBridgeUID() {
160 public PlugwiseStickConfig getConfiguration() {
161 return configurationSupplier.get();
164 public PlugwiseFilteredMessageListenerList getFilteredListeners() {
165 return filteredListeners;
168 public BlockingQueue<@Nullable Message> getReceivedQueue() {
169 return receivedQueue;
172 public PriorityBlockingQueue<@Nullable PlugwiseQueuedMessage> getSendQueue() {
176 public BlockingQueue<@Nullable PlugwiseQueuedMessage> getSentQueue() {
180 public ReentrantLock getSentQueueLock() {
181 return sentQueueLock;
184 public @Nullable SerialPort getSerialPort() {
189 * Initialize this device and open the serial port
191 * @throws PlugwiseInitializationException if port can not be found or opened
193 public void initializeSerialPort() throws PlugwiseInitializationException {
195 SerialPort localSerialPort = findSerialPortIdentifier().open(getClass().getName(), 2000);
196 localSerialPort.notifyOnDataAvailable(true);
197 localSerialPort.setSerialPortParams(115200, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
198 SerialPort.PARITY_NONE);
199 serialPort = localSerialPort;
200 } catch (PortInUseException e) {
201 throw new PlugwiseInitializationException("Serial port already in use", e);
202 } catch (UnsupportedCommOperationException e) {
203 throw new PlugwiseInitializationException("Failed to set serial port parameters", e);