Initial commit

This commit is contained in:
2013-11-05 11:44:26 -04:30
commit 9635163950
36 changed files with 1445 additions and 0 deletions

View File

@@ -0,0 +1,129 @@
package ve.ucv.ciens.ccg.nxtcam;
import ve.ucv.ciens.ccg.nxtcam.camera.CameraPreview;
import ve.ucv.ciens.ccg.nxtcam.network.ImageTransferThread;
import ve.ucv.ciens.ccg.nxtcam.utils.Logger;
import ve.ucv.ciens.ccg.nxtcam.utils.ProjectConstants;
import android.app.Activity;
import android.hardware.Camera;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.NavUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.WindowManager;
import android.widget.Toast;
public class CamActivity extends Activity{
private final String TAG = "NXTCAM_CAM";
private final String CLASS_NAME = MainActivity.class.getSimpleName();
private Camera hwCamera;
private CameraPreview cPreview;
private CameraSetupTask camSetupTask;
private ImageTransferThread imThread;
/*******************
* Android methods *
*******************/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
cPreview = new CameraPreview(this, hwCamera);
setContentView(cPreview);
imThread = new ImageTransferThread();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.cam, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// This ID represents the Home or Up button. In the case of this
// activity, the Up button is shown. Use NavUtils to allow users
// to navigate up one level in the application structure. For
// more details, see the Navigation pattern on Android Design:
//
// http://developer.android.com/design/patterns/navigation.html#up-vs-back
//
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onResume(){
super.onResume();
camSetupTask = new CameraSetupTask();
camSetupTask.execute();
}
@Override
public void onPause(){
super.onPause();
cPreview.removePreviewCallback();
cPreview.setCamera(null);
releaseCamera();
}
/******************
* My own methods *
******************/
public void startCameraPreview(){
if(hwCamera != null){
cPreview.setCamera(hwCamera);
}else{
Log.wtf(TAG, CLASS_NAME + ".startCameraPreview() :: CAMERA IS NULL!");
System.exit(1);
}
}
private void releaseCamera(){
if(hwCamera != null){
hwCamera.release();
hwCamera = null;
}
}
private class CameraSetupTask extends AsyncTask<Void, Void, Camera>{
private final String CLASS_NAME = CameraSetupTask.class.getSimpleName();
@Override
protected Camera doInBackground(Void... params) {
Camera cam = null;
try{
cam = Camera.open(0);
}catch(Exception e){
if(ProjectConstants.DEBUG) Log.e(TAG, CLASS_NAME + ".doInBackground() :: Failed to open the camera.");
}
return cam;
}
@Override
protected void onPostExecute(Camera result) {
hwCamera = result;
if(result != null){
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".onPostExecute() :: Camera successfully opened");
}else{
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".onPostExecute() :: Camera open failed on background task.");
Toast.makeText(getApplicationContext(), R.string.camera_failure, Toast.LENGTH_LONG).show();
}
startCameraPreview();
super.onPostExecute(result);
}
};
}

View File

