package controller.net; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.regex.Pattern; import common.Log; import data.GameControlData; import data.GameControlReturnData; import data.Rules; /** * * @author Marcel Steinbeck * * This class is used to receive a packet send by a robot on port {@link GameControlData#GAMECONTROLLER_RETURNDATA_PORT} via UDP * over broadcast. * If a package was received, this class will invoke {@link RobotWatcher#update(data.GameControlReturnData)} to update * the robots online status. * * This class is a sigleton! */ public class GameControlReturnDataReceiver extends Thread { /** The instance of the singleton. */ private static GameControlReturnDataReceiver instance; /** The used socket to receive the packages. */ private final DatagramSocket datagramSocket; /** * Creates a new Receiver. * * @param address the InetAddress to listen on.<br /> * Only applied if {@link Rules#dropBroadcastMessages rule.dropBroadcastMessages} is set * to true. * @throws SocketException the an error occurs while creating the socket * @throws UnknownHostException if (internally chosen) inet-address is not valid or no network * device is bound to an address matching the regex (ignoring loopback interfaces) */ private GameControlReturnDataReceiver(final InetAddress address) throws SocketException, UnknownHostException { datagramSocket = new DatagramSocket(null); datagramSocket.setReuseAddress(true); datagramSocket.setSoTimeout(500); if (Rules.league.dropBroadcastMessages) { datagramSocket.bind(new InetSocketAddress(address, GameControlData.GAMECONTROLLER_RETURNDATA_PORT)); } else { datagramSocket .bind(new InetSocketAddress(GameControlData.GAMECONTROLLER_RETURNDATA_PORT)); } } /** * Initializes the GameControlReturnDataReceiver. This needs to be called before * {@link #getInstance()} is available. * * @param address the InetAddress to listen on.<br /> * Only applied if {@link Rules#dropBroadcastMessages rule.dropBroadcastMessages} is set * to true. * @throws SocketException if an error occurs while creating the socket * @throws UnknownHostException if (internally chosen) inet-address is not valid or no network * device is bound to an address matching the regex (ignoring loopback interfaces) * @throws IllegalStateException if the Receiver is already initialized */ public synchronized static void initialize(final InetAddress address) throws SocketException, UnknownHostException { if (instance != null) { throw new IllegalStateException("receiver is already initialized"); } else { instance = new GameControlReturnDataReceiver(address); } } /** * Returns the instance of the singleton. * * @return The instance of the Receiver * @throws IllegalStateException if the Receiver is not initialized yet */ public synchronized static GameControlReturnDataReceiver getInstance() { if (instance == null) { throw new IllegalStateException("receiver is not initialized yet"); } else { return instance; } } @Override public void run() { while (!isInterrupted()) { final ByteBuffer buffer = ByteBuffer.wrap(new byte[Math.max(GameControlReturnData.SIZE, GameControlReturnData.SIZE1)]); final GameControlReturnData player = new GameControlReturnData(); final DatagramPacket packet = new DatagramPacket(buffer.array(), buffer.array().length); try { datagramSocket.receive(packet); buffer.rewind(); if (player.fromByteArray(buffer)) { RobotWatcher.update(player); } else { System.out.println("WARN: RETURN DATA NOT ACCEPTED!"); } } catch (SocketTimeoutException e) { // ignore, because we set a timeout } catch (IOException e) { Log.error("something went wrong while receiving : " + e.getMessage()); } } datagramSocket.close(); } }