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; }