@@ -0,0 +1,131 @@
package ve.ucv.ciens.ccg.nxtcam;
import java.net.InetAddress;
import java.net.UnknownHostException;
import ve.ucv.ciens.ccg.nxtcam.utils.Logger;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
private final String TAG = "NXTCAM_MAIN";
private final String CLASS_NAME = MainActivity.class.getSimpleName();
private Button startButton;
private TextView ipField;
private WifiManager wifiManager;
private boolean wifiOnByMe;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startButton = (Button)findViewById(R.id.startButton);
startButton.setOnClickListener(startClickListener);
ipField = (TextView)findViewById(R.id.ipAddressField);
wifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
if(!wifiManager.isWifiEnabled()) setWifi(true);
}
@Override
public void onResume(){
super.onResume();
if(!wifiManager.isWifiEnabled()) setWifi(true);
}
@Override
public void onPause(){
super.onPause();
if(wifiManager.isWifiEnabled() && wifiOnByMe) setWifi(false);
}
@Override
public void onDestroy(){
super.onDestroy();
if(wifiManager.isWifiEnabled() && wifiOnByMe) wifiManager.setWifiEnabled(false);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private void startCamActivity(boolean canStart){
if(canStart){
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".startCamActivity() :: Launching camera activity.");
Intent intent = new Intent(this, CamActivity.class);
startActivity(intent);
}else{
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".startCamActivity() :: Cannot launch camera activity.");
Toast.makeText(this, R.string.badIpToast, Toast.LENGTH_SHORT).show();
}
}
private void setWifi(boolean on){
wifiManager.setWifiEnabled(on);
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".ImageTransferThread() :: setting wifi to " + (on ? "on" : "off"));
if(on)
wifiOnByMe = true;
else
wifiOnByMe = false;
}
private void validateIpAddress(){
if(ipField.getText().toString().compareTo("") != 0){
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + "validateIpAddress() :: Launching verification task.");
VerifyIpAddressTask verifyIp = new VerifyIpAddressTask();
verifyIp.execute(ipField.getText().toString());
}else{
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + "validateIpAddress() :: Ip address field is empty.");
Toast.makeText(this, R.string.emptyIpToast, Toast.LENGTH_SHORT).show();
}
}
private final View.OnClickListener startClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
validateIpAddress();
}
};
private class VerifyIpAddressTask extends AsyncTask<String, Void, Boolean>{
private final String CLASS_NAME = VerifyIpAddressTask.class.getSimpleName();
@Override
protected Boolean doInBackground(String... params) {
try{
InetAddress.getByName(params[0]);
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + "doInBackground() :: IP address is valid.");
return true;
}catch(UnknownHostException uh){
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + "doInBackground() :: IP address is not valid.");
return false;
}
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
startCamActivity(result);
}
};
}

View File

@@ -0,0 +1,54 @@
package ve.ucv.ciens.ccg.nxtcam.camera;
import ve.ucv.ciens.ccg.nxtcam.utils.Logger;
public class CameraImageMonitor{
private final String TAG = "CAM_MONITOR";
private final String CLASS_NAME = CameraImageMonitor.class.getSimpleName();
private byte[] image;
private boolean imgChanged;
private CameraImageMonitor(){
imgChanged = false;
}
private static class SingletonHolder{
public static final CameraImageMonitor INSTANCE = new CameraImageMonitor();
}
public static CameraImageMonitor getInstance(){
return SingletonHolder.INSTANCE;
}
public void setImageData(byte[] image){
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".setImageData() :: Copying new image.");
synchronized(image){
this.image = new byte[image.length];
System.arraycopy(image, 0, this.image, 0, image.length);
imgChanged = true;
image.notifyAll();
}
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".setImageData() :: Data copy finished.");
}
public byte[] getImageData(){
byte[] returnImg;
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".getImageData() :: Entry point.");
synchronized(image){
while(!imgChanged){
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".getImageData() :: Waiting for new data.");
try{ image.wait(); }catch(InterruptedException ie){}
}
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".getImageData() :: Retrieving new data.");
returnImg = image.clone();
imgChanged = false;
}
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".getImageData() :: New data retreived.");
return returnImg;
}
public synchronized boolean hasChanged(){
return imgChanged;
}
}

View File

