Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
456 lines (394 sloc) 9.47 KB
package com.litlebot.rioapi.proto2015;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
/**
*
* The ComManager class manages the overall communication between the DS
* and RIO device. Make an instance with the constructor then use connect()
* and disconnect() to initialize and terminate communications.
*
* @author raystubbs
*
*/
public class ComManager
{
private static final int RIO_CONTROL_PORT = 1110, DS_STATUS_PORT = 1150;
private volatile boolean connected;
private volatile int packetIndex;
private volatile byte[] recvBuffer;
private volatile ArrayList<Joystick> joysticks;
private volatile InetAddress rioAddr;
private volatile ControlPacket cntrlPkt;
private volatile StatusPacket statPkt;
private volatile DatagramSocket sendSok;
private volatile DatagramSocket recvSok;
private volatile StateController statCtrl;
private volatile ConnectionCallback conCallback;
private volatile ComCallback comCallback;
/**
* Initializes an instance of ComManager to communicate with
* a RIO at the address rioAddr, and with statCtrl as the state
* controller, which can be null to use the default which sets the
* rio to a disabled state.
*
* @param rioAddr
* Address of the RIO device.
*
* @param statCtrl
* An instance of the StateController interface to tell the manager the
* mode and position of the RIO.
*/
public ComManager(InetAddress rioAddr, StateController statCtrl)
{
this.rioAddr = rioAddr;
this.statCtrl = statCtrl;
this.connected = false;
this.packetIndex = 0;
this.recvBuffer = new byte[48];
this.joysticks = new ArrayList<Joystick>();
}
//------------------Communications handlers-----------------------//
Thread senderThread = new Thread()
{
public void run()
{
while(connected)
{
try
{
DatagramPacket sendPkt = cntrlPkt.toDataPacket();
sendSok.send(sendPkt);
onPacketSent(cntrlPkt, sendPkt);
} catch (IOException e)
{
e.printStackTrace();
}
try
{
Thread.sleep(20);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
};
Thread receiverThread = new Thread()
{
public void run()
{
try
{
recvSok.setSoTimeout(40);
} catch (SocketException e)
{
e.printStackTrace();
}
DatagramPacket recvPkt = new DatagramPacket(recvBuffer, 48);
int failCount = 0;
while(connected && failCount < 5)
{
try
{
recvSok.receive(recvPkt);
failCount = 0;
statPkt.invalidateData(recvPkt);
onPacketReceived(statPkt, recvPkt);
} catch(InterruptedIOException e)
{
failCount++;
}catch (IOException e)
{
e.printStackTrace();
}
try
{
Thread.sleep(20);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
if(failCount >= 4)
{
connected = false;
onConnectionLost();
}
}
};
/**
* Attempts to initialize communication with the RIO device, after a call
* to this method the ComManager will send and receive UDP packets every 20 ms.
* Any joysticks should be added to the ComManager before communications are
* initialized.
*
* @return
* true if the connection attempt was successful false otherwise.
*/
public boolean connect()
{
onConnectionAttempt();
/*Separate thread for connection because networking is
* not allowed on main thread in Android*/
Thread connectionThread = new Thread()
{
public void run()
{
try
{
sendSok = new DatagramSocket();
recvSok = new DatagramSocket(DS_STATUS_PORT);
} catch (SocketException e)
{
System.err.println("Could not create DatagramSocket");
e.printStackTrace();
onConnectionFailed();
return;
}
cntrlPkt = new ConnectionPacket(0, (statCtrl == null)?defaultStatCtrl:statCtrl, rioAddr, joysticks);
statPkt = new StatusPacket();
try
{
recvSok.setSoTimeout(20);
} catch (SocketException e)
{
e.printStackTrace();
}
for(int a = 5 ; a >= 0 ; a--)
{
DatagramPacket recvPkt = new DatagramPacket(recvBuffer, 48);
try
{
sendSok.send(cntrlPkt.toDataPacket());
recvSok.receive(recvPkt);
sendSok.send(cntrlPkt.toDataPacket());
statPkt.invalidateData(recvPkt);
connected = true;
return;
} catch (IOException e)
{
e.printStackTrace();
}
}
}
};
connectionThread.start();
try
{
connectionThread.join();
} catch (InterruptedException e)
{
e.printStackTrace();
}
if(connected)
onConnected();
else
{
onConnectionFailed();
return false;
}
senderThread.start();
receiverThread.start();
return true;
}
/**
* Terminates communications with a RIO device, and waits for
* all threads in ComManager to finish before returning.
*/
public void disconnect()
{
if(connected)
{
connected = false;
onDisconnected();
try
{
senderThread.join();
receiverThread.join();
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
//--------------adders, isers, getters and setters----------------//
/**
* Is the ComManager communicating with the RIO?
*
* @return
* True if communication is occurring, otherwise false.
*/
public boolean isConnected()
{
return connected;
}
/**
* Returns the packet number of the current communications packet.
*
* @return
* Index of the current communications packets.
*/
public int getPacketIndex()
{
return packetIndex;
}
/**
* Adds joystick, giving this ComManager responsibility for
* communicating its data to the RIO.
*
* @param j
* The Joystick instance to add.
*/
public void addJoystick(Joystick j)
{
joysticks.add(j);
}
/**
* Sets the manager's ConnectionCallback.
*
* @param cc
* An instance of the ConnectionCallback interface.
*/
public void setConnectionCallback(ConnectionCallback cc)
{
conCallback = cc;
}
/**
* Sets the ComManager's ComCallback to handle packet sending
* and packet receipt events.
*
* @param cc
* An instance of the ComCallback interface.
*/
public void setComCallback(ComCallback cc)
{
comCallback = cc;
}
//------------------Event handlers---------------------------------//
private void onConnectionAttempt()
{
if(conCallback != null)
conCallback.onConnectionAttempt(this);
}
private void onConnected()
{
if(conCallback != null)
conCallback.onConnected(this);
}
private void onDisconnected()
{
if(conCallback != null)
conCallback.onDisconnected(this);
if(sendSok != null)
sendSok.close();
if(recvSok != null)
recvSok.close();
}
private void onConnectionLost()
{
if(conCallback != null)
conCallback.onConnectionLost(this);
onDisconnected();
}
private void onConnectionFailed()
{
if(conCallback != null)
conCallback.onConnectionFailed(this);
onDisconnected();
}
private void onPacketSent(ControlPacket cntrlPkt2, DatagramPacket dtPkt)
{
if(comCallback != null)
comCallback.onPacketSent(cntrlPkt2, dtPkt);
}
private void onPacketReceived(StatusPacket statPkt, DatagramPacket dtPkt)
{
if(comCallback != null)
comCallback.onPacketReceived(statPkt, dtPkt);
}
//-------------------Callback interfaces---------------------------//
/*Most of these are called from the networking threads, so be careful*/
/**
* A callback interface to handle connection events.
* @author raystubbs
*
*/
public interface ConnectionCallback
{
/**
* Called from the main thread when ComManager.connect() is called
* @param com
*/
void onConnectionAttempt(ComManager com);
/**
* Called from the connection thread when ComManager successfully establishes
* communication with the RIO.
* @param com
*/
void onConnected(ComManager com);
/**
* Called when the manager stops communication for any reason,
* could be caused by a call to ComManager.disconnect(), a connection
* failure, or an unexpected loss of communication for some other reason.
* @param com
*/
void onDisconnected(ComManager com);
/**
* Called when the ComManager looses communication for an unknown reason.
* @param com
*/
void onConnectionLost(ComManager com);
/**
* Called when the manager fails to establish communication.
* @param com
*/
void onConnectionFailed(ComManager com);
}
/**
* A callback interface to handle communication events.
* @author raystubbs
*
*/
public interface ComCallback
{
/**
* Called when the ComManager sends a packet to the RIO.
*
* @param cntrlPkt
* The packet sent in ControlPacket form
*
* @param dtPkt
* The packet sent in DatagramPacket form
*/
void onPacketSent(ControlPacket cntrlPkt, DatagramPacket dtPkt);
/**
* Called when the ComManager receives a packet from the RIO.
*
* @param statPkt
* The packet received in the form of a StatusPacket
*
* @param dtPkt
* The packet received in the form of a DatagramPacket
*/
void onPacketReceived(StatusPacket statPkt, DatagramPacket dtPkt);
}
//------------------Default StatusController----------------------------//
private StateController defaultStatCtrl = new StateController()
{
@Override
public byte getMode()
{
return 0;
}
@Override
public byte getPosition()
{
return 0;
}
};
}