Skip to content

Commit

Permalink
Added initial project
Browse files Browse the repository at this point in the history
  • Loading branch information
Christopher Piggott committed Mar 10, 2015
1 parent 183b8ac commit 68778b8
Show file tree
Hide file tree
Showing 14 changed files with 699 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
build


.idea
*.iml

.idea/workspace.xml
.idea/tasks.xml
.idea/dictionaries
Expand Down
28 changes: 28 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.autofrog.xbee</groupId>
<artifactId>xbee-api-java</artifactId>
<version>1.0-SNAPSHOT</version>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>3.3.1</version>
<scope>test</scope>
</dependency>

</dependencies>

</project>
257 changes: 257 additions & 0 deletions src/main/java/com/autofrog/xbee/api/XbeeMessageParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
package com.autofrog.xbee.api;

import com.autofrog.xbee.api.exceptions.XbeeChecksumException;
import com.autofrog.xbee.api.exceptions.XbeeEmptyMessageException;
import com.autofrog.xbee.api.exceptions.XbeeException;
import com.autofrog.xbee.api.listeners.XbeeMessageListener;
import com.autofrog.xbee.api.listeners.XbeeParsingExceptionListener;
import com.autofrog.xbee.api.messages.XbeeExplicitRxMessage;
import com.autofrog.xbee.api.messages.XbeeMessageBase;
import com.autofrog.xbee.api.protocol.XbeeApiConstants;
import com.autofrog.xbee.api.protocol.XbeeMessageType;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
* Main entry point for the Xbee API parser.
*/
public class XbeeMessageParser {

/**
* States the parser may be in on a byte-by-byte basis
*/
public enum RxState {
WAITING_FOR_FLAG,
WAITING_FOR_LEN1,
WAITING_FOR_LEN2,
WAIT_FOR_FRAME_TYPE,
COUNTING,
WAITING_FOR_CHECKSUM
}

protected RxState rxState = RxState.WAITING_FOR_FLAG;
private byte frameType;
private int count;
private int len;
private byte checksum;
private byte[] buf = null;
private boolean escaped = false;
private boolean transmissionPaused = false;
private final Map<XbeeMessageType, List<XbeeMessageListener>> frameTypeSpecificListeners;
private final List<XbeeMessageListener> allFrameTypesListeners;
private final List<XbeeParsingExceptionListener> errorListeners;

protected XbeeMessageParser() {
frameTypeSpecificListeners = new ConcurrentHashMap<XbeeMessageType, List<XbeeMessageListener>>();
allFrameTypesListeners = new CopyOnWriteArrayList<XbeeMessageListener>();
errorListeners = new CopyOnWriteArrayList<XbeeParsingExceptionListener>();
}

/**
* Add a listener for ALL xbee messages
*
* @param newListener
*/
public void addListener(XbeeMessageListener newListener) {
allFrameTypesListeners.add(newListener);
}

/**
* Add a listener for a specific xbee message type
*
* @param frameType
* @param newListener
*/
public void addListener(XbeeMessageType frameType, XbeeMessageListener newListener) {

if (frameTypeSpecificListeners.containsKey(frameType) == false) {
frameTypeSpecificListeners.put(frameType, new CopyOnWriteArrayList<XbeeMessageListener>());
}

frameTypeSpecificListeners.get(frameType).add(newListener);
}

/**
* Remove a listener from everything it's subscribed to
*
* @param listenerToRemove
*/
public void removeListener(XbeeMessageListener listenerToRemove) {
allFrameTypesListeners.remove(listenerToRemove);

for (List<XbeeMessageListener> list : frameTypeSpecificListeners.values()) {
list.remove(listenerToRemove);
}
}


public void addXbeeExceptionListener(XbeeParsingExceptionListener listener) {
errorListeners.add(listener);
}

public void removeXbeeExceptionListener(XbeeParsingExceptionListener listener) {
errorListeners.remove(listener);
}

protected void notifyListenersOfException(XbeeException e) {
for (XbeeParsingExceptionListener listener : errorListeners) {
listener.xbeeParsingError(this, e);
}
}

protected void notifyListeners(XbeeMessageBase msg) {
for (XbeeMessageListener l : allFrameTypesListeners) {
l.onXbeeMessage(this, msg);
}

List<XbeeMessageListener> specificListeners = frameTypeSpecificListeners.get(msg.getRawFrameType());
if (specificListeners != null) {
for (XbeeMessageListener specificListener : specificListeners) {
specificListener.onXbeeMessage(this, msg);
}
}
}

public void byteIn(byte b) {
XbeeMessageBase msg = processByte(b);
if (msg != null) {
notifyListeners(msg);
}
}

public void bytesIn(byte[] bytes) {
for (byte b : bytes) {
byteIn(b);
}
}

public void bytesIn(byte[] bytes, int start, int len) {
int end = start + len;
for (int i = start; i < end; i++) {
byteIn(bytes[i]);
}
}

/**
* The main state machine
* @param b
* @return
*/
private XbeeMessageBase processByte(byte b) {
switch (b) {
case XbeeApiConstants.API_FLAG:
if (rxState != RxState.WAITING_FOR_FLAG) {
/* We did not expect two flags in a row */
notifyListenersOfException(new XbeeEmptyMessageException());
}

rxState = RxState.WAITING_FOR_LEN1;
break;
case XbeeApiConstants.API_ESCAPE:
escaped = true;
break;
case XbeeApiConstants.API_XON:
transmissionPaused = false;
break;
case XbeeApiConstants.API_XOFF:
transmissionPaused = true;
break;

default:
if (escaped) {
b = (byte) (b ^ 0x20);
escaped = false;
}

switch (rxState) {
case WAITING_FOR_FLAG:
break;

case WAITING_FOR_LEN1:
len = b << 8;
rxState = RxState.WAITING_FOR_LEN2;

break;

case WAITING_FOR_LEN2:
len = len | b;
buf = new byte[len - 1];
count = 0;
rxState = RxState.WAIT_FOR_FRAME_TYPE;
break;

case WAIT_FOR_FRAME_TYPE:
frameType = b;
checksum = frameType;
rxState = RxState.COUNTING;
break;

case COUNTING:
buf[count++] = b;
checksum = (byte) ((checksum + b) & 0xFF);
/* This is len-1 to skip the frame type */
if (count >= (len-1)) {
rxState = RxState.WAITING_FOR_CHECKSUM;
}
break;

case WAITING_FOR_CHECKSUM:
byte expected = (byte) (0xFF - checksum);
if (expected == b) {

try {
return buildSpecificMessage(frameType, buf);
} catch ( Exception e) {
/* Notify of some kind of parsing error */
notifyListenersOfException(new XbeeException());

return null;
} finally {
rxState = RxState.WAITING_FOR_FLAG;
}

} else {
/* Notify of a checksum error */
notifyListenersOfException(new XbeeChecksumException());
rxState = RxState.WAITING_FOR_FLAG;
}

}
}
return null;
}

/**
* Get the RX state on a byte by byte basis. Only really useful for debugging.
*
* @return
*/
public RxState getRxState() {
return rxState;
}

private XbeeMessageBase buildSpecificMessage(byte frameType, byte[] buf) throws IOException {
XbeeMessageType type = XbeeMessageType.lookup(frameType);

switch (type) {
case EXPLICIT_RX:
return XbeeExplicitRxMessage.create(buf);
case NODE_DISCOVERY:
break;
case RX_IO_SAMPLE:
break;
default:

}
return null;
}

public String dumpParserState() {
return String.format("state=%s len=%d count=%d escaped=%s",
rxState, len, count, escaped);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.autofrog.xbee.api.exceptions;

/**
* Created by chrisp on 009 3/9/2015.
*/
public class XbeeChecksumException extends XbeeException {
public XbeeChecksumException() {
}

public XbeeChecksumException(String message) {
super(message);
}

public XbeeChecksumException(String message, Throwable cause) {
super(message, cause);
}

public XbeeChecksumException(Throwable cause) {
super(cause);
}

public XbeeChecksumException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.autofrog.xbee.api.exceptions;

/**
* Created by chrisp on 009 3/9/2015.
*/
public class XbeeEmptyMessageException extends XbeeException {
public XbeeEmptyMessageException() {
}

public XbeeEmptyMessageException(String message) {
super(message);
}

public XbeeEmptyMessageException(String message, Throwable cause) {
super(message, cause);
}

public XbeeEmptyMessageException(Throwable cause) {
super(cause);
}

public XbeeEmptyMessageException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
26 changes: 26 additions & 0 deletions src/main/java/com/autofrog/xbee/api/exceptions/XbeeException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.autofrog.xbee.api.exceptions;

/**
* Created by chrisp on 009 3/9/2015.
*/
public class XbeeException extends Exception {

public XbeeException() {
}

public XbeeException(String message) {
super(message);
}

public XbeeException(String message, Throwable cause) {
super(message, cause);
}

public XbeeException(Throwable cause) {
super(cause);
}

public XbeeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.autofrog.xbee.api.listeners;

import com.autofrog.xbee.api.messages.XbeeMessageBase;

/**
*
* @author chrisp
*/
public interface XbeeMessageListener {

public void onXbeeMessage(Object sender, XbeeMessageBase msg);

}
Loading

0 comments on commit 68778b8

Please sign in to comment.