@@ -0,0 +1,205 @@
package ve.ucv.ciens.ccg.nxtcam.camera;
import java.io.IOException;
import java.util.List;
import ve.ucv.ciens.ccg.nxtcam.utils.Logger;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.os.Build;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
/** A basic Camera preview class */
@SuppressLint("ViewConstructor")
public class CameraPreview extends ViewGroup implements SurfaceHolder.Callback, Camera.PreviewCallback {
private final String TAG = "SURFVIEW";
private final String CLASS_NAME = CameraPreview.class.getSimpleName();
private Size mPreviewSize;
private List<Size> mSupportedPreviewSizes;
private CameraImageMonitor camMonitor;
private Activity parentActivity;
private SurfaceView mSurfaceView;
private SurfaceHolder mHolder;
private Camera mCamera;
@SuppressWarnings("deprecation")
public CameraPreview(Context context, Camera camera){
super(context);
parentActivity = (Activity)context;
mSurfaceView = new SurfaceView(context);
mHolder = mSurfaceView.getHolder();
mHolder.addCallback(this);
if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB)
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void setCamera(Camera camera){
mCamera = camera;
if(mCamera != null){
camMonitor = CameraImageMonitor.getInstance();
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
requestLayout();
}
}
public void surfaceCreated(SurfaceHolder holder){
// The Surface has been created, now tell the camera where to draw the preview.
try {
if(mCamera != null){
mCamera.setPreviewDisplay(holder);
}
} catch (IOException e) {
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, "Error setting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder){
if(mCamera != null){
mCamera.stopPreview();
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h){
if(mHolder.getSurface() == null){
return;
}
try{
mCamera.stopPreview();
}catch (Exception e){ }
requestLayout();
Camera.Parameters camParams = mCamera.getParameters();
/*Size optimal = getOptimalPreviewSize(camParams.getSupportedPreviewSizes(), w, h);
if(ProjectConstants.DEBUG)
Log.d(TAG, CLASS_NAME + ".surfaceChanged() :: Preview size set at (" + optimal.width + ", " + optimal.height + ")");*/
camParams.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
mCamera.setParameters(camParams);
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(0, info);
int rotation = parentActivity.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0: degrees = 0; break;
case Surface.ROTATION_90: degrees = 90; break;
case Surface.ROTATION_180: degrees = 180; break;
case Surface.ROTATION_270: degrees = 270; break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
mCamera.setDisplayOrientation(result);
mCamera.setPreviewCallback(this);
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
}catch (Exception e){
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".surfaceChanged() :: Error starting camera preview: " + e.getMessage());
}
}
@Override
public void onPreviewFrame(byte[] data, Camera camera){
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".onPreviewFrame() :: Preview received");
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".onPreviewFrame() :: Frame has" + (camMonitor.hasChanged() ? "" : " not") + " changed.");
if(!camMonitor.hasChanged())
camMonitor.setImageData(data);
}
public void removePreviewCallback(){
mCamera.setPreviewCallback(null);
}
private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) w / h;
if (sizes == null) return null;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed && getChildCount() > 0) {
final View child = getChildAt(0);
final int width = r - l;
final int height = b - t;
int previewWidth = width;
int previewHeight = height;
if (mPreviewSize != null) {
previewWidth = mPreviewSize.width;
previewHeight = mPreviewSize.height;
}
// Center the child SurfaceView within the parent.
if (width * previewHeight > height * previewWidth) {
final int scaledChildWidth = previewWidth * height / previewHeight;
child.layout((width - scaledChildWidth) / 2, 0,
(width + scaledChildWidth) / 2, height);
} else {
final int scaledChildHeight = previewHeight * width / previewWidth;
child.layout(0, (height - scaledChildHeight) / 2,
width, (height + scaledChildHeight) / 2);
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width, height);
if (mSupportedPreviewSizes != null) {
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
}
}
}

View File

