From c1916a667e75bc5b5464c6546c22e96507c377ad Mon Sep 17 00:00:00 2001
From: Miguel Astor
Date: Tue, 26 Nov 2013 17:57:01 -0430
Subject: [PATCH 1/4] Service discovery works. TCP streams established.
---
src/ve/ucv/ciens/ccg/nxtar/Main.java | 50 +++++++++++++-
.../nxtar/interfaces/MulticastEnabler.java | 6 ++
.../interfaces/NetworkConnectionListener.java | 5 ++
.../ciens/ccg/nxtar/interfaces/Toaster.java | 6 ++
.../ccg/nxtar/network/RobotControlThread.java | 52 ++++++++++++++
.../nxtar/network/ServiceDiscoveryThread.java | 69 +++++++++++++++++++
.../nxtar/network/VideoStreamingThread.java | 52 ++++++++++++++
.../ccg/nxtar/utils/ProjectConstants.java | 10 +++
8 files changed, 249 insertions(+), 1 deletion(-)
create mode 100644 src/ve/ucv/ciens/ccg/nxtar/interfaces/MulticastEnabler.java
create mode 100644 src/ve/ucv/ciens/ccg/nxtar/interfaces/NetworkConnectionListener.java
create mode 100644 src/ve/ucv/ciens/ccg/nxtar/interfaces/Toaster.java
create mode 100644 src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java
create mode 100644 src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java
create mode 100644 src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java
create mode 100644 src/ve/ucv/ciens/ccg/nxtar/utils/ProjectConstants.java
diff --git a/src/ve/ucv/ciens/ccg/nxtar/Main.java b/src/ve/ucv/ciens/ccg/nxtar/Main.java
index 943d07b..2c615ef 100644
--- a/src/ve/ucv/ciens/ccg/nxtar/Main.java
+++ b/src/ve/ucv/ciens/ccg/nxtar/Main.java
@@ -1,5 +1,13 @@
package ve.ucv.ciens.ccg.nxtar;
+import ve.ucv.ciens.ccg.nxtar.interfaces.MulticastEnabler;
+import ve.ucv.ciens.ccg.nxtar.interfaces.NetworkConnectionListener;
+import ve.ucv.ciens.ccg.nxtar.interfaces.Toaster;
+import ve.ucv.ciens.ccg.nxtar.network.RobotControlThread;
+import ve.ucv.ciens.ccg.nxtar.network.ServiceDiscoveryThread;
+import ve.ucv.ciens.ccg.nxtar.network.VideoStreamingThread;
+
+import com.badlogic.gdx.Application;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
@@ -10,17 +18,36 @@ import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
-public class Main implements ApplicationListener {
+public class Main implements ApplicationListener, NetworkConnectionListener {
+ private static final String TAG = "NXTAR_CORE_MAIN";
+ private static final String CLASS_NAME = Main.class.getSimpleName();
+
private OrthographicCamera camera;
private SpriteBatch batch;
private Texture texture;
private Sprite sprite;
+ private Toaster toaster;
+ private MulticastEnabler mcastEnabler;
+ private int connections;
+
+ private ServiceDiscoveryThread udpThread;
+ private VideoStreamingThread videoThread;
+ private RobotControlThread robotThread;
+
+ public Main(Toaster toaster, MulticastEnabler mcastEnabler){
+ super();
+ this.toaster = toaster;
+ this.mcastEnabler = mcastEnabler;
+ connections = 0;
+ }
@Override
public void create() {
float w = Gdx.graphics.getWidth();
float h = Gdx.graphics.getHeight();
+ Gdx.app.setLogLevel(Application.LOG_DEBUG);
+
camera = new OrthographicCamera(1, h/w);
batch = new SpriteBatch();
@@ -33,6 +60,16 @@ public class Main implements ApplicationListener {
sprite.setSize(0.9f, 0.9f * sprite.getHeight() / sprite.getWidth());
sprite.setOrigin(sprite.getWidth()/2, sprite.getHeight()/2);
sprite.setPosition(-sprite.getWidth()/2, -sprite.getHeight()/2);
+
+ Gdx.app.debug(TAG, CLASS_NAME + ".create() :: Creating network threads");
+ mcastEnabler.enableMulticast();
+ udpThread = new ServiceDiscoveryThread();
+ videoThread = new VideoStreamingThread(toaster);
+ robotThread = new RobotControlThread(toaster);
+
+ udpThread.start();
+ videoThread.start();
+ robotThread.start();
}
@Override
@@ -63,4 +100,15 @@ public class Main implements ApplicationListener {
@Override
public void resume() {
}
+
+ @Override
+ public synchronized void interfaceConnected(String iface) {
+ if(iface.compareTo(VideoStreamingThread.THREAD_NAME) == 0 || iface.compareTo(RobotControlThread.THREAD_NAME) == 0)
+ connections += 1;
+ if(connections >= 2){
+ Gdx.app.debug(TAG, CLASS_NAME + ".interfaceConnected() :: Stopping service broadcast.");
+ udpThread.finish();
+ mcastEnabler.disableMulticast();
+ }
+ }
}
diff --git a/src/ve/ucv/ciens/ccg/nxtar/interfaces/MulticastEnabler.java b/src/ve/ucv/ciens/ccg/nxtar/interfaces/MulticastEnabler.java
new file mode 100644
index 0000000..5967c01
--- /dev/null
+++ b/src/ve/ucv/ciens/ccg/nxtar/interfaces/MulticastEnabler.java
@@ -0,0 +1,6 @@
+package ve.ucv.ciens.ccg.nxtar.interfaces;
+
+public interface MulticastEnabler {
+ public void enableMulticast();
+ public void disableMulticast();
+}
diff --git a/src/ve/ucv/ciens/ccg/nxtar/interfaces/NetworkConnectionListener.java b/src/ve/ucv/ciens/ccg/nxtar/interfaces/NetworkConnectionListener.java
new file mode 100644
index 0000000..a962cad
--- /dev/null
+++ b/src/ve/ucv/ciens/ccg/nxtar/interfaces/NetworkConnectionListener.java
@@ -0,0 +1,5 @@
+package ve.ucv.ciens.ccg.nxtar.interfaces;
+
+public interface NetworkConnectionListener {
+ public void interfaceConnected(String iface);
+}
diff --git a/src/ve/ucv/ciens/ccg/nxtar/interfaces/Toaster.java b/src/ve/ucv/ciens/ccg/nxtar/interfaces/Toaster.java
new file mode 100644
index 0000000..0999526
--- /dev/null
+++ b/src/ve/ucv/ciens/ccg/nxtar/interfaces/Toaster.java
@@ -0,0 +1,6 @@
+package ve.ucv.ciens.ccg.nxtar.interfaces;
+
+public interface Toaster {
+ public void showShortToast(String msg);
+ public void showLongToast(String msg);
+}
diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java b/src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java
new file mode 100644
index 0000000..2446d69
--- /dev/null
+++ b/src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java
@@ -0,0 +1,52 @@
+package ve.ucv.ciens.ccg.nxtar.network;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import ve.ucv.ciens.ccg.nxtar.interfaces.NetworkConnectionListener;
+import ve.ucv.ciens.ccg.nxtar.interfaces.Toaster;
+import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants;
+
+import com.badlogic.gdx.Gdx;
+
+public class RobotControlThread extends Thread {
+ public static final String THREAD_NAME = "RobotControlThread";
+ private static final String TAG = "NXTAR_CORE_ROBOTTHREAD";
+ private static final String CLASS_NAME = RobotControlThread.class.getSimpleName();
+
+ private NetworkConnectionListener netListener;
+ private ServerSocket server;
+ private Socket client;
+ private Toaster toaster;
+
+ public RobotControlThread(Toaster toaster){
+ super(THREAD_NAME);
+
+ this.toaster = toaster;
+ netListener = null;
+
+ try{
+ server = new ServerSocket(ProjectConstants.SERVER_TCP_PORT_2);
+ }catch(IOException io){
+ Gdx.app.error(TAG, CLASS_NAME + ".RobotControlThread() :: Error creating server: " + io.getMessage(), io);
+ }
+ }
+
+ public void addNetworkConnectionListener(NetworkConnectionListener listener){
+ netListener = listener;
+ }
+
+ @Override
+ public void run(){
+ try{
+ client = server.accept();
+ if(netListener != null)
+ netListener.interfaceConnected(THREAD_NAME);
+ toaster.showShortToast("Client connected to RobotControlThread");
+ client.close();
+ }catch(IOException io){
+ Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error accepting client: " + io.getMessage(), io);
+ }
+ }
+}
diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java b/src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java
new file mode 100644
index 0000000..598fa96
--- /dev/null
+++ b/src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java
@@ -0,0 +1,69 @@
+package ve.ucv.ciens.ccg.nxtar.network;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants;
+
+import com.badlogic.gdx.Gdx;
+
+public class ServiceDiscoveryThread extends Thread {
+ public static final String THREAD_NAME = "ServiceDiscoveryThread";
+ private static final String TAG = "NXTAR_CORE_UDPTHREAD";
+ private static final String CLASS_NAME = ServiceDiscoveryThread.class.getSimpleName();
+
+ private Object semaphore;
+ private boolean done;
+ private DatagramSocket udpServer;
+ private InetAddress group;
+
+ public ServiceDiscoveryThread(){
+ super(THREAD_NAME);
+
+ done = false;
+ semaphore = new Object();
+
+ try{
+ group = InetAddress.getByName(ProjectConstants.MULTICAST_ADDRESS);
+ }catch(UnknownHostException uh){ }
+
+ Gdx.app.debug(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Creating multicast server.");
+ try{
+ udpServer = new DatagramSocket(ProjectConstants.SERVER_UDP_PORT);
+ }catch(IOException io){
+ Gdx.app.error(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Error creating UDP socket: " + io.getMessage(), io);
+ }
+ Gdx.app.debug(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Multicast server created.");
+ }
+
+ @Override
+ public void run(){
+ byte[] buffer = (new String("NxtAR server here!")).getBytes();
+ try{
+ while(true){
+ synchronized(semaphore){
+ if(done){
+ udpServer.close();
+ break;
+ }
+ }
+ //Gdx.app.debug(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Resending packet");
+ DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group, ProjectConstants.SERVER_UDP_PORT);
+ udpServer.send(packet);
+ //Gdx.app.debug(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Packet sent");
+ try{ sleep(250L); }catch(InterruptedException ie){ }
+ }
+ }catch(IOException io){
+ Gdx.app.debug(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Error sending packet: " + io.getMessage(), io);
+ }
+ }
+
+ public void finish(){
+ synchronized(semaphore){
+ done = true;
+ }
+ }
+}
diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java b/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java
new file mode 100644
index 0000000..4e455a0
--- /dev/null
+++ b/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java
@@ -0,0 +1,52 @@
+package ve.ucv.ciens.ccg.nxtar.network;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import ve.ucv.ciens.ccg.nxtar.interfaces.NetworkConnectionListener;
+import ve.ucv.ciens.ccg.nxtar.interfaces.Toaster;
+import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants;
+
+import com.badlogic.gdx.Gdx;
+
+public class VideoStreamingThread extends Thread {
+ public static final String THREAD_NAME = "VideoStreamingThread";
+ private static final String TAG = "NXTAR_CORE_VIDEOTHREAD";
+ private static final String CLASS_NAME = VideoStreamingThread.class.getSimpleName();
+
+ private NetworkConnectionListener netListener;
+ private ServerSocket server;
+ private Socket client;
+ private Toaster toaster;
+
+ public VideoStreamingThread(Toaster toaster){
+ super(THREAD_NAME);
+
+ this.toaster = toaster;
+ netListener = null;
+
+ try{
+ server = new ServerSocket(ProjectConstants.SERVER_TCP_PORT_1);
+ }catch(IOException io){
+ Gdx.app.error(TAG, CLASS_NAME + ".VideoStreamingThread() :: Error creating server: " + io.getMessage(), io);
+ }
+ }
+
+ public void addNetworkConnectionListener(NetworkConnectionListener listener){
+ netListener = listener;
+ }
+
+ @Override
+ public void run(){
+ try{
+ client = server.accept();
+ if(netListener != null)
+ netListener.interfaceConnected(THREAD_NAME);
+ toaster.showShortToast("Client connected to VideoStreamingThread");
+ client.close();
+ }catch(IOException io){
+ Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error accepting client: " + io.getMessage(), io);
+ }
+ }
+}
diff --git a/src/ve/ucv/ciens/ccg/nxtar/utils/ProjectConstants.java b/src/ve/ucv/ciens/ccg/nxtar/utils/ProjectConstants.java
new file mode 100644
index 0000000..b1fc3f0
--- /dev/null
+++ b/src/ve/ucv/ciens/ccg/nxtar/utils/ProjectConstants.java
@@ -0,0 +1,10 @@
+package ve.ucv.ciens.ccg.nxtar.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 String MULTICAST_ADDRESS = "230.0.0.1";
+
+ public static final boolean DEBUG = true;
+}
From 44b58565ff993a38870cbbc1810257a755c3cc29 Mon Sep 17 00:00:00 2001
From: Miguel Astor
Date: Wed, 27 Nov 2013 08:10:48 -0430
Subject: [PATCH 2/4] Added error handling and comments to
ServiceDiscoveryThread. Assorted logging.
---
src/ve/ucv/ciens/ccg/nxtar/Main.java | 8 +-
.../interfaces/NetworkConnectionListener.java | 2 +-
.../ccg/nxtar/network/RobotControlThread.java | 2 +-
.../nxtar/network/ServiceDiscoveryThread.java | 86 ++++++++++++++++---
.../nxtar/network/VideoStreamingThread.java | 2 +-
5 files changed, 80 insertions(+), 20 deletions(-)
diff --git a/src/ve/ucv/ciens/ccg/nxtar/Main.java b/src/ve/ucv/ciens/ccg/nxtar/Main.java
index 2c615ef..eb9b427 100644
--- a/src/ve/ucv/ciens/ccg/nxtar/Main.java
+++ b/src/ve/ucv/ciens/ccg/nxtar/Main.java
@@ -21,7 +21,7 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion;
public class Main implements ApplicationListener, NetworkConnectionListener {
private static final String TAG = "NXTAR_CORE_MAIN";
private static final String CLASS_NAME = Main.class.getSimpleName();
-
+
private OrthographicCamera camera;
private SpriteBatch batch;
private Texture texture;
@@ -102,11 +102,11 @@ public class Main implements ApplicationListener, NetworkConnectionListener {
}
@Override
- public synchronized void interfaceConnected(String iface) {
- if(iface.compareTo(VideoStreamingThread.THREAD_NAME) == 0 || iface.compareTo(RobotControlThread.THREAD_NAME) == 0)
+ public synchronized void networkStreamConnected(String streamName) {
+ if(streamName.compareTo(VideoStreamingThread.THREAD_NAME) == 0 || streamName.compareTo(RobotControlThread.THREAD_NAME) == 0)
connections += 1;
if(connections >= 2){
- Gdx.app.debug(TAG, CLASS_NAME + ".interfaceConnected() :: Stopping service broadcast.");
+ Gdx.app.debug(TAG, CLASS_NAME + ".networkStreamConnected() :: Stopping service broadcast.");
udpThread.finish();
mcastEnabler.disableMulticast();
}
diff --git a/src/ve/ucv/ciens/ccg/nxtar/interfaces/NetworkConnectionListener.java b/src/ve/ucv/ciens/ccg/nxtar/interfaces/NetworkConnectionListener.java
index a962cad..ab3d546 100644
--- a/src/ve/ucv/ciens/ccg/nxtar/interfaces/NetworkConnectionListener.java
+++ b/src/ve/ucv/ciens/ccg/nxtar/interfaces/NetworkConnectionListener.java
@@ -1,5 +1,5 @@
package ve.ucv.ciens.ccg.nxtar.interfaces;
public interface NetworkConnectionListener {
- public void interfaceConnected(String iface);
+ public void networkStreamConnected(String streamName);
}
diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java b/src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java
index 2446d69..1dbf3bc 100644
--- a/src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java
+++ b/src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java
@@ -42,7 +42,7 @@ public class RobotControlThread extends Thread {
try{
client = server.accept();
if(netListener != null)
- netListener.interfaceConnected(THREAD_NAME);
+ netListener.networkStreamConnected(THREAD_NAME);
toaster.showShortToast("Client connected to RobotControlThread");
client.close();
}catch(IOException io){
diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java b/src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java
index 598fa96..7f78faf 100644
--- a/src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java
+++ b/src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java
@@ -10,10 +10,36 @@ import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants;
import com.badlogic.gdx.Gdx;
+/**
+ * Ad hoc service discovery server thread.
+ *
+ * This thread performs an ad hoc service discovery protocol. A multicast datagram packet is sent every
+ * 250 miliseconds carrying the string "NxtAR server is here!" on the multicast address defined
+ * in {@link ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants#MULTICAST_ADDRESS}. The port defined in
+ * {@link ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants#SERVER_UDP_PORT} is used for the transmissions. The server stops
+ * when another thread calls the {@link #finish()} method or the server fails to transmit {@link #MAX_RETRIES} packets in
+ * a row, whichever happens first.
+ *
+ * @author miky
+ * @since 27/11/2013
+ */
public class ServiceDiscoveryThread extends Thread {
+ /**
+ * The name used to identify this thread.
+ */
public static final String THREAD_NAME = "ServiceDiscoveryThread";
+ /**
+ * Tag used for logging.
+ */
private static final String TAG = "NXTAR_CORE_UDPTHREAD";
+ /**
+ * Class name used for logging.
+ */
private static final String CLASS_NAME = ServiceDiscoveryThread.class.getSimpleName();
+ /**
+ * Maximum number of transmission attempts before ending the thread abruptly.
+ */
+ private static final int MAX_RETRIES = 5;
private Object semaphore;
private boolean done;
@@ -21,48 +47,82 @@ public class ServiceDiscoveryThread extends Thread {
private InetAddress group;
public ServiceDiscoveryThread(){
+ // Setup this thread name.
super(THREAD_NAME);
done = false;
semaphore = new Object();
+ // Try to get the InetAddress defined by the IP address defined in ProjectConstants.MULTICAST_ADDRESS.
try{
group = InetAddress.getByName(ProjectConstants.MULTICAST_ADDRESS);
- }catch(UnknownHostException uh){ }
+ }catch(UnknownHostException uh){
+ group = null;
+ }
+ // Create a UDP socket at the port defined in ProjectConstants.SERVER_UDP_PORT.
Gdx.app.debug(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Creating multicast server.");
try{
udpServer = new DatagramSocket(ProjectConstants.SERVER_UDP_PORT);
}catch(IOException io){
- Gdx.app.error(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Error creating UDP socket: " + io.getMessage(), io);
+ Gdx.app.error(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Error creating UDP socket: " + io.getMessage());
+ udpServer = null;
}
Gdx.app.debug(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Multicast server created.");
}
@Override
public void run(){
+ int retries = 0;
byte[] buffer = (new String("NxtAR server here!")).getBytes();
- try{
- while(true){
- synchronized(semaphore){
- if(done){
- udpServer.close();
- break;
- }
+
+ // If failed to get any of the required network elements then end the thread right away.
+ if(group == null || udpServer == null){
+ Gdx.app.error(TAG, CLASS_NAME + ".run() :: No multicast address defined, ending thread.");
+ return;
+ }
+ if(group == null){
+ Gdx.app.error(TAG, CLASS_NAME + ".run() :: No server available, ending thread.");
+ return;
+ }
+
+ while(true){
+ // Verify if the thread should end. If that is the case, close the server.
+ synchronized(semaphore){
+ if(done){
+ udpServer.close();
+ break;
}
- //Gdx.app.debug(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Resending packet");
+ }
+ try{
+ // End the thread if already at the last retry attempt after too many failed transmissions.
+ if(retries >= MAX_RETRIES){
+ Gdx.app.error(TAG, CLASS_NAME + ".run() :: Too many failed transmissions, ending thread.");
+ udpServer.close();
+ break;
+ }
+ // Send the packet and reset the retry counter.
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group, ProjectConstants.SERVER_UDP_PORT);
udpServer.send(packet);
- //Gdx.app.debug(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Packet sent");
+ retries = 0;
try{ sleep(250L); }catch(InterruptedException ie){ }
+ }catch(IOException io){
+ Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error sending packet: " + io.getMessage());
+ retries += 1;
}
- }catch(IOException io){
- Gdx.app.debug(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Error sending packet: " + io.getMessage(), io);
}
+ if(retries < MAX_RETRIES)
+ Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Service discovery successfully terminated.");
+ else
+ Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Service discovery terminated after too many failed transmissions.");
}
+ /**
+ * Marks this thread as ready to end.
+ */
public void finish(){
synchronized(semaphore){
+ Gdx.app.debug(TAG, CLASS_NAME + ".finish() :: Finishing service discovery thread.");
done = true;
}
}
diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java b/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java
index 4e455a0..9bb70f1 100644
--- a/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java
+++ b/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java
@@ -42,7 +42,7 @@ public class VideoStreamingThread extends Thread {
try{
client = server.accept();
if(netListener != null)
- netListener.interfaceConnected(THREAD_NAME);
+ netListener.networkStreamConnected(THREAD_NAME);
toaster.showShortToast("Client connected to VideoStreamingThread");
client.close();
}catch(IOException io){
From 5eb079fb7b07ae24ad97b857d87c6a59f3b52b42 Mon Sep 17 00:00:00 2001
From: Miguel Astor
Date: Thu, 28 Nov 2013 08:59:19 -0430
Subject: [PATCH 3/4] Changed the way the Android dependent interfaces are
passed in the constructor.
---
.../ccg/nxtar/{Main.java => NxtARCore.java} | 28 +++++++++++--------
.../ccg/nxtar/utils/ProjectConstants.java | 2 ++
2 files changed, 19 insertions(+), 11 deletions(-)
rename src/ve/ucv/ciens/ccg/nxtar/{Main.java => NxtARCore.java} (81%)
diff --git a/src/ve/ucv/ciens/ccg/nxtar/Main.java b/src/ve/ucv/ciens/ccg/nxtar/NxtARCore.java
similarity index 81%
rename from src/ve/ucv/ciens/ccg/nxtar/Main.java
rename to src/ve/ucv/ciens/ccg/nxtar/NxtARCore.java
index eb9b427..e8ddc36 100644
--- a/src/ve/ucv/ciens/ccg/nxtar/Main.java
+++ b/src/ve/ucv/ciens/ccg/nxtar/NxtARCore.java
@@ -6,6 +6,7 @@ import ve.ucv.ciens.ccg.nxtar.interfaces.Toaster;
import ve.ucv.ciens.ccg.nxtar.network.RobotControlThread;
import ve.ucv.ciens.ccg.nxtar.network.ServiceDiscoveryThread;
import ve.ucv.ciens.ccg.nxtar.network.VideoStreamingThread;
+import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants;
import com.badlogic.gdx.Application;
import com.badlogic.gdx.ApplicationListener;
@@ -18,9 +19,9 @@ import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
-public class Main implements ApplicationListener, NetworkConnectionListener {
+public class NxtARCore implements ApplicationListener, NetworkConnectionListener {
private static final String TAG = "NXTAR_CORE_MAIN";
- private static final String CLASS_NAME = Main.class.getSimpleName();
+ private static final String CLASS_NAME = NxtARCore.class.getSimpleName();
private OrthographicCamera camera;
private SpriteBatch batch;
@@ -34,15 +35,20 @@ public class Main implements ApplicationListener, NetworkConnectionListener {
private VideoStreamingThread videoThread;
private RobotControlThread robotThread;
- public Main(Toaster toaster, MulticastEnabler mcastEnabler){
+ public NxtARCore(Application concreteApp){
super();
- this.toaster = toaster;
- this.mcastEnabler = mcastEnabler;
connections = 0;
+ try{
+ this.toaster = (Toaster)concreteApp;
+ this.mcastEnabler = (MulticastEnabler)concreteApp;
+ }catch(ClassCastException cc){
+ Gdx.app.debug(TAG, CLASS_NAME + ".Main() :: concreteApp does not implement any of the required interfaces.");
+ System.exit(ProjectConstants.EXIT_FAILURE);
+ }
}
@Override
- public void create() {
+ public void create(){
float w = Gdx.graphics.getWidth();
float h = Gdx.graphics.getHeight();
@@ -79,7 +85,7 @@ public class Main implements ApplicationListener, NetworkConnectionListener {
}
@Override
- public void render() {
+ public void render(){
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
@@ -90,19 +96,19 @@ public class Main implements ApplicationListener, NetworkConnectionListener {
}
@Override
- public void resize(int width, int height) {
+ public void resize(int width, int height){
}
@Override
- public void pause() {
+ public void pause(){
}
@Override
- public void resume() {
+ public void resume(){
}
@Override
- public synchronized void networkStreamConnected(String streamName) {
+ public synchronized void networkStreamConnected(String streamName){
if(streamName.compareTo(VideoStreamingThread.THREAD_NAME) == 0 || streamName.compareTo(RobotControlThread.THREAD_NAME) == 0)
connections += 1;
if(connections >= 2){
diff --git a/src/ve/ucv/ciens/ccg/nxtar/utils/ProjectConstants.java b/src/ve/ucv/ciens/ccg/nxtar/utils/ProjectConstants.java
index b1fc3f0..280b9c5 100644
--- a/src/ve/ucv/ciens/ccg/nxtar/utils/ProjectConstants.java
+++ b/src/ve/ucv/ciens/ccg/nxtar/utils/ProjectConstants.java
@@ -5,6 +5,8 @@ public abstract class ProjectConstants {
public static final int SERVER_TCP_PORT_1 = 9989;
public static final int SERVER_TCP_PORT_2 = 9990;
public static final String MULTICAST_ADDRESS = "230.0.0.1";
+ public static final int EXIT_SUCCESS = 0;
+ public static final int EXIT_FAILURE = 1;
public static final boolean DEBUG = true;
}
From ddcf536e512ae5b4653303711a11d32fbdd81677 Mon Sep 17 00:00:00 2001
From: Miguel Astor
Date: Thu, 28 Nov 2013 09:16:52 -0430
Subject: [PATCH 4/4] Networking threads are singletons now.
---
src/ve/ucv/ciens/ccg/nxtar/NxtARCore.java | 6 +--
.../ccg/nxtar/network/RobotControlThread.java | 16 +++++++-
.../nxtar/network/ServiceDiscoveryThread.java | 37 ++++++++++++++++++-
.../nxtar/network/VideoStreamingThread.java | 17 +++++++--
4 files changed, 66 insertions(+), 10 deletions(-)
diff --git a/src/ve/ucv/ciens/ccg/nxtar/NxtARCore.java b/src/ve/ucv/ciens/ccg/nxtar/NxtARCore.java
index e8ddc36..453e808 100644
--- a/src/ve/ucv/ciens/ccg/nxtar/NxtARCore.java
+++ b/src/ve/ucv/ciens/ccg/nxtar/NxtARCore.java
@@ -69,9 +69,9 @@ public class NxtARCore implements ApplicationListener, NetworkConnectionListener
Gdx.app.debug(TAG, CLASS_NAME + ".create() :: Creating network threads");
mcastEnabler.enableMulticast();
- udpThread = new ServiceDiscoveryThread();
- videoThread = new VideoStreamingThread(toaster);
- robotThread = new RobotControlThread(toaster);
+ udpThread = ServiceDiscoveryThread.getInstance();
+ videoThread = VideoStreamingThread.getInstance().setToaster(toaster);
+ robotThread = RobotControlThread.getInstance().setToaster(toaster);
udpThread.start();
videoThread.start();
diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java b/src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java
index 1dbf3bc..b0bb090 100644
--- a/src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java
+++ b/src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java
@@ -20,10 +20,9 @@ public class RobotControlThread extends Thread {
private Socket client;
private Toaster toaster;
- public RobotControlThread(Toaster toaster){
+ private RobotControlThread(){
super(THREAD_NAME);
- this.toaster = toaster;
netListener = null;
try{
@@ -32,6 +31,19 @@ public class RobotControlThread extends Thread {
Gdx.app.error(TAG, CLASS_NAME + ".RobotControlThread() :: Error creating server: " + io.getMessage(), io);
}
}
+
+ private static class SingletonHolder{
+ public static final RobotControlThread INSTANCE = new RobotControlThread();
+ }
+
+ public static RobotControlThread getInstance(){
+ return SingletonHolder.INSTANCE;
+ }
+
+ public RobotControlThread setToaster(Toaster toaster){
+ this.toaster = toaster;
+ return this;
+ }
public void addNetworkConnectionListener(NetworkConnectionListener listener){
netListener = listener;
diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java b/src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java
index 7f78faf..3a3a09b 100644
--- a/src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java
+++ b/src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java
@@ -21,7 +21,6 @@ import com.badlogic.gdx.Gdx;
* a row, whichever happens first.
*
* @author miky
- * @since 27/11/2013
*/
public class ServiceDiscoveryThread extends Thread {
/**
@@ -41,12 +40,24 @@ public class ServiceDiscoveryThread extends Thread {
*/
private static final int MAX_RETRIES = 5;
+ /**
+ * A semaphore object used to synchronize acces to this thread finish flag.
+ */
private Object semaphore;
+ /**
+ * The finish flag.
+ */
private boolean done;
+ /**
+ * The UDP server socket used for the ad hoc service discovery protocol.
+ */
private DatagramSocket udpServer;
+ /**
+ * Holder for the multicast address used in the protocol.
+ */
private InetAddress group;
- public ServiceDiscoveryThread(){
+ private ServiceDiscoveryThread(){
// Setup this thread name.
super(THREAD_NAME);
@@ -71,6 +82,28 @@ public class ServiceDiscoveryThread extends Thread {
Gdx.app.debug(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Multicast server created.");
}
+ /**
+ * Singleton holder for this class.
+ */
+ private static class SingletonHolder{
+ public static final ServiceDiscoveryThread INSTANCE = new ServiceDiscoveryThread();
+ }
+
+ /**
+ * Get the singleton instance of this class.
+ *
+ * @return The singleton instance.
+ */
+ public static ServiceDiscoveryThread getInstance(){
+ return SingletonHolder.INSTANCE;
+ }
+
+ /**
+ * This thread's run method.
+ *
+ * This method executes the ad hoc service discovery protocol implemented by this class, as
+ * described in the class introduction.
+ */
@Override
public void run(){
int retries = 0;
diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java b/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java
index 9bb70f1..fc5fd8f 100644
--- a/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java
+++ b/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java
@@ -20,12 +20,10 @@ public class VideoStreamingThread extends Thread {
private Socket client;
private Toaster toaster;
- public VideoStreamingThread(Toaster toaster){
+ private VideoStreamingThread(){
super(THREAD_NAME);
- this.toaster = toaster;
netListener = null;
-
try{
server = new ServerSocket(ProjectConstants.SERVER_TCP_PORT_1);
}catch(IOException io){
@@ -33,6 +31,19 @@ public class VideoStreamingThread extends Thread {
}
}
+ private static class SingletonHolder{
+ public static final VideoStreamingThread INSTANCE = new VideoStreamingThread();
+ }
+
+ public static VideoStreamingThread getInstance(){
+ return SingletonHolder.INSTANCE;
+ }
+
+ public VideoStreamingThread setToaster(Toaster toaster){
+ this.toaster = toaster;
+ return this;
+ }
+
public void addNetworkConnectionListener(NetworkConnectionListener listener){
netListener = listener;
}