All Downloads are FREE. Search and download functionalities are using the official Maven repository.

lejos.hardware.BrickFinder Maven / Gradle / Ivy

Go to download

leJOS (pronounced like the Spanish word "lejos" for "far") is a tiny Java Virtual Machine. In 2013 it was ported to the LEGO EV3 brick.

The newest version!
package lejos.hardware;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import lejos.hardware.ev3.LocalEV3;
import lejos.remote.ev3.RemoteEV3;

public class BrickFinder {
	private static final int DISCOVERY_PORT = 3016;
	private static final String FIND_CMD = "find";
	private static Brick defaultBrick, localBrick;
	private static final int MAX_DISCOVERY_TIME = 2000;
	private static final int MAX_PACKET_SIZE = 32;
	private static final int MAX_HOPS = 2;

	/**
	 * Internal class to implement a name server that can respond to queries 
	 * generated by discover and find methods. User code will not normally need to
	 * run an instance of this as the leJOS menu will usually do so.
	 * @author andy
	 *
	 */
	private static class DiscoveryServer implements Runnable
	{
	    DatagramSocket socket;
	    boolean forward;
	    static DiscoveryServer server;
	    static Thread serverThread;

	    private DiscoveryServer(boolean forward)
	    {
	        this.forward = forward;
	    }
	    
	    public void run()
	    {
	        try {
                // Keep a socket open to listen to all the UDP trafic that is
                // destined for this port
                socket = new DatagramSocket(DISCOVERY_PORT,
                        InetAddress.getByName("0.0.0.0"));
                socket.setBroadcast(true);
                // make sure localBrick is valid
                if (getLocal() == null) return;
                // Receive a packet
                byte[] recvBuf = new byte[MAX_PACKET_SIZE];
                DatagramPacket packet = new DatagramPacket(recvBuf,
                        recvBuf.length);
                while (true)
                {
                    // Receive a packet
                    socket.receive(packet);
                    // Packet received
                    //System.out.println(getClass().getName()
                            //+ ">>>Discovery packet received from: "
                            //+ packet.getAddress().getHostAddress());
                    // See if the packet holds the right command (message)
                    String message = new String(packet.getData(), 0, packet.getLength()).trim();
                    //System.out.println("Message " + message);
                    String[] args = message.split("\\s+");
                    if (args.length == 5 && args[0].equalsIgnoreCase(FIND_CMD))
                    {
                        InetAddress replyAddr = InetAddress.getByName(args[2]);
                        int replyPort = Integer.parseInt(args[3]);
                        int hops = Integer.parseInt(args[4]);
                        String hostname = localBrick.getName();
                        if ((!forward || hops == MAX_HOPS) && (args[1].equalsIgnoreCase("*") || args[1].equalsIgnoreCase(hostname)))
                        {
                            byte[] sendData = hostname.getBytes();
                            //System.out.println("Send response to " + replyAddr);
                            // Send a response
                            DatagramPacket sendPacket = new DatagramPacket(
                                    sendData, sendData.length, replyAddr,
                                    replyPort);
                            try {
                                socket.send(sendPacket);
                            } catch (IOException e)
                            {
                                System.out.println("Error in send" + e);
                                // ignore errors on send , we need to keep running
                            }

                            //System.out.println(getClass().getName()
                                    //+ ">>>Sent packet to: "
                                    //+ sendPacket.getAddress().getHostAddress());
                        }
                        if (forward && --hops > 0)
                            broadcastFindRequest(socket, args[1], replyAddr, replyPort, hops);
                    }
                }
	        } catch (SocketException e) {
	            // do nothing we use this to force an exit of the thread
	        } catch (IOException e) {
	            System.out.println("Brickfinder Got error " + e);
	        } finally {
    	        socket.close();
	        }
	    }

	    /**
	     * Start the discovery server for this device
	     * @param forward true if requests should be forwarded to other devices
	     */
	    public static synchronized void start(boolean forward)
	    {
	        if (server == null)
	        {
	            server = new DiscoveryServer(forward);
	            serverThread = new Thread(server);
	            serverThread.setDaemon(true);
	            serverThread.start();
	        }
	    }

	    /**
	     * Stop the discovery service on this device. Wait for the server to exit.
	     */
	    public static synchronized void stop()
	    {
	        if (server != null && server.socket != null)
	        {
	            // abort the running server
	            if (server.socket != null)
	                server.socket.close();
	            try
                {
                    serverThread.join();
                } catch (InterruptedException e)
                {
                    // not a lot to do
                }
	            server.socket = null;
	            server = null;	            
	        }
	    }
	}
	
	
	public static Brick getLocal() {
		if (localBrick != null) return localBrick;
		// Check we are running on an EV3
		localBrick = LocalEV3.get();
		return localBrick;
	}
	
	public static Brick getDefault() {
		if (defaultBrick != null) return defaultBrick;
		try {
			// See if we are running on an EV3
			defaultBrick =  LocalEV3.get();
			return defaultBrick;
		} catch (Throwable e) {
			try {
				BrickInfo[] bricks = discover();
				if (bricks.length > 0) {
					defaultBrick = new RemoteEV3(bricks[0].getIPAddress());
					return defaultBrick;
				} else throw new DeviceException("No EV3 brick found");
			} catch (Exception e1) {
			    throw new DeviceException("No brick found");
			}
		}
	}
	
