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