Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[insteon] Improve hub message processing (openhab#7347)
Browse files Browse the repository at this point in the history
* improved message processing
* added undocumented 0x5c message code definition

Signed-off-by: jsetton <jeremy.setton@gmail.com>
jsetton authored and LoungeFlyZ committed Jun 8, 2020
1 parent e5402a5 commit f362efe
Showing 4 changed files with 83 additions and 38 deletions.
Original file line number Diff line number Diff line change
@@ -300,21 +300,23 @@ public void run() {
}

private void processMessages() {
try {
// must call processData() until we get a null pointer back
for (Msg m = msgFactory.processData(); m != null; m = msgFactory.processData()) {
toAllListeners(m);
notifyWriter(m);
}
} catch (IOException e) {
// got bad data from modem,
// unblock those waiting for ack
logger.warn("bad data received: {}", e.getMessage());
synchronized (getRequestReplyLock()) {
if (reply == ReplyType.WAITING_FOR_ACK) {
logger.warn("got bad data back, must assume message was acked.");
reply = ReplyType.GOT_ACK;
getRequestReplyLock().notify();
// must call processData() until msgFactory done fully processing buffer
while (!msgFactory.isDone()) {
try {
Msg msg = msgFactory.processData();
if (msg != null) {
toAllListeners(msg);
notifyWriter(msg);
}
} catch (IOException e) {
// got bad data from modem,
// unblock those waiting for ack
synchronized (getRequestReplyLock()) {
if (reply == ReplyType.WAITING_FOR_ACK) {
logger.debug("got bad data back, must assume message was acked.");
reply = ReplyType.GOT_ACK;
getRequestReplyLock().notify();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@
import java.nio.charset.StandardCharsets;
import java.util.Base64;

import org.apache.commons.lang.StringUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.insteon.internal.driver.IOStream;
@@ -159,6 +160,7 @@ private synchronized String bufferStatus() throws IOException {
* @throws IOException
*/
private synchronized void clearBuffer() throws IOException {
logger.trace("clearing buffer");
getURL("/1?XB=M=1");
bufferIdx = 0;
}
@@ -171,14 +173,15 @@ private synchronized void clearBuffer() throws IOException {
*/
public synchronized void write(ByteBuffer msg) throws IOException {
poll(); // fetch the status buffer before we send out commands
clearBuffer(); // clear the status buffer explicitly.

StringBuilder b = new StringBuilder();
while (msg.remaining() > 0) {
b.append(String.format("%02x", msg.get()));
}
String hexMSG = b.toString();
logger.trace("writing a message");
getURL("/3?" + hexMSG + "=I=3");
bufferIdx = 0;
}

/**
@@ -210,10 +213,22 @@ public synchronized void poll() throws IOException {
return; // XXX why return here????
}

if (StringUtils.repeat("0", data.length()).equals(data)) {
logger.trace("skip cleared buffer");
bufferIdx = 0;
return;
}

StringBuilder msg = new StringBuilder();
if (nIdx < bufferIdx) {
msg.append(data.substring(bufferIdx, data.length()));
msg.append(data.substring(0, nIdx));
String msgStart = data.substring(bufferIdx, data.length());
String msgEnd = data.substring(0, nIdx);
if (StringUtils.repeat("0", msgStart.length()).equals(msgStart)) {
logger.trace("discard cleared buffer wrap around msg start");
msgStart = "";
}

msg.append(msgStart + msgEnd);
logger.trace("wrap around: copying new data on: {}", msg.toString());
} else {
msg.append(data.substring(bufferIdx, nIdx));
@@ -278,8 +293,6 @@ private String getData(InputStream is) throws IOException {
}

String s = baos.toString();
logger.trace("read:\n{}", s);

return s;
} finally {
bis.close();
Original file line number Diff line number Diff line change
@@ -40,13 +40,23 @@ public class MsgFactory {
private static final int MAX_MSG_LEN = 4096;
private byte[] buf = new byte[MAX_MSG_LEN];
private int end = 0; // offset of end of buffer
private boolean done = true; // done fully processing buffer flag

/**
* Constructor
*/
public MsgFactory() {
}

/**
* Indicates if no more complete message available in the buffer to be processed
*
* @return buffer data fully processed flag
*/
public boolean isDone() {
return done;
}

/**
* Adds incoming data to the data buffer. First call addData(), then call processData()
*
@@ -56,9 +66,13 @@ public MsgFactory() {
public void addData(byte[] data, int len) {
int l = len;
if (l + end > MAX_MSG_LEN) {
logger.warn("warn: truncating excessively long message!");
logger.warn("truncating excessively long message!");
l = MAX_MSG_LEN - end;
}
// indicate new data can be processed if length > 0
if (l > 0) {
done = false;
}
// append the new data to the one we already have
System.arraycopy(data, 0, buf, end, l);
end += l;
@@ -75,13 +89,14 @@ public void addData(byte[] data, int len) {
* @throws IOException if data was received with unknown command codes
*/
public @Nullable Msg processData() throws IOException {
Msg msg = null;
// handle the case where we get a pure nack
if (end > 0 && buf[0] == 0x15) {
logger.trace("got pure nack!");
removeFromBuffer(1);
try {
Msg m = Msg.makeMessage("PureNACK");
return m;
msg = Msg.makeMessage("PureNACK");
return msg;
} catch (InvalidMessageTypeException e) {
return null;
}
@@ -93,44 +108,48 @@ public void addData(byte[] data, int len) {
// Now see if we have enough data for a complete message.
// If not, we return null, and expect this method to be called again
// when more data has come in.
int msgLen = -1;
boolean isExtended = false;
if (end > 1) {
// we have some data, but do we have enough to read the entire header?
int headerLength = Msg.getHeaderLength(buf[1]);
isExtended = Msg.isExtended(buf, end, headerLength);
boolean isExtended = Msg.isExtended(buf, end, headerLength);
logger.trace("header length expected: {} extended: {}", headerLength, isExtended);
if (headerLength < 0) {
removeFromBuffer(1); // get rid of the leading 0x02 so draining works
bail("got unknown command code " + Utils.getHexByte(buf[1]));
bail("got unknown command code " + Utils.getHexByte(buf[0]));
} else if (headerLength >= 2) {
if (end >= headerLength) {
// only when the header is complete do we know that isExtended is correct!
msgLen = Msg.getMessageLength(buf[1], isExtended);
int msgLen = Msg.getMessageLength(buf[1], isExtended);
logger.trace("msgLen expected: {}", msgLen);
if (msgLen < 0) {
// Cannot make sense out of the combined command code & isExtended flag.
removeFromBuffer(1);
bail("unknown command code/ext flag: " + Utils.getHexByte(buf[1]));
bail("got unknown command code/ext flag " + Utils.getHexByte(buf[0]));
} else if (msgLen > 0) {
if (end >= msgLen) {
msg = Msg.createMessage(buf, msgLen, isExtended);
removeFromBuffer(msgLen);
}
} else { // should never happen
logger.warn("invalid message length, internal error!");
}
}
} else { // should never happen
logger.warn("invalid header length, internal error!");
msgLen = -1;
}
}
logger.trace("msgLen expected: {}", msgLen);
Msg msg = null;
if (msgLen > 0 && end >= msgLen) {
msg = Msg.createMessage(buf, msgLen, isExtended);
removeFromBuffer(msgLen);
// indicate no more messages available in buffer if empty or undefined message
if (end == 0 || msg == null) {
logger.trace("done processing current buffer data");
done = true;
}
logger.trace("keeping buffer len {} data: {}", end, Utils.getHexString(buf, end));
return msg;
}

private void bail(String txt) throws IOException {
drainBuffer(); // this will drain until end or it finds the next 0x02
logger.warn("{}", txt);
logger.debug("bad data received: {}", txt);
throw new IOException(txt);
}

Original file line number Diff line number Diff line change
@@ -120,6 +120,17 @@
</header>
<byte name = "statusByte"/>
</msg>
<msg name = "UnknownMessage5C" length = "11" direction = "FROM_MODEM">
<header length="9">
<byte>0x02</byte>
<byte name = "Cmd">0x5c</byte>
<address name = "fromAddress"/>
<address name = "toAddress"/>
<byte name = "messageFlags"/>
</header>
<byte name = "command1"/>
<byte name = "command2"/>
</msg>

<msg name = "GetIMInfo" length="2" direction = "TO_MODEM">
<header length="2">
@@ -281,7 +292,7 @@
</header>
<byte name = "DeviceCategory"/>
<byte name = "DeviceSubcategory"/>
<byte name = "FirmwareVersion"/>
<byte name = "FirmwareVersion"/>
</msg>
<msg name = "SetHostDeviceCategoryReply" length="6" direction = "FROM_MODEM">
<header length="2">
@@ -396,4 +407,4 @@
<byte name = "ACK/NACK"/>
</msg>

</xml>
</xml>

0 comments on commit f362efe

Please sign in to comment.