@@ -0,0 +1,191 @@
package ve.ucv.ciens.ccg.nxtcam.network;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Set;
import java.util.UUID;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.util.Log;
public class BluetoothManager{
private static final UUID SERIAL_PORT_SERVICE_CLASS_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static final String OUI_LEGO = "00:16:53";
private static final String TAG = "BTMNGR";
private boolean connected;
private BluetoothAdapter bt_adapter;
private BluetoothSocket bt_socket = null;
private OutputStream nxt_out_stream = null;
private InputStream nxt_in_stream = null;
private static class SingletonHolder{
public static final BluetoothManager INSTANCE = new BluetoothManager();
}
private BluetoothManager(){
connected = false;
bt_adapter = BluetoothAdapter.getDefaultAdapter();
bt_socket = null;
nxt_in_stream = null;
nxt_out_stream = null;
}
public static BluetoothManager getInstance(){
return SingletonHolder.INSTANCE;
}
public boolean isBTSupported(){
return bt_adapter != null;
}
public boolean isConnected(){
return connected;
}
public boolean isBTEnabled(){
return bt_adapter.isEnabled();
}
public void disableBT(){
bt_adapter.disable();
}
public Set<BluetoothDevice> getPairedDevices(){
return bt_adapter.getBondedDevices();
}
/**
* Sets up a connection with a NXT device.
*
* Verifies if the target device is a valid NXT robot by checking agains Lego's OUI.
* Also creates the socket and the streams associated with the connection
*
* @param mac_address The mac address of the target device.
* @return true if the connection was established succesfully, otherwise false.
* @throws IOException
*/
public boolean establishConnection(String mac_address) throws IOException{
if (!bt_adapter.isEnabled()){
return false;
}
if(connected){
return false;
}
if(bt_adapter.isEnabled()){
if(mac_address == "NONE"){
return false;
}else{
if(mac_address.substring(0, 8).compareTo(OUI_LEGO) != 0){
Log.d(TAG, "establishConnection() :: Not a Lego MAC. Prefix : " + mac_address.substring(0, 8) + " :: OUI : " + OUI_LEGO);
return false;
}else{
try{
Log.d(TAG, "establishConnection() :: Getting device with mac address: " + mac_address);
BluetoothDevice nxtDevice = null;
nxtDevice = bt_adapter.getRemoteDevice(mac_address);
if (nxtDevice == null) {
Log.e(TAG, "establishConnection() :: No device found.");
throw new IOException();
}
Log.d(TAG, "establishConnection() :: Opening socket.");
bt_socket = nxtDevice.createRfcommSocketToServiceRecord(SERIAL_PORT_SERVICE_CLASS_UUID);
Log.d(TAG, "establishConnection() :: Connecting.");
bt_socket.connect();
Log.d(TAG, "establishConnection() :: Opening IO streams.");
nxt_in_stream = bt_socket.getInputStream();
nxt_out_stream = bt_socket.getOutputStream();
Log.d(TAG, "establishConnection() :: Connection established.");
connected = true;
}catch(IOException e){
Log.e(TAG, "establishConnection() :: Connection failed.");
Log.e(TAG, Log.getStackTraceString(e));
connected = false;
throw e;
}
return connected;
}
}
}
return false;
}
/**
* Closes the active connection if any.
*
* Additionally clears the socket and the streams associated to said connection.
*
* @return true if the connection was succesfully closed; false if no connection exists.
* @throws IOException
*/
public boolean stopConnection() throws IOException{
try{
if(bt_socket != null){
Log.d(TAG, "stopConnection() :: Closing connection.");
bt_socket.close();
bt_socket = null;
nxt_in_stream = null;
nxt_out_stream = null;
connected = false;
Log.d(TAG, "stopConnection() :: Connection closed.");
return true;
}
}catch( IOException e){
Log.e(TAG, "stopConnection()");
Log.e(TAG, Log.getStackTraceString(e));
throw e;
}
return false;
}
/**
* Sends a message to the NXT robot.
*
* @param message The data to be sent.
* @throws IOException
*/
public synchronized void writeMessage(byte[] message) throws IOException{
if(connected){
try{
nxt_out_stream.write(message);
nxt_out_stream.flush();
}catch(IOException e){
Log.e(TAG, "writeMessage()");
Log.e(TAG, Log.getStackTraceString(e));
throw e;
}
}
}
/**
* Reads a message sent by the NXT robot.
*
* @return The data received as a byte[] if a valid connection exists, otherwise null.
* @throws IOException
*/
public synchronized byte[] readMessage(int bytes) throws IOException{
if(connected){
try{
byte[] message = new byte[bytes];
for(int i = 0; i < message.length; ++i){
message[i] = 0x00;
}
nxt_in_stream.read(message, 0, bytes);
return message;
}catch(IOException e){
Log.e(TAG, "readMessage()");
Log.e(TAG, Log.getStackTraceString(e));
throw e;
}
}else{
return null;
}
}
}

View File

