From 97b5f758a01e1e5e925abbaad1ba8b6b710ad146 Mon Sep 17 00:00:00 2001 From: James Cotton Date: Mon, 7 Mar 2011 04:54:43 -0600 Subject: [PATCH] Made a lot of critical functions synchronized to block race conditions (essentialy implements a mutex locker for that object). Also added callbacks to UAVObjects for unpacked and updated. More to come. Finally test case that checks that we get FlightStatus through UAVTalk (i.e. that the aircraft is talking). --- .../src/org/openpilot/uavtalk/UAVObject.java | 49 +++++++++++++++++-- .../org/openpilot/uavtalk/UAVObjectField.java | 18 +++---- .../openpilot/uavtalk/UAVObjectManager.java | 14 +++--- .../src/org/openpilot/uavtalk/UAVTalk.java | 18 ++++++- .../tests/org/openpilot/uavtalk/TalkTest.java | 33 +++++++++++-- 5 files changed, 107 insertions(+), 25 deletions(-) diff --git a/androidgcs/src/org/openpilot/uavtalk/UAVObject.java b/androidgcs/src/org/openpilot/uavtalk/UAVObject.java index a990def2c..6ede86094 100644 --- a/androidgcs/src/org/openpilot/uavtalk/UAVObject.java +++ b/androidgcs/src/org/openpilot/uavtalk/UAVObject.java @@ -3,9 +3,50 @@ package org.openpilot.uavtalk; import java.nio.ByteBuffer; import java.util.List; import java.util.ListIterator; +import java.util.Observer; +import java.util.Observable; public abstract class UAVObject { + public class CallbackListener extends Observable { + private UAVObject parent; + + public CallbackListener(UAVObject parent) { + this.parent = parent; + } + + public void event () { + setChanged(); + notifyObservers(parent); + } + } + + private CallbackListener updatedListeners = new CallbackListener(this); + public void addUpdatedObserver(Observer o) { + synchronized(updatedListeners) { + updatedListeners.addObserver(o); + } + } + void updated() { + synchronized(updatedListeners) { + updatedListeners.event(); + } + } + + private CallbackListener unpackedListeners = new CallbackListener(this); + public void addUnpackedObserver(Observer o) { + synchronized(unpackedListeners) { + unpackedListeners.addObserver(o); + } + } + void unpacked() { + synchronized(unpackedListeners) { + System.out.println("Unpacked!: " + unpackedListeners.countObservers() + " " + getName()); + unpackedListeners.event(); + } + } + + /** * Object update mode */ @@ -298,10 +339,12 @@ public abstract class UAVObject { UAVObjectField field = li.next(); numBytes += field.unpack(dataIn); } + + // Trigger all the listeners for the unpack event + unpacked(); + updated(); + return numBytes; - // TODO: Callbacks - // emit objectUnpacked(this); // trigger object updated event - // emit objectUpdated(this); } // /** diff --git a/androidgcs/src/org/openpilot/uavtalk/UAVObjectField.java b/androidgcs/src/org/openpilot/uavtalk/UAVObjectField.java index 0b72c33c8..21444a04b 100644 --- a/androidgcs/src/org/openpilot/uavtalk/UAVObjectField.java +++ b/androidgcs/src/org/openpilot/uavtalk/UAVObjectField.java @@ -91,8 +91,7 @@ public class UAVObjectField { * @param dataOut * @return the number of bytes added **/ - public int pack(ByteBuffer dataOut) { - //QMutexLocker locker(obj->getMutex()); + public synchronized int pack(ByteBuffer dataOut) { // Pack each element in output buffer dataOut.order(ByteOrder.LITTLE_ENDIAN); switch (type) @@ -153,7 +152,7 @@ public class UAVObjectField { return getNumBytes(); } - public int unpack(ByteBuffer dataIn) { + public synchronized int unpack(ByteBuffer dataIn) { // Unpack each element from input buffer dataIn.order(ByteOrder.LITTLE_ENDIAN); switch (type) @@ -240,10 +239,9 @@ public class UAVObjectField { return getNumBytes(); } - Object getValue() { return getValue(0); }; + public Object getValue() { return getValue(0); }; @SuppressWarnings("unchecked") - Object getValue(int index) { -// QMutexLocker locker(obj->getMutex()); + public synchronized Object getValue(int index) { // Check that index is not out of bounds if ( index >= numElements ) { @@ -287,8 +285,7 @@ public class UAVObjectField { public void setValue(Object data) { setValue(data,0); } @SuppressWarnings("unchecked") - public void setValue(Object data, int index) { - // QMutexLocker locker(obj->getMutex()); + public synchronized void setValue(Object data, int index) { // Check that index is not out of bounds //if ( index >= numElements ); //throw new Exception("Index out of bounds"); @@ -361,6 +358,7 @@ public class UAVObjectField { //throw new Exception("Sorry I haven't implemented strings yet"); } } + obj.updated(); } } @@ -449,7 +447,7 @@ public class UAVObjectField { } @SuppressWarnings("unchecked") - public void clear() { + public synchronized void clear() { switch (type) { case INT8: @@ -503,7 +501,7 @@ public class UAVObjectField { } } - public void constructorInitialize(String name, String units, FieldType type, List elementNames, List options) { + public synchronized void constructorInitialize(String name, String units, FieldType type, List elementNames, List options) { // Copy params this.name = name; this.units = units; diff --git a/androidgcs/src/org/openpilot/uavtalk/UAVObjectManager.java b/androidgcs/src/org/openpilot/uavtalk/UAVObjectManager.java index 2e531fc85..74ddb95fb 100644 --- a/androidgcs/src/org/openpilot/uavtalk/UAVObjectManager.java +++ b/androidgcs/src/org/openpilot/uavtalk/UAVObjectManager.java @@ -23,7 +23,7 @@ public class UAVObjectManager { * updates. * @throws Exception */ - public boolean registerObject(UAVDataObject obj) throws Exception + public synchronized boolean registerObject(UAVDataObject obj) throws Exception { // QMutexLocker locker(mutex); @@ -128,7 +128,7 @@ public class UAVObjectManager { return true; } - public void addObject(UAVObject obj) + public synchronized void addObject(UAVObject obj) { // Add to list List ls = new ArrayList(); @@ -143,15 +143,15 @@ public class UAVObjectManager { */ public List> getObjects() { - //QMutexLocker locker(mutex); return objects; } /** * Same as getObjects() but will only return DataObjects. */ - public List< List > getDataObjects() + public List< List > getDataObjects() { + assert(false); // TOOD This return new ArrayList>(); /* QMutexLocker locker(mutex); @@ -190,6 +190,7 @@ public class UAVObjectManager { */ public List > getMetaObjects() { + assert(false); // TODO return new ArrayList< List >(); /* QMutexLocker locker(mutex); @@ -267,7 +268,7 @@ public class UAVObjectManager { /** * Helper function for the public getObject() functions. */ - public UAVObject getObject(String name, int objId, int instId) + public synchronized UAVObject getObject(String name, int objId, int instId) { //QMutexLocker locker(mutex); // Check if this object type is already in the list @@ -310,9 +311,8 @@ public class UAVObjectManager { /** * Helper function for the public getObjectInstances() */ - public List getObjectInstances(String name, int objId) + public synchronized List getObjectInstances(String name, int objId) { - //QMutexLocker locker(mutex); // Check if this object type is already in the list ListIterator> objIter = objects.listIterator(); while(objIter.hasNext()) { diff --git a/androidgcs/src/org/openpilot/uavtalk/UAVTalk.java b/androidgcs/src/org/openpilot/uavtalk/UAVTalk.java index 07b061bc9..7ea9c7c60 100644 --- a/androidgcs/src/org/openpilot/uavtalk/UAVTalk.java +++ b/androidgcs/src/org/openpilot/uavtalk/UAVTalk.java @@ -10,6 +10,22 @@ import java.nio.ByteOrder; public class UAVTalk { + private Thread inputProcessingThread = null; + /** + * A reference to the thread for processing the incoming stream + * @return + */ + public Thread getInputProcessThread() { + if(inputProcessingThread == null) + + inputProcessingThread = new Thread() { + public void run() { + processInputStream(); + } + }; + return inputProcessingThread; + } + /** * Constants */ @@ -684,7 +700,7 @@ public class UAVTalk { * \param[in] type Transaction type * \return Success (true), Failure (false) */ - public boolean transmitSingleObject(UAVObject obj, int type, boolean allInstances) + public synchronized boolean transmitSingleObject(UAVObject obj, int type, boolean allInstances) { int length; int dataOffset; diff --git a/androidgcs/tests/org/openpilot/uavtalk/TalkTest.java b/androidgcs/tests/org/openpilot/uavtalk/TalkTest.java index 6dc23530e..252927122 100644 --- a/androidgcs/tests/org/openpilot/uavtalk/TalkTest.java +++ b/androidgcs/tests/org/openpilot/uavtalk/TalkTest.java @@ -9,6 +9,8 @@ import java.net.DatagramSocket; import java.net.InetAddress; import java.net.Socket; import java.nio.ByteBuffer; +import java.util.Observable; +import java.util.Observer; import org.junit.BeforeClass; import org.junit.Test; @@ -20,8 +22,9 @@ public class TalkTest { static UAVObjectManager objMngr; static final String IP_ADDRDESS = new String("127.0.0.1"); - static final int PORT_NUM = 8000; - + static final int PORT_NUM = 7777; + boolean succeed = false; + @BeforeClass public static void setUpBeforeClass() throws Exception { objMngr = new UAVObjectManager(); @@ -29,7 +32,7 @@ public class TalkTest { } @Test - public void testProcessInputStream() { + public void testGetFlightStatus() { Socket connection = null; UAVTalk talk = null; try{ @@ -48,7 +51,29 @@ public class TalkTest { fail("Couldn't construct UAVTalk object"); } - talk.processInputStream(); + Thread inputStream = talk.getInputProcessThread(); + inputStream.start(); + + succeed = false; + + UAVObject obj = objMngr.getObject("FlightTelemetryStats"); + + obj.addUpdatedObserver( new Observer() { + public void update(Observable observable, Object data) { + // TODO Auto-generated method stub + System.out.println("Updated: " + data.toString()); + succeed = true; + } + }); + + try { + Thread.sleep(1000); + } catch (InterruptedException e1) { + } + + if(!succeed) + fail("Never received a FlightTelemetryStats update"); + } @Test