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.VeluxBindingConstants;
20 import org.openhab.binding.velux.internal.bridge.VeluxBridgeInstance;
21 import org.openhab.binding.velux.internal.bridge.slip.utils.Packet;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
26 * 2nd Level I/O interface towards the <B>Velux</B> bridge.
27 * It provides methods for a pure client/server communication.
29 * The following class access methods exist:
31 * <LI>{@link Connection#io} for a complete pair of request and response messages,</LI>
32 * <LI>{@link Connection#isAlive} to check the presence of a connection,</LI>
33 * <LI>{@link Connection#isMessageAvailable} to check the presence of an incoming message,</LI>
34 * <LI>{@link Connection#lastSuccessfulCommunication} returns the timestamp of the last successful communication,</LI>
35 * <LI>{@link Connection#lastCommunication} returns the timestamp of the last communication,</LI>
36 * <LI>{@link Connection#resetConnection} for resetting the current connection.</LI>
39 * @author Guenther Schreiner - Initial contribution.
42 public class Connection {
43 private final Logger logger = LoggerFactory.getLogger(Connection.class);
46 * ***************************
47 * ***** Private Objects *****
51 * Timestamp of last successful communication in milliseconds.
53 private long lastSuccessfulCommunicationInMSecs = 0;
55 * Timestamp of last communication in milliseconds.
57 private long lastCommunicationInMSecs = 0;
59 * SSL socket for communication.
61 private SSLconnection connectivity = SSLconnection.UNKNOWN;
63 private String host = VeluxBindingConstants.UNKNOWN_IP_ADDRESS;
66 * **************************
67 * ***** Public Methods *****
71 * Base level communication with the <B>SlipVeluxBridg</B>.
73 * @param bridgeInstance describing the Service Access Point location i.e. hostname and TCP port.
74 * @param request as Array of bytes representing the structure of the message to be send.
75 * @return <b>response</b> of type Array of byte containing all received informations.
76 * @throws java.net.ConnectException in case of unrecoverable communication failures.
77 * @throws java.io.IOException in case of continuous communication I/O failures.
79 public synchronized byte[] io(VeluxBridgeInstance bridgeInstance, byte[] request)
80 throws ConnectException, IOException {
81 logger.trace("io() on {}: called.", host);
83 lastCommunicationInMSecs = System.currentTimeMillis();
87 IOException lastIOE = new IOException("Unexpected I/O exception.");
91 if (!connectivity.isReady()) {
94 host = bridgeInstance.veluxBridgeConfiguration().ipAddress;
95 int port = bridgeInstance.veluxBridgeConfiguration().tcpPort;
96 int timeoutMsecs = bridgeInstance.veluxBridgeConfiguration().timeoutMsecs;
98 logger.trace("io() on {}: connecting to port {}", host, port);
99 connectivity = new SSLconnection(host, port);
100 connectivity.setTimeout(timeoutMsecs);
101 } catch (ConnectException ce) {
102 throw new ConnectException(String
103 .format("raised a non-recoverable error during connection setup: %s", ce.getMessage()));
104 } catch (IOException e) {
105 logger.warn("io() on {}: raised an error during connection setup: {}.", host, e.getMessage());
106 lastIOE = new IOException(String.format("error during connection setup: %s.", e.getMessage()));
110 if (request.length > 0) {
112 if (logger.isTraceEnabled()) {
113 logger.trace("io() on {}: sending packet with {} bytes: {}", host, request.length,
114 new Packet(request));
116 logger.debug("io() on {}: sending packet of size {}.", host, request.length);
118 if (connectivity.isReady()) {
119 connectivity.send(request);
121 } catch (IOException e) {
122 logger.info("io() on {}: raised an error during sending: {}.", host, e.getMessage());
126 // Give the bridge some time to breathe
127 if (bridgeInstance.veluxBridgeConfiguration().timeoutMsecs > 0) {
128 logger.trace("io() on {}: wait time {} msecs.", host,
129 bridgeInstance.veluxBridgeConfiguration().timeoutMsecs);
131 Thread.sleep(bridgeInstance.veluxBridgeConfiguration().timeoutMsecs);
132 } catch (InterruptedException ie) {
133 logger.trace("io() on {}: wait interrupted.", host);
137 byte[] packet = new byte[0];
138 logger.trace("io() on {}: receiving bytes.", host);
139 if (connectivity.isReady()) {
140 packet = connectivity.receive();
142 if (logger.isTraceEnabled()) {
143 logger.trace("io() on {}: received packet with {} bytes: {}", host, packet.length,
146 logger.debug("io() on {}: received packet with {} bytes.", host, packet.length);
148 lastSuccessfulCommunicationInMSecs = System.currentTimeMillis();
149 lastCommunicationInMSecs = lastSuccessfulCommunicationInMSecs;
150 logger.trace("io() on {}: finished.", host);
152 } catch (IOException ioe) {
153 logger.info("io() on {}: Exception occurred during I/O: {}.", host, ioe.getMessage());
155 // Error Retries with Exponential Backoff
156 long waitTime = ((long) Math.pow(2, retryCount)
157 * bridgeInstance.veluxBridgeConfiguration().timeoutMsecs);
158 logger.trace("io() on {}: wait time {} msecs.", host, waitTime);
160 Thread.sleep(waitTime);
161 } catch (InterruptedException ie) {
162 logger.trace("io() on {}: wait interrupted.", host);
165 } while (retryCount++ < bridgeInstance.veluxBridgeConfiguration().retries);
166 if (retryCount >= bridgeInstance.veluxBridgeConfiguration().retries) {
167 logger.info("io() on {}: socket I/O failed {} times.", host,
168 bridgeInstance.veluxBridgeConfiguration().retries);
170 logger.trace("io() on {}: shutting down connection.", host);
171 if (connectivity.isReady()) {
172 connectivity.close();
174 logger.trace("io() on {}: finishes with failure by throwing exception.", host);
179 * Returns the status of the current connection.
181 * @return state as boolean.
183 public boolean isAlive() {
184 logger.trace("isAlive() on {}: called.", host);
185 return connectivity.isReady();
189 * Returns the availability of an incoming message.
191 * @return state as boolean.
193 public synchronized boolean isMessageAvailable() {
194 logger.trace("isMessageAvailable() on {}: called.", host);
196 if ((connectivity.isReady()) && (connectivity.available())) {
197 logger.trace("isMessageAvailable() on {}: there is a message waiting.", host);
200 } catch (IOException e) {
201 logger.trace("isMessageAvailable() on {}: lost connection due to {}.", host, e.getMessage());
204 logger.trace("isMessageAvailable() on {}: no message waiting.", host);
209 * Returns the timestamp in milliseconds since Unix epoch
210 * of last successful communication.
212 * @return timestamp in milliseconds.
214 public long lastSuccessfulCommunication() {
215 return lastSuccessfulCommunicationInMSecs;
219 * Returns the timestamp in milliseconds since Unix epoch
220 * of last communication.
222 * @return timestamp in milliseconds.
224 public long lastCommunication() {
225 return lastCommunicationInMSecs;
229 * Resets an open connectivity by closing the socket and resetting the authentication information.
231 public synchronized void resetConnection() {
232 logger.trace("resetConnection() on {}: called.", host);
234 connectivity.close();
235 } catch (IOException e) {
236 logger.info("resetConnection() on {}: raised an error during connection close: {}.", host, e.getMessage());
238 logger.trace("resetConnection() on {}: done.", host);