2 * Copyright (c) 2010-2020 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.velux.internal.bridge.slip.io;
15 import java.io.IOException;
16 import java.net.ConnectException;
18 import org.eclipse.jdt.annotation.NonNullByDefault;
19 import org.openhab.binding.velux.internal.bridge.VeluxBridgeInstance;
20 import org.openhab.binding.velux.internal.bridge.slip.utils.Packet;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
25 * 2nd Level I/O interface towards the <B>Velux</B> bridge.
26 * It provides methods for a pure client/server communication.
28 * The following class access methods exist:
30 * <LI>{@link Connection#io} for a complete pair of request and response messages,</LI>
31 * <LI>{@link Connection#isAlive} to check the presence of a connection,</LI>
32 * <LI>{@link Connection#isMessageAvailable} to check the presence of an incoming message,</LI>
33 * <LI>{@link Connection#lastSuccessfulCommunication} returns the timestamp of the last successful communication,</LI>
34 * <LI>{@link Connection#lastCommunication} returns the timestamp of the last communication,</LI>
35 * <LI>{@link Connection#resetConnection} for resetting the current connection.</LI>
38 * @author Guenther Schreiner - Initial contribution.
41 public class Connection {
42 private final Logger logger = LoggerFactory.getLogger(Connection.class);
45 * ***************************
46 * ***** Private Objects *****
50 * Timestamp of last successful communication in milliseconds.
52 private long lastSuccessfulCommunicationInMSecs = 0;
54 * Timestamp of last communication in milliseconds.
56 private long lastCommunicationInMSecs = 0;
58 * SSL socket for communication.
60 private SSLconnection connectivity = SSLconnection.UNKNOWN;
63 * **************************
64 * ***** Public Methods *****
68 * Base level communication with the <B>SlipVeluxBridg</B>.
70 * @param bridgeInstance describing the Service Access Point location i.e. hostname and TCP port.
71 * @param request as Array of bytes representing the structure of the message to be send.
72 * @return <b>response</b> of type Array of byte containing all received informations.
73 * @throws java.net.ConnectException in case of unrecoverable communication failures.
74 * @throws java.io.IOException in case of continuous communication I/O failures.
76 public synchronized byte[] io(VeluxBridgeInstance bridgeInstance, byte[] request)
77 throws ConnectException, IOException {
78 logger.trace("io() called.");
80 lastCommunicationInMSecs = System.currentTimeMillis();
84 IOException lastIOE = new IOException("Unexpected I/O exception.");
88 if (!connectivity.isReady()) {
91 String host = bridgeInstance.veluxBridgeConfiguration().ipAddress;
92 int port = bridgeInstance.veluxBridgeConfiguration().tcpPort;
93 int timeoutMsecs = bridgeInstance.veluxBridgeConfiguration().timeoutMsecs;
95 logger.trace("io(): connecting to {}:{}.", host, port);
96 connectivity = new SSLconnection(host, port);
97 connectivity.setTimeout(timeoutMsecs);
98 } catch (ConnectException ce) {
99 throw new ConnectException(String
100 .format("raised a non-recoverable error during connection setup: %s", ce.getMessage()));
101 } catch (IOException e) {
102 logger.warn("io(): raised an error during connection setup: {}.", e.getMessage());
103 lastIOE = new IOException(String.format("error during connection setup: %s.", e.getMessage()));
107 if (request.length > 0) {
109 if (logger.isTraceEnabled()) {
110 logger.trace("io(): sending packet with {} bytes: {}", request.length, new Packet(request));
112 logger.debug("io(): sending packet of size {}.", request.length);
114 if (connectivity.isReady()) {
115 connectivity.send(request);
117 } catch (IOException e) {
118 logger.info("io(): raised an error during sending: {}.", e.getMessage());
122 // Give the bridge some time to breathe
123 if (bridgeInstance.veluxBridgeConfiguration().timeoutMsecs > 0) {
124 logger.trace("io(): wait time {} msecs.",
125 bridgeInstance.veluxBridgeConfiguration().timeoutMsecs);
127 Thread.sleep(bridgeInstance.veluxBridgeConfiguration().timeoutMsecs);
128 } catch (InterruptedException ie) {
129 logger.trace("io() wait interrupted.");
133 byte[] packet = new byte[0];
134 logger.trace("io(): receiving bytes.");
135 if (connectivity.isReady()) {
136 packet = connectivity.receive();
138 if (logger.isTraceEnabled()) {
139 logger.trace("io(): received packet with {} bytes: {}", packet.length, new Packet(packet));
141 logger.debug("io(): received packet with {} bytes.", packet.length);
143 lastSuccessfulCommunicationInMSecs = System.currentTimeMillis();
144 lastCommunicationInMSecs = lastSuccessfulCommunicationInMSecs;
145 logger.trace("io() finished.");
147 } catch (IOException ioe) {
148 logger.info("io(): Exception occurred during I/O: {}.", ioe.getMessage());
150 // Error Retries with Exponential Backoff
151 long waitTime = ((long) Math.pow(2, retryCount)
152 * bridgeInstance.veluxBridgeConfiguration().timeoutMsecs);
153 logger.trace("io(): wait time {} msecs.", waitTime);
155 Thread.sleep(waitTime);
156 } catch (InterruptedException ie) {
157 logger.trace("io(): wait interrupted.");
160 } while (retryCount++ < bridgeInstance.veluxBridgeConfiguration().retries);
161 if (retryCount >= bridgeInstance.veluxBridgeConfiguration().retries) {
162 logger.info("io(): socket I/O failed {} times.", bridgeInstance.veluxBridgeConfiguration().retries);
164 logger.trace("io(): shutting down connection.");
165 if (connectivity.isReady()) {
166 connectivity.close();
168 logger.trace("io() finishes with failure by throwing exception.");
173 * Returns the status of the current connection.
175 * @return state as boolean.
177 public boolean isAlive() {
178 logger.trace("isAlive(): called.");
179 return connectivity.isReady();
183 * Returns the availability of an incoming message.
185 * @return state as boolean.
187 public synchronized boolean isMessageAvailable() {
188 logger.trace("isMessageAvailable(): called.");
190 if ((connectivity.isReady()) && (connectivity.available())) {
191 logger.trace("isMessageAvailable(): there is a message waiting.");
194 } catch (IOException e) {
195 logger.trace("isMessageAvailable(): lost connection due to {}.", e.getMessage());
198 logger.trace("isMessageAvailable(): no message waiting.");
203 * Returns the timestamp in milliseconds since Unix epoch
204 * of last successful communication.
206 * @return timestamp in milliseconds.
208 public long lastSuccessfulCommunication() {
209 return lastSuccessfulCommunicationInMSecs;
213 * Returns the timestamp in milliseconds since Unix epoch
214 * of last communication.
216 * @return timestamp in milliseconds.
218 public long lastCommunication() {
219 return lastCommunicationInMSecs;
223 * Resets an open connectivity by closing the socket and resetting the authentication information.
225 public synchronized void resetConnection() {
226 logger.trace("resetConnection() called.");
227 if (connectivity.isReady()) {
228 logger.trace("resetConnection(): shutting down connection.");
230 if (connectivity.isReady()) {
231 connectivity.close();
233 } catch (IOException e) {
234 logger.info("resetConnection(): raised an error during connection close: {}.", e.getMessage());
236 logger.trace("resetConnection(): clearing authentication token.");
238 logger.trace("resetConnection() done.");