@@ -0,0 +1,95 @@
package ve.ucv.ciens.ccg.nxtcam.network;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import ve.ucv.ciens.ccg.nxtcam.camera.CameraImageMonitor;
import ve.ucv.ciens.ccg.nxtcam.utils.ProjectConstants;
import android.util.Log;
public class ImageTransferThread extends Thread{
private final String TAG = "IM_THREAD";
private final String CLASS_NAME = ImageTransferThread.class.getSimpleName();
private boolean pause, done, connected;
private Object threadPauseMonitor;
private CameraImageMonitor camMonitor;
private Socket socket;
private BufferedWriter writer;
private BufferedReader reader;
private byte[] image;
public ImageTransferThread(){
pause = false;
done = false;
connected = false;
threadPauseMonitor = new Object();
socket = null;
writer = null;
reader = null;
camMonitor = CameraImageMonitor.getInstance();
}
public void run(){
if(!connected){
Log.e(TAG, CLASS_NAME + ".run() :: Not connected to a server. Finishing thread.");
}else{
while(!done){
checkPause();
image = camMonitor.getImageData();
// TODO: implement image transfer protocol.
}
}
}
public void connectToServer(String serverIp){
try{
if(ProjectConstants.DEBUG) Log.i(TAG, CLASS_NAME + ".connectToServer() :: Connecting to the server at " + serverIp);
socket = new Socket(InetAddress.getByName(serverIp), ProjectConstants.SERVER_TCP_PORT_1);
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
connected = true;
if(ProjectConstants.DEBUG) Log.i(TAG, CLASS_NAME + ".connectToServer() :: Connection successful.");
}catch(IOException io){
Log.e(TAG, CLASS_NAME + ".connectToServer() :: Connection failed with message: " + io.getMessage());
connected = false;
}
}
public synchronized void finish(){
done = true;
if(ProjectConstants.DEBUG) Log.i(TAG, CLASS_NAME + ".finish() :: Finishing thread.");
}
private void checkPause(){
synchronized (threadPauseMonitor){
while(pause){
if(ProjectConstants.DEBUG) Log.d(TAG, CLASS_NAME + ".checkPause() :: Pause requested.");
try{ threadPauseMonitor.wait(); }catch(InterruptedException ie){}
}
}
}
public synchronized void pauseThread(){
pause = true;
if(ProjectConstants.DEBUG) Log.d(TAG, CLASS_NAME + ".pauseThread() :: Pausing thread.");
}
public synchronized void resumeThread(){
if(ProjectConstants.DEBUG) Log.d(TAG, CLASS_NAME + ".resumeThread() :: Resuming thread.");
synchronized (threadPauseMonitor) {
pause = false;
threadPauseMonitor.notifyAll();
}
}
public boolean isConnected(){
return connected;
}
}

View File

@@ -0,0 +1,12 @@
package ve.ucv.ciens.ccg.nxtcam.network;
public class LCPThread extends Thread{
public LCPThread(){
}
public void run(){
}
}

View File

@@ -0,0 +1,13 @@
package ve.ucv.ciens.ccg.nxtcam.network;
public class ServiceDiscoveryThread extends Thread {
public ServiceDiscoveryThread(){
}
@Override
public void run(){
}
}

View File

@@ -0,0 +1,17 @@
package ve.ucv.ciens.ccg.nxtcam.network.protocols;
public abstract class ImageTransferProtocol{
public static enum ProtocolState{
SALUTE, IMG_FOLLOWS, SEND_DATA, PAUSED, WAITING, GOODBYE
}
public static final byte MSG_HELLO = (byte)0x89;
public static final byte MSG_GOODBYE = (byte)0x90;
public static final byte MSG_IMG_DATA = (byte)0x42;
public static final byte CMD_IMG_FOLLOWS = (byte)0x10;
public static final byte CMD_PAUSE = (byte)0x15;
public static final byte CMD_IMG_WAIT = (byte)0x20;
public static final byte ACK_SEND_IMG = (byte)0x40;
public static final byte ACK_IMG_RCVD = (byte)0x50;
}

View File

