package seng302; import seng302.Model.Boat; import seng302.Networking.BinaryMessageEncoder; import seng302.Networking.MessageEncoders.RaceVisionByteEncoder; import seng302.Networking.MessageEncoders.XMLMessageEncoder; import seng302.Networking.Utils.BoatLocationMessage; import seng302.Networking.Utils.MessageType; import seng302.Networking.Utils.RaceStatus; import seng302.Networking.Utils.XMLMessage; import java.io.*; import java.net.*; import java.util.concurrent.ArrayBlockingQueue; /** * TCP client to recive information from AC35 data source */ public class MockOutput implements Runnable { private long lastHeartbeatTime; private RaceVisionByteEncoder messageEncoder = new RaceVisionByteEncoder(); //socket port 5003 as 4940 is ac35 live port and 4941 is ac35 test port private ServerSocket serverSocket; private Socket mockSocket; private DataOutputStream outToVisualiser; //a buffer that contains items that are waiting to be sent private ArrayBlockingQueue messagesToSendBuffer = new ArrayBlockingQueue<>(99999999); private short messageNumber = 1; private short xmlSequenceNumber = 1; private int heartbeatSequenceNum = 1; private int boatLocationSequenceNumber = 1; private int raceStatusSequenceNumber = 1; private String raceXml; private String regattaXml; private String boatsXml; public MockOutput() throws IOException { lastHeartbeatTime = System.currentTimeMillis(); serverSocket = new ServerSocket(4945); } /** * calculates the time since last heartbeat * @return time since last heartbeat */ private double timeSinceHeartbeat() { long now = System.currentTimeMillis(); return (now - lastHeartbeatTime) / 1000.0; } //returns the heartbeat message private byte[] heartbeat(){ byte[] heartbeatMessage = messageEncoder.heartBeat(heartbeatSequenceNum); heartbeatSequenceNum++; BinaryMessageEncoder binaryMessageEncoder = new BinaryMessageEncoder(MessageType.HEARTBEAT, System.currentTimeMillis(), messageNumber, (short)heartbeatMessage.length, heartbeatMessage); messageNumber++; return binaryMessageEncoder.getFullMessage(); } /** * Used to give the mockOutput an xml string to be made into a message and sent * @param xmlString the xml string to send * @param messageType the kind of xml string, values given in AC35 spec (5 regatta, 6 race, 7 boat) */ public synchronized void parseXMLString(String xmlString, int messageType){ XMLMessageEncoder encoder = new XMLMessageEncoder(messageNumber, System.currentTimeMillis(), messageType, xmlSequenceNumber,(short) xmlString.length(), xmlString); //iterates the sequence numbers xmlSequenceNumber++; byte[] encodedXML = encoder.encode(); BinaryMessageEncoder binaryMessageEncoder = new BinaryMessageEncoder(MessageType.XMLMESSAGE, System.currentTimeMillis(), messageNumber, (short)encodedXML.length, encodedXML); //iterates the message number messageNumber++; addMessageToBufferToSend(binaryMessageEncoder.getFullMessage()); } /** * Used to give the mocOutput information about boat location to be made into a message and sent */ public synchronized void parseBoatLocation(int sourceID, double lat, double lon){ BoatLocationMessage boatLocationMessage = new BoatLocationMessage(sourceID, lat, lon, boatLocationSequenceNumber); //iterates the sequence number boatLocationSequenceNumber++; //encodeds the messages byte[] encodedBoatLoc = messageEncoder.boatLocation(boatLocationMessage); //encodeds the full message with header BinaryMessageEncoder binaryMessageEncoder = new BinaryMessageEncoder(MessageType.BOATLOCATION, System.currentTimeMillis(), messageNumber, (short)encodedBoatLoc.length, encodedBoatLoc); //iterates the message number messageNumber++; addMessageToBufferToSend(binaryMessageEncoder.getFullMessage()); } public synchronized void parseRaceStatus(RaceStatus raceStatus){ //iterates the sequence number raceStatusSequenceNumber++; //encodeds the messages byte[] encodedRaceStatus = messageEncoder.raceStatus(raceStatus); //encodeds the full message with header BinaryMessageEncoder binaryMessageEncoder = new BinaryMessageEncoder(MessageType.RACESTATUS, System.currentTimeMillis(), messageNumber, (short)encodedRaceStatus.length, encodedRaceStatus); //iterates the message number messageNumber++; addMessageToBufferToSend(binaryMessageEncoder.getFullMessage()); } private synchronized void addMessageToBufferToSend(byte[] messagesToSendBuffer) { this.messagesToSendBuffer.add(messagesToSendBuffer); } public void run() { try { while (true){ mockSocket = serverSocket.accept(); outToVisualiser = new DataOutputStream(mockSocket.getOutputStream()); /*******************************Test********************************/ while(boatsXml == null || regattaXml == null || raceXml == null) { } parseXMLString(raceXml, XMLMessage.XMLTypeRace); parseXMLString(regattaXml, XMLMessage.XMLTypeRegatta); parseXMLString(boatsXml, XMLMessage.XMLTypeBoat); /*******************************Test********************************/ while(true) { try { //sends a heartbeat every 5 seconds if (timeSinceHeartbeat() >= 5.00) { outToVisualiser.write(heartbeat()); lastHeartbeatTime = System.currentTimeMillis(); } //checks the buffer to see if there is anything to send if (messagesToSendBuffer.size() > 0) { for (byte[] binaryMessage : messagesToSendBuffer) { //sends the message to the visualiser outToVisualiser.write(binaryMessage); } //cleans out buffer messagesToSendBuffer.clear(); } }catch(SocketException e){ break; } } } } catch (IOException e) { e.printStackTrace(); } } public void setRaceXml(String raceXml) { this.raceXml = raceXml; } public void setRegattaXml(String regattaXml) { this.regattaXml = regattaXml; } public void setBoatsXml(String boatsXml) { this.boatsXml = boatsXml; } public static void main(String argv[]) throws Exception { MockOutput client = new MockOutput(); client.run(); } }