mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2025-02-20 10:54:14 +01:00
Merge branch 'android_hid' into android_revo
Conflicts: androidgcs/src/org/openpilot/androidgcs/telemetry/OPTelemetryService.java
This commit is contained in:
commit
6fe2cb7738
@ -10,17 +10,26 @@
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
|
||||
<uses-feature android:name="android.hardware.usb.host" />
|
||||
|
||||
<application android:icon="@drawable/ic_logo" android:label="@string/app_name" android:theme="@android:style/Theme.Holo">
|
||||
<!-- for map overlay -->
|
||||
<uses-library android:name="com.google.android.maps" />
|
||||
|
||||
<!-- Object browser - main activity at the moment -->
|
||||
<activity android:name="HomePage" android:label="@string/app_name">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- <intent-filter> -->
|
||||
<!-- <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> -->
|
||||
<!-- </intent-filter> -->
|
||||
|
||||
<!-- <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" -->
|
||||
<!-- android:resource="@xml/device_filter" /> -->
|
||||
</activity>
|
||||
|
||||
<activity android:name="ObjectBrowser" android:label="@string/object_browser_name" />
|
||||
|
@ -5,11 +5,13 @@
|
||||
<item>Fake</item>
|
||||
<item>Bluetooth</item>
|
||||
<item>Network</item>
|
||||
<item>HID</item>
|
||||
</string-array>
|
||||
<string-array name="connectTypeValues">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
</string-array>
|
||||
<item>4</item>
|
||||
</string-array>
|
||||
</resources>
|
9
androidgcs/res/xml/device_filter.xml
Normal file
9
androidgcs/res/xml/device_filter.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<resources>
|
||||
<usb-device vendor-id="8352" product-id="16730" />
|
||||
<usb-device vendor-id="8352" product-id="16731" />
|
||||
<usb-device vendor-id="8352" product-id="16732" />
|
||||
<usb-device vendor-id="8352" product-id="16733" />
|
||||
<usb-device vendor-id="8352" product-id="16734" />
|
||||
</resources>
|
@ -35,7 +35,6 @@ import org.openpilot.uavtalk.UAVObject;
|
||||
import org.openpilot.uavtalk.UAVObjectField;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
@ -72,8 +71,6 @@ public class Controller extends ObjectManagerActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.controller);
|
||||
TextView manualView = (TextView) findViewById(R.id.manualControlValues);
|
||||
if(manualView != null)
|
||||
manualView.setText("Hello");
|
||||
}
|
||||
|
||||
Observer settingsUpdated = new Observer() {
|
||||
@ -93,19 +90,16 @@ public class Controller extends ObjectManagerActivity {
|
||||
void onOPConnected() {
|
||||
super.onOPConnected();
|
||||
|
||||
Log.d(TAG, "onOPConnected()");
|
||||
|
||||
// Subscribe to updates from ManualControlCommand and show the values for crude feedback
|
||||
UAVDataObject manualControl = (UAVDataObject) objMngr.getObject("ManualControlCommand");
|
||||
if(manualControl != null) {
|
||||
manualControl.addUpdatedObserver(updatedObserver);
|
||||
}
|
||||
registerObjectUpdates(manualControl);
|
||||
|
||||
|
||||
UAVDataObject manualControlSettings = (UAVDataObject) objMngr.getObject("ManualControlSettings");
|
||||
if(manualControlSettings != null) {
|
||||
Log.d(TAG, "Requested settings update");
|
||||
manualControlSettings.addUpdatedObserver(updatedObserver);
|
||||
manualControlSettings.updateRequested();
|
||||
}
|
||||
// Request a one time update before configuring for GCS control mode
|
||||
UAVDataObject manualSettings = (UAVDataObject) objMngr.getObject("ManualControlSettings");
|
||||
manualSettings.addUpdatedObserver(settingsUpdated);
|
||||
manualSettings.updateRequested();
|
||||
|
||||
final double MOVEMENT_RANGE = 50.0;
|
||||
DualJoystickView joystick = (DualJoystickView) findViewById(R.id.dualjoystickView);
|
||||
@ -138,7 +132,9 @@ public class Controller extends ObjectManagerActivity {
|
||||
public void OnReleased() { rightJoystickHeld = false; throttle = -1; updated = true; }
|
||||
@Override
|
||||
public void OnReturnedToCenter() { }
|
||||
}) ;
|
||||
});
|
||||
|
||||
//! This timer task actually periodically sends updates to the UAV
|
||||
TimerTask controllerTask = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -175,36 +171,19 @@ public class Controller extends ObjectManagerActivity {
|
||||
});
|
||||
}
|
||||
};
|
||||
sendTimer.schedule(controllerTask, 500, 100);
|
||||
sendTimer.schedule(controllerTask, 500, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* The callbacks from the UAVOs must run in the correct thread to update the
|
||||
* UI. This is what using a runnable does.
|
||||
*/
|
||||
final Handler uavobjHandler = new Handler();
|
||||
final Runnable updateText = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateManualControl();
|
||||
}
|
||||
};
|
||||
|
||||
private final Observer updatedObserver = new Observer() {
|
||||
@Override
|
||||
public void update(Observable observable, Object data) {
|
||||
uavobjHandler.post(updateText);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the string description of manual control command
|
||||
*/
|
||||
private void updateManualControl() {
|
||||
UAVDataObject manualControl = (UAVDataObject) objMngr.getObject("ManualControlCommand");
|
||||
TextView manualView = (TextView) findViewById(R.id.manualControlValues);
|
||||
if (manualView != null && manualControl != null)
|
||||
manualView.setText(manualControl.toStringData());
|
||||
@Override
|
||||
protected void objectUpdated(UAVObject obj) {
|
||||
if (obj.getName().compareTo("ManualControlCommand") == 0) {
|
||||
TextView manualView = (TextView) findViewById(R.id.manualControlValues);
|
||||
if (manualView != null)
|
||||
manualView.setText(obj.toStringData());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,705 @@
|
||||
package org.openpilot.androidgcs.telemetry;
|
||||
|
||||
// Code based on notes from http://torvafirmus-android.blogspot.com/2011/09/implementing-usb-hid-interface-in.html
|
||||
// Taken 2012-08-10
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.hardware.usb.UsbConstants;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbDeviceConnection;
|
||||
import android.hardware.usb.UsbEndpoint;
|
||||
import android.hardware.usb.UsbInterface;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.hardware.usb.UsbRequest;
|
||||
import android.util.Log;
|
||||
|
||||
public class HidUAVTalk extends TelemetryTask {
|
||||
|
||||
private static final String TAG = HidUAVTalk.class.getSimpleName();
|
||||
public static int LOGLEVEL = 0;
|
||||
public static boolean WARN = LOGLEVEL > 1;
|
||||
public static boolean DEBUG = LOGLEVEL > 0;
|
||||
|
||||
//! USB constants
|
||||
static final int OPENPILOT_VENDOR_ID = 0x20A0;
|
||||
|
||||
static final int USB_PRODUCT_ID_OPENPILOT_MAIN = 0x415A;
|
||||
static final int USB_PRODUCT_ID_COPTERCONTROL = 0x415B;
|
||||
static final int USB_PRODUCT_ID_PIPXTREME = 0x415C;
|
||||
static final int USB_PRODUCT_ID_CC3D = 0x415D;
|
||||
static final int USB_PRODUCT_ID_REVOLUTION = 0x415E;
|
||||
static final int USB_PRODUCT_ID_OSD = 0x4194;
|
||||
static final int USB_PRODUCT_ID_SPARE = 0x4195;
|
||||
|
||||
private static final String ACTION_USB_PERMISSION = "com.access.device.USB_PERMISSION";
|
||||
|
||||
UsbDevice currentDevice;
|
||||
|
||||
public HidUAVTalk(OPTelemetryService service) {
|
||||
super(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
|
||||
CleanUpAndClose();
|
||||
//hostDisplayActivity.unregisterReceiver(usbReceiver);
|
||||
telemService.unregisterReceiver(usbPermissionReceiver);
|
||||
((TalkInputStream)inStream).stop();
|
||||
((TalkOutputStream)outStream).stop();
|
||||
|
||||
super.disconnect();
|
||||
|
||||
try {
|
||||
readThread.join();
|
||||
writeThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (readRequest != null) {
|
||||
readRequest.cancel();
|
||||
readRequest.close();
|
||||
readRequest = null;
|
||||
}
|
||||
|
||||
if (writeRequest != null) {
|
||||
writeRequest.cancel();
|
||||
writeRequest.close();
|
||||
writeRequest = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean attemptConnection() {
|
||||
if (DEBUG) Log.d(TAG, "connect()");
|
||||
|
||||
// Register to get permission requested dialog
|
||||
usbManager = (UsbManager) telemService.getSystemService(Context.USB_SERVICE);
|
||||
permissionIntent = PendingIntent.getBroadcast(telemService, 0, new Intent(ACTION_USB_PERMISSION), 0);
|
||||
permissionFilter = new IntentFilter(ACTION_USB_PERMISSION);
|
||||
telemService.registerReceiver(usbPermissionReceiver, permissionFilter);
|
||||
|
||||
// Go through all the devices plugged in
|
||||
HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
|
||||
if (DEBUG) Log.d(TAG, "Found " + deviceList.size() + " devices");
|
||||
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
|
||||
while(deviceIterator.hasNext()){
|
||||
UsbDevice dev = deviceIterator.next();
|
||||
if (DEBUG) Log.d(TAG, "Testing device: " + dev);
|
||||
usbManager.requestPermission(dev, permissionIntent);
|
||||
}
|
||||
|
||||
if (DEBUG) Log.d(TAG, "Registered the deviceAttachedFilter");
|
||||
|
||||
return deviceList.size() > 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Receives a requested broadcast from the operating system.
|
||||
* In this case the following actions are handled:
|
||||
* USB_PERMISSION
|
||||
* UsbManager.ACTION_USB_DEVICE_DETACHED
|
||||
* UsbManager.ACTION_USB_DEVICE_ATTACHED
|
||||
*/
|
||||
private final BroadcastReceiver usbPermissionReceiver = new BroadcastReceiver()
|
||||
{
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent)
|
||||
{
|
||||
if (DEBUG) Log.d(TAG,"Broadcast receiver caught intent: " + intent);
|
||||
String action = intent.getAction();
|
||||
// Validate the action against the actions registered
|
||||
if (ACTION_USB_PERMISSION.equals(action))
|
||||
{
|
||||
// A permission response has been received, validate if the user has
|
||||
// GRANTED, or DENIED permission
|
||||
synchronized (this)
|
||||
{
|
||||
UsbDevice deviceConnected = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||
|
||||
if (DEBUG) Log.d(TAG, "Device Permission requested" + deviceConnected);
|
||||
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false))
|
||||
{
|
||||
// Permission has been granted, so connect to the device
|
||||
// If this fails, then keep looking
|
||||
if (deviceConnected != null)
|
||||
{
|
||||
// call method to setup device communication
|
||||
currentDevice = deviceConnected;
|
||||
if (DEBUG) Log.d(TAG, "Device Permission Acquired" + currentDevice);
|
||||
if (!ConnectToDeviceInterface(currentDevice))
|
||||
{
|
||||
if (DEBUG) Log.d(TAG, "Unable to connect to device");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Permission has not been granted, so keep looking for another
|
||||
// device to be attached....
|
||||
if (DEBUG) Log.d(TAG, "Device Permission Denied" + deviceConnected);
|
||||
currentDevice = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
private final BroadcastReceiver usbReceiver = new BroadcastReceiver()
|
||||
{
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent)
|
||||
{
|
||||
if (DEBUG) Log.d(TAG,"Broadcast receiver taught intent: " + intent);
|
||||
String action = intent.getAction();
|
||||
// Validate the action against the actions registered
|
||||
|
||||
if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action))
|
||||
{
|
||||
// A device has been detached from the device, so close all the connections
|
||||
// and restart the search for a new device being attached
|
||||
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||
if ((device != null) && (currentDevice != null))
|
||||
{
|
||||
if (device.equals(currentDevice))
|
||||
{
|
||||
// call your method that cleans up and closes communication with the device
|
||||
CleanUpAndClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action))
|
||||
{
|
||||
// A device has been attached. If not already connected to a device,
|
||||
// validate if this device is supported
|
||||
UsbDevice searchDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||
if (DEBUG) Log.d(TAG, "Device found" + searchDevice);
|
||||
if ((searchDevice != null) && (currentDevice == null))
|
||||
{
|
||||
// call your method that cleans up and closes communication with the device
|
||||
ValidateFoundDevice(searchDevice);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private UsbEndpoint usbEndpointRead;
|
||||
|
||||
private UsbEndpoint usbEndpointWrite;
|
||||
|
||||
private UsbManager usbManager;
|
||||
|
||||
private PendingIntent permissionIntent;
|
||||
|
||||
private UsbDeviceConnection connectionRead;
|
||||
|
||||
private UsbDeviceConnection connectionWrite;
|
||||
|
||||
private IntentFilter deviceAttachedFilter;
|
||||
|
||||
private IntentFilter permissionFilter;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
//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.
|
||||
boolean ValidateFoundDevice(UsbDevice searchDevice) {
|
||||
//A vendor id is a global identifier for the manufacturer. A product id refers to the product itself, and is unique to the manufacturer. The vendor id, product id combination refers to a particular product manufactured by a vendor.
|
||||
if (DEBUG) Log.d(TAG, "ValidateFoundDevice: " + searchDevice );
|
||||
|
||||
if ( searchDevice.getVendorId() == OPENPILOT_VENDOR_ID ) {
|
||||
//Requesting permission
|
||||
if (DEBUG) Log.d(TAG, "Device: " + searchDevice );
|
||||
usbManager.requestPermission(searchDevice, permissionIntent);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
private UsbInterface usbInterfaceRead = null;
|
||||
private UsbInterface usbInterfaceWrite = null;
|
||||
private final boolean UsingSingleInterface = true;
|
||||
|
||||
private UsbDevice connectedDevice;
|
||||
|
||||
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
|
||||
// each with a single end point. Either way, it is best if you know which interface
|
||||
// you need to use and which end points
|
||||
|
||||
if (DEBUG) Log.d(TAG, "ConnectToDeviceInterface:");
|
||||
UsbEndpoint ep1 = null;
|
||||
UsbEndpoint ep2 = null;
|
||||
|
||||
|
||||
if (UsingSingleInterface)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
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");
|
||||
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");
|
||||
return false;
|
||||
}
|
||||
connectionRead = usbManager.openDevice(connectDevice);
|
||||
connectionRead.claimInterface(usbInterfaceRead, true);
|
||||
|
||||
|
||||
if (UsingSingleInterface)
|
||||
{
|
||||
connectionWrite = connectionRead;
|
||||
}
|
||||
else // if (!UsingSingleInterface)
|
||||
{
|
||||
connectionWrite = usbManager.openDevice(connectDevice);
|
||||
connectionWrite.claimInterface(usbInterfaceWrite, true);
|
||||
}
|
||||
|
||||
connectedDevice = connectDevice;
|
||||
if (DEBUG) Log.d(TAG, "Opened endpoints");
|
||||
|
||||
// Create the USB requests
|
||||
readRequest = new UsbRequest();
|
||||
readRequest.initialize(connectionRead, usbEndpointRead);
|
||||
|
||||
writeRequest = new UsbRequest();
|
||||
writeRequest.initialize(connectionWrite, usbEndpointWrite);
|
||||
|
||||
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
inStream = new TalkInputStream();
|
||||
outStream = new TalkOutputStream();
|
||||
attemptSucceeded();
|
||||
}
|
||||
});
|
||||
|
||||
readThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (!shutdown) {
|
||||
readData();
|
||||
sendData();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
readThread.start();
|
||||
|
||||
writeThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (!shutdown) {
|
||||
sendData();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
//writeThread.start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Thread readThread;
|
||||
Thread writeThread;
|
||||
private int byteToInt(byte b) { return b & 0x000000ff; }
|
||||
|
||||
private class TalkInputStream extends InputStream {
|
||||
|
||||
ByteFifo data = new ByteFifo();
|
||||
boolean stopped = false;
|
||||
|
||||
TalkInputStream() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() {
|
||||
try {
|
||||
return data.getByteBlocking();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
public void put(byte b) {
|
||||
}
|
||||
|
||||
public void write(byte[] b) {
|
||||
data.put(b);
|
||||
}
|
||||
};
|
||||
|
||||
private class ByteFifo {
|
||||
|
||||
//! The maximum size of the fifo
|
||||
private final int MAX_SIZE = 1024;
|
||||
//! The number of bytes in the buffer
|
||||
private int size = 0;
|
||||
//! Internal buffer
|
||||
private final ByteBuffer buf;
|
||||
|
||||
ByteFifo() {
|
||||
buf = ByteBuffer.allocate(MAX_SIZE);
|
||||
size = 0;
|
||||
}
|
||||
|
||||
public boolean put(byte[] dat) {
|
||||
if ((size + dat.length) > MAX_SIZE)
|
||||
return false;
|
||||
|
||||
// Place data at the end of the buffer
|
||||
synchronized(buf) {
|
||||
buf.position(size);
|
||||
buf.put(dat);
|
||||
size = size + dat.length;
|
||||
buf.notify();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public byte[] get(int size) throws InterruptedException {
|
||||
size = Math.min(size, this.size);
|
||||
if (size > 0) {
|
||||
synchronized(buf) {
|
||||
byte[] dst = new byte[size];
|
||||
buf.position(0);
|
||||
buf.get(dst,0,size);
|
||||
buf.compact();
|
||||
this.size = this.size - size;
|
||||
Assert.assertEquals(this.size, buf.position());
|
||||
|
||||
buf.wait();
|
||||
}
|
||||
}
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
public int getByteBlocking() throws InterruptedException {
|
||||
synchronized(buf) {
|
||||
if (size == 0)
|
||||
buf.wait();
|
||||
int val = byteToInt(buf.get(0));
|
||||
buf.position(1);
|
||||
buf.compact();
|
||||
size--;
|
||||
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
// 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 (connectionRead.requestWait() == readRequest) {
|
||||
// Packet format:
|
||||
// 0: Report ID (1)
|
||||
// 1: Number of valid bytes
|
||||
// 2:63: Data
|
||||
|
||||
dataSize = buffer.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(2) > (buffer.capacity() - 2)) {
|
||||
if (DEBUG) Log.d(TAG, "Badly formatted HID packet");
|
||||
} else {
|
||||
byte[] dst = new byte[dataSize];
|
||||
buffer.position(2);
|
||||
buffer.get(dst, 0, dataSize);
|
||||
if (DEBUG) Log.d(TAG, "Entered read");
|
||||
((TalkInputStream)inStream).write(dst);
|
||||
if (DEBUG) Log.d(TAG, "Got read: " + dataSize + " bytes");
|
||||
}
|
||||
} else
|
||||
return 0;
|
||||
|
||||
return dataSize;
|
||||
}
|
||||
|
||||
private class TalkOutputStream extends OutputStream {
|
||||
ByteBuffer data = ByteBuffer.allocate(1024);
|
||||
boolean stopped = false;
|
||||
int writePosition = 0;
|
||||
|
||||
public int read() throws IOException {
|
||||
if (!stopped)
|
||||
|
||||
|
||||
while(!stopped) {
|
||||
synchronized(data) {
|
||||
if(data.hasRemaining())
|
||||
return data.get();
|
||||
else
|
||||
try {
|
||||
data.wait();
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
throw new IOException();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int oneByte) throws IOException {
|
||||
if (stopped)
|
||||
throw new IOException();
|
||||
synchronized(data) {
|
||||
|
||||
data.put((byte) oneByte);
|
||||
data.notify();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b) throws IOException {
|
||||
if (stopped)
|
||||
throw new IOException();
|
||||
|
||||
synchronized(data) {
|
||||
// Move the cursor to the end of the byte array to append
|
||||
data.position(writePosition);
|
||||
if (b.length < data.remaining()) {
|
||||
data.put(b);
|
||||
writePosition = data.position();
|
||||
}
|
||||
data.notify();
|
||||
}
|
||||
}
|
||||
|
||||
public void packetizeData() {
|
||||
ByteBuffer packet;
|
||||
synchronized(data) {
|
||||
// Determine how much data to put in the packet
|
||||
int size = Math.min(writePosition, MAX_HID_PACKET_SIZE - 2);
|
||||
if (size <= 0)
|
||||
return;
|
||||
|
||||
// Format into a HID packet
|
||||
packet = ByteBuffer.allocate(MAX_HID_PACKET_SIZE);
|
||||
packet.put(0,(byte) 2); // Report ID
|
||||
packet.put(1,(byte) size); // The number of bytes of data
|
||||
data.position(0);
|
||||
data.get(packet.array(), 2, size); // Copy data into the other array
|
||||
|
||||
// Remove that data from the write buffer
|
||||
data.compact();
|
||||
writePosition -= size;
|
||||
if (DEBUG) Log.d(TAG, "packetizeData(): size="+size);
|
||||
}
|
||||
WriteToDevice(packet);
|
||||
}
|
||||
|
||||
};
|
||||
private static final int MAX_HID_PACKET_SIZE = 64;
|
||||
|
||||
/**
|
||||
* Send a packet or wait for data to be available
|
||||
*/
|
||||
public void sendData() {
|
||||
TalkOutputStream o = (TalkOutputStream) outStream;
|
||||
synchronized(o.data){
|
||||
if (o.writePosition > 0)
|
||||
o.packetizeData();
|
||||
else {
|
||||
o.data.notify();
|
||||
o.packetizeData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UsbRequest writeRequest = null;
|
||||
boolean WriteToDevice(ByteBuffer DataToSend) {
|
||||
if (DEBUG) Log.d(TAG, "Writing to device()");
|
||||
|
||||
//The report must be formatted correctly for the device being connected to. On some devices, this requires that a specific value must be the first byte in the report. This can be followed by the length of the data in the report. This format is determined by the device, and isn't specified here.
|
||||
|
||||
int bufferDataLength = usbEndpointWrite.getMaxPacketSize();
|
||||
ByteBuffer buffer = ByteBuffer.allocate(bufferDataLength + 1);
|
||||
|
||||
if(writeRequest == null) {
|
||||
writeRequest = new UsbRequest();
|
||||
writeRequest.initialize(connectionWrite, usbEndpointWrite);
|
||||
}
|
||||
|
||||
buffer.put(DataToSend);
|
||||
|
||||
writeRequest.queue(buffer, bufferDataLength);
|
||||
try
|
||||
{
|
||||
if (writeRequest.equals(connectionWrite.requestWait()))
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// An exception has occured
|
||||
return false;
|
||||
}
|
||||
|
||||
if (false) {
|
||||
writeRequest.cancel();
|
||||
writeRequest.close();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//Reading from the Device - As USB devices work with reports of a specific length. The data received from the device will always be the size specified by the report length. Even if there isn't enough data to fill the report. Some devices require the controlTransfer method for reading data. I don't cover this command in this blog.
|
||||
void ReadFromDevice() {
|
||||
//If you are expecting unsolicited data from the device, then a read thread should be started so that the data can be processed as soon as it arrives.
|
||||
|
||||
int bufferDataLength = usbEndpointRead.getMaxPacketSize();
|
||||
ByteBuffer buffer = ByteBuffer.allocate(bufferDataLength + 1);
|
||||
UsbRequest requestQueued = null;
|
||||
UsbRequest request = new UsbRequest();
|
||||
request.initialize(connectionRead, usbEndpointRead);
|
||||
|
||||
try
|
||||
{
|
||||
while (!getStopping())
|
||||
{
|
||||
request.queue(buffer, bufferDataLength);
|
||||
requestQueued = connectionRead.requestWait();
|
||||
if (request.equals(requestQueued))
|
||||
{
|
||||
byte[] byteBuffer = new byte[bufferDataLength + 1];
|
||||
buffer.get(byteBuffer, 0, bufferDataLength);
|
||||
|
||||
// Handle data received
|
||||
|
||||
buffer.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread.sleep(20);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// An exception has occured
|
||||
}
|
||||
try
|
||||
{
|
||||
request.cancel();
|
||||
request.close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// An exception has occured
|
||||
}
|
||||
}
|
||||
|
||||
private boolean getStopping() {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -56,9 +56,9 @@ public class OPTelemetryService extends Service {
|
||||
|
||||
// Logging settings
|
||||
private final String TAG = OPTelemetryService.class.getSimpleName();
|
||||
public static int LOGLEVEL = 0;
|
||||
public static boolean WARN = LOGLEVEL > 1;
|
||||
public static boolean DEBUG = LOGLEVEL > 0;
|
||||
public static int LOGLEVEL = 2;
|
||||
public static boolean DEBUG = LOGLEVEL > 1;
|
||||
public static boolean WARN = LOGLEVEL > 0;
|
||||
|
||||
// Intent category
|
||||
public final static String INTENT_CATEGORY_GCS = "org.openpilot.intent.category.GCS";
|
||||
@ -81,6 +81,7 @@ public class OPTelemetryService extends Service {
|
||||
private boolean terminate = false;
|
||||
|
||||
private Thread activeTelem;
|
||||
private TelemetryTask telemTask;
|
||||
|
||||
private final IBinder mBinder = new LocalBinder();
|
||||
|
||||
@ -130,7 +131,13 @@ public class OPTelemetryService extends Service {
|
||||
break;
|
||||
case 3:
|
||||
Toast.makeText(getApplicationContext(), "Attempting TCP connection", Toast.LENGTH_SHORT).show();
|
||||
activeTelem = new TcpTelemetryThread();
|
||||
telemTask = new TcpUAVTalk(this);
|
||||
activeTelem = new Thread(telemTask);
|
||||
break;
|
||||
case 4:
|
||||
Toast.makeText(getApplicationContext(), "Attempting USB HID connection", Toast.LENGTH_SHORT).show();
|
||||
telemTask = new HidUAVTalk(this);
|
||||
activeTelem = new Thread(telemTask);
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unsupported");
|
||||
@ -139,8 +146,19 @@ public class OPTelemetryService extends Service {
|
||||
break;
|
||||
case MSG_DISCONNECT:
|
||||
Toast.makeText(getApplicationContext(), "Disconnect requested", Toast.LENGTH_SHORT).show();
|
||||
if (DEBUG) Log.d(TAG, "Calling disconnect");
|
||||
terminate = true;
|
||||
if (activeTelem != null) {
|
||||
if (telemTask != null) {
|
||||
telemTask.disconnect();
|
||||
telemTask = null;
|
||||
|
||||
try {
|
||||
activeTelem.join();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
else if (activeTelem != null) {
|
||||
activeTelem.interrupt();
|
||||
try {
|
||||
activeTelem.join();
|
||||
@ -149,6 +167,7 @@ public class OPTelemetryService extends Service {
|
||||
}
|
||||
activeTelem = null;
|
||||
}
|
||||
if (DEBUG) Log.d(TAG, "Telemetry thread terminated");
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(INTENT_ACTION_DISCONNECTED);
|
||||
sendBroadcast(intent,null);
|
||||
@ -211,12 +230,25 @@ public class OPTelemetryService extends Service {
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
|
||||
if (telemTask != null) {
|
||||
Log.d(TAG, "onDestory() shutting down telemetry task");
|
||||
telemTask.disconnect();
|
||||
telemTask = null;
|
||||
|
||||
try {
|
||||
activeTelem.join();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "onDestory() shut down telemetry task");
|
||||
Toast.makeText(this, "Telemetry service done", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
public class LocalBinder extends Binder {
|
||||
public TelemTask getTelemTask(int id) {
|
||||
return (TelemTask) activeTelem;
|
||||
return telemTask.getTelemTaskIface();
|
||||
}
|
||||
public void openConnection() {
|
||||
Toast.makeText(getApplicationContext(), "Requested open connection", Toast.LENGTH_SHORT).show();
|
||||
@ -391,95 +423,4 @@ public class OPTelemetryService extends Service {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private class TcpTelemetryThread extends Thread implements TelemTask {
|
||||
|
||||
private final UAVObjectManager objMngr;
|
||||
private UAVTalk uavTalk;
|
||||
private Telemetry tel;
|
||||
private TelemetryMonitor mon;
|
||||
|
||||
@Override
|
||||
public UAVObjectManager getObjectManager() { return objMngr; };
|
||||
|
||||
TcpTelemetryThread() {
|
||||
objMngr = new UAVObjectManager();
|
||||
UAVObjectsInitialize.register(objMngr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (DEBUG) Log.d(TAG, "Telemetry Thread started");
|
||||
|
||||
Looper.prepare();
|
||||
|
||||
TcpUAVTalk tcp = new TcpUAVTalk(OPTelemetryService.this);
|
||||
for( int i = 0; i < 10; i++ ) {
|
||||
if (DEBUG) Log.d(TAG, "Attempting network Connection");
|
||||
|
||||
tcp.connect(objMngr);
|
||||
|
||||
if( tcp.getConnected() )
|
||||
|
||||
break;
|
||||
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(TAG, "Thread interrupted while trying to connect");
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if( ! tcp.getConnected() || terminate ) {
|
||||
toastMessage("TCP connection failed");
|
||||
return;
|
||||
}
|
||||
toastMessage("TCP Connected");
|
||||
|
||||
if (DEBUG) Log.d(TAG, "Connected via network");
|
||||
|
||||
uavTalk = tcp.getUavtalk();
|
||||
tel = new Telemetry(uavTalk, objMngr);
|
||||
mon = new TelemetryMonitor(objMngr,tel);
|
||||
mon.addObserver(new Observer() {
|
||||
@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()) {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(INTENT_ACTION_CONNECTED);
|
||||
sendBroadcast(intent,null);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (DEBUG) Log.d(TAG, "Entering UAVTalk processing loop");
|
||||
while( !terminate ) {
|
||||
try {
|
||||
if( !uavTalk.processInputStream() )
|
||||
break;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
toastMessage("TCP Stream interrupted");
|
||||
break;
|
||||
}
|
||||
}
|
||||
Looper.myLooper().quit();
|
||||
|
||||
// Shut down all the attached
|
||||
mon.stopMonitor();
|
||||
mon = null;
|
||||
tel.stopTelemetry();
|
||||
tel = null;
|
||||
|
||||
// Finally close the stream if it is still open
|
||||
tcp.disconnect();
|
||||
|
||||
if (DEBUG) Log.d(TAG, "UAVTalk stream disconnected");
|
||||
toastMessage("TCP Thread finished");
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
@ -28,15 +28,11 @@ import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.openpilot.uavtalk.UAVObjectManager;
|
||||
import org.openpilot.uavtalk.UAVTalk;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
public class TcpUAVTalk {
|
||||
public class TcpUAVTalk extends TelemetryTask {
|
||||
private final String TAG = "TcpUAVTalk";
|
||||
public static int LOGLEVEL = 2;
|
||||
public static boolean WARN = LOGLEVEL > 1;
|
||||
@ -46,16 +42,23 @@ public class TcpUAVTalk {
|
||||
private String ip_address = "1";
|
||||
private int port = 9001;
|
||||
|
||||
private UAVTalk uavTalk;
|
||||
private boolean connected;
|
||||
private Socket socket;
|
||||
|
||||
/**
|
||||
* Construct a TcpUAVTalk object attached to the OPTelemetryService. Gets the
|
||||
* connection settings from the preferences.
|
||||
*/
|
||||
public TcpUAVTalk(Context caller) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(caller);
|
||||
public TcpUAVTalk(OPTelemetryService caller) {
|
||||
super(caller);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean attemptConnection() {
|
||||
|
||||
if( getConnected() )
|
||||
return true;
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(telemService);
|
||||
ip_address = prefs.getString("ip_address","127.0.0.1");
|
||||
try {
|
||||
port = Integer.decode(prefs.getString("port", ""));
|
||||
@ -65,44 +68,6 @@ public class TcpUAVTalk {
|
||||
|
||||
if (DEBUG) Log.d(TAG, "Trying to open UAVTalk with " + ip_address);
|
||||
|
||||
connected = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect a TCP object to an object manager. Returns true if already
|
||||
* connected, otherwise returns true if managed a successful socket.
|
||||
*/
|
||||
public boolean connect(UAVObjectManager objMngr) {
|
||||
if( getConnected() )
|
||||
return true;
|
||||
if( !openTelemetryTcp(objMngr) )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
socket = null;
|
||||
}
|
||||
|
||||
public boolean getConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
public UAVTalk getUavtalk() {
|
||||
return uavTalk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a TCP socket to the address determined on construction. If successful
|
||||
* creates a UAVTalk stream connection this socket to the passed in object manager
|
||||
*/
|
||||
private boolean openTelemetryTcp(UAVObjectManager objMngr) {
|
||||
Log.d(TAG, "Opening connection to " + ip_address + " at address " + port);
|
||||
|
||||
InetAddress serverAddr = null;
|
||||
@ -121,18 +86,43 @@ public class TcpUAVTalk {
|
||||
return false;
|
||||
}
|
||||
|
||||
connected = true;
|
||||
|
||||
try {
|
||||
uavTalk = new UAVTalk(socket.getInputStream(), socket.getOutputStream(), objMngr);
|
||||
inStream = socket.getInputStream();
|
||||
outStream = socket.getOutputStream();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG,"Error starting UAVTalk");
|
||||
// TODO Auto-generated catch block
|
||||
//e.printStackTrace();
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException e2) {
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Post message to call attempt succeeded on the parent class
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
TcpUAVTalk.this.attemptSucceeded();
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
super.disconnect();
|
||||
if (socket != null) {
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
socket = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,235 @@
|
||||
package org.openpilot.androidgcs.telemetry;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
|
||||
import org.openpilot.uavtalk.Telemetry;
|
||||
import org.openpilot.uavtalk.TelemetryMonitor;
|
||||
import org.openpilot.uavtalk.UAVObjectManager;
|
||||
import org.openpilot.uavtalk.UAVTalk;
|
||||
import org.openpilot.uavtalk.uavobjects.UAVObjectsInitialize;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
public abstract class TelemetryTask implements Runnable {
|
||||
|
||||
// Logging settings
|
||||
private final String TAG = TelemetryTask.class.getSimpleName();
|
||||
public static final int LOGLEVEL = 2;
|
||||
public static final boolean WARN = LOGLEVEL > 1;
|
||||
public static final boolean DEBUG = LOGLEVEL > 0;
|
||||
|
||||
/*
|
||||
* This is a self contained runnable that will establish (if possible)
|
||||
* a telemetry connection and provides a listener interface to be
|
||||
* notified of a set of events
|
||||
*
|
||||
* 1. attempt to establish connection
|
||||
* 2. callback when it succeeds (or fails) which notifies listener
|
||||
* 3. once physical connection is established instantiate uavtalk / objectmanager
|
||||
* 4. notify listener they are connected
|
||||
* 5. detect physical link failure and notify listener about that
|
||||
* 6. provide "close link" method
|
||||
*
|
||||
* There are essentially four tasks that need to occur here
|
||||
* 1. Transfer data from the outputStream to the physical link (some protocols do this automatically)
|
||||
* 2. Transfer data from the physical link to the inputStream (again some protocols do this automatically)
|
||||
* 3. Transfer data from the inputStream to UAVTalk (uavTalk.processInputByte)
|
||||
* 4. Transfer data from objects via UAVTalk to output stream (occurs from Telemetry object)
|
||||
*/
|
||||
|
||||
//! Private variables
|
||||
protected Handler handler;
|
||||
|
||||
//! Handle to the parent service
|
||||
protected final OPTelemetryService telemService;
|
||||
|
||||
//! The object manager that will be used for this telemetry task
|
||||
protected UAVObjectManager objMngr;
|
||||
|
||||
//! The UAVTalk connected to the below streams
|
||||
private UAVTalk uavTalk;
|
||||
|
||||
//! The input stream for the telemetry channel
|
||||
protected InputStream inStream;
|
||||
|
||||
//! The output stream for the telemetry channel
|
||||
protected OutputStream outStream;
|
||||
|
||||
//! The telemetry object which takes care of higher level transactions
|
||||
private Telemetry tel;
|
||||
|
||||
//! The telemetry monitor which takes care of high level connects / disconnects
|
||||
private TelemetryMonitor mon;
|
||||
|
||||
//! Flag to indicate a shut down was requested. Derived classes should take care to respect this.
|
||||
boolean shutdown;
|
||||
|
||||
//! Indicate a physical connection is established
|
||||
private boolean connected;
|
||||
|
||||
TelemetryTask(OPTelemetryService s) {
|
||||
telemService = s;
|
||||
shutdown = false;
|
||||
connected = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt a connection. This method may return before the results are
|
||||
* known.
|
||||
* @return False if the attempt failed and no connection will be established
|
||||
* @return True if the attempt succeeded but does not guarantee success
|
||||
*/
|
||||
abstract boolean attemptConnection();
|
||||
|
||||
/**
|
||||
* Called when a physical channel is opened
|
||||
*
|
||||
* When this method is called the derived class must have
|
||||
* created a valid inStream and outStream
|
||||
*/
|
||||
boolean attemptSucceeded() {
|
||||
// Create a new object manager and register all objects
|
||||
// in the future the particular register method should
|
||||
// be dependent on what is connected (e.g. board and
|
||||
// version number).
|
||||
objMngr = new UAVObjectManager();
|
||||
UAVObjectsInitialize.register(objMngr);
|
||||
|
||||
// Create the required telemetry objects attached to this
|
||||
// data stream
|
||||
uavTalk = new UAVTalk(inStream, outStream, objMngr);
|
||||
tel = new Telemetry(uavTalk, objMngr);
|
||||
mon = new TelemetryMonitor(objMngr,tel);
|
||||
|
||||
// Create an observer to notify system of connection
|
||||
mon.addObserver(connectionObserver);
|
||||
|
||||
// Create a new thread that processes the input bytes
|
||||
startInputProcessing();
|
||||
|
||||
connected = true;
|
||||
return connected;
|
||||
}
|
||||
|
||||
boolean attemptedFailed() {
|
||||
connected = false;
|
||||
return connected;
|
||||
}
|
||||
|
||||
void disconnect() {
|
||||
// Make the default input procesing loop stop
|
||||
shutdown = true;
|
||||
|
||||
// Shut down all the attached
|
||||
if (mon != null) {
|
||||
mon.stopMonitor();
|
||||
mon.deleteObserver(connectionObserver);
|
||||
mon = null;
|
||||
}
|
||||
if (tel != null) {
|
||||
tel.stopTelemetry();
|
||||
tel = null;
|
||||
}
|
||||
|
||||
// Stop the master telemetry thread
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Looper.myLooper().quit();
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Make sure the input and output stream is closed
|
||||
|
||||
// TODO: Make sure any threads for input and output are closed
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation for processing input stream
|
||||
* which creates a new thread that keeps attempting
|
||||
* to read from the input stream.
|
||||
*/
|
||||
private void startInputProcessing() {
|
||||
new Thread(new processUavTalk()).start();
|
||||
}
|
||||
|
||||
//! Runnable to process input stream
|
||||
class processUavTalk implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
if (DEBUG) Log.d(TAG, "Entering UAVTalk processing loop");
|
||||
while (!shutdown) {
|
||||
try {
|
||||
if( !uavTalk.processInputStream() )
|
||||
break;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
telemService.toastMessage("Telemetry input stream interrupted");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (DEBUG) Log.d(TAG, "UAVTalk processing loop finished");
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
|
||||
Looper.prepare();
|
||||
handler = new Handler();
|
||||
|
||||
if (DEBUG) Log.d(TAG, "Attempting connection");
|
||||
if( attemptConnection() == false )
|
||||
return; // Attempt failed
|
||||
|
||||
Looper.loop();
|
||||
|
||||
if (DEBUG) Log.d(TAG, "TelemetryTask runnable finished");
|
||||
|
||||
} catch (Throwable t) {
|
||||
Log.e(TAG, "halted due to an error", t);
|
||||
}
|
||||
|
||||
telemService.toastMessage("Telemetry Thread finished");
|
||||
}
|
||||
|
||||
private final Observer connectionObserver = new Observer() {
|
||||
@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()) {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(OPTelemetryService.INTENT_ACTION_CONNECTED);
|
||||
telemService.sendBroadcast(intent,null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**** General accessors ****/
|
||||
|
||||
public boolean getConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
public UAVTalk getUavtalk() {
|
||||
return uavTalk;
|
||||
}
|
||||
|
||||
public OPTelemetryService.TelemTask getTelemTaskIface() {
|
||||
return new OPTelemetryService.TelemTask() {
|
||||
@Override
|
||||
public UAVObjectManager getObjectManager() {
|
||||
return objMngr;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -40,10 +40,10 @@ import android.util.Log;
|
||||
public class Telemetry {
|
||||
|
||||
private final String TAG = "Telemetry";
|
||||
public static int LOGLEVEL = 0;
|
||||
public static int LOGLEVEL = 1;
|
||||
public static boolean WARN = LOGLEVEL > 2;
|
||||
public static boolean ERROR = LOGLEVEL > 1;
|
||||
public static boolean DEBUG = LOGLEVEL > 0;
|
||||
public static boolean DEBUG = LOGLEVEL > 1;
|
||||
public static boolean ERROR = LOGLEVEL > 0;
|
||||
public class TelemetryStats {
|
||||
public int txBytes;
|
||||
public int rxBytes;
|
||||
@ -66,6 +66,17 @@ public class Telemetry {
|
||||
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) {
|
||||
|
||||
};
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class ObjectTransactionInfo {
|
||||
@ -112,13 +123,13 @@ public class Telemetry {
|
||||
}
|
||||
});
|
||||
|
||||
// Listen to transaction completions
|
||||
this.utalk.setOnTransactionCompletedListener(
|
||||
this.utalk.new OnTransactionCompletedListener() {
|
||||
// Listen to transaction completions from uavtalk
|
||||
utalk.setOnTransactionCompletedListener(
|
||||
utalk.new OnTransactionCompletedListener() {
|
||||
@Override
|
||||
void TransactionSucceeded(UAVObject data) {
|
||||
try {
|
||||
transactionCompleted(data);
|
||||
transactionCompleted(data, true);
|
||||
} catch (IOException e) {
|
||||
// Disconnect when stream fails
|
||||
utalk.setOnTransactionCompletedListener(null);
|
||||
@ -127,8 +138,9 @@ public class Telemetry {
|
||||
@Override
|
||||
void TransactionFailed(UAVObject data) {
|
||||
try {
|
||||
Log.d(TAG, "TransactionFailed(" + data.getName() + ")");
|
||||
transactionCompleted(data);
|
||||
if (DEBUG) Log.d(TAG, "TransactionFailed(" + data.getName() + ")");
|
||||
|
||||
transactionCompleted(data, false);
|
||||
} catch (IOException e) {
|
||||
// Disconnect when stream fails
|
||||
utalk.setOnTransactionCompletedListener(null);
|
||||
@ -250,6 +262,54 @@ 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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Connect to all instances of an object depending on the event mask specified
|
||||
*/
|
||||
@ -260,66 +320,23 @@ public class Telemetry {
|
||||
while(li.hasNext())
|
||||
{
|
||||
obj = li.next();
|
||||
//TODO: Disconnect all
|
||||
// obj.disconnect(this);
|
||||
|
||||
// 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(new Observer() {
|
||||
@Override
|
||||
public void update(Observable observable, Object data) {
|
||||
try {
|
||||
objectUnpacked( (UAVObject) data);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
obj.addUnpackedObserver(unpackedObserver);
|
||||
if ( (eventMask&EV_UPDATED) != 0)
|
||||
{
|
||||
obj.addUpdatedAutoObserver(new Observer() {
|
||||
@Override
|
||||
public void update(Observable observable, Object data) {
|
||||
try {
|
||||
objectUpdatedAuto( (UAVObject) data);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
obj.addUpdatedAutoObserver(updatedAutoObserver);
|
||||
if ( (eventMask&EV_UPDATED_MANUAL) != 0)
|
||||
{
|
||||
obj.addUpdatedManualObserver(new Observer() {
|
||||
@Override
|
||||
public void update(Observable observable, Object data) {
|
||||
try {
|
||||
objectUpdatedManual( (UAVObject) data);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
obj.addUpdatedManualObserver(updatedManualObserver);
|
||||
if ( (eventMask&EV_UPDATE_REQ) != 0)
|
||||
{
|
||||
obj.addUpdateRequestedObserver(new Observer() {
|
||||
@Override
|
||||
public void update(Observable observable, Object data) {
|
||||
try {
|
||||
updateRequested( (UAVObject) data);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
obj.addUpdateRequestedObserver(updatedRequestedObserver);
|
||||
}
|
||||
}
|
||||
|
||||
@ -376,7 +393,7 @@ public class Telemetry {
|
||||
* Called when a transaction is successfully completed (uavtalk event)
|
||||
* @throws IOException
|
||||
*/
|
||||
private synchronized void transactionCompleted(UAVObject obj) throws IOException
|
||||
private synchronized 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
|
||||
@ -386,13 +403,15 @@ public class Telemetry {
|
||||
// Complete transaction
|
||||
transTimer.cancel();
|
||||
transPending = false;
|
||||
// Send signal
|
||||
obj.transactionCompleted(true);
|
||||
|
||||
//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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -416,13 +435,13 @@ public class Telemetry {
|
||||
}
|
||||
else
|
||||
{
|
||||
// Terminate transaction
|
||||
utalk.cancelTransaction();
|
||||
transPending = false;
|
||||
// Send signal
|
||||
transInfo.obj.transactionCompleted(false);
|
||||
// Process new object updates from queue
|
||||
processObjectQueue();
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -463,10 +482,10 @@ public class Telemetry {
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the event received from an object
|
||||
* @throws IOException
|
||||
* 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 synchronized void processObjectUpdates(UAVObject obj, int event, boolean allInstances, boolean priority) throws IOException
|
||||
private synchronized 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);
|
||||
@ -478,28 +497,34 @@ public class Telemetry {
|
||||
objInfo.allInstances = allInstances;
|
||||
if (priority)
|
||||
{
|
||||
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());
|
||||
}
|
||||
// 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
|
||||
{
|
||||
if ( objQueue.size() < MAX_QUEUE_SIZE )
|
||||
{
|
||||
objQueue.add(objInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
++txErrors;
|
||||
obj.transactionCompleted(false);
|
||||
}
|
||||
// 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
|
||||
@ -520,7 +545,7 @@ public class Telemetry {
|
||||
// Don nothing if a transaction is already in progress (should not happen)
|
||||
if (transPending)
|
||||
{
|
||||
if (ERROR) Log.e(TAG,"Dequeue while a transaction pending");
|
||||
if (WARN) Log.e(TAG,"Dequeue while a transaction pending");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -588,7 +613,7 @@ public class Telemetry {
|
||||
// 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 )
|
||||
if ( objInfo.event == EV_UNPACKED && !transPending)
|
||||
processObjectQueue();
|
||||
|
||||
}
|
||||
@ -628,7 +653,7 @@ public class Telemetry {
|
||||
objinfo.timeToNextUpdateMs = objinfo.updatePeriodMs - offset;
|
||||
// Send object
|
||||
startTime = System.currentTimeMillis();
|
||||
processObjectUpdates(objinfo.obj, EV_UPDATED_MANUAL, true, false);
|
||||
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;
|
||||
@ -682,25 +707,6 @@ public class Telemetry {
|
||||
txRetries = 0;
|
||||
}
|
||||
|
||||
private synchronized void objectUpdatedAuto(UAVObject obj) throws IOException
|
||||
{
|
||||
processObjectUpdates(obj, EV_UPDATED, false, true);
|
||||
}
|
||||
|
||||
private synchronized void objectUpdatedManual(UAVObject obj) throws IOException
|
||||
{
|
||||
processObjectUpdates(obj, EV_UPDATED_MANUAL, false, true);
|
||||
}
|
||||
|
||||
private synchronized void objectUnpacked(UAVObject obj) throws IOException
|
||||
{
|
||||
processObjectUpdates(obj, EV_UNPACKED, false, true);
|
||||
}
|
||||
|
||||
public synchronized void updateRequested(UAVObject obj) throws IOException
|
||||
{
|
||||
processObjectUpdates(obj, EV_UPDATE_REQ, false, true);
|
||||
}
|
||||
|
||||
private void newObject(UAVObject obj)
|
||||
{
|
||||
|
@ -39,14 +39,17 @@ import android.util.Log;
|
||||
public class TelemetryMonitor extends Observable {
|
||||
|
||||
private static final String TAG = "TelemetryMonitor";
|
||||
public static int LOGLEVEL = 0;
|
||||
public static boolean WARN = LOGLEVEL > 1;
|
||||
public static boolean DEBUG = LOGLEVEL > 0;
|
||||
public static final int LOGLEVEL = 0;
|
||||
public static boolean DEBUG = LOGLEVEL > 2;
|
||||
public static final boolean WARN = LOGLEVEL > 1;
|
||||
public static final boolean ERROR = LOGLEVEL > 0;
|
||||
|
||||
static final int STATS_UPDATE_PERIOD_MS = 4000;
|
||||
static final int STATS_CONNECT_PERIOD_MS = 1000;
|
||||
static final int CONNECTION_TIMEOUT_MS = 8000;
|
||||
|
||||
private final boolean HANDSHAKE_IS_CONNECTED = false;
|
||||
|
||||
private final UAVObjectManager objMngr;
|
||||
private final Telemetry tel;
|
||||
// private UAVObject objPending;
|
||||
@ -146,6 +149,20 @@ public class TelemetryMonitor extends Observable {
|
||||
queue.clear();
|
||||
}
|
||||
|
||||
final Observer transactionObserver = new Observer() {
|
||||
@Override
|
||||
public void update(Observable observable, Object data) {
|
||||
try {
|
||||
UAVObject.TransactionResult result = (UAVObject.TransactionResult) data;
|
||||
transactionCompleted(result.obj, result.success);
|
||||
} catch (IOException e) {
|
||||
// When the telemetry stream is broken disconnect these
|
||||
// updates
|
||||
observable.deleteObserver(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the next object in the queue
|
||||
*
|
||||
@ -154,11 +171,12 @@ public class TelemetryMonitor extends Observable {
|
||||
public synchronized void retrieveNextObject() throws IOException {
|
||||
// If queue is empty return
|
||||
if (queue.isEmpty()) {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "All objects retrieved: Connected Successfully");
|
||||
if (DEBUG || true) Log.d(TAG, "All objects retrieved: Connected Successfully");
|
||||
objects_updated = true;
|
||||
setChanged();
|
||||
notifyObservers();
|
||||
if (!HANDSHAKE_IS_CONNECTED) {
|
||||
setChanged();
|
||||
notifyObservers();
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Get next object from the queue
|
||||
@ -173,26 +191,10 @@ public class TelemetryMonitor extends Observable {
|
||||
|
||||
// TODO: Does this need to stay here permanently? This appears to be
|
||||
// used for setup mainly
|
||||
obj.addTransactionCompleted(new Observer() {
|
||||
@Override
|
||||
public void update(Observable observable, Object data) {
|
||||
UAVObject.TransactionResult result = (UAVObject.TransactionResult) data;
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "Got transaction completed event from "
|
||||
+ result.obj.getName() + " status: "
|
||||
+ result.success);
|
||||
try {
|
||||
transactionCompleted(result.obj, result.success);
|
||||
} catch (IOException e) {
|
||||
// When the telemetry stream is broken disconnect these
|
||||
// updates
|
||||
observable.deleteObserver(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
obj.addTransactionCompleted(transactionObserver);
|
||||
|
||||
// Request update
|
||||
tel.updateRequested(obj);
|
||||
obj.updateRequested();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -205,15 +207,17 @@ public class TelemetryMonitor extends Observable {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "transactionCompleted. Status: " + success);
|
||||
|
||||
// Remove the listener for the event that just finished
|
||||
obj.removeTransactionCompleted(transactionObserver);
|
||||
|
||||
if (!success) {
|
||||
// Right now success = false means received a NAK so don't
|
||||
// re-attempt
|
||||
Log.e(TAG, "Transaction failed.");
|
||||
if (ERROR) Log.e(TAG, "Transaction failed.");
|
||||
}
|
||||
|
||||
// Process next object if telemetry is still available
|
||||
if (((String) gcsStatsObj.getField("Status").getValue())
|
||||
.compareTo("Connected") == 0) {
|
||||
if (((String) gcsStatsObj.getField("Status").getValue()).compareTo("Connected") == 0) {
|
||||
retrieveNextObject();
|
||||
} else {
|
||||
stopRetrievingObjects();
|
||||
@ -347,7 +351,7 @@ public class TelemetryMonitor extends Observable {
|
||||
connected = true;
|
||||
objects_updated = false;
|
||||
startRetrievingObjects();
|
||||
setChanged();
|
||||
if (HANDSHAKE_IS_CONNECTED) setChanged(); // Enabling this line makes the opConnected signal occur whenever we get a handshake
|
||||
}
|
||||
if (gcsDisconnected && gcsStatusChanged) {
|
||||
if (DEBUG)
|
||||
|
@ -105,6 +105,11 @@ public abstract class UAVObject {
|
||||
unpackedListeners.addObserver(o);
|
||||
}
|
||||
}
|
||||
public void removeUnpackedObserver(Observer o) {
|
||||
synchronized(unpackedListeners) {
|
||||
unpackedListeners.deleteObserver(o);
|
||||
}
|
||||
}
|
||||
void unpacked() {
|
||||
synchronized(unpackedListeners) {
|
||||
unpackedListeners.event();
|
||||
@ -117,6 +122,11 @@ public abstract class UAVObject {
|
||||
updatedAutoListeners.addObserver(o);
|
||||
}
|
||||
}
|
||||
public void removeUpdatedAutoObserver(Observer o) {
|
||||
synchronized(updatedAutoListeners) {
|
||||
updatedAutoListeners.deleteObserver(o);
|
||||
}
|
||||
}
|
||||
void updatedAuto() {
|
||||
synchronized(updatedAutoListeners) {
|
||||
updatedAutoListeners.event();
|
||||
@ -129,6 +139,11 @@ public abstract class UAVObject {
|
||||
updatedManualListeners.addObserver(o);
|
||||
}
|
||||
}
|
||||
public void removeUpdatedManualObserver(Observer o) {
|
||||
synchronized(updatedManualListeners) {
|
||||
updatedManualListeners.deleteObserver(o);
|
||||
}
|
||||
}
|
||||
void updatedManual() {
|
||||
synchronized(updatedManualListeners) {
|
||||
updatedManualListeners.event();
|
||||
@ -141,6 +156,11 @@ public abstract class UAVObject {
|
||||
updateRequestedListeners.addObserver(o);
|
||||
}
|
||||
}
|
||||
public void removeUpdateRequestedObserver(Observer o) {
|
||||
synchronized(updateRequestedListeners) {
|
||||
updateRequestedListeners.deleteObserver(o);
|
||||
}
|
||||
}
|
||||
public void updateRequested() {
|
||||
synchronized(updateRequestedListeners) {
|
||||
updateRequestedListeners.event();
|
||||
|
@ -106,9 +106,13 @@ public class UAVTalk {
|
||||
|
||||
static final int TYPE_MASK = 0xF8;
|
||||
static final int TYPE_VER = 0x20;
|
||||
//! Packet contains an object
|
||||
static final int TYPE_OBJ = (TYPE_VER | 0x00);
|
||||
//! Packet is a request for an object
|
||||
static final int TYPE_OBJ_REQ = (TYPE_VER | 0x01);
|
||||
//! Packet is an object with a request for an ack
|
||||
static final int TYPE_OBJ_ACK = (TYPE_VER | 0x02);
|
||||
//! Packet is an ack for an object
|
||||
static final int TYPE_ACK = (TYPE_VER | 0x03);
|
||||
static final int TYPE_NACK = (TYPE_VER | 0x04);
|
||||
|
||||
@ -135,8 +139,14 @@ public class UAVTalk {
|
||||
OutputStream outStream;
|
||||
UAVObjectManager objMngr;
|
||||
|
||||
//! Currently only one UAVTalk transaction is permitted at a time. If this is null none are in process
|
||||
//! otherwise points to the pending object
|
||||
UAVObject respObj;
|
||||
//! If the pending transaction is for all the instances
|
||||
boolean respAllInstances;
|
||||
//! The type of response we are expecting
|
||||
int respType;
|
||||
|
||||
// Variables used by the receive state machine
|
||||
ByteBuffer rxTmpBuffer /* 4 */;
|
||||
ByteBuffer rxBuffer;
|
||||
@ -217,6 +227,8 @@ public class UAVTalk {
|
||||
//inStream.wait();
|
||||
val = inStream.read();
|
||||
|
||||
if (VERBOSE) Log.v(TAG, "Read: " + val);
|
||||
|
||||
if (val == -1) {
|
||||
return false;
|
||||
}
|
||||
@ -234,7 +246,6 @@ public class UAVTalk {
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean sendObjectRequest(UAVObject obj, boolean allInstances) throws IOException {
|
||||
// QMutexLocker locker(mutex);
|
||||
return objectTransaction(obj, TYPE_OBJ_REQ, allInstances);
|
||||
}
|
||||
|
||||
@ -245,8 +256,7 @@ public class UAVTalk {
|
||||
* Success (true), Failure (false)
|
||||
* @throws IOException
|
||||
*/
|
||||
public synchronized boolean sendObject(UAVObject obj, boolean acked,
|
||||
boolean allInstances) throws IOException {
|
||||
public boolean sendObject(UAVObject obj, boolean acked, boolean allInstances) throws IOException {
|
||||
if (acked) {
|
||||
return objectTransaction(obj, TYPE_OBJ_ACK, allInstances);
|
||||
} else {
|
||||
@ -255,10 +265,46 @@ public class UAVTalk {
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a pending transaction
|
||||
* UAVTalk takes care of it's own transactions but if the caller knows
|
||||
* it wants to give up on one (after a timeout) then it can cancel it
|
||||
* @return True if that object was pending, False otherwise
|
||||
*/
|
||||
public synchronized void cancelTransaction() {
|
||||
public synchronized boolean cancelPendingTransaction(UAVObject obj) {
|
||||
if(respObj != null && respObj.getObjID() == obj.getObjID()) {
|
||||
if(transactionListener != null) {
|
||||
Log.d(TAG,"Canceling transaction: " + respObj.getName());
|
||||
transactionListener.TransactionFailed(respObj);
|
||||
}
|
||||
respObj = null;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a pending transaction. If there is a pending transaction and
|
||||
* a listener then notify them that the transaction failed.
|
||||
*/
|
||||
/*private synchronized void cancelPendingTransaction() {
|
||||
if(respObj != null && transactionListener != null) {
|
||||
Log.d(TAG,"Canceling transaction: " + respObj.getName());
|
||||
transactionListener.TransactionFailed(respObj);
|
||||
}
|
||||
respObj = null;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* This is the code that sets up a new UAVTalk packet that expects a response.
|
||||
*/
|
||||
private synchronized void setupTransaction(UAVObject obj, boolean allInstances, int type) {
|
||||
|
||||
// Only cancel if it is for a different object
|
||||
if(respObj != null && respObj.getObjID() != obj.getObjID())
|
||||
cancelPendingTransaction(obj);
|
||||
|
||||
respObj = obj;
|
||||
respAllInstances = allInstances;
|
||||
respType = type;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -269,21 +315,9 @@ public class UAVTalk {
|
||||
* Success (true), Failure (false)
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean objectTransaction(UAVObject obj, int type,
|
||||
boolean allInstances) throws IOException {
|
||||
// Send object depending on if a response is needed
|
||||
if (type == TYPE_OBJ_ACK || type == TYPE_OBJ_REQ) {
|
||||
if (transmitObject(obj, type, allInstances)) {
|
||||
if(type == TYPE_OBJ_REQ)
|
||||
if (ERROR) Log.e(TAG, "Sending obj req");
|
||||
respObj = obj;
|
||||
respAllInstances = allInstances;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (type == TYPE_OBJ) {
|
||||
return transmitObject(obj, TYPE_OBJ, allInstances);
|
||||
private synchronized boolean objectTransaction(UAVObject obj, int type, boolean allInstances) throws IOException {
|
||||
if (type == TYPE_OBJ_ACK || type == TYPE_OBJ_REQ || type == TYPE_OBJ) {
|
||||
return transmitObject(obj, type, allInstances);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@ -294,207 +328,210 @@ public class UAVTalk {
|
||||
* byte \return Success (true), Failure (false)
|
||||
* @throws IOException
|
||||
*/
|
||||
public synchronized boolean processInputByte(int rxbyte) throws IOException {
|
||||
public boolean processInputByte(int rxbyte) throws IOException {
|
||||
Assert.assertNotNull(objMngr);
|
||||
|
||||
// Update stats
|
||||
stats.rxBytes++;
|
||||
// Only need to synchronize this method on the state machine state
|
||||
synchronized(rxState) {
|
||||
// Update stats
|
||||
stats.rxBytes++;
|
||||
|
||||
rxPacketLength++; // update packet byte count
|
||||
rxPacketLength++; // update packet byte count
|
||||
|
||||
// Receive state machine
|
||||
switch (rxState) {
|
||||
case STATE_SYNC:
|
||||
// Receive state machine
|
||||
switch (rxState) {
|
||||
case STATE_SYNC:
|
||||
|
||||
if (rxbyte != SYNC_VAL)
|
||||
if (rxbyte != SYNC_VAL)
|
||||
break;
|
||||
|
||||
// Initialize and update CRC
|
||||
rxCS = updateCRC(0, rxbyte);
|
||||
|
||||
rxPacketLength = 1;
|
||||
|
||||
rxState = RxStateType.STATE_TYPE;
|
||||
break;
|
||||
|
||||
// Initialize and update CRC
|
||||
rxCS = updateCRC(0, rxbyte);
|
||||
case STATE_TYPE:
|
||||
|
||||
rxPacketLength = 1;
|
||||
// Update CRC
|
||||
rxCS = updateCRC(rxCS, rxbyte);
|
||||
|
||||
rxState = RxStateType.STATE_TYPE;
|
||||
break;
|
||||
|
||||
case STATE_TYPE:
|
||||
|
||||
// Update CRC
|
||||
rxCS = updateCRC(rxCS, rxbyte);
|
||||
|
||||
if ((rxbyte & TYPE_MASK) != TYPE_VER) {
|
||||
Log.e(TAG, "Unknown UAVTalk type:" + rxbyte);
|
||||
rxState = RxStateType.STATE_SYNC;
|
||||
break;
|
||||
}
|
||||
|
||||
rxType = rxbyte;
|
||||
if (VERBOSE) Log.v(TAG, "Received packet type: " + rxType);
|
||||
packetSize = 0;
|
||||
|
||||
rxState = RxStateType.STATE_SIZE;
|
||||
rxCount = 0;
|
||||
break;
|
||||
|
||||
case STATE_SIZE:
|
||||
|
||||
// Update CRC
|
||||
rxCS = updateCRC(rxCS, rxbyte);
|
||||
|
||||
if (rxCount == 0) {
|
||||
packetSize += rxbyte;
|
||||
rxCount++;
|
||||
break;
|
||||
}
|
||||
|
||||
packetSize += (rxbyte << 8) & 0xff00;
|
||||
|
||||
if (packetSize < MIN_HEADER_LENGTH
|
||||
|| packetSize > MAX_HEADER_LENGTH + MAX_PAYLOAD_LENGTH) { // incorrect
|
||||
// packet
|
||||
// size
|
||||
rxState = RxStateType.STATE_SYNC;
|
||||
break;
|
||||
}
|
||||
|
||||
rxCount = 0;
|
||||
rxState = RxStateType.STATE_OBJID;
|
||||
rxTmpBuffer.position(0);
|
||||
break;
|
||||
|
||||
case STATE_OBJID:
|
||||
|
||||
// Update CRC
|
||||
rxCS = updateCRC(rxCS, rxbyte);
|
||||
|
||||
rxTmpBuffer.put(rxCount++, (byte) (rxbyte & 0xff));
|
||||
if (rxCount < 4)
|
||||
break;
|
||||
|
||||
// Search for object, if not found reset state machine
|
||||
rxObjId = rxTmpBuffer.getInt(0);
|
||||
// Because java treats ints as only signed we need to do this manually
|
||||
if (rxObjId < 0)
|
||||
rxObjId = 0x100000000l + rxObjId;
|
||||
{
|
||||
UAVObject rxObj = objMngr.getObject(rxObjId);
|
||||
if (rxObj == null) {
|
||||
if (DEBUG) Log.d(TAG, "Unknown ID: " + rxObjId);
|
||||
stats.rxErrors++;
|
||||
if ((rxbyte & TYPE_MASK) != TYPE_VER) {
|
||||
if (ERROR) Log.e(TAG, "Unknown UAVTalk type:" + rxbyte);
|
||||
rxState = RxStateType.STATE_SYNC;
|
||||
break;
|
||||
}
|
||||
|
||||
// Determine data length
|
||||
if (rxType == TYPE_OBJ_REQ || rxType == TYPE_ACK || rxType == TYPE_NACK)
|
||||
rxLength = 0;
|
||||
else
|
||||
rxLength = rxObj.getNumBytes();
|
||||
rxType = rxbyte;
|
||||
if (VERBOSE) Log.v(TAG, "Received packet type: " + rxType);
|
||||
packetSize = 0;
|
||||
|
||||
// Check length and determine next state
|
||||
if (rxLength >= MAX_PAYLOAD_LENGTH) {
|
||||
stats.rxErrors++;
|
||||
rxState = RxStateType.STATE_SIZE;
|
||||
rxCount = 0;
|
||||
break;
|
||||
|
||||
case STATE_SIZE:
|
||||
|
||||
// Update CRC
|
||||
rxCS = updateCRC(rxCS, rxbyte);
|
||||
|
||||
if (rxCount == 0) {
|
||||
packetSize += rxbyte;
|
||||
rxCount++;
|
||||
break;
|
||||
}
|
||||
|
||||
packetSize += (rxbyte << 8) & 0xff00;
|
||||
|
||||
if (packetSize < MIN_HEADER_LENGTH
|
||||
|| packetSize > MAX_HEADER_LENGTH + MAX_PAYLOAD_LENGTH) { // incorrect
|
||||
// packet
|
||||
// size
|
||||
rxState = RxStateType.STATE_SYNC;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check the lengths match
|
||||
if ((rxPacketLength + rxLength) != packetSize) { // packet error
|
||||
// -
|
||||
// mismatched
|
||||
// packet
|
||||
// size
|
||||
stats.rxErrors++;
|
||||
rxState = RxStateType.STATE_SYNC;
|
||||
break;
|
||||
}
|
||||
rxCount = 0;
|
||||
rxState = RxStateType.STATE_OBJID;
|
||||
rxTmpBuffer.position(0);
|
||||
break;
|
||||
|
||||
// Check if this is a single instance object (i.e. if the
|
||||
// instance ID field is coming next)
|
||||
if (rxObj.isSingleInstance()) {
|
||||
// If there is a payload get it, otherwise receive checksum
|
||||
if (rxLength > 0)
|
||||
rxState = RxStateType.STATE_DATA;
|
||||
case STATE_OBJID:
|
||||
|
||||
// Update CRC
|
||||
rxCS = updateCRC(rxCS, rxbyte);
|
||||
|
||||
rxTmpBuffer.put(rxCount++, (byte) (rxbyte & 0xff));
|
||||
if (rxCount < 4)
|
||||
break;
|
||||
|
||||
// Search for object, if not found reset state machine
|
||||
rxObjId = rxTmpBuffer.getInt(0);
|
||||
// Because java treats ints as only signed we need to do this manually
|
||||
if (rxObjId < 0)
|
||||
rxObjId = 0x100000000l + rxObjId;
|
||||
{
|
||||
UAVObject rxObj = objMngr.getObject(rxObjId);
|
||||
if (rxObj == null) {
|
||||
if (DEBUG) Log.d(TAG, "Unknown ID: " + rxObjId);
|
||||
stats.rxErrors++;
|
||||
rxState = RxStateType.STATE_SYNC;
|
||||
break;
|
||||
}
|
||||
|
||||
// Determine data length
|
||||
if (rxType == TYPE_OBJ_REQ || rxType == TYPE_ACK || rxType == TYPE_NACK)
|
||||
rxLength = 0;
|
||||
else
|
||||
rxState = RxStateType.STATE_CS;
|
||||
rxInstId = 0;
|
||||
rxCount = 0;
|
||||
} else {
|
||||
rxState = RxStateType.STATE_INSTID;
|
||||
rxCount = 0;
|
||||
rxLength = rxObj.getNumBytes();
|
||||
|
||||
// Check length and determine next state
|
||||
if (rxLength >= MAX_PAYLOAD_LENGTH) {
|
||||
stats.rxErrors++;
|
||||
rxState = RxStateType.STATE_SYNC;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check the lengths match
|
||||
if ((rxPacketLength + rxLength) != packetSize) { // packet error
|
||||
// -
|
||||
// mismatched
|
||||
// packet
|
||||
// size
|
||||
stats.rxErrors++;
|
||||
rxState = RxStateType.STATE_SYNC;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if this is a single instance object (i.e. if the
|
||||
// instance ID field is coming next)
|
||||
if (rxObj.isSingleInstance()) {
|
||||
// If there is a payload get it, otherwise receive checksum
|
||||
if (rxLength > 0)
|
||||
rxState = RxStateType.STATE_DATA;
|
||||
else
|
||||
rxState = RxStateType.STATE_CS;
|
||||
rxInstId = 0;
|
||||
rxCount = 0;
|
||||
} else {
|
||||
rxState = RxStateType.STATE_INSTID;
|
||||
rxCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case STATE_INSTID:
|
||||
|
||||
// Update CRC
|
||||
rxCS = updateCRC(rxCS, rxbyte);
|
||||
|
||||
rxTmpBuffer.put(rxCount++, (byte) (rxbyte & 0xff));
|
||||
if (rxCount < 2)
|
||||
break;
|
||||
|
||||
rxInstId = rxTmpBuffer.getShort(0);
|
||||
case STATE_INSTID:
|
||||
|
||||
rxCount = 0;
|
||||
// Update CRC
|
||||
rxCS = updateCRC(rxCS, rxbyte);
|
||||
|
||||
rxTmpBuffer.put(rxCount++, (byte) (rxbyte & 0xff));
|
||||
if (rxCount < 2)
|
||||
break;
|
||||
|
||||
rxInstId = rxTmpBuffer.getShort(0);
|
||||
|
||||
rxCount = 0;
|
||||
|
||||
// If there is a payload get it, otherwise receive checksum
|
||||
if (rxLength > 0)
|
||||
rxState = RxStateType.STATE_DATA;
|
||||
else
|
||||
rxState = RxStateType.STATE_CS;
|
||||
|
||||
break;
|
||||
|
||||
case STATE_DATA:
|
||||
|
||||
// Update CRC
|
||||
rxCS = updateCRC(rxCS, rxbyte);
|
||||
|
||||
rxBuffer.put(rxCount++, (byte) (rxbyte & 0xff));
|
||||
if (rxCount < rxLength)
|
||||
break;
|
||||
|
||||
// If there is a payload get it, otherwise receive checksum
|
||||
if (rxLength > 0)
|
||||
rxState = RxStateType.STATE_DATA;
|
||||
else
|
||||
rxState = RxStateType.STATE_CS;
|
||||
|
||||
break;
|
||||
|
||||
case STATE_DATA:
|
||||
|
||||
// Update CRC
|
||||
rxCS = updateCRC(rxCS, rxbyte);
|
||||
|
||||
rxBuffer.put(rxCount++, (byte) (rxbyte & 0xff));
|
||||
if (rxCount < rxLength)
|
||||
rxCount = 0;
|
||||
break;
|
||||
|
||||
rxState = RxStateType.STATE_CS;
|
||||
rxCount = 0;
|
||||
break;
|
||||
case STATE_CS:
|
||||
|
||||
case STATE_CS:
|
||||
// The CRC byte
|
||||
rxCSPacket = rxbyte;
|
||||
|
||||
// The CRC byte
|
||||
rxCSPacket = rxbyte;
|
||||
if (rxCS != rxCSPacket) { // packet error - faulty CRC
|
||||
if (DEBUG) Log.d(TAG,"Bad crc");
|
||||
stats.rxErrors++;
|
||||
rxState = RxStateType.STATE_SYNC;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rxPacketLength != (packetSize + 1)) { // packet error -
|
||||
// mismatched packet
|
||||
// size
|
||||
if (DEBUG) Log.d(TAG,"Bad size");
|
||||
stats.rxErrors++;
|
||||
rxState = RxStateType.STATE_SYNC;
|
||||
break;
|
||||
}
|
||||
|
||||
if (DEBUG) Log.d(TAG,"Received");
|
||||
|
||||
rxBuffer.position(0);
|
||||
receiveObject(rxType, rxObjId, rxInstId, rxBuffer);
|
||||
stats.rxObjectBytes += rxLength;
|
||||
stats.rxObjects++;
|
||||
|
||||
if (rxCS != rxCSPacket) { // packet error - faulty CRC
|
||||
if (DEBUG) Log.d(TAG,"Bad crc");
|
||||
stats.rxErrors++;
|
||||
rxState = RxStateType.STATE_SYNC;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rxPacketLength != (packetSize + 1)) { // packet error -
|
||||
// mismatched packet
|
||||
// size
|
||||
if (DEBUG) Log.d(TAG,"Bad size");
|
||||
stats.rxErrors++;
|
||||
default:
|
||||
rxState = RxStateType.STATE_SYNC;
|
||||
break;
|
||||
stats.rxErrors++;
|
||||
}
|
||||
|
||||
if (DEBUG) Log.d(TAG,"Received");
|
||||
|
||||
rxBuffer.position(0);
|
||||
receiveObject(rxType, rxObjId, rxInstId, rxBuffer);
|
||||
stats.rxObjectBytes += rxLength;
|
||||
stats.rxObjects++;
|
||||
|
||||
rxState = RxStateType.STATE_SYNC;
|
||||
break;
|
||||
|
||||
default:
|
||||
rxState = RxStateType.STATE_SYNC;
|
||||
stats.rxErrors++;
|
||||
}
|
||||
|
||||
// Done
|
||||
@ -510,8 +547,7 @@ public class UAVTalk {
|
||||
* length \return Success (true), Failure (false)
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean receiveObject(int type, long objId, long instId,
|
||||
ByteBuffer data) throws IOException {
|
||||
public boolean receiveObject(int type, long objId, long instId, ByteBuffer data) throws IOException {
|
||||
|
||||
if (DEBUG) Log.d(TAG, "Received object ID: " + objId);
|
||||
assert (objMngr != null);
|
||||
@ -526,11 +562,13 @@ public class UAVTalk {
|
||||
// All instances, not allowed for OBJ messages
|
||||
if (!allInstances) {
|
||||
if (DEBUG) Log.d(TAG,"Received object: " + objMngr.getObject(objId).getName());
|
||||
|
||||
// Get object and update its data
|
||||
obj = updateObject(objId, instId, data);
|
||||
// Check if an ack is pending
|
||||
|
||||
if (obj != null) {
|
||||
updateAck(obj);
|
||||
// Check if this is a response to a UAVTalk transaction
|
||||
updateObjReq(obj);
|
||||
} else {
|
||||
error = true;
|
||||
}
|
||||
@ -581,7 +619,7 @@ public class UAVTalk {
|
||||
// Check if object exists:
|
||||
if (obj != null)
|
||||
{
|
||||
updateNack(obj);
|
||||
receivedNack(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -620,11 +658,13 @@ public class UAVTalk {
|
||||
|
||||
// Get object
|
||||
UAVObject obj = objMngr.getObject(objId, instId);
|
||||
|
||||
// If the instance does not exist create it
|
||||
if (obj == null) {
|
||||
// Get the object type
|
||||
UAVObject tobj = objMngr.getObject(objId);
|
||||
if (tobj == null) {
|
||||
// TODO: Return a NAK since we don't know this object
|
||||
return null;
|
||||
}
|
||||
// Make sure this is a data object
|
||||
@ -661,32 +701,62 @@ public class UAVTalk {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a transaction is pending and if yes complete it.
|
||||
* Called when an object is received to check if this completes
|
||||
* a UAVTalk transaction
|
||||
*/
|
||||
void updateNack(UAVObject obj)
|
||||
{
|
||||
if (DEBUG) Log.d(TAG, "NACK received: " + obj.getName());
|
||||
private synchronized void updateObjReq(UAVObject obj) {
|
||||
// Check if this is not a possible candidate
|
||||
Assert.assertNotNull(obj);
|
||||
//obj.transactionCompleted(false);
|
||||
if (respObj != null && respObj.getObjID() == obj.getObjID() &&
|
||||
(respObj.getInstID() == obj.getInstID() || respAllInstances)) {
|
||||
if (transactionListener != null)
|
||||
transactionListener.TransactionFailed(obj);
|
||||
|
||||
if(respObj != null && respType == TYPE_OBJ_REQ && respObj.getObjID() == obj.getObjID() &&
|
||||
((respObj.getInstID() == obj.getInstID() || !respAllInstances))) {
|
||||
|
||||
// Indicate complete
|
||||
respObj = null;
|
||||
|
||||
// Notify listener
|
||||
if (transactionListener != null)
|
||||
transactionListener.TransactionSucceeded(obj);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a transaction is pending and if yes complete it.
|
||||
*/
|
||||
synchronized void updateAck(UAVObject obj) {
|
||||
if (DEBUG) Log.d(TAG, "ACK received: " + obj.getName());
|
||||
private synchronized void receivedNack(UAVObject obj)
|
||||
{
|
||||
Assert.assertNotNull(obj);
|
||||
if(respObj != null && (respType == TYPE_OBJ_REQ || respType == TYPE_OBJ_ACK ) &&
|
||||
respObj.getObjID() == obj.getObjID()) {
|
||||
|
||||
if (DEBUG) Log.d(TAG, "NAK: " + obj.getName());
|
||||
|
||||
// Indicate complete
|
||||
respObj = null;
|
||||
|
||||
// Notify listener
|
||||
if (transactionListener != null)
|
||||
transactionListener.TransactionFailed(obj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a transaction is pending that this acked object corresponds to
|
||||
* and if yes complete it.
|
||||
*/
|
||||
private synchronized void updateAck(UAVObject obj) {
|
||||
if (DEBUG) Log.d(TAG, "Received ack: " + obj.getName());
|
||||
Assert.assertNotNull(obj);
|
||||
if (respObj != null && respObj.getObjID() == obj.getObjID()
|
||||
&& (respObj.getInstID() == obj.getInstID() || respAllInstances)) {
|
||||
|
||||
// Indicate complete
|
||||
respObj = null;
|
||||
|
||||
// Notify listener
|
||||
if (transactionListener != null)
|
||||
transactionListener.TransactionSucceeded(obj);
|
||||
respObj = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -698,7 +768,7 @@ public class UAVTalk {
|
||||
* @return Success (true), Failure (false)
|
||||
* @throws IOException
|
||||
*/
|
||||
public synchronized boolean transmitObject(UAVObject obj, int type, boolean allInstances) throws IOException {
|
||||
private boolean transmitObject(UAVObject obj, int type, boolean allInstances) throws IOException {
|
||||
// If all instances are requested on a single instance object it is an
|
||||
// error
|
||||
if (allInstances && obj.isSingleInstance()) {
|
||||
@ -712,6 +782,10 @@ public class UAVTalk {
|
||||
int numInst = objMngr.getNumInstances(obj.getObjID());
|
||||
// Send all instances
|
||||
for (int instId = 0; instId < numInst; ++instId) {
|
||||
// TODO: This code is buggy probably. We should send each request
|
||||
// and wait for an ack in the case of an TYPE_OBJ_ACK
|
||||
Assert.assertNotSame(type, TYPE_OBJ_ACK); // catch any buggy calls
|
||||
|
||||
UAVObject inst = objMngr.getObject(obj.getObjID(), instId);
|
||||
transmitSingleObject(inst, type, false);
|
||||
}
|
||||
@ -738,8 +812,7 @@ public class UAVTalk {
|
||||
* @param[in] obj Object handle to send
|
||||
* @param[in] type Transaction type \return Success (true), Failure (false)
|
||||
*/
|
||||
public synchronized boolean transmitSingleObject(UAVObject obj, int type,
|
||||
boolean allInstances) throws IOException {
|
||||
private boolean transmitSingleObject(UAVObject obj, int type, boolean allInstances) throws IOException {
|
||||
int length;
|
||||
int allInstId = ALL_INSTANCES;
|
||||
|
||||
@ -793,6 +866,13 @@ public class UAVTalk {
|
||||
bbuf.position(0);
|
||||
byte[] dst = new byte[packlen];
|
||||
bbuf.get(dst, 0, packlen);
|
||||
|
||||
if (type == TYPE_OBJ_ACK || type == TYPE_OBJ_REQ) {
|
||||
// Once we send a UAVTalk packet that requires an ack or object let's set up
|
||||
// the transaction here
|
||||
setupTransaction(obj, allInstances, type);
|
||||
}
|
||||
|
||||
outStream.write(dst);
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user