/**
* 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.gc100ir.lib;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.openhab.binding.gc100ir.internal.response.GC100IRCommand;
import org.openhab.binding.gc100ir.internal.response.GC100IRResponseCommandCodeFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Handles the discovery and discarding of the GC-100 devices. It implements
* IGC100IRControlPoint. This class follows the Singleton pattern, hence the
* contructor is private. The singleton instance can be instantiated by using the
* getInstance() method
*
* @author Parikshit Thakur & Team
* @since 1.9.0
*/
public class GC100IRControlPoint implements IGC100IRControlPoint {
/**
* Port number to connect to device.
*/
public static final int CONNECT_PORT = 4998;
private static final String DEVICE_TYPE_IR = "IR";
public static final String GC_100_QUERY_MESSAGE_GET_DEVICES = "getdevices\r";
private static final String END_LIST_DEVICES = "endlistdevices";
private List<GC100IRConnection> socketList;
private Map<GC100IRDevice, Long> deviceLastDisTimeMap;
private Map<GC100IRDevice, Map<String, String>> deviceGCPropsMap;
private static GC100IRControlPoint gc100ControlPoint;
private static Logger logger = LoggerFactory.getLogger(GC100IRControlPoint.class);
/**
* Returns an Instance of GC100IRControlPoint. Singleton Pattern.
*
* @return an Object of GC100IRControlPoint
*/
public static GC100IRControlPoint getInstance() {
if (gc100ControlPoint == null) {
gc100ControlPoint = new GC100IRControlPoint();
}
return gc100ControlPoint;
}
private GC100IRControlPoint() {
deviceGCPropsMap = new LinkedHashMap<GC100IRDevice, Map<String, String>>();
deviceLastDisTimeMap = new HashMap<GC100IRDevice, Long>();
socketList = new ArrayList<GC100IRConnection>();
}
/**
* {@inheritDoc}
*/
@Override
public boolean connectTarget(String gc100host, int module, int connector) {
if (gc100host == null || module <= 0 || connector <= 0) {
logger.warn("hostname, module or connector is invalid");
return false;
}
Set<GC100IRDevice> deviceSet = deviceLastDisTimeMap.keySet();
if ((deviceSet == null) || (deviceSet.size() == 0)) {
logger.warn("deviceSet is null or its size is 0.");
return false;
}
GC100IRDevice gcDevice = null;
for (GC100IRDevice gcDev : deviceSet) {
String ipOfDevice = gcDev.getIPAddressString();
if (ipOfDevice.equals(gc100host)) {
if ((module != gcDev.getModule()) || (connector != gcDev.getConnector())) {
continue;
}
gcDevice = gcDev;
break;
}
}
if (gcDevice == null) {
logger.warn("gcDevice is null.");
return false;
}
GC100IRConnection tcpIpSoc = gcDevice.getTCPIPSocket();
if (tcpIpSoc != null) {
if (!tcpIpSoc.isConnected()) {
try {
tcpIpSoc.connect();
return true;
} catch (IOException e) {
logger.error("IOException : ", e);
}
}
}
InetAddress ipAddress;
try {
ipAddress = gcDevice.getInetAddress();
} catch (UnknownHostException e) {
logger.warn("UnknownHostException : ", e);
return false;
}
String deviceType = gcDevice.getConnectorType();
Socket socket = null;
try {
synchronized (socketList) {
for (GC100IRConnection tcpIpSocket : socketList) {
if (tcpIpSocket == null) {
continue;
}
InetAddress tcpInetAddress = tcpIpSocket.getInetAddress();
int tcpPort = tcpIpSocket.getPort();
if (tcpInetAddress.equals(ipAddress) && (tcpPort == CONNECT_PORT)) {
String tcpIPString = tcpIpSocket.getInetAddress().toString();
if (tcpIPString.startsWith("/")) {
tcpIPString = tcpIPString.substring(1);
}
if (gc100host != null && gc100host.equals(tcpIPString) && (module == tcpIpSocket.getModule())
&& (connector == tcpIpSocket.getConnector())) {
if (!tcpIpSocket.isConnected()) {
tcpIpSocket.connect();
}
return true;
}
if (socket == null) {
if (!tcpIpSocket.isConnected()) {
tcpIpSocket.connect();
}
socket = tcpIpSocket.getSocket();
}
}
}
}
String gchost = null;
if (ipAddress != null && ipAddress.toString().startsWith("/")) {
gchost = ipAddress.toString().substring(1);
} else if (ipAddress != null) {
gchost = ipAddress.toString();
}
if (socket != null) {
GC100IRConnection tcpIpSocket = new GC100IRConnection(ipAddress, gchost, CONNECT_PORT, module,
connector, deviceType, socket);
synchronized (FLOW_CONTROL_VALUE_FLOW_HARDWARE) {
socketList.add(tcpIpSocket);
}
gcDevice.setTCPIPSocket(tcpIpSocket);
return true;
} else {
GC100IRConnection tcpIpSocket = new GC100IRConnection(ipAddress, gchost, CONNECT_PORT, module,
connector, deviceType);
if (tcpIpSocket.connect()) {
synchronized (FLOW_CONTROL_VALUE_FLOW_HARDWARE) {
socketList.add(tcpIpSocket);
}
gcDevice.setTCPIPSocket(tcpIpSocket);
return true;
}
}
} catch (UnknownHostException e) {
logger.error("UnknownHostException :", e);
} catch (IOException e) {
logger.error("IOException :", e);
}
return false;
}
/**
* Used to check TCP/IP socket is connected or not.
*
* @param ipAddress
* @return true if socket is connected with ipAddress
*/
public boolean isGC100ItemConnected(String ipAddress) {
synchronized (socketList) {
for (GC100IRConnection gc100IRConnection : socketList) {
if (gc100IRConnection == null) {
continue;
}
String tcpIpString = gc100IRConnection.getInetAddress().toString();
if (tcpIpString.startsWith("/")) {
tcpIpString = tcpIpString.substring(1);
}
if (ipAddress.equals(tcpIpString)) {
return gc100IRConnection.isConnected();
}
}
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public void disconnectTarget(String ipAddress, int module, int connector) {
Set<GC100IRDevice> deviceSet = null;
synchronized (deviceLastDisTimeMap) {
deviceSet = deviceLastDisTimeMap.keySet();
}
if ((deviceSet == null) || (deviceSet.size() == 0)) {
return;
}
for (GC100IRDevice gcDevice : deviceSet) {
String ipAddressOfDevice = gcDevice.getIPAddressString();
if (ipAddressOfDevice.equals(ipAddress)) {
deviceLastDisTimeMap.remove(gcDevice);
}
}
}
/**
* Returns GC100IRDevice instance.
*
* @param ipAddress
* @param module
* @param connector
* @return GC100Device object
*/
private GC100IRDevice getGCDevice(String ipAddress, int module, int connector) {
if ((ipAddress == null) || (module == 0) || (connector == 0)) {
return null;
}
synchronized (deviceLastDisTimeMap) {
for (GC100IRDevice device : deviceLastDisTimeMap.keySet()) {
if (device == null) {
continue;
}
if (ipAddress.equals(device.getIPAddressString()) && module == device.getModule()
&& connector == device.getConnector()) {
return device;
}
}
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public synchronized GC100IRCommand doAction(String ipAddressString, int module, int connector, String code) {
if (code == null) {
return null;
}
GC100IRDevice device = getGCDevice(ipAddressString, module, connector);
if (device == null) {
logger.warn("Unable to get GC 100 Device for IP Address '{}', module '{}' and connector '{}'.",
ipAddressString, module, connector);
return null;
}
GC100IRConnection tcpIPSocket = device.getTCPIPSocket();
if (tcpIPSocket == null) {
if (connectTarget(ipAddressString, module, connector)) {
device = getGCDevice(ipAddressString, module, connector);
if (device == null) {
logger.warn("Unable to get GC 100 Device for IP Address '{}', module '{}' and connector '{}'.",
ipAddressString, module, connector);
return null;
}
tcpIPSocket = device.getTCPIPSocket();
if (tcpIPSocket == null) {
logger.warn("Unable to get TCP IP Socket for IP Address '{}', module '{}' and connector '{}'.",
ipAddressString, module, connector);
return null;
}
}
}
if (tcpIPSocket == null) {
logger.warn("Unable to get TCP IP Socket for IP Address '{}', module '{}' and connector '{}'.",
ipAddressString, module, connector);
return null;
}
String deviceType = tcpIPSocket.getDeviceType();
if (deviceType == null) {
logger.warn("Unable to get Device Type for IP Address '{}', module '{}' and connector '{}'.",
ipAddressString, module, connector);
return null;
}
try {
if (deviceType.equals(DEVICE_TYPE_IR)) {
Socket socket = tcpIPSocket.getSocket();
if (socket == null) {
logger.warn("Unable to get Socket for IP Address '{}', module '{}' and connector '{}'.",
ipAddressString, module, connector);
return null;
}
if (!tcpIPSocket.isConnected()) {
tcpIPSocket.connect();
}
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (in.ready()) {
in.read();
}
out.write(code.getBytes());
StringBuilder response = new StringBuilder();
int count = 0;
while (!in.ready()) {
count++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
logger.warn("InterruptedException", e);
}
if (count > 20) {
return null;
}
} // Wait till data available to input stream
while (true) {
response.append((char) in.read());
if (!in.ready()) {
break;
}
}
return responseOfDevice(response.toString());
}
} catch (IOException e) {
logger.warn("IOException", e);
}
return null;
}
/**
* Returns response of device for the specified 'response'.
*
* @param response
* a String value of response
* @return a GC100IRCommand command response
*/
private GC100IRCommand responseOfDevice(String response) {
return GC100IRResponseCommandCodeFactory.INSTANCE.produce(response);
}
/**
* {@inheritDoc}
*/
@Override
public Set<GC100IRDevice> getAvailableDevices() {
if (deviceGCPropsMap == null) {
return null;
}
synchronized (deviceGCPropsMap) {
return deviceGCPropsMap.keySet();
}
}
/**
* Gets the list of devices.
*
* @param getDevices
* a String value of getDevices
* @param sourceAddress
* a String value of sourceAddress
* @return a String value for no. of devices that can be attached to the
* GC-100
* @throws UnknownHostException
* @throws IOException
*/
public String queryGC100(String getDevices, String sourceAddress) throws IOException {
List<String> deviceList = new ArrayList<String>();
Socket socket = new Socket(sourceAddress, GC100IRControlPoint.CONNECT_PORT);
SocketAddress socketAddress = socket.getRemoteSocketAddress();
if (socket.isConnected()) {
logger.info("Connected Successfully to {}", socketAddress);
}
DataOutputStream outToServer = new DataOutputStream(socket.getOutputStream());
BufferedReader inFromServer = new BufferedReader(new InputStreamReader(socket.getInputStream()));
byte[] commandInBytes = getDevices.getBytes();
outToServer.write(commandInBytes);
int c;
StringBuilder response = new StringBuilder();
while ((c = inFromServer.read()) != '\0') {
response.append((char) c);
if (c == 13) {
deviceList.add(response.toString());
response = new StringBuilder();
}
if (response.toString().equals(END_LIST_DEVICES)) {
break;
}
}
StringBuilder temp = new StringBuilder();
for (String deviceStr : deviceList) {
temp.append(deviceStr);
}
try {
socket.close();
} catch (SocketException e) {
logger.warn("Socket Exception occurred", e);
}
return temp.toString();
}
/**
* Parses the list of devices available to the GC-100 device.
*
* @param ipAddress
* a String value of ipAddress
* @param getDevicesResponse
* a String value of get Devices Response
*/
public void parseDevices(String ipAddress, String getDevicesResponse) {
if ((getDevicesResponse == null)) {
return;
}
StringTokenizer st = new StringTokenizer(getDevicesResponse, "\r ,");
String configURL = ipAddress;
if (configURL == null) {
return;
}
Date date = new Date();
Map<String, String> gcProps = new HashMap<String, String>();
while (st.hasMoreTokens()) {
if (st.nextToken().equals("device")) {
int m = Integer.parseInt(st.nextToken());
gcProps.put("module", String.valueOf(m));
int c = Integer.parseInt(st.nextToken());
gcProps.put("connector", String.valueOf(c));
String deviceType = st.nextToken();
gcProps.put("deviceType", deviceType);
for (int i = 1; i <= c; i++) {
GC100IRDevice gcDevice = new GC100IRDevice(gc100ControlPoint, configURL, m, i, deviceType);
try {
gcProps.put("inetAddress", gcDevice.getInetAddress().toString());
} catch (UnknownHostException e) {
logger.error("UnknownHostException :", e);
}
deviceGCPropsMap.put(gcDevice, gcProps);
synchronized (deviceLastDisTimeMap) {
deviceLastDisTimeMap.put(gcDevice, date.getTime());
}
}
}
}
}
}