@@ -0,0 +1,248 @@
package ve.ucv.ciens.ccg.nxtcam.network.protocols;
import java.security.InvalidParameterException;
public abstract class LegoCommunicationProtocol{
/**
* Command types. Byte 0;
*/
private static final byte DIRECT_COMMAND_REPLY = 0x00;
private static final byte SYSTEM_COMMAND_REPLY = 0x01;
private static final byte DIRECT_COMMAND_NO_REPLY = (byte)0x80;
/**
* Comand bytes. Byte 1;
*/
private static final byte GET_FIRMWARE_VERSION = (byte)0x88;
private static final byte GET_DEVICE_INFO = (byte)0x9B;
private static final byte SET_OUTPUT_STATE = 0x04;
private static final byte SET_INPUT_MODE = 0x05;
private static final byte GET_OUTPUT_STATE = 0x06;
private static final byte GET_INPUT_VALUES = 0x07;
/**
* Ports for get/setOutputState() and get/setInputMode().
*/
public static final byte PORT_0 = 0x00;
public static final byte PORT_1 = 0x01;
public static final byte PORT_2 = 0x02;
public static final byte PORT_3 = 0x03;
/**
* Mode bytes for setOutputState().
*/
public static final byte MOTORON = 0x01;
public static final byte BRAKE = 0x02;
public static final byte REGULATED = 0x04;
/**
* Regulation modes for setOutputState().
*/
public static final byte REGULATION_MODE_IDLE = 0x00;
public static final byte REGULATION_MODE_MOTOR_SPEED = 0x01;
public static final byte REGULATION_MODE_MOTOR_SYNC = 0x02;
/**
* Run states for setOutputState().
*/
public static final byte MOTOR_RUN_STATE_IDLE = 0x00;
public static final byte MOTOR_RUN_STATE_RAMPUP = 0x10;
public static final byte MOTOR_RUN_STATE_RUNNING = 0x20;
public static final byte MOTOR_RUN_STATE_RAMPDOWN = 0x40;
/**
* Sensor types for setInputMode().
*/
public static final byte NO_SENSOR = 0x00;
public static final byte SWITCH = 0x01;
public static final byte TEMPERATURE = 0x02;
public static final byte REFLECTION = 0x03;
public static final byte ANGLE = 0x04;
public static final byte LIGHT_ACTIVE = 0x05;
public static final byte LIGHT_INACTIVE = 0x06;
public static final byte SOUND_DB = 0x07;
public static final byte SOUND_DBA = 0x08;
public static final byte CUSTOM = 0x09;
public static final byte LOWSPEED = 0x0A;
public static final byte LOWSPEED_9V = 0x0B;
public static final byte NO_OF_SENSOR_TYPES = 0x0C;
/**
* Sensor modes for setInputMode().
*/
public static final byte RAWMODE = 0x00;
public static final byte BOOLEANMODE = 0x20;
public static final byte TRANSITIONCNTMODE = 0x40;
public static final byte PERIODCOUNTERMODE = 0x60;
public static final byte PCTFULLSCALEMODE = (byte)0x80;
public static final byte CELSIUSMODE = (byte)0xA0;
public static final byte FARENHEITMODE = (byte)0xC0;
public static final byte ANGLESTEPMODE = (byte)0xE0;
public static final byte SLOPEMASK = (byte)0x1F;
public static final byte MODEMASK = (byte)0xE0;
/**
* Firmware and protocol version request pdu. Page 11 of appendix 1.
*
* @return byte[4], the pdu.
*/
public static byte[] getFirmwareVersion(){
byte[] message = new byte[4];
message[0] = 0x02;
message[1] = 0x00;
message[2] = SYSTEM_COMMAND_REPLY;
message[3] = GET_FIRMWARE_VERSION;
return message;
}
/**
* Device info request pdu. Page 14 of appendix 1.
*
* @return byte[4], the pdu.
*/
public static byte[] getDeviceInfo(){
byte[] message = new byte[4];
message[0] = 0x02;
message[1] = 0x00;
message[2] = SYSTEM_COMMAND_REPLY;
message[3] = GET_DEVICE_INFO;
return message;
}
/**
* Set motor configuration pdu. Page 6 of appendix 2.
*
* @param output_port The port in the brick the motor is connected to.
* @param power_set_point
* @param mode_byte
* @param regulation_mode
* @param turn_ratio
* @param run_state
* @return byte[15], the pdu.
* @throws InvalidParameterException When any parameter is out of range or is an invalid enum. Ranges defined in appendix 2.
*/
public static byte[] setOutputState(byte output_port, byte power_set_point, byte mode_byte, byte regulation_mode, byte turn_ratio, byte run_state) throws InvalidParameterException{
byte[] message = new byte[15];
if(output_port < PORT_0 || output_port > PORT_2){
throw new InvalidParameterException("Output port out of range.");
}
if(power_set_point < -100 || power_set_point > 100){
throw new InvalidParameterException("Power set point out of range.");
}
if(turn_ratio < -100 || turn_ratio > 100){
throw new InvalidParameterException("Turn ratio out of range.");
}
if(mode_byte != MOTORON && mode_byte != BRAKE && mode_byte != REGULATED){
throw new InvalidParameterException("Invalid mode byte.");
}
if(regulation_mode != REGULATION_MODE_IDLE && regulation_mode != REGULATION_MODE_MOTOR_SPEED && regulation_mode != REGULATION_MODE_MOTOR_SYNC){
throw new InvalidParameterException("Invalid regulation mode.");
}
if(run_state != MOTOR_RUN_STATE_IDLE && run_state != MOTOR_RUN_STATE_RAMPUP && run_state != MOTOR_RUN_STATE_RUNNING && run_state != MOTOR_RUN_STATE_RAMPDOWN){
throw new InvalidParameterException("Invalid run state.");
}
message[0] = 0x0C;
message[1] = 0x00;
message[2] = DIRECT_COMMAND_NO_REPLY;
message[3] = SET_OUTPUT_STATE;
message[4] = output_port;
message[5] = power_set_point;
message[6] = mode_byte;
message[7] = regulation_mode;
message[8] = turn_ratio;
message[9] = run_state;
message[10] = message[11] = message[12] = message[13] = message[14] = 0x00;
return message;
}
/**
* Motor configuration request pdu. Page 8 of appendix 2.
*
* @param output_port The port in the brick the motor is connected to.
* @return byte[5], the pdu.
* @throws InvalidParameterException When any parameter is out of range or is an invalid enum. Ranges defined in appendix 2.
*/
public static byte[] getOutputState(byte output_port) throws InvalidParameterException{
byte[] message = new byte[5];
if(output_port < PORT_0 || output_port > PORT_2){
throw new InvalidParameterException("Output port out of range.");
}
message[0] = 0x03;
message[1] = 0x00;
message[2] = DIRECT_COMMAND_REPLY;
message[3] = GET_OUTPUT_STATE;
message[4] = output_port;
return message;
}
/**
* Sensor feed request pdu. Page 8 of appendix 2.
*
* @param input_port The port in the brick the sensor is connected to.
* @return byte[5], the pdu.
* @throws InvalidParameterException When any parameter is out of range or is an invalid enum. Ranges defined in appendix 2.
*/
public static byte[] getInputValues(byte input_port) throws InvalidParameterException{
byte[] message = new byte[5];
if(input_port < PORT_0 || input_port > PORT_3){
throw new InvalidParameterException("Input port is out of range.");
}
message[0] = 0x03;
message[1] = 0x00;
message[2] = DIRECT_COMMAND_REPLY;
message[3] = GET_INPUT_VALUES;
message[4] = input_port;
return message;
}
/**
* Sensor configuration pdu.
*
* @param input_port The port in the brick the sensor is connected to.
* @param sensor_type The sensor to be configured.
* @param sensor_mode The configuration to set.
* @return byte[7], the pdu.
* @throws InvalidParameterException When any parameter is out of range or is an invalid enum. Ranges defined in appendix 2.
*/
public static byte[] setInputMode(byte input_port, byte sensor_type, byte sensor_mode) throws InvalidParameterException{
byte[] message = new byte[7];
if(input_port < PORT_0 || input_port > PORT_3){
throw new InvalidParameterException("Input port is out of range.");
}
if(sensor_type < 0x00 || sensor_type > 0x0C){
throw new InvalidParameterException("Invalid sensor type.");
}
message[0] = 0x05;
message[1] = 0x00;
message[2] = DIRECT_COMMAND_NO_REPLY;
message[3] = SET_INPUT_MODE;
message[4] = input_port;
message[5] = sensor_type;
switch(sensor_mode){
case RAWMODE:
case BOOLEANMODE:
case TRANSITIONCNTMODE:
case PERIODCOUNTERMODE:
case PCTFULLSCALEMODE:
case CELSIUSMODE:
case FARENHEITMODE:
case ANGLESTEPMODE: // Same case as MODEMASK.
case SLOPEMASK:
message[6] = sensor_mode;
break;
}
return message;
}
}

View File

@@ -0,0 +1,32 @@
package ve.ucv.ciens.ccg.nxtcam.utils;
import android.util.Log;
public abstract class Logger {
public static enum LOG_TYPES{ DEBUG, INFO, WARN, ERROR, VERBOSE, WTF }
public static void log(LOG_TYPES log_type, String tag, String msg){
if(ProjectConstants.DEBUG){
switch(log_type){
case DEBUG:
Log.d(tag, msg);
break;
case INFO:
Log.i(tag, msg);
break;
case WARN:
Log.w(tag, msg);
break;
case ERROR:
Log.e(tag, msg);
break;
case VERBOSE:
Log.v(tag, msg);
break;
case WTF:
Log.wtf(tag, msg);
break;
}
}
}
}

View File

@@ -0,0 +1,9 @@
package ve.ucv.ciens.ccg.nxtcam.utils;
public abstract class ProjectConstants {
public static final int SERVER_UDP_PORT = 8889;
public static final int SERVER_TCP_PORT_1 = 9989;
public static final int SERVER_TCP_PORT_2 = 9990;
public static final boolean DEBUG = true;
}