	private static void broadcastFindRequest(DatagramSocket socket, String name, InetAddress replyAddr, int replyPort, int hop)
	{
        try
        {
            // Broadcast the message over all the network interfaces
            Enumeration interfaces = NetworkInterface
                    .getNetworkInterfaces();
            while (interfaces.hasMoreElements())
            {
                NetworkInterface networkInterface = (NetworkInterface) interfaces
                        .nextElement();

                if (networkInterface.isLoopback() || !networkInterface.isUp())
                {
                    continue; // Don't want to broadcast to the loopback
                              // interface
                }

                for (InterfaceAddress interfaceAddress : networkInterface
                        .getInterfaceAddresses())
                {
                    InetAddress broadcast = interfaceAddress.getBroadcast();
                    if (broadcast == null)
                        continue;
                    String message = FIND_CMD + " " + name;
                    if (replyAddr == null)
                        message += " " + interfaceAddress.getAddress().getHostAddress() + " " + socket.getLocalPort();
                    else
                        message += " " + replyAddr.getHostAddress() + " " + replyPort;
                    message += " " + hop;
                        
                    byte[] sendData = message.getBytes();

                    // Send the broadcast packet.
                    try
                    {
                        //System.out.println("Send to " + broadcast.getHostAddress() + " port " + DISCOVERY_PORT );
                        DatagramPacket sendPacket = new DatagramPacket(
                                sendData, sendData.length, broadcast, DISCOVERY_PORT);
                        socket.send(sendPacket);
                    } catch (Exception e)
                    {
                        System.err
                                .println("Exception sending to : "
                                        + networkInterface.getDisplayName()
                                        + " : " + e);
                    }
                }
            }
        } catch (IOException ex)
        {
            System.err.println("Exception opening socket : " + ex);
        }	    
	}

	/**
	 * Search for a named EV3. Return a table of the addresses that can be used to contact
	 * the device. An empty table is returned if no EV3s are found.
	 * @param name
	 * @return A table of matching devices
	 */
	public static BrickInfo[] find(String name)
	{
        DatagramSocket socket=null;
        Map ev3s = new HashMap();
        try
        {
            socket = new DatagramSocket();
            socket.setBroadcast(true);
            boolean findAll = name.equalsIgnoreCase("*");
            broadcastFindRequest(socket, name, null, -1, MAX_HOPS);
            socket.setSoTimeout(MAX_DISCOVERY_TIME/4);
            DatagramPacket packet = new DatagramPacket (new byte[MAX_PACKET_SIZE], MAX_PACKET_SIZE);
    
            long start = System.currentTimeMillis();
            
            while ((System.currentTimeMillis() - start) < MAX_DISCOVERY_TIME) {
                try {
                    socket.receive (packet);
                    String message = new String(packet.getData(), "UTF-8").trim();
                    if (findAll || message.equalsIgnoreCase(name))
                    {
                        String ip = packet.getAddress().getHostAddress();
                        ev3s.put(ip, new BrickInfo(message.trim(),ip,"EV3"));
                    }
                } catch (SocketTimeoutException e)
                {
                    // No response ask again
                    broadcastFindRequest(socket, name, null, -1, MAX_HOPS);                    
                }
            }
        } catch (IOException ex)
        {
            System.err.println("Exception opening socket : " + ex);
        } finally
        {
            if (socket != null)
                socket.close();
        }
        BrickInfo[] devices = new BrickInfo[ev3s.size()];
        int i = 0;
        
        for(String ev3: ev3s.keySet()) {
            devices[i++] = ev3s.get(ev3);
        }        
        return devices;
	}
	
	
	/**
	 * Search for available EV3s and populate table with results.
	 */
	public static BrickInfo[] discover() {	
	    return find("*");
	}
	
	public static BrickInfo[] discoverNXT() {
		try {
			Collection nxts = Bluetooth.getLocalDevice().search();
			BrickInfo[] bricks = new BrickInfo[nxts.size()];
			int i = 0;
			for(RemoteBTDevice d: nxts) {
				BrickInfo b = new BrickInfo(d.getName(), d.getAddress(), "NXT");
				bricks[i++] = b;
			}
			return bricks;
		} catch (Exception e) {
			throw new DeviceException("Error finding remote NXTs", e);
		}
	}
	
	public static void setDefault(Brick brick) {
		defaultBrick = brick;
	}

	
	/**
	 * Start the discovery server running. There should be a single discovery server
	 * running on each EV3. This provides responses to remote discover and find
	 * requests. Normally this server will be run by the leJOS menu and so user code
	 * does not need to start a copy.
     * @param forward true if requests should be forwarded to other devices
	 */
	public static void startDiscoveryServer(boolean forward)
	{
	    DiscoveryServer.start(forward);
	}

	/**
	 * Stop the discovery server
	 */
	public static void stopDiscoveryServer()
	{
	    DiscoveryServer.stop();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy