package controller.net;
import common.Log;
import data.AdvancedData;
import data.GameControlData;
import data.Rules;
import java.io.IOException;
import java.net.*;
/**
* @author Marcel Steinbeck
*
* This class is used to send the current {@link GameControlData} (game-state) to all robots every 500 ms.
* The package will be send via UDP on port {@link GameControlData#GAMECONTROLLER_GAMEDATA_PORT} over broadcast.
*
* To prevent race-conditions (the sender is executed in its thread-context), the sender will hold a deep copy
* of {@link GameControlData} (have a closer look to the copy-constructor
* {@link GameControlData#GameControlData(data.GameControlData)}).
*
* This class is a singleton!
*/
public class Sender extends Thread
{
/** The instance of the singleton. */
private static Sender instance;
/** The packet number that is increased with each packet sent. */
private byte packetNumber = 0;
/** The socket, which is used to send the current game-state */
private final DatagramSocket datagramSocket;
/** The used inet-address (the broadcast address). */
private final InetAddress group;
/** The current deep copy of the game-state. */
private AdvancedData data;
/**
* Creates a new Sender.
*
* @throws SocketException if an error occurs while creating the socket
* @throws UnknownHostException if the used inet-address is not valid
*/
private Sender(final InetAddress broadcastAddress) throws SocketException, UnknownHostException
{
instance = this;
this.datagramSocket = new DatagramSocket();
this.group = broadcastAddress;
}
/**
* Initialises the Sender. This needs to be called before {@link #getInstance()} is available.
* @param broadcastAddress the broadcast address to use
* @throws SocketException if an error occurs while creating the socket
* @throws UnknownHostException if the used inet-address is not valid
* @throws IllegalStateException if the sender is already initialized
*/
public synchronized static void initialize(final InetAddress broadcastAddress) throws SocketException, UnknownHostException
{
if (null != instance) {
throw new IllegalStateException("sender is already initialized");
} else {
instance = new Sender(broadcastAddress);
}
}
/**
* Returns the instance of the singleton.
*
* @return The instance of the Sender
* @throws IllegalStateException if the Sender is not initialized yet
*/
public synchronized static Sender getInstance()
{
if (null == instance) {
throw new IllegalStateException("sender is not initialized yet");
} else {
return instance;
}
}
/**
* Sets the current game-state to send. Creates a deep copy of data to prevent race-conditions.
* Have a closer look to {@link GameControlData#GameControlData(data.GameControlData)}
*
* @param data the current game-state to send to all robots
*/
public void send(AdvancedData data)
{
this.data = (AdvancedData) data.clone();
}
@Override
public void run()
{
while (!isInterrupted()) {
if (data != null) {
data.updateTimes();
data.packetNumber = packetNumber;
byte[] arr = data.toByteArray().array();
DatagramPacket packet = new DatagramPacket(arr, arr.length, group, GameControlData.GAMECONTROLLER_GAMEDATA_PORT);
try {
datagramSocket.send(packet);
packetNumber++;
} catch (IOException e) {
Log.error("Error while sending");
e.printStackTrace();
}
}
if (data != null) {
if (Rules.league.compatibilityToVersion7) {
byte[] arr = data.toByteArray7().array();
DatagramPacket packet = new DatagramPacket(arr, arr.length, group, GameControlData.GAMECONTROLLER_GAMEDATA_PORT);
try {
datagramSocket.send(packet);
} catch (IOException e) {
Log.error("Error while sending");
e.printStackTrace();
}
}
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
interrupt();
}
}
datagramSocket.close();
}
}