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.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_COMPARATOR = (o1, o2) -> {
64 if (o1 == null || o2 == null) {
67 int result = o1.getPriority().compareTo(o2.getPriority());
69 result = o1.getDateTime().compareTo(o2.getDateTime());
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,
82 private final ReentrantLock sentQueueLock = new ReentrantLock();
83 private final PlugwiseFilteredMessageListenerList filteredListeners = new PlugwiseFilteredMessageListenerList();
85 private final ThingUID bridgeUID;
86 private final Supplier<PlugwiseStickConfig> configurationSupplier;
87 private final SerialPortManager serialPortManager;
88 private @Nullable SerialPort serialPort;
90 public PlugwiseCommunicationContext(ThingUID bridgeUID, Supplier<PlugwiseStickConfig> configurationSupplier,
91 SerialPortManager serialPortManager) {
92 this.bridgeUID = bridgeUID;
93 this.configurationSupplier = configurationSupplier;
94 this.serialPortManager = serialPortManager;
97 public void clearQueues() {
98 acknowledgedQueue.clear();
99 receivedQueue.clear();
104 public void closeSerialPort() {
105 SerialPort localSerialPort = serialPort;
106 if (localSerialPort != null) {
108 InputStream inputStream = localSerialPort.getInputStream();
109 if (inputStream != null) {
112 } catch (IOException e) {
113 logger.debug("Error while closing the input stream: {}", e.getMessage());
116 OutputStream outputStream = localSerialPort.getOutputStream();
117 if (outputStream != null) {
119 outputStream.close();
120 } catch (IOException e) {
121 logger.debug("Error while closing the output stream: {}", e.getMessage());
124 localSerialPort.close();
126 } catch (IOException e) {
127 logger.warn("An exception occurred while closing the serial port {} ({})", localSerialPort,
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());
140 // Build exception message when port not found
141 String availablePorts = serialPortManager.getIdentifiers().map(SerialPortIdentifier::getName)
142 .collect(Collectors.joining(System.lineSeparator()));
144 throw new PlugwiseInitializationException(
145 String.format("Serial port '%s' could not be found. Available ports are:%n%s",
146 getConfiguration().getSerialPort(), availablePorts));
149 public BlockingQueue<@Nullable AcknowledgementMessage> getAcknowledgedQueue() {
150 return acknowledgedQueue;
153 public ThingUID getBridgeUID() {
157 public PlugwiseStickConfig getConfiguration() {
158 return configurationSupplier.get();
161 public PlugwiseFilteredMessageListenerList getFilteredListeners() {
162 return filteredListeners;
165 public BlockingQueue<@Nullable Message> getReceivedQueue() {
166 return receivedQueue;
169 public PriorityBlockingQueue<@Nullable PlugwiseQueuedMessage> getSendQueue() {
173 public BlockingQueue<@Nullable PlugwiseQueuedMessage> getSentQueue() {
177 public ReentrantLock getSentQueueLock() {
178 return sentQueueLock;
181 public @Nullable SerialPort getSerialPort() {
186 * Initialize this device and open the serial port
188 * @throws PlugwiseInitializationException if port can not be found or opened
190 public void initializeSerialPort() throws PlugwiseInitializationException {
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);