From 1d2d787168f565994d1cc68ade18a495f6baa3d6 Mon Sep 17 00:00:00 2001 From: Ola Andersson Date: Sat, 2 May 2015 22:15:05 +0200 Subject: [PATCH 1/6] Added basic changes for moving from RXTX to jSSC --- pom.xml | 8 +- .../xbee/RxTxSerialEventListener.java | 7 - .../rapplogic/xbee/SerialPortConnection.java | 173 ++++++++---------- src/com/rapplogic/xbee/XBeeConnection.java | 10 +- .../rapplogic/xbee/api/InputStreamThread.java | 9 +- src/com/rapplogic/xbee/api/PacketParser.java | 22 +-- src/com/rapplogic/xbee/api/XBee.java | 18 +- 7 files changed, 105 insertions(+), 142 deletions(-) delete mode 100644 src/com/rapplogic/xbee/RxTxSerialEventListener.java diff --git a/pom.xml b/pom.xml index 274dba4..4d80b41 100644 --- a/pom.xml +++ b/pom.xml @@ -33,10 +33,10 @@ - - org.rxtx - rxtx - 2.1.7 + + org.scream3r + jssc + 2.8.0 diff --git a/src/com/rapplogic/xbee/RxTxSerialEventListener.java b/src/com/rapplogic/xbee/RxTxSerialEventListener.java deleted file mode 100644 index c19f349..0000000 --- a/src/com/rapplogic/xbee/RxTxSerialEventListener.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.rapplogic.xbee; - -import gnu.io.SerialPortEvent; - -public interface RxTxSerialEventListener { - public void handleSerialEvent(SerialPortEvent event); -} diff --git a/src/com/rapplogic/xbee/SerialPortConnection.java b/src/com/rapplogic/xbee/SerialPortConnection.java index 6e73b5c..a4d69f9 100644 --- a/src/com/rapplogic/xbee/SerialPortConnection.java +++ b/src/com/rapplogic/xbee/SerialPortConnection.java @@ -19,20 +19,19 @@ package com.rapplogic.xbee; -import gnu.io.CommPortIdentifier; -import gnu.io.PortInUseException; -import gnu.io.SerialPort; -import gnu.io.SerialPortEvent; -import gnu.io.SerialPortEventListener; -import gnu.io.UnsupportedCommOperationException; - -import java.io.BufferedOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Enumeration; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import java.util.TooManyListenersException; +import jssc.SerialPort; +import jssc.SerialPortEvent; +import jssc.SerialPortEventListener; +import jssc.SerialPortException; +import jssc.SerialPortList; +import jssc.SerialPortTimeoutException; + import org.apache.log4j.Logger; import com.rapplogic.xbee.api.XBeeException; @@ -47,68 +46,42 @@ public class SerialPortConnection implements XBeeConnection, SerialPortEventListener { private final static Logger log = Logger.getLogger(SerialPortConnection.class); - - private InputStream inputStream; - private OutputStream outputStream; + + private static final int DEFAULT_TIMEOUT = 5000; private SerialPort serialPort; + private int timeout; + public SerialPortConnection() { - + } - - public void openSerialPort(String port, int baudRate) throws PortInUseException, UnsupportedCommOperationException, TooManyListenersException, IOException, XBeeException { - this.openSerialPort(port, "XBee", 0, baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE, SerialPort.FLOWCONTROL_NONE); + + public void openSerialPort(String port, int baudRate) throws TooManyListenersException, IOException, XBeeException, SerialPortException { + this.openSerialPort(port, "XBee", DEFAULT_TIMEOUT, baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE, SerialPort.FLOWCONTROL_NONE); } - - public void openSerialPort(String port, String appName, int timeout, int baudRate) throws PortInUseException, UnsupportedCommOperationException, TooManyListenersException, IOException, XBeeException { + + public void openSerialPort(String port, String appName, int timeout, int baudRate) throws TooManyListenersException, IOException, XBeeException, SerialPortException { this.openSerialPort(port, appName, timeout, baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE, SerialPort.FLOWCONTROL_NONE); } - - @SuppressWarnings("unchecked") - public void openSerialPort(String port, String appName, int timeout, int baudRate, int dataBits, int stopBits, int parity, int flowControl) throws PortInUseException, UnsupportedCommOperationException, TooManyListenersException, IOException, XBeeException { - // Apparently you can't query for a specific port, but instead must iterate - Enumeration portList = CommPortIdentifier.getPortIdentifiers(); - - CommPortIdentifier portId = null; - boolean found = false; - - while (portList.hasMoreElements()) { + public void openSerialPort(String port, String appName, int timeout, int baudRate, int dataBits, int stopBits, int parity, int flowControl) throws TooManyListenersException, IOException, XBeeException, SerialPortException { + // Apparently you can't query for a specific port, but instead must iterate + Set serialPorts = new HashSet<>(Arrays.asList(SerialPortList.getPortNames())); - portId = portList.nextElement(); + if (!serialPorts.contains(port)) { + throw new XBeeException("Could not find port: " + port); + } - if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { + this.timeout = timeout; - //log.debug("Found port: " + portId.getName()); + serialPort = new SerialPort(port); + serialPort.openPort(); - if (portId.getName().equals(port)) { - //log.debug("Using Port: " + portId.getName()); - found = true; - break; - } - } - } - - if (!found) { - throw new XBeeException("Could not find port: " + port); - } - - serialPort = (SerialPort) portId.open(appName, timeout); - - serialPort.setSerialPortParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); + serialPort.setParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE); - // activate the DATA_AVAILABLE notifier - serialPort.notifyOnDataAvailable(true); - - // activate the OUTPUT_BUFFER_EMPTY notifier - //serialPort.notifyOnOutputEmpty(true); - serialPort.addEventListener(this); - - inputStream = serialPort.getInputStream(); - outputStream = new BufferedOutputStream(serialPort.getOutputStream()); } /** @@ -117,58 +90,62 @@ public void openSerialPort(String port, String appName, int timeout, int baudRat @Override public void close() throws IOException { try { - serialPort.getInputStream().close(); - } catch (NullPointerException e) { - + serialPort.closePort(); + } catch (SerialPortException e) { + throw new IOException(e); } + } + public void serialEvent(SerialPortEvent event) { try { - serialPort.getOutputStream().close(); - } catch (NullPointerException e) { + if (serialPort.getInputBufferBytesCount() > 0) { + try { + log.debug("serialEvent: " + serialPort.getInputBufferBytesCount() + " bytes available"); + synchronized (this) { + this.notify(); + } + } catch (Exception e) { + log.error("Error in handleSerialData method", e); + } + } else { + log.warn("We were notified of new data but available() is returning 0"); + } + } catch (SerialPortException ex) { + // it's best not to throw the exception because the RXTX thread may not be prepared to handle + log.error("RXTX error in serialEvent method", ex); } - - try { - // this call blocks while thread is attempting to read from inputstream - serialPort.close(); - } catch (NullPointerException e) { + } + @Override + public int getByte() throws IOException { + try { + return serialPort.readBytes(1, timeout)[0] & 0xFF; + } catch (SerialPortException | SerialPortTimeoutException e) { + throw new IOException(e); } } - - public OutputStream getOutputStream() { - return outputStream; + + @Override + public boolean hasData() throws IOException { + try { + return serialPort.getInputBufferBytesCount() > 0; + } catch (SerialPortException e) { + throw new IOException(e); + } } - public InputStream getInputStream() { - return inputStream; + @Override + public void writeIntArray(int[] packet) throws IOException { + try { + serialPort.writeIntArray(packet); + } catch (SerialPortException e) { + throw new IOException(e); + } } - - public void serialEvent(SerialPortEvent event) { - - switch (event.getEventType()) { - case SerialPortEvent.DATA_AVAILABLE: - try { - if (this.getInputStream().available() > 0) { - try { - log.debug("serialEvent: " + serialPort.getInputStream().available() + " bytes available"); - - synchronized (this) { - this.notify(); - } - } catch (Exception e) { - log.error("Error in handleSerialData method", e); - } - } else { - log.warn("We were notified of new data but available() is returning 0"); - } - } catch (IOException ex) { - // it's best not to throw the exception because the RXTX thread may not be prepared to handle - log.error("RXTX error in serialEvent method", ex); - } - default: - log.debug("Ignoring serial port event type: " + event.getEventType()); - } + @Override + public boolean isConnected() { + return serialPort != null && serialPort.isOpened(); } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/XBeeConnection.java b/src/com/rapplogic/xbee/XBeeConnection.java index 4fc57a9..7686fd7 100644 --- a/src/com/rapplogic/xbee/XBeeConnection.java +++ b/src/com/rapplogic/xbee/XBeeConnection.java @@ -1,8 +1,6 @@ package com.rapplogic.xbee; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; /** * Represents a protocol independent connection to a XBee radio (e.g. could be a serial connection, socket, xmpp etc.) @@ -19,7 +17,9 @@ * */ public interface XBeeConnection { - public OutputStream getOutputStream(); - public InputStream getInputStream(); - public void close() throws IOException; + public int getByte() throws IOException; + public boolean hasData() throws IOException; + public void close() throws IOException; + public void writeIntArray(int[] packet) throws IOException; + public boolean isConnected(); } diff --git a/src/com/rapplogic/xbee/api/InputStreamThread.java b/src/com/rapplogic/xbee/api/InputStreamThread.java index 8ac92e1..06a38bf 100644 --- a/src/com/rapplogic/xbee/api/InputStreamThread.java +++ b/src/com/rapplogic/xbee/api/InputStreamThread.java @@ -138,13 +138,14 @@ public void run() { try { while (!done) { try { - if (connection.getInputStream().available() > 0) { + if (connection.hasData()) { log.debug("About to read from input stream"); - val = connection.getInputStream().read(); + + val = connection.getByte(); log.debug("Read " + ByteUtils.formatByte(val) + " from input stream"); if (val == XBeePacket.SpecialByte.START_BYTE.getValue()) { - packetStream = new PacketParser(connection.getInputStream()); + packetStream = new PacketParser(connection); response = packetStream.parsePacket(); if (log.isInfoEnabled()) { @@ -163,7 +164,7 @@ public void run() { // we will wait here for RXTX to notify us of new data synchronized (this.connection) { // There's a chance that we got notified after the first in.available check - if (connection.getInputStream().available() > 0) { + if (connection.hasData()) { continue; } diff --git a/src/com/rapplogic/xbee/api/PacketParser.java b/src/com/rapplogic/xbee/api/PacketParser.java index f685b2d..48dc298 100644 --- a/src/com/rapplogic/xbee/api/PacketParser.java +++ b/src/com/rapplogic/xbee/api/PacketParser.java @@ -20,12 +20,12 @@ package com.rapplogic.xbee.api; import java.io.IOException; -import java.io.InputStream; import java.util.HashMap; import java.util.Map; import org.apache.log4j.Logger; +import com.rapplogic.xbee.XBeeConnection; import com.rapplogic.xbee.api.wpan.RxResponse16; import com.rapplogic.xbee.api.wpan.RxResponse64; import com.rapplogic.xbee.api.wpan.RxResponseIoSample; @@ -37,7 +37,6 @@ import com.rapplogic.xbee.api.zigbee.ZNetTxStatusResponse; import com.rapplogic.xbee.util.ByteUtils; import com.rapplogic.xbee.util.IIntInputStream; -import com.rapplogic.xbee.util.InputStreamWrapper; import com.rapplogic.xbee.util.IntArrayOutputStream; /** @@ -55,8 +54,6 @@ public class PacketParser implements IIntInputStream, IPacketParser { private final static Logger log = Logger.getLogger(PacketParser.class); - private IIntInputStream in; - // size of packet after special bytes have been escaped private XBeePacketLength length; private Checksum checksum = new Checksum(); @@ -70,6 +67,8 @@ public class PacketParser implements IIntInputStream, IPacketParser { private ApiId apiId; private int intApiId; + private final XBeeConnection connection; + private static Map> handlerMap = new HashMap>(); // TODO reuse this object for all packets @@ -112,16 +111,11 @@ static void unRegisterResponseHandler(int apiId) { throw new IllegalArgumentException("No response handler for: " + apiId); } } - - public PacketParser(InputStream in) { - this.in = new InputStreamWrapper(in); - } - - // for parsing a packet from a byte array - public PacketParser(IIntInputStream in) { - this.in = in; + + public PacketParser(XBeeConnection connection) { + this.connection = connection; } - + /** * This method is guaranteed (unless I screwed up) to return an instance of XBeeResponse and should never throw an exception * If an exception occurs, it will be packaged and returned as an ErrorResponse. @@ -215,7 +209,7 @@ public int read(String context) throws IOException { * @throws IOException */ private int readFromStream() throws IOException { - int b = in.read(); + int b = connection.getByte(); // save raw bytes to transfer via network rawBytes.write(b); diff --git a/src/com/rapplogic/xbee/api/XBee.java b/src/com/rapplogic/xbee/api/XBee.java index ca1bc03..30c6363 100644 --- a/src/com/rapplogic/xbee/api/XBee.java +++ b/src/com/rapplogic/xbee/api/XBee.java @@ -25,6 +25,8 @@ import java.util.List; import java.util.concurrent.TimeUnit; +import jssc.SerialPortException; + import org.apache.log4j.Logger; import com.rapplogic.xbee.SerialPortConnection; @@ -233,7 +235,7 @@ public void sendPacket(XBeePacket packet) throws IOException { * @param packet * @throws RuntimeException when serial device is disconnected */ - public void sendPacket(int[] packet) throws IOException { + public void sendPacket(int[] packet) throws IOException { // TODO should we synchronize on read lock so we are sending/recv. simultaneously? // TODO call request listener with byte array @@ -245,14 +247,17 @@ public void sendPacket(int[] packet) throws IOException { log.info("Sending packet to XBee " + ByteUtils.toBase16(packet)); } + xbeeConnection.writeIntArray(packet); + + /* for (int packetByte : packet) { // if connection lost //Caused by: com.rapplogic.xbee.api.XBeeException //Caused by: java.io.IOException: Input/output error in writeArray xbeeConnection.getOutputStream().write(packetByte); } - xbeeConnection.getOutputStream().flush(); + */ } /** @@ -540,14 +545,11 @@ public void close() { } try { -// xbeeConnection.getOutputStream().close(); xbeeConnection.close(); } catch (IOException e) { log.warn("Failed to close connection", e); } - - this.type = null; parser = null; xbeeConnection = null; @@ -561,11 +563,7 @@ public void close() { */ public boolean isConnected() { try { - if (parser.getXBeeConnection().getInputStream() != null && parser.getXBeeConnection().getOutputStream() != null) { - return true; - } - - return false; + return xbeeConnection.isConnected(); } catch (Exception e) { return false; } From f6980483e436bc1364f1eb7768bd3b67e51140eb Mon Sep 17 00:00:00 2001 From: Ola Andersson Date: Sun, 3 May 2015 15:23:09 +0200 Subject: [PATCH 2/6] Added XBeeInformation class to hold basic information of the XBee device. The code needs further cleanup. --- .../rapplogic/xbee/SerialPortConnection.java | 6 +- .../rapplogic/xbee/api/InputStreamThread.java | 6 +- src/com/rapplogic/xbee/api/XBee.java | 36 ++++++++- .../rapplogic/xbee/api/XBeeConfiguration.java | 10 +++ .../rapplogic/xbee/api/XBeeInformation.java | 74 +++++++++++++++++++ 5 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 src/com/rapplogic/xbee/api/XBeeInformation.java diff --git a/src/com/rapplogic/xbee/SerialPortConnection.java b/src/com/rapplogic/xbee/SerialPortConnection.java index a4d69f9..0aa25a4 100644 --- a/src/com/rapplogic/xbee/SerialPortConnection.java +++ b/src/com/rapplogic/xbee/SerialPortConnection.java @@ -67,7 +67,7 @@ public void openSerialPort(String port, String appName, int timeout, int baudRat public void openSerialPort(String port, String appName, int timeout, int baudRate, int dataBits, int stopBits, int parity, int flowControl) throws TooManyListenersException, IOException, XBeeException, SerialPortException { // Apparently you can't query for a specific port, but instead must iterate - Set serialPorts = new HashSet<>(Arrays.asList(SerialPortList.getPortNames())); + Set serialPorts = new HashSet(Arrays.asList(SerialPortList.getPortNames())); if (!serialPorts.contains(port)) { throw new XBeeException("Could not find port: " + port); @@ -121,7 +121,9 @@ public void serialEvent(SerialPortEvent event) { public int getByte() throws IOException { try { return serialPort.readBytes(1, timeout)[0] & 0xFF; - } catch (SerialPortException | SerialPortTimeoutException e) { + } catch (SerialPortException e) { + throw new IOException(e); + } catch (SerialPortTimeoutException e) { throw new IOException(e); } } diff --git a/src/com/rapplogic/xbee/api/InputStreamThread.java b/src/com/rapplogic/xbee/api/InputStreamThread.java index 06a38bf..55fce29 100644 --- a/src/com/rapplogic/xbee/api/InputStreamThread.java +++ b/src/com/rapplogic/xbee/api/InputStreamThread.java @@ -48,6 +48,7 @@ public class InputStreamThread implements Runnable { private volatile boolean done = false; private final XBeeConnection connection; private XBeeConfiguration conf; + private XBeeInformation info; public XBeeConnection getXBeeConnection() { return connection; @@ -57,6 +58,7 @@ public XBeeConnection getXBeeConnection() { // TODO use weak references private final List packetListenerList = new LinkedList(); + public List getPacketListenerList() { return packetListenerList; @@ -66,9 +68,10 @@ public BlockingQueue getResponseQueue() { return responseQueue; } - public InputStreamThread(final XBeeConnection connection, XBeeConfiguration conf) { + public InputStreamThread(final XBeeConnection connection, XBeeConfiguration conf, XBeeInformation info) { this.connection = connection; this.conf = conf; + this.info = info; // Create an executor to deliver incoming packets to listeners. We'll use a single // thread with an unbounded queue. @@ -154,6 +157,7 @@ public void run() { } // success + info.touchLastReceivedTime(); this.addResponse(response); } else { log.warn("expected start byte but got this " + ByteUtils.toBase16(val) + ", discarding"); diff --git a/src/com/rapplogic/xbee/api/XBee.java b/src/com/rapplogic/xbee/api/XBee.java index 30c6363..7501814 100644 --- a/src/com/rapplogic/xbee/api/XBee.java +++ b/src/com/rapplogic/xbee/api/XBee.java @@ -50,6 +50,7 @@ public class XBee implements IXBee { private XBeeConnection xbeeConnection; private InputStreamThread parser; private XBeeConfiguration conf; + private XBeeInformation info; private RadioType type; public XBee() { @@ -69,11 +70,12 @@ public void run() { } }); } + info = new XBeeInformation(); } private void doStartupChecks() throws XBeeException { // Perform startup checks - try { + try { AtCommandResponse ap = this.sendAtCommand(new AtCommand("AP")); if (!ap.isOk()) { @@ -103,19 +105,40 @@ private void doStartupChecks() throws XBeeException { if (radioType == RadioType.UNKNOWN) { log.warn("Unknown radio type (HV): " + ap.getValue()[0]); - } + } + info.setHardwareVersion(radioType); + AtCommandResponse vr = this.sendAtCommand(new AtCommand("VR")); if (vr.isOk()) { log.info("Firmware version is " + ByteUtils.toBase16(vr.getValue())); } + info.setFirmwareVersion(vr.getValue()); + this.clearResponseQueue(); } catch (XBeeTimeoutException ex) { throw new XBeeException("AT command timed-out while attempt to set/read in API mode. Check that the XBee radio is in API mode (AP=2); it will not function propertly in AP=1"); } } + + private void doStartupGetInformation() throws XBeeException { + try { + AtCommandResponse id = this.sendAtCommand(new AtCommand("ID")); + info.setPanID(id.getValue()); + + AtCommandResponse sh = this.sendAtCommand(new AtCommand("SH")); + info.setSerialHigh(sh.getValue()); + + AtCommandResponse sl = this.sendAtCommand(new AtCommand("SL")); + info.setSerialLow(sl.getValue()); + + } catch (XBeeTimeoutException ex) { + throw new XBeeException("AT command timed-out while attempt to set/read in API mode. Check that the XBee radio is in API mode (AP=2); it will not function propertly in AP=1"); + } + + } /** * If XBeeConnection.startUpChecks is set to true (default), this method will check if the AP parameter @@ -165,12 +188,15 @@ private void initConnection(XBeeConnection conn) throws XBeeException { try { this.xbeeConnection = conn; - parser = new InputStreamThread(this.xbeeConnection, conf); + parser = new InputStreamThread(this.xbeeConnection, conf, info); // startup heuristics if (conf.isStartupChecks()) { this.doStartupChecks(); } + if (conf.isStartupGetInformation()) { + this.doStartupGetInformation(); + } } catch (XBeeException e) { throw e; } catch (Exception e) { @@ -622,4 +648,8 @@ public void clearResponseQueue() { parser.getResponseQueue().clear(); } + + public XBeeInformation getInformation() { + return info; + } } diff --git a/src/com/rapplogic/xbee/api/XBeeConfiguration.java b/src/com/rapplogic/xbee/api/XBeeConfiguration.java index 6fb5070..6f3f161 100644 --- a/src/com/rapplogic/xbee/api/XBeeConfiguration.java +++ b/src/com/rapplogic/xbee/api/XBeeConfiguration.java @@ -4,6 +4,7 @@ public class XBeeConfiguration { private boolean shutdownHook = false; private boolean startupChecks = true; + private boolean getInformation = true; private int maxQueueSize = 100; private int sendSynchronousTimeout = 5000; private ResponseFilter responseQueueFilter; @@ -41,6 +42,11 @@ public XBeeConfiguration withStartupChecks(boolean startupChecks) { this.startupChecks = startupChecks; return this; } + + public XBeeConfiguration withStartupGetInformation(boolean getInformation) { + this.getInformation = getInformation; + return this; + } /** * Sets the maximum size of the internal queue that supports the getResponse(..) method. @@ -96,4 +102,8 @@ public int getSendSynchronousTimeout() { public boolean isShutdownHook() { return shutdownHook; } + + public boolean isStartupGetInformation() { + return getInformation; + } } diff --git a/src/com/rapplogic/xbee/api/XBeeInformation.java b/src/com/rapplogic/xbee/api/XBeeInformation.java new file mode 100644 index 0000000..b69745e --- /dev/null +++ b/src/com/rapplogic/xbee/api/XBeeInformation.java @@ -0,0 +1,74 @@ +package com.rapplogic.xbee.api; + +import com.rapplogic.xbee.api.HardwareVersion.RadioType; + +public class XBeeInformation { + enum XBeeMode { + COORDINATOR, ROUTER, END_DEVICE, UNKNOWN; + } + private RadioType hardwareVersion; + private int firmwareVersion; + private long panID; + private int serialHigh; + private int serialLow; + private XBeeMode mode = XBeeMode.UNKNOWN; + private long lastReceivedMessage; + + public void setHardwareVersion(RadioType hardwareVersion) { + this.hardwareVersion = hardwareVersion; + } + + public void setFirmwareVersion(int[] fv) { + this.firmwareVersion = fv[0] << 8 | fv[1]; + // First byte of the firmware version tells us which mode we're running in + // in case we're using API mode (which we are). + switch(fv[0]) + { + case 0x21: + mode = XBeeMode.COORDINATOR; + break; + case 0x23: + mode = XBeeMode.ROUTER; + break; + case 0x29: + mode = XBeeMode.END_DEVICE; + break; + default: + mode = XBeeMode.UNKNOWN; + } + } + + public void setPanID(int[] id) { + this.panID = (long)id[0] << 56 | (long)id[1] << 48 | (long)id[2] << 40 | (long)id[3] << 32 | + id[4] << 24 | id[5] << 16 | id[6] << 8 | id[7]; + } + + public void setSerialHigh(int[] sh) { + this.serialHigh = sh[0] << 24 | sh[1] << 16 | sh[2] << 8 | sh[3]; + } + + public void setSerialLow(int[] sl) { + this.serialLow = sl[0] << 24 | sl[1] << 16 | sl[2] << 8 | sl[3]; + } + + public XBeeMode getMode() { + return mode; + } + + public void touchLastReceivedTime() { + lastReceivedMessage = System.currentTimeMillis(); + } + + public long getLastReceviedTime() { + return lastReceivedMessage; + } + + @Override + public String toString() { + return "XBeeInformation [hardwareVersion=" + hardwareVersion + + ", firmwareVersion=" + String.format("%02X", firmwareVersion) + ", panID=" + String.format("%08X", panID) + + ", serialHigh=" + String.format("%04X", serialHigh) + ", serialLow=" + String.format("%04X", serialLow) + + ", mode " + mode.toString() + "]"; + } + +} From c21a66c1fcd7c7a02120d44a3c4aad80064d63a1 Mon Sep 17 00:00:00 2001 From: Ola Andersson Date: Wed, 10 Feb 2016 10:54:27 +0100 Subject: [PATCH 3/6] Refactored / moved code around to make the project in-line with maven standards. --- .classpath | 19 +- .project | 6 + .settings/org.eclipse.core.resources.prefs | 6 + .settings/org.eclipse.jdt.core.prefs | 13 + .settings/org.eclipse.m2e.core.prefs | 4 + build.properties | 0 build.xml | 118 -- lib/RXTXcomm.jar | Bin 59464 -> 0 bytes lib/log4j.jar | Bin 352668 -> 0 bytes librxtxSerial.jnilib | Bin 271860 -> 0 bytes librxtxSerial.so | Bin 154682 -> 0 bytes pom.xml | 36 +- rxtxSerial.dll | Bin 77759 -> 0 bytes .../rapplogic/xbee/api/XBeeInformation.java | 74 - .../java}/com/rapplogic/xbee/api/ApiId.java | 0 .../com/rapplogic/xbee/api/Checksum.java | 198 +-- .../rapplogic/xbee/api/CollectTerminator.java | 0 .../rapplogic/xbee/api/HardwareVersion.java | 151 +- .../com/rapplogic/xbee/api/IPacketParser.java | 0 .../java}/com/rapplogic/xbee/api/IXBee.java | 0 .../rapplogic/xbee/api/InputStreamThread.java | 452 +++--- .../rapplogic/xbee/api/PacketListener.java | 0 .../com/rapplogic/xbee/api/PacketParser.java | 763 +++++----- .../rapplogic/xbee/api/ResponseFilter.java | 0 .../java}/com/rapplogic/xbee/api/XBee.java | 1285 ++++++++--------- .../com/rapplogic/xbee/api/XBeeAddress.java | 84 +- .../com/rapplogic/xbee/api/XBeeAddress16.java | 196 +-- .../com/rapplogic/xbee/api/XBeeAddress64.java | 242 ++-- .../rapplogic/xbee/api/XBeeConfiguration.java | 21 +- .../com/rapplogic/xbee/api/XBeeException.java | 116 +- .../rapplogic/xbee/api/XBeeInformation.java | 88 ++ .../xbee/api/XBeeNotConnectedException.java | 68 +- .../com/rapplogic/xbee/api/XBeePacket.java | 658 ++++----- .../rapplogic/xbee/api/XBeePacketHandler.java | 48 +- .../rapplogic/xbee/api/XBeePacketLength.java | 98 +- .../xbee/api/XBeeParseException.java | 58 +- .../com/rapplogic/xbee/api/XBeeRequest.java | 170 +-- .../com/rapplogic/xbee/api/XBeeResponse.java | 384 +++-- .../xbee/api/XBeeTimeoutException.java | 70 +- .../xbee/api/requests}/AtCommand.java | 278 ++-- .../xbee/api/requests}/AtCommandQueue.java | 91 +- .../xbee/api/requests}/RemoteAtRequest.java | 8 +- .../api/responses}/AtCommandResponse.java | 307 ++-- .../xbee/api/responses}/ErrorResponse.java | 142 +- .../xbee/api/responses}/GenericResponse.java | 107 +- .../api/responses}/ModemStatusResponse.java | 229 +-- .../api/responses}/NoRequestResponse.java | 2 +- .../xbee/api/responses}/RemoteAtResponse.java | 6 +- .../api/responses}/XBeeFrameIdResponse.java | 91 +- .../com/rapplogic/xbee/api/wpan/IoSample.java | 574 ++++---- .../xbee/api/wpan/RxBaseResponse.java | 199 +-- .../rapplogic/xbee/api/wpan/RxResponse.java | 120 +- .../rapplogic/xbee/api/wpan/RxResponse16.java | 99 +- .../rapplogic/xbee/api/wpan/RxResponse64.java | 98 +- .../xbee/api/wpan/RxResponseIoSample.java | 592 ++++---- .../rapplogic/xbee/api/wpan/TxRequest16.java | 232 +-- .../rapplogic/xbee/api/wpan/TxRequest64.java | 254 ++-- .../xbee/api/wpan/TxRequestBase.java | 224 +-- .../xbee/api/wpan/TxStatusResponse.java | 246 ++-- .../xbee/api/wpan/WpanNodeDiscover.java | 250 ++-- .../xbee/api/zigbee/AssociationStatus.java | 140 +- .../xbee/api/zigbee/ZBForceSampleRequest.java | 90 +- .../xbee/api/zigbee/ZBNodeDiscover.java | 420 +++--- .../api/zigbee/ZNetExplicitRxResponse.java | 214 +-- .../api/zigbee/ZNetExplicitTxRequest.java | 404 +++--- .../ZNetNodeIdentificationResponse.java | 542 +++---- .../xbee/api/zigbee/ZNetRxBaseResponse.java | 312 ++-- .../api/zigbee/ZNetRxIoSampleResponse.java | 1098 +++++++------- .../xbee/api/zigbee/ZNetRxResponse.java | 126 +- .../xbee/api/zigbee/ZNetTxRequest.java | 484 +++---- .../xbee/api/zigbee/ZNetTxStatusResponse.java | 472 +++--- .../connections}/AbstractXBeeConnection.java | 2 +- .../connections}/SerialPortConnection.java | 316 ++-- .../xbee/connections}/XBeeConnection.java | 4 +- .../connections/XBeeConnectionException.java | 13 + .../rapplogic/xbee/connections}/XBeePin.java | 11 +- .../IConnectionProvider.java | 10 + .../SerialPortConnectionProvider.java | 26 + .../rapplogic/xbee/examples/ApiAtExample.java | 156 +- .../xbee/examples/RemoteAtExample.java | 212 +-- .../xbee/examples/ResponseHandlerExample.java | 0 .../examples/wpan/ApiReceiverExample.java | 198 +-- .../xbee/examples/wpan/ApiSenderExample.java | 270 ++-- .../xbee/examples/wpan/IoSamplesExample.java | 276 ++-- .../xbee/examples/wpan/Processing.java | 0 .../wpan/WpanNodeDiscoverExample.java | 210 +-- .../zigbee/BroadcastReceiverExample.java | 118 +- .../zigbee/BroadcastSenderExample.java | 154 +- .../examples/zigbee/SleepTestCoordinator.java | 4 +- .../examples/zigbee/ZBForceSampleExample.java | 188 +-- .../zigbee/ZBNodeDiscoverExample.java | 192 +-- .../examples/zigbee/ZNetApiAtExample.java | 342 ++--- .../zigbee/ZNetExplicitReceiverExample.java | 154 +- .../zigbee/ZNetExplicitSenderExample.java | 196 +-- .../examples/zigbee/ZNetIoSampleExample.java | 170 +-- .../examples/zigbee/ZNetReceiverExample.java | 200 +-- .../examples/zigbee/ZNetSenderExample.java | 364 ++--- .../xbee/test/OpenCloseConnectionsTest.java | 2 +- .../com/rapplogic/xbee/util/ByteUtils.java | 610 ++++---- .../com/rapplogic/xbee/util/DoubleByte.java | 210 +-- .../rapplogic/xbee/util/ExceptionHandler.java | 0 .../com/rapplogic/xbee/util/IIntArray.java | 48 +- .../rapplogic/xbee/util/IIntInputStream.java | 56 +- .../xbee/util/InputStreamWrapper.java | 0 .../xbee/util/IntArrayInputStream.java | 120 +- .../xbee/util/IntArrayOutputStream.java | 126 +- .../main/resources/log4j.properties | 0 .../com/rapplogic/xbee/SimpleSeries2Test.java | 29 + .../xbee/TestConnectionProvider.java | 15 + 109 files changed, 9486 insertions(+), 9393 deletions(-) create mode 100644 .settings/org.eclipse.core.resources.prefs create mode 100644 .settings/org.eclipse.jdt.core.prefs create mode 100644 .settings/org.eclipse.m2e.core.prefs delete mode 100644 build.properties delete mode 100644 build.xml delete mode 100644 lib/RXTXcomm.jar delete mode 100644 lib/log4j.jar delete mode 100644 librxtxSerial.jnilib delete mode 100644 librxtxSerial.so delete mode 100644 rxtxSerial.dll delete mode 100644 src/com/rapplogic/xbee/api/XBeeInformation.java rename src/{ => main/java}/com/rapplogic/xbee/api/ApiId.java (100%) rename src/{ => main/java}/com/rapplogic/xbee/api/Checksum.java (95%) rename src/{ => main/java}/com/rapplogic/xbee/api/CollectTerminator.java (100%) rename src/{ => main/java}/com/rapplogic/xbee/api/HardwareVersion.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/api/IPacketParser.java (100%) rename src/{ => main/java}/com/rapplogic/xbee/api/IXBee.java (100%) rename src/{ => main/java}/com/rapplogic/xbee/api/InputStreamThread.java (87%) rename src/{ => main/java}/com/rapplogic/xbee/api/PacketListener.java (100%) rename src/{ => main/java}/com/rapplogic/xbee/api/PacketParser.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/api/ResponseFilter.java (100%) rename src/{ => main/java}/com/rapplogic/xbee/api/XBee.java (89%) rename src/{ => main/java}/com/rapplogic/xbee/api/XBeeAddress.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/api/XBeeAddress16.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/api/XBeeAddress64.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/api/XBeeConfiguration.java (86%) rename src/{ => main/java}/com/rapplogic/xbee/api/XBeeException.java (96%) create mode 100644 src/main/java/com/rapplogic/xbee/api/XBeeInformation.java rename src/{ => main/java}/com/rapplogic/xbee/api/XBeeNotConnectedException.java (93%) rename src/{ => main/java}/com/rapplogic/xbee/api/XBeePacket.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/api/XBeePacketHandler.java (97%) rename src/{ => main/java}/com/rapplogic/xbee/api/XBeePacketLength.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/api/XBeeParseException.java (97%) rename src/{ => main/java}/com/rapplogic/xbee/api/XBeeRequest.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/api/XBeeResponse.java (95%) rename src/{ => main/java}/com/rapplogic/xbee/api/XBeeTimeoutException.java (96%) rename src/{com/rapplogic/xbee/api => main/java/com/rapplogic/xbee/api/requests}/AtCommand.java (94%) rename src/{com/rapplogic/xbee/api => main/java/com/rapplogic/xbee/api/requests}/AtCommandQueue.java (88%) rename src/{com/rapplogic/xbee/api => main/java/com/rapplogic/xbee/api/requests}/RemoteAtRequest.java (96%) rename src/{com/rapplogic/xbee/api => main/java/com/rapplogic/xbee/api/responses}/AtCommandResponse.java (94%) rename src/{com/rapplogic/xbee/api => main/java/com/rapplogic/xbee/api/responses}/ErrorResponse.java (87%) rename src/{com/rapplogic/xbee/api => main/java/com/rapplogic/xbee/api/responses}/GenericResponse.java (87%) rename src/{com/rapplogic/xbee/api => main/java/com/rapplogic/xbee/api/responses}/ModemStatusResponse.java (92%) rename src/{com/rapplogic/xbee/api => main/java/com/rapplogic/xbee/api/responses}/NoRequestResponse.java (84%) rename src/{com/rapplogic/xbee/api => main/java/com/rapplogic/xbee/api/responses}/RemoteAtResponse.java (94%) rename src/{com/rapplogic/xbee/api => main/java/com/rapplogic/xbee/api/responses}/XBeeFrameIdResponse.java (88%) rename src/{ => main/java}/com/rapplogic/xbee/api/wpan/IoSample.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/api/wpan/RxBaseResponse.java (94%) rename src/{ => main/java}/com/rapplogic/xbee/api/wpan/RxResponse.java (95%) rename src/{ => main/java}/com/rapplogic/xbee/api/wpan/RxResponse16.java (91%) rename src/{ => main/java}/com/rapplogic/xbee/api/wpan/RxResponse64.java (95%) rename src/{ => main/java}/com/rapplogic/xbee/api/wpan/RxResponseIoSample.java (95%) rename src/{ => main/java}/com/rapplogic/xbee/api/wpan/TxRequest16.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/api/wpan/TxRequest64.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/api/wpan/TxRequestBase.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/api/wpan/TxStatusResponse.java (94%) rename src/{ => main/java}/com/rapplogic/xbee/api/wpan/WpanNodeDiscover.java (91%) mode change 100755 => 100644 rename src/{ => main/java}/com/rapplogic/xbee/api/zigbee/AssociationStatus.java (95%) rename src/{ => main/java}/com/rapplogic/xbee/api/zigbee/ZBForceSampleRequest.java (91%) rename src/{ => main/java}/com/rapplogic/xbee/api/zigbee/ZBNodeDiscover.java (92%) rename src/{ => main/java}/com/rapplogic/xbee/api/zigbee/ZNetExplicitRxResponse.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/api/zigbee/ZNetExplicitTxRequest.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/api/zigbee/ZNetNodeIdentificationResponse.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/api/zigbee/ZNetRxBaseResponse.java (95%) rename src/{ => main/java}/com/rapplogic/xbee/api/zigbee/ZNetRxIoSampleResponse.java (95%) rename src/{ => main/java}/com/rapplogic/xbee/api/zigbee/ZNetRxResponse.java (92%) rename src/{ => main/java}/com/rapplogic/xbee/api/zigbee/ZNetTxRequest.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/api/zigbee/ZNetTxStatusResponse.java (95%) rename src/{com/rapplogic/xbee => main/java/com/rapplogic/xbee/connections}/AbstractXBeeConnection.java (97%) rename src/{com/rapplogic/xbee => main/java/com/rapplogic/xbee/connections}/SerialPortConnection.java (91%) rename src/{com/rapplogic/xbee => main/java/com/rapplogic/xbee/connections}/XBeeConnection.java (87%) create mode 100644 src/main/java/com/rapplogic/xbee/connections/XBeeConnectionException.java rename src/{com/rapplogic/xbee => main/java/com/rapplogic/xbee/connections}/XBeePin.java (98%) create mode 100644 src/main/java/com/rapplogic/xbee/connections/connectionprovider/IConnectionProvider.java create mode 100644 src/main/java/com/rapplogic/xbee/connections/connectionprovider/SerialPortConnectionProvider.java rename src/{ => main/java}/com/rapplogic/xbee/examples/ApiAtExample.java (95%) rename src/{ => main/java}/com/rapplogic/xbee/examples/RemoteAtExample.java (94%) rename src/{ => main/java}/com/rapplogic/xbee/examples/ResponseHandlerExample.java (100%) rename src/{ => main/java}/com/rapplogic/xbee/examples/wpan/ApiReceiverExample.java (95%) rename src/{ => main/java}/com/rapplogic/xbee/examples/wpan/ApiSenderExample.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/examples/wpan/IoSamplesExample.java (95%) rename src/{ => main/java}/com/rapplogic/xbee/examples/wpan/Processing.java (100%) rename src/{ => main/java}/com/rapplogic/xbee/examples/wpan/WpanNodeDiscoverExample.java (94%) rename src/{ => main/java}/com/rapplogic/xbee/examples/zigbee/BroadcastReceiverExample.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/examples/zigbee/BroadcastSenderExample.java (97%) rename src/{ => main/java}/com/rapplogic/xbee/examples/zigbee/SleepTestCoordinator.java (94%) rename src/{ => main/java}/com/rapplogic/xbee/examples/zigbee/ZBForceSampleExample.java (95%) rename src/{ => main/java}/com/rapplogic/xbee/examples/zigbee/ZBNodeDiscoverExample.java (94%) rename src/{ => main/java}/com/rapplogic/xbee/examples/zigbee/ZNetApiAtExample.java (95%) mode change 100755 => 100644 rename src/{ => main/java}/com/rapplogic/xbee/examples/zigbee/ZNetExplicitReceiverExample.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/examples/zigbee/ZNetExplicitSenderExample.java (97%) rename src/{ => main/java}/com/rapplogic/xbee/examples/zigbee/ZNetIoSampleExample.java (97%) rename src/{ => main/java}/com/rapplogic/xbee/examples/zigbee/ZNetReceiverExample.java (94%) rename src/{ => main/java}/com/rapplogic/xbee/examples/zigbee/ZNetSenderExample.java (97%) rename src/{ => main/java}/com/rapplogic/xbee/test/OpenCloseConnectionsTest.java (97%) rename src/{ => main/java}/com/rapplogic/xbee/util/ByteUtils.java (95%) rename src/{ => main/java}/com/rapplogic/xbee/util/DoubleByte.java (95%) rename src/{ => main/java}/com/rapplogic/xbee/util/ExceptionHandler.java (100%) rename src/{ => main/java}/com/rapplogic/xbee/util/IIntArray.java (97%) rename src/{ => main/java}/com/rapplogic/xbee/util/IIntInputStream.java (97%) rename src/{ => main/java}/com/rapplogic/xbee/util/InputStreamWrapper.java (100%) mode change 100755 => 100644 rename src/{ => main/java}/com/rapplogic/xbee/util/IntArrayInputStream.java (96%) rename src/{ => main/java}/com/rapplogic/xbee/util/IntArrayOutputStream.java (96%) rename log4j.properties => src/main/resources/log4j.properties (100%) create mode 100644 src/test/java/com/rapplogic/xbee/SimpleSeries2Test.java create mode 100644 src/test/java/com/rapplogic/xbee/TestConnectionProvider.java diff --git a/.classpath b/.classpath index 29da01f..3e73630 100644 --- a/.classpath +++ b/.classpath @@ -1,8 +1,17 @@ - - - - - + + + + + + + + + + + + + + diff --git a/.project b/.project index ede8aac..d747867 100644 --- a/.project +++ b/.project @@ -10,8 +10,14 @@ + + org.eclipse.m2e.core.maven2Builder + + + + org.eclipse.m2e.core.maven2Nature org.eclipse.jdt.core.javanature diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..ed7df2b --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,6 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/main/resources=UTF-8 +encoding//src/test/java=UTF-8 +encoding/=UTF-8 +encoding/src=UTF-8 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..13b3428 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,13 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/build.properties b/build.properties deleted file mode 100644 index e69de29..0000000 diff --git a/build.xml b/build.xml deleted file mode 100644 index 5e89f62..0000000 --- a/build.xml +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/RXTXcomm.jar b/lib/RXTXcomm.jar deleted file mode 100644 index 84e5f01dff26139e016015725e00afc37ce58e0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59464 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

h 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 ZpwQU!arKSVgfB^e*VGmSLZO53>3_Oi6RiLM diff --git a/lib/log4j.jar b/lib/log4j.jar deleted file mode 100644 index 493a3ccc1361b0d84889798a995a351138826511..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 352668 zcmb@u1C%7~wyxV{+wQV$cXhe4Y}>YN+qP}nwrzE(i(R_)ul4VH_E~qGHSQkcWJYG> z7&&ufM#PhI&hP!+2w4CKDD=0#ZkLFPbpLqq_Y>6DT1r?^fL2^ugkJV54D}o2*Zp6^ zQ2%?Flz_Cjh_Hepos@{xbQd%*BO>T7U#tt{3i+*&zwAnW5W>iC-sU9aXS*73aVD2K48gycnt^`JMJzwul&(W zb(6)UgTWmZ6RNavEP+_ZU&-gj!oFJBhhsf4%9IZxx~v4k5Ou>brr9|CDFs6o00GIY zo2C-_^*aZ?9)E4gKR5X7Up5%@>s=drlYeaMzmI_VJ3`M^&%o5^zXks5PC@$L11)V# zSj_+1KK$2jCir*Z0=BkB)`mv*bOx4s4h~+i+zS0aP=jWO5PxV>l|u4iXU)bAPmcqS z_|N%c!E~^$sp;vnSpzeFu)_HG@{ivd3wi=s*X2BUZsHafbFZi((aOJvJrQDkzQy$XLuAIcA=y{%q-R+yBwckgn&M`E&F4(9Lv zp+bfwmC*{$gX9TrVC^F&JtB!6XoJ*8v5|X&^T|I$50ZJwdvpd^R@kwj!828RAQYx@qYVPVZ z+gDoIcVn9)8iy_0@dzGQoob{mbxN-)LWAKIqrv0?_@(*gy=2+HGN4&&8tRS`axU?# z5hEbNP++T5U?pQz>MqoVr|aRt4Ujd%w$vG{{@k#|@CbfAl?5IE+TwKN2l|T$O#6x& z0MNH@g~0zL;-75)>v19fPsc^U!pPFd(Z>3p$0tfj!xlve^Mg&QLAzz7cHE%0At)V+*b$&;$c|9*0{+{o#U`T87j~j zq@>KUq(Zr_abqt5;pTQ?q6~v=$NkBbrGWmp%<_!LPUPWJqnLckWo;m*EnafUToCtc zVDoV*Y;N^ze3M})H_ax&MTdHKc{bFKEBxT9e0^}PO&uym+L_2b%3>zK%4i4~rwJu< z{ZOz(a=t}i#F!hszu}XX;BNO2i}Q4ZKrRwxuXJ zs+4jP49_ZX19-BMF2H4mcj>#xC}|AaQopOPX=4%GLV&I7vXPnx;^sk`SiGoJc@=5f zw|a3B+x-5P=>UCKj zVkX5=KPYs?Y{Z)88i87BhZAiKYR*|8uqWvANGW~kEakg034htQi zg`yAk^%OI;B%HrY3mwUi3YC?Y3VC0e(XyV$x)2zMa)0}bg!vvzumum3t3@N6r!xR) z`EA;h#fkPkcHGnFJY4dqoxu^!g%g*s2|bn;ag(zUn#cQL2Y!^r+*x)`x^OQDAmRO9 zuTf(}z=|9$V>E=#q@AF$5zL;-eS7JMecNvcSx?rz01-v#A^s$GOICO$-`fVKtt0o8 zW^1%1_flh@5Aj5aM?^ZNDX8&GWuDs(vwf6~Y;oD|=qlODO80#P1>F{FKpX^b>kk`fdM%+Pd`OnAP{dZ zvP=Y!hL<-Ewggl~v#lWexM$iE1`c$_SZ;Limbk=QU8x9Ck+zCZi|jY&3H)od3q*<0s5r*9k(K;Vl{OudUG~p7! z@dmg|@FkVyOUAPk?kZa+sjA0=|PF?-dzi zTz6Qo(5sVWsslE+<}HN)9Mqfi1%YBTf7zRlQW4Xhn#8Uewrgkp2;=4X&c%6*Z5EUd z!KYNvdx?a~-~w7*2-S)cFtI?p@{1$XeHh|TR>#}ENvEtq2Z8qGXD8N#>Lpf3tGY8H zSvU^kd;Y&_ieF8Y-zV6&ZwGMysiu(p+nVCwW^M4l7yKmUcNN?tG;i?aEh8*3V5mUd z{row^@0Lx<83+ZnKP15BfeC>&hW)a%z*EwuE-l3*7oU}ys+tJQGZd|Ug*2Ma#4Z0Y zpD8;@B)B}Yd9Qje`xM_e>E?!oQ4<@tzj~d1-G16W*?#SGIllgUxu^fuexnDQB_^n%;5amlz{2$O*Tpp-D~EIQiK36s&gj~l-xwd zWD)Bo_i1ZdU>tL?$)wQo7!N~6Dq{oZ!W6lCrhgWb!aK-2d5n1}3oKy3E2F~Y#GdS7 z%X;RZnlXtL;@Dm-JF=+KbNa@$f2sD#7^JI@E$N|>*~&H90AbZEWS5+kMWflI-J0+u zt!yFKLc`g3bn+xBvW(DkvYz&uW3LGhcLh;`i?Z4pr_s`(m4V%;-2hlf@csLg@d(^X zyonFv;gqTy+-(M-(+U0hLIn_#QdFEo6`%R)it_Pbd3|qb-P8-~Lo#h6mS5g+r4Uxd zOJ)+LBNJBerptCCuTj@{Vp(v9jbIKI3u(XFL1)}3wlf)HZSrI{%!0ABy;M{AS!F`X zqLE|`cZL?&=simd9t}Rdt1{c13C6MH%8krYs57Eze%0Vi=?U`CT!02RL46dbQdY(^ zS{!UOF^GjE*I=b*oWAk7a#61HBINE;+i{!!QTmI(LB4`6@P(GcG^b59Ld&!cZ1gWG z477<_+8!jGQY-m5hAdX5PKV8F%EEO*)x=P`9XQ8Mg1FdCte>n*LD)(qt%ocoX4^-0sR?}7SfyM2>lIySXY_+ z<|t1(&p*=1Aaknvgu`?VB{!Z=m9|kSdRu z;AN>7-amzDth{4Oe)Uni$NuJ+6b?G$53d?cLQ5gfB6eN&knGKb&YbgJt?XrbC+6&G zK5Iu@kyEY3aoEaugu^F!+lKRFT8p!-c_^(8B5}k|jY($o!Y3B%jon^T2V-Sw4(Yx> zh#pOGrsf%D9F!&be|l%}rwQBRb1M$2*wCxtSs5Ti_l zYKyX#)P+6Dw01PuSZ?a}J?FX6+e zok(%ZrigjKlvJqj>d3Ohuo6Oa-Bt2M0R8hj0PS=2Jp zS|OfrZ*k8#7wTQ_r7sKJ_(au@M;#F#;Dsk&k{6vcJrjP|+Q2c0A_6}Bmd^fiYV zD2IF&Lo5gb02zBf0LWDbQ|hx_&3(_EeimS*+AnJ@PqB3w86Tm-7H>(=tK{hmclKyClJS*>EZT{JAuUNfe`ucAW_RY6&* zj-tkp@5&p_xTE)zH^3!L@`Hi_Lpj1GDYpuG_D<=Dz&mw)KYu;MZAJv-I&rrY-c~We zYv|R9tatA0ptN+;^xz#C5e+Cb%-71~y`QCqda}zxNaNmO;YSg;zuD?6BH!*+h~%!Q z;nFoAlNDl`%x1|Oe$)F^?eL0V)%!#2HOF$^TX|ui#<%Ldd8>QJ=xRH6hkx$6B;#4D zXoP(gKHNLz6%D?nrFN%=^)6NYJ+tyzBdp3+mwJ-TUUiKtg4|PMi_{XTiDqwC#NqRB zV^1f8VfZGEYlvE&#ieH^)wE8b+2(Mn2Oxn3Cy^$ve*F0*6+l3K%5uR5Kz#eQjqsm} zI>o;$>Ob`y%nSr=tc}e~ob2@+ZT=p~2PIs~Ed0P6=^1HvqBh!3oZ=kxudozklievJ z5$neZ$7UoMeBNKqDw;T@)iSxCxq~B)e};G##mc!jH^$Y^*xYiM`R$oa;PP~E(sKPR zeU(jjKidy&b4Bk=bxMEAHy8q3zO>wn8w+1wOH@iUi}3d*!kcLbp;83_v#CctF{U+# z_jhf$nRO!1kqzE;N{;!XE(82O64GoPmLSyV-VdP~TpZ6h?!x*$<9|Cs0P+@)=53kFP|hvFbb4TP>C=_52zc$zH|~REx#f>165@XRAzh7;eE$pHInpaz-2Hx4jl!)-FL6 z!0!bOurS%7+w!kI6G`gt=?1FhoC?o!sDJ!0Tzp=_53n$>Do)%=n5_UuR3C&097DZS z$7lQHG6gwY@a?`Nym@JJnbwiL-ShhJ5iHm&0qzBP{uzx-BO(;L=u@1V+27z}!<>#_ zCh=J4;<(xZo#n-1VzC7HMV+Ci7Epl7L7VI#E>LPkT1in+Y$<`R6H+Alzt=X*afn{o z%%i(mC(InAD&lIOwBf3xMW@{pEh3Ri!W_tS_~w~j#^~!HI)pe=*(Dz7`~}<;-op*f zmnv}w@}GeFw~CRVo}-b8jlJ7H6^Sf07*9o&w9lNj-=mV&$jHd1V!(T7@o7A{cZd>s zge0_9nBRZ6DXzeeNijJxB}_~DI%{-jT~rINY&AW|RVr7YH7&teL499WuK0d6(Be#R ze&t+oHSkBhs-pVAXS&Pb#Q0bmvOmV-I`ftHW9Ptg$AwPk&sIq`&@AO+@D1;q#kWuN z-ky-%z6e6o=NLrmVSvEp(9_Wo#NFM%w-4q(MD6&L&v@!jt?19bn4g~RYSr(m)$c;p z?~2v$mAL#PpWrtU-@MhLxjKn_J|((8dcOa;wGjWEftce>{qmvn@s{|s@s{p5DeUFWJ4sRPy7%WAjO1><086k?z`K5pDRfwnejJHe za>~$MJ<0cEiKL0b+#@#uWC1{$D&dPd7Wf=G;+}6Pq-#PQqhN0Fr|qR!B{dQt+VR(P z@xfS}1*j*~L1-LeYdL=s{#kg`2)JlewUD{t!(O%bOEAgDt?Ig%F&K8ZnY+H40ZCGK zfZ=wx7O$M&cw?NnR3StZEy`FP=n@^nlCdxv?wttn4jV_8Z0uFAd;g%Sb z3?7;WTXK*o%_y;9GO-6S4kxFf+m7aaR?WiHVYWEmo&pjd_+D zC~Ze1@=b6!FD%ag+b<5sRZJEj@Zm^FGB){IK^kDcT9^z2qZv~!C&gjuF=Up;>4z#u zA+9Wa*SFz1S26$&*Cx}j5kdJ|N`_GdV>TOjTDo8&8}7t#t6jhVrFAJG+O!uPaKzCs z$q@y|JG>Q5KdtS(4Tyy5G$VjPcqHbUwS$M9z)+}0r9`zfAnVU^b371r%Azxw%ha@B z^kp@f&-D#M)E@pdyTyjTGJB2sQdKZ4ZvmtAbY3PUu#`0}JivluPY&9`0Yy2#_AGH%HwhS~Op?F~U|e5!n9FEEz)9s~Vj(A$k=8dt z<#v*_NvVjd)K$oa(SNo@BIci^++zEXCuo}1I0YNaLUHX`X7oL2JiQN>!jwE}A-<$E zZ<1c0v9HYkXrCN|xN!qE|E3UOY(AxU+z38s;~))o%$~|U1`hi``P}_rY!B7NjK{zk zuMJ>Qy$NB&U!Ty$l)Xkptz;u_&CPUi8~L!ALNy0=nVDEmwV!4csQddB>5iQBjfUQN-MKj~ON_9hqs` zip_y5u9Hil^EF)^V8e&jA&9Q0X6#7BoyaxL;1)A%*!Xr_N?QBIgu}>g zfZh>NSnEb*~=v=L3aGbP(8WRRsOiIiOu z#G3839QO;OIp8NIc)5agPgg_Ux#0Z@04r$GC0z2DD|XbO^#Ba&Au7BPyBxY|^qM(4 z810gXCH<&qmwhduI%SNDL5np>z%mB>Vzszpr6$G&l#XR@Kzpl*<>F8iN9h4!=Ijtz z=E|+R*}Tqvjbj}a1js%Yp3TsP;~X4%)=)~;`6+Il@Q55POej8HOn4R7cGS};#}C?k zjO+PZ0iOH_KFS6e&6)_DK{h;}K=#vTP(Ca~_Vax*NhUZtonX#+^yB4ZsAv$!;d;|@ zP8T+_Xp?lTsYP8)c!ep^gS9zh<@aOK_EU_FG$#O;6Oxhb5d2vfWhPW;7V?c2*9Uk4 zP#pjRM874Pe!NyEUali>K=G>k)zMSok4sD=ZpLomc zaSW*gGz>CCm4Bdd*`cNB;|1cz{^Mq9ho^E=kd!w^He35|h&XMBSe~TEv;c{$ zwW~i0Ww&@^)O=DmVzMTYLk)9^(M|+_i1RD!rAhE2mJaF6^HWEfjjzTo(Q4rYyW`|} zkuZ=BKgy`NWIk9pM{dj8g2#YW)8>M0EyPR1qvf(In=#y^S_TI+ZaeO(%j>7~MfbL! zA07zJqP4=uqiX!EUiqIt?N5$RFohhDsUog zTg`RjS$DASc$`pksu^piG*NTzMGd9;e&}83bwzlmxQ#mbKU`hlhw3vv?i<%q5JJ{i z0=_km=T}@=l6TU_Hf6cg(6pRSUxB=Y;7Pt4%N%q`F4K-8VTW394ws=A1@YC&jdPz{ttv z>|A-xKB_o39T`&TS#|Ya1|Vb#Muou7<`$q;Q1JLmJLv>9om>0&q+k0jyg3C=bB^fb z^lN60{PYZlcMsN_(c|5*>ic~z3Gu~SZy-&B%tJMnPGLMUp|7@^1)PeEdnqkAr zbr97$!t^lXmQsBNDWhlYa|o<%3R9p&U0Q1G@oK_&O$fR%qu2c{r3RVf_B_H(hhFa- zNXM9^=ckTOl5NlMvlpJ!GkEfy3%9oIbI*YP$pG~6Mi)N3>46wl{Ua1Vx(Kl;&Y&Rc! zQ;+=E!t#sPfb{F2^uyrv>oP!uK&XU?{aEtkXoMG1qphaf6y<(ghz4mq`2MT)h>6bA0}7+P)Y_@k*9eGH~#OTH048q^R?@TwXCD_ zojPWvdM556OkE;oON$AM=SQy)H@QWu1RUriCE znQ0s7p8YGzO&J9B?$T(iM`=klmvPevxhbw>&I!oqj%^I!qtZ~~as8LWfHIPhE(X{xe&0}d5!7sF7W^RCkZc_S-r70^zE~72R-<2Rh|J+is z`>4_motk_Je*)q@Uh31EM_#h0JoaPbk?-AC>UuIYyua!rA256S0)3kj1> z6GXllM8_CEb3Xc;$m!Hu?U%9M`N!`SzL^Ee^^(Rk`5{B%Oey9=v={=ZVRm z`kglsDN+K66tQmaz-!m~lWr2qc3UoUi1D{d&2|fG+Z0iA#ji+sgIjf5^o4a1mvb#VETRTt)Tf4ugk}JF* zTjVBJ^`Z&)42ytYd^v(C!bwy~&Cu&X74k{QGWL7*0jKj}JL0Cr=4L!FbkT#m_$Yvj z!_Zu1BjjES3Lm*v zRm6=7(#O*2qB6<{w&mP1C5kn}(gxZ6>OYp4%9FavLBKoPa-AqmQi0uRvmH=gFzSu?qDty|f?8io-eaLCZ`yXUE}g0_ zS>-qb3P!!Tsyy$uc6ig^W_N(rpamg3b{QNDbyshBe5Mq01XMr0o zB3E9dPCrQNA^Tsk#4njTM0Dy0wh_GWEvsKEw6>wUhK8VX5exd|^aGP5&M9jc2V@v@ zPrtc%NK;Bh^MlliBD~kyWqb?zwH>WT^`JsJ9nh|lN^Dnyyxk7j>Mb}e4`TBs4_u?d zX|~Gg@&I|^tB$#9EX7U5nbN_-5wGUY$Rh7>)6~?3dnfIxZls#j^VtkT*Y}e0O#=2`OGO zBxO)^nJ4)(2byz!TEjDwdZlRXc{G36B&Gf!Q|2o6Ep1zHURC(9Xazi{t=I5^#=coI zSNINYwQfVt?B`iA2)bYFQ6ktf0lE*DKaSInYr~n|$+g~z){yyQ{Ltr3RXakRfY*ZK zK4R?S_bj}v%#H0Y59M3eq!3&MMsPgzxaQE+sTZghyD zHPRRfEGQ~vu_&S5Bm#B`HDKFR&5A9)!5F_zCPRMF%-EJqV^(dwI{m2Nm66v0RyKIS zIlJF{wCuQ2$rM;3IpJ7>g|?M`4rRuLw2ZoCkS1%sRxxBn%y$_($ui-k!6Zz z{wSN30?2*4@UWB2+S&z0gdm4PuEevlk%CXH9p{dq=W_-FKE=3(AXN~8`Qj2zWt`_; zN!{JWQ|NzK5IOkTlD%Tx@ez6cS`E3&W&=Jg!9*Exrx@;=ibUD{X5`*A&>ATKl#N(* zhHiK9j_#(8Rh2hMPT?UMs~2~P!BDQUJK%Ug75qM6M%ht*A~HG6ngi9cP|$s_6U*Rc=;pQnGo#Be?-co3<{D@=*u%sndlT38fn zp(rr?8zJD4BMW+MqC!7Su-4_M29i#};Otx8^-q7HQ9l&m1lm=`b5s;=DtR%2s6awu zOyf`Bzr;Qsj`rJyFR}0Y*R=8f*_ej?@BShNrg~=94tg&C!B+N{o$oJ0+uwGzf4b2A z-_Lz1(~kBwma=-*MwTRuf4i6zc4SZl5I>r$cr|38n|{c8m!VDE9tgQDq?#5$6;eP2 zlNYRMuQ+F1(6(u)|1n9EmGorbdlkhpNl^|XI^&(3x}2JPnqv3h_x64V*G1WIJG5mF zxo9<+^fDNCJ=V=4+&R)qyDkfc#G+zFWB2kN{2F=)E^S)B3Pem$ z?D6{4d`7Pb>M`dq9m4;KvxcGt35HrYph%`lRu2!YB>v!06kz6-I=lBA87g0&OEXengwz*b73d)JVTCLv)vAvWRE=E4dI@ihoAwqlqMHZ~SpovK$tzl8y{ zg8Ju2;G#H-bK@yPB2`6GkDORg(k&f0Wg;YRdK}9_@W4LllTD&vM=#U6DQ7#dTcNm= zeB4_GgUsBh8zE7ek0_;weZ)Sp+co9%Y{#J&(C=k%F}Dkl!mcPFrW~tBmNgn95CR6l zW$u{~8tV=3B0ZM_EO^QbtT&dd?Y=ievy^UC%j?8HIYbV@7CUjg0ly*0ZJ7Dw`K{}| z94w-$!Zf;Ze~M}iEs_~cDljSaa{i%Tmpt1h@qZzm^kt{~U#`)Ab=Lh4&}8}>O+}5b z3V_MGu`HrrD+|rE-4|}v3X1d~Fp@$QEiG#j&ht`V$ZaK&u3L}#tTrv1dD};@8)N_G zj6)aQ$i}?(QhkznJ<;6b{rL=~3pOK_>dFz!NK<+c$3;a}UMoeTPuX?V7hAtwk5i^d+F0l#yg4d4)1)U@H&j(FAUN64HipzTJ1)OzvPtMo=4Pmb*#k(I@) zM}yg3y%C<(O?BGnoIuDr>_y0!exbajsHqmo0sMD># ziq~egC=L!)WzBqOuK4qy&6|Sn(#2V*5(g{X`tVjaW2F0TL01}X_#60z7G`)W`V9#Z zYMtn!rgniGZFq#L_qK(tc<#}-4zRFF;Jh22+bbT^HiZoJl!F!XcdsLs#EdwiFyHcND}#o70;>nX~QV!iOrDa&cKZcIH)w4=8v6GwB% zE?}6rww$Efw>HSdo!}b%m>6-{B-CyIr6*pSM_TP*HvQ0SS#b4m+ zQo_bepy_}*1&g-~aeI!4H^0~JRaQr21wcy~87;_!N4%#|$x$3oxDE(TC3@|LbbF59 zc<<&37-cq@x&;s_*=UWDu~DR+?R^Nz;45XV5^@H{GQ0*n(Gz-Jk?gY!bEfn<;c zusnLRA^;>y`JJIh8&g~qHqnf6L@{Kl;|6%Y3IJB-Lh}KM}Ot%tI0%QGL?u zSL_N#?)|CClN(&1d@$0yG*?9*QwJB15E_p~rC97{y@e77q&A5dMEYNSu}gftiDqX` ze5D^ix?G`8Re;}@F<64kcpV^A9#G#S&bU7Q(LsdsI|plgVf*v{7Pc(^g{_thiX0+u zOO=)y#$VXtRIbv^3JdG7bfYq+`;!gvz1VkjnYCEEj9ppz_2k}Ol%)OzF36kQa9hfq zUf{fxGZVARaVDoj%lrGu3H>)x9yUE9et)D*C1dF{4>D+q^jgN9r%_B*hFa6yLEXpJ zoD&N@!DrYY2~1YJ?y;?8+qc@bP3KMYC;wC%P77|F&MO$Lmq5M3c9hZN*z5^i%E;Wk zYWIE{21%9m&XJZZ*9n;P=?b_y+6O47BBP0j-++L$alP*avpxeLQ;FW^VA-C-H{Pek zdPbS7rgg)yR0>dn#rt6TT^nT)V(d(Y)Oi!{*fBh4PYINykwA@+DFWZ~>t_yBWry+Y zkKdTYn<(gsUYr~HNd1msK4B@`iDIWEPMx_Ac~s)ES*Dt17#yrQ<%IH49tR1BDBw_7 zgbx_lD)iXxhssNYqH=I+3u|d5d+QjYE5Tq2(3voqYYq4K@G)IY^{`@2?L(3#8H#1` za*S=YS2p@d(Hcx5N?q$v8?}NB1t@d4-ajxSi({z_N72HQ45G^;OB0tF*f%kLzJ>>B z>QFBHEZoncd5cl5++%y6mu!Cnh!It=34R!rl2<4@66G)92~)b0#}>H8ce;q3N=|d{|*HNRJc4_S9ml5tWBtT$jPqw5e4sl%*MDHjFQGE=d$+50l`k6hOGY1*@4 zTobDufjtq6d~}z@UXuFf2M8>GaqA@e#ckg|xrO*&+)l#+|IIBMB8#8e))%)<81H^( z4sT$8b-wxf2_k=`Ti*X$+_L@`xBs7Xn+ZQF0QSXgE~a<|*>{<4++y;DzBAY~PTCu4 zY?~Zi;MC_`h-bNBFZd(A{FB86sEMTtjR+&Xf^fuI|cLliPnPxUIp?-)Y(u4{^kc<96@G z8ISWr<^SL_a@~27?Xk!tZLA-T)hI*{{z)4;SmA7;MdBWtry9YFUwum=Ysa3Z5W(mh zeajRsRF8Sbf|h9a=c`Z1RiYh_5bEBt{OS{oUJ3m2iHXkmBD$O-GEb=HC4PS;T$H~O zE|I$3UkNupbrd6NU{y10BaA#5xM}f5+)pXUN)NO;NDq}(6|+&cH`b36=IOS#R}kj6 zYHrBJ^*F2`^b(wSz^l8LpljpM;TmAkUlF>UPH!AQsP&tfiT&k zgh}f8H5{Q|S*%lB@urGsT)4;L2SHtZ@W#Cb^BGucAWir=*P%}EAJnBZX(7k%0t(R` zq(lmQ(rY{>E(LpUU(N-OQ4VI0QLYA$QLhqDa0A2Ni*TNOX!X+q{KIxq2TARx;|kbJ z>k4mbk9K+lF9yg8Dg3dMG3ln4x$mK+z8S5;x{>JDK<1gi zf5Lw7=YVj{!QAgt=ITJ*Ysk#Pl08h~X)Y<;lNVn+!~BIUNLe80U+Grh{}#4ve`71Z zWxc?F$!pzF2#2*sh3SJsjVJTz4%4?Umv4W({-E@-;e+1PZucRxUD|T@lDYF;J-1#Q;0rRGP#q8G z>re!HD<>u-`s}|b7M7>-WEH(60<>-m99=snMkF|n9fGG%RWr&Mm`rXa6_sxjA|3*M zX4K1aYjSrz{WLezW{NbAI1lz{9ht>OGz?`b8+siGsH4F1%gaFHRPpFcY}Te}wgiFI zh1@Zt9rxQ;A~hu~OQZD`p%vy~ub<`4e^X6_(9h9p$eDW1EO{F()Te6{*Wn(=iO`ac z;hwDZMoOqQQ^ZYfbEUT0T=HqzZf-}M2o==)iJ2c29rH@qjJOcBI5ktd4@^-_eim{o zd5CI$(38fbh9yukID5H)-+aCWGaz3LN1IQL^^m=w2{Piv#kZ7J6UE2S(N z60Xd89+z~dH(r$7lq5!G_U$s1nnfM3%dKp7nI$)NQ{E7cK8(HwhZDGBYo+%dZrq{9 z{o;SD()i&HZvCE^|6$v0VbbZa!&ZF|tgz#luF<-$xLENT{f|acv!?4~P+&W`FxSs^1sIBvT`igC{`?Us8)eDx}Iv)79NM~CRfd<@Fc0FB(s+9sJ6OZbA7F9PP2yrjS%b z5YHwLx)qJyT7Nj$Og~jX*T?|XPN+JOVJT~V>#tjc&1*>d$aRa3?43P7N!OhXj@5nc zJcc*~tYJ2U!gENpXIPZ>;PCZdvOK>i(Zqx84N|Uez39&%>VI|Nwo}rUa0$q3@#2)@ zi=&C^B>+H@JWxk}@EV^+X0n(Ql+B7T!pS0GNR(d%?w!KL#*@J z8#}7N>e_0HQSQ5sCJ4oj;KkWGW&NSC)d7pM41e}QbrFuxt2Tyo@2ugeBQLzmPK`BD zXXkO)JcVU85uQ(HFVMb>IPojyc0+a-KH;R;OAa6^#?2gQ4SN>BZqJn zC_KR3EPtOYOXiW@=wfKnIPwmCOa~upp8_t{F#%%8mvIV1;(!YG)+-!;#eko}U~rxk zU2Y-efVsT#Y<4%jKMqCWrEJ&p-7A#Fi%F363F#&O0cuVE1JU49ZlnzO1_j+)555QP z&wcGMqkE7vRbXttQi2Oy%9%yN2;0ds#9tYWj}pUL;VYvB{KxS+&HryH>7TGRsknLK zD&c*0k6le0GjXP8gYfE$Z%oy#Z4XINO3fD+OU=&)1~)2muVq;`TRQJw6^bjBO8B|U zCMr|x`-yTw&qrK>Cnp0Ftu8lENGSaPQ$!sCkwbmyN?UVIur&CHefNCC>v?+ph<`xO!cQmQWv)o~+^l!JV01_jJVObEd)zUsSWy-Bn8HK7p*ze%JY0}9n4e3|s! zfSuf%FGZx~?wD=+@?h#$=Zk+R_vUkcAe3feAidocea9_YN6*4lkNJ}hvq$t>d!UVO zPiO~;rhkW0#CVqy=_Sh@c`mL>UF+rZ@G1#Z`o=CinwiPwf ztc|hw8`@K{SfQo2u9S6&>NG`U0JB^GoalkMj#(BytG;`oaFjVxuw9hKl2tio9Rd@fUx)4Fsh;oMt~#mCHcrGP^Y-1? zVU2_R?lhKxto9b?!*Oiu7d*Q1GT_&Tc|;fYSJGX|*C+U*_k6V0hHyT?jXVX^#btee zYSE#J@p#Frr7qYf(exc56bFv&MFafqYq|03L*kbS*vviP1Y?*aaj8a$9Gv_@i-+em z>+?c>BGK|Xsg;c~OeDk^Rf$9zDyA5T^W^!^&BP*gdK%#mOzy=RZNe^G&mWT3>J`-yv+>*bgO<%1> zdr$`9Cu+jYm7+7VYehJu)l59Yd{7tJ*wtL57Q8iapESs0Aa?s1SA zpz!rFWc*McEk`88Rgmoh8NJiCvJrT6OyG=ex-4*+ORH_ zEk-z1i;`&GSVE)0o0eKdBv&WPl(ckSmy2GZ0s@`|#zgXCWN>!9UqlE--2yg(PIY}= zo=Zwvhb(fZ&TdLn5`&i$*;Gn^c8X+N4z!?omM#=aO;l*Wr^k ze-CqkA04kTifNwjps^xGa1d7g+5O|G9Id%xj605f5vO_VRm5H8ZoQ>YxN;6Uyc5>u zJX+%gM(5lqbb;yeY*6{gfnNJ0GyU>ecKuw*DeC5? zhFdlfw;l18EEv>T)z{T zyysSEEflmPlRI~zlpb>1M9RFzKuaMaW2o?NX`n_+AstKV&2%10O7Io6E0SH7)6Q#+ zr;y3*zrg)rRE)PByD}^EgNr@LEpshv@GYeF$B&}@W}*50{zuDJO;UD0VM?0g z)=iS+)mBXy(TEh|A@T6UUJr#Ehs;EwrlBixjf!6P=hnf|%` zJ7*i99iasU>Mz4`jxzkBBkBs7+(`;rTPeRETDT`DHA-oXq9MRPv$vcu9qN`ZdQ{~u z0vL*g;)Sx3?3Z|@cy68iSc~lo6bsx4tAnPWyzF8^uX@qPQUx{Pj2ddB}*o*RTfL#qSaBVQ&tXy3{^N5HWrzT+ z2rEmBKdbon4IZ|5+@V=CemJX8o1@Zt7#=_#tJJF#+5=UWQk!Ilx;YGBuR&O?fYd0D zAZT2~<9$9o#w(2STi;q-h#*!DlF9)+J&q7j&gZv6g*)Jzp{PJ`WZE& zAkp%a`tco!CF)!wcoFb;ZJE9P`v0ngo@u_q>hpQN%)zo=QFjPrCqD+^UFvY3qZybNg!QMw>;ZmTEarK|xo zCmtFUN4zbcM6{zocXa+Q>()@F(8q$-a1+vrS;hbc_S7_YV*K1~ggBsK0;S9t&E^`N7_$A4Ydqx$Qj9slRK8S4Mo>j@b->Y4p>*6{Cfi3*e_>Jrvxk6}Xt zEO8)MVD4-vp>;g#1gSD|K`kP*6fh|z%lfjkGx^y3Ru%_0Aar?M9j(T^>wIo`xjDIc z8C{r-0_~T$1^q9tou-t>ZiI)=smozYG02V{x8GgYUe_Kw@7->9FQPHuOqJWCsc+ce zRLol?M8T?;<-)qS*!I$~Oh=lqs~3>^S-HqpLTm$~TMk6hHVgOgxQt&6*u~(%>%2&eq8n>V(Z9!jHWo z;l=FR{y&s`V|OOpwrnTq*iN3+G49X+up_1I-ghM2j505p+iD1X(TC2&63{ zb=O%3MM$XijPa69*5C)iBU6up=B%exB7^Pupi`3UyZ0UIoI?V3LhVZVgl#3~I&P5J3t;sD&HsjMNQZ|>Y z3+`0BVi#)S!66PI@#Y`xMPh^*e1q^TU&8&r0VSAEjIP;BvA^yfCdlt$+XxGp z><}=YNK-QjfLR<$bMXiLE!W(~vzy9*BiNT8UxJyzDAnY>oI6``jJ00&@+}#N@35}W z@Nd8i)|bsLo`DEB2gqNPQ(eXn%@DSL(%(opU|)fM0WWx8bZsZ7GCABiuwA`nf%CA% zw@D?GFv{h?Fe`6O#JYJty_KJT&`+#^D$b`SH)7meQS`Dng}CDt#zLmj_>>y`h2}U8V)Y>?z%NMe&32pjYeU96$O}6>Rf5LhazZ%_ z(rc;V$<1oRCbHz9P#V9loYFB>5VAo#%YbZKMvIp0zKn{ai_Ju)9X=mTHx!EE`*#mG zr>N68agC?k)IU4sY)I8WM9Q?jP0H0dNtrDPUm5-CiYg08sl2g_tUj8NbPC}k38O|> z5k-WkwF0UV^7RB^TKJ>tLc`W*DODRxgsH;;_8CK0lX2NJ+I`6;_r;Q){crWDg_q>6 zM)kRAg6K)9#M`j(A!nVT!Q8%3siLJ<#1c(8TFzwi0=G~lMO;OOYgwA9o5?fn#Lw5o z&}Qy){hb-4!*gnma@phEgA7cE^RIyFtpSgI9;C4HON_o_tNHZ<2Gd9dUju7=!ECKz zR}?E%qD(BAu6B-T{v#_`b0@W}4((+%*-08C0nM(qeU0)O3hEds2Oslv(VW9pDQYVp z5ikzZKljcQ$rQfg9hC~wfJEZeo0Z-B>k<^F#3RS%ptdtPF|O2mP5&R&O@fTw`t!6@ zW^l0TYlmuD(hL02&bkCEPYlMYrjV#b*4fTAz8+c@Vjt{X0p?U(yKoI@Qm_7pnLduJ zCmHJD1IMEbu&cy981G#IGQLd2Au3xB>FL;)7F!Hc$LYY`jtDfk2oaZhfM9idqj4qc#HAZrPn3+VtJ_WopVGFo9P3U>ZFqNr42)q8*1!tk!4Z2iut zikFe7EYQfu3px?p;%*dkddsutMC;V<61`f;J`;d-THsre)GKF2l3LpQ7M8&(1Y`Bh zZIAS`wl$H-^K4KiIy^yumtCJ#SfK2N$qBc*`QdGD^`-)`c-Rm7q;a*bkcB_P<#77H z^;1m}0q&sVj0#`_lwz~BBV_AkerwLNLF=*-BFocpV<){NS}j5AoBUl*pq2{_-+05#`BaB}BYA<*+{9tVuQf-ZH1G`Orz+G>}FtCaf zGmdcbx-SM5CS)Zi)y9*)O_TZEIIv3S%zoX&xI}Z?*=RQ%H#&{p)E5Iuih|RwL4(Wb z3%2rlbUVhdz-2z+fYSZE{j`p-J@{)4A#WfxX!?X&D}#{DLw%5!&@I%NeB^Y^UR4Cw z6E4+G4Jqg2C5!*7|2vQ7tktcI`uK?#?;kH>M~zn@o&zAug1yv8&Ub|HkeR!DlDS(A zybN^M)|WWkY}wx}Zx-lw*WGlRDm89g$97#2;5yQ|T9N8)G2>b@%-#Oxo-x-(1K;Z$dt46>GbA`Er5pwZm{dT5$ETRHX>;x|~Ph>nJ7 z!?3frR#EMwJ(u^B!v(W$5Ei~8`btj<=e^_=xX+A~y~vai3E(`F=G$Xr@13VbU9ASY z(rs2%X+gIp!*O^tSZ)Cygz))S@P0lJNRAK}aC)EmgPsFeBK3DaGDxPA zU30FpY`DF7o{6Ew=*&|ZEYs#zd-+n=4Q>M zgPDBns+xWr<2i7fA}2Dd$KMU`db-@WyUaM|WWSH-{{8~fLu_U)v0vzCsG_f8+%#yL zxn~>-TY_HMzjLKiiT=aTEK$~^5 z-vq@2HZ{TE>+L+EH%)gNF1f0iN%4zk$x0%%U;GP!J2{yX714&gV*iwAfj`lQqHKH= z8c<1@pwmqer*;p}X`;Rrw7DWlhRc2W>jGm8dvn+-_8=igWR6Tyo4@@05dk$(70V@r zbgs$x=(e>MCA@(fJ_buwW+PcUmm~|_`lL*BK3bwuWSnc7a{0IF;Ic zJduAaJD$&)%a*jf`3I`F*IB%QR^~!R3H3LCFp}Uko%O&jv&7N;fJ)jH%u`=^>uGhZ+-C=k zV`CW8%f=9_cFhe(9czTa^5EwH_JDYZBD=LAZlk!D{sCgCPxuIHPd_S_DrN+XE)(@Z zN3<<|V%cgL<15T`f?@4b(&$xA(0ekDa&!}{(b0!5K#Y#=JUw-Py4+hCo!#bM%9pBT zW2UsM?qMeHPl&~$QkjJ|mdTq{+kzWXP!AmFbb5>p`tk(rm*<~u;)<6so$hG@nT5Z0 z89p5pMyr?+j8-u@V|YBdr<0D^uSTY%wU6AjE9L;BA3wMAPGzIcvt6^$5vI%0M6x1j zqBz&opc%-pYzeN7kZFjstQvxg5~Z8t92!)hm+6GC`Zxqc@$5kv8ny{fV)equyG4R& z7M51*aq;$nZFZ4t6kt3k(R9Pv;z68}$ixLi;lcq+bHK@13v-|m`BkgmUtWKzNVVly{6vToa_T1-O6lo^p47I6(V^&OkU7P=!{4}$6-eTK zy8C^sihcR5^GU_PCQymoD-&_>in&A|7JP?diC3ZzNr*T&#J;A*dQ6Bl0}s84(!V6Y z94z8Ky8Wq7IUJXxfB%n9yULYNT~LcYi)vC9Vs5yNeC_$`gsr`&NH0{eAhUv4(l7FZ z^eqEKglGNf$@Ckqhinehr;}Q`ogasC%l6x?JVCD&^N84Ohd-+dxx%Mr4nX3GVgDxyu=)HIH; zHm&SRO^%hZ5uH?8aMaP~TQE3HtVU91#H>j>M-e7EmHXnz*J8+P*;0d_{pr|76RF$L zY^fb)R4!7ONH1Ds&2X`wblj+FR#22f@}=CUcaln05n`AHc|cimRlv==)Eu>9#lU6) z5_;)1L!6GqC{fSWWUM(oC0ydGAxWNps$5AiMq$HKbUJKnyH*6#NLDr1u=5(SXgn9F zZaU8(J+w%$&-JWl70l6F>cN?Z?ES50)Up*ExB1~gp&}4>(w^hbtpjKa2msUo+JbOH zY<~C9lAPhybY#YaxPw5u73Z7nvTS3Vju?Y2LATeTJMhyT!g&&`pjqJuH~AxNhXDPx zW&!CwHEb&|vQ7UY4W~Cv^*)nthu@&Gr?#e~{_`uH?X2N3BhEJX<^e$C!ML^{G4H7% zU(N&e^=ejjVP!hRgZ5_QuB@hSs?9FYb|pDi1i>#JKZb&u7wLqGqw6q2sNX&rjys-n zXT%_N!zIoaMUEOyDbxt$ENgU7fq{P|ryJ!%pd_LJKR-@9F`9$5AvyvlP1q)4D7uS1 z=nRNP@XPm$;)G(;=o1zY9d)@8kR0YKWb*jYE$xug^n>l}10}u)q@jmCPq@T{C`b>8 zRN6D5Mamv38Jp|JyV6_4>HLf7;FJBeCq9XY0iiGC6<>e$Ah``F7Z~H{*BDn!J)imv zsF0}!rmdMBMwG~Q}FYx z4X_jqrRY~1usZ$3=Ur%O;V7>v)|27I5+7|A-fheUZ|0lKvG7L`TE(%}OQdh!dun~> zN^d45L|Tl&d175mPPmS_wrzXX(7&D^nSKIWZwH^A@p6A!g97L?-Q?#FaQs_f6(u^+h5``En2`wKu` zB4%$^KXRe>U8KNoUw`x<^rC~XDc_r%~ z7;Za+JcPqqc2l8mrf#8(XJt$=l!8Lc@0^ruZxLFy%My`b_CShOD5xi#TeZbHNII8k z!`x7?Yh-3lVKFm_MJSF*9#}}pWZjYy;P*Bi9&gizdJ?tZ=#DZ6Ss)r#TgI*%nX(dn zU;zEXl|f~GG^J+JnT$>c9m~UCv?YtWWGIY7yg3s7ojJ`8m65uppF>4hBSOOeCuCEF z|4;;03E?tZBF#O2T>d-3A*cvj?lDSDd?hfAty(t_DKMGlWO)4ZIl{g3Iimehf#pph zMLQ3CBgL>dI-L2g7p3ayh0#T-C?ScnGEFh>@lKZIqLPu>wBVOm$~81(gHVNA$QCg2 z;+)hSYKSO24XI;ly@5hsiEt6fkdcDG7}@@BcU*z@Dz7=5mdN0swfy}EJAr=bKI>+w zGJl$#MVq_TLj0~cKmFf<- zT2bF`G2%2M<6Rhn?9~hu!IKWpO(A(dSy|Otr{Y zx`&IcVlO@5daDh)Gw?v!Q@aQ5#xF<*SN@V8g10vUcse+OS`NFs5I>()=Tdjn8j@xH zK;c`sb^H+MTW>Ruxu~g@pA))TynTHxuMaLpd7C9|3!k;B=NIS2Se@07h% zhwOy=edur_>2Z`10&R-YfB3J>lgwt5m-2*mzV8ogjVOP}59HpOW+;J-Kmm|N7?NF_ z2j-{7D|D>GOkZ_xRrj?@gl8TeQIyWSGi!FAray(sc=a8&TAqTRgY zOL)*C>vQ_G;UQ?Gv!R39JCB+shP?HGDHQ4Z1LUodGC0|xorjMnAnu1rEFy7A9Eja8 z@7DwT#&(!LaQ@ET%6>=+;>FCmQ-*txM~>4Jpe6~QB!`AjI{?0-|GF`&Iv?Na!MSu$C1h0TDt8iaqRcTnO$qm9# z#O||e!p87S4wP%nK#l}m+8AqzKF9_+G2xed#wP|C;&2Fs zPcy)bULVCiLY~)P*-!^?b+KbarJ%j1L#e1A8|9M7QT>=zdNOsV@F%Q=jk@ zU<4PFZJ<+hYC&c#3wJDip%?Ne$3R=z`3#pU^#l{YgNe%kWb1*^BE|D01In)EdB=lD z);LJuB9gfo6jrfv`QU0OqP|gh1)Fh`8D4nhNhl3;-^^SZ>x3nPUmZp;#5iJieueRz z>e^-5$<@9Jjf<%MGM5>Ox;X_fSFM35j(#Z)WQlp-9N(Uoe21^1D)#H{7+L*(8GrTX zup3YkI)Nywi-`#;dhGxoK;6l;%pN(bGfVI!x+gN1>CYOFg!Zt8L%8pNa@E5g@X{;&s423_y}O@gQ-S}EE*9bd9AHh1 z$#r4WoB07E8k;cnI^AD&SI7KV9!60iz7er20T3%*>dhX5tc5ou&U+RxFZSs(Kdn9Z zHXDM(3YGb~BC+a(78|iNb+m-k0(0w)rg?VUGD|~ZWlN(E9*X4DN3?BH*C-jL+SX5; z_^>QjID+0W(Hn=zRxgSvw{6%>T-9q)PIe43zID$3WV~c#L*UW({jNnWlTGNTZo)I%eZaF}y!a8b%Lt6l`WbpE^HcSX5$`9xL|f&bb>u55jaqA+JL+mV{k{@V zR%{LrMDq~~-iIQzF!WG)dwXcUH7o{yG>=wr#Wv`Lpigtc6aL{7f;6`@0B(G|c|3bs zTIA%^Y z%&mV&zN${8Y8~%pb5nSvqM!g&kI$fL`=yh+xfy?XIaqH9Z+>zE1g=zP89X3eb47Vn zYw{kDmLmZQ6;BWkXQS-xfB(lpWW?rX;{);I$MkpB^MB_LZ2u2nV`^${W2#_mXl(9c z>_Dt&C+jZ+U?fa;we;Qrg^IF`Py~%QV>h?nIrTxA6svDF zb~lV#4ih5Ib^!$0t=S48s%<)Gmtm>yBr09q)_YYC)`{$QUDb|`iCiICZ0U$+ zT%c<7Jf(!#VAf*|L5VEx&6HyFpHJ5?tPJGL!FWehmu_g$;%DlsDJ5@MI#@0y&Yq&D z*9ppZn*kRPJ)&G2gFU}*j@3JtBJ7PA$&$q~<$tck-Z@l8w&yy@+rw@U>hGLAHbK?m z+-PW6AMKdkO4}qe`XGI0OXr@d3@Q^e>Uc!aBSR+jR?3;9B!9pTq|MWZ!Lw}kN`vy5 z?XHqu1x(c9+ z<-TjQe^Y88t74gH0FZFqFRwZ&w%0^bvXnwv$bOE+H!72~zOZa~$AnolkhgRrElDx@ zt_%M@7?jCo76jkAOzD)|HF|r&{;PV+Gxp1WP&u3ux47T}B7f;k+%J)_H#(Ae#!n0T zf|l=Lt#u;0@+`QspU)yUf`Aq)+1vcEaVg<8!l7=M&w$vxM^G88MyPpAFY!si2UoL4|(6Sy*1Q(eEB1 z;%$|YVpX{og&D*eB9E{qFW5aNoby~a)AY9d)LEUq`{h#t&hxOPuTL(zXYFz#9YGKE zd2MH{N_{=y`7J}|$3=wS$nlju+hRDMKUe*HTSH`gpFNFsKQmw|VdW=heM|XF?U!;@ z?YDCA3eh_kceyp#`gI9l<>=+53b9(}LSxA*nc9ROVXpOnTp^9o+&77mIIQ$@O0&6)0nD=80Gc@X<#b>;<1L~jVKBr@$oj$rQ;+C!-Z*?c z)O-E;{S)t{GFF0nisLGlr}VGz-}n|+69R&;a%niG@2R!(dClfZ`+l*ia2@%M%!}LI_E7Gzg^{t%TUq zLI7nzS58Y)v_~YlYo>-pRfDoZ)mQrYXlcMs2PVqT0-=hrBoy|FPaEErIy8<7TFXS9 zt}s<@7s`*-NPUe>d$vSb%ji^07Rz8Iz9B)#$T zB+K*yzrZxm@x4k2*KkkanhzjIYJu%`(r}f!>#jd_jU=UDtCI+0Jr)q|&NpcFnRn3w zV;#=J?Y6p&uv|}Jhb%Rj!$1xK78yHC=}f9usx8NI1g$qFDT#zzaWn`VwZ3PS;$mTK z58|ap3|@udGNh@&Tp`F9xP$9>qlK@AS*Szh>cnVi?UY)*F4JywsODGRqGuPz0T5Q% zQTIqBvq-x_E+|&w=ytZ?$lYkByo;;W3M@uxJHqfo};iwT$sCpD*;ct)(lZ*Zs0fb<<4D1{rf!8oYE2c)vn|5 z6_s4Hr8>7gA@Fw5`L-f#+WWX;8!|$%7_8d1f^u`W1U2R_5O_?FEqDj{8oYEiN;eD} z+Iy&DTrM01d&piDyG*dT2hgCp7B3QYUjS9iD(iev(+@DJ?T)1ql8d}W}d|i6| zY0n(sc~tdBzPSh4B^Q=6khqHyc zfUh&mj+LSA$NtCY3;t2PbzO{T?cYZHEWz0L2^ZiVasI%x{J&yE#apvxUB4BXu_5<-EoE=V3`91xVlD zZ-tHE`>&q=9gLFx<20B4ZdUkDBSNskcMg&tg$MoI#Z}u%=nlD9fCLpu1$l>gu%sMP z+*(Rv*L^kSYS$&?5)Q>Dh16m{kgpfdxQj{Y4(N=S%kkte%aPB-_v`Hoq!(@3@Mjy| z9spO!E$k?*RaBX~#vmBs8<=3tK>n$r2q|YrS`#a2$L!&jYK)ibKuLVW$*qNu|0}f- z-$ZG+Wm%lOMOwvT3YmL_NNLR!7o?$TzQ%=%H)^1YeHcaUB29YFBTw#{8-S{c0u*E< zYN_7jD4&uLzl7Niplh294xa~MGCwI%@(@!qcYz-Y7aJNZxfim?y!I3r8S3fy^^#WS zg_?-)JY^q0);f-g*&d`BlZ!9H!;h+uDvMNQK*J9r>t51WaTJwGY6UwOpA4$sb9Qd| zu~X_;VMWzAZYI#SaB$KYb(yUo%2-pq90O&qs3a`c%5mJJK>}7^UM}r zVai3^%s8MMc7C?NwtmAvH3++XBjwEE5*Rzp1KDcldH%=b z9Mxx-+gtdkpW?}FI^&MdW>2>jK{5)l(6##**Tw_Y9{#&>;$J(?ohSo~+V21k=|2W= zi2t8Q;QzkjYVLX{OE_OT4P#OvKvTf+ph3a`L0ZsI5RiEUp+6nPfeC=;^_j%M*tqOn z1$rwjs(G%AtDCENtb%b{P~)U6G^%-;nxAaG&27)dH_K0+wk`vt>cGdbtqsu|M*``15rga+LC2gqQ&;j(8hO)+G!+`%(uFI6$6&R#Mj zOu{eRJ$ZR7UUR=FdWc`W)JIfdyij!)Zk<6YZ>zL=^7k^qDDP-+c35w5yi{#^O84Hu zmUqD&9t2_e80*mZSZ|TrU(5X`ZX22VaxGti{WU%`u6>D8cgBxg9bUU*_%L3GzKnKf zULr$Im_L?&5y_mrWPW^h#`uoz_(~{$6(8(yei7~ct=w}5xq87v2a<-+K138cydeDL zCZ886AZ-Tbl9)8>EI%l#e`=6IE2TZTJcKQnMK)auQ=o>-PccD}Aw9V?#O=XsEa~ol^6ObH&y1Q>d}`(t|DMv8t_gA_8{f_17J*KgYd~??}v3%%`<;bu@5a zO5id96;k8=XS@vE-r&0>=)LB8EsEOWKIt3LavUm79T7vT?^T~z_tDy?%|3*_L?HuJ zIyH${IlMx}FNs2s6l7TqqT=cgRPyNpT{?y^jYW6jYuMEsWK{Ca!$BNdX!n%rbrOU! zS5IsI2aT+@Emm%~UKuBKaD$elg=md>ZI%A`_ecF+EV~5Yy|Vz5qA)Rv(FVOZCqrOk zCVyOcBTW)w`6hlY-mWDHh$M0xIMD@ zXCyAGhiP~>g{Kn@uvG%}W704hIxQp*1_>8YwAVMKtC)zgTtoImQ&($65k|NOURN#} zZsdD#BuxJ3m`_=1MS-F&{f?JKP~6E{B*va4rL{`cy%my=iM^ZH&q|B3+Wx4<9J7wZ zR@-|^4(dd9bBi0SD-VXWlBnK_%qi0rg$p<`LRX}sU{di?!xrMdkfEgz`{hIK)=nX* z(>Z3P>*c5!8K}`3jh1XHEp0BUF5pyylWC3s+efQJjNG)mt-JU%*amXP5*~_LNX|GYn_Ns}mT=9LU+>l>P(WMVi{$G~&8O5l01!g9tUH+8WtL2^jiE zGF+I~j>!56aPaEkMcK21P>NdADo|GJ-WV~RI;-LD1@6X`F^ zibY7&rPYuxr0vsJ^&+ARNTpc}qTS-bLaPbWpWLb`Si`5JeltyM3$k>=FFl$AOGxk6I9P zh=9y2P;RORV#Hcc|5G5SHC4cz93v1*W!~=%=}JFIOs}dEQj6!p9Se-$8R~RV;YhnV z|0}!XG$B~uCI)z#HV$=`RvPQiq3L|Ij;W`92y^%ga9l-FCSh>t(Jw$2MWIsM`ZY>g zGKqT!t@IUS6ie|h=YHqz$)w?kjoCKmljP*R4wjNtk`DDYFLFz>(U|A;@beB9pDv|$ zm9nSWqr=nT%MNA~&s*lE^EBr{9Fr9-X4uZk%AOwKEenHI3Xee+(rsGUR-@xy=J;Rp zp&3$kgw$ST;P#6so`c)v>KjRQU`Lni#f5gWdiqw1t|Gsu`pD9o-i|&LS}0XrvvMmb z8cL`4>*CEpu&u@&;DeDEch0YlgpxrBx3o)RzE@*F${CA2aq1?{a3&UNlYR6<HK- zzoDDi#b;t9`E-+(wNt2-t%(u|P-{@jct z#M7o-F6#`M;VTt6%KnVTfzS>P3N=$D9ZS;mE19bU$(DgW(dQ0bXu3|1ws8NZ(@K%9}9tiXUw)4@xLsy&Ck~iceRtXABa6iMw{bA2ass%qlwIi64wmPiZ#?w0MQq%gG?&@N5DbFv z7t9`Z!45IpHZg!N5H^s#x^kCO3*&RqK=ZH}_(64ulPkIzi*V_J(!0y7u82#G9n1>X zZHgjHb3p&XRoUFd2)gR)CV`OpdgO%Z722!go2 za~PodjAHtCp_)?6egX?`&np2t-}K5PiAbs`p_af05eOvk4kZM*l!cf>NuhQ38jW|X0zGoTH}+8 zZyearOfi0WTe02^1&0L)(@Yi5kiJasDrutzl8`IBlRa3w?;2aEXy-(amS*-B-(Z?M z*SwFd0K-aV+1S)%wI+l!)t(K};Z@TK&Z_wPlo5;81l^Ebp)5!I z7Vb&!&juXZvB@uf2`j|fIFfg8U5TB9=Xbv{PS6zMxThGfF+`KBJUe9MlM2F>cG92?jF z2$3uX-)ye5pG<1~{rK}8|3||`+uLo4lt4tgZfm>P&xw2omZWnkTSx^CwzyL2y%xPm z>OKDzdTz&yQ}H4M2V$}e9#0pjhqT!CoEQqX*%P=1Pui66xNGZNk8q|4ebnv{2bOGH)-#o<*&9fd z(d?vwHW_^gt`>#7Md@$h(Vujv1y#yqPB9O#Uut*k86!O&g%4CIe^WNb$0Y3Psc&B_ z6eZpKa3b2yJt3+-J&F9K6EjLVZ{LoqjQ~SlkMR<+k4iYD4xCI_UXrYtna4WqE|GZ6 zj2ihTcZW5*i@ycDSdR#=62!0f0oG>){wI|8)iwEDKTJ)iXU($=i{0T$rDW*fhk|0?4r4P~?4M4e3{$l-pj#q7ktok2+ikM@h-gcac* z2`lSA6W0GeFLDmHhQ^ML|M_vKT4hTSOBvoiiQXw)~a_e~Vf65q}n=^n?xMRgX_r&lRn@?yLnd~dgY_j^o}k_<6Q*%^g6 z-f%p7eNA_hy^rkh{RQ47yrl2AHNn_&t4wyW1>gI8{R?II6BD#hKZ;=D5Gt_}u`AG( z2xBBDKs-e&a+k9tO^S(Y!1ShGC1y<>)Dh|!brS)i-MDZ*6)o(<%9N(q+RQ_}M9S0J zji)%3WuU?=x7g%{&Z4p;t66e>5~At&M$<;UBShw@FvPe`-QQ>d4lDxL-+mpc=>$e{ zTKT3%Vnh)k?AS`1DKf*5UKbsVT|B~bp#D4Ny&B`kX{|nw$<{0(JU=Iw*!TXVC5Piv zYJnK-C=k&9c)Z-~sWHA-)x6krBr#HM)7HS;UOoZ4QdbQ#U@R+ME%ZhOtS>pPpkG+j zZgk0{%7e%ISEYOjmuTnO|28u6d23Rz?zJYK>Sp)pw8?5+6jNE*mwJ-@V*xqa-gQHxM6sS5v7E zfJfO>xFg_Iu+tnGnf>{eDJU~U=>+;ML%Awd`6o~g$koTlDy3zuY0E7vh<4Kg<3tl4 ztF9Ewd`7VfK`T#GXfL}p`dvG=tGRS}qE@9T*vux!&e#>gQM|jGE&`-agx=@P&o9j$ zY#E+!Di~pP11~QiZaj+hIR{IKM5PBj**FUO#j!W&5X~6Q;U)68cQmVdsQywMbY0ClPfNPE?*Cwz!l)MNzKZyr_9GCj%aC1buTd$4Bl+X+-QzPX zDqL|=^LQ3Gcr*`Ob*6$NAfL|h^g%jQ2a~)Y)hEBhT%}v+ue{H5Q%=RFPTvT!{|nQg zarna=K!!VnF5c_sU%`DZc1dO-&R*p%siw{nVISRxuAmEE&^No9V@WT5PN?iFtAU;H zUO0gbIfp@Fp8#KQnu+!u%g$w*CgNNxzE39l=8?Y7zJm^aS)E8K5qQ3RL zdNn)rD_-h9R(nR5>2~Niz&rc|nSQ>PlORnG{M2oM2b#|2Dc-`MxZI!mbd!)0F3^a_ zo-VUv?kFPPPN(|WS_lNrYPs?V#Zz!120y&+KLoh>{*%C*;W7zN&hxnfSSGXn*y>YA zxQ<57$l~)IzY&dq8DxLpm)AK5xA{9NsU1qd#a-C(I3wT z&H`-VbckfNy!S60NKFrkJF?^3}7Piuc zwUq=g?%GFNBru7>SJN+cYlXJE${K}D7X@@Fs%FyeCp7>5=B%J1g&*&G2}bgdOR#^= z1^oAu^>6L0TFu={X~p>~hMCEhnVXm)0YT#j5O5GNF@hWNV9RVkaI;=?9Frb6ZXucZ zE%kjS8MHuFiMoZW;}(gzea5ml`Vx%|KL(@#aE!Thrqjecs|zyORwDaWqqC&Pw6`r9 ztYOO6Tj;g-^S0xR&&;bx?$_4{9f(;W8F1LHBcoSOP$=E#ttsP2JB%-4FZx|Z$O_Ac z@edy_3O^(-4nO;@{5>il>eSatfmc&7B6J5f3bT3ACf;R?`j!8GGYGGj_f4tZTNVJGJn)i_neIQj(+3$17MT{gq5fRMN@Pzy#yxT@CdCjU(1a!LCw-dI;c|^Ts3g>6ets~ z957X=3-OglOIJ*32}fn5yITt8)QHI%ESL z?ac&!Y~}J!>*VKF3Hpt7*7pl&1ZK-+B!2YoDH@+mOS0BJ7S9W;eAds3;F%I!%Erz$GjqB^-wm0};a- zZbP-1K>`JEci_9|Kq&Z|U||qVXbn0>I%7e262r#!dr*3vr_@H+u!2{8Q>kUsv)#_^ zsNTw{*^QV8vLU-w>Xu}s?=FDJTXx$VgSc|Xe=%hkpo7U>vyRJi>7w6p(wfe z+OgXxN*?9R1d@sLf0x64w#ADBa1um> z=hIvq4tB%k56mX9rD8G}`XjJ$lr6->1j&$|lFUVkf-FL_5m=z)4|0qcO#qN^B46PL zb^*4o%!ti5M=+M_j3T#Ajl?a6_!;z}Ci|k&l4I!4#6%)ilXV3RE^ItcN=e>ZPJz^8 zEPl--p5>+47`J^5;tROq(=KGDNG%E_x!p6KTVtzI++6cS2SI|yi(Th!VCc8f5HlG%)uzWwzDR`9FCQ_gh)nA!)Lje*9&$z^lFUy|hBJVl znZ^j_$QkA(;UJmRGU}D3S2k)4%x(j8f9Q@*=w!D#+^af`&*=STxaOu5n4rldQ}7P) zoWj~mGL+%v;^3fA-;XVshmSoNm#vNR&i2$5s{d`fmYU48aG>zLQ1dH2%Oh8?c_dRP zL5Z9*%1u_HEVdfhPk*XsMZRX~iHqn-{6iISFK;Z9@X?OyKF*Ty&N`lk=fjxHKP?-)eIkw#Jcx~(6DE$=%hJU zhLo5H7NR+6^hV2F^H%GJ2=uXc#)`|Hv!G_`J!e|?I)N&et!nhp9nJs;d8MhwHw-1xRHH0DSd*Eex1 z4j5}rU;MICOl5HzorxKyrbsOl@o}@SOKA}sL;eOL_^B@60^tl`VN7vbMf=nrh+qVXujf>WIa#tl|TUsXq?#w9^d-_N& zbMXkrS+#(;-<3Xyg0+xqCbou#Wq3bnVd=W#T0|W?+awd)*z=-q zU$?q(yt+|xX)U?2k%A?gO^x(nA33+vL+pxe*ajUX0xJ2^SiQ_;fr+>#!6sVsy2%Qv z&-JNfioxVKP)0pu4dsesD?8QD>!Jy_-Wd}yL*zoMuD%Uo?2jxy;m;W$t92Hj57nQO zQPSV0J1#z@NeJ80TIxCj^yO~9`?nB+_D@@acJ{`uhzSxTf!z-{qBz`vD9Nk2H33NB zQP%whD9X#koF3UzO6bp|T{O-;ALe5XT9i&GPO)Dm%a=>ip!A|}?C>&_gUMN_&gmmz z95i{Bl6^bUR1Kb(vsYIa0Ej;h%80R$0p!_YBqn{cHzY2nuDmGIdgwq=5)p1 z*;1-BH*M}tAt(1{7Ie?QTwdyy-d~~+JJ)6+0;gxlOC3vmMNsSfx=$k0!^J(v-PtaI zHpIl#tt>m>ve4YbIR2)Ur6sA_WG2X~vo^2cslWau`UOIW0gfP4%}nuCfr^lCVl!`H zLY{AO-4&5gACrLze5XZBE?A8TTh0PP$imbk_t3fdD(>pj%EdIiyG9oc5%dgX@#l-A z68^Bt;x)Dpp*Hupc4f~H_g8^CqQ?UXH9TFxBe&OwI$DnCE1^o3sQm+Xz=?ccX0n%# zGj7iD{=+RRxN44QGeEVxUL?9%O2-xVPih^f7o=tXR8v@Uq#1_U9krhJ1n_dMa>ifs z*yn<%kzBvfZu#LHwW%kzhN~;9XPK7f#H-F+>kGpx@YNUup9G#y7|-X@)xw$mk(Vcl z5#SH@2cX;(`Y9$WJcg;pnruTU#Ysow2g%!DaGkyKeTRvqg+Llr&Dz`a5QSoKOwl~* zLhbSn#|YWngDrR}b52Ji-Wuk9SyQ7Xs&qeBTrAq$Y5KeJsn}z!r|)iV7@y_t&9Oy; zkXX7E=d7_#o>arKnQj_gzEF6|e7yddPA^hzUas@5nL-=f=oLA%O*f#-{*zhuP?iVA znv4;XHCu-#wwdtNsaFBrNyH8!A}3LiQ@T3SBsmNHL@zJa$}X;=F3pv>vvOY{T99%IC6=+%xt=jk3Z4{D^E)(v{nmBw>Eup|$wB~5Fz;Jm37f@m3?t9{VvuJ@Z~ zTOQ5Bp1~kR`i=jGvv&-xEZo+%W81cEqhmWOcCupI>Dabyvy+Z(8y(wLr_*2d-sjX; z=dFEqz4iWDHCL_m=czfLXUuVr`?|fg6mq!Ob^=8>k@pI+kQ*(`@|=lxIjA>XM|KsF zIC0c%ct!({)kncxbcQtaj1;loJ`SeN&~yCr%@os}AT%=;%QFV)zn~kqpORb^RoZy+7En;b1o(a-!oNQ|SHdB;UcV z6R`EAK~WJut2JHfzzp{nnVSP0ZmVD6R(7j-;cBL78nB2DqA1}$CZ;j8+_K?(W0iVm z;CzEIJ(DqCj}Lfk|dB~XR)LE<1_Fk(p zSjo3U4%P zooXkBS|=D?CGM37f8ckGb(8d2)a&=vF=4a0dgq*8Q*E8O3GGOpK296^#omxzQ+CCC z(dv%z>`*1j8@fi+ig~qZMqg0#e)nHgS-1#TB2nJ!Z;@gP#3(TBf0CKc4fCpb!13F( zXwF|duBnCa{{i^Vj-up9c{EsncIU|ZxgfYldHMK)`kWNpt$^2D>id^5Q1!gBfFXMJ~<9NnSfsnQ+b0BF2(D&Aa{2W zm63{{lCEJ4By&WSpMm5Rvie34jY!eNV4tFPNr$s4N!?2DRp|-t@vt78uR5lYlKw^6 zg%h6jtBPPhVE2sVkAbdUdw(E+VdlE+>dT!BM?xxdC5Zh(8^5r=d1vL1Nt8XXhbwF? z&fIAxe$dZMr`XuD%{by(n-Z@w8y#Yld1CGZp1>r9V}iTRcLB9Jh_{;4U`ROwblW?3 zbnfGaw)b}$23G{O-DL|tn2V)5i)R`Z!B$(b`wV7Pm>eSUh4~B!Dsbc+!NnZW7`f_o zR*orC?dBu3Ok!(ei0>3Gcay!no>;|U89Gf|YrdcxnSpXNu+18Q5)7S1c`@O?jtzr+ zSB?(76ZCylb5FN!4aY^6HSap>LJ;ngq0^9y&(lbH(^Ds`uzJHa;Tvv`gGccj+rxI4 z`vzS2bv{6a^uJ5oT{XNSe9i$AL7S!#UZUW1lc1WIExuottA8wPeZmCz68_+`kFapa z+>9i!4@j!Ky-@=u^zRCcK>QWD+}&XfeqbCK%Gj!}6uWf>X8lW;Tlp@` zUwK-SAu4mfHAhi{RP*CMeV~93ym9UD4bykqZV$lYRm)OJ!M8oy3-6^i>Vg5v@pI7mZg08uO#bL z6b$S3`gG?ed_iAdv#w6+{$$BQbn@_LvJt7GZ znIa_`WfW?+E5ei3ZI1MvaAkYb%RkD%?WFnXBfE%~#T}6>Xou-Zduv3M?u2A*xx+zV zXR5w6SsNB6PGy0bB~x7hLE+odPs9$7TPRpIn>jw0wWeUe!<&LLwzgu-&JFAg%QVP1 z$9)Z2Ojk;68Zs2KQ%8V#nJur;c51^JBD1ol%b*>lyDcGuy3I??KL5d}`Sf`zsmDE-n0~Ghsh2pTO&uo}mH`LLodf!i(OF5$mqaWp zHa@pBhgx#Z2w?PmU=%96=1R zl-O{lyU*)eWd4M!iknj(HT2s(e5a#h#!yvoO97aX~{M%SD4R6 z_Iyg?DRx+;s8`AM=Dh50v}h_T>n%Zs7yB#bK<7>K-3m*GZl|+KOK(Oo-%nkX0PL8* z{a79ALCcO7K)h+KX9jxKZ3~5L%1v5$PE9zJQKku6|Yh}7>rc~x5f>b$Q z)|BRB{UD#TNu7f`2|)0MO82`ZYV_>yjaK^!?TvO|Zu(d>bD=hu@Z>LUz~TV2<|Lx9 zMvXOfui{X3mQ%CRFS&VgZr;B^f_C8L6Qd2@M)s=Usl@Gn9~f&Gu&aizKD1qg0}AYh zYdt}7=WaPwIbjmF4MirsG$Hks-oHT-lKwG*>`o_m7HhSTQ>Why(^ltRp5=!Rl-hGnT$SLlZ^uk;}Ng+700bz2|p z$acZTvm9~9bIQpfyarOR!d3i&#hac9W{r7}&pX|%azY%c;AyC1uenAVP-2%z-;?$F zsp$MV#yfmRLnYusJ%%oaXF}Y++T*ecfY2<64=^}mX`2&M!E+g&gyP!8=cy&K?lT=~ zmAsm9)RenEVEGl^0=f-wtsIJJBukE|W3GTlbu} zeQED`3Oy!e{)>%*zuQah-%lUHa3XU8oDh^jWhIs$=wtd2O}QxUD8WQUjwvuXWwW^6 zeniQhXtV<1v|ZCrep56bNIz*(?sSXv$_E@GRdeBbDYwrk{L3SALaoSNIQ}-2L{{b} zu`k`2{7k0ypJ0ZAX{B$sL);9H6f-*+T>}g%o!gW4ltfgw`{8=P_?3IKOGR{c4Kgzq zyMe|_Jpa^DtSM!dD3T39k=Vd6NYu)lzh=8;V!w96{XG%W4u8dVtv}kFDh<|LlLv;U zGGx#5*f@ljXWFJuGV$PvtwcM;r^h^*!I;LX%V64eYE0GH%v0X9B)KB@&`wAC4GiD2 z-d6k@YkqA7R_}{+><8UFQ~txu^8?%BvCw!2KaURYv!nFY<*{W-&0f0)rlEGfrfK|Ob1RGg5ZWgApZ?|S@MVf~^^yX*x_+7DzGS|CUWysp+L{=f z+Wf2O9vi1%4JLvTYKWT~Su9GTnUG+6sn8cH*G~e40CT;mxSlDq$S7F5yR7F4f=oFR zA*!CRROC-|bFggRyAF0*fO)_w89bNOS^DSm-o9U`2DW&0R=%!;Wx<*?4_G656VC=K z)cW4zT(n&lVz!@Va7V%^?j-nnPl;MAbIMmAswe$4IKcBK5BBI%V0jCBI$!$e|31(C?}dr~TI&C?X#ZNHT#etF7yyF52%VPC5tUuX9?8*#(sPT&C1lw*q0E_qdig75ckW)KIDvRfg zZq(|<;TsmQ)WndniHnR3N!c|*CW3=dHZqYq|b)IvS}$+(ngSQ7P&1-1N}aR79uLsu|^ku~(QeISty( z1+Lm7s7$&T0iC=AAo}NUI}TpL7AxmZ)45-z<5g4_rUbT~K#7$XXRWhLVzXsqq!n{c z1}7SXu_%l$#UQ=Kpi!8LRoPlPh7gN0Ggd8G!|xlFd>%;0V>Z*DFu4F}0GTsKY`wyQ zTcq*iS^Bsa6{Nya8$D_&79nFXDQ(?km8c|qQBt02I_?{Fvv`+y?{x?NW`gkq#VUvc za;Znpb)o|LxRXNESaCBt=}{O?f+bLl3j8Xnt}>DblqoDjG}ud0RN#sJE{&iqv8tll_0vPU2zJD zEO*nda>~&I6B<0Jc@ynRZy!3K2VK48jCX+WjlS{pWi|p!hjcBL>O)4bMEn5rA5sGI zA8{+2U}tYKj(dFsv^2EqYnI;(e!Zsik_^){ga{W%o)v##O~aFd zx#gsB*}K8BytDMJM#55rF)F>--Q-Nx7*brz%&#`O*nWB!wpz)(HMMmM+b*JSa7c066nKN16yS6t@4 z+0nj1a3vEX4=YQUGOsLjI&)9|l;>Xy?heamBd78vFG5i=BG$I#AS)3K6sTI7UNS}1 zBN3$k})eqTwY0dKQ42> zU;iPDyWgPDSk}#5fo@RPZC|xgFmXehskU9?Wo#@HTD2+_`J`B(Qqgd5+4z)n;>^C| zb;)h(q?s-CDJt6Lifsr&;d7}ZjZkw$9dN;_%USOXxaxgCuw(ST<5(%3TQS9Lb;B?e zEVS#h9yp`g=0=}w4Kd0OYzTGTkiG!f4`CF!w{V&>D#>;Zax8tZ7MLFMj7lWPAzcSy zRSiMmL*a0)M$K)>l6%%Hh+bTvtVv27x}#+xOU|k zckCsOQ=jwB4AZ#!3^XieGTpOcU?*xjv>Y*R+qTHHVHhPn>6G@IJ1gskc@2zb*zIgi z6FE=TzztiA<;*>G*N=v@ToR_iZMr7csffSYwuQaH=GEhQvuQ8?OqqH91Q+Uo0s&z~ z`cLQLzo!ZP&vQ}P*~q-K~&_0nFmbM?CRYWB1GS zGH*keX&#KeJrs2UgnfUn9;LLGNoR_XP|t5^&|(?E$YY7&A>Qo}@w>sG0(7&ZArGY5 zLIgunqOUY@5Vx$2zwbbVRD#}cQi9_r?`4bw=pvLRCoRlwEK%|i_q9;IFhUPSW-JlZ z5G%?1BWk1wuqW`O0@n67qBNe(Oi^sGmu&6+ar^l0jv3B^s_w> zNid$`ZwltWb>4qJ9V!ausuZ-)W)$(>;I|0nc4=RAwsh-=t;pjb+O*2oSGwVQCkLU4 z;Y$M*q$b4Jn6srhZDpyG%%%8@&#fk^_|G8@$Pej{?t_)YV)m3zxux#~G0Y%2mhmO!;?!8s3e%q*I zU(SPbNOe3Ueo$tjweG@P(p?xMRp`OKCRww2 z%u>ymS)uj&xq{9nFjSoe7P7~mRk0((d?9$W;-=F1yw5G+U*3Om2t#G-*1T_$_T7 zA_GQ3ixqQ>HL5zsN?X3NdPV8Mshl~KC+Ba&e70#v5|}>yvsen{s%7v@0Yb+t=VzaAn7w_>fUkfLq+M>a zCFomS%-+EpKv$q}#p!4s$Y;gA(rYtCU{OH7A>acsKvUT*GkAJ>{WT13mpG}0TjEL> z_yr<^>CD31y6ugrnOCsZJ?v?-4x6tXc2`?BOWR13KR?qDn>4GDBO`-6zhG_3G~kLQ z)PihHUnYKos)%UOH{rG{+oC<>el**k#wU}Q7@qTCWjTpXKI$@h0j2g;dF zapcI1uh{a{QvbT78p>_(p~c8pYKF7FU6DWkJq-xkE}!fp4yZjm(Xgsd)6SQ@h|3}5 z-6k&`nm55QWx6EcUBS>``H_Hr9m2%iB{rQj5YCnd!Ic^x z=HXpYMB;9v7NhecFR^P3vU3?J7yxAW<3!q;fi@4z@YC1JZ6-VTCt+Ud6Je;{r*QT$ znP!&ZPV#ch6hy!;BlnI!cAbMoD<)VAN#bRKt?_@#fQK$L3v@-4=I2N&lQ>jzy*z`S zyuWs%TUw*$8^xSROwaw2g-#-rSKmhhxa_8~7$7Xlz3J*K*atyN_(BzN<{8sa%U~G?Q&L?*-ace|kXJhEB4j(_ z8>tj&G^W_OrwB>{iGA&+v9@Q7Ulj+1WfxYgAc|AVRS0UwNr!rqR0g9} zrk?2ba}}(S%*o_&qF+1lmMiv!@lf@oWWO=COdp9bI(>ts+{RLYcff((){&HXKF$x7 zrBItFJ66WK5`a`n_kiv{W?>9JqrR8^NOR2v%Csu+rFA)b@#+wlv=msD;RAb2GgQSS z*vnc8LT6pJnoVF*Ia3GPCEFOR-_V=f+NCQ~$Va#R2Ymz_nsaahFV3FW-n$79eYw~s zi?_K>y6q&P+fXHVO;^UZuoJH*EZqBKBmphv5bQ1)oI&?bVi`oWm4d#_GHxnqK#cwm*VqZs734?)eDe?zL62 z2e%OQ=B3PEd|WbCPw@wu-HJHq8s({wufpp72`j zltg}N)zyNWY-l!}s$xKbH@6!`fB95V-j%#nq^hhPD^)$3!~6hOQN%t?>q`Sk+JQ>- z23SjDL|R)Swbk{sM_{^e!TeLpj9W^f<=9?U*N@iuMzqz94^%yFywFOGz^gr1xkdYt zdkSltS!{jNo<;n`f>jBu*6enHL%heGktc+m(qfI3u9&xA`8l*KeLk2QnH7aQk{~y7 z&rzra5$IGirg;5{hj%cqCS5b)GGpPg1Ca71k+w1Y%l=Nr7CF0YuNHd7m?#O#$i}fE zK86<2()J5r-K*gFuF#N2qIJ{+2W~%NaC_R5!LeXerAqx|6BTawRns$GJbAmoD8U#` zbY2F?i$U6wAAxcBT4Oluoj=seSjPIc8EBF}lj$xygen5MHnegyyMRTGgxZNVhlfx+ z=3u)M4!4_p7Y9*Ukkp9$H167@R^*hw4WZG=`inAr-U$5X{=W?2CcbjqGhcKfF|_|w zDE;3w5Y+zJb@^AFl=|PH7niKrzsCDTze|9@i?2~o>|scQP+~)feG4`buSzycVz75l z`=tP(>*Kk!v6#n|*SaFtWj? z;O|Yg1M%mMemH1KwA!zBSYc(y<{;vqiJ|`7w(Y~QI$K$>R*}|Q3B61nDPlwYQZ9`kAaFE=fyUe_-A%}r7fzo1WskAnfG_135B;() z1SyN@)U zhYX}r$88JzZ2PS(7uFuH*Hah1+FcMl^UvF)?C{4MVAuXYb<)uboPo<8GSkO=sBp)v z1@E8gCJ;L!y`tZaGz(#4{5eJREFCw598@C zcXjZ8lVXU+(gQkg3w=MOWZ&xH|4xobfj#QNOrUEoOpIa}KkVumV>jT|@K+k0=z zX|Bjv(Z>-v%egs~l^L1B0Ws%|@RsF{a>+XgB9rXz56{`hd za(EhBRQe}2x}}+-A&=v_?b!C8B$e9z)I+jj^DMIsh*N5f?=6*6sT86`!CS70u2 z>!MnRrp2ni7UxMtG?nY;E8PROZsQ-Bd+EANn)J$8WK(?^QJ5s;0mB@LjLM!%moQVp zJ=RIvSL*Z8N|h!*Z)r`!=oC@dF;yy}v`yN+eQFg0_OM;lSv9BB#1rWhF&@6^m@fxZ zLfhzRWOb2N3e4-snIU%M>7IM#J(^xslxm~$FegGS4f+vXPqfgAdh%ZUQ;XXhdbd-2 z06+dbl4R($CJZ*KW?hs78*Tb7TwqlEIvYe;d`K3hux$KKvAit(S>g0y_8&R2*axze zj*)a}Q9v^Xwl|u^9b<8+MvE&N%^_z%4uwWT#|*o!#8ina%arzkVarii89E3{;1)b72kkx%YKn;Bo{)twZhn*zePEQy0u7BA{~R$NzJ~rWlP@5%ehNgJeTf#*c)Jrf=EZU zHazn6vfC)#VA6ax3dOiZM7A{~DU-=vMhQbRrI7p<>)gy}zhKH*t7cBBoO7n8n^0MK zH@QdVAi8__t&EAA(bOV=@|@Jh8EY>EJ)@#BK3j*%g2&uvd~M5Wb?e(*W4UO#d0Khj zfT`=v;|hQ?On~=HLH;@lf85FCxqZv+j1&)x+<@;b{&WsVpO!0u>@m_g6&bfUnIg}Q z4BbNU0aSgvmMbG4j8IFs3!;U+=sa*E?bbye9Tg>g(smo&*Wn=A#7dz84h^r1YbJEt zznGJ4pjuicm_t)Nt`1ZK{v2ZzxhMGOw|94xb4v1SDDi;C5Pqbhk0O{rMna}O)zpT_ygrzO~K@hv7~Jd~AG zS*UCCZ#=CKK`r4Tyx^TI9s_W}e*8IYO%z>v>t>@NNj~Aql-4z{xtb=WwC};{NF*%kGgg z{^~k@gAKt4`cO-MH9buzMO=6CP)nr!abPtL$|EOx6VjCKpbg_R9U6N9C#xT6TO-Q7 zQ>L>7Gbk;coQ13SrY$?xMM>&nI_^&s?1)I(*;w*OvIXWbuow@F4Nq8f2I<)D5;@cr zPwq0fmV;)^j2Dr|6%~oRvX|$?we_LJrRj66fZa3~+0#Kpw)Qlb%;u{p<$ltNhq7g{ zd&+)xkY775tW_DJ84s6;C=Tzmoe99EsM!{^mdW>lS=7OaJu8KW-oual%DlG1cXrgs zdfXB}JhJD0&Tv287xhhA5>-r3bKNrzDH-mhaUIcl_Yy7bCf@XY4$h!;M24d$6oS4n ze=6KK8DnN{$>)w(UmaYJrxcPpL-|Nm%rACCaiJsJ?hqO!IeyY^?8KJvOPW&KcX`bT z=24l*VRn3y;!Z~}>5l+UBsR~s%^Vqw^{d|r1gOLZmX zLn}mI*h5SMuUxv3f#nEVx&2ZSY;kdfk@&kW+MJn5+Q`HyyX(QuB znA;mz*fEbV!!{YI?#MI%bOmH|Mo;e^s$HKr@dh_T!sDJZ&pF{n(0fv&Hl+tTC4M5} z`KXruvLGJm64P2rdNh?-_()BAgKjvFm=%hUjm#N*{(>P@b_4FaN~TU4=69A;Atbd+ zlb9?R*K0Rrwv&$xGz?YqTyP$!$%KI74)@;r3uV?aCn@Um$!`^R>%g*0kK(rBV;3&u zB70rodj?bA3D6qhru4d1mXva1M4|x~&v{!Z(&mPTj_r|zEky>z0*v1c5FQP%SJa~- z_gfNmVqaXsqDvV20lySSVW7Y_TI7AVp7EBPK{N|#H7l<{`9Kzr^h~Qn{!9{lx9%|J zeL49_G=#m$S2(qeA@zXN)Ln6Qu#myjn!t3}2p;1?6fBnH_6YibZ3FcB%$SLcrta2o+&XwDfsV(@r~;6xg}M1kgw97(#n6z(o z4!bDDP{2PBHgsT+e}nSGWe7B3np0BnPbX6^^!zl?Q%XLHotSy(Uc%OIfR^#;`5W*L zGqBP0QwPip zrZ&cX0uJCDX$UHH8G{tE(Z~XdofYn2?BMaPUZ;{#6{x73@3^LX$DF% z03}_lV^IkG8SeJ-!!RJSC5N+ErAsYm>u$>8~CO z$EMmZejkCf=UjO;)WyePcMBc6TIr(3@792DC`RL#t2GcX51i) zyzs(1OEa>I*4(p^ltrU#qWSXlQaRUV8LyIY+#&8b$iJA3Yi#%!B3YQl>v)d!EXes7 zvbL^DV#;LgcdYjdeV1W|)1*hr!1hTvu3Hg!{n!H%s>i&zg^414z8cxH@Xe(5&4l1T z?up{WRn*2WLHqM7cl&?8C;ly@Ow7UF#laRR;^+vpH~a6deJJ2cbyEXHu!)sR6t(~d zOAYPYa?V&en?K^?l8A(=|Se>}I$hRz!QiK`9rnoXw zk)71EQ_}sDHq>&U)+&9?Ojq3{dA6bfP+~=3h%6rmGxoA z{!M`d=2rrKUoZK2Hnk?vG!f{GKUJ4#i6Lj**knV%l8${*ClV@m%8;El$Zb1#)TX?B zf5{eL4KuyKxNs|1u;pbf0#(%~BfV zTtR0httsEaWs}*;_1k=4h&XR*G{oj(cA+9AkI}zEqNBa**!W&vUBav^*gzss!jf-h zHQRWZOVB$pnp9}FVFjY=;iAi^V>ljC%GdFRlrG!(8)@a45?L?7G0riq#;Ecw(fNLb zQp>4q{6$O(nVs!`7?vXXRf{?AdS@tT&c`UG%y4zb7Q3^uj+2l7v_d?(Jy5wV2eF)x zJyIa7)?R_Sx0L(*sdI>(;TwPbeO9#9eBC#W_ao_!!rTc>v&*VYBxfVjury>#V*5m1 zgg(-4qE)e!r+$Zt`(n%eF$hp9Jn@7-%@w<&@a_%1e>!S`10k_ME1BhY9u0J2SJ5CN0XbCSyzivcv4r_0$$1kJD)ZsUdU!Wnl*yZUjQjEYfn*u@=jYL0EvNXbg@DRL7!s0*3 zCC$VMTOaV1=&l4}LIVvOGTgGRF2lVY-r^yrMf64NQAj#cc)-}AXv?hvwvmVt2-M6$ zLV@6GwPi3g(&bPnKqI<0+|%`3z5KU#4?g$+zYm;W(Lw;S5{Som7Dw4chTNXe7Cte% z1P-uk(!k#(pBQzfSu|59t>GzVZIMYtstaf2c)29?^>`^gWujoRKkMle#y(?T^Now# zcV4?HOCwPn*=j5{^C^r@!M(YZ3aVD)5~)V4TGP`^AG=50o}d_O+0{xK^~lPYv)=_V zsBi}K^BY4P21FQC_#t9*b}L7iPM!Xude5}DlT1{pa-3#s_VaZ^)mKR9DwH1KWiGyQ zO>ti7{G#|QvlnXs3c(=Aqu?2j89r3zdM3#v4fc4CVhR%*^mRCZRfwAsI8 z*sjby)^l4Bi3;{(gWxPhBp_o%G2u99QznCp#uP+UFhs-;(NIxEhW>K1uiGZ8I|lH7 zf%CTicE9}E$^QN6vY87)r>+fhvI`|_KQ)X*0JBTA#n7$m%Y*YBn|sWtFT5Yb`Kq7P z>laLuerC+KD_rT!(pKSdJ3dXG zK0N2+({rvTHR$U#51*`<^B!U7 zogE{-d&|;|<>p$Gm78g9khL<7W_CpRwByzmLEO`M-ODTaya*R3hl6#s%lwk^m`lWk z4woH9lIm@JwN=edW0`q%j0*-B(okW07`9zyHNTj&J*1uGL)vkho9F`y7Gm~$ScBDh zWxJ@z!OZQoBangMrplI6O|r|TV_?X&L^u9<rk^kU6rvF1tcn`i5gVzl3FN^& zwn}XjUmfSC=cY1Yy;sIRZk%_F8taP4=@3!Ejiiy%7c|eFFaANje`w{rawewEbXUi+ zAEvFKAuG#G-)@7h67+x{+rGWExmZ}fF+j+wZ>A(Z&^W!9LTVa*%GDX$BD!w5y%G|u zl|NW;T1u5kk^GaTqsokg^2X_&*LNJqr zhup<+mTj{lE$(j&LuVM)9-E=0amrHDH&K5c6TD7XTU@>=yJt!Xl>zvdMr_cTEBvVl9CPAAN({b^V>>hK+$wwGuG-#b6 zzhUaq-i)e5LLK)DJw8PNfl0S+a~=t+b(yoXZ5;lpTx5$fvkV8z@zZ3s!!jR!+XuQS z;=#7F5|#Cg({r*>x6Y?_tFx72InBaX@Lewfz%?71*+4HDnhOEfT-K?A#G|)db93B@ zBj!L$*n<-&P+z&RAinp$tnA)WeXv9N?bZSD=1WtOe_Z%BDp?Ws%V-yBhxw5sCmh-4 z6^8GyGu#{E>piB=G=L|@IXpNL#>r*>|M zN1Md~f(rG72Up&e@6;ZzDQJcr42!Eni39H%KI}l{Y$UeMKDiIq6fE34xY$BlTD2_a zht#SEYSV7OHv&*Jr{?Ujj_m!;jIM+JL&umn)p^gxU8y_P{E5{i2u>9Qm4Zqc>!z}@ zib^3)1+_&$VVR;bJBDm_jBD00{vetNIS~QebdqY7Z&{gnB>l983kPP3W@6T)7E+of zbSC-v*`ry0Q%TJ&X0RvY`WS$Z}X9=Imv}Oi}vnpb;$;q|yO?23~P`0pFd#ZYf%@OkdVjg{i5#68tGQIG0tAK1sm*4!z^c>rTg30}-%4+yB6Mr4E-<}`$_T59H< zPtvPwTWhj3CeLkJ8Tx#c()Fge`nt7V-adJxP6h^vlyUJw;4lvfLg-y#uD|=EmLt&j3C$lS2M)(jsz`^I@sH+mOYVGA)EisslHJV5FlTGnOZh7ze{w72N5~djVwMZfi$P)@ z&$uOoV<0w9pAUI!>PH<~m*)v$;_^Rt20^dv394n|jga|joJD0fk#U%UsL4#4@?Wu( zP5SPUC|Z(vLb(YhA83PTOq)01nCyWZBarcYCqsi~c_E4Vt#W7tf4n}1zK${V12nwB zNQ)VB?BKVE^*yP-ns7{}Cj`(FBl;Fcv0tTT=)kfM{c)e#Ytv@}jn5MP7^WMh^|c8T z9)xvDA*cs-5Kz`8+#l0h;gTiy&k&d-1jxZPHMpYS z2*_j@qVlvEq(Cg1K(M!f9Hr3c(xZUd9K7ov?6VE#187go-*@ z+4@&BJ?p6Vxcd+9Z+2BpV*#&wV*%#Bi=KA*wfD;MURa4Qz^>vt)wc>Y^tmdkeFjcg z8O}`2-vT-1Wx8k%EkGv3ZVt*4*m zr2y0AR$;%3Cwk*ImQ@n7mro0O0UpK3=^xQ^#C?IK$3-PLP-Qb7BO0HFX^WmihnFSJ~ z(6`p@=^DqB%oVO}bNNE+s9iM|mA>lV6r99z>M+Yy2<6qXOI5>`D%8f!Ize{Uv_F?B zBdUSY)r*cDI&qxf{TyXs%8muTjt4!T|)klfAo()MRX ztdx`OmXC6G`dIYb4~t-WgIW9IRdDtlSN!9zAW}&V8f;f95VW-?3-l^X=J2vuX|^_- z4LTE>AZ^d&y%YL#2B$(Hc4=f#PH8kILH$35foWpd~{8$|5Wz>+u1>Jpt-S|t*e-^E6~Eh z*-O&c)b;D*zl;Ye{fYzMFmgggON&TgD)eHoMaTtD5ubucS*>orlisy2wPMK0O%&bL zKZ+Mc67Ndk8m2=b8|@o=Y+q+*=1%{9_x}qq_igzb+$Nta6dKJ%#p&97<79%6ho7ba z8->jiQSb`fHKf>#v)_DX+&fv-)MRkK|6ZbPt1_G@zE_Jm__)U z=0Yz@qSvKlMy62o5N11jxgaVrt07_uDFNNUgNWOWeCg{=GdI=du#d=Wq8Gwuda+fH zEPCG)w)=TsA=Sn-bp%Q7jIcJ#PL+}KbDCJ`^w2oYunFQ>@hicw!GO;=(!cn}Wx^P3 zW;|MGN+b4{y>qD=em&uSmK>Z}sIe>}(E|&#V#~Co5{~Y#Yd8y{n`05uK z|HppOm#q}U!P$b@*wNV363A@pV8LO{ENF1}c`YOCE|^MoKvAOu*9Bc*IFWE0g{ zgHte?;f^u7q$;!wNL?ujx$(L0CHbiL(~=3bVoyR+vF%BP_Astu#^kBXEW%+y2U%b( z42XpCX5r&JMg`-0@g~!xYrb|}ZPSfB$6qGf=(u2vs8x$`SGZ-2dZMpJBX8BI<}plx^uKi zT8O3utg!CGMr|Tn*z9;Ak&wmBBEJ&Q)~Q!QuTT|l?*dd&g}(1%koy%;!u`#o8=$02htn8*xm}Z=ZMs!-sx3~<5 zHcc9nq!uC-n^H{)E}{L!tM0E_w|ZD1+*T2supBtrNfugzb-b&tdW)Tmxv*ylxSv>} z3`rl{frEn3dO*durIeam)e$^mZLvcm9B6ef#r5O00sG54Y)oSeLa7dTgcUURmB}}; zOwYLV^&>#y@i+qO@8c-jn=<43pMOdty|ms8XId`!7*ft z<_Et1^_9zrV!M+W9iQFk6!B&5G`M9U03qqD_jq$%9c~9+T^)ac0cTlI4vBX#$Ao*R zdXC`_ZF+Zvj^CKIg~CslNq9fa8&{6KVkI zKKX-9G4#)P$Ei`p9?E%*222TC=nKAjps#qq_db*RMxqqQb*92=p{1rLYgZp|0Q>ya zTCqZ$W=61@|4nYs*Ul*!aim70CtD1HM;oC5SD1}KoPKN?ak)$Ocx_TZpCfYJ-Pune zgTo&Gg5uf$a6$&KN+vM+TyQIAS2?_5jg~%Gudin?T&&()1%Bb63XN<#XY-if?xJ?sLz$r~5l&-1&d~nsdE) z-UnnXp*eZwfx%NCIj#sKcp$NkyLx7Br zgn~ed`H%0JSH#RAa%xu^yLXxObz*gEbJsq}>VasARvgPnVCqKAFbcU>>p$J*)?p>6 z1HN%5nSaEcNdCKe_TOMls;h3_>EmBIUdE$$?(xVS4!FySMR0c`&A1YZ2>`iyRw6kS zoMp+V&EGNA^}OL#^*;*%ve0x%&9ue8Yfzz0n2Ldr!w#FcN@EXkbv*+9{NUv6V+p

QtPh>7vn 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

9N&ZxCO@RdZ0h&V+St6OH{6>db={MNm4>69~ z0nA(a3r?R2j28S~oPrjmN~;QxgKDS}#$e*cCXth;q)FCd11`kC$ohL_-jYF|=%EWE zCTkUdD=LEH+o|M<%L9r z%*4uui!yAn$Fmvz1iKLH5Qp@yJn|ey{vUq8kR+=7SIhBUv!lsKVdDl?tsJ*+e?E-@ zU@1DkPII0%(+V(S@>Y3*Z7%Fbetm{^?Ln6O$`*2VgG$Dn?#DdWH0{{G#SvvY^$H|N zv~bJaj0L-huVdKbQN_trF^DW?&b#{a)koAwt-5@^fn-P(S6Uvr`eR%!`TqQHG&8lOBURt#{eS|CIBD`?pI)dQV9pqivsL1PJ2kZ zz$sy+iJ%Eg1gMKNy;>**u6jTpj(|}Q8b7c)B)P-cQ6JJ$R4QRMMJdQ#Bc&bAN z6j^(=ZCoOxI)%S*du2DvL%hZeYH3j_PCbvM8I{1=C^| zg(15rD%xM;x>(2EXVC38yJSJ%0*WL6q|tdcE*iVgUdjl!k3fQ!$82AbKR)JWN7>Kg;GP8|xixeZFb#c6iYpfk&3w=8A5yNuYIzsu$Ff6f$(%owDB!#~hO#O8 zd#8t%9cgD2<-8!(BS(kS!DF1Yiy1?8sDHq1yK3@6x#GyyNJg z=JaUpDn9dhf#rnz_NeYUJ~u!6Gu)8eg;%RZKc~YcWF|=lNXPptON^R~{$i2!P>Yf> z9WNYRl!$mS!b6KqlNepKdI+=qsUE*pQ?Wr*#z?c}xuu$AI#cBbesk{U>)$EgY!jucNtN-yb3 zh@zM(ZOV-fBQ<`akax|J-haH~)NeVPGJx~!g^L3S>uPB(_Ud^2?vi+r=6H%D!q<)t zlyd)pjV~(LO6?!!P;o|X5x-YUhM|}=T(tO$2W83aN5PpSc3-NElEtY{b^g4TwLs%* zWQ}ZCCxMW2#MRI4KjX5vN*oP0DdH!eLJ}bs4-%1`=^dgeh1*HSGor~vt*G=6XK@Z( zi6de3$|`+U>IdP*j#=EVPLX;}{JHS7!a2m4%0n>HR(!tr0G(Ta5ds6r()`~Fq_&02 z%p`p$cbYWY`rdfQ*i`pensWV`O!y!N{%}qGg*y1cKn7$*f~uM1Z9MyUlAf|N6^BBq z5Cawn!(?Eh<8Hh~CA@sPq^R1fxJ82D&yIouCv>HG?Ku&QfkUr0q3}t-o8+7+ODh;x z9qx%PbpC-pt$K{T8Lr{P!HZZRI1Yt2%7NG%zF~G7BW0uoSz)`ZB_R$ktT(ndz9)|G zrwC0OwS|H$xMzKnkoO-G+ez#W91((A+xZ)qw=))iSo>A;i3YFDTFn`b(L+0=!}#b# z0p6&!01IosTr1+x2>w_H=+LN2Dybm7Xj3ntJIcaXi6w=z2gQe85yO_+Z%GUgZ~*k* zls0?%Wwuz=@MDNp25u%u0R6KBygBv+~Sw8+O z2<2OpR65@XDsEJj(fMsXcQ9A*i+SOT|Njj!qjnKMPIIk4Sx=I4BDYQ?p841|k+ zkQwRqCwl{vtI~hUkMBZW@JHJxQlC5oPx9R>@C&v$sBt-|5BG+czpW8v~!yDfYtxF|;m zJ0I`tmKKY}=j^O4TA7SEoawzXo@JG#;FAAoR&0QDY5QEs)J}HWGo8Fci>kJqa8#Cd zxVVsjRuJh6E;a?PT`MV>wrGDTa@k!y7Ni5l#5Sze}n&( zznAl3meZ4_Po_d`j2NW8oCHKpQQ$Vyq9p{p*%RAZs(8B2TrX2{VqAJe8fR|7no8DF z*ldvE=2D}q=moA9G~u6wDlMDVC-YmX?OUvo*sVNVuaN93&yl1rfuheJOb0Qv(=2Q9 zdn+X!f1y^T^8I3lIY781xMA-x4dq6${)%h#)xW@W>rC`}Fx=f67mEtv5K0NHXc0q=P!ny|yeeO{-6H9V9`nVs~bDO?ljA zJxn+)_D7K-<<|N=iB22$*{x3+-(jCqfxK-gX>S#rX*zXEF5$Fw$u8+0zhY^BnW;{c zpJ8jGpSGzjp|)k`p0&1B%|0k-SI<4{wAsu!*H^{NKVY=+$S$Fuc(hm9&Og{_^A?w3(JBHqk{}K!wg`o;g>WW2kI7ymB#_W36;8T3fH@ca-&dv-6$; z0F*`V0@IY59*p|^%6AFS-74AD0@lXdufhH3h1ES;t*61 zT$cfSQH^1%g|9aN-0#`JSC-Fyz}{Tk-dv7ukE3CwhD=Z#jHZ*&CSw)KipW)pILZh- zdYmo~fBcR5INMXp7Oco}JdEV>i{)P~U{997(h zGO6O{v+V0lZVJ?!jPdCITJ%Tzv*P4%yGHigPdvRbLjv~OHF#kE(Q2RIrsHWnTCxJ9|RI=^w& zdf#=3t~Q*`(0fAc!5y_DAm^@O9E2+Ith_E09XU}}Or35j033u#;+0l&@prULeLrQ2 zQqFLa0`dDIrOwvuY4kLg1xIE&BP++!P?C%F$$P202x}a!*m#3xYWdF)ZeY)Rbu8I; z)=!VBXX3c)@nX)}!z|)qO}7q#&4h)+^hbYyx^l=a@_$a$shuiocT~x=N zDt8WKdtM#2dvY=2Wh%F2Q)|L9!!;*H&^GSQ3Ydart^sml{(sw_Pq(1W_p|02w9MsJ zWZp(!ez`KOmwJiBe=pY}C7x;7_L+(qdI3Kv@c2zTB;6m-`^)!e;`AJq2&;hGk0eBI z^J9x(wPx!31tE8kDXb+pTS<_%Uq4N0;aH2?{M|x%AaqYPl2`I22t@C<@StPGMV6fASfo-HQSes$o zt+#%s$u&jn3~;vk`sqz{%8bp-Vd#9mj29kKrPn@-2LIQ7O>8+dWP!=~d_qfb0RZXx zWSVUMW~J)}=^AO~y$ZXoKCJC+k;9@iT-D1{87-S-0CDN_w#AxmBl{=9DI5;RG%j-+ z{ot3&4lysftbXWKUMHJ08D6oc>`X4*Y!H(h4Cx8>#?-CkBQb5yZJ$G*Ybj!Kx~fEY zb9&yVbi*wQZlM;I^~6j($~nUL88nPsXmfDs1wvoLjO1I4`@!Usz8p549bM2%594K; zcHd>>Y;CA#5HXB1B1o{1+}?D|pWYJEsHrMpz|b3Ph^sq3bO8&?-CHNtky*vSdpkYE zm5&masfU-M9}{9Wx6Jmro_jO+Z=C|7IZ=vW4Nba*5Yyl>?}_mDx2(st!bBj7df(?;yF za5_stTN3M=Z-ShkVKpyuQLJM5?iNZhskC}8j5tV%lB^=v+Ke%Cm5l+vq_ox6XirR6@#8JZ(y5T^CZ8#lV=As$?uWX5owHCv2 zF#w2RuFdl$iP@%TtmgZo(Tbs=Yza|~ob0PbNdbpzd~hOXD~^JTH#Rji3!|%MJfD}| zl$C_%YDRWvt=BxX5<&1vft=7nWDR@kNFdYGw>a&hSYrzqA!PE zZQGT}#ngct4EF}X!t)X4k-XG(`Xl$Xday54hXL2M;jUT26_EM6zgfqpUte?VU99Ug z=;k)!)wDe<0f53 zddYdIA_nR%=YmICH^GwQI&0d6J}SD`nQX;)Grnc>>!8xnUM0)@^sXk^?j|N7Z+lD4 zS3icXS;FN(KmhalBKBK+5PKZBfKWXNoFZqgUo%k{I59Apy^$P7i6f_}%rlNw;@h=I zj`Ca#*C=n)wlepeRjEu?l2z8Zn$*d>%|tv$MJX= zuWPx`Z(VdaNuHYVD%?q)S)6^O-6{vk{d!g--p$0f$rdJm408>krV-$z7+?seXO{dD zp(uTnK8@3dd1Lk-*+2jWRMAB1nUmbIZj>wa0F@dEWVVeU2VUF12Dj<9ht}#;*K`hB zF}c6d87gu|>~5C3&owxqJG@ZpqEi?;`oLhR$gp)=ICQ8cuq={?cCUtNN+E{6S}}s- zy8E$%ioYEAw@P6zWL-AbPxgJSa)BRjv$9^|ZN-cA+`Imu_s}m8$~z*1|H7^3Cj1c` zFX7_ZXFHXsfBmz<+_M))V@HJx5r-9PVBg4DmLriT$OS+&5Ul4dCc0p`h7v?S zwMf1Q+-vj4yZstt3;$)Ve+v)p9EpIghro!m-)s9X66qM3BMI#H+lKQWRTuyB?kjF% z=j`>3Z|W>U@RziJl$Ij|w;Xw*;%^jUEv2~(6){vA1tmwM(6>R1c+jP2mW)3g z(z-xl{sW-!Ga+RhmV_Fm2f!+~-)yNB+ppQ@qv|uPA5>~ial>jr-T>DdC}djFo7LXL z0CT?%^AeSl%AR)oS!Z1q<7IqaiBn55A1AFF&hRO*H-3e!#9VQwLQK}R!}@rwTmC;a zTT#*hkx&>9@`Ga)&>eKV{tt(8_|0((VtV9q4A#xvIB`6&qKH&P{77TRau&vS*{Su5 zp!nO$d#U###F{h)g}2v7lI81>o)R0{(lDivQi%b`D{)<1OgzryET5{UKDGQ7$~Atq zo>ZxgIStNY8DktcUsoAak4@m{Ot{$hoT(4=A@Kq3@X)C{aa?(@E)UhEvdk;L3P9_O z&%Ez(*UsByg6r29?Ch2G9(qaL7-5x(r0(!iLb9eqFJh*F&&RHhcEhr;M9(sd$L3H6 z=5FDQrsmUuHt)1Rtn_1_&^tW^r35iI7(ugl)ngj}8ltAq#o@SgGu;EHN$!8*gU20s zXH=f+0X%(kBV3OOv+GIKaHOv~jRtwhTXLN~!2VSp9qKvkE#D1U^FKD^|6O_fL%PpW zShM+d#d6F1%SW%^@lEQ({C;aGBsnssT0hz#Y#+W1{G732z%mMlGOy(##6vpc_@ z{E4hwFeFkXsJZQqxtl zZldb~cc4m79KYMDi#R^+`Cy%xnD+h6GMnBJ0TV}Mr^NXk9?2{sKQ9J=#nrckkk05`?bDGJq z$=mvOJcDcy{dzu1Kaiie(SZ1lXJI#q4h1c!?|2TmLNxadGTZoyoEuJcOnd49+mTt_x!>FUIlqL7>ozN}(wR@F%_legyhSvy{i~)Uq7Uu|}N)*u^ zc69^&uX1>I9TX_}Z}a(oR}TM}q_PyX|K(5Po?o1h!z5Rn|=BZgH26_LT2_`}_;OT>HKD5iv=b zVw0UETWDQjPL{k}IBDn-2>TR*wRZ%+K+F?mm8O+SCTRN;USYH{L}6MxOUN+BvY`$# zX2n%c1!rML6mJN1e~JoFSr$)aCQbbap7Q}nbfSl{CGVSSb9q^Z#8V__3h($W49D@r zaR)!Nv7#9E4i4C{C+F@=BacXW$`CUl)xGi}1gwRLtfp8S3s+0QZZ-TQnb^^Rq^0F- z(G}Rh%rIkHrrZI-RfsSVi_j-PL=UZ*-f{_zfF#2N! zuD$cdSk$^&H0r@U&qmD5y>}xNcdZ(7JJFB?x4H@HqE%W=;gpvvQH#v7tPbIEbg&%{bp|BA-ZGerJl(qgSm!NHPii!BCIM5 z9F%4kC#ova0;f=Iyn#$}G`cvWa!8Uq8u>EL5>0-5DyWVSe*UUa{r=d)E`>1%B<;6j z?bk9;-0b82m#9qPsITSYyT(5LW6SxU&w&3|BK703Zr~kXY3a(K_@1vDCqeA zF^4MPQVz#Qf%3baNlr#XVQpU9Z(@B~T$jds_Ss&;7>@CBc)x_15<68J#^7Cs|CEn5 zebUib9l|iI)$i%~^9>qVI1DBQ z3zkX+sAOb-iAY)Xm^HK-=b_qCP`Sc%Wc_5F4b}${M#h1%4agcu1=7!BTD6=EHDbsX z8WqBjX-JbhD5@=P9IDS9hPw(J&-aui|bSafXx1<V)Ke8%j@;ZVj|*V z&P+x$^MA=*L`axgtt0T4*XHN@?MUjQfn7d)>3&Vd&nEk^%O(doS>3X1FO@YNyDD*W zVy$wPRHz^K5FU zpld&ISYg**Gw{f>1d@SH`pGJr^W;|*dBz(VGAia3M|e?2Kb7UzM=un@QpuEknYr2{>;M5V!<+b`Y0&Of;Bc#^jjrur%#! zoISoIQEcUN2*Y+m6*Q;K(!D&lZ6Z+E5T~RJYV|@E`~fpqen@p?kd|?X!Ldx5EN~b~ zj=rL?yim_?R3N+;&i4!E0#fKC@qkW4RdYypKQBf=0N$Ak12_hfD>}WcC{t9`~Tf|f` zvg97L!3-ks!y74_MLQQDERogGzM&Ak6QM`d8V(kU7H4A4m`UdXv>BH8RmPY{WRX=h zzL370O2hJ@c_`VUQRU*k=D)SvII;;jIQPuaUNCC8v~c(!-~@(=bO}V z-uLeMtvrHeWFBB(ugBnH>{i9*HSp*qW8b=McNDuYBPenM1l08&?e)AHdn{f(>?ip? z+53cjQ?_SVxi?{ow#7`r$G%zhZVVOgI4LsbdhVoJ?hjf^CeSQ5 zjkP{twr-|!_O6)#?GF`tt7!sl=8UgM|Sr|B{9Fh>pTIq z6qf$_GdIAs&I=Wx*C5r;`{JB+V;|nQ9;=reu*y#wFeIYERg>`UjkH#}ZIZBSyyPpx z*=7+lE(My0$|Qmkd(0O1Lwku05-?cc=}`U0%0DqCip&~Yu$;RGlEH}iN^BD}cJaK2NgJua~n+1IUAXebBwOMq#y4&c03`RL8{f$>3 zRq~5m#ks#uMJwydts7XcYm!dxl`gaNa4hh~e0IJvE!yUxW#O#d$|=@d1?azkKlJZ$ z25I6AjXr+!#~?0GP8X=}{^Twe^WyTWRpt_=qwvN_)m{pZV~~j>ZtMo3(j5WdtdF?H zjsZ?GaA0CN)TH1CSFcrpFM6%x-PMTe?`H%fQb<@21t8}LWKo_9EV-}6Uc@7;-|Tvvad2do7kDIHr@7Ueq6Y*|IR>s z$OrQF$A;U6xI`?_6{tYb!A`QX{lOzNNP5}N_alH!C48?%wgm2lArcXYZKuCPO%{rA zzyCwPK_YhEKNE4k0w4bQ`VYLJzxN2Za>X7Hacp7_iIkWagWIr`v(^!e@=XIr{uISx zg-JnTKGAMN#{Of03G=1CS$IZTVxCNSn0VE(iG{DmEEz{4M%46eBoeD4DD2mDhrXw;8#0;>JwcEC!heKy8uD^6qvq%xi|Q~L4|{6?R7 z@T>nyi^)pA))Ay86Kw4c*fjkJL%?E5l?ED_+g6d5)%pzi;MFLZQnvg9b&O1zNH`fr zdcSb(4TjCKL|jz=?nqd&ecY9FFNRhd^Wco!PdZ>Yxl<<~>}eT#E9c+gGByO3mdA)GG;Ye`E!!W%;!tiY5g_B z4a`_7w9 zi`ri>4f^dt-gat7-Bh*3vo_HqGQ0jeSa|S1wB75ffTVyy2 zSw=?*7j=%pE;XwhNzz(r@d}Ys?>PbxoGiP^M4w5@bp|zB-fdb&{6?;coJcS8(Xj?= zlly7HfOx2b4I2Ha-$3X_+U+wV#mwD+Tx~b24AlaOXwVWCFL<{8vumDB+70hqPHE3GLg%_S06nu*4}1e&PceV!0J-8ja~6X^ z-`3WPW8L9&hW+H{fTnk9)6U8U?ZBnJxD~1DX@p?khjXvWp9rEYRnZXmqoN_?p3+QH zEmjgwRRZDuNSnv#Hc#juu`F&Ty!Uu#uJ z`PGE@5es%Fnz$h=7bYtoGEM%LqZrlo=iN6<#w*CjD+*@xJs{g72(zgcfeU#%GJz_Ul@veRWNU2|7GHfjduFQ^PP4sD9 zMu)jBs|CbW!M33e(uN%ZpBCm`28yo z?QfA7fBzm+F~kQ<0*hl}l|~63l3-*`%&;N@uTBa?R5mrK6;{#MG&DjJG9e&Huv%6& zHPWn3uFiF?4qko~KXP7mri~IKCvUy`9N8RYzhyagc}`~>eY)QN{O$3+6!CXiJ5aer zdLV@Bxu&5xqNP6~W;=hbU2$Mo$V+Pg7tuBBIWV+cc%8_gI#f2iWl7RL=l=3(8;Fjw zR?`F*o4G2zMD4)WRY8+K?DM?$RDqN2iF@z6CcI zeX87_UsU85uqR=9Y;%RSvcCWxa=oB5n*MA4Yalmqc24+q(jNMS{(YOr*K&r`0-TB- z$PVP-{etq*M>bWm1~3%las1`DG2r0$O*;f{v1r~I zxboW!@LFplHDc9qYv2Y$aG-;RE-Mi#L)+60hUDmHF(Van_;ph8 z-s}jmAuDjIWv)TX`)2J7qWm#F#r?JCD|`L|pb@3bCD|yetQKLJeh%O-6K?PmQ5$;B zB>`ZVqM@fKA_wFcd9Jd%oET}q)UKBp4%+lZ{4)}3fXNJW(*);L zH~_g8gTJ#=5y(J+d?($(nh3L(W(dFBSK_ug1wV*o&2@#~F(WlXHqn^`BBSQ%%I6_l zD6qwkgK;H>7o0?T>qhz3C`Q_&W+Xw|(vY|pq?N(!M(WtUCURx46_5A7aGw zes0{4vHHw$sn+F&FuJxHL@GE6#N#C_%QQX{JT($v)@&As>Bsw zTV^)IOj(5rLPMw_+z@7lUdulqf`~)NA?&nMNx4aPqyx&M5;2`2OPM{`ti%;!>v`P~ zj(&{;-&MZr=@!>~s)ZXGy(|`j3p-nryZiR+7ml}^7@l%n7ml|x{H-@Yr@EU~%Oq~# zz1c3N^a)l~5~45IJ=(o5e+~-Q-E9ch4SQymalghw?H;^RIv=2g_o?26catB)yGajU z0-c9juh^A+t_Sy(zftYj>D^L#$N7@Gk?sAU%OrdmHq{yIzQNn9cC-&tn({8K6u(%H zNERMnZw2dzxTMExyQA^#(0>eIn&w@3)9ZwB*ppyH@7rwX>4GrzRxX>a8hlG&m_Ev< zJc>S8j2Y{IuIQFe&UHrg7E1{saFitC$6f7mhpsp7j*apljwdYw{wk=7`O8EkH#CU_ z)2YkqraDoL(1rGCqE3keGo)!4Y?EWleY@lc-8n|13^Ux)azJBRPDa7H7hyLpB$B3P zn)C8AIY*ED*XWauwvE{H2D0?e+tYN>)=6Md9$2I2U#<=o1vpBu-sDX*{b#46^LjVR zGL(de_h7nQUXHJ8wN#75vM%B_`ha1M8|Ov=)= zxDSh`!Qt6>kI2fvQudhqjraVza9`RVcwYlG(X~hMZ3a!27YyaeD|L2hG?vLLI+oSV z%hu%^O)J*z8#Xm-Q7&ux%~>wv;X{kiLAIaHiMAk#Dcgte;ewnn!grH0eYr2;@pcxg z1Z*+=Nr4&2vYwvb%TJy$l7X5+e$}O&!$f&CQW#cOH>imeqRVb$*VGvTTV;>LPz^-a z#PTKV3FLETh3BZm-|VD^Zv^SFOYdJLjk7ZWC)!X(?A3w!~^MDW)nP5I*~>KTu4V3{To0(H09F zutQyX-hpDKSz4zq<Xoa~NaIoq(ZUIf%Q+Y8AMSWMD`0AP1e_QjuXKnr6bH zo8K*;7%-pe4nDg9W1)cB?8U7C1C2+u>RV%v--I=a5a7fros}#AH-8W@SrqsD3xSjr zf#!8A7|IRG@mh(E%-wyu+=dZ(3xdH4Ju%9E$M#pDCPwji^G~NcY$Hw{O)2>_*r?^y zP>X4~xqf;T-AjEzVkKGR!xSIJyS0@GRXc|bBGn@mIN=r$rrLPl@Nvf05HOUg? z0ZIWv?Q_H}Zas8ml6KmrDKRC|18OL!!){kI!Yb`6w)Q5`&h5fr(mj|G#7SIqCPk`j{P+_I> z^q;mWmY_mwXhpTA^+M$~N^Q_Mtb@)x*MV-Xi0@W9isbbDy;I7&yx^zVT7vTL1=m{e0eO5}AuBia{LJ)+Kdj=lyYtO4V6 zrl9e9F^Lvrw{SN&eJDELQregFrb-evqz}+xJJ6guR8sD7i=fybrj&x3ITO(noC(&jgiqC)sx8W;CigGR9D ze_JSZT6VZCzNa@Zl>aor(fw%8K%{W&)mG5c!#&*OUN*g+at%5Ul8O z+hfMYypDw1JOMcmDSBa4&5+8tcA?}$y+OHsalwUUeuhwT96n%RO-2N=an$y6EHAE0 zOsQvnW0v+$Bh&q8cD+4Nj-`lkL&&Kj_eKyt_lE*N3?slq0v`c~c__+mOpL6*Hq)>$ zfy;96_r7DBor{XzQ_%1pX6z@llD3G5p7KtC*OWw1X{;)TRbl12yr1&}e7)JRVbSUZ zfue_oc|9jDix~%HiIFu~q*Bg~n!^R9q$3g9XmTs-e7p&*2Ex)JD@uEj?f;?d9itpV?^^DB#$_B)ugiI zl~o;P4@;uvG@zHme&$Uj+19Na5qbocKxqhQ94)>~id9l@W72U(lU$n_ zVw0sC>BJZ7hoAj4AIJ=x>yAL((9U0sy02K7FqwG=)h*`geGki2r|~PbwBWMhlWpni z3P1aa<@Gsmb7XRs!6OxP(L8PT%n3mQ#7L3o|3+4__-&2{Y1kXW(*`+y&pdg<5q=t?gzlpFsU@r%k z>~S&&&(HEECGVnp6h`+Jx}xAEuF(&Kne(OO80nN-B z?%=9YdbNjEr1lC<(FxYcI;DrvQrks`Q&MnLo{-9DUbrJvE2g=eRidM9nq_TTrn0sPGVP&}dy=O*qPyn< z?KzH^vu^M?5B!n7_JjB-=6Stx%p)f@^;G6^zy7HK)cW&{mVRphw9x;q2=M>^Dgt8m zPPT?Fq8`Sk4*%dw|5J8VsFxdHK=6el3{apz>4Sp?6nRmCK%FVgQ5sR6U4b2RIPH(i zLiG*>4gLndQ4EU|WFTNPkUn<5-rnMUx%@an6@bqMwYC^pjjkU-&^=XB2MPIQ$A(Vb z7fpeRuP&EL#hc9#BmUB&#b^+=;3zEJNPd&Vanl*Vix^ zC##kM2jf|N(#w6zw)Xe+_W`yrstki+v$0t(V>*mYuI)z-vAXQT4XfYPdgk@p!j+)6 z;rt6H++vGIDm7~O1T}$82);$2U zA}{V!WkLgTm7zz~OVVJ+h{97?JE)*!d=$(0oqvM0otAc$K2TR6W?TOEr4Wj|q&?$c z@fwpUApBHTBr=3g_;y(&fSi8OWu0@u%%~xV#tG)Hf9!Z&>O=R9VwFYLVs`ErIpXP! zda$m>!^l{}ZzTBj`y@^k*|SGM<2T$3TGZ7h^i$ABNfqIMg6wb@XqR6sT7yV}2^N^t zlr2F^l#fZ%BKD?w=7T;Jf1FvY#Tx6xU{b`6D6139&i{#-`ddIJb-pK!>|0~_*AvJ3 z|2T0<_U=lib|$7y|65E*R^4+#{)d>5I8`#y(vqpfRX`K+-JcUoMDt4 zF<`SG-Qb#eX~Pyhvk(2Ojw`EZj^pj4~kk}JuZ8vjh;~PEovB9J;5F8N)gV|(oK#m;um4=mu%D32M z-t8Av0QUa%f+El-!gTz6*j@sf`VF8y8EnpM89Va8qRF8Qs)31J=6d9HRo zfBS1a*>=R=XKC%;L_0)|vutQDIZrn}nif;4`C3F)tiF@NxA&MHjJs1%n2jhvl)C0G zcj?+A7VEJbj<&?vldjj5t=)u`%Id0y-ZcM?8hU`Xg`(2Y*g#xc*YRt0TO*$W(13M~ zWNynQ(&+M*8D&|^sudN?I!Urh=_sY3+bH@@YHU&7;S1v1#+hP$K?2H4eiay@dm0x{ z_H8mpU_CKO)P2Kdq8}sYH0OD-rK1Mnj{kN>ts3ZbH&HT56`;C~N|RfX%XySiPidEI z+F)}~@(?rx{;DM9Dvcv}fs9XhZ4gTCyv=$aPfhdP8rf{$S;eI)y)BkrW}A3qu%n+h zloD4z;~5Ix#U||;3m%ZQvRL%I$7MDR@tMAQCJp}wtdOo+HltD zhGL`&T~b|9tMf`!%H~6jlPeXl*Q9UL`i~k zZlvk2)9)N1mGfePr0!+%7#RXqdMS^gEmQujlx(%b7dTz0m!H2#Mwt2 zlRmRj#$dlVV%a&FkwIl{fNZVmBR|<)#DI#kN-q6k4L3dkTHxvU2CMwS*96=feY?sS z_pfa^2jlfGRkpF_+zi|pXAFjq`yzDN3r^|2IA6D#sELDs8s{wYTkDzXicl*3*!vuJT^w>KGXX|+oCD) zUEW)C_K4E?u)KYv7xoyqzmDdT9Sn1AGke;i`2)jZ#YoNRENAy4c*`YARJRA}K z?M5O5|95Mi=l^5Pt2h}NTYszLf+kk3&Mvm5b}s)_NBoBm5=jw77aR=i+iz;EUHKT; zj7q_zM^T;M$094MPyw%|_m=_3&>@Y{Io~hFPY9X5JlyXmzUihp0Rr;eV5sJs(=uP< z6W80wOzjR(QeCPL)VU-{eY_AFyIEa$CQ=KDjdU9+VJT5;jtnFF)zF(k@d)vz|SSO)ICBq+5#eXWa$kReKIQ z@nie;In<6LmvP=LukK2C8AmdmkHw>5?1d|w7Vm)Lr{GI=nHlsJhe{Y}pDbG^Y6Njz zJ9m-qT+)@3TcDMf^x8-u-1Ed&V@!3^&ZvylXY_b8YsXYWM>*KWD__ z0EPr=&rejMTm(*d&Qy~a8i#DERtEVTHWMSD)UkOf151|)cBA1@zO|RA6Ukd(C}CR* zD11au_@k@>3=qStJylUnl~L8P2#OUm^A46+v4*Vg8|(D{(7~fg z>+GbyYZSqM(ZNprT=asREI$Liftu;q{%ob4(A_# z&RGB(W}DN?nmP8|o>|cE?eznX8`1_OrX?`Y?!^Zpn4vW7)d$M)@$@PCeMHsf+#9lm zaQrcuq-N>e%>+QwlXe+FpsU@HhKPKpO0GDePO&*x=2)-&!4dA&o2yt#I>?~3RbQA8 ztJqIt-Y74qw^zaFIOFh)A(68AJNq~uVtxuU=Q+{R1qjhTHTygFt#-My!G?IuHAK&E zPlxRFtgummJq^z_k7xYR^1;Um>26Rpl|UQI&AOJCdrqY0DzR)c;kB{m4vI<-InHI2 zX=?C|9`xv-tT^9Al$x7E6b4WCIp#n&dz)XWSJUEHf~mdI0xHfs%{jo+I}dL<`h}tN zMJ#%BTRFin&m=n442vssXBc7FcH4630WhE0*5R6s)}3*(37ok1BDMJ0!b0ELaCXt& zp;+g^y5Z~Ipd-<5jcOlfhVhz8H}IRZ=n!JIp#JEW&WfYzVSQNvcsEjt3XFk*Xh1H^ z0pCuE%BBdrF#Hk4`7s5xQxd9f^*-S|RY#n`$%k}9Ie4bsU50rY(c5hZf`=(iD+!iz zKAk38h3Zt1th-m!QsKF^sjFrqtDz9Y_O z6&ybV<`Osr6R0Nzfjqbgr!!bP;L8|Szl5L@kZXm0^EL#FP(wJ}UKOK_mZAjY^vHCG zl_bWUTN8BtHl|MOA@Ia>4cu|SMfzIrq!QR~Ix=K$p^eWi{EQQiFGMNh7*bX{WnMbmnlh)UwHI_{G>CdW0!u%lY! zyUJ*Zrzo>FY6OqzvOuQ52>RGA1=&%%WKkQYHxz1~rfa(f=KJOt`uIjVyly z`~`vE%Z)YBK>7x)d6muil;u2g$9)|2`MC4+36TeW9em?wl6gbnMD;-MN2qKhQV=5{ zX$J^-84krklFki6l;dtJdBU_^Cw`(!r=~(f;WnJi2Z=ryp|4bE^_P6*vkV07vkgKn z^xnM(XBd6Zji?W07(MuHGC>bk|H{o@f}Z^SWQ3SqQPkQB585E(QLtOtc7JKQ9k_kh z_Flw(_nR$*=*FUNud5FHzAWmF@~tlb9*zX;ja=zq;WmD+?=U@l?(_C=^ya~UDOmwQ za>_EbIW0eZZ8gpK-0ZO^Fxzp0hAZP##wvswB?wHU56yB45ffl*eWr&YwWPA9_14oE zP6?nrZ;GEg^{DW-sd7<3lY%23DHSI}E-V}i5ILJY_RR4o z$`iZV;YGIwAms_>B;8~qM`3ET)?_OQ?DhV@KwecPsGM5EKDQWnE6bVl%PVDbX&I=P ztyrpksmXKE(Dv1V)j{12v=V%xc`Z!Y8IhH_XpMfC`~GEbx{SOI$edNf zn!M*J7#`cHY(Aq@oPf}ZDNnIdwr|?X477Tpzr{8DG{vR301=)#3U066&tOdtA+$Sb zv9l%LKz&R}nWH{@4n=i2+!HY8dKPCkspY0;;-LdZ_#$MSVqH3vXXgP>r3=HO#2lvN zK^3DP^>E?UmK;FPoH}W3gb6=QTB79Gy5=C#H-L}eQd@+USsV*L3276rJ&~Js%BG0d zK^Y=qzGn2qEfU{;K||Ki)tl?mb}3*cveGhZJ;eg^e7iwJ<%b{+H&^Y#9o8)B$LOz% zzz?}4?jqJ7?oS(Zz`!4ZHk6SJo!+Bt*{oJB*p;|UFs~k3Gi+><8j2lwhP0FL_NPkC zgz~bIf(_H0`ZnjGQT}D#pESN4OIl43nF@mkN4*ZYb$$;_W~sy!?fGJg(%)OTz$Hz8 z5L>+5k@*|nzfPY$c?^r%3whAmR-D%^LcOXcxEez@idOS|($MJR4WSl$>W!tlo7!V4 zF0!817%ngVuq^hPCiS~Ax{iH35Wa$WA#j?!@X?Tvi#ThK@9D%?$UGo zF@Y0|9w3tj1A32lcBQVE(1 z4Wc^K5wuD^a(%+u>)8!WQQ!ugTE0>BFH1P7nOb&*Y7`L6jh0e&ry}Jla&|`gZ+5F0 zyeAsgc=pF)DPLhJQE86t;!-;VA7*B@3Uig_8|)o7U$BR;3cKJph3|OaDoy(!&je>T z7&~VOLkDsGaJ#}qRD8|RI`}oaT!+rh`{7V$drW`Fpw57Du@aoeHaJ1sLd&xcBF-0l zA|2J+*j;D@+m9e0ooyewes2|)HScDI8-!Ny2~(VvwIxUK|rzYjk&{Gm4_*A?>NfeU^M7IT0lx69RWTjK65eF~O! z8ox?^-E^t%DRlB?q@ zZ&C#_7I%rM6LphnB8cbyOLFu*(&@M*(Yhw(js58J7r}Jx7$sf6)@Wicg~&10qf9js zlPfknjo~%YW45m71PZk=zachf9!(+Zk|+A!=&$TZ4iDT~5je?pg~PhdkmPNF72YPX z^^{u$`#e?$_z_~o>lZS(n*C9g6{MK)RNZb^UB2!fRtNO#N)EPWk*iSjTM26e#LIYO zA1#p)CN6D|tqB6}C)uXz+rKwWkzd61OdGUZE{M*f2fls@Wc9J<3sniAjeMi)5(U|u zepwh7#>)pXiv)CaZhUeovWZ_rUuI>(3es?NE|UZO((jl4xTy{xaGz1sdh_y(7PRk& zuNX&y)hlJfE!BKoGO`VjLzM3E@_)OGfw0UhLWh!yJ%vFygTYJIzJUG#g;~re<&(d+ zZe9ak321-Opo>JLb3b%ar+YfQ3zyM^B0?8Lc{=(N z5Ow!hACD(L#WtP1)-1>TH*tQcsaFE%^ruC&-5q&DjU)ETOFZFLkFqN4&a%b?5{ z%Xh(QsIwMW1McxUNKetBpfuDOBE*@Th5J}-AYeSEQU!J_GXWvnWlZ)~8(!HZ7}0N+ z@dp%+Ct&kAmrzB{k1#AIiSkWV#zJPi)4WXGH{x9cGk9~89KBXI6WB>POjv`WT_6F{ zQNC@H$sYmlg||AyJf?4Hw|MTcyXK%e(@g|S{{2tDpOg4EUlH4rh``viF5Lo80nx1e ztTA*o2Zxyg@G1|DAgK(&DDuf{Nb@Z*44p}i8g_|8wi@aRsv)10P>&r!5)Zi-&>hAaX0(HdyoW1E9`*jY zy0+z*39YeF4I@9!aQWsScOhwcG+AD-7D^855tr2Lt2iMZnZrvv~A&^<;%7+xR zuD@$OQ2)S@+}iw{kS0^aMdC8^99bQ_m<1oX-n8|lGGWOwgK+j}gzvsMV-y2+9XmFW zd@zxa-)r?J{F+H^t=91&0-buMXNM2a%uPP(+FQ8x8S#FvjM9;-n3b<$J!#;=k(Tch zxG$dMYJRI6;vaAcCI$131@QU>)h(L5oiBdv7|WM8{dd?0jLwMHcz&i$0Zn&bCuZgd z<`T;4LbgWL0hyY?|DSaQS0s;l?pq2V{kM&Z|6^VGepLSR!BBfs)-lKQ>*;pp&=GD=ir+5c*#Foef2Siho(7COK09W`q;kq-F|c5_Io0?|8XKB*yop~ zx*D1kI3+>78VT-`4QwQ?n z9_baD&u6YgpA0h37>Fedb!PX*kj3LmLC;) z9$ZFA&Z(@$T2&6J!=R*f)TGtD7Dp~K(jE{k_Kc-6!2K!b9v1 zdPD5D1^;w&N@k(2`WmlSdH@trM;I&i@o*5z=;2c%)Ng6&d#a5T$E)dMgCnjbX0P{I zQFjt6p`ZDjJ2tCB@0ghznzV>z;@HmS&0eIWo2H7GY&oS>HXKp;f5WTN7zP3H&Y{J< zMZ!pUmqtiq-0n9-tO#BY-ULP<&tFk8XvBTK#z!P3@Pxc+K%t&xmFtXApGQ%lfmiZo z%#7xtMbpp+Dq&s0+0axyMl=scVKgc&+p5~M4gs35H4>1|;y{x!K{AFFyxcwO@Lnh-dB(K#jmzEPmn5a8fQYw8oECpwHw=&dx zp+Hr*5v7tZMNj8lrTq)l_SWLG$!`7wH<`*4q&&@J9{fUjKr3O~R-3*(Y9>`0l@gen ztw>BqzUZ$E8;<1AGo!=8t4fZuIzZR1NwX>T;5&on?Zt~@FY)hGvV02Oadlg2<8Q$l z+!G;K@fIMZD{|6F%~5r755RuG(EPxhG2*c!L<1BJ*h4|AGwQ2!YUn8KOp8w70aX^D zt)|bHiLxK`$Hhnj$bUiJQuUV-V)5G!3A4=(U^EAW*ydHA2b@qH1B@1RC=x%k$6s)b zZ@jq<;jK;7H^e9!=1@%|%{>={*X1d_iSF*D%UrXw+M={}sBeO5^j(}zFSNwA22mIB z3FUz@L(uD;XJY3F9AGoy+umz{?YKfK8sX~0tjyIFkg&#PNuGbaXm!>0JY~-Q6{P^* z%s=;g%3So2PHpr?4E8ZtGIC$AaKU^%6nR{y6!ACD0CnYjH^B=S0L z$RpIh=gWlkBl7*LB=vKE#y2(J51c&cCt(&tFtq00Fy>yLb${r@CO!1f-Sv;Iqkmd`JdSzT?b{A{^Iki&t%>VFMbJ5dGRjn9l~;SR&CbkT#@?_X}F^iSW|?d@7a8 zY*RRSBWk|nj%JI%A-}j0U6bDReEsrC=*|m!hu==+#36SI8qFuVQy9^1v&CFO7IXyk z?U}Zv)8`Y8%@SI-8OsCVj-b`YcJdz2HjRH9I<6_Gcs32QFZa@UG-yA$H3<=(l<)DZ zwj-ws;HwLA*YU+lJ$|@{1Gz*B!i0g})`b}e!4Q&((M)tyV?|8pLrv4g%!~EtMs+vM z1*qBdvA7I-{p=cA0>z$=YOdI~ACnrmY~qPcMk7W3pyDddF>00!$3S6>#9|!jbi{bv zlH|7IUUq}RY<@N4VtQxt$^QdvM{m8pPYLCP<#!9y(nwep6k)_*%}dW}LpG%F!l16i zVnVIe8r2;&jGMX_cWI=mJY>YcG2@tACy@Bi$g?L8q~G`?`0`JUg-UM+r~Mlzdcgh1 z#`3$1_WQc{ZyF2g|AZCoOkKWz0t6K02gLX5zu!XruWk`BH8XUzaS?Shv~e~3&IkPu z5D~06AqOmoPz9EPs)6crgCNRdAyDXj8**EKO;3Vh#N&YNh!JYz=BUATo7-g(MTYnO zgMXZ3mw0%HW30$~UX^uetJZ#o{>P(&EOGc6${&VYA+&ZDv^oo;vB8FL9zst7UQ8UM z{pMFrLdQdWCm}hwnIx1z>QhMaLp_1j3RYiI8#aS2YP-&!)$5IcfTqkI1pP9xZev3* zN@M`EXp&5o28Hp`U`8<1GGrzqY%KUA<8^&JHV2`zIL!?z;f6V3qwXKbPS?3(U0Vb9 zv!`C4&$!j+6}mnL&rw|=&h?G*gr0;x*N^tTh z=0NJSM>?@-YE?4ncddzD;Hns2`Jl@7>?O5$Eq=wWDGTu~EG@YL?ilz&V9Vz1d3)cx zxMQGt??L?dw(OrbaR1;5hPGW9g}?C}&400W{P(#b`&V-#C95px;P6j3$A4lwb#Mc0 zb1b@=3ejPdox za@B8-?I3IIep}yY>1Dy}<$y_W5bnA1^yGxw$K8l|6Yj~lY=dt=ol*!HH5C?jo;9K2ymoPrQ$r8tEA6D7Z^1kotdMYEU4<3|+*erx&1 zhb(AD>KW(wHZEvJ`Z+L&zN0{&DuQpYkzV>aLbe|L;slAijbyKqE#1~9`qvlNeb3xa z`+aRgAM72-gU;t1>-)DB3kZIQy-;9j8Cq2are%}f__1OpPu7jn7)#*zLqt~{S(Bwr z2czFa+YY6_iR9ZhX6Lo0$ICR84Lr$G_bgj;8k>xl%~(xOhl`9$>J3cZ#6#a;Z7G~( zi1!?}XGfSVr&fKfm3FD5XUvr{nc7)69ayUt6)**<(lYks?kgeBunQ@iv?4fxY2A=Uki>^42Wy64daq3ZOej+=1|4QyG;u?HYo zYZHWsD&g%p5*Y5&MU?RI$jo^UKovVZs*~nh)(LD`Ye(%!EHXc`qQzIdlQBH^Sr>Sjc2IAZS$!8xWX0 zMphQ#IaSA`lZ8g+&H-_T;e<82myvL*{4O(mS+%W0b(~Fx>oN@yb+G_F9>!HenG~kf zSR7&ZOBRzxLrSg{#acvJ>UAyu0&6S{>SM-CtofqI=u}FoSXqM5{+FihIm=4;CiJvY7$Zy?Q94oa)5 zrywS1FTsi0)vauBJ|Q3R5lJ>2OsJ3^C&2;cGb!#usL4B&O0p!5`~6?u>@Bl+i4}sq zshFwRlM6*E&~ju-FoO*K-eG$a7~>%GUeuE-Gt`*82M+tvg2Vlk{68{FZ24vzko8h4 zSGwHr?ylD-8}00C&J!tkrcp7rfmhci?|A-k@qLH^rlg!}?tuvh5%>sY+#*L>8u17L z%{(Fdtb!q56bIjpQc;QsPO0!36^!KSDZBb{(0jE^IdC1tXj93Z9#-Se*q?L`!m)-Y zAxw-3V^$H~8jYgh@Fh$lGRG&2Fx*!xPe=G182$XF@6ca=THeKX*BeThi(Xx<8}&wu zBk@ji&X%Fqnqcb)J9sy_>e34zJWdv<@ALLO2KS>k4i%;Bmd&^)FS|i>Rpa?4LuHCG z>42sy(jZa&RW1Kes^=|+;I8_cZU#W6D^Lg($e{rt!i0M^l%eOx#hEbbvp@{~xf|zJ zq%=mU<*H(o7`H*ku|88CYSqo0R9A2smAeqFs#|vMycIRKJK8(K~v8D%=CA3A%+C*RNsF^xl3AQcf3KO_TCI8^U zt7cKomOedq%o!sO@RnBVDLYYF!Z_1j(&3s|uD$mM`kMsdsO|i?q#r#brRik9fo{?1 zq#ki|u{m6(@#e3f2&&Qj5mHzxb}|0w9*yxTg6I;NRl%j^m3=9Zs;s#%k(S+@K}4dtd;&IwLtnD20}TaC`1GTI zLz?lhxXlk^hMEu2+91bWAD)<9fVh@Z!|>;~tGxARl8*bmLMRJY}* z7Vl;ey5|f?-WLa%?_!rS)ps&dcQcsaLjZ;7o-Qu35n^ipSybX}+SpH@Xa0ARWzf?+ z#Y_Pup62gHOO~tbKom_6AoaNpOSI@)i6HX@jlqUUbHr%zlp7l?>GX!lWxGFXEGZl7 z)Dmw*T8~3Bh0X3zhB>(-OWo-83B%Sz<1F9Iu|=4Ubgre&2xAL((-ORaEK|dBxYrg< zhOsSA{>}-^GPna=Jta+8^HiSf4bl8`!C(v^kU6YTAoQ4W&jYHiR6N-mYBdLM3%z|? z^*U^3H-u|`8^JT`fQRkZ$b2%xuMtQY!folI5$_axCmQz@4LkTr1LHW7^7KW_dQqE^ z=*1KQe(>_8&E`ytq80;N>~votT)8Jtuv`hT^@)8SrFXy10YZtk^x@L=7SJ5A9qtGY z43XawgWRiT({xQ?|6T{tu9rt^T5py@L7)Mq-8$f(6apg0zCEd;t|uoYa=- zFu#t$J_y`7%s6D105?wnCe3^|IV!N;blOw$Cf4dw>TSCT1yOzABAt2l9h`dlsQmS_ z3o zhc4|A9>o$tq(^li*0L0m8t&l<&2)0K>FN${xrIZ#PI5&4<8i0^?$H|dG|KU_An57t&6n2W$93ur5S~vQD<8@J z01_Wk+DloGCnWyC+z93qIf+jKudh^pDdv`WTG@&nJ`eGuBrvX*^qlhms7nA3dBbQ|Ug8In=3YYBGWtXpo$sofFNh|e_lJ}61#TB&wvjD(xdcTU?*H4 zwgd-QHnjmbkxO*1UuLVSjizv?F@;Ai!w096B&AStOlg0U!Z6Qu$|-gYL$_xPd2(y}r!X$EdoO~i#vKZ|N)$JFCD3}2K!@u6vXjygD z1$Lc{OVpqj6?27~(qQc)V7bEa4{T-{V*|zEkXy@*m#crYu7HHCRa~0MAeX{+C zz)*)|TAs;Rddg>}9-&XUDKyt+;atW|xTWUBd(r__5O+vKebpP*&FWo58f5UQ;@xwZ z-?ktRvWn7WBT+tj#hdEQMvV3*EM)quWGW4b%`W1swCYE}RxL%1p;aa55(C*JE}G=JWqO+InnP}BSEf=wTjyr!tpFp{!TTz4)i6A1Q=P(1|9#enfe`w90Dt;(ZJ;$ZdX=Ni$xV23<1IS~roQCokoJ zxqHXJZdL6!6NF{-fZpHqfQxd-3T2kA@6dR>HUZC)UG=P}Z`6eHj@DZ_*jB;wuA%%} z?hJFQEzg{qL1kbs%9S$)>ie*Ou;;=+e#hT#Ph|@k!WZc2G*UONyA;h|lX72yBf$Qj za2AoOsxJ3u?4gqxY)hw-#I=>88{=`fEmmomk;bi3Fs$>gjJV5CUfIA~J;RqlhV$U% z<>mmE&2$4jeB~Q;ImIJ5wv0QcpKcz&GmjG_d(0U(7_tiKBx)0fp0gRcug-wotr@d>Oa(@Vmja9%eB%KxGg^+`@67-Jf zB8P{Slhua&Vn_sXnoOWM!}x`*CAl8jWz`MdmHgY|GT$?^WlYx1F+1a}-;&J#V|lnP36$A)G(q*N~A zRtfT8+lVsCtdvy`e(!f6{s{2nimBH6gSrAK^+nf;SJUq-8hzznOA z_SWUt-ZGZhhnc{45WgK2)Jlblw}n>Y6l^k{!wD+Bqs);=q%WROyzdVV}M9V9ZXL%83F^7ml_gM=#SrYK8Sp9gspD31Eff)7|vLnLX z!nY3B4qfH!s1cFfePG*!8;K26yCNtHy2&@BgLv`E2t^9`>7{O4;OdspC-&(U>h8QkLuaUIM{d|w)Yu0(y7Lkxmav7fIF zp?>Txv2Wbb1XzL59&c53q(91cbPd^>3E4nWvS@fm%wPO3$%y*b7v5RAw$szvt#6l)1nhv_||;SWt@HxmP`?q_~#% znnJ{jpG@m%3I#GEVwgE9jnqcOCM}->AM_V*NOARt)kXFzv3g4OLy4>F%qVzh75 zVrV^dGRReYv`5&o&VMI9$HZLkdt>DwA@8i*fB@Y#8Tg6~z`IJlON=yn$PL|dx$R|z z;9GkL4n%V4^~%Yt*}H!Zoq| zin^p;jpH=UuAc`?OqGI*CXcw99B!)0X!K7z!YgMx<_R=#o2!g?vU1bPfRUe-(9 z7G2^u%b~R;jCycZI&9wIrmvtI_eCkI&30q)>=mgGAgV{C98j$;x)uOwu-)LY^CJM) zy}x4uurqC_8OHY*=i;Hbh|_1)2EYIt6Rz>Futd_CQ1Ls94)1$wS-Ma-Ge>&b#WqK# zy_K9@%SN2^CM-bc$V8}{+airGniV3+&H%4z=hE!=I^rTb>$n&phKmnG zhFbTIzzwYTd!c+nZ!V-e-x>BB=`41JFyna`%x4%fRGXX|UUvHqLc$VuTPArb(GA#f zN)U0;#~*ytU0P$q-N~olzCe*-O7NBm)CFf*!)+FZ*cDiuL2{FakN|c^y&-KdzhHf4 zkc>Ym0$$pE%a9Y0hgH9stFt`Jv`VaVStYR<5$El1;7+j~bXKMTk4X?>-X*AMRCE|^ zVbVrVtx$GjU@7=`ln={@6!UP(1FclQ`fLM z!w8WTlGE54gK-i37|+BtWgaWPa~-0R-DJ)T{4r#a{z~E)nJw5+HDp3vmRPVia}mSv zM2W+^V;)2D7rbiuVLwA}tKF)D_z&!yPPjT{A2FP$LMay!GvKn`^$VZ3I|PP* zFr-Hmn?(XpzZh=tKFbN(BS`H^Lt~=gLNus)P$gy845CNrRZq5y$!3=P-@8cinlpf zSdF&^CuCxyV!MH$qrA%YSB@e`LNA19OJ}pkk(#+=BIuK%x^b3jY-y` z9KC?(R`R^Zb@Z#47cw89t|Y6oZdy`LL<=-XxrEvWC3E{OwmLmFWQ|YXic#OmHu!#> zMgH-a+)@Zl6?HTyKS`yZ*dT5MK9?qiRQw#e(DeMxrqjsK9|)a3FO{O-!l`>;ilYfv zl_QPC+|08F%4}`HJ18Ho)BW4RUpEc>VG~t+K^EEPvX;cJ>GBwy`1z=8O#62{nY;Ju z{byjHH=+L}&aJMYcUmKhcd)Pa4DdR=!))MQ2SOe0Q#~sYkPj5`1-An~A1)J`?arv+qnNKE7`dcBWA=>y12s^l?RvEhe<*^xS`J~(&Cm$>Ug*jB{(0~5J$r;{0Elod&k^Ex6e30Jtq7x^K%q#g1|LQP*qpv`fO#f_lNKH7y54U5=nrVNhFj+aP4 z=R#LEC6>+%bZ8-C$>~*x`R0?T(sxZ$M?24C4hX2_8zuK){vd`uW9YJ#lL2sjZ+u1q5;6u?9}FcN%8 zu97uK%*3M=GjbydGiA^f9SQj{4c=N3QZM2Os?|ec3Zn56M8P>2t*Boe03_ zxk8|q%tapT<}Z{F$c_seR8O%H?-l&+29v)yTrc+TCzE^MHJ+n6o=X9f=YT-`id7n~ z=Xmh&aWJhnn68Ily17US3XapPuS7qL=d7`^P_$wBDs{9_`!D`1_CBe+2Bv6)8a`QynUo z$q@4A5B@L4&M`XEXxq{i+fKeD729^jwr$%^Dz=R;so1t{+qPX{r8?)HK6l){-S>?C z^^UQBzhkep*Lvoh&CqPRP){g)s4Hl75;Z_(v^upw`|qX*D9Mkv0YPGm~KI=0gU%-?G$C7 ze_00O)fBC#@PVD0de+j+xW>+{eyZiHWdrsj*QW}QZA z#(*)=Ktb3#nwwSQl+eZ49@S=&rfukR0m1nyfwUsCwp=mk`%^E ziISFE2~89&CHbNlK*bk@f0av8!;D!T2Q)JjHf9p2l)+g+$co-5iqjg{_pw}#zTmJS zBcG;PU_$Gl&2{CRrn$MAdaFsCCRo8lb8i{&#PubxZ$55otI1P#paw7LurI-b#;E(y z)m--->s-w!v;mJzcs&zRtaz!sh@QP;yL8qeW=Gp~?@F)rQtLS4sSt`31KE^@p8ocv z;TFc0eQMuBWX1U@rokRFZ_FjJ5eUe1M%#13zob$6K%5^YCXhTSDKIJ#t)%INuz*#&VQ_l z4wo$|oDQ9+(!|V>!UFCL^(}sGX0cHXV7vrihREy2PKBCUaNKH)1=3Y}&V;#4hI z@agV~iZa=+T0O;GFDf?+VOhB(@S1^L&z%~rm(GzjN*1UAmQR)1WeTQ1yL3WnEZ_`> zDiMeC49W3xwAri*Y=vg0Ke?|~H&)SD&wtTitXDgVc16}2tV?*-XkUn!%tne075800 zO|_yVN00u7#hr3${y#Q=T`eeicHxr ztUWlcRV_-yR%YqbJg!w^U6omF4?FyrvS}-y<54(lJpGoUO@=Ljf6BwU#-@Jjfg_hc z==iYO;tAVn<;Z;xCR+<+A!%}$E3>9VOjy*(Or~yR5`epL*sp53Ev&l@->BjhI5YLc z?~o!plPR?2Loq$$F>4d(DYU~mRwFfx0v5%cQXh`H)7eqF(a)4pNvICXJle!yJ{k~t z{h=qZB9~3gvu7O}d@#FgnilDIpaGV46xu_b1QR1{XZUDs&1dms*)dE@HTkpmrOjx$ zw1(~b({F?ZqZ6qV)*b^N5sE7>p1>Rj47E=ySvHtg_R_~^-@op#hi-FiyptWAB|f&o zj=8Q(T_?=ph1GdFXSsveewmr@);9L&O*E_Nik6Fr&sOlA@_mylPl>9AuJ%9haUpE^=6pSBzj=!my=yb~m3D?QDs+`->F=pF zQ(fLDosW>-)U9bqEYX((F=8{M`lgrL%H}B+grv65AFUp8#k9*T>RENcL-ofxsRLV2 zqV5yn9npoX{o6_AvlBGjv=gc7D)*T^l$Sah(rK6Yw@}+_3kG`5U5o<34G;+r1l~@~ zhf-40EkAy5=GE}9z?g6~9%nkGikDMjO@ACxi9SmN z+P&YiBazh%kjcNj@`ZJ1>ZM35TucgYXWyFH?E*8rJ#u{KGk93s$EPP{xo1GmsDF6C z9M;6Df57w}NPD(`7j=R*ehtgKDSpX(ci8MhtJLPLbe*gSK3U3xNe(+sGXPkXx&Sa_ zimnj&z`wHTh81yO;|i#yKkGNi+*#V=}r?)!MwS%bIBbLlY)^*t~G%l2!NGMZLM=f4#jon5hRG z@e1&TAa;L_)62u7Sb9SO`KC$~wihS!Ih&_;%MrTg5loFw9b3MZIvjW971OzbEn!PZ zeX%6aV{_B8Qa~rz2`LWnVTCP~l10d6xaGZXOOWTuNzZ)GlY7kNbQMoq&zp|qUF1Q! zp6a1l8X!KKS%UhIGg1xDuYY%LumH3n%B7IVSBca48;VzUUoo^>M>r_xtAs8M{)jcismhTE6dE16hQqz zrj|7e=9(H&KO>|kgC!VF|GbYz_#~RgP;U&y|9jMP>b?)A$(}tO^>JWjx;>3tH?OoG z_%k|@dYsp8%gJcTg5e8@h3u0%(uA85UDCTE% zw!q1b-rhDryge^D>MgFDB~cBGtkZ0*aN4!0{hjQuOO&w55hhXxjx9900Cco|V@}B^ z#ZUczw9dZun54&F^cWxZe{G%r`$&rAe{7xqV;J?X=9%R;|lANno;fOkBf+I1aabf_1;{ekJ+p z`4|@Kh?%(zcc01ZJkBWgB*|tbM}e_8bU$p>oNRpD%`o(UV)q{~ZVzg(utrZDc(L&J zBS^k$^ideH8qx;Cz}QR45l3sMC!bz17CVRwDkYW-a7M{v6q07c6ks$Y@?sSqG$8Y< zJ8%ShLckGi!Wk;|{Xy=ACZa>&Q@AY(41+g^mv&g8;|e^%UIhy!S7hwWF$}Gs+L#(~ z6`#TM*lc_#m~f1Wdg%n}?iuwvwx%3ha|%Cvgp$GPU~4LM+i8Th_94gQNOagI5T(~T zXXlZbYWOs(Oggv=&ZLV(6kb-^(Vtfn-N7_uPB z4bi3t*eE*L#OLFlFHE+~D?Nuw-}7N(=Gt`yL{`&yv5MouuCkMPGP!n2ov4 z+}^T2L?FZPO1?n$3@-r&-HYD|}J zim%&;uTw1r6;AlFx065@TxtU;Gd^4BN^SV_R<%>^d5Jmsg}Z&cH5k=DGRjm(sHb^h z5x15Xbh1~oIa;te5S)c+C{lt24#$jS2B7t9lH%zx3BJ6B>t}M|G0uKV@s7zL@t5JZ zZW~8|-x5uiau!nb91AHZN-py)okK7(TZi*ue9%~swH|| z`lqmo6{oh;nIosyD`$K1B$IiFN{4B_lgozZmY#-ZesjIo9p@P^jldS^T2w-~1FubD z{T3x^*k>||BC6sg3%2gQFh{|3-U~`<(sDEw8R3#)X!E&_HbvSY^43R@AMXw zWi857G{@_)kX;G6a=!C2B~`3p7kKQITic7>?$*-Q9gtOu!Z<|>)frCi>0{BV&?$SGt!p>c-2(rR=2oWm%T?y$VY&@qw zy0$D8Td5N2Gg!(}DblKoi#bJf#TMHxx**ha)jPRQ8akm&6XGBUY-N34x#JXM1vD#NTY)H28&-uHtEH0GwzHNKksmdWl1mv1n}~^9JdE)z-zYcY zK~^k5jYg82C1|J)1eF~Kp;_n1j8RMV3UJksw49-;@cqXiq@a#mzaBs({t&gJJlh$uf zlk&tQU*S?K!>bjM34Se-+;4U984OsIoi0&_3MNAHPlCRaN8jv;zP|B0yO|VLPPn*A z)@f!NuNO=>*bY?Gh8itPXipnXFv+Z}Rv8P>Cyww*yXb(DnAF!TFu$RW74C71Be2+! zD~TnR-6;H$*71j7ur9TpE>A`^B%N0|$JK2%hCkVqVA|r2S>x6d$Skb0ebXKo1ZdiUoj@__@7d@gDR_LidDEY1|mSxRN55p)! zV)}%$mTB9%by%O&x`W&{q>f?Gf1kze7aASg;vg|H1F^#}%jdB_5ET$wCfw*wrUVT2 zH^gyEtIKk|fQ`0JXOpsWsJi-<(x`)>`D&FMs$^EJf>P6tQSr+-0LUd}+>&zHl--yt z^C@ZS3Mfl>g*BR%@oq7G9-AtlR<{(QXQx=Wh(w`c87Od{Hy}$oCGdNai#^mwX^I-A zA1?p}mJ3y1P2JwB>Dhb%h>}E3(boAy;u3oBRHDlk%i`m)WJ%svj3nYJ+9e~8LdJTe z@~#iRktEUNEpwB0@hi&v!nFso9;^GSO~|JhY4`iIuc7B5kA}(VX3e>&RPUb6C3FtQ z%iFN&^V*)slX5nskK~+C*+C|r5>n9%!EtWsH755v9=-UTrI=ti3=mj1M-@S~r|!I+ zhOO7?$Av!Y9?Nq~HJ@nf%k3XWXy>hv;97AxDqg89#^zcu4qm6ur<-F~I=D*eatZjN z0xe9Bj{MNYXjokTK#Rb=E0W6ISvEE4Qqh>bqktQry%DMO^@#w6SvP~PbmsU>R|A0e zp-K6dcwWdM@$OqqHzt(rwS;}A{UE__il{(xVz?57@4VpOBN;ceAHKE5WD^J@*qzuX z^3-O_yMmZ_i244wC!@7Gm-e7l_|D@KkM#asy_-Y=X@x79x;Bv23$o9L^JX&WfquU; zi}QwP+Pe<)`Dj-sW2gj4R^4O$J?rciq6_}ocS*nA`5{T67S#wXO1(qY4^RR z6VrQS=Ep%zKH?uv(2{}xKP&tnukp=R=ykpTL0*v0#QYoz#fT)X%!>MxE{cj*kZXE{ z6JJOSEGF4@kZ7KW`r>lxME5Dl#L}mNK)7|$=%m=@k%?@#OsPQ1l8kXfAO5K|5VZ(2h)*u0S1g@?AC-*r{JT4&=Qyi#m1mP_OCiPzoBowy>C|{s3 z7H1C4_2k1QSKKQE=M}y+R!9B;bV4iyk7*q|!;8PE_9L z4%77|$gI`&Eg$68{+NsQcPl)sHNQU1*+U+!eig0Jt}*4$m9bvD!wcC@UVk|0J9aYv zG2%Cxw|8r?XZzD5`?H&ymmh!lTt5P|KEwGxBFbLPH{*I3r9UIZZ(FiSc@vHvz`4iT zh8MpIgp`DFrh?^06m;Zzp_L0RH&zv55vSj*1k?cE_(J*U-7(#Nn!}SddzMG~y_@_%PE=$`U5N z>cVH^XrIR41j{lcy$Zr9(X5)9&!8w*um)|jZ13B_bVIvD%$eYxvZg?!sTj>fpLTo| zwB+Lp567#=MA*_a5j(HQ60{|nVzKPFph^y3f>RHz6|eNhiuMHwHE|DFiP+D~1h8^S zxbif^UEA(CC{+M(ogt6CGT7=tawZV)eMs~B5Op6b2H=eMhge+$Cj&B8hR=^kG0?Wy zA8OGcBGtZ3KAhfSZT~pZOu~}T_Y(sp39uoKpECZ&wa?q^i4?>j{aSEK)Y5C4_L&Qki*Ti3#&j z5rgNbOlXG8{cKPE)vKGScDHG|(Q!xi#f?sP!JiZV&pVu-_%GhbKu*;tY_TIJE&8~L zqv$}2z}th$&g`U;2d6nuY&hbW=uT(|EKqFquSq!pSDOrFEA|h)NObhmL=#F%NC(LP zoSzuMF^Pc_f}26gAUX-}NrE~<${@US&$&>yaFQQIf!0bd>Vh^SH9;PbGmy90jn8-x zJ<7M<5Hn~#q63XE$)uE_hDA=8J3?hJSM`_-Yi&M0B89dCm*kaglp=Q~k}J+{oHW_w zm2}u%u^9uJ$`%=(O`XMYBf7X1uU9tHR$G$ch49Y2)fi1KYJ(5M9xEa`SZ+R}68h+F z{+5>pb(D|M@zqvg)#q+%t8J_oHxqS9V8LMSw%^;nylJ7syhcZ%``cCIu!4&E(;35> z>N?|FV;WNl=O(i|)x-HCvWx+!$EJpetqG`$h1GBm3VIE+eV1GA5oN9kIEZz}*;y~W zZLgIzbv6Z@H=}q7=@ALR74w;kwqJ1;`E<W4S;|2glwR69j`b?kl zkilIjb<~nsW;rWh&8m2(d{wtcrQDmqc)C<9oma1Fr}le&c=s|o!~iA~3y!iz_Pi|I zaZ;zsDYL{hdYoJuqs@Jao%9-w#AdZFIMajpJBajrhp=FQ`9%UIZi1ET+_oPXY&09YEIG0vV;; zY?uxmAOf||@Tot;IiR)GtYIy zZ82_kA=Ukfa5f2O?;XziA^PMiECs1-Z=L0MQt8vhh${DWJ4<$bF$i~s11SKCcNCua zZW#WlVZ}=u@3?We*~0G z6`$KrJ(}s_4=>%!H;yH&xYjZfnl`_OvH8Vko9Z7nk}hd9lT$1$o2J(j>!TNA$m1M* zcr z4Jm81gNab!pua3H>z~rCYW3KDVWXG|UX{9|C289l^1@TEA2|T(KI5hGm14All!0(?D_3V;i@qP`2I5!3ww<1(hW0jILyJ}jjv@9=@jv2lMox8uJ3 zKqxJeEiIT@{h?hOf;%TA8?MMg@Cy@Q&{lc@$LF!yH?Jy4E+57T8RyJBsp#QI82hUw z@E+n=LNA$br8B5`Hd8)NtP3><%+wY~s(i9GLNRoi$vKj3#TYJAQN0gaG@hwhD386g z5&wd!AA4g?MzK)=UYly#!#yA9#-+j0MnI#Xjjc%dwnk0F6*l3VQ`R*3CqSv8l+cuM zTl)iVyKeja1}AvDln~lB+BXss>U@#6ty(B&@Lk!IR~!tVWtCS!?7{$NFPLF*^)ySF z+E_QAd~s7_034C=HyJ_9)Gt#9%;)Is{;s?#7HIeBUH)yM=Z+KG`_1tQ_;pUShE@J? zYJs*8iT)92-x!_#-^|O1q4~gS)r=*)zrJ3muBNOMA4#_yZvyg^(d7lvKZtvYb1A<% zAS7JQ61>^rEvysxCx@!HbmC{@ly@9fqByF}!>$ z94{oekKcJ0r?wi95E^r776vdp!)ct5Z5JnW7sEKo>at>q1;JM3gHH3r9n$4~#mVL8 zhB`)zG0MalCd=)?14Dm}BosA_E_mt?BitnRlA1D!a%~nyr5Ew?8*A&w2W*(^Ti4$r zti7a4wF;}>^69#A?@^W352wi8?(zVREIbhzyO5}gFAp~#4S&k>wT21;PLu_D=JP3hsgg==bC@0k^f0F{+GJ>FMWiz3}6N4)69KMvjz&S z))60XL>iT+wt219F?(>3G=0}=gRK=ZDU-Blz)1rViDzPxw)=2i`f@W{+SyvTTr7e* zC9M#(DyFKnoVcmHf;s^}rMmN$&imBlYqfQgt(j`7SwV^Vaq?Tg`!DzH%3r?^sBie` zF(eUtY!)NN+yrqX06oE-dF%RdR%9NQM!sf zQ+9M;>;b*ZP<+}rK7I%%yRlXnUJ%8RkEedJ33;qCXVpRUAt%k)mvIEfzyfzpf|XzC zfjGBcWuD)UR+fIg{X@DPM>gy;AH7z-<}=U!gS&)n;jIytAQJKSI0UQN`Q%f_`yBh@ zKST@kk&Lq*y26FKt=*7Yj-Am*jw{I-Up`|hb1j#P@uN!a6rx>6!@o@d9BIsOW^O`- zHWV%H%qduYR=s06=%0K07Vdf9?~VJroN*+mnQbVn-%^w0Pe$mHB1q!|_ zcQToL=k()iODpKDc?eW5G_})T4{+^P8E$BbOiTd}thj58@;fiKX z(4Kv6FKFhe+|yZ6zvH?semlpgR2d#Ss`ROL7d!kT2O&RN70RR5S7TUJqVUKmN|gk! zuxiGicJ8M8Ad^|0OTPJe7j%gIK%H0RJmfOx6q3d?!zrq78X!5xw?~LTP14eO09R2O zz>r}o8Qq8=J^Dm4;k*%FVMZ#m@D!9di%jScX{QFLHzDAtmQW3I>F@pH(c4SlV$oXE zPLJS&_Kdw6<&AJT;0sBR(kamAtI~oFLLQ#LtpOlR6XTdTywBD!WH8)i%wcmtJVD?M zd*IIDxCPZ?oehH0@TGyk&I&HaAa7oK0163Gsa->CLh2>fn-*wp81XDC3Nl)|&mKpoTjL+8+U7%mpiEQ72k;151g$1bJmItid#s}o?r^l`!bOvy&kyTW| zf!Xibf(azueYD;CO%{0@Rf4vMcIlf#jz)J6h-m;TLzgk4qwK*bLg;*8NZZpxbZs-v z2i==%T4Am7SSYXfogfeS^ZK*1X)Sx9K*$>v4f=0IY|bDjmrel6ldeibrkaNYnqEz zNvO%A{+3mpX;(Ijzqo>*l|4*rJtE~k(#kFx4=Ptk-c(w!-K6(I)8TRle{EOe>sg@b zk-HMMI4WTui}S>1|FT0p!&Tx9ap3Uh;#tgT5UK)TSse}TTE(Z4gt`dGlYD?%>u4vX z<&ojx)#wXRdSdLMrJ=M1w->s>%$dBPK6)1AC%LfX7qSz7#MTUfv-kI7$`JQ;KuJTE zB16p%sp5KYul1Yx>dM-4M*75JI_7;`3O*Vt?v!528NX#r8oLK&bxa^I3JRp<1fWzpWKZ1Tv z4$v*Y{LBa(iMjb}nuQava1_+3I+SKh~XTO}JHU^x5GW>VT4E;v4 zZA<1T1yEISp-7W^waXOx+MqXY$H*ImdBPnvMBHJ%9b@hzUw`-5&Xx0Oqkxjp_HtuT z9v4=-;I=bSRguYF5MEDK$=-%0pSQ#pPzK(K{nM}=&S#ME0}>(qo}JK`9+5q!529EkK}g#g64}4KmXW~JBW>2d+Z_~5Px-$b0t1@-RgZy zDW9;t)kqM?I^jJp4G20JJi~L|{`#vCFN|(zu6GYuDTgv2~|v0j=!a)#R_ei zv8nG(Q7!dk`Q?Nact_IYM-`DdQK@rc6MWCQ0GczY6z^+dp`{<6ngu(o?c5r0;d%&% z`pK`)vOfXlf4;IyjvQ1#=PCj|N+9FoKWHjQHMrL1L$!kh>;YXAm=9aX4T zFn>Y^zh<`)i@_w7!Rszj$Lr3?%+D0|w<^PQ z_>^^jY=d65vL!!*oSyn^Xdnkh=g_XCM#90SAte&wLfBLM7*HXN*UE?zhqbk8BcV?* zG{$fuHuSNT0Ov3-9mAFrQ))0qVKq{$ZENg6t>1yxJGl^+j-;=)e~6`0L41daz;S)j zWB$y^rWy$)j)MFMJ9R45l_Y(NIhq z98J=Sov5UcOynHb6tvZ#*RJO zPMQ-GHzTsp=(8zjKrNUs74t=zVD${H&cIo`WvOPQ1y+2=X*MH46=w`1Av`y6F~?*Q z{({Lg2gL|s*k*pBj)1E=j~c(7;RrYYThb7QJ3 zRsr8q0XL);n* zWg7>A@g~d`G-@8!jJbPaopgNiCABW7F(sp}N7K}spei1kiaBeX&qyP;67b0tZOHH8&<~M3`U)oY#kqd0G|KZ81-57JF^RiyNr5BwejBLRDr* zW=}R+>Bh&vGoVGj0YMy-^po5bnq2_;W2R$QGz~E;toO$#iy*RfVg0 z&&nL=t?LGb$^BgJ<>U>J<@g0|W#5aJ{zLKxd~sXWauP3$J@%DDA+ta8x3vai!2PzP ziVCfaG0GfPiDDa93>g)=9@(OjiZRLf=|p3Q!%Cvt_aEHsb0zw8ia{bZB(YxgMl^S< zzt{!@)eHy;lGU1}a?^ESYG8Lqq#E~#-d0Ul<7%x1@QS*Gbc_r6ZsDXBG~OtDJLcx4 zi|PJi(7-zUR8wuTtduccTCMW8w(Q_`y&&9^8p>o{E}mW2AZ1^&tduwOHg-%+!W$bi z9jZ>Ve1XuG#SK8G#^p0=u*{%{S*N4<8|+!1w3;WmQA@;OF@(fCjQ__#8L=~2vC?529uZ6ebZr(m!*KXr}>Di#9)X| z-bm6a(St_6R|;=mA~T>t-dV&M1r{t;1zAFB1X7d_?Dvjuye8hV4b~UoMQA)HMFLEw z^*!(MHp<|3?(b2IbfH9BteSiZE!hH&BybM$)%ilgZ*-pb-j7YR$>RWWws54*GasNeg`kI(~B zPQ5YT;QCeUxtqT5 zeg4{u0X?luUu<+Phkf9=zjz+ zUgWW>U%zBTvsH6lm!w})*0uIhn|DM?q)D#Z^i8E8LW<$FYD;h$!p!oYf|GjxV#;+1_5;t5=&C# zPHAlTja}>$(qyOd9U1iyLzAZ;~34UY9oG>h1Q!dK) z?tUy5?PI>62a(Vudi?Xx0kD!eCN?0FJA2T$`&D+9j#VtHYy1JYdVGtvz>EL1Zzk?;5xGo)wo<8pJc6H&;zqp|3PW5Ij&fx4$QFSC{#ubEBT#e1FvsQ39a?}e~~w(2>&EwrYqADi`KUu_6oJpqNZ~_ zHqPBGb7^)IArIU}ig3pn{Sw+Q=HSxgFnry^*4AKiq>@7e5e9c``SIv84^fOUIIAdP zU8$Bk>WaUDTtsZL%MM_6K-(r~^z67;9V@Nc$9L5#sLZnIF!j!84ZtJQ1f`q2&ikR0 zVr$!^Lv`k-8Bi8&zP1_5?WR(!xoA)Tk0+5Y+ns_VgheD9%@|B1#=H`R2`GwC1YKy! zqLLrK&o0e~&k>y}wY`c%Wyi(j7_?v#Lo-G|K{?RqYKpaG*{Q@F+g%Wm-3g3>*RWuV z?c=}yXnUVJ(A5iqgF2T>cJARZO-G&I{QYfy{fNP=y|e7!P=E-ZtAWo?v6RD6J}y{xuIW~K6#V99Ban=9z+sIRYc7NxA; z$>~NZ))q!3XQ%$^Nk(HEMf=JU|$-( zw5T2|wTmzQXmYW*cF{pqEZ&j`L{;gsND-0I`~_$(w+x4|s3(&soL1+f<@mIRi?x`P zZJpMX&3e@DolY0GZfr{fhHGY#6b%Cdj<0ZX0u|{virfxK3}?;aBeRjaH%M?BIU#Wa*)zAtm3W3m1)Q_u6Jc)HM`(V{zn(!6TbOFyzMK%T!($ zelwmzYZWIxncrOdwR|aZ;tihJp#ymlxWcp}x}EO##!w959^7+Q;TZdof=>;O&jiCc z>hJNt#ni;g=WNV*p9+uI7-7%LJ4^HHSDOf!dAMn_LNr`+hILo_M>&e}uQB8Xat>~4 z3A3Gjl(AeYSPn6t@gHu)&R5B0RS;q{H%|J0B^W)%(;@0Sc2qk|r>l%|dd!zn7r$@D zLL@5nZ9Xs_wScFp!?V1A)~KGn3<|MjH8fY5DS0YoCdj6CyU%h@Q8_TeoSm`%5_#TR(PU_}5l{d*#c8W1>y`Q)+`6jZXj=xD3K27lS{mFv6dj>};i> zD&U`EOnT6%yDYFYO2ybFlJEXh8kWlR<=#MXy+69hGoI)|0i>Woo^mxtih4a$dZU+T^9qid$-K(WrzP z<^!aeM4C1YW2^ygvU3i8<9rK;0{B!ZTRh_FB@(5~gB*x46uJ*5KSebZZO>?{Tr|K* zO*k$FrlQI0Y@1Pf7ip+$n2^x$R~d=r#kMVq@Yxi4!f(8#wv%o#=^)k1$xcyTP9ff( zv%i!gtJdrOTKOa!{QIp|6IQVZ7S8AIZ*_1)$xbaaZrm=|2^t2Dw5^SOE>h_pR13rOk+Chf07&mFH^rp%7tP0vtRf{-(q5=DK)}8IkWwFwES*(3=u-~UwxTS30TU9X(Vd;S1z2oT zaP4)Sa7QC4WVnZa&tBL^m;x_iHhBiOvKTX)ha6ZichzW(7_A(5==`Co4}Jm%@yBz? zsHHa~Ov`EQ2Izdy&ac$EQxuM6gg)K8)P*IPAHaA|pwpkddAjKLoQ*2+XpA1@_aZeI z)Y#*2=3;2%4eu2sZVtV$q>p^ldtWs&8T-sj46DY2BRnWlB+y>);~7ePS(Oi_qaZSR zB1`EhbQiQL@qofgy?Ldw)c}-1h^&3~1P%q+CA?T+Kg=G~Wzq_HTAD&GF zQCpt-?Avvg7dLjYhE?43)$1)bxZrAJ$C%gF;bpbooo6C@Qp_+Q9o2ezGh|X4MHTK) zyEmtV#bRjG1DTK33cG1fS&9gYQsD6%2*>Zf74I<~?+QZR5!v((SoD%qMv7 zmBxW`ukPirM-o7!N4O6<5?7Y3u47s|?7(#xR-^z~cJ#nzMm6{*$p z4B=iS!w$D^6$F>xOGpWS+K^u$pXM#d=O@zBi!cy*WX7DcYLDTl4K_eTxH%J~O0lxQ z*e*%Ei4O=I_8HI$t ziSWGl$s2uvPR7XLq>GhF!qcIk-YEFA%ce`2bVQQetk?)|(^OPy?#kix90VHx_4yx? zSFZ!1Q1P$at-yx%*KP28cW4MAZ@ zkyF2+3jJJ+A0+(+9*?+3hLbTlsQF7{V}7~0v2kgAIo}q;maY+aE+P3tVN(PB8UMM+ zPPL-RYAfw;CtVz)Ptk*~`}XS%&p&d+zQV-bV}0t}1yEALsR^qJvz#p;w)*|weP23K z$~My5fq$PD-~&O3XJ{b3L(U7x z6Kdd$Vu@rhYH#zDvWbTna{$*EB%MZu-!)fiEuJ*lWS;^>s(yhi%}Ma0M3bgM3`o+! zF?~-mw{(_8Gq&6(Q*CmMegJ>PW+*M!oL87(8IXxBFQSXWd6$hnKAQ5ll}Y%{Ftf;Z zPE3&ncWymxX~sLC)YjOZaL0?L&Vd7q38zF05hL3);ZZ$@-c~EB?^IpZP@UIaZpS=h z<1{U^moYV+!JN~!$fZ2Zq77tG_B6|KHp88{beLATm09Ov0h<4Y#-4s zi%;dGz?8CJD!s=k#}q$+5nO8!PEatS7r~I#ARpDm*R6oTRl$n1cp9r_)2bwEC9kZT zn`<`k&}76kvXN^?1Ln2ky$^2 zeFCNAX%9Vm&W-qN_bdR*n749SagGhw8-4g%{Y-g0Ex$_Lgo+uDxIHp1JaS;5yNpCD z_*Wr@)&#ep0;fj}ygPYRw6LyDd)G6cv`7`pvMQOVEH))?g+DV*ygNdH(BLHgqU0S7 z{aWBR!qB$0bSPScG8PD15y<5rN({xi@$f^99>6odLQp5`A37{urY2IuQ~k?dHrDqD zRO6RztzS_ih>L0S*ZFFxr;+{3AF(ok?qdt)?;xe4qffnm&a@ zWs2z}#`;lf((}WaV{tu2+lY%JE%g-h!q1FCiOnK4BTElAGwF^Eto=UFnbN9J&^cfC zqm|wS(h>NlkAN>|=F~1Ywu+}(Z(xbg&ry-8fAzJhcWt(ERm z7mc;4yMW1MYkDcYoC8kVXo^O95zQz;sQ^V%p*f{LE(evCee{bF5gFr?j>^M%;P0a{ zA?toLXo?m2C*}%a(xMy%4u=}TQ)G6$eMw>W*zhCPJj8S9YWR!VlH?@XEN#X9ZK1N1 zHm;>NoLeLc)N%G^AJvH)s-4u^@W>10`*gt48Q>fvFE_a`T3=57Xmm_F3hW173>~7_ zawwM$#VHXRVzE>;saM5#XF#SgxR=Xopd6PVq1dBietY2e;#`%1iADNQv)@s3Skcjy zf-72t@E3BT6GA&FjvGza#0AgxyI?^VaB6*Fa|k|;0g2>QSp}R##toAZ6f1*MfOI{R zlQPeMay}UU`tv@wzm4nIF++6zWYvKM7MxZ24-U^?dzSnfFerQv1cxlJ8i-{;D}L)h z08fB0q7VNqHxwe6JC_4E=Xvi|FBYc*Jm+;U%x)oA4b=8-@6NaF(_So4|Ahbs(9cvb zf4mnS#7>x(4#W?>-5Z|Up#7&k@}F?`NATWBJ* zi5*9a4cD^w5X~(Pqto-ZLl{5)>2QJbv)8Ic-Lz|FQsDLCFopU`VQG=y`Jt=EV@k5% zlwe3JU~s!}kD=YY9afu2wVWKH(=-120I(3(2I7u1_VY9^`!mwW!3Y&k`^G`Lx@>eP zjZ=AXY4_N@0yIGPa9y~YKw?X@Ev(Fkg6iF(_kN320e?~2^P@WrvV!_lZ~R6S9f6?& zLZbmFmNglmMhg|ky=X8v@u}B?T&yEP?bHhJ2uc4nU%(u#qmfvQ(R|np6(xmBhL)}# zTKEZS;>AmfmaZ;Z_(^K=vK60=-0W4D#Y>KsPmPUUURr!4U*>BSqL!{gS_p-r>%Vnq zrXg~h(dE_=IIVdMMUkf02~5WMP4@hPAKg68zO^C^#EBbh`QGjZaT3LSuOSdO3qR(JJkV@PJFm=M@m;Ox`*TNF_4tbq3#g0A>nHp9+bfd2Xb zv`+vQefmxj9ddnF!(nUzRvnNi&pJYTJTG59gSWtgr~K{FI00Ls2!BO$cjd*rVJmPT zkCAet3EE`|%4O#&Mu%Jh$ImC~uaVh;Cpsq)wRUs3kKD!KR#1rdH~6KX?fzBPZ~5+O5J{xG+wx$=tSl&yJKIs77(U**1nSVS`16>9iw+bHv)4E;``FOpu^ zSXHY*a9tDX{Jou??z|TpJKb2B9mPk?)}$rlhBwGl9?uE;WeJDa?)LA~QDB*aR&~%T zTF6<>uGwHi$`uyGeiG42J~uS1>X(c(H;gnpLMGOS5AO|R1`K(gWa4a!q*!7_xjGd( zBK$R&9yt8?gV4@pI)Tsq%Q3QgX}zTiD4ifI?Etl5l+a`Wh&~kh##o)^=zHi++Hp`o z0}oX`u+-VAZEnkNjWAN_17HKBJlSRf#g{}V>c z`tSPLO2$^-gJA!iju8FVK$yObk=1{5+$E`)J0dG1b7!R$hKh&s;RONp*9ws&7_Wj6 z{}zBnOd~QAP*bW+b01?mJ;m|xsK=`S3HN&&E+$Scrg{Wb{tnOTsd!&Ew|9B~Gw155 zL1y^EkKWgthgpu-9joi_OL&-e zV%6{yGYBX(;CcKhIR*2~G;oOXvZD>a?kr@|2@b}kY^Z|-u;6+UY4Ny}K$_!?PHZ@S zNPHwvXlr===<(eH6e5LhvElLsap#QXKU$9?UF6F*92SqTl6!OW*QIwB z_hwgTg(81&1`~tjGP&9BwP`&i+4I}N-|0^~#fCbuJo*aXlrX_bfV(I5F2m#uPj z)pNA@j$=LQf>l_Y%K`w^QX?5BZ<}R+VdD}Sb09{kOr{3M{Exn$UYw4|5c!qoWhD|? z2dsK9_`)wuVhT8Fvt>I>Q>Dx$J9yjLQpQ#@H>8YH9!W&z%n|S?Z}g?v-@ijhn9!(7 z?w9NkE-|;_k8JnjLtO3D2T-(@W}{r0J42tDD+fH_zK4>*BTPuVOkMQ&L{5|CEJ_%E zz}47lqd{A~K;cfzkV2z|Iz3I*LM1iI87kLOlyvTTsxU{&5w zsQvWi{pe@biGGcbo<$-~yS^Y_{=G>l6}Ja!7_$_L_0^RJ*0RA;VE^S<`)TEbD8U2rxlXMiyacm#{Obi~jW+v?GSdtTHx9=*w{E@~=GFU8>)s|uQpS6&OpZfP6P5&}+Pyr;1Z0!+9^$!F?$NJC zL+wM6E6 z-dsJT172QLSv|%c&DM@1K#{#u*da7d|YMJ>fWc@6XM8?(d z2DkBsXGyXRUTKV6AI@z2*`lBqDi^x5kUfb$nZ{%B_c3qq!)~rHnsJHoEBi)fv{9?a zmV=b_<-6CrVsWLV6j_5S=0))qkNT=D+Fys}kDtk0``g!U-|&@VE`RM_5cuW8%!+R! zCV4G_$cZ3laL>~7eYGT~=3~CY)6pfz6TAZ;gH>Sz%43wqXb6Z(>i#OFZ+*906(7=)WN0!7X*tSEXf1PU++s*ZOmPbz|fY_fT7Y0eL z@epj=-q_bkdK~NtSayLyHudc-FV8zEY>_pwz1Bpx%-%hD&n@T>ZL`-@-IQgYZC>pZ zd5qE>?wY@lR)&HS@uS$KR^YSAv3I$-;+A?Oc+=9U5 zekMy8ZT0`4I^ZAZGgfr-0`Z4@)P_&mzIGRfcGSE*5pHC8p-q%71LcVM}q zy|+Hex}Bo1c9j{PMov3*-t~X&Zp-B~tPS(+!NLnpzu9kn6RJ9Har?eMPQU;yyii5( zPd@FSbXFMBL=YV%-DlTtt=#gXa-b$U$_LXL*ZrOt@0xe0F|mh})RT-0-r%8v)?FH}oRr+EABF=v&!{7n|2#=F|0vj>#VKS#&?_e-p>h_k-n>A@K zC0Mn7Qxq|P569Hz5Q#gT*kDaqoG?yF&BxK)H}f^0UA`oD>Qgp_z(X@sQNU#m{`S|; z1h9Se)F>$Xn&E}+LnPA+{H96AAfx<^@_3Ix!Jz{8jg9el_OjYR9d=TJTf7PLhw~a= z!1WL5RU4^JX*+9nGCGm>AEW7a#fUtr0?#X>sq5iK7_czY_`|&2NTzv|cbOjjk=3q1 zij$d;6_vp#{22O0ICK}9{k2+1?^_sl;?kr6h*F~2PE#sDuRX6G0j{zByR|Z9Tc}t% zAokwMworxG+(C>5ZCHQqWK40`H2+~?uI-)p%RTZ|7~`NIaR(QYu%1mv%NdH<`u$gt zYnXjsQ_Br@J{{dVY*@+M(aOu8krweTOU!A)2K=ECY@u78;HF0s^ZpiEdkH@ zv)E{Yd+VeymBS2bL|}zT(EVWDxK3@d_sBssHwvQ7H5ePh0dKEGA< z_J+iau4p20n#;9ity#fX&4B21D6H)QFHCYmVFF1U&u65KGbTTC2ftnN*8!-sOLo zhW}Jb`)AYe4fq4HbugvXx6?N?Gp7BnjWb!$+Wb$Uw0~aluh9%nv@%MM^!!q25DH;YFVXNN&sQ9@TJf(o0~G zSud7eC$gR|PH7bX(I;3i24&W^S=AI^O$|c zlkn&ym_+gLM-bn8EKH$h8QHotFWxxx02+C#={ZlbAid7@v+2mKX)NQBMu`KYX|$?v*p6)1N#RX;n(|*E&El$c zNNhX%E=5)*=vyGyDtRi;L2%^ITx~x*O==ms=qPW@0vSzFO-z-C!PGBO?keV!_P;f?Lh}u5@8P@wp0p?gqR@7ls;}xA- zEYK!H^j1T!Ji{cCA#e>tEaStwSMC(E|zLByA=vus>Y%SOU_blIFbpra{ zz6U#Pncv}iVs-@Ke0y=3y@JT$w!wa7i0}l&FnbZGM(eA?JK6C`fGTj%6zwZ;;1?k& zaF7=zoa`IUqt`)w7Vgfl4M=Qn7!uUB^xb@J4R*>ieXDA$DHP{S2NfJ2lUi$&ILZ!` zu@|VZT!dS}eU8J|TSvT%g`%wus9YxJH)tLesV_E8%8B< zpqfUA{F(?Rd3$Ub`(kW%7wlHR(oys^3*+WHrxupX8B1SiwxQLh{i?#*I~G6&UW-Ts zssFX)r8JdQAGE|Jml3magw*u6Fug@^H2LTS#bN}IFSJnzFo4^u7Qo&^KZY%Qxs4lE zB{#4BC1aS{FvzAjz$P8(7ZGVdvEyldzZ%*iT&My2Ry$(Q2mWvQsdd|RVbY2F)R7a(h595h$9(xUeBdE z@PTGOkm)$a3>M$#2}Lr*9drPY@eM0pE{5UOHSoM+6soSo_xM?7>~PW+>c&7GX0;n; z+?Wk}39DSE3`|AnmU0s6J^^SrsmB(Gz!Y2!M6am8kky0y>k?~9%Gi4UG3o&)4kP9EA(Kcxb>W1qZI~KsDsW{RSr zc7SJ0tEdiX27!EV+IoaoLf^pJxb(G^^2V7Ku0;6}WGeg;6Gw5*r?**s=~J@v5Pqgd zD`nAywoJ9t)hM;ZsMSy$V%J{f#zc!m=ex3?o2ddHOf1UG8eDq4;b8ZciR7qcas`Ok@JXPkJ z0=ksB>cUA?Yiq(kD%ffR0+oqW=DGrmP|B1GH9dKRueF7*_@}N4dfw*7B{qWt7ZFeT z-lV1>8yS$R*1kahQR3;*8Yb`hjYQl)SVo2tB}iV1*zbkAQ--V9t-^f}a**UwGfVAPoJ=xJ+`ao!R-;^;8(?=O>VF zqJ6vMA|B+=%g(fJ*86oIukJ3;lU#KH_zIT_PBq6_yIL8t%u=zTk|3*!P11wu2RTHF z?C^ub0~@@lDD)t$GWT^feO+|pdvkiUm_<CXY;L=O)GFZaC4xYxU$F~x!@WoKww6)CnAv=PFn)c==(q{tR4TyG5#~VHue9pO4``b zQQy?~e;=i{lBOb(I^Yw&1zQk+jv&0jd=3s88R@FMZyj99j~FW?-?_sFufG?YmL5s- zj_eEk^KGwt$GD=|MfVw3-Pn4KJK+aHY?;#Q!^-CNspD(K{q|SR7X)311$GHNY9PYE z8k+_^iGB3tUzx_8fI;CfNyHT5YC1dRJ=NwFHezb~tSqdHjl|ZWY3s1<)h&HHErWn8 zSWZ{st2#$rf(aLeJ>WlO=njZ~*^220+<&$egwjtA%l*zHo3-C8*Z7q7UtYX1^l!E= zDFibbOH*EozXJ>;r=_FacEq8l`hx(sN#OkzWyKr5_7}m&(4Lgnv*k4J85JBvnD{FP{Oi5C37MF z$?xMqLpxhQI`V5qvH4U}hMAL}NS0pq(ZfMt$*Tf$Cx~aj@yM4y<7{j)-O^qDfEbnS zP698F^qd<~hy=;`Yz$+@fonpS)C@tE&6RvrPbI1UNjXe_m<*m;u>#xS@-bh~4r2y% zFTY9RBejA?pHeNC5zu^fS4O>6;(NYD;ut)+9`De`>p!qRjn$D@$>B54+?5`#L8PpV z=my8>uS~*r>1I6OG*yIRyejk8V92iXh(2ilK=XQyN5+4@10}StsvUm8c?@Azk#nR+ z5n(rW0YpRnuT$}nI;6z}G&Yprc9dK$ZBsKfZa87!el9=oP;luS!CCK-6)YjZaK_btz4(MnPQ2tk`eo zAR`hxtC8QTiI)^LCQ|IiuRj_ozZ-GWgdLGBKgg@yrmv6ktLEQz#I}DZ-oS*Mm?MbI zO{@qcXM?`t64#_u;{D;d1KyLp5nbp@qoWErQMVwii2963b~V`6HS>N2wr-pAosC~< zdXjdU%=#OY+KbCyg<$39=TO_tQ)C%`&oWzL`PQ$jwX)Ya-!$C|5EZ}^Y9}z_CEN^p z5E7#$#KearD5NdtK*#@x#3nef3xcI{2#W24V#lQaLdNG6og5(`!eFq?ebdNr@yfj< zl(EWH-X0u#F34be%${BirG1e|k=$ne$NPSCDov36&UIA3&Gf%7gOL53mnv&u@y*@& zzaJ^*qwNY01Ox;(gp3P>iwgw22t;(^d+uUq;v#_v1T$PtCRN zr8H6APJI4k=lAp~MD@=5eExw_%VegK2!#G`iQo8T!X9{!k+2efg->6@z{AEc%3j(3 zo|34){uO>LrNF=do2@c5G}1HDGXMe>K^1{8fj|XB_@jt1(ANTgZwV5LyjTq~>U#rS z-@kvq#eWH$`!-?!y*X)pyZ;8x{dGlBM*h@^XE}AMMK`OX0hp2FTO;ucfobK+@&Ca? z<`-HtXE28lB#|P4YNfPk^}{Nqswevx+Si{`IAK4Nb%^#2^!4`*dg)3tPcKFo$G7de z?%3|S-g+zc{qX!?1+s^wbo-$J2;?tBeu-7t39ZHpkpc)p2aGc5(bw~%Ln9jrtWv+F z2#yBVio)MROCcA|ZULx*z2*I*6aa4mZo)QKs&+t-Sho~9FuTrecOgnQ#fjb(2-I$x zXYQgh+euFF;1jNbL*Icm8y|#PREn*K7okn&>t`$j`$wPZFR&(eB)VWHEIzTh$jJ8!tHvfk zkHsppm<|)+0ry>kFGb6yyVR(Um#kdNo`V0Oy9djTqrW?4cAsG^#HnIC)Hy64YC`|v z$e=8nyuw!a2ZYjMPl~b`CtM~h)jYe>Pzi@i%!WybuF(Y5c}l{V^~id2+>AUc`W6Gz z{kjq%p?}Agkaf&g+gZa&s6^JEORo`428;6-St#7zdzp~DR!l@mXn=@)+cI7&U$g2F9E zbg5c+LA^Vlo=Q}EK|Yc;2=x*~6}2(L{K_u%ejSeeq%Ou<*`A{pKAoqk>R>?*&Gp=t zwmpjOo)|#4>_y{D#?MBLPj*CGcI21rh%_b7+5&~kP8(iy8C$gN22x8KmPgqL)!}?x zr@3gP*WB`5bKaVV5f>Wqnyo$4?`x&Z*d0>k%zl@3PJ5Z7%!%zJo~k|1O!+!s*>bjs zM+YYvVFj6)i71=de%zaaaMK&VaO+mQS(S*3#NI5|jQ&x-(rHE1?Q%W?H3#%U zfi5j-6TXazsO56Vk;dG3YV`^BJ!!=;bi1|G46M|7 zAo0`OSJK7-m}5-Zc8O-QlY-=sa5cA_yguvl4m)#!tvcO=bDc~IhJz&xQwap?0ox7s zHcwVcP>iO0P3yyImAUPd#u4Q-nqOxkfOb)lg;JxUIB5RJNjJ2`s?Zv{K3{#lAWDF5qKwRe5n_ z7_ecFlBYXo7iP)BHTnb(d8-3vhnr4^Ho>mA$>;_}xyrhuU+Hu!gjICj>f!|porgJa z24&)Oi`E?|U4qS2htW4Fzp>_wv5RE}J%c%P@kz&DHuvMwbji)W^j?H7m-ZS#V+ zwVBf@URGjJ#?w4iM!fF)jUkh2`WM+*QJ7lrQnX~uK0@^m(+6}>9s=v3eW5#~ z0$|INPoO9K{zsf>ux0%&SKy3aBz%s?aEoCP^!44gV2+$M`q@9xDs1?1bVLuk;G7Ok(#w98V|tc_{|5$s z-?(~>%3Ca6$##dG796Oek)=3#nS5^ey8W9;!E0WMM+SqJxrJJN?CVav+NYXtjZRR_*7>mwBu$zFKk zh{XeqNOXqdDCFQ7;>~y({BPF%?kd^fP&fUH>kS8ps{C_@se(4Uw00mTWj|wNmK1Ya z!JfOxu$woGzUmsVbu>{^j6TA-zytI;4f~TcUvs^8AN|SDe{!%K0R>$TfBXH5|COHo zbF%gCVtYq@xBn5_|MM!ke-knMV->%XlY_Z|vy-voe>>Oz@~leRvI}y^pEfH^hDyRl zegPmzCTkqw0g4Br0j2(I^x^yumo`q?;^yMt6fZzuKXAPFP;^CR2Tvk?e#)PH;&mbDfM$P)T%u@XrY$D#@FLxu1O`xk>T1P7KG zXbf4SxS}V5k2v-dhXnDojp5b2{6QTks^3>XpV|yZ>D)s}Vd^sO8Q+w~VH4m{9@pPA zr$aa9t(EQ`sc@cf^7eEYc>rzbd!9O^gUys+Y{aCcw*3QLsh5r4bUA`3C_{UFEK;Oyh;HTRv0Ay5?-U8spx4@So^2?66lCe{ zWEO?5?ie5<_CnnSFX%B~3!!`-Mkw*ijMiA9aB((p0`? zapfF&_=7>#wL%NStHuaD{3+!ZRPER%3Ak9WkCoBM0%uS^I?b$fafZaUzYynlBBK*; zVrJS%dOxCvOL&i>vCs(m5lipKBcS00g)#4c0|c=TB;`d)ak|mZDh65VBeFL|e_$G1 z6T2KiD#nm#)Rrifm?$`D91nRSkr*E;hSeqUJ&;_GDlZco5BR|yLG6@vHeYwu)z2Uy z@_BRKF(>dWp9g+<5_znkG!I3OPRt~t)qp-MJs)Wpkj>?g8X|*65+h2sLk?>dS0&H` znn9J{yp;N7ZVjS6)KsdjiJotUn6=*PypbyrO5P837;mm!e%p8pMjYJ)6o$dblYD>x@$$wvM^R&EQ{Qjr_ zToeQiO${L-aDHS!oPs|A)ZmCU#yEgu0ah_U5((`S7)jb`7aA?SG$P%MsQpp2?AXN( z`40fiafbW)I@9s^{q@5ciw_LOhAB_6AN+^5jZ&`SurPnerrl0_Bo03<%oqxWD)<6r zp6slp^2vx_jd{BX9GwzaKm+VFzM;5r zX_$FC=8SQCIr~6@B~e)v26;EjgtYVxr$9+leib8_VMto^9hGcV=^QiRW)~*&r@te?-5^So{(Z)A^9V7odXfW-YRhV8= zxq4H+vr0w}vCDOuE-Kj0$WK(MVtWkA^ToF7?w{*G2F3;syXF=PPL zY1bdR;G}3p_(G0+vK7$a0yJ#StrUNuctVO@b9047an#exj_?XB%B6KaUf@8p5f`e| zx;Wn9rHo^<#~nbb@;92WYdV_56lcj1g}a(sDwwxm*o8v_Pg`m)8BC+Pmh8}@MS#=d41oB|O_px=EFFUTXPOz@XkQ5U8F^5%&U zE{z_#HhM|4bes`R$_+aA=neywR-dBQkmJK1xOz892DL`{HNBeImfvP>)=`)y<+S{p z#SMhD87cCkTNn_2u5<*707Cxo$Zf2hliUKvEN}t(|9Y=3lkPBs}Invv*4cIXsM6XI!0nP7ZKWXx6`f`j%hZZ51_}+3}UeOIw_NF z*O|5B%ww-ETc4NX3|rvHU2WFl&XYc7S`Yip0bBYysSdz%e;Ag4*l%NzT%G59m<9o< z_L~qu^^e62195ly(8kadcYG5qw~1iz{!^^5v{eO)4%8f3T2fThg!|JbDXI)sN{8 zMTn5eakrpw; z<4ywo9}|c)da-m)jqze`SggdAc<24)LG~N*H}DX|8^gl=ysW4#I;3A*|;?vBD$b`%Ch`<05!5N!vf5CVpv87JiZ^q=EU3F=4jV&rbNX$hT z-4d7h6F<+m(Qm;r@@FY~i^p{4>D#L13DHuf9%KXCRM%0uJ6}y;2~4 z079<=y+y~1pDo+40YPWPQ*BHwsOakPPSrP0)-=lUMSl~hD_cFNEE~2vbW2Y2?r){t zs(eStd7eukML(Q=e%$;7o`4)45kDTmSe_A-*Lmoo8WGb_f~{#y6oOCDH5iI07ns9s zLhC+f*uCNE{6NO%xfQtX59e(kA?t10z5Gi~i?N*toPm4g%`8Q-(~b}e=f#Rgg{~b0 z$)n>r{DEsM*fN_J2AZ`HLKe%6{RqKiUKX|HwPbkvQtOEd7iuoq*(Subp}GP3uf^E* zlI->pon`w@i;maCUygHh6J;G0Cr2#2d^3G4wgrk_)-`)<*Yg`JUW5XD)HhLFA-*;T zr#-$)7qS|Idj52dVW4bfR7NUk7)W=(ESHelD{7uAJ)Q{r9*NtPN+4z;mVB- zT(E)5VzL_9HU9~x`<;@g$MYQ}SVH|zdvy9w%FMX`(jGaO+d7y#x&Nmr%KC>X`W_o% z$%v8Gl0YQ0?gi1H6wjr45UiSAM@E#APGHJgY+Py&vQ{&hYR-ToROQ&O^5q+e@Yr=* zz|c%2R}>W;;q*;>$!>rNA{vgKJji@zf9sxkbBFi+_}F6wT3Cg`4{-qSP&FK)YN~*= zPCflxQ}~hp(>2v8h5A_lj2AlY!5-)t<0Xi?9O_*?k#4A$!r!(=A~iXeE~7jM<3|H< z<&PVT^;y3i-uM)n2jbMvL%QxvFYQ=wKIpL2`|D^V28Pq4Rwl zaJMwp^;9H|!wr!4dk^iF8Ynh|85ffb>B`ulfVScvCo`wc)Z6b)$_>d&%7TW6^cCfG z`5GJpS9vQsD_DaS+!o~!KSuVc8-thNsw`%RujI<63Lc$#=O8KG`J@{rei@_g|2UIX zv!zl`x3E;{lVBtT5!vUa=*`R`>Q2;0o)Qr(A=|WVU)F05>)<{|(^$mO&`83b&lPJv zO%&Gb~HHS3ElUqgu zqj~Dc5RnSP&lELjAr5cI6s;K!?>saN!vx>nXJ&(ho&i_Fzl>ap51x^J&ya3IqDpC6I};o+ydiH=;9JB<&NUMTih zpAJJEQG9KCe?pY1Us@2TR6+hY?dRN?!Do4iY}%Qjk=IW@43=2Sd#@39K{eHBT60~J zdoK;KiNy@O*${*dceoX8-m{8EHoWJv^)ZH@SARJg4KNkzOSqyra85i^Qz7!~tVFII zYrRd_cm@nGNqKj$Ez8^(N@@ns04LbATC3;ZS`{ zxcg=F#svj?UJD-)%Z-<4pvDv%1SWtO4+J6PHP%T4 zI?&0+s4uUyVMlFK1%1_wnNseD7|?hnuxWk1yi8+JWm(}|w%l~Q(K1Pt2KMgz*UNFH z>p0u<$?RXR8xeMxuGO0+HGlt?;2PnBUYY!M< zpdsxAPgvlSqTOr=~a)hA44 z*A{>h*_8T&CazAQuSln|-vZncS*uu&8mUHc$9cHi^eWkO2U?4KKMb11Ee~Fmyfxid zc5en#gg4E8g~Ba7G93-8F3c4SWf8?nrB=941za$)OQ!D;Iwb88PA4)qPcI{&WE@?Y z?kM;X{aBXMmxZELyl<-0G9sXa+>OI_b$pynFcO(dv9H0cHHz1i@rWHdyJE+Ea{4b< zTb3vqnOj}}8o66tKuRqVocY%5ev?5)ISSNMu=`Ob_{XZo%sa;HjP z7W9?E4F`CQsR!##e^ljO#*1S^WXq@vQ#0bl{7ij8@Y9uZ7E;D)ass?jC_1peeFcfg# zXu8(c!ovJ+17TtBfS1|zv$NNxt{LP7ZZi|qmBmDrB%+x`Xf|^bId6Asb8UBLV{ybU z5{!%j;vzUEb$Ml74NYaOHkC{z<<3TA{0j2QA%g^Q*#v2BX;hDNDUWomHSLVRgv9z- z*EpH^^JbyFDi5y3AiD0K3PaY`9z><02*XeQ@@3Za>-$jIZFyp3Jh=GOl%C)LmU#$7 z+@>la1Rlz7AtKxwGa)9oktEce0udqT%_Q2l%W|;#7Pc8HLQyQ-nA-wRmherDqRjE3 zZZlArPq5ZG8B2eTKH6lfwoC>E`U6u}{G|hw)X6GU&9Q##cvykTNm-wLm`M^wbLSfD<>20L~XG^l8nQ14#n+AP;V_t z%$Mj{(qB74KGupr4-FYzH|Nd;(3RPG!dL_eU1ZKea`N`}UqzX$R@^R^;#e~G^Y6jx zyZoGb2MQnfHRma`o@?Srlt1ZIq7QFVr9X&fVc#X3YjQR<<)O&cC{G)4HkCJ3m_mkM z>-k_X$Q9g3Unw0i>_mV_n6(nVvshpRlY(PTI)fQkSfP-P&)e)pYTYGc>M_M+$#6su zED0MOq-oXH*Fv<3$l!1Mew!Z^(%}e>1qsnCw`Wn09z;&*bdiY5tY`YWs^Le4Q*&Jy z4UQC-^43BNIK{PLnVRb&L~NK6%Mv*vKD z=h-FWU&_j*fED9z(I~b=s-gS|5r{X5PEU%w72;~rac8~+@2ey zCpQZb_3vR}u!tuc`fv1GAbDtrbnK?NZa&5=B(mhiIYejq3t2~kX zd}0H-CZ6U?lMn2ml+g##CAiU6)uFdr;H?MB&(8Ax`x}t{AVO3$lxO{t@HcgE43Hwc zQr6}LFEpIoI}@2StK{!LbS`y~tbhsB@yV5-7a$iy!hlQ|5{V(Q3*;UpAPbkf0pKUx zn=c?Y#tsNN-~!LzUm*c!KGQH*BOp;C=9lM?mcZ zm7ke-1}uD4hK|+3e1lSy+~Bg6cWEo9^^F(vhBO|7x<;V}=kKXLUo`e;gXQywLx7S* z+K0Mw_aa_2gE|SZw%3}{wI+~jL|u8CWnxB#?2{=WE9=cAK%V<-pp6*x{LOp6HQ1B( z18g2ug0IOdcCT-qNnR0XV3pRS1eVRg8M+JiCSIVItR4MfYPHwA!pekz)8ha=?8rYL z+?e$wL``g1^09&lpg~aTfLxpW&xl7VUMRYY`yp@g!hL&r$6rWMbVqm>^0)~n?IAU3 zjdFT+bf?ba3PjT1vAXlVO>c6z??{XC3uNcMRg;B#qaTetxw3&Pob#o%wFnc<`_A>%$K7{egs3qCVUvptd-u!58x0kZ+pTJJ^S zSK_TMh0h=YU2e!Y&ORgJ9A+y?Ejh9+-0$M3u(!9$4ktUDlF1n5ch>HjJX;OC4#o;t{pxSk9c4=rIo;7(7LL$1h-U z_1^e=!CS)$x4hV%y1hIoJw`Yp>kMlsJiX4;qa3j$sHHa;Ue$ZPyOt>3{p!Its%5b%ICQ z>_P!gd%8omJ9fH#D%2FAcBnk}nhZZSW-_BrhSI!kvFAjO^Y&cD>bQrO3&_oSFUAa- zdGX2h&B%lE=BOTwJQ1kLnFVaQ9%LGye0WjdMqSW=LQhZ1I!o$LQpUgfX3&9&hcPC$@fT+m zvd=Mx#-hK9mpYeI;1Nhk)aq$djly#2#@LpeR^gUXW*AOuh_^Mj$?8B0%2aB(7?kq1 zTwEo>aZDYUD|@{y(hx#QbA}=>IBtT*=cE+2Tpcmi%gOe6 zFDZt%(FQkJ1$1e)D7g@U$9kMy%7k>13|;nfL^LhU!?`O_xYl8upaM*!p3wkTxocbU zdZ(CcD$+h^xjV!|qGy#O83-;nPf$_}dUuVoUpDm*!gesw2oP>!Hx&TlW;PMw9T zkl=07h8giVnSUBpwt80n5K;*Du;FEh<3%OqzLvV=C5z{tF;H&w9#rRe3kE86%D4!Z zEb5lqFCT5{lQ6xCp9h~nn^THlJd8NQij4BGXR~_ddG`FzmD$hl2hj^6f&>zf^RyG za973*j--dJ1qrn>fM<8h2m$o51;)B?E!GNdQ`D5Rpcdc};2Q^4gZI)*^x_i08Nbs6 zwjBqiis)sE*cr6b5x;W})y?@sNBV{~Vdr!w)H8kuoDTX*I-r7o*OIKyb5NH8<*F_r z_bmYrHwwqk`{9zPG@YpU4A1Kef0FPyX$q*!4Nxp!iYU zNsZCuJ_aKLJTqDKVOf*16dr-9&1p~;T|L)n`Ocjjt>1Kb|~TvAFX zW`3eO^6_Jk7&qu#G8(B+BYj9WL{am*0PN<2Y*DsO#(TU-!r_r)C;nU{5~9VEBHdng zI>0e;86A?3plATzqf$>H$rDl2Z!u*j!bc`9!#Iy-T?IqW_;OMO%Jw*q=pYk?8BUPt zdNFc#L45@@?1cmoq)~LK{5-aJ?w_=3Ov?Z`v=4Z8C`HWCw_=sUnCqa*cbQR{*n4{D zW7b*z}p_86yop`8-G`Wk;1o=0M$O%1*gPd+)`oFX^yp{pQ^0 zBS$3lD6aejI)EqAq|srAMjx~= zSOz8v%b3W5Fx`ShX!3GYsTDh|PffiEWTeSsSV3)?$u)W4WOGX|QEP`#C47N7Ih}i> z1Ic4A!7h*)e(hKz*R~9>b%GY}WW`3g3bJp3O1sg5*|Sy-aElYvxC-geZu}Y=gle#-ox`3O+5-UTemPdYRPQrkg>z2b~H=|O3ZAe>KJqD%^V0CfY{ew=w86UVV^KRM&cK}xWq z3}J95yf4Xl`^&FY=;yA+kqLKd~tvfF+{1Pc|1{vKB+M{7~-E)Nk(R|^ZW^&dSV!}G-YCyfej;lW3<8o1A0lh%u2{y3xNrJklF zeX{Lv+UdYkKlyTiY{AdB3n<+Jq?pXKh$v>9v+5BolIkN`0#r8lk9#Pg-8TFnJBjbjYVxb#aUq# zVhPrgfCoHxUOO<;&Fi)9lDJ9@GTX8;JNK9^BlDhxtOdc=EnF#@wR6t`QGX3i0P-u; z59vK#6auzn=G2HDxN#8Jcc_YFEXKq`MFoB3}yDmXZak zsbqyip)*oU^1>F01?Yif3CT`?rpV|HOC61DL6dak>`_k74v!~bdYTwL^kfc@UQg#1 z@pVRWZ7|Qfs^kYTWxzXZPF!a@P~^5!`mXRhrnjxf07yQ8(_xZOIZwuFf`3)5)2X9# zbwSPR`u&c_0?{~kp3%yYx79+{DD0Rkoi2K6zf~)R?&^1FI&ToV9or$!6T=spK{g|M zH@@U}DUu;S6uNatc!iy3$R1gajPhaKrEX_>=Hqh&X89Pwjf7bOvrRA1 z%yKGeR&4)_E007A0_!o+w)B3n2CkWjXY7bI%&c(E~RCcSzTJLn%DH;7chC(w%%n_M`%X&i^sFd+_;{Q3KFX!U$-#{CmXm zF9pcu9shw?su?&NnH$@g{gWt8*p$Q;KpE&>msdwC}J=i2a0 z6g?lryf)z)Btxt?G@lJIU5h zg;gQqgM)tvdg^&FET;)-8Qq0rA2}#6ey`Ym~iD#=*+ARB9r? z7jcKVc<1cjq^@kF|=pkw^u^SlBLdI+L4L76SD+o7^eY050w?I{JrWtt>f`7opaLbKNO!8a0eXhaE^J9brW^6S&iX`vt)#u{e6blD{5Rpm1Fk?uIfMs=qC zV>B`V+|Qq1GE%h5CrO@;ZJ116 zbeOWD`a(76KDrNQ&L&2KqK3c`s6j9@lT^P=n=#3YKakg9K2M*9SfE|3o)QO+KfgB+ ztk|S|cC%A$@14J!zp4eV>-09rvz~cOd&IaH^2?Y=6y1Yn&hLVZ@b=LpT!6Xj0pGum z+MfwfvcDn+6z#07Eo{w%46FgIS_Y2)IBCJkI@SQap7)7&*QVJz8kGJ3o$o`g5js;; zmU$3wU#p}lY$(w{v(95$g+kNDovG&ZP*D9Cv3B%!<*=Pq1oK6k6p;3Zh4p-P$Jw)` zy|1gT)_$HeHmIbpwH{_)aBuQHWHP_Gyocfg)e~X|N1Q`wP7zmbP-wm6FU8i^KKHrm zxs1~AzL#8dHBdr-`S{4E{%im_5jCSC=zcjkpWHuiX;md&(|Jp22Pbk2Ov5Dv9WpeB831U3_E@|>9n&BojkY3@y5 z8dmd+VuSrw7NsV~^jQIO3DX#hSgSILMFyqb0f)C*n`7Ka?+sAT(4n3~J^}Ssq#R^6 z-s~ZB4H`n8yw={0-Ot1IaS?|uO?v#tuBf-Ck3fMxc7hgZCW^G2o)-A3{Y`QR`rcu` zTYV3?yh{8=O%n@^aw-vufV-1ZxnUJn$?j?u!l^ zroysvLQhmZdj3W(8cT^IRvc1o$xuwWrvXWIPMx;bg;YDizCB1yf@8oQv%<|LjPl3l z@@PtvJ}Hh-vREI3`Q9_H&c0@ekI=##>MJ3I>?$O226$`l77%;XDD>BP1AlJC*k{cv2t zvYv3fw>-!6G#V3A%$yw_vif8f}7hJ2>sZ7M|G5&Txmv zxZ1gHSFr7&7so!@#qgKRGP~%{2%Xbn&4T#5ZqFcU7GQo;H1CBskCA-+JU*fVRPlm2 zN|_zGU@c!{oE;2=%bs!|^`&IHEXMTVW0N1%dNJ!Ah4G?iBuuc!bPQsAF@41XQh zss2Zx5w);3`Il7KKY~rNs)jT6Jmz~ftyBb0<1T3k0=EN$3#!KV77)qrj3m`UnG|di z(rblZg;NIV0xBH}<81^9l8RWSU?GxFAf&!I8%Rm*CDr3RD^471d3mlJ-q-v5GRl$a z{Frd3S*?@%Wwh>f2pAIC`N4J*`r-SC=#S@(B8Y#AHMEJ{#dVucF&Z^xW|9WIm4^^e z8^}TX(%Zv_+(k?{x2=$unY{lUp(AE6&|MHO8)l0V@g;omQWACJ8a%yK{vELMgU(ry?I8dhz;mPhXU!NlR+#46?PN6nk zAFCU3#b>e@X;8QC8lUDSkDEGLIOreV)=PLj=?1I27a8+h#i`VL?UExihpV&2OAW#; zdz9bfq58dQjH@m+mlq4#2bemBQ=T0_;RKM>L$_Tj1W#>g&tx_-tHxwMyz;|^^G93E zZgkQ_Md1K1@7Xgc^_TV6CiZuM-$V@zMWvNcyAsI>ko9&?sx!tHC-VIS(HXlO^a&iq z$GYMvR?pR6X>Hhx-iBi2<|5JkA2bk&jBL6g4IAwaZz~~v$rYTucooblI#hu+ zP*x_t?bvati&*LA9(fCkii6ypo+jKV?u&~G7j@QnoiBKKQ0un%-Fv{a=xA*6*N>j( zeeGbE;GIWV_7WXbMfKE)z8zIAnbAOI(+StH3`gaoHaHL6k2sYvavAnH)RCWJZA`Vu ztuSlhzQAtVwjOK5qwP>tqeit-gV~GY-1@j^)cM2~s807;VE4PU!2Er&DEZWD87x0$ zO*;^<5i~6EC`_eSAw$068!6ZO8Ezr@Vs%Zzyy7IDZWWDJ)4mFJQ`B$)!xPX&HT54# z0AFnB0AFnDpozgFd&#Fu@+3R{Lg;=-yMtTZd1CUl*iv(R+QneA7o_updARXJ{gr|W z$aGTo5zkV^jiC=3;oy>JCP_*>VZ8mR!6Y3sJfuPF=eugI^1fO~0duN@o%hlO zYgLYu92_mC@3$7zL^u_%%Q&=f%!Qcy9&*ML(yXlVd`ZM&WMt~Hi3-grcMgeeaRVVXEI*^Q4t52ium+*x?dP*SpYZL0vY z)XH+Na%h+r*jL`{QY3yAZS#-tk z)Gpsjl;}y63xRY4mIQ-Y1mar{w9NJ&7~)Yb1XWSnihQijJ*ZQChgu{n|GfIsR)n^t zHXC0okPZR$dj`Q&u2(5xZekK@R*JY=y${}dgZ&Evfi15?#&PMED2@)gnyk=Lg-wG# z{?1&=tS`*$9ik5-nbd0MVuK$)=loiRg&*G6hOtdx{_(|WZ{&6K8{C+3vyNrv&C_&x zcF#}X>*52=k;*cAz;kV9X;)S|;u?LHZDF`Qr6tWe5;c`08821}{)I)Xxw7W71RKerAWEGA8zGVE>VTQ-NJ5Ld%cVpBA9lmsn^ zOna==@@w`)qa*A!I!JSLD1VTS(WiT~Ei|zQ+#Yz*!n&Js4K725BM`;(ir_%p z2crZa{niA5k(d!%@<58HLsN!xBGRY4DO~da~k&y8>^_ zH)p^kcp!#1TMO@=#Z`l-?;MJj%pD_2sAq4OVoUGyzd&qF1$?4KI#f2QYzNztH^J*8#4Z5(8hSU3?IbmbPlZ&8quc2}RMzZ7exe)bjImlZTaEj*e%sdXR+!cW z;HzOY#Vj=Z?%GQvIl|@4J-5E$rA;4r*gt)iF|l_(p42#arJ9YN=3cRRpSjs~@o9?v zQZP?%VO;B)<&L-Z8f>@yeqYkkJYdQ)fD2k3{y$#>e|~%s|J5a}WMN}(ZSr5$*-ERn zvtJN+Gn#)IcuGiVLKRWcZWYRC6SRMU7E~4}H-;ioL2S#lX>rgdbv5P|XuBF3y!-^A z2uZ@{A1gS%f0;`T{oP09cT+Cf`}Frc4g!bn1LQL#A&o6>wp zB2`Xb!%{V2PbV&28>gSc8LXe%dJ)4k%4lQTGjO$7_ey7!4ZqQfb=#J)uaS23`hr=w zVGRh;HIulT!Bo9*$%-95xOEa9yHEmKHklo+*-BzfjmmfZ8sh+Vkkw^lXTD_$J4S(n`#zQb|l95zinFu9FQv<))XDEi#QgPD~*Ku$x-3fiHpEjgnOmHp3}b?Ib+5^O>+gObBdN;4#f$Xy2FXp8E@pJWUo+ z9pM#`=}Vv69&X-8;Hw$MG@a<4-_J;O^eZ}~Nm6NYa3oAc;3wn>*gm>B_};c3NtpE} zkVzD)STaVqpw?66Hee0Lo`4}(;>SpIO04y6%`-KYq+KWq9v@=-+$GQ9FfVEF^7rV! zJ-CV&ioyW^9i#(N&EN0Yzn1+%{VNZ4vbXq;UbsI$f&a2w==Z<;D&{<~~11476Lb(nFGDH8jp?>xcOjqT9^d zJVw}7>+SPF83dV!j>P>dRJ3>zcM;L`ZFvv9BDJ6mG)&)XMNu_S-oM9gRd6*n zmKA;cY)2>#QQHsk`-KY{eebTe(yIM-u=B-Fp<1RVR+4$UWw!~*vCLz^8SxxW`j=$5 zP$t8Z?oIs|=Xc=Gd7%gHK8K-vTh|zYY`mHE9#Ea*gcB<`XU&KM)w4c9tzN0#e-s{) z;_z|311vrLU)aK*qsd>wc)tz%{}vtE#I*xViXz&nC5Q|aoDv-mo8oEkWduS*IU*uy z=A4YRns_t@2RvNOXbZ@lqOS;I`s>wNPQlB1>|~SMC*&w47$qEJIMbUK3r?@h4J6q} z%?@#cqFF=4KnE^z7`ryB0%r$On-Zo>goLL$3Z^KCP^-x^vx3%Kh(&b%&n5ZlDI&U$ zxxv=P4GKM;J}QxojQN+E74qc+O=QoP>3Y==fx#DoMdwi_laGs(Klz_jktRM|&ft(l z{SNtg=F1~+CYz(7$jVuB&bX{zKm=p`rg|~&;~38D$|f_k;{xIMD zddR^qfcd`sPxG<-oh$#Jj*h*ZlLeq|(?iL`8ldxF_fMcES)p6UwZ>Fs^lQbMglK z1-z9_nuVG|f3_zjI3?Hyj642&cBwLXEDU&utRS5sA`EG)vpdgDqPl}#Xr6R6dLMGg zOc_?iZB(~a&C>2Y=9rTYHKt_;*68T*+ZQr+Lj=T`OBaIt4sm7GNdacua>(B4YasM% zS?PjXGeJ2~rre-4R-v?awyv$l_2dt;2SDy;tY#v5|DHLI84kXk8-)u_st%L&;bBs0 z2E~K48>a^=3NnM1Vo75~SHI48Z&5&o5#fB22EEcK;DL!6!KR{94(n-i0XxR+9ekDP zv_PR+hHWoykoBlC;=d3jBaS-sLppi7Y&s~7Y|<+CsJ+Wwb12!dHhz?Yxko$1fY^++ zx37yhI@&o3{oh7HJXM8`reSx}gY^_t5?V}lVul>b( zWO&3L9r_UE31D<|&ahNi<;vI0(4s#DPsXd?bQ)q41!v=rf-|4i@A^{2rla-DA@j-g zpu&$u()AoEh*Jib>~PZ?wAs$$^^9rI5Yd^`Ip*)`_ym2Od*K>ylS!NAsj<>&eWuMo z|0P7YcFbjFSuyhvmCV&izhv8L@If0Sv1(LQ?ugcbrm-?583cFMxQ#}0l;C-$lq=T= z;AiAu;fKsZCWnQ3iLaL~gLqiI9UhlQd!K&&5zYJJOI$zzR?YseBkp@i&g$xFBauZ#HxQ+eS*} zPzY+aO~h&M)G^Ar_uJ*WP{KK($a)IfjfawjWw*#AiJl!=P6wxJJVi z*n~ehN#vV0TlXL2>Gg`hz~H*wuc3?RgwT2V&q6p9yhPsz!ZE;jZp}M{o}X_W{ei?} zw9_Jf0vL4ve;SqW@8Svoh5_{9{R4pUiPMq#tbh=*IUGyd!1BUET^czRa9_+#jKB<<{dD-a0}&yvai-C&)i_K z2am9NKaud1Q{ZUXkYcT){;i7}d#NC=t|rOOSlwXRr2XnkE_RP2?G!O_{V{|ejZw}k zVx{O?$9g#zqc3QXaf52bW@Cx4^r0lRlR#MV2yV3sv}Zz`OHxYom62MA&Ga2-qT6S+ zM_j~mN1=8ZrLr+19{XQ z6s|>PKeFKZgpK1wV&q@Vz=PJ5EnWn@Tz_!K(b=6{%Jo+ETIG3l!0dvnZzbR0ydoos zU&}(()(+M8J%ARb&N^ip0Or3W4^84aYVSvllV7EA_yX~YCGZRyC-L-R4US3s-yL!G z48%T>7{0PK9z?M)4?t!tMYwY-qIUq28E^=dFUw!80J|psUqR;2$AR(hc23;F#L>Xf z$lT)}N~IP~&L+11+DV(3Zm57SC?XfB7C)gh?}0_OtXG06!t#Tsh|Kwqg~Y5yMUh_{ z(0sn&qgqUnd1XL9*wbA+weWv>ywr};2DGaX#juMAP?7zpkWhg+@hFw)5h2V^a<5KQ z8EDGS7tSC%DyYGwnSfCeQq!58%y*k%N1oO_=wsj(XX+8QxXH_-p;k10m7WVQSY8G6 zJ%3Q2J!JJcIrz`l4kOQ4F&Qt zj1%cdS_zkvr1lYbVCetP!yRWeB3f!Z1!9PF;H(McB>xI)K zHg-L-A3Xdz58WRs{P^172*Q~8Xu1v*=XMAgc}|63J$+KcX$_Zt3E?+BT|zudeaGeQ z`=#*RQySyq*6>>#Oqc*-LM=KXqgb#os*tqxt|10W;6bMzHbNwOEz@u41W;e)JZBbEd=(Q)Nzgs?(HA?|rW1w>Z1Ei`j2oH(kaStRE$XP?LyKME01bw_6 z8=|)Q!%ku73I~}(vc6Rol~~ER-STC&?5~(#TMVDl1uO1cM~WR6RpJc4Zz`w$JP;?s zr!)J~*Nr<)mIwfA$j~#XYEaz#jK7fdGx0M=IQY$~;kQsNyqG>`i2xX`+hGihI&Fro!ii>vr+`xne zy-Vya3qx)~gS6V(hk$^_9Fd$AbS&~q=97l5g~O2YJDK5oe5=k@uD)3S7Tu^1SvDH2)9(>ycO2Z)Y?dm&sEFn(!!>$ z@mM3rsplg*Se`?*rOLdvaCL0Jgxj6ob+pj&Dn=or$KH^y$SSY<1(n5Kc+z$fRSG_;Khn7S zYQ}{9YC7nD;?y5cI6|A?n+{$W6W!y9#4iY*V{<&~V(}$DAK<rVPg_Rw71r)Ov(O3)co9uiUZo}jR1fX{L6y1@Rp zE~xH`xsMLnp2-rp&~&eB<+|k?!-xd-4C%*RcJ_tRb=vWhiaZ0n0T{JlI+vz?E-Ym| zaUfpmQUneEu+Y-5n$&KA$reIGC?)3z0{$-X9m-}QGDo=C6;*aG)ka;2fKga2*0_Iz zWuA1MsuiX)xZPSTc(bG8Y^;lUaK;9D>*dfn4m!GakX&`|J)nPogda<-#$#!XftNf#eNM*cNTu7HH#+uFz%{oJ$7Ig&$zK z0%4h-5P+rwzIbAjaCl9^O&)g}Yfu#IJujT_ZZI|(U*jq=Tt+(B%^GL4jV?B*L8IUD z6@F+3OT$~31|NCdrS37woR2t!9vZ=ICG5ne&xCCU+%B;Bnrw&bD@Q1AtV0ZJr(^2Z zZ2x9OoUEuvE28(1uujsH3-#>HA3VMU>GhaVz1UE0`JVAxj~9(~wk1l3r*5m$m( z^jV+VRGPM`kif}z--6xk5H4zjudz4tH-Pd|O_oMM^Y>vdO3*=zTn`gYK$$@@Uc9+D2BI#nBaKKBF!LTT^IM6=DYHlIni zPT!5sU3_h)UAo}95KAc<_p$|vbmMligXV(yK3@W5yD})g%l6kLS;ROP%y(1V7sX^e z*@fzXI5+T5>*}JVLYj+Qgl>Y|@`OzO6v|l*C=U!NgN*sU1*E0GlwgF3K2!Qg#9=d6 z7_-RbT`Oi1PA2rA!X+g3*%iAOchrBR1>5$tth4b#xc8$9y;g9ok4i=F-6b(DM73Dy zq6EzMhfiV(a~jw}v;G0qF=x|#Y_UMWBW7zfKLr%M{_>&9(5V~omaei_t$isyUyoh2swJ=rqp2>fwNZjBoSL>Xr7#ww~XAvrI!(%htGO1_{Wyl~hhVbWzh*A)+AmMRQmT({>Mt@a#AsIt4_pT!bA-q7Xvy^I!`+5t@ z7IF*e88R10Da*uEx|!mal=GL43$KjZ&IjHQM6mizHU6I$bcfkDId2nA^xlsapBn0> z`^b2VwhF(R$rx`HkTG2`BI7=~v#ne~vSupwHe>Nw>?pa*j`LN8OqOo{0OKL@hTQ|(oQ@t*IxA&a9e%O>V)^)0 z^L?r-c5XQq_ElKot?X;ge(q;}_ABUDMWEMYuJ;BkU!$F-$1JRDC2?Q79r(urtasyl z5I4s=3E@%HqA!B9jrEVUgW?bIaNdI;DHOWd^7 z;RJx5e^(^jcwa37Y!Fah_5rqgmLOzTyS3#_1Xy@D#~&tY?sCm^STT1C4dj{Pl@E#n zsw^9;R@?!gv<@XrbUX;>zkZxF|MXN_@AFAWi@x_aa&suq(s%Iyw=p1{!f$w3ny$xT z3MN5$6T<9hXeDlQgYJda7fomE#T@AgL~2_vCJi4O7@Jyy>@1p(<|a#gS3uw z5)NHna>BrYPCQ% zewDEI%isVmt&CQNX6aL(b7;qsEO61rv_LmD=bLLj{lI1KbmPXaw2EDg>-S6gl6e2Wm)(}jnCF8Luu!8Nm+$@bX6-Vt4mQ&a-&j#XS@ZTiXh;B2iz3IwE4oOMVYf!0NNd92VaV7)|>>wuagW{)89SQOb*rFvaWy2wk8*f!DgzDH{ z4dyR-0V?wJs3FXZWR=xf4^l0{%eJNKy4bhR{m zJ2sH3M3Yl7-jz2jCv&h=C?<$0*StqWbQ{!se1qaQ0gsPn7B1{DoNqgF3D>Oo* z5#Xi~W|$G|i?EZxtV-0q#zHFVKx-B+rI$>z7O4fRVL8kc{AL2>g~9zZX+K1{^sJSJ zQ;mwYtmh0FN!HIt{Rmjg>Rs7P3n{^}yY7dCuau>6FkwT=+^|PVW=)s1N{iT(#gL?w z>aZpBMekdPw*GLiV|;Ky^Lh{eo+0#0t&%*C(gKEKIO!b~LX_~Dk|+GVrej&Sqcltroyg^Nt`?`NGu{6M6&tW zm+~wMRzt}tEaC;EGE_-;U4dvDRk!Uzuy*~tA=)Ox^S`V?24OYqzDsHwU-?MVEQ&`4 zzj17F)i1DGRk2xBm8?<>(BLGhy53TLwUfwb;Z!1#F`cxS$Sg$-MkdYm5mZ|rJ(RU( zx07a!>MnJaT4{4u@eeLC9_W}@EpI(u?N4TIJezZ0g}IP! zBF1^;3j}5{A(~+$!yl$BgQy)(R$1~PTbf?=%?znyYlnr5XO!bcK@7}Bm86lpBLmS% z0!wvuGGWmg?Rei$auwUvQ0dxYCz?+w2CO~5iE|&6$?u>9B*%Co*ru!|>9`2+XR}~_ z#6?~BBl0kA6^{c?TU(PB@WD1`g$iE?7iU|`9;EsL-~ifGa3xm4Pi|#=lNU1O^Eanl zrG5hQCy5n{rNuegCZ)RNoz^AKqwIn2E6-kup14OYIb!Hn9j71ZZpu;<^6NR9hJ8QL zV2;|cf11wDya-_hw)z1rp4xc&<)mQCuLm5`FP1`aa)+tedy}f=1jB2*vIUFAqh7=I z$_o)F^G2P@k28$Pk3%?y7zC@r7|E6fh5jUcR~mxP`l4$V?uYW!3lTCP&k*%fjMZPw zK^ro1H)FUBd6`9m&bC{7O;Wrm52ne8lUs-Nyw8K>M6(Sv+S;?`AbM1rfn`qy%Q}&* z@qV+vCeEt z+#k2n$L5z4*jHq7gK$;M@_3$~gLf$iuRXHJ<>xpfoSKt)BX$f>%6o|=U>zGk(gW=x zgE3~0Xud^;%Gxvg97?JW*Kk3{4QD1&r0pdZJ>*FI#24S_4DN8fSoC;!YqOJS=gD?l z&S~?;2JUNL+tPu%c08wyCVUV#2e~P+yiu{d;lcFb4K`pHzKkJBXWLLT>)$iR5!9tn z3hu{1gDFVTDoXT*cGi9i^Y)YW<gH$bF{ zwPdS?Q;|2MKk+oO-swZ;{zHIIS6{UwjA(d@XhfTBSYE@w)unWgIJ9i0LS+kk8hLfF z0VUIiLS&KrD81v8 z$MsEoXw&x?3OPV=)t*?GLSEZf#cwf2ktexqcOGb?aMMPXo=Br1-)|Y{_Ihx-uTy@R zDL6nB&66ukW0iX#9oD7A*@7ge)3NVWmp3a$k)Nq_8wa8gt4Ye!+$azhmF~w@K^rsv zDuS_5oWLhQrMz$L?$sL=ym7@LI`<6e^Kl2~G~LZ||5hSV2AD06D|EzoHe>8P0u^&1 z`>SmOI-5wa0?(`;1DG1(#IRu{6)B-E;a(>=w^9}|Bj0$b4g5kTk)WFrlIUcH48jk% zNFr{FUo#vEzVT|EavExo6CUzAY5;7mMHx^LjI0o z;b$_Zo|-9Q6M@`8el_q;7kS|A1m3}pE=`gTrAISGxNCgqz4P**{aWIgdvb0j>h*|C zxpaA+0BrQD)rzRJ8l|+F+z7oIt)i~PXa`tv8=i+F^8plg!2(!jt3~Mk@*=#7~tD~P}PmN4~*2&k5NUF2uD3ny7yWOS0fR=?Dd|-(8Ttfr|LBO6&qAi&VxvKF8h*P z_bX_|wzCX3jLMHy#1Rb?`;gB*5tyoMHdB*vPU4tl>(8IO$d$f??}(~;50Leb7y>uS zlbI~(_I%TbQMufQA4<_T;eC9jZw|DCYk2}z?P`LtrbTm_YQ}QC>AjaH?`igN6qyM; zjq26=y)$BK_poAsr!L3;q;vij{E@T&O^Q@pW;76n6^a?^O3GTs>#7=R5D^C|`YN>e8VVRXikADz`nE`<_&Nv_^F>ICf&s;8 z{^#5WHc>E9FfuR%ePbZafv(SnjJ2O7$iUeCz?7*Fnh;XHr#yuU2@{_T1H z65myFvA4H#{HNsS|M67Rvx;G8ZvwQ=yVjZ@bb+d(aZ|o^-6#$r3NOun&{UKPqIYcY z=dBNGZmoLNpS(HHMA58!I1uM$QN;3sH|#}Hf9g`W^fd2WNz`hSMMg=C^GO4BQ?_Cl z9EV`?JEhzZ8B}C`Gr9cso!6Mi_wciwTrIK2iF)O%HbQZYk}@@^%YKt`&ugekd6JI@ zWa4?KS-b?n?Ud{!!L7lkal9^Tlz{nP7<_+NVh)NirVd~Ql>Z{C{%ncAEiw8x*7e^t z95&LtP(2J7K6+KrT##^h0^yiEfADq6 zsZbDKAr10=*0e9+z~gwEdE(mzIqEF%S(XJ@%F$#kVpF&3@42~1rLw*wX>DK*2JMqu zE;(?Ajg(gqRldK!EkNF|#K~qgtl^*Oqk#y9fQ&#A?@1W&*oW-E{`TtOpv#STz*^`2 z=dS-PfTL(<=PYF4Y-09*h-PrYrWHU@e6X{Le}-<9ErWB>_7_vDMMh7+R$_8EN-?}Z zU$g-J#fnq{qSvU6=Ca;iMF5w=rOIV2(xi-#+G&Uc$1pGFp;O1<+RMx2L(V6-DJrAY zKBn_@1_wX|{u`64q`fr866@8cyE%;>UC0-hfgk1wq@ggYuzKZPyFwe#!RVt~!#@ur z!^2j`l|lr??m>Ig@nQQ;u3DM%WFIx8@m9a_;^jD^9=jaSkQlHSmeM1M^$vMao6Ky7 zLlm}{Gou*k1m}4w`A}hp6(T#9uRoM@YegT7Z|}#8*EO3wdlwLu(tzr}_><|NL zyOxIIw}@Xz4GZ*3u2fqsI?zgx4(}n&mdtWl~hV=Md{Su1q>qA4T6zkA;8jlv&WKQN>!*)wK>X zu(!me*f%IoCYJ=~&nXL?Q>ayXg!q~&Cr5^K4WQUA4H@eheBk1%S444upbV?hg!w5b-+DS~)lHj7OZ!83zV;1ohe5a8g=pa^QF z7-cc_D0XUr)-`><^IK-ET$9HrRU7YT6 z?4@6X6Uycicm$S;AuOOoOh4p)NH@^Nw?~bt=SkFL{px}&gD#Ya-znbt`N`;={C8}e zClGbe0{HE3$=QE{+kd_XnE%#q9W8$IJ^#YDMO+Q6UH&b}=n_Q>*YgD>=rDQaGZdUZ zsmK&7hqHnVLau$R>CKYqtW6_-sF3|9IFa1XLi&Biecqe99<9LQjM0dLsR(GroF2sa z?PVkGm{!tzWL}(?O3Ru=E;QRZ9|Lvuq z`bXj7f2N_y$*ZyeE&h;?b+nPFz=eq1ln4q#N^{#kfOLFUqWGUGi7=8 z+n75-)ix~iNvF-*?sdFDU8>S#syHO}^SQ0=EAQT{-n`_V-wsP^KE)UiMI=&z$d-j| zJENE>3)@R}!)2-f+~ny>5CvU@;^@%C|C>}B@6hNzMYS*^2|+eQpVR0wGOv=W3^lXS zp3dqqx;YL*WHC1dg6!QGgmB0isF1$c1PxZ=ezHez+QK1iVB}ew?f6m5@T4R24%%2k zS6=2BjmfPFuO0(aO1rZsr;;XX(SXjNGpwqmy31HTbuIV)gTZls(b)Kt$;dA=3j;N+ zN*|HnvY!UB=0oVtp?iW(y%`irba~&rC6DbQyh#!6_Ns^+vK^)Bh1RT7B75$4h>}ey z>tIIAq5WUd#;ecx;W=a8Ggpt&pcSE$oxkdQtqG#cm0dO=w~Izi#T7Mn-Psf1sUbyD-_Xri=6 z{adXCNh3F;X`AW!#E|7-Nn@v5Zef^B$2_IIQq+P(q{rz46RQ;zSmGmu#XvH&-4tg9Lihj$a_f^kt zmGq0>JJ5ZQ9@1B~L}#KZayc6m4DAn{3rMcgnh;6o(Z}2f; zxQlvDPDima9e}1ah|+deVlvX+Wr$}D=6yi66p0EX-tQK)j+v-k(kZp_2R5k)zmTX z!`7@#H{RvWk97}-Ef`A$oPJ{vp=N@iiU8^0OHC9VcO3}y-e!~{W@2&GQm23nw6%ST zjjpJz4VYb8!o7l25dnKUXeRUlREG(%TK{}|nxIQrh)9@q)A=n&j|q8JrFj&v#=a@F z1exlifrHF$>R+@{n@TgRiJMFhJa5$FP`&!TDjRXLtQtx)&}5j&Qsa`r*oiRR(B;MH zN_%PIiltvf>7720RAAAwEUGmtB&k^zhVS1cXlo_Gstm<2>8s9z^@{7VC)I0zlTyj9 zJ|0<~cmAF_ytgmYZlM@O45{`ifmqopO)7md&|;0oexi!-4Ig!7%k+Bz(AU0%Pe)2? zWaWV^$R$#kZh<=yC0t}}+*;^LJW1UV$#lIxgIbS7bz}}*Lz!t}b3)Ogaw3jdj z@PYkDrbkFabXBIE1KCT}-+)^mra{{+k{;`+g0kCva}WthGrJOzLL8)f!UzgD2Bt3B zL3^IcgLW~DASMs5(Ic57IUz50(g`3TrFy(^FC?13J_xC&*dx~=sA71t50E6u!+n)1 zg=ioWM`c=;O_T}70jTY>7Gi{am?@MJ(^(}i{#r9H{K4`=r{;^H=-4~LIL+z?en{b9 z!g6|X*}{y7OBVexRudY%pxceM(iqj<_cnOr5!ju!q`@>cLhyahEGv_8mUsub&bME( zJV?<{*7R-yx&Tpqs$XmXYc*tjPs89Ue2qTPd<^6nGpv8>zGu#updJJKcD+=;g1mB%ro*9 z(NNsQjS(f!S!Zlx7mtDS7*bkc62UF{NP@Mac~5_~RzA?Ji`Zb~C{T{81;3l{aSJ&h z;^p1}MhgM5Iq0JY(Ftrrh-L>DjgYTwy8x4%Ao?IkZwDrj+uY9h$TYxSK(?L3JrW1R zR8)U3*CQy|h~<;g_K``lf=R|iU4s_}l09R4&d*lP8XH*RC{;%w)xT0yOV1Te42rAM zPba*hby}~<@=2JUp9w0rA(&nXzGsAZh&pq#|#29lS+&B~8h8W{dxst#iOYTE& zyqtRr3TFsrK1mWzrwpTHhO~RvJsKtkWO;T&YeK)MyWkU{s{InXcZS&GKkwM=%_)n_ zX$!y(i+Z@U%fB$kYovILWV`&Nf@FK_qt@4f7#e}i%6NwO=%wGW0~?4uhnVRa+v@A4 zUhCc7a2mL~zxJ+z8)P}|b4Kldx_|b}7Ty?eUGFn|_orAcyb^*iT>Z8aHQD`XwTI#n z9`uaZwQK(#NOQx{*^A?_CsXFG?-aBuu-p^o67uSd@rzYt?2_Qq!yOZ0lGUS9^}Ws- zx$}`yt1(~s^B;*z=k5=p**DJW1^YX3{p(F2lz)}DoOJC>b^qHg(EkQ#|B|x!`z1j` zD_1@Xi~l|(9U(XNy=j5S<@{Rv@KIdVDm%X#iVA3(N4opVKO15oqKpn}cRo3uk?L87 z+JUI~`qw+@jWoPa1;nou_oH?9$?=xE$In}E9V~ky16uiRi=RmRo+5ATt3(E74-)*3 zxF)2%)fImnS6kYYb^_WTQPJvtiMS=v&l_d=t{e+Dfhlr;YDzTb+vicz*s!E2EitME zjdpE4wd-F9k*^bnHPQ zVCxnvu5E^)(xj+6T2p@pxm8khKw}KE#OFoqm{lwZv=vZs*k~4FIws&)F3BX9NQ$ex zi}19u!m#J$x%@El=NYWAs$}w}wDT|c8jq5Fxf{+81p@U{zBA+#PF&)=qKxdPYkGxr z+ZD|2Hs=PWINo!_HRXjPiuCss7L?SI81cpSqD$(c6B@+~k0OE8Wj(mMv8G`&TfCH~E$ju{|_0Mo_jtT!=||5`vMYc#%`9O^uW^A`@k>D`ly z`sQN5{zv`zzdtkEzmE+em%qGdbS*4g|58CQH27z2f{fq)0H~TpPbI&E6r#hD@Xgd4 zej&qC`1k)1=GPa4h=4_~H<4Mz>l-&g!h9P@9QkD zdt!I5V&n1p^8t+;bwEh1t0e&M%+@+I^FgRL+#h3@rGMu=(Z`BC5Q8#Je(?K5s-;mC z4wVLLq>_=+lqI6{D#giONXKBKX6<{LKDC~m{O5OzzG zy%0}R)tao&E>%KASlO+RQS1ByR?%XiUdk(?lwk$Ep^5{FD4IN_(QSo|TVb=3vmSA3 zXTCsjPkQuCr{6lo(&yl=a@pgIVk$B5p>kIx(TtOY>S5e5VM7*s1IwDiiv*P=c4c>M zZ-VEClmJG)!3Im)tg_(}i>aZH!NQ4)I*n+WZYF5SE07dULlYz}=UFEz(C%ogtX;m?kvS95H{@UM#@Ccz^*+F)StV%2|BH?+Dzi@eKa` zUJ}qh;ID`eK9+WKhfkbH8XTf>|I?2b1 z4`{5rFUJLY$W~7+;kY2VEmNR>Fl7e-Y}`$vk9DH&bqb#RZ&hQIz!`c#D5JyPvx!Fj z;4NAQUmQIjBik%N&#N&BN?YIP?AzVUi!jZBOqNsy z!r^C$VgcII1*@Wead2S)6wf7rQ|ac_0#}t=!A8WwD}TF^*7JO*)K>=Zd5+tISnpV?yxupw z+W4Rjwb_>msO8mOOOO_^oTB+C=tQI^3g3^W!uV2A9I_8=Y*OexHgn`I*z{nsTqL&u zp2nz!t8bfTMVPON#Z<_h%_<2E82;=v5W9pm5KVOlZ3v!sODNBTH3_wKXiyo|?XaBE zcsYYMlv9-~za?XKju>T9Nti|1VGVXFvu0=n1M%6wog4!IHDyUkejlUGa!hMFOjVv3 zh18w|uTGDZaq;r}aw6Enqc8|#6;62N6m4jE8mm*q4zf%X?ZK{R7#7=p%HniD!(gQ* zLeHvj1m697=hKjwdiWhki~5XBE2(rP!e?esN5q;vme659-!^dH9HA+zOoM)ZWtgO~ zCoB>%Hz|y$?`El69vt{1E#4}KQLvu7l(#}1HPWG&uOkW2gQmyWaEmG3cT8zu^4e8^ zu~)W8Tn5|>iG*&%CYvc@Wpp(aw9eYoNiEE%&R}+3&D>d0g|ws@X7x%fLqRU^A}BA{ z*A+=ulJ?xx(hv#5C0h?%E>Iueht8vtO#mkO;G{p=e6f>+cK^fdp|j;kg1F(`aIF?Ws2!LGY)&96Ew*Q)0?f z;X!*S<)yiaEind6rR-Fs>LAk?q^;bgLoT+E%01591t_gFi&TUt+yz5c@bIBQi)>|M zjl90D^z4iC7*;hOY_^V35`Fes-&2D!kV2U|Qve#x)3>*BzH7;cM94_3o7iU#btyP# zj+=*=w|pB~)diHr9Mfb7uRn-6G78!B%{9Bvd5b?d0iWRGN{dKo$E8_nJlo9_d-OV9 zXktrfssP!IAno~i85lO|eRJ~|?V#<{ZVmatOrwI)u6vw^h#|vexCfV1b;cSw%Q8%g zx+%=^=a1YM?|VRd3N?5H_{AJjJ{`Zz)$F**B@`FPTPCt5t5Dvv(J!F09GfE3f>zvi z0qE=pwDbo6PCRm-47ZR*W^f#-U!LftvRb&XuRnXXrg7XtxcN(_U=fjf zFU+E`aRFqf3GN+yO=RF!fO6TXo(Mydyj3##TvOfxURA&S`WUnNNT5*CCc96KlY8rr zT*89gO`!dI5rR~aJ}US%@D}vwMTdbF??80|dl_2XV3G*gy`OAT4B!yul6<)ev3M>l z7YtS}MhW**Tzt5Gxr^mr+IMViE8~shu7G&BpnoMYW3lApQUq-yu`Frn1_TS?|%Ve`2{5uRzflI90P=XxJ<@?>I1}QmRYN zI}P84DP1xoKOyrFQRJXW(*eKJs+k&1XLk_mESXWshV#GRd%3&{FO}*-E*pP`-w0y5%D5l-hN#|FxAo^yG8j z_V^F{5E|NKK>BTA_x=qk|2>_B`|t6CsG{uOgmZ?1syqT8>?g4gB_%}|=B^Js5)&X{ zvCA${FuKMhF8WsBL^Ne(+nIsitNc4qyQ*OVaiYS@FG}rq0lg{sF~lGkqq+_Tt_^LR zxs9!FkN~U$=!!*5-I$9)5Nf~}ernQ7Bt&iAoGZ{bfE-?OrHHj;qgi1^Y9~KL6>i>K zu@e~yy{;(xgxFH0%P+!0?C6_5d+nV=c5OJ^T?}hMkRo7F{ z$1rCtfX8*&dX?z0gcJ}INkQz(>jH!~ga+S)H3fD>zb^TK!tZ1gke+f2;oDP>DKoC9 zRRP{IuB^H1pw>CDfdnM*#$HE8$z~YX>37cM*~%L^U^qaK`ukZ}br=fIzVqX^>~zN> z`_?v^jDLzopHFC^1`69c?(I=@%taB53IoOwE{ElA_^SpCVM9<5dz2 zU?l~Msq6He=N=b_sFLGbgmtAffE><#`mHEu`cjY0n@=ak338X3c45SRkB!Nm`KeXr_S`@ptgW7#&#Rt5i^aoI7 zC4ka~;sz~Tv0nrEJxF;;;Ap2Jike6FJA}o>CJ9A<(mRb>ffKP`!&O?Z7^A#8AIXvS_ zVQie*;l!J39|Fn;bqzdx!5x{I?~#qq-`V;L5ZRGjE^~gPc!KXGihq9+3IF3s{D0*l zzg^t^VyHZ&=bYvo-uSYoeS33%$JS4jA{Hacky0VfBCgfT!aAm5&QXn7ty_q9D2t#y z^jZ(i$XTCOFfbcB2pjKD;_S%SdQ1{e%QR=teT#;^abJBuLu@ZcU0Yv((Sv%BAhgTA zAFP#$2wlR|XxeXilcbjW=`8sWN_ zNx9AC+bN{P^O+HPzkMoAT%`Kq15JGnYm>T&e)(vo2$_%2YoaC3<(R18{;nPa;9`on zNI9zLMt7D-&|$wWaO4nl5j@}WPc8?dG&oxabfA?j?s$y+6p;qm*OvxMb zxz`-C-ORYf6hR)e5Xq!U$XAc%j!2VPtHer&LzS`EdlICMs{@E2(BcDkFtAWIqG&oQ z$f`TU65mCmv1Y3qh3Go#9n|4d>~T?O4r5TRy%(wVJ+4HT8nO!`j7=)&e$-GT?b@}I z^1s0XR+?GNDNnpW4hfTTN8+JDGWO|q6qR(ww& zd)=>){l2s6ms$rBCxzmeG7IiBH89igE z-?g!qo3p#_J*+KggJc!?&6`ol2C>&p5k)hQObo&npslvNSFu~#=XmTNL6KFY^FqF_ zdmo_A(q7GXR<@S7m!NJf*au5A^|H(YB}~BzTjc1!KgxphKY8~ww2`FDO|$|Ar;1-K zR0S-MDkw}QpcQ5seTS>==OR6mJ^C@Cvx~IqZT>`QZ5^HbGUFl4iwbU$m>)vzCt(ed zi6JCc44denFnHgW~9)Zp2JnLXnCw`Ux3vEPCB4HDzU{vH&Rd2P(~wi9u2jy5Zv zK6g(ambPiZX_uXfkZcnk1d4ATjBpPztKI z<56cP?vZ|$hX`V8v04rPnmr@CnQk_)l4XFN2rX$VG(O|d-FA5_t&R=1gB_Jx6Vzv! zg)}))>eiZdw`<{zO+W=^D}u=_m|fsI7wK(3--iz7j<;6Ub*y^AM| zNLi-RLx2>;_g<%b4%}xV(PrGHHfQD2CWIY-uR-VFVLkUm&KaC7evHvz#brJ-(Y4Wu z{B(G(2Hmi!YXb9uPSLmT6()6E=iW=3`yn86cr{k2Zq^*p@*!X)7j~DNk5cWYHNpHU zkAdqs|I=+$Czs>?ykG?!FS*;G)7LN`q5e;#v}V|3$lP8uZA9m>hrb{XunD0lTb+D_FoELNjogn0Ez| zM1tX6AoCAL^E}kB5@t{;z=LsHiCeE9UW*t5p{VvF8EsrAs7`(>rr_IgE(@N!Ms z*m4ZCAwiGlUpWYjlS_8u#HAsQaFZGQ+yQps`SsjR;Z{9V#0!mPyBY)nPTo|c%Z1{d z_~un5S{(7m*rx40kejp4EHcf`F;O9@N?aMPt#C~vVk8mr_lTNVk;_-_|7haEqyKss z`6eF*{-b&G|JND$t!rg$_&?5w{FMA0FXCsT>1=Euj;{isARLt?9DZ=9FJxq{pL}-~ zX7UYeuQaGUyyRRY_I;somD}U8-=t{9&325RSNz@@Bces0sB!Dt=Gs%|Rfjc8*Vp?y zkPbkTLNThP07k9eNXf9igxg8r zr%VhCp-=}DjXBavv`ngZTGXeeb9jNugc5xCGY=W4pr@@!076l3BTa zpWwzbZ~o4{^&hqK0BBQ{VVn;s>W`8+;AAsvlym)!a&(LiJMn>tj8GTHqB`PzOpwI1 z6+7%!CdYz7foaLQs9ZVk^MCUFL5ljmkdiYeeM}%vOz=#s+U}?KfORX~!m37r0|N04 z+|O4kjo++|0!C}EsKGpDJ;3_zw-sMW318!W!1B!EE_3*Cff3*5G^Dko^$!Egzr$3w z*9R@L-0B^}(XIN}_R|H-8JIpu-s4Mg^r77ckm8WZ?@u0d39O1tsX*E4F5-FxVwS~L zoD)#>G7u=!Ye4_IgI(G3FE0>*;vB9A6&75`I ztR~UT5aMwZLJ91t=HR;isS4|5IZeuUeGoIVKWK7g9Vhwh-L8o$ow+KqzpaUhyne^oU zu!CBzKugtkkWGB|4;31HuL$9c@3`;&k8w}-?=j|IGqnFD6ZU`SjAH(=ts1x`b>1%p zP;gMk=n)St`Wy@s@XHK{AC!==3{y?fcWqI%MLm3J`Aqbk1R4=Kfp-_=IuCYlLa)3r zx8;0%a{O*`{IY6m%L@Q?#|VV_)!t-R4P%MJ;LUPf=h`6FJk#HI$0@sZ_=xOVaUQ4a zc&3wQCuRTB2ySROpp-HXFCOAQEQHepd)?<_8jQbKrJHDYfc*>TUcCXhZ1)ms@SHz_DgMBJAY_jEN}` z#>|C7YZWLMiG|%VVXWg%-n$FDbZZmLWa2Nv#N&3e>jR#@AcQL=8|(CUQ{nhOHWk?a zeVF_#5B5(&zhwAK{~v9>dCc^%U|2cXYsTV!AWo-P zr*m5Aph>~&y6+2b8{}C|?*Wx193Q*2wd}M*8RFyK;|o*=sZKSY?otOo3~;b*CO`~C z9=Z@~9G0F5O;lSjwtC-muPqm^6lE5wzDvm&Cc;V-LG$YLTH}R^0N+yF|Gp4hv0g}& zx+)$1cU$Y=dwUUo%T{Z-s68e5PZ3~FO{AU@+R@hw9r9LLsovKoGiBuCM8j+$sen(I z0e7=ks$MgUF)d9EFqZfj_ur4=TZ(B=2X3oR1!QA^R^&=8)GLi~R0y!Iows1BchG0e zaglD49J5sN3GSnl&DUqJWsQ2!s`s$SU-fsG^vA^IAI66M{G7VD{99bb^M*|L!VKGys^z^iU+9+a8ffU|Ti^u&`L#A||Y=>4+kB#`!SYwITCnC}0$3UTKxiT%I7 z^~ZNw`1fTe!GHMHf3=PNOEX^?Oc~>QLmBud!?3)?m;~hf<8n_8r9L7iiIv_G)L}JE?^;8LlH(J439| zo*R9R0^TDdQ6N2*y2Ex~h<Vu%K0(0^tH5Q$Z`->lSte1>9 zL}3-DA5CS<$nxwUk;S7X*B4zlY&zMW#B@8ar(YLxW^q=?^kt;9Yf?7O{dPAf;i{kK zwtS1+_zX^(qR{S}1gGH28B@h#6UFEENz)t1-e-Wh*V1ulxwMt zchZY?8j%o*-Xe|?M_>+JIl>i<47LbWA=B1O>vDNFlJC%6C@DPEV1yStrebGN0Jm9$ zH?2FKSYFIG?4NqOkdtP|@N{E&>52|^l^IRvl%a{8#W(1elRMb;zLL_s0Eql_H`)fWTT~a9v$_-?JF)e!8 z;F!dtPZY)jM5~*qgl(l3WBYMUoVMoSr@1+M%ys#EZ56& z$hW4@){NoSKwU0=Gm)azqW@M5S2)+myzW4I=?qG23P45rwKh>bcv3nc_eS4WHuup! z{qup=KsuVUgNT)Q7~S0CCfY{(gQU5T%+6i6fUWRhG0fPxINqwBT;?k8v~hb zh;S6u1no4@Uw=UU>6`lrsYeQ#k(Hq+!w@d)vH?LWFX4iagv1y{t?#Zk1n)$Rd&54W zX?TvUDX51Xv98*ST?_C2qSA|{P)eh}B}@yJ3P?8_GOfCMha0m3nu~FzG7zIZw1Asg zU?fSsR3cQ5Nt5RYXL-lU*^E5m>Bde?4I@aM^2w>kQmh$e*pP|k$ghbv5GQ6*xhtnex37;WRInkz5j!G5!OGg|od{G<)px4hlOu=k z=z``9U7oFZOfc4Le(kb*3?GXn!O9#jnxWErb2Q!V9SJxx>57Thk|>?3njYLoVPRU^5`BI`nGD*p4i$Ap_%B2FDV} zp>*tOoj>3*8GsolSMtzjq~q~XoB>Vb6Fb>&O)5&lxd4=8`HB5{zJfZ@GbPzAh$l)-wZeBN3ANRpl68#BF}Y-%>_TWb~Mj zwQ$B>RKBuwP{WuwkiPg7KI1FeR5K)HY2)&)EI(kMku#+$3?Ys z7TiM6#16*hqgpbffMLg|SU{a8ymAuaF=3O!c4$IfW`B8yt=kjs9G4oa@g)j$C}|(e zo?pk_Il@RJPy*pd>pi=cQWsUTN?J2tugk|`OVAm+MI(n=Vu0E_+Mv>1u5@)=Lmll7 z_DVjfGz96*`&}prSO4mBz@Dec&Z9~0m8IagxCLXFOoJ7{dKyEN?*%`FI=>@YLe^`7 zL+#4`QykmiGgy=IU7;jhP#S;+XHl7~LIN`cZVk^?>`hX}fr{)(EYf<`qJG0?35l>dS|8cEhe$On(iG7V@tS%j0!2gsK2GOW zmW|y?S9ORiecnc01j?DQ{J1%c4eQA!E9bs(ubVbbW$E)W-i3aEQdqyB46gF3fxTB7}J(Xi6Lb`hA=58ta7R@U>7LqmB{ncF;$cb zo+776#LzcXzbCp`hIiF}=kkH2CJRtVx>*6D5hYk3i9#WFP6U=-Ah1P2#4tI@)9;+5Y(oK=7$CxU)zT!vMZIj4>O~D=G5faeFmg%CSc7xMoWB9Qh|K} zl-6=2?s7(8S4(v>hja+6zs^69%!6i`K?U#Glq)Xyk(V}$ekC{*#3TzZlLyKO%uj88 zgKQAc{2&YB*ze*>{C{w?|6Bf&=wF@ozgGwTR}HRg?tp2C=Gkcww_#BTwxY3GU%xS} zUfU)m4k~0qMP+G*Z>lD0Emp$1l0g_}QhTFL31HK*_ z^>(8JTktFx9VauIATu%}aC=kj;h|RfhuC`x_=e3xOS%&mNBe$Hf=^-3=SL^yR@Cr- z$gk(nbWWv^p&P{l$q`_i-}csSY+_<>b#HxWNN$)Y^J=W*39(_@Xe(7NS+ zdWdAcSpATpu)OV$$Jx3go)W#eYHR%ZQFO3cw|U=!>lw^|gqe|mF(t8stE%V7VI~OEV9+`b+4-@?WIy01d zVT&*6! z4ItavaB({if4Y`i@XHv9iB4Se!@-XgE1ub|rEi~Z+OF0AW^B<=KBrX@k{l0Dv*cR5 zrWIIa)6KUq)JVY#0x+-S*UFA;yV1`edL*{H;fhju^uExMuy$_juJtfcFGVY51f|qk zi?YVzR_5nQ1=rTK+Vo?niAo1FH~xz0Yn`kFsiBCC zLdu2J7o-*P9Hw>{O?0PiW}kyF5TQ`b=qPR|_Zk@ibTlZOes5q55vF$BDG@g+j&hQ? z#rzo1H6;~Q0uVP9A~pQ-DgL*c+`ur@x#^G%bkFd;@-2RHt1)OOF%hfthTcSavUEA- zfC>kfVCsMo0heF|_x@{#AbIvM8V|uf&F7FniRA%YY|DjfdCa>#V|1-v)IFIDX=7Iu zBAnR!brKx*)?wY4SDm6|Q^U9ro6t`RnLhY;L$HU?Ksn5~@s3gA5_n?Dwu%yx3HOt| za((G3Q$uhZQN!B3J*8FRPm8oJ#Xjj1k@~ICK#_Z}+Eiw&*n$T))&wcyzQFlT+x`&| z_HPIu`pQc+=Odf!mp5rFX;o2t81>uuOkk2<1P*ppQP+f!6P+c(!kfTEwvsEm% zk~$A<_nvN@yn0SmSIIGq7GipPdQw7<1|&YAd=ssGi$gq(dtzKtmZ@}AoB9LneZE8_ zdn&%%XkMFjjRzKpciGjyh7T!=fOOnJL?W6$q;+EO8xJbZam3W)Zfg}K1yOWMT=v%5 zv!OH%*VJ2wBSvJal?EN4ty)Vt%2fO)4M~u+ft5p^cauI_MJg#fUiIkHci9F z7Po%arvSUPx^rAzo~?^4;~gUBwTB94ZeY+?0_5kqz^6b#cVj$_P>x8Tkc%g$v!{vc{V9m?)w&zSn=F=g(R= zySgpvsy1euTzcis2$F@^+U)iOuTTY+gxY1`}K0T zphUKUxNE|c;9ECJo(jjz*1v*swUh?n|AVcJfja zU6OKvXA06Pxc*c-F`Z1*5QflJM3LLzi7@A9V)JLMI-pP(5`li8$@Zy<@0GyXF|6y2 zh`hcH=E8?Jki#O-xCWoS$;=s~I-GU)XjWN?6#n91Cz7 zAp=A3s4{d}(R0>d`IJ=TltyRdr7(@o1fuxVZF^Sc0vg znNU-ndEJeBAyb`c&d(5biD+mRmX(pYp(W%X53KwyVc8XI``xKxKhrDuO2g%bfo@q2 z2QVfG)|*QS(>UU1sprmGsq<2D+WR;sO13~6$7lyvbQVX=>Kajha@ z)c?DT>WyK22mW4@;6nJjjQV?3;tW>dugdBF`Ta}N6cD2E2nhs;CnZ(N+nUwQ7fQ2p zP1csxsIr0a73EH~$|{`ZS{oXyDk{_)8c(m>FWs$gVxr^%$2=y=v;(@ zaSk@{KG}GXcZXiU{H7po;@;yOGNEr7-ot+IP-J8KIzyi~EUOR6v?#j=i4?Q4XRF18mrV@3 zK-eI|E5)oZFZ+hRj*lrxH}A=32J~iDI=zg8<>*c8lARL@#WW#H#BbP|BmNM0+cCb` zx-QFbJ^+Gts$=SDR4icdkQ2y0E*pelX2koaFVoY%rwmF9K~@A(@q^K9pm64!L383Q)b=1RVF#X=P3+Boj^c;abNYlp@sxg28AAY*Q&%v6@_; zAD?2;X+Rth_v}}@d+NP3cyS=^2HADHJkw}Yp|=`pe%A1?Q3EK|!-O~cifkBE17UGu zsXTz0YV@Ui(dg5`Ec=8koj^r2SDIg`&;ZneSuu0Z0?E$>?EE0b@1bdd1G54Tnb9c4 zp9^sLL;f}Kr;x=AzW4bHVi@EE26eM1zJ|pMwF~G16$x8s`$H*#uZcO6^z$qH510ZO z=EZlAKs}l|#Sn%T9XZtW^8hzSES|1XKH}rRawGH$ zNfquYJL$OhM$kpYMJ1{#3$xf4doywjjEu?L(bRxRl)RkI%@t7=rV-!NQXxeq<}(QN z(ldh6E*99i2Nj706CdPRdN}kSIE2{_wqv06J-9Cz=$qK7dEyA#hnBKxYj8gO{ zlpNJxjoV366{#IIom|YNtV{0UVOCOrbO}OYfBgqiY&cmJS`{(NPEvKuQ8Yt34Ct0}@O{WHx z2=|gN&m@c386@>Jwor6PGbf36`Dv0PoeSwhEXbi!K^F68EgAGOY7QTt!wpI=s^^Ri z*bx}aep_1JI0#tbCq|eedlrJvl=N;XK)AyyVrN32FF6{l?X3thG*%<)WtA*)%Rg|- z*G$cqIO7o2M58KEWH>BLZBrWHs1<=~+&1-lvJLiWE+Sf> zM4ym=EZqLlx4K}{Dv9tY$_y(r7~t$ls}C;^vdP$#dZ3#)!>Bma>8_v%!WfIl?FccIbIg zmEoX%;v&QVLK-NTcn`~R#R<-V9EdC+wQcq}y1_usifb&EF9G;G8E789D#zS^1#n@M{?1Gy3z_?${Cf+ZYt;V8V z>Mh(>z#9?-!>DGVDEv@mQE%A~S#TM7N@*>)B*J)2p33)?+g89??1ypSgVZ&Gxv7`| zLK%Xp#kd5l_$yu9oqj-t5=E0e?At*n^05$<8af`FxYep)SUObW>tgEXJB+T3z^F$E zC|~2G+%*aPW2akD>T;_BiW|s9Lyj5+ZfI}571b0Qq1P;NBFwKza-PPBicwZIi)mcM z3Y0=egh)jYLdk02T`DIY6FN+7EE(_=2LLP7x>_=>eUm9a7;iyrXoE!7O-ar+UYB6= zYOJOv_=zhthiG`PV4j8U+lt287GEQd;f`N_el_ zBY=0?u+L;p1exfR&O}zk9}1a2v=$rlVGoP;qx?yh7LPUQMAj`O%%pM3>TaXA#ykX) zY1Qn*-PDLsMJVk;;+L)4Tl4BDAWCnR$wLIceVF>5PTiX9IeHKC2r@Hjs&fv#c6#O# zfce4Ss*Kqio2@BOFus26$mUvW@=U&EkSB8hM{3VBnF??ce?EFGPh~AiL_0wf_A(Cj zRunTJsGHYCpNg{#ec?#wFR?kLMdjx+P{4*uFcO$QU|L3=NK%)iYycG{NE{f5mBCVt z)%9o2B}MY7uuld*N3*Qt6B7Tq@Juv%nhXqz?;HMUGWVFzBG|iQH?4FI+R(4;(152< zgg{{-kVQd2dvD>+o@pvWq+V}-YuKtJO6|nUy*H^0FYO4WL=tz?s8UP6Q5bc`4BeF= z%W(0<N%Hv7e@1)td!$I{5v0M-Nja=rGb{uU&9-CG<*HWUmp<7n@jZ&*$T0!Gx0A_V1 zR?5zR^G{L_zaIL$Pnj1oPDOq`=*w>uc`s#nWlKPQ`&tpYo9$adDX zsi^~{=AHhQBSDG?9WoE0W?JYt<<2%$t!GV8W0VDT)@I5j3tx^|$Tn$?ws5WkYuy|~UUnLG*uIK`9a8dsNUkl+Fc zzR>LUHCV_iT!o^ZpIXo_bkpF{EhSkXhPbLxB+i;RN>1OEmgg$SWOk*?0@N7NRLvfN zxKFl!t*zfb0l75P3IiNF7P0dHLgvK zEhGyKD^$Ci&ZwhoE##V6$F>w3z?)EDPE}bjHDw2%9HW{XV>&w6Zug))n%<>((^=uc zGG_OPRBDm63f!6;%&Unk7Rcu$hbGAYqEUpcB*{3wCKx4a$e}h$7NoMBBbWjNxe$%3Lpbnyp>Fz>uFxPRkGE0I52S%bqaGz zB;Qf~X>!}yyX4>Q!8Y*W&qa7f3Plk^BfzC(MN>Lbc@`eVuPs*vo~+HVaCQns%x|~$ z-M|;JqB=bfW=}|`MDO=G7#OShSa%`zA*3_QpazdpFQh8suh;T^-%K^THKC?o$ePcB zlLkQ!{P}<)-%;`j?in#k{gI>4O}WGPR$Lf>p^uC_*Qq%?_dfKr4r~?9z4ZtE&d@ELWZX_HFxBWmDwUP# z0c+D4qAeAtP(2q>RpXoHT=J>49!R7W2*#R?Wxv%)crhm9h{j@+tM+erhq$HD6)1z7 zl6vaeK?2pOVP+*qJd^4N4j{Ds_;&KTv*4g3l=md1iQW;1(N6?^P zq?VC`nbXF|c)>Iii1`J`6I!i*qIxiAgFzm&3$5acp38oghmN}I!<=Djn}(8qRX~B^N8eAngeyVQ1SId98A0OB%_A*8t~#tu7v9E zpADMS^AAJ-=9{VGBC0~2R{0d~HF^}v>*nobNt^2CZ{JmGPQYBky^DVEtsw|5Y=FWn z!Amp=-pK^L>znqMODfyjw+}RKzcV1na?IU-;Vm(9;YE>I5tAT~?HM7kyFm58>$q^^ zCmN%^#}`dAc#u*uDrKHS3;vt}^+%i%9VWlJM>kc}y*h6VbRkaAfQq^Ra~I}IpwcW; zswhj=m7=yDX-v+Xi51*9A*1Kg=hUCsm5~&nh@{aY*G{g%oG9m9yO)F2 z%}e$ZhY8xahceDJe1)yys-jr}>Y`@veRbBBWL4wLO9k-6KjktQ`O}&I)o$0)Hs617 zS|BLRIl#m?fMn90d{!QeFiBTb51E?Ep~hEpzol$?2kU$!&xK{F|7g`|l- zSv@lLtPqFbHr)xsPkI&G0~*D`g}{l>GrEK#)tv!>bHn(4Pu^^C_JUhFRxj8nVHAqK zV6S7z%cXxy2o}ZeOq#S~cSJ|zh0a{Gl_2v2V|A%eRLpEIMY3E9ymYtSFdM`kSRvvO z>apGNGQqrrn zJzjJbxu`KEs*J$MpJ*jKE8UPHIWZrcv%A82tnQ6S+iuu|GTB8O&7p;^TZ${$3t}HW z!jl%NgTel6eK2R=J>eb?$H+!r3S02sWpihp&Xm^G)I%bislKukz`9{tXh}-_REpkX zu0lrcNu;-Z!x!c)xEd9zO6gf|^Tw=g*z&&>ps1RmA4pe4^e&BKl^(yNH`vlK2VTVl z<`><4%Pf2xsVm>&2!o4Lpiac<1=l3FPAFk|MH5|wicE>LIcsqCtK#dP2U%iNy9SNT3J|OY5daK=%7o0GVOTDimp#Q52dV1EDw3T z08xu&Tub53agp&4^0WCWs{eHxjdPD%*g#HNZ^Ko}x^~m%A7a1l zGNgnP;mC0r!3JcM+&*#(xGvoVyXUXud)#bprK) zA^p02XXE{RN`q69oMBV(kd~y5zvF(`o(|Mm`#-H+2RN1Q`$UejrfQUw)BqRF~VN&*Y=u zo=Dz=4uQLeTLM0mPs{RM&TjVTq5McOojjW0_&q7wEZ}qi?fIE*%t({C2yYWXlgX-B zZUK(I?)?;BgxXfFeLGM~sE7acjZb;QvM~Kz2nSos-um%9tl^fT7qW$wKUI&=Kbq`R zF^O+qeQ@qF?p3_@r|yA+L^^Gg`LpwJj-=`YqpnlUMAniSQpR!(A4Uw`ydad5(DS{K z&w4f3NY>(g6yZR*kcqs$d7;(ex=N(FVX;PX*cIunnrYFxk!AG^TGemvwL|yEZ+mO=uP=SS7hApX4Lk9oVX#JSmGGHW+xrYe1o!&Nmp#?>%H%C4UZ{U@ z_MI`0!97nBapQJ;nDfG9wB1V&Kj~|dUb%Sx+NJBqd}ocS z7O|z>S}F2Gj|txkVSFR7mo|z{gwGgP`8_8Ki9L~WcAIeAY_n)TNB0#WLz;++Cceg4 zoL!}dl=^Qo3hu@`PNn5Sc-faLXHYnP_a6RcoPz6IY3ft9EXX(V*ILMsCz?H2^h1(v zj#e3XW-U8};&J ztq|G>^A|lxjas(#4%c}tiEHUity2Aa<`K4sqN!CrZmFs8lyjRXLmz;hv?I!Zg95ds4Z)2gq$MoarqLOOpWO)>p*av?>p;!o#4Gr48vy!-(t{3m178x z?VLXrzlu#jME5ixdw;*;*V5W3l0NZ+pKkA~9xT(+>Qdytgx!bmx8qLM+kI%!<^_M- zz#@$;*&#;P%OA->=$= zh_(~yh_ZrHrwx(E6PQ_-Z)7mMyVfFvq&O00I%dxowGw%Kl}J}^HLYz7YhYAz{!P@t zS+R_;3zzRk=h_6$_y!(#<317>aydabApCvNO9=znkuS6lBck$Fc5BUdR^ohe9%Q~= zDSv8Fpov{QYEM8#P`xA0Mew*7eh^0zoiJTv-FFJ?Be%4=jfM(}$VZ+o^ z46l2`tW*23Eu^2Ea=={KV^Gx}IO#)_8`H9D$H6d~TT`lenfMurZbNDJY^XT*rg^nV z)G9iF`0 zJ0aw0X+@N9n?~M>cE1K&AL7Q@?t}D#d#BTkUq&cIPf~|dTaZu37w2W@nsGeuLM!bh9Xf*qNo<(=j^cBNI8xb23<+9J!D+ckAs!1Q* z$aAao-XD`cQ?yI$(vnE~V5|9!J!uLV*E~G4j=t;ejToSMHYqnSm`iNGPs*7byM%JU zhwipJ-N-keRpwB|l=%SqI!P;Eys|)|kRf5rmD~PTr%$&Bha1t*Nm_h)fZTU$?vO0@vg#V9Q;BleV?L6CpG

OF5tCb=``Si1g<}%8 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 C%co)h diff --git a/librxtxSerial.jnilib b/librxtxSerial.jnilib deleted file mode 100644 index b15dfa5e8772b7272e6fabd11b689a4939827924..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 271860 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∈goUx6EP4bU9Ep>_BUhwpcI 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

3*q_-;WH?Eq(xe&`0-TK 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 diff --git a/pom.xml b/pom.xml index 4d80b41..0b95f0b 100644 --- a/pom.xml +++ b/pom.xml @@ -5,28 +5,23 @@ com.rapplogic xbeeapi jar - 0.9.1-SNAPSHOT + 0.9.2-SNAPSHOT XBee-API UTF-8 + 1.8 + 1.8 - src - - - resources - - - org.apache.maven.plugins maven-compiler-plugin - 1.5 - 1.5 + 1.8 + 1.8 @@ -34,15 +29,26 @@ - org.scream3r - jssc - 2.8.0 + org.scream3r + jssc + 2.8.0 - log4j log4j - 1.2.16 + 1.2.17 + + + org.testng + testng + 6.9.10 + test + + + org.assertj + assertj-core + 3.3.0 + test \ No newline at end of file diff --git a/rxtxSerial.dll b/rxtxSerial.dll deleted file mode 100644 index c0e6b58225b30c623cae7fd4553a4dd0ed581bb0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77759 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 diff --git a/src/com/rapplogic/xbee/api/XBeeInformation.java b/src/com/rapplogic/xbee/api/XBeeInformation.java deleted file mode 100644 index b69745e..0000000 --- a/src/com/rapplogic/xbee/api/XBeeInformation.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.rapplogic.xbee.api; - -import com.rapplogic.xbee.api.HardwareVersion.RadioType; - -public class XBeeInformation { - enum XBeeMode { - COORDINATOR, ROUTER, END_DEVICE, UNKNOWN; - } - private RadioType hardwareVersion; - private int firmwareVersion; - private long panID; - private int serialHigh; - private int serialLow; - private XBeeMode mode = XBeeMode.UNKNOWN; - private long lastReceivedMessage; - - public void setHardwareVersion(RadioType hardwareVersion) { - this.hardwareVersion = hardwareVersion; - } - - public void setFirmwareVersion(int[] fv) { - this.firmwareVersion = fv[0] << 8 | fv[1]; - // First byte of the firmware version tells us which mode we're running in - // in case we're using API mode (which we are). - switch(fv[0]) - { - case 0x21: - mode = XBeeMode.COORDINATOR; - break; - case 0x23: - mode = XBeeMode.ROUTER; - break; - case 0x29: - mode = XBeeMode.END_DEVICE; - break; - default: - mode = XBeeMode.UNKNOWN; - } - } - - public void setPanID(int[] id) { - this.panID = (long)id[0] << 56 | (long)id[1] << 48 | (long)id[2] << 40 | (long)id[3] << 32 | - id[4] << 24 | id[5] << 16 | id[6] << 8 | id[7]; - } - - public void setSerialHigh(int[] sh) { - this.serialHigh = sh[0] << 24 | sh[1] << 16 | sh[2] << 8 | sh[3]; - } - - public void setSerialLow(int[] sl) { - this.serialLow = sl[0] << 24 | sl[1] << 16 | sl[2] << 8 | sl[3]; - } - - public XBeeMode getMode() { - return mode; - } - - public void touchLastReceivedTime() { - lastReceivedMessage = System.currentTimeMillis(); - } - - public long getLastReceviedTime() { - return lastReceivedMessage; - } - - @Override - public String toString() { - return "XBeeInformation [hardwareVersion=" + hardwareVersion - + ", firmwareVersion=" + String.format("%02X", firmwareVersion) + ", panID=" + String.format("%08X", panID) - + ", serialHigh=" + String.format("%04X", serialHigh) + ", serialLow=" + String.format("%04X", serialLow) - + ", mode " + mode.toString() + "]"; - } - -} diff --git a/src/com/rapplogic/xbee/api/ApiId.java b/src/main/java/com/rapplogic/xbee/api/ApiId.java similarity index 100% rename from src/com/rapplogic/xbee/api/ApiId.java rename to src/main/java/com/rapplogic/xbee/api/ApiId.java diff --git a/src/com/rapplogic/xbee/api/Checksum.java b/src/main/java/com/rapplogic/xbee/api/Checksum.java similarity index 95% rename from src/com/rapplogic/xbee/api/Checksum.java rename to src/main/java/com/rapplogic/xbee/api/Checksum.java index e0fa669..8d12374 100644 --- a/src/com/rapplogic/xbee/api/Checksum.java +++ b/src/main/java/com/rapplogic/xbee/api/Checksum.java @@ -1,99 +1,99 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -import org.apache.log4j.Logger; - -import com.rapplogic.xbee.util.ByteUtils; - -/** - * Computes and verifies packet checksums - *

- * @author andrew - * - */ -public class Checksum { - - private final static Logger log = Logger.getLogger(Checksum.class); - - public int checksum = 0; - - /** - * Don't add Checksum byte when computing checksum!! - * - * @param val - */ - public void addByte(int val) { - checksum+= val; - } - - /** - * Computes checksum and stores in checksum instance variable - * - * @return - */ - public void compute() { - // discard values > 1 byte - checksum = 0xff & checksum; - // perform 2s complement - checksum = 0xff - checksum; - - log.debug("computed checksum is " + ByteUtils.formatByte(checksum)); - } - - /** - * First add all relevant bytes, including checksum - * - * @return - */ - public boolean verify() { - checksum = checksum & 0xff; - log.debug("verify checksum is " + checksum); - return 0xff == checksum; - } - - public int getChecksum() { - return checksum; - } - - public static void main(String[] args) { - //83 56 78 24 00 01 02 00 03 ff 85 - Checksum ck = new Checksum(); - - ck.addByte(0x83); - ck.addByte(0x56); - ck.addByte(0x78); - ck.addByte(0x26); - ck.addByte(0x00); - ck.addByte(0x01); - ck.addByte(0x02); - ck.addByte(0x00); - ck.addByte(0x03); - ck.addByte(0xff); - // checksum - ck.addByte(0x83); - - // checksum is 0x83 - //ck.compute(); - ck.verify(); - - System.out.println("checksum is " + ByteUtils.formatByte(ck.getChecksum())); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api; + +import org.apache.log4j.Logger; + +import com.rapplogic.xbee.util.ByteUtils; + +/** + * Computes and verifies packet checksums + *

+ * @author andrew + * + */ +public class Checksum { + + private final static Logger log = Logger.getLogger(Checksum.class); + + public int checksum = 0; + + /** + * Don't add Checksum byte when computing checksum!! + * + * @param val + */ + public void addByte(int val) { + checksum+= val; + } + + /** + * Computes checksum and stores in checksum instance variable + * + * @return + */ + public void compute() { + // discard values > 1 byte + checksum = 0xff & checksum; + // perform 2s complement + checksum = 0xff - checksum; + + log.debug("computed checksum is " + ByteUtils.formatByte(checksum)); + } + + /** + * First add all relevant bytes, including checksum + * + * @return + */ + public boolean verify() { + checksum = checksum & 0xff; + log.debug("verify checksum is " + checksum); + return 0xff == checksum; + } + + public int getChecksum() { + return checksum; + } + + public static void main(String[] args) { + //83 56 78 24 00 01 02 00 03 ff 85 + Checksum ck = new Checksum(); + + ck.addByte(0x83); + ck.addByte(0x56); + ck.addByte(0x78); + ck.addByte(0x26); + ck.addByte(0x00); + ck.addByte(0x01); + ck.addByte(0x02); + ck.addByte(0x00); + ck.addByte(0x03); + ck.addByte(0xff); + // checksum + ck.addByte(0x83); + + // checksum is 0x83 + //ck.compute(); + ck.verify(); + + System.out.println("checksum is " + ByteUtils.formatByte(ck.getChecksum())); + } +} diff --git a/src/com/rapplogic/xbee/api/CollectTerminator.java b/src/main/java/com/rapplogic/xbee/api/CollectTerminator.java similarity index 100% rename from src/com/rapplogic/xbee/api/CollectTerminator.java rename to src/main/java/com/rapplogic/xbee/api/CollectTerminator.java diff --git a/src/com/rapplogic/xbee/api/HardwareVersion.java b/src/main/java/com/rapplogic/xbee/api/HardwareVersion.java similarity index 96% rename from src/com/rapplogic/xbee/api/HardwareVersion.java rename to src/main/java/com/rapplogic/xbee/api/HardwareVersion.java index feedf4d..a3b5f7c 100644 --- a/src/com/rapplogic/xbee/api/HardwareVersion.java +++ b/src/main/java/com/rapplogic/xbee/api/HardwareVersion.java @@ -1,75 +1,76 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - - -/** - * Represents a XBee Address. - *

- * @author andrew - * - */ -public class HardwareVersion { - - public enum RadioType { - SERIES1("Series 1"), - SERIES1_PRO("Series 1 Pro"), - SERIES2("Series 2"), - SERIES2_PRO("Series 2 Pro"), - SERIES2B_PRO("Series 2B Pro"), - UNKNOWN("Unknown"); - - private String name; - - RadioType(String name) { - this.name = name; - } - - public String toString() { - return name; - } - } - - public static RadioType parse(AtCommandResponse response) throws XBeeException { - - if (!response.getCommand().equals("HV")) { - throw new IllegalArgumentException("This is only applicable to the HV command"); - } - - if (!response.isOk()) { - throw new XBeeException("Attempt to query HV parameter failed"); - } - - switch (response.getValue()[0]) { - case 0x17: - return RadioType.SERIES1; - case 0x18: - return RadioType.SERIES1_PRO; - case 0x19: - return RadioType.SERIES2; - case 0x1a: - return RadioType.SERIES2_PRO; - case 0x1e: - return RadioType.SERIES2B_PRO; - } - - return RadioType.UNKNOWN; - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api; + +import com.rapplogic.xbee.api.responses.AtCommandResponse; + +/** + * Represents a XBee Address. + *

+ * @author andrew + * + */ +public class HardwareVersion { + + public enum RadioType { + SERIES1("Series 1"), + SERIES1_PRO("Series 1 Pro"), + SERIES2("Series 2"), + SERIES2_PRO("Series 2 Pro"), + SERIES2B_PRO("Series 2B Pro"), + UNKNOWN("Unknown"); + + private String name; + + RadioType(String name) { + this.name = name; + } + + public String toString() { + return name; + } + } + + public static RadioType parse(AtCommandResponse response) throws XBeeException { + + if (!response.getCommand().equals("HV")) { + throw new IllegalArgumentException("This is only applicable to the HV command"); + } + + if (!response.isOk()) { + throw new XBeeException("Attempt to query HV parameter failed"); + } + + switch (response.getValue()[0]) { + case 0x17: + return RadioType.SERIES1; + case 0x18: + return RadioType.SERIES1_PRO; + case 0x19: + return RadioType.SERIES2; + case 0x1a: + return RadioType.SERIES2_PRO; + case 0x1e: + return RadioType.SERIES2B_PRO; + } + + return RadioType.UNKNOWN; + } +} diff --git a/src/com/rapplogic/xbee/api/IPacketParser.java b/src/main/java/com/rapplogic/xbee/api/IPacketParser.java similarity index 100% rename from src/com/rapplogic/xbee/api/IPacketParser.java rename to src/main/java/com/rapplogic/xbee/api/IPacketParser.java diff --git a/src/com/rapplogic/xbee/api/IXBee.java b/src/main/java/com/rapplogic/xbee/api/IXBee.java similarity index 100% rename from src/com/rapplogic/xbee/api/IXBee.java rename to src/main/java/com/rapplogic/xbee/api/IXBee.java diff --git a/src/com/rapplogic/xbee/api/InputStreamThread.java b/src/main/java/com/rapplogic/xbee/api/InputStreamThread.java similarity index 87% rename from src/com/rapplogic/xbee/api/InputStreamThread.java rename to src/main/java/com/rapplogic/xbee/api/InputStreamThread.java index 55fce29..0a3fce8 100644 --- a/src/com/rapplogic/xbee/api/InputStreamThread.java +++ b/src/main/java/com/rapplogic/xbee/api/InputStreamThread.java @@ -1,230 +1,224 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; - -import org.apache.log4j.Logger; - -import com.rapplogic.xbee.XBeeConnection; -import com.rapplogic.xbee.util.ByteUtils; - -/** - * Reads data from the input stream and hands off to PacketParser for packet parsing. - * Notifies XBee class when a new packet is parsed - *

- * @author andrew - * - */ -public class InputStreamThread implements Runnable { - - private final static Logger log = Logger.getLogger(InputStreamThread.class); - - private Thread thread; - private ExecutorService listenerPool; - private volatile boolean done = false; - private final XBeeConnection connection; - private XBeeConfiguration conf; - private XBeeInformation info; - - public XBeeConnection getXBeeConnection() { - return connection; - } - - private final BlockingQueue responseQueue = new LinkedBlockingQueue(); - - // TODO use weak references - private final List packetListenerList = new LinkedList(); - - - public List getPacketListenerList() { - return packetListenerList; - } - - public BlockingQueue getResponseQueue() { - return responseQueue; - } - - public InputStreamThread(final XBeeConnection connection, XBeeConfiguration conf, XBeeInformation info) { - this.connection = connection; - this.conf = conf; - this.info = info; - - // Create an executor to deliver incoming packets to listeners. We'll use a single - // thread with an unbounded queue. - listenerPool = Executors.newSingleThreadExecutor(); - - thread = new Thread(this); - thread.setName("InputStreamThread"); - thread.start(); - - log.debug("starting packet parser thread"); - } - - private void addResponse(final XBeeResponse response) throws InterruptedException { - - if (conf.getResponseQueueFilter() != null) { - if (conf.getResponseQueueFilter().accept(response)) { - this.addToResponseQueue(response); - } - } else { - this.addToResponseQueue(response); - } - - listenerPool.submit(new Runnable() { - public void run() { - // must synchronize to avoid java.util.ConcurrentModificationException at java.util.AbstractList$Itr.checkForComodification(Unknown Source) - // this occurs if packet listener add/remove is called while we are iterating - - synchronized (packetListenerList) { - for (PacketListener pl : packetListenerList) { - try { - if (pl != null) { - pl.processResponse(response); - } else { - log.warn("PacketListener is null, size is " + packetListenerList.size()); - } - } catch (Throwable th) { - log.warn("Exception in packet listener", th); - } - } - } - } - }); - } - - private void addToResponseQueue(final XBeeResponse response) throws InterruptedException{ - - if (conf.getMaxQueueSize() == 0) { - // warn - return; - } - // trim the queue - while (responseQueue.size() >= conf.getMaxQueueSize()) { - log.info("Response queue has reached the maximum size of " + conf.getMaxQueueSize() + " packets. Trimming a packet from head of queue to make room"); - responseQueue.poll(); - } - - responseQueue.put(response); - } - - public void run() { - - int val = -1; - - XBeeResponse response = null; - PacketParser packetStream = null; - - try { - while (!done) { - try { - if (connection.hasData()) { - log.debug("About to read from input stream"); - - val = connection.getByte(); - log.debug("Read " + ByteUtils.formatByte(val) + " from input stream"); - - if (val == XBeePacket.SpecialByte.START_BYTE.getValue()) { - packetStream = new PacketParser(connection); - response = packetStream.parsePacket(); - - if (log.isInfoEnabled()) { - log.info("Received packet from XBee: " + response); -// log.debug("Received packet: int[] packet = {" + ByteUtils.toBase16(response.getRawPacketBytes(), ", ") + "};"); - } - - // success - info.touchLastReceivedTime(); - this.addResponse(response); - } else { - log.warn("expected start byte but got this " + ByteUtils.toBase16(val) + ", discarding"); - } - } else { - log.debug("No data available.. waiting for new data event"); - - // we will wait here for RXTX to notify us of new data - synchronized (this.connection) { - // There's a chance that we got notified after the first in.available check - if (connection.hasData()) { - continue; - } - - // wait until new data arrives - this.connection.wait(); - } - } - } catch (Exception e) { - if (e instanceof InterruptedException) throw ((InterruptedException)e); - - log.error("Error while parsing packet:", e); - - if (e instanceof IOException) { - // this is thrown by RXTX if the serial device unplugged while we are reading data; if we are waiting then it will waiting forever - log.error("Serial device IOException.. exiting"); - break; - } - } - } - } catch(InterruptedException ie) { - // We've been told to stop -- the user called the close() method - log.info("Packet parser thread was interrupted. This occurs when close() is called"); - } catch (Throwable t) { - log.error("Error in input stream thread.. exiting", t); - } finally { - try { - if (connection != null) { - connection.close(); - } - - if (listenerPool != null) { - try { - listenerPool.shutdownNow(); - } catch (Throwable t) { - log.warn("Failed to shutdown listner thread pool", t); - } - } - } catch (Throwable t) { - log.error("Error in input stream thread finally", t); - } - } - - log.info("InputStreamThread is exiting"); - } - - public void setDone(boolean done) { - this.done = done; - } - - public void interrupt() { - if (thread != null) { - try { - thread.interrupt(); - } catch (Exception e) { - log.warn("Error interrupting parser thread", e); - } - } - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; + +import org.apache.log4j.Logger; + +import com.rapplogic.xbee.connections.XBeeConnection; +import com.rapplogic.xbee.util.ByteUtils; + +/** + * Reads data from the input stream and hands off to PacketParser for packet parsing. + * Notifies XBee class when a new packet is parsed + *

+ * @author andrew + * + */ +public class InputStreamThread implements Runnable { + + private final static Logger log = Logger.getLogger(InputStreamThread.class); + + private Thread thread; + private ExecutorService listenerPool; + private volatile boolean done = false; + private final XBeeConnection connection; + private XBeeConfiguration conf; + + public XBeeConnection getXBeeConnection() { + return connection; + } + + private final BlockingQueue responseQueue = new LinkedBlockingQueue(); + + // TODO use weak references + private final List packetListenerList = new LinkedList(); + + + public List getPacketListenerList() { + return packetListenerList; + } + + public BlockingQueue getResponseQueue() { + return responseQueue; + } + + public InputStreamThread(final XBeeConnection connection, XBeeConfiguration conf) { + this.connection = connection; + this.conf = conf; + + // Create an executor to deliver incoming packets to listeners. We'll use a single + // thread with an unbounded queue. + listenerPool = Executors.newSingleThreadExecutor(); + + thread = new Thread(this); + thread.setName("InputStreamThread"); + thread.start(); + + log.debug("starting packet parser thread"); + } + + private void addResponse(final XBeeResponse response) throws InterruptedException { + + if (conf.getResponseQueueFilter() != null) { + if (conf.getResponseQueueFilter().accept(response)) { + this.addToResponseQueue(response); + } + } else { + this.addToResponseQueue(response); + } + + listenerPool.submit(new Runnable() { + public void run() { + // must synchronize to avoid java.util.ConcurrentModificationException at java.util.AbstractList$Itr.checkForComodification(Unknown Source) + // this occurs if packet listener add/remove is called while we are iterating + + synchronized (packetListenerList) { + for (PacketListener pl : packetListenerList) { + try { + if (pl != null) { + pl.processResponse(response); + } else { + log.warn("PacketListener is null, size is " + packetListenerList.size()); + } + } catch (Throwable th) { + log.warn("Exception in packet listener", th); + } + } + } + } + }); + } + + private void addToResponseQueue(final XBeeResponse response) throws InterruptedException{ + + if (conf.getMaxQueueSize() == 0) { + // warn + return; + } + // trim the queue + while (responseQueue.size() >= conf.getMaxQueueSize()) { + log.info("Response queue has reached the maximum size of " + conf.getMaxQueueSize() + " packets. Trimming a packet from head of queue to make room"); + responseQueue.poll(); + } + + responseQueue.put(response); + } + + public void run() { + + int val = -1; + + XBeeResponse response = null; + PacketParser packetStream = null; + + try { + while (!done) { + try { + if (connection.hasData()) { + log.debug("About to read from input stream"); + + val = connection.getByte(); + log.debug("Read " + ByteUtils.formatByte(val) + " from input stream"); + + if (val == XBeePacket.SpecialByte.START_BYTE.getValue()) { + packetStream = new PacketParser(connection); + response = packetStream.parsePacket(); + + log.info("Received packet from XBee: " + response); + + // success + this.addResponse(response); + } else { + log.warn("expected start byte but got this " + ByteUtils.toBase16(val) + ", discarding"); + } + } else { + log.debug("No data available.. waiting for new data event"); + + // we will wait here for RXTX to notify us of new data + synchronized (this.connection) { + // There's a chance that we got notified after the first in.available check + if (connection.hasData()) { + continue; + } + + // wait until new data arrives + this.connection.wait(); + } + } + } catch (Exception e) { + if (e instanceof InterruptedException) throw ((InterruptedException)e); + + log.error("Error while parsing packet:", e); + + if (e instanceof IOException) { + // this is thrown by RXTX if the serial device unplugged while we are reading data; if we are waiting then it will waiting forever + log.error("Serial device IOException.. exiting"); + break; + } + } + } + } catch(InterruptedException ie) { + // We've been told to stop -- the user called the close() method + log.info("Packet parser thread was interrupted. This occurs when close() is called"); + } catch (Throwable t) { + log.error("Error in input stream thread.. exiting", t); + } finally { + try { + if (connection != null) { + connection.close(); + } + + if (listenerPool != null) { + try { + listenerPool.shutdownNow(); + } catch (Throwable t) { + log.warn("Failed to shutdown listner thread pool", t); + } + } + } catch (Throwable t) { + log.error("Error in input stream thread finally", t); + } + } + + log.info("InputStreamThread is exiting"); + } + + public void setDone(boolean done) { + this.done = done; + } + + public void interrupt() { + if (thread != null) { + try { + thread.interrupt(); + } catch (Exception e) { + log.warn("Error interrupting parser thread", e); + } + } + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/PacketListener.java b/src/main/java/com/rapplogic/xbee/api/PacketListener.java similarity index 100% rename from src/com/rapplogic/xbee/api/PacketListener.java rename to src/main/java/com/rapplogic/xbee/api/PacketListener.java diff --git a/src/com/rapplogic/xbee/api/PacketParser.java b/src/main/java/com/rapplogic/xbee/api/PacketParser.java similarity index 96% rename from src/com/rapplogic/xbee/api/PacketParser.java rename to src/main/java/com/rapplogic/xbee/api/PacketParser.java index 48dc298..818cf1e 100644 --- a/src/com/rapplogic/xbee/api/PacketParser.java +++ b/src/main/java/com/rapplogic/xbee/api/PacketParser.java @@ -1,379 +1,384 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import org.apache.log4j.Logger; - -import com.rapplogic.xbee.XBeeConnection; -import com.rapplogic.xbee.api.wpan.RxResponse16; -import com.rapplogic.xbee.api.wpan.RxResponse64; -import com.rapplogic.xbee.api.wpan.RxResponseIoSample; -import com.rapplogic.xbee.api.wpan.TxStatusResponse; -import com.rapplogic.xbee.api.zigbee.ZNetExplicitRxResponse; -import com.rapplogic.xbee.api.zigbee.ZNetNodeIdentificationResponse; -import com.rapplogic.xbee.api.zigbee.ZNetRxIoSampleResponse; -import com.rapplogic.xbee.api.zigbee.ZNetRxResponse; -import com.rapplogic.xbee.api.zigbee.ZNetTxStatusResponse; -import com.rapplogic.xbee.util.ByteUtils; -import com.rapplogic.xbee.util.IIntInputStream; -import com.rapplogic.xbee.util.IntArrayOutputStream; - -/** - * Reads a packet from the input stream, verifies checksum and creates an XBeeResponse object - *

- * Notes: - *

- * Escaped bytes increase packet length but packet stated length only indicates un-escaped bytes. - * Stated length includes all bytes after Length bytes, not including the checksum - *

- * @author Andrew Rapp - * - */ -public class PacketParser implements IIntInputStream, IPacketParser { - - private final static Logger log = Logger.getLogger(PacketParser.class); - - // size of packet after special bytes have been escaped - private XBeePacketLength length; - private Checksum checksum = new Checksum(); - - private boolean done = false; - - private int bytesRead; - private int escapeBytes; - - private XBeeResponse response; - private ApiId apiId; - private int intApiId; - - private final XBeeConnection connection; - - private static Map> handlerMap = new HashMap>(); - - // TODO reuse this object for all packets - - // experiment to preserve original byte array for transfer over network (Starts with length) - private IntArrayOutputStream rawBytes = new IntArrayOutputStream(); - - static { - // TODO put all response handlers in specific packet and load all. implement static handlesApi method - handlerMap.put(ApiId.AT_RESPONSE.getValue(), AtCommandResponse.class); - handlerMap.put(ApiId.MODEM_STATUS_RESPONSE.getValue(), ModemStatusResponse.class); - handlerMap.put(ApiId.REMOTE_AT_RESPONSE.getValue(), RemoteAtResponse.class); - handlerMap.put(ApiId.RX_16_IO_RESPONSE.getValue(), RxResponseIoSample.class); - handlerMap.put(ApiId.RX_64_IO_RESPONSE.getValue(), RxResponseIoSample.class); - handlerMap.put(ApiId.RX_16_RESPONSE.getValue(), RxResponse16.class); - handlerMap.put(ApiId.RX_64_RESPONSE.getValue(), RxResponse64.class); - handlerMap.put(ApiId.TX_STATUS_RESPONSE.getValue(), TxStatusResponse.class); - handlerMap.put(ApiId.ZNET_EXPLICIT_RX_RESPONSE.getValue(), ZNetExplicitRxResponse.class); - handlerMap.put(ApiId.ZNET_IO_NODE_IDENTIFIER_RESPONSE.getValue(), ZNetNodeIdentificationResponse.class); - handlerMap.put(ApiId.ZNET_IO_SAMPLE_RESPONSE.getValue(), ZNetRxIoSampleResponse.class); - handlerMap.put(ApiId.ZNET_RX_RESPONSE.getValue(), ZNetRxResponse.class); - handlerMap.put(ApiId.ZNET_TX_STATUS_RESPONSE.getValue(), ZNetTxStatusResponse.class); - } - - static void registerResponseHandler(int apiId, Class clazz) { - if (handlerMap.get(apiId) == null) { - log.info("Registering response handler " + clazz.getCanonicalName() + " for apiId: " + apiId); - } else { - log.warn("Overriding existing implementation: " + handlerMap.get(apiId).getCanonicalName() + ", with " + clazz.getCanonicalName() + " for apiId: " + apiId); - } - - handlerMap.put(apiId, clazz); - } - - static void unRegisterResponseHandler(int apiId) { - if (handlerMap.get(apiId) != null) { - log.info("Unregistering response handler " + handlerMap.get(apiId).getCanonicalName() + " for apiId: " + apiId); - handlerMap.remove(apiId); - } else { - throw new IllegalArgumentException("No response handler for: " + apiId); - } - } - - public PacketParser(XBeeConnection connection) { - this.connection = connection; - } - - /** - * This method is guaranteed (unless I screwed up) to return an instance of XBeeResponse and should never throw an exception - * If an exception occurs, it will be packaged and returned as an ErrorResponse. - * - * @return - */ - public XBeeResponse parsePacket() { - - Exception exception = null; - - try { - // BTW, length doesn't account for escaped bytes - int msbLength = this.read("Length MSB"); - int lsbLength = this.read("Length LSB"); - - // length of api structure, starting here (not including start byte or length bytes, or checksum) - this.length = new XBeePacketLength(msbLength, lsbLength); - - log.debug("packet length is " + String.format("[0x%03X]", length.getLength())); - - // total packet length = stated length + 1 start byte + 1 checksum byte + 2 length bytes - - intApiId = this.read("API ID"); - - this.apiId = ApiId.get(intApiId); - - if (apiId == null) { - this.apiId = ApiId.UNKNOWN; - } - - log.info("Handling ApiId: " + apiId); - - // TODO parse I/O data page 12. 82 API Identifier Byte for 64 bit address A/D data (83 is for 16bit A/D data) - - for (Integer handlerApiId : handlerMap.keySet()) { - if (intApiId == handlerApiId) { - log.debug("Found response handler for apiId [" + ByteUtils.toBase16(intApiId) + "]: " + handlerMap.get(handlerApiId).getCanonicalName()); - response = (XBeeResponse) handlerMap.get(handlerApiId).newInstance(); - response.parse(this); - break; - } - } - - if (response == null) { - log.info("Did not find a response handler for ApiId [" + ByteUtils.toBase16(intApiId) + "]. Returning GenericResponse"); - response = new GenericResponse(); - response.parse(this); - } - - response.setChecksum(this.read("Checksum")); - - if (!this.isDone()) { - throw new XBeeParseException("There are remaining bytes according to stated packet length but we have read all the bytes we thought were required for this packet (if that makes sense)"); - } - - response.finish(); - } catch (Exception e) { - // added bytes read for troubleshooting - log.error("Failed due to exception. Returning ErrorResponse. bytes read: " + ByteUtils.toBase16(rawBytes.getIntArray()), e); - exception = e; - - response = new ErrorResponse(); - - ((ErrorResponse)response).setErrorMsg(exception.getMessage()); - // but this isn't - ((ErrorResponse)response).setException(e); - } - - if (response != null) { - response.setLength(length); - response.setApiId(apiId); - // preserve original byte array for transfer over networks - response.setRawPacketBytes(rawBytes.getIntArray()); - } - - return response; - } - - /** - * Same as read() but logs the context of the byte being read. useful for debugging - */ - public int read(String context) throws IOException { - int b = this.read(); - log.debug("Read " + context + " byte, val is " + ByteUtils.formatByte(b)); - return b; - } - - /** - * This method should only be called by read() - * - * @throws IOException - */ - private int readFromStream() throws IOException { - int b = connection.getByte(); - // save raw bytes to transfer via network - rawBytes.write(b); - - return b; - } - - /** - * This method reads bytes from the underlying input stream and performs the following tasks: - * 1. Keeps track of how many bytes we've read - * 2. Un-escapes bytes if necessary and verifies the checksum. - */ - public int read() throws IOException { - - if (done) { - throw new XBeeParseException("Packet has read all of its bytes"); - } - - int b = this.readFromStream(); - - - if (b == -1) { - throw new XBeeParseException("Read -1 from input stream while reading packet!"); - } - - if (XBeePacket.isSpecialByte(b)) { - log.debug("Read special byte that needs to be unescaped"); - - if (b == XBeePacket.SpecialByte.ESCAPE.getValue()) { - log.debug("found escape byte"); - // read next byte - b = this.readFromStream(); - - log.debug("next byte is " + ByteUtils.formatByte(b)); - b = 0x20 ^ b; - log.debug("unescaped (xor) byte is " + ByteUtils.formatByte(b)); - - escapeBytes++; - } else { - // TODO some responses such as AT Response for node discover do not escape the bytes?? shouldn't occur if AP mode is 2? - // while reading remote at response Found unescaped special byte base10=19,base16=0x13,base2=00010011 at position 5 - log.warn("Found unescaped special byte " + ByteUtils.formatByte(b) + " at position " + bytesRead); - } - } - - bytesRead++; - - // do this only after reading length bytes - if (bytesRead > 2) { - - // when verifying checksum you must add the checksum that we are verifying - // checksum should only include unescaped bytes!!!! - // when computing checksum, do not include start byte, length, or checksum; when verifying, include checksum - checksum.addByte(b); - - log.debug("Read byte " + ByteUtils.formatByte(b) + " at position " + bytesRead + ", packet length is " + this.length.get16BitValue() + ", #escapeBytes is " + escapeBytes + ", remaining bytes is " + this.getRemainingBytes()); - - // escape bytes are not included in the stated packet length - if (this.getFrameDataBytesRead() >= (length.get16BitValue() + 1)) { - // this is checksum and final byte of packet - done = true; - - log.debug("Checksum byte is " + b); - - if (!checksum.verify()) { - throw new XBeeParseException("Checksum is incorrect. Expected 0xff, but got " + checksum.getChecksum()); - } - } - } - - return b; - } - - /** - * Reads all remaining bytes except for checksum - * @return - * @throws IOException - */ - public int[] readRemainingBytes() throws IOException { - - // minus one since we don't read the checksum - int[] value = new int[this.getRemainingBytes() - 1]; - - log.debug("There are " + value.length + " remaining bytes"); - - for (int i = 0; i < value.length; i++) { - value[i] = this.read("Remaining bytes " + i); - } - - return value; - } - - public XBeeAddress64 parseAddress64() throws IOException { - XBeeAddress64 addr = new XBeeAddress64(); - - for (int i = 0; i < 8; i++) { - addr.getAddress()[i] = this.read("64-bit Address byte " + i); - } - - return addr; - } - - public XBeeAddress16 parseAddress16() throws IOException { - XBeeAddress16 addr16 = new XBeeAddress16(); - - addr16.setMsb(this.read("Address 16 MSB")); - addr16.setLsb(this.read("Address 16 LSB")); - - return addr16; - } - - /** - * Returns number of bytes remaining, relative to the stated packet length (not including checksum). - * @return - */ - public int getFrameDataBytesRead() { - // subtract out the 2 length bytes - return this.getBytesRead() - 2; - } - - /** - * Number of bytes remaining to be read, including the checksum - * @return - */ - public int getRemainingBytes() { - // add one for checksum byte (not included) in packet length - return this.length.get16BitValue() - this.getFrameDataBytesRead() + 1; - } - - // get unescaped packet length - // get escaped packet length - - /** - * Does not include any escape bytes - * @return - */ - public int getBytesRead() { - return bytesRead; - } - - public void setBytesRead(int bytesRead) { - this.bytesRead = bytesRead; - } - - public boolean isDone() { - return done; - } - - public void setDone(boolean done) { - this.done = done; - } - - public int getChecksum() { - return checksum.getChecksum(); - } - - public XBeePacketLength getLength() { - return length; - } - - public ApiId getApiId() { - return apiId; - } - - public int getIntApiId() { - return this.intApiId; - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.Logger; + +import com.rapplogic.xbee.api.responses.AtCommandResponse; +import com.rapplogic.xbee.api.responses.ErrorResponse; +import com.rapplogic.xbee.api.responses.GenericResponse; +import com.rapplogic.xbee.api.responses.ModemStatusResponse; +import com.rapplogic.xbee.api.responses.RemoteAtResponse; +import com.rapplogic.xbee.api.wpan.RxResponse16; +import com.rapplogic.xbee.api.wpan.RxResponse64; +import com.rapplogic.xbee.api.wpan.RxResponseIoSample; +import com.rapplogic.xbee.api.wpan.TxStatusResponse; +import com.rapplogic.xbee.api.zigbee.ZNetExplicitRxResponse; +import com.rapplogic.xbee.api.zigbee.ZNetNodeIdentificationResponse; +import com.rapplogic.xbee.api.zigbee.ZNetRxIoSampleResponse; +import com.rapplogic.xbee.api.zigbee.ZNetRxResponse; +import com.rapplogic.xbee.api.zigbee.ZNetTxStatusResponse; +import com.rapplogic.xbee.connections.XBeeConnection; +import com.rapplogic.xbee.util.ByteUtils; +import com.rapplogic.xbee.util.IIntInputStream; +import com.rapplogic.xbee.util.IntArrayOutputStream; + +/** + * Reads a packet from the input stream, verifies checksum and creates an XBeeResponse object + *

+ * Notes: + *

+ * Escaped bytes increase packet length but packet stated length only indicates un-escaped bytes. + * Stated length includes all bytes after Length bytes, not including the checksum + *

+ * @author Andrew Rapp + * + */ +public class PacketParser implements IIntInputStream, IPacketParser { + + private final static Logger log = Logger.getLogger(PacketParser.class); + + // size of packet after special bytes have been escaped + private XBeePacketLength length; + private Checksum checksum = new Checksum(); + + private boolean done = false; + + private int bytesRead; + private int escapeBytes; + + private XBeeResponse response; + private ApiId apiId; + private int intApiId; + + private final XBeeConnection connection; + + private static Map> handlerMap = new HashMap>(); + + // TODO reuse this object for all packets + + // experiment to preserve original byte array for transfer over network (Starts with length) + private IntArrayOutputStream rawBytes = new IntArrayOutputStream(); + + static { + // TODO put all response handlers in specific packet and load all. implement static handlesApi method + handlerMap.put(ApiId.AT_RESPONSE.getValue(), AtCommandResponse.class); + handlerMap.put(ApiId.MODEM_STATUS_RESPONSE.getValue(), ModemStatusResponse.class); + handlerMap.put(ApiId.REMOTE_AT_RESPONSE.getValue(), RemoteAtResponse.class); + handlerMap.put(ApiId.RX_16_IO_RESPONSE.getValue(), RxResponseIoSample.class); + handlerMap.put(ApiId.RX_64_IO_RESPONSE.getValue(), RxResponseIoSample.class); + handlerMap.put(ApiId.RX_16_RESPONSE.getValue(), RxResponse16.class); + handlerMap.put(ApiId.RX_64_RESPONSE.getValue(), RxResponse64.class); + handlerMap.put(ApiId.TX_STATUS_RESPONSE.getValue(), TxStatusResponse.class); + handlerMap.put(ApiId.ZNET_EXPLICIT_RX_RESPONSE.getValue(), ZNetExplicitRxResponse.class); + handlerMap.put(ApiId.ZNET_IO_NODE_IDENTIFIER_RESPONSE.getValue(), ZNetNodeIdentificationResponse.class); + handlerMap.put(ApiId.ZNET_IO_SAMPLE_RESPONSE.getValue(), ZNetRxIoSampleResponse.class); + handlerMap.put(ApiId.ZNET_RX_RESPONSE.getValue(), ZNetRxResponse.class); + handlerMap.put(ApiId.ZNET_TX_STATUS_RESPONSE.getValue(), ZNetTxStatusResponse.class); + } + + static void registerResponseHandler(int apiId, Class clazz) { + if (handlerMap.get(apiId) == null) { + log.info("Registering response handler " + clazz.getCanonicalName() + " for apiId: " + apiId); + } else { + log.warn("Overriding existing implementation: " + handlerMap.get(apiId).getCanonicalName() + ", with " + clazz.getCanonicalName() + " for apiId: " + apiId); + } + + handlerMap.put(apiId, clazz); + } + + static void unRegisterResponseHandler(int apiId) { + if (handlerMap.get(apiId) != null) { + log.info("Unregistering response handler " + handlerMap.get(apiId).getCanonicalName() + " for apiId: " + apiId); + handlerMap.remove(apiId); + } else { + throw new IllegalArgumentException("No response handler for: " + apiId); + } + } + + public PacketParser(XBeeConnection connection) { + this.connection = connection; + } + + /** + * This method is guaranteed (unless I screwed up) to return an instance of XBeeResponse and should never throw an exception + * If an exception occurs, it will be packaged and returned as an ErrorResponse. + * + * @return + */ + public XBeeResponse parsePacket() { + + Exception exception = null; + + try { + // BTW, length doesn't account for escaped bytes + int msbLength = this.read("Length MSB"); + int lsbLength = this.read("Length LSB"); + + // length of api structure, starting here (not including start byte or length bytes, or checksum) + this.length = new XBeePacketLength(msbLength, lsbLength); + + log.debug("packet length is " + String.format("[0x%03X]", length.getLength())); + + // total packet length = stated length + 1 start byte + 1 checksum byte + 2 length bytes + + intApiId = this.read("API ID"); + + this.apiId = ApiId.get(intApiId); + + if (apiId == null) { + this.apiId = ApiId.UNKNOWN; + } + + log.info("Handling ApiId: " + apiId); + + // TODO parse I/O data page 12. 82 API Identifier Byte for 64 bit address A/D data (83 is for 16bit A/D data) + + for (Integer handlerApiId : handlerMap.keySet()) { + if (intApiId == handlerApiId) { + log.debug("Found response handler for apiId [" + ByteUtils.toBase16(intApiId) + "]: " + handlerMap.get(handlerApiId).getCanonicalName()); + response = (XBeeResponse) handlerMap.get(handlerApiId).newInstance(); + response.parse(this); + break; + } + } + + if (response == null) { + log.info("Did not find a response handler for ApiId [" + ByteUtils.toBase16(intApiId) + "]. Returning GenericResponse"); + response = new GenericResponse(); + response.parse(this); + } + + response.setChecksum(this.read("Checksum")); + + if (!this.isDone()) { + throw new XBeeParseException("There are remaining bytes according to stated packet length but we have read all the bytes we thought were required for this packet (if that makes sense)"); + } + + response.finish(); + } catch (Exception e) { + // added bytes read for troubleshooting + log.error("Failed due to exception. Returning ErrorResponse. bytes read: " + ByteUtils.toBase16(rawBytes.getIntArray()), e); + exception = e; + + response = new ErrorResponse(); + + ((ErrorResponse)response).setErrorMsg(exception.getMessage()); + // but this isn't + ((ErrorResponse)response).setException(e); + } + + if (response != null) { + response.setLength(length); + response.setApiId(apiId); + // preserve original byte array for transfer over networks + response.setRawPacketBytes(rawBytes.getIntArray()); + } + + return response; + } + + /** + * Same as read() but logs the context of the byte being read. useful for debugging + */ + public int read(String context) throws IOException { + int b = this.read(); + log.debug("Read " + context + " byte, val is " + ByteUtils.formatByte(b)); + return b; + } + + /** + * This method should only be called by read() + * + * @throws IOException + */ + private int readFromStream() throws IOException { + int b = connection.getByte(); + // save raw bytes to transfer via network + rawBytes.write(b); + + return b; + } + + /** + * This method reads bytes from the underlying input stream and performs the following tasks: + * 1. Keeps track of how many bytes we've read + * 2. Un-escapes bytes if necessary and verifies the checksum. + */ + public int read() throws IOException { + + if (done) { + throw new XBeeParseException("Packet has read all of its bytes"); + } + + int b = this.readFromStream(); + + + if (b == -1) { + throw new XBeeParseException("Read -1 from input stream while reading packet!"); + } + + if (XBeePacket.isSpecialByte(b)) { + log.debug("Read special byte that needs to be unescaped"); + + if (b == XBeePacket.SpecialByte.ESCAPE.getValue()) { + log.debug("found escape byte"); + // read next byte + b = this.readFromStream(); + + log.debug("next byte is " + ByteUtils.formatByte(b)); + b = 0x20 ^ b; + log.debug("unescaped (xor) byte is " + ByteUtils.formatByte(b)); + + escapeBytes++; + } else { + // TODO some responses such as AT Response for node discover do not escape the bytes?? shouldn't occur if AP mode is 2? + // while reading remote at response Found unescaped special byte base10=19,base16=0x13,base2=00010011 at position 5 + log.warn("Found unescaped special byte " + ByteUtils.formatByte(b) + " at position " + bytesRead); + } + } + + bytesRead++; + + // do this only after reading length bytes + if (bytesRead > 2) { + + // when verifying checksum you must add the checksum that we are verifying + // checksum should only include unescaped bytes!!!! + // when computing checksum, do not include start byte, length, or checksum; when verifying, include checksum + checksum.addByte(b); + + log.debug("Read byte " + ByteUtils.formatByte(b) + " at position " + bytesRead + ", packet length is " + this.length.get16BitValue() + ", #escapeBytes is " + escapeBytes + ", remaining bytes is " + this.getRemainingBytes()); + + // escape bytes are not included in the stated packet length + if (this.getFrameDataBytesRead() >= (length.get16BitValue() + 1)) { + // this is checksum and final byte of packet + done = true; + + log.debug("Checksum byte is " + b); + + if (!checksum.verify()) { + throw new XBeeParseException("Checksum is incorrect. Expected 0xff, but got " + checksum.getChecksum()); + } + } + } + + return b; + } + + /** + * Reads all remaining bytes except for checksum + * @return + * @throws IOException + */ + public int[] readRemainingBytes() throws IOException { + + // minus one since we don't read the checksum + int[] value = new int[this.getRemainingBytes() - 1]; + + log.debug("There are " + value.length + " remaining bytes"); + + for (int i = 0; i < value.length; i++) { + value[i] = this.read("Remaining bytes " + i); + } + + return value; + } + + public XBeeAddress64 parseAddress64() throws IOException { + XBeeAddress64 addr = new XBeeAddress64(); + + for (int i = 0; i < 8; i++) { + addr.getAddress()[i] = this.read("64-bit Address byte " + i); + } + + return addr; + } + + public XBeeAddress16 parseAddress16() throws IOException { + XBeeAddress16 addr16 = new XBeeAddress16(); + + addr16.setMsb(this.read("Address 16 MSB")); + addr16.setLsb(this.read("Address 16 LSB")); + + return addr16; + } + + /** + * Returns number of bytes remaining, relative to the stated packet length (not including checksum). + * @return + */ + public int getFrameDataBytesRead() { + // subtract out the 2 length bytes + return this.getBytesRead() - 2; + } + + /** + * Number of bytes remaining to be read, including the checksum + * @return + */ + public int getRemainingBytes() { + // add one for checksum byte (not included) in packet length + return this.length.get16BitValue() - this.getFrameDataBytesRead() + 1; + } + + // get unescaped packet length + // get escaped packet length + + /** + * Does not include any escape bytes + * @return + */ + public int getBytesRead() { + return bytesRead; + } + + public void setBytesRead(int bytesRead) { + this.bytesRead = bytesRead; + } + + public boolean isDone() { + return done; + } + + public void setDone(boolean done) { + this.done = done; + } + + public int getChecksum() { + return checksum.getChecksum(); + } + + public XBeePacketLength getLength() { + return length; + } + + public ApiId getApiId() { + return apiId; + } + + public int getIntApiId() { + return this.intApiId; + } +} diff --git a/src/com/rapplogic/xbee/api/ResponseFilter.java b/src/main/java/com/rapplogic/xbee/api/ResponseFilter.java similarity index 100% rename from src/com/rapplogic/xbee/api/ResponseFilter.java rename to src/main/java/com/rapplogic/xbee/api/ResponseFilter.java diff --git a/src/com/rapplogic/xbee/api/XBee.java b/src/main/java/com/rapplogic/xbee/api/XBee.java similarity index 89% rename from src/com/rapplogic/xbee/api/XBee.java rename to src/main/java/com/rapplogic/xbee/api/XBee.java index 7501814..c7e6350 100644 --- a/src/com/rapplogic/xbee/api/XBee.java +++ b/src/main/java/com/rapplogic/xbee/api/XBee.java @@ -1,655 +1,630 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import jssc.SerialPortException; - -import org.apache.log4j.Logger; - -import com.rapplogic.xbee.SerialPortConnection; -import com.rapplogic.xbee.XBeeConnection; -import com.rapplogic.xbee.api.HardwareVersion.RadioType; -import com.rapplogic.xbee.util.ByteUtils; - -/** - * This is an API for communicating with Digi XBee 802.15.4 and ZigBee radios - * via the serial port - *

- * @author Andrew Rapp - * - */ -public class XBee implements IXBee { - - private final static Logger log = Logger.getLogger(XBee.class); - - // object to synchronize on to protect access to sendPacket - private Object sendPacketBlock = new Object(); - private XBeeConnection xbeeConnection; - private InputStreamThread parser; - private XBeeConfiguration conf; - private XBeeInformation info; - private RadioType type; - - public XBee() { - this(new XBeeConfiguration().withMaxQueueSize(100).withStartupChecks(true)); - } - - public XBee(XBeeConfiguration conf) { - this.conf = conf; - - if (this.conf.isShutdownHook()) { - Runtime.getRuntime().addShutdownHook(new Thread() { - public void run() { - if (isConnected()) { - log.info("ShutdownHook is closing connection"); - close(); - } - } - }); - } - info = new XBeeInformation(); - } - - private void doStartupChecks() throws XBeeException { - // Perform startup checks - try { - AtCommandResponse ap = this.sendAtCommand(new AtCommand("AP")); - - if (!ap.isOk()) { - throw new XBeeException("Attempt to query AP parameter failed"); - } - - if (ap.getValue()[0] != 2) { - log.warn("XBee radio is in API mode without escape characters (AP=1). The radio must be configured in API mode with escape bytes (AP=2) for use with this library."); - - log.info("Attempting to set AP to 2"); - ap = this.sendAtCommand(new AtCommand("AP", 2)); - - if (ap.isOk()) { - log.info("Successfully set AP mode to 2. This setting will not persist a power cycle without the WR (write) command"); - } else { - throw new XBeeException("Attempt to set AP=2 failed"); - } - } else { - log.info("Radio is in correct AP mode (AP=2)"); - } - - ap = this.sendAtCommand(new AtCommand("HV")); - - RadioType radioType = HardwareVersion.parse(ap); - - log.info("XBee radio is " + radioType); - - if (radioType == RadioType.UNKNOWN) { - log.warn("Unknown radio type (HV): " + ap.getValue()[0]); - } - - info.setHardwareVersion(radioType); - - AtCommandResponse vr = this.sendAtCommand(new AtCommand("VR")); - - if (vr.isOk()) { - log.info("Firmware version is " + ByteUtils.toBase16(vr.getValue())); - } - - info.setFirmwareVersion(vr.getValue()); - - this.clearResponseQueue(); - } catch (XBeeTimeoutException ex) { - throw new XBeeException("AT command timed-out while attempt to set/read in API mode. Check that the XBee radio is in API mode (AP=2); it will not function propertly in AP=1"); - } - } - - private void doStartupGetInformation() throws XBeeException { - try { - AtCommandResponse id = this.sendAtCommand(new AtCommand("ID")); - info.setPanID(id.getValue()); - - AtCommandResponse sh = this.sendAtCommand(new AtCommand("SH")); - info.setSerialHigh(sh.getValue()); - - AtCommandResponse sl = this.sendAtCommand(new AtCommand("SL")); - info.setSerialLow(sl.getValue()); - - } catch (XBeeTimeoutException ex) { - throw new XBeeException("AT command timed-out while attempt to set/read in API mode. Check that the XBee radio is in API mode (AP=2); it will not function propertly in AP=1"); - } - - } - - /** - * If XBeeConnection.startUpChecks is set to true (default), this method will check if the AP parameter - * is set correctly and attempt to update if AP=1. If AP=0 (Transparent mode), an - * exception will be thrown. - */ - public void open(String port, int baudRate) throws XBeeException { - try { - if (this.isConnected()) { - throw new IllegalStateException("Cannot open new connection -- existing connection is still open. Please close first"); - } - - this.type = null; - - SerialPortConnection serial = new SerialPortConnection(); - serial.openSerialPort(port, baudRate); - - this.initConnection(serial); - } catch (XBeeException e) { - throw e; - } catch (Exception e) { - throw new XBeeException(e); - } - } - - public static void registerResponseHandler(int apiId, Class clazz) { - PacketParser.registerResponseHandler(apiId, clazz); - } - - public static void unRegisterResponseHandler(int apiId) { - PacketParser.unRegisterResponseHandler(apiId); - } - - /** - * Allows a protocol specific implementation of XBeeConnection to be used instead of the default RXTX connection. - * The connection must already be established as the interface has no means to do so. - */ - public void initProviderConnection(XBeeConnection connection) throws XBeeException { - if (this.isConnected()) { - throw new IllegalStateException("Cannot open new connection -- existing connection is still open. Please close first"); - } - - initConnection(connection); - } - - private void initConnection(XBeeConnection conn) throws XBeeException { - try { - this.xbeeConnection = conn; - - parser = new InputStreamThread(this.xbeeConnection, conf, info); - - // startup heuristics - if (conf.isStartupChecks()) { - this.doStartupChecks(); - } - if (conf.isStartupGetInformation()) { - this.doStartupGetInformation(); - } - } catch (XBeeException e) { - throw e; - } catch (Exception e) { - throw new XBeeException(e); - } - } - - public void addPacketListener(PacketListener packetListener) { - if (parser == null) { - throw new IllegalStateException("No connection"); - } - - synchronized (parser.getPacketListenerList()) { - this.parser.getPacketListenerList().add(packetListener); - } - } - - public void removePacketListener(PacketListener packetListener) { - if (parser == null) { - throw new IllegalStateException("No connection"); - } - - synchronized (parser.getPacketListenerList()) { - this.parser.getPacketListenerList().remove(packetListener); - } - } - - public void sendRequest(XBeeRequest request) throws IOException { - if (this.type != null) { - // TODO use interface to mark series type - if (type == RadioType.SERIES1 && request.getClass().getPackage().getName().indexOf("api.zigbee") > -1) { - throw new IllegalArgumentException("You are connected to a Series 1 radio but attempting to send Series 2 requests"); - } else if (type == RadioType.SERIES2 && request.getClass().getPackage().getName().indexOf("api.wpan") > -1) { - throw new IllegalArgumentException("You are connected to a Series 2 radio but attempting to send Series 1 requests"); - } - } - - log.info("Sending request to XBee: " + request); - this.sendPacket(request.getXBeePacket()); - } - - /** - * It's possible for packets to get interspersed if multiple threads send simultaneously. - * This method is not thread-safe because doing so would introduce a synchronized performance penalty - * for the vast majority of users that will not never need thread safety. - * That said, it is responsibility of the user to provide synchronization if multiple threads are sending. - * - * Not thread safe. - * - * @param packet - * @throws IOException - */ - public void sendPacket(XBeePacket packet) throws IOException { - this.sendPacket(packet.getByteArray()); - } - - /** - * This exists solely for the XMPP project. Use sendRequest instead - * - * Not Thread Safe - * - * @param packet - * @throws RuntimeException when serial device is disconnected - */ - public void sendPacket(int[] packet) throws IOException { - // TODO should we synchronize on read lock so we are sending/recv. simultaneously? - // TODO call request listener with byte array - - if (!this.isConnected()) { - throw new XBeeNotConnectedException(); - } - - if (log.isInfoEnabled()) { - log.info("Sending packet to XBee " + ByteUtils.toBase16(packet)); - } - - xbeeConnection.writeIntArray(packet); - - /* - for (int packetByte : packet) { - // if connection lost - //Caused by: com.rapplogic.xbee.api.XBeeException - //Caused by: java.io.IOException: Input/output error in writeArray - xbeeConnection.getOutputStream().write(packetByte); - } - xbeeConnection.getOutputStream().flush(); - */ - } - - /** - * Sends an XBeeRequest though the XBee interface in an asynchronous manner, such that - * it will return immediately, without waiting for a response. - * Refer to the getResponse method for obtaining a response - * - * Not thread safe - * - * @param request - * @throws XBeeException - */ - public void sendAsynchronous(XBeeRequest request) throws XBeeException { - - try { - this.sendRequest(request); - } catch (Exception e) { - throw new XBeeException(e); - } - } - - /** - * Uses sendSynchronous to send an AtCommand and collect the response - *

- * Timeout value is fixed at 5 seconds - * - * @deprecated Use this.sendSynchronous(command, timeout); - * @param command - * @return - * @throws XBeeException - */ - public AtCommandResponse sendAtCommand(AtCommand command) throws XBeeException { - return (AtCommandResponse) this.sendSynchronous(command, 5000); - } - - /** - * Synchronous method for sending an XBeeRequest and obtaining the - * corresponding response (response that has same frame id). - *

- * This method returns the first response object with a matching frame id, within the timeout - * period, so it is important to use a unique frame id (relative to previous subsequent requests). - *

- * This method must only be called with requests that receive a response of - * type XBeeFrameIdResponse. All other request types will timeout. - *

- * Keep in mind responses received here will also be available through the getResponse method - * and the packet listener. If you would prefer to not have these responses added to the response queue, - * you can add a ResponseQueueFilter via XBeeConfiguration to ignore packets that are sent in response to - * a request. Another alternative is to call clearResponseQueue prior to calling this method. - *

- * It is recommended to use a timeout of at least 5 seconds, since some responses can take a few seconds or more - * (e.g. if remote radio is not powered on). - *

- * This method is thread-safe - * - * @param xbeeRequest - * - * @return - * @throws XBeeException - * @throws XBeeTimeoutException thrown if no matching response is identified - */ - public XBeeResponse sendSynchronous(final XBeeRequest xbeeRequest, int timeout) throws XBeeTimeoutException, XBeeException { - if (xbeeRequest.getFrameId() == XBeeRequest.NO_RESPONSE_FRAME_ID) { - throw new XBeeException("Frame Id cannot be 0 for a synchronous call -- it will always timeout as there is no response!"); - } - - PacketListener pl = null; - - try { - final List container = new LinkedList(); - - // this makes it thread safe -- prevents multiple threads from writing to output stream simultaneously - synchronized (sendPacketBlock) { - this.sendRequest(xbeeRequest); - } - - pl = new PacketListener() { - // TODO handle error response as well - public void processResponse(XBeeResponse response) { - if (response instanceof XBeeFrameIdResponse && ((XBeeFrameIdResponse)response).getFrameId() == xbeeRequest.getFrameId()) { - // frame id matches -- yay we found it - container.add(response); - - synchronized(container) { - container.notify(); - } - } - } - }; - - this.addPacketListener(pl); - - synchronized (container) { - try { - container.wait(timeout); - } catch (InterruptedException e) { } - } - - if (container.size() == 0) { - // we didn't find a matching packet - throw new XBeeTimeoutException(); - } - - return (XBeeResponse) container.get(0); - } catch (IOException io) { - throw new XBeeException(io); - } finally { - if (pl != null) { - this.removePacketListener(pl); - } - } - } - - /** - * Uses sendSynchronous timeout defined in XBeeConfiguration (default is 5000ms) - */ - public XBeeResponse sendSynchronous(final XBeeRequest request) throws XBeeTimeoutException, XBeeException { - return this.sendSynchronous(request, conf.getSendSynchronousTimeout()); - } - - - /** - * Same as getResponse(int) but does not timeout. - * It's highly recommend that you always use a timeout because - * if the serial connection dies under certain conditions, you will end up waiting forever! - *

- * Consider using the PacketListener for asynchronous (non-blocking) behavior - * - * @return - * @throws XBeeException - */ - public XBeeResponse getResponse() throws XBeeException { - return getResponseTimeout(null); - } - - /** - * This method returns an XBeeResponse from the queue, if available, or - * waits up to "timeout" milliseconds for a response. - *

- * There are three possible outcomes: - *

- * 1. A packet is returned within "timeout" milliseconds
- * 2. An XBeeTimeoutException is thrown (i.e. queue was empty for duration of timeout)
- * 3. Null is returned if timeout is 0 and queue is empty.
- *

- * @param timeout milliseconds to wait for a response. A value of zero disables the timeout - * @return - * @throws XBeeException - * @throws XBeeTimeoutException if timeout occurs before a response is received - */ - public XBeeResponse getResponse(int timeout) throws XBeeException, XBeeTimeoutException { - return this.getResponseTimeout(timeout); - } - - private XBeeResponse getResponseTimeout(Integer timeout) throws XBeeException, XBeeTimeoutException { - - // seeing this with xmpp - if (!this.isConnected()) { - throw new XBeeNotConnectedException(); - } - - XBeeResponse response; - try { - if (timeout != null) { - response = parser.getResponseQueue().poll(timeout, TimeUnit.MILLISECONDS); - } else { - response = parser.getResponseQueue().take(); - } - } catch (InterruptedException e) { - throw new XBeeException("Error while attempting to remove packet from queue", e); - } - - if (response == null && timeout > 0) { - throw new XBeeTimeoutException(); - } - - return response; - } - -// public List collectResponses(int wait, ResponseFilter filter, CollectTerminator terminator) throws XBeeException { -// -// } - - /** - * Collects responses until the timeout is reached or the CollectTerminator returns true - * - * @param wait - * @param terminator - * @return - * @throws XBeeException - */ - public List collectResponses(int wait, CollectTerminator terminator) throws XBeeException { - - // seeing this with xmpp - if (!this.isConnected()) { - throw new XBeeNotConnectedException(); - } - - long start = System.currentTimeMillis(); - long callStart = 0; - int waitTime; - - List responseList = new ArrayList(); - XBeeResponse response = null; - - try { - while (true) { - // compute the remaining wait time - waitTime = wait - (int)(System.currentTimeMillis() - start); - - if (waitTime <= 0) { - break; - } - - log.debug("calling getResponse with waitTime: " + waitTime); - - if (log.isDebugEnabled()) { - callStart = System.currentTimeMillis(); - } - - response = this.getResponse(waitTime); - - if (log.isDebugEnabled()) { - log.debug("Got response in " + (System.currentTimeMillis() - callStart)); - } - - responseList.add(response); - - if (terminator != null && terminator.stop(response)) { - log.debug("Found terminating response.. exiting"); - break; - } - } - } catch (XBeeTimeoutException e) { - // ok, we'll just return whatever is in the list - } catch (XBeeException e) { - throw e; - } - - log.debug("Time is up.. returning list with " + responseList.size() + " packets"); - - return responseList; - } - - /** - * Collects responses for wait milliseconds and returns responses as List - * - * @param wait - * @return - * @throws XBeeException - */ - public List collectResponses(int wait) throws XBeeException { - return this.collectResponses(wait, null); - } - - /** - * Returns the number of packets available in the response queue for immediate consumption - * - * @return - */ - public int getResponseQueueSize() { - // seeing this with xmpp - if (!this.isConnected()) { - throw new XBeeNotConnectedException(); - } - - return parser.getResponseQueue().size(); - } - - /** - * Shuts down RXTX and packet parser thread - */ - public void close() { - - if (!this.isConnected()) { - throw new IllegalStateException("XBee is not connected"); - } - - // shutdown parser thread - if (parser != null) { - parser.setDone(true); - // interrupts thread, if waiting. does not interrupt thread if blocking on read - // serial port close will be closed prior to thread exit - parser.interrupt(); - } - - try { - xbeeConnection.close(); - } catch (IOException e) { - log.warn("Failed to close connection", e); - } - - this.type = null; - parser = null; - xbeeConnection = null; - } - - /** - * Indicates if serial port connection has been established. - * The open method may be called if this returns true - * - * @return - */ - public boolean isConnected() { - try { - return xbeeConnection.isConnected(); - } catch (Exception e) { - return false; - } - } - - // TODO move to its own class - private int sequentialFrameId = 0xff; - - public int getCurrentFrameId() { - // TODO move to separate class (e.g. FrameIdCounter) - return sequentialFrameId; - } - - /** - * This is useful for obtaining a frame id when composing your XBeeRequest. - * It will return frame ids in a sequential manner until the maximum is reached (0xff) - * and it flips to 1 and starts over. - * - * Not Thread-safe - * - * @return - */ - public int getNextFrameId() { - if (sequentialFrameId == 0xff) { - // flip - sequentialFrameId = 1; - } else { - sequentialFrameId++; - } - - return sequentialFrameId; - } - - /** - * Updates the frame id. Any value between 1 and ff is valid - * - * @param val - * Jan 24, 2009 - */ - public void updateFrameId(int val) { - if (val <=0 || val > 0xff) { - throw new IllegalArgumentException("invalid frame id"); - } - - this.sequentialFrameId = val; - } - - /** - * Removes all packets off of the response queue - */ - public void clearResponseQueue() { - // seeing this with xmpp - if (!this.isConnected()) { - throw new XBeeNotConnectedException(); - } - - parser.getResponseQueue().clear(); - } - - public XBeeInformation getInformation() { - return info; - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.apache.log4j.Logger; + +import com.rapplogic.xbee.api.HardwareVersion.RadioType; +import com.rapplogic.xbee.api.requests.AtCommand; +import com.rapplogic.xbee.api.responses.AtCommandResponse; +import com.rapplogic.xbee.api.responses.XBeeFrameIdResponse; +import com.rapplogic.xbee.connections.SerialPortConnection; +import com.rapplogic.xbee.connections.XBeeConnection; +import com.rapplogic.xbee.connections.connectionprovider.IConnectionProvider; +import com.rapplogic.xbee.connections.connectionprovider.SerialPortConnectionProvider; +import com.rapplogic.xbee.util.ByteUtils; + +/** + * This is an API for communicating with Digi XBee 802.15.4 and ZigBee radios + * via the serial port + *

+ * @author Andrew Rapp + * + */ +public class XBee implements IXBee { + + private final static Logger log = Logger.getLogger(XBee.class); + + // object to synchronize on to protect access to sendPacket + private Object sendPacketBlock = new Object(); + private XBeeConnection xbeeConnection; + private InputStreamThread parser; + private final XBeeConfiguration conf; + private XBeeInformation info; + private RadioType type; + private final IConnectionProvider connectionProvider; + + public XBee() { + this(new XBeeConfiguration().withMaxQueueSize(100).withStartupChecks(true).withConnectionProvider(new SerialPortConnectionProvider())); + } + + public XBee(XBeeConfiguration conf) { + this.conf = conf; + this.connectionProvider = conf.getConnectionProvider(); + + if (this.conf.isShutdownHook()) { + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + if (isConnected()) { + log.info("ShutdownHook is closing connection"); + close(); + } + } + }); + } + } + + private void doStartupChecks() throws XBeeException { + // Perform startup checks + try { + AtCommandResponse ap = this.sendAtCommand(new AtCommand("AP")); + + if (!ap.isOk()) { + throw new XBeeException("Attempt to query AP parameter failed"); + } + + if (ap.getValue()[0] != 2) { + log.warn("XBee radio is in API mode without escape characters (AP=1). The radio must be configured in API mode with escape bytes (AP=2) for use with this library."); + + log.info("Attempting to set AP to 2"); + ap = this.sendAtCommand(new AtCommand("AP", 2)); + + if (ap.isOk()) { + log.info("Successfully set AP mode to 2. This setting will not persist a power cycle without the WR (write) command"); + } else { + throw new XBeeException("Attempt to set AP=2 failed"); + } + } else { + log.info("Radio is in correct AP mode (AP=2)"); + } + + ap = this.sendAtCommand(new AtCommand("HV")); + + RadioType radioType = HardwareVersion.parse(ap); + + log.info("XBee radio is " + radioType); + + if (radioType == RadioType.UNKNOWN) { + log.warn("Unknown radio type (HV): " + ap.getValue()[0]); + } + + AtCommandResponse vr = this.sendAtCommand(new AtCommand("VR")); + + if (vr.isOk()) { + log.info("Firmware version is " + ByteUtils.toBase16(vr.getValue())); + } + + AtCommandResponse id = this.sendAtCommand(new AtCommand("ID")); + + AtCommandResponse sh = this.sendAtCommand(new AtCommand("SH")); + + AtCommandResponse sl = this.sendAtCommand(new AtCommand("SL")); + + info = new XBeeInformation(radioType, vr.getValue(), id.getValue(), sh.getValue(), sl.getValue()); + + this.clearResponseQueue(); + } catch (XBeeTimeoutException ex) { + throw new XBeeException("AT command timed-out while attempt to set/read in API mode. Check that the XBee radio is in API mode (AP=2); it will not function propertly in AP=1"); + } + } + + + /** + * If XBeeConnection.startUpChecks is set to true (default), this method will check if the AP parameter + * is set correctly and attempt to update if AP=1. If AP=0 (Transparent mode), an + * exception will be thrown. + */ + public void open(String port, int baudRate) throws XBeeException { + try { + if (this.isConnected()) { + throw new IllegalStateException("Cannot open new connection -- existing connection is still open. Please close first"); + } + + this.type = null; + + this.initConnection(connectionProvider.open(port, baudRate)); + } catch (XBeeException e) { + throw e; + } catch (Exception e) { + throw new XBeeException(e); + } + } + + public static void registerResponseHandler(int apiId, Class clazz) { + PacketParser.registerResponseHandler(apiId, clazz); + } + + public static void unRegisterResponseHandler(int apiId) { + PacketParser.unRegisterResponseHandler(apiId); + } + + /** + * Allows a protocol specific implementation of XBeeConnection to be used instead of the default RXTX connection. + * The connection must already be established as the interface has no means to do so. + */ + public void initProviderConnection(XBeeConnection connection) throws XBeeException { + if (this.isConnected()) { + throw new IllegalStateException("Cannot open new connection -- existing connection is still open. Please close first"); + } + + initConnection(connection); + } + + private void initConnection(XBeeConnection conn) throws XBeeException { + try { + this.xbeeConnection = conn; + + parser = new InputStreamThread(this.xbeeConnection, conf); + + // startup heuristics + if (conf.isStartupChecks()) { + this.doStartupChecks(); + } + } catch (XBeeException e) { + throw e; + } catch (Exception e) { + throw new XBeeException(e); + } + } + + public void addPacketListener(PacketListener packetListener) { + if (parser == null) { + throw new IllegalStateException("No connection"); + } + + synchronized (parser.getPacketListenerList()) { + this.parser.getPacketListenerList().add(packetListener); + } + } + + public void removePacketListener(PacketListener packetListener) { + if (parser == null) { + throw new IllegalStateException("No connection"); + } + + synchronized (parser.getPacketListenerList()) { + this.parser.getPacketListenerList().remove(packetListener); + } + } + + public void sendRequest(XBeeRequest request) throws IOException { + if (this.type != null) { + // TODO use interface to mark series type + if (type == RadioType.SERIES1 && request.getClass().getPackage().getName().indexOf("api.zigbee") > -1) { + throw new IllegalArgumentException("You are connected to a Series 1 radio but attempting to send Series 2 requests"); + } else if (type == RadioType.SERIES2 && request.getClass().getPackage().getName().indexOf("api.wpan") > -1) { + throw new IllegalArgumentException("You are connected to a Series 2 radio but attempting to send Series 1 requests"); + } + } + + log.info("Sending request to XBee: " + request); + this.sendPacket(request.getXBeePacket()); + } + + /** + * It's possible for packets to get interspersed if multiple threads send simultaneously. + * This method is not thread-safe because doing so would introduce a synchronized performance penalty + * for the vast majority of users that will not never need thread safety. + * That said, it is responsibility of the user to provide synchronization if multiple threads are sending. + * + * Not thread safe. + * + * @param packet + * @throws IOException + */ + public void sendPacket(XBeePacket packet) throws IOException { + this.sendPacket(packet.getByteArray()); + } + + /** + * This exists solely for the XMPP project. Use sendRequest instead + * + * Not Thread Safe + * + * @param packet + * @throws RuntimeException when serial device is disconnected + */ + public void sendPacket(int[] packet) throws IOException { + // TODO should we synchronize on read lock so we are sending/recv. simultaneously? + + if (!this.isConnected()) { + throw new XBeeNotConnectedException(); + } + + if (log.isInfoEnabled()) { + log.info("Sending packet to XBee " + ByteUtils.toBase16(packet)); + } + + xbeeConnection.writeIntArray(packet); + } + + /** + * Sends an XBeeRequest though the XBee interface in an asynchronous manner, such that + * it will return immediately, without waiting for a response. + * Refer to the getResponse method for obtaining a response + * + * Not thread safe + * + * @param request + * @throws XBeeException + */ + public void sendAsynchronous(XBeeRequest request) throws XBeeException { + + try { + this.sendRequest(request); + } catch (Exception e) { + throw new XBeeException(e); + } + } + + /** + * Uses sendSynchronous to send an AtCommand and collect the response + *

+ * Timeout value is fixed at 5 seconds + * + * @deprecated Use this.sendSynchronous(command, timeout); + * @param command + * @return + * @throws XBeeException + */ + public AtCommandResponse sendAtCommand(AtCommand command) throws XBeeException { + return (AtCommandResponse) this.sendSynchronous(command, 5000); + } + + /** + * Synchronous method for sending an XBeeRequest and obtaining the + * corresponding response (response that has same frame id). + *

+ * This method returns the first response object with a matching frame id, within the timeout + * period, so it is important to use a unique frame id (relative to previous subsequent requests). + *

+ * This method must only be called with requests that receive a response of + * type XBeeFrameIdResponse. All other request types will timeout. + *

+ * Keep in mind responses received here will also be available through the getResponse method + * and the packet listener. If you would prefer to not have these responses added to the response queue, + * you can add a ResponseQueueFilter via XBeeConfiguration to ignore packets that are sent in response to + * a request. Another alternative is to call clearResponseQueue prior to calling this method. + *

+ * It is recommended to use a timeout of at least 5 seconds, since some responses can take a few seconds or more + * (e.g. if remote radio is not powered on). + *

+ * This method is thread-safe + * + * @param xbeeRequest + * + * @return + * @throws XBeeException + * @throws XBeeTimeoutException thrown if no matching response is identified + */ + public XBeeResponse sendSynchronous(final XBeeRequest xbeeRequest, int timeout) throws XBeeTimeoutException, XBeeException { + if (xbeeRequest.getFrameId() == XBeeRequest.NO_RESPONSE_FRAME_ID) { + throw new XBeeException("Frame Id cannot be 0 for a synchronous call -- it will always timeout as there is no response!"); + } + + PacketListener pl = null; + + try { + final List container = new LinkedList(); + + // this makes it thread safe -- prevents multiple threads from writing to output stream simultaneously + synchronized (sendPacketBlock) { + this.sendRequest(xbeeRequest); + } + + pl = new PacketListener() { + // TODO handle error response as well + public void processResponse(XBeeResponse response) { + if (response instanceof XBeeFrameIdResponse && ((XBeeFrameIdResponse)response).getFrameId() == xbeeRequest.getFrameId()) { + // frame id matches -- yay we found it + container.add(response); + + synchronized(container) { + container.notify(); + } + } + } + }; + + this.addPacketListener(pl); + + synchronized (container) { + try { + container.wait(timeout); + } catch (InterruptedException e) { } + } + + if (container.size() == 0) { + // we didn't find a matching packet + throw new XBeeTimeoutException(); + } + + return (XBeeResponse) container.get(0); + } catch (IOException io) { + throw new XBeeException(io); + } finally { + if (pl != null) { + this.removePacketListener(pl); + } + } + } + + /** + * Uses sendSynchronous timeout defined in XBeeConfiguration (default is 5000ms) + */ + public XBeeResponse sendSynchronous(final XBeeRequest request) throws XBeeTimeoutException, XBeeException { + return this.sendSynchronous(request, conf.getSendSynchronousTimeout()); + } + + + /** + * Same as getResponse(int) but does not timeout. + * It's highly recommend that you always use a timeout because + * if the serial connection dies under certain conditions, you will end up waiting forever! + *

+ * Consider using the PacketListener for asynchronous (non-blocking) behavior + * + * @return + * @throws XBeeException + */ + public XBeeResponse getResponse() throws XBeeException { + return getResponseTimeout(null); + } + + /** + * This method returns an XBeeResponse from the queue, if available, or + * waits up to "timeout" milliseconds for a response. + *

+ * There are three possible outcomes: + *

+ * 1. A packet is returned within "timeout" milliseconds
+ * 2. An XBeeTimeoutException is thrown (i.e. queue was empty for duration of timeout)
+ * 3. Null is returned if timeout is 0 and queue is empty.
+ *

+ * @param timeout milliseconds to wait for a response. A value of zero disables the timeout + * @return + * @throws XBeeException + * @throws XBeeTimeoutException if timeout occurs before a response is received + */ + public XBeeResponse getResponse(int timeout) throws XBeeException, XBeeTimeoutException { + return this.getResponseTimeout(timeout); + } + + private XBeeResponse getResponseTimeout(Integer timeout) throws XBeeException, XBeeTimeoutException { + + // seeing this with xmpp + if (!this.isConnected()) { + throw new XBeeNotConnectedException(); + } + + XBeeResponse response; + try { + if (timeout != null) { + response = parser.getResponseQueue().poll(timeout, TimeUnit.MILLISECONDS); + } else { + response = parser.getResponseQueue().take(); + } + } catch (InterruptedException e) { + throw new XBeeException("Error while attempting to remove packet from queue", e); + } + + if (response == null && timeout > 0) { + throw new XBeeTimeoutException(); + } + + return response; + } + +// public List collectResponses(int wait, ResponseFilter filter, CollectTerminator terminator) throws XBeeException { +// +// } + + /** + * Collects responses until the timeout is reached or the CollectTerminator returns true + * + * @param wait + * @param terminator + * @return + * @throws XBeeException + */ + public List collectResponses(int wait, CollectTerminator terminator) throws XBeeException { + + // seeing this with xmpp + if (!this.isConnected()) { + throw new XBeeNotConnectedException(); + } + + long start = System.currentTimeMillis(); + long callStart = 0; + int waitTime; + + List responseList = new ArrayList(); + XBeeResponse response = null; + + try { + while (true) { + // compute the remaining wait time + waitTime = wait - (int)(System.currentTimeMillis() - start); + + if (waitTime <= 0) { + break; + } + + log.debug("calling getResponse with waitTime: " + waitTime); + + if (log.isDebugEnabled()) { + callStart = System.currentTimeMillis(); + } + + response = this.getResponse(waitTime); + + if (log.isDebugEnabled()) { + log.debug("Got response in " + (System.currentTimeMillis() - callStart)); + } + + responseList.add(response); + + if (terminator != null && terminator.stop(response)) { + log.debug("Found terminating response.. exiting"); + break; + } + } + } catch (XBeeTimeoutException e) { + // ok, we'll just return whatever is in the list + } catch (XBeeException e) { + throw e; + } + + log.debug("Time is up.. returning list with " + responseList.size() + " packets"); + + return responseList; + } + + /** + * Collects responses for wait milliseconds and returns responses as List + * + * @param wait + * @return + * @throws XBeeException + */ + public List collectResponses(int wait) throws XBeeException { + return this.collectResponses(wait, null); + } + + /** + * Returns the number of packets available in the response queue for immediate consumption + * + * @return + */ + public int getResponseQueueSize() { + // seeing this with xmpp + if (!this.isConnected()) { + throw new XBeeNotConnectedException(); + } + + return parser.getResponseQueue().size(); + } + + /** + * Shuts down RXTX and packet parser thread + */ + public void close() { + + if (!this.isConnected()) { + throw new IllegalStateException("XBee is not connected"); + } + + // shutdown parser thread + if (parser != null) { + parser.setDone(true); + // interrupts thread, if waiting. does not interrupt thread if blocking on read + // serial port close will be closed prior to thread exit + parser.interrupt(); + } + + try { + xbeeConnection.close(); + } catch (IOException e) { + log.warn("Failed to close connection", e); + } + + this.type = null; + parser = null; + xbeeConnection = null; + } + + /** + * Indicates if serial port connection has been established. + * The open method may be called if this returns true + * + * @return + */ + public boolean isConnected() { + try { + return xbeeConnection.isConnected(); + } catch (Exception e) { + return false; + } + } + + // TODO move to its own class + private int sequentialFrameId = 0xff; + + public int getCurrentFrameId() { + // TODO move to separate class (e.g. FrameIdCounter) + return sequentialFrameId; + } + + /** + * This is useful for obtaining a frame id when composing your XBeeRequest. + * It will return frame ids in a sequential manner until the maximum is reached (0xff) + * and it flips to 1 and starts over. + * + * Not Thread-safe + * + * @return + */ + public int getNextFrameId() { + if (sequentialFrameId == 0xff) { + // flip + sequentialFrameId = 1; + } else { + sequentialFrameId++; + } + + return sequentialFrameId; + } + + /** + * Updates the frame id. Any value between 1 and ff is valid + * + * @param val + * Jan 24, 2009 + */ + public void updateFrameId(int val) { + if (val <=0 || val > 0xff) { + throw new IllegalArgumentException("invalid frame id"); + } + + this.sequentialFrameId = val; + } + + /** + * Removes all packets off of the response queue + */ + public void clearResponseQueue() { + // seeing this with xmpp + if (!this.isConnected()) { + throw new XBeeNotConnectedException(); + } + + parser.getResponseQueue().clear(); + } + + public XBeeInformation getInformation() { + return info; + } +} diff --git a/src/com/rapplogic/xbee/api/XBeeAddress.java b/src/main/java/com/rapplogic/xbee/api/XBeeAddress.java similarity index 96% rename from src/com/rapplogic/xbee/api/XBeeAddress.java rename to src/main/java/com/rapplogic/xbee/api/XBeeAddress.java index 975c5a2..95846af 100644 --- a/src/com/rapplogic/xbee/api/XBeeAddress.java +++ b/src/main/java/com/rapplogic/xbee/api/XBeeAddress.java @@ -1,42 +1,42 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -import com.rapplogic.xbee.util.ByteUtils; - -/** - * Represents a XBee Address. - *

- * @author andrew - * - */ -public abstract class XBeeAddress { - - - public XBeeAddress() { - - } - - public abstract int[] getAddress(); - - public String toString() { - return ByteUtils.toBase16(this.getAddress()); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api; + +import com.rapplogic.xbee.util.ByteUtils; + +/** + * Represents a XBee Address. + *

+ * @author andrew + * + */ +public abstract class XBeeAddress { + + + public XBeeAddress() { + + } + + public abstract int[] getAddress(); + + public String toString() { + return ByteUtils.toBase16(this.getAddress()); + } +} diff --git a/src/com/rapplogic/xbee/api/XBeeAddress16.java b/src/main/java/com/rapplogic/xbee/api/XBeeAddress16.java similarity index 96% rename from src/com/rapplogic/xbee/api/XBeeAddress16.java rename to src/main/java/com/rapplogic/xbee/api/XBeeAddress16.java index df09286..c619c9c 100644 --- a/src/com/rapplogic/xbee/api/XBeeAddress16.java +++ b/src/main/java/com/rapplogic/xbee/api/XBeeAddress16.java @@ -1,98 +1,98 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -import com.rapplogic.xbee.util.DoubleByte; - -/** - * Represents a 16-bit XBee Address. - *

- * @author andrew - * - */ -public class XBeeAddress16 extends XBeeAddress { - - public static final XBeeAddress16 BROADCAST = new XBeeAddress16(0xFF, 0xFF); - public static final XBeeAddress16 ZNET_BROADCAST = new XBeeAddress16(0xFF, 0xFE); - - private DoubleByte doubleByte = new DoubleByte(); - - /** - * Provide address as msb byte and lsb byte - * - * @param msb - * @param lsb - */ - public XBeeAddress16(int msb, int lsb) { - this.doubleByte.setMsb(msb); - this.doubleByte.setLsb(lsb); - } - - public XBeeAddress16(int[] arr) { - this.doubleByte.setMsb(arr[0]); - this.doubleByte.setLsb(arr[1]); - } - - public XBeeAddress16() { - - } - - public int get16BitValue() { - return this.doubleByte.get16BitValue(); - } - - public int getMsb() { - return this.doubleByte.getMsb(); - } - - public void setMsb(int msb) { - this.doubleByte.setMsb(msb); - } - - public int getLsb() { - return this.doubleByte.getLsb(); - } - - public void setLsb(int lsb) { - this.doubleByte.setLsb(lsb); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - XBeeAddress16 that = (XBeeAddress16) o; - - if (doubleByte != null ? !doubleByte.equals(that.doubleByte) : that.doubleByte != null) return false; - - return true; - } - - @Override - public int hashCode() { - return doubleByte != null ? doubleByte.hashCode() : 0; - } - - @Override - public int[] getAddress() { - return new int[] { this.doubleByte.getMsb(), this.doubleByte.getLsb() }; - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api; + +import com.rapplogic.xbee.util.DoubleByte; + +/** + * Represents a 16-bit XBee Address. + *

+ * @author andrew + * + */ +public class XBeeAddress16 extends XBeeAddress { + + public static final XBeeAddress16 BROADCAST = new XBeeAddress16(0xFF, 0xFF); + public static final XBeeAddress16 ZNET_BROADCAST = new XBeeAddress16(0xFF, 0xFE); + + private DoubleByte doubleByte = new DoubleByte(); + + /** + * Provide address as msb byte and lsb byte + * + * @param msb + * @param lsb + */ + public XBeeAddress16(int msb, int lsb) { + this.doubleByte.setMsb(msb); + this.doubleByte.setLsb(lsb); + } + + public XBeeAddress16(int[] arr) { + this.doubleByte.setMsb(arr[0]); + this.doubleByte.setLsb(arr[1]); + } + + public XBeeAddress16() { + + } + + public int get16BitValue() { + return this.doubleByte.get16BitValue(); + } + + public int getMsb() { + return this.doubleByte.getMsb(); + } + + public void setMsb(int msb) { + this.doubleByte.setMsb(msb); + } + + public int getLsb() { + return this.doubleByte.getLsb(); + } + + public void setLsb(int lsb) { + this.doubleByte.setLsb(lsb); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + XBeeAddress16 that = (XBeeAddress16) o; + + if (doubleByte != null ? !doubleByte.equals(that.doubleByte) : that.doubleByte != null) return false; + + return true; + } + + @Override + public int hashCode() { + return doubleByte != null ? doubleByte.hashCode() : 0; + } + + @Override + public int[] getAddress() { + return new int[] { this.doubleByte.getMsb(), this.doubleByte.getLsb() }; + } +} diff --git a/src/com/rapplogic/xbee/api/XBeeAddress64.java b/src/main/java/com/rapplogic/xbee/api/XBeeAddress64.java similarity index 96% rename from src/com/rapplogic/xbee/api/XBeeAddress64.java rename to src/main/java/com/rapplogic/xbee/api/XBeeAddress64.java index a222d95..0834533 100644 --- a/src/com/rapplogic/xbee/api/XBeeAddress64.java +++ b/src/main/java/com/rapplogic/xbee/api/XBeeAddress64.java @@ -1,121 +1,121 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -import java.util.StringTokenizer; -import java.util.Arrays; - -/** - * Represents a 64-bit XBee Address - *

- * @author andrew - * - */ -public class XBeeAddress64 extends XBeeAddress { - - public static final XBeeAddress64 BROADCAST = new XBeeAddress64(new int[] {0, 0, 0, 0, 0, 0, 0xff, 0xff}); - public static final XBeeAddress64 ZNET_COORDINATOR = new XBeeAddress64(new int[] {0, 0, 0, 0, 0, 0, 0, 0}); - - private int[] address; - - /** - * Parses an 64-bit XBee address from a string representation - * May be contain spaces ## ## ## ## ## ## ## ## or without ################ but cannot use the 0x prefix - * ex: 0013A200408B98FF or 00 13 A2 00 40 8B 98 FF - * - * @param addressStr - */ - public XBeeAddress64(String addressStr) { - address = new int[8]; - - if (addressStr.contains(" ")) { - StringTokenizer st = new StringTokenizer(addressStr, " "); - - for (int i = 0; i < address.length; i++) { - String byteStr = st.nextToken(); - address[i] = Integer.parseInt(byteStr, 16); - } - } else { - // secretly also handle no space format - for (int i = 0; i < address.length; i++) { - address[i] = Integer.parseInt(addressStr.substring(i*2, i*2+2), 16); - } - } - } - - /** - * Creates a 64-bit address - * - * @param b1 MSB - * @param b2 - * @param b3 - * @param b4 - * @param b5 - * @param b6 - * @param b7 - * @param b8 LSB - */ - public XBeeAddress64(int b1, int b2, int b3, int b4, int b5, int b6, int b7, int b8) { - address = new int[8]; - - address[0] = b1; - address[1] = b2; - address[2] = b3; - address[3] = b4; - address[4] = b5; - address[5] = b6; - address[6] = b7; - address[7] = b8; - } - - public XBeeAddress64(int[] address) { - this.address = address; - } - - public XBeeAddress64() { - address = new int[8]; - } - - public void setAddress(int[] address) { - this.address = address; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - XBeeAddress64 that = (XBeeAddress64) o; - - if (!Arrays.equals(address, that.address)) return false; - - return true; - } - - @Override - public int hashCode() { - return address != null ? Arrays.hashCode(address) : 0; - } - - @Override - public int[] getAddress() { - return address; - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api; + +import java.util.StringTokenizer; +import java.util.Arrays; + +/** + * Represents a 64-bit XBee Address + *

+ * @author andrew + * + */ +public class XBeeAddress64 extends XBeeAddress { + + public static final XBeeAddress64 BROADCAST = new XBeeAddress64(new int[] {0, 0, 0, 0, 0, 0, 0xff, 0xff}); + public static final XBeeAddress64 ZNET_COORDINATOR = new XBeeAddress64(new int[] {0, 0, 0, 0, 0, 0, 0, 0}); + + private int[] address; + + /** + * Parses an 64-bit XBee address from a string representation + * May be contain spaces ## ## ## ## ## ## ## ## or without ################ but cannot use the 0x prefix + * ex: 0013A200408B98FF or 00 13 A2 00 40 8B 98 FF + * + * @param addressStr + */ + public XBeeAddress64(String addressStr) { + address = new int[8]; + + if (addressStr.contains(" ")) { + StringTokenizer st = new StringTokenizer(addressStr, " "); + + for (int i = 0; i < address.length; i++) { + String byteStr = st.nextToken(); + address[i] = Integer.parseInt(byteStr, 16); + } + } else { + // secretly also handle no space format + for (int i = 0; i < address.length; i++) { + address[i] = Integer.parseInt(addressStr.substring(i*2, i*2+2), 16); + } + } + } + + /** + * Creates a 64-bit address + * + * @param b1 MSB + * @param b2 + * @param b3 + * @param b4 + * @param b5 + * @param b6 + * @param b7 + * @param b8 LSB + */ + public XBeeAddress64(int b1, int b2, int b3, int b4, int b5, int b6, int b7, int b8) { + address = new int[8]; + + address[0] = b1; + address[1] = b2; + address[2] = b3; + address[3] = b4; + address[4] = b5; + address[5] = b6; + address[6] = b7; + address[7] = b8; + } + + public XBeeAddress64(int[] address) { + this.address = address; + } + + public XBeeAddress64() { + address = new int[8]; + } + + public void setAddress(int[] address) { + this.address = address; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + XBeeAddress64 that = (XBeeAddress64) o; + + if (!Arrays.equals(address, that.address)) return false; + + return true; + } + + @Override + public int hashCode() { + return address != null ? Arrays.hashCode(address) : 0; + } + + @Override + public int[] getAddress() { + return address; + } +} diff --git a/src/com/rapplogic/xbee/api/XBeeConfiguration.java b/src/main/java/com/rapplogic/xbee/api/XBeeConfiguration.java similarity index 86% rename from src/com/rapplogic/xbee/api/XBeeConfiguration.java rename to src/main/java/com/rapplogic/xbee/api/XBeeConfiguration.java index 6f3f161..8e8b6a7 100644 --- a/src/com/rapplogic/xbee/api/XBeeConfiguration.java +++ b/src/main/java/com/rapplogic/xbee/api/XBeeConfiguration.java @@ -1,13 +1,16 @@ package com.rapplogic.xbee.api; +import com.rapplogic.xbee.api.responses.NoRequestResponse; +import com.rapplogic.xbee.connections.connectionprovider.IConnectionProvider; + public class XBeeConfiguration { private boolean shutdownHook = false; private boolean startupChecks = true; - private boolean getInformation = true; private int maxQueueSize = 100; private int sendSynchronousTimeout = 5000; private ResponseFilter responseQueueFilter; + private IConnectionProvider connectionProvider; private final ResponseFilter noRequestResponseQueueFilter = new ResponseFilter() { public boolean accept(XBeeResponse response) { @@ -42,11 +45,6 @@ public XBeeConfiguration withStartupChecks(boolean startupChecks) { this.startupChecks = startupChecks; return this; } - - public XBeeConfiguration withStartupGetInformation(boolean getInformation) { - this.getInformation = getInformation; - return this; - } /** * Sets the maximum size of the internal queue that supports the getResponse(..) method. @@ -72,6 +70,11 @@ public XBeeConfiguration withSendSynchronousTimeout(int sendSynchronousTimeout) this.sendSynchronousTimeout = sendSynchronousTimeout; return this; } + + public XBeeConfiguration withConnectionProvider(IConnectionProvider connectionProvider) { + this.connectionProvider = connectionProvider; + return this; + } /** * Only adds responses that implement NoRequestResponse @@ -102,8 +105,8 @@ public int getSendSynchronousTimeout() { public boolean isShutdownHook() { return shutdownHook; } - - public boolean isStartupGetInformation() { - return getInformation; + + public IConnectionProvider getConnectionProvider() { + return connectionProvider; } } diff --git a/src/com/rapplogic/xbee/api/XBeeException.java b/src/main/java/com/rapplogic/xbee/api/XBeeException.java similarity index 96% rename from src/com/rapplogic/xbee/api/XBeeException.java rename to src/main/java/com/rapplogic/xbee/api/XBeeException.java index 38b57f6..e119350 100644 --- a/src/com/rapplogic/xbee/api/XBeeException.java +++ b/src/main/java/com/rapplogic/xbee/api/XBeeException.java @@ -1,58 +1,58 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -/** - * I usually detest checked exceptions but given this is a public api, it is reasonable to - * notify users what they can expect. - * - * @author andrew - * - */ -public class XBeeException extends Exception { - - private static final long serialVersionUID = -5501299728920565639L; - private Exception cause; - - public XBeeException(String message) { - super(message); - } - - public XBeeException(String message, Exception e) { - super(message, e); - } - - public XBeeException() { - super(); - } - - public XBeeException(Exception cause) { - super(); - this.setCause(cause); - } - - public Exception getCause() { - return cause; - } - - public void setCause(Exception cause) { - this.cause = cause; - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api; + +/** + * I usually detest checked exceptions but given this is a public api, it is reasonable to + * notify users what they can expect. + * + * @author andrew + * + */ +public class XBeeException extends Exception { + + private static final long serialVersionUID = -5501299728920565639L; + private Exception cause; + + public XBeeException(String message) { + super(message); + } + + public XBeeException(String message, Exception e) { + super(message, e); + } + + public XBeeException() { + super(); + } + + public XBeeException(Exception cause) { + super(); + this.setCause(cause); + } + + public Exception getCause() { + return cause; + } + + public void setCause(Exception cause) { + this.cause = cause; + } +} diff --git a/src/main/java/com/rapplogic/xbee/api/XBeeInformation.java b/src/main/java/com/rapplogic/xbee/api/XBeeInformation.java new file mode 100644 index 0000000..e10f571 --- /dev/null +++ b/src/main/java/com/rapplogic/xbee/api/XBeeInformation.java @@ -0,0 +1,88 @@ +package com.rapplogic.xbee.api; + +import com.rapplogic.xbee.api.HardwareVersion.RadioType; + +public class XBeeInformation { + enum XBeeMode { + COORDINATOR, ROUTER, END_DEVICE, UNKNOWN; + } + private final RadioType hardwareVersion; + private final int firmwareVersion; + private final long panID; + private final int serialHigh; + private final int serialLow; + private final XBeeMode mode; + private long lastReceivedMessage; + + public XBeeInformation(RadioType hardwareVersion, int[] firmwareVersion, int[] panID, int[] serialHigh, int[] seriallow) { + + this.hardwareVersion = hardwareVersion; + + this.firmwareVersion = firmwareVersion[0] << 8 | firmwareVersion[1]; + + this.panID = (long)panID[0] << 56 | (long)panID[1] << 48 | (long)panID[2] << 40 | (long)panID[3] << 32 | + panID[4] << 24 | panID[5] << 16 | panID[6] << 8 | panID[7]; + + this.serialHigh = serialHigh[0] << 24 | serialHigh[1] << 16 | serialHigh[2] << 8 | serialHigh[3]; + + this.serialLow = seriallow[0] << 24 | seriallow[1] << 16 | seriallow[2] << 8 | seriallow[3]; + + this.mode = getModeFromFirmwareVersion(firmwareVersion); + } + + private XBeeMode getModeFromFirmwareVersion(int[] firmwareVersion) { + // First byte of the firmware version tells us which mode we're running in + // in case we're using API mode (which we always should be). + switch(firmwareVersion[0]) { + case 0x21: + return XBeeMode.COORDINATOR; + case 0x23: + return XBeeMode.ROUTER; + case 0x29: + return XBeeMode.END_DEVICE; + default: + return XBeeMode.UNKNOWN; + } + } + + public XBeeMode getMode() { + return mode; + } + + public void touchLastReceivedTime() { + lastReceivedMessage = System.currentTimeMillis(); + } + + public RadioType getHardwareVersion() { + return hardwareVersion; + } + + public int getFirmwareVersion() { + return firmwareVersion; + } + + public long getPanID() { + return panID; + } + + public int getSerialHigh() { + return serialHigh; + } + + public int getSerialLow() { + return serialLow; + } + + public long getLastReceivedMessage() { + return lastReceivedMessage; + } + + @Override + public String toString() { + return "XBeeInformation [hardwareVersion=" + hardwareVersion + + ", firmwareVersion=" + String.format("%02X", firmwareVersion) + ", panID=" + String.format("%08X", panID) + + ", serialHigh=" + String.format("%04X", serialHigh) + ", serialLow=" + String.format("%04X", serialLow) + + ", mode " + mode.toString() + "]"; + } + +} diff --git a/src/com/rapplogic/xbee/api/XBeeNotConnectedException.java b/src/main/java/com/rapplogic/xbee/api/XBeeNotConnectedException.java similarity index 93% rename from src/com/rapplogic/xbee/api/XBeeNotConnectedException.java rename to src/main/java/com/rapplogic/xbee/api/XBeeNotConnectedException.java index 34fbc7a..398b00e 100644 --- a/src/com/rapplogic/xbee/api/XBeeNotConnectedException.java +++ b/src/main/java/com/rapplogic/xbee/api/XBeeNotConnectedException.java @@ -1,33 +1,35 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -/** - * Indicates there is not connection to the radio - *

- * @author andrew - * - */ -public class XBeeNotConnectedException extends RuntimeException { - - public XBeeNotConnectedException() { - super(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api; + +/** + * Indicates there is not connection to the radio + *

+ * @author andrew + * + */ +public class XBeeNotConnectedException extends RuntimeException { + + private static final long serialVersionUID = -9072619514834558213L; + + public XBeeNotConnectedException() { + super(); + } +} diff --git a/src/com/rapplogic/xbee/api/XBeePacket.java b/src/main/java/com/rapplogic/xbee/api/XBeePacket.java similarity index 96% rename from src/com/rapplogic/xbee/api/XBeePacket.java rename to src/main/java/com/rapplogic/xbee/api/XBeePacket.java index 2aa35d9..cccb3c9 100644 --- a/src/com/rapplogic/xbee/api/XBeePacket.java +++ b/src/main/java/com/rapplogic/xbee/api/XBeePacket.java @@ -1,330 +1,330 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; - -import org.apache.log4j.Logger; - -import com.rapplogic.xbee.util.ByteUtils; - -/** - * Packages a frame data array into an XBee packet. - *

- * @author andrew - * - */ -public class XBeePacket { - - public enum SpecialByte { - START_BYTE (0x7e), // ~ - ESCAPE (0x7d), // } - XON (0x11), - XOFF (0x13); - - private static final Map lookup = new HashMap(); - - static { - for(SpecialByte s : EnumSet.allOf(SpecialByte.class)) { - lookup.put(s.getValue(), s); - } - } - - public static SpecialByte get(int value) { - return lookup.get(value); - } - - private final int value; - - SpecialByte(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - } - - private final static Logger log = Logger.getLogger(XBeePacket.class); - - private int[] packet; - - /** - * Performs the necessary activities to construct an XBee packet from the frame data. - * This includes: computing the checksum, escaping the necessary bytes, adding the start byte and length bytes. - * - * The format of a packet is as follows: - * - * start byte - msb length byte - lsb length byte - frame data - checksum byte - * - * @param frameData - */ - public XBeePacket(int[] frameData) { - - // checksum is always computed on pre-escaped packet - Checksum checksum = new Checksum(); - - for (int aFrameData : frameData) { - checksum.addByte(aFrameData); - } - - checksum.compute(); - - // packet size is frame data + start byte + 2 length bytes + checksum byte - packet = new int[frameData.length + 4]; - packet[0] = SpecialByte.START_BYTE.getValue(); - - // Packet length does not include escape bytes or start, length and checksum bytes - XBeePacketLength length = new XBeePacketLength(frameData.length); - - // msb length (will be zero until maybe someday when > 255 bytes packets are supported) - packet[1] = length.getMsb(); - // lsb length - packet[2] = length.getLsb(); - - for (int i = 0; i < frameData.length; i++) { - if (frameData[i] > 255) { - throw new RuntimeException("Packet values must not be greater than one byte (255): " + frameData[i]); - } - - packet[3 + i] = frameData[i]; - } - - // set last byte as checksum - // note: if checksum is not correct, XBee won't send out packet or return error. ask me how I know. - - packet[packet.length - 1] = checksum.getChecksum(); - -// for (int i = 0; i < packet.length; i++) { -// log.debug("XBeeApi pre-escape packet byte " + i + " is " + ByteUtils.toBase16(packet[i])); -// } - - int preEscapeLength = packet.length; - - // TODO save escaping for the serial out method. this is an unnecessary operation - packet = escapePacket(packet); - - if (log.isDebugEnabled()) { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append("Packet: "); - for (int i = 0; i < packet.length; i++) { - stringBuilder.append(ByteUtils.toBase16(packet[i])); - if (i < packet.length - 1) { - stringBuilder.append(" "); - } - } - log.debug(stringBuilder); - - log.debug("pre-escape packet size is " + preEscapeLength + ", post-escape packet size is " + packet.length); - } - } - - /** - * Escape all bytes in packet after start byte, and including checksum - * - * @param packet - * @return - */ - private static int[] escapePacket(int[] packet) { - int escapeBytes = 0; - - // escape packet. start at one so we don't escape the start byte - for (int i = 1; i < packet.length; i++) { - if (isSpecialByte(packet[i])) { - log.debug("escapeFrameData: packet byte requires escaping byte " + ByteUtils.toBase16(packet[i])); - escapeBytes++; - } - } - - if (escapeBytes == 0) { - return packet; - } else { - log.debug("packet requires escaping"); - - int[] escapePacket = new int[packet.length + escapeBytes]; - - int pos = 1; - - escapePacket[0] = SpecialByte.START_BYTE.getValue(); - - for (int i = 1; i < packet.length; i++) { - if (isSpecialByte(packet[i])) { - escapePacket[pos] = SpecialByte.ESCAPE.getValue(); - escapePacket[++pos] = 0x20 ^ packet[i]; - - log.debug("escapeFrameData: xor'd byte is 0x" + Integer.toHexString(escapePacket[pos])); - } else { - escapePacket[pos] = packet[i]; - } - - pos++; - } - - return escapePacket; - } - } - - /** - * @deprecated use getByteArray - * @return - */ - public int[] getPacket() { - return this.getByteArray(); - } - - public int[] getByteArray() { - return packet; - } - - public static boolean isSpecialByte(int b) { - return b == SpecialByte.START_BYTE.getValue() || b == SpecialByte.ESCAPE.getValue() || b == SpecialByte.XON.getValue() || - b == SpecialByte.XOFF.getValue(); - - } - - public String toString() { - return ByteUtils.toBase16(this.packet); - } - - /** - * Must be called with unescapted packet - * - * @param packet - * @return - */ - public static int getPacketLength(int[] packet) { - return new XBeePacketLength(packet[1], packet[2]).getLength(); - } - - /** - * Returns true if the packet is valid, Verifies both escaped and un-escaped packets - * - * @param packet - * @return - */ - public static boolean verify(int[] packet) { - boolean valid = true; - - try { - if (packet[0] != SpecialByte.START_BYTE.getValue()) { - return false; - } - - if (packetEndsWithEscapeByte(packet)) { - // packet can never end on a escape byte since the following byte is xor to unescape and will result in a array out of bounds exception - // this is an incomplete packet - return false; - } - - // first need to un-escape packet since escape bytes complicate things - int[] unEscaped = unEscapePacket(packet); - - // in theory 3 bytes are the minimum for 0 byte packet with no escape bytes. any less it can't be valid - if (unEscaped.length < 3) { - return false; - } - - int len = getPacketLength(unEscaped); - - // total packet length = stated length + 1 start byte + 1 checksum byte + 2 length bytes - // stated packet length does include escaping bytes, so actual packet size can be much larger, almost double - int expectedPacketLength = len + 4; - - // if we are less bytes than expected packet length it can't be valid - if (unEscaped.length != expectedPacketLength) { - return false; - } - - int[] frameData = new int[len]; - - Checksum checksum = new Checksum(); - - for (int i = 3; i < unEscaped.length - 1; i++) { - frameData[i - 3] = unEscaped[i]; - checksum.addByte(frameData[i - 3]); - } - - // add checksum byte to verify -- the last byte - checksum.addByte(unEscaped[unEscaped.length - 1]); - - if (!checksum.verify()) { - valid = false; - } - } catch (Exception e) { - throw new RuntimeException("Packet verification failed with error: ", e); - } - - return valid; - } - - public static boolean packetEndsWithEscapeByte(int[] packet) { - return (packet[packet.length - 1] == SpecialByte.ESCAPE.getValue()); - } - - /** - * - * @param packet - * @return - * @throws IncompletePacketException - */ - public static int[] unEscapePacket(int[] packet) { - - int escapeBytes = 0; - - if (packetEndsWithEscapeByte(packet)) { - //packet can never end on a escape byte since the following byte is xor to unescape and will result in a array out of bounds exception - throw new RuntimeException("Invalid packet -- packet cannot end with an escape byte " + ByteUtils.toBase16(packet)); - } - - // first check if escape byte exists, if not we don't allocate a new array - for (int b : packet) { - if (b == SpecialByte.ESCAPE.getValue()) { - escapeBytes++; - } - } - - if (escapeBytes == 0) { - return packet; - } - - int[] unEscapedPacket = new int[packet.length - escapeBytes]; - - int pos = 0; - - for (int i = 0; i < packet.length; i++) { - if (packet[i] == SpecialByte.ESCAPE.getValue()) { - // discard escape byte and un-escape following byte - if (i >= packet.length - 1) { - return packet; - } - - unEscapedPacket[pos] = 0x20 ^ packet[++i]; - } else { - unEscapedPacket[pos] = packet[i]; - } - - pos++; - } - - return unEscapedPacket; - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.Logger; + +import com.rapplogic.xbee.util.ByteUtils; + +/** + * Packages a frame data array into an XBee packet. + *

+ * @author andrew + * + */ +public class XBeePacket { + + public enum SpecialByte { + START_BYTE (0x7e), // ~ + ESCAPE (0x7d), // } + XON (0x11), + XOFF (0x13); + + private static final Map lookup = new HashMap(); + + static { + for(SpecialByte s : EnumSet.allOf(SpecialByte.class)) { + lookup.put(s.getValue(), s); + } + } + + public static SpecialByte get(int value) { + return lookup.get(value); + } + + private final int value; + + SpecialByte(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + private final static Logger log = Logger.getLogger(XBeePacket.class); + + private int[] packet; + + /** + * Performs the necessary activities to construct an XBee packet from the frame data. + * This includes: computing the checksum, escaping the necessary bytes, adding the start byte and length bytes. + * + * The format of a packet is as follows: + * + * start byte - msb length byte - lsb length byte - frame data - checksum byte + * + * @param frameData + */ + public XBeePacket(int[] frameData) { + + // checksum is always computed on pre-escaped packet + Checksum checksum = new Checksum(); + + for (int aFrameData : frameData) { + checksum.addByte(aFrameData); + } + + checksum.compute(); + + // packet size is frame data + start byte + 2 length bytes + checksum byte + packet = new int[frameData.length + 4]; + packet[0] = SpecialByte.START_BYTE.getValue(); + + // Packet length does not include escape bytes or start, length and checksum bytes + XBeePacketLength length = new XBeePacketLength(frameData.length); + + // msb length (will be zero until maybe someday when > 255 bytes packets are supported) + packet[1] = length.getMsb(); + // lsb length + packet[2] = length.getLsb(); + + for (int i = 0; i < frameData.length; i++) { + if (frameData[i] > 255) { + throw new RuntimeException("Packet values must not be greater than one byte (255): " + frameData[i]); + } + + packet[3 + i] = frameData[i]; + } + + // set last byte as checksum + // note: if checksum is not correct, XBee won't send out packet or return error. ask me how I know. + + packet[packet.length - 1] = checksum.getChecksum(); + +// for (int i = 0; i < packet.length; i++) { +// log.debug("XBeeApi pre-escape packet byte " + i + " is " + ByteUtils.toBase16(packet[i])); +// } + + int preEscapeLength = packet.length; + + // TODO save escaping for the serial out method. this is an unnecessary operation + packet = escapePacket(packet); + + if (log.isDebugEnabled()) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("Packet: "); + for (int i = 0; i < packet.length; i++) { + stringBuilder.append(ByteUtils.toBase16(packet[i])); + if (i < packet.length - 1) { + stringBuilder.append(" "); + } + } + log.debug(stringBuilder); + + log.debug("pre-escape packet size is " + preEscapeLength + ", post-escape packet size is " + packet.length); + } + } + + /** + * Escape all bytes in packet after start byte, and including checksum + * + * @param packet + * @return + */ + private static int[] escapePacket(int[] packet) { + int escapeBytes = 0; + + // escape packet. start at one so we don't escape the start byte + for (int i = 1; i < packet.length; i++) { + if (isSpecialByte(packet[i])) { + log.debug("escapeFrameData: packet byte requires escaping byte " + ByteUtils.toBase16(packet[i])); + escapeBytes++; + } + } + + if (escapeBytes == 0) { + return packet; + } else { + log.debug("packet requires escaping"); + + int[] escapePacket = new int[packet.length + escapeBytes]; + + int pos = 1; + + escapePacket[0] = SpecialByte.START_BYTE.getValue(); + + for (int i = 1; i < packet.length; i++) { + if (isSpecialByte(packet[i])) { + escapePacket[pos] = SpecialByte.ESCAPE.getValue(); + escapePacket[++pos] = 0x20 ^ packet[i]; + + log.debug("escapeFrameData: xor'd byte is 0x" + Integer.toHexString(escapePacket[pos])); + } else { + escapePacket[pos] = packet[i]; + } + + pos++; + } + + return escapePacket; + } + } + + /** + * @deprecated use getByteArray + * @return + */ + public int[] getPacket() { + return this.getByteArray(); + } + + public int[] getByteArray() { + return packet; + } + + public static boolean isSpecialByte(int b) { + return b == SpecialByte.START_BYTE.getValue() || b == SpecialByte.ESCAPE.getValue() || b == SpecialByte.XON.getValue() || + b == SpecialByte.XOFF.getValue(); + + } + + public String toString() { + return ByteUtils.toBase16(this.packet); + } + + /** + * Must be called with unescapted packet + * + * @param packet + * @return + */ + public static int getPacketLength(int[] packet) { + return new XBeePacketLength(packet[1], packet[2]).getLength(); + } + + /** + * Returns true if the packet is valid, Verifies both escaped and un-escaped packets + * + * @param packet + * @return + */ + public static boolean verify(int[] packet) { + boolean valid = true; + + try { + if (packet[0] != SpecialByte.START_BYTE.getValue()) { + return false; + } + + if (packetEndsWithEscapeByte(packet)) { + // packet can never end on a escape byte since the following byte is xor to unescape and will result in a array out of bounds exception + // this is an incomplete packet + return false; + } + + // first need to un-escape packet since escape bytes complicate things + int[] unEscaped = unEscapePacket(packet); + + // in theory 3 bytes are the minimum for 0 byte packet with no escape bytes. any less it can't be valid + if (unEscaped.length < 3) { + return false; + } + + int len = getPacketLength(unEscaped); + + // total packet length = stated length + 1 start byte + 1 checksum byte + 2 length bytes + // stated packet length does include escaping bytes, so actual packet size can be much larger, almost double + int expectedPacketLength = len + 4; + + // if we are less bytes than expected packet length it can't be valid + if (unEscaped.length != expectedPacketLength) { + return false; + } + + int[] frameData = new int[len]; + + Checksum checksum = new Checksum(); + + for (int i = 3; i < unEscaped.length - 1; i++) { + frameData[i - 3] = unEscaped[i]; + checksum.addByte(frameData[i - 3]); + } + + // add checksum byte to verify -- the last byte + checksum.addByte(unEscaped[unEscaped.length - 1]); + + if (!checksum.verify()) { + valid = false; + } + } catch (Exception e) { + throw new RuntimeException("Packet verification failed with error: ", e); + } + + return valid; + } + + public static boolean packetEndsWithEscapeByte(int[] packet) { + return (packet[packet.length - 1] == SpecialByte.ESCAPE.getValue()); + } + + /** + * + * @param packet + * @return + * @throws IncompletePacketException + */ + public static int[] unEscapePacket(int[] packet) { + + int escapeBytes = 0; + + if (packetEndsWithEscapeByte(packet)) { + //packet can never end on a escape byte since the following byte is xor to unescape and will result in a array out of bounds exception + throw new RuntimeException("Invalid packet -- packet cannot end with an escape byte " + ByteUtils.toBase16(packet)); + } + + // first check if escape byte exists, if not we don't allocate a new array + for (int b : packet) { + if (b == SpecialByte.ESCAPE.getValue()) { + escapeBytes++; + } + } + + if (escapeBytes == 0) { + return packet; + } + + int[] unEscapedPacket = new int[packet.length - escapeBytes]; + + int pos = 0; + + for (int i = 0; i < packet.length; i++) { + if (packet[i] == SpecialByte.ESCAPE.getValue()) { + // discard escape byte and un-escape following byte + if (i >= packet.length - 1) { + return packet; + } + + unEscapedPacket[pos] = 0x20 ^ packet[++i]; + } else { + unEscapedPacket[pos] = packet[i]; + } + + pos++; + } + + return unEscapedPacket; + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/XBeePacketHandler.java b/src/main/java/com/rapplogic/xbee/api/XBeePacketHandler.java similarity index 97% rename from src/com/rapplogic/xbee/api/XBeePacketHandler.java rename to src/main/java/com/rapplogic/xbee/api/XBeePacketHandler.java index 8138546..68ee116 100644 --- a/src/com/rapplogic/xbee/api/XBeePacketHandler.java +++ b/src/main/java/com/rapplogic/xbee/api/XBeePacketHandler.java @@ -1,24 +1,24 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -public interface XBeePacketHandler { - public void handlePacket(XBeeResponse response); -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api; + +public interface XBeePacketHandler { + public void handlePacket(XBeeResponse response); +} diff --git a/src/com/rapplogic/xbee/api/XBeePacketLength.java b/src/main/java/com/rapplogic/xbee/api/XBeePacketLength.java similarity index 96% rename from src/com/rapplogic/xbee/api/XBeePacketLength.java rename to src/main/java/com/rapplogic/xbee/api/XBeePacketLength.java index ddef4d1..3cbabcf 100644 --- a/src/com/rapplogic/xbee/api/XBeePacketLength.java +++ b/src/main/java/com/rapplogic/xbee/api/XBeePacketLength.java @@ -1,49 +1,49 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -import com.rapplogic.xbee.util.DoubleByte; - -/** - * Supports a 16-bit XBee packet length - *

- * @author andrew - * - */ -public class XBeePacketLength extends DoubleByte { - - /** - * Manual says max packet length is 100 bytes so not sure why 2 bytes are needed - * - * @param msb - * @param lsb - */ - public XBeePacketLength(int msb, int lsb) { - super(msb, lsb); - } - - public XBeePacketLength(int length) { - super(length); - } - - public int getLength() { - return this.get16BitValue(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api; + +import com.rapplogic.xbee.util.DoubleByte; + +/** + * Supports a 16-bit XBee packet length + *

+ * @author andrew + * + */ +public class XBeePacketLength extends DoubleByte { + + /** + * Manual says max packet length is 100 bytes so not sure why 2 bytes are needed + * + * @param msb + * @param lsb + */ + public XBeePacketLength(int msb, int lsb) { + super(msb, lsb); + } + + public XBeePacketLength(int length) { + super(length); + } + + public int getLength() { + return this.get16BitValue(); + } +} diff --git a/src/com/rapplogic/xbee/api/XBeeParseException.java b/src/main/java/com/rapplogic/xbee/api/XBeeParseException.java similarity index 97% rename from src/com/rapplogic/xbee/api/XBeeParseException.java rename to src/main/java/com/rapplogic/xbee/api/XBeeParseException.java index d27be89..dcd14f0 100644 --- a/src/com/rapplogic/xbee/api/XBeeParseException.java +++ b/src/main/java/com/rapplogic/xbee/api/XBeeParseException.java @@ -1,29 +1,29 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -public class XBeeParseException extends RuntimeException { - - private static final long serialVersionUID = 6752060371295132748L; - - public XBeeParseException(String s) { - super(s); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api; + +public class XBeeParseException extends RuntimeException { + + private static final long serialVersionUID = 6752060371295132748L; + + public XBeeParseException(String s) { + super(s); + } +} diff --git a/src/com/rapplogic/xbee/api/XBeeRequest.java b/src/main/java/com/rapplogic/xbee/api/XBeeRequest.java similarity index 96% rename from src/com/rapplogic/xbee/api/XBeeRequest.java rename to src/main/java/com/rapplogic/xbee/api/XBeeRequest.java index 9cf62fc..5648d0d 100644 --- a/src/com/rapplogic/xbee/api/XBeeRequest.java +++ b/src/main/java/com/rapplogic/xbee/api/XBeeRequest.java @@ -1,85 +1,85 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -import java.io.Serializable; - - -/** - * The super class of all XBee transmit packets. - * Constructs frame data portion of an XBee packet - *

- * @author andrew - * - */ - -public abstract class XBeeRequest implements Serializable { - - private static final long serialVersionUID = -9181542059678009341L; - - public static final int DEFAULT_FRAME_ID = 1; - // XBee will not generate a TX Status Packet if this frame id sent - public static final int NO_RESPONSE_FRAME_ID = 0; - - private ApiId apiId; - private int frameId; - - public XBeeRequest() { - - } - - // TODO create XBeePacket(XBeeRequest) constructor and move operation there - public XBeePacket getXBeePacket() { - int[] frameData = this.getFrameData(); - - if (frameData == null) { - throw new RuntimeException("frame data is null"); - } - - // TODO xbee packet should handle api/frame id - XBeePacket packet = new XBeePacket(frameData); - - return packet; - } - - public abstract int[] getFrameData(); - - public ApiId getApiId() { - return apiId; - } - - public int getFrameId() { - return frameId; - } - - public String toString() { - return "apiId=" + this.getApiId() + ",frameId=" + this.getFrameId(); - } - - public void setApiId(ApiId apiId) { - this.apiId = apiId; - } - - public void setFrameId(int frameId) { - this.frameId = frameId; - } - - // TODO clear method to reuse request -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api; + +import java.io.Serializable; + + +/** + * The super class of all XBee transmit packets. + * Constructs frame data portion of an XBee packet + *

+ * @author andrew + * + */ + +public abstract class XBeeRequest implements Serializable { + + private static final long serialVersionUID = -9181542059678009341L; + + public static final int DEFAULT_FRAME_ID = 1; + // XBee will not generate a TX Status Packet if this frame id sent + public static final int NO_RESPONSE_FRAME_ID = 0; + + private ApiId apiId; + private int frameId; + + public XBeeRequest() { + + } + + // TODO create XBeePacket(XBeeRequest) constructor and move operation there + public XBeePacket getXBeePacket() { + int[] frameData = this.getFrameData(); + + if (frameData == null) { + throw new RuntimeException("frame data is null"); + } + + // TODO xbee packet should handle api/frame id + XBeePacket packet = new XBeePacket(frameData); + + return packet; + } + + public abstract int[] getFrameData(); + + public ApiId getApiId() { + return apiId; + } + + public int getFrameId() { + return frameId; + } + + public String toString() { + return "apiId=" + this.getApiId() + ",frameId=" + this.getFrameId(); + } + + public void setApiId(ApiId apiId) { + this.apiId = apiId; + } + + public void setFrameId(int frameId) { + this.frameId = frameId; + } + + // TODO clear method to reuse request +} diff --git a/src/com/rapplogic/xbee/api/XBeeResponse.java b/src/main/java/com/rapplogic/xbee/api/XBeeResponse.java similarity index 95% rename from src/com/rapplogic/xbee/api/XBeeResponse.java rename to src/main/java/com/rapplogic/xbee/api/XBeeResponse.java index bd6ea25..4b53c18 100644 --- a/src/com/rapplogic/xbee/api/XBeeResponse.java +++ b/src/main/java/com/rapplogic/xbee/api/XBeeResponse.java @@ -1,194 +1,192 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -import java.io.IOException; -import java.io.Serializable; -import java.util.Arrays; - -import com.rapplogic.xbee.util.ByteUtils; - -/** - * The super class of all XBee Receive packets - * - * @author andrew - * - */ -public abstract class XBeeResponse implements Serializable { - - // TODO consider adding UUID to each response - - private static final long serialVersionUID = -7038123612643874495L; - - // the raw (escaped) bytes of this packet (minus start byte) - // this is the most compact representation of the packet; - // useful for sending the packet over a wire (e.g. xml), - // for later reconstitution - private int[] rawPacketBytes; - private int[] processedPacketBytes; - - private ApiId apiId; - private int checksum; - - private XBeePacketLength length; - - private boolean error = false; - - public XBeeResponse() { - - } - - public XBeePacketLength getLength() { - return length; - } - - public void setLength(XBeePacketLength length) { - this.length = length; - } - - public ApiId getApiId() { - return apiId; - } - - public void setApiId(ApiId apiId) { - this.apiId = apiId; - } - - public int getChecksum() { - return checksum; - } - - public void setChecksum(int checksum) { - this.checksum = checksum; - } - - /** - * Indicates an error occurred during the parsing of the packet. - * This may indicate a bug in this software or in the XBee firmware. - * Absence of an error does not indicate the request was successful; - * you will need to inspect the status byte of the response object (if available) - * to determine success. - * - * @return - */ - public boolean isError() { - return error; - } - - public void setError(boolean error) { - this.error = error; - } - - /** - * @deprecated Use getRawPacketBytes instead - * @return - */ - public int[] getPacketBytes() { - return this.getRawPacketBytes(); - } - - /** - * Returns an array all bytes (as received off radio, including escape bytes) in packet except the start byte. - * - * @return - */ - public int[] getRawPacketBytes() { - return rawPacketBytes; - } - - /** - * Returns an array of all bytes (after being un-escaped) in the packet except the start byte. - * @return - */ - public int[] getProcessedPacketBytes() { - return processedPacketBytes; - } - - public void setRawPacketBytes(int[] packetBytes) { - this.rawPacketBytes = packetBytes; - this.processedPacketBytes = XBeePacket.unEscapePacket(packetBytes); - } - - /** - * For internal use only. Called after successful parsing to allow subclass to do any final processing before delivery - */ - public void finish() { - - } - - /** - * All subclasses must implement to parse the packet from the input stream. - * The subclass must parse all bytes in the packet starting after the API_ID, and - * up to but not including the checksum. Reading either more or less bytes that expected will - * result in an error. - * - * @param parser - * @throws IOException - */ - protected abstract void parse(IPacketParser parser) throws IOException; - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((apiId == null) ? 0 : apiId.hashCode()); - result = prime * result + checksum; - result = prime * result + (error ? 1231 : 1237); - result = prime * result + ((length == null) ? 0 : length.hashCode()); - result = prime * result + Arrays.hashCode(rawPacketBytes); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - XBeeResponse other = (XBeeResponse) obj; - if (apiId == null) { - if (other.apiId != null) - return false; - } else if (!apiId.equals(other.apiId)) - return false; - if (checksum != other.checksum) - return false; - if (error != other.error) - return false; - if (length == null) { - if (other.length != null) - return false; - } else if (!length.equals(other.length)) - return false; - if (!Arrays.equals(rawPacketBytes, other.rawPacketBytes)) - return false; - return true; - } - - public String toString() { - // 8/19/09 fixed null pointer on length.get16BitValue - return "apiId=" + this.apiId + - ",length=" + (length == null ? "null" : length.get16BitValue()) + - ",checksum=" + ByteUtils.toBase16(checksum) + - ",error=" + this.error; - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api; + +import java.io.IOException; +import java.io.Serializable; +import java.util.Arrays; + +import com.rapplogic.xbee.util.ByteUtils; + +/** + * The super class of all XBee Receive packets + * + * @author andrew + * + */ +public abstract class XBeeResponse implements Serializable { + + private static final long serialVersionUID = -7038123612643874495L; + + // the raw (escaped) bytes of this packet (minus start byte) + // this is the most compact representation of the packet; + // useful for sending the packet over a wire (e.g. xml), + // for later reconstitution + private int[] rawPacketBytes; + private int[] processedPacketBytes; + + private ApiId apiId; + private int checksum; + + private XBeePacketLength length; + + private boolean error = false; + + public XBeeResponse() { + + } + + public XBeePacketLength getLength() { + return length; + } + + public void setLength(XBeePacketLength length) { + this.length = length; + } + + public ApiId getApiId() { + return apiId; + } + + public void setApiId(ApiId apiId) { + this.apiId = apiId; + } + + public int getChecksum() { + return checksum; + } + + public void setChecksum(int checksum) { + this.checksum = checksum; + } + + /** + * Indicates an error occurred during the parsing of the packet. + * This may indicate a bug in this software or in the XBee firmware. + * Absence of an error does not indicate the request was successful; + * you will need to inspect the status byte of the response object (if available) + * to determine success. + * + * @return + */ + public boolean isError() { + return error; + } + + public void setError(boolean error) { + this.error = error; + } + + /** + * @deprecated Use getRawPacketBytes instead + * @return + */ + public int[] getPacketBytes() { + return this.getRawPacketBytes(); + } + + /** + * Returns an array all bytes (as received off radio, including escape bytes) in packet except the start byte. + * + * @return + */ + public int[] getRawPacketBytes() { + return rawPacketBytes; + } + + /** + * Returns an array of all bytes (after being un-escaped) in the packet except the start byte. + * @return + */ + public int[] getProcessedPacketBytes() { + return processedPacketBytes; + } + + public void setRawPacketBytes(int[] packetBytes) { + this.rawPacketBytes = packetBytes; + this.processedPacketBytes = XBeePacket.unEscapePacket(packetBytes); + } + + /** + * For internal use only. Called after successful parsing to allow subclass to do any final processing before delivery + */ + public void finish() { + + } + + /** + * All subclasses must implement to parse the packet from the input stream. + * The subclass must parse all bytes in the packet starting after the API_ID, and + * up to but not including the checksum. Reading either more or less bytes that expected will + * result in an error. + * + * @param parser + * @throws IOException + */ + protected abstract void parse(IPacketParser parser) throws IOException; + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((apiId == null) ? 0 : apiId.hashCode()); + result = prime * result + checksum; + result = prime * result + (error ? 1231 : 1237); + result = prime * result + ((length == null) ? 0 : length.hashCode()); + result = prime * result + Arrays.hashCode(rawPacketBytes); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + XBeeResponse other = (XBeeResponse) obj; + if (apiId == null) { + if (other.apiId != null) + return false; + } else if (!apiId.equals(other.apiId)) + return false; + if (checksum != other.checksum) + return false; + if (error != other.error) + return false; + if (length == null) { + if (other.length != null) + return false; + } else if (!length.equals(other.length)) + return false; + if (!Arrays.equals(rawPacketBytes, other.rawPacketBytes)) + return false; + return true; + } + + public String toString() { + // 8/19/09 fixed null pointer on length.get16BitValue + return "apiId=" + this.apiId + + ",length=" + (length == null ? "null" : length.get16BitValue()) + + ",checksum=" + ByteUtils.toBase16(checksum) + + ",error=" + this.error; + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/XBeeTimeoutException.java b/src/main/java/com/rapplogic/xbee/api/XBeeTimeoutException.java similarity index 96% rename from src/com/rapplogic/xbee/api/XBeeTimeoutException.java rename to src/main/java/com/rapplogic/xbee/api/XBeeTimeoutException.java index 485a982..bb66bcc 100644 --- a/src/com/rapplogic/xbee/api/XBeeTimeoutException.java +++ b/src/main/java/com/rapplogic/xbee/api/XBeeTimeoutException.java @@ -1,35 +1,35 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -/** - * Indicates an operation did not succeed within the alloted time - *

- * @author andrew - * - */ -public class XBeeTimeoutException extends XBeeException { - - private static final long serialVersionUID = 1045336675379821890L; - - public XBeeTimeoutException() { - super(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api; + +/** + * Indicates an operation did not succeed within the alloted time + *

+ * @author andrew + * + */ +public class XBeeTimeoutException extends XBeeException { + + private static final long serialVersionUID = 1045336675379821890L; + + public XBeeTimeoutException() { + super(); + } +} diff --git a/src/com/rapplogic/xbee/api/AtCommand.java b/src/main/java/com/rapplogic/xbee/api/requests/AtCommand.java similarity index 94% rename from src/com/rapplogic/xbee/api/AtCommand.java rename to src/main/java/com/rapplogic/xbee/api/requests/AtCommand.java index fd6b2dc..03a81c0 100644 --- a/src/com/rapplogic/xbee/api/AtCommand.java +++ b/src/main/java/com/rapplogic/xbee/api/requests/AtCommand.java @@ -1,138 +1,142 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -import com.rapplogic.xbee.util.ByteUtils; -import com.rapplogic.xbee.util.IntArrayOutputStream; - -/** - * API technique to set/query commands - *

- * WARNING: Any changes made will not survive a power cycle unless written to memory with WR command - * According to the manual, the WR command can only be written so many times.. however many that is. - *

- * API ID: 0x8 - *

- * Determining radio type with HV:
- * Byte 1, Part Number
- * x17, XB24 (series 1)
- * x18, XBP24 (series 1)
- * x19, XB24-B (series 2)
- * x1A, XBP24-B (series 2)
- *

- * XB24-ZB
- * XBP24-ZB
- * @author andrew - */ -public class AtCommand extends XBeeRequest { - - private String command; - private int[] value; - -// // common i/o pin settings. it is up to the developer to ensure the setting is applicable to the pin (e.g. not all pins support analog input) -// public enum IoSetting { -// DISABLED (new int[] {0x0}), -// ANALOG_INPUT (new int[] {0x2}), -// DIGITAL_INPUT (new int[] {0x3}), -// DIGITAL_OUTPUT_LOW (new int[] {0x4}), -// DIGITAL_OUTPUT_HIGH (new int[] {0x5}); -// -// private final int[] value; -// -// IoSetting(int[] value) { -// this.value = value; -// } -// } - - public AtCommand(String command) { - this(command, null, DEFAULT_FRAME_ID); - } - - public AtCommand(String command, int value) { - this(command, new int[] {value}, DEFAULT_FRAME_ID); - } - - public AtCommand(String command, int value[]) { - this(command, value, DEFAULT_FRAME_ID); - } - - /** - * Warning: frameId must be > 0 for a response - * - * @param command - * @param value - * @param frameId - */ - public AtCommand(String command, int[] value, int frameId) { - this.command = command; - this.value = value; - this.setFrameId(frameId); - } - - public int[] getFrameData() { - if (command.length() > 2) { - throw new IllegalArgumentException("Command should be two characters. Do not include AT prefix"); - } - - IntArrayOutputStream out = new IntArrayOutputStream(); - - // api id - out.write(this.getApiId().getValue()); - // frame id - out.write(this.getFrameId()); - // at command byte 1 - out.write((int) command.substring(0, 1).toCharArray()[0]); - // at command byte 2 - out.write((int) command.substring(1, 2).toCharArray()[0]); - - // int value is up to four bytes to represent command value - if (value != null) { - out.write(value); - } - - return out.getIntArray(); - } - - public ApiId getApiId() { - return ApiId.AT_COMMAND; - } - - public String getCommand() { - return command; - } - - public void setCommand(String command) { - this.command = command; - } - - public int[] getValue() { - return value; - } - - public void setValue(int[] value) { - this.value = value; - } - - public String toString() { - return super.toString() + - ",command=" + this.command + - ",value=" + (value == null ? "null" : ByteUtils.toBase16(value)); - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.requests; + +import com.rapplogic.xbee.api.ApiId; +import com.rapplogic.xbee.api.XBeeRequest; +import com.rapplogic.xbee.util.ByteUtils; +import com.rapplogic.xbee.util.IntArrayOutputStream; + +/** + * API technique to set/query commands + *

+ * WARNING: Any changes made will not survive a power cycle unless written to memory with WR command + * According to the manual, the WR command can only be written so many times.. however many that is. + *

+ * API ID: 0x8 + *

+ * Determining radio type with HV:
+ * Byte 1, Part Number
+ * x17, XB24 (series 1)
+ * x18, XBP24 (series 1)
+ * x19, XB24-B (series 2)
+ * x1A, XBP24-B (series 2)
+ *

+ * XB24-ZB
+ * XBP24-ZB
+ * @author andrew + */ +public class AtCommand extends XBeeRequest { + + private static final long serialVersionUID = -3820024650079482977L; + + private String command; + private int[] value; + +// // common i/o pin settings. it is up to the developer to ensure the setting is applicable to the pin (e.g. not all pins support analog input) +// public enum IoSetting { +// DISABLED (new int[] {0x0}), +// ANALOG_INPUT (new int[] {0x2}), +// DIGITAL_INPUT (new int[] {0x3}), +// DIGITAL_OUTPUT_LOW (new int[] {0x4}), +// DIGITAL_OUTPUT_HIGH (new int[] {0x5}); +// +// private final int[] value; +// +// IoSetting(int[] value) { +// this.value = value; +// } +// } + + public AtCommand(String command) { + this(command, null, DEFAULT_FRAME_ID); + } + + public AtCommand(String command, int value) { + this(command, new int[] {value}, DEFAULT_FRAME_ID); + } + + public AtCommand(String command, int value[]) { + this(command, value, DEFAULT_FRAME_ID); + } + + /** + * Warning: frameId must be > 0 for a response + * + * @param command + * @param value + * @param frameId + */ + public AtCommand(String command, int[] value, int frameId) { + this.command = command; + this.value = value; + this.setFrameId(frameId); + } + + public int[] getFrameData() { + if (command.length() > 2) { + throw new IllegalArgumentException("Command should be two characters. Do not include AT prefix"); + } + + IntArrayOutputStream out = new IntArrayOutputStream(); + + // api id + out.write(this.getApiId().getValue()); + // frame id + out.write(this.getFrameId()); + // at command byte 1 + out.write((int) command.substring(0, 1).toCharArray()[0]); + // at command byte 2 + out.write((int) command.substring(1, 2).toCharArray()[0]); + + // int value is up to four bytes to represent command value + if (value != null) { + out.write(value); + } + + return out.getIntArray(); + } + + public ApiId getApiId() { + return ApiId.AT_COMMAND; + } + + public String getCommand() { + return command; + } + + public void setCommand(String command) { + this.command = command; + } + + public int[] getValue() { + return value; + } + + public void setValue(int[] value) { + this.value = value; + } + + public String toString() { + return super.toString() + + ",command=" + this.command + + ",value=" + (value == null ? "null" : ByteUtils.toBase16(value)); + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/AtCommandQueue.java b/src/main/java/com/rapplogic/xbee/api/requests/AtCommandQueue.java similarity index 88% rename from src/com/rapplogic/xbee/api/AtCommandQueue.java rename to src/main/java/com/rapplogic/xbee/api/requests/AtCommandQueue.java index f2c81b0..6882ddf 100644 --- a/src/com/rapplogic/xbee/api/AtCommandQueue.java +++ b/src/main/java/com/rapplogic/xbee/api/requests/AtCommandQueue.java @@ -1,43 +1,48 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -/** - * AT Command Queue - *

- * API ID: 0x9 - *

- * @author andrew - * - */ -public class AtCommandQueue extends AtCommand { - //TODO test - public AtCommandQueue(String command) { - this(command, null, DEFAULT_FRAME_ID); - } - - public AtCommandQueue(String command, int[] value, int frameId) { - super(command, value, frameId); - } - - public ApiId getApiId() { - return ApiId.AT_COMMAND_QUEUE; - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.requests; + +import com.rapplogic.xbee.api.ApiId; + +/** + * AT Command Queue + *

+ * API ID: 0x9 + *

+ * @author andrew + * + */ +public class AtCommandQueue extends AtCommand { + + private static final long serialVersionUID = 8000847616413855924L; + + //TODO test + public AtCommandQueue(String command) { + this(command, null, DEFAULT_FRAME_ID); + } + + public AtCommandQueue(String command, int[] value, int frameId) { + super(command, value, frameId); + } + + public ApiId getApiId() { + return ApiId.AT_COMMAND_QUEUE; + } +} diff --git a/src/com/rapplogic/xbee/api/RemoteAtRequest.java b/src/main/java/com/rapplogic/xbee/api/requests/RemoteAtRequest.java similarity index 96% rename from src/com/rapplogic/xbee/api/RemoteAtRequest.java rename to src/main/java/com/rapplogic/xbee/api/requests/RemoteAtRequest.java index 9a05393..ce62539 100644 --- a/src/com/rapplogic/xbee/api/RemoteAtRequest.java +++ b/src/main/java/com/rapplogic/xbee/api/requests/RemoteAtRequest.java @@ -17,8 +17,12 @@ * along with XBee-API. If not, see . */ -package com.rapplogic.xbee.api; +package com.rapplogic.xbee.api.requests; +import com.rapplogic.xbee.api.ApiId; +import com.rapplogic.xbee.api.XBeeAddress16; +import com.rapplogic.xbee.api.XBeeAddress64; +import com.rapplogic.xbee.api.XBeeRequest; import com.rapplogic.xbee.util.IntArrayOutputStream; /** @@ -35,6 +39,8 @@ * */ public class RemoteAtRequest extends AtCommand { + + private static final long serialVersionUID = 916569156209610858L; private XBeeAddress64 remoteAddr64; private XBeeAddress16 remoteAddr16; diff --git a/src/com/rapplogic/xbee/api/AtCommandResponse.java b/src/main/java/com/rapplogic/xbee/api/responses/AtCommandResponse.java similarity index 94% rename from src/com/rapplogic/xbee/api/AtCommandResponse.java rename to src/main/java/com/rapplogic/xbee/api/responses/AtCommandResponse.java index dab1b1a..7d357fe 100644 --- a/src/com/rapplogic/xbee/api/AtCommandResponse.java +++ b/src/main/java/com/rapplogic/xbee/api/responses/AtCommandResponse.java @@ -1,153 +1,156 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -import java.io.IOException; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; - -import com.rapplogic.xbee.util.ByteUtils; - -/** - * Sent in response to an AtCommand - *

- * API ID: 0x88 - *

- * @author andrew - * - */ -public class AtCommandResponse extends XBeeFrameIdResponse { - - public enum Status { -// 0 = OK -// 1 = ERROR -// 2 = Invalid Command -// 3 = Invalid Parameter -// 4 = Remote Command Transmission Failed - - OK (0), - ERROR (1), - INVALID_COMMAND (2), - INVALID_PARAMETER (3), - NO_RESPONSE (4); // series 1 remote AT only according to spec. also series 2 in 2x64 zb pro firmware - - private static final Map lookup = new HashMap(); - - static { - for(Status s : EnumSet.allOf(Status.class)) { - lookup.put(s.getValue(), s); - } - } - - public static Status get(int value) { - return lookup.get(value); - } - - private final int value; - - Status(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - } - - private int char1; - private int char2; - private Status status; - // response value msb to lsb - private int[] value; - - public AtCommandResponse() { - - } - - public int getChar1() { - return char1; - } - - - public void setChar1(int char1) { - this.char1 = char1; - } - - - public int getChar2() { - return char2; - } - - - public void setChar2(int char2) { - this.char2 = char2; - } - - public Status getStatus() { - return status; - } - - public void setStatus(Status status) { - this.status = status; - } - - public boolean isOk() { - return status == Status.OK; - } - - // TODO should return null if not specified - /** - * Returns the command data byte array. - * A zero length array will be returned if the command data is not specified. - * This is the case if the at command set a value, or executed a command that does - * not have a value (like FR) - * - * @return - */ - public int[] getValue() { - return value; - } - - public void setValue(int[] data) { - this.value = data; - } - - public String getCommand() { - return String.valueOf((char)this.char1) + String.valueOf((char)this.char2); - } - - public void parse(IPacketParser parser) throws IOException { - this.setFrameId(parser.read("AT Response Frame Id")); - this.setChar1(parser.read("AT Response Char 1")); - this.setChar2(parser.read("AT Response Char 2")); - this.setStatus(Status.get(parser.read("AT Response Status"))); - - this.setValue(parser.readRemainingBytes()); - } - - public String toString() { - return "command=" + this.getCommand() + - ",status=" + this.getStatus() + ",value=" + - (this.value == null ? "null" : ByteUtils.toBase16(this.getValue())) + - "," + - super.toString(); - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.responses; + +import java.io.IOException; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +import com.rapplogic.xbee.api.IPacketParser; +import com.rapplogic.xbee.util.ByteUtils; + +/** + * Sent in response to an AtCommand + *

+ * API ID: 0x88 + *

+ * @author andrew + * + */ +public class AtCommandResponse extends XBeeFrameIdResponse { + + private static final long serialVersionUID = 3989959825185846517L; + + public enum Status { +// 0 = OK +// 1 = ERROR +// 2 = Invalid Command +// 3 = Invalid Parameter +// 4 = Remote Command Transmission Failed + + OK (0), + ERROR (1), + INVALID_COMMAND (2), + INVALID_PARAMETER (3), + NO_RESPONSE (4); // series 1 remote AT only according to spec. also series 2 in 2x64 zb pro firmware + + private static final Map lookup = new HashMap(); + + static { + for(Status s : EnumSet.allOf(Status.class)) { + lookup.put(s.getValue(), s); + } + } + + public static Status get(int value) { + return lookup.get(value); + } + + private final int value; + + Status(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + private int char1; + private int char2; + private Status status; + // response value msb to lsb + private int[] value; + + public AtCommandResponse() { + + } + + public int getChar1() { + return char1; + } + + + public void setChar1(int char1) { + this.char1 = char1; + } + + + public int getChar2() { + return char2; + } + + + public void setChar2(int char2) { + this.char2 = char2; + } + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + + public boolean isOk() { + return status == Status.OK; + } + + // TODO should return null if not specified + /** + * Returns the command data byte array. + * A zero length array will be returned if the command data is not specified. + * This is the case if the at command set a value, or executed a command that does + * not have a value (like FR) + * + * @return + */ + public int[] getValue() { + return value; + } + + public void setValue(int[] data) { + this.value = data; + } + + public String getCommand() { + return String.valueOf((char)this.char1) + String.valueOf((char)this.char2); + } + + public void parse(IPacketParser parser) throws IOException { + this.setFrameId(parser.read("AT Response Frame Id")); + this.setChar1(parser.read("AT Response Char 1")); + this.setChar2(parser.read("AT Response Char 2")); + this.setStatus(Status.get(parser.read("AT Response Status"))); + + this.setValue(parser.readRemainingBytes()); + } + + public String toString() { + return "command=" + this.getCommand() + + ",status=" + this.getStatus() + ",value=" + + (this.value == null ? "null" : ByteUtils.toBase16(this.getValue())) + + "," + + super.toString(); + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/ErrorResponse.java b/src/main/java/com/rapplogic/xbee/api/responses/ErrorResponse.java similarity index 87% rename from src/com/rapplogic/xbee/api/ErrorResponse.java rename to src/main/java/com/rapplogic/xbee/api/responses/ErrorResponse.java index c36fd8d..ef38514 100644 --- a/src/com/rapplogic/xbee/api/ErrorResponse.java +++ b/src/main/java/com/rapplogic/xbee/api/responses/ErrorResponse.java @@ -1,69 +1,75 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -/** - * Represents a Java error during packet parsing. - * This is the only class that extends XBeeResponse and does not map - * to a XBee API ID - *

- * @author andrew - * - */ -public class ErrorResponse extends XBeeResponse { - - private String errorMsg; - private Exception exception; - - public ErrorResponse() { - super(); - this.setApiId(ApiId.ERROR_RESPONSE); - this.setError(true); - } - - /** - * A bit redundant in that it is the same as getException.getMessage() - * - * @return - */ - public String getErrorMsg() { - return errorMsg; - } - - public void setErrorMsg(String errorMsg) { - this.errorMsg = errorMsg; - } - - public Exception getException() { - return exception; - } - - public void setException(Exception exception) { - this.exception = exception; - } - - public void parse(IPacketParser parser) { - // nothing to do - } - - public String toString() { - return super.toString() + ",errorMsg=" + this.errorMsg + ",exception=" + this.exception; - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.responses; + +import com.rapplogic.xbee.api.ApiId; +import com.rapplogic.xbee.api.IPacketParser; +import com.rapplogic.xbee.api.XBeeResponse; + +/** + * Represents a Java error during packet parsing. + * This is the only class that extends XBeeResponse and does not map + * to a XBee API ID + *

+ * @author andrew + * + */ +public class ErrorResponse extends XBeeResponse { + + private static final long serialVersionUID = 8103651351769538022L; + + private String errorMsg; + private Exception exception; + + public ErrorResponse() { + super(); + this.setApiId(ApiId.ERROR_RESPONSE); + this.setError(true); + } + + /** + * A bit redundant in that it is the same as getException.getMessage() + * + * @return + */ + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + + public Exception getException() { + return exception; + } + + public void setException(Exception exception) { + this.exception = exception; + } + + public void parse(IPacketParser parser) { + // nothing to do + } + + public String toString() { + return super.toString() + ",errorMsg=" + this.errorMsg + ",exception=" + this.exception; + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/GenericResponse.java b/src/main/java/com/rapplogic/xbee/api/responses/GenericResponse.java similarity index 87% rename from src/com/rapplogic/xbee/api/GenericResponse.java rename to src/main/java/com/rapplogic/xbee/api/responses/GenericResponse.java index c04cc37..33b8201 100644 --- a/src/com/rapplogic/xbee/api/GenericResponse.java +++ b/src/main/java/com/rapplogic/xbee/api/responses/GenericResponse.java @@ -1,52 +1,57 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -import java.io.IOException; - -/** - * Container for unknown response - *

- * @author andrew - * - */ -public class GenericResponse extends XBeeResponse { - - private int genericApiId; - - public GenericResponse() { - - } - - public int getGenericApiId() { - return genericApiId; - } - - public void setGenericApiId(int genericApiId) { - this.genericApiId = genericApiId; - } - - public void parse(IPacketParser parser) throws IOException { - //eat packet bytes -- they will be save to bytearray and stored in response - parser.readRemainingBytes(); - // TODO gotta save it because it isn't know to the enum apiId won't - this.setGenericApiId(parser.getIntApiId()); - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.responses; + +import java.io.IOException; + +import com.rapplogic.xbee.api.IPacketParser; +import com.rapplogic.xbee.api.XBeeResponse; + +/** + * Container for unknown response + *

+ * @author andrew + * + */ +public class GenericResponse extends XBeeResponse { + + private static final long serialVersionUID = -4460804659208803948L; + + private int genericApiId; + + public GenericResponse() { + + } + + public int getGenericApiId() { + return genericApiId; + } + + public void setGenericApiId(int genericApiId) { + this.genericApiId = genericApiId; + } + + public void parse(IPacketParser parser) throws IOException { + //eat packet bytes -- they will be save to bytearray and stored in response + parser.readRemainingBytes(); + // TODO gotta save it because it isn't know to the enum apiId won't + this.setGenericApiId(parser.getIntApiId()); + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/ModemStatusResponse.java b/src/main/java/com/rapplogic/xbee/api/responses/ModemStatusResponse.java similarity index 92% rename from src/com/rapplogic/xbee/api/ModemStatusResponse.java rename to src/main/java/com/rapplogic/xbee/api/responses/ModemStatusResponse.java index f885d8b..ec68116 100644 --- a/src/com/rapplogic/xbee/api/ModemStatusResponse.java +++ b/src/main/java/com/rapplogic/xbee/api/responses/ModemStatusResponse.java @@ -1,113 +1,118 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -import java.io.IOException; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; - -import org.apache.log4j.Logger; - -/** - * RF module status messages are sent from the module in response to specific conditions. - *

- * API ID: 0x8a - *

- * @author andrew - * - */ -public class ModemStatusResponse extends XBeeResponse implements NoRequestResponse { - - private final static Logger log = Logger.getLogger(ModemStatusResponse.class); - - public enum Status { - HARDWARE_RESET (0), - WATCHDOG_TIMER_RESET (1), - ASSOCIATED (2), - DISASSOCIATED (3), - SYNCHRONIZATION_LOST (4), - COORDINATOR_REALIGNMENT (5), - COORDINATOR_STARTED (6), - NETWORK_SEC_KEY_UPDATED (7), - VOLTAGE_SUPPLY_EXCEEDED (0x0D), - MODEM_CONFIG_CHANGED (0x11), - STACK_ERROR (0x80), - UNKNOWN(-1); - - - private static final Map lookup = new HashMap(); - - static { - for(Status s : EnumSet.allOf(Status.class)) { - lookup.put(s.getValue(), s); - } - } - - public static Status get(int value) { - return lookup.get(value); - } - - private final int value; - - Status(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - } - - private Status status; - - public ModemStatusResponse() { - - } - - public Status getStatus() { - return status; - } - - public void setStatus(Status status) { - this.status = status; - } - - protected void parse(IPacketParser parser) throws IOException { - int value = parser.read("Modem Status"); - - if (value >= 0x80) { - this.setStatus(ModemStatusResponse.Status.STACK_ERROR); - } else { - ModemStatusResponse.Status status = ModemStatusResponse.Status.get(value); - - if (status == null) { - log.warn("Unknown status " + value); - this.setStatus(ModemStatusResponse.Status.UNKNOWN); - } else { - this.setStatus(status); - } - } - } - - public String toString() { - return super.toString() + ",status=" + this.status; - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.responses; + +import java.io.IOException; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.Logger; + +import com.rapplogic.xbee.api.IPacketParser; +import com.rapplogic.xbee.api.XBeeResponse; + +/** + * RF module status messages are sent from the module in response to specific conditions. + *

+ * API ID: 0x8a + *

+ * @author andrew + * + */ +public class ModemStatusResponse extends XBeeResponse implements NoRequestResponse { + + private static final long serialVersionUID = 1410454529315058759L; + + private final static Logger log = Logger.getLogger(ModemStatusResponse.class); + + public enum Status { + HARDWARE_RESET (0), + WATCHDOG_TIMER_RESET (1), + ASSOCIATED (2), + DISASSOCIATED (3), + SYNCHRONIZATION_LOST (4), + COORDINATOR_REALIGNMENT (5), + COORDINATOR_STARTED (6), + NETWORK_SEC_KEY_UPDATED (7), + VOLTAGE_SUPPLY_EXCEEDED (0x0D), + MODEM_CONFIG_CHANGED (0x11), + STACK_ERROR (0x80), + UNKNOWN(-1); + + + private static final Map lookup = new HashMap(); + + static { + for(Status s : EnumSet.allOf(Status.class)) { + lookup.put(s.getValue(), s); + } + } + + public static Status get(int value) { + return lookup.get(value); + } + + private final int value; + + Status(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + private Status status; + + public ModemStatusResponse() { + + } + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + + protected void parse(IPacketParser parser) throws IOException { + int value = parser.read("Modem Status"); + + if (value >= 0x80) { + this.setStatus(ModemStatusResponse.Status.STACK_ERROR); + } else { + ModemStatusResponse.Status status = ModemStatusResponse.Status.get(value); + + if (status == null) { + log.warn("Unknown status " + value); + this.setStatus(ModemStatusResponse.Status.UNKNOWN); + } else { + this.setStatus(status); + } + } + } + + public String toString() { + return super.toString() + ",status=" + this.status; + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/NoRequestResponse.java b/src/main/java/com/rapplogic/xbee/api/responses/NoRequestResponse.java similarity index 84% rename from src/com/rapplogic/xbee/api/NoRequestResponse.java rename to src/main/java/com/rapplogic/xbee/api/responses/NoRequestResponse.java index aea7004..21486ae 100644 --- a/src/com/rapplogic/xbee/api/NoRequestResponse.java +++ b/src/main/java/com/rapplogic/xbee/api/responses/NoRequestResponse.java @@ -1,4 +1,4 @@ -package com.rapplogic.xbee.api; +package com.rapplogic.xbee.api.responses; /** * This filter is used to capture only RX packets that are not a response to a TX packet. diff --git a/src/com/rapplogic/xbee/api/RemoteAtResponse.java b/src/main/java/com/rapplogic/xbee/api/responses/RemoteAtResponse.java similarity index 94% rename from src/com/rapplogic/xbee/api/RemoteAtResponse.java rename to src/main/java/com/rapplogic/xbee/api/responses/RemoteAtResponse.java index 8421311..6b9fade 100644 --- a/src/com/rapplogic/xbee/api/RemoteAtResponse.java +++ b/src/main/java/com/rapplogic/xbee/api/responses/RemoteAtResponse.java @@ -17,10 +17,14 @@ * along with XBee-API. If not, see . */ -package com.rapplogic.xbee.api; +package com.rapplogic.xbee.api.responses; import java.io.IOException; +import com.rapplogic.xbee.api.IPacketParser; +import com.rapplogic.xbee.api.XBeeAddress16; +import com.rapplogic.xbee.api.XBeeAddress64; + //TODO Now supported by series 1 XBee. parseIoSample now needs to handle series 1 and 2 diff --git a/src/com/rapplogic/xbee/api/XBeeFrameIdResponse.java b/src/main/java/com/rapplogic/xbee/api/responses/XBeeFrameIdResponse.java similarity index 88% rename from src/com/rapplogic/xbee/api/XBeeFrameIdResponse.java rename to src/main/java/com/rapplogic/xbee/api/responses/XBeeFrameIdResponse.java index 4a30b03..48ec3e4 100644 --- a/src/com/rapplogic/xbee/api/XBeeFrameIdResponse.java +++ b/src/main/java/com/rapplogic/xbee/api/responses/XBeeFrameIdResponse.java @@ -1,45 +1,48 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api; - -import com.rapplogic.xbee.util.ByteUtils; - -/** - * Represents all XBee responses that contain a frame id - *

- * @author andrew - * - */ -public abstract class XBeeFrameIdResponse extends XBeeResponse { - - private int frameId; - - public int getFrameId() { - return frameId; - } - - public void setFrameId(int frameId) { - this.frameId = frameId; - } - - public String toString() { - return super.toString() + ",frameId=" + ByteUtils.toBase16(this.frameId); - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.responses; + +import com.rapplogic.xbee.api.XBeeResponse; +import com.rapplogic.xbee.util.ByteUtils; + +/** + * Represents all XBee responses that contain a frame id + *

+ * @author andrew + * + */ +public abstract class XBeeFrameIdResponse extends XBeeResponse { + + private static final long serialVersionUID = -1719082648663672811L; + + private int frameId; + + public int getFrameId() { + return frameId; + } + + public void setFrameId(int frameId) { + this.frameId = frameId; + } + + public String toString() { + return super.toString() + ",frameId=" + ByteUtils.toBase16(this.frameId); + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/wpan/IoSample.java b/src/main/java/com/rapplogic/xbee/api/wpan/IoSample.java similarity index 96% rename from src/com/rapplogic/xbee/api/wpan/IoSample.java rename to src/main/java/com/rapplogic/xbee/api/wpan/IoSample.java index 337f8e5..3ebfe01 100644 --- a/src/com/rapplogic/xbee/api/wpan/IoSample.java +++ b/src/main/java/com/rapplogic/xbee/api/wpan/IoSample.java @@ -1,288 +1,288 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.wpan; - -import com.rapplogic.xbee.util.ByteUtils; - -/** - * Series 1 XBee. Represents an I/O Sample, sent from a remote radio. - * Each I/O packet (RxResponseIoSample) may contain one for more IoSample instances. - *

- * This class is accessed from the getSamples() method of RxResponseIoSample, which - * returns an array of IoSample objects. - *

- * Provides access to XBee's 8 Digital (0-7) and 6 Analog (0-5) IO pins - *

- * @author andrew - * - */ -public class IoSample { - - private RxResponseIoSample parent; - - private Integer dioMsb; - private Integer dioLsb; - private Integer[] analog = new Integer[6]; - - public IoSample(RxResponseIoSample parent) { - this.parent = parent; - } - - public void setDioMsb(Integer dioMsb) { - this.dioMsb = dioMsb; - } - - public void setDioLsb(Integer dioLsb) { - this.dioLsb = dioLsb; - } - - public Integer getDioMsb() { - return dioMsb; - } - - public Integer getDioLsb() { - return dioLsb; - } - - /** - * Returns the 10-bit analog value of the specified pin. - * Returns null if pin is not configured for Analog input. - * - * @return - */ - public Integer getAnalog(int pin) { - if (parent.isAnalogEnabled(pin)) { - return analog[pin]; - } - - return null; - } - - public Integer getAnalog0() { - return this.getAnalog(0); - } - - public void setAnalog0(Integer analog0) { - analog[0] = analog0; - } - - /** - * Returns the 10-bit analog value of pin 19 (D1), when this pin configured for Analog Input (D1=2) - * Returns null if pin 19 is not configured for Analog input. - * - * @return - */ - public Integer getAnalog1() { - return this.getAnalog(1); - } - - public void setAnalog1(Integer analog1) { - analog[1] = analog1; - } - - /** - * Returns the 10-bit analog value of pin 18 (D2), when this pin configured for Analog Input (D2=2) - * Returns null if pin 18 is not configured for Analog input. - * - * @return - */ - public Integer getAnalog2() { - return this.getAnalog(2); - } - - public void setAnalog2(Integer analog2) { - analog[2] = analog2; - } - - /** - * Returns the 10-bit analog value of pin 17 (D3), when this pin configured for Analog Input (D3=2) - * Returns null if pin 17 is not configured for Analog input. - * - * @return - */ - public Integer getAnalog3() { - return this.getAnalog(3); - } - - public void setAnalog3(Integer analog3) { - analog[3] = analog3; - } - - /** - * Returns the 10-bit analog value of pin 11 (D4), when this pin configured for Analog Input (D4=2) - * Returns null if pin 11 is not configured for Analog input. - * - * @return - */ - public Integer getAnalog4() { - return this.getAnalog(4); - } - - public void setAnalog4(Integer analog4) { - analog[4] = analog4; - } - - /** - * Returns the 10-bit analog value of pin 15 (D5), when this pin configured for Analog Input (D5=2) - * Returns null if pin 15 is not configured for Analog input. - * - * @return - */ - public Integer getAnalog5() { - return this.getAnalog(5); - } - - public void setAnalog5(Integer analog5) { - analog[5] = analog5; - } - - /** - * Returns the digital value of the specified pin. - * Returns null if pin is not configured for Digital input - * - * @return - */ - public Boolean isDigitalOn(int pin) { - - if (!parent.isDigitalEnabled(pin)) { - return null; - } - - if (pin >= 0 && pin <= 7) { - return ByteUtils.getBit(dioLsb, pin + 1); - } else { - // pin 8 - return ByteUtils.getBit(dioMsb, 1); - } - } - - /** - * Returns the digital value of pin 20 (D0) when this pin is configured for Digital input (D0=3) - * Returns null if pin 20 is not configured for Digital input - * - * @return - */ - public Boolean isD0On() { - return this.isDigitalOn(0); - } - - /** - * Returns the digital value of pin 19 (D1) when this pin is configured for Digital input (D1=3) - * Returns null if pin 19 is not configured for Digital input - * - * @return - */ - public Boolean isD1On() { - return this.isDigitalOn(1); - } - - /** - * Returns the digital value of pin 18 (D2) when this pin is configured for Digital input (D2=3) - * Returns null if pin 18 is not configured for Digital input - * - * @return - */ - public Boolean isD2On() { - return this.isDigitalOn(2); - } - - /** - * Returns the digital value of pin 17 (D3) when this pin is configured for Digital input (D3=3) - * Returns null if pin 17 is not configured for Digital input - * - * @return - */ - public Boolean isD3On() { - return this.isDigitalOn(3); } - - /** - * Returns the digital value of pin 11 (D4) when this pin is configured for Digital input (D4=3) - * Returns null if pin 11 is not configured for Digital input - * - * @return - */ - public Boolean isD4On() { - return this.isDigitalOn(4); - } - - /** - * Returns the digital value of pin 15 (D5) when this pin is configured for Digital input (D5=3) - * Returns null if pin 15 is not configured for Digital input - * - * @return - */ - public Boolean isD5On() { - return this.isDigitalOn(5); - } - - /** - * Returns the digital value of pin 16 (D6) when this pin is configured for Digital input (D6=3) - * Returns null if pin 16 is not configured for Digital input - * - * @return - */ - public Boolean isD6On() { - return this.isDigitalOn(6); - } - - /** - * Returns the digital value of pin 12 (D7) when this pin is configured for Digital input (D7=3) - * Returns null if pin 12 is not configured for Digital input - * - * @return - */ - public Boolean isD7On() { - return this.isDigitalOn(7); - } - - /** - * Returns the digital value of pin 9 (D8) when this pin is configured for Digital input (D8=3) - * Returns null if pin 9 is not configured for Digital input - * - * @return - */ - public Boolean isD8On() { - return this.isDigitalOn(8); - } - - - public String toString() { - StringBuilder builder = new StringBuilder(); - // TODO only prefix with comma if not first entry written. Use reflection - - if (parent.containsDigital()) { - for (int i = 0; i <= 8; i++) { - if (parent.isDigitalEnabled(i)) { - builder.append(",digital[" + i + "]=" + (this.isDigitalOn(i) ? "high" : "low")); - } - } - } - - if (parent.containsAnalog()) { - for (int i = 0; i <= 5; i++) { - if (parent.isAnalogEnabled(i)) { - builder.append(",analog[" + i + "]=" + this.getAnalog(i)); - } - } - } - - return builder.toString(); - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.wpan; + +import com.rapplogic.xbee.util.ByteUtils; + +/** + * Series 1 XBee. Represents an I/O Sample, sent from a remote radio. + * Each I/O packet (RxResponseIoSample) may contain one for more IoSample instances. + *

+ * This class is accessed from the getSamples() method of RxResponseIoSample, which + * returns an array of IoSample objects. + *

+ * Provides access to XBee's 8 Digital (0-7) and 6 Analog (0-5) IO pins + *

+ * @author andrew + * + */ +public class IoSample { + + private RxResponseIoSample parent; + + private Integer dioMsb; + private Integer dioLsb; + private Integer[] analog = new Integer[6]; + + public IoSample(RxResponseIoSample parent) { + this.parent = parent; + } + + public void setDioMsb(Integer dioMsb) { + this.dioMsb = dioMsb; + } + + public void setDioLsb(Integer dioLsb) { + this.dioLsb = dioLsb; + } + + public Integer getDioMsb() { + return dioMsb; + } + + public Integer getDioLsb() { + return dioLsb; + } + + /** + * Returns the 10-bit analog value of the specified pin. + * Returns null if pin is not configured for Analog input. + * + * @return + */ + public Integer getAnalog(int pin) { + if (parent.isAnalogEnabled(pin)) { + return analog[pin]; + } + + return null; + } + + public Integer getAnalog0() { + return this.getAnalog(0); + } + + public void setAnalog0(Integer analog0) { + analog[0] = analog0; + } + + /** + * Returns the 10-bit analog value of pin 19 (D1), when this pin configured for Analog Input (D1=2) + * Returns null if pin 19 is not configured for Analog input. + * + * @return + */ + public Integer getAnalog1() { + return this.getAnalog(1); + } + + public void setAnalog1(Integer analog1) { + analog[1] = analog1; + } + + /** + * Returns the 10-bit analog value of pin 18 (D2), when this pin configured for Analog Input (D2=2) + * Returns null if pin 18 is not configured for Analog input. + * + * @return + */ + public Integer getAnalog2() { + return this.getAnalog(2); + } + + public void setAnalog2(Integer analog2) { + analog[2] = analog2; + } + + /** + * Returns the 10-bit analog value of pin 17 (D3), when this pin configured for Analog Input (D3=2) + * Returns null if pin 17 is not configured for Analog input. + * + * @return + */ + public Integer getAnalog3() { + return this.getAnalog(3); + } + + public void setAnalog3(Integer analog3) { + analog[3] = analog3; + } + + /** + * Returns the 10-bit analog value of pin 11 (D4), when this pin configured for Analog Input (D4=2) + * Returns null if pin 11 is not configured for Analog input. + * + * @return + */ + public Integer getAnalog4() { + return this.getAnalog(4); + } + + public void setAnalog4(Integer analog4) { + analog[4] = analog4; + } + + /** + * Returns the 10-bit analog value of pin 15 (D5), when this pin configured for Analog Input (D5=2) + * Returns null if pin 15 is not configured for Analog input. + * + * @return + */ + public Integer getAnalog5() { + return this.getAnalog(5); + } + + public void setAnalog5(Integer analog5) { + analog[5] = analog5; + } + + /** + * Returns the digital value of the specified pin. + * Returns null if pin is not configured for Digital input + * + * @return + */ + public Boolean isDigitalOn(int pin) { + + if (!parent.isDigitalEnabled(pin)) { + return null; + } + + if (pin >= 0 && pin <= 7) { + return ByteUtils.getBit(dioLsb, pin + 1); + } else { + // pin 8 + return ByteUtils.getBit(dioMsb, 1); + } + } + + /** + * Returns the digital value of pin 20 (D0) when this pin is configured for Digital input (D0=3) + * Returns null if pin 20 is not configured for Digital input + * + * @return + */ + public Boolean isD0On() { + return this.isDigitalOn(0); + } + + /** + * Returns the digital value of pin 19 (D1) when this pin is configured for Digital input (D1=3) + * Returns null if pin 19 is not configured for Digital input + * + * @return + */ + public Boolean isD1On() { + return this.isDigitalOn(1); + } + + /** + * Returns the digital value of pin 18 (D2) when this pin is configured for Digital input (D2=3) + * Returns null if pin 18 is not configured for Digital input + * + * @return + */ + public Boolean isD2On() { + return this.isDigitalOn(2); + } + + /** + * Returns the digital value of pin 17 (D3) when this pin is configured for Digital input (D3=3) + * Returns null if pin 17 is not configured for Digital input + * + * @return + */ + public Boolean isD3On() { + return this.isDigitalOn(3); } + + /** + * Returns the digital value of pin 11 (D4) when this pin is configured for Digital input (D4=3) + * Returns null if pin 11 is not configured for Digital input + * + * @return + */ + public Boolean isD4On() { + return this.isDigitalOn(4); + } + + /** + * Returns the digital value of pin 15 (D5) when this pin is configured for Digital input (D5=3) + * Returns null if pin 15 is not configured for Digital input + * + * @return + */ + public Boolean isD5On() { + return this.isDigitalOn(5); + } + + /** + * Returns the digital value of pin 16 (D6) when this pin is configured for Digital input (D6=3) + * Returns null if pin 16 is not configured for Digital input + * + * @return + */ + public Boolean isD6On() { + return this.isDigitalOn(6); + } + + /** + * Returns the digital value of pin 12 (D7) when this pin is configured for Digital input (D7=3) + * Returns null if pin 12 is not configured for Digital input + * + * @return + */ + public Boolean isD7On() { + return this.isDigitalOn(7); + } + + /** + * Returns the digital value of pin 9 (D8) when this pin is configured for Digital input (D8=3) + * Returns null if pin 9 is not configured for Digital input + * + * @return + */ + public Boolean isD8On() { + return this.isDigitalOn(8); + } + + + public String toString() { + StringBuilder builder = new StringBuilder(); + // TODO only prefix with comma if not first entry written. Use reflection + + if (parent.containsDigital()) { + for (int i = 0; i <= 8; i++) { + if (parent.isDigitalEnabled(i)) { + builder.append(",digital[" + i + "]=" + (this.isDigitalOn(i) ? "high" : "low")); + } + } + } + + if (parent.containsAnalog()) { + for (int i = 0; i <= 5; i++) { + if (parent.isAnalogEnabled(i)) { + builder.append(",analog[" + i + "]=" + this.getAnalog(i)); + } + } + } + + return builder.toString(); + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/wpan/RxBaseResponse.java b/src/main/java/com/rapplogic/xbee/api/wpan/RxBaseResponse.java similarity index 94% rename from src/com/rapplogic/xbee/api/wpan/RxBaseResponse.java rename to src/main/java/com/rapplogic/xbee/api/wpan/RxBaseResponse.java index 1a20d8e..0706943 100644 --- a/src/com/rapplogic/xbee/api/wpan/RxBaseResponse.java +++ b/src/main/java/com/rapplogic/xbee/api/wpan/RxBaseResponse.java @@ -1,100 +1,101 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.wpan; - -import java.io.IOException; - -import com.rapplogic.xbee.api.IPacketParser; -import com.rapplogic.xbee.api.XBeeAddress; -import com.rapplogic.xbee.api.XBeeResponse; -import com.rapplogic.xbee.util.ByteUtils; -import com.rapplogic.xbee.util.IIntInputStream; - -/** - * Series 1 XBee. Common elements of 16 and 64 bit Address Receive packets - *

- * @author andrew - * - */ -public abstract class RxBaseResponse extends XBeeResponse { - - private XBeeAddress sourceAddress; - - private int rssi; - private int options; - - public RxBaseResponse() { - - } - - public int getRssi() { - return rssi; - } - - public void setRssi(int rssi) { - this.rssi = rssi; - } - - public int getOptions() { - return options; - } - - public void setOptions(int options) { - this.options = options; - } - - public boolean isAddressBroadcast() { - return ByteUtils.getBit(options, 2); - } - - public boolean isPanBroadcast() { - return ByteUtils.getBit(options, 3); - } - - /** - * Returns either a XBeeAddress16 or XBeeAddress64 - * depending on if the packet is configured for 16 or 64 bit addressing. - * - * @return - */ - public XBeeAddress getSourceAddress() { - return sourceAddress; - } - - public void setSourceAddress(XBeeAddress sourceAddress) { - this.sourceAddress = sourceAddress; - } - - protected void parseBase(IPacketParser parser) throws IOException { - int rssi = parser.read("RSSI"); - - // rssi is a negative dbm value - this.setRssi(-rssi); - - int options = parser.read("Options"); - - this.setOptions(options); - } - - public String toString() { - return super.toString() + ",sourceAddress=" + this.getSourceAddress() + ",rssi=" + this.getRssi() + ",options=" + this.getOptions() + - ",isAddressBroadcast=" + this.isAddressBroadcast() + ",isPanBroadcast=" + this.isPanBroadcast(); - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.wpan; + +import java.io.IOException; + +import com.rapplogic.xbee.api.IPacketParser; +import com.rapplogic.xbee.api.XBeeAddress; +import com.rapplogic.xbee.api.XBeeResponse; +import com.rapplogic.xbee.util.ByteUtils; + +/** + * Series 1 XBee. Common elements of 16 and 64 bit Address Receive packets + *

+ * @author andrew + * + */ +public abstract class RxBaseResponse extends XBeeResponse { + + private static final long serialVersionUID = 4610077108257181555L; + + private XBeeAddress sourceAddress; + + private int rssi; + private int options; + + public RxBaseResponse() { + + } + + public int getRssi() { + return rssi; + } + + public void setRssi(int rssi) { + this.rssi = rssi; + } + + public int getOptions() { + return options; + } + + public void setOptions(int options) { + this.options = options; + } + + public boolean isAddressBroadcast() { + return ByteUtils.getBit(options, 2); + } + + public boolean isPanBroadcast() { + return ByteUtils.getBit(options, 3); + } + + /** + * Returns either a XBeeAddress16 or XBeeAddress64 + * depending on if the packet is configured for 16 or 64 bit addressing. + * + * @return + */ + public XBeeAddress getSourceAddress() { + return sourceAddress; + } + + public void setSourceAddress(XBeeAddress sourceAddress) { + this.sourceAddress = sourceAddress; + } + + protected void parseBase(IPacketParser parser) throws IOException { + int rssi = parser.read("RSSI"); + + // rssi is a negative dbm value + this.setRssi(-rssi); + + int options = parser.read("Options"); + + this.setOptions(options); + } + + public String toString() { + return super.toString() + ",sourceAddress=" + this.getSourceAddress() + ",rssi=" + this.getRssi() + ",options=" + this.getOptions() + + ",isAddressBroadcast=" + this.isAddressBroadcast() + ",isPanBroadcast=" + this.isPanBroadcast(); + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/wpan/RxResponse.java b/src/main/java/com/rapplogic/xbee/api/wpan/RxResponse.java similarity index 95% rename from src/com/rapplogic/xbee/api/wpan/RxResponse.java rename to src/main/java/com/rapplogic/xbee/api/wpan/RxResponse.java index f045c6f..0f67d15 100644 --- a/src/com/rapplogic/xbee/api/wpan/RxResponse.java +++ b/src/main/java/com/rapplogic/xbee/api/wpan/RxResponse.java @@ -1,60 +1,62 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.wpan; - -import java.io.IOException; - -import com.rapplogic.xbee.api.IPacketParser; -import com.rapplogic.xbee.util.ByteUtils; - -public class RxResponse extends RxBaseResponse { - - private int[] data; - - public RxResponse() { - - } - - public int[] getData() { - return data; - } - - public void setData(int[] data) { - this.data = data; - } - - public void parse(IPacketParser parser) throws IOException { - int[] payload = new int[parser.getLength().getLength() - parser.getFrameDataBytesRead()]; - - int bytesRead = parser.getFrameDataBytesRead(); - - for (int i = 0; i < parser.getLength().getLength() - bytesRead; i++) { - payload[i] = parser.read("Payload byte " + i); - //log.debug("rx data payload [" + i + "] " + payload[i]); - } - - this.setData(payload); - } - - public String toString() { - return super.toString() + - ",data=" + ByteUtils.toBase16(this.data); - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.wpan; + +import java.io.IOException; + +import com.rapplogic.xbee.api.IPacketParser; +import com.rapplogic.xbee.util.ByteUtils; + +public class RxResponse extends RxBaseResponse { + + private static final long serialVersionUID = -221176512562200634L; + + private int[] data; + + public RxResponse() { + + } + + public int[] getData() { + return data; + } + + public void setData(int[] data) { + this.data = data; + } + + public void parse(IPacketParser parser) throws IOException { + int[] payload = new int[parser.getLength().getLength() - parser.getFrameDataBytesRead()]; + + int bytesRead = parser.getFrameDataBytesRead(); + + for (int i = 0; i < parser.getLength().getLength() - bytesRead; i++) { + payload[i] = parser.read("Payload byte " + i); + //log.debug("rx data payload [" + i + "] " + payload[i]); + } + + this.setData(payload); + } + + public String toString() { + return super.toString() + + ",data=" + ByteUtils.toBase16(this.data); + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/wpan/RxResponse16.java b/src/main/java/com/rapplogic/xbee/api/wpan/RxResponse16.java similarity index 91% rename from src/com/rapplogic/xbee/api/wpan/RxResponse16.java rename to src/main/java/com/rapplogic/xbee/api/wpan/RxResponse16.java index b8f6c3a..79bc04c 100644 --- a/src/com/rapplogic/xbee/api/wpan/RxResponse16.java +++ b/src/main/java/com/rapplogic/xbee/api/wpan/RxResponse16.java @@ -1,50 +1,51 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.wpan; - -import java.io.IOException; - -import com.rapplogic.xbee.api.ApiId; -import com.rapplogic.xbee.api.IPacketParser; -import com.rapplogic.xbee.api.NoRequestResponse; -import com.rapplogic.xbee.api.XBeeAddress16; - -/** - * Series 1 XBee. 16-bit address Receive packet. - * This packet is received when a remote radio transmits a TxRequest16 - * packet to this radio's MY address - *

- * API ID: 0x81 - * - * @author andrew - * - */ -public class RxResponse16 extends RxResponse implements NoRequestResponse { - - public XBeeAddress16 getRemoteAddress() { - return (XBeeAddress16) this.getSourceAddress(); - } - - public void parse(IPacketParser parser) throws IOException { - this.setSourceAddress(parser.parseAddress16()); - super.parseBase(parser); - super.parse(parser); - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.wpan; + +import java.io.IOException; + +import com.rapplogic.xbee.api.IPacketParser; +import com.rapplogic.xbee.api.XBeeAddress16; +import com.rapplogic.xbee.api.responses.NoRequestResponse; + +/** + * Series 1 XBee. 16-bit address Receive packet. + * This packet is received when a remote radio transmits a TxRequest16 + * packet to this radio's MY address + *

+ * API ID: 0x81 + * + * @author andrew + * + */ +public class RxResponse16 extends RxResponse implements NoRequestResponse { + + private static final long serialVersionUID = 5083877725752608901L; + + public XBeeAddress16 getRemoteAddress() { + return (XBeeAddress16) this.getSourceAddress(); + } + + public void parse(IPacketParser parser) throws IOException { + this.setSourceAddress(parser.parseAddress16()); + super.parseBase(parser); + super.parse(parser); + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/wpan/RxResponse64.java b/src/main/java/com/rapplogic/xbee/api/wpan/RxResponse64.java similarity index 95% rename from src/com/rapplogic/xbee/api/wpan/RxResponse64.java rename to src/main/java/com/rapplogic/xbee/api/wpan/RxResponse64.java index cdfd581..b1f9c74 100644 --- a/src/com/rapplogic/xbee/api/wpan/RxResponse64.java +++ b/src/main/java/com/rapplogic/xbee/api/wpan/RxResponse64.java @@ -1,49 +1,51 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.wpan; - -import java.io.IOException; - -import com.rapplogic.xbee.api.IPacketParser; -import com.rapplogic.xbee.api.XBeeAddress64; - -/** - * Series 1 XBee. 64-bit address Receive packet. - * This packet is received when a remote radio transmits a TxRequest64 - * packet to this radio's SH + SL address. - *

- * Note: MY address must be set to 0xffff to receive this packet type. - *

- * API ID: 0x80 - * - * @author andrew - */ -public class RxResponse64 extends RxResponse { - - public XBeeAddress64 getRemoteAddress() { - return (XBeeAddress64) this.getSourceAddress(); - } - - public void parse(IPacketParser parser) throws IOException { - this.setSourceAddress(parser.parseAddress64()); - super.parseBase(parser); - super.parse(parser); - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.wpan; + +import java.io.IOException; + +import com.rapplogic.xbee.api.IPacketParser; +import com.rapplogic.xbee.api.XBeeAddress64; + +/** + * Series 1 XBee. 64-bit address Receive packet. + * This packet is received when a remote radio transmits a TxRequest64 + * packet to this radio's SH + SL address. + *

+ * Note: MY address must be set to 0xffff to receive this packet type. + *

+ * API ID: 0x80 + * + * @author andrew + */ +public class RxResponse64 extends RxResponse { + + private static final long serialVersionUID = 5482472190377241887L; + + public XBeeAddress64 getRemoteAddress() { + return (XBeeAddress64) this.getSourceAddress(); + } + + public void parse(IPacketParser parser) throws IOException { + this.setSourceAddress(parser.parseAddress64()); + super.parseBase(parser); + super.parse(parser); + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/wpan/RxResponseIoSample.java b/src/main/java/com/rapplogic/xbee/api/wpan/RxResponseIoSample.java similarity index 95% rename from src/com/rapplogic/xbee/api/wpan/RxResponseIoSample.java rename to src/main/java/com/rapplogic/xbee/api/wpan/RxResponseIoSample.java index 01d0f57..ccb3cfb 100644 --- a/src/com/rapplogic/xbee/api/wpan/RxResponseIoSample.java +++ b/src/main/java/com/rapplogic/xbee/api/wpan/RxResponseIoSample.java @@ -1,296 +1,298 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.wpan; - -import java.io.IOException; - -import org.apache.log4j.Logger; - -import com.rapplogic.xbee.api.ApiId; -import com.rapplogic.xbee.api.IPacketParser; -import com.rapplogic.xbee.api.NoRequestResponse; -import com.rapplogic.xbee.util.ByteUtils; -import com.rapplogic.xbee.util.IIntInputStream; - -/** - * Series 1 XBee. Represents an I/O sample - *

- * See http://code.google.com/p/xbee-api/wiki/XBeePins for information on configuring - * the XBee for digital/analog inputs. - *

- * API ID: 64-bit 0x82, 16-bit: 0x83 - *

- * @author andrew - * - */ -public class RxResponseIoSample extends RxBaseResponse implements NoRequestResponse { - - private final static Logger log = Logger.getLogger(RxResponseIoSample.class); - - public final static int ADC_CHANNEL1 = 0x7e; //01111110 - public final static int DIO_CHANNEL1 = 0x1; //00000001 - - private IoSample[] samples; - - private int channelIndicator1; - private int channelIndicator2; - - public RxResponseIoSample() { - - } - - public void parse(IPacketParser parser) throws IOException { - - if (parser.getApiId() == ApiId.RX_16_IO_RESPONSE) { - this.setSourceAddress(parser.parseAddress16()); - } else { - this.setSourceAddress(parser.parseAddress64()); - } - - super.parseBase(parser); - - log.debug("this is a I/O sample!"); - // first byte is # of samples - int sampleSize = parser.read("# I/O Samples"); - - // create i/o samples array - this.setSamples(new IoSample[sampleSize]); - - // channel indicator 1 - this.setChannelIndicator1(parser.read("Channel Indicator 1")); - - log.debug("channel indicator 1 is " + ByteUtils.formatByte(this.getChannelIndicator1())); - - // channel indicator 2 (dio) - this.setChannelIndicator2(parser.read("Channel Indicator 2")); - - log.debug("channel indicator 2 is " + ByteUtils.formatByte(this.getChannelIndicator2())); - - // collect each sample - for (int i = 0; i < this.getSamples().length; i++) { - - log.debug("parsing sample " + (i + 1)); - - IoSample sample = parseIoSample((IIntInputStream)parser); - - // attach sample to parent - this.getSamples()[i] = sample; - } - } - - private IoSample parseIoSample(IIntInputStream parser) throws IOException { - - IoSample sample = new IoSample(this); - - // DIO 8 occupies the first bit of the adcHeader - if (this.containsDigital()) { - // at least one DIO line is active - // next two bytes are DIO - - log.debug("Digital I/O was received"); - - sample.setDioMsb(parser.read("DIO MSB")); - sample.setDioLsb(parser.read("DIO LSB")); - } - - // ADC is active if any of bits 2-7 are on - if (this.containsAnalog()) { - // adc is active - // adc is 10 bits - - log.debug("Analog input was received"); - - // 10-bit values are read two bytes per sample - - int analog = 0; - - // Analog inputs A0-A5 are bits 2-7 of the adcHeader - - if (this.isA0Enabled()) { - sample.setAnalog0(ByteUtils.parse10BitAnalog(parser, analog)); - analog++; - } - - if (this.isA1Enabled()) { - sample.setAnalog1(ByteUtils.parse10BitAnalog(parser, analog)); - analog++; - } - - if (this.isA2Enabled()) { - sample.setAnalog2(ByteUtils.parse10BitAnalog(parser, analog)); - analog++; - } - - if (this.isA3Enabled()) { - sample.setAnalog3(ByteUtils.parse10BitAnalog(parser, analog)); - analog++; - } - - if (this.isA4Enabled()) { - sample.setAnalog4(ByteUtils.parse10BitAnalog(parser, analog)); - analog++; - } - - if (this.isA5Enabled()) { - sample.setAnalog5(ByteUtils.parse10BitAnalog(parser, analog)); - analog++; - } - - log.debug("There are " + analog + " analog inputs turned on"); - } - - return sample; - } - - public IoSample[] getSamples() { - return samples; - } - - public void setSamples(IoSample[] samples) { - this.samples = samples; - } - - public boolean isDigitalEnabled(int pin) { - if (pin >= 0 && pin <= 7) { - return ByteUtils.getBit(channelIndicator2, pin + 1); - } else if (pin == 8) { - return ByteUtils.getBit(channelIndicator1, 1); - } else { - throw new IllegalArgumentException("Unsupported pin: " + pin); - } - } - - public boolean isD0Enabled() { - return ByteUtils.getBit(channelIndicator2, 1); - } - - public boolean isD1Enabled() { - return ByteUtils.getBit(channelIndicator2, 2); - } - - public boolean isD2Enabled() { - return ByteUtils.getBit(channelIndicator2, 3); - } - - public boolean isD3Enabled() { - return ByteUtils.getBit(channelIndicator2, 4); - } - - public boolean isD4Enabled() { - return ByteUtils.getBit(channelIndicator2, 5); - } - - public boolean isD5Enabled() { - return ByteUtils.getBit(channelIndicator2, 6); - } - - public boolean isD6Enabled() { - return ByteUtils.getBit(channelIndicator2, 7); - } - - public boolean isD7Enabled() { - return ByteUtils.getBit(channelIndicator2, 8); - } - - public boolean isD8Enabled() { - return ByteUtils.getBit(channelIndicator1, 1); - } - - public boolean isAnalogEnabled(int pin) { - if (pin >= 0 && pin <= 5) { - return ByteUtils.getBit(channelIndicator1, pin + 2); - } else { - throw new IllegalArgumentException("Unsupported pin: " + pin); - } - } - - public boolean isA0Enabled() { - return ByteUtils.getBit(channelIndicator1, 2); - } - - public boolean isA1Enabled() { - return ByteUtils.getBit(channelIndicator1, 3); - } - - public boolean isA2Enabled() { - return ByteUtils.getBit(channelIndicator1, 4); - } - - public boolean isA3Enabled() { - return ByteUtils.getBit(channelIndicator1, 5); - } - - public boolean isA4Enabled() { - return ByteUtils.getBit(channelIndicator1, 6); - } - - public boolean isA5Enabled() { - return ByteUtils.getBit(channelIndicator1, 7); - } - - public int getChannelIndicator1() { - return channelIndicator1; - } - - public void setChannelIndicator1(int channelIndicator1) { - this.channelIndicator1 = channelIndicator1; - } - - public int getChannelIndicator2() { - return channelIndicator2; - } - - public void setChannelIndicator2(int channelIndicator2) { - this.channelIndicator2 = channelIndicator2; - } - - /** - * Return true if this packet contains at least one analog sample - */ - public boolean containsAnalog() { - // ADC is active if > 0 after channel mask is applied - return (this.channelIndicator1 & ADC_CHANNEL1) > 0; - } - - /** - * Returns true if this packet contains at least one digital sample - * - * @return - */ - public boolean containsDigital() { - // DIO 8 occupies the first bit of the adcHeader - return (this.channelIndicator1 & DIO_CHANNEL1) > 0 || this.channelIndicator2 > 0; - } - - public String toString() { - - StringBuilder sb = new StringBuilder(); - - sb.append(super.toString()); - - sb.append(",#samples=" + this.samples.length); - - for (int i = 0; i < samples.length; i++) { - sb.append(",Sample#" + (i + 1) + ":" + samples[i].toString() + "]"); - } - - return sb.toString(); - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.wpan; + +import java.io.IOException; + +import org.apache.log4j.Logger; + +import com.rapplogic.xbee.api.ApiId; +import com.rapplogic.xbee.api.IPacketParser; +import com.rapplogic.xbee.api.responses.NoRequestResponse; +import com.rapplogic.xbee.util.ByteUtils; +import com.rapplogic.xbee.util.IIntInputStream; + +/** + * Series 1 XBee. Represents an I/O sample + *

+ * See http://code.google.com/p/xbee-api/wiki/XBeePins for information on configuring + * the XBee for digital/analog inputs. + *

+ * API ID: 64-bit 0x82, 16-bit: 0x83 + *

+ * @author andrew + * + */ +public class RxResponseIoSample extends RxBaseResponse implements NoRequestResponse { + + private static final long serialVersionUID = 3818093630598995408L; + + private final static Logger log = Logger.getLogger(RxResponseIoSample.class); + + public final static int ADC_CHANNEL1 = 0x7e; //01111110 + public final static int DIO_CHANNEL1 = 0x1; //00000001 + + private IoSample[] samples; + + private int channelIndicator1; + private int channelIndicator2; + + public RxResponseIoSample() { + + } + + public void parse(IPacketParser parser) throws IOException { + + if (parser.getApiId() == ApiId.RX_16_IO_RESPONSE) { + this.setSourceAddress(parser.parseAddress16()); + } else { + this.setSourceAddress(parser.parseAddress64()); + } + + super.parseBase(parser); + + log.debug("this is a I/O sample!"); + // first byte is # of samples + int sampleSize = parser.read("# I/O Samples"); + + // create i/o samples array + this.setSamples(new IoSample[sampleSize]); + + // channel indicator 1 + this.setChannelIndicator1(parser.read("Channel Indicator 1")); + + log.debug("channel indicator 1 is " + ByteUtils.formatByte(this.getChannelIndicator1())); + + // channel indicator 2 (dio) + this.setChannelIndicator2(parser.read("Channel Indicator 2")); + + log.debug("channel indicator 2 is " + ByteUtils.formatByte(this.getChannelIndicator2())); + + // collect each sample + for (int i = 0; i < this.getSamples().length; i++) { + + log.debug("parsing sample " + (i + 1)); + + IoSample sample = parseIoSample((IIntInputStream)parser); + + // attach sample to parent + this.getSamples()[i] = sample; + } + } + + private IoSample parseIoSample(IIntInputStream parser) throws IOException { + + IoSample sample = new IoSample(this); + + // DIO 8 occupies the first bit of the adcHeader + if (this.containsDigital()) { + // at least one DIO line is active + // next two bytes are DIO + + log.debug("Digital I/O was received"); + + sample.setDioMsb(parser.read("DIO MSB")); + sample.setDioLsb(parser.read("DIO LSB")); + } + + // ADC is active if any of bits 2-7 are on + if (this.containsAnalog()) { + // adc is active + // adc is 10 bits + + log.debug("Analog input was received"); + + // 10-bit values are read two bytes per sample + + int analog = 0; + + // Analog inputs A0-A5 are bits 2-7 of the adcHeader + + if (this.isA0Enabled()) { + sample.setAnalog0(ByteUtils.parse10BitAnalog(parser, analog)); + analog++; + } + + if (this.isA1Enabled()) { + sample.setAnalog1(ByteUtils.parse10BitAnalog(parser, analog)); + analog++; + } + + if (this.isA2Enabled()) { + sample.setAnalog2(ByteUtils.parse10BitAnalog(parser, analog)); + analog++; + } + + if (this.isA3Enabled()) { + sample.setAnalog3(ByteUtils.parse10BitAnalog(parser, analog)); + analog++; + } + + if (this.isA4Enabled()) { + sample.setAnalog4(ByteUtils.parse10BitAnalog(parser, analog)); + analog++; + } + + if (this.isA5Enabled()) { + sample.setAnalog5(ByteUtils.parse10BitAnalog(parser, analog)); + analog++; + } + + log.debug("There are " + analog + " analog inputs turned on"); + } + + return sample; + } + + public IoSample[] getSamples() { + return samples; + } + + public void setSamples(IoSample[] samples) { + this.samples = samples; + } + + public boolean isDigitalEnabled(int pin) { + if (pin >= 0 && pin <= 7) { + return ByteUtils.getBit(channelIndicator2, pin + 1); + } else if (pin == 8) { + return ByteUtils.getBit(channelIndicator1, 1); + } else { + throw new IllegalArgumentException("Unsupported pin: " + pin); + } + } + + public boolean isD0Enabled() { + return ByteUtils.getBit(channelIndicator2, 1); + } + + public boolean isD1Enabled() { + return ByteUtils.getBit(channelIndicator2, 2); + } + + public boolean isD2Enabled() { + return ByteUtils.getBit(channelIndicator2, 3); + } + + public boolean isD3Enabled() { + return ByteUtils.getBit(channelIndicator2, 4); + } + + public boolean isD4Enabled() { + return ByteUtils.getBit(channelIndicator2, 5); + } + + public boolean isD5Enabled() { + return ByteUtils.getBit(channelIndicator2, 6); + } + + public boolean isD6Enabled() { + return ByteUtils.getBit(channelIndicator2, 7); + } + + public boolean isD7Enabled() { + return ByteUtils.getBit(channelIndicator2, 8); + } + + public boolean isD8Enabled() { + return ByteUtils.getBit(channelIndicator1, 1); + } + + public boolean isAnalogEnabled(int pin) { + if (pin >= 0 && pin <= 5) { + return ByteUtils.getBit(channelIndicator1, pin + 2); + } else { + throw new IllegalArgumentException("Unsupported pin: " + pin); + } + } + + public boolean isA0Enabled() { + return ByteUtils.getBit(channelIndicator1, 2); + } + + public boolean isA1Enabled() { + return ByteUtils.getBit(channelIndicator1, 3); + } + + public boolean isA2Enabled() { + return ByteUtils.getBit(channelIndicator1, 4); + } + + public boolean isA3Enabled() { + return ByteUtils.getBit(channelIndicator1, 5); + } + + public boolean isA4Enabled() { + return ByteUtils.getBit(channelIndicator1, 6); + } + + public boolean isA5Enabled() { + return ByteUtils.getBit(channelIndicator1, 7); + } + + public int getChannelIndicator1() { + return channelIndicator1; + } + + public void setChannelIndicator1(int channelIndicator1) { + this.channelIndicator1 = channelIndicator1; + } + + public int getChannelIndicator2() { + return channelIndicator2; + } + + public void setChannelIndicator2(int channelIndicator2) { + this.channelIndicator2 = channelIndicator2; + } + + /** + * Return true if this packet contains at least one analog sample + */ + public boolean containsAnalog() { + // ADC is active if > 0 after channel mask is applied + return (this.channelIndicator1 & ADC_CHANNEL1) > 0; + } + + /** + * Returns true if this packet contains at least one digital sample + * + * @return + */ + public boolean containsDigital() { + // DIO 8 occupies the first bit of the adcHeader + return (this.channelIndicator1 & DIO_CHANNEL1) > 0 || this.channelIndicator2 > 0; + } + + public String toString() { + + StringBuilder sb = new StringBuilder(); + + sb.append(super.toString()); + + sb.append(",#samples=" + this.samples.length); + + for (int i = 0; i < samples.length; i++) { + sb.append(",Sample#" + (i + 1) + ":" + samples[i].toString() + "]"); + } + + return sb.toString(); + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/wpan/TxRequest16.java b/src/main/java/com/rapplogic/xbee/api/wpan/TxRequest16.java similarity index 96% rename from src/com/rapplogic/xbee/api/wpan/TxRequest16.java rename to src/main/java/com/rapplogic/xbee/api/wpan/TxRequest16.java index df0291d..41c5db2 100644 --- a/src/com/rapplogic/xbee/api/wpan/TxRequest16.java +++ b/src/main/java/com/rapplogic/xbee/api/wpan/TxRequest16.java @@ -1,115 +1,117 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.wpan; - -import com.rapplogic.xbee.api.ApiId; -import com.rapplogic.xbee.api.XBeeAddress16; -import com.rapplogic.xbee.util.IntArrayOutputStream; - -/** - * Series 1 XBee. 16-bit address Transmit Packet. This is received on the destination XBee - * radio as a RxResponse16 response - *

- * API ID: 0x1 - *

- * @author andrew - * - */ -public class TxRequest16 extends TxRequestBase { - - private XBeeAddress16 remoteAddr16; - - /** - * 16 bit Tx Request with default frame id and awk option - * - * Keep in mind that if you programmed the destination address with X-CTU, the unit is - * hex, so if you set MY=1234, use 0x1234. - * - * @param remoteAddr16 - * @param payload - */ - public TxRequest16(XBeeAddress16 remoteAddr16, int[] payload) { - this(remoteAddr16, DEFAULT_FRAME_ID, Option.UNICAST, payload); - } - - /** - * 16 bit Tx Request with frame id argument - * - * - * Payload size is limited to 100 bytes, according to MaxStream documentation. - * - * @param remoteAddr16 - * @param frameId - * @param payload - */ - public TxRequest16(XBeeAddress16 remoteAddr16, int frameId, int[] payload) { - this(remoteAddr16, frameId, Option.UNICAST, payload); - } - - /** - * Note: if option is DISABLE_ACK_OPTION you will not get a ack response and you must use the asynchronous send method - * - * @param remoteAddr16 - * @param frameId - * @param payload - * @param option - */ - public TxRequest16(XBeeAddress16 remoteAddr16, int frameId, Option option, int[] payload) { - this.remoteAddr16 = remoteAddr16; - this.setFrameId(frameId); - this.setOption(option); - this.setPayload(payload); - } - - public int[] getFrameData() { - // 3/6/10 fixed bug -- broadcast address is used with broadcast option, not no ACK - - IntArrayOutputStream out = new IntArrayOutputStream(); - - // api id - out.write(this.getApiId().getValue()); - // frame id (arbitrary byte that will be sent back with ack) - out.write(this.getFrameId()); - // destination address (broadcast is 0xFFFF) - out.write(remoteAddr16.getAddress()); - // options byte disable ack = 1, send pan id = 4 - out.write(this.getOption().getValue()); - out.write(this.getPayload()); - - return out.getIntArray(); - } - - public ApiId getApiId() { - return ApiId.TX_REQUEST_16; - } - - public XBeeAddress16 getRemoteAddr16() { - return remoteAddr16; - } - - public void setRemoteAddr16(XBeeAddress16 remoteAddr16) { - this.remoteAddr16 = remoteAddr16; - } - - public String toString() { - return super.toString() + - ",remoteAddress16=" + this.remoteAddr16; - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.wpan; + +import com.rapplogic.xbee.api.ApiId; +import com.rapplogic.xbee.api.XBeeAddress16; +import com.rapplogic.xbee.util.IntArrayOutputStream; + +/** + * Series 1 XBee. 16-bit address Transmit Packet. This is received on the destination XBee + * radio as a RxResponse16 response + *

+ * API ID: 0x1 + *

+ * @author andrew + * + */ +public class TxRequest16 extends TxRequestBase { + + private static final long serialVersionUID = -9195390160474504099L; + + private XBeeAddress16 remoteAddr16; + + /** + * 16 bit Tx Request with default frame id and awk option + * + * Keep in mind that if you programmed the destination address with X-CTU, the unit is + * hex, so if you set MY=1234, use 0x1234. + * + * @param remoteAddr16 + * @param payload + */ + public TxRequest16(XBeeAddress16 remoteAddr16, int[] payload) { + this(remoteAddr16, DEFAULT_FRAME_ID, Option.UNICAST, payload); + } + + /** + * 16 bit Tx Request with frame id argument + * + * + * Payload size is limited to 100 bytes, according to MaxStream documentation. + * + * @param remoteAddr16 + * @param frameId + * @param payload + */ + public TxRequest16(XBeeAddress16 remoteAddr16, int frameId, int[] payload) { + this(remoteAddr16, frameId, Option.UNICAST, payload); + } + + /** + * Note: if option is DISABLE_ACK_OPTION you will not get a ack response and you must use the asynchronous send method + * + * @param remoteAddr16 + * @param frameId + * @param payload + * @param option + */ + public TxRequest16(XBeeAddress16 remoteAddr16, int frameId, Option option, int[] payload) { + this.remoteAddr16 = remoteAddr16; + this.setFrameId(frameId); + this.setOption(option); + this.setPayload(payload); + } + + public int[] getFrameData() { + // 3/6/10 fixed bug -- broadcast address is used with broadcast option, not no ACK + + IntArrayOutputStream out = new IntArrayOutputStream(); + + // api id + out.write(this.getApiId().getValue()); + // frame id (arbitrary byte that will be sent back with ack) + out.write(this.getFrameId()); + // destination address (broadcast is 0xFFFF) + out.write(remoteAddr16.getAddress()); + // options byte disable ack = 1, send pan id = 4 + out.write(this.getOption().getValue()); + out.write(this.getPayload()); + + return out.getIntArray(); + } + + public ApiId getApiId() { + return ApiId.TX_REQUEST_16; + } + + public XBeeAddress16 getRemoteAddr16() { + return remoteAddr16; + } + + public void setRemoteAddr16(XBeeAddress16 remoteAddr16) { + this.remoteAddr16 = remoteAddr16; + } + + public String toString() { + return super.toString() + + ",remoteAddress16=" + this.remoteAddr16; + } +} diff --git a/src/com/rapplogic/xbee/api/wpan/TxRequest64.java b/src/main/java/com/rapplogic/xbee/api/wpan/TxRequest64.java similarity index 96% rename from src/com/rapplogic/xbee/api/wpan/TxRequest64.java rename to src/main/java/com/rapplogic/xbee/api/wpan/TxRequest64.java index 0b17750..c99d262 100644 --- a/src/com/rapplogic/xbee/api/wpan/TxRequest64.java +++ b/src/main/java/com/rapplogic/xbee/api/wpan/TxRequest64.java @@ -1,126 +1,128 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.wpan; - -import com.rapplogic.xbee.api.ApiId; -import com.rapplogic.xbee.api.XBeeAddress64; -import com.rapplogic.xbee.util.IntArrayOutputStream; - -// TODO test setting DH/DL to 0 and SH/SL - -/** - * Series 1 XBee. 64-bit address Transmit Packet. This is received on the destination XBee - * radio as a RxResponse64 response - *

- * Constructs frame data portion of a 64-bit transmit request - *

- * Note: The MY address of the receiving XBee must be set to 0xffff to receive this as a RxResponse64; - * otherwise the packet will be transmitted but will be received as a RxResponse16 - *

- * API ID: 0x0 - *

- * @author andrew - * - */ -public class TxRequest64 extends TxRequestBase { - - private XBeeAddress64 remoteAddr64; - - /** - * 16 bit Tx Request with default frame id and awk option - * - * @param destinationAddress - * @param payload - */ - public TxRequest64(XBeeAddress64 destination, int[] payload) { - this(destination, DEFAULT_FRAME_ID, Option.UNICAST, payload); - } - - /** - * 16 bit Tx Request. - * - * Keep in mind that if you programmed the destination address with AT commands, it is in Hex, - * so prepend int with 0x (e.g. 0x1234). - * - * Payload size is limited to 100 bytes, according to MaxStream documentation. - * - * @param destinationAddress - * @param awkFrameId - * @param payload - */ - public TxRequest64(XBeeAddress64 destination, int frameId, int[] payload) { - this(destination, frameId, Option.UNICAST, payload); - } - - /** - * Note: if option is DISABLE_ACK_OPTION you will not get a ack response and you must use the asynchronous send method - * - * @param destinationAddress - * @param awkFrameId - * @param payload - * @param option - */ - public TxRequest64(XBeeAddress64 remoteAddr64, int frameId, Option option, int[] payload) { - this.remoteAddr64 = remoteAddr64; - this.setFrameId(frameId); - this.setOption(option); - this.setPayload(payload); - } - - public int[] getFrameData() { - - // 3/6/10 fixed bug -- broadcast address is used with broadcast option, not no ACK - - IntArrayOutputStream out = new IntArrayOutputStream(); - - // api id - out.write(this.getApiId().getValue()); - // frame id (arbitrary byte that will be sent back with ack) - out.write(this.getFrameId()); - // destination high (broadcast is 0xFFFF) - - // add 64-bit dest address - out.write(remoteAddr64.getAddress()); - - // options byte disable ack = 1, send pan id = 4 - out.write(this.getOption().getValue()); - out.write(this.getPayload()); - - return out.getIntArray(); - } - - public ApiId getApiId() { - return ApiId.TX_REQUEST_64; - } - - public XBeeAddress64 getRemoteAddr64() { - return remoteAddr64; - } - - public void setRemoteAddr64(XBeeAddress64 remoteAddr64) { - this.remoteAddr64 = remoteAddr64; - } - - public String toString() { - return super.toString() + - ",remoteAddress64=" + this.remoteAddr64.toString(); - } - -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.wpan; + +import com.rapplogic.xbee.api.ApiId; +import com.rapplogic.xbee.api.XBeeAddress64; +import com.rapplogic.xbee.util.IntArrayOutputStream; + +// TODO test setting DH/DL to 0 and SH/SL + +/** + * Series 1 XBee. 64-bit address Transmit Packet. This is received on the destination XBee + * radio as a RxResponse64 response + *

+ * Constructs frame data portion of a 64-bit transmit request + *

+ * Note: The MY address of the receiving XBee must be set to 0xffff to receive this as a RxResponse64; + * otherwise the packet will be transmitted but will be received as a RxResponse16 + *

+ * API ID: 0x0 + *

+ * @author andrew + * + */ +public class TxRequest64 extends TxRequestBase { + + private static final long serialVersionUID = -6376854137744787256L; + + private XBeeAddress64 remoteAddr64; + + /** + * 16 bit Tx Request with default frame id and awk option + * + * @param destinationAddress + * @param payload + */ + public TxRequest64(XBeeAddress64 destination, int[] payload) { + this(destination, DEFAULT_FRAME_ID, Option.UNICAST, payload); + } + + /** + * 16 bit Tx Request. + * + * Keep in mind that if you programmed the destination address with AT commands, it is in Hex, + * so prepend int with 0x (e.g. 0x1234). + * + * Payload size is limited to 100 bytes, according to MaxStream documentation. + * + * @param destinationAddress + * @param awkFrameId + * @param payload + */ + public TxRequest64(XBeeAddress64 destination, int frameId, int[] payload) { + this(destination, frameId, Option.UNICAST, payload); + } + + /** + * Note: if option is DISABLE_ACK_OPTION you will not get a ack response and you must use the asynchronous send method + * + * @param destinationAddress + * @param awkFrameId + * @param payload + * @param option + */ + public TxRequest64(XBeeAddress64 remoteAddr64, int frameId, Option option, int[] payload) { + this.remoteAddr64 = remoteAddr64; + this.setFrameId(frameId); + this.setOption(option); + this.setPayload(payload); + } + + public int[] getFrameData() { + + // 3/6/10 fixed bug -- broadcast address is used with broadcast option, not no ACK + + IntArrayOutputStream out = new IntArrayOutputStream(); + + // api id + out.write(this.getApiId().getValue()); + // frame id (arbitrary byte that will be sent back with ack) + out.write(this.getFrameId()); + // destination high (broadcast is 0xFFFF) + + // add 64-bit dest address + out.write(remoteAddr64.getAddress()); + + // options byte disable ack = 1, send pan id = 4 + out.write(this.getOption().getValue()); + out.write(this.getPayload()); + + return out.getIntArray(); + } + + public ApiId getApiId() { + return ApiId.TX_REQUEST_64; + } + + public XBeeAddress64 getRemoteAddr64() { + return remoteAddr64; + } + + public void setRemoteAddr64(XBeeAddress64 remoteAddr64) { + this.remoteAddr64 = remoteAddr64; + } + + public String toString() { + return super.toString() + + ",remoteAddress64=" + this.remoteAddr64.toString(); + } + +} diff --git a/src/com/rapplogic/xbee/api/wpan/TxRequestBase.java b/src/main/java/com/rapplogic/xbee/api/wpan/TxRequestBase.java similarity index 96% rename from src/com/rapplogic/xbee/api/wpan/TxRequestBase.java rename to src/main/java/com/rapplogic/xbee/api/wpan/TxRequestBase.java index ce0bdf8..daf213d 100644 --- a/src/com/rapplogic/xbee/api/wpan/TxRequestBase.java +++ b/src/main/java/com/rapplogic/xbee/api/wpan/TxRequestBase.java @@ -1,111 +1,113 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.wpan; - -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; - -import com.rapplogic.xbee.api.XBeeRequest; -import com.rapplogic.xbee.util.ByteUtils; - -/** - * Series 1 XBee. Super class for 16 and 64 bit address Transmit packets - *

- * @author andrew - * - */ -public abstract class TxRequestBase extends XBeeRequest { - - /** - * Maximum payload size as specified in the series 1 XBee manual. - * This is provided for reference only and is not used for validation - */ - public final static int MAX_PAYLOAD_SIZE = 100; - - private int maxPayloadSize; - - public enum Option { - - UNICAST (0), - DISABLE_ACK (1), - BROADCAST(4); - - private static final Map lookup = new HashMap(); - - static { - for(Option s : EnumSet.allOf(Option.class)) { - lookup.put(s.getValue(), s); - } - } - - public static Option get(int value) { - return lookup.get(value); - } - - private final int value; - - Option(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - } - - private int[] payload; - private Option option; - - // TODO inconsistency: tx uses setPayload, rx uses getData - public int[] getPayload() { - return payload; - } - - public void setPayload(int[] payload) { - - if (this.getMaxPayloadSize() > 0 && payload.length > this.getMaxPayloadSize()) { - throw new IllegalArgumentException("Payload exceeds user-defined maximum payload size of " + this.getMaxPayloadSize() + " bytes. Please re-package into multiple packets"); - } - - this.payload = payload; - } - - public Option getOption() { - return option; - } - - public void setOption(Option option) { - this.option = option; - } - - public String toString() { - return super.toString() + ",option=" + this.option + - ",payload=" + ByteUtils.toBase16(this.payload); - } - - public int getMaxPayloadSize() { - return maxPayloadSize; - } - - public void setMaxPayloadSize(int maxPayloadSize) { - this.maxPayloadSize = maxPayloadSize; - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.wpan; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +import com.rapplogic.xbee.api.XBeeRequest; +import com.rapplogic.xbee.util.ByteUtils; + +/** + * Series 1 XBee. Super class for 16 and 64 bit address Transmit packets + *

+ * @author andrew + * + */ +public abstract class TxRequestBase extends XBeeRequest { + + private static final long serialVersionUID = -7813304462907316907L; + + /** + * Maximum payload size as specified in the series 1 XBee manual. + * This is provided for reference only and is not used for validation + */ + public final static int MAX_PAYLOAD_SIZE = 100; + + private int maxPayloadSize; + + public enum Option { + + UNICAST (0), + DISABLE_ACK (1), + BROADCAST(4); + + private static final Map lookup = new HashMap(); + + static { + for(Option s : EnumSet.allOf(Option.class)) { + lookup.put(s.getValue(), s); + } + } + + public static Option get(int value) { + return lookup.get(value); + } + + private final int value; + + Option(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + private int[] payload; + private Option option; + + // TODO inconsistency: tx uses setPayload, rx uses getData + public int[] getPayload() { + return payload; + } + + public void setPayload(int[] payload) { + + if (this.getMaxPayloadSize() > 0 && payload.length > this.getMaxPayloadSize()) { + throw new IllegalArgumentException("Payload exceeds user-defined maximum payload size of " + this.getMaxPayloadSize() + " bytes. Please re-package into multiple packets"); + } + + this.payload = payload; + } + + public Option getOption() { + return option; + } + + public void setOption(Option option) { + this.option = option; + } + + public String toString() { + return super.toString() + ",option=" + this.option + + ",payload=" + ByteUtils.toBase16(this.payload); + } + + public int getMaxPayloadSize() { + return maxPayloadSize; + } + + public void setMaxPayloadSize(int maxPayloadSize) { + this.maxPayloadSize = maxPayloadSize; + } +} diff --git a/src/com/rapplogic/xbee/api/wpan/TxStatusResponse.java b/src/main/java/com/rapplogic/xbee/api/wpan/TxStatusResponse.java similarity index 94% rename from src/com/rapplogic/xbee/api/wpan/TxStatusResponse.java rename to src/main/java/com/rapplogic/xbee/api/wpan/TxStatusResponse.java index de8071c..312bbdb 100644 --- a/src/com/rapplogic/xbee/api/wpan/TxStatusResponse.java +++ b/src/main/java/com/rapplogic/xbee/api/wpan/TxStatusResponse.java @@ -1,123 +1,125 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.wpan; - -import java.io.IOException; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; - -import com.rapplogic.xbee.api.IPacketParser; -import com.rapplogic.xbee.api.XBeeFrameIdResponse; - -/** - * Series 1 XBee. This is sent out the UART of the transmitting XBee immediately following - * a Transmit packet. Indicates if the Transmit (TxRequest16 or TxRequest64) - * was successful. - *

- * API ID: 0x89 - *

- * @author andrew - * - */ -public class TxStatusResponse extends XBeeFrameIdResponse { - - public enum Status { - - SUCCESS (0), - NO_ACK (1), - CCA_FAILURE(2), - PURGED(3); - - private static final Map lookup = new HashMap(); - - static { - for(Status s : EnumSet.allOf(Status.class)) { - lookup.put(s.getValue(), s); - } - } - - public static Status get(int value) { - return lookup.get(value); - } - - private final int value; - - Status(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - } - - private Status status; - - public TxStatusResponse() { - - } - - public Status getStatus() { - return status; - } - - public void setStatus(Status status) { - this.status = status; - } - - /** - * Returns true if the delivery status is SUCCESS - */ - public boolean isSuccess() { - return this.status == Status.SUCCESS; - } - - // isError() was overridding XBeeResponse isError() - - public boolean isAckError() { - return this.status == Status.NO_ACK; - } - - public boolean isCcaError() { - return this.status == Status.CCA_FAILURE; - } - - public boolean isPurged() { - return this.status == Status.PURGED; - } - - - public void parse(IPacketParser parser) throws IOException { - // frame id - int frameId = parser.read("TxStatus Frame Id"); - this.setFrameId(frameId); - - //log.debug("frame id is " + frameId); - - // Status: 0=Success, 1= No Ack, 2= CCA Failure, 3= Purge - int status = parser.read("TX Status"); - this.setStatus(TxStatusResponse.Status.get(status)); - } - - public String toString() { - return super.toString() + ",status=" + this.getStatus(); - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.wpan; + +import java.io.IOException; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +import com.rapplogic.xbee.api.IPacketParser; +import com.rapplogic.xbee.api.responses.XBeeFrameIdResponse; + +/** + * Series 1 XBee. This is sent out the UART of the transmitting XBee immediately following + * a Transmit packet. Indicates if the Transmit (TxRequest16 or TxRequest64) + * was successful. + *

+ * API ID: 0x89 + *

+ * @author andrew + * + */ +public class TxStatusResponse extends XBeeFrameIdResponse { + + private static final long serialVersionUID = 3413667044925902290L; + + public enum Status { + + SUCCESS (0), + NO_ACK (1), + CCA_FAILURE(2), + PURGED(3); + + private static final Map lookup = new HashMap(); + + static { + for(Status s : EnumSet.allOf(Status.class)) { + lookup.put(s.getValue(), s); + } + } + + public static Status get(int value) { + return lookup.get(value); + } + + private final int value; + + Status(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + private Status status; + + public TxStatusResponse() { + + } + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + + /** + * Returns true if the delivery status is SUCCESS + */ + public boolean isSuccess() { + return this.status == Status.SUCCESS; + } + + // isError() was overridding XBeeResponse isError() + + public boolean isAckError() { + return this.status == Status.NO_ACK; + } + + public boolean isCcaError() { + return this.status == Status.CCA_FAILURE; + } + + public boolean isPurged() { + return this.status == Status.PURGED; + } + + + public void parse(IPacketParser parser) throws IOException { + // frame id + int frameId = parser.read("TxStatus Frame Id"); + this.setFrameId(frameId); + + //log.debug("frame id is " + frameId); + + // Status: 0=Success, 1= No Ack, 2= CCA Failure, 3= Purge + int status = parser.read("TX Status"); + this.setStatus(TxStatusResponse.Status.get(status)); + } + + public String toString() { + return super.toString() + ",status=" + this.getStatus(); + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/wpan/WpanNodeDiscover.java b/src/main/java/com/rapplogic/xbee/api/wpan/WpanNodeDiscover.java old mode 100755 new mode 100644 similarity index 91% rename from src/com/rapplogic/xbee/api/wpan/WpanNodeDiscover.java rename to src/main/java/com/rapplogic/xbee/api/wpan/WpanNodeDiscover.java index b574891..34ad04c --- a/src/com/rapplogic/xbee/api/wpan/WpanNodeDiscover.java +++ b/src/main/java/com/rapplogic/xbee/api/wpan/WpanNodeDiscover.java @@ -1,127 +1,123 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.wpan; - -import org.apache.log4j.Logger; - -import com.rapplogic.xbee.api.AtCommandResponse; -import com.rapplogic.xbee.api.XBeeAddress16; -import com.rapplogic.xbee.api.XBeeAddress64; -import com.rapplogic.xbee.util.IntArrayInputStream; - -// tested ok via xmpp on 4/13/09 - -/** - * Series 1 XBee. Parses a Node Discover (ND) AT Command Response - */ -public class WpanNodeDiscover { - - private final static Logger log = Logger.getLogger(WpanNodeDiscover.class); - - private XBeeAddress16 nodeAddress16; - private XBeeAddress64 nodeAddress64; - private int rssi; - private String nodeIdentifier; - - public static WpanNodeDiscover parse(AtCommandResponse response) { - - if (!response.getCommand().equals("ND")) { - throw new IllegalArgumentException("This method is only applicable for the ND command"); - } - - int[] data = response.getValue(); - - if (data == null || data.length == 0) { - throw new IllegalArgumentException("ND command has no value"); - } - - IntArrayInputStream in = new IntArrayInputStream(data); - - WpanNodeDiscover nd = new WpanNodeDiscover(); - - nd.setNodeAddress16(new XBeeAddress16(in.read(2))); - - nd.setNodeAddress64(new XBeeAddress64(in.read(8))); - - nd.setRssi(-1*in.read()); - - StringBuilder ni = new StringBuilder(); - - int ch; - - // NI is terminated with 0 - while ((ch = in.read()) != 0) { - if (ch < 32 || ch > 126) { - throw new RuntimeException("Node Identifier " + ch + " is non-ascii"); - } - - ni.append((char)ch); - } - - nd.setNodeIdentifier(ni.toString()); - - return nd; - } - - public String toString() { - return "nodeAddress16=" + this.nodeAddress16 + - ", nodeAddress64=" + this.nodeAddress64 + - ", rssi=" + this.rssi + - ", nodeIdentifier=" + this.nodeIdentifier; - } - - - public XBeeAddress16 getNodeAddress16() { - return nodeAddress16; - } - - - public void setNodeAddress16(XBeeAddress16 my) { - this.nodeAddress16 = my; - } - - - public XBeeAddress64 getNodeAddress64() { - return nodeAddress64; - } - - - public void setNodeAddress64(XBeeAddress64 serial) { - this.nodeAddress64 = serial; - } - - - public String getNodeIdentifier() { - return nodeIdentifier; - } - - - public void setNodeIdentifier(String nodeIdentifier) { - this.nodeIdentifier = nodeIdentifier; - } - - public int getRssi() { - return rssi; - } - - public void setRssi(int rssi) { - this.rssi = rssi; - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.wpan; + +import com.rapplogic.xbee.api.XBeeAddress16; +import com.rapplogic.xbee.api.XBeeAddress64; +import com.rapplogic.xbee.api.responses.AtCommandResponse; +import com.rapplogic.xbee.util.IntArrayInputStream; + +// tested ok via xmpp on 4/13/09 + +/** + * Series 1 XBee. Parses a Node Discover (ND) AT Command Response + */ +public class WpanNodeDiscover { + + private XBeeAddress16 nodeAddress16; + private XBeeAddress64 nodeAddress64; + private int rssi; + private String nodeIdentifier; + + public static WpanNodeDiscover parse(AtCommandResponse response) { + + if (!response.getCommand().equals("ND")) { + throw new IllegalArgumentException("This method is only applicable for the ND command"); + } + + int[] data = response.getValue(); + + if (data == null || data.length == 0) { + throw new IllegalArgumentException("ND command has no value"); + } + + IntArrayInputStream in = new IntArrayInputStream(data); + + WpanNodeDiscover nd = new WpanNodeDiscover(); + + nd.setNodeAddress16(new XBeeAddress16(in.read(2))); + + nd.setNodeAddress64(new XBeeAddress64(in.read(8))); + + nd.setRssi(-1*in.read()); + + StringBuilder ni = new StringBuilder(); + + int ch; + + // NI is terminated with 0 + while ((ch = in.read()) != 0) { + if (ch < 32 || ch > 126) { + throw new RuntimeException("Node Identifier " + ch + " is non-ascii"); + } + + ni.append((char)ch); + } + + nd.setNodeIdentifier(ni.toString()); + + return nd; + } + + public String toString() { + return "nodeAddress16=" + this.nodeAddress16 + + ", nodeAddress64=" + this.nodeAddress64 + + ", rssi=" + this.rssi + + ", nodeIdentifier=" + this.nodeIdentifier; + } + + + public XBeeAddress16 getNodeAddress16() { + return nodeAddress16; + } + + + public void setNodeAddress16(XBeeAddress16 my) { + this.nodeAddress16 = my; + } + + + public XBeeAddress64 getNodeAddress64() { + return nodeAddress64; + } + + + public void setNodeAddress64(XBeeAddress64 serial) { + this.nodeAddress64 = serial; + } + + + public String getNodeIdentifier() { + return nodeIdentifier; + } + + + public void setNodeIdentifier(String nodeIdentifier) { + this.nodeIdentifier = nodeIdentifier; + } + + public int getRssi() { + return rssi; + } + + public void setRssi(int rssi) { + this.rssi = rssi; + } +} diff --git a/src/com/rapplogic/xbee/api/zigbee/AssociationStatus.java b/src/main/java/com/rapplogic/xbee/api/zigbee/AssociationStatus.java similarity index 95% rename from src/com/rapplogic/xbee/api/zigbee/AssociationStatus.java rename to src/main/java/com/rapplogic/xbee/api/zigbee/AssociationStatus.java index 557ffff..d5bc26d 100644 --- a/src/com/rapplogic/xbee/api/zigbee/AssociationStatus.java +++ b/src/main/java/com/rapplogic/xbee/api/zigbee/AssociationStatus.java @@ -1,71 +1,71 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.zigbee; - -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; - -import com.rapplogic.xbee.api.AtCommandResponse; - -// TODO create for wpan -public enum AssociationStatus { - SUCCESS (0, "Successful completion - Coordinator started or Router/End Device found and joined with a parent."), - NO_PAN (0x21, "Scan found no PANs"), - NO_VALID_PAN (0x22, "Scan found no valid PANs based on current SC and ID settings"), - NJ_EXPIRED (0x23, "Valid Coordinator or Routers found, but they are not allowing joining (NJ expired)"), - NJ_FAILED (0x27, "Node Joining attempt failed (typically due to incompatible security settings)"), - COORDINATOR_START_FAILED (0x2a, "Coordinator Start attempt failed"), - SCANNING_FOR_PARENT (0xff, "Scanning for a Parent"), - EXISTING_COORDINATOR_CHECK (0x2b, "Checking for an existing coordinator"); - - private static final Map lookup = new HashMap(); - - static { - for(AssociationStatus s : EnumSet.allOf(AssociationStatus.class)) { - lookup.put(s.getValue(), s); - } - } - - public static AssociationStatus get(int value) { - return lookup.get(value); - } - - public static AssociationStatus get(AtCommandResponse response) { - return AssociationStatus.get(response.getValue()[0]); - } - - private final int value; - private final String description; - - AssociationStatus(int value, String description) { - this.value = value; - this.description = description; - - } - - public int getValue() { - return value; - } - - public String getDescription() { - return description; - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.zigbee; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +import com.rapplogic.xbee.api.responses.AtCommandResponse; + +// TODO create for wpan +public enum AssociationStatus { + SUCCESS (0, "Successful completion - Coordinator started or Router/End Device found and joined with a parent."), + NO_PAN (0x21, "Scan found no PANs"), + NO_VALID_PAN (0x22, "Scan found no valid PANs based on current SC and ID settings"), + NJ_EXPIRED (0x23, "Valid Coordinator or Routers found, but they are not allowing joining (NJ expired)"), + NJ_FAILED (0x27, "Node Joining attempt failed (typically due to incompatible security settings)"), + COORDINATOR_START_FAILED (0x2a, "Coordinator Start attempt failed"), + SCANNING_FOR_PARENT (0xff, "Scanning for a Parent"), + EXISTING_COORDINATOR_CHECK (0x2b, "Checking for an existing coordinator"); + + private static final Map lookup = new HashMap(); + + static { + for(AssociationStatus s : EnumSet.allOf(AssociationStatus.class)) { + lookup.put(s.getValue(), s); + } + } + + public static AssociationStatus get(int value) { + return lookup.get(value); + } + + public static AssociationStatus get(AtCommandResponse response) { + return AssociationStatus.get(response.getValue()[0]); + } + + private final int value; + private final String description; + + AssociationStatus(int value, String description) { + this.value = value; + this.description = description; + + } + + public int getValue() { + return value; + } + + public String getDescription() { + return description; + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/zigbee/ZBForceSampleRequest.java b/src/main/java/com/rapplogic/xbee/api/zigbee/ZBForceSampleRequest.java similarity index 91% rename from src/com/rapplogic/xbee/api/zigbee/ZBForceSampleRequest.java rename to src/main/java/com/rapplogic/xbee/api/zigbee/ZBForceSampleRequest.java index e29b9c3..9a60852 100644 --- a/src/com/rapplogic/xbee/api/zigbee/ZBForceSampleRequest.java +++ b/src/main/java/com/rapplogic/xbee/api/zigbee/ZBForceSampleRequest.java @@ -1,44 +1,46 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.zigbee; - -import com.rapplogic.xbee.api.XBeeAddress16; -import com.rapplogic.xbee.api.XBeeAddress64; -import com.rapplogic.xbee.api.XBeeRequest; -import com.rapplogic.xbee.api.RemoteAtRequest; - -/** - * Uses Remote AT to send a Force Sample (IS) AT command to a remote XBee - * - * @author andrew - * - */ -public class ZBForceSampleRequest extends RemoteAtRequest { - - /** - * Creates a Force Sample Remote AT request - * - * @param dest64 - * @param command - */ - public ZBForceSampleRequest(XBeeAddress64 dest64) { - super(XBeeRequest.DEFAULT_FRAME_ID, dest64, XBeeAddress16.ZNET_BROADCAST, false, "IS", null); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.zigbee; + +import com.rapplogic.xbee.api.XBeeAddress16; +import com.rapplogic.xbee.api.XBeeAddress64; +import com.rapplogic.xbee.api.XBeeRequest; +import com.rapplogic.xbee.api.requests.RemoteAtRequest; + +/** + * Uses Remote AT to send a Force Sample (IS) AT command to a remote XBee + * + * @author andrew + * + */ +public class ZBForceSampleRequest extends RemoteAtRequest { + + private static final long serialVersionUID = -5556592595956235088L; + + /** + * Creates a Force Sample Remote AT request + * + * @param dest64 + * @param command + */ + public ZBForceSampleRequest(XBeeAddress64 dest64) { + super(XBeeRequest.DEFAULT_FRAME_ID, dest64, XBeeAddress16.ZNET_BROADCAST, false, "IS", null); + } +} diff --git a/src/com/rapplogic/xbee/api/zigbee/ZBNodeDiscover.java b/src/main/java/com/rapplogic/xbee/api/zigbee/ZBNodeDiscover.java similarity index 92% rename from src/com/rapplogic/xbee/api/zigbee/ZBNodeDiscover.java rename to src/main/java/com/rapplogic/xbee/api/zigbee/ZBNodeDiscover.java index 46458c4..f0a98e0 100644 --- a/src/com/rapplogic/xbee/api/zigbee/ZBNodeDiscover.java +++ b/src/main/java/com/rapplogic/xbee/api/zigbee/ZBNodeDiscover.java @@ -1,212 +1,208 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.zigbee; - -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; - -import org.apache.log4j.Logger; - -import com.rapplogic.xbee.api.AtCommandResponse; -import com.rapplogic.xbee.api.XBeeAddress16; -import com.rapplogic.xbee.api.XBeeAddress64; -import com.rapplogic.xbee.util.ByteUtils; -import com.rapplogic.xbee.util.IntArrayInputStream; - -/** - * Series 2 XBee. Parses a Node Discover (ND) AT Command Response - *

- * @author andrew - * - */ -public class ZBNodeDiscover { - - private final static Logger log = Logger.getLogger(ZBNodeDiscover.class); - - public enum DeviceType { - DEV_TYPE_COORDINATOR (0), - DEV_TYPE_ROUTER (1), - DEV_TYPE_END_DEVICE (2); - - private static final Map lookup = new HashMap(); - - static { - for(DeviceType s : EnumSet.allOf(DeviceType.class)) { - lookup.put(s.getValue(), s); - } - } - - public static DeviceType get(int value) { - return lookup.get(value); - } - - private final int value; - - DeviceType(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - } - - private XBeeAddress16 nodeAddress16; - private XBeeAddress64 nodeAddress64; - private String nodeIdentifier; - private XBeeAddress16 parent; - private DeviceType deviceType; - private int status; - private int[] profileId; - private int[] mfgId; - - public DeviceType getDeviceType() { - return deviceType; - } - - - public void setDeviceType(DeviceType deviceType) { - this.deviceType = deviceType; - } - - - public int getStatus() { - return status; - } - - - public void setStatus(int status) { - this.status = status; - } - - - public int[] getProfileId() { - return profileId; - } - - - public void setProfileId(int[] profileId) { - this.profileId = profileId; - } - - - public int[] getMfgId() { - return mfgId; - } - - - public void setMfgId(int[] mfgId) { - this.mfgId = mfgId; - } - - - public static ZBNodeDiscover parse(AtCommandResponse response) { - - if (!response.getCommand().equals("ND")) { - throw new RuntimeException("This method is only applicable for the ND command"); - } - - int[] data = response.getValue(); - - IntArrayInputStream in = new IntArrayInputStream(data); - - ZBNodeDiscover nd = new ZBNodeDiscover(); - - nd.setNodeAddress16(new XBeeAddress16(in.read(2))); - - nd.setNodeAddress64(new XBeeAddress64(in.read(8))); - - StringBuffer ni = new StringBuffer(); - - int ch; - - // NI is terminated with 0 - while ((ch = in.read()) != 0) { - if (ch < 32 || ch > 126) { - throw new RuntimeException("Node Identifier " + ch + " is non-ascii"); - } - - ni.append((char)ch); - } - - nd.setNodeIdentifier(ni.toString()); - - nd.setParent(new XBeeAddress16(in.read(2))); - nd.setDeviceType(DeviceType.get(in.read())); - // TODO this is being reported as 1 (router) for my end device - nd.setStatus(in.read()); - nd.setProfileId(in.read(2)); - nd.setMfgId(in.read(2)); - - return nd; - } - - public String toString() { - return "nodeAddress16=" + this.nodeAddress16 + - ", nodeAddress64=" + this.nodeAddress64 + - ", nodeIdentifier=" + this.nodeIdentifier + - ", parentAddress=" + this.getParent() + - ", deviceType=" + this.deviceType + - ", status=" + this.status + - ", profileId=" + ByteUtils.toBase16(this.profileId) + - ", mfgId=" + ByteUtils.toBase16(this.mfgId); - } - - - public XBeeAddress16 getNodeAddress16() { - return nodeAddress16; - } - - - public void setNodeAddress16(XBeeAddress16 my) { - this.nodeAddress16 = my; - } - - - public XBeeAddress64 getNodeAddress64() { - return nodeAddress64; - } - - - public void setNodeAddress64(XBeeAddress64 serial) { - this.nodeAddress64 = serial; - } - - - public String getNodeIdentifier() { - return nodeIdentifier; - } - - - public void setNodeIdentifier(String nodeIdentifier) { - this.nodeIdentifier = nodeIdentifier; - } - - - public XBeeAddress16 getParent() { - return parent; - } - - - public void setParent(XBeeAddress16 parent) { - this.parent = parent; - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.zigbee; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +import com.rapplogic.xbee.api.XBeeAddress16; +import com.rapplogic.xbee.api.XBeeAddress64; +import com.rapplogic.xbee.api.responses.AtCommandResponse; +import com.rapplogic.xbee.util.ByteUtils; +import com.rapplogic.xbee.util.IntArrayInputStream; + +/** + * Series 2 XBee. Parses a Node Discover (ND) AT Command Response + *

+ * @author andrew + * + */ +public class ZBNodeDiscover { + + public enum DeviceType { + DEV_TYPE_COORDINATOR (0), + DEV_TYPE_ROUTER (1), + DEV_TYPE_END_DEVICE (2); + + private static final Map lookup = new HashMap(); + + static { + for(DeviceType s : EnumSet.allOf(DeviceType.class)) { + lookup.put(s.getValue(), s); + } + } + + public static DeviceType get(int value) { + return lookup.get(value); + } + + private final int value; + + DeviceType(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + private XBeeAddress16 nodeAddress16; + private XBeeAddress64 nodeAddress64; + private String nodeIdentifier; + private XBeeAddress16 parent; + private DeviceType deviceType; + private int status; + private int[] profileId; + private int[] mfgId; + + public DeviceType getDeviceType() { + return deviceType; + } + + + public void setDeviceType(DeviceType deviceType) { + this.deviceType = deviceType; + } + + + public int getStatus() { + return status; + } + + + public void setStatus(int status) { + this.status = status; + } + + + public int[] getProfileId() { + return profileId; + } + + + public void setProfileId(int[] profileId) { + this.profileId = profileId; + } + + + public int[] getMfgId() { + return mfgId; + } + + + public void setMfgId(int[] mfgId) { + this.mfgId = mfgId; + } + + + public static ZBNodeDiscover parse(AtCommandResponse response) { + + if (!response.getCommand().equals("ND")) { + throw new RuntimeException("This method is only applicable for the ND command"); + } + + int[] data = response.getValue(); + + IntArrayInputStream in = new IntArrayInputStream(data); + + ZBNodeDiscover nd = new ZBNodeDiscover(); + + nd.setNodeAddress16(new XBeeAddress16(in.read(2))); + + nd.setNodeAddress64(new XBeeAddress64(in.read(8))); + + StringBuffer ni = new StringBuffer(); + + int ch; + + // NI is terminated with 0 + while ((ch = in.read()) != 0) { + if (ch < 32 || ch > 126) { + throw new RuntimeException("Node Identifier " + ch + " is non-ascii"); + } + + ni.append((char)ch); + } + + nd.setNodeIdentifier(ni.toString()); + + nd.setParent(new XBeeAddress16(in.read(2))); + nd.setDeviceType(DeviceType.get(in.read())); + // TODO this is being reported as 1 (router) for my end device + nd.setStatus(in.read()); + nd.setProfileId(in.read(2)); + nd.setMfgId(in.read(2)); + + return nd; + } + + public String toString() { + return "nodeAddress16=" + this.nodeAddress16 + + ", nodeAddress64=" + this.nodeAddress64 + + ", nodeIdentifier=" + this.nodeIdentifier + + ", parentAddress=" + this.getParent() + + ", deviceType=" + this.deviceType + + ", status=" + this.status + + ", profileId=" + ByteUtils.toBase16(this.profileId) + + ", mfgId=" + ByteUtils.toBase16(this.mfgId); + } + + + public XBeeAddress16 getNodeAddress16() { + return nodeAddress16; + } + + + public void setNodeAddress16(XBeeAddress16 my) { + this.nodeAddress16 = my; + } + + + public XBeeAddress64 getNodeAddress64() { + return nodeAddress64; + } + + + public void setNodeAddress64(XBeeAddress64 serial) { + this.nodeAddress64 = serial; + } + + + public String getNodeIdentifier() { + return nodeIdentifier; + } + + + public void setNodeIdentifier(String nodeIdentifier) { + this.nodeIdentifier = nodeIdentifier; + } + + + public XBeeAddress16 getParent() { + return parent; + } + + + public void setParent(XBeeAddress16 parent) { + this.parent = parent; + } +} diff --git a/src/com/rapplogic/xbee/api/zigbee/ZNetExplicitRxResponse.java b/src/main/java/com/rapplogic/xbee/api/zigbee/ZNetExplicitRxResponse.java similarity index 96% rename from src/com/rapplogic/xbee/api/zigbee/ZNetExplicitRxResponse.java rename to src/main/java/com/rapplogic/xbee/api/zigbee/ZNetExplicitRxResponse.java index a4bcf2d..1e14a11 100644 --- a/src/com/rapplogic/xbee/api/zigbee/ZNetExplicitRxResponse.java +++ b/src/main/java/com/rapplogic/xbee/api/zigbee/ZNetExplicitRxResponse.java @@ -1,107 +1,109 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.zigbee; - -import java.io.IOException; - -import com.rapplogic.xbee.api.IPacketParser; -import com.rapplogic.xbee.util.ByteUtils; -import com.rapplogic.xbee.util.DoubleByte; - -// TODO deprecate all ZNet* classes and replace with ZigBee* -/** - * Series 2 XBee. This packet is received when a remote XBee sends a ZNetExplicitTxRequest - *

- * Radio must be configured for explicit frames to use this class (AO=1) - *

- * API ID: 0x91 - */ -public class ZNetExplicitRxResponse extends ZNetRxResponse { - - private int sourceEndpoint; - private int destinationEndpoint; - private DoubleByte clusterId; - private DoubleByte profileId; - - public ZNetExplicitRxResponse() { - super(); - } - - public int getSourceEndpoint() { - return sourceEndpoint; - } - - public void setSourceEndpoint(int sourceEndpoint) { - this.sourceEndpoint = sourceEndpoint; - } - - public int getDestinationEndpoint() { - return destinationEndpoint; - } - - public void setDestinationEndpoint(int destinationEndpoint) { - this.destinationEndpoint = destinationEndpoint; - } - - public DoubleByte getClusterId() { - return clusterId; - } - - public void setClusterId(DoubleByte clusterId) { - this.clusterId = clusterId; - } - - public DoubleByte getProfileId() { - return profileId; - } - - public void setProfileId(DoubleByte profileId) { - this.profileId = profileId; - } - - public void parse(IPacketParser parser) throws IOException { - this.parseAddress(parser); - - this.setSourceEndpoint(parser.read("Reading Source Endpoint")); - this.setDestinationEndpoint(parser.read("Reading Destination Endpoint")); - DoubleByte clusterId = new DoubleByte(); - clusterId.setMsb(parser.read("Reading Cluster Id MSB")); - clusterId.setLsb(parser.read("Reading Cluster Id LSB")); - this.setClusterId(clusterId); - - DoubleByte profileId = new DoubleByte(); - profileId.setMsb(parser.read("Reading Profile Id MSB")); - profileId.setLsb(parser.read("Reading Profile Id LSB")); - this.setProfileId(profileId); - - this.parseOption(parser); - this.setData(parser.readRemainingBytes()); - } - - public String toString() { - return super.toString() + - ",sourceEndpoint=" + ByteUtils.toBase16(this.getSourceEndpoint()) + - ",destinationEndpoint=" + ByteUtils.toBase16(this.getDestinationEndpoint()) + - ",clusterId(msb)=" + ByteUtils.toBase16(this.getClusterId().getMsb()) + - ",clusterId(lsb)=" + ByteUtils.toBase16(this.getClusterId().getLsb()) + - ",profileId(msb)=" + ByteUtils.toBase16(this.getProfileId().getMsb()) + - ",profileId(lsb)=" + ByteUtils.toBase16(this.getProfileId().getLsb()); - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.zigbee; + +import java.io.IOException; + +import com.rapplogic.xbee.api.IPacketParser; +import com.rapplogic.xbee.util.ByteUtils; +import com.rapplogic.xbee.util.DoubleByte; + +// TODO deprecate all ZNet* classes and replace with ZigBee* +/** + * Series 2 XBee. This packet is received when a remote XBee sends a ZNetExplicitTxRequest + *

+ * Radio must be configured for explicit frames to use this class (AO=1) + *

+ * API ID: 0x91 + */ +public class ZNetExplicitRxResponse extends ZNetRxResponse { + + private static final long serialVersionUID = -6559407185467903247L; + + private int sourceEndpoint; + private int destinationEndpoint; + private DoubleByte clusterId; + private DoubleByte profileId; + + public ZNetExplicitRxResponse() { + super(); + } + + public int getSourceEndpoint() { + return sourceEndpoint; + } + + public void setSourceEndpoint(int sourceEndpoint) { + this.sourceEndpoint = sourceEndpoint; + } + + public int getDestinationEndpoint() { + return destinationEndpoint; + } + + public void setDestinationEndpoint(int destinationEndpoint) { + this.destinationEndpoint = destinationEndpoint; + } + + public DoubleByte getClusterId() { + return clusterId; + } + + public void setClusterId(DoubleByte clusterId) { + this.clusterId = clusterId; + } + + public DoubleByte getProfileId() { + return profileId; + } + + public void setProfileId(DoubleByte profileId) { + this.profileId = profileId; + } + + public void parse(IPacketParser parser) throws IOException { + this.parseAddress(parser); + + this.setSourceEndpoint(parser.read("Reading Source Endpoint")); + this.setDestinationEndpoint(parser.read("Reading Destination Endpoint")); + DoubleByte clusterId = new DoubleByte(); + clusterId.setMsb(parser.read("Reading Cluster Id MSB")); + clusterId.setLsb(parser.read("Reading Cluster Id LSB")); + this.setClusterId(clusterId); + + DoubleByte profileId = new DoubleByte(); + profileId.setMsb(parser.read("Reading Profile Id MSB")); + profileId.setLsb(parser.read("Reading Profile Id LSB")); + this.setProfileId(profileId); + + this.parseOption(parser); + this.setData(parser.readRemainingBytes()); + } + + public String toString() { + return super.toString() + + ",sourceEndpoint=" + ByteUtils.toBase16(this.getSourceEndpoint()) + + ",destinationEndpoint=" + ByteUtils.toBase16(this.getDestinationEndpoint()) + + ",clusterId(msb)=" + ByteUtils.toBase16(this.getClusterId().getMsb()) + + ",clusterId(lsb)=" + ByteUtils.toBase16(this.getClusterId().getLsb()) + + ",profileId(msb)=" + ByteUtils.toBase16(this.getProfileId().getMsb()) + + ",profileId(lsb)=" + ByteUtils.toBase16(this.getProfileId().getLsb()); + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/zigbee/ZNetExplicitTxRequest.java b/src/main/java/com/rapplogic/xbee/api/zigbee/ZNetExplicitTxRequest.java similarity index 96% rename from src/com/rapplogic/xbee/api/zigbee/ZNetExplicitTxRequest.java rename to src/main/java/com/rapplogic/xbee/api/zigbee/ZNetExplicitTxRequest.java index ab8ce5b..db60a4d 100644 --- a/src/com/rapplogic/xbee/api/zigbee/ZNetExplicitTxRequest.java +++ b/src/main/java/com/rapplogic/xbee/api/zigbee/ZNetExplicitTxRequest.java @@ -1,201 +1,203 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.zigbee; - -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; - -import com.rapplogic.xbee.api.ApiId; -import com.rapplogic.xbee.api.XBeeAddress16; -import com.rapplogic.xbee.api.XBeeAddress64; -import com.rapplogic.xbee.util.ByteUtils; -import com.rapplogic.xbee.util.DoubleByte; -import com.rapplogic.xbee.util.IntArrayOutputStream; - -/** - * Series 2 XBee. Sends a packet to a remote radio. The remote radio - * receives the packet as a ZNetExplicitRxResponse packet. - *

- * Radio must be configured for explicit frames to use this class (AO=1) - *

- * API ID: 0x11 - * - * @author andrew - * - */ -public class ZNetExplicitTxRequest extends ZNetTxRequest { - - public enum Endpoint { - ZDO_ENDPOINT(0), - COMMAND (0xe6), - DATA (0xe8); - - private static final Map lookup = new HashMap(); - - static { - for(Endpoint s : EnumSet.allOf(Endpoint.class)) { - lookup.put(s.getValue(), s); - } - } - - public static Endpoint get(int value) { - return lookup.get(value); - } - - private final int value; - - Endpoint(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - } - - public enum ClusterId { - TRANSPARENT_SERIAL (Endpoint.DATA, 0x11), - SERIAL_LOOPBACK (Endpoint.DATA, 0x12), - IO_SAMPLE (Endpoint.DATA, 0x92), - XBEE_SENSOR (Endpoint.DATA, 0x94), - NODE_IDENTIFICATION (Endpoint.DATA, 0x95); - - private static final Map lookup = new HashMap(); - - static { - for(ClusterId s : EnumSet.allOf(ClusterId.class)) { - lookup.put(s.getValue(), s); - } - } - - public static ClusterId get(int value) { - return lookup.get(value); - } - - private final int value; - private final Endpoint endpoint; - - ClusterId(Endpoint endpoint, int value) { - this.endpoint = endpoint; - this.value = value; - } - - public int getValue() { - return value; - } - - public Endpoint getEndpoint() { - return this.endpoint; - } - } - - // TODO ZDO commands - - private int sourceEndpoint; - private int destinationEndpoint; - private DoubleByte clusterId; - private DoubleByte profileId; - - public final static DoubleByte znetProfileId = new DoubleByte(0xc1, 0x05); - public final static DoubleByte zdoProfileId = new DoubleByte(0, 0); - - public ZNetExplicitTxRequest(int frameId, XBeeAddress64 dest64, XBeeAddress16 dest16, int broadcastRadius, ZNetTxRequest.Option option, int[] payload, int sourceEndpoint, int destinationEndpoint, DoubleByte clusterId, DoubleByte profileId) { - super(frameId, dest64, dest16, broadcastRadius, option, payload); - this.sourceEndpoint = sourceEndpoint; - this.destinationEndpoint = destinationEndpoint; - this.clusterId = clusterId; - this.profileId = profileId; - } - - /** - * Gets frame data from tx request (super) and inserts necessary bytes - */ - public int[] getFrameData() { - - // get frame id from tx request - IntArrayOutputStream frameData = this.getFrameDataAsIntArrayOutputStream(); - - // overwrite api id - frameData.getInternalList().set(0, this.getApiId().getValue()); - - // insert explicit bytes - - // source endpoint - frameData.getInternalList().add(12, this.getSourceEndpoint()); - // dest endpoint - frameData.getInternalList().add(13, this.getDestinationEndpoint()); - // cluster id msb - frameData.getInternalList().add(14, this.getClusterId().getMsb()); - // cluster id lsb - frameData.getInternalList().add(15, this.getClusterId().getLsb()); - // profile id - frameData.getInternalList().add(16, this.getProfileId().getMsb()); - frameData.getInternalList().add(17, this.getProfileId().getLsb()); - - return frameData.getIntArray(); - } - - public ApiId getApiId() { - return ApiId.ZNET_EXPLICIT_TX_REQUEST; - } - - public int getSourceEndpoint() { - return sourceEndpoint; - } - - public void setSourceEndpoint(int sourceEndpoint) { - this.sourceEndpoint = sourceEndpoint; - } - - public int getDestinationEndpoint() { - return destinationEndpoint; - } - - public void setDestinationEndpoint(int destinationEndpoint) { - this.destinationEndpoint = destinationEndpoint; - } - - public DoubleByte getClusterId() { - return clusterId; - } - - public void setClusterId(DoubleByte clusterId) { - this.clusterId = clusterId; - } - - public DoubleByte getProfileId() { - return profileId; - } - - public void setProfileId(DoubleByte profileId) { - this.profileId = profileId; - } - - public String toString() { - return super.toString() + - ",sourceEndpoint=" + ByteUtils.toBase16(this.getSourceEndpoint()) + - ",destinationEndpoint=" + ByteUtils.toBase16(this.getDestinationEndpoint()) + - ",clusterId(msb)=" + ByteUtils.toBase16(this.getClusterId().getMsb()) + - ",clusterId(lsb)=" + ByteUtils.toBase16(this.getClusterId().getLsb()) + - ",profileId(msb)=" + ByteUtils.toBase16(this.getProfileId().getMsb()) + - ",profileId(lsb)=" + ByteUtils.toBase16(this.getProfileId().getLsb()); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.zigbee; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +import com.rapplogic.xbee.api.ApiId; +import com.rapplogic.xbee.api.XBeeAddress16; +import com.rapplogic.xbee.api.XBeeAddress64; +import com.rapplogic.xbee.util.ByteUtils; +import com.rapplogic.xbee.util.DoubleByte; +import com.rapplogic.xbee.util.IntArrayOutputStream; + +/** + * Series 2 XBee. Sends a packet to a remote radio. The remote radio + * receives the packet as a ZNetExplicitRxResponse packet. + *

+ * Radio must be configured for explicit frames to use this class (AO=1) + *

+ * API ID: 0x11 + * + * @author andrew + * + */ +public class ZNetExplicitTxRequest extends ZNetTxRequest { + + private static final long serialVersionUID = -1208466120818907727L; + + public enum Endpoint { + ZDO_ENDPOINT(0), + COMMAND (0xe6), + DATA (0xe8); + + private static final Map lookup = new HashMap(); + + static { + for(Endpoint s : EnumSet.allOf(Endpoint.class)) { + lookup.put(s.getValue(), s); + } + } + + public static Endpoint get(int value) { + return lookup.get(value); + } + + private final int value; + + Endpoint(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + public enum ClusterId { + TRANSPARENT_SERIAL (Endpoint.DATA, 0x11), + SERIAL_LOOPBACK (Endpoint.DATA, 0x12), + IO_SAMPLE (Endpoint.DATA, 0x92), + XBEE_SENSOR (Endpoint.DATA, 0x94), + NODE_IDENTIFICATION (Endpoint.DATA, 0x95); + + private static final Map lookup = new HashMap(); + + static { + for(ClusterId s : EnumSet.allOf(ClusterId.class)) { + lookup.put(s.getValue(), s); + } + } + + public static ClusterId get(int value) { + return lookup.get(value); + } + + private final int value; + private final Endpoint endpoint; + + ClusterId(Endpoint endpoint, int value) { + this.endpoint = endpoint; + this.value = value; + } + + public int getValue() { + return value; + } + + public Endpoint getEndpoint() { + return this.endpoint; + } + } + + // TODO ZDO commands + + private int sourceEndpoint; + private int destinationEndpoint; + private DoubleByte clusterId; + private DoubleByte profileId; + + public final static DoubleByte znetProfileId = new DoubleByte(0xc1, 0x05); + public final static DoubleByte zdoProfileId = new DoubleByte(0, 0); + + public ZNetExplicitTxRequest(int frameId, XBeeAddress64 dest64, XBeeAddress16 dest16, int broadcastRadius, ZNetTxRequest.Option option, int[] payload, int sourceEndpoint, int destinationEndpoint, DoubleByte clusterId, DoubleByte profileId) { + super(frameId, dest64, dest16, broadcastRadius, option, payload); + this.sourceEndpoint = sourceEndpoint; + this.destinationEndpoint = destinationEndpoint; + this.clusterId = clusterId; + this.profileId = profileId; + } + + /** + * Gets frame data from tx request (super) and inserts necessary bytes + */ + public int[] getFrameData() { + + // get frame id from tx request + IntArrayOutputStream frameData = this.getFrameDataAsIntArrayOutputStream(); + + // overwrite api id + frameData.getInternalList().set(0, this.getApiId().getValue()); + + // insert explicit bytes + + // source endpoint + frameData.getInternalList().add(12, this.getSourceEndpoint()); + // dest endpoint + frameData.getInternalList().add(13, this.getDestinationEndpoint()); + // cluster id msb + frameData.getInternalList().add(14, this.getClusterId().getMsb()); + // cluster id lsb + frameData.getInternalList().add(15, this.getClusterId().getLsb()); + // profile id + frameData.getInternalList().add(16, this.getProfileId().getMsb()); + frameData.getInternalList().add(17, this.getProfileId().getLsb()); + + return frameData.getIntArray(); + } + + public ApiId getApiId() { + return ApiId.ZNET_EXPLICIT_TX_REQUEST; + } + + public int getSourceEndpoint() { + return sourceEndpoint; + } + + public void setSourceEndpoint(int sourceEndpoint) { + this.sourceEndpoint = sourceEndpoint; + } + + public int getDestinationEndpoint() { + return destinationEndpoint; + } + + public void setDestinationEndpoint(int destinationEndpoint) { + this.destinationEndpoint = destinationEndpoint; + } + + public DoubleByte getClusterId() { + return clusterId; + } + + public void setClusterId(DoubleByte clusterId) { + this.clusterId = clusterId; + } + + public DoubleByte getProfileId() { + return profileId; + } + + public void setProfileId(DoubleByte profileId) { + this.profileId = profileId; + } + + public String toString() { + return super.toString() + + ",sourceEndpoint=" + ByteUtils.toBase16(this.getSourceEndpoint()) + + ",destinationEndpoint=" + ByteUtils.toBase16(this.getDestinationEndpoint()) + + ",clusterId(msb)=" + ByteUtils.toBase16(this.getClusterId().getMsb()) + + ",clusterId(lsb)=" + ByteUtils.toBase16(this.getClusterId().getLsb()) + + ",profileId(msb)=" + ByteUtils.toBase16(this.getProfileId().getMsb()) + + ",profileId(lsb)=" + ByteUtils.toBase16(this.getProfileId().getLsb()); + } +} diff --git a/src/com/rapplogic/xbee/api/zigbee/ZNetNodeIdentificationResponse.java b/src/main/java/com/rapplogic/xbee/api/zigbee/ZNetNodeIdentificationResponse.java similarity index 96% rename from src/com/rapplogic/xbee/api/zigbee/ZNetNodeIdentificationResponse.java rename to src/main/java/com/rapplogic/xbee/api/zigbee/ZNetNodeIdentificationResponse.java index db906a8..63c05e9 100644 --- a/src/com/rapplogic/xbee/api/zigbee/ZNetNodeIdentificationResponse.java +++ b/src/main/java/com/rapplogic/xbee/api/zigbee/ZNetNodeIdentificationResponse.java @@ -1,271 +1,273 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.zigbee; - -import java.io.IOException; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; - -import org.apache.log4j.Logger; - -import com.rapplogic.xbee.api.IPacketParser; -import com.rapplogic.xbee.api.XBeeAddress16; -import com.rapplogic.xbee.api.XBeeAddress64; -import com.rapplogic.xbee.api.XBeeResponse; -import com.rapplogic.xbee.api.zigbee.ZNetRxBaseResponse.Option; -import com.rapplogic.xbee.util.DoubleByte; - -public class ZNetNodeIdentificationResponse extends XBeeResponse { - - private final static Logger log = Logger.getLogger(ZNetNodeIdentificationResponse.class); - - // TODO this is repeated in NodeDiscover - public enum DeviceType { - COORDINATOR (0x1), - ROUTER (0x2), - END_DEVICE (0x3), - UNKNOWN(-1); - - private static final Map lookup = new HashMap(); - - static { - for(DeviceType s: EnumSet.allOf(DeviceType.class)) { - lookup.put(s.getValue(), s); - } - } - - public static DeviceType get(int value) { - return lookup.get(value); - } - - private final int value; - - DeviceType(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - } - - public enum SourceAction { - PUSHBUTTON (0x1), - JOINING (0x2), - POWER_CYCLE(0x3), - UNKNOWN(-1); - - private static final Map lookup = new HashMap(); - - static { - for(SourceAction s: EnumSet.allOf(SourceAction.class)) { - lookup.put(s.getValue(), s); - } - } - - public static SourceAction get(int value) { - return lookup.get(value); - } - - private final int value; - - SourceAction(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - } - - private XBeeAddress64 remoteAddress64; - private XBeeAddress16 remoteAddress16; - private Option option; - // TODO Digi WTF why duplicated?? p.70 - private XBeeAddress64 remoteAddress64_2; - private XBeeAddress16 remoteAddress16_2; - - private String nodeIdentifier; - private XBeeAddress16 parentAddress; - private DeviceType deviceType; - private SourceAction sourceAction; - private DoubleByte profileId; - private DoubleByte mfgId; - - public ZNetNodeIdentificationResponse() { - - } - - public XBeeAddress64 getRemoteAddress64() { - return remoteAddress64; - } - - public void setRemoteAddress64(XBeeAddress64 remoteAddress64) { - this.remoteAddress64 = remoteAddress64; - } - - public XBeeAddress16 getRemoteAddress16() { - return remoteAddress16; - } - - public void setRemoteAddress16(XBeeAddress16 remoteAddress16) { - this.remoteAddress16 = remoteAddress16; - } - - public Option getOption() { - return option; - } - - public void setOption(Option option) { - this.option = option; - } - - public XBeeAddress64 getRemoteAddress64_2() { - return remoteAddress64_2; - } - - public void setRemoteAddress64_2(XBeeAddress64 remoteAddress64_2) { - this.remoteAddress64_2 = remoteAddress64_2; - } - - public XBeeAddress16 getRemoteAddress16_2() { - return remoteAddress16_2; - } - - public void setRemoteAddress16_2(XBeeAddress16 remoteAddress16_2) { - this.remoteAddress16_2 = remoteAddress16_2; - } - - public String getNodeIdentifier() { - return nodeIdentifier; - } - - public void setNodeIdentifier(String nodeIdentifier) { - this.nodeIdentifier = nodeIdentifier; - } - - public XBeeAddress16 getParentAddress() { - return parentAddress; - } - - public void setParentAddress(XBeeAddress16 parentAddress) { - this.parentAddress = parentAddress; - } - - public DeviceType getDeviceType() { - return deviceType; - } - - public void setDeviceType(DeviceType deviceType) { - this.deviceType = deviceType; - } - - public SourceAction getSourceAction() { - return sourceAction; - } - - public void setSourceAction(SourceAction sourceAction) { - this.sourceAction = sourceAction; - } - - public DoubleByte getProfileId() { - return profileId; - } - - public void setProfileId(DoubleByte profileId) { - this.profileId = profileId; - } - - public DoubleByte getMfgId() { - return mfgId; - } - - public void setMfgId(DoubleByte mfgId) { - this.mfgId = mfgId; - } - - public void parse(IPacketParser parser) throws IOException { - this.setRemoteAddress64(parser.parseAddress64()); - this.setRemoteAddress16(parser.parseAddress16()); - - int option = parser.read("Option"); - - this.setOption(ZNetRxBaseResponse.getOption(option)); - - this.setRemoteAddress16_2(parser.parseAddress16()); - this.setRemoteAddress64_2(parser.parseAddress64()); - - - StringBuffer ni = new StringBuffer(); - - int ch = 0; - - // NI is terminated with 0 - while ((ch = parser.read("Node Identifier")) != 0) { - ni.append((char)ch); - } - - this.setNodeIdentifier(ni.toString()); - this.setParentAddress(parser.parseAddress16()); - - int deviceType = parser.read("Device Type"); - - if (DeviceType.get(deviceType) != null) { - this.setDeviceType(DeviceType.get(deviceType)); - } else { - log.warn("Unknown device type " + deviceType); - this.setDeviceType(DeviceType.UNKNOWN); - } - - int sourceAction = parser.read("Source Action"); - - if (SourceAction.get(sourceAction) != null) { - this.setSourceAction(SourceAction.get(sourceAction)); - } else { - log.warn("Unknown source event " + sourceAction); - this.setSourceAction(SourceAction.UNKNOWN); - } - - - DoubleByte profileId = new DoubleByte(); - profileId.setMsb(parser.read("Profile MSB")); - profileId.setLsb(parser.read("Profile LSB")); - this.setProfileId(profileId); - - DoubleByte mfgId = new DoubleByte(); - mfgId.setMsb(parser.read("MFG MSB")); - mfgId.setLsb(parser.read("MFG LSB")); - this.setMfgId(mfgId); - } - - @Override - public String toString() { - return "ZNetNodeIdentificationResponse [deviceType=" + deviceType - + ", mfgId=" + mfgId + ", nodeIdentifier=" + nodeIdentifier - + ", option=" + option + ", parentAddress=" + parentAddress - + ", profileId=" + profileId + ", remoteAddress16=" - + remoteAddress16 + ", remoteAddress16_2=" + remoteAddress16_2 - + ", remoteAddress64=" + remoteAddress64 - + ", remoteAddress64_2=" + remoteAddress64_2 - + ", sourceAction=" + sourceAction + "]" + - super.toString(); - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.zigbee; + +import java.io.IOException; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.Logger; + +import com.rapplogic.xbee.api.IPacketParser; +import com.rapplogic.xbee.api.XBeeAddress16; +import com.rapplogic.xbee.api.XBeeAddress64; +import com.rapplogic.xbee.api.XBeeResponse; +import com.rapplogic.xbee.api.zigbee.ZNetRxBaseResponse.Option; +import com.rapplogic.xbee.util.DoubleByte; + +public class ZNetNodeIdentificationResponse extends XBeeResponse { + + private static final long serialVersionUID = 5182657003442708884L; + + private final static Logger log = Logger.getLogger(ZNetNodeIdentificationResponse.class); + + // TODO this is repeated in NodeDiscover + public enum DeviceType { + COORDINATOR (0x1), + ROUTER (0x2), + END_DEVICE (0x3), + UNKNOWN(-1); + + private static final Map lookup = new HashMap(); + + static { + for(DeviceType s: EnumSet.allOf(DeviceType.class)) { + lookup.put(s.getValue(), s); + } + } + + public static DeviceType get(int value) { + return lookup.get(value); + } + + private final int value; + + DeviceType(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + public enum SourceAction { + PUSHBUTTON (0x1), + JOINING (0x2), + POWER_CYCLE(0x3), + UNKNOWN(-1); + + private static final Map lookup = new HashMap(); + + static { + for(SourceAction s: EnumSet.allOf(SourceAction.class)) { + lookup.put(s.getValue(), s); + } + } + + public static SourceAction get(int value) { + return lookup.get(value); + } + + private final int value; + + SourceAction(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + private XBeeAddress64 remoteAddress64; + private XBeeAddress16 remoteAddress16; + private Option option; + // TODO Digi WTF why duplicated?? p.70 + private XBeeAddress64 remoteAddress64_2; + private XBeeAddress16 remoteAddress16_2; + + private String nodeIdentifier; + private XBeeAddress16 parentAddress; + private DeviceType deviceType; + private SourceAction sourceAction; + private DoubleByte profileId; + private DoubleByte mfgId; + + public ZNetNodeIdentificationResponse() { + + } + + public XBeeAddress64 getRemoteAddress64() { + return remoteAddress64; + } + + public void setRemoteAddress64(XBeeAddress64 remoteAddress64) { + this.remoteAddress64 = remoteAddress64; + } + + public XBeeAddress16 getRemoteAddress16() { + return remoteAddress16; + } + + public void setRemoteAddress16(XBeeAddress16 remoteAddress16) { + this.remoteAddress16 = remoteAddress16; + } + + public Option getOption() { + return option; + } + + public void setOption(Option option) { + this.option = option; + } + + public XBeeAddress64 getRemoteAddress64_2() { + return remoteAddress64_2; + } + + public void setRemoteAddress64_2(XBeeAddress64 remoteAddress64_2) { + this.remoteAddress64_2 = remoteAddress64_2; + } + + public XBeeAddress16 getRemoteAddress16_2() { + return remoteAddress16_2; + } + + public void setRemoteAddress16_2(XBeeAddress16 remoteAddress16_2) { + this.remoteAddress16_2 = remoteAddress16_2; + } + + public String getNodeIdentifier() { + return nodeIdentifier; + } + + public void setNodeIdentifier(String nodeIdentifier) { + this.nodeIdentifier = nodeIdentifier; + } + + public XBeeAddress16 getParentAddress() { + return parentAddress; + } + + public void setParentAddress(XBeeAddress16 parentAddress) { + this.parentAddress = parentAddress; + } + + public DeviceType getDeviceType() { + return deviceType; + } + + public void setDeviceType(DeviceType deviceType) { + this.deviceType = deviceType; + } + + public SourceAction getSourceAction() { + return sourceAction; + } + + public void setSourceAction(SourceAction sourceAction) { + this.sourceAction = sourceAction; + } + + public DoubleByte getProfileId() { + return profileId; + } + + public void setProfileId(DoubleByte profileId) { + this.profileId = profileId; + } + + public DoubleByte getMfgId() { + return mfgId; + } + + public void setMfgId(DoubleByte mfgId) { + this.mfgId = mfgId; + } + + public void parse(IPacketParser parser) throws IOException { + this.setRemoteAddress64(parser.parseAddress64()); + this.setRemoteAddress16(parser.parseAddress16()); + + int option = parser.read("Option"); + + this.setOption(ZNetRxBaseResponse.getOption(option)); + + this.setRemoteAddress16_2(parser.parseAddress16()); + this.setRemoteAddress64_2(parser.parseAddress64()); + + + StringBuffer ni = new StringBuffer(); + + int ch = 0; + + // NI is terminated with 0 + while ((ch = parser.read("Node Identifier")) != 0) { + ni.append((char)ch); + } + + this.setNodeIdentifier(ni.toString()); + this.setParentAddress(parser.parseAddress16()); + + int deviceType = parser.read("Device Type"); + + if (DeviceType.get(deviceType) != null) { + this.setDeviceType(DeviceType.get(deviceType)); + } else { + log.warn("Unknown device type " + deviceType); + this.setDeviceType(DeviceType.UNKNOWN); + } + + int sourceAction = parser.read("Source Action"); + + if (SourceAction.get(sourceAction) != null) { + this.setSourceAction(SourceAction.get(sourceAction)); + } else { + log.warn("Unknown source event " + sourceAction); + this.setSourceAction(SourceAction.UNKNOWN); + } + + + DoubleByte profileId = new DoubleByte(); + profileId.setMsb(parser.read("Profile MSB")); + profileId.setLsb(parser.read("Profile LSB")); + this.setProfileId(profileId); + + DoubleByte mfgId = new DoubleByte(); + mfgId.setMsb(parser.read("MFG MSB")); + mfgId.setLsb(parser.read("MFG LSB")); + this.setMfgId(mfgId); + } + + @Override + public String toString() { + return "ZNetNodeIdentificationResponse [deviceType=" + deviceType + + ", mfgId=" + mfgId + ", nodeIdentifier=" + nodeIdentifier + + ", option=" + option + ", parentAddress=" + parentAddress + + ", profileId=" + profileId + ", remoteAddress16=" + + remoteAddress16 + ", remoteAddress16_2=" + remoteAddress16_2 + + ", remoteAddress64=" + remoteAddress64 + + ", remoteAddress64_2=" + remoteAddress64_2 + + ", sourceAction=" + sourceAction + "]" + + super.toString(); + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/zigbee/ZNetRxBaseResponse.java b/src/main/java/com/rapplogic/xbee/api/zigbee/ZNetRxBaseResponse.java similarity index 95% rename from src/com/rapplogic/xbee/api/zigbee/ZNetRxBaseResponse.java rename to src/main/java/com/rapplogic/xbee/api/zigbee/ZNetRxBaseResponse.java index df800a7..97e7a91 100644 --- a/src/com/rapplogic/xbee/api/zigbee/ZNetRxBaseResponse.java +++ b/src/main/java/com/rapplogic/xbee/api/zigbee/ZNetRxBaseResponse.java @@ -1,156 +1,158 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.zigbee; - -import java.io.IOException; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; - -import org.apache.log4j.Logger; - -import com.rapplogic.xbee.api.IPacketParser; -import com.rapplogic.xbee.api.XBeeAddress16; -import com.rapplogic.xbee.api.XBeeAddress64; -import com.rapplogic.xbee.api.XBeeResponse; - -/** - * Series 2 XBee. Super class of all Receive packets. - *

- * Note: ZNet RX packets do not include RSSI since it is a mesh network and potentially requires several - * hops to get to the destination. The RSSI of the last hop is available using the DB AT command. - * If your network is not mesh (i.e. composed of a single coordinator and end devices -- no routers) - * then the DB command should provide accurate RSSI. - *

- * @author Andrew Rapp - * - */ -public abstract class ZNetRxBaseResponse extends XBeeResponse { - - private final static Logger log = Logger.getLogger(ZNetRxBaseResponse.class); - - public enum Option { -// 0x01 - Packet Acknowledged -// 0x02 - Packet was a broadcast packet -// 0x20 - Packet encrypted with APS encryption -// 0x40 - Packet was sent from an end device (if known) -// Note: Option values can be combined. For example, a -// 0x40 and a 0x01 will show as a 0x41. Other possible -// values 0x21, 0x22, 0x41, 0x42, 0x60, 0x61, 0x62. - - // TODO ugh this is mess now with bitfield indicators - // TODO ditch the enum, and replace with a class that has isBroadcast(), isPacketAcknowledged() etc - - PACKET_ACKNOWLEDGED (0x01), - BROADCAST_PACKET (0x02), - PACKET_ENCRYPTED_WITH_APS (0x20), - PACKET_SENT_FROM_END_DEVICE(0x40), - PACKET_ACKNOWLEDGED_0x21 (0x21), - PACKET_ACKNOWLEDGED_0x41 (0x41), - PACKET_ACKNOWLEDGED_0x61 (0x61), - PACKET_ENCRYPTED_WITH_APS_PACKET_SENT_FROM_END_DEVICE (0x60), - BROADCAST_PACKET_0x22 (0x22), - BROADCAST_PACKET_0x42 (0x42), - BROADCAST_PACKET_0x62 (0x62), - UNKNOWN(-1); - - private static final Map lookup = new HashMap(); - - static { - for(Option s : EnumSet.allOf(Option.class)) { - lookup.put(s.getValue(), s); - } - } - - public static Option get(int value) { - return lookup.get(value); - } - - private final int value; - - Option(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - } - - // TODO where is frame id?? - - private XBeeAddress64 remoteAddress64; - private XBeeAddress16 remoteAddress16; - private Option option; - - public ZNetRxBaseResponse() { - - } - - public XBeeAddress64 getRemoteAddress64() { - return remoteAddress64; - } - - public void setRemoteAddress64(XBeeAddress64 remoteAddress64) { - this.remoteAddress64 = remoteAddress64; - } - - public XBeeAddress16 getRemoteAddress16() { - return remoteAddress16; - } - - public void setRemoteAddress16(XBeeAddress16 remoteAddress16) { - this.remoteAddress16 = remoteAddress16; - } - - public Option getOption() { - return option; - } - - public void setOption(Option option) { - this.option = option; - } - - protected void parseAddress(IPacketParser parser) throws IOException { - this.setRemoteAddress64(parser.parseAddress64()); - this.setRemoteAddress16(parser.parseAddress16()); - } - - protected static Option getOption(int option) { - if (Option.get(option) != null) { - return Option.get(option); - } else { - log.warn("Unknown response option " + option); - return Option.UNKNOWN; - } - } - - protected void parseOption(IPacketParser parser) throws IOException { - int option = parser.read("ZNet RX Response Option"); - this.setOption(this.getOption(option)); - } - - public String toString() { - return super.toString() + - ",remoteAddress64=" + this.remoteAddress64 + - ",remoteAddress16=" + this.remoteAddress16 + - ",option=" + this.option; - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.zigbee; + +import java.io.IOException; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.Logger; + +import com.rapplogic.xbee.api.IPacketParser; +import com.rapplogic.xbee.api.XBeeAddress16; +import com.rapplogic.xbee.api.XBeeAddress64; +import com.rapplogic.xbee.api.XBeeResponse; + +/** + * Series 2 XBee. Super class of all Receive packets. + *

+ * Note: ZNet RX packets do not include RSSI since it is a mesh network and potentially requires several + * hops to get to the destination. The RSSI of the last hop is available using the DB AT command. + * If your network is not mesh (i.e. composed of a single coordinator and end devices -- no routers) + * then the DB command should provide accurate RSSI. + *

+ * @author Andrew Rapp + * + */ +public abstract class ZNetRxBaseResponse extends XBeeResponse { + + private static final long serialVersionUID = -6988650066046251132L; + + private final static Logger log = Logger.getLogger(ZNetRxBaseResponse.class); + + public enum Option { +// 0x01 - Packet Acknowledged +// 0x02 - Packet was a broadcast packet +// 0x20 - Packet encrypted with APS encryption +// 0x40 - Packet was sent from an end device (if known) +// Note: Option values can be combined. For example, a +// 0x40 and a 0x01 will show as a 0x41. Other possible +// values 0x21, 0x22, 0x41, 0x42, 0x60, 0x61, 0x62. + + // TODO ugh this is mess now with bitfield indicators + // TODO ditch the enum, and replace with a class that has isBroadcast(), isPacketAcknowledged() etc + + PACKET_ACKNOWLEDGED (0x01), + BROADCAST_PACKET (0x02), + PACKET_ENCRYPTED_WITH_APS (0x20), + PACKET_SENT_FROM_END_DEVICE(0x40), + PACKET_ACKNOWLEDGED_0x21 (0x21), + PACKET_ACKNOWLEDGED_0x41 (0x41), + PACKET_ACKNOWLEDGED_0x61 (0x61), + PACKET_ENCRYPTED_WITH_APS_PACKET_SENT_FROM_END_DEVICE (0x60), + BROADCAST_PACKET_0x22 (0x22), + BROADCAST_PACKET_0x42 (0x42), + BROADCAST_PACKET_0x62 (0x62), + UNKNOWN(-1); + + private static final Map lookup = new HashMap(); + + static { + for(Option s : EnumSet.allOf(Option.class)) { + lookup.put(s.getValue(), s); + } + } + + public static Option get(int value) { + return lookup.get(value); + } + + private final int value; + + Option(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + // TODO where is frame id?? + + private XBeeAddress64 remoteAddress64; + private XBeeAddress16 remoteAddress16; + private Option option; + + public ZNetRxBaseResponse() { + + } + + public XBeeAddress64 getRemoteAddress64() { + return remoteAddress64; + } + + public void setRemoteAddress64(XBeeAddress64 remoteAddress64) { + this.remoteAddress64 = remoteAddress64; + } + + public XBeeAddress16 getRemoteAddress16() { + return remoteAddress16; + } + + public void setRemoteAddress16(XBeeAddress16 remoteAddress16) { + this.remoteAddress16 = remoteAddress16; + } + + public Option getOption() { + return option; + } + + public void setOption(Option option) { + this.option = option; + } + + protected void parseAddress(IPacketParser parser) throws IOException { + this.setRemoteAddress64(parser.parseAddress64()); + this.setRemoteAddress16(parser.parseAddress16()); + } + + protected static Option getOption(int option) { + if (Option.get(option) != null) { + return Option.get(option); + } else { + log.warn("Unknown response option " + option); + return Option.UNKNOWN; + } + } + + protected void parseOption(IPacketParser parser) throws IOException { + int option = parser.read("ZNet RX Response Option"); + this.setOption(ZNetRxBaseResponse.getOption(option)); + } + + public String toString() { + return super.toString() + + ",remoteAddress64=" + this.remoteAddress64 + + ",remoteAddress16=" + this.remoteAddress16 + + ",option=" + this.option; + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/zigbee/ZNetRxIoSampleResponse.java b/src/main/java/com/rapplogic/xbee/api/zigbee/ZNetRxIoSampleResponse.java similarity index 95% rename from src/com/rapplogic/xbee/api/zigbee/ZNetRxIoSampleResponse.java rename to src/main/java/com/rapplogic/xbee/api/zigbee/ZNetRxIoSampleResponse.java index c95d167..e930a62 100644 --- a/src/com/rapplogic/xbee/api/zigbee/ZNetRxIoSampleResponse.java +++ b/src/main/java/com/rapplogic/xbee/api/zigbee/ZNetRxIoSampleResponse.java @@ -1,549 +1,551 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.zigbee; - -import java.io.IOException; - -import org.apache.log4j.Logger; - -import com.rapplogic.xbee.api.AtCommandResponse; -import com.rapplogic.xbee.api.IPacketParser; -import com.rapplogic.xbee.api.NoRequestResponse; -import com.rapplogic.xbee.api.XBeeParseException; -import com.rapplogic.xbee.util.ByteUtils; -import com.rapplogic.xbee.util.IIntInputStream; -import com.rapplogic.xbee.util.IntArrayInputStream; - -/** - * Series 2 XBee. Represents an I/O Sample response sent from a remote radio. - * Provides access to the XBee's 4 Analog (0-4), 11 Digital (0-7,10-12), and 1 Supply Voltage pins - *

- * Note: Series 2 XBee does not support multiple samples (IT) per packet - *

- * @author andrew - * - */ -public class ZNetRxIoSampleResponse extends ZNetRxBaseResponse implements NoRequestResponse { - - private final static Logger log = Logger.getLogger(ZNetRxIoSampleResponse.class); - - private int digitalChannelMaskMsb; - private int digitalChannelMaskLsb; - private int analogChannelMask; - - // all values that may not be in the packet use Integer to distinguish between null and non-null - private Integer dioMsb; - private Integer dioLsb; - - private final static int SUPPLY_VOLTAGE_INDEX = 4; - private Integer[] analog = new Integer[5]; - - public ZNetRxIoSampleResponse() { - - } - - public static ZNetRxIoSampleResponse parseIsSample(AtCommandResponse response) throws IOException { - - if (!response.getCommand().equals("IS")) { - throw new RuntimeException("This is only applicable to the \"IS\" AT command"); - } - - IntArrayInputStream in = new IntArrayInputStream(response.getValue()); - ZNetRxIoSampleResponse sample = new ZNetRxIoSampleResponse(); - sample.parseIoSample(in); - - return sample; - } - - public void parse(IPacketParser parser) throws IOException { - this.parseAddress(parser); - this.parseOption(parser); - this.parseIoSample((IIntInputStream)parser); - } - - /** - * This method is a bit non standard since it needs to parse an IO sample - * from either a RX response or a Remote AT/Local AT response (IS). - * - * @param ps - * @throws IOException - */ - public void parseIoSample(IIntInputStream parser) throws IOException { - // eat sample size.. always 1 - int size = parser.read("ZNet RX IO Sample Size"); - - if (size != 1) { - throw new XBeeParseException("Sample size is not supported if > 1 for ZNet I/O"); - } - - this.setDigitalChannelMaskMsb(parser.read("ZNet RX IO Sample Digital Mask 1")); - this.setDigitalChannelMaskLsb(parser.read("ZNet RX IO Sample Digital Mask 2")); - this.setAnalogChannelMask(parser.read("ZNet RX IO Sample Analog Channel Mask")); - - // zero out n/a bits - this.analogChannelMask = this.analogChannelMask & 0x8f; //10001111 - // zero out all but bits 3-5 - // TODO apparent bug: channel mask on ZigBee Pro firmware has DIO10/P0 as enabled even though it's set to 01 (RSSI). Digital value reports low. - this.digitalChannelMaskMsb = this.digitalChannelMaskMsb & 0x1c; //11100 - - if (this.containsDigital()) { - log.info("response contains digital data"); - // next two bytes are digital - this.setDioMsb(parser.read("ZNet RX IO DIO MSB")); - this.setDioLsb(parser.read("ZNet RX IO DIO LSB")); - } else { - log.info("response does not contain digital data"); - } - - // parse 10-bit analog values - - int enabledCount = 0; - - for (int i = 0; i < 4; i++) { - if (this.isAnalogEnabled(i)) { - log.info("response contains analog[" + i + "]"); - analog[i] = ByteUtils.parse10BitAnalog(parser, enabledCount); - enabledCount++; - } - } - - if (this.isSupplyVoltageEnabled()) { - analog[SUPPLY_VOLTAGE_INDEX] = ByteUtils.parse10BitAnalog(parser, enabledCount); - enabledCount++; - } - - log.debug("There are " + analog + " analog inputs in this packet"); - } - - public int getDigitalChannelMaskMsb() { - return digitalChannelMaskMsb; - } - - private void setDigitalChannelMaskMsb(int digitalChannelMask1) { - this.digitalChannelMaskMsb = digitalChannelMask1; - } - - public int getDigitalChannelMaskLsb() { - return digitalChannelMaskLsb; - } - - private void setDigitalChannelMaskLsb(int digitalChannelMask2) { - this.digitalChannelMaskLsb = digitalChannelMask2; - } - - public int getAnalogChannelMask() { - return analogChannelMask; - } - - private void setAnalogChannelMask(int analogChannelMask) { - this.analogChannelMask = analogChannelMask; - } - - /** - * Consider using isDigitalEnabled(pin) instead - * - * @return - */ - public boolean isD0Enabled() { - return this.isDigitalEnabled(0); - } - - public boolean isD1Enabled() { - return this.isDigitalEnabled(1); - } - - public boolean isD2Enabled() { - return this.isDigitalEnabled(2); - } - - public boolean isD3Enabled() { - return this.isDigitalEnabled(3); - } - - public boolean isD4Enabled() { - return this.isDigitalEnabled(4); - } - - public boolean isD5Enabled() { - return this.isDigitalEnabled(5); - } - - public boolean isD6Enabled() { - return this.isDigitalEnabled(6); - } - - public boolean isD7Enabled() { - return this.isDigitalEnabled(7); - } - - public boolean isD10Enabled() { - return this.isDigitalEnabled(10); - } - - public boolean isD11Enabled() { - return this.isDigitalEnabled(11); - } - - public boolean isD12Enabled() { - return this.isDigitalEnabled(12); - } - - /** - * Consider using isAnalogEnable(pin) instead - * - * @return - */ - public boolean isA0Enabled() { - return this.isAnalogEnabled(0); - } - - public boolean isA1Enabled() { - return this.isAnalogEnabled(1); - } - - public boolean isA2Enabled() { - return this.isAnalogEnabled(2); - } - - public boolean isA3Enabled() { - return this.isAnalogEnabled(3); - } - - public boolean isDigitalEnabled(int pin) { - if (pin >=0 && pin <= 7) { - return ByteUtils.getBit(this.digitalChannelMaskLsb, pin + 1); - } else if (pin >=10 && pin <= 12) { - return ByteUtils.getBit(this.digitalChannelMaskMsb, pin - 7); - } else { - throw new IllegalArgumentException("Unsupported pin: " + pin); - } - } - - public boolean isAnalogEnabled(int pin) { - if (pin >=0 && pin <= 3) { - return ByteUtils.getBit(this.analogChannelMask, pin + 1); - } else { - throw new IllegalArgumentException("Unsupported pin: " + pin); - } - } - - /** - * (from the spec) The voltage supply threshold is set with the V+ command. If the measured supply voltage falls - * below or equal to this threshold, the supply voltage will be included in the IO sample set. V+ is - * set to 0 by default (do not include the supply voltage). - - * @return - */ - public boolean isSupplyVoltageEnabled() { - return ByteUtils.getBit(this.analogChannelMask, 8); - } - - /** - * Consider using isDigitalOn(pin) - * - * @return - */ - public Boolean isD0On() { - if (isDigitalEnabled(0)) { - return isDigitalOn(0); - } - - return null; - } - - // consider using underscore for readability (isD1_On) - public Boolean isD1On() { - if (isDigitalEnabled(1)) { - return isDigitalOn(1); - } - - return null; - } - - public Boolean isD2On() { - if (isDigitalEnabled(2)) { - return isDigitalOn(2); - } - - return null; - } - - public Boolean isD3On() { - if (isDigitalEnabled(3)) { - return isDigitalOn(3); - } - - return null; - } - - public Boolean isD4On() { - if (isDigitalEnabled(4)) { - return isDigitalOn(4); - } - - return null; - } - - public Boolean isD5On() { - if (isDigitalEnabled(5)) { - return isDigitalOn(5); - } - - return null; - } - - public Boolean isD6On() { - if (isDigitalEnabled(6)) { - return isDigitalOn(6); - } - - return null; - } - - public Boolean isD7On() { - if (isDigitalEnabled(7)) { - return isDigitalOn(7); - } - - return null; - } - - public Boolean isD10On() { - if (isDigitalEnabled(10)) { - return isDigitalOn(10); - } - - return null; - } - - public Boolean isD11On() { - if (isDigitalEnabled(11)) { - return isDigitalOn(11); - } - - return null; - } - - public Boolean isD12On() { - if (isDigitalEnabled(12)) { - return isDigitalOn(12); - } - - return null; - } - - /** - * If digital I/O line (DIO0) is enabled: returns true if digital 0 is HIGH (ON); false if it is LOW (OFF). - * If digital I/O line is not enabled this method returns null as it has no value. - *

- * Important: the pin number corresponds to the logical pin (e.g. D4), not the physical pin number. - *

- * Digital I/O pins seem to report high when open circuit (unconnected) - * - * @return - */ - public Boolean isDigitalOn(int pin) { - if (this.isDigitalEnabled(pin)) { - if (pin >=0 && pin <= 7) { - return ByteUtils.getBit(dioLsb, pin + 1); - } else if (pin >=10 && pin <= 12) { - return ByteUtils.getBit(dioMsb, pin - 7); - } - } - - return null; - } - - /** - * Returns true if this sample contains data from digital inputs - * - * See manual page 68 for byte bit mapping - * - * @return - */ - public boolean containsDigital() { - return this.getDigitalChannelMaskMsb() > 0 || this.getDigitalChannelMaskLsb() > 0; - } - - /** - * Returns true if this sample contains data from analog inputs or supply voltage - * - * How does supply voltage get enabled?? - * - * See manual page 68 for byte bit mapping - * - * @return - */ - public boolean containsAnalog() { - return this.getAnalogChannelMask() > 0; - } - - /** - * Returns the DIO MSB, only if sample contains digital; null otherwise - * - * @return - */ - public Integer getDioMsb() { - return dioMsb; - } - - private void setDioMsb(Integer dioMsb) { - this.dioMsb = dioMsb; - } - - /** - * Returns the DIO LSB, only if sample contains digital; null otherwise - * - * @return - */ - public Integer getDioLsb() { - return dioLsb; - } - - private void setDioLsb(Integer dioLsb) { - this.dioLsb = dioLsb; - } - - /** - * Consider using getAnalog(pin) instead - * - * @return - */ - public Integer getAnalog0() { - return analog[0]; - } - - public void setAnalog0(Integer analog0) { - analog[0] = analog0; - } - - public Integer getAnalog1() { - return analog[1]; - } - - public void setAnalog1(Integer analog1) { - analog[1] = analog1; - } - - public Integer getAnalog2() { - return analog[2]; - } - - public void setAnalog2(Integer analog2) { - analog[2] = analog2; - } - - public Integer getAnalog3() { - return analog[3]; - } - - public void setAnalog3(Integer analog3) { - analog[3] = analog3; - } - - /** - * Returns a 10 bit value of ADC line 0, if enabled. - * Returns null if ADC line 0 is not enabled. - *

- * The range of Digi XBee series 2 ADC is 0 - 1.2V and although I couldn't find this in the spec - * a few google searches seems to confirm. When I connected 3.3V to just one of the ADC pins, it - * displayed its displeasure by reporting all ADC pins at 1023. - *

- * Analog pins seem to float around 512 when open circuit - *

- * The reason this returns null is to prevent bugs in the event that you thought you were reading the - * actual value when the pin is not enabled. - * - * @return - */ - public Integer getAnalog(int pin) { - if (this.isAnalogEnabled(pin)) { - return analog[pin]; - } - - return null; - } - -// public Integer getAnalog(int pin) { -// // analog starts 19 bytes after MSB length, if no dio enabled -// int startIndex = 18; -// -// if (!isAnalogEnabled(pin)) { -// // this pin is not turned on -// return null; -// } -// -// if (this.containsDigital()) { -// // make room for digital i/o -// startIndex+=2; -// } -// -// // start depends on how many pins before this pin are enabled -// for (int i = 0; i < pin; i++) { -// if (isAnalogEnabled(i)) { -// startIndex+=2; -// } -// } -// -// return (this.getProcessedPacketBytes()[startIndex] << 8) + this.getProcessedPacketBytes()[startIndex + 1]; -// } - - public Integer getSupplyVoltage() { - return analog[SUPPLY_VOLTAGE_INDEX]; - } - - public void setSupplyVoltage(Integer supplyVoltage) { - analog[SUPPLY_VOLTAGE_INDEX] = supplyVoltage; - } - - public String toString() { - StringBuilder builder = new StringBuilder(); - - builder.append(super.toString()); - - if (this.containsDigital()) { - for (int i = 0; i <= 7; i++) { - if (this.isDigitalEnabled(i)) { - builder.append(",digital[" + i + "]=" + (this.isDigitalOn(i) ? "high" : "low")); - } - } - - for (int i = 10; i <= 12; i++) { - if (this.isDigitalEnabled(i)) { - builder.append(",digital[" + i + "]=" + (this.isDigitalOn(i) ? "high" : "low")); - } - } - } - - if (this.containsAnalog()) { - for (int i = 0; i <= 3; i++) { - if (this.isAnalogEnabled(i)) { - builder.append(",analog[" + i + "]=" + this.getAnalog(i)); - } - } - - if (this.isSupplyVoltageEnabled()) { - builder.append(",supplyVoltage=" + this.getSupplyVoltage()); - } - } - - return builder.toString(); - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.zigbee; + +import java.io.IOException; + +import org.apache.log4j.Logger; + +import com.rapplogic.xbee.api.IPacketParser; +import com.rapplogic.xbee.api.XBeeParseException; +import com.rapplogic.xbee.api.responses.AtCommandResponse; +import com.rapplogic.xbee.api.responses.NoRequestResponse; +import com.rapplogic.xbee.util.ByteUtils; +import com.rapplogic.xbee.util.IIntInputStream; +import com.rapplogic.xbee.util.IntArrayInputStream; + +/** + * Series 2 XBee. Represents an I/O Sample response sent from a remote radio. + * Provides access to the XBee's 4 Analog (0-4), 11 Digital (0-7,10-12), and 1 Supply Voltage pins + *

+ * Note: Series 2 XBee does not support multiple samples (IT) per packet + *

+ * @author andrew + * + */ +public class ZNetRxIoSampleResponse extends ZNetRxBaseResponse implements NoRequestResponse { + + private static final long serialVersionUID = 8598971303883889698L; + + private final static Logger log = Logger.getLogger(ZNetRxIoSampleResponse.class); + + private int digitalChannelMaskMsb; + private int digitalChannelMaskLsb; + private int analogChannelMask; + + // all values that may not be in the packet use Integer to distinguish between null and non-null + private Integer dioMsb; + private Integer dioLsb; + + private final static int SUPPLY_VOLTAGE_INDEX = 4; + private Integer[] analog = new Integer[5]; + + public ZNetRxIoSampleResponse() { + + } + + public static ZNetRxIoSampleResponse parseIsSample(AtCommandResponse response) throws IOException { + + if (!response.getCommand().equals("IS")) { + throw new RuntimeException("This is only applicable to the \"IS\" AT command"); + } + + IntArrayInputStream in = new IntArrayInputStream(response.getValue()); + ZNetRxIoSampleResponse sample = new ZNetRxIoSampleResponse(); + sample.parseIoSample(in); + + return sample; + } + + public void parse(IPacketParser parser) throws IOException { + this.parseAddress(parser); + this.parseOption(parser); + this.parseIoSample((IIntInputStream)parser); + } + + /** + * This method is a bit non standard since it needs to parse an IO sample + * from either a RX response or a Remote AT/Local AT response (IS). + * + * @param ps + * @throws IOException + */ + public void parseIoSample(IIntInputStream parser) throws IOException { + // eat sample size.. always 1 + int size = parser.read("ZNet RX IO Sample Size"); + + if (size != 1) { + throw new XBeeParseException("Sample size is not supported if > 1 for ZNet I/O"); + } + + this.setDigitalChannelMaskMsb(parser.read("ZNet RX IO Sample Digital Mask 1")); + this.setDigitalChannelMaskLsb(parser.read("ZNet RX IO Sample Digital Mask 2")); + this.setAnalogChannelMask(parser.read("ZNet RX IO Sample Analog Channel Mask")); + + // zero out n/a bits + this.analogChannelMask = this.analogChannelMask & 0x8f; //10001111 + // zero out all but bits 3-5 + // TODO apparent bug: channel mask on ZigBee Pro firmware has DIO10/P0 as enabled even though it's set to 01 (RSSI). Digital value reports low. + this.digitalChannelMaskMsb = this.digitalChannelMaskMsb & 0x1c; //11100 + + if (this.containsDigital()) { + log.info("response contains digital data"); + // next two bytes are digital + this.setDioMsb(parser.read("ZNet RX IO DIO MSB")); + this.setDioLsb(parser.read("ZNet RX IO DIO LSB")); + } else { + log.info("response does not contain digital data"); + } + + // parse 10-bit analog values + + int enabledCount = 0; + + for (int i = 0; i < 4; i++) { + if (this.isAnalogEnabled(i)) { + log.info("response contains analog[" + i + "]"); + analog[i] = ByteUtils.parse10BitAnalog(parser, enabledCount); + enabledCount++; + } + } + + if (this.isSupplyVoltageEnabled()) { + analog[SUPPLY_VOLTAGE_INDEX] = ByteUtils.parse10BitAnalog(parser, enabledCount); + enabledCount++; + } + + log.debug("There are " + analog + " analog inputs in this packet"); + } + + public int getDigitalChannelMaskMsb() { + return digitalChannelMaskMsb; + } + + private void setDigitalChannelMaskMsb(int digitalChannelMask1) { + this.digitalChannelMaskMsb = digitalChannelMask1; + } + + public int getDigitalChannelMaskLsb() { + return digitalChannelMaskLsb; + } + + private void setDigitalChannelMaskLsb(int digitalChannelMask2) { + this.digitalChannelMaskLsb = digitalChannelMask2; + } + + public int getAnalogChannelMask() { + return analogChannelMask; + } + + private void setAnalogChannelMask(int analogChannelMask) { + this.analogChannelMask = analogChannelMask; + } + + /** + * Consider using isDigitalEnabled(pin) instead + * + * @return + */ + public boolean isD0Enabled() { + return this.isDigitalEnabled(0); + } + + public boolean isD1Enabled() { + return this.isDigitalEnabled(1); + } + + public boolean isD2Enabled() { + return this.isDigitalEnabled(2); + } + + public boolean isD3Enabled() { + return this.isDigitalEnabled(3); + } + + public boolean isD4Enabled() { + return this.isDigitalEnabled(4); + } + + public boolean isD5Enabled() { + return this.isDigitalEnabled(5); + } + + public boolean isD6Enabled() { + return this.isDigitalEnabled(6); + } + + public boolean isD7Enabled() { + return this.isDigitalEnabled(7); + } + + public boolean isD10Enabled() { + return this.isDigitalEnabled(10); + } + + public boolean isD11Enabled() { + return this.isDigitalEnabled(11); + } + + public boolean isD12Enabled() { + return this.isDigitalEnabled(12); + } + + /** + * Consider using isAnalogEnable(pin) instead + * + * @return + */ + public boolean isA0Enabled() { + return this.isAnalogEnabled(0); + } + + public boolean isA1Enabled() { + return this.isAnalogEnabled(1); + } + + public boolean isA2Enabled() { + return this.isAnalogEnabled(2); + } + + public boolean isA3Enabled() { + return this.isAnalogEnabled(3); + } + + public boolean isDigitalEnabled(int pin) { + if (pin >=0 && pin <= 7) { + return ByteUtils.getBit(this.digitalChannelMaskLsb, pin + 1); + } else if (pin >=10 && pin <= 12) { + return ByteUtils.getBit(this.digitalChannelMaskMsb, pin - 7); + } else { + throw new IllegalArgumentException("Unsupported pin: " + pin); + } + } + + public boolean isAnalogEnabled(int pin) { + if (pin >=0 && pin <= 3) { + return ByteUtils.getBit(this.analogChannelMask, pin + 1); + } else { + throw new IllegalArgumentException("Unsupported pin: " + pin); + } + } + + /** + * (from the spec) The voltage supply threshold is set with the V+ command. If the measured supply voltage falls + * below or equal to this threshold, the supply voltage will be included in the IO sample set. V+ is + * set to 0 by default (do not include the supply voltage). + + * @return + */ + public boolean isSupplyVoltageEnabled() { + return ByteUtils.getBit(this.analogChannelMask, 8); + } + + /** + * Consider using isDigitalOn(pin) + * + * @return + */ + public Boolean isD0On() { + if (isDigitalEnabled(0)) { + return isDigitalOn(0); + } + + return null; + } + + // consider using underscore for readability (isD1_On) + public Boolean isD1On() { + if (isDigitalEnabled(1)) { + return isDigitalOn(1); + } + + return null; + } + + public Boolean isD2On() { + if (isDigitalEnabled(2)) { + return isDigitalOn(2); + } + + return null; + } + + public Boolean isD3On() { + if (isDigitalEnabled(3)) { + return isDigitalOn(3); + } + + return null; + } + + public Boolean isD4On() { + if (isDigitalEnabled(4)) { + return isDigitalOn(4); + } + + return null; + } + + public Boolean isD5On() { + if (isDigitalEnabled(5)) { + return isDigitalOn(5); + } + + return null; + } + + public Boolean isD6On() { + if (isDigitalEnabled(6)) { + return isDigitalOn(6); + } + + return null; + } + + public Boolean isD7On() { + if (isDigitalEnabled(7)) { + return isDigitalOn(7); + } + + return null; + } + + public Boolean isD10On() { + if (isDigitalEnabled(10)) { + return isDigitalOn(10); + } + + return null; + } + + public Boolean isD11On() { + if (isDigitalEnabled(11)) { + return isDigitalOn(11); + } + + return null; + } + + public Boolean isD12On() { + if (isDigitalEnabled(12)) { + return isDigitalOn(12); + } + + return null; + } + + /** + * If digital I/O line (DIO0) is enabled: returns true if digital 0 is HIGH (ON); false if it is LOW (OFF). + * If digital I/O line is not enabled this method returns null as it has no value. + *

+ * Important: the pin number corresponds to the logical pin (e.g. D4), not the physical pin number. + *

+ * Digital I/O pins seem to report high when open circuit (unconnected) + * + * @return + */ + public Boolean isDigitalOn(int pin) { + if (this.isDigitalEnabled(pin)) { + if (pin >=0 && pin <= 7) { + return ByteUtils.getBit(dioLsb, pin + 1); + } else if (pin >=10 && pin <= 12) { + return ByteUtils.getBit(dioMsb, pin - 7); + } + } + + return null; + } + + /** + * Returns true if this sample contains data from digital inputs + * + * See manual page 68 for byte bit mapping + * + * @return + */ + public boolean containsDigital() { + return this.getDigitalChannelMaskMsb() > 0 || this.getDigitalChannelMaskLsb() > 0; + } + + /** + * Returns true if this sample contains data from analog inputs or supply voltage + * + * How does supply voltage get enabled?? + * + * See manual page 68 for byte bit mapping + * + * @return + */ + public boolean containsAnalog() { + return this.getAnalogChannelMask() > 0; + } + + /** + * Returns the DIO MSB, only if sample contains digital; null otherwise + * + * @return + */ + public Integer getDioMsb() { + return dioMsb; + } + + private void setDioMsb(Integer dioMsb) { + this.dioMsb = dioMsb; + } + + /** + * Returns the DIO LSB, only if sample contains digital; null otherwise + * + * @return + */ + public Integer getDioLsb() { + return dioLsb; + } + + private void setDioLsb(Integer dioLsb) { + this.dioLsb = dioLsb; + } + + /** + * Consider using getAnalog(pin) instead + * + * @return + */ + public Integer getAnalog0() { + return analog[0]; + } + + public void setAnalog0(Integer analog0) { + analog[0] = analog0; + } + + public Integer getAnalog1() { + return analog[1]; + } + + public void setAnalog1(Integer analog1) { + analog[1] = analog1; + } + + public Integer getAnalog2() { + return analog[2]; + } + + public void setAnalog2(Integer analog2) { + analog[2] = analog2; + } + + public Integer getAnalog3() { + return analog[3]; + } + + public void setAnalog3(Integer analog3) { + analog[3] = analog3; + } + + /** + * Returns a 10 bit value of ADC line 0, if enabled. + * Returns null if ADC line 0 is not enabled. + *

+ * The range of Digi XBee series 2 ADC is 0 - 1.2V and although I couldn't find this in the spec + * a few google searches seems to confirm. When I connected 3.3V to just one of the ADC pins, it + * displayed its displeasure by reporting all ADC pins at 1023. + *

+ * Analog pins seem to float around 512 when open circuit + *

+ * The reason this returns null is to prevent bugs in the event that you thought you were reading the + * actual value when the pin is not enabled. + * + * @return + */ + public Integer getAnalog(int pin) { + if (this.isAnalogEnabled(pin)) { + return analog[pin]; + } + + return null; + } + +// public Integer getAnalog(int pin) { +// // analog starts 19 bytes after MSB length, if no dio enabled +// int startIndex = 18; +// +// if (!isAnalogEnabled(pin)) { +// // this pin is not turned on +// return null; +// } +// +// if (this.containsDigital()) { +// // make room for digital i/o +// startIndex+=2; +// } +// +// // start depends on how many pins before this pin are enabled +// for (int i = 0; i < pin; i++) { +// if (isAnalogEnabled(i)) { +// startIndex+=2; +// } +// } +// +// return (this.getProcessedPacketBytes()[startIndex] << 8) + this.getProcessedPacketBytes()[startIndex + 1]; +// } + + public Integer getSupplyVoltage() { + return analog[SUPPLY_VOLTAGE_INDEX]; + } + + public void setSupplyVoltage(Integer supplyVoltage) { + analog[SUPPLY_VOLTAGE_INDEX] = supplyVoltage; + } + + public String toString() { + StringBuilder builder = new StringBuilder(); + + builder.append(super.toString()); + + if (this.containsDigital()) { + for (int i = 0; i <= 7; i++) { + if (this.isDigitalEnabled(i)) { + builder.append(",digital[" + i + "]=" + (this.isDigitalOn(i) ? "high" : "low")); + } + } + + for (int i = 10; i <= 12; i++) { + if (this.isDigitalEnabled(i)) { + builder.append(",digital[" + i + "]=" + (this.isDigitalOn(i) ? "high" : "low")); + } + } + } + + if (this.containsAnalog()) { + for (int i = 0; i <= 3; i++) { + if (this.isAnalogEnabled(i)) { + builder.append(",analog[" + i + "]=" + this.getAnalog(i)); + } + } + + if (this.isSupplyVoltageEnabled()) { + builder.append(",supplyVoltage=" + this.getSupplyVoltage()); + } + } + + return builder.toString(); + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/zigbee/ZNetRxResponse.java b/src/main/java/com/rapplogic/xbee/api/zigbee/ZNetRxResponse.java similarity index 92% rename from src/com/rapplogic/xbee/api/zigbee/ZNetRxResponse.java rename to src/main/java/com/rapplogic/xbee/api/zigbee/ZNetRxResponse.java index 0db24cb..f09d66e 100644 --- a/src/com/rapplogic/xbee/api/zigbee/ZNetRxResponse.java +++ b/src/main/java/com/rapplogic/xbee/api/zigbee/ZNetRxResponse.java @@ -1,63 +1,65 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.zigbee; - -import java.io.IOException; - -import com.rapplogic.xbee.api.IPacketParser; -import com.rapplogic.xbee.api.NoRequestResponse; -import com.rapplogic.xbee.util.ByteUtils; - -/** - * Series 2 XBee. This packet is received when - * a remote XBee sends a ZNetTxRequest - *

- * API ID: 0x90 - *

- * @author andrew - * - */ -public class ZNetRxResponse extends ZNetRxBaseResponse implements NoRequestResponse { - - private int[] data; - - public ZNetRxResponse() { - super(); - } - - public int[] getData() { - return data; - } - - public void setData(int[] data) { - this.data = data; - } - - public void parse(IPacketParser parser) throws IOException { - this.parseAddress(parser); - this.parseOption(parser); - this.setData(parser.readRemainingBytes()); - } - - public String toString() { - return super.toString() + - ",data=" + ByteUtils.toBase16(this.data); - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.zigbee; + +import java.io.IOException; + +import com.rapplogic.xbee.api.IPacketParser; +import com.rapplogic.xbee.api.responses.NoRequestResponse; +import com.rapplogic.xbee.util.ByteUtils; + +/** + * Series 2 XBee. This packet is received when + * a remote XBee sends a ZNetTxRequest + *

+ * API ID: 0x90 + *

+ * @author andrew + * + */ +public class ZNetRxResponse extends ZNetRxBaseResponse implements NoRequestResponse { + + private static final long serialVersionUID = 4719910421796674035L; + + private int[] data; + + public ZNetRxResponse() { + super(); + } + + public int[] getData() { + return data; + } + + public void setData(int[] data) { + this.data = data; + } + + public void parse(IPacketParser parser) throws IOException { + this.parseAddress(parser); + this.parseOption(parser); + this.setData(parser.readRemainingBytes()); + } + + public String toString() { + return super.toString() + + ",data=" + ByteUtils.toBase16(this.data); + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/api/zigbee/ZNetTxRequest.java b/src/main/java/com/rapplogic/xbee/api/zigbee/ZNetTxRequest.java similarity index 96% rename from src/com/rapplogic/xbee/api/zigbee/ZNetTxRequest.java rename to src/main/java/com/rapplogic/xbee/api/zigbee/ZNetTxRequest.java index 93ee444..a0636b9 100644 --- a/src/com/rapplogic/xbee/api/zigbee/ZNetTxRequest.java +++ b/src/main/java/com/rapplogic/xbee/api/zigbee/ZNetTxRequest.java @@ -1,241 +1,243 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.zigbee; - -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; - -import com.rapplogic.xbee.api.ApiId; -import com.rapplogic.xbee.api.XBeeAddress16; -import com.rapplogic.xbee.api.XBeeAddress64; -import com.rapplogic.xbee.api.XBeeRequest; -import com.rapplogic.xbee.util.ByteUtils; -import com.rapplogic.xbee.util.IntArrayOutputStream; - -/** - * Series 2 XBee. Sends a packet to a remote radio. The remote radio - * receives the data as a ZNetRxResponse packet. - *

- * API ID: 0x10 - *

- * @author andrew - * - */ -public class ZNetTxRequest extends XBeeRequest { - - // 10/28/08 the datasheet states 72 is maximum payload size but I was able to push 75 through successfully, - // even with all bytes as escape bytes (a total post-escape packet size of 169!). - - /** - * This is the maximum payload size for ZNet firmware, as specified in the datasheet. - * This value is provided for reference only and is not enforced by this software unless - * max size unless specified in the setMaxPayloadSize(int) method. - * Note: this size refers to the packet size prior to escaping the control bytes. - * Note: ZB Pro firmware provides the ATNP command to determine max payload size. - * For ZB Pro firmware, the TX Status will return a PAYLOAD_TOO_LARGE (0x74) delivery status - * if the payload size is exceeded - */ - public final static int ZNET_MAX_PAYLOAD_SIZE = 72; - public final static int DEFAULT_BROADCAST_RADIUS = 0; - - private XBeeAddress64 destAddr64; - private XBeeAddress16 destAddr16; - private int broadcastRadius; - private Option option; - private int[] payload; - private int maxPayloadSize; - - //TODO frameId should go last in all Request constructors since it is not specific to any one request - - public enum Option { - - // TODO handle new options. option needs to be an int so you can do Option.UNICAST | Option.DISABLE_RETRIES | Option.USE_EXTENDED_TIMEOUT etc - -// 0x01 - Disable retries and route repair -// 0x20 - Enable APS encryption (if EE=1) -// 0x40 - Use the extended transmission timeout - - UNICAST (0), - BROADCAST (8), - DISABLE_RETRIES (1), - ENABLE_APS_ENCRYPTION (0x20), - USE_EXTENDED_TIMEOUT (0x40); - - private static final Map lookup = new HashMap(); - - static { - for(Option s : EnumSet.allOf(Option.class)) { - lookup.put(s.getValue(), s); - } - } - - public static Option get(int value) { - return lookup.get(value); - } - - private final int value; - - Option(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - } - - /** - * From manual p. 33: - * The ZigBee Transmit Request API frame specifies the 64-bit Address and the network address (if - * known) that the packet should be sent to. By supplying both addresses, the module will forego - * network address Discovery and immediately attempt to route the data packet to the remote. If the - * network address of a particular remote changes, network address and route discovery will take - * place to establish a new route to the correct node. Upon successful - * - * Key points: - * - always specify the 64-bit address and also specify the 16-bit address, if known. Set - * the 16-bit network address to 0xfffe if not known. - * - check the 16-bit address of the tx status response frame as it may change. - * - keep a hash table mapping of 64-bit address to 16-bit network address. - * - * - * @param frameId - * @param dest64 - * @param dest16 - * @param broadcastRadius - * @param option - * @param payload - */ - public ZNetTxRequest(int frameId, XBeeAddress64 dest64, XBeeAddress16 dest16, int broadcastRadius, Option option, int[] payload) { - this.setFrameId(frameId); - this.destAddr64 = dest64; - this.destAddr16 = dest16; - this.broadcastRadius = broadcastRadius; - this.option = option; - this.payload = payload; - } - - /** - * Abbreviated constructor for sending a unicast TX packet - * - * @param dest64 - * @param payload - */ - public ZNetTxRequest(XBeeAddress64 dest64, int[] payload) { - this(XBeeRequest.DEFAULT_FRAME_ID, dest64, XBeeAddress16.ZNET_BROADCAST, ZNetTxRequest.DEFAULT_BROADCAST_RADIUS, Option.UNICAST, payload); - } - - protected IntArrayOutputStream getFrameDataAsIntArrayOutputStream() { - - if (this.getMaxPayloadSize() > 0 && payload.length > this.getMaxPayloadSize()) { - throw new IllegalArgumentException("Payload exceeds user-defined maximum payload size of " + this.getMaxPayloadSize() + " bytes. Please package into multiple packets"); - } - - IntArrayOutputStream out = new IntArrayOutputStream(); - - // api id - out.write(this.getApiId().getValue()); - - // frame id (arbitrary byte that will be sent back with ack) - out.write(this.getFrameId()); - - // add 64-bit dest address - out.write(destAddr64.getAddress()); - - // add 16-bit dest address - out.write(destAddr16.getAddress()); - - // write broadcast radius - out.write(broadcastRadius); - - // write options byte - out.write(option.getValue()); - - out.write(payload); - - return out; - } - - public int[] getFrameData() { - return this.getFrameDataAsIntArrayOutputStream().getIntArray(); - } - - public ApiId getApiId() { - return ApiId.ZNET_TX_REQUEST; - } - - public XBeeAddress64 getDestAddr64() { - return destAddr64; - } - - public void setDestAddr64(XBeeAddress64 destAddr64) { - this.destAddr64 = destAddr64; - } - - public XBeeAddress16 getDestAddr16() { - return destAddr16; - } - - public void setDestAddr16(XBeeAddress16 destAddr16) { - this.destAddr16 = destAddr16; - } - - public int getBroadcastRadius() { - return broadcastRadius; - } - - public void setBroadcastRadius(int broadcastRadius) { - this.broadcastRadius = broadcastRadius; - } - - public Option getOption() { - return option; - } - - public void setOption(Option option) { - this.option = option; - } - - public int[] getPayload() { - return payload; - } - - public void setPayload(int[] payload) { - this.payload = payload; - } - - public String toString() { - return super.toString() + - ",destAddr64=" + this.destAddr64 + - ",destAddr16=" + this.destAddr16 + - ",broadcastRadius=" + this.broadcastRadius + - ",option=" + this.option + - ",payload=" + ByteUtils.toBase16(this.payload); - } - - public int getMaxPayloadSize() { - return maxPayloadSize; - } - - public void setMaxPayloadSize(int maxPayloadSize) { - this.maxPayloadSize = maxPayloadSize; - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.zigbee; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +import com.rapplogic.xbee.api.ApiId; +import com.rapplogic.xbee.api.XBeeAddress16; +import com.rapplogic.xbee.api.XBeeAddress64; +import com.rapplogic.xbee.api.XBeeRequest; +import com.rapplogic.xbee.util.ByteUtils; +import com.rapplogic.xbee.util.IntArrayOutputStream; + +/** + * Series 2 XBee. Sends a packet to a remote radio. The remote radio + * receives the data as a ZNetRxResponse packet. + *

+ * API ID: 0x10 + *

+ * @author andrew + * + */ +public class ZNetTxRequest extends XBeeRequest { + + private static final long serialVersionUID = -5391715539731414314L; + + // 10/28/08 the datasheet states 72 is maximum payload size but I was able to push 75 through successfully, + // even with all bytes as escape bytes (a total post-escape packet size of 169!). + + /** + * This is the maximum payload size for ZNet firmware, as specified in the datasheet. + * This value is provided for reference only and is not enforced by this software unless + * max size unless specified in the setMaxPayloadSize(int) method. + * Note: this size refers to the packet size prior to escaping the control bytes. + * Note: ZB Pro firmware provides the ATNP command to determine max payload size. + * For ZB Pro firmware, the TX Status will return a PAYLOAD_TOO_LARGE (0x74) delivery status + * if the payload size is exceeded + */ + public final static int ZNET_MAX_PAYLOAD_SIZE = 72; + public final static int DEFAULT_BROADCAST_RADIUS = 0; + + private XBeeAddress64 destAddr64; + private XBeeAddress16 destAddr16; + private int broadcastRadius; + private Option option; + private int[] payload; + private int maxPayloadSize; + + //TODO frameId should go last in all Request constructors since it is not specific to any one request + + public enum Option { + + // TODO handle new options. option needs to be an int so you can do Option.UNICAST | Option.DISABLE_RETRIES | Option.USE_EXTENDED_TIMEOUT etc + +// 0x01 - Disable retries and route repair +// 0x20 - Enable APS encryption (if EE=1) +// 0x40 - Use the extended transmission timeout + + UNICAST (0), + BROADCAST (8), + DISABLE_RETRIES (1), + ENABLE_APS_ENCRYPTION (0x20), + USE_EXTENDED_TIMEOUT (0x40); + + private static final Map lookup = new HashMap(); + + static { + for(Option s : EnumSet.allOf(Option.class)) { + lookup.put(s.getValue(), s); + } + } + + public static Option get(int value) { + return lookup.get(value); + } + + private final int value; + + Option(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + /** + * From manual p. 33: + * The ZigBee Transmit Request API frame specifies the 64-bit Address and the network address (if + * known) that the packet should be sent to. By supplying both addresses, the module will forego + * network address Discovery and immediately attempt to route the data packet to the remote. If the + * network address of a particular remote changes, network address and route discovery will take + * place to establish a new route to the correct node. Upon successful + * + * Key points: + * - always specify the 64-bit address and also specify the 16-bit address, if known. Set + * the 16-bit network address to 0xfffe if not known. + * - check the 16-bit address of the tx status response frame as it may change. + * - keep a hash table mapping of 64-bit address to 16-bit network address. + * + * + * @param frameId + * @param dest64 + * @param dest16 + * @param broadcastRadius + * @param option + * @param payload + */ + public ZNetTxRequest(int frameId, XBeeAddress64 dest64, XBeeAddress16 dest16, int broadcastRadius, Option option, int[] payload) { + this.setFrameId(frameId); + this.destAddr64 = dest64; + this.destAddr16 = dest16; + this.broadcastRadius = broadcastRadius; + this.option = option; + this.payload = payload; + } + + /** + * Abbreviated constructor for sending a unicast TX packet + * + * @param dest64 + * @param payload + */ + public ZNetTxRequest(XBeeAddress64 dest64, int[] payload) { + this(XBeeRequest.DEFAULT_FRAME_ID, dest64, XBeeAddress16.ZNET_BROADCAST, ZNetTxRequest.DEFAULT_BROADCAST_RADIUS, Option.UNICAST, payload); + } + + protected IntArrayOutputStream getFrameDataAsIntArrayOutputStream() { + + if (this.getMaxPayloadSize() > 0 && payload.length > this.getMaxPayloadSize()) { + throw new IllegalArgumentException("Payload exceeds user-defined maximum payload size of " + this.getMaxPayloadSize() + " bytes. Please package into multiple packets"); + } + + IntArrayOutputStream out = new IntArrayOutputStream(); + + // api id + out.write(this.getApiId().getValue()); + + // frame id (arbitrary byte that will be sent back with ack) + out.write(this.getFrameId()); + + // add 64-bit dest address + out.write(destAddr64.getAddress()); + + // add 16-bit dest address + out.write(destAddr16.getAddress()); + + // write broadcast radius + out.write(broadcastRadius); + + // write options byte + out.write(option.getValue()); + + out.write(payload); + + return out; + } + + public int[] getFrameData() { + return this.getFrameDataAsIntArrayOutputStream().getIntArray(); + } + + public ApiId getApiId() { + return ApiId.ZNET_TX_REQUEST; + } + + public XBeeAddress64 getDestAddr64() { + return destAddr64; + } + + public void setDestAddr64(XBeeAddress64 destAddr64) { + this.destAddr64 = destAddr64; + } + + public XBeeAddress16 getDestAddr16() { + return destAddr16; + } + + public void setDestAddr16(XBeeAddress16 destAddr16) { + this.destAddr16 = destAddr16; + } + + public int getBroadcastRadius() { + return broadcastRadius; + } + + public void setBroadcastRadius(int broadcastRadius) { + this.broadcastRadius = broadcastRadius; + } + + public Option getOption() { + return option; + } + + public void setOption(Option option) { + this.option = option; + } + + public int[] getPayload() { + return payload; + } + + public void setPayload(int[] payload) { + this.payload = payload; + } + + public String toString() { + return super.toString() + + ",destAddr64=" + this.destAddr64 + + ",destAddr16=" + this.destAddr16 + + ",broadcastRadius=" + this.broadcastRadius + + ",option=" + this.option + + ",payload=" + ByteUtils.toBase16(this.payload); + } + + public int getMaxPayloadSize() { + return maxPayloadSize; + } + + public void setMaxPayloadSize(int maxPayloadSize) { + this.maxPayloadSize = maxPayloadSize; + } +} diff --git a/src/com/rapplogic/xbee/api/zigbee/ZNetTxStatusResponse.java b/src/main/java/com/rapplogic/xbee/api/zigbee/ZNetTxStatusResponse.java similarity index 95% rename from src/com/rapplogic/xbee/api/zigbee/ZNetTxStatusResponse.java rename to src/main/java/com/rapplogic/xbee/api/zigbee/ZNetTxStatusResponse.java index ae1eeb5..be39d73 100644 --- a/src/com/rapplogic/xbee/api/zigbee/ZNetTxStatusResponse.java +++ b/src/main/java/com/rapplogic/xbee/api/zigbee/ZNetTxStatusResponse.java @@ -1,236 +1,238 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.api.zigbee; - -import java.io.IOException; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; - -import org.apache.log4j.Logger; - -import com.rapplogic.xbee.api.IPacketParser; -import com.rapplogic.xbee.api.XBeeAddress16; -import com.rapplogic.xbee.api.XBeeFrameIdResponse; - -/** - * Series 2 XBee. This is sent out the UART of the transmitting XBee immediately following - * a Transmit packet. Indicates if the Transmit packet (ZNetTxRequest) - * was successful. - *

- * API ID: 0x8b - *

- * @author andrew - */ -public class ZNetTxStatusResponse extends XBeeFrameIdResponse { - - private final static Logger log = Logger.getLogger(ZNetTxStatusResponse.class); - - public enum DeliveryStatus { -// 0x00 = Success -// 0x01 = MAC ACK Failure -// 0x02 = CCA Failure -// 0x15 = Invalid destination -// endpoint -// 0x21 = Network ACK Failure -// 0x22 = Not Joined to Network -// 0x23 = Self-addressed -// 0x24 = Address Not Found -// 0x25 = Route Not Found -// 0x26 = Broadcast source failed to hear a neighbor relay -// the message -// 0x2B = Invalid binding table index -// 0x2C = Resource error lack of free buffers, timers, etc. -// 0x2D = Attempted broadcast with APS transmission -// 0x2E = Attempted unicast with APS transmission, but -// EE=0 -// 0x32 = Resource error lack of free -// 0x74 = Data payload too large - - SUCCESS (0), - MAC_FAILURE (1), - CCA_FAILURE (0x02), - INVALID_DESTINATION_ENDPOINT (0x15), - NETWORK_ACK_FAILURE (0x21), - NOT_JOINED_TO_NETWORK (0x22), - SELF_ADDRESSED (0x23), - ADDRESS_NOT_FOUND (0x24), - ROUTE_NOT_FOUND (0x25), - BROADCAST_SOURCE_NEIGHBOR_FAILURE (0x26), - INVALID_BINDING_TABLE_INDEX (0x2B), - RESOURCE_ERROR_LACK_FREE_BUFFERS (0x2C), - ATTEMPTED_BROADCAST_WITH_APS_TX (0x2D), - ATTEMPTED_UNICAST_WITH_APS_TX_EE_ZERO (0x2E), - RESOURCE_ERROR_LACK_FREE_BUFFERS_0x32 (0x32), // WUT, SAME AS 2C - PAYLOAD_TOO_LARGE(0x74), // ZB Pro firmware only - UNKNOWN(-1); - - private static final Map lookup = new HashMap(); - - static { - for(DeliveryStatus s : EnumSet.allOf(DeliveryStatus.class)) { - lookup.put(s.getValue(), s); - } - } - - private final int value; - - DeliveryStatus(int value) { - this.value = value; - } - - public static DeliveryStatus get(int value) { - return lookup.get(value); - } - - public int getValue() { - return value; - } - } - - public enum DiscoveryStatus { -// 0x00 = No Discovery Overhead -// 0x01 = Address Discovery -// 0x02 = Route Discovery -// 0x03 = Address and Route -// 0x40 = Extended Timeout Discovery - - // NOTE 0x40 IS A bit field so going to be painful with enums - - NO_DISCOVERY (0), - ADDRESS_DISCOVERY (1), - ROUTE_DISCOVERY (2), - ADDRESS_AND_ROUTE_DISCOVERY (3), - EXTENDED_TIMEOUT_DISCOVERY (0x40), - EXTENDED_TIMEOUT_DISCOVERY_0x41 (0x41), - EXTENDED_TIMEOUT_DISCOVERY_0x42 (0x42), - EXTENDED_TIMEOUT_DISCOVERY_0x43 (0x43), - UNKNOWN(-1); - - private static final Map lookup = new HashMap(); - - static { - for(DiscoveryStatus s : EnumSet.allOf(DiscoveryStatus.class)) { - lookup.put(s.getValue(), s); - } - } - - private final int value; - - DiscoveryStatus(int value) { - this.value = value; - } - - public static DiscoveryStatus get(int value) { - return lookup.get(value); - } - - public int getValue() { - return value; - } - } - - private XBeeAddress16 remoteAddress16; - private int retryCount; - private DeliveryStatus deliveryStatus; - private DiscoveryStatus discoveryStatus; - - - public ZNetTxStatusResponse() { - - } - - public XBeeAddress16 getRemoteAddress16() { - return remoteAddress16; - } - - - public void setRemoteAddress16(XBeeAddress16 remoteAddress) { - this.remoteAddress16 = remoteAddress; - } - - - public int getRetryCount() { - return retryCount; - } - - - public void setRetryCount(int retryCount) { - this.retryCount = retryCount; - } - - public DeliveryStatus getDeliveryStatus() { - return deliveryStatus; - } - - public void setDeliveryStatus(DeliveryStatus deliveryStatus) { - this.deliveryStatus = deliveryStatus; - } - - public DiscoveryStatus getDiscoveryStatus() { - return discoveryStatus; - } - - public void setDiscoveryStatus(DiscoveryStatus discoveryStatus) { - this.discoveryStatus = discoveryStatus; - } - - /** - * Returns true if the delivery status is SUCCESS - * - * @return - */ - public boolean isSuccess() { - return this.getDeliveryStatus() == DeliveryStatus.SUCCESS; - } - - public void parse(IPacketParser parser) throws IOException { - this.setFrameId(parser.read("ZNet Tx Status Frame Id")); - - this.setRemoteAddress16(parser.parseAddress16()); - this.setRetryCount(parser.read("ZNet Tx Status Tx Count")); - - int deliveryStatus = parser.read("ZNet Tx Status Delivery Status"); - - if (DeliveryStatus.get(deliveryStatus) != null) { - this.setDeliveryStatus(DeliveryStatus.get(deliveryStatus)); - } else { - log.warn("Unknown delivery status " + deliveryStatus); - this.setDeliveryStatus(DeliveryStatus.UNKNOWN); - } - - int discoveryStatus = parser.read("ZNet Tx Status Discovery Status"); - - if (DiscoveryStatus.get(discoveryStatus) != null) { - this.setDiscoveryStatus(DiscoveryStatus.get(discoveryStatus)); - } else { - log.warn("Unknown discovery status " + discoveryStatus); - this.setDiscoveryStatus(DiscoveryStatus.UNKNOWN); - } - } - - public String toString() { - return super.toString() + - ",remoteAddress16=" + this.remoteAddress16 + - ",retryCount=" + this.retryCount + - ",deliveryStatus=" + this.deliveryStatus + - ",discoveryStatus=" + this.discoveryStatus; - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.api.zigbee; + +import java.io.IOException; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.Logger; + +import com.rapplogic.xbee.api.IPacketParser; +import com.rapplogic.xbee.api.XBeeAddress16; +import com.rapplogic.xbee.api.responses.XBeeFrameIdResponse; + +/** + * Series 2 XBee. This is sent out the UART of the transmitting XBee immediately following + * a Transmit packet. Indicates if the Transmit packet (ZNetTxRequest) + * was successful. + *

+ * API ID: 0x8b + *

+ * @author andrew + */ +public class ZNetTxStatusResponse extends XBeeFrameIdResponse { + + private static final long serialVersionUID = 6572415977878338714L; + + private final static Logger log = Logger.getLogger(ZNetTxStatusResponse.class); + + public enum DeliveryStatus { +// 0x00 = Success +// 0x01 = MAC ACK Failure +// 0x02 = CCA Failure +// 0x15 = Invalid destination +// endpoint +// 0x21 = Network ACK Failure +// 0x22 = Not Joined to Network +// 0x23 = Self-addressed +// 0x24 = Address Not Found +// 0x25 = Route Not Found +// 0x26 = Broadcast source failed to hear a neighbor relay +// the message +// 0x2B = Invalid binding table index +// 0x2C = Resource error lack of free buffers, timers, etc. +// 0x2D = Attempted broadcast with APS transmission +// 0x2E = Attempted unicast with APS transmission, but +// EE=0 +// 0x32 = Resource error lack of free +// 0x74 = Data payload too large + + SUCCESS (0), + MAC_FAILURE (1), + CCA_FAILURE (0x02), + INVALID_DESTINATION_ENDPOINT (0x15), + NETWORK_ACK_FAILURE (0x21), + NOT_JOINED_TO_NETWORK (0x22), + SELF_ADDRESSED (0x23), + ADDRESS_NOT_FOUND (0x24), + ROUTE_NOT_FOUND (0x25), + BROADCAST_SOURCE_NEIGHBOR_FAILURE (0x26), + INVALID_BINDING_TABLE_INDEX (0x2B), + RESOURCE_ERROR_LACK_FREE_BUFFERS (0x2C), + ATTEMPTED_BROADCAST_WITH_APS_TX (0x2D), + ATTEMPTED_UNICAST_WITH_APS_TX_EE_ZERO (0x2E), + RESOURCE_ERROR_LACK_FREE_BUFFERS_0x32 (0x32), // WUT, SAME AS 2C + PAYLOAD_TOO_LARGE(0x74), // ZB Pro firmware only + UNKNOWN(-1); + + private static final Map lookup = new HashMap(); + + static { + for(DeliveryStatus s : EnumSet.allOf(DeliveryStatus.class)) { + lookup.put(s.getValue(), s); + } + } + + private final int value; + + DeliveryStatus(int value) { + this.value = value; + } + + public static DeliveryStatus get(int value) { + return lookup.get(value); + } + + public int getValue() { + return value; + } + } + + public enum DiscoveryStatus { +// 0x00 = No Discovery Overhead +// 0x01 = Address Discovery +// 0x02 = Route Discovery +// 0x03 = Address and Route +// 0x40 = Extended Timeout Discovery + + // NOTE 0x40 IS A bit field so going to be painful with enums + + NO_DISCOVERY (0), + ADDRESS_DISCOVERY (1), + ROUTE_DISCOVERY (2), + ADDRESS_AND_ROUTE_DISCOVERY (3), + EXTENDED_TIMEOUT_DISCOVERY (0x40), + EXTENDED_TIMEOUT_DISCOVERY_0x41 (0x41), + EXTENDED_TIMEOUT_DISCOVERY_0x42 (0x42), + EXTENDED_TIMEOUT_DISCOVERY_0x43 (0x43), + UNKNOWN(-1); + + private static final Map lookup = new HashMap(); + + static { + for(DiscoveryStatus s : EnumSet.allOf(DiscoveryStatus.class)) { + lookup.put(s.getValue(), s); + } + } + + private final int value; + + DiscoveryStatus(int value) { + this.value = value; + } + + public static DiscoveryStatus get(int value) { + return lookup.get(value); + } + + public int getValue() { + return value; + } + } + + private XBeeAddress16 remoteAddress16; + private int retryCount; + private DeliveryStatus deliveryStatus; + private DiscoveryStatus discoveryStatus; + + + public ZNetTxStatusResponse() { + + } + + public XBeeAddress16 getRemoteAddress16() { + return remoteAddress16; + } + + + public void setRemoteAddress16(XBeeAddress16 remoteAddress) { + this.remoteAddress16 = remoteAddress; + } + + + public int getRetryCount() { + return retryCount; + } + + + public void setRetryCount(int retryCount) { + this.retryCount = retryCount; + } + + public DeliveryStatus getDeliveryStatus() { + return deliveryStatus; + } + + public void setDeliveryStatus(DeliveryStatus deliveryStatus) { + this.deliveryStatus = deliveryStatus; + } + + public DiscoveryStatus getDiscoveryStatus() { + return discoveryStatus; + } + + public void setDiscoveryStatus(DiscoveryStatus discoveryStatus) { + this.discoveryStatus = discoveryStatus; + } + + /** + * Returns true if the delivery status is SUCCESS + * + * @return + */ + public boolean isSuccess() { + return this.getDeliveryStatus() == DeliveryStatus.SUCCESS; + } + + public void parse(IPacketParser parser) throws IOException { + this.setFrameId(parser.read("ZNet Tx Status Frame Id")); + + this.setRemoteAddress16(parser.parseAddress16()); + this.setRetryCount(parser.read("ZNet Tx Status Tx Count")); + + int deliveryStatus = parser.read("ZNet Tx Status Delivery Status"); + + if (DeliveryStatus.get(deliveryStatus) != null) { + this.setDeliveryStatus(DeliveryStatus.get(deliveryStatus)); + } else { + log.warn("Unknown delivery status " + deliveryStatus); + this.setDeliveryStatus(DeliveryStatus.UNKNOWN); + } + + int discoveryStatus = parser.read("ZNet Tx Status Discovery Status"); + + if (DiscoveryStatus.get(discoveryStatus) != null) { + this.setDiscoveryStatus(DiscoveryStatus.get(discoveryStatus)); + } else { + log.warn("Unknown discovery status " + discoveryStatus); + this.setDiscoveryStatus(DiscoveryStatus.UNKNOWN); + } + } + + public String toString() { + return super.toString() + + ",remoteAddress16=" + this.remoteAddress16 + + ",retryCount=" + this.retryCount + + ",deliveryStatus=" + this.deliveryStatus + + ",discoveryStatus=" + this.discoveryStatus; + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/AbstractXBeeConnection.java b/src/main/java/com/rapplogic/xbee/connections/AbstractXBeeConnection.java similarity index 97% rename from src/com/rapplogic/xbee/AbstractXBeeConnection.java rename to src/main/java/com/rapplogic/xbee/connections/AbstractXBeeConnection.java index f83810b..cbf0de7 100644 --- a/src/com/rapplogic/xbee/AbstractXBeeConnection.java +++ b/src/main/java/com/rapplogic/xbee/connections/AbstractXBeeConnection.java @@ -1,4 +1,4 @@ -package com.rapplogic.xbee; +package com.rapplogic.xbee.connections; import java.io.IOException; import java.io.InputStream; diff --git a/src/com/rapplogic/xbee/SerialPortConnection.java b/src/main/java/com/rapplogic/xbee/connections/SerialPortConnection.java similarity index 91% rename from src/com/rapplogic/xbee/SerialPortConnection.java rename to src/main/java/com/rapplogic/xbee/connections/SerialPortConnection.java index 0aa25a4..ca690e1 100644 --- a/src/com/rapplogic/xbee/SerialPortConnection.java +++ b/src/main/java/com/rapplogic/xbee/connections/SerialPortConnection.java @@ -1,153 +1,165 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee; - -import java.io.IOException; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.TooManyListenersException; - -import jssc.SerialPort; -import jssc.SerialPortEvent; -import jssc.SerialPortEventListener; -import jssc.SerialPortException; -import jssc.SerialPortList; -import jssc.SerialPortTimeoutException; - -import org.apache.log4j.Logger; - -import com.rapplogic.xbee.api.XBeeException; - -/** - * This class encapsulates a RXTX serial port, providing access to input/output streams, - * and notifying the subclass of new data events via the handleSerialData method. - * - * @author andrew - * - */ -public class SerialPortConnection implements XBeeConnection, SerialPortEventListener { - - private final static Logger log = Logger.getLogger(SerialPortConnection.class); - - private static final int DEFAULT_TIMEOUT = 5000; - - private SerialPort serialPort; - - private int timeout; - - public SerialPortConnection() { - - } - - public void openSerialPort(String port, int baudRate) throws TooManyListenersException, IOException, XBeeException, SerialPortException { - this.openSerialPort(port, "XBee", DEFAULT_TIMEOUT, baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE, SerialPort.FLOWCONTROL_NONE); - } - - public void openSerialPort(String port, String appName, int timeout, int baudRate) throws TooManyListenersException, IOException, XBeeException, SerialPortException { - this.openSerialPort(port, appName, timeout, baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE, SerialPort.FLOWCONTROL_NONE); - } - - public void openSerialPort(String port, String appName, int timeout, int baudRate, int dataBits, int stopBits, int parity, int flowControl) throws TooManyListenersException, IOException, XBeeException, SerialPortException { - // Apparently you can't query for a specific port, but instead must iterate - Set serialPorts = new HashSet(Arrays.asList(SerialPortList.getPortNames())); - - if (!serialPorts.contains(port)) { - throw new XBeeException("Could not find port: " + port); - } - - this.timeout = timeout; - - serialPort = new SerialPort(port); - serialPort.openPort(); - - serialPort.setParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); - serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE); - - serialPort.addEventListener(this); - } - - /** - * Shuts down RXTX - */ - @Override - public void close() throws IOException { - try { - serialPort.closePort(); - } catch (SerialPortException e) { - throw new IOException(e); - } - } - - public void serialEvent(SerialPortEvent event) { - try { - if (serialPort.getInputBufferBytesCount() > 0) { - try { - log.debug("serialEvent: " + serialPort.getInputBufferBytesCount() + " bytes available"); - - synchronized (this) { - this.notify(); - } - } catch (Exception e) { - log.error("Error in handleSerialData method", e); - } - } else { - log.warn("We were notified of new data but available() is returning 0"); - } - } catch (SerialPortException ex) { - // it's best not to throw the exception because the RXTX thread may not be prepared to handle - log.error("RXTX error in serialEvent method", ex); - } - } - - @Override - public int getByte() throws IOException { - try { - return serialPort.readBytes(1, timeout)[0] & 0xFF; - } catch (SerialPortException e) { - throw new IOException(e); - } catch (SerialPortTimeoutException e) { - throw new IOException(e); - } - } - - @Override - public boolean hasData() throws IOException { - try { - return serialPort.getInputBufferBytesCount() > 0; - } catch (SerialPortException e) { - throw new IOException(e); - } - } - - @Override - public void writeIntArray(int[] packet) throws IOException { - try { - serialPort.writeIntArray(packet); - } catch (SerialPortException e) { - throw new IOException(e); - } - } - - @Override - public boolean isConnected() { - return serialPort != null && serialPort.isOpened(); - } +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.connections; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.TooManyListenersException; + +import jssc.SerialPort; +import jssc.SerialPortEvent; +import jssc.SerialPortEventListener; +import jssc.SerialPortException; +import jssc.SerialPortList; +import jssc.SerialPortTimeoutException; + +import org.apache.log4j.Logger; + +import com.rapplogic.xbee.api.XBeeException; + +/** + * This class encapsulates a RXTX serial port, providing access to input/output streams, + * and notifying the subclass of new data events via the handleSerialData method. + * + * @author andrew + * + */ +public class SerialPortConnection implements XBeeConnection, SerialPortEventListener { + + private final static Logger log = Logger.getLogger(SerialPortConnection.class); + + private static final int DEFAULT_TIMEOUT = 5000; + + private SerialPort serialPort; + + private int timeout; + + private long lastDataReceiveTime = 0; + + private long lastDataSendTime = 0; + + public SerialPortConnection() { + + } + + public void openSerialPort(String port, int baudRate) throws TooManyListenersException, IOException, XBeeException, SerialPortException { + this.openSerialPort(port, "XBee", DEFAULT_TIMEOUT, baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE, SerialPort.FLOWCONTROL_NONE); + } + + public void openSerialPort(String port, String appName, int timeout, int baudRate) throws TooManyListenersException, IOException, XBeeException, SerialPortException { + this.openSerialPort(port, appName, timeout, baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE, SerialPort.FLOWCONTROL_NONE); + } + + public void openSerialPort(String port, String appName, int timeout, int baudRate, int dataBits, int stopBits, int parity, int flowControl) throws TooManyListenersException, IOException, XBeeException, SerialPortException { + // Apparently you can't query for a specific port, but instead must iterate + Set serialPorts = new HashSet(Arrays.asList(SerialPortList.getPortNames())); + + if (!serialPorts.contains(port)) { + throw new XBeeException("Could not find port: " + port); + } + + this.timeout = timeout; + + serialPort = new SerialPort(port); + serialPort.openPort(); + + serialPort.setParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); + serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE); + + serialPort.addEventListener(this); + } + + /** + * Shuts down RXTX + */ + public void close() throws IOException { + try { + serialPort.closePort(); + } catch (SerialPortException e) { + throw new IOException(e); + } + } + + public void serialEvent(SerialPortEvent event) { + try { + if (serialPort.getInputBufferBytesCount() > 0) { + try { + log.debug("serialEvent: " + serialPort.getInputBufferBytesCount() + " bytes available"); + + synchronized (this) { + this.notify(); + } + } catch (Exception e) { + log.error("Error in handleSerialData method", e); + } + } else { + log.warn("We were notified of new data but available() is returning 0"); + } + } catch (SerialPortException ex) { + // it's best not to throw the exception because the RXTX thread may not be prepared to handle + log.error("RXTX error in serialEvent method", ex); + } + } + + public int getByte() throws IOException { + try { + int data = serialPort.readBytes(1, timeout)[0] & 0xFF; + lastDataReceiveTime = System.currentTimeMillis(); + return data; + } catch (SerialPortException e) { + throw new IOException(e); + } catch (SerialPortTimeoutException e) { + throw new IOException(e); + } + } + + public boolean hasData() throws IOException { + try { + return serialPort.getInputBufferBytesCount() > 0; + } catch (SerialPortException e) { + throw new IOException(e); + } + } + + public void writeIntArray(int[] packet) throws IOException { + try { + serialPort.writeIntArray(packet); + lastDataSendTime = System.currentTimeMillis(); + } catch (SerialPortException e) { + throw new IOException(e); + } + } + + public boolean isConnected() { + return serialPort != null && serialPort.isOpened(); + } + + @Override + public long getLastDataReceiveTime() { + return lastDataReceiveTime; + } + + @Override + public long getLastDataSendTime() { + return lastDataSendTime; + } } \ No newline at end of file diff --git a/src/com/rapplogic/xbee/XBeeConnection.java b/src/main/java/com/rapplogic/xbee/connections/XBeeConnection.java similarity index 87% rename from src/com/rapplogic/xbee/XBeeConnection.java rename to src/main/java/com/rapplogic/xbee/connections/XBeeConnection.java index 7686fd7..1e5f8d5 100644 --- a/src/com/rapplogic/xbee/XBeeConnection.java +++ b/src/main/java/com/rapplogic/xbee/connections/XBeeConnection.java @@ -1,4 +1,4 @@ -package com.rapplogic.xbee; +package com.rapplogic.xbee.connections; import java.io.IOException; @@ -22,4 +22,6 @@ public interface XBeeConnection { public void close() throws IOException; public void writeIntArray(int[] packet) throws IOException; public boolean isConnected(); + public long getLastDataReceiveTime(); + public long getLastDataSendTime(); } diff --git a/src/main/java/com/rapplogic/xbee/connections/XBeeConnectionException.java b/src/main/java/com/rapplogic/xbee/connections/XBeeConnectionException.java new file mode 100644 index 0000000..cf18b8f --- /dev/null +++ b/src/main/java/com/rapplogic/xbee/connections/XBeeConnectionException.java @@ -0,0 +1,13 @@ +package com.rapplogic.xbee.connections; + +import com.rapplogic.xbee.api.XBeeException; + +public class XBeeConnectionException extends XBeeException { + + private static final long serialVersionUID = 8283447104282352813L; + + public XBeeConnectionException(String message, Exception e) { + super(message, e); + } + +} diff --git a/src/com/rapplogic/xbee/XBeePin.java b/src/main/java/com/rapplogic/xbee/connections/XBeePin.java similarity index 98% rename from src/com/rapplogic/xbee/XBeePin.java rename to src/main/java/com/rapplogic/xbee/connections/XBeePin.java index c6b410b..a347544 100644 --- a/src/com/rapplogic/xbee/XBeePin.java +++ b/src/main/java/com/rapplogic/xbee/connections/XBeePin.java @@ -17,7 +17,7 @@ * along with XBee-API. If not, see . */ -package com.rapplogic.xbee; +package com.rapplogic.xbee.connections; import java.util.ArrayList; import java.util.Collections; @@ -225,10 +225,11 @@ public void setAtPin(Integer atPin) { * @author andrew * */ - private static class PinSorter implements Comparator { - public int compare(Object o1, Object o2) { - return ((XBeePin)o1).getPin().compareTo(((XBeePin)o2).getPin()); - } + private static class PinSorter implements Comparator { + @Override + public int compare(XBeePin o1, XBeePin o2) { + return o1.getPin().compareTo(o2.getPin()); + } } public static List getZigBeePins() { diff --git a/src/main/java/com/rapplogic/xbee/connections/connectionprovider/IConnectionProvider.java b/src/main/java/com/rapplogic/xbee/connections/connectionprovider/IConnectionProvider.java new file mode 100644 index 0000000..81ddeaa --- /dev/null +++ b/src/main/java/com/rapplogic/xbee/connections/connectionprovider/IConnectionProvider.java @@ -0,0 +1,10 @@ +package com.rapplogic.xbee.connections.connectionprovider; + +import com.rapplogic.xbee.connections.XBeeConnection; +import com.rapplogic.xbee.connections.XBeeConnectionException; + +public interface IConnectionProvider { + + XBeeConnection open(String port, int baudRate) throws XBeeConnectionException; + +} diff --git a/src/main/java/com/rapplogic/xbee/connections/connectionprovider/SerialPortConnectionProvider.java b/src/main/java/com/rapplogic/xbee/connections/connectionprovider/SerialPortConnectionProvider.java new file mode 100644 index 0000000..2f7d45e --- /dev/null +++ b/src/main/java/com/rapplogic/xbee/connections/connectionprovider/SerialPortConnectionProvider.java @@ -0,0 +1,26 @@ +package com.rapplogic.xbee.connections.connectionprovider; + +import java.io.IOException; +import java.util.TooManyListenersException; + +import com.rapplogic.xbee.api.XBeeException; +import com.rapplogic.xbee.connections.SerialPortConnection; +import com.rapplogic.xbee.connections.XBeeConnection; +import com.rapplogic.xbee.connections.XBeeConnectionException; + +import jssc.SerialPortException; + +public class SerialPortConnectionProvider implements IConnectionProvider { + + @Override + public XBeeConnection open(String port, int baudRate) throws XBeeConnectionException { + SerialPortConnection serial = new SerialPortConnection(); + try { + serial.openSerialPort(port, baudRate); + } catch (TooManyListenersException | IOException | XBeeException | SerialPortException e) { + throw new XBeeConnectionException("Failed to open connection", e); + } + return serial; + } + +} diff --git a/src/com/rapplogic/xbee/examples/ApiAtExample.java b/src/main/java/com/rapplogic/xbee/examples/ApiAtExample.java similarity index 95% rename from src/com/rapplogic/xbee/examples/ApiAtExample.java rename to src/main/java/com/rapplogic/xbee/examples/ApiAtExample.java index 3d6876a..ec8bdf3 100644 --- a/src/com/rapplogic/xbee/examples/ApiAtExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/ApiAtExample.java @@ -1,78 +1,78 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.examples; - -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; - -import com.rapplogic.xbee.api.AtCommand; -import com.rapplogic.xbee.api.XBee; -import com.rapplogic.xbee.api.XBeeConfiguration; -import com.rapplogic.xbee.api.XBeeException; - -/** - * The AtCommand/AtCommandResponse classes are supported by both ZNet and WPAN XBees but certain - * commands are specific to ZNet or WPAN. - * - * Commands that are ZNet specific are located in the ZNetApiAtTest. - * - * Refer to the manual for more information on available commands - * - * @author andrew - * - */ -public class ApiAtExample { - -// TODO split class in to WPAN class - - private final static Logger log = Logger.getLogger(ApiAtExample.class); - - private XBee xbee = new XBee(new XBeeConfiguration().withStartupChecks(false)); - - public ApiAtExample() throws XBeeException { - - try { - // replace with port and baud rate of your XBee - xbee.open("/dev/tty.usbserial-A6005uPi", 9600); - -// // set D1 analog input -// this.sendCommand(new AtCommand("D1", 2)); -// // set D2 digital input -// this.sendCommand(new AtCommand("D2", 3)); -// // send sample every 5 seconds -// this.sendCommand(new AtCommand("IR", new int[] {0x13, 0x88})); - - log.info("MY is " + xbee.sendAtCommand(new AtCommand("MY"))); -// log.info("SH is " + xbee.sendAtCommand(new AtCommand("SH"))); -// log.info("SL is " + xbee.sendAtCommand(new AtCommand("SL"))); - } catch (Exception e) { - log.error("at command failed", e); - } finally { - if (xbee != null && xbee.isConnected()) { - xbee.close(); - } - } - } - - public static void main(String[] args) throws XBeeException { - PropertyConfigurator.configure("log4j.properties"); - new ApiAtExample(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.examples; + +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; + +import com.rapplogic.xbee.api.XBee; +import com.rapplogic.xbee.api.XBeeConfiguration; +import com.rapplogic.xbee.api.XBeeException; +import com.rapplogic.xbee.api.requests.AtCommand; + +/** + * The AtCommand/AtCommandResponse classes are supported by both ZNet and WPAN XBees but certain + * commands are specific to ZNet or WPAN. + * + * Commands that are ZNet specific are located in the ZNetApiAtTest. + * + * Refer to the manual for more information on available commands + * + * @author andrew + * + */ +public class ApiAtExample { + +// TODO split class in to WPAN class + + private final static Logger log = Logger.getLogger(ApiAtExample.class); + + private XBee xbee = new XBee(new XBeeConfiguration().withStartupChecks(false)); + + public ApiAtExample() throws XBeeException { + + try { + // replace with port and baud rate of your XBee + xbee.open("/dev/tty.usbserial-A6005uPi", 9600); + +// // set D1 analog input +// this.sendCommand(new AtCommand("D1", 2)); +// // set D2 digital input +// this.sendCommand(new AtCommand("D2", 3)); +// // send sample every 5 seconds +// this.sendCommand(new AtCommand("IR", new int[] {0x13, 0x88})); + + log.info("MY is " + xbee.sendAtCommand(new AtCommand("MY"))); +// log.info("SH is " + xbee.sendAtCommand(new AtCommand("SH"))); +// log.info("SL is " + xbee.sendAtCommand(new AtCommand("SL"))); + } catch (Exception e) { + log.error("at command failed", e); + } finally { + if (xbee != null && xbee.isConnected()) { + xbee.close(); + } + } + } + + public static void main(String[] args) throws XBeeException { + PropertyConfigurator.configure("log4j.properties"); + new ApiAtExample(); + } +} diff --git a/src/com/rapplogic/xbee/examples/RemoteAtExample.java b/src/main/java/com/rapplogic/xbee/examples/RemoteAtExample.java similarity index 94% rename from src/com/rapplogic/xbee/examples/RemoteAtExample.java rename to src/main/java/com/rapplogic/xbee/examples/RemoteAtExample.java index 73e1579..ed713a5 100644 --- a/src/com/rapplogic/xbee/examples/RemoteAtExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/RemoteAtExample.java @@ -1,106 +1,106 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.examples; - -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; - -import com.rapplogic.xbee.api.RemoteAtRequest; -import com.rapplogic.xbee.api.RemoteAtResponse; -import com.rapplogic.xbee.api.XBee; -import com.rapplogic.xbee.api.XBeeAddress64; -import com.rapplogic.xbee.api.XBeeException; -import com.rapplogic.xbee.api.XBeeTimeoutException; - -/** - * This example uses Remote AT to turn on/off I/O pins. - * This example is more interesting if you connect a LED to pin 20 on your end device. - * Remember to use a resistor to limit the current flow. I used a 215 Ohm resistor. - *

- * Note: if your coordinator is powered on and receiving I/O samples, make sure you power off/on to drain - * the traffic before running this example. - * - * @author andrew - * - */ -public class RemoteAtExample { - - private final static Logger log = Logger.getLogger(RemoteAtExample.class); - - private RemoteAtExample() throws XBeeException, InterruptedException { - - XBee xbee = new XBee(); - - try { - // replace with your coordinator com/baud - xbee.open("/dev/tty.usbserial-A6005v5M", 9600); - // xbee.open("COM5", 9600); - - // replace with SH + SL of your end device - XBeeAddress64 addr64 = new XBeeAddress64(0, 0x13, 0xa2, 0, 0x40, 0x0a, 0x3e, 0x02); - - // turn on end device (pin 20) D0 (Digital output high = 5) - //RemoteAtRequest request = new RemoteAtRequest(addr64, "D0", new int[] {5}); - //RemoteAtRequest request = new RemoteAtRequest(addr64, "IR", new int[] {0x7f, 0xff}); - //RemoteAtRequest request = new RemoteAtRequest(addr64, "D5", new int[] {3}); - //RemoteAtRequest request = new RemoteAtRequest(addr64, "D0", new int[] {2}); - //RemoteAtRequest request = new RemoteAtRequest(addr64, "P2", new int[] {3}); - RemoteAtRequest request = new RemoteAtRequest(addr64, "P0", new int[] {1}); - - RemoteAtResponse response = (RemoteAtResponse) xbee.sendSynchronous(request, 10000); - - if (response.isOk()) { - log.info("successfully turned on pin 20 (D0)"); - } else { - throw new RuntimeException("failed to turn on pin 20. status is " + response.getStatus()); - } - - System.exit(0); - - // wait a bit - Thread.sleep(5000); -// -// // now turn off end device D0 - request.setValue(new int[] {4}); - - response = (RemoteAtResponse) xbee.sendSynchronous(request, 10000); - - if (response.isOk()) { - log.info("successfully turned off pin 20 (D0)"); - } else { - throw new RuntimeException("failed to turn off pin 20. status is " + response.getStatus()); - } - - } catch (XBeeTimeoutException e) { - log.error("request timed out. make sure you remote XBee is configured and powered on"); - } catch (Exception e) { - log.error("unexpected error", e); - } finally { - if (xbee != null && xbee.isConnected()) { - xbee.close(); - } - } - } - - public static void main(String[] args) throws XBeeException, InterruptedException { - PropertyConfigurator.configure("log4j.properties"); - new RemoteAtExample(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.examples; + +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; + +import com.rapplogic.xbee.api.XBee; +import com.rapplogic.xbee.api.XBeeAddress64; +import com.rapplogic.xbee.api.XBeeException; +import com.rapplogic.xbee.api.XBeeTimeoutException; +import com.rapplogic.xbee.api.requests.RemoteAtRequest; +import com.rapplogic.xbee.api.responses.RemoteAtResponse; + +/** + * This example uses Remote AT to turn on/off I/O pins. + * This example is more interesting if you connect a LED to pin 20 on your end device. + * Remember to use a resistor to limit the current flow. I used a 215 Ohm resistor. + *

+ * Note: if your coordinator is powered on and receiving I/O samples, make sure you power off/on to drain + * the traffic before running this example. + * + * @author andrew + * + */ +public class RemoteAtExample { + + private final static Logger log = Logger.getLogger(RemoteAtExample.class); + + private RemoteAtExample() throws XBeeException, InterruptedException { + + XBee xbee = new XBee(); + + try { + // replace with your coordinator com/baud + xbee.open("/dev/tty.usbserial-A6005v5M", 9600); + // xbee.open("COM5", 9600); + + // replace with SH + SL of your end device + XBeeAddress64 addr64 = new XBeeAddress64(0, 0x13, 0xa2, 0, 0x40, 0x0a, 0x3e, 0x02); + + // turn on end device (pin 20) D0 (Digital output high = 5) + //RemoteAtRequest request = new RemoteAtRequest(addr64, "D0", new int[] {5}); + //RemoteAtRequest request = new RemoteAtRequest(addr64, "IR", new int[] {0x7f, 0xff}); + //RemoteAtRequest request = new RemoteAtRequest(addr64, "D5", new int[] {3}); + //RemoteAtRequest request = new RemoteAtRequest(addr64, "D0", new int[] {2}); + //RemoteAtRequest request = new RemoteAtRequest(addr64, "P2", new int[] {3}); + RemoteAtRequest request = new RemoteAtRequest(addr64, "P0", new int[] {1}); + + RemoteAtResponse response = (RemoteAtResponse) xbee.sendSynchronous(request, 10000); + + if (response.isOk()) { + log.info("successfully turned on pin 20 (D0)"); + } else { + throw new RuntimeException("failed to turn on pin 20. status is " + response.getStatus()); + } + + System.exit(0); + + // wait a bit + Thread.sleep(5000); +// +// // now turn off end device D0 + request.setValue(new int[] {4}); + + response = (RemoteAtResponse) xbee.sendSynchronous(request, 10000); + + if (response.isOk()) { + log.info("successfully turned off pin 20 (D0)"); + } else { + throw new RuntimeException("failed to turn off pin 20. status is " + response.getStatus()); + } + + } catch (XBeeTimeoutException e) { + log.error("request timed out. make sure you remote XBee is configured and powered on"); + } catch (Exception e) { + log.error("unexpected error", e); + } finally { + if (xbee != null && xbee.isConnected()) { + xbee.close(); + } + } + } + + public static void main(String[] args) throws XBeeException, InterruptedException { + PropertyConfigurator.configure("log4j.properties"); + new RemoteAtExample(); + } +} diff --git a/src/com/rapplogic/xbee/examples/ResponseHandlerExample.java b/src/main/java/com/rapplogic/xbee/examples/ResponseHandlerExample.java similarity index 100% rename from src/com/rapplogic/xbee/examples/ResponseHandlerExample.java rename to src/main/java/com/rapplogic/xbee/examples/ResponseHandlerExample.java diff --git a/src/com/rapplogic/xbee/examples/wpan/ApiReceiverExample.java b/src/main/java/com/rapplogic/xbee/examples/wpan/ApiReceiverExample.java similarity index 95% rename from src/com/rapplogic/xbee/examples/wpan/ApiReceiverExample.java rename to src/main/java/com/rapplogic/xbee/examples/wpan/ApiReceiverExample.java index b2de0eb..2f8b4a3 100644 --- a/src/com/rapplogic/xbee/examples/wpan/ApiReceiverExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/wpan/ApiReceiverExample.java @@ -1,99 +1,99 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.examples.wpan; - -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; - -import com.rapplogic.xbee.api.ApiId; -import com.rapplogic.xbee.api.ErrorResponse; -import com.rapplogic.xbee.api.XBee; -import com.rapplogic.xbee.api.XBeeResponse; -import com.rapplogic.xbee.api.wpan.RxResponse16; -import com.rapplogic.xbee.api.wpan.RxResponse64; -import com.rapplogic.xbee.util.ByteUtils; - -/** - * Receives IO samples from remote radio - * I have a photoresistor connected to analog0 and a thermistor is connected to analog1 - * Also there is a breadboard switch connected to digital2 with change detect configured - * - * @author andrew - * - */ -public class ApiReceiverExample { - - private final static Logger log = Logger.getLogger(ApiReceiverExample.class); - - private long last = System.currentTimeMillis(); - - private ApiReceiverExample() throws Exception { - XBee xbee = new XBee(); - - int count = 0; - int errors = 0; - - try { - // my end device - xbee.open("/dev/tty.usbserial-A6005v5M", 9600); - // my coordinator - //xbee.open("/dev/tty.usbserial-A4004Rim", 9600); - - while (true) { - - try { - XBeeResponse response = xbee.getResponse(); - count++; - - if (response.isError()) { - log.info("response contains errors", ((ErrorResponse)response).getException()); - errors++; - } - - for (int i = 0; i < response.getPacketBytes().length; i++) { - log.info("packet [" + i + "] " + ByteUtils.toBase16(response.getPacketBytes()[i])); - } - - if (response.getApiId() == ApiId.RX_16_RESPONSE) { - log.info("Received RX 16 packet " + ((RxResponse16)response)); - } else if (response.getApiId() == ApiId.RX_64_RESPONSE) { - log.info("Received RX 64 packet " + ((RxResponse64)response)); - } else { - log.info("Ignoring mystery packet " + response.toString()); - } - - log.debug("Received response: " + response.toString() + ", count is " + count + ", errors is " + errors); - } catch (Exception e) { - log.error(e); - } - } - } finally { - if (xbee != null && xbee.isConnected()) { - xbee.close(); - } - } - } - - public static void main(String[] args) throws Exception { - // init log4j - PropertyConfigurator.configure("log4j.properties"); - new ApiReceiverExample(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.examples.wpan; + +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; + +import com.rapplogic.xbee.api.ApiId; +import com.rapplogic.xbee.api.XBee; +import com.rapplogic.xbee.api.XBeeResponse; +import com.rapplogic.xbee.api.responses.ErrorResponse; +import com.rapplogic.xbee.api.wpan.RxResponse16; +import com.rapplogic.xbee.api.wpan.RxResponse64; +import com.rapplogic.xbee.util.ByteUtils; + +/** + * Receives IO samples from remote radio + * I have a photoresistor connected to analog0 and a thermistor is connected to analog1 + * Also there is a breadboard switch connected to digital2 with change detect configured + * + * @author andrew + * + */ +public class ApiReceiverExample { + + private final static Logger log = Logger.getLogger(ApiReceiverExample.class); + + private long last = System.currentTimeMillis(); + + private ApiReceiverExample() throws Exception { + XBee xbee = new XBee(); + + int count = 0; + int errors = 0; + + try { + // my end device + xbee.open("/dev/tty.usbserial-A6005v5M", 9600); + // my coordinator + //xbee.open("/dev/tty.usbserial-A4004Rim", 9600); + + while (true) { + + try { + XBeeResponse response = xbee.getResponse(); + count++; + + if (response.isError()) { + log.info("response contains errors", ((ErrorResponse)response).getException()); + errors++; + } + + for (int i = 0; i < response.getPacketBytes().length; i++) { + log.info("packet [" + i + "] " + ByteUtils.toBase16(response.getPacketBytes()[i])); + } + + if (response.getApiId() == ApiId.RX_16_RESPONSE) { + log.info("Received RX 16 packet " + ((RxResponse16)response)); + } else if (response.getApiId() == ApiId.RX_64_RESPONSE) { + log.info("Received RX 64 packet " + ((RxResponse64)response)); + } else { + log.info("Ignoring mystery packet " + response.toString()); + } + + log.debug("Received response: " + response.toString() + ", count is " + count + ", errors is " + errors); + } catch (Exception e) { + log.error(e); + } + } + } finally { + if (xbee != null && xbee.isConnected()) { + xbee.close(); + } + } + } + + public static void main(String[] args) throws Exception { + // init log4j + PropertyConfigurator.configure("log4j.properties"); + new ApiReceiverExample(); + } +} diff --git a/src/com/rapplogic/xbee/examples/wpan/ApiSenderExample.java b/src/main/java/com/rapplogic/xbee/examples/wpan/ApiSenderExample.java similarity index 96% rename from src/com/rapplogic/xbee/examples/wpan/ApiSenderExample.java rename to src/main/java/com/rapplogic/xbee/examples/wpan/ApiSenderExample.java index 32dcd38..15c59ac 100644 --- a/src/com/rapplogic/xbee/examples/wpan/ApiSenderExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/wpan/ApiSenderExample.java @@ -1,135 +1,135 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.examples.wpan; - -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; - -import com.rapplogic.xbee.api.ApiId; -import com.rapplogic.xbee.api.XBee; -import com.rapplogic.xbee.api.XBeeAddress16; -import com.rapplogic.xbee.api.XBeePacket; -import com.rapplogic.xbee.api.XBeeResponse; -import com.rapplogic.xbee.api.wpan.TxRequest16; -import com.rapplogic.xbee.api.wpan.TxRequest64; -import com.rapplogic.xbee.api.wpan.TxStatusResponse; - -/** - * Sends a TX Request every 5000 ms and waits for TX status packet. - * If the radio is sending samples it will continue to wait for tx status. - * - * @author andrew - * - */ -public class ApiSenderExample { - - private final static Logger log = Logger.getLogger(ApiSenderExample.class); - - private ApiSenderExample() throws Exception { - - XBee xbee = new XBee(); - - final int sleep = 5000; - - int count = 0; - int errors = 0; - int ackErrors = 0; - int ccaErrors = 0; - int purgeErrors = 0; - - long now; - - try { - // replace with port and baud rate of your XBee - xbee.open("/dev/tty.usbserial-A6005uPi", 9600); - - while (true) { - - // log.debug("Sending count " + count); - // XBeeResponse response = xbee.sendTxRequest16(destination, 0x0a, payload); - - // int[] payload = new int[] {1,2,3,4,5,6,7,8}; - // to verify correct byte escaping, we'll send a start byte - int[] payload = new int[] { XBeePacket.SpecialByte.START_BYTE.getValue() }; - - // specify the remote XBee 16-bit MY address - XBeeAddress16 destination = new XBeeAddress16(0x18, 0x74); - TxRequest16 tx = new TxRequest16(destination, payload); - // or send a TX64 (same thing except we are addressing by SH+SL address) -// XBeeAddress64 destination = new XBeeAddress64(0, 0x13, 0xa2, 0, 0x40, 0x08, 0xb4, 0x8f); -// TxRequest64 tx2 = new TxRequest64(destination64, payload); - - now = System.currentTimeMillis(); - xbee.sendAsynchronous(tx); - - XBeeResponse response = null; - - while (true) { - // blocks until we get response - response = xbee.getResponse(); - - if (response.getApiId() != ApiId.TX_STATUS_RESPONSE) { - log.debug("expected tx status but received " + response); - } else { -// log.debug("got tx status"); - - if (((TxStatusResponse) response).getFrameId() != tx.getFrameId()) { - throw new RuntimeException("frame id does not match"); - } - - if (((TxStatusResponse) response).getStatus() != TxStatusResponse.Status.SUCCESS) { - errors++; - - if (((TxStatusResponse) response).isAckError()) { - ackErrors++; - } else if (((TxStatusResponse) response).isCcaError()) { - ccaErrors++; - } else if (((TxStatusResponse) response).isPurged()) { - purgeErrors++; - } - - log.debug("Tx status failure with status: " + ((TxStatusResponse) response).getStatus()); - } else { - // success - log.debug("Success. count is " + count + ", errors is " + errors + ", in " + (System.currentTimeMillis() - now) + ", ack errors " - + ackErrors + ", ccaErrors " + ccaErrors + ", purge errors " + purgeErrors); - } - - count++; - - break; - } - } - - Thread.sleep(sleep); - } - } finally { - if (xbee != null && xbee.isConnected()) { - xbee.close(); - } - } - } - - public static void main(String[] args) throws Exception { - // init log4j - PropertyConfigurator.configure("log4j.properties"); - new ApiSenderExample(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.examples.wpan; + +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; + +import com.rapplogic.xbee.api.ApiId; +import com.rapplogic.xbee.api.XBee; +import com.rapplogic.xbee.api.XBeeAddress16; +import com.rapplogic.xbee.api.XBeePacket; +import com.rapplogic.xbee.api.XBeeResponse; +import com.rapplogic.xbee.api.wpan.TxRequest16; +import com.rapplogic.xbee.api.wpan.TxRequest64; +import com.rapplogic.xbee.api.wpan.TxStatusResponse; + +/** + * Sends a TX Request every 5000 ms and waits for TX status packet. + * If the radio is sending samples it will continue to wait for tx status. + * + * @author andrew + * + */ +public class ApiSenderExample { + + private final static Logger log = Logger.getLogger(ApiSenderExample.class); + + private ApiSenderExample() throws Exception { + + XBee xbee = new XBee(); + + final int sleep = 5000; + + int count = 0; + int errors = 0; + int ackErrors = 0; + int ccaErrors = 0; + int purgeErrors = 0; + + long now; + + try { + // replace with port and baud rate of your XBee + xbee.open("/dev/tty.usbserial-A6005uPi", 9600); + + while (true) { + + // log.debug("Sending count " + count); + // XBeeResponse response = xbee.sendTxRequest16(destination, 0x0a, payload); + + // int[] payload = new int[] {1,2,3,4,5,6,7,8}; + // to verify correct byte escaping, we'll send a start byte + int[] payload = new int[] { XBeePacket.SpecialByte.START_BYTE.getValue() }; + + // specify the remote XBee 16-bit MY address + XBeeAddress16 destination = new XBeeAddress16(0x18, 0x74); + TxRequest16 tx = new TxRequest16(destination, payload); + // or send a TX64 (same thing except we are addressing by SH+SL address) +// XBeeAddress64 destination = new XBeeAddress64(0, 0x13, 0xa2, 0, 0x40, 0x08, 0xb4, 0x8f); +// TxRequest64 tx2 = new TxRequest64(destination64, payload); + + now = System.currentTimeMillis(); + xbee.sendAsynchronous(tx); + + XBeeResponse response = null; + + while (true) { + // blocks until we get response + response = xbee.getResponse(); + + if (response.getApiId() != ApiId.TX_STATUS_RESPONSE) { + log.debug("expected tx status but received " + response); + } else { +// log.debug("got tx status"); + + if (((TxStatusResponse) response).getFrameId() != tx.getFrameId()) { + throw new RuntimeException("frame id does not match"); + } + + if (((TxStatusResponse) response).getStatus() != TxStatusResponse.Status.SUCCESS) { + errors++; + + if (((TxStatusResponse) response).isAckError()) { + ackErrors++; + } else if (((TxStatusResponse) response).isCcaError()) { + ccaErrors++; + } else if (((TxStatusResponse) response).isPurged()) { + purgeErrors++; + } + + log.debug("Tx status failure with status: " + ((TxStatusResponse) response).getStatus()); + } else { + // success + log.debug("Success. count is " + count + ", errors is " + errors + ", in " + (System.currentTimeMillis() - now) + ", ack errors " + + ackErrors + ", ccaErrors " + ccaErrors + ", purge errors " + purgeErrors); + } + + count++; + + break; + } + } + + Thread.sleep(sleep); + } + } finally { + if (xbee != null && xbee.isConnected()) { + xbee.close(); + } + } + } + + public static void main(String[] args) throws Exception { + // init log4j + PropertyConfigurator.configure("log4j.properties"); + new ApiSenderExample(); + } +} diff --git a/src/com/rapplogic/xbee/examples/wpan/IoSamplesExample.java b/src/main/java/com/rapplogic/xbee/examples/wpan/IoSamplesExample.java similarity index 95% rename from src/com/rapplogic/xbee/examples/wpan/IoSamplesExample.java rename to src/main/java/com/rapplogic/xbee/examples/wpan/IoSamplesExample.java index f707c3e..8e8d5be 100644 --- a/src/com/rapplogic/xbee/examples/wpan/IoSamplesExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/wpan/IoSamplesExample.java @@ -1,138 +1,138 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.examples.wpan; - -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; - -import com.rapplogic.xbee.api.ApiId; -import com.rapplogic.xbee.api.ErrorResponse; -import com.rapplogic.xbee.api.XBee; -import com.rapplogic.xbee.api.XBeeResponse; -import com.rapplogic.xbee.api.wpan.IoSample; -import com.rapplogic.xbee.api.wpan.RxResponseIoSample; -import com.rapplogic.xbee.util.ByteUtils; - -/** - * Series 1 XBee -- receive IO samples from remote radio - * In this example we are going to set pin 20 to analog input, pins 11 and 12 to digital input, and configure change detect for pin 12. - * Change detect sends a sample whenever the remote XBee detects a change from or to ground; - * it does not detect the transition from open circuit to high or vice versa. - * - * Configuration: - * - * This assumes we are starting with the factory default settings and the XBee is in API mode (AP = 2) - * - * Remote radio: - * - * MY = 1 necessary? - * sets pin 20 to analog input - * D0 = 2 - * sets pin 11 to digital input - * D4 = 3 - * sets pin 12 to digital input for change detect - * D7 = 3 - * turn on change detect for D7. we need a bit mask of 10000000 (if you can't convert base 2 to hex in your head either, it's 0x80) - * why 10000000? we place a 1 in the position of each input we want to enable, so if you want to enable for D4 and D7 it would be 10010000 - * IC = 80 - * we want 1 sample per packet - * IT = 1 - * receive samples every 5 seconds - * IR = 1388 - * (coordinator address) necessary? doesn't it always send to coordinator? - * DL = 0 - * (save configuration) - * WR - * - * Coordinator: - * - * (set as coordinator) - * CE = 1 - * (set address) - * MY = 0 - * (end device) necessary? - * DL = 1 - * - * - * @author andrew - * - */ -public class IoSamplesExample { - - private final static Logger log = Logger.getLogger(IoSamplesExample.class); - - private IoSamplesExample() throws Exception { - XBee xbee = new XBee(); - - try { - xbee.open("/dev/tty.usbserial-A6005v5M", 9600); - - while (true) { - - try { - XBeeResponse response = xbee.getResponse(); - - log.info("Received i/o response: " + response); - log.info("packet bytes is " + ByteUtils.toBase16(response.getPacketBytes())); - - if (response.isError()) { - log.info("response contains errors", ((ErrorResponse)response).getException()); - continue; - } - - if (response.getApiId() == ApiId.RX_16_IO_RESPONSE) { - RxResponseIoSample ioSample = (RxResponseIoSample)response; - - log.info("Received I/O sample from " + ioSample.getSourceAddress()); - // optionally output the rssi strength - //log.info("rssi is " + ioSample.getRssi()); - - // loops just once since IT = 1 - for (IoSample sample: ioSample.getSamples()) { - if (ioSample.containsAnalog()) { - log.info("Analog pin 20 10-bit reading is " + sample.getAnalog0()); - log.info("Digital pin 11 is " + (sample.isD4On() ? "on" : "off")); - log.info("Digital pin 12 is " + (sample.isD7On() ? "on" : "off")); - } else { - // we know it's change detect since analog was not sent - log.info("Received change detect for Digital pin 12: " + (sample.isD7On() ? "on" : "off")); - } - } - } else { - // not what we expected - log.info("Ignoring mystery packet " + response.toString()); - } - } catch (Exception e) { - log.error(e); - } - } - } finally { - if (xbee != null && xbee.isConnected()) { - xbee.close(); - } - } - } - - public static void main(String[] args) throws Exception { - // init log4j - PropertyConfigurator.configure("log4j.properties"); - new IoSamplesExample(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.examples.wpan; + +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; + +import com.rapplogic.xbee.api.ApiId; +import com.rapplogic.xbee.api.XBee; +import com.rapplogic.xbee.api.XBeeResponse; +import com.rapplogic.xbee.api.responses.ErrorResponse; +import com.rapplogic.xbee.api.wpan.IoSample; +import com.rapplogic.xbee.api.wpan.RxResponseIoSample; +import com.rapplogic.xbee.util.ByteUtils; + +/** + * Series 1 XBee -- receive IO samples from remote radio + * In this example we are going to set pin 20 to analog input, pins 11 and 12 to digital input, and configure change detect for pin 12. + * Change detect sends a sample whenever the remote XBee detects a change from or to ground; + * it does not detect the transition from open circuit to high or vice versa. + * + * Configuration: + * + * This assumes we are starting with the factory default settings and the XBee is in API mode (AP = 2) + * + * Remote radio: + * + * MY = 1 necessary? + * sets pin 20 to analog input + * D0 = 2 + * sets pin 11 to digital input + * D4 = 3 + * sets pin 12 to digital input for change detect + * D7 = 3 + * turn on change detect for D7. we need a bit mask of 10000000 (if you can't convert base 2 to hex in your head either, it's 0x80) + * why 10000000? we place a 1 in the position of each input we want to enable, so if you want to enable for D4 and D7 it would be 10010000 + * IC = 80 + * we want 1 sample per packet + * IT = 1 + * receive samples every 5 seconds + * IR = 1388 + * (coordinator address) necessary? doesn't it always send to coordinator? + * DL = 0 + * (save configuration) + * WR + * + * Coordinator: + * + * (set as coordinator) + * CE = 1 + * (set address) + * MY = 0 + * (end device) necessary? + * DL = 1 + * + * + * @author andrew + * + */ +public class IoSamplesExample { + + private final static Logger log = Logger.getLogger(IoSamplesExample.class); + + private IoSamplesExample() throws Exception { + XBee xbee = new XBee(); + + try { + xbee.open("/dev/tty.usbserial-A6005v5M", 9600); + + while (true) { + + try { + XBeeResponse response = xbee.getResponse(); + + log.info("Received i/o response: " + response); + log.info("packet bytes is " + ByteUtils.toBase16(response.getPacketBytes())); + + if (response.isError()) { + log.info("response contains errors", ((ErrorResponse)response).getException()); + continue; + } + + if (response.getApiId() == ApiId.RX_16_IO_RESPONSE) { + RxResponseIoSample ioSample = (RxResponseIoSample)response; + + log.info("Received I/O sample from " + ioSample.getSourceAddress()); + // optionally output the rssi strength + //log.info("rssi is " + ioSample.getRssi()); + + // loops just once since IT = 1 + for (IoSample sample: ioSample.getSamples()) { + if (ioSample.containsAnalog()) { + log.info("Analog pin 20 10-bit reading is " + sample.getAnalog0()); + log.info("Digital pin 11 is " + (sample.isD4On() ? "on" : "off")); + log.info("Digital pin 12 is " + (sample.isD7On() ? "on" : "off")); + } else { + // we know it's change detect since analog was not sent + log.info("Received change detect for Digital pin 12: " + (sample.isD7On() ? "on" : "off")); + } + } + } else { + // not what we expected + log.info("Ignoring mystery packet " + response.toString()); + } + } catch (Exception e) { + log.error(e); + } + } + } finally { + if (xbee != null && xbee.isConnected()) { + xbee.close(); + } + } + } + + public static void main(String[] args) throws Exception { + // init log4j + PropertyConfigurator.configure("log4j.properties"); + new IoSamplesExample(); + } +} diff --git a/src/com/rapplogic/xbee/examples/wpan/Processing.java b/src/main/java/com/rapplogic/xbee/examples/wpan/Processing.java similarity index 100% rename from src/com/rapplogic/xbee/examples/wpan/Processing.java rename to src/main/java/com/rapplogic/xbee/examples/wpan/Processing.java diff --git a/src/com/rapplogic/xbee/examples/wpan/WpanNodeDiscoverExample.java b/src/main/java/com/rapplogic/xbee/examples/wpan/WpanNodeDiscoverExample.java similarity index 94% rename from src/com/rapplogic/xbee/examples/wpan/WpanNodeDiscoverExample.java rename to src/main/java/com/rapplogic/xbee/examples/wpan/WpanNodeDiscoverExample.java index 6ca8727..54a7b89 100644 --- a/src/com/rapplogic/xbee/examples/wpan/WpanNodeDiscoverExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/wpan/WpanNodeDiscoverExample.java @@ -1,105 +1,105 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.examples.wpan; - -import java.util.List; - -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; - -import com.rapplogic.xbee.api.AtCommand; -import com.rapplogic.xbee.api.AtCommandResponse; -import com.rapplogic.xbee.api.CollectTerminator; -import com.rapplogic.xbee.api.XBee; -import com.rapplogic.xbee.api.XBeeException; -import com.rapplogic.xbee.api.XBeeResponse; -import com.rapplogic.xbee.api.wpan.WpanNodeDiscover; -import com.rapplogic.xbee.util.ByteUtils; - -/** - * Example of performing a node discover for Series 1 XBees. - * You must connect to the coordinator to run this example and - * have one or more end devices that are associated. - * - * @author andrew - * - */ -public class WpanNodeDiscoverExample { - - private final static Logger log = Logger.getLogger(WpanNodeDiscoverExample.class); - - private XBee xbee = new XBee(); - - public WpanNodeDiscoverExample() throws XBeeException, InterruptedException { - - try { - // my coordinator com/baud - xbee.open("/dev/tty.usbserial-A4004Rim", 9600); - - // get the Node discovery timeout - xbee.sendAsynchronous(new AtCommand("NT")); - AtCommandResponse nodeTimeout = (AtCommandResponse) xbee.getResponse(); - - // default is 2.5 seconds for series 1 - int nodeDiscoveryTimeout = ByteUtils.convertMultiByteToInt(nodeTimeout.getValue()) * 100; - log.info("Node discovery timeout is " + nodeDiscoveryTimeout + " milliseconds"); - - xbee.sendAsynchronous(new AtCommand("ND")); - - // collect responses up to the timeout or until the terminating response is received, whichever occurs first - List responses = xbee.collectResponses(10000, new CollectTerminator() { - public boolean stop(XBeeResponse response) { - if (response instanceof AtCommandResponse) { - AtCommandResponse at = (AtCommandResponse) response; - if (at.getCommand().equals("ND") && at.getValue() != null && at.getValue().length == 0) { - log.debug("Found terminating response"); - return true; - } - } - return false; - } - }); - - //TODO check for terminating node - - log.info("Time is up! You should have heard back from all nodes by now. If not make sure all nodes are associated and/or try increasing the node timeout (NT)"); - - for (XBeeResponse response : responses) { - if (response instanceof AtCommandResponse) { - AtCommandResponse atResponse = (AtCommandResponse) response; - - if (atResponse.getCommand().equals("ND") && atResponse.getValue() != null && atResponse.getValue().length > 0) { - WpanNodeDiscover nd = WpanNodeDiscover.parse((AtCommandResponse)response); - log.info("Node Discover is " + nd); - } - } - } - } finally { - if (xbee != null && xbee.isConnected()) { - xbee.close(); - } - } - } - - public static void main(String[] args) throws XBeeException, InterruptedException { - PropertyConfigurator.configure("log4j.properties"); - new WpanNodeDiscoverExample(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.examples.wpan; + +import java.util.List; + +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; + +import com.rapplogic.xbee.api.CollectTerminator; +import com.rapplogic.xbee.api.XBee; +import com.rapplogic.xbee.api.XBeeException; +import com.rapplogic.xbee.api.XBeeResponse; +import com.rapplogic.xbee.api.requests.AtCommand; +import com.rapplogic.xbee.api.responses.AtCommandResponse; +import com.rapplogic.xbee.api.wpan.WpanNodeDiscover; +import com.rapplogic.xbee.util.ByteUtils; + +/** + * Example of performing a node discover for Series 1 XBees. + * You must connect to the coordinator to run this example and + * have one or more end devices that are associated. + * + * @author andrew + * + */ +public class WpanNodeDiscoverExample { + + private final static Logger log = Logger.getLogger(WpanNodeDiscoverExample.class); + + private XBee xbee = new XBee(); + + public WpanNodeDiscoverExample() throws XBeeException, InterruptedException { + + try { + // my coordinator com/baud + xbee.open("/dev/tty.usbserial-A4004Rim", 9600); + + // get the Node discovery timeout + xbee.sendAsynchronous(new AtCommand("NT")); + AtCommandResponse nodeTimeout = (AtCommandResponse) xbee.getResponse(); + + // default is 2.5 seconds for series 1 + int nodeDiscoveryTimeout = ByteUtils.convertMultiByteToInt(nodeTimeout.getValue()) * 100; + log.info("Node discovery timeout is " + nodeDiscoveryTimeout + " milliseconds"); + + xbee.sendAsynchronous(new AtCommand("ND")); + + // collect responses up to the timeout or until the terminating response is received, whichever occurs first + List responses = xbee.collectResponses(10000, new CollectTerminator() { + public boolean stop(XBeeResponse response) { + if (response instanceof AtCommandResponse) { + AtCommandResponse at = (AtCommandResponse) response; + if (at.getCommand().equals("ND") && at.getValue() != null && at.getValue().length == 0) { + log.debug("Found terminating response"); + return true; + } + } + return false; + } + }); + + //TODO check for terminating node + + log.info("Time is up! You should have heard back from all nodes by now. If not make sure all nodes are associated and/or try increasing the node timeout (NT)"); + + for (XBeeResponse response : responses) { + if (response instanceof AtCommandResponse) { + AtCommandResponse atResponse = (AtCommandResponse) response; + + if (atResponse.getCommand().equals("ND") && atResponse.getValue() != null && atResponse.getValue().length > 0) { + WpanNodeDiscover nd = WpanNodeDiscover.parse((AtCommandResponse)response); + log.info("Node Discover is " + nd); + } + } + } + } finally { + if (xbee != null && xbee.isConnected()) { + xbee.close(); + } + } + } + + public static void main(String[] args) throws XBeeException, InterruptedException { + PropertyConfigurator.configure("log4j.properties"); + new WpanNodeDiscoverExample(); + } +} diff --git a/src/com/rapplogic/xbee/examples/zigbee/BroadcastReceiverExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/BroadcastReceiverExample.java similarity index 96% rename from src/com/rapplogic/xbee/examples/zigbee/BroadcastReceiverExample.java rename to src/main/java/com/rapplogic/xbee/examples/zigbee/BroadcastReceiverExample.java index 61db559..ee6d6a0 100644 --- a/src/com/rapplogic/xbee/examples/zigbee/BroadcastReceiverExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/BroadcastReceiverExample.java @@ -1,59 +1,59 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.examples.zigbee; - -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; - -import com.rapplogic.xbee.api.XBee; -import com.rapplogic.xbee.api.XBeeException; -import com.rapplogic.xbee.api.XBeeResponse; - -/** - * @author andrew - */ -public class BroadcastReceiverExample { - - private final static Logger log = Logger.getLogger(BroadcastReceiverExample.class); - - private BroadcastReceiverExample() throws XBeeException { - - XBee xbee = new XBee(); - - try { - // replace with your com port and baud rate. this is the com port of my coordinator - xbee.open("/dev/tty.usbserial-A6005uPi", 9600); - - while (true) { - XBeeResponse response = xbee.getResponse(); - log.info("received response " + response); - } - } finally { - if (xbee != null && xbee.isConnected()) { - xbee.close(); - } - } - } - - public static void main(String[] args) throws XBeeException, InterruptedException { - PropertyConfigurator.configure("log4j.properties"); - new BroadcastReceiverExample(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.examples.zigbee; + +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; + +import com.rapplogic.xbee.api.XBee; +import com.rapplogic.xbee.api.XBeeException; +import com.rapplogic.xbee.api.XBeeResponse; + +/** + * @author andrew + */ +public class BroadcastReceiverExample { + + private final static Logger log = Logger.getLogger(BroadcastReceiverExample.class); + + private BroadcastReceiverExample() throws XBeeException { + + XBee xbee = new XBee(); + + try { + // replace with your com port and baud rate. this is the com port of my coordinator + xbee.open("/dev/tty.usbserial-A6005uPi", 9600); + + while (true) { + XBeeResponse response = xbee.getResponse(); + log.info("received response " + response); + } + } finally { + if (xbee != null && xbee.isConnected()) { + xbee.close(); + } + } + } + + public static void main(String[] args) throws XBeeException, InterruptedException { + PropertyConfigurator.configure("log4j.properties"); + new BroadcastReceiverExample(); + } +} diff --git a/src/com/rapplogic/xbee/examples/zigbee/BroadcastSenderExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/BroadcastSenderExample.java similarity index 97% rename from src/com/rapplogic/xbee/examples/zigbee/BroadcastSenderExample.java rename to src/main/java/com/rapplogic/xbee/examples/zigbee/BroadcastSenderExample.java index 7d21e31..0b2d10d 100644 --- a/src/com/rapplogic/xbee/examples/zigbee/BroadcastSenderExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/BroadcastSenderExample.java @@ -1,77 +1,77 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.examples.zigbee; - -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; - -import com.rapplogic.xbee.api.XBee; -import com.rapplogic.xbee.api.XBeeAddress64; -import com.rapplogic.xbee.api.XBeeException; -import com.rapplogic.xbee.api.zigbee.ZNetTxRequest; -import com.rapplogic.xbee.util.ByteUtils; - -/** - * @author andrew - */ -public class BroadcastSenderExample { - - private final static Logger log = Logger.getLogger(BroadcastSenderExample.class); - - private BroadcastSenderExample() throws XBeeException { - - XBee xbee = new XBee(); - - try { - // replace with your com port and baud rate. this is the com port of my coordinator - xbee.open("/dev/ttyUSB0", 9600); - - while (true) { - // put some arbitrary data in the payload - int[] payload = ByteUtils.stringToIntArray("the\nquick\nbrown\nfox"); - - ZNetTxRequest request = new ZNetTxRequest(XBeeAddress64.BROADCAST, payload); - // make it a broadcast packet - request.setOption(ZNetTxRequest.Option.BROADCAST); - - log.info("request packet bytes (base 16) " + ByteUtils.toBase16(request.getXBeePacket().getPacket())); - - xbee.sendAsynchronous(request); - // we just assume it was sent. that's just the way it is with broadcast. - // no transmit status response is sent, so don't bother calling getResponse() - - try { - // wait a bit then send another packet - Thread.sleep(15000); - } catch (InterruptedException e) { - } - } - } finally { - if (xbee != null && xbee.isConnected()) { - xbee.close(); - } - } - } - - public static void main(String[] args) throws XBeeException, InterruptedException { - PropertyConfigurator.configure("log4j.properties"); - new BroadcastSenderExample(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.examples.zigbee; + +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; + +import com.rapplogic.xbee.api.XBee; +import com.rapplogic.xbee.api.XBeeAddress64; +import com.rapplogic.xbee.api.XBeeException; +import com.rapplogic.xbee.api.zigbee.ZNetTxRequest; +import com.rapplogic.xbee.util.ByteUtils; + +/** + * @author andrew + */ +public class BroadcastSenderExample { + + private final static Logger log = Logger.getLogger(BroadcastSenderExample.class); + + private BroadcastSenderExample() throws XBeeException { + + XBee xbee = new XBee(); + + try { + // replace with your com port and baud rate. this is the com port of my coordinator + xbee.open("/dev/ttyUSB0", 9600); + + while (true) { + // put some arbitrary data in the payload + int[] payload = ByteUtils.stringToIntArray("the\nquick\nbrown\nfox"); + + ZNetTxRequest request = new ZNetTxRequest(XBeeAddress64.BROADCAST, payload); + // make it a broadcast packet + request.setOption(ZNetTxRequest.Option.BROADCAST); + + log.info("request packet bytes (base 16) " + ByteUtils.toBase16(request.getXBeePacket().getPacket())); + + xbee.sendAsynchronous(request); + // we just assume it was sent. that's just the way it is with broadcast. + // no transmit status response is sent, so don't bother calling getResponse() + + try { + // wait a bit then send another packet + Thread.sleep(15000); + } catch (InterruptedException e) { + } + } + } finally { + if (xbee != null && xbee.isConnected()) { + xbee.close(); + } + } + } + + public static void main(String[] args) throws XBeeException, InterruptedException { + PropertyConfigurator.configure("log4j.properties"); + new BroadcastSenderExample(); + } +} diff --git a/src/com/rapplogic/xbee/examples/zigbee/SleepTestCoordinator.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/SleepTestCoordinator.java similarity index 94% rename from src/com/rapplogic/xbee/examples/zigbee/SleepTestCoordinator.java rename to src/main/java/com/rapplogic/xbee/examples/zigbee/SleepTestCoordinator.java index c21bff5..4fcd4f2 100644 --- a/src/com/rapplogic/xbee/examples/zigbee/SleepTestCoordinator.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/SleepTestCoordinator.java @@ -3,13 +3,13 @@ import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; -import com.rapplogic.xbee.api.RemoteAtRequest; -import com.rapplogic.xbee.api.RemoteAtResponse; import com.rapplogic.xbee.api.XBee; import com.rapplogic.xbee.api.XBeeAddress64; import com.rapplogic.xbee.api.XBeeConfiguration; import com.rapplogic.xbee.api.XBeeException; import com.rapplogic.xbee.api.XBeeTimeoutException; +import com.rapplogic.xbee.api.requests.RemoteAtRequest; +import com.rapplogic.xbee.api.responses.RemoteAtResponse; /** * The premise of this example is we have a end device configured in cyclic sleep. We'll send a command diff --git a/src/com/rapplogic/xbee/examples/zigbee/ZBForceSampleExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZBForceSampleExample.java similarity index 95% rename from src/com/rapplogic/xbee/examples/zigbee/ZBForceSampleExample.java rename to src/main/java/com/rapplogic/xbee/examples/zigbee/ZBForceSampleExample.java index 71e22eb..8b44363 100644 --- a/src/com/rapplogic/xbee/examples/zigbee/ZBForceSampleExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZBForceSampleExample.java @@ -1,94 +1,94 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.examples.zigbee; - -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; - -import com.rapplogic.xbee.api.XBee; -import com.rapplogic.xbee.api.XBeeAddress64; -import com.rapplogic.xbee.api.XBeeRequest; -import com.rapplogic.xbee.api.XBeeResponse; -import com.rapplogic.xbee.api.XBeeTimeoutException; -import com.rapplogic.xbee.api.RemoteAtResponse; -import com.rapplogic.xbee.api.zigbee.ZBForceSampleRequest; -import com.rapplogic.xbee.api.zigbee.ZNetRxIoSampleResponse; - -/** - * To run this sample you will need to configure pin 19 (D1) to Analog input on the remote XBee: D1=2. - * To do this you can use X-CTU, the AtCommand [new AtCommand("D1", 2)] with your remote XBee connected to the serial port - * or with RemoteAtRequest and send the request through the coordinator. - * - * @author andrew - * - */ -public class ZBForceSampleExample { - - private final static Logger log = Logger.getLogger(ZBForceSampleExample.class); - - private ZBForceSampleExample() throws Exception { - XBee xbee = new XBee(); - - try { - // replace with the com port of your XBee coordinator - xbee.open("/dev/tty.usbserial-A6005v5M", 9600); - - while (true) { - // All XBees allow you to request an I/O sample on a local XBee (serial connected), however this is not very interesting. - // With ZNet/ZB Pro radios we can use Remote AT to force an I/O sample on an end device. - // The following code issues a force sample on a XBee end device and parses the io sample. - - // replace with your end device 64-bit address - XBeeAddress64 addr64 = new XBeeAddress64(0, 0x13, 0xa2, 0, 0x40, 0x0a, 0x3e, 0x02); - - XBeeRequest request = new ZBForceSampleRequest(addr64); - - try { - XBeeResponse response = xbee.sendSynchronous(request, 6000); - RemoteAtResponse remoteAt = (RemoteAtResponse) response; - - if (remoteAt.isOk()) { - // extract the i/o sample - ZNetRxIoSampleResponse ioSample = ZNetRxIoSampleResponse.parseIsSample(remoteAt); - // make sure you configured the remote XBee to D1=2 (analog input) or you will get an error - log.info("10 bit analog1 sample is " + ioSample.getAnalog1()); - } else { - log.info("Remote AT request failed: " + response); - } - } catch (XBeeTimeoutException e) { - log.info("request timed out"); - } - - // wait a bit - Thread.sleep(2000); - } - } finally { - if (xbee != null && xbee.isConnected()) { - xbee.close(); - } - } - } - - public static void main(String[] args) throws Exception { - // init log4j - PropertyConfigurator.configure("log4j.properties"); - new ZBForceSampleExample(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.examples.zigbee; + +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; + +import com.rapplogic.xbee.api.XBee; +import com.rapplogic.xbee.api.XBeeAddress64; +import com.rapplogic.xbee.api.XBeeRequest; +import com.rapplogic.xbee.api.XBeeResponse; +import com.rapplogic.xbee.api.XBeeTimeoutException; +import com.rapplogic.xbee.api.responses.RemoteAtResponse; +import com.rapplogic.xbee.api.zigbee.ZBForceSampleRequest; +import com.rapplogic.xbee.api.zigbee.ZNetRxIoSampleResponse; + +/** + * To run this sample you will need to configure pin 19 (D1) to Analog input on the remote XBee: D1=2. + * To do this you can use X-CTU, the AtCommand [new AtCommand("D1", 2)] with your remote XBee connected to the serial port + * or with RemoteAtRequest and send the request through the coordinator. + * + * @author andrew + * + */ +public class ZBForceSampleExample { + + private final static Logger log = Logger.getLogger(ZBForceSampleExample.class); + + private ZBForceSampleExample() throws Exception { + XBee xbee = new XBee(); + + try { + // replace with the com port of your XBee coordinator + xbee.open("/dev/tty.usbserial-A6005v5M", 9600); + + while (true) { + // All XBees allow you to request an I/O sample on a local XBee (serial connected), however this is not very interesting. + // With ZNet/ZB Pro radios we can use Remote AT to force an I/O sample on an end device. + // The following code issues a force sample on a XBee end device and parses the io sample. + + // replace with your end device 64-bit address + XBeeAddress64 addr64 = new XBeeAddress64(0, 0x13, 0xa2, 0, 0x40, 0x0a, 0x3e, 0x02); + + XBeeRequest request = new ZBForceSampleRequest(addr64); + + try { + XBeeResponse response = xbee.sendSynchronous(request, 6000); + RemoteAtResponse remoteAt = (RemoteAtResponse) response; + + if (remoteAt.isOk()) { + // extract the i/o sample + ZNetRxIoSampleResponse ioSample = ZNetRxIoSampleResponse.parseIsSample(remoteAt); + // make sure you configured the remote XBee to D1=2 (analog input) or you will get an error + log.info("10 bit analog1 sample is " + ioSample.getAnalog1()); + } else { + log.info("Remote AT request failed: " + response); + } + } catch (XBeeTimeoutException e) { + log.info("request timed out"); + } + + // wait a bit + Thread.sleep(2000); + } + } finally { + if (xbee != null && xbee.isConnected()) { + xbee.close(); + } + } + } + + public static void main(String[] args) throws Exception { + // init log4j + PropertyConfigurator.configure("log4j.properties"); + new ZBForceSampleExample(); + } +} diff --git a/src/com/rapplogic/xbee/examples/zigbee/ZBNodeDiscoverExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZBNodeDiscoverExample.java similarity index 94% rename from src/com/rapplogic/xbee/examples/zigbee/ZBNodeDiscoverExample.java rename to src/main/java/com/rapplogic/xbee/examples/zigbee/ZBNodeDiscoverExample.java index ce94fb3..562ca66 100644 --- a/src/com/rapplogic/xbee/examples/zigbee/ZBNodeDiscoverExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZBNodeDiscoverExample.java @@ -1,96 +1,96 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.examples.zigbee; - -import java.util.List; - -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; - -import com.rapplogic.xbee.api.ApiId; -import com.rapplogic.xbee.api.AtCommand; -import com.rapplogic.xbee.api.AtCommandResponse; -import com.rapplogic.xbee.api.PacketListener; -import com.rapplogic.xbee.api.XBee; -import com.rapplogic.xbee.api.XBeeException; -import com.rapplogic.xbee.api.XBeeResponse; -import com.rapplogic.xbee.api.zigbee.ZBNodeDiscover; -import com.rapplogic.xbee.util.ByteUtils; - -/** - * Example of performing a node discover for Series 2 XBees. - * You must connect to the coordinator to run this example and - * have one or more end device/routers that are associated. - * - * @author andrew - * - */ -public class ZBNodeDiscoverExample { - - private final static Logger log = Logger.getLogger(ZBNodeDiscoverExample.class); - - private XBee xbee = new XBee(); - - public ZBNodeDiscoverExample() throws XBeeException, InterruptedException { - - try { - // replace with your serial port - xbee.open("/dev/tty.usbserial-A6005v5M", 9600); - - - // get the Node discovery timeout - xbee.sendAsynchronous(new AtCommand("NT")); - AtCommandResponse nodeTimeout = (AtCommandResponse) xbee.getResponse(); - - // default is 6 seconds - int nodeDiscoveryTimeout = ByteUtils.convertMultiByteToInt(nodeTimeout.getValue()) * 100; - log.info("Node discovery timeout is " + nodeDiscoveryTimeout + " milliseconds"); - - log.info("Sending Node Discover command"); - xbee.sendAsynchronous(new AtCommand("ND")); - - // NOTE: increase NT if you are not seeing all your nodes reported - - List responses = xbee.collectResponses(nodeDiscoveryTimeout); - - log.info("Time is up! You should have heard back from all nodes by now. If not make sure all nodes are associated and/or try increasing the node timeout (NT)"); - - for (XBeeResponse response : responses) { - if (response instanceof AtCommandResponse) { - AtCommandResponse atResponse = (AtCommandResponse) response; - - if (atResponse.getCommand().equals("ND") && atResponse.getValue() != null && atResponse.getValue().length > 0) { - ZBNodeDiscover nd = ZBNodeDiscover.parse((AtCommandResponse)response); - log.info("Node Discover is " + nd); - } - } - } - } finally { - if (xbee != null && xbee.isConnected()) { - xbee.close(); - } - } - } - - public static void main(String[] args) throws XBeeException, InterruptedException { - PropertyConfigurator.configure("log4j.properties"); - new ZBNodeDiscoverExample(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.examples.zigbee; + +import java.util.List; + +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; + +import com.rapplogic.xbee.api.ApiId; +import com.rapplogic.xbee.api.PacketListener; +import com.rapplogic.xbee.api.XBee; +import com.rapplogic.xbee.api.XBeeException; +import com.rapplogic.xbee.api.XBeeResponse; +import com.rapplogic.xbee.api.requests.AtCommand; +import com.rapplogic.xbee.api.responses.AtCommandResponse; +import com.rapplogic.xbee.api.zigbee.ZBNodeDiscover; +import com.rapplogic.xbee.util.ByteUtils; + +/** + * Example of performing a node discover for Series 2 XBees. + * You must connect to the coordinator to run this example and + * have one or more end device/routers that are associated. + * + * @author andrew + * + */ +public class ZBNodeDiscoverExample { + + private final static Logger log = Logger.getLogger(ZBNodeDiscoverExample.class); + + private XBee xbee = new XBee(); + + public ZBNodeDiscoverExample() throws XBeeException, InterruptedException { + + try { + // replace with your serial port + xbee.open("/dev/tty.usbserial-A6005v5M", 9600); + + + // get the Node discovery timeout + xbee.sendAsynchronous(new AtCommand("NT")); + AtCommandResponse nodeTimeout = (AtCommandResponse) xbee.getResponse(); + + // default is 6 seconds + int nodeDiscoveryTimeout = ByteUtils.convertMultiByteToInt(nodeTimeout.getValue()) * 100; + log.info("Node discovery timeout is " + nodeDiscoveryTimeout + " milliseconds"); + + log.info("Sending Node Discover command"); + xbee.sendAsynchronous(new AtCommand("ND")); + + // NOTE: increase NT if you are not seeing all your nodes reported + + List responses = xbee.collectResponses(nodeDiscoveryTimeout); + + log.info("Time is up! You should have heard back from all nodes by now. If not make sure all nodes are associated and/or try increasing the node timeout (NT)"); + + for (XBeeResponse response : responses) { + if (response instanceof AtCommandResponse) { + AtCommandResponse atResponse = (AtCommandResponse) response; + + if (atResponse.getCommand().equals("ND") && atResponse.getValue() != null && atResponse.getValue().length > 0) { + ZBNodeDiscover nd = ZBNodeDiscover.parse((AtCommandResponse)response); + log.info("Node Discover is " + nd); + } + } + } + } finally { + if (xbee != null && xbee.isConnected()) { + xbee.close(); + } + } + } + + public static void main(String[] args) throws XBeeException, InterruptedException { + PropertyConfigurator.configure("log4j.properties"); + new ZBNodeDiscoverExample(); + } +} diff --git a/src/com/rapplogic/xbee/examples/zigbee/ZNetApiAtExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetApiAtExample.java old mode 100755 new mode 100644 similarity index 95% rename from src/com/rapplogic/xbee/examples/zigbee/ZNetApiAtExample.java rename to src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetApiAtExample.java index f786936..81f0351 --- a/src/com/rapplogic/xbee/examples/zigbee/ZNetApiAtExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetApiAtExample.java @@ -1,171 +1,171 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.examples.zigbee; - -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; - -import com.rapplogic.xbee.api.AtCommand; -import com.rapplogic.xbee.api.AtCommandResponse; -import com.rapplogic.xbee.api.XBee; -import com.rapplogic.xbee.api.XBeeException; -import com.rapplogic.xbee.api.XBeeResponse; -import com.rapplogic.xbee.api.zigbee.AssociationStatus; -import com.rapplogic.xbee.util.ByteUtils; - -/** - * This class contains AtCommand examples that are specific to ZNet radios - * - * @author andrew - * - */ -public class ZNetApiAtExample { - - private final static Logger log = Logger.getLogger(ZNetApiAtExample.class); - - private XBee xbee = new XBee(); - - public ZNetApiAtExample() throws XBeeException { - try { - - // replace with port and baud rate of your XBee - xbee.open("COM6", 9600); - - // get the 8 byte SH/SL address - log.debug("SH is " + ByteUtils.toBase16(((AtCommandResponse)xbee.sendAtCommand(new AtCommand("SH"))).getValue())); - log.debug("SL is " + ByteUtils.toBase16(((AtCommandResponse)xbee.sendAtCommand(new AtCommand("SL"))).getValue())); - - // uncomment to run -// this.configureIOSamples(xbee); -// this.associationStatus(xbee); -// this.nodeDiscover(xbee); -// this.configureCoordinator(xbee); -// this.configureEndDevice(xbee); - } finally { - if (xbee != null && xbee.isConnected()) { - xbee.close(); - } - } - } - - private void associationStatus(XBee xbee) throws XBeeException { - // get association status - success indicates it is associated to another XBee - AtCommandResponse response = (AtCommandResponse) xbee.sendAtCommand(new AtCommand("AI")); - log.debug("Association Status is " + AssociationStatus.get(response)); - } - - private void configureEndDevice(XBee xbee) throws XBeeException { - - // basic end device configuration (assumes ZNet radio flashed with end device API firmware) - XBeeResponse response = null; - - // reset to factory settings - response = xbee.sendAtCommand(new AtCommand("RE")); - log.debug("RE is " + response); - - // set PAN id to arbitrary value - response = xbee.sendAtCommand(new AtCommand("ID", new int[] {0x1a, 0xaa})); - log.debug("ID is " + response); - - // set NI -- can be any arbitrary sequence of chars - response = xbee.sendAtCommand(new AtCommand("NI", new int[] {'E','N','D','_','D','E','V','I','C','E','_','2' })); - log.debug("NI is " + response); - - // set API mode to 2. factory setting is 1 - response = xbee.sendAtCommand(new AtCommand("AP", 2)); - log.debug("AP is " + response); - - // save to settings to survive power cycle - response = xbee.sendAtCommand(new AtCommand("WR")); - log.debug("WR is " + response); - - // software reset - response = xbee.sendAtCommand(new AtCommand("FR")); - log.debug("FR is " + response); - } - - private void configureCoordinator(XBee xbee) throws XBeeException { - // basic coordinator configuration (assumes ZNet radio flashed with COORDINATOR API firmware) - XBeeResponse response = null; - - // reset to factory settings - response = xbee.sendAtCommand(new AtCommand("RE")); - log.debug("RE is " + response); - - // set PAN id to arbitrary value - response = xbee.sendAtCommand(new AtCommand("ID", new int[] {0x1a, 0xaa})); - log.debug("RE is " + response); - - // set NI - response = xbee.sendAtCommand(new AtCommand("NI", new int[] {'C','O','O','R','D','I','N','A','T','O','R' })); - log.debug("NI is " + response); - - // set API mode to 2. factory setting is 1 - response = xbee.sendAtCommand(new AtCommand("AP", 2)); - log.debug("AP is " + response); - - // save to settings to survive power cycle - response = xbee.sendAtCommand(new AtCommand("WR")); - log.debug("WR is " + response); - - // software reset - response = xbee.sendAtCommand(new AtCommand("FR")); - log.debug("FR is " + response); - } - - /** - * This assumes an end device that is already has the configureEndDevice configuration - * Does not save configuration! -- use WR if you want this configure to survive power on/off. - * - * @param xbee - * @throws XBeeException - */ - private void configureIOSamples(XBee xbee) throws XBeeException { - // basic coordinator configuration (assumes ZNet radio flashed with COORDINATOR API firmware) - XBeeResponse response = null; - - // set IR to 1 sample every 10 seconds. Set to 0 to disable - response = xbee.sendAtCommand(new AtCommand("IR", new int[] {0x27, 0x10})); - log.debug("IR is " + response); - - // set pin 20 to monitor digital input - response = xbee.sendAtCommand(new AtCommand("DO", 0x3)); - log.debug("DO is " + response); - - // set pin 19 to monitor analog input - response = xbee.sendAtCommand(new AtCommand("D1", 0x2)); - log.debug("D1 is " + response); - - // set pin 18 to monitor analog input - response = xbee.sendAtCommand(new AtCommand("D2", 0x2)); - log.debug("D2 is " + response); - - // set pin 16 to monitor digital input - response = xbee.sendAtCommand(new AtCommand("D6", 0x3)); - log.debug("D6 is " + response); - - // optionally configure DH + DL; if set to zero (default), samples will be sent to coordinator - } - - public static void main(String[] args) throws XBeeException { - PropertyConfigurator.configure("log4j.properties"); - new ZNetApiAtExample(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.examples.zigbee; + +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; + +import com.rapplogic.xbee.api.XBee; +import com.rapplogic.xbee.api.XBeeException; +import com.rapplogic.xbee.api.XBeeResponse; +import com.rapplogic.xbee.api.requests.AtCommand; +import com.rapplogic.xbee.api.responses.AtCommandResponse; +import com.rapplogic.xbee.api.zigbee.AssociationStatus; +import com.rapplogic.xbee.util.ByteUtils; + +/** + * This class contains AtCommand examples that are specific to ZNet radios + * + * @author andrew + * + */ +public class ZNetApiAtExample { + + private final static Logger log = Logger.getLogger(ZNetApiAtExample.class); + + private XBee xbee = new XBee(); + + public ZNetApiAtExample() throws XBeeException { + try { + + // replace with port and baud rate of your XBee + xbee.open("COM6", 9600); + + // get the 8 byte SH/SL address + log.debug("SH is " + ByteUtils.toBase16(((AtCommandResponse)xbee.sendAtCommand(new AtCommand("SH"))).getValue())); + log.debug("SL is " + ByteUtils.toBase16(((AtCommandResponse)xbee.sendAtCommand(new AtCommand("SL"))).getValue())); + + // uncomment to run +// this.configureIOSamples(xbee); +// this.associationStatus(xbee); +// this.nodeDiscover(xbee); +// this.configureCoordinator(xbee); +// this.configureEndDevice(xbee); + } finally { + if (xbee != null && xbee.isConnected()) { + xbee.close(); + } + } + } + + private void associationStatus(XBee xbee) throws XBeeException { + // get association status - success indicates it is associated to another XBee + AtCommandResponse response = (AtCommandResponse) xbee.sendAtCommand(new AtCommand("AI")); + log.debug("Association Status is " + AssociationStatus.get(response)); + } + + private void configureEndDevice(XBee xbee) throws XBeeException { + + // basic end device configuration (assumes ZNet radio flashed with end device API firmware) + XBeeResponse response = null; + + // reset to factory settings + response = xbee.sendAtCommand(new AtCommand("RE")); + log.debug("RE is " + response); + + // set PAN id to arbitrary value + response = xbee.sendAtCommand(new AtCommand("ID", new int[] {0x1a, 0xaa})); + log.debug("ID is " + response); + + // set NI -- can be any arbitrary sequence of chars + response = xbee.sendAtCommand(new AtCommand("NI", new int[] {'E','N','D','_','D','E','V','I','C','E','_','2' })); + log.debug("NI is " + response); + + // set API mode to 2. factory setting is 1 + response = xbee.sendAtCommand(new AtCommand("AP", 2)); + log.debug("AP is " + response); + + // save to settings to survive power cycle + response = xbee.sendAtCommand(new AtCommand("WR")); + log.debug("WR is " + response); + + // software reset + response = xbee.sendAtCommand(new AtCommand("FR")); + log.debug("FR is " + response); + } + + private void configureCoordinator(XBee xbee) throws XBeeException { + // basic coordinator configuration (assumes ZNet radio flashed with COORDINATOR API firmware) + XBeeResponse response = null; + + // reset to factory settings + response = xbee.sendAtCommand(new AtCommand("RE")); + log.debug("RE is " + response); + + // set PAN id to arbitrary value + response = xbee.sendAtCommand(new AtCommand("ID", new int[] {0x1a, 0xaa})); + log.debug("RE is " + response); + + // set NI + response = xbee.sendAtCommand(new AtCommand("NI", new int[] {'C','O','O','R','D','I','N','A','T','O','R' })); + log.debug("NI is " + response); + + // set API mode to 2. factory setting is 1 + response = xbee.sendAtCommand(new AtCommand("AP", 2)); + log.debug("AP is " + response); + + // save to settings to survive power cycle + response = xbee.sendAtCommand(new AtCommand("WR")); + log.debug("WR is " + response); + + // software reset + response = xbee.sendAtCommand(new AtCommand("FR")); + log.debug("FR is " + response); + } + + /** + * This assumes an end device that is already has the configureEndDevice configuration + * Does not save configuration! -- use WR if you want this configure to survive power on/off. + * + * @param xbee + * @throws XBeeException + */ + private void configureIOSamples(XBee xbee) throws XBeeException { + // basic coordinator configuration (assumes ZNet radio flashed with COORDINATOR API firmware) + XBeeResponse response = null; + + // set IR to 1 sample every 10 seconds. Set to 0 to disable + response = xbee.sendAtCommand(new AtCommand("IR", new int[] {0x27, 0x10})); + log.debug("IR is " + response); + + // set pin 20 to monitor digital input + response = xbee.sendAtCommand(new AtCommand("DO", 0x3)); + log.debug("DO is " + response); + + // set pin 19 to monitor analog input + response = xbee.sendAtCommand(new AtCommand("D1", 0x2)); + log.debug("D1 is " + response); + + // set pin 18 to monitor analog input + response = xbee.sendAtCommand(new AtCommand("D2", 0x2)); + log.debug("D2 is " + response); + + // set pin 16 to monitor digital input + response = xbee.sendAtCommand(new AtCommand("D6", 0x3)); + log.debug("D6 is " + response); + + // optionally configure DH + DL; if set to zero (default), samples will be sent to coordinator + } + + public static void main(String[] args) throws XBeeException { + PropertyConfigurator.configure("log4j.properties"); + new ZNetApiAtExample(); + } +} diff --git a/src/com/rapplogic/xbee/examples/zigbee/ZNetExplicitReceiverExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetExplicitReceiverExample.java similarity index 96% rename from src/com/rapplogic/xbee/examples/zigbee/ZNetExplicitReceiverExample.java rename to src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetExplicitReceiverExample.java index 93cf0e2..2562823 100644 --- a/src/com/rapplogic/xbee/examples/zigbee/ZNetExplicitReceiverExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetExplicitReceiverExample.java @@ -1,77 +1,77 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.examples.zigbee; - -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; - -import com.rapplogic.xbee.api.ApiId; -import com.rapplogic.xbee.api.XBee; -import com.rapplogic.xbee.api.XBeeResponse; -import com.rapplogic.xbee.api.zigbee.ZNetExplicitRxResponse; - -/** - * Set AO=1 for to enable explicit frames for this example - * - * @author andrew - * - */ -public class ZNetExplicitReceiverExample { - - private final static Logger log = Logger.getLogger(ZNetExplicitReceiverExample.class); - - private ZNetExplicitReceiverExample() throws Exception { - XBee xbee = new XBee(); - - try { - // replace with the com port or your receiving XBee - // this is the com port of my end device on my mac - xbee.open("/dev/tty.usbserial-A6005uRz", 9600); - - while (true) { - - try { - // we wait here until a packet is received. - XBeeResponse response = xbee.getResponse(); - - if (response.getApiId() == ApiId.ZNET_EXPLICIT_RX_RESPONSE) { - ZNetExplicitRxResponse rx = (ZNetExplicitRxResponse) response; - - log.info("received explicit packet response " + response.toString()); - } else { - log.debug("received unexpected packet " + response.toString()); - } - } catch (Exception e) { - log.error(e); - } - } - } finally { - if (xbee != null && xbee.isConnected()) { - xbee.close(); - } - } - } - - public static void main(String[] args) throws Exception { - // init log4j - PropertyConfigurator.configure("log4j.properties"); - new ZNetExplicitReceiverExample(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.examples.zigbee; + +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; + +import com.rapplogic.xbee.api.ApiId; +import com.rapplogic.xbee.api.XBee; +import com.rapplogic.xbee.api.XBeeResponse; +import com.rapplogic.xbee.api.zigbee.ZNetExplicitRxResponse; + +/** + * Set AO=1 for to enable explicit frames for this example + * + * @author andrew + * + */ +public class ZNetExplicitReceiverExample { + + private final static Logger log = Logger.getLogger(ZNetExplicitReceiverExample.class); + + private ZNetExplicitReceiverExample() throws Exception { + XBee xbee = new XBee(); + + try { + // replace with the com port or your receiving XBee + // this is the com port of my end device on my mac + xbee.open("/dev/tty.usbserial-A6005uRz", 9600); + + while (true) { + + try { + // we wait here until a packet is received. + XBeeResponse response = xbee.getResponse(); + + if (response.getApiId() == ApiId.ZNET_EXPLICIT_RX_RESPONSE) { + ZNetExplicitRxResponse rx = (ZNetExplicitRxResponse) response; + + log.info("received explicit packet response " + response.toString()); + } else { + log.debug("received unexpected packet " + response.toString()); + } + } catch (Exception e) { + log.error(e); + } + } + } finally { + if (xbee != null && xbee.isConnected()) { + xbee.close(); + } + } + } + + public static void main(String[] args) throws Exception { + // init log4j + PropertyConfigurator.configure("log4j.properties"); + new ZNetExplicitReceiverExample(); + } +} diff --git a/src/com/rapplogic/xbee/examples/zigbee/ZNetExplicitSenderExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetExplicitSenderExample.java similarity index 97% rename from src/com/rapplogic/xbee/examples/zigbee/ZNetExplicitSenderExample.java rename to src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetExplicitSenderExample.java index 5b99721..1b6aae4 100644 --- a/src/com/rapplogic/xbee/examples/zigbee/ZNetExplicitSenderExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetExplicitSenderExample.java @@ -1,98 +1,98 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.examples.zigbee; - -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; - -import com.rapplogic.xbee.api.XBee; -import com.rapplogic.xbee.api.XBeeAddress16; -import com.rapplogic.xbee.api.XBeeAddress64; -import com.rapplogic.xbee.api.XBeeException; -import com.rapplogic.xbee.api.XBeeResponse; -import com.rapplogic.xbee.api.zigbee.ZNetExplicitTxRequest; -import com.rapplogic.xbee.api.zigbee.ZNetTxRequest; -import com.rapplogic.xbee.util.DoubleByte; - -/** - * Set AO=1 for to enable explicit frames for this example. - * Once set, you should use explicit tx/rx packets instead of plain vanilla tx requests (ZNetTxRequest). - * You can still send ZNetTxRequest requests but they will be received as explicit responses (ZNetExplicitRxResponse) - * - * @author andrew - * - */ -public class ZNetExplicitSenderExample { - - private final static Logger log = Logger.getLogger(ZNetExplicitSenderExample.class); - - private ZNetExplicitSenderExample() throws XBeeException { - - XBee xbee = new XBee(); - - try { - // replace with your com port and baud rate. this is the com port of my coordinator - //xbee.open("COM5", 9600); - xbee.open("/dev/tty.usbserial-A6005v5M", 9600); - - // replace with end device's 64-bit address (SH + SL) - XBeeAddress64 addr64 = new XBeeAddress64(0, 0x13, 0xa2, 0, 0x40, 0x0a, 0x3e, 0x02); - - // create an array of arbitrary data to send - int[] payload = new int[] { 0, 0x66, 0xee }; - - // loopback test - int sourceEndpoint = 0; - int destinationEndpoint = ZNetExplicitTxRequest.Endpoint.DATA.getValue(); - - DoubleByte clusterId = new DoubleByte(0x0, ZNetExplicitTxRequest.ClusterId.SERIAL_LOOPBACK.getValue()); - //DoubleByte clusterId = new DoubleByte(0x0, ZNetExplicitTxRequest.ClusterId.TRANSPARENT_SERIAL.getValue()); - - // first request we just send 64-bit address. we get 16-bit network address with status response - ZNetExplicitTxRequest request = new ZNetExplicitTxRequest(0xff, addr64, XBeeAddress16.ZNET_BROADCAST, - ZNetTxRequest.DEFAULT_BROADCAST_RADIUS, ZNetTxRequest.Option.UNICAST, payload, sourceEndpoint, destinationEndpoint, clusterId, ZNetExplicitTxRequest.znetProfileId); - - log.info("sending explicit " + request.toString()); - - while (true) { - xbee.sendAsynchronous(request); - - XBeeResponse response = xbee.getResponse(); - - log.info("received response " + response.toString()); - - try { - // wait a bit then send another packet - Thread.sleep(5000); - } catch (InterruptedException e) { - } - } - } finally { - if (xbee != null && xbee.isConnected()) { - xbee.close(); - } - } - } - - public static void main(String[] args) throws XBeeException, InterruptedException { - PropertyConfigurator.configure("log4j.properties"); - new ZNetExplicitSenderExample(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.examples.zigbee; + +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; + +import com.rapplogic.xbee.api.XBee; +import com.rapplogic.xbee.api.XBeeAddress16; +import com.rapplogic.xbee.api.XBeeAddress64; +import com.rapplogic.xbee.api.XBeeException; +import com.rapplogic.xbee.api.XBeeResponse; +import com.rapplogic.xbee.api.zigbee.ZNetExplicitTxRequest; +import com.rapplogic.xbee.api.zigbee.ZNetTxRequest; +import com.rapplogic.xbee.util.DoubleByte; + +/** + * Set AO=1 for to enable explicit frames for this example. + * Once set, you should use explicit tx/rx packets instead of plain vanilla tx requests (ZNetTxRequest). + * You can still send ZNetTxRequest requests but they will be received as explicit responses (ZNetExplicitRxResponse) + * + * @author andrew + * + */ +public class ZNetExplicitSenderExample { + + private final static Logger log = Logger.getLogger(ZNetExplicitSenderExample.class); + + private ZNetExplicitSenderExample() throws XBeeException { + + XBee xbee = new XBee(); + + try { + // replace with your com port and baud rate. this is the com port of my coordinator + //xbee.open("COM5", 9600); + xbee.open("/dev/tty.usbserial-A6005v5M", 9600); + + // replace with end device's 64-bit address (SH + SL) + XBeeAddress64 addr64 = new XBeeAddress64(0, 0x13, 0xa2, 0, 0x40, 0x0a, 0x3e, 0x02); + + // create an array of arbitrary data to send + int[] payload = new int[] { 0, 0x66, 0xee }; + + // loopback test + int sourceEndpoint = 0; + int destinationEndpoint = ZNetExplicitTxRequest.Endpoint.DATA.getValue(); + + DoubleByte clusterId = new DoubleByte(0x0, ZNetExplicitTxRequest.ClusterId.SERIAL_LOOPBACK.getValue()); + //DoubleByte clusterId = new DoubleByte(0x0, ZNetExplicitTxRequest.ClusterId.TRANSPARENT_SERIAL.getValue()); + + // first request we just send 64-bit address. we get 16-bit network address with status response + ZNetExplicitTxRequest request = new ZNetExplicitTxRequest(0xff, addr64, XBeeAddress16.ZNET_BROADCAST, + ZNetTxRequest.DEFAULT_BROADCAST_RADIUS, ZNetTxRequest.Option.UNICAST, payload, sourceEndpoint, destinationEndpoint, clusterId, ZNetExplicitTxRequest.znetProfileId); + + log.info("sending explicit " + request.toString()); + + while (true) { + xbee.sendAsynchronous(request); + + XBeeResponse response = xbee.getResponse(); + + log.info("received response " + response.toString()); + + try { + // wait a bit then send another packet + Thread.sleep(5000); + } catch (InterruptedException e) { + } + } + } finally { + if (xbee != null && xbee.isConnected()) { + xbee.close(); + } + } + } + + public static void main(String[] args) throws XBeeException, InterruptedException { + PropertyConfigurator.configure("log4j.properties"); + new ZNetExplicitSenderExample(); + } +} diff --git a/src/com/rapplogic/xbee/examples/zigbee/ZNetIoSampleExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetIoSampleExample.java similarity index 97% rename from src/com/rapplogic/xbee/examples/zigbee/ZNetIoSampleExample.java rename to src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetIoSampleExample.java index fa498cb..6a4e9d6 100644 --- a/src/com/rapplogic/xbee/examples/zigbee/ZNetIoSampleExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetIoSampleExample.java @@ -1,85 +1,85 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.examples.zigbee; - -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; - -import com.rapplogic.xbee.api.ApiId; -import com.rapplogic.xbee.api.PacketListener; -import com.rapplogic.xbee.api.XBee; -import com.rapplogic.xbee.api.XBeeResponse; -import com.rapplogic.xbee.api.wpan.IoSample; -import com.rapplogic.xbee.api.wpan.RxResponseIoSample; -import com.rapplogic.xbee.api.zigbee.ZNetRxIoSampleResponse; - -/** - * Series 2 XBees -- Example of receiving I/O samples. To configure your radio for this example, connect - * your end device to your serial connection and run the configureIOSamples() method - * in ZNetApiAtTest. - * - * @author andrew - * - */ -public class ZNetIoSampleExample implements PacketListener { - - private final static Logger log = Logger.getLogger(ZNetIoSampleExample.class); - - private ZNetIoSampleExample() throws Exception { - XBee xbee = new XBee(); - - try { - // replace with the com port of your XBee coordinator - xbee.open("/dev/tty.usbserial-A6005v5M", 9600); - xbee.addPacketListener(this); - - // wait forever - synchronized(this) { this.wait(); } - } finally { - if (xbee != null && xbee.isConnected()) { - xbee.close(); - } - } - } - - /** - * Called by XBee API thread when a packet is received - */ - public void processResponse(XBeeResponse response) { - // This is a I/O sample response. You will only get this is you are connected to a Coordinator that is configured to - // receive I/O samples from a remote XBee. - - if (response.getApiId() == ApiId.ZNET_IO_SAMPLE_RESPONSE) { - ZNetRxIoSampleResponse ioSample = (ZNetRxIoSampleResponse) response; - - log.debug("received i/o sample packet. contains analog is " + ioSample.containsAnalog() + ", contains digital is " + ioSample.containsDigital()); - - // check the value of the input pins - log.debug("pin 20 (DO) digital is " + ioSample.isD0On()); - log.debug("pin 19 (D1) analog is " + ioSample.getAnalog1()); - } - } - - public static void main(String[] args) throws Exception { - // init log4j - PropertyConfigurator.configure("log4j.properties"); - new ZNetIoSampleExample(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.examples.zigbee; + +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; + +import com.rapplogic.xbee.api.ApiId; +import com.rapplogic.xbee.api.PacketListener; +import com.rapplogic.xbee.api.XBee; +import com.rapplogic.xbee.api.XBeeResponse; +import com.rapplogic.xbee.api.wpan.IoSample; +import com.rapplogic.xbee.api.wpan.RxResponseIoSample; +import com.rapplogic.xbee.api.zigbee.ZNetRxIoSampleResponse; + +/** + * Series 2 XBees -- Example of receiving I/O samples. To configure your radio for this example, connect + * your end device to your serial connection and run the configureIOSamples() method + * in ZNetApiAtTest. + * + * @author andrew + * + */ +public class ZNetIoSampleExample implements PacketListener { + + private final static Logger log = Logger.getLogger(ZNetIoSampleExample.class); + + private ZNetIoSampleExample() throws Exception { + XBee xbee = new XBee(); + + try { + // replace with the com port of your XBee coordinator + xbee.open("/dev/tty.usbserial-A6005v5M", 9600); + xbee.addPacketListener(this); + + // wait forever + synchronized(this) { this.wait(); } + } finally { + if (xbee != null && xbee.isConnected()) { + xbee.close(); + } + } + } + + /** + * Called by XBee API thread when a packet is received + */ + public void processResponse(XBeeResponse response) { + // This is a I/O sample response. You will only get this is you are connected to a Coordinator that is configured to + // receive I/O samples from a remote XBee. + + if (response.getApiId() == ApiId.ZNET_IO_SAMPLE_RESPONSE) { + ZNetRxIoSampleResponse ioSample = (ZNetRxIoSampleResponse) response; + + log.debug("received i/o sample packet. contains analog is " + ioSample.containsAnalog() + ", contains digital is " + ioSample.containsDigital()); + + // check the value of the input pins + log.debug("pin 20 (DO) digital is " + ioSample.isD0On()); + log.debug("pin 19 (D1) analog is " + ioSample.getAnalog1()); + } + } + + public static void main(String[] args) throws Exception { + // init log4j + PropertyConfigurator.configure("log4j.properties"); + new ZNetIoSampleExample(); + } +} diff --git a/src/com/rapplogic/xbee/examples/zigbee/ZNetReceiverExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetReceiverExample.java similarity index 94% rename from src/com/rapplogic/xbee/examples/zigbee/ZNetReceiverExample.java rename to src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetReceiverExample.java index cf300da..2f4e383 100644 --- a/src/com/rapplogic/xbee/examples/zigbee/ZNetReceiverExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetReceiverExample.java @@ -1,100 +1,100 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.examples.zigbee; - -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; - -import com.rapplogic.xbee.api.ApiId; -import com.rapplogic.xbee.api.AtCommand; -import com.rapplogic.xbee.api.AtCommandResponse; -import com.rapplogic.xbee.api.XBee; -import com.rapplogic.xbee.api.XBeeResponse; -import com.rapplogic.xbee.api.zigbee.ZNetRxResponse; -import com.rapplogic.xbee.util.ByteUtils; - -/** - * This class is the companion to ZNetSenderTest.java, and as such, it receives packets sent by ZNetSenderTest.java - * See the ZNetSenderTest.java for information on how to configure your XBee for this demo - * - * You can start ZNetSenderTest.java and this class in any order but it's generally best to start this class first. - * - * @author andrew - * - */ -public class ZNetReceiverExample { - - private final static Logger log = Logger.getLogger(ZNetReceiverExample.class); - - private ZNetReceiverExample() throws Exception { - XBee xbee = new XBee(); - - try { - // replace with the com port of your receiving XBee (typically your end device) - // router - xbee.open("/dev/tty.usbserial-A6005uPi", 9600); - - while (true) { - - try { - // we wait here until a packet is received. - XBeeResponse response = xbee.getResponse(); - - log.info("received response " + response.toString()); - - if (response.getApiId() == ApiId.ZNET_RX_RESPONSE) { - // we received a packet from ZNetSenderTest.java - ZNetRxResponse rx = (ZNetRxResponse) response; - - log.info("Received RX packet, option is " + rx.getOption() + ", sender 64 address is " + ByteUtils.toBase16(rx.getRemoteAddress64().getAddress()) + ", remote 16-bit address is " + ByteUtils.toBase16(rx.getRemoteAddress16().getAddress()) + ", data is " + ByteUtils.toBase16(rx.getData())); - - // optionally we may want to get the signal strength (RSSI) of the last hop. - // keep in mind if you have routers in your network, this will be the signal of the last hop. - AtCommand at = new AtCommand("DB"); - xbee.sendAsynchronous(at); - XBeeResponse atResponse = xbee.getResponse(); - - if (atResponse.getApiId() == ApiId.AT_RESPONSE) { - // remember rssi is a negative db value - log.info("RSSI of last response is " + -((AtCommandResponse)atResponse).getValue()[0]); - } else { - // we didn't get an AT response - log.info("expected RSSI, but received " + atResponse.toString()); - } - } else { - log.debug("received unexpected packet " + response.toString()); - } - } catch (Exception e) { - log.error(e); - } - } - } finally { - if (xbee != null && xbee.isConnected()) { - xbee.close(); - } - } - } - - public static void main(String[] args) throws Exception { - // init log4j - PropertyConfigurator.configure("log4j.properties"); - new ZNetReceiverExample(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.examples.zigbee; + +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; + +import com.rapplogic.xbee.api.ApiId; +import com.rapplogic.xbee.api.XBee; +import com.rapplogic.xbee.api.XBeeResponse; +import com.rapplogic.xbee.api.requests.AtCommand; +import com.rapplogic.xbee.api.responses.AtCommandResponse; +import com.rapplogic.xbee.api.zigbee.ZNetRxResponse; +import com.rapplogic.xbee.util.ByteUtils; + +/** + * This class is the companion to ZNetSenderTest.java, and as such, it receives packets sent by ZNetSenderTest.java + * See the ZNetSenderTest.java for information on how to configure your XBee for this demo + * + * You can start ZNetSenderTest.java and this class in any order but it's generally best to start this class first. + * + * @author andrew + * + */ +public class ZNetReceiverExample { + + private final static Logger log = Logger.getLogger(ZNetReceiverExample.class); + + private ZNetReceiverExample() throws Exception { + XBee xbee = new XBee(); + + try { + // replace with the com port of your receiving XBee (typically your end device) + // router + xbee.open("/dev/tty.usbserial-A6005uPi", 9600); + + while (true) { + + try { + // we wait here until a packet is received. + XBeeResponse response = xbee.getResponse(); + + log.info("received response " + response.toString()); + + if (response.getApiId() == ApiId.ZNET_RX_RESPONSE) { + // we received a packet from ZNetSenderTest.java + ZNetRxResponse rx = (ZNetRxResponse) response; + + log.info("Received RX packet, option is " + rx.getOption() + ", sender 64 address is " + ByteUtils.toBase16(rx.getRemoteAddress64().getAddress()) + ", remote 16-bit address is " + ByteUtils.toBase16(rx.getRemoteAddress16().getAddress()) + ", data is " + ByteUtils.toBase16(rx.getData())); + + // optionally we may want to get the signal strength (RSSI) of the last hop. + // keep in mind if you have routers in your network, this will be the signal of the last hop. + AtCommand at = new AtCommand("DB"); + xbee.sendAsynchronous(at); + XBeeResponse atResponse = xbee.getResponse(); + + if (atResponse.getApiId() == ApiId.AT_RESPONSE) { + // remember rssi is a negative db value + log.info("RSSI of last response is " + -((AtCommandResponse)atResponse).getValue()[0]); + } else { + // we didn't get an AT response + log.info("expected RSSI, but received " + atResponse.toString()); + } + } else { + log.debug("received unexpected packet " + response.toString()); + } + } catch (Exception e) { + log.error(e); + } + } + } finally { + if (xbee != null && xbee.isConnected()) { + xbee.close(); + } + } + } + + public static void main(String[] args) throws Exception { + // init log4j + PropertyConfigurator.configure("log4j.properties"); + new ZNetReceiverExample(); + } +} diff --git a/src/com/rapplogic/xbee/examples/zigbee/ZNetSenderExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetSenderExample.java similarity index 97% rename from src/com/rapplogic/xbee/examples/zigbee/ZNetSenderExample.java rename to src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetSenderExample.java index a4d100b..5410520 100644 --- a/src/com/rapplogic/xbee/examples/zigbee/ZNetSenderExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetSenderExample.java @@ -1,182 +1,182 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.examples.zigbee; - -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; - -import com.rapplogic.xbee.api.XBee; -import com.rapplogic.xbee.api.XBeeAddress16; -import com.rapplogic.xbee.api.XBeeAddress64; -import com.rapplogic.xbee.api.XBeeException; -import com.rapplogic.xbee.api.XBeeTimeoutException; -import com.rapplogic.xbee.api.zigbee.ZNetTxRequest; -import com.rapplogic.xbee.api.zigbee.ZNetTxStatusResponse; -import com.rapplogic.xbee.util.ByteUtils; - -/** - * To run this example you need to have at least two ZNet XBees powered up and configured to the same PAN ID (ATID) in API mode (2). - * This software requires the XBee to be configured in API mode; if your ZNet radios are flashed with the transparent (AT) firmware, - * you will need to re-flash with API firmware to run this software. - * - * I use the Digi X-CTU software to configure my XBee's, but if you don't have Windows (X-CTU only works on Windows), you can still use the configureCoordinator and - * configureEndDevice methods in ZNetApiAtTest.java. - * - * There are a few chicken and egg situations where you need to know some basic configuration before you can connect to the XBee. This - * includes the baud rate and the API mode. The default baud rate is 9600 and if you ever change it, you will want to remember the setting. - * If you can't connect at 9600 and you don't know the baud rate, try all possibilities until it works. Same with the API Mode: if you click - * Test/Query in X-CTU, try changing the API mode until it succeeds, then write it down somewhere for next time. - * - * Here's my setup configuration (assumes factory configuration): - * - * COORDINATOR config: - * - * - Reset to factory settings: - * ATRE - * - Put in API mode 2 - * ATAP 2 - * - Set PAN id to arbitrary value - * ATID 1AAA - * - Set the Node Identifier (give it a meaningful name) - * ATNI COORDINATOR - * - Save config - * ATWR - * - reboot - * ATFR - * - * The XBee network will assign the network 16-bit MY address. The coordinator MY address is always 0 - * - * X-CTU tells me my SH Address is 00 13 a2 00 and SL is 40 0a 3e 02 - * - * END DEVICE config: - * - * - Reset to factory settings: - * ATRE - * - Put in API mode 2 - * ATAP 2 - * - Set PAN id to arbitrary value - * ATID 1AAA - * - Set the Node Identifier (give it a meaningful name) - * ATNI END_DEVICE_1 - * - Save config - * ATWR - * - reboot - * ATFR - * - * Only one XBee needs to be connected to the computer (serial-usb); the other may be remote, but can also be connected to the computer. - * I use the XBee Explorer from SparkFun to connect my XBees to my computer as it makes it incredibly easy -- just drop in the XBee. - * - * For this example, I use my XBee COORDINATOR as my "sender" (runs this class) and the END DEVICE as my "receiver" XBee. - * You could alternatively use your END DEVICE as the sender -- it doesn't matter because any XBee, either configured as a COORDINATOR - * or END DEVICE, can both send and receive. - * - * How to find the COM port: - * - * Java is nice in that it runs on many platforms. I use mac/windows and linux (server) and the com port is different on all three. - * On the mac it appears as /dev/tty.usbserial-A6005v5M on my machine. I just plug in each XBee one at a time and check the /dev dir - * to match the XBee to the device name: ls -l /dev/tty.u (hit tab twice to see all entries) - * - * On Windows you can simply select Start->My Computer->Manage, select Device Manager and expand "Ports" - * - * For Linux I'm not exactly sure just yet although I found mine by trial and error to be /dev/ttyUSB0 I think it could easily be different - * for other distros. - * - * To run, simply right-click on the class, in the left pane, and select Run As->Java Application. Eclipse will let you run multiple - * processes in one IDE, but there is only one console and it will switch between the two processes as it is updated. - * - * If you are running the sender and receiver in the same eclipse, remember to hit the terminate button twice to kill both - * or you won't be able to start it again. If this situation occurs, simply restart eclipse. - * - * @author andrew - */ -public class ZNetSenderExample { - - private final static Logger log = Logger.getLogger(ZNetSenderExample.class); - - private ZNetSenderExample() throws XBeeException { - - XBee xbee = new XBee(); - - // replace with port and baud rate of your XBee. this is the com port of my coordinator - //coord - xbee.open("/dev/tty.usbserial-A6005uRz", 9600); - // coord (21A7) - //XBeeAddress64 addr64 = new XBeeAddress64(0, 0x13, 0xa2, 0, 0x40, 0x8b, 0x98, 0xfe); - - // replace with end device's 64-bit address (SH + SL) - // router (firmware 23A7) - XBeeAddress64 addr64 = new XBeeAddress64(0, 0x13, 0xa2, 0, 0x40, 0x8b, 0x98, 0xff); - - // create an array of arbitrary data to send - int[] payload = new int[] { 'X', 'B', 'e', 'e' }; - - // first request we just send 64-bit address. we get 16-bit network address with status response - ZNetTxRequest request = new ZNetTxRequest(addr64, payload); - - log.debug("zb request is " + request.getXBeePacket().getPacket()); - - log.info("sending tx " + request); - - while (true) { - log.info("request packet bytes (base 16) " + ByteUtils.toBase16(request.getXBeePacket().getPacket())); - - long start = System.currentTimeMillis(); - //log.info("sending tx packet: " + request.toString()); - - try { - ZNetTxStatusResponse response = (ZNetTxStatusResponse) xbee.sendSynchronous(request, 10000); - // update frame id for next request - request.setFrameId(xbee.getNextFrameId()); - - log.info("received response " + response); - - //log.debug("status response bytes:" + ByteUtils.toBase16(response.getPacketBytes())); - - if (response.getDeliveryStatus() == ZNetTxStatusResponse.DeliveryStatus.SUCCESS) { - // the packet was successfully delivered - if (response.getRemoteAddress16().equals(XBeeAddress16.ZNET_BROADCAST)) { - // specify 16-bit address for faster routing?.. really only need to do this when it changes - request.setDestAddr16(response.getRemoteAddress16()); - } - } else { - // packet failed. log error - // it's easy to create this error by unplugging/powering off your remote xbee. when doing so I get: packet failed due to error: ADDRESS_NOT_FOUND - log.error("packet failed due to error: " + response.getDeliveryStatus()); - } - - // I get the following message: Response in 75, Delivery status is SUCCESS, 16-bit address is 0x08 0xe5, retry count is 0, discovery status is SUCCESS - log.info("Response in " + (System.currentTimeMillis() - start) + ", Delivery status is " + response.getDeliveryStatus() + ", 16-bit address is " + ByteUtils.toBase16(response.getRemoteAddress16().getAddress()) + ", retry count is " + response.getRetryCount() + ", discovery status is " + response.getDeliveryStatus()); - } catch (XBeeTimeoutException e) { - log.warn("request timed out"); - } - - try { - // wait a bit then send another packet - Thread.sleep(10000); - } catch (InterruptedException e) { - } - } - } - - public static void main(String[] args) throws XBeeException, InterruptedException { - PropertyConfigurator.configure("log4j.properties"); - new ZNetSenderExample(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.examples.zigbee; + +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; + +import com.rapplogic.xbee.api.XBee; +import com.rapplogic.xbee.api.XBeeAddress16; +import com.rapplogic.xbee.api.XBeeAddress64; +import com.rapplogic.xbee.api.XBeeException; +import com.rapplogic.xbee.api.XBeeTimeoutException; +import com.rapplogic.xbee.api.zigbee.ZNetTxRequest; +import com.rapplogic.xbee.api.zigbee.ZNetTxStatusResponse; +import com.rapplogic.xbee.util.ByteUtils; + +/** + * To run this example you need to have at least two ZNet XBees powered up and configured to the same PAN ID (ATID) in API mode (2). + * This software requires the XBee to be configured in API mode; if your ZNet radios are flashed with the transparent (AT) firmware, + * you will need to re-flash with API firmware to run this software. + * + * I use the Digi X-CTU software to configure my XBee's, but if you don't have Windows (X-CTU only works on Windows), you can still use the configureCoordinator and + * configureEndDevice methods in ZNetApiAtTest.java. + * + * There are a few chicken and egg situations where you need to know some basic configuration before you can connect to the XBee. This + * includes the baud rate and the API mode. The default baud rate is 9600 and if you ever change it, you will want to remember the setting. + * If you can't connect at 9600 and you don't know the baud rate, try all possibilities until it works. Same with the API Mode: if you click + * Test/Query in X-CTU, try changing the API mode until it succeeds, then write it down somewhere for next time. + * + * Here's my setup configuration (assumes factory configuration): + * + * COORDINATOR config: + * + * - Reset to factory settings: + * ATRE + * - Put in API mode 2 + * ATAP 2 + * - Set PAN id to arbitrary value + * ATID 1AAA + * - Set the Node Identifier (give it a meaningful name) + * ATNI COORDINATOR + * - Save config + * ATWR + * - reboot + * ATFR + * + * The XBee network will assign the network 16-bit MY address. The coordinator MY address is always 0 + * + * X-CTU tells me my SH Address is 00 13 a2 00 and SL is 40 0a 3e 02 + * + * END DEVICE config: + * + * - Reset to factory settings: + * ATRE + * - Put in API mode 2 + * ATAP 2 + * - Set PAN id to arbitrary value + * ATID 1AAA + * - Set the Node Identifier (give it a meaningful name) + * ATNI END_DEVICE_1 + * - Save config + * ATWR + * - reboot + * ATFR + * + * Only one XBee needs to be connected to the computer (serial-usb); the other may be remote, but can also be connected to the computer. + * I use the XBee Explorer from SparkFun to connect my XBees to my computer as it makes it incredibly easy -- just drop in the XBee. + * + * For this example, I use my XBee COORDINATOR as my "sender" (runs this class) and the END DEVICE as my "receiver" XBee. + * You could alternatively use your END DEVICE as the sender -- it doesn't matter because any XBee, either configured as a COORDINATOR + * or END DEVICE, can both send and receive. + * + * How to find the COM port: + * + * Java is nice in that it runs on many platforms. I use mac/windows and linux (server) and the com port is different on all three. + * On the mac it appears as /dev/tty.usbserial-A6005v5M on my machine. I just plug in each XBee one at a time and check the /dev dir + * to match the XBee to the device name: ls -l /dev/tty.u (hit tab twice to see all entries) + * + * On Windows you can simply select Start->My Computer->Manage, select Device Manager and expand "Ports" + * + * For Linux I'm not exactly sure just yet although I found mine by trial and error to be /dev/ttyUSB0 I think it could easily be different + * for other distros. + * + * To run, simply right-click on the class, in the left pane, and select Run As->Java Application. Eclipse will let you run multiple + * processes in one IDE, but there is only one console and it will switch between the two processes as it is updated. + * + * If you are running the sender and receiver in the same eclipse, remember to hit the terminate button twice to kill both + * or you won't be able to start it again. If this situation occurs, simply restart eclipse. + * + * @author andrew + */ +public class ZNetSenderExample { + + private final static Logger log = Logger.getLogger(ZNetSenderExample.class); + + private ZNetSenderExample() throws XBeeException { + + XBee xbee = new XBee(); + + // replace with port and baud rate of your XBee. this is the com port of my coordinator + //coord + xbee.open("/dev/tty.usbserial-A6005uRz", 9600); + // coord (21A7) + //XBeeAddress64 addr64 = new XBeeAddress64(0, 0x13, 0xa2, 0, 0x40, 0x8b, 0x98, 0xfe); + + // replace with end device's 64-bit address (SH + SL) + // router (firmware 23A7) + XBeeAddress64 addr64 = new XBeeAddress64(0, 0x13, 0xa2, 0, 0x40, 0x8b, 0x98, 0xff); + + // create an array of arbitrary data to send + int[] payload = new int[] { 'X', 'B', 'e', 'e' }; + + // first request we just send 64-bit address. we get 16-bit network address with status response + ZNetTxRequest request = new ZNetTxRequest(addr64, payload); + + log.debug("zb request is " + request.getXBeePacket().getPacket()); + + log.info("sending tx " + request); + + while (true) { + log.info("request packet bytes (base 16) " + ByteUtils.toBase16(request.getXBeePacket().getPacket())); + + long start = System.currentTimeMillis(); + //log.info("sending tx packet: " + request.toString()); + + try { + ZNetTxStatusResponse response = (ZNetTxStatusResponse) xbee.sendSynchronous(request, 10000); + // update frame id for next request + request.setFrameId(xbee.getNextFrameId()); + + log.info("received response " + response); + + //log.debug("status response bytes:" + ByteUtils.toBase16(response.getPacketBytes())); + + if (response.getDeliveryStatus() == ZNetTxStatusResponse.DeliveryStatus.SUCCESS) { + // the packet was successfully delivered + if (response.getRemoteAddress16().equals(XBeeAddress16.ZNET_BROADCAST)) { + // specify 16-bit address for faster routing?.. really only need to do this when it changes + request.setDestAddr16(response.getRemoteAddress16()); + } + } else { + // packet failed. log error + // it's easy to create this error by unplugging/powering off your remote xbee. when doing so I get: packet failed due to error: ADDRESS_NOT_FOUND + log.error("packet failed due to error: " + response.getDeliveryStatus()); + } + + // I get the following message: Response in 75, Delivery status is SUCCESS, 16-bit address is 0x08 0xe5, retry count is 0, discovery status is SUCCESS + log.info("Response in " + (System.currentTimeMillis() - start) + ", Delivery status is " + response.getDeliveryStatus() + ", 16-bit address is " + ByteUtils.toBase16(response.getRemoteAddress16().getAddress()) + ", retry count is " + response.getRetryCount() + ", discovery status is " + response.getDeliveryStatus()); + } catch (XBeeTimeoutException e) { + log.warn("request timed out"); + } + + try { + // wait a bit then send another packet + Thread.sleep(10000); + } catch (InterruptedException e) { + } + } + } + + public static void main(String[] args) throws XBeeException, InterruptedException { + PropertyConfigurator.configure("log4j.properties"); + new ZNetSenderExample(); + } +} diff --git a/src/com/rapplogic/xbee/test/OpenCloseConnectionsTest.java b/src/main/java/com/rapplogic/xbee/test/OpenCloseConnectionsTest.java similarity index 97% rename from src/com/rapplogic/xbee/test/OpenCloseConnectionsTest.java rename to src/main/java/com/rapplogic/xbee/test/OpenCloseConnectionsTest.java index e0776d9..31fcc21 100644 --- a/src/com/rapplogic/xbee/test/OpenCloseConnectionsTest.java +++ b/src/main/java/com/rapplogic/xbee/test/OpenCloseConnectionsTest.java @@ -3,9 +3,9 @@ import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; -import com.rapplogic.xbee.api.AtCommand; import com.rapplogic.xbee.api.XBee; import com.rapplogic.xbee.api.XBeeException; +import com.rapplogic.xbee.api.requests.AtCommand; /** * Tests opening and closing connections to the radio diff --git a/src/com/rapplogic/xbee/util/ByteUtils.java b/src/main/java/com/rapplogic/xbee/util/ByteUtils.java similarity index 95% rename from src/com/rapplogic/xbee/util/ByteUtils.java rename to src/main/java/com/rapplogic/xbee/util/ByteUtils.java index 666e6ea..fd5090d 100644 --- a/src/com/rapplogic/xbee/util/ByteUtils.java +++ b/src/main/java/com/rapplogic/xbee/util/ByteUtils.java @@ -1,305 +1,305 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.util; - -import java.io.IOException; - -public class ByteUtils { - - // not to be instantiated - private ByteUtils() { - - } - - /** - * There is a slight problem with this method that you might have noticed; a Java int is signed, so we can't make - * use of the 32nd bit. This means this method does not support a four byte value with msb greater than 01111111 ((2^7-1) or 127) - * and will throw a runtime exception if it encounters this situation. - * - * TODO use long instead of int to support 4 bytes values. note that long assignments are not atomic. - * - * Not Used - */ - public static int convertMultiByteToInt(int[] bytes) { - - if (bytes.length > 4) { - throw new RuntimeException("too big"); - } else if (bytes.length == 4 && ((bytes[0] & 0x80) == 0x80)) { - // 0x80 == 10000000, 0x7e == 01111111 - throw new IllegalArgumentException("Java int can't support a four byte value with msb byte greater than 7e"); - } - - int val = 0; - - for (int i = 0; i < bytes.length; i++) { - - if (bytes[i] > 0xFF) { - throw new IllegalArgumentException("Values exceeds byte range: " + bytes[i]); - } - - if (i == (bytes.length - 1)) { - val+= bytes[i]; - } else { - val+= bytes[i] << ((bytes.length - i - 1) * 8); - } - } - - return val; - } - - /** - * Works for positive values only - * - * @param val - * @return - */ - public static int[] convertInttoMultiByte(int val) { - - if (val < 0) { - throw new IllegalArgumentException("Negative values are not supported"); - } - - // must decompose into a max of 4 bytes - // b1 b2 b3 b4 - // 01111111 11111111 11111111 11111111 - // 127 255 255 255 - - int size = 0; - - if ((val >> 24) > 0) { - size = 4; - } else if ((val >> 16) > 0) { - size = 3; - } else if ((val >> 8) > 0) { - size = 2; - } else { - size = 1; - } - - int[] data = new int[size]; - - for (int i = 0; i < size; i++) { - data[i] = (val >> (size - i - 1) * 8) & 0xFF; - } - - return data; - } - - public static String toBase16(int[] arr) { - return toBase16(arr, ","); - } - - public static String toBase16(int[] arr, String delimiter) { - - if (arr == null) { - return ""; - } - - StringBuffer sb = new StringBuffer(); - - for (int i = 0; i < arr.length; i++) { - sb.append(toBase16(arr[i])); - - if (i < arr.length - 1) { - sb.append(delimiter); - } - } - - return sb.toString(); - } - - public static String toBase2(int[] arr) { - - if (arr == null) { - return ""; - } - - StringBuffer sb = new StringBuffer(); - - for (int i = 0; i < arr.length; i++) { - sb.append(toBase2(arr[i])); - - if (i < arr.length - 1) { - sb.append(" "); - } - } - - return sb.toString(); - } - - public static String toBase10(int[] arr) { - - if (arr == null) { - return ""; - } - - StringBuffer sb = new StringBuffer(); - - for (int i = 0; i < arr.length; i++) { - sb.append((arr[i])); - - if (i < arr.length - 1) { - sb.append(" "); - } - } - - return sb.toString(); - } - - /** - * @deprecated use toString(int[] arr) - */ - public static String toChar(int[] arr) { - return toString(arr); - } - - /** - * Converts an int array to string. - * Note: this method does not validate that int values map - * to valid characters - * - * @param arr - * @return - */ - public static String toString(int[] arr) { - - if (arr == null) { - return ""; - } - - StringBuffer sb = new StringBuffer(); - - for (int anArr : arr) { - sb.append((char) anArr); - } - - return sb.toString(); - } - - private static String padBase2(String s) { - - for (int i = s.length(); i < 8; i++) { - s = "0" + s; - } - - return s; - } - -// /** -// * Determines the bit value of a single byte -// * -// * @param b -// * @return -// */ -// public static boolean[] parseBits(int b) { -// -// boolean[] results = new boolean[8]; -// -// for (int i = 0; i < 8; i++) { -// if (((b >> i) & 0x1) == 0x1) { -// results[i] = true; -// } else { -// results[i] = false; -// } -// } -// -// return results; -// } - - /** - * Returns true if the bit is on (1) at the specified position - * Position range: 1-8 - */ - public static boolean getBit(int b, int position) { - - if (position < 1 || position > 8) { - throw new IllegalArgumentException("Position is out of range"); - } - - if (b > 0xff) { - throw new IllegalArgumentException("input value [" + b + "] is larger than a byte"); - } - - return ((b >> (--position)) & 0x1) == 0x1; - - } - - public static String toBase16(int b) { - - if (b > 0xff) { - throw new IllegalArgumentException("input value [" + b + "] is larger than a byte"); - } - - if (b < 0x10) { - return "0x0" + Integer.toHexString(b); - } else { - return "0x" + Integer.toHexString(b); - } - } - - public static String toBase2(int b) { - - if (b > 0xff) { - throw new IllegalArgumentException("input value [" + b + "] is larger than a byte"); - } - - return padBase2(Integer.toBinaryString(b)); - } - - public static String formatByte(int b) { - return "base10=" + Integer.toString(b) + ",base16=" + toBase16(b) + ",base2=" + toBase2(b); - } - - public static int[] stringToIntArray(String s) { - int[] intArr = new int[s.length()]; - - for (int i = 0; i < s.length(); i++) { - intArr[i] = (char)s.charAt(i); - } - - return intArr; - } - - /** - * Parses a 10-bit analog value from the input stream - * - * @return - * @throws IOException - */ - public static int parse10BitAnalog(int msb, int lsb) throws IOException { - msb = msb & 0xff; - - // shift up bits 9 and 10 of the msb - msb = (msb & 0x3) << 8; - -// log.debug("shifted msb is " + msb); - - lsb = lsb & 0xff; - - return msb + lsb; - } - - public static int parse10BitAnalog(IIntInputStream in, int pos) throws IOException { - int adcMsb = in.read("Analog " + pos + " MSB"); - int adcLsb = in.read("Analog " + pos + " LSB"); - - return ByteUtils.parse10BitAnalog(adcMsb, adcLsb); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.util; + +import java.io.IOException; + +public class ByteUtils { + + // not to be instantiated + private ByteUtils() { + + } + + /** + * There is a slight problem with this method that you might have noticed; a Java int is signed, so we can't make + * use of the 32nd bit. This means this method does not support a four byte value with msb greater than 01111111 ((2^7-1) or 127) + * and will throw a runtime exception if it encounters this situation. + * + * TODO use long instead of int to support 4 bytes values. note that long assignments are not atomic. + * + * Not Used + */ + public static int convertMultiByteToInt(int[] bytes) { + + if (bytes.length > 4) { + throw new RuntimeException("too big"); + } else if (bytes.length == 4 && ((bytes[0] & 0x80) == 0x80)) { + // 0x80 == 10000000, 0x7e == 01111111 + throw new IllegalArgumentException("Java int can't support a four byte value with msb byte greater than 7e"); + } + + int val = 0; + + for (int i = 0; i < bytes.length; i++) { + + if (bytes[i] > 0xFF) { + throw new IllegalArgumentException("Values exceeds byte range: " + bytes[i]); + } + + if (i == (bytes.length - 1)) { + val+= bytes[i]; + } else { + val+= bytes[i] << ((bytes.length - i - 1) * 8); + } + } + + return val; + } + + /** + * Works for positive values only + * + * @param val + * @return + */ + public static int[] convertInttoMultiByte(int val) { + + if (val < 0) { + throw new IllegalArgumentException("Negative values are not supported"); + } + + // must decompose into a max of 4 bytes + // b1 b2 b3 b4 + // 01111111 11111111 11111111 11111111 + // 127 255 255 255 + + int size = 0; + + if ((val >> 24) > 0) { + size = 4; + } else if ((val >> 16) > 0) { + size = 3; + } else if ((val >> 8) > 0) { + size = 2; + } else { + size = 1; + } + + int[] data = new int[size]; + + for (int i = 0; i < size; i++) { + data[i] = (val >> (size - i - 1) * 8) & 0xFF; + } + + return data; + } + + public static String toBase16(int[] arr) { + return toBase16(arr, ","); + } + + public static String toBase16(int[] arr, String delimiter) { + + if (arr == null) { + return ""; + } + + StringBuffer sb = new StringBuffer(); + + for (int i = 0; i < arr.length; i++) { + sb.append(toBase16(arr[i])); + + if (i < arr.length - 1) { + sb.append(delimiter); + } + } + + return sb.toString(); + } + + public static String toBase2(int[] arr) { + + if (arr == null) { + return ""; + } + + StringBuffer sb = new StringBuffer(); + + for (int i = 0; i < arr.length; i++) { + sb.append(toBase2(arr[i])); + + if (i < arr.length - 1) { + sb.append(" "); + } + } + + return sb.toString(); + } + + public static String toBase10(int[] arr) { + + if (arr == null) { + return ""; + } + + StringBuffer sb = new StringBuffer(); + + for (int i = 0; i < arr.length; i++) { + sb.append((arr[i])); + + if (i < arr.length - 1) { + sb.append(" "); + } + } + + return sb.toString(); + } + + /** + * @deprecated use toString(int[] arr) + */ + public static String toChar(int[] arr) { + return toString(arr); + } + + /** + * Converts an int array to string. + * Note: this method does not validate that int values map + * to valid characters + * + * @param arr + * @return + */ + public static String toString(int[] arr) { + + if (arr == null) { + return ""; + } + + StringBuffer sb = new StringBuffer(); + + for (int anArr : arr) { + sb.append((char) anArr); + } + + return sb.toString(); + } + + private static String padBase2(String s) { + + for (int i = s.length(); i < 8; i++) { + s = "0" + s; + } + + return s; + } + +// /** +// * Determines the bit value of a single byte +// * +// * @param b +// * @return +// */ +// public static boolean[] parseBits(int b) { +// +// boolean[] results = new boolean[8]; +// +// for (int i = 0; i < 8; i++) { +// if (((b >> i) & 0x1) == 0x1) { +// results[i] = true; +// } else { +// results[i] = false; +// } +// } +// +// return results; +// } + + /** + * Returns true if the bit is on (1) at the specified position + * Position range: 1-8 + */ + public static boolean getBit(int b, int position) { + + if (position < 1 || position > 8) { + throw new IllegalArgumentException("Position is out of range"); + } + + if (b > 0xff) { + throw new IllegalArgumentException("input value [" + b + "] is larger than a byte"); + } + + return ((b >> (--position)) & 0x1) == 0x1; + + } + + public static String toBase16(int b) { + + if (b > 0xff) { + throw new IllegalArgumentException("input value [" + b + "] is larger than a byte"); + } + + if (b < 0x10) { + return "0x0" + Integer.toHexString(b); + } else { + return "0x" + Integer.toHexString(b); + } + } + + public static String toBase2(int b) { + + if (b > 0xff) { + throw new IllegalArgumentException("input value [" + b + "] is larger than a byte"); + } + + return padBase2(Integer.toBinaryString(b)); + } + + public static String formatByte(int b) { + return "base10=" + Integer.toString(b) + ",base16=" + toBase16(b) + ",base2=" + toBase2(b); + } + + public static int[] stringToIntArray(String s) { + int[] intArr = new int[s.length()]; + + for (int i = 0; i < s.length(); i++) { + intArr[i] = (char)s.charAt(i); + } + + return intArr; + } + + /** + * Parses a 10-bit analog value from the input stream + * + * @return + * @throws IOException + */ + public static int parse10BitAnalog(int msb, int lsb) throws IOException { + msb = msb & 0xff; + + // shift up bits 9 and 10 of the msb + msb = (msb & 0x3) << 8; + +// log.debug("shifted msb is " + msb); + + lsb = lsb & 0xff; + + return msb + lsb; + } + + public static int parse10BitAnalog(IIntInputStream in, int pos) throws IOException { + int adcMsb = in.read("Analog " + pos + " MSB"); + int adcLsb = in.read("Analog " + pos + " LSB"); + + return ByteUtils.parse10BitAnalog(adcMsb, adcLsb); + } +} diff --git a/src/com/rapplogic/xbee/util/DoubleByte.java b/src/main/java/com/rapplogic/xbee/util/DoubleByte.java similarity index 95% rename from src/com/rapplogic/xbee/util/DoubleByte.java rename to src/main/java/com/rapplogic/xbee/util/DoubleByte.java index 0bfeb8d..4d1879d 100644 --- a/src/com/rapplogic/xbee/util/DoubleByte.java +++ b/src/main/java/com/rapplogic/xbee/util/DoubleByte.java @@ -1,105 +1,105 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.util; - -public class DoubleByte { - - private int msb; - private int lsb; - - public DoubleByte() { - - } - - /** - * Decomposes a 16bit int into high and low bytes - * - * @param val - */ - public DoubleByte(int val) { - if (val > 0xFFFF || val < 0) { - throw new IllegalArgumentException("value is out of range"); - } - - // split address into high and low bytes - msb = val >> 8; - lsb = val & 0xFF; - } - - /** - * Constructs a 16bit value from two bytes (high and low) - * - * @param msb - * @param lsb - */ - public DoubleByte(int msb, int lsb) { - - if (msb > 0xFF || lsb > 0xFF) { - throw new IllegalArgumentException("msb or lsb are out of range"); - } - - this.msb = msb; - this.lsb = lsb; - } - - public int getMsb() { - return msb; - } - - public int getLsb() { - return lsb; - } - - public int get16BitValue() { - return (this.msb << 8) + this.lsb; - } - - public void setMsb(int msb) { - this.msb = msb; - } - - public void setLsb(int lsb) { - this.lsb = lsb; - } - - public int[] getArray() { - return new int[] { this.msb, this.lsb }; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - DoubleByte that = (DoubleByte) o; - - if (lsb != that.lsb) return false; - if (msb != that.msb) return false; - - return true; - } - - @Override - public int hashCode() { - int result = msb; - result = 31 * result + lsb; - return result; - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.util; + +public class DoubleByte { + + private int msb; + private int lsb; + + public DoubleByte() { + + } + + /** + * Decomposes a 16bit int into high and low bytes + * + * @param val + */ + public DoubleByte(int val) { + if (val > 0xFFFF || val < 0) { + throw new IllegalArgumentException("value is out of range"); + } + + // split address into high and low bytes + msb = val >> 8; + lsb = val & 0xFF; + } + + /** + * Constructs a 16bit value from two bytes (high and low) + * + * @param msb + * @param lsb + */ + public DoubleByte(int msb, int lsb) { + + if (msb > 0xFF || lsb > 0xFF) { + throw new IllegalArgumentException("msb or lsb are out of range"); + } + + this.msb = msb; + this.lsb = lsb; + } + + public int getMsb() { + return msb; + } + + public int getLsb() { + return lsb; + } + + public int get16BitValue() { + return (this.msb << 8) + this.lsb; + } + + public void setMsb(int msb) { + this.msb = msb; + } + + public void setLsb(int lsb) { + this.lsb = lsb; + } + + public int[] getArray() { + return new int[] { this.msb, this.lsb }; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DoubleByte that = (DoubleByte) o; + + if (lsb != that.lsb) return false; + if (msb != that.msb) return false; + + return true; + } + + @Override + public int hashCode() { + int result = msb; + result = 31 * result + lsb; + return result; + } +} diff --git a/src/com/rapplogic/xbee/util/ExceptionHandler.java b/src/main/java/com/rapplogic/xbee/util/ExceptionHandler.java similarity index 100% rename from src/com/rapplogic/xbee/util/ExceptionHandler.java rename to src/main/java/com/rapplogic/xbee/util/ExceptionHandler.java diff --git a/src/com/rapplogic/xbee/util/IIntArray.java b/src/main/java/com/rapplogic/xbee/util/IIntArray.java similarity index 97% rename from src/com/rapplogic/xbee/util/IIntArray.java rename to src/main/java/com/rapplogic/xbee/util/IIntArray.java index 9eb0177..e539e3e 100644 --- a/src/com/rapplogic/xbee/util/IIntArray.java +++ b/src/main/java/com/rapplogic/xbee/util/IIntArray.java @@ -1,24 +1,24 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.util; - -public interface IIntArray { - public int[] getIntArray(); -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.util; + +public interface IIntArray { + public int[] getIntArray(); +} diff --git a/src/com/rapplogic/xbee/util/IIntInputStream.java b/src/main/java/com/rapplogic/xbee/util/IIntInputStream.java similarity index 97% rename from src/com/rapplogic/xbee/util/IIntInputStream.java rename to src/main/java/com/rapplogic/xbee/util/IIntInputStream.java index 145cee5..5d7bc5d 100644 --- a/src/com/rapplogic/xbee/util/IIntInputStream.java +++ b/src/main/java/com/rapplogic/xbee/util/IIntInputStream.java @@ -1,28 +1,28 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.util; - -import java.io.IOException; - -// TODO rename to IIntInputStream -public interface IIntInputStream { - public int read() throws IOException; - public int read(String s) throws IOException; -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.util; + +import java.io.IOException; + +// TODO rename to IIntInputStream +public interface IIntInputStream { + public int read() throws IOException; + public int read(String s) throws IOException; +} diff --git a/src/com/rapplogic/xbee/util/InputStreamWrapper.java b/src/main/java/com/rapplogic/xbee/util/InputStreamWrapper.java old mode 100755 new mode 100644 similarity index 100% rename from src/com/rapplogic/xbee/util/InputStreamWrapper.java rename to src/main/java/com/rapplogic/xbee/util/InputStreamWrapper.java diff --git a/src/com/rapplogic/xbee/util/IntArrayInputStream.java b/src/main/java/com/rapplogic/xbee/util/IntArrayInputStream.java similarity index 96% rename from src/com/rapplogic/xbee/util/IntArrayInputStream.java rename to src/main/java/com/rapplogic/xbee/util/IntArrayInputStream.java index 8108051..828482d 100644 --- a/src/com/rapplogic/xbee/util/IntArrayInputStream.java +++ b/src/main/java/com/rapplogic/xbee/util/IntArrayInputStream.java @@ -1,60 +1,60 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.util; - -/** - * //TODO replace with nio.IntBuffer - */ -public class IntArrayInputStream implements IIntInputStream { - - private int[] source; - private int pos; - - public IntArrayInputStream(int[] source) { - this.source = source; - } - - public int read() { - if (pos >= source.length) { - throw new IllegalStateException("end of input stream"); - } - - return source[pos++]; - } - - /** - * Reads size bytes from the input stream and returns the bytes in an array - * - * @param size - * @return - * Apr 13, 2009 - */ - public int[] read(int size) { - int[] block = new int[size]; - System.arraycopy(source, pos, block, 0, size); - // index pos - pos+=size; - return block; - } - - public int read(String s) { - return read(); - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.util; + +/** + * //TODO replace with nio.IntBuffer + */ +public class IntArrayInputStream implements IIntInputStream { + + private int[] source; + private int pos; + + public IntArrayInputStream(int[] source) { + this.source = source; + } + + public int read() { + if (pos >= source.length) { + throw new IllegalStateException("end of input stream"); + } + + return source[pos++]; + } + + /** + * Reads size bytes from the input stream and returns the bytes in an array + * + * @param size + * @return + * Apr 13, 2009 + */ + public int[] read(int size) { + int[] block = new int[size]; + System.arraycopy(source, pos, block, 0, size); + // index pos + pos+=size; + return block; + } + + public int read(String s) { + return read(); + } +} diff --git a/src/com/rapplogic/xbee/util/IntArrayOutputStream.java b/src/main/java/com/rapplogic/xbee/util/IntArrayOutputStream.java similarity index 96% rename from src/com/rapplogic/xbee/util/IntArrayOutputStream.java rename to src/main/java/com/rapplogic/xbee/util/IntArrayOutputStream.java index a856bbe..9f40279 100644 --- a/src/com/rapplogic/xbee/util/IntArrayOutputStream.java +++ b/src/main/java/com/rapplogic/xbee/util/IntArrayOutputStream.java @@ -1,63 +1,63 @@ -/** - * Copyright (c) 2008 Andrew Rapp. All rights reserved. - * - * This file is part of XBee-API. - * - * XBee-API is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * XBee-API is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBee-API. If not, see . - */ - -package com.rapplogic.xbee.util; - -import java.util.ArrayList; -import java.util.List; - -/** - * TODO replace with nio.IntBuffer - */ -public class IntArrayOutputStream implements IIntArray { - - private List intList = new ArrayList(); - - public IntArrayOutputStream() { - - } - - public void write (int val) { - intList.add(val); - } - - public void write(int[] val) { - for (int aVal : val) { - this.write(aVal); - } - } - - public int[] getIntArray() { - //int[] integer = (int[]) intList.toArray(new int[0]); - // TODO there has got to be a better way -- how to convert list to int[] array? - int[] intArr = new int[intList.size()]; - - int i = 0; - - for (Integer integer : intList) { - intArr[i++] = integer; - } - - return intArr; - } - - public List getInternalList() { - return intList; - } -} +/** + * Copyright (c) 2008 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-API. + * + * XBee-API is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * XBee-API is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBee-API. If not, see . + */ + +package com.rapplogic.xbee.util; + +import java.util.ArrayList; +import java.util.List; + +/** + * TODO replace with nio.IntBuffer + */ +public class IntArrayOutputStream implements IIntArray { + + private List intList = new ArrayList(); + + public IntArrayOutputStream() { + + } + + public void write (int val) { + intList.add(val); + } + + public void write(int[] val) { + for (int aVal : val) { + this.write(aVal); + } + } + + public int[] getIntArray() { + //int[] integer = (int[]) intList.toArray(new int[0]); + // TODO there has got to be a better way -- how to convert list to int[] array? + int[] intArr = new int[intList.size()]; + + int i = 0; + + for (Integer integer : intList) { + intArr[i++] = integer; + } + + return intArr; + } + + public List getInternalList() { + return intList; + } +} diff --git a/log4j.properties b/src/main/resources/log4j.properties similarity index 100% rename from log4j.properties rename to src/main/resources/log4j.properties diff --git a/src/test/java/com/rapplogic/xbee/SimpleSeries2Test.java b/src/test/java/com/rapplogic/xbee/SimpleSeries2Test.java new file mode 100644 index 0000000..62c73f3 --- /dev/null +++ b/src/test/java/com/rapplogic/xbee/SimpleSeries2Test.java @@ -0,0 +1,29 @@ +package com.rapplogic.xbee; + +import static org.assertj.core.api.Assertions.assertThat; +import org.testng.annotations.Test; + +import com.rapplogic.xbee.api.XBee; +import com.rapplogic.xbee.api.XBeeConfiguration; +import com.rapplogic.xbee.api.XBeeException; + +public class SimpleSeries2Test { + + //TODO: Add series2 tests to get easy example of how it could be done and how to abstract the HW. + + @Test + public void createXBee() { + XBee xbee = getDefaultXBee(); + assertThat(xbee).isNotNull(); + } + + @Test(expectedExceptions=XBeeException.class) + public void createXBeeAndOpenConnectionWithInvalidConnectionProvider() throws XBeeException { + XBee xbee = getDefaultXBee(); + xbee.open("foobar", 12345); + } + + private XBee getDefaultXBee() { + return new XBee(new XBeeConfiguration().withMaxQueueSize(100).withStartupChecks(true).withConnectionProvider(new TestConnectionProvider())); + } +} diff --git a/src/test/java/com/rapplogic/xbee/TestConnectionProvider.java b/src/test/java/com/rapplogic/xbee/TestConnectionProvider.java new file mode 100644 index 0000000..f763096 --- /dev/null +++ b/src/test/java/com/rapplogic/xbee/TestConnectionProvider.java @@ -0,0 +1,15 @@ +package com.rapplogic.xbee; + +import com.rapplogic.xbee.connections.XBeeConnection; +import com.rapplogic.xbee.connections.XBeeConnectionException; +import com.rapplogic.xbee.connections.connectionprovider.IConnectionProvider; + +public class TestConnectionProvider implements IConnectionProvider { + + @Override + public XBeeConnection open(String port, int baudRate) throws XBeeConnectionException { + // TODO Auto-generated method stub + return null; + } + +} From 64b40071186491807a9925b9195c4f748cc4268d Mon Sep 17 00:00:00 2001 From: Ola Andersson Date: Wed, 10 Feb 2016 10:56:48 +0100 Subject: [PATCH 4/6] Removed eclipse specific files that should not be in a git repo. --- .classpath | 17 ---------------- .project | 23 ---------------------- .settings/org.eclipse.core.resources.prefs | 6 ------ .settings/org.eclipse.jdt.core.prefs | 13 ------------ .settings/org.eclipse.m2e.core.prefs | 4 ---- 5 files changed, 63 deletions(-) delete mode 100644 .classpath delete mode 100644 .project delete mode 100644 .settings/org.eclipse.core.resources.prefs delete mode 100644 .settings/org.eclipse.jdt.core.prefs delete mode 100644 .settings/org.eclipse.m2e.core.prefs diff --git a/.classpath b/.classpath deleted file mode 100644 index 3e73630..0000000 --- a/.classpath +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/.project b/.project deleted file mode 100644 index d747867..0000000 --- a/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - xbee-api - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.m2e.core.maven2Nature - org.eclipse.jdt.core.javanature - - diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index ed7df2b..0000000 --- a/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,6 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/main/java=UTF-8 -encoding//src/main/resources=UTF-8 -encoding//src/test/java=UTF-8 -encoding/=UTF-8 -encoding/src=UTF-8 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 13b3428..0000000 --- a/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,13 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.source=1.8 diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index f897a7f..0000000 --- a/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 From 05cea87b43ca4d24c759aace5b761448d3a5e94f Mon Sep 17 00:00:00 2001 From: Ola Andersson Date: Wed, 10 Feb 2016 10:59:09 +0100 Subject: [PATCH 5/6] Updated gitignore to avoid getting unneccessary stuff into git. --- .gitignore | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 934e0e0..5dc5600 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,76 @@ -/bin -/target + +# Created by https://www.gitignore.io/api/java,eclipse,maven + +### Java ### +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + + +### Eclipse ### + +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath + +# Eclipse Core +.project + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + + +### Maven ### +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +test-output/ From 0d3529c3227913c7000ebf69b3ba799094d97a21 Mon Sep 17 00:00:00 2001 From: Ola Andersson Date: Wed, 10 Feb 2016 14:29:34 +0100 Subject: [PATCH 6/6] Updated code to remove some warnings and added a skeleton for the testing. --- .gitignore | 2 + pom.xml | 1 + .../java/com/rapplogic/xbee/api/XBee.java | 1 - .../rapplogic/xbee/api/XBeeConfiguration.java | 8 ++- .../rapplogic/xbee/examples/ApiAtExample.java | 3 +- .../xbee/examples/RemoteAtExample.java | 2 - .../examples/wpan/ApiReceiverExample.java | 5 -- .../xbee/examples/wpan/ApiSenderExample.java | 5 +- .../xbee/examples/wpan/IoSamplesExample.java | 3 -- .../zigbee/BroadcastReceiverExample.java | 2 - .../zigbee/BroadcastSenderExample.java | 2 - .../examples/zigbee/SleepTestCoordinator.java | 2 +- .../examples/zigbee/ZBForceSampleExample.java | 2 - .../zigbee/ZBNodeDiscoverExample.java | 4 -- .../examples/zigbee/ZNetApiAtExample.java | 2 - .../zigbee/ZNetExplicitReceiverExample.java | 3 -- .../zigbee/ZNetExplicitSenderExample.java | 2 - .../examples/zigbee/ZNetIoSampleExample.java | 5 -- .../examples/zigbee/ZNetReceiverExample.java | 3 -- .../examples/zigbee/ZNetSenderExample.java | 1 - .../xbee/TestConnectionProvider.java | 4 +- .../com/rapplogic/xbee/mocks/Series2Mock.java | 51 +++++++++++++++++++ 22 files changed, 66 insertions(+), 47 deletions(-) create mode 100644 src/test/java/com/rapplogic/xbee/mocks/Series2Mock.java diff --git a/.gitignore b/.gitignore index 5dc5600..ab2ad75 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,8 @@ local.properties # STS (Spring Tool Suite) .springBeans +*.log + ### Maven ### target/ diff --git a/pom.xml b/pom.xml index 0b95f0b..f868e7c 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,7 @@ org.apache.maven.plugins maven-compiler-plugin + 3.5.1 1.8 1.8 diff --git a/src/main/java/com/rapplogic/xbee/api/XBee.java b/src/main/java/com/rapplogic/xbee/api/XBee.java index c7e6350..42b7b22 100644 --- a/src/main/java/com/rapplogic/xbee/api/XBee.java +++ b/src/main/java/com/rapplogic/xbee/api/XBee.java @@ -31,7 +31,6 @@ import com.rapplogic.xbee.api.requests.AtCommand; import com.rapplogic.xbee.api.responses.AtCommandResponse; import com.rapplogic.xbee.api.responses.XBeeFrameIdResponse; -import com.rapplogic.xbee.connections.SerialPortConnection; import com.rapplogic.xbee.connections.XBeeConnection; import com.rapplogic.xbee.connections.connectionprovider.IConnectionProvider; import com.rapplogic.xbee.connections.connectionprovider.SerialPortConnectionProvider; diff --git a/src/main/java/com/rapplogic/xbee/api/XBeeConfiguration.java b/src/main/java/com/rapplogic/xbee/api/XBeeConfiguration.java index 8e8b6a7..3bbfe57 100644 --- a/src/main/java/com/rapplogic/xbee/api/XBeeConfiguration.java +++ b/src/main/java/com/rapplogic/xbee/api/XBeeConfiguration.java @@ -2,6 +2,7 @@ import com.rapplogic.xbee.api.responses.NoRequestResponse; import com.rapplogic.xbee.connections.connectionprovider.IConnectionProvider; +import com.rapplogic.xbee.connections.connectionprovider.SerialPortConnectionProvider; public class XBeeConfiguration { @@ -19,7 +20,7 @@ public boolean accept(XBeeResponse response) { }; public XBeeConfiguration() { - + } /** @@ -76,6 +77,11 @@ public XBeeConfiguration withConnectionProvider(IConnectionProvider connectionPr return this; } + public XBeeConfiguration withDefaultConnectionProvider() { + this.connectionProvider = new SerialPortConnectionProvider(); + return this; + } + /** * Only adds responses that implement NoRequestResponse * diff --git a/src/main/java/com/rapplogic/xbee/examples/ApiAtExample.java b/src/main/java/com/rapplogic/xbee/examples/ApiAtExample.java index ec8bdf3..2951db8 100644 --- a/src/main/java/com/rapplogic/xbee/examples/ApiAtExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/ApiAtExample.java @@ -44,7 +44,7 @@ public class ApiAtExample { private final static Logger log = Logger.getLogger(ApiAtExample.class); - private XBee xbee = new XBee(new XBeeConfiguration().withStartupChecks(false)); + private XBee xbee = new XBee(new XBeeConfiguration().withStartupChecks(false).withDefaultConnectionProvider()); public ApiAtExample() throws XBeeException { @@ -72,7 +72,6 @@ public ApiAtExample() throws XBeeException { } public static void main(String[] args) throws XBeeException { - PropertyConfigurator.configure("log4j.properties"); new ApiAtExample(); } } diff --git a/src/main/java/com/rapplogic/xbee/examples/RemoteAtExample.java b/src/main/java/com/rapplogic/xbee/examples/RemoteAtExample.java index ed713a5..4c62377 100644 --- a/src/main/java/com/rapplogic/xbee/examples/RemoteAtExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/RemoteAtExample.java @@ -51,7 +51,6 @@ private RemoteAtExample() throws XBeeException, InterruptedException { try { // replace with your coordinator com/baud xbee.open("/dev/tty.usbserial-A6005v5M", 9600); - // xbee.open("COM5", 9600); // replace with SH + SL of your end device XBeeAddress64 addr64 = new XBeeAddress64(0, 0x13, 0xa2, 0, 0x40, 0x0a, 0x3e, 0x02); @@ -100,7 +99,6 @@ private RemoteAtExample() throws XBeeException, InterruptedException { } public static void main(String[] args) throws XBeeException, InterruptedException { - PropertyConfigurator.configure("log4j.properties"); new RemoteAtExample(); } } diff --git a/src/main/java/com/rapplogic/xbee/examples/wpan/ApiReceiverExample.java b/src/main/java/com/rapplogic/xbee/examples/wpan/ApiReceiverExample.java index 2f8b4a3..6cd65d8 100644 --- a/src/main/java/com/rapplogic/xbee/examples/wpan/ApiReceiverExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/wpan/ApiReceiverExample.java @@ -20,7 +20,6 @@ package com.rapplogic.xbee.examples.wpan; import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; import com.rapplogic.xbee.api.ApiId; import com.rapplogic.xbee.api.XBee; @@ -42,8 +41,6 @@ public class ApiReceiverExample { private final static Logger log = Logger.getLogger(ApiReceiverExample.class); - private long last = System.currentTimeMillis(); - private ApiReceiverExample() throws Exception { XBee xbee = new XBee(); @@ -92,8 +89,6 @@ private ApiReceiverExample() throws Exception { } public static void main(String[] args) throws Exception { - // init log4j - PropertyConfigurator.configure("log4j.properties"); new ApiReceiverExample(); } } diff --git a/src/main/java/com/rapplogic/xbee/examples/wpan/ApiSenderExample.java b/src/main/java/com/rapplogic/xbee/examples/wpan/ApiSenderExample.java index 15c59ac..18b6228 100644 --- a/src/main/java/com/rapplogic/xbee/examples/wpan/ApiSenderExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/wpan/ApiSenderExample.java @@ -20,7 +20,6 @@ package com.rapplogic.xbee.examples.wpan; import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; import com.rapplogic.xbee.api.ApiId; import com.rapplogic.xbee.api.XBee; @@ -28,7 +27,6 @@ import com.rapplogic.xbee.api.XBeePacket; import com.rapplogic.xbee.api.XBeeResponse; import com.rapplogic.xbee.api.wpan.TxRequest16; -import com.rapplogic.xbee.api.wpan.TxRequest64; import com.rapplogic.xbee.api.wpan.TxStatusResponse; /** @@ -59,6 +57,7 @@ private ApiSenderExample() throws Exception { try { // replace with port and baud rate of your XBee xbee.open("/dev/tty.usbserial-A6005uPi", 9600); + //xbee.open("COM6", 38400); while (true) { @@ -128,8 +127,6 @@ private ApiSenderExample() throws Exception { } public static void main(String[] args) throws Exception { - // init log4j - PropertyConfigurator.configure("log4j.properties"); new ApiSenderExample(); } } diff --git a/src/main/java/com/rapplogic/xbee/examples/wpan/IoSamplesExample.java b/src/main/java/com/rapplogic/xbee/examples/wpan/IoSamplesExample.java index 8e8d5be..7187999 100644 --- a/src/main/java/com/rapplogic/xbee/examples/wpan/IoSamplesExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/wpan/IoSamplesExample.java @@ -20,7 +20,6 @@ package com.rapplogic.xbee.examples.wpan; import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; import com.rapplogic.xbee.api.ApiId; import com.rapplogic.xbee.api.XBee; @@ -131,8 +130,6 @@ private IoSamplesExample() throws Exception { } public static void main(String[] args) throws Exception { - // init log4j - PropertyConfigurator.configure("log4j.properties"); new IoSamplesExample(); } } diff --git a/src/main/java/com/rapplogic/xbee/examples/zigbee/BroadcastReceiverExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/BroadcastReceiverExample.java index ee6d6a0..b9a55b5 100644 --- a/src/main/java/com/rapplogic/xbee/examples/zigbee/BroadcastReceiverExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/BroadcastReceiverExample.java @@ -20,7 +20,6 @@ package com.rapplogic.xbee.examples.zigbee; import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; import com.rapplogic.xbee.api.XBee; import com.rapplogic.xbee.api.XBeeException; @@ -53,7 +52,6 @@ private BroadcastReceiverExample() throws XBeeException { } public static void main(String[] args) throws XBeeException, InterruptedException { - PropertyConfigurator.configure("log4j.properties"); new BroadcastReceiverExample(); } } diff --git a/src/main/java/com/rapplogic/xbee/examples/zigbee/BroadcastSenderExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/BroadcastSenderExample.java index 0b2d10d..244c96b 100644 --- a/src/main/java/com/rapplogic/xbee/examples/zigbee/BroadcastSenderExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/BroadcastSenderExample.java @@ -20,7 +20,6 @@ package com.rapplogic.xbee.examples.zigbee; import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; import com.rapplogic.xbee.api.XBee; import com.rapplogic.xbee.api.XBeeAddress64; @@ -71,7 +70,6 @@ private BroadcastSenderExample() throws XBeeException { } public static void main(String[] args) throws XBeeException, InterruptedException { - PropertyConfigurator.configure("log4j.properties"); new BroadcastSenderExample(); } } diff --git a/src/main/java/com/rapplogic/xbee/examples/zigbee/SleepTestCoordinator.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/SleepTestCoordinator.java index 4fcd4f2..31edeab 100644 --- a/src/main/java/com/rapplogic/xbee/examples/zigbee/SleepTestCoordinator.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/SleepTestCoordinator.java @@ -27,7 +27,7 @@ public SleepTestCoordinator(String args[]) throws XBeeTimeoutException, XBeeExce PropertyConfigurator.configure("log4j.properties"); - XBee xbee = new XBee(new XBeeConfiguration().withStartupChecks(false)); + XBee xbee = new XBee(new XBeeConfiguration().withStartupChecks(false).withDefaultConnectionProvider()); //coord xbee.open("/dev/tty.usbserial-A6005uRz", 9600); diff --git a/src/main/java/com/rapplogic/xbee/examples/zigbee/ZBForceSampleExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZBForceSampleExample.java index 8b44363..7f93363 100644 --- a/src/main/java/com/rapplogic/xbee/examples/zigbee/ZBForceSampleExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZBForceSampleExample.java @@ -87,8 +87,6 @@ private ZBForceSampleExample() throws Exception { } public static void main(String[] args) throws Exception { - // init log4j - PropertyConfigurator.configure("log4j.properties"); new ZBForceSampleExample(); } } diff --git a/src/main/java/com/rapplogic/xbee/examples/zigbee/ZBNodeDiscoverExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZBNodeDiscoverExample.java index 562ca66..769c96b 100644 --- a/src/main/java/com/rapplogic/xbee/examples/zigbee/ZBNodeDiscoverExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZBNodeDiscoverExample.java @@ -22,10 +22,7 @@ import java.util.List; import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; -import com.rapplogic.xbee.api.ApiId; -import com.rapplogic.xbee.api.PacketListener; import com.rapplogic.xbee.api.XBee; import com.rapplogic.xbee.api.XBeeException; import com.rapplogic.xbee.api.XBeeResponse; @@ -90,7 +87,6 @@ public ZBNodeDiscoverExample() throws XBeeException, InterruptedException { } public static void main(String[] args) throws XBeeException, InterruptedException { - PropertyConfigurator.configure("log4j.properties"); new ZBNodeDiscoverExample(); } } diff --git a/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetApiAtExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetApiAtExample.java index 81f0351..567665f 100644 --- a/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetApiAtExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetApiAtExample.java @@ -20,7 +20,6 @@ package com.rapplogic.xbee.examples.zigbee; import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; import com.rapplogic.xbee.api.XBee; import com.rapplogic.xbee.api.XBeeException; @@ -165,7 +164,6 @@ private void configureIOSamples(XBee xbee) throws XBeeException { } public static void main(String[] args) throws XBeeException { - PropertyConfigurator.configure("log4j.properties"); new ZNetApiAtExample(); } } diff --git a/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetExplicitReceiverExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetExplicitReceiverExample.java index 2562823..43e3ae2 100644 --- a/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetExplicitReceiverExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetExplicitReceiverExample.java @@ -20,7 +20,6 @@ package com.rapplogic.xbee.examples.zigbee; import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; import com.rapplogic.xbee.api.ApiId; import com.rapplogic.xbee.api.XBee; @@ -70,8 +69,6 @@ private ZNetExplicitReceiverExample() throws Exception { } public static void main(String[] args) throws Exception { - // init log4j - PropertyConfigurator.configure("log4j.properties"); new ZNetExplicitReceiverExample(); } } diff --git a/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetExplicitSenderExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetExplicitSenderExample.java index 1b6aae4..c5578e3 100644 --- a/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetExplicitSenderExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetExplicitSenderExample.java @@ -20,7 +20,6 @@ package com.rapplogic.xbee.examples.zigbee; import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; import com.rapplogic.xbee.api.XBee; import com.rapplogic.xbee.api.XBeeAddress16; @@ -92,7 +91,6 @@ private ZNetExplicitSenderExample() throws XBeeException { } public static void main(String[] args) throws XBeeException, InterruptedException { - PropertyConfigurator.configure("log4j.properties"); new ZNetExplicitSenderExample(); } } diff --git a/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetIoSampleExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetIoSampleExample.java index 6a4e9d6..84bcb66 100644 --- a/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetIoSampleExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetIoSampleExample.java @@ -20,14 +20,11 @@ package com.rapplogic.xbee.examples.zigbee; import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; import com.rapplogic.xbee.api.ApiId; import com.rapplogic.xbee.api.PacketListener; import com.rapplogic.xbee.api.XBee; import com.rapplogic.xbee.api.XBeeResponse; -import com.rapplogic.xbee.api.wpan.IoSample; -import com.rapplogic.xbee.api.wpan.RxResponseIoSample; import com.rapplogic.xbee.api.zigbee.ZNetRxIoSampleResponse; /** @@ -78,8 +75,6 @@ public void processResponse(XBeeResponse response) { } public static void main(String[] args) throws Exception { - // init log4j - PropertyConfigurator.configure("log4j.properties"); new ZNetIoSampleExample(); } } diff --git a/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetReceiverExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetReceiverExample.java index 2f4e383..5101b7f 100644 --- a/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetReceiverExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetReceiverExample.java @@ -20,7 +20,6 @@ package com.rapplogic.xbee.examples.zigbee; import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; import com.rapplogic.xbee.api.ApiId; import com.rapplogic.xbee.api.XBee; @@ -93,8 +92,6 @@ private ZNetReceiverExample() throws Exception { } public static void main(String[] args) throws Exception { - // init log4j - PropertyConfigurator.configure("log4j.properties"); new ZNetReceiverExample(); } } diff --git a/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetSenderExample.java b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetSenderExample.java index 5410520..370a9e7 100644 --- a/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetSenderExample.java +++ b/src/main/java/com/rapplogic/xbee/examples/zigbee/ZNetSenderExample.java @@ -176,7 +176,6 @@ private ZNetSenderExample() throws XBeeException { } public static void main(String[] args) throws XBeeException, InterruptedException { - PropertyConfigurator.configure("log4j.properties"); new ZNetSenderExample(); } } diff --git a/src/test/java/com/rapplogic/xbee/TestConnectionProvider.java b/src/test/java/com/rapplogic/xbee/TestConnectionProvider.java index f763096..be34cb6 100644 --- a/src/test/java/com/rapplogic/xbee/TestConnectionProvider.java +++ b/src/test/java/com/rapplogic/xbee/TestConnectionProvider.java @@ -3,13 +3,13 @@ import com.rapplogic.xbee.connections.XBeeConnection; import com.rapplogic.xbee.connections.XBeeConnectionException; import com.rapplogic.xbee.connections.connectionprovider.IConnectionProvider; +import com.rapplogic.xbee.mocks.Series2Mock; public class TestConnectionProvider implements IConnectionProvider { @Override public XBeeConnection open(String port, int baudRate) throws XBeeConnectionException { - // TODO Auto-generated method stub - return null; + return new Series2Mock(); } } diff --git a/src/test/java/com/rapplogic/xbee/mocks/Series2Mock.java b/src/test/java/com/rapplogic/xbee/mocks/Series2Mock.java new file mode 100644 index 0000000..a93b8cc --- /dev/null +++ b/src/test/java/com/rapplogic/xbee/mocks/Series2Mock.java @@ -0,0 +1,51 @@ +package com.rapplogic.xbee.mocks; + +import java.io.IOException; + +import com.rapplogic.xbee.connections.XBeeConnection; + +public class Series2Mock implements XBeeConnection { + + @Override + public int getByte() throws IOException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean hasData() throws IOException { + // TODO Auto-generated method stub + return false; + } + + @Override + public void close() throws IOException { + // TODO Auto-generated method stub + + } + + @Override + public void writeIntArray(int[] packet) throws IOException { + // TODO Auto-generated method stub + + } + + @Override + public boolean isConnected() { + // TODO Auto-generated method stub + return false; + } + + @Override + public long getLastDataReceiveTime() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public long getLastDataSendTime() { + // TODO Auto-generated method stub + return 0; + } + +}