/** * Copyright (c) 2010-2016 by the respective copyright holders. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.openhab.binding.insteonhub.internal.hardware.api.serial; import java.io.IOException; import java.net.Socket; import org.openhab.binding.insteonhub.internal.hardware.InsteonHubAdjustmentType; import org.openhab.binding.insteonhub.internal.hardware.InsteonHubProxy; import org.openhab.binding.insteonhub.internal.hardware.InsteonHubProxyListener; import org.openhab.binding.insteonhub.internal.util.InsteonHubBindingLogUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * {@link InsteonHubProxy} implementation that uses the Serial API to * communicate with the INSTEON Hub. This proxy takes care of the connection and * translating method calls to queue-able commands. The transport will take care * of actually sending and receiving the messages. * * @author Eric Thill * @since 1.4.0 */ public class InsteonHubSerialProxy implements InsteonHubProxy { private static final Logger logger = LoggerFactory.getLogger(InsteonHubSerialProxy.class); public static final int DEFAULT_PORT = 9761; private static final long RETRY_INTERVAL_SECONDS = 30; private static final long MILLIS_PER_SECOND = 1000; private final InsteonHubSerialMessageBuilder msgBuilder = InsteonHubSerialMessageBuilder.getInstance(); private final InsteonHubMessagePool commandPool = new InsteonHubMessagePool(32, InsteonHubSerialMessageBuilder.STD_MSG_SIZE); private final InsteonHubSerialTransport transport; private final String host; private final int port; private volatile Socket socket; private volatile Connecter connecter; public InsteonHubSerialProxy(String host) { this(host, DEFAULT_PORT); } public InsteonHubSerialProxy(String host, int port) { this.host = host; this.port = port; transport = new InsteonHubSerialTransport(this); } @Override public String getConnectionString() { return host + ":" + port; } @Override public synchronized void start() { reconnect(); } protected synchronized void reconnect() { // if not currently connecting => reconnect if (connecter == null) { // stop and cleanup existing connection stop(); // asynchronously reconnect connecter = new Connecter(); new Thread(connecter, getConnectionString() + " connecter").start(); } } @Override public synchronized void stop() { // tell all threads to gracefully exit connecter = null; transport.stop(); // try to close the socket try { if (socket != null) { socket.close(); } } catch (IOException e) { // ignore } } @Override public void setDevicePower(String device, boolean power) { byte[] msgBuffer = commandPool.checkout(); msgBuilder.buildFastPowerMessage(msgBuffer, device, power); enqueueCommand(msgBuffer); } @Override public void setDeviceLevel(String device, int level) { byte[] msgBuffer = commandPool.checkout(); msgBuilder.buildSetLevelMessage(msgBuffer, device, level); enqueueCommand(msgBuffer); } @Override public void startDeviceAdjustment(String device, InsteonHubAdjustmentType adjustmentType) { byte[] msgBuffer = commandPool.checkout(); msgBuilder.buildStartDimBrtMessage(msgBuffer, device, adjustmentType); enqueueCommand(msgBuffer); } @Override public void stopDeviceAdjustment(String device) { byte[] msgBuffer = commandPool.checkout(); msgBuilder.buildStopDimBrtMessage(msgBuffer, device); enqueueCommand(msgBuffer); } @Override public void requestDeviceLevel(String device) { byte[] msgBuffer = commandPool.checkout(); msgBuilder.buildRequestLevelMessage(msgBuffer, device); enqueueCommand(msgBuffer); } private void enqueueCommand(byte[] msgBuffer) { if (!transport.isStarted()) { logger.info("Not sending message - Not connected to Hub"); return; } transport.enqueueCommand(msgBuffer); } @Override public void addListener(InsteonHubProxyListener listener) { transport.addListener(listener); } @Override public void removeListener(InsteonHubProxyListener listener) { transport.removeListener(listener); } private class Connecter implements Runnable { @Override public void run() { while (connecter == this) { try { // try connecting socket = new Socket(host, port); // no IOException => success => start send/rec threads transport.start(socket.getInputStream(), socket.getOutputStream()); connecter = null; logger.info("Connected to Insteon Hub: " + host + ":" + port); return; } catch (IOException e) { // connection failure => log and retry in a bit InsteonHubBindingLogUtil.logCommunicationFailure(logger, InsteonHubSerialProxy.this, e); logger.info("Will retry connection in " + RETRY_INTERVAL_SECONDS + " seconds..."); try { Thread.sleep(RETRY_INTERVAL_SECONDS * MILLIS_PER_SECOND); } catch (InterruptedException e1) { // ignore } } } } } }