1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2024-11-30 08:24:11 +01:00

Merge branch 'android' into sim_android

This commit is contained in:
James Cotton 2012-08-10 10:10:47 -05:00
commit a8fa1c6b0d
8 changed files with 815 additions and 503 deletions

View File

@ -29,11 +29,14 @@
*/
package org.openpilot.androidgcs;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import org.junit.Assert;
import org.openpilot.androidgcs.fragments.ObjectManagerFragment;
import org.openpilot.androidgcs.telemetry.OPTelemetryService;
import org.openpilot.androidgcs.telemetry.OPTelemetryService.LocalBinder;
@ -76,105 +79,22 @@ public abstract class ObjectManagerActivity extends Activity {
BroadcastReceiver connectedReceiver;
//! Indicate if this activity has already connected it's telemetry callbacks
private boolean telemetryStatsConnected = false;
//! Maintain a list of all the UAVObject listeners for this activity
private HashMap<Observer, UAVObject> listeners;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
connectedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG)
Log.d(TAG, "Received intent");
TelemTask task;
if(intent.getAction().compareTo(OPTelemetryService.INTENT_ACTION_CONNECTED) == 0) {
if(binder == null)
return;
if((task = binder.getTelemTask(0)) == null)
return;
objMngr = task.getObjectManager();
mConnected = true;
onOPConnected();
Log.d(TAG, "Connected()");
} else if (intent.getAction().compareTo(OPTelemetryService.INTENT_ACTION_DISCONNECTED) == 0) {
objMngr = null;
mConnected = false;
onOPDisconnected();
Log.d(TAG, "Disonnected()");
}
}
};
IntentFilter filter = new IntentFilter();
filter.addCategory(OPTelemetryService.INTENT_CATEGORY_GCS);
filter.addAction(OPTelemetryService.INTENT_ACTION_CONNECTED);
filter.addAction(OPTelemetryService.INTENT_ACTION_DISCONNECTED);
registerReceiver(connectedReceiver, filter);
}
/**
* Called whenever any objects subscribed to via registerObjects
* whenever this Activity is not paused
*/
protected void objectUpdated(UAVObject obj) {
}
/**
* A message handler and a custom Observer to use it which calls
* objectUpdated with the right object type
*/
final Handler uavobjHandler = new Handler();
private class ActivityUpdatedObserver implements Observer {
UAVObject obj;
ActivityUpdatedObserver(UAVObject obj) { this.obj = obj; };
@Override
public void update(Observable observable, Object data) {
uavobjHandler.post(new Runnable() {
@Override
public void run() { objectUpdated(obj); }
});
}
};
private class FragmentUpdatedObserver implements Observer {
UAVObject obj;
ObjectManagerFragment frag;
FragmentUpdatedObserver(UAVObject obj, ObjectManagerFragment frag) {
this.obj = obj;
this.frag = frag;
};
@Override
public void update(Observable observable, Object data) {
uavobjHandler.post(new Runnable() {
@Override
public void run() { frag.objectUpdated(obj); }
});
}
};
/**
* Register an activity to receive updates from this object
*
* the objectUpdated() method will be called in the original UI thread
*/
protected void registerObjectUpdates(UAVObject object) {
object.addUpdatedObserver(new ActivityUpdatedObserver(object));
}
public void registerObjectUpdates(UAVObject object,
ObjectManagerFragment frag) {
object.addUpdatedObserver(new FragmentUpdatedObserver(object, frag));
}
protected void registerObjectUpdates(List<List<UAVObject>> objects) {
ListIterator<List<UAVObject>> li = objects.listIterator();
while(li.hasNext()) {
ListIterator<UAVObject> li2 = li.next().listIterator();
while(li2.hasNext())
registerObjectUpdates(li2.next());
}
}
private void updateStats() {
UAVObject stats = objMngr.getObject("GCSTelemetryStats");
TextView rxRate = (TextView) findViewById(R.id.telemetry_stats_rx_rate);
@ -208,14 +128,21 @@ public abstract class ObjectManagerActivity extends Activity {
if ( telemetryStatsConnected )
return;
// Create a map for all the object updates register for this activity. If anyone
// tries to register an object update before this a null exception will occur
listeners = new HashMap<Observer,UAVObject>();
// We are not using the objectUpdated mechanism in place so that all the children
// don't have to sort through the messages.
UAVObject stats = objMngr.getObject("GCSTelemetryStats");
if (stats == null)
return;
stats.addUpdatedObserver(telemetryObserver);
telemetryStatsConnected = true;
if (!telemetryStatsConnected) {
UAVObject stats = objMngr.getObject("GCSTelemetryStats");
if (stats == null)
return;
stats.addUpdatedObserver(telemetryObserver);
telemetryStatsConnected = true;
}
updateStats();
if (DEBUG) Log.d(TAG, "Notifying listeners about connection. There are " + connectionListeners.countObservers());
@ -236,46 +163,103 @@ public abstract class ObjectManagerActivity extends Activity {
// Providing a null update triggers a disconnect on fragments
connectionListeners.disconnected();
if (objMngr == null) {
Log.d(TAG, "onOPDisconnected(): Object manager already went away");
return;
}
if (telemetryStatsConnected) {
UAVObject stats = objMngr.getObject("GCSTelemetryStats");
if (stats != null) {
stats.removeUpdatedObserver(telemetryObserver);
}
telemetryStatsConnected = false;
}
// Disconnect from any UAVO updates
if (DEBUG) Log.d(TAG, "onOpDisconnected(): Pausing the listeners and deleting the list");
pauseObjectUpdates();
listeners = null;
}
@Override
protected void onResume() {
super.onResume();
if (mConnected && !telemetryStatsConnected) {
UAVObject stats = objMngr.getObject("GCSTelemetryStats");
if (stats == null)
return;
stats.addUpdatedObserver(telemetryObserver);
telemetryStatsConnected = true;
}
resumeObjectUpdates();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (!mBound || binder == null) {
Log.e(TAG, "Unable to connect to service");
return super.onOptionsItemSelected(item);
}
switch(item.getItemId()) {
case R.id.menu_connect:
binder.openConnection();
return true;
case R.id.menu_disconnect:
binder.stopConnection();
return true;
case R.id.menu_settings:
startActivity(new Intent(this, Preferences.class));
return true;
default:
return super.onOptionsItemSelected(item);
protected void onPause() {
super.onPause();
if (telemetryStatsConnected) {
UAVObject stats = objMngr.getObject("GCSTelemetryStats");
Assert.assertNotNull(stats); // Should not be null if we connected
stats.removeUpdatedObserver(telemetryObserver);
telemetryStatsConnected = false;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.status_menu, menu);
inflater.inflate(R.menu.options_menu, menu);
return true;
pauseObjectUpdates();
}
@Override
public void onStart() {
super.onStart();
if (DEBUG) Log.d(TAG, "onStart()");
// Register a receiver to get connected/disconnected signals from the telemetry
// service
connectedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG)
Log.d(TAG, "Received intent");
TelemTask task;
if(intent.getAction().compareTo(OPTelemetryService.INTENT_ACTION_CONNECTED) == 0) {
if(binder == null)
return;
if((task = binder.getTelemTask(0)) == null)
return;
objMngr = task.getObjectManager();
mConnected = true;
onOPConnected();
Log.d(TAG, "Connected()");
} else if (intent.getAction().compareTo(OPTelemetryService.INTENT_ACTION_DISCONNECTED) == 0) {
onOPDisconnected();
objMngr = null;
mConnected = false;
Log.d(TAG, "Disonnected()");
}
}
};
// Set up the filters
IntentFilter filter = new IntentFilter();
filter.addCategory(OPTelemetryService.INTENT_CATEGORY_GCS);
filter.addAction(OPTelemetryService.INTENT_ACTION_CONNECTED);
filter.addAction(OPTelemetryService.INTENT_ACTION_DISCONNECTED);
registerReceiver(connectedReceiver, filter);
// Bind to the telemetry service (which will start it)
Intent intent = new Intent(getApplicationContext(),
org.openpilot.androidgcs.telemetry.OPTelemetryService.class);
if (DEBUG)
Log.d(TAG, "Attempting to bind: " + intent);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
/**
@ -284,14 +268,137 @@ public abstract class ObjectManagerActivity extends Activity {
@Override
public void onStop() {
super.onStop();
if (DEBUG) Log.d(TAG, "onStop()");
unbindService(mConnection);
//unregisterReceiver(connectedReceiver);
unregisterReceiver(connectedReceiver);
connectedReceiver = null;
// Disconnect from any UAVO updates
if (DEBUG) Log.d(TAG, "onStop(): Pausing the listeners and deleting the list");
pauseObjectUpdates();
listeners = null;
}
public void onBind() {
/*********** This provides the object update messaging service ************/
/**
* A message handler and a custom Observer to use it which calls
* objectUpdated with the right object type
*/
final Handler uavobjHandler = new Handler();
private class ActivityUpdatedObserver implements Observer {
UAVObject obj;
ActivityUpdatedObserver(UAVObject obj) { this.obj = obj; };
@Override
public void update(Observable observable, Object data) {
uavobjHandler.post(new Runnable() {
@Override
public void run() { objectUpdated(obj); }
});
}
};
private class FragmentUpdatedObserver implements Observer {
UAVObject obj;
ObjectManagerFragment frag;
FragmentUpdatedObserver(UAVObject obj, ObjectManagerFragment frag) {
this.obj = obj;
this.frag = frag;
};
@Override
public void update(Observable observable, Object data) {
uavobjHandler.post(new Runnable() {
@Override
public void run() { frag.objectUpdated(obj); }
});
}
};
/**
* Unregister all the objects connected to this activity
*/
private boolean paused = false;
/**
* When an activity is paused, disconnect from all
* updates to ensure we don't draw to an invalid view
*/
protected void pauseObjectUpdates()
{
// When listeners is null then a pause occurred after
// disconnecting from the service
if (listeners == null)
return;
Set<Observer> s = listeners.keySet();
Iterator<Observer> i = s.iterator();
while (i.hasNext()) {
Observer o = i.next();
UAVObject obj = listeners.get(o);
obj.removeUpdatedObserver(o);
}
paused = true;
}
/**
* When an activity is resumed, reconnect all now the view
* is valid again
*/
protected void resumeObjectUpdates()
{
// When listeners is null this is the resume at the beginning
// before connecting to the telemetry service
if(listeners == null)
return;
Set<Observer> s = listeners.keySet();
Iterator<Observer> i = s.iterator();
while (i.hasNext()) {
Observer o = i.next();
UAVObject obj = listeners.get(o);
obj.addUpdatedObserver(o);
}
paused = false;
}
/**
* Register to listen to a single object from a fragment
* @param object The object to listen to updates from
* @param frag The fragment who should be notified
* the objectUpdated() method will be called in the original UI thread
*/
public void registerObjectUpdates(UAVObject object,
ObjectManagerFragment frag) {
Observer o = new FragmentUpdatedObserver(object, frag);
listeners.put(o, object);
if (!paused)
object.addUpdatedObserver(o);
}
/**
* Register an activity to receive updates from this object
* @param object The object the activity should listen to updates from
* the objectUpdated() method will be called in the original UI thread
*/
protected void registerObjectUpdates(UAVObject object) {
Observer o = new ActivityUpdatedObserver(object);
listeners.put(o, object);
if (!paused)
object.addUpdatedObserver(o);
}
/**
* Helper method to register array of objects
*/
protected void registerObjectUpdates(List<List<UAVObject>> objects) {
for (int i = 0; i < objects.size(); i++) {
List<UAVObject> inner = objects.get(i);
for (int j = 0; j < inner.size(); j++)
registerObjectUpdates(inner.get(j));
}
}
/*********** Deals with fragments listening for connections ***************/
/**
* Callbacks so ObjectManagerFragments get the onOPConnected and onOPDisconnected signals
@ -328,7 +435,6 @@ public abstract class ObjectManagerActivity extends Activity {
}
} ;
public void addOnConnectionListenerFragment(ObjectManagerFragment frag) {
connectionListeners.addObserver(new OnConnectionListener(frag));
if (DEBUG) Log.d(TAG, "Connecting " + frag + " there are now " + connectionListeners.countObservers());
@ -337,6 +443,8 @@ public abstract class ObjectManagerActivity extends Activity {
}
/*********** Deals with (dis)connection to telemetry service ***************/
/** Defines callbacks for service binding, passed to bindService() */
private final ServiceConnection mConnection = new ServiceConnection() {
@Override
@ -359,13 +467,45 @@ public abstract class ObjectManagerActivity extends Activity {
@Override
public void onServiceDisconnected(ComponentName name) {
onOPDisconnected();
mBound = false;
binder = null;
mConnected = false;
objMngr = null;
objMngr = null;
mConnected = false;
onOPDisconnected();
}
};
/************* Deals with menus *****************/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (!mBound || binder == null) {
Log.e(TAG, "Unable to connect to service");
return super.onOptionsItemSelected(item);
}
switch(item.getItemId()) {
case R.id.menu_connect:
binder.openConnection();
return true;
case R.id.menu_disconnect:
binder.stopConnection();
return true;
case R.id.menu_settings:
startActivity(new Intent(this, Preferences.class));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.status_menu, menu);
inflater.inflate(R.menu.options_menu, menu);
return true;
}
}

View File

@ -23,14 +23,18 @@
package org.openpilot.androidgcs;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import org.openpilot.androidgcs.fragments.ObjectManagerFragment;
import org.openpilot.androidgcs.telemetry.OPTelemetryService;
import org.openpilot.androidgcs.telemetry.OPTelemetryService.LocalBinder;
import org.openpilot.androidgcs.telemetry.OPTelemetryService.TelemTask;
import org.openpilot.uavtalk.UAVDataObject;
import org.openpilot.uavtalk.UAVObject;
import org.openpilot.uavtalk.UAVObjectManager;
@ -46,6 +50,7 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.view.Menu;
@ -73,6 +78,7 @@ public class UAVLocation extends MapActivity
UAVObjectManager objMngr;
boolean mBound = false;
boolean mConnected = false;
BroadcastReceiver connectedReceiver;
org.openpilot.androidgcs.telemetry.OPTelemetryService.LocalBinder binder;
GeoPoint homeLocation;
@ -103,36 +109,6 @@ public class UAVLocation extends MapActivity
mapView.postInvalidate();
// ObjectManager related stuff (can't inherit standard class)
BroadcastReceiver connectedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Received intent");
TelemTask task;
if(intent.getAction().compareTo(OPTelemetryService.INTENT_ACTION_CONNECTED) == 0) {
if(binder == null)
return;
if((task = binder.getTelemTask(0)) == null)
return;
objMngr = task.getObjectManager();
mConnected = true;
onOPConnected();
Log.d(TAG, "Connected()");
} else if (intent.getAction().compareTo(OPTelemetryService.INTENT_ACTION_DISCONNECTED) == 0) {
objMngr = null;
mConnected = false;
onOPDisconnected();
Log.d(TAG, "Disonnected()");
}
}
};
IntentFilter filter = new IntentFilter();
filter.addCategory(OPTelemetryService.INTENT_CATEGORY_GCS);
filter.addAction(OPTelemetryService.INTENT_ACTION_CONNECTED);
filter.addAction(OPTelemetryService.INTENT_ACTION_DISCONNECTED);
registerReceiver(connectedReceiver, filter);
}
//@Override
@ -192,40 +168,10 @@ public class UAVLocation extends MapActivity
void onOPConnected() {
UAVObject obj = objMngr.getObject("HomeLocation");
if(obj != null)
obj.addUpdatedObserver(new Observer() {
@Override
public void update(Observable observable, Object data) {
UAVDataObject obj = (UAVDataObject) data;
Double lat = obj.getField("Latitude").getDouble() / 10;
Double lon = obj.getField("Longitude").getDouble() / 10;
homeLocation = new GeoPoint(lat.intValue(), lon.intValue());
runOnUiThread(new Runnable() {
@Override
public void run() {
mapController.setCenter(homeLocation);
}
});
System.out.println("HomeLocation: " + homeLocation.toString());
}
});
obj.updateRequested();
registerObjectUpdates(obj);
obj = objMngr.getObject("PositionActual");
if(obj != null)
obj.addUpdatedObserver(new Observer() {
@Override
public void update(Observable observable, Object data) {
uavLocation = getUavLocation();
runOnUiThread(new Runnable() {
@Override
public void run() {
mapView.invalidate();
}
});
}
});
registerObjectUpdates(obj);
}
private GeoPoint getUavLocation() {
@ -260,7 +206,7 @@ public class UAVLocation extends MapActivity
}
void onOPDisconnected() {
unregisterObjectUpdates();
}
@Override
@ -291,10 +237,56 @@ public class UAVLocation extends MapActivity
@Override
public void onStart() {
super.onStart();
// ObjectManager related stuff (can't inherit standard class)
connectedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Received intent");
TelemTask task;
if(intent.getAction().compareTo(OPTelemetryService.INTENT_ACTION_CONNECTED) == 0) {
if(binder == null)
return;
if((task = binder.getTelemTask(0)) == null)
return;
objMngr = task.getObjectManager();
mConnected = true;
onOPConnected();
Log.d(TAG, "Connected()");
} else if (intent.getAction().compareTo(OPTelemetryService.INTENT_ACTION_DISCONNECTED) == 0) {
objMngr = null;
mConnected = false;
onOPDisconnected();
Log.d(TAG, "Disonnected()");
}
}
};
IntentFilter filter = new IntentFilter();
filter.addCategory(OPTelemetryService.INTENT_CATEGORY_GCS);
filter.addAction(OPTelemetryService.INTENT_ACTION_CONNECTED);
filter.addAction(OPTelemetryService.INTENT_ACTION_DISCONNECTED);
registerReceiver(connectedReceiver, filter);
Intent intent = new Intent(this, OPTelemetryService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
/**
* When stopping disconnect form the service and the broadcast receiver
*/
@Override
public void onStop() {
super.onStop();
if (DEBUG) Log.d(TAG, "onStop()");
unbindService(mConnection);
unregisterReceiver(connectedReceiver);
connectedReceiver = null;
// TODO: The register and unregister probably should move to onPause / onResume
unregisterObjectUpdates();
}
public void onBind() {
}
@ -330,4 +322,96 @@ public class UAVLocation extends MapActivity
onOPDisconnected();
}
};
/******* STRAIGHT COPY PASTE FROM ObjectManagerActivity *************/
/**
* Called whenever any objects subscribed to via registerObjects
*/
protected void objectUpdated(UAVObject obj) {
if (obj == null)
return;
if (obj.getName().compareTo("HomeLocation") == 0) {
Double lat = obj.getField("Latitude").getDouble() / 10;
Double lon = obj.getField("Longitude").getDouble() / 10;
homeLocation = new GeoPoint(lat.intValue(), lon.intValue());
mapController.setCenter(homeLocation);
} else if (obj.getName().compareTo("PositionActual") == 0) {
uavLocation = getUavLocation();
mapView.invalidate();
}
}
/**
* A message handler and a custom Observer to use it which calls
* objectUpdated with the right object type
*/
final Handler uavobjHandler = new Handler();
private class ActivityUpdatedObserver implements Observer {
UAVObject obj;
ActivityUpdatedObserver(UAVObject obj) { this.obj = obj; };
@Override
public void update(Observable observable, Object data) {
uavobjHandler.post(new Runnable() {
@Override
public void run() { objectUpdated(obj); }
});
}
};
private class FragmentUpdatedObserver implements Observer {
UAVObject obj;
ObjectManagerFragment frag;
FragmentUpdatedObserver(UAVObject obj, ObjectManagerFragment frag) {
this.obj = obj;
this.frag = frag;
};
@Override
public void update(Observable observable, Object data) {
uavobjHandler.post(new Runnable() {
@Override
public void run() { frag.objectUpdated(obj); }
});
}
};
/**
* Register an activity to receive updates from this object
*
* the objectUpdated() method will be called in the original UI thread
*/
HashMap<Observer, UAVObject> listeners = new HashMap<Observer,UAVObject>();
protected void registerObjectUpdates(UAVObject object) {
Observer o = new ActivityUpdatedObserver(object);
object.addUpdatedObserver(o);
listeners.put(o, object);
}
/**
* Unregister all the objects connected to this activity
*/
protected void unregisterObjectUpdates()
{
Set<Observer> s = listeners.keySet();
Iterator<Observer> i = s.iterator();
while (i.hasNext()) {
Observer o = i.next();
UAVObject obj = listeners.get(o);
obj.removeUpdatedObserver(o);
}
listeners.clear();
}
public void registerObjectUpdates(UAVObject object,
ObjectManagerFragment frag) {
Observer o = new FragmentUpdatedObserver(object, frag);
object.addUpdatedObserver(o);
listeners.put(o, object);
}
protected void registerObjectUpdates(List<List<UAVObject>> objects) {
ListIterator<List<UAVObject>> li = objects.listIterator();
while(li.hasNext()) {
ListIterator<UAVObject> li2 = li.next().listIterator();
while(li2.hasNext())
registerObjectUpdates(li2.next());
}
}
}

