]> git.basschouten.com Git - openhab-addons.git/blob
fab83ad856349e8c7e15a28a65855bd11c6faa1f
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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_COMPERATOR = new Comparator<@Nullable PlugwiseQueuedMessage>() {
64         @Override
65         public int compare(@Nullable PlugwiseQueuedMessage o1, @Nullable PlugwiseQueuedMessage o2) {
66             if (o1 == null || o2 == null) {
67                 return -1;
68             }
69             int result = o1.getPriority().compareTo(o2.getPriority());
70             if (result == 0) {
71                 result = o1.getDateTime().compareTo(o2.getDateTime());
72             }
73             return result;
74         }
75     };
76
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,
84             true);
85     private final ReentrantLock sentQueueLock = new ReentrantLock();
86     private final PlugwiseFilteredMessageListenerList filteredListeners = new PlugwiseFilteredMessageListenerList();
87
88     private final ThingUID bridgeUID;
89     private final Supplier<PlugwiseStickConfig> configurationSupplier;
90     private final SerialPortManager serialPortManager;
91     private @Nullable SerialPort serialPort;
92
93     public PlugwiseCommunicationContext(ThingUID bridgeUID, Supplier<PlugwiseStickConfig> configurationSupplier,
94             SerialPortManager serialPortManager) {
95         this.bridgeUID = bridgeUID;
96         this.configurationSupplier = configurationSupplier;
97         this.serialPortManager = serialPortManager;
98     }
99
100     public void clearQueues() {
101         acknowledgedQueue.clear();
102         receivedQueue.clear();
103         sendQueue.clear();
104         sentQueue.clear();
105     }
106
107     public void closeSerialPort() {
108         SerialPort localSerialPort = serialPort;
109         if (localSerialPort != null) {
110             try {
111                 InputStream inputStream = localSerialPort.getInputStream();
112                 if (inputStream != null) {
113                     try {
114                         inputStream.close();
115                     } catch (IOException e) {
116                         logger.debug("Error while closing the input stream: {}", e.getMessage());
117                     }
118                 }
119                 OutputStream outputStream = localSerialPort.getOutputStream();
120                 if (outputStream != null) {
121                     try {
122                         outputStream.close();
123                     } catch (IOException e) {
124                         logger.debug("Error while closing the output stream: {}", e.getMessage());
125                     }
126                 }
127                 localSerialPort.close();
128                 serialPort = null;
129             } catch (IOException e) {
130                 logger.warn("An exception occurred while closing the serial port {} ({})", localSerialPort,
131                         e.getMessage());
132             }
133         }
134     }
135
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());
140             return identifier;
141         }
142
143         // Build exception message when port not found
144         String availablePorts = serialPortManager.getIdentifiers().map(id -> id.getName())
145                 .collect(Collectors.joining(System.lineSeparator()));
146
147         throw new PlugwiseInitializationException(
148                 String.format("Serial port '%s' could not be found. Available ports are:%n%s",
149                         getConfiguration().getSerialPort(), availablePorts));
150     }
151
152     public BlockingQueue<@Nullable AcknowledgementMessage> getAcknowledgedQueue() {
153         return acknowledgedQueue;
154     }
155
156     public ThingUID getBridgeUID() {
157         return bridgeUID;
158     }
159
160     public PlugwiseStickConfig getConfiguration() {
161         return configurationSupplier.get();
162     }
163
164     public PlugwiseFilteredMessageListenerList getFilteredListeners() {
165         return filteredListeners;
166     }
167
168     public BlockingQueue<@Nullable Message> getReceivedQueue() {
169         return receivedQueue;
170     }
171
172     public PriorityBlockingQueue<@Nullable PlugwiseQueuedMessage> getSendQueue() {
173         return sendQueue;
174     }
175
176     public BlockingQueue<@Nullable PlugwiseQueuedMessage> getSentQueue() {
177         return sentQueue;
178     }
179
180     public ReentrantLock getSentQueueLock() {
181         return sentQueueLock;
182     }
183
184     public @Nullable SerialPort getSerialPort() {
185         return serialPort;
186     }
187
188     /**
189      * Initialize this device and open the serial port
190      *
191      * @throws PlugwiseInitializationException if port can not be found or opened
192      */
193     public void initializeSerialPort() throws PlugwiseInitializationException {
194         try {
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);
204         }
205     }
206 }