diff --git a/androidgcs/src/org/openpilot/androidgcs/telemetry/OPTelemetryService.java b/androidgcs/src/org/openpilot/androidgcs/telemetry/OPTelemetryService.java index fe7b2b6d1..c03510a83 100644 --- a/androidgcs/src/org/openpilot/androidgcs/telemetry/OPTelemetryService.java +++ b/androidgcs/src/org/openpilot/androidgcs/telemetry/OPTelemetryService.java @@ -294,6 +294,7 @@ public class OPTelemetryService extends Service { numRead = inputStream.read(buffer); } } + private void copyAssets(String JAR_DIR, String JAR_NAME) { File jarsDir = getDir(JAR_DIR, MODE_WORLD_READABLE); @@ -320,6 +321,19 @@ public class OPTelemetryService extends Service { catch (IOException e) { Log.e(TAG, e.toString(), e); + String[] list; + try { + list = assetManager.list("uavos/"); + Log.i(TAG, "Listing found uavos"); + for(int i = 0; i < list.length; i++) { + Log.i(TAG, "Found: " + list[i]); + } + + } catch (IOException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + } } @@ -351,6 +365,10 @@ public class OPTelemetryService extends Service { final String JAR_DIR = "jars"; final String DEX_DIR = "optimized_dex"; + File jarsDir = getDir(JAR_DIR, MODE_WORLD_READABLE); + if (jarsDir.exists()) + deleteDirectoryContents(jarsDir); + copyAssets(JAR_DIR, jar); Log.d(TAG, "Starting dex loader"); @@ -360,7 +378,6 @@ public class OPTelemetryService extends Service { if (dexDir.exists()) deleteDirectoryContents(dexDir); - File jarsDir = getDir(JAR_DIR, MODE_WORLD_READABLE); String classpath = new File(jarsDir, jar).getAbsolutePath(); DexClassLoader loader = new DexClassLoader(classpath, dexDir.getAbsolutePath(), null, getClassLoader()); diff --git a/androidgcs/src/org/openpilot/androidgcs/telemetry/TelemetryTask.java b/androidgcs/src/org/openpilot/androidgcs/telemetry/TelemetryTask.java index 3f5122579..ad23b2e00 100644 --- a/androidgcs/src/org/openpilot/androidgcs/telemetry/TelemetryTask.java +++ b/androidgcs/src/org/openpilot/androidgcs/telemetry/TelemetryTask.java @@ -8,8 +8,6 @@ import java.util.Observer; import org.openpilot.uavtalk.Telemetry; import org.openpilot.uavtalk.TelemetryMonitor; -import org.openpilot.uavtalk.UAVObject; -import org.openpilot.uavtalk.UAVObjectField; import org.openpilot.uavtalk.UAVObjectManager; import org.openpilot.uavtalk.UAVTalk; import org.openpilot.uavtalk.uavobjects.TelemObjectsInitialize; @@ -93,32 +91,6 @@ public abstract class TelemetryTask implements Runnable { */ abstract boolean attemptConnection(); - private final Observer firmwareIapUpdated = new Observer() { - @Override - public void update(Observable observable, Object data) { - if (DEBUG) Log.d(TAG, "Received firmware IAP Updated message"); - - UAVObject obj = objMngr.getObject("FirmwareIAPObj"); - UAVObjectField description = obj.getField("Description"); - if(description == null || description.getNumElements() < 100) { - telemService.toastMessage("Failed to determine UAVO set"); - } else { - final int HASH_SIZE_USED = 8; - String jarName = new String(); - for(int i = 0; i < HASH_SIZE_USED; i++) - jarName += Integer.toHexString((int) description.getDouble(i+60)); - jarName += ".jar"; - if (DEBUG) Log.d(TAG, "Attempting to load: " + jarName); - if (telemService.loadUavobjects(jarName, objMngr) ) { - telemService.toastMessage("Loaded appropriate UAVO set"); - } else - telemService.toastMessage("Failed to determine UAVO set"); - } - - obj.removeUpdatedObserver(this); - } - }; - /** * Called when a physical channel is opened * @@ -133,11 +105,6 @@ public abstract class TelemetryTask implements Runnable { objMngr = new UAVObjectManager(); TelemObjectsInitialize.register(objMngr); - // Register to get an update from FirmwareIAP in order to register - // the appropriate objects - UAVObject obj = objMngr.getObject("FirmwareIAPObj"); - obj.addUpdatedObserver(firmwareIapUpdated); - // Create the required telemetry objects attached to this // data stream uavTalk = new UAVTalk(inStream, outStream, objMngr); diff --git a/androidgcs/src/org/openpilot/uavtalk/Telemetry.java b/androidgcs/src/org/openpilot/uavtalk/Telemetry.java index 6fcbb2491..efb5b2252 100644 --- a/androidgcs/src/org/openpilot/uavtalk/Telemetry.java +++ b/androidgcs/src/org/openpilot/uavtalk/Telemetry.java @@ -202,7 +202,7 @@ public class Telemetry { /** * Register a new object for periodic updates (if enabled) */ - private synchronized void registerObject(UAVObject obj) { + private void registerObject(UAVObject obj) { // Setup object for periodic updates addObject(obj); @@ -213,24 +213,27 @@ public class Telemetry { /** * Add an object in the list used for periodic updates */ - private synchronized void addObject(UAVObject obj) { - // Check if object type is already in the list - ListIterator li = objList.listIterator(); - while (li.hasNext()) { - ObjectTimeInfo n = li.next(); - if (n.obj.getObjID() == obj.getObjID()) { - // Object type (not instance!) is already in the list, do - // nothing - return; - } - } + private void addObject(UAVObject obj) { - // If this point is reached, then the object type is new, let's add it - ObjectTimeInfo timeInfo = new ObjectTimeInfo(); - timeInfo.obj = obj; - timeInfo.timeToNextUpdateMs = 0; - timeInfo.updatePeriodMs = 0; - objList.add(timeInfo); + synchronized (objList) { + // Check if object type is already in the list + ListIterator li = objList.listIterator(); + while (li.hasNext()) { + ObjectTimeInfo n = li.next(); + if (n.obj.getObjID() == obj.getObjID()) { + // Object type (not instance!) is already in the list, do + // nothing + return; + } + } + + // If this point is reached, then the object type is new, let's add it + ObjectTimeInfo timeInfo = new ObjectTimeInfo(); + timeInfo.obj = obj; + timeInfo.timeToNextUpdateMs = 0; + timeInfo.updatePeriodMs = 0; + objList.add(timeInfo); + } } /** @@ -281,7 +284,7 @@ public class Telemetry { * Connect to all instances of an object depending on the event mask * specified */ - private synchronized void connectToObjectInstances(UAVObject obj, + private void connectToObjectInstances(UAVObject obj, int eventMask) { List objs = objMngr.getObjectInstances(obj.getObjID()); ListIterator li = objs.listIterator(); @@ -312,43 +315,46 @@ public class Telemetry { * Update an object based on its metadata properties */ private void updateObject(UAVObject obj) { - // Get metadata - UAVObject.Metadata metadata = obj.getMetadata(); - // Setup object depending on update mode - int eventMask; - if (metadata.GetGcsTelemetryUpdateMode() == UAVObject.UpdateMode.UPDATEMODE_PERIODIC) { - // Set update period - setUpdatePeriod(obj, metadata.gcsTelemetryUpdatePeriod); - // Connect signals for all instances - eventMask = EV_UPDATED_MANUAL | EV_UPDATE_REQ; - if (obj.isMetadata()) - eventMask |= EV_UNPACKED; // we also need to act on remote - // updates (unpack events) + synchronized(obj) { + // Get metadata + UAVObject.Metadata metadata = obj.getMetadata(); - connectToObjectInstances(obj, eventMask); - } else if (metadata.GetGcsTelemetryUpdateMode() == UAVObject.UpdateMode.UPDATEMODE_ONCHANGE) { - // Set update period - setUpdatePeriod(obj, 0); - // Connect signals for all instances - eventMask = EV_UPDATED | EV_UPDATED_MANUAL | EV_UPDATE_REQ; - if (obj.isMetadata()) - eventMask |= EV_UNPACKED; // we also need to act on remote - // updates (unpack events) + // Setup object depending on update mode + int eventMask; + if (metadata.GetGcsTelemetryUpdateMode() == UAVObject.UpdateMode.UPDATEMODE_PERIODIC) { + // Set update period + setUpdatePeriod(obj, metadata.gcsTelemetryUpdatePeriod); + // Connect signals for all instances + eventMask = EV_UPDATED_MANUAL | EV_UPDATE_REQ; + if (obj.isMetadata()) + eventMask |= EV_UNPACKED; // we also need to act on remote + // updates (unpack events) - connectToObjectInstances(obj, eventMask); - } else if (metadata.GetGcsTelemetryUpdateMode() == UAVObject.UpdateMode.UPDATEMODE_THROTTLED) { - // TODO - } else if (metadata.GetGcsTelemetryUpdateMode() == UAVObject.UpdateMode.UPDATEMODE_MANUAL) { - // Set update period - setUpdatePeriod(obj, 0); - // Connect signals for all instances - eventMask = EV_UPDATED_MANUAL | EV_UPDATE_REQ; - if (obj.isMetadata()) - eventMask |= EV_UNPACKED; // we also need to act on remote - // updates (unpack events) + connectToObjectInstances(obj, eventMask); + } else if (metadata.GetGcsTelemetryUpdateMode() == UAVObject.UpdateMode.UPDATEMODE_ONCHANGE) { + // Set update period + setUpdatePeriod(obj, 0); + // Connect signals for all instances + eventMask = EV_UPDATED | EV_UPDATED_MANUAL | EV_UPDATE_REQ; + if (obj.isMetadata()) + eventMask |= EV_UNPACKED; // we also need to act on remote + // updates (unpack events) - connectToObjectInstances(obj, eventMask); + connectToObjectInstances(obj, eventMask); + } else if (metadata.GetGcsTelemetryUpdateMode() == UAVObject.UpdateMode.UPDATEMODE_THROTTLED) { + // TODO + } else if (metadata.GetGcsTelemetryUpdateMode() == UAVObject.UpdateMode.UPDATEMODE_MANUAL) { + // Set update period + setUpdatePeriod(obj, 0); + // Connect signals for all instances + eventMask = EV_UPDATED_MANUAL | EV_UPDATE_REQ; + if (obj.isMetadata()) + eventMask |= EV_UNPACKED; // we also need to act on remote + // updates (unpack events) + + connectToObjectInstances(obj, eventMask); + } } } @@ -448,7 +454,7 @@ public class Telemetry { registerObject(obj); } - private synchronized void newInstance(UAVObject obj) { + private void newInstance(UAVObject obj) { registerObject(obj); } @@ -632,11 +638,7 @@ public class Telemetry { return; } - // Store this as the active transaction - transPending = newTransactionPending; - transInfo = newTrans; - - if (DEBUG) Log.d(TAG, "Process Object transaction for " + transInfo.obj.getName()); + if (DEBUG) Log.d(TAG, "Process Object transaction for " + newTrans.obj.getName()); // Remove this one from the list of pending transactions handler.removeActivatedQueue(objInfo); @@ -644,12 +646,12 @@ public class Telemetry { try { // 3. Execute transaction by sending the appropriate UAVTalk command - if (transInfo.objRequest) { - if (DEBUG) Log.d(TAG, "Sending object request " + transInfo.obj.getName()); - utalk.sendObjectRequest(transInfo.obj, transInfo.allInstances); + if (newTrans.objRequest) { + if (DEBUG) Log.d(TAG, "Sending object request " + newTrans.obj.getName()); + utalk.sendObjectRequest(newTrans.obj, newTrans.allInstances); } else { - if (DEBUG) Log.d(TAG, "Sending object " + transInfo.obj.getName()); - utalk.sendObject(transInfo.obj, transInfo.acked, transInfo.allInstances); + if (DEBUG) Log.d(TAG, "Sending object " + newTrans.obj.getName()); + utalk.sendObject(newTrans.obj, newTrans.acked, newTrans.allInstances); } } catch (IOException e) { @@ -657,9 +659,17 @@ public class Telemetry { e.printStackTrace(); } - // Post a timeout timer if a response is epxected - if (transPending) + // Store this as the active transaction. However in the case + // of transPending && !newTransactionPending we need ot not + // override the previous pending transaction + if (!transPending && newTransactionPending) { + transPending = newTransactionPending; + transInfo = newTrans; + + // Post a timeout timer if a response is epxected handler.postDelayed(transactionTimeout, REQ_TIMEOUT_MS); + } + } } } @@ -744,7 +754,7 @@ public class Telemetry { //Send signal obj.transactionCompleted(result); } else { - if (ERROR) Log.e(TAG, "Error: received a transaction completed when did not expect it."); + if (ERROR) Log.e(TAG, "Error: received a transaction completed when did not expect it. " + obj.getName()); transPending = false; } } diff --git a/androidgcs/src/org/openpilot/uavtalk/TelemetryMonitor.java b/androidgcs/src/org/openpilot/uavtalk/TelemetryMonitor.java index 121a5ebcc..47d5744a0 100644 --- a/androidgcs/src/org/openpilot/uavtalk/TelemetryMonitor.java +++ b/androidgcs/src/org/openpilot/uavtalk/TelemetryMonitor.java @@ -54,9 +54,12 @@ public class TelemetryMonitor extends Observable { private final UAVObjectManager objMngr; private final Telemetry tel; + + private boolean objectsRegistered; // private UAVObject objPending; private UAVObject gcsStatsObj; private UAVObject flightStatsObj; + private final UAVObject firmwareIapObj; private Timer periodicTask; private int currentPeriod; private long lastUpdateTime; @@ -74,7 +77,8 @@ public class TelemetryMonitor extends Observable { return objects_updated; }; - public TelemetryMonitor(UAVObjectManager objMngr, Telemetry tel, OPTelemetryService s) { + public TelemetryMonitor(UAVObjectManager objMngr, Telemetry tel, + OPTelemetryService s) { this(objMngr, tel); telemService = s; } @@ -85,9 +89,15 @@ public class TelemetryMonitor extends Observable { // this.objPending = null; queue = new ArrayList(); + objectsRegistered = false; + // Get stats objects gcsStatsObj = objMngr.getObject("GCSTelemetryStats"); flightStatsObj = objMngr.getObject("FlightTelemetryStats"); + firmwareIapObj = objMngr.getObject("FirmwareIAPObj"); + + // The first update of the firmwareIapObj will trigger registering the objects + firmwareIapObj.addUpdatedObserver(firmwareIapUpdated); flightStatsObj.addUpdatedObserver(new Observer() { @Override @@ -360,8 +370,13 @@ public class TelemetryMonitor extends Observable { setPeriod(STATS_UPDATE_PERIOD_MS); connected = true; objects_updated = false; - startRetrievingObjects(); - if (HANDSHAKE_IS_CONNECTED) setChanged(); // Enabling this line makes the opConnected signal occur whenever we get a handshake + if (objectsRegistered) + startRetrievingObjects(); + else + firmwareIapObj.updateRequested(); + if (HANDSHAKE_IS_CONNECTED) + setChanged(); // Enabling this line makes the opConnected signal + // occur whenever we get a handshake } if (gcsDisconnected && gcsStatusChanged) { if (DEBUG) @@ -405,4 +420,37 @@ public class TelemetryMonitor extends Observable { periodicTask = null; } + private final Observer firmwareIapUpdated = new Observer() { + @Override + public void update(Observable observable, Object data) { + if (DEBUG) Log.d(TAG, "Received firmware IAP Updated message"); + + UAVObjectField description = firmwareIapObj.getField("Description"); + if (description == null || description.getNumElements() < 100) { + telemService.toastMessage("Failed to determine UAVO set"); + } else { + final int HASH_SIZE_USED = 8; + String jarName = new String(); + for (int i = 0; i < HASH_SIZE_USED; i++) { + jarName += String.format("%02x", (int) description.getDouble(i + 60)); + } + jarName += ".jar"; + if (DEBUG) Log.d(TAG, "Attempting to load: " + jarName); + if (telemService.loadUavobjects(jarName, objMngr)) { + telemService.toastMessage("Loaded appropriate UAVO set"); + objectsRegistered = true; + try { + startRetrievingObjects(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } else + telemService.toastMessage("Failed to load UAVO set: " + jarName); + } + + firmwareIapObj.removeUpdatedObserver(this); + } + }; + } diff --git a/androidgcs/src/org/openpilot/uavtalk/UAVTalk.java b/androidgcs/src/org/openpilot/uavtalk/UAVTalk.java index c996da845..fae619714 100644 --- a/androidgcs/src/org/openpilot/uavtalk/UAVTalk.java +++ b/androidgcs/src/org/openpilot/uavtalk/UAVTalk.java @@ -269,16 +269,18 @@ public class UAVTalk { * it wants to give up on one (after a timeout) then it can cancel it * @return True if that object was pending, False otherwise */ - public synchronized boolean cancelPendingTransaction(UAVObject obj) { - if(respObj != null && respObj.getObjID() == obj.getObjID()) { - if(transactionListener != null) { - Log.d(TAG,"Canceling transaction: " + respObj.getName()); - transactionListener.TransactionFailed(respObj); - } - respObj = null; - return true; - } else - return false; + public boolean cancelPendingTransaction(UAVObject obj) { + synchronized (respObj) { + if(respObj != null && respObj.getObjID() == obj.getObjID()) { + if(transactionListener != null) { + Log.d(TAG,"Canceling transaction: " + respObj.getName()); + transactionListener.TransactionFailed(respObj); + } + respObj = null; + return true; + } else + return false; + } } /** @@ -296,15 +298,16 @@ public class UAVTalk { /** * This is the code that sets up a new UAVTalk packet that expects a response. */ - private synchronized void setupTransaction(UAVObject obj, boolean allInstances, int type) { + private void setupTransaction(UAVObject obj, boolean allInstances, int type) { + synchronized (this) { + // Only cancel if it is for a different object + if(respObj != null && respObj.getObjID() != obj.getObjID()) + cancelPendingTransaction(obj); - // Only cancel if it is for a different object - if(respObj != null && respObj.getObjID() != obj.getObjID()) - cancelPendingTransaction(obj); - - respObj = obj; - respAllInstances = allInstances; - respType = type; + respObj = obj; + respAllInstances = allInstances; + respType = type; + } } /** @@ -315,7 +318,7 @@ public class UAVTalk { * Success (true), Failure (false) * @throws IOException */ - private synchronized boolean objectTransaction(UAVObject obj, int type, boolean allInstances) throws IOException { + private boolean objectTransaction(UAVObject obj, int type, boolean allInstances) throws IOException { if (type == TYPE_OBJ_ACK || type == TYPE_OBJ_REQ || type == TYPE_OBJ) { return transmitObject(obj, type, allInstances); } else { @@ -415,7 +418,7 @@ public class UAVTalk { { UAVObject rxObj = objMngr.getObject(rxObjId); if (rxObj == null) { - if (DEBUG) Log.d(TAG, "Unknown ID: " + rxObjId); + if (WARN) Log.w(TAG, "Unknown ID: " + rxObjId); stats.rxErrors++; rxState = RxStateType.STATE_SYNC; break; @@ -429,17 +432,7 @@ public class UAVTalk { // Check length and determine next state if (rxLength >= MAX_PAYLOAD_LENGTH) { - stats.rxErrors++; - rxState = RxStateType.STATE_SYNC; - break; - } - - // Check the lengths match - if ((rxPacketLength + rxLength) != packetSize) { // packet error - // - - // mismatched - // packet - // size + if (WARN) Log.w(TAG, "Greater than max payload length"); stats.rxErrors++; rxState = RxStateType.STATE_SYNC; break; @@ -503,7 +496,7 @@ public class UAVTalk { rxCSPacket = rxbyte; if (rxCS != rxCSPacket) { // packet error - faulty CRC - if (DEBUG) Log.d(TAG,"Bad crc"); + if (WARN) Log.w(TAG,"Bad crc"); stats.rxErrors++; rxState = RxStateType.STATE_SYNC; break; @@ -512,7 +505,7 @@ public class UAVTalk { if (rxPacketLength != (packetSize + 1)) { // packet error - // mismatched packet // size - if (DEBUG) Log.d(TAG,"Bad size"); + if (WARN) Log.w(TAG,"Bad size"); stats.rxErrors++; rxState = RxStateType.STATE_SYNC; break; @@ -529,6 +522,7 @@ public class UAVTalk { break; default: + if (WARN) Log.w(TAG, "Bad state"); rxState = RxStateType.STATE_SYNC; stats.rxErrors++; } @@ -701,21 +695,30 @@ public class UAVTalk { * Called when an object is received to check if this completes * a UAVTalk transaction */ - private synchronized void updateObjReq(UAVObject obj) { + private void updateObjReq(UAVObject obj) { // Check if this is not a possible candidate Assert.assertNotNull(obj); - if(respObj != null && respType == TYPE_OBJ_REQ && respObj.getObjID() == obj.getObjID() && - ((respObj.getInstID() == obj.getInstID() || !respAllInstances))) { + boolean succeeded = false; - // Indicate complete - respObj = null; + // The lock on UAVTalk must be release before the transaction succeeded signal is sent + // because otherwise if a transaction timeout occurs at the same time we can get a + // deadlock: + // 1. processInputStream -> updateObjReq (locks uavtalk) -> tranactionCompleted (locks transInfo) + // 2. transactionTimeout (locks transInfo) -> sendObjectRequest -> É -> setupTransaction (locks uavtalk) + synchronized(this) { + if(respObj != null && respType == TYPE_OBJ_REQ && respObj.getObjID() == obj.getObjID() && + ((respObj.getInstID() == obj.getInstID() || !respAllInstances))) { - // Notify listener - if (transactionListener != null) - transactionListener.TransactionSucceeded(obj); + // Indicate complete + respObj = null; + succeeded = true; + } } + // Notify listener + if (succeeded && transactionListener != null) + transactionListener.TransactionSucceeded(obj); } /**