diff --git a/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/ExampleSimulationProgramTam.java b/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/ExampleSimulationProgramTam.java new file mode 100644 index 0000000000000000000000000000000000000000..a6ab8626861c9b6cebd7655b81eaa4763fdd30d8 --- /dev/null +++ b/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/ExampleSimulationProgramTam.java @@ -0,0 +1,504 @@ +/** + * Copyright (c) 2008-2022, MOVES Institute, Naval Postgraduate School (NPS). All rights reserved. + * This work is provided under a BSD open-source license, see project license.html and license.txt + * @author brutzman@nps.edu + */ +package MV3500Cohort2022MayJune.homework2.Tam; + +import edu.nps.moves.dis7.entities.swe.platform.surface._001Poseidon; +import edu.nps.moves.dis7.entities.swe.platform.surface._002Triton; +import edu.nps.moves.dis7.enumerations.*; +import edu.nps.moves.dis7.pdus.*; +import edu.nps.moves.dis7.utilities.DisThreadedNetworkInterface; +import edu.nps.moves.dis7.utilities.DisTime; +import edu.nps.moves.dis7.utilities.PduFactory; +import edu.nps.moves.dis7.utilities.SimulationManager; +import edu.nps.moves.dis7.utilities.stream.PduRecorder; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** The purpose of this inheritable class is to provide an easily modifiable example simulation program + * that includes DIS-capable entities performing tasks of interest, and then reporting activity via PDUs + * to the network. + * Default program initialization includes PDU recording turned on by default. + * @see <a href="https://gitlab.nps.edu/Savage/NetworkedGraphicsMV3500/-/blob/master/examples/src/OpenDis7Examples/ExampleSimulationProgramLog.txt">ExampleSimulationProgramLog.txt</a> + */ +public class ExampleSimulationProgramTam +{ + private boolean verboseComments = true; + static final String NETWORK_ADDRESS_DEFAULT = "239.1.2.3"; + static final int NETWORK_PORT_DEFAULT = 3000; + String networkAddress = NETWORK_ADDRESS_DEFAULT; + int networkPort = NETWORK_PORT_DEFAULT; + String thisHostName = "localhost"; + String DEFAULT_OUTPUT_DIRECTORY = "./pduLog"; + + /** seconds for real-time execution (not simulation time, which may or may not be the same) */ + double currentTimeStep = 2.0; // seconds + /** initial simulation time */ + double initialTime = 0.0; + /** current simulation time */ + double simulationTime; + + /** + * Output prefix to help with logging by identifying this class (can be overridden in subclass). + */ + protected static String TRACE_PREFIX; + + /* Declare DIS Protocol Data Unit (PDU) classes for simulation entities */ + + DisTime.TimestampStyle timestampStyle = DisTime.TimestampStyle.IEEE_ABSOLUTE; + PduFactory pduFactory = new PduFactory(timestampStyle); + + /** EntityID settings for entity 1 */ + protected EntityID entityID_1 = new EntityID(); + /** EntityID settings for entity 2 */ + protected EntityID entityID_2 = new EntityID(); + /** ESPDU for entity 1 */ + protected EntityStatePdu entityStatePdu_1 = pduFactory.makeEntityStatePdu(); + /** ESPDU for entity 2 */ + protected EntityStatePdu entityStatePdu_2 = pduFactory.makeEntityStatePdu(); + /** FirePdu for entity 1 first weapon (if any) */ + protected FirePdu firePdu_1a = pduFactory.makeFirePdu(); + /** FirePdu for entity 1 second weapon (if any) */ + protected FirePdu firePdu_1b = pduFactory.makeFirePdu(); + /** MunitionDescriptor for these weapons */ + protected MunitionDescriptor munitionDescriptor1 = new MunitionDescriptor(); + + /** this class instantiated as an object */ + SimulationManager simulationManager = new SimulationManager(); + + /** Get ready, get set... initialize simulation entities + */ + public void initializeSimulationEntities() + { + // Your model setup: define participants. who's who in this zoo? + // Assuming you keep track of entity objects... here is some support for for Entity 1. + + // PDU objects are already created, now set their values. + entityID_1.setSiteID(1).setApplicationID(2).setEntityID(3); // made-up example ID; + + entityID_2.setSiteID(1).setApplicationID(2).setEntityID(4); // made-up example ID; + // TODO someday, use enumerations; is there a unique site triplet for MOVES Institute? + + entityStatePdu_1.setEntityID(entityID_1); + entityStatePdu_1.setForceId(ForceID.FRIENDLY); + entityStatePdu_1.setEntityType(new _001Poseidon()); // note import statement above + entityStatePdu_1.setMarking("Entity #1"); + entityStatePdu_1.getMarkingString(); // check + + entityStatePdu_2.setEntityID(entityID_2); + entityStatePdu_2.setForceId(ForceID.OPPOSING); + entityStatePdu_2.setEntityType(new _002Triton()); // note import statement above + entityStatePdu_2.setMarking("Entity #2"); + + // TODO how should we customize this munition? what is it for your simulation? + munitionDescriptor1.setQuantity(1); + firePdu_1a.setDescriptor(munitionDescriptor1).setRange(1000.0f); + + // TODO simulation management PDUs for startup, planning to design special class support +// simulationManager.addEntity(); + simulationManager.setDescriptor("ExampleSimulationProgram"); + simulationManager.addHost(thisHostName); + simulationManager.setDisThreadedNetworkInterface(disNetworkInterface); + } + + /** + * This runSimulationLoops() method is for you, a customizable programmer-modifiable + * code block for defining and running a new simulation of interest. + * + * Welcome! Other parts of this program handle bookkeeping and plumbing tasks so that + * you can focus on your model entities and activities. + * Expandable support includes DIS EntityStatePdu, FirePdu and CommentPdu all available for + * modification and sending in a simulation loop. + * Continuous improvement efforts seek to make this program as easy and straightforward + * as possible for DIS simulationists to use and adapt. + * All of the other methods are setup, teardown and configuration that you may find + * interesting, even helpful, but don't really have to worry about. + */ + @SuppressWarnings("SleepWhileInLoop") // yes we do that + public void runSimulationLoops () + { + try + { + final int SIMULATION_MAX_LOOP_COUNT = 10; // be deliberate out there! also avoid infinite loops. + int simulationLoopCount = 0; // variable, initialized at 0 + boolean simulationComplete = false; // sentinel variable as termination condition, are we done yet? + + // TODO reset Clock Time to today's date and timestamp to zero, providing consistent outputs for each simulation run + + pduRecorder.setVerbose(true); + + initializeSimulationEntities(); + + simulationManager.simulationJoin(); + simulationManager.simulationStart(); + + // =================================================================================================== + // loop the simulation while allowed, programmer can set additional conditions to break out and finish + while (simulationLoopCount < SIMULATION_MAX_LOOP_COUNT) // are we done yet? + { + simulationLoopCount++; // good practice: increment loop counter as first action in that loop + + // ============================================================================================= + // * your own simulation code starts here! * + // ============================================================================================= + + // are there any other variables to modify at the beginning of your loop? + + // compute a track, update an ESPDU, whatever it is that your model is doing... + + // Where is my entity? Insert changes in position; this sample only changes X position. + entityStatePdu_1.getEntityLocation().setX(entityStatePdu_1.getEntityLocation().getX() + 1.0); // 1m per timestep + + // decide whether to fire, and then update the firePdu. Hmmm, you might want a target to shoot at! + + // etc. etc. your code goes here for your simulation of interest + + // something happens between my simulation entities, la de da de da... + System.out.println ("... My simulation just did something, no really..."); + System.out.flush(); + + + // make your reports: narrative code for CommentPdu here (set all to empty strings to avoid sending) + narrativeMessage1 = "MV3500 ExampleSimulationProgram"; + narrativeMessage2 = "runSimulation() loop " + simulationLoopCount; + narrativeMessage3 = ""; // intentionally blank for testing + + // your loop termination condition goes here + if (simulationLoopCount > 4) // for example + { + simulationComplete = true; + } + // ============================================================================================= + // * your own simulation code is finished here! * + // ============================================================================================= + + // staying synchronized with timestep: wait duration for elapsed time in this loop + // Thread.sleep needs a (long) parameter for milliseconds, which are clumsy to use sometimes + Thread.sleep((long)(currentTimeStep * 1000)); // seconds * (1000 msec/sec) = milliseconds + System.out.println ("... [Pausing for " + currentTimeStep + " seconds]"); + + // OK now send the status PDUs for this loop, and then continue + System.out.println ("sending PDUs for simulation step " + simulationLoopCount + ", monitor loopback to confirm sent"); + System.out.flush(); + sendAllPdusForLoopTimestep(entityStatePdu_1, firePdu_1a, currentTimeStepComment, narrativeMessage1, narrativeMessage2, narrativeMessage3); + sendSinglePdu(entityStatePdu_2); // me too i.e. 2! + System.out.println ("... [PDUs successfully sent for this loop]"); + System.out.flush(); + + // =============================== + // current loop now finished, check whether to terminate if simulation complete, otherwise continue + if (simulationComplete || (simulationLoopCount > 10000)) // for example; including fail-safe condition is good + { + System.out.println ("... [loop termination condition met, simulationComplete=" + simulationComplete + "]"); // ", final loopCount=" + loopCount + + System.out.flush(); + break; + } + } // end of simulation loop + // =================================================================================================== + + narrativeMessage2 = "runSimulation() completed successfully"; // all done + sendCommentPdu(narrativeComment, narrativeMessage1, narrativeMessage2, narrativeMessage3); + System.out.println ("... [final CommentPdu successfully sent for simulation]"); + + // TODO simulation management PDUs + simulationManager.simulationStop(); + simulationManager.simulationLeave(); + } + catch (InterruptedException iex) // handle any exception that your code might choose to provoke! + { + Logger.getLogger(ExampleSimulationProgramTam.class.getName()).log(Level.SEVERE, null, iex); + } + } + /* **************************** infrastructure code, modification is seldom needed ************************* */ + + String narrativeMessage1 = new String(); + String narrativeMessage2 = new String(); + String narrativeMessage3 = new String(); + + /* VariableRecordType enumerations have potential use with CommentPdu logs */ + /* TODO contrast to EntityType */ + VariableRecordType descriptionComment = VariableRecordType.DESCRIPTION; + VariableRecordType narrativeComment = VariableRecordType.COMPLETE_EVENT_REPORT; + VariableRecordType statusComment = VariableRecordType.APPLICATION_STATUS; + VariableRecordType currentTimeStepComment = VariableRecordType.APPLICATION_TIMESTEP; + VariableRecordType otherComment = VariableRecordType.OTHER; + + // class variables + DisThreadedNetworkInterface disNetworkInterface; + DisThreadedNetworkInterface.PduListener pduListener; + Pdu receivedPdu; + PduRecorder pduRecorder; + + /** + * Constructor design goal: additional built-in initialization conveniences can go here + * to keep student efforts focused on the runSimulation() method. + */ + public ExampleSimulationProgramTam() + { + DisTime.setTimestampStyle(timestampStyle); + + try + { + thisHostName = InetAddress.getLocalHost().getHostName(); + System.out.println(TRACE_PREFIX + "thisHostName=" + thisHostName); + } + catch (UnknownHostException uhe) + { + System.out.println(TRACE_PREFIX + thisHostName + "not connected to network: " + uhe.getMessage()); + } + } + + /** + * Utility Constructor that allows your example simulation program to override default network address and port + * @param address network address to use + * @param port corresponding network port to use + */ + public ExampleSimulationProgramTam(String address, int port) + { + super(); + + setNetworkAddress(address); + + setNetworkPort(port); + } + + /** + * get current networkAddress as a string + * @return the networkAddress + */ + public String getNetworkAddress() + { + return networkAddress; + } + /** + * set current networkAddress using a string + * @param newNetworkAddress the networkAddress to set + */ + public final void setNetworkAddress(String newNetworkAddress) + { + this.networkAddress = newNetworkAddress; + } + + /** + * get current networkPort + * @return the networkPort + */ + public int getNetworkPort() + { + return networkPort; + } + /** + * set current networkPort + * @param newNetworkPort the networkPort to set + */ + public final void setNetworkPort(int newNetworkPort) + { + this.networkPort = newNetworkPort; + } + /** + * Get timestampStyle used by PduFactory + * @return current timestampStyle + */ + public DisTime.TimestampStyle getTimestampStyle() + { + return timestampStyle; + } + /** + * Set timestampStyle used by PduFactory + * @param newTimestampStyle the timestampStyle to set + * @return same object to permit progressive setters + */ + public ExampleSimulationProgramTam setTimestampStyle(DisTime.TimestampStyle newTimestampStyle) + { + timestampStyle = newTimestampStyle; + DisTime.setTimestampStyle(newTimestampStyle); + return this; + } + + /** + * Initialize network interface, choosing best available network interface + */ + public void setUpNetworkInterface() + { + disNetworkInterface = new DisThreadedNetworkInterface(getNetworkAddress(), getNetworkPort()); + disNetworkInterface.setDescriptor ("ExampleSimulationProgram pdu looping"); + + System.out.println("Network confirmation:" + + " address=" + disNetworkInterface.getAddress()+ // disNetworkInterface.getMulticastGroup() + + " port=" + disNetworkInterface.getPort()); // + disNetworkInterface.getDisPort()); + pduListener = new DisThreadedNetworkInterface.PduListener() + { + /** Callback handler for listener */ + @Override + public void incomingPdu(Pdu newPdu) + { + receivedPdu = newPdu; + } + }; + disNetworkInterface.addListener(pduListener); + + String outputDirectory = DEFAULT_OUTPUT_DIRECTORY; + System.out.println("Beginning pdu save to directory " + outputDirectory); + pduRecorder = new PduRecorder(outputDirectory, getNetworkAddress(), getNetworkPort()); // assumes save + pduRecorder.setEncodingPduLog(PduRecorder.ENCODING_PLAINTEXT); + pduRecorder.setVerbose(true); // either sending, receiving or both + pduRecorder.start(); // begin running + } + + /** All done, release network resources */ + public void tearDownNetworkInterface() + { + pduRecorder.stop(); // handles disNetworkInterface.close(), tears down threads and sockets + } + + /** + * Send a single Protocol Data Unit (PDU) of any type + * @param pdu the pdu to send + */ + protected void sendSinglePdu(Pdu pdu) + { + try + { + disNetworkInterface.send(pdu); + Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally + } + catch (InterruptedException ex) + { + System.err.println(this.getClass().getName() + " Error sending PDU: " + ex.getLocalizedMessage()); + System.exit(1); + } + } + + /** + * Send Comment PDU + * @see <a href="https://docs.oracle.com/javase/tutorial/java/javaOO/arguments.html">Passing Information to a Method or a Constructor</a> Arbitrary Number of Arguments + * @param commentType enumeration value describing purpose of the narrative comment + * @param comments String array of narrative comments + */ + public void sendCommentPdu(VariableRecordType commentType, + // vararg... variable-length set of String comments can optionally follow + String... comments) + { + sendAllPdusForLoopTimestep (null, null, commentType, comments); + } + + /** + * Send EntityState, Fire, Comment PDUs that got updated for this loop, reflecting state of current simulation timestep. + * @see <a href="https://docs.oracle.com/javase/tutorial/java/javaOO/arguments.html">Passing Information to a Method or a Constructor</a> Arbitrary Number of Arguments + * @param entityStatePdu the ESPDU to send, if any + * @param firePdu the FirePDU to send, if any + * @param commentType enumeration value describing purpose of the narrative comment + * @param comments String array of narrative comments + */ + public void sendAllPdusForLoopTimestep(EntityStatePdu entityStatePdu, + FirePdu firePdu, + VariableRecordType commentType, + // vararg... variable-length set of String comments can optionally follow + String... comments) + { + if (entityStatePdu != null) + sendSinglePdu(entityStatePdu); + + if (firePdu != null) + sendSinglePdu(firePdu); // bang + + if ((comments != null) && (comments.length > 0)) + { + ArrayList<String> newCommentsList = new ArrayList<>(); + for (String comment : comments) + { + if (!comment.isEmpty()) + { + newCommentsList.add(comment); // OK found something to send + } + } + if (!newCommentsList.isEmpty()) + { + if (commentType == null) + commentType = otherComment; // fallback value otherComment + // now build the commentPdu from these string inputs, thus constructing a narrative entry + CommentPdu commentPdu = pduFactory.makeCommentPdu(commentType, newCommentsList.toArray(new String[0])); // comments); + sendSinglePdu(commentPdu); + if (isVerboseComments()) + { + System.out.println("*** [Narrative comment sent: " + commentType.name() + "] " + newCommentsList.toString()); + System.out.flush(); + } + } + } + } + + /** + * test for verboseComments mode + * @return whether verboseComments mode is enabled + */ + public boolean isVerboseComments() { + return verboseComments; + } + + /** + * set verboseComments mode + * @param newVerboseComments whether verboseComments mode is enabled + */ + public void setVerboseComments(boolean newVerboseComments) { + this.verboseComments = newVerboseComments; + } + + /** + * Initial execution via main() method: handle args array of command-line initialization (CLI) arguments here + * @param args command-line parameters: network address and port + */ + protected void handleArgs (String[] args) + { + // initial execution: handle args array of initialization arguments here + if (args.length == 2) + { + if ((args[0] != null) && !args[0].isEmpty()) + thisProgram.setNetworkAddress(args[0]); + if ((args[1] != null) && !args[1].isEmpty()) + thisProgram.setNetworkPort(Integer.parseInt(args[1])); + } + else if (args.length != 0) + { + System.err.println("Usage: " + thisProgram.getClass().getSimpleName() + " [address port]"); + System.exit(-1); + } + } + + /** Locally instantiable copy of program, can be subclassed. */ + protected static ExampleSimulationProgramTam thisProgram; + + /** + * Main method is first executed when a program instance is loaded. + * @see <a href="https://docs.oracle.com/javase/tutorial/getStarted/application/index.html">Java Tutorials: A Closer Look at the "Hello World!" Application</a> + * @param args command-line parameters: network address and port. + * Command-line arguments are an array of optional String parameters that are passed from execution environment during invocation + */ + public static void main(String[] args) + { + TRACE_PREFIX = "[" + ExampleSimulationProgramTam.class.getName() + "] "; + + System.out.println(TRACE_PREFIX + "main() started..."); + + thisProgram = new ExampleSimulationProgramTam(); // creates instance of self within static main() method + + thisProgram.handleArgs (args); // process command-line invocation arguments + + thisProgram.setUpNetworkInterface(); + +// thisProgram.pduRecorder.setDescriptor (TRACE_PREFIX.replace("[","").replace("]","") + " pduRecorder"); + + thisProgram.runSimulationLoops(); // ... your simulation execution code goes in there ... + + thisProgram.tearDownNetworkInterface(); // make sure no processes are left lingering + + System.out.println(TRACE_PREFIX + "complete."); // report successful completion + + System.exit(0); // ensure all threads and sockets released + } +} diff --git a/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/ExampleTrackInterpolationTam.java b/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/ExampleTrackInterpolationTam.java new file mode 100644 index 0000000000000000000000000000000000000000..93d9b1c9f010129aac3f9da8c566516d2a8396be --- /dev/null +++ b/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/ExampleTrackInterpolationTam.java @@ -0,0 +1,263 @@ +/** + * Copyright (c) 2008-2022, MOVES Institute, Naval Postgraduate School (NPS). All rights reserved. + * This work is provided under a BSD open-source license, see project license.html and license.txt + * + * @author brutzman@nps.edu + */ +package MV3500Cohort2022MayJune.homework2.Tam; + + +import static MV3500Cohort2022MayJune.homework2.Tam.ExampleSimulationProgramTam.TRACE_PREFIX; +import static MV3500Cohort2022MayJune.homework2.Tam.ExampleSimulationProgramTam.thisProgram; +import edu.nps.moves.dis7.entities.swe.platform.surface._001Poseidon; +import edu.nps.moves.dis7.enumerations.ForceID; +import edu.nps.moves.dis7.pdus.EntityStatePdu; +import edu.nps.moves.dis7.pdus.Pdu; +import edu.nps.moves.dis7.pdus.Vector3Double; +import edu.nps.moves.dis7.utilities.DisTime; +import edu.nps.moves.dis7.utilities.stream.X3dCreateInterpolators; +import edu.nps.moves.dis7.utilities.stream.X3dCreateLineSet; +import java.util.ArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * The purpose of this program is to provide an easily modifiable example + * simulation for networked entity tracks and presentation, including + * DIS-capable entities doing tasks and reporting them to the network. + * Default settings include PDU recording turned on by default. + * @see <a href="https://gitlab.nps.edu/Savage/NetworkedGraphicsMV3500/-/blob/master/examples/src/OpenDis7Examples/ExampleTrackInterpolationLog.txt">ExampleTrackInterpolationLog.txt</a> + * @see <a href="https://calhoun.nps.edu/handle/10945/65436">REPEATABLE UNIT TESTING OF DISTRIBUTED INTERACTIVE SIMULATION (DIS) PROTOCOL BEHAVIOR STREAMS USING WEB STANDARDS</a> by Tobias Brennenstuhl, Masters Thesis, Naval Postgraduate School (NPS), June 2020 + * @see <a href="https://gitlab.nps.edu/Savage/SavageTheses/-/tree/master/BrennenstuhlTobias">https://gitlab.nps.edu/Savage/SavageTheses/-/tree/master/BrennenstuhlTobias</a> + */ +public class ExampleTrackInterpolationTam extends ExampleSimulationProgramTam +{ + // -------------------- Begin Variables for X3D autogenerated code + private X3dCreateInterpolators x3dInterpolators = new X3dCreateInterpolators(); + private X3dCreateLineSet x3dLineSet = new X3dCreateLineSet(); + private byte[] globalByteBufferForX3dInterpolators = null; + // -------------------- End Variables for X3D autogenerated code + + ArrayList<Pdu> pduSentList = new ArrayList<>(); + + /** + * This runSimulationLoops() method is a programmer-modifiable method for + * defining and running a new simulation of interest. Welcome! Other parts + * of this program handle bookkeeping and plumbing tasks so that you can + * focus on your model entities and activities. Expandable support includes + * DIS EntityStatePdu, FirePdu and CommentPdu all available for modification + * and sending in a simulation loop. Continuous improvement efforts seek to + * make this program as easy and straightforward as possible for DIS + * simulationists to use and adapt. All of the other methods are setup, + * teardown and configuration that you may find interesting, even helpful, + * but don't really have to worry about. + */ + @SuppressWarnings("SleepWhileInLoop") // yes we do that + @Override // indicates that this method supercedes corresponding superclass method + public void runSimulationLoops() + { + try + { + final int SIMULATION_MAX_LOOP_COUNT = 50; // be deliberate out there! also avoid infinite loops. + int simulationLoopCount = 0; // variable, initialized at 0 + boolean simulationComplete = false; // sentinel variable as termination condition, are we done yet? + + // TODO reset Clock Time to today's date and timestamp to zero, providing consistent outputs for each simulation run + DisTime.setEpochLvcNow(); + simulationTime = initialTime - currentTimeStep; // pre-initialization for first loop + + initializeSimulationEntities(); + + // use declared PDU objects and set their values. + entityID_1.setSiteID(1).setApplicationID(2).setEntityID(3); // made-up example ID; + // TODO someday, use enumerations; is there a unique site triplet for MOVES Institute? + + pduRecorder.setVerbose(false); + pduRecorder.hasVerboseOutput(); // debug check + + EntityStatePdu espdu_1 = pduFactory.makeEntityStatePdu(); + espdu_1.setEntityID(entityID_1); + espdu_1.setForceId(ForceID.FRIENDLY); + espdu_1.setEntityType(new _001Poseidon()); // note import statement above + espdu_1.setMarking("track path"); +// espdu_1.clearMarking(); // test +// espdu_1.getMarkingString(); // trace +// espdu_1.setEntityLocation(new Vector3Double().setX(0).setY(0).setZ(0)); // long form + espdu_1.setEntityLocation(0, 0, 0); // utility method + + float speedEntity_1 = 1.0f; // meters/second + EntityStatePdu.Direction directionEntity_1 = EntityStatePdu.Direction.NORTH; + + PduTrack pduTrack_1 = new PduTrack(); + pduTrack_1.setDescriptor("testing 123"); + pduTrack_1.setDefaultWaypointInterval(1.0f); // overrides timestamps + pduTrack_1.setAddLineBreaksWithinKeyValues(true); + pduTrack_1.setAuthor("Don Brutzman"); + pduTrack_1.setX3dModelName("ExampleTrackInterpolation.x3d"); + pduTrack_1.setX3dModelIdentifier("https://gitlab.nps.edu/Savage/NetworkedGraphicsMV3500/-/blob/master/examples/src/OpenDis7Examples/ExampleTrackInterpolation.x3d"); + pduTrack_1.addPdu(espdu_1); // initial location + + // OK send initial PDUs prior to loop + if (pduRecorder.hasVerboseOutput()) + System.out.println("sending PDUs for simulation step " + simulationLoopCount + ", monitor loopback to confirm sent"); + sendSinglePdu(espdu_1); +// sendCommentPdu(currentTimeStepComment, narrativeMessage1, narrativeMessage2, narrativeMessage3); + pduSentList.add(espdu_1); + reportPdu(simulationLoopCount, espdu_1.getEntityLocation(), directionEntity_1); + + // TODO simulation management PDUs for startup, planning to design special class support + // =================================================================================================== + // loop the simulation while allowed, programmer can set additional conditions to break out and finish + while (simulationLoopCount < SIMULATION_MAX_LOOP_COUNT) // are we done yet? + { + simulationLoopCount++; // good practice: increment loop counter as first action in that loop + simulationTime += currentTimeStep; // good practice: update clock along with loop index + + // ============================================================================================= + // * your own simulation code starts here! * + // ============================================================================================= + // are there any other variables to modify at the beginning of your loop? + // compute a track, update an ESPDU, whatever it is that your model is doing... + + // Pick direction, change each 10 seconds, traverse a box. No physics. Walk a box! + if (simulationLoopCount <= 10) + directionEntity_1 = EntityStatePdu.Direction.NORTH; + else if (simulationLoopCount <= 20) + directionEntity_1 = EntityStatePdu.Direction.EAST; + else if (simulationLoopCount <= 30) + directionEntity_1 = EntityStatePdu.Direction.SOUTH; + else // if (simulationLoopCount <= 40) + directionEntity_1 = EntityStatePdu.Direction.WEST; + + // use utility method to simply update velocity vector using speed value and direction + espdu_1.setEntityLinearVelocity(speedEntity_1, directionEntity_1); + + // Where is my entity? Insert changes in position; this sample only changes X position. + espdu_1.advanceEntityLocation(currentTimeStep); + + // make your reports: narrative code for CommentPdu here (set all to empty strings to avoid sending) + narrativeMessage1 = "MV3500 TrackSimulationProgram"; + narrativeMessage2 = "runSimulation() loop " + simulationLoopCount + " at time " + simulationTime; + narrativeMessage3 = ""; // intentionally blank for testing + + // your loop termination condition goes here + if (simulationLoopCount > 40) // for example + { + simulationComplete = true; + } + // ============================================================================================= + // * your own simulation code is finished here! * + // ============================================================================================= + + // staying synchronized with timestep: wait duration for elapsed time in this loop + // Thread.sleep needs a (long) parameter for milliseconds, which are clumsy to use sometimes + if (false) // real-time operation or simulation speedup + { + Thread.sleep((long) (currentTimeStep * 1000)); // seconds * (1000 msec/sec) = milliseconds + System.out.println(TRACE_PREFIX + "Pausing for " + currentTimeStep + " seconds"); + } + + // OK now send the status PDUs for this loop, and then continue + if (pduRecorder.hasVerboseOutput()) + System.out.println("sending PDUs for simulation step " + simulationLoopCount + ", monitor loopback to confirm sent"); + sendSinglePdu(espdu_1); + sendCommentPdu(currentTimeStepComment, narrativeMessage1, narrativeMessage2, narrativeMessage3); + if (pduRecorder.hasVerboseOutput()) + System.out.println(TRACE_PREFIX + "PDUs successfully sent for this loop"); + pduSentList.add(espdu_1); + pduTrack_1.addPdu(espdu_1); + Vector3Double location = espdu_1.getEntityLocation(); + reportPdu(simulationLoopCount, location, directionEntity_1); + + // =============================== + // current loop now finished, check whether to terminate if simulation complete, otherwise continue + if (simulationComplete || (simulationLoopCount > 10000)) // for example; including fail-safe condition is good + { + System.out.println(TRACE_PREFIX + "loop termination condition met, simulationComplete=" + simulationComplete); // ", final loopCount=" + loopCount + + break; + } + } // end of simulation loop + // =================================================================================================== + System.out.println(TRACE_PREFIX + "all PDUs successfully sent for this loop (pduSentList.size()=" + pduSentList.size() + " total)"); + + // track analysis + System.out.println(TRACE_PREFIX + "pduTrack_1 initialLocation=" + pduTrack_1.getInitialLocation() + ", latestLocation=" + pduTrack_1.getLatestLocation()); + pduTrack_1.sortPdus() + .createRawWaypoints(); + + System.out.println("pduTrack_1 getEspduCount()=" + pduTrack_1.getEspduCount()); + System.out.println("pduTrack_1 duration = " + pduTrack_1.getTotalDurationSeconds() + " seconds = " + + pduTrack_1.getTotalDurationTicks() + " ticks"); + System.out.println("================================="); + System.out.println(pduTrack_1.createX3dModel()); + System.out.println("================================="); + + narrativeMessage2 = "runSimulation() completed successfully"; // all done + sendCommentPdu(narrativeComment, narrativeMessage1, narrativeMessage2, narrativeMessage3); + if (pduRecorder.hasVerboseOutput()) + System.out.println(TRACE_PREFIX + "final CommentPdu successfully sent for simulation"); + // TODO simulation management PDUs + } + catch (InterruptedException iex) // handle any exception that your code might choose to provoke! + { + Logger.getLogger(ExampleTrackInterpolationTam.class.getName()).log(Level.SEVERE, null, iex); + } + } + + /** + * Report current PDU information to console + * @param simulationLoopCount current loop index + * @param location current location + * @param directionEntity current direction + * @return same object to permit progressive setters + */ + public ExampleTrackInterpolationTam reportPdu(int simulationLoopCount, Vector3Double location, EntityStatePdu.Direction directionEntity) + { + System.out.println (String.format("%2d ", simulationLoopCount) + "Entity location=(" + + String.format("%4.1f", location.getX()) + ", " + + String.format("%4.1f", location.getY()) + ", " + + String.format("%4.1f", location.getZ()) + ") " + + String.format("%-5s", directionEntity.name()) +// + " " + espdu_1.getEntityLinearVelocity().toString() + ); + return this; + } + + /* Default constructors used unless otherwise defined/overridden. */ + /** + * Main method is first executed when a program instance is loaded. + * + * @see + * <a href="https://docs.oracle.com/javase/tutorial/getStarted/application/index.html">Java + * Tutorials: A Closer Look at the "Hello World!" Application</a> + * @param args command-line arguments are an array of optional String + * parameters that are passed from execution environment during invocation + */ + public static void main(String[] args) + { + TRACE_PREFIX = "[" + ExampleTrackInterpolationTam.class.getName() + "] "; + + System.out.println(TRACE_PREFIX + "main() started..."); + + thisProgram = new ExampleTrackInterpolationTam(); // creates instance of self within static main() method + + thisProgram.handleArgs (args); // process command-line invocation arguments + + thisProgram.setUpNetworkInterface(); + + thisProgram.pduRecorder.setDescriptor (TRACE_PREFIX.replace("[","").replace("]","") + " pduRecorder"); + + thisProgram.pduRecorder.setVerbose(false); + thisProgram.setVerboseComments(false); + thisProgram.disNetworkInterface.setVerbose(false); + + thisProgram.runSimulationLoops(); // ... your simulation execution code goes in there ... + + thisProgram.tearDownNetworkInterface(); // make sure no processes are left lingering + + System.out.println(TRACE_PREFIX + "complete."); // report successful completion + + System.exit(0); // ensure all threads and sockets released + } + +} diff --git a/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/PduTrack.java b/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/PduTrack.java new file mode 100644 index 0000000000000000000000000000000000000000..504ea3db17ddc26610c304f9c2ee1cfeefa57cd1 --- /dev/null +++ b/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/PduTrack.java @@ -0,0 +1,1043 @@ +/* +Copyright (c) 1995-2022 held by the author(s). All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the names of the Naval Postgraduate School (NPS) + Modeling Virtual Environments and Simulation (MOVES) Institute + https://www.nps.edu and https://www.nps.edu/web/moves + nor the names of its contributors may be used to endorse or + promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +// TODO move into opendis7 distribution tree in package edu.nps.moves.dis7.utilities.stream; + +package MV3500Cohort2022MayJune.homework2.Tam; + +import MV3500Cohort2022MayJune.homework2.Duran.*; +import edu.nps.moves.dis7.entities.swe.platform.surface._001Poseidon; +import edu.nps.moves.dis7.enumerations.DisPduType; +import edu.nps.moves.dis7.enumerations.ForceID; +import edu.nps.moves.dis7.pdus.EntityID; +import edu.nps.moves.dis7.pdus.EntityStatePdu; +import edu.nps.moves.dis7.pdus.EulerAngles; +import edu.nps.moves.dis7.pdus.Pdu; +import edu.nps.moves.dis7.pdus.Vector3Double; +import edu.nps.moves.dis7.utilities.DisTime; +import edu.nps.moves.dis7.utilities.PduFactory; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * Create a track from DIS ESPDUs + * @author brutzman + */ +public class PduTrack +{ + private String descriptor = new String(); + private ArrayList<Pdu> pduList = new ArrayList<>(); + private EntityStatePdu initialEspdu; + private EntityStatePdu latestEspdu; + private Vector3Double initialLocation; + private Vector3Double latestLocation; + + /** waypoint timelineList in seconds */ + private ArrayList<Float> timelineList = new ArrayList<>(); + private ArrayList<Vector3Double> waypointsList = new ArrayList<>(); + private ArrayList<EulerAngles> eulerAnglesList = new ArrayList<>(); + + private String author = new String(); + private String x3dModelIdentifier = new String(); + private String x3dModelName = "PduTrackInterpolation.x3d"; + private float defaultWaypointInterval = -1; + private float durationSeconds = -1; + private String x3dTimeSensorDEF = new String(); + private String x3dPositionInterpolatorDEF = new String(); + private String x3dOrientationInterpolatorDEF = new String(); + private boolean addLineBreaksWithinKeyValues = false; + /** what kind of timestamp is being used */ + public DisTime.TimestampStyle timestampStyle = DisTime.TimestampStyle.IEEE_ABSOLUTE; + /** direct access to pduFactory for creating new PDU instances */ + protected PduFactory pduFactory = new PduFactory(timestampStyle); + private LocalDateTime recordingStart; + private LocalDateTime recordingStop; + private String todaysDateString = new String(); + + /** direct access to byteArrayOutputStream */ + protected ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + /** direct access to DataOutputStream */ + protected DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream); + private String TRACE_PREFIX = "[" + (PduTrack.class.getSimpleName()) + "] "; + + /** + * Constructor for PduTrack + */ + public PduTrack() + { + // initialization code here + + // https://docs.oracle.com/javase/tutorial/datetime/TOC.html + // https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/package-summary.html + // https://stackoverflow.com/questions/5175728/how-to-get-the-current-date-time-in-java/5175900 (scroll down to java.time) + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d MMMM yyyy"); + todaysDateString = LocalDate.now().format(formatter); +// System.out.println(TRACE_PREFIX + "today=" + todayString); + } + + /** + * Set timestampStyle used by PduFactory + * @param newTimestampStyle new value to set + */ + public PduTrack(DisTime.TimestampStyle newTimestampStyle) + { + timestampStyle = newTimestampStyle; + DisTime.setTimestampStyle(newTimestampStyle); + // automatic super invocation: return PduTrack(); + } + + /** + * Get simple descriptor (such as parent class name) for this SimulationManager, used in trace statements + * @return simple descriptor name + */ + public String getDescriptor() + { + return descriptor; + } + /** + * Set new simple descriptor (such as parent class name) for this SimulationManager, used in trace statements + * @param newDescriptor simple descriptor name for this interface + * @return same object to permit progressive setters */ + public PduTrack setDescriptor(String newDescriptor) + { + if (newDescriptor != null) + this.descriptor = newDescriptor.trim(); + TRACE_PREFIX = "[" + (PduTrack.class.getSimpleName() + " " + descriptor) + "] "; + return this; + } + /** + * Get timestampStyle used by PduFactory + * @return current timestampStyle + */ + public DisTime.TimestampStyle getTimestampStyle() + { + return timestampStyle; + } + /** + * Set timestampStyle used by PduFactory + * @param newTimestampStyle the timestampStyle to set + * @return same object to permit progressive setters + */ + public PduTrack setTimestampStyle(DisTime.TimestampStyle newTimestampStyle) + { + this.timestampStyle = newTimestampStyle; + DisTime.setTimestampStyle(newTimestampStyle); + return this; + } + + /** + * Determine initial location, reset to (0 0 0) if not found + * @return current initialLocation + */ + public Vector3Double getInitialLocation() { + if (initialLocation == null) + { + System.out.println (TRACE_PREFIX + "getInitialLocation() not found, isTrackEmpty()=" + isTrackEmpty() + ", returning 0 0 0"); + return new Vector3Double(); + } + return initialLocation; + } + /** + * Determine current location, reset to (0 0 0) if not found + * @return current latestLocation + */ + public Vector3Double getLatestLocation() { + if (latestLocation == null) + { + System.out.println (TRACE_PREFIX + "getLatestLocation() not found, isTrackEmpty()=" + isTrackEmpty() + ", returning 0 0 0"); + return new Vector3Double(); + } + return latestLocation; + } + /** + * Get individual Pdu from pduList, index must not exceed existing pduList size + * @param index for pdu of interest + * @return pdu of interest + */ + public Pdu getPdu(int index) throws IndexOutOfBoundsException + { + if ((index >= pduList.size()) || (index < 0)) + { + System.out.println (TRACE_PREFIX + "getPdu(" + index + ") out of bounds, pduList.size()=" + pduList.size()); + // then throw exception + } + return pduList.get(index); + } + /** + * get current pduList + * @return current pduList + */ + public ArrayList<Pdu> getPduList() { + return pduList; + } + /** + * get current waypointsList + * @return current waypointsList + */ + public ArrayList<Vector3Double> getWaypointsList() { + return waypointsList; + } + /** + * current eulerAnglesList + * @return current eulerAnglesList + */ + public ArrayList<EulerAngles> getEulerAnglesList() { + return eulerAnglesList; + } + /** + * Time in seconds corresponding to each PDU + * @return current timelineList + */ + public ArrayList<Float> getTimelineList() { + return timelineList; + } + /** + * Add PDU, typically ESPDU + * @param newPdu new Pdu to add, typically ESPDU + * @return same object to permit progressive setters + */ + public PduTrack addPdu(Pdu newPdu) + { + if (newPdu.getPduType() == DisPduType.ENTITY_STATE) + { +//// EntityStatePdu deepCopyEspdu = new EntityStatePdu(); +// EntityStatePdu deepCopyEspdu = pduFactory.makeEntityStatePdu(); +// deepCopyEspdu.setTimestamp (((EntityStatePdu)newPdu).getTimestamp()); +// deepCopyEspdu.setMarking (((EntityStatePdu)newPdu).getMarking()); +// deepCopyEspdu.setEntityID (((EntityStatePdu)newPdu).getEntityID()); +// deepCopyEspdu.setForceId (((EntityStatePdu)newPdu).getForceId()); +// deepCopyEspdu.setEntityType (((EntityStatePdu)newPdu).getEntityType()); +// deepCopyEspdu.setEntityLocation (((EntityStatePdu)newPdu).getEntityLocation()); +// deepCopyEspdu.setEntityOrientation(((EntityStatePdu)newPdu).getEntityOrientation()); + + EntityStatePdu deepCopyEspdu = ((EntityStatePdu)newPdu).copy(); + pduList.add(deepCopyEspdu); +// alternative trials: +// pduList.add(((EntityStatePdu)newPdu).copyDataOutputStream()); +// pduList.add(((EntityStatePdu)newPdu).copy()); + + if (initialLocation == null) + { + initialEspdu = deepCopyEspdu; // must save object since pduList might contain more than ESPDUs + initialLocation = deepCopyEspdu.getEntityLocation(); + } + latestEspdu = deepCopyEspdu; // must save object since pduList might contain more than ESPDUs + latestLocation = deepCopyEspdu.getEntityLocation(); + } + else pduList.add(newPdu); // TODO copy() - careful, must be a new object and not a reference + return this; + } + /** + * clear all PDUs + * @return same object to permit progressive setters + */ + public PduTrack clearPduLists() + { + getPduList().clear(); + waypointsList.clear(); + eulerAnglesList.clear(); + timelineList.clear(); + initialEspdu = null; + latestEspdu = null; + initialLocation = null; + latestLocation = null; + return this; + } + + private String normalizeNameToken(String candidateDEF) + { + return candidateDEF.replace(" ", "").replace("-", "") + .replace("(", "").replace(")", "") + .replace("[", "").replace("])", ""); + } + + /** + * Provide DEF value if not defined by program + * @return current x3dTimeSensorDEF + */ + public String getX3dTimeSensorDEF() { + if (x3dTimeSensorDEF.isEmpty()) + x3dTimeSensorDEF = normalizeNameToken(getDescriptor()) + "Clock"; + return x3dTimeSensorDEF; + } + + /** + * Set DEF value for X3D node + * @param x3dTimeSensorDEF the x3dTimeSensorDEF to set + */ + public void setX3dTimeSensorDEF(String x3dTimeSensorDEF) { + this.x3dTimeSensorDEF = normalizeNameToken(x3dTimeSensorDEF); + } + + /** + * Provide DEF value if not defined by program + * @return current x3dPositionInterpolatorDEF + */ + public String getX3dPositionInterpolatorDEF() { + if (x3dPositionInterpolatorDEF.isEmpty()) + x3dPositionInterpolatorDEF = normalizeNameToken(getDescriptor()) + "Positions"; + return x3dPositionInterpolatorDEF; + } + + /** + * Set DEF value for X3D node + * @param x3dPositionInterpolatorDEF the x3dPositionInterpolatorDEF to set + */ + public void setX3dPositionInterpolatorDEF(String x3dPositionInterpolatorDEF) { + this.x3dPositionInterpolatorDEF = normalizeNameToken(x3dPositionInterpolatorDEF); + } + + /** + * Provide DEF value if not defined by program + * @return current x3dOrientationInterpolatorDEF + */ + public String getX3dOrientationInterpolatorDEF() { + if (x3dOrientationInterpolatorDEF.isEmpty()) + x3dOrientationInterpolatorDEF = normalizeNameToken(getDescriptor()) + "Orientations"; + return x3dOrientationInterpolatorDEF; + } + + /** + * Set DEF value for X3D node + * @param x3dOrientationInterpolatorDEF the x3dOrientationInterpolatorDEF to set + */ + public void setX3dOrientationInterpolatorDEF(String x3dOrientationInterpolatorDEF) { + this.x3dOrientationInterpolatorDEF = normalizeNameToken(x3dOrientationInterpolatorDEF); + } + + /** + * Sort all PDUs by timestamp + * @see <a href="https://stackoverflow.com/questions/16252269/how-to-sort-an-arraylist">StackOverflow: How to sort an ArrayList?</a> + * @see <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/doc-files/coll-overview.html">Collections Framework Overview</a> + * @return same object to permit progressive setters + */ + public PduTrack sortPdus() + { + Collections.sort(pduList, new Comparator<Pdu>() { + @Override + public int compare(Pdu lhs, Pdu rhs) + { + // -1 less than, 1 greater than, 0 equal + if (lhs.occursBefore(rhs)) + return -1; + else if (lhs.occursSameTime(rhs)) + return 0; + else return 1; + } + }); + return this; + } + /** + * Reverse order of PDU list + * @see <a href="https://stackoverflow.com/questions/10766492/what-is-the-simplest-way-to-reverse-an-arraylist">StackOverflow: What is the Simplest Way to Reverse an ArrayList?</a> + * @see <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/doc-files/coll-overview.html">Collections Framework Overview</a> + * @return same object to permit progressive setters + */ + public PduTrack reversePdus() + { + Collections.reverse(pduList); + return this; + } + + /** + * Determine whether any ESPDUs have been received by this track + * @return whether track is empty + */ + public boolean isTrackEmpty() + { + return (getEspduCount() == 0); + } + /** + * Count ESPDUs in pduList + * @return number of ESPDUs in pduList + */ + public int getEspduCount() + { + int counter = 0; + for (Pdu nextPdu : getPduList()) + { + if (nextPdu.getPduType() == DisPduType.ENTITY_STATE) + counter += 1; + } + return counter; + } + /** + * Compute track duration in timestamp ticks + * @return duration in timestamp ticks between initial and final ESPDU timestamps in waypointList + */ + public int getTotalDurationTicks() + { + int initialTime = -1; + int finalTime = -1; + int durationTicks = -1; // used if pduList is empty + + // must skip through pduList since non-ESPDU PDUs may be present + for (Pdu nextPdu : getPduList()) + { + if (nextPdu.getPduType() == DisPduType.ENTITY_STATE) + { + if (initialTime == -1) + initialTime = nextPdu.getTimestamp(); + finalTime = nextPdu.getTimestamp(); + } + } + if ((initialTime >= 0) && (finalTime >= 0)) + durationTicks = (finalTime - initialTime); + if (getPduList().isEmpty()) + { + System.out.println(TRACE_PREFIX + "getTrackDuration() computed illegal duration=" + durationTicks + " due to empty pdu list"); + } + else if ((durationTicks <= 0) && (defaultWaypointInterval <= 0)) + { + System.out.println(TRACE_PREFIX + "getTrackDuration() computed illegal duration=" + durationTicks + " due to illegal pdu list"); + } + return durationTicks; + } + /** + * Compute track duration in seconds + * @return duration in seconds between initial and final ESPDU timestamps in waypointList + */ + public float getTotalDurationSeconds() + { + if (defaultWaypointInterval > 0) + { + return getEspduCount() * defaultWaypointInterval; + } + else if (getTotalDurationTicks() < 0) + durationSeconds = getTotalDurationTicks() * 1.0f; // TODO convert + return durationSeconds; + } + /** + * Create waypoints and angles using all ESPDU points, with no linear regression or array reduction. + * @return same object to permit progressive setters + */ + public PduTrack createRawWaypoints() + { + // https://stackoverflow.com/questions/6536094/java-arraylist-copy + + timelineList.clear(); + waypointsList.clear(); + eulerAnglesList.clear(); + float clock = 0.0f; + for (int i = 0; i < pduList.size(); i++) + { + Pdu nextPdu = pduList.get(i); + if (nextPdu.getPduType() == DisPduType.ENTITY_STATE) + { + EntityStatePdu espdu = (EntityStatePdu)nextPdu; + if (defaultWaypointInterval > 0) + { + timelineList.add(clock); + clock += defaultWaypointInterval; + } + else + { + timelineList.add(espdu.getTimestamp() * 1.0f); // TODO convert + } + waypointsList.add(espdu.getEntityLocation()); + eulerAnglesList.add(espdu.getEntityOrientation()); + } + } + return this; + } + /** + * Utility method to create TimeSensor + * @return TimeSensor string in XML format + */ + public String createX3dTimeSensorString() + { + StringBuilder sb = new StringBuilder(); + sb.append(" <TimeSensor"); + sb.append(" DEF='").append(getX3dTimeSensorDEF()).append("'"); + sb.append(" cycleInterval='").append(String.valueOf(getTotalDurationSeconds())).append("'"); + sb.append(" loop='true'"); + sb.append("/>").append("\n"); + + return sb.toString(); + } + /** + * Create PositionInterpolator from Pdu list + * @return X3D PositionInterpolator as string + */ + public String createX3dPositionInterpolatorString() + { + StringBuilder sb = new StringBuilder(); + sb.append(" <PositionInterpolator"); + sb.append(" DEF='").append(getX3dPositionInterpolatorDEF()).append("'"); + sb.append(" key='"); + for (int i = 0; i < timelineList.size(); i++) + { + float nextDuration = timelineList.get(i) * 1.0f; // TODO convert + sb.append(String.valueOf(nextDuration)); + if (i < timelineList.size() - 1) + sb.append(" "); + } + sb.append("'"); + sb.append(" keyValue='"); + for (int i = 0; i < waypointsList.size(); i++) + { + if (hasAddLineBreaksWithinKeyValues()) + sb.append("\n"); + Vector3Double nextPosition = waypointsList.get(i); + sb.append(String.valueOf(nextPosition.getX())).append(" ") + .append(String.valueOf(nextPosition.getY())).append(" ") + .append(String.valueOf(nextPosition.getZ())); + if (i < waypointsList.size() - 1) + sb.append(","); + } + sb.append("'"); + sb.append("/>").append("\n"); + + return sb.toString(); + } + /** + * Create OrientationInterpolator from Pdu list + * TODO preliminary support only includes horizontal rotation. + * @return X3D OrientationInterpolator as string + */ + public String createX3dOrientationInterpolatorString() + { + StringBuilder sb = new StringBuilder(); + sb.append(" <OrientationInterpolator"); + sb.append(" DEF='").append(getX3dOrientationInterpolatorDEF()).append("'"); + sb.append(" key='"); + for (int i = 0; i < timelineList.size(); i++) + { + float nextDuration = timelineList.get(i) * 1.0f; // TODO convert + sb.append(String.valueOf(nextDuration)); + if (i < timelineList.size() - 1) + sb.append(" "); + } + sb.append("'"); + sb.append(" keyValue='"); + for (int i = 0; i < eulerAnglesList.size(); i++) + { + if (hasAddLineBreaksWithinKeyValues()) + sb.append("\n"); + EulerAngles nextEulerAngle = new EulerAngles(); + float axisX = 0.0f; + float axisY = 1.0f; + float axisZ = 0.0f; + float angle = 0.0f; // radians + + nextEulerAngle = eulerAnglesList.get(i); + angle = nextEulerAngle.getTheta(); + + sb.append(String.valueOf(axisX)).append(" ") + .append(String.valueOf(axisY)).append(" ") + .append(String.valueOf(axisZ)).append(" ") + .append(String.valueOf(angle)); + if (i < eulerAnglesList.size() - 1) + sb.append(","); + } + sb.append("'"); + sb.append("/>").append("\n"); + + return sb.toString(); + } + + /** + * Get name of author used as creator of X3D model + * @return current author + */ + public String getAuthor() { + return author; + } + + /** + * Set name of author used as creator of X3D model + * @param author the author to set + */ + public void setAuthor(String author) { + if (author == null) + author = new String(); + author = author.trim(); + this.author = author; + } + + /** + * Get name of online url identifier for X3D model + * @return current x3dModelIdentifier + */ + public String getX3dModelIdentifier() { + return x3dModelIdentifier; + } + + /** + * Set name of online url identifier for X3D model + * @param x3dModelIdentifier the x3dModelIdentifier to set + */ + public void setX3dModelIdentifier(String x3dModelIdentifier) { + if (x3dModelIdentifier == null) + x3dModelIdentifier = new String(); + x3dModelIdentifier = x3dModelIdentifier.trim(); + if (!x3dModelIdentifier.startsWith("http://") && !x3dModelIdentifier.startsWith("https://")) + System.out.println(TRACE_PREFIX + "warning, identifier typically begins with https:// or http://"); + else this.x3dModelIdentifier = x3dModelIdentifier; + } + + /** + * File name for X3D model production + * @return current x3dModelName + */ + public String getX3dModelName() { + return x3dModelName; + } + /** + * File name for X3D model production + * @param x3dModelName the x3dModelName to set + */ + public void setX3dModelName(String x3dModelName) { + if (x3dModelName == null) + x3dModelName = new String(); + x3dModelName = x3dModelName.trim(); + this.x3dModelName = x3dModelName; + } + + /** + * Verbose (but more readable) output of numeric arrays in X3D model + * @return current addLineBreaksWithinKeyValues + */ + public boolean hasAddLineBreaksWithinKeyValues() { + return addLineBreaksWithinKeyValues; + } + + /** + * Verbose (but more readable) output of numeric arrays in X3D model + * @param addLineBreaksWithinKeyValues the addLineBreaksWithinKeyValues to set + */ + public void setAddLineBreaksWithinKeyValues(boolean addLineBreaksWithinKeyValues) { + this.addLineBreaksWithinKeyValues = addLineBreaksWithinKeyValues; + } + /** + * Create full X3D interpolator model from Pdu list, assembling sections of scene graph + * @return X3D model as string + */ + public String createX3dModel() + { + StringBuilder sb = new StringBuilder(); + sb.append(createX3dModelHeaderString()); + sb.append(createX3dTimeSensorString()); + sb.append(createX3dPositionInterpolatorString()); + sb.append(createX3dOrientationInterpolatorString()); + sb.append(createX3dRoutesGeometryFooterString()); + return sb.toString(); + } + /** + * Create PositionInterpolator from Pdu list + * @return X3D PositionInterpolator as string + */ + public String createX3dModelHeaderString() + { + StringBuilder sb = new StringBuilder(); + + sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>").append("\n"); + sb.append("<!DOCTYPE X3D PUBLIC \"ISO//Web3D//DTD X3D 4.0//EN\" \"https://www.web3d.org/specifications/x3d-4.0.dtd\">").append("\n"); + sb.append("<X3D profile='Interchange' version='4.0' xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation='https://www.web3d.org/specifications/x3d-4.0.xsd'>").append("\n"); + sb.append(" <head>").append("\n"); + if (!getX3dModelName().isEmpty()) + sb.append(" <meta content='").append(getX3dModelName()).append("' name='title'/>").append("\n"); + sb.append(" <meta content='Conversion of ESPDU track into X3D animation interpolators and LineSet.' name='description'/>").append("\n"); + + sb.append(" <meta content='1 January 2022' name='created'/>").append("\n"); + sb.append(" <meta content='").append(todaysDateString).append("' name='modified'/>").append("\n"); + if (!getAuthor().isEmpty()) + sb.append(" <meta content='").append(getAuthor()).append("' name='creator'/>").append("\n"); + if (!getX3dModelIdentifier().isEmpty()) + sb.append(" <meta content='").append(getX3dModelIdentifier()).append("' name='identifier'/>").append("\n"); + + sb.append(" <meta content='PduTrack utility, opendis7-java Library https://github.com/open-dis/opendis7-java' name='generator'/>").append("\n"); + sb.append(" <meta content='NPS MOVES MV3500 Networked Graphics https://gitlab.nps.edu/Savage/NetworkedGraphicsMV3500' name='reference'/>").append("\n"); + sb.append(" <meta content='X3D Resources https://www.web3d.org/x3d/content/examples/X3dResources.html' name='reference'/>").append("\n"); + sb.append(" <meta content='X3D Scene Authoring Hints https://www.web3d.org/x3d/content/examples/X3dSceneAuthoringHints.html' name='reference'/>").append("\n"); + sb.append(" <meta content='X3D Tooltips https://www.web3d.org/x3d/tooltips/X3dTooltips.html' name='reference'/>").append("\n"); + sb.append(" <meta content='X3D Validator https://savage.nps.edu/X3dValidator' name='reference'/>").append("\n"); + sb.append(" <meta content='Open source https://raw.githubusercontent.com/open-dis/opendis7-java/master/license.html' name='license'/>").append("\n"); + sb.append(" </head>").append("\n"); + sb.append(" <Scene>").append("\n"); + sb.append(" <WorldInfo title='PduTrackInterpolation.x3d'/>").append("\n"); + + return sb.toString(); + } + /** + * Create X3D ROUTEs and footer to connect TimeSensor to interpolators + * @return X3D PositionInterpolator as string + */ + public String createX3dRoutesGeometryFooterString() + { + StringBuilder sb = new StringBuilder(); + + sb.append(" <ROUTE fromField='fraction_changed' fromNode='") + .append(getX3dTimeSensorDEF()) + .append("' toField='set_fraction' toNode='") + .append(getX3dPositionInterpolatorDEF()) + .append("'/>").append("\n"); + sb.append(" <ROUTE fromField='fraction_changed' fromNode='") + .append(getX3dTimeSensorDEF()) + .append("' toField='set_fraction' toNode='") + .append(getX3dOrientationInterpolatorDEF()) + .append("'/>").append("\n"); + + sb.append(" <Shape>").append("\n"); + sb.append(" <Appearance DEF='TrackAppearance'>").append("\n"); + sb.append(" <Material emissiveColor='0.2 0.8 0.8'/>").append("\n"); + sb.append(" </Appearance>").append("\n"); + sb.append(" <LineSet vertexCount='").append(waypointsList.size()).append("'>").append("\n"); + sb.append(" <Coordinate point='"); + for (int i = 0; i < waypointsList.size(); i++) + { + if (hasAddLineBreaksWithinKeyValues()) + sb.append("\n"); + Vector3Double nextPosition = waypointsList.get(i); + sb.append(String.valueOf(nextPosition.getX())).append(" ") + .append(String.valueOf(nextPosition.getY())).append(" ") + .append(String.valueOf(nextPosition.getZ())); + if (i < waypointsList.size() - 1) + sb.append(","); + } + sb.append("'/>").append("\n"); + sb.append(" </LineSet>").append("\n"); + sb.append(" </Shape>").append("\n"); + sb.append(" <Transform DEF='AnimationTransform'>").append("\n"); + sb.append(" <Transform rotation='0 0 1 1.57'>").append("\n"); + sb.append(" <Shape>").append("\n"); + sb.append(" <Appearance USE='TrackAppearance'/>").append("\n"); + sb.append(" <Cone bottomRadius='0.5'/>").append("\n"); + sb.append(" </Shape>").append("\n"); + sb.append(" </Transform>").append("\n"); + sb.append(" </Transform>").append("\n"); + sb.append(" <ROUTE fromField='value_changed' fromNode='") + .append(getX3dPositionInterpolatorDEF()) + .append("' toField='translation' toNode='AnimationTransform'/>").append("\n"); + sb.append(" <ROUTE fromField='value_changed' fromNode='") + .append(getX3dOrientationInterpolatorDEF()) + .append("' toField='rotation' toNode='AnimationTransform'/>").append("\n"); + + sb.append(" </Scene>").append("\n"); + sb.append("</X3D>").append("\n"); + + return sb.toString(); + } + + /** + * get defaultWaypointInterval + * @return current wayPointInterval + */ + public float getDefaultWaypointInterval() { + return defaultWaypointInterval; + } + + /** + * Set uniform waypoint interval (currently for testing) + * @param newWaypointInterval the wayPointInterval to set, in seconds, must be greater than zero + * @return same object to permit progressive setters */ + public PduTrack setDefaultWaypointInterval(float newWaypointInterval) { + if (newWaypointInterval > 0.0) + this.defaultWaypointInterval = newWaypointInterval; + else + { + System.out.println(TRACE_PREFIX + "error in setWaypointInterval(newWaypointInterval=" + newWaypointInterval + ") must be greater than zero"); + return this; + } + + float clock = 0.0f; + if (!timelineList.isEmpty()) + { + ArrayList<Float> newTimelineList = new ArrayList<>(); + for (int i = 0; i < getEspduCount(); i++) + { + newTimelineList.add(clock); + clock += defaultWaypointInterval; + } + timelineList = newTimelineList; // TO Array copy? + } + return this; + } + /** whether or not to insert commas between hex values */ + private boolean insertCommas = true; + /** + * determine whether comma insertion is turned on + * @return whether or not to insert commas between hex values + */ + public boolean hasInsertCommas() { + return insertCommas; + } + /** + * set whether comma insertion is turned on + * @param insertCommas the insertCommas value to set + */ + public void setInsertCommas(boolean insertCommas) { + this.insertCommas = insertCommas; + } + /** + * Convert byte array to hex string + * @param bytes input data + * @param insertCommas whether to insert commas between hex values + * @return hex string + */ + public String bytesToHex(byte[] bytes, boolean insertCommas) + { + this.setInsertCommas(insertCommas); + return bytesToHex(bytes); + } + /** + * Convert byte array to hex string + * @param bytes input data + * @see <a href="https://stackoverflow.com/questions/9655181/how-to-convert-a-byte-array-to-a-hex-string-in-java">https://stackoverflow.com/questions/9655181/how-to-convert-a-byte-array-to-a-hex-string-in-java</a> + * @return hex string + */ + public static String bytesToHex(byte[] bytes) + { + boolean insertCommas = true; + final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); + char[] hexChars = new char[bytes.length * 2]; + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = HEX_ARRAY[v >>> 4]; + hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; +// if (!(hexChars[j * 2] == '0')) // omit leading zero + sb.append(hexChars[j * 2]); + sb.append(hexChars[j * 2 + 1]); + if (insertCommas && (j < bytes.length - 1)) + sb.append(", "); + } + return sb.toString(); + } + /** + * Report current PDU information to console + * @param anEspdu EntityStatePdu of interest + * @return same object to permit progressive setters + */ + public PduTrack reportPdu(EntityStatePdu anEspdu) + { + System.out.println ( + String.format("%s", anEspdu.getMarkingString().trim()) + ", " + + DisTime.convertToString(anEspdu.getTimestamp()) + " (" + + String.format("%08d", anEspdu.getTimestamp()) + "), " + + "EntityID=(" + + anEspdu.getEntityID().getSiteID() + ", " + + anEspdu.getEntityID().getApplicationID() + ", " + + anEspdu.getEntityID().getEntityID() + "), " + + "location=(" + + String.format("%4.1f", anEspdu.getEntityLocation().getX()) + ", " + + String.format("%4.1f", anEspdu.getEntityLocation().getY()) + ", " + + String.format("%4.1f", anEspdu.getEntityLocation().getZ()) + ")" +// + " " + espdu_1.getEntityLinearVelocity().toString() + ); + return this; + } + + /** Flush all buffers to reduce console scrambling while threaded + */ + protected void flushBuffers() + { + try + { + dataOutputStream.flush(); + byteArrayOutputStream.flush(); + byteArrayOutputStream.reset(); + System.err.flush(); + System.out.flush(); + } + catch (IOException ioe) + { + System.out.println(TRACE_PREFIX + "flushBuffers() IOException: " + ioe.getMessage()); + } + } + + /** Self test to check basic operation, invoked by main() + */ + @SuppressWarnings("SleepWhileInLoop") + public void selfTest() + { + final int TOTAL_PDUS = 5; + System.out.println(TRACE_PREFIX + "selfTest() start..."); + + PduTrack pduTrack = new PduTrack(); + pduTrack.setDescriptor("PduTrack Self Test"); + pduTrack.setAuthor("Don Brutzman"); + pduTrack.setX3dModelIdentifier("https://gitlab.nps.edu/Savage/NetworkedGraphicsMV3500/-/blob/master/examples/src/OpenDis7Examples/PduTrackInterpolation.x3d"); + pduTrack.setDefaultWaypointInterval(1.0f); // experimentation with timestamp values + + DisTime.setEpochLvcNow(); + recordingStart = LocalDateTime.now(); + Instant epoch = DisTime.getEpochLvc(); + System.out.println(TRACE_PREFIX + "DisTime.hasEpochLvc()=" + DisTime.hasEpochLvc() + + ", DisTime.getEpochLvc()=" + epoch + + ", Instant.now()=" + Instant.now()); + + EntityID entityID_123 = new EntityID(); + entityID_123.setSiteID(1).setApplicationID(2).setEntityID(3); // made-up example ID; + // TODO someday, use enumerations; is there a unique site triplet for MOVES Institute? + + for (int i = 0; i < TOTAL_PDUS; i++) // create espdus and add each to track pduList + { +// EntityStatePdu espdu = new EntityStatePdu(); + EntityStatePdu espdu = pduFactory.makeEntityStatePdu(); // TODO check Pdu.Type + espdu.setTimestamp(DisTime.getCurrentDisTimestamp()); // chooses appropriate version + espdu.setMarking("ESPDU " + i); + espdu.setEntityLocation(i, i, i); + espdu.setEntityOrientation(0, (float)(45.0 * Math.PI / 180.0), 0); + espdu.setEntityID(entityID_123); + espdu.setForceId(ForceID.FRIENDLY); + espdu.setEntityType(new _001Poseidon()); // note import statement above + pduTrack.addPdu(espdu); // create copy + reportPdu(espdu); + try + { + Thread.sleep(100l); + } + catch (InterruptedException ie) + { + System.out.println(TRACE_PREFIX + "exceptional sleep dulay when generating ESPDUs: " + ie.getMessage()); + } + } +// System.out.println(TRACE_PREFIX + "reversePdus() then sortPdus() to test track operations"); +// pduTrack.reversePdus(); // test +// pduTrack.sortPdus(); // restore + pduTrack.createRawWaypoints(); // copies all ESPDU points to waypoints + + System.out.println(TRACE_PREFIX + "getEspduCount()=" + pduTrack.getEspduCount()); + System.out.println(TRACE_PREFIX + "getDefaultWaypointInterval()=" + pduTrack.getDefaultWaypointInterval()); + System.out.println(TRACE_PREFIX + "getTotalDurationSeconds()=" + pduTrack.getTotalDurationSeconds()); + + System.out.println("================================="); + System.out.println("PduTrack pduList marshalling checks"); + System.out.println("= = = = = = = = = = = = = = = = ="); + try + { +// int BYTE_BUFFER_SIZE = 400; // TODO what is expected max buffer size? + for (int i = 0; i < TOTAL_PDUS; i++) + { + Pdu pdu = pduTrack.getPduList().get(i); + if (!(pdu instanceof EntityStatePdu)) + continue; // skip remainder of this loop + EntityStatePdu espdu = (EntityStatePdu) pdu; + System.out.println("espdu from pduTrack pduList"); + reportPdu(espdu); + byte[] byteArray = espdu.marshal(); + System.out.println("espdu.marshal() byteArray: " + bytesToHex(byteArray)); + flushBuffers(); + + ByteBuffer byteBuffer = ByteBuffer.allocate(byteArray.length); + espdu.marshal(byteBuffer); + System.out.println("espdu.marshal(byteBuffer): " + bytesToHex(byteBuffer.array())); + flushBuffers(); + + espdu.marshal(dataOutputStream); + byte[] byteArrayDOS = byteArrayOutputStream.toByteArray(); + System.out.println("espdu.marshal(dataOutputStream): " + bytesToHex(byteArrayDOS)); + flushBuffers(); + + System.out.println(); // - - - - - - - - - - - - - - - - - + + System.out.println("espdu.copyByteBuffer()"); + reportPdu(espdu.copyByteBuffer()); + byte[] byteArrayCopy = espdu.copyByteBuffer().marshal(); + System.out.println("espdu.copyByteBuffer().marshal() byteArray: " + bytesToHex(byteArrayCopy)); + flushBuffers(); + + ByteBuffer byteBufferCopy = ByteBuffer.allocate(byteArray.length); // TODO is there a better way to reset? + espdu.copyByteBuffer().marshal(byteBufferCopy); + System.out.println("espdu.copyByteBuffer().marshal(byteBufferCopy): " + bytesToHex(byteBufferCopy.array())); + flushBuffers(); + + espdu.copyByteBuffer().marshal(dataOutputStream); + byte[] byteArrayDosCopy = byteArrayOutputStream.toByteArray(); + System.out.println("espdu.copyByteBuffer().marshal(dataOutputStream): " + bytesToHex(byteArrayDosCopy)); + flushBuffers(); + + System.out.println(); // - - - - - - - - - - - - - - - - - + + System.out.println("espdu.copyDataOutputStream()"); + reportPdu(espdu.copyDataOutputStream()); + byte[] byteArrayCopyDOS = espdu.copyDataOutputStream().marshal(); + System.out.println("espdu.copyDataOutputStream().marshal() byteArray: " + bytesToHex(byteArrayCopyDOS)); + flushBuffers(); + + ByteBuffer byteBufferCopyDOS = ByteBuffer.allocate(byteArray.length); // TODO is there a better way to reset? + espdu.copyDataOutputStream().marshal(byteBufferCopyDOS); + System.out.println("espdu.copyDataOutputStream().marshal(byteBufferCopy): " + bytesToHex(byteBufferCopyDOS.array())); + flushBuffers(); + + espdu.copyDataOutputStream().marshal(dataOutputStream); + byte[] byteArrayDosCopy2 = byteArrayOutputStream.toByteArray(); + System.out.println("espdu.copyDataOutputStream().marshal(dataOutputStream): " + bytesToHex(byteArrayDosCopy2)); + flushBuffers(); + System.out.println(); + + System.out.println("= = = = = = = = = = = = = = = = ="); + } + } + catch(Exception e) + { + System.out.println(TRACE_PREFIX + "Marshalling test exception: " + e.getMessage()); + } + System.out.println("================================="); + pduTrack.setAddLineBreaksWithinKeyValues(true); + System.out.println(pduTrack.createX3dModel()); // + System.out.println("================================="); + + recordingStop = LocalDateTime.now(); + System.out.println(TRACE_PREFIX + "selfTest() complete."); + } + + /** + * Main method for testing. + * @see <a href="https://docs.oracle.com/javase/tutorial/getStarted/application/index.html">Java Tutorials: A Closer Look at the "Hello World!" Application</a> + * @param args [address, port, descriptor] command-line arguments are an array of optional String parameters that are passed from execution environment during invocation + */ + public static void main(String[] args) + { + System.out.println("*** PduTrack.main() self test started..."); + + PduTrack pduTrack = new PduTrack(); + + pduTrack.setDescriptor("main() self test"); + + pduTrack.selfTest(); + + System.out.println("*** PduTrack.main() self test complete."); + } + +} diff --git a/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/TamTcpExampleClient.java b/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/TamTcpExampleClient.java deleted file mode 100644 index 05157977aa8db7a52194779fff949a180b893e4b..0000000000000000000000000000000000000000 --- a/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/TamTcpExampleClient.java +++ /dev/null @@ -1,89 +0,0 @@ -package MV3500Cohort2022MayJune.homework2.Tam; -import MV3500Cohort2022MayJune.homework2.Duran.*; -import MV3500Cohort2021JulySeptember.homework2.Domonique.*; -import java.io.*; -import java.net.*; - -/** - * - * @author Johanna Tam - */ -public class TamTcpExampleClient { - - /** IPv6 String constant for localhost address, similarly IPv4 127.0.0.1 - * @see <a href="https://en.wikipedia.org/wiki/localhost">https://en.wikipedia.org/wiki/localhost</a> - * @see <a href="https://en.wikipedia.org/wiki/IPv6_address">https://en.wikipedia.org/wiki/IPv6_address</a> - */ - public final static String LOCALHOST = "0:0:0:0:0:0:0:1"; - - /** - * Program invocation, execution starts here - * @param args command-line arguments - * @throws java.lang.InterruptedException user can cancel execution - */ - public static void main(String[] args) throws InterruptedException { - - // Local variables/fields - Socket socket = null; - InputStream is; - Reader isr; - BufferedReader br; - String serverMessage; - int clientLoopCount = 0; - - try { - while (true) - { - clientLoopCount++; // increment at beginning of loop for reliability - System.out.println(TamTcpExampleClient.class.getName() + " creating socket..."); - - // We request an IP to connect to ("localhost") and - // port number at that IP (2317). This establishes - // a connection to that IP in the form of a Socket - // object; the server uses a ServerSocket to wait for - // connections. - socket = new Socket(LOCALHOST, 2317); // locohost? - - // Now hook everything up (i.e. set up the streams), Java style: - is = socket.getInputStream(); - isr = new InputStreamReader(is); - br = new BufferedReader(isr); - - // Read a single line written by the server. We'd - // do things a bit differently if there were many lines to be read - // from the server instead of one only. - serverMessage = br.readLine(); - System.out.println("=================================================="); - -// System.out.print ("Client loop " + clientLoopCount + ": "); - System.out.println("I am hungry"); - System.out.println("The message the server sent was: '" + serverMessage + "'"); - // socket gets closed, either automatically/silently by this code (or possibly by the server) - - Thread.sleep(500l); // slow things down, for example 500l (long) = 500 msec (1/2 second) - - } // end while(true) // infinite loops are dangerous, be sure to kill this process! - } - catch (IOException e) - { - System.err.println("Problem with " + TamTcpExampleClient.class.getName() + " networking:"); // describe what is happening - System.err.println("Error: " + e); - - // Provide more helpful information to user if exception occurs due to running twice at one time - if (e instanceof java.net.BindException) { - System.err.println("*** Be sure to stop any other running instances of programs using this port!"); - } - } - finally // occurs after any other activity when shutting down - { - try { - if (socket != null) - socket.close(); - } catch (IOException e) {} - - // program exit: tell somebody about that happening. Likely cause: server drops connection. - System.out.println(); - System.out.println(TamTcpExampleClient.class.getName() + " exit"); - } - } -} \ No newline at end of file diff --git a/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/TamTcpExampleServer.java b/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/TamTcpExampleServer.java deleted file mode 100644 index 18154a40ec224319c2be33d7d4f5c8425f0e5158..0000000000000000000000000000000000000000 --- a/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/TamTcpExampleServer.java +++ /dev/null @@ -1,90 +0,0 @@ -package MV3500Cohort2022MayJune.homework2.Tam; - -import MV3500Cohort2022MayJune.homework2.Duran.*; -import MV3500Cohort2021JulySeptember.homework2.Domonique.*; -import java.io.*; -import java.net.*; - -/** - * homework assignment - * @author Johanna Tam - */ -public class TamTcpExampleServer { - - /** - * Program invocation, execution starts here - * If already compiled, can run using console in directory ../../build/classes/ by invoking \ - * java -classpath . TcpExamples.TcpExample3Server - * @param args command-line arguments - */ - public static void main(String[] args) { - try { - - // ServerSocket waits for a connection from a client. - // Notice that it is outside the loop; ServerSocket - // needs to be made only once. - System.out.println(TamTcpExampleServer.class.getName() + " has started..."); // it helps debugging to put this on console first - - ServerSocket serverSocket = new ServerSocket(2317); - OutputStream os; - PrintStream ps; - InetAddress localAddress, remoteAddress; - int localPort, remotePort; - int serverLoopCount = 0; - - // Server is up and waiting (i.e. "blocked" or paused) - // Loop, infinitely, waiting for client connections. - // Stop the program somewhere else. - while (true) { - - // block until connected to a client - try (Socket clientConnectionSocket = serverSocket.accept()) - { - serverLoopCount++; // increment at beginning of loop for reliability - - // Now hook everything up (i.e. set up the streams), Java style: - os = clientConnectionSocket.getOutputStream(); - ps = new PrintStream(os); - ps.println("okay " + serverLoopCount + " let's go to Starbucks"); // this gets sent back to client! - - // Print some information locally about the Socket connection. - // This includes the port and IP numbers on both sides (the socket pair). - localAddress = clientConnectionSocket.getLocalAddress(); - remoteAddress = clientConnectionSocket.getInetAddress(); - localPort = clientConnectionSocket.getLocalPort(); - remotePort = clientConnectionSocket.getPort(); - - System.out.print ("Server loop " + serverLoopCount + ": "); - - // My socket pair connection looks like this, to localhost: - // Socket pair: (( /0:0:0:0:0:0:0:1, 2317 ), ( /0:0:0:0:0:0:0:1, 54876 )) - // Socket pair: (( /0:0:0:0:0:0:0:1, 2317 ), ( /0:0:0:0:0:0:0:1, 54881 )) - - // Why is the first IP/port the same, while the second set has different ports? - System.out.println(TamTcpExampleServer.class.getName() + " socket pair showing host name, address, port:"); - System.out.println(" (( " + - localAddress.getHostName() + "=" + localAddress.getHostAddress() + ", " + localPort + " ), ( " + - remoteAddress.getHostName() + "=" + remoteAddress.getHostAddress() + ", " + remotePort + " ))"); - - if ( localAddress.getHostName().equals( localAddress.getHostAddress()) || - remoteAddress.getHostName().equals(remoteAddress.getHostAddress())) - System.out.println(" note HostName matches address if host has no DNS name"); - - // Notice the use of flush() and try w/ resources. Without - // the try w/ resources the Socket object may stay open for - // a while after the client has stopped needing this - // connection. try w/ resources explicitly ends the connection. - ps.flush(); - // like it or not, you're outta here! - } - } - } catch (IOException e) { - System.err.println("Problem with " + TamTcpExampleServer.class.getName() + " networking: " + e); - - // Provide more helpful information to user if exception occurs due to running twice at one time - if (e instanceof java.net.BindException) { - System.err.println("*** Be sure to stop any other running instances of programs using this port!"); - } - } - } -} diff --git a/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/package-info.java b/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/package-info.java deleted file mode 100644 index 76d553f312e083d8f2c34650e0ce660cd13e0953..0000000000000000000000000000000000000000 --- a/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/package-info.java +++ /dev/null @@ -1,10 +0,0 @@ -/** - * ExampleSimpleSimulation program-modification homework assignment supporting the NPS MOVES MV3500 Networked Graphics course. - * - * @see <a href="https://gitlab.nps.edu/Savage/NetworkedGraphicsMV3500/-/tree/master/assignments">networkedGraphicsMV3500 assignments</a> - * @see java.lang.Package - * @see <a href="https://stackoverflow.com/questions/22095487/why-is-package-info-java-useful">StackOverflow: why-is-package-info-java-useful</a> - * @see <a href="https://stackoverflow.com/questions/624422/how-do-i-document-packages-in-java">StackOverflow: how-do-i-document-packages-in-java</a> - */ - -package MV3500Cohort2022MayJune.homework2.Tam;