/**
* Copyright (c) 2010-2016, openHAB.org and others.
*
* 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.upb.internal;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import org.apache.commons.lang.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class monitors the input stream of a UPB modem. This is done
* asynchronously. When messages are received, they are broadcast to all
* subscribed {@link Listener listeners}.
*
* @author cvanorman
* @since 1.9.0
*/
public class UPBReader implements Runnable {
/**
* Listener class for handling received messages. A listener can be added by
* calling {@link UPBReader#addListener(Listener)}.
*
* @author cvanorman
*
*/
public interface Listener {
/**
* Called whenever a message has been received from the UPB modem.
*
* @param message
* the message that was received.
*/
void messageReceived(UPBMessage message);
}
private static final Logger logger = LoggerFactory.getLogger(UPBReader.class);
private Collection<Listener> listeners = new LinkedHashSet<>();
private byte[] buffer = new byte[512];
private int bufferLength = 0;
private InputStream inputStream;
private Thread thread;
/**
* Instantiates a new {@link UPBReader}.
*
* @param inputStream
* the inputStream from the UPB modem.
*/
public UPBReader(InputStream inputStream) {
this.inputStream = inputStream;
thread = new Thread(this);
thread.start();
}
/**
* Subscribes the listener to any future message events.
*
* @param listener
* the listener to add.
*/
public synchronized void addListener(Listener listener) {
listeners.add(listener);
}
/**
* Removes the listener from further messages.
*
* @param listener
* the listener to remove.
*/
public synchronized void removeListener(Listener listener) {
listeners.remove(listener);
}
/**
* Adds data to the buffer.
*
* @param data
* the data to add.
* @param length
* the length of data to add.
*/
private void addData(byte[] data, int length) {
if (bufferLength + length > buffer.length) {
// buffer overflow discard entire buffer
bufferLength = 0;
}
System.arraycopy(data, 0, buffer, bufferLength, length);
bufferLength += length;
interpretBuffer();
}
/**
* Shuts the reader down.
*/
public void shutdown() {
if (thread != null) {
thread.interrupt();
}
try {
inputStream.close();
} catch (IOException e) {
}
}
private int findMessageLength(byte[] buffer, int bufferLength) {
int messageLength = ArrayUtils.INDEX_NOT_FOUND;
for (int i = 0; i < bufferLength; i++) {
if (buffer[i] == 13) {
messageLength = i;
break;
}
}
return messageLength;
}
/**
* Attempts to interpret any messages that may be contained in the buffer.
*/
private void interpretBuffer() {
int messageLength = findMessageLength(buffer, bufferLength);
while (messageLength != ArrayUtils.INDEX_NOT_FOUND) {
String message = new String(Arrays.copyOfRange(buffer, 0, messageLength));
logger.debug("UPB Message: {}", message);
int remainingBuffer = bufferLength - messageLength - 1;
if (remainingBuffer > 0) {
System.arraycopy(buffer, messageLength + 1, buffer, 0, remainingBuffer);
}
bufferLength = remainingBuffer;
notifyListeners(UPBMessage.fromString(message));
messageLength = findMessageLength(buffer, bufferLength);
}
}
private synchronized void notifyListeners(UPBMessage message) {
for (Listener l : new ArrayList<>(listeners)) {
l.messageReceived(message);
}
}
/**
* {@inheritDoc}
*/
@Override
public void run() {
byte[] buffer = new byte[256];
try {
for (int len = -1; (len = inputStream.read(buffer)) >= 0;) {
if (len > 0) {
logger.debug("Received: {}", ArrayUtils.subarray(buffer, 0, len));
}
addData(buffer, len);
if (Thread.interrupted()) {
break;
}
}
} catch (Exception e) {
logger.debug("Failed to read input stream.", e);
}
logger.debug("UPBReader stopped.");
}
}