View File

@ -30,6 +30,7 @@ import org.openpilot.uavtalk.UAVObject;
import org.openpilot.uavtalk.UAVObjectField;
import org.openpilot.uavtalk.UAVObjectManager;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
@ -72,7 +73,16 @@ public class SystemAlarmsFragment extends ObjectManagerFragment {
if (DEBUG)
Log.d(TAG, "Updated");
if (obj.getName().compareTo("SystemAlarms") == 0) {
Activity activity = getActivity();
if (activity == null) {
// TODO: Need to unregister all the callbacks
return;
}
TextView alarms = (TextView) getActivity().findViewById(R.id.system_alarms_fragment_field);
if (alarms == null) {
// TODO: Need to figure out how to unregister all the callbacks
return;
}
UAVObjectField a = obj.getField("Alarm");
List<String> names = a.getElementNames();
String contents = new String();

View File

@ -27,6 +27,7 @@
package org.openpilot.androidgcs.telemetry;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Observable;
import java.util.Observer;
@ -83,75 +84,85 @@ public class OPTelemetryService extends Service {
private final IBinder mBinder = new LocalBinder();
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
if (DEBUG)
Log.d(TAG, "handleMessage: " + msg);
switch(msg.arg1) {
case MSG_START:
stopSelf(msg.arg2);
break;
case MSG_CONNECT:
terminate = false;
int connection_type;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(OPTelemetryService.this);
try {
connection_type = Integer.decode(prefs.getString("connection_type", ""));
} catch (NumberFormatException e) {
connection_type = 0;
}
static class ServiceHandler extends Handler {
private final WeakReference<OPTelemetryService> mService;
switch(connection_type) {
case 0: // No connection
return;
case 1:
Toast.makeText(getApplicationContext(), "Attempting fake connection", Toast.LENGTH_SHORT).show();
activeTelem = new FakeTelemetryThread();
break;
case 2:
Toast.makeText(getApplicationContext(), "Attempting BT connection", Toast.LENGTH_SHORT).show();
activeTelem = new BTTelemetryThread();
break;
case 3:
Toast.makeText(getApplicationContext(), "Attempting TCP connection", Toast.LENGTH_SHORT).show();
activeTelem = new TcpTelemetryThread();
break;
default:
throw new Error("Unsupported");
}
activeTelem.start();
break;
case MSG_DISCONNECT:
Toast.makeText(getApplicationContext(), "Disconnect requested", Toast.LENGTH_SHORT).show();
terminate = true;
activeTelem.interrupt();
try {
activeTelem.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
activeTelem = null;
ServiceHandler(OPTelemetryService service, Looper looper) {
super(looper);
mService = new WeakReference<OPTelemetryService>(service);
}
Intent intent = new Intent();
intent.setAction(INTENT_ACTION_DISCONNECTED);
sendBroadcast(intent,null);
@Override
public void handleMessage(Message msg)
{
OPTelemetryService service = mService.get();
if (service != null) {
service.handleMessage(msg);
}
}
}
stopSelf();
break;
case MSG_TOAST:
Toast.makeText(OPTelemetryService.this, (String) msg.obj, Toast.LENGTH_SHORT).show();
break;
default:
System.out.println(msg.toString());
throw new Error("Invalid message");
void handleMessage(Message msg) {
switch(msg.arg1) {
case MSG_START:
stopSelf(msg.arg2);
break;
case MSG_CONNECT:
terminate = false;
int connection_type;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(OPTelemetryService.this);
try {
connection_type = Integer.decode(prefs.getString("connection_type", ""));
} catch (NumberFormatException e) {
connection_type = 0;
}
switch(connection_type) {
case 0: // No connection
return;
case 1:
Toast.makeText(getApplicationContext(), "Attempting fake connection", Toast.LENGTH_SHORT).show();
activeTelem = new FakeTelemetryThread();
break;
case 2:
Toast.makeText(getApplicationContext(), "Attempting BT connection", Toast.LENGTH_SHORT).show();
activeTelem = new BTTelemetryThread();
break;
case 3:
Toast.makeText(getApplicationContext(), "Attempting TCP connection", Toast.LENGTH_SHORT).show();
activeTelem = new TcpTelemetryThread();
break;
default:
throw new Error("Unsupported");
}
activeTelem.start();
break;
case MSG_DISCONNECT:
Toast.makeText(getApplicationContext(), "Disconnect requested", Toast.LENGTH_SHORT).show();
terminate = true;
activeTelem.interrupt();
try {
activeTelem.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
activeTelem = null;
Intent intent = new Intent();
intent.setAction(INTENT_ACTION_DISCONNECTED);
sendBroadcast(intent,null);
stopSelf();
break;
case MSG_TOAST:
Toast.makeText(this, (String) msg.obj, Toast.LENGTH_SHORT).show();
break;
default:
System.out.println(msg.toString());
throw new Error("Invalid message");
}
};
}
/**
* Called when the service starts. It creates a thread to handle messages (e.g. connect and disconnect)
@ -160,15 +171,12 @@ public class OPTelemetryService extends Service {
public void startup() {
Toast.makeText(getApplicationContext(), "Telemetry service starting", Toast.LENGTH_SHORT).show();
if (DEBUG)
Log.d(TAG, "startup()");
thread = new HandlerThread("TelemetryServiceHandler", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
mServiceHandler = new ServiceHandler(this, mServiceLooper);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(OPTelemetryService.this);
if(prefs.getBoolean("autoconnect", false)) {
@ -190,16 +198,13 @@ public class OPTelemetryService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Currently only using as bound service
if (DEBUG)
Log.d(TAG, "onStartCommand()");
// If we get killed, after returning from here, restart
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
if (DEBUG)
Log.d(TAG, "onBind()");
return mBinder;
}
@ -360,7 +365,7 @@ public class OPTelemetryService extends Service {
mon.addObserver(new Observer() {
@Override
public void update(Observable arg0, Object arg1) {
System.out.println("Mon updated. Connected: " + mon.getConnected() + " objects updated: " + mon.getObjectsUpdated());
if (DEBUG) Log.d(TAG, "Mon updated. Connected: " + mon.getConnected() + " objects updated: " + mon.getObjectsUpdated());
if(mon.getConnected() /*&& mon.getObjectsUpdated()*/) {
Intent intent = new Intent();
intent.setAction(INTENT_ACTION_CONNECTED);
@ -439,8 +444,8 @@ public class OPTelemetryService extends Service {
mon.addObserver(new Observer() {
@Override
public void update(Observable arg0, Object arg1) {
System.out.println("Mon updated. Connected: " + mon.getConnected() + " objects updated: " + mon.getObjectsUpdated());
if(mon.getConnected() /*&& mon.getObjectsUpdated()*/) {
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);

View File

@ -87,9 +87,9 @@ public class Telemetry {
/**
* Constructor
*/
public Telemetry(UAVTalk utalk, UAVObjectManager objMngr)
public Telemetry(UAVTalk utalkIn, UAVObjectManager objMngr)
{
this.utalk = utalk;
this.utalk = utalkIn;
this.objMngr = objMngr;
// Process all objects in the list
@ -113,16 +113,28 @@ public class Telemetry {
});
// Listen to transaction completions
utalk.addObserver(new Observer() {
this.utalk.setOnTransactionCompletedListener(
this.utalk.new OnTransactionCompletedListener() {
@Override
public void update(Observable observable, Object data) {
try {
transactionCompleted((UAVObject) data);
void TransactionSucceeded(UAVObject data) {
try {
transactionCompleted(data);
} catch (IOException e) {
// Disconnect when stream fails
observable.deleteObserver(this);
utalk.setOnTransactionCompletedListener(null);
}
}
}
@Override
void TransactionFailed(UAVObject data) {
try {
Log.d(TAG, "TransactionFailed(" + data.getName() + ")");
transactionCompleted(data);
} catch (IOException e) {
// Disconnect when stream fails
utalk.setOnTransactionCompletedListener(null);
}
}
});
// Get GCS stats object
@ -475,7 +487,6 @@ public class Telemetry {
++txErrors;
obj.transactionCompleted(false);
Log.w(TAG,"Telemetry: priority event queue is full, event lost " + obj.getName());
new Exception().printStackTrace();
}
}
else

View File

@ -36,20 +36,20 @@ import java.util.TimerTask;
import android.util.Log;
public class TelemetryMonitor extends Observable{
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;
static final int STATS_UPDATE_PERIOD_MS = 4000;
static final int STATS_CONNECT_PERIOD_MS = 1000;
static final int CONNECTION_TIMEOUT_MS = 8000;
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 UAVObjectManager objMngr;
private final Telemetry tel;
// private UAVObject objPending;
// private UAVObject objPending;
private UAVObject gcsStatsObj;
private UAVObject flightStatsObj;
private Timer periodicTask;
@ -60,324 +60,333 @@ public class TelemetryMonitor extends Observable{
private boolean connected = false;
private boolean objects_updated = false;
public boolean getConnected() { return connected; };
public boolean getObjectsUpdated() { return objects_updated; };
public boolean getConnected() {
return connected;
};
public TelemetryMonitor(UAVObjectManager objMngr, Telemetry tel)
{
this.objMngr = objMngr;
this.tel = tel;
// this.objPending = null;
queue = new ArrayList<UAVObject>();
public boolean getObjectsUpdated() {
return objects_updated;
};
// Get stats objects
gcsStatsObj = objMngr.getObject("GCSTelemetryStats");
flightStatsObj = objMngr.getObject("FlightTelemetryStats");
public TelemetryMonitor(UAVObjectManager objMngr, Telemetry tel) {
this.objMngr = objMngr;
this.tel = tel;
// this.objPending = null;
queue = new ArrayList<UAVObject>();
flightStatsObj.addUpdatedObserver(new Observer() {
// Get stats objects
gcsStatsObj = objMngr.getObject("GCSTelemetryStats");
flightStatsObj = objMngr.getObject("FlightTelemetryStats");
flightStatsObj.addUpdatedObserver(new Observer() {
@Override
public void update(Observable observable, Object data) {
try {
flightStatsUpdated((UAVObject) data);
} catch (IOException e) {
// The UAVTalk stream was broken, disconnect this signal
// TODO: Should this actually be disconnected. Do we create a new TelemetryMonitor for this
// TODO: Should this actually be disconnected. Do we create
// a new TelemetryMonitor for this
// or fix the stream?
flightStatsObj.removeUpdatedObserver(this);
}
}
});
});
// Start update timer
setPeriod(STATS_CONNECT_PERIOD_MS);
// Start update timer
setPeriod(STATS_CONNECT_PERIOD_MS);
}
/**
* Initiate object retrieval, initialize queue with objects to be retrieved.
*
* @throws IOException
*/
public synchronized void startRetrievingObjects() throws IOException
{
if (DEBUG) Log.d(TAG, "Start retrieving objects");
public synchronized void startRetrievingObjects() throws IOException {
if (DEBUG)
Log.d(TAG, "Start retrieving objects");
// Clear object queue
queue.clear();
// Get all objects, add metaobjects, settings and data objects with OnChange update mode to the queue
List< List<UAVObject> > objs = objMngr.getObjects();
// Clear object queue
queue.clear();
// Get all objects, add metaobjects, settings and data objects with
// OnChange update mode to the queue
List<List<UAVObject>> objs = objMngr.getObjects();
ListIterator<List<UAVObject>> objListIterator = objs.listIterator();
while( objListIterator.hasNext() )
{
List <UAVObject> instList = objListIterator.next();
UAVObject obj = instList.get(0);
UAVObject.Metadata mdata = obj.getMetadata();
if ( obj.isMetadata() )
{
queue.add(obj);
}
else /* Data object */
{
UAVDataObject dobj = (UAVDataObject) obj;
if ( dobj.isSettings() )
{
queue.add(obj);
}
else
{
if ( mdata.GetFlightTelemetryUpdateMode() == UAVObject.UpdateMode.UPDATEMODE_ONCHANGE )
{
queue.add(obj);
}
}
}
}
// Start retrieving
System.out.println(TAG + "Starting to retrieve meta and settings objects from the autopilot (" + queue.size() + " objects)");
retrieveNextObject();
ListIterator<List<UAVObject>> objListIterator = objs.listIterator();
while (objListIterator.hasNext()) {
List<UAVObject> instList = objListIterator.next();
UAVObject obj = instList.get(0);
UAVObject.Metadata mdata = obj.getMetadata();
if (obj.isMetadata()) {
queue.add(obj);
} else /* Data object */
{
UAVDataObject dobj = (UAVDataObject) obj;
if (dobj.isSettings()) {
queue.add(obj);
} else {
if (mdata.GetFlightTelemetryUpdateMode() == UAVObject.UpdateMode.UPDATEMODE_ONCHANGE) {
queue.add(obj);
}
}
}
}
// Start retrieving
Log.d(TAG,
"Starting to retrieve meta and settings objects from the autopilot ("
+ queue.size() + " objects)");
retrieveNextObject();
}
/**
* Cancel the object retrieval
*/
public void stopRetrievingObjects()
{
public void stopRetrievingObjects() {
Log.d(TAG, "Stop retrieving objects");
queue.clear();
queue.clear();
}
/**
* Retrieve the next object in the queue
*
* @throws IOException
*/
public synchronized void retrieveNextObject() throws IOException
{
// If queue is empty return
if ( queue.isEmpty() )
{
if (DEBUG) Log.d(TAG, "All objects retrieved: Connected Successfully");
objects_updated = true;
setChanged();
notifyObservers();
return;
}
// Get next object from the queue
UAVObject obj = queue.remove(0);
public synchronized void retrieveNextObject() throws IOException {
// If queue is empty return
if (queue.isEmpty()) {
if (DEBUG)
Log.d(TAG, "All objects retrieved: Connected Successfully");
objects_updated = true;
setChanged();
notifyObservers();
return;
}
// Get next object from the queue
UAVObject obj = queue.remove(0);
if(obj == null) {
throw new Error("Got null object forom transaction queue");
}
if (obj == null) {
throw new Error("Got null object forom transaction queue");
}
if (DEBUG) Log.d(TAG, "Retrieving object: " + obj.getName()) ;
// Connect to object
if (DEBUG)
Log.d(TAG, "Retrieving object: " + obj.getName());
// TODO: Does this need to stay here permanently? This appears to be used for setup mainly
obj.addTransactionCompleted(new Observer() {
// 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);
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
// When the telemetry stream is broken disconnect these
// updates
observable.deleteObserver(this);
}
}
});
});
// Request update
tel.updateRequested(obj);
// objPending = obj;
// Request update
tel.updateRequested(obj);
}
/**
* Called by the retrieved object when a transaction is completed.
*
* @throws IOException
*/
public synchronized void transactionCompleted(UAVObject obj, boolean success) throws IOException
{
//QMutexLocker locker(mutex);
// Disconnect from sending object
if (DEBUG) Log.d(TAG,"transactionCompleted. Status: " + success);
// TODO: Need to be able to disconnect signals
//obj->disconnect(this);
// objPending = null;
public synchronized void transactionCompleted(UAVObject obj, boolean success)
throws IOException {
if (DEBUG)
Log.d(TAG, "transactionCompleted. Status: " + success);
if(!success) {
//Log.e(TAG, "Transaction failed: " + obj.getName() + " sending again.");
return;
}
if (!success) {
// Right now success = false means received a NAK so don't
// re-attempt
Log.e(TAG, "Transaction failed.");
}
// Process next object if telemetry is still available
if ( ((String) gcsStatsObj.getField("Status").getValue()).compareTo("Connected") == 0 )
{
retrieveNextObject();
}
else
{
stopRetrievingObjects();
}
// Process next object if telemetry is still available
if (((String) gcsStatsObj.getField("Status").getValue())
.compareTo("Connected") == 0) {
retrieveNextObject();
} else {
stopRetrievingObjects();
}
}
/**
* Called each time the flight stats object is updated by the autopilot
*
* @throws IOException
*/
public synchronized void flightStatsUpdated(UAVObject obj) throws IOException
{
// Force update if not yet connected
gcsStatsObj = objMngr.getObject("GCSTelemetryStats");
flightStatsObj = objMngr.getObject("FlightTelemetryStats");
if (DEBUG) Log.d(TAG,"GCS Status: " + gcsStatsObj.getField("Status").getValue());
if (DEBUG) Log.d(TAG,"Flight Status: " + flightStatsObj.getField("Status").getValue());
if ( ((String) gcsStatsObj.getField("Status").getValue()).compareTo("Connected") != 0 ||
((String) flightStatsObj.getField("Status").getValue()).compareTo("Connected") == 0 )
{
processStatsUpdates();
}
public synchronized void flightStatsUpdated(UAVObject obj)
throws IOException {
// Force update if not yet connected
gcsStatsObj = objMngr.getObject("GCSTelemetryStats");
flightStatsObj = objMngr.getObject("FlightTelemetryStats");
if (DEBUG)
Log.d(TAG, "GCS Status: "
+ gcsStatsObj.getField("Status").getValue());
if (DEBUG)
Log.d(TAG, "Flight Status: "
+ flightStatsObj.getField("Status").getValue());
if (((String) gcsStatsObj.getField("Status").getValue())
.compareTo("Connected") != 0
|| ((String) flightStatsObj.getField("Status").getValue())
.compareTo("Connected") == 0) {
processStatsUpdates();
}
}
private long lastStatsTime;
/**
* Called periodically to update the statistics and connection status.
*
* @throws IOException
*/
public synchronized void processStatsUpdates() throws IOException
{
// Get telemetry stats
if (DEBUG) Log.d(TAG, "processStatsUpdates()");
Telemetry.TelemetryStats telStats = tel.getStats();
public synchronized void processStatsUpdates() throws IOException {
// Get telemetry stats
if (DEBUG)
Log.d(TAG, "processStatsUpdates()");
Telemetry.TelemetryStats telStats = tel.getStats();
if (DEBUG) Log.d(TAG, "processStatsUpdates() - stats reset");
if (DEBUG)
Log.d(TAG, "processStatsUpdates() - stats reset");
// Need to compute time because this update is not regular enough
float dT = (System.currentTimeMillis() - lastStatsTime) / 1000.0f;
lastStatsTime = System.currentTimeMillis();
// Need to compute time because this update is not regular enough
float dT = (System.currentTimeMillis() - lastStatsTime) / 1000.0f;
lastStatsTime = System.currentTimeMillis();
// Update stats object
gcsStatsObj.getField("RxDataRate").setDouble( telStats.rxBytes / dT );
gcsStatsObj.getField("TxDataRate").setDouble( telStats.txBytes / dT );
UAVObjectField field = gcsStatsObj.getField("RxFailures");
field.setDouble(field.getDouble() + telStats.rxErrors);
field = gcsStatsObj.getField("TxFailures");
field.setDouble(field.getDouble() + telStats.txErrors);
field = gcsStatsObj.getField("TxRetries");
field.setDouble(field.getDouble() + telStats.txRetries);
// Update stats object
gcsStatsObj.getField("RxDataRate").setDouble(telStats.rxBytes / dT);
gcsStatsObj.getField("TxDataRate").setDouble(telStats.txBytes / dT);
UAVObjectField field = gcsStatsObj.getField("RxFailures");
field.setDouble(field.getDouble() + telStats.rxErrors);
field = gcsStatsObj.getField("TxFailures");
field.setDouble(field.getDouble() + telStats.txErrors);
field = gcsStatsObj.getField("TxRetries");
field.setDouble(field.getDouble() + telStats.txRetries);
tel.resetStats();
tel.resetStats();
if (DEBUG) Log.d(TAG, "processStatsUpdates() - stats updated");
if (DEBUG)
Log.d(TAG, "processStatsUpdates() - stats updated");
// Check for a connection timeout
boolean connectionTimeout;
if ( telStats.rxObjects > 0 )
{
lastUpdateTime = System.currentTimeMillis();
// Check for a connection timeout
boolean connectionTimeout;
if (telStats.rxObjects > 0) {
lastUpdateTime = System.currentTimeMillis();
}
if ( (System.currentTimeMillis() - lastUpdateTime) > CONNECTION_TIMEOUT_MS )
{
connectionTimeout = true;
}
else
{
connectionTimeout = false;
}
}
if ((System.currentTimeMillis() - lastUpdateTime) > CONNECTION_TIMEOUT_MS) {
connectionTimeout = true;
} else {
connectionTimeout = false;
}
// Update connection state
gcsStatsObj = objMngr.getObject("GCSTelemetryStats");
flightStatsObj = objMngr.getObject("FlightTelemetryStats");
if(gcsStatsObj == null) {
System.out.println("No GCS stats yet");
return;
}
UAVObjectField statusField = gcsStatsObj.getField("Status");
String oldStatus = new String((String) statusField.getValue());
// Update connection state
gcsStatsObj = objMngr.getObject("GCSTelemetryStats");
flightStatsObj = objMngr.getObject("FlightTelemetryStats");
if (gcsStatsObj == null) {
Log.d(TAG, "No GCS stats yet");
return;
}
UAVObjectField statusField = gcsStatsObj.getField("Status");
String oldStatus = new String((String) statusField.getValue());
if (DEBUG) Log.d(TAG,"GCS: " + statusField.getValue() + " Flight: " + flightStatsObj.getField("Status").getValue());
if (DEBUG)
Log.d(TAG, "GCS: " + statusField.getValue() + " Flight: "
+ flightStatsObj.getField("Status").getValue());
if ( oldStatus.compareTo("Disconnected") == 0 )
{
// Request connection
statusField.setValue("HandshakeReq");
}
else if ( oldStatus.compareTo("HandshakeReq") == 0 )
{
// Check for connection acknowledge
if ( ((String) flightStatsObj.getField("Status").getValue()).compareTo("HandshakeAck") == 0 )
{
statusField.setValue("Connected");
if (DEBUG) Log.d(TAG,"Connected" + statusField.toString());
}
}
else if ( oldStatus.compareTo("Connected") == 0 )
{
// Check if the connection is still active and the the autopilot is still connected
if ( ((String) flightStatsObj.getField("Status").getValue()).compareTo("Disconnected") == 0 || connectionTimeout)
{
statusField.setValue("Disconnected");
}
}
if (oldStatus.compareTo("Disconnected") == 0) {
// Request connection
statusField.setValue("HandshakeReq");
} else if (oldStatus.compareTo("HandshakeReq") == 0) {
// Check for connection acknowledge
if (((String) flightStatsObj.getField("Status").getValue())
.compareTo("HandshakeAck") == 0) {
statusField.setValue("Connected");
if (DEBUG)
Log.d(TAG, "Connected" + statusField.toString());
}
} else if (oldStatus.compareTo("Connected") == 0) {
// Check if the connection is still active and the the autopilot is
// still connected
if (((String) flightStatsObj.getField("Status").getValue())
.compareTo("Disconnected") == 0 || connectionTimeout) {
statusField.setValue("Disconnected");
}
}
// Force telemetry update if not yet connected
boolean gcsStatusChanged = !oldStatus.equals(statusField.getValue());
// Force telemetry update if not yet connected
boolean gcsStatusChanged = !oldStatus.equals(statusField.getValue());
boolean gcsConnected = statusField.getValue().equals("Connected");
boolean gcsDisconnected = statusField.getValue().equals("Disconnected");
boolean flightConnected = flightStatsObj.getField("Status").equals("Connected");
boolean gcsConnected = statusField.getValue().equals("Connected");
boolean gcsDisconnected = statusField.getValue().equals("Disconnected");
boolean flightConnected = flightStatsObj.getField("Status").equals(
"Connected");
if ( !gcsConnected || !flightConnected )
{
if (DEBUG) Log.d(TAG,"Sending gcs status");
gcsStatsObj.updated();
}
if (!gcsConnected || !flightConnected) {
if (DEBUG)
Log.d(TAG, "Sending gcs status");
gcsStatsObj.updated();
}
// Act on new connections or disconnections
if (gcsConnected && gcsStatusChanged)
{
if (DEBUG) Log.d(TAG,"Connection with the autopilot established");
setPeriod(STATS_UPDATE_PERIOD_MS);
connected = true;
objects_updated = false;
startRetrievingObjects();
setChanged();
}
if (gcsDisconnected && gcsStatusChanged)
{
if (DEBUG) Log.d(TAG,"Trying to connect to the autopilot");
setPeriod(STATS_CONNECT_PERIOD_MS);
connected = false;
objects_updated = false;
setChanged();
}
// Act on new connections or disconnections
if (gcsConnected && gcsStatusChanged) {
if (DEBUG)
Log.d(TAG, "Connection with the autopilot established");
setPeriod(STATS_UPDATE_PERIOD_MS);
connected = true;
objects_updated = false;
startRetrievingObjects();
setChanged();
}
if (gcsDisconnected && gcsStatusChanged) {
if (DEBUG)
Log.d(TAG, "Trying to connect to the autopilot");
setPeriod(STATS_CONNECT_PERIOD_MS);
connected = false;
objects_updated = false;
setChanged();
}
if (DEBUG) Log.d(TAG, "processStatsUpdates() - before notify");
notifyObservers();
if (DEBUG) Log.d(TAG, "processStatsUpdates() - after notify");
if (DEBUG)
Log.d(TAG, "processStatsUpdates() - before notify");
notifyObservers();
if (DEBUG)
Log.d(TAG, "processStatsUpdates() - after notify");
}
private void setPeriod(int ms) {
if(periodicTask == null)
periodicTask = new Timer();
if (periodicTask == null)
periodicTask = new Timer();
periodicTask.cancel();
currentPeriod = ms;
periodicTask = new Timer();
periodicTask.scheduleAtFixedRate(new TimerTask() {
periodicTask.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
try {
processStatsUpdates();
} catch (IOException e) {
// Once the stream has died stop trying to process these updates
// Once the stream has died stop trying to process these
// updates
periodicTask.cancel();
}
}
}, currentPeriod, currentPeriod);
}, currentPeriod, currentPeriod);
}
public void stopMonitor()
{
public void stopMonitor() {
periodicTask.cancel();
periodicTask = null;
}

View File

@ -174,9 +174,10 @@ public class UAVObjectField {
Integer val = (Integer) getValue(index);
dataOut.put(val.byteValue());
}
break;
case STRING:
// TODO: Implement strings
throw new Error("Strings not yet implemented");
throw new Error("Strings not yet implemented. Field name: " + getName());
}
// Done
return getNumBytes();

View File

@ -31,16 +31,18 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Observable;
import junit.framework.Assert;
import android.util.Log;
public class UAVTalk extends Observable {
public class UAVTalk {
static final String TAG = "UAVTalk";
public static int LOGLEVEL = 0;
public static boolean WARN = LOGLEVEL > 1;
public static boolean DEBUG = LOGLEVEL > 0;
public static boolean VERBOSE = LOGLEVEL > 3;
public static boolean WARN = LOGLEVEL > 2;
public static boolean DEBUG = LOGLEVEL > 1;
public static boolean ERROR = LOGLEVEL > 0;
private Thread inputProcessingThread = null;
@ -102,12 +104,13 @@ public class UAVTalk extends Observable {
STATE_SYNC, STATE_TYPE, STATE_SIZE, STATE_OBJID, STATE_INSTID, STATE_DATA, STATE_CS
};
static final int TYPE_MASK = 0xFC;
static final int TYPE_MASK = 0xF8;
static final int TYPE_VER = 0x20;
static final int TYPE_OBJ = (TYPE_VER | 0x00);
static final int TYPE_OBJ_REQ = (TYPE_VER | 0x01);
static final int TYPE_OBJ_ACK = (TYPE_VER | 0x02);
static final int TYPE_ACK = (TYPE_VER | 0x03);
static final int TYPE_NACK = (TYPE_VER | 0x04);
static final int MIN_HEADER_LENGTH = 8; // sync(1), type (1), size(2),
// object ID(4)
@ -271,6 +274,8 @@ public class UAVTalk extends Observable {
// 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;
@ -290,7 +295,7 @@ public class UAVTalk extends Observable {
* @throws IOException
*/
public synchronized boolean processInputByte(int rxbyte) throws IOException {
assert (objMngr != null);
Assert.assertNotNull(objMngr);
// Update stats
stats.rxBytes++;
@ -318,12 +323,13 @@ public class UAVTalk extends Observable {
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;
@ -380,7 +386,7 @@ public class UAVTalk extends Observable {
}
// Determine data length
if (rxType == TYPE_OBJ_REQ || rxType == TYPE_ACK)
if (rxType == TYPE_OBJ_REQ || rxType == TYPE_ACK || rxType == TYPE_NACK)
rxLength = 0;
else
rxLength = rxObj.getNumBytes();
@ -565,6 +571,24 @@ public class UAVTalk extends Observable {
error = true;
}
break;
case TYPE_NACK:
if (DEBUG) Log.d(TAG, "Received NAK: " + objId + " " + objMngr.getObject(objId).getName());
// All instances, not allowed for NACK messages
if (!allInstances)
{
// Get object
obj = objMngr.getObject(objId, instId);
// Check if object exists:
if (obj != null)
{
updateNack(obj);
}
else
{
error = true;
}
}
break;
case TYPE_ACK:
// All instances, not allowed for ACK messages
if (!allInstances) {
@ -636,15 +660,33 @@ public class UAVTalk extends Observable {
}
}
/**
* Check if a transaction is pending and if yes complete it.
*/
void updateNack(UAVObject obj)
{
if (DEBUG) Log.d(TAG, "NACK received: " + obj.getName());
Assert.assertNotNull(obj);
//obj.transactionCompleted(false);
if (respObj != null && respObj.getObjID() == obj.getObjID() &&
(respObj.getInstID() == obj.getInstID() || respAllInstances)) {
if (transactionListener != null)
transactionListener.TransactionFailed(obj);
respObj = null;
}
}
/**
* 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());
Assert.assertNotNull(obj);
if (respObj != null && respObj.getObjID() == obj.getObjID()
&& (respObj.getInstID() == obj.getInstID() || respAllInstances)) {
if (transactionListener != null)
transactionListener.TransactionSucceeded(obj);
respObj = null;
setChanged();
notifyObservers(obj);
}
}
@ -784,4 +826,14 @@ public class UAVTalk extends Observable {
return crc;
}
private OnTransactionCompletedListener transactionListener = null;
abstract class OnTransactionCompletedListener {
abstract void TransactionSucceeded(UAVObject data);
abstract void TransactionFailed(UAVObject data);
};
void setOnTransactionCompletedListener(OnTransactionCompletedListener onTransactionListener) {
this.transactionListener = onTransactionListener;
}
}