From 9f8d22961fa5224043fd032b0c9a13a1ae72fd55 Mon Sep 17 00:00:00 2001 From: Stacey Sheldon Date: Sat, 11 Aug 2012 20:13:09 -0400 Subject: [PATCH 01/24] cc makefile: enable -ffunction-sections and -fdata-sections These compiler options place each function and each global variable into its own ELF section in each .o file. This, combined with the linker option --gc-sections allows the linker to evict unused functions and variables from the final ELF file. On CC, the firmware flash bank is only 118784 bytes in total. This commit reduces the .text segment from 114120 to 83536 and .data from 572 bytes to 560 bytes. That frees up a grand total of 30596 bytes of flash and 12 bytes of RAM. --- flight/CopterControl/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/flight/CopterControl/Makefile b/flight/CopterControl/Makefile index 9acfdff41..f786df65c 100644 --- a/flight/CopterControl/Makefile +++ b/flight/CopterControl/Makefile @@ -503,6 +503,7 @@ endif CFLAGS += -Wall CFLAGS += -Werror +CFLAGS += -ffunction-sections -fdata-sections CFLAGS += -Wa,-adhlns=$(addprefix $(OUTDIR)/, $(notdir $(addsuffix .lst, $(basename $<)))) # Compiler flags to generate dependency files: CFLAGS += -MD -MP -MF $(OUTDIR)/dep/$(@F).d From a0ca56a071c8980ae82494fd5907bddcf105cae5 Mon Sep 17 00:00:00 2001 From: James Cotton Date: Sun, 12 Aug 2012 13:28:38 -0500 Subject: [PATCH 02/24] HID: move all the variables to the top --- .../androidgcs/telemetry/HidUAVTalk.java | 44 ++++++++----------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java b/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java index 991e83871..6ec0a950a 100644 --- a/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java +++ b/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java @@ -28,7 +28,7 @@ import android.util.Log; public class HidUAVTalk extends TelemetryTask { private static final String TAG = HidUAVTalk.class.getSimpleName(); - public static int LOGLEVEL = 0; + public static int LOGLEVEL = 2; public static boolean WARN = LOGLEVEL > 1; public static boolean DEBUG = LOGLEVEL > 0; @@ -46,7 +46,24 @@ public class HidUAVTalk extends TelemetryTask { private static final String ACTION_USB_PERMISSION = "com.access.device.USB_PERMISSION"; - UsbDevice currentDevice; + //! Define whether to use a single interface for reading and writing + private final boolean UsingSingleInterface = true; + + private UsbDevice currentDevice; + private UsbEndpoint usbEndpointRead; + private UsbEndpoint usbEndpointWrite; + private UsbManager usbManager; + private PendingIntent permissionIntent; + private UsbDeviceConnection connectionRead; + private UsbDeviceConnection connectionWrite; + private IntentFilter permissionFilter; + private UsbInterface usbInterfaceRead = null; + private UsbInterface usbInterfaceWrite = null; + private TalkInputStream inTalkStream; + private TalkOutputStream outTalkStream; + private UsbRequest writeRequest = null; + private UsbRequest readRequest = null; + private Thread readWriteThread; public HidUAVTalk(OPTelemetryService service) { super(service); @@ -199,19 +216,6 @@ public class HidUAVTalk extends TelemetryTask { } }; */ - private UsbEndpoint usbEndpointRead; - - private UsbEndpoint usbEndpointWrite; - - private UsbManager usbManager; - - private PendingIntent permissionIntent; - - private UsbDeviceConnection connectionRead; - - private UsbDeviceConnection connectionWrite; - - private IntentFilter permissionFilter; protected void CleanUpAndClose() { if (UsingSingleInterface) { @@ -244,14 +248,6 @@ public class HidUAVTalk extends TelemetryTask { return false; } - private UsbInterface usbInterfaceRead = null; - private UsbInterface usbInterfaceWrite = null; - private final boolean UsingSingleInterface = true; - - private TalkInputStream inTalkStream; - private TalkOutputStream outTalkStream; - UsbRequest writeRequest = null; - boolean ConnectToDeviceInterface(UsbDevice connectDevice) { // Connecting to the Device - If you are reading and writing, then the device // can either have two end points on a single interface, or two interfaces @@ -370,7 +366,6 @@ public class HidUAVTalk extends TelemetryTask { return true; } - Thread readWriteThread; void displayBuffer(String msg, byte[] buf) { msg += " ("; @@ -384,7 +379,6 @@ public class HidUAVTalk extends TelemetryTask { * Gets a report from HID, extract the meaningful data and push * it to the input stream */ - UsbRequest readRequest = null; public int readData() { int bufferDataLength = usbEndpointRead.getMaxPacketSize(); ByteBuffer buffer = ByteBuffer.allocate(bufferDataLength + 1); From badbadef2ff971dcc812a34431a517b917347e89 Mon Sep 17 00:00:00 2001 From: James Cotton Date: Sun, 12 Aug 2012 13:41:15 -0500 Subject: [PATCH 03/24] Remove legacy code for supporting two USB interfaces --- .../androidgcs/telemetry/HidUAVTalk.java | 93 +++++-------------- 1 file changed, 23 insertions(+), 70 deletions(-) diff --git a/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java b/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java index 6ec0a950a..4f3c64a5a 100644 --- a/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java +++ b/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java @@ -28,9 +28,10 @@ import android.util.Log; public class HidUAVTalk extends TelemetryTask { private static final String TAG = HidUAVTalk.class.getSimpleName(); - public static int LOGLEVEL = 2; - public static boolean WARN = LOGLEVEL > 1; - public static boolean DEBUG = LOGLEVEL > 0; + public static final int LOGLEVEL = 2; + public static final boolean DEBUG = LOGLEVEL > 2; + public static final boolean WARN = LOGLEVEL > 1; + public static final boolean ERROR = LOGLEVEL > 0; //! USB constants private static final int MAX_HID_PACKET_SIZE = 64; @@ -46,19 +47,14 @@ public class HidUAVTalk extends TelemetryTask { private static final String ACTION_USB_PERMISSION = "com.access.device.USB_PERMISSION"; - //! Define whether to use a single interface for reading and writing - private final boolean UsingSingleInterface = true; - private UsbDevice currentDevice; private UsbEndpoint usbEndpointRead; private UsbEndpoint usbEndpointWrite; private UsbManager usbManager; private PendingIntent permissionIntent; - private UsbDeviceConnection connectionRead; - private UsbDeviceConnection connectionWrite; + private UsbDeviceConnection usbDeviceConnection; private IntentFilter permissionFilter; - private UsbInterface usbInterfaceRead = null; - private UsbInterface usbInterfaceWrite = null; + private UsbInterface usbInterface = null; private TalkInputStream inTalkStream; private TalkOutputStream outTalkStream; private UsbRequest writeRequest = null; @@ -218,19 +214,9 @@ public class HidUAVTalk extends TelemetryTask { protected void CleanUpAndClose() { - if (UsingSingleInterface) { - if(connectionRead != null && usbInterfaceRead != null) - connectionRead.releaseInterface(usbInterfaceRead); - usbInterfaceRead = null; - } - else { - if(connectionRead != null && usbInterfaceRead != null) - connectionRead.releaseInterface(usbInterfaceRead); - if(connectionWrite != null && usbInterfaceWrite != null) - connectionWrite.releaseInterface(usbInterfaceWrite); - usbInterfaceWrite = null; - usbInterfaceRead = null; - } + if(usbDeviceConnection != null && usbInterface != null) + usbDeviceConnection.releaseInterface(usbInterface); + usbInterface = null; } //Validating the Connected Device - Before asking for permission to connect to the device, it is essential that you ensure that this is a device that you support or expect to connect to. This can be done by validating the devices Vendor ID and Product ID. @@ -258,87 +244,54 @@ public class HidUAVTalk extends TelemetryTask { UsbEndpoint ep1 = null; UsbEndpoint ep2 = null; - - if (UsingSingleInterface) + // Using the same interface for reading and writing + usbInterface = connectDevice.getInterface(0x2); + if (usbInterface.getEndpointCount() == 2) { - // Using the same interface for reading and writing - usbInterfaceRead = connectDevice.getInterface(0x2); - usbInterfaceWrite = usbInterfaceRead; - if (usbInterfaceRead.getEndpointCount() == 2) - { - ep1 = usbInterfaceRead.getEndpoint(0); - ep2 = usbInterfaceRead.getEndpoint(1); - } + ep1 = usbInterface.getEndpoint(0); + ep2 = usbInterface.getEndpoint(1); } - else // if (!UsingSingleInterface) - { - usbInterfaceRead = connectDevice.getInterface(0x01); - usbInterfaceWrite = connectDevice.getInterface(0x02); - if ((usbInterfaceRead.getEndpointCount() == 1) && (usbInterfaceWrite.getEndpointCount() == 1)) - { - ep1 = usbInterfaceRead.getEndpoint(0); - ep2 = usbInterfaceWrite.getEndpoint(0); - } - } - if ((ep1 == null) || (ep2 == null)) { - if (DEBUG) Log.d(TAG, "Null endpoints"); + if (ERROR) Log.e(TAG, "Null endpoints"); return false; } // Determine which endpoint is the read, and which is the write - if (ep1.getType() == UsbConstants.USB_ENDPOINT_XFER_INT) { if (ep1.getDirection() == UsbConstants.USB_DIR_IN) - { usbEndpointRead = ep1; - } else if (ep1.getDirection() == UsbConstants.USB_DIR_OUT) - { usbEndpointWrite = ep1; - } } if (ep2.getType() == UsbConstants.USB_ENDPOINT_XFER_INT) { if (ep2.getDirection() == UsbConstants.USB_DIR_IN) - { usbEndpointRead = ep2; - } else if (ep2.getDirection() == UsbConstants.USB_DIR_OUT) - { usbEndpointWrite = ep2; - } } if ((usbEndpointRead == null) || (usbEndpointWrite == null)) { - if (DEBUG) Log.d(TAG, "Endpoints wrong way around"); + if (ERROR) Log.e(TAG, "Could not find write and read endpoint"); return false; } - connectionRead = usbManager.openDevice(connectDevice); - connectionRead.claimInterface(usbInterfaceRead, true); + // Claim the interface + usbDeviceConnection = usbManager.openDevice(connectDevice); + usbDeviceConnection.claimInterface(usbInterface, true); - if (UsingSingleInterface) - { - connectionWrite = connectionRead; - } - else // if (!UsingSingleInterface) - { - connectionWrite = usbManager.openDevice(connectDevice); - connectionWrite.claimInterface(usbInterfaceWrite, true); - } if (DEBUG) Log.d(TAG, "Opened endpoints"); // Create the USB requests readRequest = new UsbRequest(); - readRequest.initialize(connectionRead, usbEndpointRead); + readRequest.initialize(usbDeviceConnection, usbEndpointRead); writeRequest = new UsbRequest(); - writeRequest.initialize(connectionWrite, usbEndpointWrite); + writeRequest.initialize(usbDeviceConnection, usbEndpointWrite); inTalkStream = new TalkInputStream(); outTalkStream = new TalkOutputStream(); @@ -393,7 +346,7 @@ public class HidUAVTalk extends TelemetryTask { int dataSize; // wait for status event - if (connectionRead.requestWait() == readRequest) { + if (usbDeviceConnection.requestWait() == readRequest) { // Packet format: // 0: Report ID (1) // 1: Number of valid bytes @@ -435,7 +388,7 @@ public class HidUAVTalk extends TelemetryTask { writeRequest.queue(packet, bufferDataLength); try { - if (!writeRequest.equals(connectionWrite.requestWait())) + if (!writeRequest.equals(usbDeviceConnection.requestWait())) Log.e(TAG, "writeRequest failed"); } catch (Exception ex) From 02a1c9454502eb56cf7a37f25f3c05bc661f29fa Mon Sep 17 00:00:00 2001 From: James Cotton Date: Sun, 12 Aug 2012 14:00:50 -0500 Subject: [PATCH 04/24] HID: Since there is only one usb device connection queue read and write events onto that single connection and wait for either. --- .../androidgcs/telemetry/HidUAVTalk.java | 99 +++++++++++-------- 1 file changed, 56 insertions(+), 43 deletions(-) diff --git a/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java b/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java index 4f3c64a5a..5abb4dd09 100644 --- a/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java +++ b/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java @@ -28,7 +28,7 @@ import android.util.Log; public class HidUAVTalk extends TelemetryTask { private static final String TAG = HidUAVTalk.class.getSimpleName(); - public static final int LOGLEVEL = 2; + public static final int LOGLEVEL = 0; public static final boolean DEBUG = LOGLEVEL > 2; public static final boolean WARN = LOGLEVEL > 1; public static final boolean ERROR = LOGLEVEL > 0; @@ -61,6 +61,9 @@ public class HidUAVTalk extends TelemetryTask { private UsbRequest readRequest = null; private Thread readWriteThread; + private boolean readPending = false; + private boolean writePending = false; + public HidUAVTalk(OPTelemetryService service) { super(service); } @@ -307,8 +310,23 @@ public class HidUAVTalk extends TelemetryTask { readWriteThread = new Thread(new Runnable() { @Override public void run() { + + // Enqueue the first read + readData(); + while (!shutdown) { - readData(); + // If there are no request + + // Enqueue requests appropriately first + UsbRequest returned = usbDeviceConnection.requestWait(); + if (returned == readRequest) { + if (DEBUG) Log.d(TAG, "Received read request"); + readData(); + } else if (returned == writeRequest) { + if (DEBUG) Log.d(TAG, "Received write completed request"); + writePending = false; + } + sendData(); } } @@ -332,70 +350,65 @@ public class HidUAVTalk extends TelemetryTask { * Gets a report from HID, extract the meaningful data and push * it to the input stream */ - public int readData() { - int bufferDataLength = usbEndpointRead.getMaxPacketSize(); - ByteBuffer buffer = ByteBuffer.allocate(bufferDataLength + 1); + ByteBuffer readBuffer = ByteBuffer.allocate(MAX_HID_PACKET_SIZE); + private void queueRead() { + if(!readRequest.queue(readBuffer, MAX_HID_PACKET_SIZE)) { + if (ERROR) Log.e(TAG, "Failed to queue request"); + } else + readPending = true; + } + public void readData() { - // queue a request on the interrupt endpoint - if(!readRequest.queue(buffer, bufferDataLength)) { - if (DEBUG) Log.d(TAG, "Failed to queue request"); - return 0; - } - - if (DEBUG) Log.d(TAG, "Request queued"); - - int dataSize; - // wait for status event - if (usbDeviceConnection.requestWait() == readRequest) { - // Packet format: + if (!readPending) { + queueRead(); + } else { // We just received a read + readPending = false; + // Packet format: // 0: Report ID (1) // 1: Number of valid bytes // 2:63: Data - dataSize = buffer.get(1); // Data size + int dataSize = readBuffer.get(1); // Data size //Assert.assertEquals(1, buffer.get()); // Report ID //Assert.assertTrue(dataSize < buffer.capacity()); - if (buffer.get(0) != 1 || buffer.get(1) < 0 || buffer.get(1) > (buffer.capacity() - 2)) { - if (DEBUG) Log.d(TAG, "Badly formatted HID packet"); + if (readBuffer.get(0) != 1 || readBuffer.get(1) < 0 || readBuffer.get(1) > (readBuffer.capacity() - 2)) { + if (ERROR) Log.e(TAG, "Badly formatted HID packet"); } else { byte[] dst = new byte[dataSize]; - buffer.position(2); - buffer.get(dst, 0, dataSize); + readBuffer.position(2); + readBuffer.get(dst, 0, dataSize); if (DEBUG) Log.d(TAG, "Entered read"); inTalkStream.write(dst); if (DEBUG) Log.d(TAG, "Got read: " + dataSize + " bytes"); } - } else - return 0; - return dataSize; + // Queue another read + queueRead(); + } } /** * Send a packet if data is available */ public void sendData() { - ByteBuffer packet = null; - do { // Send all the data available to prevent sending backlog - packet = outTalkStream.getHIDpacket(); - if (packet != null) { - if (DEBUG) Log.d(TAG, "Writing to device()"); - int bufferDataLength = usbEndpointWrite.getMaxPacketSize(); - Assert.assertTrue(packet.capacity() <= bufferDataLength); + // Don't try and send data till previous request completes + if (writePending) + return; - writeRequest.queue(packet, bufferDataLength); - try - { - if (!writeRequest.equals(usbDeviceConnection.requestWait())) - Log.e(TAG, "writeRequest failed"); - } - catch (Exception ex) - { - } - } - } while (packet != null); + ByteBuffer packet = outTalkStream.getHIDpacket(); + if (packet != null) { + if (DEBUG) Log.d(TAG, "Writing to device()"); + + int bufferDataLength = usbEndpointWrite.getMaxPacketSize(); + Assert.assertTrue(packet.capacity() <= bufferDataLength); + + if(writeRequest.queue(packet, bufferDataLength)) + writePending = true; + else if (ERROR) + Log.e(TAG, "Write queuing failed"); + } } /*********** Helper classes for telemetry streams ************/ From ae56dc74c22fbe8df3ed5e64d1e78c4f80be110c Mon Sep 17 00:00:00 2001 From: James Cotton Date: Sun, 12 Aug 2012 14:12:35 -0500 Subject: [PATCH 05/24] Make sure the USB scheduling is thread safe. --- .../androidgcs/telemetry/HidUAVTalk.java | 110 +++++++++++------- 1 file changed, 65 insertions(+), 45 deletions(-) diff --git a/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java b/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java index 5abb4dd09..a074e79ca 100644 --- a/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java +++ b/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java @@ -312,7 +312,7 @@ public class HidUAVTalk extends TelemetryTask { public void run() { // Enqueue the first read - readData(); + queueRead(); while (!shutdown) { // If there are no request @@ -325,9 +325,8 @@ public class HidUAVTalk extends TelemetryTask { } else if (returned == writeRequest) { if (DEBUG) Log.d(TAG, "Received write completed request"); writePending = false; + sendData(); } - - sendData(); } } @@ -351,63 +350,79 @@ public class HidUAVTalk extends TelemetryTask { * it to the input stream */ ByteBuffer readBuffer = ByteBuffer.allocate(MAX_HID_PACKET_SIZE); - private void queueRead() { - if(!readRequest.queue(readBuffer, MAX_HID_PACKET_SIZE)) { - if (ERROR) Log.e(TAG, "Failed to queue request"); - } else - readPending = true; - } - public void readData() { - if (!readPending) { - queueRead(); - } else { // We just received a read + /** + * Schedules a USB read + */ + private void queueRead() { + synchronized(readRequest) { + if(!readRequest.queue(readBuffer, MAX_HID_PACKET_SIZE)) { + if (ERROR) Log.e(TAG, "Failed to queue request"); + } else + readPending = true; + } + } + + /** + * Reads data from the last USB transaction and schedules another read + */ + public void readData() { + synchronized(readRequest) { + + if (!readPending) { + if (ERROR) Log.e(TAG, "Tried to read read while a transaction was not pending"); + return; + } + + // We just received a read readPending = false; // Packet format: - // 0: Report ID (1) - // 1: Number of valid bytes - // 2:63: Data + // 0: Report ID (1) + // 1: Number of valid bytes + // 2:63: Data - int dataSize = readBuffer.get(1); // Data size - //Assert.assertEquals(1, buffer.get()); // Report ID - //Assert.assertTrue(dataSize < buffer.capacity()); + int dataSize = readBuffer.get(1); // Data size + //Assert.assertEquals(1, buffer.get()); // Report ID + //Assert.assertTrue(dataSize < buffer.capacity()); - if (readBuffer.get(0) != 1 || readBuffer.get(1) < 0 || readBuffer.get(1) > (readBuffer.capacity() - 2)) { - if (ERROR) Log.e(TAG, "Badly formatted HID packet"); - } else { - byte[] dst = new byte[dataSize]; - readBuffer.position(2); - readBuffer.get(dst, 0, dataSize); - if (DEBUG) Log.d(TAG, "Entered read"); - inTalkStream.write(dst); - if (DEBUG) Log.d(TAG, "Got read: " + dataSize + " bytes"); - } + if (readBuffer.get(0) != 1 || readBuffer.get(1) < 0 || readBuffer.get(1) > (readBuffer.capacity() - 2)) { + if (ERROR) Log.e(TAG, "Badly formatted HID packet"); + } else { + byte[] dst = new byte[dataSize]; + readBuffer.position(2); + readBuffer.get(dst, 0, dataSize); + if (DEBUG) Log.d(TAG, "Entered read"); + inTalkStream.write(dst); + if (DEBUG) Log.d(TAG, "Got read: " + dataSize + " bytes"); + } - // Queue another read - queueRead(); - } + // Queue another read + queueRead(); + + } } /** * Send a packet if data is available */ public void sendData() { + synchronized(writeRequest) { + // Don't try and send data till previous request completes + if (writePending) + return; - // Don't try and send data till previous request completes - if (writePending) - return; + ByteBuffer packet = outTalkStream.getHIDpacket(); + if (packet != null) { + if (DEBUG) Log.d(TAG, "Writing to device()"); - ByteBuffer packet = outTalkStream.getHIDpacket(); - if (packet != null) { - if (DEBUG) Log.d(TAG, "Writing to device()"); + int bufferDataLength = usbEndpointWrite.getMaxPacketSize(); + Assert.assertTrue(packet.capacity() <= bufferDataLength); - int bufferDataLength = usbEndpointWrite.getMaxPacketSize(); - Assert.assertTrue(packet.capacity() <= bufferDataLength); - - if(writeRequest.queue(packet, bufferDataLength)) - writePending = true; - else if (ERROR) - Log.e(TAG, "Write queuing failed"); + if(writeRequest.queue(packet, bufferDataLength)) + writePending = true; + else if (ERROR) + Log.e(TAG, "Write queuing failed"); + } } } @@ -446,6 +461,9 @@ public class HidUAVTalk extends TelemetryTask { data.put((byte) oneByte); data.notify(); } + + // If there is not a write request queued, add one + sendData(); } @Override @@ -457,6 +475,8 @@ public class HidUAVTalk extends TelemetryTask { data.put(b); data.notify(); } + + sendData(); } }; From d77912e4b080f1e7b9ad2e78246b18c107b6863f Mon Sep 17 00:00:00 2001 From: James Cotton Date: Sun, 12 Aug 2012 17:21:13 -0500 Subject: [PATCH 06/24] AndroidGCS Controller: Make sure to remove the callback on manual control settings before updating it. --- androidgcs/src/org/openpilot/androidgcs/Controller.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/androidgcs/src/org/openpilot/androidgcs/Controller.java b/androidgcs/src/org/openpilot/androidgcs/Controller.java index 3ef291d4d..0d3a3c911 100644 --- a/androidgcs/src/org/openpilot/androidgcs/Controller.java +++ b/androidgcs/src/org/openpilot/androidgcs/Controller.java @@ -78,11 +78,11 @@ public class Controller extends ObjectManagerActivity { public void update(Observable observable, Object data) { // Once we have updated settings we can active the GCS receiver mode Log.d(TAG,"Got update from settings"); - activateGcsReceiver(); UAVDataObject manualControlSettings = (UAVDataObject) objMngr.getObject("ManualControlSettings"); if(manualControlSettings != null) { manualControlSettings.removeUpdatedObserver(this); } + activateGcsReceiver(); } }; From ef581342accbc46d75246fd74ffd1f60d80de86c Mon Sep 17 00:00:00 2001 From: James Cotton Date: Sun, 12 Aug 2012 17:21:40 -0500 Subject: [PATCH 07/24] AndroidGCS: Add back a few synchronize blocks in telemetry to avoid removing elements twice. --- .../src/org/openpilot/uavtalk/Telemetry.java | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/androidgcs/src/org/openpilot/uavtalk/Telemetry.java b/androidgcs/src/org/openpilot/uavtalk/Telemetry.java index 21525ea14..f130c33eb 100644 --- a/androidgcs/src/org/openpilot/uavtalk/Telemetry.java +++ b/androidgcs/src/org/openpilot/uavtalk/Telemetry.java @@ -551,17 +551,22 @@ public class Telemetry { // Get object information from queue (first the priority and then the regular queue) ObjectQueueInfo objInfo; - if ( !objPriorityQueue.isEmpty() ) - { - objInfo = objPriorityQueue.remove(); - } - else if ( !objQueue.isEmpty() ) - { - objInfo = objQueue.remove(); - } - else - { - return; + synchronized (objPriorityQueue) { + if ( !objPriorityQueue.isEmpty() ) + { + objInfo = objPriorityQueue.remove(); + } else { + synchronized (objQueue) { + if ( !objQueue.isEmpty() ) + { + objInfo = objQueue.remove(); + } + else + { + return; + } + } + } } // Check if a connection has been established, only process GCSTelemetryStats updates From f012248fd11141170b361616a4377f9b81adfd13 Mon Sep 17 00:00:00 2001 From: James Cotton Date: Sun, 12 Aug 2012 14:44:39 -0500 Subject: [PATCH 08/24] Updated android objects from merge --- .../uavtalk/uavobjects/FirmwareIAPObj.java | 2 +- .../uavobjects/ManualControlSettings.java | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/androidgcs/src/org/openpilot/uavtalk/uavobjects/FirmwareIAPObj.java b/androidgcs/src/org/openpilot/uavtalk/uavobjects/FirmwareIAPObj.java index 48d211ae9..90a9e8893 100644 --- a/androidgcs/src/org/openpilot/uavtalk/uavobjects/FirmwareIAPObj.java +++ b/androidgcs/src/org/openpilot/uavtalk/uavobjects/FirmwareIAPObj.java @@ -157,7 +157,7 @@ public class FirmwareIAPObj extends UAVDataObject { UAVObject.Metadata.AccessModeNum(UAVObject.AccessMode.ACCESS_READWRITE) << UAVOBJ_GCS_ACCESS_SHIFT | 1 << UAVOBJ_TELEMETRY_ACKED_SHIFT | 1 << UAVOBJ_GCS_TELEMETRY_ACKED_SHIFT | - UAVObject.Metadata.UpdateModeNum(UAVObject.UpdateMode.UPDATEMODE_MANUAL) << UAVOBJ_TELEMETRY_UPDATE_MODE_SHIFT | + UAVObject.Metadata.UpdateModeNum(UAVObject.UpdateMode.UPDATEMODE_ONCHANGE) << UAVOBJ_TELEMETRY_UPDATE_MODE_SHIFT | UAVObject.Metadata.UpdateModeNum(UAVObject.UpdateMode.UPDATEMODE_MANUAL) << UAVOBJ_GCS_TELEMETRY_UPDATE_MODE_SHIFT; metadata.flightTelemetryUpdatePeriod = 0; metadata.gcsTelemetryUpdatePeriod = 0; diff --git a/androidgcs/src/org/openpilot/uavtalk/uavobjects/ManualControlSettings.java b/androidgcs/src/org/openpilot/uavtalk/uavobjects/ManualControlSettings.java index 0b47b3e10..e42662409 100644 --- a/androidgcs/src/org/openpilot/uavtalk/uavobjects/ManualControlSettings.java +++ b/androidgcs/src/org/openpilot/uavtalk/uavobjects/ManualControlSettings.java @@ -179,10 +179,17 @@ public class ManualControlSettings extends UAVDataObject { Stabilization3SettingsEnumOptions.add("VirtualBar"); fields.add( new UAVObjectField("Stabilization3Settings", "", UAVObjectField.FieldType.ENUM, Stabilization3SettingsElemNames, Stabilization3SettingsEnumOptions) ); + List FlightModeNumberElemNames = new ArrayList(); + FlightModeNumberElemNames.add("0"); + fields.add( new UAVObjectField("FlightModeNumber", "", UAVObjectField.FieldType.UINT8, FlightModeNumberElemNames, null) ); + List FlightModePositionElemNames = new ArrayList(); FlightModePositionElemNames.add("0"); FlightModePositionElemNames.add("1"); FlightModePositionElemNames.add("2"); + FlightModePositionElemNames.add("3"); + FlightModePositionElemNames.add("4"); + FlightModePositionElemNames.add("5"); List FlightModePositionEnumOptions = new ArrayList(); FlightModePositionEnumOptions.add("Manual"); FlightModePositionEnumOptions.add("Stabilized1"); @@ -196,10 +203,6 @@ public class ManualControlSettings extends UAVDataObject { FlightModePositionEnumOptions.add("Land"); fields.add( new UAVObjectField("FlightModePosition", "", UAVObjectField.FieldType.ENUM, FlightModePositionElemNames, FlightModePositionEnumOptions) ); - List FlightModeNumberElemNames = new ArrayList(); - FlightModeNumberElemNames.add("0"); - fields.add( new UAVObjectField("FlightModeNumber", "", UAVObjectField.FieldType.UINT8, FlightModeNumberElemNames, null) ); - List FailsafeBehaviorElemNames = new ArrayList(); FailsafeBehaviorElemNames.add("0"); List FailsafeBehaviorEnumOptions = new ArrayList(); @@ -308,10 +311,13 @@ public class ManualControlSettings extends UAVDataObject { getField("Stabilization3Settings").setValue("Attitude",0); getField("Stabilization3Settings").setValue("Attitude",1); getField("Stabilization3Settings").setValue("Rate",2); + getField("FlightModeNumber").setValue(3); getField("FlightModePosition").setValue("Manual",0); getField("FlightModePosition").setValue("Stabilized1",1); getField("FlightModePosition").setValue("Stabilized2",2); - getField("FlightModeNumber").setValue(3); + getField("FlightModePosition").setValue("Stabilized3",3); + getField("FlightModePosition").setValue("Stabilized1",4); + getField("FlightModePosition").setValue("Stabilized2",5); getField("FailsafeBehavior").setValue("None"); } @@ -341,7 +347,7 @@ public class ManualControlSettings extends UAVDataObject { } // Constants - protected static final long OBJID = 0x6C188320l; + protected static final long OBJID = 0xBA39E41Al; protected static final String NAME = "ManualControlSettings"; protected static String DESCRIPTION = "Settings to indicate how to decode receiver input by @ref ManualControlModule."; protected static final boolean ISSINGLEINST = 1 > 0; From daab45d14d39f5bea82d3b06b4c48eb4a85b680a Mon Sep 17 00:00:00 2001 From: James Cotton Date: Mon, 13 Aug 2012 01:07:09 -0500 Subject: [PATCH 09/24] AndroidGCS HID: Go back to a read and write thread but now use synchronous bultTransfer for write which gets rid of the segfaults with running two asynchronous transfers. --- .../androidgcs/telemetry/HidUAVTalk.java | 98 ++++++++++++------- 1 file changed, 64 insertions(+), 34 deletions(-) diff --git a/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java b/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java index a074e79ca..9cb2d8be7 100644 --- a/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java +++ b/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java @@ -28,7 +28,7 @@ import android.util.Log; public class HidUAVTalk extends TelemetryTask { private static final String TAG = HidUAVTalk.class.getSimpleName(); - public static final int LOGLEVEL = 0; + public static final int LOGLEVEL = 1; public static final boolean DEBUG = LOGLEVEL > 2; public static final boolean WARN = LOGLEVEL > 1; public static final boolean ERROR = LOGLEVEL > 0; @@ -57,9 +57,10 @@ public class HidUAVTalk extends TelemetryTask { private UsbInterface usbInterface = null; private TalkInputStream inTalkStream; private TalkOutputStream outTalkStream; - private UsbRequest writeRequest = null; + private final UsbRequest writeRequest = null; private UsbRequest readRequest = null; - private Thread readWriteThread; + private Thread readThread; + private Thread writeThread; private boolean readPending = false; private boolean writePending = false; @@ -78,9 +79,15 @@ public class HidUAVTalk extends TelemetryTask { super.disconnect(); try { - if(readWriteThread != null) { - readWriteThread.join(); - readWriteThread = null; + if(readThread != null) { + readThread.interrupt(); // Make sure not blocking for data + readThread.join(); + readThread = null; + } + if(writeThread != null) { + writeThread.interrupt(); + writeThread.join(); + writeThread = null; } } catch (InterruptedException e) { e.printStackTrace(); @@ -91,13 +98,6 @@ public class HidUAVTalk extends TelemetryTask { readRequest.close(); readRequest = null; } - - if (writeRequest != null) { - writeRequest.cancel(); - writeRequest.close(); - writeRequest = null; - } - } @Override @@ -293,9 +293,6 @@ public class HidUAVTalk extends TelemetryTask { readRequest = new UsbRequest(); readRequest.initialize(usbDeviceConnection, usbEndpointRead); - writeRequest = new UsbRequest(); - writeRequest.initialize(usbDeviceConnection, usbEndpointWrite); - inTalkStream = new TalkInputStream(); outTalkStream = new TalkOutputStream(); inStream = inTalkStream; @@ -307,31 +304,39 @@ public class HidUAVTalk extends TelemetryTask { } }); - readWriteThread = new Thread(new Runnable() { + readThread = new Thread(new Runnable() { @Override public void run() { - // Enqueue the first read queueRead(); - while (!shutdown) { - // If there are no request - - // Enqueue requests appropriately first UsbRequest returned = usbDeviceConnection.requestWait(); if (returned == readRequest) { if (DEBUG) Log.d(TAG, "Received read request"); readData(); - } else if (returned == writeRequest) { - if (DEBUG) Log.d(TAG, "Received write completed request"); - writePending = false; - sendData(); - } + } else + Log.e(TAG, "Received unknown USB response"); } } - }, "HID Read Write"); - readWriteThread.start(); + }, "HID Read"); + readThread.start(); + + writeThread = new Thread(new Runnable() { + @Override + public void run() { + if (DEBUG) Log.d(TAG, "Starting HID write thread"); + while(!shutdown) { + try { + sendDataSynchronous(); + } catch (InterruptedException e) { + break; + } + } + if (DEBUG) Log.d(TAG, "Ending HID write thread"); + } + }, "HID Write"); + writeThread.start(); return true; } @@ -402,6 +407,7 @@ public class HidUAVTalk extends TelemetryTask { } } + /** * Send a packet if data is available */ @@ -426,6 +432,21 @@ public class HidUAVTalk extends TelemetryTask { } } + /** + * Send a packet if data is available + * @throws InterruptedException + */ + public void sendDataSynchronous() throws InterruptedException { + + ByteBuffer packet = outTalkStream.getHIDpacketBlocking(); + if (packet != null) { + if (DEBUG) Log.d(TAG, "sendDataSynchronous() Writing to device()"); + + if (usbDeviceConnection.bulkTransfer(usbEndpointWrite, packet.array(), MAX_HID_PACKET_SIZE, 1000) < 0) + Log.e(TAG, "Failed to perform bult write"); + } + } + /*********** Helper classes for telemetry streams ************/ class TalkOutputStream extends OutputStream { @@ -433,6 +454,20 @@ public class HidUAVTalk extends TelemetryTask { // and ByteFifo.put(byte []) ByteFifo data = new ByteFifo(); + /** + * Blocks until data is available and then returns a properly formatted HID packet + */ + public ByteBuffer getHIDpacketBlocking() throws InterruptedException { + synchronized(data) { + if (data.remaining() == 0) + data.wait(); + return getHIDpacket(); + } + } + + /** + * Gets data from the ByteFifo in a properly formatted HID packet + */ public ByteBuffer getHIDpacket() { ByteBuffer packet = null; synchronized(data) { @@ -461,9 +496,6 @@ public class HidUAVTalk extends TelemetryTask { data.put((byte) oneByte); data.notify(); } - - // If there is not a write request queued, add one - sendData(); } @Override @@ -475,8 +507,6 @@ public class HidUAVTalk extends TelemetryTask { data.put(b); data.notify(); } - - sendData(); } }; From 3ea9ecd53b3864c25949079c52242e6daf918a58 Mon Sep 17 00:00:00 2001 From: James Cotton Date: Mon, 13 Aug 2012 01:25:08 -0500 Subject: [PATCH 10/24] AndroidGCS HID: Use the dettached message to shut down HID telemetry properly --- .../androidgcs/telemetry/HidUAVTalk.java | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java b/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java index 9cb2d8be7..c1c848a0a 100644 --- a/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java +++ b/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java @@ -64,6 +64,7 @@ public class HidUAVTalk extends TelemetryTask { private boolean readPending = false; private boolean writePending = false; + private IntentFilter deviceAttachedFilter; public HidUAVTalk(OPTelemetryService service) { super(service); @@ -73,7 +74,7 @@ public class HidUAVTalk extends TelemetryTask { public void disconnect() { CleanUpAndClose(); - //hostDisplayActivity.unregisterReceiver(usbReceiver); + telemService.unregisterReceiver(usbReceiver); telemService.unregisterReceiver(usbPermissionReceiver); super.disconnect(); @@ -110,6 +111,11 @@ public class HidUAVTalk extends TelemetryTask { permissionFilter = new IntentFilter(ACTION_USB_PERMISSION); telemService.registerReceiver(usbPermissionReceiver, permissionFilter); + deviceAttachedFilter = new IntentFilter(); + //deviceAttachedFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); + deviceAttachedFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); + telemService.registerReceiver(usbReceiver, deviceAttachedFilter); + // Go through all the devices plugged in HashMap deviceList = usbManager.getDeviceList(); if (DEBUG) Log.d(TAG, "Found " + deviceList.size() + " devices"); @@ -176,7 +182,6 @@ public class HidUAVTalk extends TelemetryTask { } }; - /* TODO: Detect dettached events and close the connection private final BroadcastReceiver usbReceiver = new BroadcastReceiver() { @Override @@ -195,8 +200,10 @@ public class HidUAVTalk extends TelemetryTask { { if (device.equals(currentDevice)) { + if (DEBUG) Log.d(TAG, "Matching device disconnected"); + // call your method that cleans up and closes communication with the device - CleanUpAndClose(); + disconnect(); } } } @@ -213,7 +220,7 @@ public class HidUAVTalk extends TelemetryTask { } } } - }; */ + }; protected void CleanUpAndClose() { @@ -314,8 +321,10 @@ public class HidUAVTalk extends TelemetryTask { if (returned == readRequest) { if (DEBUG) Log.d(TAG, "Received read request"); readData(); - } else + } else { Log.e(TAG, "Received unknown USB response"); + break; + } } } @@ -328,7 +337,8 @@ public class HidUAVTalk extends TelemetryTask { if (DEBUG) Log.d(TAG, "Starting HID write thread"); while(!shutdown) { try { - sendDataSynchronous(); + if (sendDataSynchronous() == false) + break; } catch (InterruptedException e) { break; } @@ -436,15 +446,18 @@ public class HidUAVTalk extends TelemetryTask { * Send a packet if data is available * @throws InterruptedException */ - public void sendDataSynchronous() throws InterruptedException { + public boolean sendDataSynchronous() throws InterruptedException { ByteBuffer packet = outTalkStream.getHIDpacketBlocking(); if (packet != null) { if (DEBUG) Log.d(TAG, "sendDataSynchronous() Writing to device()"); - if (usbDeviceConnection.bulkTransfer(usbEndpointWrite, packet.array(), MAX_HID_PACKET_SIZE, 1000) < 0) - Log.e(TAG, "Failed to perform bult write"); + if (usbDeviceConnection.bulkTransfer(usbEndpointWrite, packet.array(), MAX_HID_PACKET_SIZE, 1000) < 0) { + Log.e(TAG, "Failed to perform bulk write"); + return false; + } } + return true; } /*********** Helper classes for telemetry streams ************/ From aa9894c4817f64423ea243668c3f3d224055b6c4 Mon Sep 17 00:00:00 2001 From: James Cotton Date: Mon, 13 Aug 2012 01:37:15 -0500 Subject: [PATCH 11/24] AndroidGCS: Add a toast message when connected (all objects downloaded) --- .../openpilot/androidgcs/telemetry/HidUAVTalk.java | 6 ++++-- .../androidgcs/telemetry/TelemetryTask.java | 2 +- .../src/org/openpilot/uavtalk/TelemetryMonitor.java | 12 +++++++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java b/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java index c1c848a0a..ab170ff48 100644 --- a/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java +++ b/androidgcs/src/org/openpilot/androidgcs/telemetry/HidUAVTalk.java @@ -112,7 +112,7 @@ public class HidUAVTalk extends TelemetryTask { telemService.registerReceiver(usbPermissionReceiver, permissionFilter); deviceAttachedFilter = new IntentFilter(); - //deviceAttachedFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); + deviceAttachedFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); deviceAttachedFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); telemService.registerReceiver(usbReceiver, deviceAttachedFilter); @@ -200,8 +200,8 @@ public class HidUAVTalk extends TelemetryTask { { if (device.equals(currentDevice)) { + telemService.toastMessage("Device unplugged while in use"); if (DEBUG) Log.d(TAG, "Matching device disconnected"); - // call your method that cleans up and closes communication with the device disconnect(); } @@ -348,6 +348,8 @@ public class HidUAVTalk extends TelemetryTask { }, "HID Write"); writeThread.start(); + telemService.toastMessage("HID Device Opened"); + return true; } diff --git a/androidgcs/src/org/openpilot/androidgcs/telemetry/TelemetryTask.java b/androidgcs/src/org/openpilot/androidgcs/telemetry/TelemetryTask.java index 87196bc1e..f09cc7236 100644 --- a/androidgcs/src/org/openpilot/androidgcs/telemetry/TelemetryTask.java +++ b/androidgcs/src/org/openpilot/androidgcs/telemetry/TelemetryTask.java @@ -106,7 +106,7 @@ public abstract class TelemetryTask implements Runnable { // data stream uavTalk = new UAVTalk(inStream, outStream, objMngr); tel = new Telemetry(uavTalk, objMngr); - mon = new TelemetryMonitor(objMngr,tel); + mon = new TelemetryMonitor(objMngr,tel, telemService); // Create an observer to notify system of connection mon.addObserver(connectionObserver); diff --git a/androidgcs/src/org/openpilot/uavtalk/TelemetryMonitor.java b/androidgcs/src/org/openpilot/uavtalk/TelemetryMonitor.java index a1e341197..121a5ebcc 100644 --- a/androidgcs/src/org/openpilot/uavtalk/TelemetryMonitor.java +++ b/androidgcs/src/org/openpilot/uavtalk/TelemetryMonitor.java @@ -34,6 +34,8 @@ import java.util.Observer; import java.util.Timer; import java.util.TimerTask; +import org.openpilot.androidgcs.telemetry.OPTelemetryService; + import android.util.Log; public class TelemetryMonitor extends Observable { @@ -60,6 +62,7 @@ public class TelemetryMonitor extends Observable { private long lastUpdateTime; private final List queue; + private OPTelemetryService telemService; private boolean connected = false; private boolean objects_updated = false; @@ -71,6 +74,11 @@ public class TelemetryMonitor extends Observable { return objects_updated; }; + public TelemetryMonitor(UAVObjectManager objMngr, Telemetry tel, OPTelemetryService s) { + this(objMngr, tel); + telemService = s; + } + public TelemetryMonitor(UAVObjectManager objMngr, Telemetry tel) { this.objMngr = objMngr; this.tel = tel; @@ -171,7 +179,9 @@ public class TelemetryMonitor extends Observable { public synchronized void retrieveNextObject() throws IOException { // If queue is empty return if (queue.isEmpty()) { - if (DEBUG || true) Log.d(TAG, "All objects retrieved: Connected Successfully"); + if (telemService != null) + telemService.toastMessage("Connected"); + if (DEBUG) Log.d(TAG, "All objects retrieved: Connected Successfully"); objects_updated = true; if (!HANDSHAKE_IS_CONNECTED) { setChanged(); From 2f6595295231c4b432315e068669420924de10b2 Mon Sep 17 00:00:00 2001 From: James Cotton Date: Mon, 13 Aug 2012 02:00:54 -0500 Subject: [PATCH 12/24] AndroidGCS HID: Remove more locks to try and prevent HID deadlocking --- .../src/org/openpilot/uavtalk/Telemetry.java | 72 ++++++++++--------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/androidgcs/src/org/openpilot/uavtalk/Telemetry.java b/androidgcs/src/org/openpilot/uavtalk/Telemetry.java index f130c33eb..8e6161074 100644 --- a/androidgcs/src/org/openpilot/uavtalk/Telemetry.java +++ b/androidgcs/src/org/openpilot/uavtalk/Telemetry.java @@ -343,7 +343,7 @@ public class Telemetry { /** * Update an object based on its metadata properties */ - private synchronized void updateObject(UAVObject obj) + private void updateObject(UAVObject obj) { // Get metadata UAVObject.Metadata metadata = obj.getMetadata(); @@ -393,7 +393,7 @@ public class Telemetry { * Called when a transaction is successfully completed (uavtalk event) * @throws IOException */ - private synchronized void transactionCompleted(UAVObject obj, boolean result) throws IOException + private void transactionCompleted(UAVObject obj, boolean result) throws IOException { if (DEBUG) Log.d(TAG,"UAVTalk transactionCompleted"); // Check if there is a pending transaction and the objects match @@ -401,8 +401,11 @@ public class Telemetry { { if (DEBUG) Log.d(TAG,"Telemetry: transaction completed for " + obj.getName()); // Complete transaction - transTimer.cancel(); - transPending = false; + + synchronized(transTimer) { + transTimer.cancel(); + transPending = false; + } //Send signal obj.transactionCompleted(result); @@ -419,39 +422,41 @@ public class Telemetry { * Called when a transaction is not completed within the timeout period (timer event) * @throws IOException */ - private synchronized void transactionTimeout() throws IOException + private void transactionTimeout() throws IOException { if (DEBUG) Log.d(TAG,"Telemetry: transaction timeout."); - transTimer.cancel(); - // Proceed only if there is a pending transaction - if ( transPending ) - { - // Check if more retries are pending - if (transInfo.retriesRemaining > 0) - { - --transInfo.retriesRemaining; - processObjectTransaction(); - ++txRetries; - } - else - { - if (ERROR) Log.e(TAG, "Transaction failed for: " + transInfo.obj.getName()); + synchronized(transTimer) { + transTimer.cancel(); + // Proceed only if there is a pending transaction + if ( transPending ) + { + // Check if more retries are pending + if (transInfo.retriesRemaining > 0) + { + --transInfo.retriesRemaining; + processObjectTransaction(); + ++txRetries; + } + else + { + if (ERROR) Log.e(TAG, "Transaction failed for: " + transInfo.obj.getName()); - // Terminate transaction. This triggers UAVTalk to send a transaction - // failed signal which will make the next queue entry be processed - // Note this is UAVTalk listener TransactionFailed function and not the - // object specific transaction failed. - utalk.cancelPendingTransaction(transInfo.obj); - ++txErrors; - } - } + // Terminate transaction. This triggers UAVTalk to send a transaction + // failed signal which will make the next queue entry be processed + // Note this is UAVTalk listener TransactionFailed function and not the + // object specific transaction failed. + utalk.cancelPendingTransaction(transInfo.obj); + ++txErrors; + } + } + } } /** * Start an object transaction with UAVTalk, all information is stored in transInfo * @throws IOException */ - private synchronized void processObjectTransaction() throws IOException + private void processObjectTransaction() throws IOException { if (transPending) { @@ -472,8 +477,10 @@ public class Telemetry { } else { - transTimer.cancel(); - transPending = false; + synchronized(transTimer) { + transTimer.cancel(); + transPending = false; + } } } else { @@ -628,11 +635,12 @@ public class Telemetry { * TODO: Clean-up * @throws IOException */ - private synchronized void processPeriodicUpdates() throws IOException + private void processPeriodicUpdates() throws IOException { if (DEBUG) Log.d(TAG, "processPeriodicUpdates()"); // Stop timer + updateTimer.cancel(); // Iterate through each object and update its timer, if zero then transmit object. @@ -705,7 +713,7 @@ public class Telemetry { return stats; } - public synchronized void resetStats() + public void resetStats() { utalk.resetStats(); txErrors = 0; From 2d7bb4d3bbbdcb1b8b0ee2aa5dc9aef6d2ba0e73 Mon Sep 17 00:00:00 2001 From: James Cotton Date: Mon, 13 Aug 2012 15:02:15 -0500 Subject: [PATCH 13/24] AndroidGCS: Start moving the telemetry object queue to a handler and a looper --- .../telemetry/OPTelemetryService.java | 15 +- .../androidgcs/telemetry/TelemetryTask.java | 2 +- .../src/org/openpilot/uavtalk/Telemetry.java | 238 ++++++++++++------ .../uavtalk/TelemetryMonitorTest.java | 20 +- 4 files changed, 176 insertions(+), 99 deletions(-) diff --git a/androidgcs/src/org/openpilot/androidgcs/telemetry/OPTelemetryService.java b/androidgcs/src/org/openpilot/androidgcs/telemetry/OPTelemetryService.java index 4eeb57abe..4698900ef 100644 --- a/androidgcs/src/org/openpilot/androidgcs/telemetry/OPTelemetryService.java +++ b/androidgcs/src/org/openpilot/androidgcs/telemetry/OPTelemetryService.java @@ -26,16 +26,10 @@ */ package org.openpilot.androidgcs.telemetry; -import java.io.IOException; import java.lang.ref.WeakReference; -import java.util.Observable; -import java.util.Observer; -import org.openpilot.uavtalk.Telemetry; -import org.openpilot.uavtalk.TelemetryMonitor; import org.openpilot.uavtalk.UAVDataObject; import org.openpilot.uavtalk.UAVObjectManager; -import org.openpilot.uavtalk.UAVTalk; import org.openpilot.uavtalk.uavobjects.UAVObjectsInitialize; import android.app.Service; @@ -127,7 +121,7 @@ public class OPTelemetryService extends Service { break; case 2: Toast.makeText(getApplicationContext(), "Attempting BT connection", Toast.LENGTH_SHORT).show(); - activeTelem = new BTTelemetryThread(); + //activeTelem = new BTTelemetryThread(); break; case 3: Toast.makeText(getApplicationContext(), "Attempting TCP connection", Toast.LENGTH_SHORT).show(); @@ -347,6 +341,8 @@ public class OPTelemetryService extends Service { } } } + + /* private class BTTelemetryThread extends Thread implements TelemTask { private final UAVObjectManager objMngr; @@ -401,7 +397,7 @@ public class OPTelemetryService extends Service { @Override public void update(Observable arg0, Object arg1) { if (DEBUG) Log.d(TAG, "Mon updated. Connected: " + mon.getConnected() + " objects updated: " + mon.getObjectsUpdated()); - if(mon.getConnected() /*&& mon.getObjectsUpdated()*/) { + if(mon.getConnected() ) { Intent intent = new Intent(); intent.setAction(INTENT_ACTION_CONNECTED); sendBroadcast(intent,null); @@ -423,6 +419,5 @@ public class OPTelemetryService extends Service { } if (DEBUG) Log.d(TAG, "UAVTalk stream disconnected"); } - - }; + };*/ } diff --git a/androidgcs/src/org/openpilot/androidgcs/telemetry/TelemetryTask.java b/androidgcs/src/org/openpilot/androidgcs/telemetry/TelemetryTask.java index f09cc7236..3d16d94f9 100644 --- a/androidgcs/src/org/openpilot/androidgcs/telemetry/TelemetryTask.java +++ b/androidgcs/src/org/openpilot/androidgcs/telemetry/TelemetryTask.java @@ -105,7 +105,7 @@ public abstract class TelemetryTask implements Runnable { // Create the required telemetry objects attached to this // data stream uavTalk = new UAVTalk(inStream, outStream, objMngr); - tel = new Telemetry(uavTalk, objMngr); + tel = new Telemetry(uavTalk, objMngr, Looper.myLooper()); mon = new TelemetryMonitor(objMngr,tel, telemService); // Create an observer to notify system of connection diff --git a/androidgcs/src/org/openpilot/uavtalk/Telemetry.java b/androidgcs/src/org/openpilot/uavtalk/Telemetry.java index 8e6161074..ac814e76b 100644 --- a/androidgcs/src/org/openpilot/uavtalk/Telemetry.java +++ b/androidgcs/src/org/openpilot/uavtalk/Telemetry.java @@ -35,9 +35,17 @@ import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentLinkedQueue; +import junit.framework.Assert; +import android.os.Handler; +import android.os.Looper; import android.util.Log; public class Telemetry { + /** + * Telemetry provides a messaging handler to handle all the object updates and transfer + * requests. This handler can either be attached to a new loop attached to the thread + * started by the telemetry service. + */ private final String TAG = "Telemetry"; public static int LOGLEVEL = 1; @@ -98,11 +106,14 @@ public class Telemetry { /** * Constructor */ - public Telemetry(UAVTalk utalkIn, UAVObjectManager objMngr) + public Telemetry(UAVTalk utalkIn, UAVObjectManager objMngr, Looper l) { this.utalk = utalkIn; this.objMngr = objMngr; + // Create a handler for object messages + handler = new ObjectUpdateHandler(l); + // Process all objects in the list List< List > objs = objMngr.getObjects(); ListIterator> li = objs.listIterator(); @@ -265,48 +276,28 @@ public class Telemetry { final Observer unpackedObserver = new Observer() { @Override public void update(Observable observable, Object data) { - try { - enqueueObjectUpdates((UAVObject) data, EV_UNPACKED, false, true); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } + handler.unpacked((UAVObject) data); } }; final Observer updatedAutoObserver = new Observer() { @Override public void update(Observable observable, Object data) { - try { - enqueueObjectUpdates((UAVObject) data, EV_UPDATED, false, true); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } + handler.updatedAuto((UAVObject) data); } }; final Observer updatedManualObserver = new Observer() { @Override public void update(Observable observable, Object data) { - try { - enqueueObjectUpdates((UAVObject) data, EV_UPDATED_MANUAL, false, true); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } + handler.updatedManual((UAVObject) data); } }; final Observer updatedRequestedObserver = new Observer() { @Override public void update(Observable observable, Object data) { - try { - enqueueObjectUpdates((UAVObject) data, EV_UPDATE_REQ, false, true); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } + handler.updateRequested((UAVObject) data); } }; @@ -488,59 +479,6 @@ public class Telemetry { } } - /** - * Enqueue the event received from an object. This is the main method that handles all the callbacks - * from UAVObjects (due to updates, or update requests) - */ - private void enqueueObjectUpdates(UAVObject obj, int event, boolean allInstances, boolean priority) throws IOException - { - // Push event into queue - if (DEBUG) Log.d(TAG, "Push event into queue for obj " + obj.getName() + " event " + event); - if(event == 8 && obj.getName().compareTo("GCSTelemetryStats") == 0) - Thread.dumpStack(); - ObjectQueueInfo objInfo = new ObjectQueueInfo(); - objInfo.obj = obj; - objInfo.event = event; - objInfo.allInstances = allInstances; - if (priority) - { - // Only enqueue if an identical transaction does not already exist - if(!objPriorityQueue.contains(objInfo)) { - if ( objPriorityQueue.size() < MAX_QUEUE_SIZE ) - { - objPriorityQueue.add(objInfo); - } - else - { - ++txErrors; - obj.transactionCompleted(false); - Log.w(TAG,"Telemetry: priority event queue is full, event lost " + obj.getName()); - } - } - } - else - { - // Only enqueue if an identical transaction does not already exist - if(!objQueue.contains(objInfo)) { - if ( objQueue.size() < MAX_QUEUE_SIZE ) - { - objQueue.add(objInfo); - } - else - { - ++txErrors; - obj.transactionCompleted(false); - } - } - } - - // If there is no transaction in progress then process event - if (!transPending) - { - processObjectQueue(); - } - } - /** * Process events from the object queue * @throws IOException @@ -666,7 +604,8 @@ public class Telemetry { objinfo.timeToNextUpdateMs = objinfo.updatePeriodMs - offset; // Send object startTime = System.currentTimeMillis(); - enqueueObjectUpdates(objinfo.obj, EV_UPDATED_MANUAL, true, false); + handler.updatedManual(objinfo.obj); + //enqueueObjectUpdates(objinfo.obj, EV_UPDATED_MANUAL, true, false); elapsedMs = (int) (System.currentTimeMillis() - startTime); // Update timeToNextUpdateMs with the elapsed delay of sending the object; timeToNextUpdateMs += elapsedMs; @@ -780,6 +719,147 @@ public class Telemetry { private static final int MIN_UPDATE_PERIOD_MS = 1; private static final int MAX_QUEUE_SIZE = 20; + private final ObjectUpdateHandler handler; + public class ObjectUpdateHandler extends Handler { + //! This can only be created while attaching to a particular looper + ObjectUpdateHandler(Looper l) { + super(l); + } + + //! Generic enqueue + void enqueueObjectUpdates(UAVObject obj, int event, boolean allInstances, boolean priority) { + + if (DEBUG) Log.d(TAG, "Enqueing update " + obj.getName() + " event " + event); + + ObjectQueueInfo objInfo = new ObjectQueueInfo(); + objInfo.obj = obj; + objInfo.event = event; + objInfo.allInstances = allInstances; + + post(new ObjectRunnable(objInfo)); + } + + //! Enqueue an unpacked event + void unpacked(UAVObject obj) { + enqueueObjectUpdates(obj, EV_UNPACKED, false, true); + } + + //! Enqueue an updated auto event + void updatedAuto(UAVObject obj) { + enqueueObjectUpdates(obj,EV_UPDATED, false, true); + } + + //! Enqueue an updated manual event + void updatedManual(UAVObject obj) { + enqueueObjectUpdates(obj, EV_UPDATE_REQ, false, true); + } + + //! Enqueue an update requested event + void updateRequested(UAVObject obj) { + enqueueObjectUpdates(obj, EV_UPDATE_REQ, false, true); + } + + } + + class ObjectRunnable implements Runnable { + + //! Transaction information to perform + private final ObjectQueueInfo objInfo; +// private final ObjectTransactionInfo transInfo = new ObjectTransactionInfo(); + + ObjectRunnable(ObjectQueueInfo info) { + Assert.assertNotNull(info); + objInfo = info; + } + + //! Perform the transaction on the looper thread + @Override + public void run () { + Log.d(TAG,"object transaction running"); + // 1. Check GCS is connected, throw this out if not + // 2. Set up a transaction which includes multiple retries, whether to wait for ack etc + // 3. Send UAVTalk message + // 4. Based on transaction type either wait for update or end + + // 1. Check if a connection has been established, only process GCSTelemetryStats updates + // (used to establish the connection) + gcsStatsObj = objMngr.getObject("GCSTelemetryStats"); + if ( ((String) gcsStatsObj.getField("Status").getValue()).compareTo("Connected") != 0 ) + { + if ( objInfo.obj.getObjID() != objMngr.getObject("GCSTelemetryStats").getObjID() ) + { + if (DEBUG) Log.d(TAG,"transactionCompleted(false) due to receiving object not GCSTelemetryStats while not connected."); + objInfo.obj.transactionCompleted(false); + return; + } + } + + Log.e(TAG, "A"); + // 2. Setup transaction (skip if unpack event) + if ( objInfo.event != EV_UNPACKED ) + { + Log.e(TAG, "A1"); + UAVObject.Metadata metadata = objInfo.obj.getMetadata(); + transInfo.obj = objInfo.obj; + transInfo.allInstances = objInfo.allInstances; + transInfo.retriesRemaining = MAX_RETRIES; + transInfo.acked = metadata.GetGcsTelemetryAcked(); + if ( objInfo.event == EV_UPDATED || objInfo.event == EV_UPDATED_MANUAL ) + { + transInfo.objRequest = false; + } + else if ( objInfo.event == EV_UPDATE_REQ ) + { + transInfo.objRequest = true; + } + // Start transaction + transPending = true; + } + Log.e(TAG, "B"); + // If this is a metaobject then make necessary telemetry updates (this is why we catch unpack) + if (objInfo.obj.isMetadata()) + { + UAVMetaObject metaobj = (UAVMetaObject) objInfo.obj; + updateObject( metaobj.getParentObject() ); + } + Log.e(TAG, "C"); + // 3. Execute transaction + if (transPending) + { + Log.e(TAG, "D"); + try { + if (DEBUG || true) Log.d(TAG, "Process Object transaction for " + transInfo.obj.getName()); + // Initiate transaction + if (transInfo.objRequest) + { + utalk.sendObjectRequest(transInfo.obj, transInfo.allInstances); + } + else + { + Log.d(TAG, "Sending object"); + utalk.sendObject(transInfo.obj, transInfo.acked, transInfo.allInstances); + } + + // TODO: Block if request expected (??) + if ( transInfo.objRequest || transInfo.acked ) + { + transTimerSetPeriod(REQ_TIMEOUT_MS); + } + else + { + synchronized(transTimer) { + transTimer.cancel(); + transPending = false; + } + } + } catch (IOException e) { + // TODO Auto-generated catch block + Log.e(TAG, "E"); + e.printStackTrace(); + } + } + } + } } diff --git a/androidgcs/tests/org/openpilot/uavtalk/TelemetryMonitorTest.java b/androidgcs/tests/org/openpilot/uavtalk/TelemetryMonitorTest.java index 0b0cc1d73..6ddcfe03e 100644 --- a/androidgcs/tests/org/openpilot/uavtalk/TelemetryMonitorTest.java +++ b/androidgcs/tests/org/openpilot/uavtalk/TelemetryMonitorTest.java @@ -1,6 +1,6 @@ package org.openpilot.uavtalk; -import static org.junit.Assert.*; +import static org.junit.Assert.fail; import java.io.IOException; import java.net.InetAddress; @@ -8,11 +8,12 @@ import java.net.Socket; import org.junit.Test; import org.openpilot.uavtalk.uavobjects.UAVObjectsInitialize; -import org.openpilot.uavtalk.UAVTalk; + +import android.os.Looper; public class TelemetryMonitorTest { - + static UAVObjectManager objMngr; static UAVTalk talk; static final String IP_ADDRDESS = new String("127.0.0.1"); @@ -32,7 +33,7 @@ public class TelemetryMonitorTest { e.printStackTrace(); fail("Couldn't connect to test platform"); } - + try { talk = new UAVTalk(connection.getInputStream(), connection.getOutputStream(), objMngr); } catch (IOException e) { @@ -40,16 +41,17 @@ public class TelemetryMonitorTest { e.printStackTrace(); fail("Couldn't construct UAVTalk object"); } - + Thread inputStream = talk.getInputProcessThread(); inputStream.start(); - - Telemetry tel = new Telemetry(talk, objMngr); + + Looper.prepare(); + Telemetry tel = new Telemetry(talk, objMngr, Looper.myLooper()); @SuppressWarnings("unused") TelemetryMonitor mon = new TelemetryMonitor(objMngr,tel); - + Thread.sleep(10000); - + System.out.println("Done"); } From 653702ac23ffaead2f894a43725c60f990392ece Mon Sep 17 00:00:00 2001 From: James Cotton Date: Mon, 13 Aug 2012 23:47:37 -0500 Subject: [PATCH 14/24] AndroidGCS Telemetry: Finish moving telemetry into a runnable. --- .../src/org/openpilot/uavtalk/Telemetry.java | 1357 +++++++++-------- 1 file changed, 679 insertions(+), 678 deletions(-) diff --git a/androidgcs/src/org/openpilot/uavtalk/Telemetry.java b/androidgcs/src/org/openpilot/uavtalk/Telemetry.java index ac814e76b..3e4007777 100644 --- a/androidgcs/src/org/openpilot/uavtalk/Telemetry.java +++ b/androidgcs/src/org/openpilot/uavtalk/Telemetry.java @@ -42,147 +42,158 @@ import android.util.Log; public class Telemetry { /** - * Telemetry provides a messaging handler to handle all the object updates and transfer - * requests. This handler can either be attached to a new loop attached to the thread - * started by the telemetry service. + * Telemetry provides a messaging handler to handle all the object updates + * and transfer requests. This handler can either be attached to a new loop + * attached to the thread started by the telemetry service. */ private final String TAG = "Telemetry"; - public static int LOGLEVEL = 1; + public static int LOGLEVEL = 0; public static boolean WARN = LOGLEVEL > 2; public static boolean DEBUG = LOGLEVEL > 1; public static boolean ERROR = LOGLEVEL > 0; - public class TelemetryStats { - public int txBytes; - public int rxBytes; - public int txObjectBytes; - public int rxObjectBytes; - public int rxObjects; - public int txObjects; - public int txErrors; - public int rxErrors; - public int txRetries; - } ; - class ObjectTimeInfo { - UAVObject obj; - int updatePeriodMs; /** Update period in ms or 0 if no periodic updates are needed */ - int timeToNextUpdateMs; /** Time delay to the next update */ - }; + public class TelemetryStats { + public int txBytes; + public int rxBytes; + public int txObjectBytes; + public int rxObjectBytes; + public int rxObjects; + public int txObjects; + public int txErrors; + public int rxErrors; + public int txRetries; + }; - class ObjectQueueInfo { - UAVObject obj; - int event; - boolean allInstances; + class ObjectTimeInfo { + UAVObject obj; + int updatePeriodMs; + /** Update period in ms or 0 if no periodic updates are needed */ + int timeToNextUpdateMs; + /** Time delay to the next update */ + }; - @Override + class ObjectQueueInfo { + UAVObject obj; + int event; + boolean allInstances; + + @Override public boolean equals(Object e) { - try { - ObjectQueueInfo o = (ObjectQueueInfo) e; - return o.obj.getObjID() == obj.getObjID() && o.event == event && o.allInstances == allInstances; - } catch (Exception err) { + try { + ObjectQueueInfo o = (ObjectQueueInfo) e; + return o.obj.getObjID() == obj.getObjID() && o.event == event + && o.allInstances == allInstances; + } catch (Exception err) { - }; - return false; - } - }; - - class ObjectTransactionInfo { - UAVObject obj; - boolean allInstances; - boolean objRequest; - int retriesRemaining; - boolean acked; - } ; - - /** - * Events generated by objects. Not enum because used in mask. - */ - private static final int EV_UNPACKED = 0x01; /** Object data updated by unpacking */ - private static final int EV_UPDATED = 0x02; /** Object data updated by changing the data structure */ - private static final int EV_UPDATED_MANUAL = 0x04; /** Object update event manually generated */ - private static final int EV_UPDATE_REQ = 0x08; /** Request to update object data */ - - /** - * Constructor - */ - public Telemetry(UAVTalk utalkIn, UAVObjectManager objMngr, Looper l) - { - this.utalk = utalkIn; - this.objMngr = objMngr; - - // Create a handler for object messages - handler = new ObjectUpdateHandler(l); - - // Process all objects in the list - List< List > objs = objMngr.getObjects(); - ListIterator> li = objs.listIterator(); - while(li.hasNext()) - registerObject(li.next().get(0)); // we only need to register one instance per object type - - // Listen to new object creations - objMngr.addNewInstanceObserver(new Observer() { - @Override - public void update(Observable observable, Object data) { - newInstance((UAVObject) data); } - }); - objMngr.addNewObjectObserver(new Observer() { + ; + return false; + } + }; + + class ObjectTransactionInfo { + UAVObject obj; + boolean allInstances; + boolean objRequest; + int retriesRemaining; + boolean acked; + }; + + /** + * Events generated by objects. Not enum because used in mask. + */ + private static final int EV_UNPACKED = 0x01; + /** Object data updated by unpacking */ + private static final int EV_UPDATED = 0x02; + /** Object data updated by changing the data structure */ + private static final int EV_UPDATED_MANUAL = 0x04; + /** Object update event manually generated */ + private static final int EV_UPDATE_REQ = 0x08; + + /** Request to update object data */ + + /** + * Constructor + */ + public Telemetry(UAVTalk utalkIn, UAVObjectManager objMngr, Looper l) { + this.utalk = utalkIn; + this.objMngr = objMngr; + + // Create a handler for object messages + handler = new ObjectUpdateHandler(l); + + // Process all objects in the list + List> objs = objMngr.getObjects(); + ListIterator> li = objs.listIterator(); + while (li.hasNext()) + registerObject(li.next().get(0)); // we only need to register one + // instance per object type + + // Listen to new object creations + objMngr.addNewInstanceObserver(new Observer() { @Override public void update(Observable observable, Object data) { - newObject((UAVObject) data); - } - }); + newInstance((UAVObject) data); + } + }); + objMngr.addNewObjectObserver(new Observer() { + @Override + public void update(Observable observable, Object data) { + newObject((UAVObject) data); + } + }); - // Listen to transaction completions from uavtalk - utalk.setOnTransactionCompletedListener( - utalk.new OnTransactionCompletedListener() { + // Listen to transaction completions from uavtalk + utalk.setOnTransactionCompletedListener(utalk.new OnTransactionCompletedListener() { @Override void TransactionSucceeded(UAVObject data) { - try { + try { transactionCompleted(data, true); } catch (IOException e) { // Disconnect when stream fails utalk.setOnTransactionCompletedListener(null); } - } + } + @Override void TransactionFailed(UAVObject data) { - try { - if (DEBUG) Log.d(TAG, "TransactionFailed(" + data.getName() + ")"); + try { + if (DEBUG) + Log.d(TAG, "TransactionFailed(" + data.getName() + ")"); transactionCompleted(data, false); } catch (IOException e) { // Disconnect when stream fails utalk.setOnTransactionCompletedListener(null); } - } + } - }); + }); - // Get GCS stats object - gcsStatsObj = objMngr.getObject("GCSTelemetryStats"); + // Get GCS stats object + gcsStatsObj = objMngr.getObject("GCSTelemetryStats"); - // Setup transaction timer - transPending = false; - // Setup and start the periodic timer - timeToNextUpdateMs = 0; - updateTimerSetPeriod(1000); - // Setup and start the stats timer - txErrors = 0; - txRetries = 0; - } + // Setup transaction timer + transPending = false; + // Setup and start the periodic timer + timeToNextUpdateMs = 0; + updateTimerSetPeriod(1000); + // Setup and start the stats timer + txErrors = 0; + txRetries = 0; + } - synchronized void transTimerSetPeriod(int periodMs) { - if(transTimerTask != null) - transTimerTask.cancel(); + synchronized void transTimerSetPeriod(int periodMs) { + if (transTimerTask != null) + transTimerTask.cancel(); - if(transTimer != null) - transTimer.purge(); + if (transTimer != null) + transTimer.purge(); - transTimer = new Timer(); + transTimer = new Timer(); - transTimerTask = new TimerTask() { + transTimerTask = new TimerTask() { @Override public void run() { try { @@ -191,21 +202,21 @@ public class Telemetry { cancel(); } } - }; - transTimer.schedule(transTimerTask, periodMs, periodMs); - } + }; + transTimer.schedule(transTimerTask, periodMs, periodMs); + } - synchronized void updateTimerSetPeriod(int periodMs) { - if (updateTimer != null) { - updateTimer.cancel(); - updateTimer = null; - } - if (updateTimerTask != null) { - updateTimerTask.cancel(); - updateTimerTask = null; - } - updateTimer = new Timer(); - updateTimerTask = new TimerTask() { + synchronized void updateTimerSetPeriod(int periodMs) { + if (updateTimer != null) { + updateTimer.cancel(); + updateTimer = null; + } + if (updateTimerTask != null) { + updateTimerTask.cancel(); + updateTimerTask = null; + } + updateTimer = new Timer(); + updateTimerTask = new TimerTask() { @Override public void run() { try { @@ -215,651 +226,641 @@ public class Telemetry { updateTimer.cancel(); } } - }; - updateTimer.schedule(updateTimerTask, periodMs, periodMs); + }; + updateTimer.schedule(updateTimerTask, periodMs, periodMs); - } + } - /** - * Register a new object for periodic updates (if enabled) - */ - private synchronized void registerObject(UAVObject obj) - { - // Setup object for periodic updates - addObject(obj); + /** + * Register a new object for periodic updates (if enabled) + */ + private synchronized void registerObject(UAVObject obj) { + // Setup object for periodic updates + addObject(obj); - // Setup object for telemetry updates - updateObject(obj); - } + // Setup object for telemetry updates + updateObject(obj); + } - /** - * 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; - } - } + /** + * 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; + } + } - // 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); - } + // 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); + } - /** - * Update the object's timers - */ - private synchronized void setUpdatePeriod(UAVObject obj, int periodMs) - { - // Find object type (not instance!) and update its period - ListIterator li = objList.listIterator(); - while(li.hasNext()) { - ObjectTimeInfo n = li.next(); - if ( n.obj.getObjID() == obj.getObjID() ) - { - n.updatePeriodMs = periodMs; - n.timeToNextUpdateMs = (int) (periodMs * (new java.util.Random()).nextDouble()); // avoid bunching of updates - } - } - } + /** + * Update the object's timers + */ + private synchronized void setUpdatePeriod(UAVObject obj, int periodMs) { + // Find object type (not instance!) and update its period + ListIterator li = objList.listIterator(); + while (li.hasNext()) { + ObjectTimeInfo n = li.next(); + if (n.obj.getObjID() == obj.getObjID()) { + n.updatePeriodMs = periodMs; + n.timeToNextUpdateMs = (int) (periodMs * (new java.util.Random()) + .nextDouble()); // avoid bunching of updates + } + } + } - final Observer unpackedObserver = new Observer() { + final Observer unpackedObserver = new Observer() { @Override public void update(Observable observable, Object data) { handler.unpacked((UAVObject) data); - } + } }; final Observer updatedAutoObserver = new Observer() { @Override public void update(Observable observable, Object data) { handler.updatedAuto((UAVObject) data); - } + } }; final Observer updatedManualObserver = new Observer() { @Override public void update(Observable observable, Object data) { handler.updatedManual((UAVObject) data); - } + } }; final Observer updatedRequestedObserver = new Observer() { @Override public void update(Observable observable, Object data) { handler.updateRequested((UAVObject) data); - } + } }; - /** - * Connect to all instances of an object depending on the event mask specified - */ - private synchronized void connectToObjectInstances(UAVObject obj, int eventMask) - { - List objs = objMngr.getObjectInstances(obj.getObjID()); - ListIterator li = objs.listIterator(); - while(li.hasNext()) - { - obj = li.next(); + /** + * Connect to all instances of an object depending on the event mask + * specified + */ + private synchronized void connectToObjectInstances(UAVObject obj, + int eventMask) { + List objs = objMngr.getObjectInstances(obj.getObjID()); + ListIterator li = objs.listIterator(); + while (li.hasNext()) { + obj = li.next(); - // Disconnect all previous observers from telemetry. This is imortant as this can - // be called multiple times - obj.removeUnpackedObserver(unpackedObserver); - obj.removeUpdatedAutoObserver(updatedAutoObserver); - obj.removeUpdatedManualObserver(updatedManualObserver); - obj.removeUpdateRequestedObserver(updatedRequestedObserver); + // Disconnect all previous observers from telemetry. This is + // imortant as this can + // be called multiple times + obj.removeUnpackedObserver(unpackedObserver); + obj.removeUpdatedAutoObserver(updatedAutoObserver); + obj.removeUpdatedManualObserver(updatedManualObserver); + obj.removeUpdateRequestedObserver(updatedRequestedObserver); - // Connect only the selected events - if ( (eventMask&EV_UNPACKED) != 0) - obj.addUnpackedObserver(unpackedObserver); - if ( (eventMask&EV_UPDATED) != 0) - obj.addUpdatedAutoObserver(updatedAutoObserver); - if ( (eventMask&EV_UPDATED_MANUAL) != 0) - obj.addUpdatedManualObserver(updatedManualObserver); - if ( (eventMask&EV_UPDATE_REQ) != 0) - obj.addUpdateRequestedObserver(updatedRequestedObserver); - } - } + // Connect only the selected events + if ((eventMask & EV_UNPACKED) != 0) + obj.addUnpackedObserver(unpackedObserver); + if ((eventMask & EV_UPDATED) != 0) + obj.addUpdatedAutoObserver(updatedAutoObserver); + if ((eventMask & EV_UPDATED_MANUAL) != 0) + obj.addUpdatedManualObserver(updatedManualObserver); + if ((eventMask & EV_UPDATE_REQ) != 0) + obj.addUpdateRequestedObserver(updatedRequestedObserver); + } + } - /** - * Update an object based on its metadata properties - */ - private void updateObject(UAVObject obj) - { - // Get metadata - UAVObject.Metadata metadata = obj.getMetadata(); + /** + * 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) + // 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_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); + } 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); - } - 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_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); - } - } + connectToObjectInstances(obj, eventMask); + } + } - /** - * Called when a transaction is successfully completed (uavtalk event) - * @throws IOException - */ - private void transactionCompleted(UAVObject obj, boolean result) throws IOException - { - if (DEBUG) Log.d(TAG,"UAVTalk transactionCompleted"); - // Check if there is a pending transaction and the objects match - if ( transPending && transInfo.obj.getObjID() == obj.getObjID() ) - { - if (DEBUG) Log.d(TAG,"Telemetry: transaction completed for " + obj.getName()); - // Complete transaction + /** + * Called when a transaction is successfully completed (uavtalk event) + * + * @throws IOException + */ + private void transactionCompleted(UAVObject obj, boolean result) + throws IOException { + if (DEBUG) + Log.d(TAG, "UAVTalk transactionCompleted"); + // Check if there is a pending transaction and the objects match + if (transPending && transInfo.obj.getObjID() == obj.getObjID()) { + if (DEBUG) Log.d(TAG, "Telemetry: transaction completed for " + obj.getName()); - synchronized(transTimer) { - transTimer.cancel(); - transPending = false; - } + // Complete transaction + synchronized(transTimer) { + transTimer.cancel(); + transPending = false; + } - //Send signal - obj.transactionCompleted(result); - // Process new object updates from queue - processObjectQueue(); - } else - { - if (ERROR) Log.e(TAG,"Error: received a transaction completed when did not expect it."); - transPending = false; - } - } + //Send signal + obj.transactionCompleted(result); + } else { + if (ERROR) + Log.e(TAG, + "Error: received a transaction completed when did not expect it."); + transPending = false; + } + } - /** - * Called when a transaction is not completed within the timeout period (timer event) - * @throws IOException - */ - private void transactionTimeout() throws IOException - { - if (DEBUG) Log.d(TAG,"Telemetry: transaction timeout."); - synchronized(transTimer) { - transTimer.cancel(); - // Proceed only if there is a pending transaction - if ( transPending ) - { - // Check if more retries are pending - if (transInfo.retriesRemaining > 0) - { - --transInfo.retriesRemaining; - processObjectTransaction(); - ++txRetries; - } - else - { - if (ERROR) Log.e(TAG, "Transaction failed for: " + transInfo.obj.getName()); + /** + * Called when a transaction is not completed within the timeout period + * (timer event) + * + * @throws IOException + */ + private void transactionTimeout() throws IOException { + if (DEBUG) + Log.d(TAG, "Telemetry: transaction timeout."); + synchronized (transTimer) { + transTimer.cancel(); + // Proceed only if there is a pending transaction + if (transPending) { + // Check if more retries are pending + if (transInfo.retriesRemaining > 0) { + --transInfo.retriesRemaining; + processObjectTransaction(); + ++txRetries; + } else { + if (ERROR) + Log.e(TAG, + "Transaction failed for: " + + transInfo.obj.getName()); - // Terminate transaction. This triggers UAVTalk to send a transaction - // failed signal which will make the next queue entry be processed - // Note this is UAVTalk listener TransactionFailed function and not the - // object specific transaction failed. - utalk.cancelPendingTransaction(transInfo.obj); - ++txErrors; - } - } - } - } + // Terminate transaction. This triggers UAVTalk to send a + // transaction + // failed signal which will make the next queue entry be + // processed + // Note this is UAVTalk listener TransactionFailed function + // and not the + // object specific transaction failed. + utalk.cancelPendingTransaction(transInfo.obj); + ++txErrors; + } + } + } + } - /** - * Start an object transaction with UAVTalk, all information is stored in transInfo - * @throws IOException - */ - private void processObjectTransaction() throws IOException - { - if (transPending) - { - if (DEBUG) Log.d(TAG, "Process Object transaction for " + transInfo.obj.getName()); - // Initiate transaction - if (transInfo.objRequest) - { - utalk.sendObjectRequest(transInfo.obj, transInfo.allInstances); - } - else - { - utalk.sendObject(transInfo.obj, transInfo.acked, transInfo.allInstances); - } - // Start timer if a response is expected - if ( transInfo.objRequest || transInfo.acked ) - { - transTimerSetPeriod(REQ_TIMEOUT_MS); - } - else - { - synchronized(transTimer) { - transTimer.cancel(); - transPending = false; - } - } - } else - { - if (ERROR) Log.e(TAG,"Error: inside of processObjectTransaction with no transPending"); - } - } + /** + * Start an object transaction with UAVTalk, all information is stored in + * transInfo + * + * @throws IOException + */ + private void processObjectTransaction() throws IOException { + if (transPending) { + if (DEBUG) + Log.d(TAG, + "Process Object transaction for " + + transInfo.obj.getName()); + // Initiate transaction + if (transInfo.objRequest) { + utalk.sendObjectRequest(transInfo.obj, transInfo.allInstances); + } else { + utalk.sendObject(transInfo.obj, transInfo.acked, + transInfo.allInstances); + } + // Start timer if a response is expected + if (transInfo.objRequest || transInfo.acked) { + transTimerSetPeriod(REQ_TIMEOUT_MS); + } else { + synchronized (transTimer) { + transTimer.cancel(); + transPending = false; + } + } + } else { + if (ERROR) + Log.e(TAG, + "Error: inside of processObjectTransaction with no transPending"); + } + } - /** - * Process events from the object queue - * @throws IOException - */ - private void processObjectQueue() throws IOException - { - if (DEBUG) Log.d(TAG, "Process object queue - Depth " + objQueue.size() + " priority " + objPriorityQueue.size()); + /** + * Process events from the object queue + * + * @throws IOException + */ + private void processObjectQueue() throws IOException { + if (DEBUG) + Log.d(TAG, "Process object queue - Depth " + objQueue.size() + + " priority " + objPriorityQueue.size()); - // Don nothing if a transaction is already in progress (should not happen) - if (transPending) - { - if (WARN) Log.e(TAG,"Dequeue while a transaction pending"); - return; - } + // Don nothing if a transaction is already in progress (should not + // happen) + if (transPending) { + if (WARN) + Log.e(TAG, "Dequeue while a transaction pending"); + return; + } - // Get object information from queue (first the priority and then the regular queue) - ObjectQueueInfo objInfo; - synchronized (objPriorityQueue) { - if ( !objPriorityQueue.isEmpty() ) - { - objInfo = objPriorityQueue.remove(); - } else { - synchronized (objQueue) { - if ( !objQueue.isEmpty() ) - { - objInfo = objQueue.remove(); - } - else - { - return; - } - } - } - } + // Get object information from queue (first the priority and then the + // regular queue) + ObjectQueueInfo objInfo; + synchronized (objPriorityQueue) { + if (!objPriorityQueue.isEmpty()) { + objInfo = objPriorityQueue.remove(); + } else { + synchronized (objQueue) { + if (!objQueue.isEmpty()) { + objInfo = objQueue.remove(); + } else { + return; + } + } + } + } - // Check if a connection has been established, only process GCSTelemetryStats updates - // (used to establish the connection) - gcsStatsObj = objMngr.getObject("GCSTelemetryStats"); - if ( ((String) gcsStatsObj.getField("Status").getValue()).compareTo("Connected") != 0 ) - { - objQueue.clear(); - if ( objInfo.obj.getObjID() != objMngr.getObject("GCSTelemetryStats").getObjID() ) - { - if (DEBUG) Log.d(TAG,"transactionCompleted(false) due to receiving object not GCSTelemetryStats while not connected."); - objInfo.obj.transactionCompleted(false); - return; - } - } + // Check if a connection has been established, only process + // GCSTelemetryStats updates + // (used to establish the connection) + gcsStatsObj = objMngr.getObject("GCSTelemetryStats"); + if (((String) gcsStatsObj.getField("Status").getValue()) + .compareTo("Connected") != 0) { + objQueue.clear(); + if (objInfo.obj.getObjID() != objMngr + .getObject("GCSTelemetryStats").getObjID()) { + if (DEBUG) + Log.d(TAG, + "transactionCompleted(false) due to receiving object not GCSTelemetryStats while not connected."); + objInfo.obj.transactionCompleted(false); + return; + } + } - // Setup transaction (skip if unpack event) - if ( objInfo.event != EV_UNPACKED ) - { - UAVObject.Metadata metadata = objInfo.obj.getMetadata(); - transInfo.obj = objInfo.obj; - transInfo.allInstances = objInfo.allInstances; - transInfo.retriesRemaining = MAX_RETRIES; - transInfo.acked = metadata.GetGcsTelemetryAcked(); - if ( objInfo.event == EV_UPDATED || objInfo.event == EV_UPDATED_MANUAL ) - { - transInfo.objRequest = false; - } - else if ( objInfo.event == EV_UPDATE_REQ ) - { - transInfo.objRequest = true; - } - // Start transaction - transPending = true; - processObjectTransaction(); - } else - { -// qDebug() << QString("Process object queue: this is an unpack event for %1").arg(objInfo.obj->getName()); - } + // Setup transaction (skip if unpack event) + if (objInfo.event != EV_UNPACKED) { + UAVObject.Metadata metadata = objInfo.obj.getMetadata(); + transInfo.obj = objInfo.obj; + transInfo.allInstances = objInfo.allInstances; + transInfo.retriesRemaining = MAX_RETRIES; + transInfo.acked = metadata.GetGcsTelemetryAcked(); + if (objInfo.event == EV_UPDATED + || objInfo.event == EV_UPDATED_MANUAL) { + transInfo.objRequest = false; + } else if (objInfo.event == EV_UPDATE_REQ) { + transInfo.objRequest = true; + } + // Start transaction + transPending = true; + processObjectTransaction(); + } else { + // qDebug() << + // QString("Process object queue: this is an unpack event for %1").arg(objInfo.obj->getName()); + } - // If this is a metaobject then make necessary telemetry updates - if (objInfo.obj.isMetadata()) - { - UAVMetaObject metaobj = (UAVMetaObject) objInfo.obj; - updateObject( metaobj.getParentObject() ); - } + // If this is a metaobject then make necessary telemetry updates + if (objInfo.obj.isMetadata()) { + UAVMetaObject metaobj = (UAVMetaObject) objInfo.obj; + updateObject(metaobj.getParentObject()); + } - // The fact we received an unpacked event does not mean that - // we do not have additional objects still in the queue, - // so we have to reschedule queue processing to make sure they are not - // stuck: - if ( objInfo.event == EV_UNPACKED && !transPending) - processObjectQueue(); + // The fact we received an unpacked event does not mean that + // we do not have additional objects still in the queue, + // so we have to reschedule queue processing to make sure they are not + // stuck: + if (objInfo.event == EV_UNPACKED && !transPending) + processObjectQueue(); - } + } - /** - * Check is any objects are pending for periodic updates - * TODO: Clean-up - * @throws IOException - */ - private void processPeriodicUpdates() throws IOException - { + /** + * Check is any objects are pending for periodic updates TODO: Clean-up + * + * @throws IOException + */ + private void processPeriodicUpdates() throws IOException { - if (DEBUG) Log.d(TAG, "processPeriodicUpdates()"); - // Stop timer + if (DEBUG) + Log.d(TAG, "processPeriodicUpdates()"); + // Stop timer - updateTimer.cancel(); + updateTimer.cancel(); - // Iterate through each object and update its timer, if zero then transmit object. - // Also calculate smallest delay to next update (will be used for setting timeToNextUpdateMs) - int minDelay = MAX_UPDATE_PERIOD_MS; - ObjectTimeInfo objinfo; - int elapsedMs = 0; - long startTime; - int offset; - ListIterator li = objList.listIterator(); - while(li.hasNext()) - { - objinfo = li.next(); - // If object is configured for periodic updates - if (objinfo.updatePeriodMs > 0) - { - objinfo.timeToNextUpdateMs -= timeToNextUpdateMs; - // Check if time for the next update - if (objinfo.timeToNextUpdateMs <= 0) - { - // Reset timer - offset = (-objinfo.timeToNextUpdateMs) % objinfo.updatePeriodMs; - objinfo.timeToNextUpdateMs = objinfo.updatePeriodMs - offset; - // Send object - startTime = System.currentTimeMillis(); - handler.updatedManual(objinfo.obj); - //enqueueObjectUpdates(objinfo.obj, EV_UPDATED_MANUAL, true, false); - elapsedMs = (int) (System.currentTimeMillis() - startTime); - // Update timeToNextUpdateMs with the elapsed delay of sending the object; - timeToNextUpdateMs += elapsedMs; - } - // Update minimum delay - if (objinfo.timeToNextUpdateMs < minDelay) - { - minDelay = objinfo.timeToNextUpdateMs; - } - } - } + // Iterate through each object and update its timer, if zero then + // transmit object. + // Also calculate smallest delay to next update (will be used for + // setting timeToNextUpdateMs) + int minDelay = MAX_UPDATE_PERIOD_MS; + ObjectTimeInfo objinfo; + int elapsedMs = 0; + long startTime; + int offset; + ListIterator li = objList.listIterator(); + while (li.hasNext()) { + objinfo = li.next(); + // If object is configured for periodic updates + if (objinfo.updatePeriodMs > 0) { + objinfo.timeToNextUpdateMs -= timeToNextUpdateMs; + // Check if time for the next update + if (objinfo.timeToNextUpdateMs <= 0) { + // Reset timer + offset = (-objinfo.timeToNextUpdateMs) + % objinfo.updatePeriodMs; + objinfo.timeToNextUpdateMs = objinfo.updatePeriodMs + - offset; + // Send object + startTime = System.currentTimeMillis(); + handler.updatedManual(objinfo.obj); + // enqueueObjectUpdates(objinfo.obj, EV_UPDATED_MANUAL, + // true, false); + elapsedMs = (int) (System.currentTimeMillis() - startTime); + // Update timeToNextUpdateMs with the elapsed delay of + // sending the object; + timeToNextUpdateMs += elapsedMs; + } + // Update minimum delay + if (objinfo.timeToNextUpdateMs < minDelay) { + minDelay = objinfo.timeToNextUpdateMs; + } + } + } - // Check if delay for the next update is too short - if (minDelay < MIN_UPDATE_PERIOD_MS) - { - minDelay = MIN_UPDATE_PERIOD_MS; - } + // Check if delay for the next update is too short + if (minDelay < MIN_UPDATE_PERIOD_MS) { + minDelay = MIN_UPDATE_PERIOD_MS; + } - // Done - timeToNextUpdateMs = minDelay; + // Done + timeToNextUpdateMs = minDelay; - // Restart timer - updateTimerSetPeriod(timeToNextUpdateMs); - } + // Restart timer + updateTimerSetPeriod(timeToNextUpdateMs); + } - public TelemetryStats getStats() - { - // Get UAVTalk stats - UAVTalk.ComStats utalkStats = utalk.getStats(); + public TelemetryStats getStats() { + // Get UAVTalk stats + UAVTalk.ComStats utalkStats = utalk.getStats(); - // Update stats - TelemetryStats stats = new TelemetryStats(); - stats.txBytes = utalkStats.txBytes; - stats.rxBytes = utalkStats.rxBytes; - stats.txObjectBytes = utalkStats.txObjectBytes; - stats.rxObjectBytes = utalkStats.rxObjectBytes; - stats.rxObjects = utalkStats.rxObjects; - stats.txObjects = utalkStats.txObjects; - stats.txErrors = utalkStats.txErrors + txErrors; - stats.rxErrors = utalkStats.rxErrors; - stats.txRetries = txRetries; + // Update stats + TelemetryStats stats = new TelemetryStats(); + stats.txBytes = utalkStats.txBytes; + stats.rxBytes = utalkStats.rxBytes; + stats.txObjectBytes = utalkStats.txObjectBytes; + stats.rxObjectBytes = utalkStats.rxObjectBytes; + stats.rxObjects = utalkStats.rxObjects; + stats.txObjects = utalkStats.txObjects; + stats.txErrors = utalkStats.txErrors + txErrors; + stats.rxErrors = utalkStats.rxErrors; + stats.txRetries = txRetries; - // Done - return stats; - } + // Done + return stats; + } - public void resetStats() - { - utalk.resetStats(); - txErrors = 0; - txRetries = 0; - } + public void resetStats() { + utalk.resetStats(); + txErrors = 0; + txRetries = 0; + } + private void newObject(UAVObject obj) { + registerObject(obj); + } - private void newObject(UAVObject obj) - { - registerObject(obj); - } + private synchronized void newInstance(UAVObject obj) { + registerObject(obj); + } - private synchronized void newInstance(UAVObject obj) - { - registerObject(obj); - } - - /** - * Stop all the telemetry timers - */ - public void stopTelemetry() - { - if (updateTimerTask != null) - updateTimerTask.cancel(); - updateTimerTask = null; - if (updateTimer != null) - updateTimer.cancel(); - updateTimer = null; - if (transTimerTask != null) - transTimerTask.cancel(); - transTimerTask = null; - if (transTimer != null) - transTimer.cancel(); - transTimer = null; - } + /** + * Stop all the telemetry timers + */ + public void stopTelemetry() { + if (updateTimerTask != null) + updateTimerTask.cancel(); + updateTimerTask = null; + if (updateTimer != null) + updateTimer.cancel(); + updateTimer = null; + if (transTimerTask != null) + transTimerTask.cancel(); + transTimerTask = null; + if (transTimer != null) + transTimer.cancel(); + transTimer = null; + } /** * Private variables */ - private final UAVObjectManager objMngr; - private final UAVTalk utalk; - private UAVObject gcsStatsObj; - private final List objList = new ArrayList(); - private final Queue objQueue = new ConcurrentLinkedQueue(); - private final Queue objPriorityQueue = new ConcurrentLinkedQueue(); - private final ObjectTransactionInfo transInfo = new ObjectTransactionInfo(); - private boolean transPending; + private final UAVObjectManager objMngr; + private final UAVTalk utalk; + private UAVObject gcsStatsObj; + private final List objList = new ArrayList(); + private final Queue objQueue = new ConcurrentLinkedQueue(); + private final Queue objPriorityQueue = new ConcurrentLinkedQueue(); + private final ObjectTransactionInfo transInfo = new ObjectTransactionInfo(); + private boolean transPending; - private Timer updateTimer; - private TimerTask updateTimerTask; - private Timer transTimer; - private TimerTask transTimerTask; + private Timer updateTimer; + private TimerTask updateTimerTask; + private Timer transTimer; + private TimerTask transTimerTask; - private int timeToNextUpdateMs; - private int txErrors; - private int txRetries; + private int timeToNextUpdateMs; + private int txErrors; + private int txRetries; - /** - * Private constants - */ - private static final int REQ_TIMEOUT_MS = 250; - private static final int MAX_RETRIES = 2; - private static final int MAX_UPDATE_PERIOD_MS = 1000; - private static final int MIN_UPDATE_PERIOD_MS = 1; - private static final int MAX_QUEUE_SIZE = 20; + /** + * Private constants + */ + private static final int REQ_TIMEOUT_MS = 250; + private static final int MAX_RETRIES = 2; + private static final int MAX_UPDATE_PERIOD_MS = 1000; + private static final int MIN_UPDATE_PERIOD_MS = 1; + private static final int MAX_QUEUE_SIZE = 20; - private final ObjectUpdateHandler handler; + private final ObjectUpdateHandler handler; - public class ObjectUpdateHandler extends Handler { + public class ObjectUpdateHandler extends Handler { - //! This can only be created while attaching to a particular looper - ObjectUpdateHandler(Looper l) { - super(l); - } - - //! Generic enqueue - void enqueueObjectUpdates(UAVObject obj, int event, boolean allInstances, boolean priority) { - - if (DEBUG) Log.d(TAG, "Enqueing update " + obj.getName() + " event " + event); - - ObjectQueueInfo objInfo = new ObjectQueueInfo(); - objInfo.obj = obj; - objInfo.event = event; - objInfo.allInstances = allInstances; - - post(new ObjectRunnable(objInfo)); - } - - //! Enqueue an unpacked event - void unpacked(UAVObject obj) { - enqueueObjectUpdates(obj, EV_UNPACKED, false, true); + // ! This can only be created while attaching to a particular looper + ObjectUpdateHandler(Looper l) { + super(l); } - //! Enqueue an updated auto event - void updatedAuto(UAVObject obj) { - enqueueObjectUpdates(obj,EV_UPDATED, false, true); - } + // ! Generic enqueue + void enqueueObjectUpdates(UAVObject obj, int event, + boolean allInstances, boolean priority) { - //! Enqueue an updated manual event - void updatedManual(UAVObject obj) { - enqueueObjectUpdates(obj, EV_UPDATE_REQ, false, true); - } + if (DEBUG) + Log.d(TAG, "Enqueing update " + obj.getName() + " event " + + event); - //! Enqueue an update requested event - void updateRequested(UAVObject obj) { - enqueueObjectUpdates(obj, EV_UPDATE_REQ, false, true); - } + ObjectQueueInfo objInfo = new ObjectQueueInfo(); + objInfo.obj = obj; + objInfo.event = event; + objInfo.allInstances = allInstances; - } + post(new ObjectRunnable(objInfo)); + } - class ObjectRunnable implements Runnable { + // ! Enqueue an unpacked event + void unpacked(UAVObject obj) { + enqueueObjectUpdates(obj, EV_UNPACKED, false, true); + } - //! Transaction information to perform - private final ObjectQueueInfo objInfo; -// private final ObjectTransactionInfo transInfo = new ObjectTransactionInfo(); + // ! Enqueue an updated auto event + void updatedAuto(UAVObject obj) { + enqueueObjectUpdates(obj, EV_UPDATED, false, true); + } - ObjectRunnable(ObjectQueueInfo info) { - Assert.assertNotNull(info); - objInfo = info; - } + // ! Enqueue an updated manual event + void updatedManual(UAVObject obj) { + enqueueObjectUpdates(obj, EV_UPDATED_MANUAL, false, true); + } - //! Perform the transaction on the looper thread - @Override - public void run () { - Log.d(TAG,"object transaction running"); - // 1. Check GCS is connected, throw this out if not - // 2. Set up a transaction which includes multiple retries, whether to wait for ack etc - // 3. Send UAVTalk message - // 4. Based on transaction type either wait for update or end + // ! Enqueue an update requested event + void updateRequested(UAVObject obj) { + enqueueObjectUpdates(obj, EV_UPDATE_REQ, false, true); + } - // 1. Check if a connection has been established, only process GCSTelemetryStats updates - // (used to establish the connection) - gcsStatsObj = objMngr.getObject("GCSTelemetryStats"); - if ( ((String) gcsStatsObj.getField("Status").getValue()).compareTo("Connected") != 0 ) - { - if ( objInfo.obj.getObjID() != objMngr.getObject("GCSTelemetryStats").getObjID() ) - { - if (DEBUG) Log.d(TAG,"transactionCompleted(false) due to receiving object not GCSTelemetryStats while not connected."); - objInfo.obj.transactionCompleted(false); - return; - } - } + } - Log.e(TAG, "A"); - // 2. Setup transaction (skip if unpack event) - if ( objInfo.event != EV_UNPACKED ) - { - Log.e(TAG, "A1"); - UAVObject.Metadata metadata = objInfo.obj.getMetadata(); - transInfo.obj = objInfo.obj; - transInfo.allInstances = objInfo.allInstances; - transInfo.retriesRemaining = MAX_RETRIES; - transInfo.acked = metadata.GetGcsTelemetryAcked(); - if ( objInfo.event == EV_UPDATED || objInfo.event == EV_UPDATED_MANUAL ) - { - transInfo.objRequest = false; - } - else if ( objInfo.event == EV_UPDATE_REQ ) - { - transInfo.objRequest = true; - } - // Start transaction - transPending = true; - } - Log.e(TAG, "B"); - // If this is a metaobject then make necessary telemetry updates (this is why we catch unpack) - if (objInfo.obj.isMetadata()) - { - UAVMetaObject metaobj = (UAVMetaObject) objInfo.obj; - updateObject( metaobj.getParentObject() ); - } - Log.e(TAG, "C"); - // 3. Execute transaction - if (transPending) - { - Log.e(TAG, "D"); - try { - if (DEBUG || true) Log.d(TAG, "Process Object transaction for " + transInfo.obj.getName()); - // Initiate transaction - if (transInfo.objRequest) - { - utalk.sendObjectRequest(transInfo.obj, transInfo.allInstances); - } - else - { - Log.d(TAG, "Sending object"); - utalk.sendObject(transInfo.obj, transInfo.acked, transInfo.allInstances); - } + class ObjectRunnable implements Runnable { - // TODO: Block if request expected (??) - if ( transInfo.objRequest || transInfo.acked ) - { - transTimerSetPeriod(REQ_TIMEOUT_MS); - } - else - { - synchronized(transTimer) { - transTimer.cancel(); - transPending = false; - } - } - } catch (IOException e) { - // TODO Auto-generated catch block - Log.e(TAG, "E"); - e.printStackTrace(); - } - } - } - } + // ! Transaction information to perform + private final ObjectQueueInfo objInfo; + + // private final ObjectTransactionInfo transInfo = new + // ObjectTransactionInfo(); + + ObjectRunnable(ObjectQueueInfo info) { + Assert.assertNotNull(info); + objInfo = info; + } + + // ! Perform the transaction on the looper thread + @Override + public void run() { + if (DEBUG) Log.d(TAG, "Object transaction running. Event:" + objInfo.event); + // 1. Check GCS is connected, throw this out if not + // 2. Set up a transaction which includes multiple retries, whether + // to wait for ack etc + // 3. Send UAVTalk message + // 4. Based on transaction type either wait for update or end + + // 1. Check if a connection has been established, only process + // GCSTelemetryStats updates + // (used to establish the connection) + gcsStatsObj = objMngr.getObject("GCSTelemetryStats"); + if (((String) gcsStatsObj.getField("Status").getValue()) + .compareTo("Connected") != 0) { + if (objInfo.obj.getObjID() != objMngr.getObject( + "GCSTelemetryStats").getObjID()) { + if (DEBUG) + Log.d(TAG, + "transactionCompleted(false) due to receiving object not GCSTelemetryStats while not connected."); + objInfo.obj.transactionCompleted(false); + return; + } + } + + // 2. Setup transaction (skip if unpack event) + if (objInfo.event != EV_UNPACKED) { + UAVObject.Metadata metadata = objInfo.obj.getMetadata(); + transInfo.obj = objInfo.obj; + transInfo.allInstances = objInfo.allInstances; + transInfo.retriesRemaining = MAX_RETRIES; + transInfo.acked = metadata.GetGcsTelemetryAcked(); + if (objInfo.event == EV_UPDATED + || objInfo.event == EV_UPDATED_MANUAL) { + transInfo.objRequest = false; + } else if (objInfo.event == EV_UPDATE_REQ) { + transInfo.objRequest = true; + } + // Start transaction + transPending = true; + } + + // If this is a metaobject then make necessary telemetry updates + // (this is why we catch unpack) + if (objInfo.obj.isMetadata()) { + UAVMetaObject metaobj = (UAVMetaObject) objInfo.obj; + updateObject(metaobj.getParentObject()); + } + + // 3. Execute transaction + if (transPending) { + try { + if (DEBUG) Log.d(TAG, "Process Object transaction for " + transInfo.obj.getName()); + + // Initiate transaction + if (transInfo.objRequest) { + if (DEBUG) Log.d(TAG, "Sending object request"); + utalk.sendObjectRequest(transInfo.obj, transInfo.allInstances); + } else { + if (DEBUG) Log.d(TAG, "Sending object " + transInfo.obj.getName() + " " + transInfo.obj.toStringData()); + utalk.sendObject(transInfo.obj, transInfo.acked, transInfo.allInstances); + } + + + // TODO: Block if request expected (??) + if (transInfo.objRequest || transInfo.acked ) { + transTimerSetPeriod(REQ_TIMEOUT_MS); + } else { + synchronized(transTimer) { + transTimer.cancel(); + transPending = false; + } + } + + } catch (IOException e) { + // TODO Auto-generated catch block + Log.e(TAG, "E"); + e.printStackTrace(); + } + } + } + } } From 1bbfb354466e0e78d87412379fcac493249cfc69 Mon Sep 17 00:00:00 2001 From: James Cotton Date: Wed, 15 Aug 2012 00:01:14 -0500 Subject: [PATCH 15/24] AndroidGCS: Handler based telemetry. Now reschedule transactions if one is pending. --- .../src/org/openpilot/uavtalk/Telemetry.java | 238 ++++++------------ 1 file changed, 72 insertions(+), 166 deletions(-) diff --git a/androidgcs/src/org/openpilot/uavtalk/Telemetry.java b/androidgcs/src/org/openpilot/uavtalk/Telemetry.java index 3e4007777..e514e61c4 100644 --- a/androidgcs/src/org/openpilot/uavtalk/Telemetry.java +++ b/androidgcs/src/org/openpilot/uavtalk/Telemetry.java @@ -30,10 +30,8 @@ import java.util.List; import java.util.ListIterator; import java.util.Observable; import java.util.Observer; -import java.util.Queue; import java.util.Timer; import java.util.TimerTask; -import java.util.concurrent.ConcurrentLinkedQueue; import junit.framework.Assert; import android.os.Handler; @@ -49,8 +47,8 @@ public class Telemetry { private final String TAG = "Telemetry"; public static int LOGLEVEL = 0; - public static boolean WARN = LOGLEVEL > 2; - public static boolean DEBUG = LOGLEVEL > 1; + public static boolean DEBUG = LOGLEVEL > 2; + public static boolean WARN = LOGLEVEL > 1; public static boolean ERROR = LOGLEVEL > 0; public class TelemetryStats { @@ -391,8 +389,8 @@ public class Telemetry { */ private void transactionCompleted(UAVObject obj, boolean result) throws IOException { - if (DEBUG) - Log.d(TAG, "UAVTalk transactionCompleted"); + if (DEBUG) Log.d(TAG, "UAVTalk transactionCompleted"); + // Check if there is a pending transaction and the objects match if (transPending && transInfo.obj.getObjID() == obj.getObjID()) { if (DEBUG) Log.d(TAG, "Telemetry: transaction completed for " + obj.getName()); @@ -406,9 +404,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."); transPending = false; } } @@ -433,16 +429,11 @@ public class Telemetry { ++txRetries; } else { if (ERROR) - Log.e(TAG, - "Transaction failed for: " - + transInfo.obj.getName()); + Log.e(TAG, "Transaction failed for: " + transInfo.obj.getName()); - // Terminate transaction. This triggers UAVTalk to send a - // transaction - // failed signal which will make the next queue entry be - // processed + // Terminate transaction. This triggers UAVTalk to send a transaction + // failed signal which will make the next queue entry be processed // Note this is UAVTalk listener TransactionFailed function - // and not the // object specific transaction failed. utalk.cancelPendingTransaction(transInfo.obj); ++txErrors; @@ -460,9 +451,8 @@ public class Telemetry { private void processObjectTransaction() throws IOException { if (transPending) { if (DEBUG) - Log.d(TAG, - "Process Object transaction for " - + transInfo.obj.getName()); + Log.d(TAG, "Process Object transaction for " + transInfo.obj.getName()); + // Initiate transaction if (transInfo.objRequest) { utalk.sendObjectRequest(transInfo.obj, transInfo.allInstances); @@ -470,6 +460,7 @@ public class Telemetry { utalk.sendObject(transInfo.obj, transInfo.acked, transInfo.allInstances); } + // Start timer if a response is expected if (transInfo.objRequest || transInfo.acked) { transTimerSetPeriod(REQ_TIMEOUT_MS); @@ -481,99 +472,10 @@ public class Telemetry { } } else { if (ERROR) - Log.e(TAG, - "Error: inside of processObjectTransaction with no transPending"); + Log.e(TAG, "Error: inside of processObjectTransaction with no transPending"); } } - /** - * Process events from the object queue - * - * @throws IOException - */ - private void processObjectQueue() throws IOException { - if (DEBUG) - Log.d(TAG, "Process object queue - Depth " + objQueue.size() - + " priority " + objPriorityQueue.size()); - - // Don nothing if a transaction is already in progress (should not - // happen) - if (transPending) { - if (WARN) - Log.e(TAG, "Dequeue while a transaction pending"); - return; - } - - // Get object information from queue (first the priority and then the - // regular queue) - ObjectQueueInfo objInfo; - synchronized (objPriorityQueue) { - if (!objPriorityQueue.isEmpty()) { - objInfo = objPriorityQueue.remove(); - } else { - synchronized (objQueue) { - if (!objQueue.isEmpty()) { - objInfo = objQueue.remove(); - } else { - return; - } - } - } - } - - // Check if a connection has been established, only process - // GCSTelemetryStats updates - // (used to establish the connection) - gcsStatsObj = objMngr.getObject("GCSTelemetryStats"); - if (((String) gcsStatsObj.getField("Status").getValue()) - .compareTo("Connected") != 0) { - objQueue.clear(); - if (objInfo.obj.getObjID() != objMngr - .getObject("GCSTelemetryStats").getObjID()) { - if (DEBUG) - Log.d(TAG, - "transactionCompleted(false) due to receiving object not GCSTelemetryStats while not connected."); - objInfo.obj.transactionCompleted(false); - return; - } - } - - // Setup transaction (skip if unpack event) - if (objInfo.event != EV_UNPACKED) { - UAVObject.Metadata metadata = objInfo.obj.getMetadata(); - transInfo.obj = objInfo.obj; - transInfo.allInstances = objInfo.allInstances; - transInfo.retriesRemaining = MAX_RETRIES; - transInfo.acked = metadata.GetGcsTelemetryAcked(); - if (objInfo.event == EV_UPDATED - || objInfo.event == EV_UPDATED_MANUAL) { - transInfo.objRequest = false; - } else if (objInfo.event == EV_UPDATE_REQ) { - transInfo.objRequest = true; - } - // Start transaction - transPending = true; - processObjectTransaction(); - } else { - // qDebug() << - // QString("Process object queue: this is an unpack event for %1").arg(objInfo.obj->getName()); - } - - // If this is a metaobject then make necessary telemetry updates - if (objInfo.obj.isMetadata()) { - UAVMetaObject metaobj = (UAVMetaObject) objInfo.obj; - updateObject(metaobj.getParentObject()); - } - - // The fact we received an unpacked event does not mean that - // we do not have additional objects still in the queue, - // so we have to reschedule queue processing to make sure they are not - // stuck: - if (objInfo.event == EV_UNPACKED && !transPending) - processObjectQueue(); - - } - /** * Check is any objects are pending for periodic updates TODO: Clean-up * @@ -697,9 +599,7 @@ public class Telemetry { private final UAVTalk utalk; private UAVObject gcsStatsObj; private final List objList = new ArrayList(); - private final Queue objQueue = new ConcurrentLinkedQueue(); - private final Queue objPriorityQueue = new ConcurrentLinkedQueue(); - private final ObjectTransactionInfo transInfo = new ObjectTransactionInfo(); + private ObjectTransactionInfo transInfo = new ObjectTransactionInfo(); private boolean transPending; private Timer updateTimer; @@ -718,7 +618,6 @@ public class Telemetry { private static final int MAX_RETRIES = 2; private static final int MAX_UPDATE_PERIOD_MS = 1000; private static final int MIN_UPDATE_PERIOD_MS = 1; - private static final int MAX_QUEUE_SIZE = 20; private final ObjectUpdateHandler handler; @@ -733,9 +632,7 @@ public class Telemetry { void enqueueObjectUpdates(UAVObject obj, int event, boolean allInstances, boolean priority) { - if (DEBUG) - Log.d(TAG, "Enqueing update " + obj.getName() + " event " - + event); + if (DEBUG) Log.d(TAG, "Enqueing update " + obj.getName() + " event " + event); ObjectQueueInfo objInfo = new ObjectQueueInfo(); objInfo.obj = obj; @@ -794,35 +691,15 @@ public class Telemetry { // GCSTelemetryStats updates // (used to establish the connection) gcsStatsObj = objMngr.getObject("GCSTelemetryStats"); - if (((String) gcsStatsObj.getField("Status").getValue()) - .compareTo("Connected") != 0) { - if (objInfo.obj.getObjID() != objMngr.getObject( - "GCSTelemetryStats").getObjID()) { + if (((String) gcsStatsObj.getField("Status").getValue()).compareTo("Connected") != 0) { + if (objInfo.obj.getObjID() != objMngr.getObject("GCSTelemetryStats").getObjID()) { if (DEBUG) - Log.d(TAG, - "transactionCompleted(false) due to receiving object not GCSTelemetryStats while not connected."); + Log.d(TAG, "transactionCompleted(false) due to receiving object not GCSTelemetryStats while not connected."); objInfo.obj.transactionCompleted(false); return; } } - // 2. Setup transaction (skip if unpack event) - if (objInfo.event != EV_UNPACKED) { - UAVObject.Metadata metadata = objInfo.obj.getMetadata(); - transInfo.obj = objInfo.obj; - transInfo.allInstances = objInfo.allInstances; - transInfo.retriesRemaining = MAX_RETRIES; - transInfo.acked = metadata.GetGcsTelemetryAcked(); - if (objInfo.event == EV_UPDATED - || objInfo.event == EV_UPDATED_MANUAL) { - transInfo.objRequest = false; - } else if (objInfo.event == EV_UPDATE_REQ) { - transInfo.objRequest = true; - } - // Start transaction - transPending = true; - } - // If this is a metaobject then make necessary telemetry updates // (this is why we catch unpack) if (objInfo.obj.isMetadata()) { @@ -830,35 +707,64 @@ public class Telemetry { updateObject(metaobj.getParentObject()); } - // 3. Execute transaction - if (transPending) { - try { + // 2. Setup transaction (skip if unpack event) + ObjectTransactionInfo newTrans = new ObjectTransactionInfo(); + boolean newTransactionPending = false; + if (objInfo.event != EV_UNPACKED) { + UAVObject.Metadata metadata = objInfo.obj.getMetadata(); + newTrans.obj = objInfo.obj; + newTrans.allInstances = objInfo.allInstances; + newTrans.retriesRemaining = MAX_RETRIES; + newTrans.acked = metadata.GetGcsTelemetryAcked(); + if (objInfo.event == EV_UPDATED || objInfo.event == EV_UPDATED_MANUAL) { + newTrans.objRequest = false; + } else if (objInfo.event == EV_UPDATE_REQ) { + newTrans.objRequest = true; + } + + // Determine if this will schedule a new transaction + newTransactionPending = !(newTrans.objRequest || newTrans.acked); + + // If there is a transaction pending and this would set up a new one reschedule it + if (transPending && newTransactionPending) { + if (WARN) Log.w(TAG, "Postponing transaction for" + newTrans.obj.getName()); + handler.postDelayed(this, 100); + return; + } + + synchronized (transInfo) { + transPending = newTransactionPending; + transInfo = newTrans; + if (DEBUG) Log.d(TAG, "Process Object transaction for " + transInfo.obj.getName()); - // Initiate transaction - if (transInfo.objRequest) { - if (DEBUG) Log.d(TAG, "Sending object request"); - utalk.sendObjectRequest(transInfo.obj, transInfo.allInstances); - } else { - if (DEBUG) Log.d(TAG, "Sending object " + transInfo.obj.getName() + " " + transInfo.obj.toStringData()); - utalk.sendObject(transInfo.obj, transInfo.acked, transInfo.allInstances); + // 3. Execute transaction + try { + + // Initiate transaction + if (transInfo.objRequest) { + if (DEBUG) Log.d(TAG, "Sending object request"); + utalk.sendObjectRequest(transInfo.obj, transInfo.allInstances); + } else { + if (DEBUG) Log.d(TAG, "Sending object " + transInfo.obj.getName() + " " + transInfo.obj.toStringData()); + utalk.sendObject(transInfo.obj, transInfo.acked, transInfo.allInstances); + } + + + // TODO: Block if request expected (??) + if (transPending) { + transTimerSetPeriod(REQ_TIMEOUT_MS); + } else if (transTimer != null) { + synchronized(transTimer) { + transTimer.cancel(); + transPending = false; + } + } + } catch (IOException e) { + // TODO Auto-generated catch block + Log.e(TAG, "E"); + e.printStackTrace(); } - - - // TODO: Block if request expected (??) - if (transInfo.objRequest || transInfo.acked ) { - transTimerSetPeriod(REQ_TIMEOUT_MS); - } else { - synchronized(transTimer) { - transTimer.cancel(); - transPending = false; - } - } - - } catch (IOException e) { - // TODO Auto-generated catch block - Log.e(TAG, "E"); - e.printStackTrace(); } } } From 7f028f6d42446476c63d47e02220477e81a9f25e Mon Sep 17 00:00:00 2001 From: James Cotton Date: Wed, 15 Aug 2012 01:14:57 -0500 Subject: [PATCH 16/24] AndroidGCS Telemetry: Use a runnable for the transaction timeout. Now all of telemetry is using handlers nicely, but we still can have multiple transactions queued for the same object. --- .../src/org/openpilot/uavtalk/Telemetry.java | 286 ++++++++---------- 1 file changed, 118 insertions(+), 168 deletions(-) diff --git a/androidgcs/src/org/openpilot/uavtalk/Telemetry.java b/androidgcs/src/org/openpilot/uavtalk/Telemetry.java index e514e61c4..10885f746 100644 --- a/androidgcs/src/org/openpilot/uavtalk/Telemetry.java +++ b/androidgcs/src/org/openpilot/uavtalk/Telemetry.java @@ -126,7 +126,7 @@ public class Telemetry { ListIterator> li = objs.listIterator(); while (li.hasNext()) registerObject(li.next().get(0)); // we only need to register one - // instance per object type + // instance per object type // Listen to new object creations objMngr.addNewInstanceObserver(new Observer() { @@ -146,25 +146,15 @@ public class Telemetry { utalk.setOnTransactionCompletedListener(utalk.new OnTransactionCompletedListener() { @Override void TransactionSucceeded(UAVObject data) { - try { - transactionCompleted(data, true); - } catch (IOException e) { - // Disconnect when stream fails - utalk.setOnTransactionCompletedListener(null); - } + transactionCompleted(data, true); } @Override void TransactionFailed(UAVObject data) { - try { - if (DEBUG) - Log.d(TAG, "TransactionFailed(" + data.getName() + ")"); + if (DEBUG) + Log.d(TAG, "TransactionFailed(" + data.getName() + ")"); - transactionCompleted(data, false); - } catch (IOException e) { - // Disconnect when stream fails - utalk.setOnTransactionCompletedListener(null); - } + transactionCompleted(data, false); } }); @@ -182,28 +172,6 @@ public class Telemetry { txRetries = 0; } - synchronized void transTimerSetPeriod(int periodMs) { - if (transTimerTask != null) - transTimerTask.cancel(); - - if (transTimer != null) - transTimer.purge(); - - transTimer = new Timer(); - - transTimerTask = new TimerTask() { - @Override - public void run() { - try { - transactionTimeout(); - } catch (IOException e) { - cancel(); - } - } - }; - transTimer.schedule(transTimerTask, periodMs, periodMs); - } - synchronized void updateTimerSetPeriod(int periodMs) { if (updateTimer != null) { updateTimer.cancel(); @@ -354,7 +322,7 @@ public class Telemetry { eventMask = EV_UPDATED_MANUAL | EV_UPDATE_REQ; if (obj.isMetadata()) eventMask |= EV_UNPACKED; // we also need to act on remote - // updates (unpack events) + // updates (unpack events) connectToObjectInstances(obj, eventMask); } else if (metadata.GetGcsTelemetryUpdateMode() == UAVObject.UpdateMode.UPDATEMODE_ONCHANGE) { @@ -364,7 +332,7 @@ public class Telemetry { 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) + // updates (unpack events) connectToObjectInstances(obj, eventMask); } else if (metadata.GetGcsTelemetryUpdateMode() == UAVObject.UpdateMode.UPDATEMODE_THROTTLED) { @@ -376,106 +344,12 @@ public class Telemetry { eventMask = EV_UPDATED_MANUAL | EV_UPDATE_REQ; if (obj.isMetadata()) eventMask |= EV_UNPACKED; // we also need to act on remote - // updates (unpack events) + // updates (unpack events) connectToObjectInstances(obj, eventMask); } } - /** - * Called when a transaction is successfully completed (uavtalk event) - * - * @throws IOException - */ - private void transactionCompleted(UAVObject obj, boolean result) - throws IOException { - if (DEBUG) Log.d(TAG, "UAVTalk transactionCompleted"); - - // Check if there is a pending transaction and the objects match - if (transPending && transInfo.obj.getObjID() == obj.getObjID()) { - if (DEBUG) Log.d(TAG, "Telemetry: transaction completed for " + obj.getName()); - - // Complete transaction - synchronized(transTimer) { - transTimer.cancel(); - transPending = false; - } - - //Send signal - obj.transactionCompleted(result); - } else { - if (ERROR) Log.e(TAG, "Error: received a transaction completed when did not expect it."); - transPending = false; - } - } - - /** - * Called when a transaction is not completed within the timeout period - * (timer event) - * - * @throws IOException - */ - private void transactionTimeout() throws IOException { - if (DEBUG) - Log.d(TAG, "Telemetry: transaction timeout."); - synchronized (transTimer) { - transTimer.cancel(); - // Proceed only if there is a pending transaction - if (transPending) { - // Check if more retries are pending - if (transInfo.retriesRemaining > 0) { - --transInfo.retriesRemaining; - processObjectTransaction(); - ++txRetries; - } else { - if (ERROR) - Log.e(TAG, "Transaction failed for: " + transInfo.obj.getName()); - - // Terminate transaction. This triggers UAVTalk to send a transaction - // failed signal which will make the next queue entry be processed - // Note this is UAVTalk listener TransactionFailed function - // object specific transaction failed. - utalk.cancelPendingTransaction(transInfo.obj); - ++txErrors; - } - } - } - } - - /** - * Start an object transaction with UAVTalk, all information is stored in - * transInfo - * - * @throws IOException - */ - private void processObjectTransaction() throws IOException { - if (transPending) { - if (DEBUG) - Log.d(TAG, "Process Object transaction for " + transInfo.obj.getName()); - - // Initiate transaction - if (transInfo.objRequest) { - utalk.sendObjectRequest(transInfo.obj, transInfo.allInstances); - } else { - utalk.sendObject(transInfo.obj, transInfo.acked, - transInfo.allInstances); - } - - // Start timer if a response is expected - if (transInfo.objRequest || transInfo.acked) { - transTimerSetPeriod(REQ_TIMEOUT_MS); - } else { - synchronized (transTimer) { - transTimer.cancel(); - transPending = false; - } - } - } else { - if (ERROR) - Log.e(TAG, "Error: inside of processObjectTransaction with no transPending"); - } - } - /** * Check is any objects are pending for periodic updates TODO: Clean-up * @@ -513,6 +387,8 @@ public class Telemetry { - offset; // Send object startTime = System.currentTimeMillis(); + + if (DEBUG) Log.d(TAG, "Manual update: " + objinfo.obj.getName()); handler.updatedManual(objinfo.obj); // enqueueObjectUpdates(objinfo.obj, EV_UPDATED_MANUAL, // true, false); @@ -584,12 +460,6 @@ public class Telemetry { if (updateTimer != null) updateTimer.cancel(); updateTimer = null; - if (transTimerTask != null) - transTimerTask.cancel(); - transTimerTask = null; - if (transTimer != null) - transTimer.cancel(); - transTimer = null; } /** @@ -604,8 +474,6 @@ public class Telemetry { private Timer updateTimer; private TimerTask updateTimerTask; - private Timer transTimer; - private TimerTask transTimerTask; private int timeToNextUpdateMs; private int txErrors; @@ -664,14 +532,15 @@ public class Telemetry { } + /** + * Perform an update on an object where on an event based on the contents provided + * to the constructors. This update will also set a timeout for transaction failure. + */ class ObjectRunnable implements Runnable { // ! Transaction information to perform private final ObjectQueueInfo objInfo; - // private final ObjectTransactionInfo transInfo = new - // ObjectTransactionInfo(); - ObjectRunnable(ObjectQueueInfo info) { Assert.assertNotNull(info); objInfo = info; @@ -725,48 +594,129 @@ public class Telemetry { // Determine if this will schedule a new transaction newTransactionPending = !(newTrans.objRequest || newTrans.acked); - // If there is a transaction pending and this would set up a new one reschedule it - if (transPending && newTransactionPending) { - if (WARN) Log.w(TAG, "Postponing transaction for" + newTrans.obj.getName()); - handler.postDelayed(this, 100); - return; - } - synchronized (transInfo) { + + // If there is a transaction pending and this would set up a new one reschedule it + if (transPending && newTransactionPending) { + if (WARN) Log.w(TAG, "Postponing transaction for" + newTrans.obj.getName()); + handler.postDelayed(this, 100); + return; + } + + // Store this as the active transaction transPending = newTransactionPending; transInfo = newTrans; if (DEBUG) Log.d(TAG, "Process Object transaction for " + transInfo.obj.getName()); - // 3. Execute transaction try { - // Initiate transaction + // 3. Execute transaction by sending the appropriate UAVTalk command if (transInfo.objRequest) { - if (DEBUG) Log.d(TAG, "Sending object request"); + if (DEBUG) Log.d(TAG, "Sending object request" + transInfo.obj.getName()); utalk.sendObjectRequest(transInfo.obj, transInfo.allInstances); } else { - if (DEBUG) Log.d(TAG, "Sending object " + transInfo.obj.getName() + " " + transInfo.obj.toStringData()); + if (DEBUG) Log.d(TAG, "Sending object " + transInfo.obj.getName()); utalk.sendObject(transInfo.obj, transInfo.acked, transInfo.allInstances); } - - // TODO: Block if request expected (??) - if (transPending) { - transTimerSetPeriod(REQ_TIMEOUT_MS); - } else if (transTimer != null) { - synchronized(transTimer) { - transTimer.cancel(); - transPending = false; - } - } } catch (IOException e) { - // TODO Auto-generated catch block - Log.e(TAG, "E"); + if (ERROR) Log.e(TAG, "Unable to send UAVTalk message"); e.printStackTrace(); } + + // Post a timeout timer if a response is epxected + if (transPending) + handler.postDelayed(transactionTimeout, REQ_TIMEOUT_MS); } } } } + + + /** + * Runnable posted to handle a timeout of a transaction. Tracks the number of retry attempts + * retries that many, and finally sends a transaction failed signal. + */ + final Runnable transactionTimeout = new Runnable() { + @Override + public void run() { + // Lock on the transaction + synchronized (transInfo) { + + // Proceed only if there is a pending transaction + if (!transPending) { + if (WARN) Log.w(TAG,"Transaction completed but timeout still called. Probable race condition"); + return; + } + + if (DEBUG) Log.d(TAG, "Telemetry: transaction timeout."); + + // Check if more retries are pending + if (transInfo.retriesRemaining > 0) { + --transInfo.retriesRemaining; + + // Repeat whatever is required for this transaction type + // (transInfo.objRequest) { + if (DEBUG) Log.d(TAG, "Sending object request"); + + try { + // 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); + } else { + if (DEBUG) Log.d(TAG, "Sending object " + transInfo.obj.getName()); + utalk.sendObject(transInfo.obj, transInfo.acked, transInfo.allInstances); + } + } catch (IOException e) { + if (ERROR) Log.e(TAG, "Unable to send UAVTalk message"); + e.printStackTrace(); + } + + handler.postDelayed(transactionTimeout, REQ_TIMEOUT_MS); + + ++txRetries; + } else { + if (ERROR) Log.e(TAG, "Transaction failed for: " + transInfo.obj.getName()); + + // Terminate transaction. This triggers UAVTalk to send a transaction + // failed signal which will make the next queue entry be processed + // Note this is UAVTalk listener TransactionFailed function + // object specific transaction failed. + utalk.cancelPendingTransaction(transInfo.obj); + ++txErrors; + } + } + } + }; + + + /** + * Called when a transaction is successfully completed (UAVTalk event) and maps that to + * the appropriate object event as well as canceling the pending transaction and timeout + */ + private void transactionCompleted(UAVObject obj, boolean result) { + + if (DEBUG) Log.d(TAG, "UAVTalk transactionCompleted"); + + // Check if there is a pending transaction and the objects match + synchronized(transInfo) { + if (transPending && transInfo.obj.getObjID() == obj.getObjID()) { + if (DEBUG) Log.d(TAG, "Telemetry: transaction completed for " + obj.getName()); + + // Cancel timeout and complete transaction + handler.removeCallbacks(transactionTimeout); + transPending = false; + + //Send signal + obj.transactionCompleted(result); + } else { + if (ERROR) Log.e(TAG, "Error: received a transaction completed when did not expect it."); + transPending = false; + } + } + } + } + From 8a5819379a4fe16e0c22689c45e98b8383e522cb Mon Sep 17 00:00:00 2001 From: James Cotton Date: Wed, 15 Aug 2012 02:02:05 -0500 Subject: [PATCH 17/24] AndroidGCS Telemetry: Fix the determination of whether a transaction is pending --- .../src/org/openpilot/uavtalk/Telemetry.java | 42 ++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/androidgcs/src/org/openpilot/uavtalk/Telemetry.java b/androidgcs/src/org/openpilot/uavtalk/Telemetry.java index 10885f746..6fcbb2491 100644 --- a/androidgcs/src/org/openpilot/uavtalk/Telemetry.java +++ b/androidgcs/src/org/openpilot/uavtalk/Telemetry.java @@ -30,8 +30,10 @@ import java.util.List; import java.util.ListIterator; import java.util.Observable; import java.util.Observer; +import java.util.Queue; import java.util.Timer; import java.util.TimerTask; +import java.util.concurrent.ConcurrentLinkedQueue; import junit.framework.Assert; import android.os.Handler; @@ -487,8 +489,14 @@ public class Telemetry { private static final int MAX_UPDATE_PERIOD_MS = 1000; private static final int MIN_UPDATE_PERIOD_MS = 1; - private final ObjectUpdateHandler handler; + static private ObjectUpdateHandler handler; + //! Accessor for the object updated handler + ObjectUpdateHandler getHandler() { return handler; } + + /** + * Handler which posts all the messages for individual object updates + */ public class ObjectUpdateHandler extends Handler { // ! This can only be created while attaching to a particular looper @@ -496,6 +504,8 @@ public class Telemetry { super(l); } + Queue objQueue = new ConcurrentLinkedQueue(); + // ! Generic enqueue void enqueueObjectUpdates(UAVObject obj, int event, boolean allInstances, boolean priority) { @@ -507,7 +517,26 @@ public class Telemetry { objInfo.event = event; objInfo.allInstances = allInstances; - post(new ObjectRunnable(objInfo)); + // For now maintain a list of objects in the queue so we don't add duplicates + // later we should make the runnables static to each class so we can use removeCallback + synchronized(objQueue) { + if (objQueue.contains(objInfo)) { + if (WARN) Log.w(TAG, "Found previously scheduled queue element: " + objInfo.obj.getName()); + } else { + objQueue.add(objInfo); + post(new ObjectRunnable(objInfo)); + } + } + } + + public boolean removeActivatedQueue(ObjectQueueInfo objInfo) { + synchronized(objQueue) { + if (objQueue.remove(objInfo)) { + if (WARN) Log.w(TAG, "Unable to find queue element to remove"); + return false; + } + } + return true; } // ! Enqueue an unpacked event @@ -592,13 +621,13 @@ public class Telemetry { } // Determine if this will schedule a new transaction - newTransactionPending = !(newTrans.objRequest || newTrans.acked); + newTransactionPending = (newTrans.objRequest || newTrans.acked); synchronized (transInfo) { // If there is a transaction pending and this would set up a new one reschedule it if (transPending && newTransactionPending) { - if (WARN) Log.w(TAG, "Postponing transaction for" + newTrans.obj.getName()); + if (WARN) Log.w(TAG, "Postponing transaction for" + newTrans.obj.getName() + " existing transaction for " + transInfo.obj.getName()); handler.postDelayed(this, 100); return; } @@ -609,11 +638,14 @@ public class Telemetry { if (DEBUG) Log.d(TAG, "Process Object transaction for " + transInfo.obj.getName()); + // Remove this one from the list of pending transactions + handler.removeActivatedQueue(objInfo); + try { // 3. Execute transaction by sending the appropriate UAVTalk command if (transInfo.objRequest) { - if (DEBUG) Log.d(TAG, "Sending object request" + transInfo.obj.getName()); + if (DEBUG) Log.d(TAG, "Sending object request " + transInfo.obj.getName()); utalk.sendObjectRequest(transInfo.obj, transInfo.allInstances); } else { if (DEBUG) Log.d(TAG, "Sending object " + transInfo.obj.getName()); From d186069ebd282978a4075fd0cbcc5fcdf35f8a0b Mon Sep 17 00:00:00 2001 From: James Cotton Date: Sun, 19 Aug 2012 12:48:48 -0500 Subject: [PATCH 18/24] Update hardcoded OSG paths. Very dirty I need to fix this properly. --- .../src/plugins/osgearthview/osgviewerwidget.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ground/openpilotgcs/src/plugins/osgearthview/osgviewerwidget.cpp b/ground/openpilotgcs/src/plugins/osgearthview/osgviewerwidget.cpp index b15862b50..554c0ffd7 100644 --- a/ground/openpilotgcs/src/plugins/osgearthview/osgviewerwidget.cpp +++ b/ground/openpilotgcs/src/plugins/osgearthview/osgviewerwidget.cpp @@ -109,7 +109,7 @@ OsgViewerWidget::OsgViewerWidget(QWidget *parent) : QWidget(parent) setAttribute(Qt::WA_PaintOnScreen, true); osg::Group* root = new osg::Group; - osg::Node* earth = osgDB::readNodeFile("/Users/jcotton81/Documents/Programming/osgearth/tests/boston.earth"); + osg::Node* earth = osgDB::readNodeFile("/Users/Cotton/Programming/osg/osgearth/tests/boston.earth"); mapNode = osgEarth::MapNode::findMapNode( earth ); if (!mapNode) { @@ -225,10 +225,10 @@ osg::Node* OsgViewerWidget::createAirplane() case SystemSettings::AIRFRAMETYPE_FIXEDWING: case SystemSettings::AIRFRAMETYPE_FIXEDWINGELEVON: case SystemSettings::AIRFRAMETYPE_FIXEDWINGVTAIL: - uav = osgDB::readNodeFile("/Users/jcotton81/Documents/Programming/OpenPilot/artwork/3D Model/planes/Easystar/easystar.3ds"); + uav = osgDB::readNodeFile("/Users/Cotton/Programming/OpenPilot/artwork/3D Model/planes/Easystar/easystar.3ds"); break; default: - uav = osgDB::readNodeFile("/Users/jcotton81/Documents/Programming/OpenPilot/artwork/3D Model/multi/joes_cnc/J14-QT_+.3DS"); + uav = osgDB::readNodeFile("/Users/Cotton/Programming/OpenPilot/artwork/3D Model/multi/joes_cnc/J14-QT_+.3DS"); } if(uav) { From 237ec188f93205383a84c7e8515232472c221c78 Mon Sep 17 00:00:00 2001 From: James Cotton Date: Sun, 19 Aug 2012 21:17:37 -0500 Subject: [PATCH 19/24] GCS: When PipX is detected do not make it the active configuration tab to prevent the dialog about dirty tabs appearing. --- ground/openpilotgcs/src/plugins/config/configgadgetwidget.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/ground/openpilotgcs/src/plugins/config/configgadgetwidget.cpp b/ground/openpilotgcs/src/plugins/config/configgadgetwidget.cpp index 9bf8bed0a..4a3b27e38 100644 --- a/ground/openpilotgcs/src/plugins/config/configgadgetwidget.cpp +++ b/ground/openpilotgcs/src/plugins/config/configgadgetwidget.cpp @@ -225,7 +225,6 @@ void ConfigGadgetWidget::updatePipXStatus(UAVObject *) qDebug()<<"ConfigGadgetWidget onPipxtremeConnect"; QWidget *qwd = new ConfigPipXtremeWidget(this); ftw->insertTab(ConfigGadgetWidget::pipxtreme, qwd, QIcon(":/configgadget/images/PipXtreme.png"), QString("PipXtreme")); - ftw->setCurrentIndex(ConfigGadgetWidget::pipxtreme); pipxConnected = true; } } From 4a0d43cb54bb8566bea95ef2dca8860157139e9b Mon Sep 17 00:00:00 2001 From: James Cotton Date: Sun, 19 Aug 2012 21:17:37 -0500 Subject: [PATCH 20/24] GCS: When PipX is detected do not make it the active configuration tab to prevent the dialog about dirty tabs appearing. Conflicts: ground/openpilotgcs/src/plugins/config/configgadgetwidget.cpp --- .../src/plugins/config/configgadgetwidget.cpp | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/ground/openpilotgcs/src/plugins/config/configgadgetwidget.cpp b/ground/openpilotgcs/src/plugins/config/configgadgetwidget.cpp index c12dbaee8..c466df760 100644 --- a/ground/openpilotgcs/src/plugins/config/configgadgetwidget.cpp +++ b/ground/openpilotgcs/src/plugins/config/configgadgetwidget.cpp @@ -218,17 +218,15 @@ void ConfigGadgetWidget::tabAboutToChange(int i,bool * proceed) */ void ConfigGadgetWidget::updatePipXStatus(UAVObject *object) { - - // Restart the disconnection timer. - pipxTimeout->start(5000); - if (!pipxConnected) - { - qDebug()<<"ConfigGadgetWidget onPipxtremeConnect"; - QWidget *qwd = new ConfigPipXtremeWidget(this); - ftw->insertTab(ConfigGadgetWidget::pipxtreme, qwd, QIcon(":/configgadget/images/PipXtreme.png"), QString("PipXtreme")); - ftw->setCurrentIndex(ConfigGadgetWidget::pipxtreme); - pipxConnected = true; - } + // Restart the disconnection timer. + pipxTimeout->start(5000); + if (!pipxConnected) + { + qDebug()<<"ConfigGadgetWidget onPipxtremeConnect"; + QWidget *qwd = new ConfigPipXtremeWidget(this); + ftw->insertTab(ConfigGadgetWidget::pipxtreme, qwd, QIcon(":/configgadget/images/PipXtreme.png"), QString("PipXtreme")); + pipxConnected = true; + } } void ConfigGadgetWidget::onPipxtremeDisconnect() { From d58831d3f1d9cb73721174480c3fc72dda3e47c4 Mon Sep 17 00:00:00 2001 From: James Cotton Date: Mon, 20 Aug 2012 15:29:03 -0500 Subject: [PATCH 21/24] GCS: Add conditial flag to include OGS plugins. Must compile with qmake CONFIG+=OSG To include this module from the command line: make GCS_QMAKE_OPTS="CONFIG+=OSG" -j7 gcs --- Makefile | 2 +- ground/openpilotgcs/src/plugins/plugins.pro | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 47b9a0a3c..2087e7fc4 100644 --- a/Makefile +++ b/Makefile @@ -473,7 +473,7 @@ gcs_clean: openpilotgcs_clean openpilotgcs: uavobjects_gcs $(V1) mkdir -p $(BUILD_DIR)/ground/$@ $(V1) ( cd $(BUILD_DIR)/ground/$@ && \ - $(QMAKE) $(ROOT_DIR)/ground/openpilotgcs/openpilotgcs.pro -spec $(QT_SPEC) -r CONFIG+=$(GCS_BUILD_CONF) && \ + $(QMAKE) $(ROOT_DIR)/ground/openpilotgcs/openpilotgcs.pro -spec $(QT_SPEC) -r CONFIG+=$(GCS_BUILD_CONF) $(GCS_QMAKE_OPTS) && \ $(MAKE) -w ; \ ) diff --git a/ground/openpilotgcs/src/plugins/plugins.pro b/ground/openpilotgcs/src/plugins/plugins.pro index f441d52b3..8580ba2ea 100644 --- a/ground/openpilotgcs/src/plugins/plugins.pro +++ b/ground/openpilotgcs/src/plugins/plugins.pro @@ -197,11 +197,13 @@ plugin_uavobjectutil.depends += plugin_uavobjects SUBDIRS += plugin_uavobjectutil # OSG Earth View plugin -plugin_osgearthview.subdir = osgearthview -plugin_osgearthview.depends = plugin_coreplugin -plugin_osgearthview.depends += plugin_uavobjects -plugin_osgearthview.depends += plugin_uavobjectwidgetutils -SUBDIRS += plugin_osgearthview +OSG { + plugin_osgearthview.subdir = osgearthview + plugin_osgearthview.depends = plugin_coreplugin + plugin_osgearthview.depends += plugin_uavobjects + plugin_osgearthview.depends += plugin_uavobjectwidgetutils + SUBDIRS += plugin_osgearthview +} # Magic Waypoint gadget plugin_magicwaypoint.subdir = magicwaypoint From 789746151ab2985d7feb169923070377c54a0a4b Mon Sep 17 00:00:00 2001 From: a*morale Date: Sat, 25 Aug 2012 11:29:01 +0200 Subject: [PATCH 22/24] Fixes the selection of the oversample settings from the pios_ms5611_cfg.oversapling configuration --- flight/PiOS/Common/pios_ms5611.c | 7 +++---- flight/Revolution/System/pios_board.c | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/flight/PiOS/Common/pios_ms5611.c b/flight/PiOS/Common/pios_ms5611.c index 1d06f1dea..41bf7c80e 100644 --- a/flight/PiOS/Common/pios_ms5611.c +++ b/flight/PiOS/Common/pios_ms5611.c @@ -56,7 +56,6 @@ static uint32_t oversampling; static const struct pios_ms5611_cfg * dev_cfg; static int32_t i2c_id; -static enum pios_ms5611_osr osr = MS5611_OSR_256; /** * Initialise the MS5611 sensor @@ -90,10 +89,10 @@ int32_t PIOS_MS5611_StartADC(ConversionTypeTypeDef Type) { /* Start the conversion */ if (Type == TemperatureConv) { - while (PIOS_MS5611_WriteCommand(MS5611_TEMP_ADDR + osr) != 0) + while (PIOS_MS5611_WriteCommand(MS5611_TEMP_ADDR + oversampling) != 0) continue; } else if (Type == PressureConv) { - while (PIOS_MS5611_WriteCommand(MS5611_PRES_ADDR + osr) != 0) + while (PIOS_MS5611_WriteCommand(MS5611_PRES_ADDR + oversampling) != 0) continue; } @@ -106,7 +105,7 @@ int32_t PIOS_MS5611_StartADC(ConversionTypeTypeDef Type) * @brief Return the delay for the current osr */ int32_t PIOS_MS5611_GetDelay() { - switch(osr) { + switch(oversampling) { case MS5611_OSR_256: return 2; case MS5611_OSR_512: diff --git a/flight/Revolution/System/pios_board.c b/flight/Revolution/System/pios_board.c index b9360bfef..b098f3cd0 100644 --- a/flight/Revolution/System/pios_board.c +++ b/flight/Revolution/System/pios_board.c @@ -131,7 +131,7 @@ static const struct pios_hmc5883_cfg pios_hmc5883_cfg = { #if defined(PIOS_INCLUDE_MS5611) #include "pios_ms5611.h" static const struct pios_ms5611_cfg pios_ms5611_cfg = { - .oversampling = 1, + .oversampling = MS5611_OSR_512, }; #endif /* PIOS_INCLUDE_MS5611 */ From 257b00a400de345c69b0df823ddf31974b85694b Mon Sep 17 00:00:00 2001 From: a*morale Date: Sat, 25 Aug 2012 12:28:25 +0200 Subject: [PATCH 23/24] Fixed a typo on I2C speed for the MS5611 --- flight/board_hw_defs/revolution/board_hw_defs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flight/board_hw_defs/revolution/board_hw_defs.c b/flight/board_hw_defs/revolution/board_hw_defs.c index b54dd119d..2bc703cd8 100644 --- a/flight/board_hw_defs/revolution/board_hw_defs.c +++ b/flight/board_hw_defs/revolution/board_hw_defs.c @@ -1224,7 +1224,7 @@ static const struct pios_i2c_adapter_cfg pios_i2c_pressure_adapter_cfg = { .I2C_Ack = I2C_Ack_Enable, .I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit, .I2C_DutyCycle = I2C_DutyCycle_2, - .I2C_ClockSpeed = 40000, /* bits/s */ + .I2C_ClockSpeed = 400000, /* bits/s */ }, .transfer_timeout_ms = 50, .scl = { From c696cccff8507c58c060089d231317e2fd931659 Mon Sep 17 00:00:00 2001 From: a*morale Date: Sat, 25 Aug 2012 13:04:41 +0200 Subject: [PATCH 24/24] Added an option to be able to change the interleave between Pressure and Temperature conversion for the MS5611 To enable this option define the PIOS_MS5611_SLOW_TEMP_RATE with the number of Pressure conversion for each Temperature conversion. --- flight/Modules/Altitude/revolution/altitude.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/flight/Modules/Altitude/revolution/altitude.c b/flight/Modules/Altitude/revolution/altitude.c index 7f9052038..54b241408 100644 --- a/flight/Modules/Altitude/revolution/altitude.c +++ b/flight/Modules/Altitude/revolution/altitude.c @@ -100,6 +100,13 @@ static void altitudeTask(void *parameters) // TODO: Check the pressure sensor and set a warning if it fails test +// Option to change the interleave between Temp and Pressure conversions +// Undef for normal operation +//#define PIOS_MS5611_SLOW_TEMP_RATE 20 + +#ifdef PIOS_MS5611_SLOW_TEMP_RATE + uint8_t temp_press_interleave_count = 1; +#endif // Main task loop while (1) { @@ -128,11 +135,20 @@ static void altitudeTask(void *parameters) } #endif float temp, press; - +#ifdef PIOS_MS5611_SLOW_TEMP_RATE + temp_press_interleave_count --; + if(temp_press_interleave_count == 0) + { +#endif // Update the temperature data PIOS_MS5611_StartADC(TemperatureConv); vTaskDelay(PIOS_MS5611_GetDelay()); PIOS_MS5611_ReadADC(); + +#ifdef PIOS_MS5611_SLOW_TEMP_RATE + temp_press_interleave_count=PIOS_MS5611_SLOW_TEMP_RATE; + } +#endif // Update the pressure data PIOS_MS5611_StartADC(PressureConv);