package teamcomm.data; import teamcomm.data.event.RobotStateEvent; import teamcomm.data.event.RobotStateEventListener; import data.PlayerInfo; import data.SPLStandardMessage; import java.util.LinkedList; import java.util.ListIterator; import javax.swing.event.EventListenerList; /** * Class representing the state of a robot. * * @author Felix Thielke */ public class RobotState { public static enum ConnectionStatus { INACTIVE(10000), OFFLINE(2000), HIGH_LATENCY(500), ONLINE(0); public final int threshold; private ConnectionStatus(final int threshold) { this.threshold = threshold; } } private static final int AVERAGE_CALCULATION_TIME = 10000; private final String address; private SPLStandardMessage lastMessage; private long lastMessageTimestamp; private final LinkedList<Long> recentMessageTimestamps = new LinkedList<>(); private int messageCount = 0; private int illegalMessageCount = 0; private final int teamNumber; private Integer playerNumber = null; private byte penalty = PlayerInfo.PENALTY_NONE; private ConnectionStatus lastConnectionStatus = ConnectionStatus.ONLINE; private final EventListenerList listeners = new EventListenerList(); /** * Constructor. * * @param address IP address of the robot * @param teamNumber team number associated with the port the robot sends * his messages on */ public RobotState(final String address, final int teamNumber) { this.address = address; this.teamNumber = teamNumber; } /** * Handles a message received by the robot this object corresponds to. * * @param message received message or null if the message was invalid */ public void registerMessage(final SPLStandardMessage message) { if (!message.valid) { illegalMessageCount++; } lastMessage = message; if (message.playerNumValid) { playerNumber = (int) message.playerNum; } lastMessageTimestamp = System.currentTimeMillis(); synchronized (recentMessageTimestamps) { recentMessageTimestamps.addFirst(lastMessageTimestamp); } messageCount++; for (final RobotStateEventListener listener : listeners.getListeners(RobotStateEventListener.class)) { listener.robotStateChanged(new RobotStateEvent(this)); listener.connectionStatusChanged(new RobotStateEvent(this)); } } /** * Returns the IP address of the robot. * * @return IP address */ public String getAddress() { return address; } /** * Returns the most recent legal message received from this robot. * * @return message */ public SPLStandardMessage getLastMessage() { return lastMessage; } /** * Returns the average number of messages per second. * * @return number of messages per second */ public double getMessagesPerSecond() { synchronized (recentMessageTimestamps) { final ListIterator<Long> it = recentMessageTimestamps.listIterator(recentMessageTimestamps.size()); final long curTime = System.currentTimeMillis(); while (curTime - it.previous() > AVERAGE_CALCULATION_TIME) { it.remove(); } return recentMessageTimestamps.size() > 0 ? (recentMessageTimestamps.size() * 1000.0 / Math.max(1000, curTime - recentMessageTimestamps.getLast())) : 0; } } /** * Updates the current network status of the robot internally. Sends events * about a change of the connection status if needed. * * @return the current connection status */ public ConnectionStatus updateConnectionStatus() { final ConnectionStatus c = getConnectionStatus(); if (c != lastConnectionStatus) { lastConnectionStatus = c; for (final RobotStateEventListener listener : listeners.getListeners(RobotStateEventListener.class)) { listener.connectionStatusChanged(new RobotStateEvent(this)); } } return c; } /** * Returns the current network status of the robot. * * @return connection status */ public ConnectionStatus getConnectionStatus() { final long timeSinceLastMessage = System.currentTimeMillis() - lastMessageTimestamp; for (final ConnectionStatus c : ConnectionStatus.values()) { if (timeSinceLastMessage >= c.threshold) { return c; } } return ConnectionStatus.ONLINE; } /** * Returns the total count of received messages. * * @return total message count */ public int getMessageCount() { return messageCount; } /** * Returns the total count of illegal messages. * * @return illegal messasge count */ public int getIllegalMessageCount() { return illegalMessageCount; } /** * Returns the ratio of illegal messages to the total count of messages. * * @return ratio */ public double getIllegalMessageRatio() { return (double) illegalMessageCount / (double) messageCount; } /** * Returns the team number of this robot. * * @return team number */ public int getTeamNumber() { return teamNumber; } /** * Returns the player number of the robot or null if it did not send any. * * @return player number or null */ public Integer getPlayerNumber() { return playerNumber; } /** * Returns the current penalty of the robot. * * @return penalty * @see PlayerInfo#penalty */ public byte getPenalty() { return penalty; } /** * Sets the current penalty of the robot. * * @param penalty penalty * @see PlayerInfo#penalty */ public void setPenalty(final byte penalty) { this.penalty = penalty; } /** * Registeres a GUI component as a listener receiving events when this robot * sends a message. * * @param listener component */ public void addListener(final RobotStateEventListener listener) { listeners.add(RobotStateEventListener.class, listener); } /** * Removes an event listener from this robot. * * @param listener listener */ public void removeListener(final RobotStateEventListener listener) { listeners.remove(RobotStateEventListener.class, listener); } }