/**
* 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.primare.internal.protocol;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.openhab.binding.primare.internal.PrimareEventListener;
import org.openhab.binding.primare.internal.PrimareStatusUpdateEvent;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Abstract base class for Primare communication. It contains the communication
* methods and Primare model-specific message factories for converting
* OpenHAB commands to Primare messages.
*
*
* @author Veli-Pekka Juslin
* @since 1.7.0
*/
public abstract class PrimareConnector {
private static final Logger logger = LoggerFactory.getLogger(PrimareTCPConnector.class);
protected String deviceId;
protected byte[] buffer = new byte[1024];
protected int total = 0;
protected int double_dle_start = -1;
protected List<PrimareEventListener> _listeners = new ArrayList<PrimareEventListener>();
// Timestamps for supervising connection, might become useful some day
protected Date bytesSentAt = null;
protected Date messageSentAt = null;
protected Date bytesReceivedAt = null;
protected Date messageReceivedAt = null;
// Change to true if we detect a broken connection which we are not
// able to fix right away
protected boolean connectionBroken = false;
protected PrimareMessageFactory messageFactory = null;
protected PrimareResponseFactory responseFactory = null;
/**
* Add event listener, which will be invoked when status update is received from receiver.
**/
public synchronized void addEventListener(PrimareEventListener listener) {
_listeners.add(listener);
}
/**
* Remove event listener.
**/
public synchronized void removeEventListener(PrimareEventListener listener) {
_listeners.remove(listener);
}
/**
* Connect to device
*
* @throws Exception
*/
public abstract void connect() throws Exception;
/**
* Connect to device and set EventListener;
*
* @throws Exception
*/
public void connectAndAddEventListener(PrimareEventListener listener) throws Exception {
connect();
addEventListener(listener);
}
/**
* Disconnect from device
*
*/
public abstract void disconnect();
/**
* Connect to device and set EventListener;
*
* @throws Exception
*/
public void disconnectAndRemoveEventListener(PrimareEventListener listener) throws Exception {
disconnect();
removeEventListener(listener);
}
/**
* Are we connected?
*
* @throws Exception
*/
public abstract boolean isConnected();
/**
* Send bytes to device (no escaping)
*
* @throws Exception
*/
public abstract void sendBytes(byte[] msg) throws Exception;
/*
* Method sendMessage implements standard Primare escaping:
* - If command, variable or value is equal to 16 (DLE), it is transmitted twice
* (double DLE)
* Override this in concrete class if necessary
*/
private void sendMessagePart(byte[] msg) throws Exception {
sendBytes(PrimareUtils.escapeMessage(msg));
messageSentAt = new Date();
}
private void sendMessage(byte[][] msgs) throws Exception {
for (byte[] msg : msgs) {
sendMessagePart(msg);
}
}
public void sendMessage(PrimareMessage deviceMsg) throws Exception {
sendMessage(deviceMsg.getMessageParts());
messageSentAt = new Date();
}
public void sendMessage(PrimareMessage[] deviceMsgs) throws Exception {
for (PrimareMessage deviceMsg : deviceMsgs) {
sendMessage(deviceMsg);
}
}
public void sendCommand(Command command, String deviceCmdString) throws Exception {
sendMessage(messageFactory.getMessage(command, deviceCmdString));
}
public PrimareMessageFactory getMessageFactory() {
return messageFactory;
}
public PrimareResponseFactory getResponseFactory() {
return responseFactory;
}
public void sendPingMessages() throws Exception {
PrimareMessage[] pms = messageFactory.getPingMessages();
if (pms != null) {
sendMessage(pms);
}
}
public void sendInitMessages() throws Exception {
PrimareMessage[] ims = messageFactory.getInitMessages();
if (ims != null) {
sendMessage(ims);
}
}
protected int parseData(int i) {
logger.trace("Response from {} parse index:{} containing:{}", PrimareConnector.this.toString(), i, buffer[i]);
if (buffer[i] == (byte) 0x10) { // DLE
if (double_dle_start == i - 1) {
// seeing double DLE, deal with it later
double_dle_start = -1;
} else {
double_dle_start = i;
}
} else if (buffer[i] != (byte) 0x03) { // non-ETX
double_dle_start = -1;
}
if (buffer[i] == (byte) 0x03 && double_dle_start == i - 1) {
logger.trace("Response from {} DLE ETX seen", PrimareConnector.this.toString());
// End-flag received, do some work with the data
// Increment i since we need to include the end-byte
i++;
// Copy the message
byte[] data = Arrays.copyOf(buffer, i);
// Copy the buffer onto itself to remove the message from buffer
System.arraycopy(buffer, i, buffer, 0, total - i);
total = total - i;
logger.trace("Response from {} received {} bytes: (hex) [{}]", PrimareConnector.this.toString(),
data.length, PrimareUtils.byteArrayToHex(data));
if (data[0] == (byte) 0x02) {
logger.trace("Response from {} start consuming command response (hex) {}",
PrimareConnector.this.toString(), PrimareUtils.byteArrayToHex(data));
messageReceivedAt = new Date();
// send (unescaped) message to event listeners
try {
Iterator<PrimareEventListener> iterator = _listeners.iterator();
while (iterator.hasNext()) {
iterator.next().statusUpdateReceived(new PrimareStatusUpdateEvent(this), deviceId,
PrimareUtils.unescapeMessage(data));
}
} catch (Exception e) {
logger.error("Response from {} event listener invoking error - {}",
PrimareConnector.this.toString(), e);
}
logger.trace("Response from {} consumed command response (hex) {}", PrimareConnector.this.toString(),
PrimareUtils.byteArrayToHex(data));
} else {
// If a status message exists, implement handling here
}
}
return i;
}
}