1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2025-03-01 18:29:16 +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; package org.openpilot.androidgcs;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.ListIterator;
import java.util.Observable; import java.util.Observable;
import java.util.Observer; import java.util.Observer;
import java.util.Set;
import org.junit.Assert;
import org.openpilot.androidgcs.fragments.ObjectManagerFragment; import org.openpilot.androidgcs.fragments.ObjectManagerFragment;
import org.openpilot.androidgcs.telemetry.OPTelemetryService; import org.openpilot.androidgcs.telemetry.OPTelemetryService;
import org.openpilot.androidgcs.telemetry.OPTelemetryService.LocalBinder; import org.openpilot.androidgcs.telemetry.OPTelemetryService.LocalBinder;
@ -76,105 +79,22 @@ public abstract class ObjectManagerActivity extends Activity {
BroadcastReceiver connectedReceiver; BroadcastReceiver connectedReceiver;
//! Indicate if this activity has already connected it's telemetry callbacks //! Indicate if this activity has already connected it's telemetry callbacks
private boolean telemetryStatsConnected = false; 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. */ /** Called when the activity is first created. */
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(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 * Called whenever any objects subscribed to via registerObjects
* whenever this Activity is not paused
*/ */
protected void objectUpdated(UAVObject obj) { 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() { private void updateStats() {
UAVObject stats = objMngr.getObject("GCSTelemetryStats"); UAVObject stats = objMngr.getObject("GCSTelemetryStats");
TextView rxRate = (TextView) findViewById(R.id.telemetry_stats_rx_rate); TextView rxRate = (TextView) findViewById(R.id.telemetry_stats_rx_rate);
@ -208,14 +128,21 @@ public abstract class ObjectManagerActivity extends Activity {
if ( telemetryStatsConnected ) if ( telemetryStatsConnected )
return; 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 // We are not using the objectUpdated mechanism in place so that all the children
// don't have to sort through the messages. // don't have to sort through the messages.
UAVObject stats = objMngr.getObject("GCSTelemetryStats");
if (stats == null)
return;
stats.addUpdatedObserver(telemetryObserver); if (!telemetryStatsConnected) {
telemetryStatsConnected = true; UAVObject stats = objMngr.getObject("GCSTelemetryStats");
if (stats == null)
return;
stats.addUpdatedObserver(telemetryObserver);
telemetryStatsConnected = true;
}
updateStats(); updateStats();
if (DEBUG) Log.d(TAG, "Notifying listeners about connection. There are " + connectionListeners.countObservers()); 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 // Providing a null update triggers a disconnect on fragments
connectionListeners.disconnected(); 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 @Override
public boolean onOptionsItemSelected(MenuItem item) { protected void onPause() {
if (!mBound || binder == null) { super.onPause();
Log.e(TAG, "Unable to connect to service");
return super.onOptionsItemSelected(item); if (telemetryStatsConnected) {
} UAVObject stats = objMngr.getObject("GCSTelemetryStats");
switch(item.getItemId()) { Assert.assertNotNull(stats); // Should not be null if we connected
case R.id.menu_connect:
binder.openConnection(); stats.removeUpdatedObserver(telemetryObserver);
return true; telemetryStatsConnected = false;
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);
} }
} pauseObjectUpdates();
@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;
} }
@Override @Override
public void onStart() { public void onStart() {
super.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(), Intent intent = new Intent(getApplicationContext(),
org.openpilot.androidgcs.telemetry.OPTelemetryService.class); org.openpilot.androidgcs.telemetry.OPTelemetryService.class);
if (DEBUG) if (DEBUG)
Log.d(TAG, "Attempting to bind: " + intent); Log.d(TAG, "Attempting to bind: " + intent);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE); bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
} }
/** /**
@ -284,14 +268,137 @@ public abstract class ObjectManagerActivity extends Activity {
@Override @Override
public void onStop() { public void onStop() {
super.onStop(); super.onStop();
if (DEBUG) Log.d(TAG, "onStop()");
unbindService(mConnection); 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 * Callbacks so ObjectManagerFragments get the onOPConnected and onOPDisconnected signals
@ -328,7 +435,6 @@ public abstract class ObjectManagerActivity extends Activity {
} }
} ; } ;
public void addOnConnectionListenerFragment(ObjectManagerFragment frag) { public void addOnConnectionListenerFragment(ObjectManagerFragment frag) {
connectionListeners.addObserver(new OnConnectionListener(frag)); connectionListeners.addObserver(new OnConnectionListener(frag));
if (DEBUG) Log.d(TAG, "Connecting " + frag + " there are now " + connectionListeners.countObservers()); 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() */ /** Defines callbacks for service binding, passed to bindService() */
private final ServiceConnection mConnection = new ServiceConnection() { private final ServiceConnection mConnection = new ServiceConnection() {
@Override @Override
@ -359,13 +467,45 @@ public abstract class ObjectManagerActivity extends Activity {
@Override @Override
public void onServiceDisconnected(ComponentName name) { public void onServiceDisconnected(ComponentName name) {
onOPDisconnected();
mBound = false; mBound = false;
binder = null; binder = null;
mConnected = false; mConnected = false;
objMngr = null; objMngr = null;
objMngr = null; objMngr = null;
mConnected = false; 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; package org.openpilot.androidgcs;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.ListIterator;
import java.util.Observable; import java.util.Observable;
import java.util.Observer; 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;
import org.openpilot.androidgcs.telemetry.OPTelemetryService.LocalBinder; import org.openpilot.androidgcs.telemetry.OPTelemetryService.LocalBinder;
import org.openpilot.androidgcs.telemetry.OPTelemetryService.TelemTask; import org.openpilot.androidgcs.telemetry.OPTelemetryService.TelemTask;
import org.openpilot.uavtalk.UAVDataObject;
import org.openpilot.uavtalk.UAVObject; import org.openpilot.uavtalk.UAVObject;
import org.openpilot.uavtalk.UAVObjectManager; import org.openpilot.uavtalk.UAVObjectManager;
@ -46,6 +50,7 @@ import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Point; import android.graphics.Point;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
@ -73,6 +78,7 @@ public class UAVLocation extends MapActivity
UAVObjectManager objMngr; UAVObjectManager objMngr;
boolean mBound = false; boolean mBound = false;
boolean mConnected = false; boolean mConnected = false;
BroadcastReceiver connectedReceiver;
org.openpilot.androidgcs.telemetry.OPTelemetryService.LocalBinder binder; org.openpilot.androidgcs.telemetry.OPTelemetryService.LocalBinder binder;
GeoPoint homeLocation; GeoPoint homeLocation;
@ -103,36 +109,6 @@ public class UAVLocation extends MapActivity
mapView.postInvalidate(); 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 //@Override
@ -192,40 +168,10 @@ public class UAVLocation extends MapActivity
void onOPConnected() { void onOPConnected() {
UAVObject obj = objMngr.getObject("HomeLocation"); UAVObject obj = objMngr.getObject("HomeLocation");
if(obj != null) registerObjectUpdates(obj);
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();
obj = objMngr.getObject("PositionActual"); obj = objMngr.getObject("PositionActual");
if(obj != null) registerObjectUpdates(obj);
obj.addUpdatedObserver(new Observer() {
@Override
public void update(Observable observable, Object data) {
uavLocation = getUavLocation();
runOnUiThread(new Runnable() {
@Override
public void run() {
mapView.invalidate();
}
});
}
});
} }
private GeoPoint getUavLocation() { private GeoPoint getUavLocation() {
@ -260,7 +206,7 @@ public class UAVLocation extends MapActivity
} }
void onOPDisconnected() { void onOPDisconnected() {
unregisterObjectUpdates();
} }
@Override @Override
@ -291,10 +237,56 @@ public class UAVLocation extends MapActivity
@Override @Override
public void onStart() { public void onStart() {
super.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); Intent intent = new Intent(this, OPTelemetryService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 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() { public void onBind() {
} }
@ -330,4 +322,96 @@ public class UAVLocation extends MapActivity
onOPDisconnected(); 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.UAVObjectField;
import org.openpilot.uavtalk.UAVObjectManager; import org.openpilot.uavtalk.UAVObjectManager;
import android.app.Activity;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -72,7 +73,16 @@ public class SystemAlarmsFragment extends ObjectManagerFragment {
if (DEBUG) if (DEBUG)
Log.d(TAG, "Updated"); Log.d(TAG, "Updated");
if (obj.getName().compareTo("SystemAlarms") == 0) { 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); 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"); UAVObjectField a = obj.getField("Alarm");
List<String> names = a.getElementNames(); List<String> names = a.getElementNames();
String contents = new String(); String contents = new String();

View File

@ -27,6 +27,7 @@
package org.openpilot.androidgcs.telemetry; package org.openpilot.androidgcs.telemetry;
import java.io.IOException; import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Observable; import java.util.Observable;
import java.util.Observer; import java.util.Observer;
@ -83,75 +84,85 @@ public class OPTelemetryService extends Service {
private final IBinder mBinder = new LocalBinder(); private final IBinder mBinder = new LocalBinder();
private final class ServiceHandler extends Handler { static class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) { private final WeakReference<OPTelemetryService> mService;
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;
}
switch(connection_type) { ServiceHandler(OPTelemetryService service, Looper looper) {
case 0: // No connection super(looper);
return; mService = new WeakReference<OPTelemetryService>(service);
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(); @Override
intent.setAction(INTENT_ACTION_DISCONNECTED); public void handleMessage(Message msg)
sendBroadcast(intent,null); {
OPTelemetryService service = mService.get();
if (service != null) {
service.handleMessage(msg);
}
}
}
stopSelf(); void handleMessage(Message msg) {
switch(msg.arg1) {
break; case MSG_START:
case MSG_TOAST: stopSelf(msg.arg2);
Toast.makeText(OPTelemetryService.this, (String) msg.obj, Toast.LENGTH_SHORT).show(); break;
break; case MSG_CONNECT:
default: terminate = false;
System.out.println(msg.toString()); int connection_type;
throw new Error("Invalid message"); 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) * 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() { public void startup() {
Toast.makeText(getApplicationContext(), "Telemetry service starting", Toast.LENGTH_SHORT).show(); 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 = new HandlerThread("TelemetryServiceHandler", Process.THREAD_PRIORITY_BACKGROUND);
thread.start(); thread.start();
// Get the HandlerThread's Looper and use it for our Handler // Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper(); mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper); mServiceHandler = new ServiceHandler(this, mServiceLooper);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(OPTelemetryService.this); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(OPTelemetryService.this);
if(prefs.getBoolean("autoconnect", false)) { if(prefs.getBoolean("autoconnect", false)) {
@ -190,16 +198,13 @@ public class OPTelemetryService extends Service {
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
// Currently only using as bound service // Currently only using as bound service
if (DEBUG)
Log.d(TAG, "onStartCommand()");
// If we get killed, after returning from here, restart // If we get killed, after returning from here, restart
return START_STICKY; return START_STICKY;
} }
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
if (DEBUG)
Log.d(TAG, "onBind()");
return mBinder; return mBinder;
} }
@ -360,7 +365,7 @@ public class OPTelemetryService extends Service {
mon.addObserver(new Observer() { mon.addObserver(new Observer() {
@Override @Override
public void update(Observable arg0, Object arg1) { 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()*/) { if(mon.getConnected() /*&& mon.getObjectsUpdated()*/) {
Intent intent = new Intent(); Intent intent = new Intent();
intent.setAction(INTENT_ACTION_CONNECTED); intent.setAction(INTENT_ACTION_CONNECTED);
@ -439,8 +444,8 @@ public class OPTelemetryService extends Service {
mon.addObserver(new Observer() { mon.addObserver(new Observer() {
@Override @Override
public void update(Observable arg0, Object arg1) { 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()*/) { if(mon.getConnected()) {
Intent intent = new Intent(); Intent intent = new Intent();
intent.setAction(INTENT_ACTION_CONNECTED); intent.setAction(INTENT_ACTION_CONNECTED);
sendBroadcast(intent,null); sendBroadcast(intent,null);

View File

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

View File

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

View File

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

View File

@ -31,16 +31,18 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.Observable;
import junit.framework.Assert;
import android.util.Log; import android.util.Log;
public class UAVTalk extends Observable { public class UAVTalk {
static final String TAG = "UAVTalk"; static final String TAG = "UAVTalk";
public static int LOGLEVEL = 0; public static int LOGLEVEL = 0;
public static boolean WARN = LOGLEVEL > 1; public static boolean VERBOSE = LOGLEVEL > 3;
public static boolean DEBUG = LOGLEVEL > 0; public static boolean WARN = LOGLEVEL > 2;
public static boolean DEBUG = LOGLEVEL > 1;
public static boolean ERROR = LOGLEVEL > 0;
private Thread inputProcessingThread = null; 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 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_VER = 0x20;
static final int TYPE_OBJ = (TYPE_VER | 0x00); static final int TYPE_OBJ = (TYPE_VER | 0x00);
static final int TYPE_OBJ_REQ = (TYPE_VER | 0x01); static final int TYPE_OBJ_REQ = (TYPE_VER | 0x01);
static final int TYPE_OBJ_ACK = (TYPE_VER | 0x02); static final int TYPE_OBJ_ACK = (TYPE_VER | 0x02);
static final int TYPE_ACK = (TYPE_VER | 0x03); 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), static final int MIN_HEADER_LENGTH = 8; // sync(1), type (1), size(2),
// object ID(4) // object ID(4)
@ -271,6 +274,8 @@ public class UAVTalk extends Observable {
// Send object depending on if a response is needed // Send object depending on if a response is needed
if (type == TYPE_OBJ_ACK || type == TYPE_OBJ_REQ) { if (type == TYPE_OBJ_ACK || type == TYPE_OBJ_REQ) {
if (transmitObject(obj, type, allInstances)) { if (transmitObject(obj, type, allInstances)) {
if(type == TYPE_OBJ_REQ)
if (ERROR) Log.e(TAG, "Sending obj req");
respObj = obj; respObj = obj;
respAllInstances = allInstances; respAllInstances = allInstances;
return true; return true;
@ -290,7 +295,7 @@ public class UAVTalk extends Observable {
* @throws IOException * @throws IOException
*/ */
public synchronized boolean processInputByte(int rxbyte) throws IOException { public synchronized boolean processInputByte(int rxbyte) throws IOException {
assert (objMngr != null); Assert.assertNotNull(objMngr);
// Update stats // Update stats
stats.rxBytes++; stats.rxBytes++;
@ -318,12 +323,13 @@ public class UAVTalk extends Observable {
rxCS = updateCRC(rxCS, rxbyte); rxCS = updateCRC(rxCS, rxbyte);
if ((rxbyte & TYPE_MASK) != TYPE_VER) { if ((rxbyte & TYPE_MASK) != TYPE_VER) {
Log.e(TAG, "Unknown UAVTalk type:" + rxbyte);
rxState = RxStateType.STATE_SYNC; rxState = RxStateType.STATE_SYNC;
break; break;
} }
rxType = rxbyte; rxType = rxbyte;
if (VERBOSE) Log.v(TAG, "Received packet type: " + rxType);
packetSize = 0; packetSize = 0;
rxState = RxStateType.STATE_SIZE; rxState = RxStateType.STATE_SIZE;
@ -380,7 +386,7 @@ public class UAVTalk extends Observable {
} }
// Determine data length // Determine data length
if (rxType == TYPE_OBJ_REQ || rxType == TYPE_ACK) if (rxType == TYPE_OBJ_REQ || rxType == TYPE_ACK || rxType == TYPE_NACK)
rxLength = 0; rxLength = 0;
else else
rxLength = rxObj.getNumBytes(); rxLength = rxObj.getNumBytes();
@ -565,6 +571,24 @@ public class UAVTalk extends Observable {
error = true; error = true;
} }
break; 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: case TYPE_ACK:
// All instances, not allowed for ACK messages // All instances, not allowed for ACK messages
if (!allInstances) { 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. * Check if a transaction is pending and if yes complete it.
*/ */
synchronized void updateAck(UAVObject obj) { synchronized void updateAck(UAVObject obj) {
if (DEBUG) Log.d(TAG, "ACK received: " + obj.getName());
Assert.assertNotNull(obj);
if (respObj != null && respObj.getObjID() == obj.getObjID() if (respObj != null && respObj.getObjID() == obj.getObjID()
&& (respObj.getInstID() == obj.getInstID() || respAllInstances)) { && (respObj.getInstID() == obj.getInstID() || respAllInstances)) {
if (transactionListener != null)
transactionListener.TransactionSucceeded(obj);
respObj = null; respObj = null;
setChanged();
notifyObservers(obj);
} }
} }
@ -784,4 +826,14 @@ public class UAVTalk extends Observable {
return crc; 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;
}
} }