/** * Copyright (c) 2008-2021, 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 * * */ package MV3500Cohort2021JulySeptember.homework3.Frank; import edu.nps.moves.dis7.enumerations.Country; import edu.nps.moves.dis7.enumerations.EntityKind; import edu.nps.moves.dis7.enumerations.MunitionDescriptorFuse; import edu.nps.moves.dis7.enumerations.PlatformDomain; import edu.nps.moves.dis7.enumerations.VariableRecordType; import edu.nps.moves.dis7.pdus.CommentPdu; import edu.nps.moves.dis7.pdus.CommentReliablePdu; import edu.nps.moves.dis7.pdus.Domain; import edu.nps.moves.dis7.pdus.EntityID; import edu.nps.moves.dis7.pdus.EntityStatePdu; import edu.nps.moves.dis7.pdus.EntityType; import edu.nps.moves.dis7.pdus.EulerAngles; import edu.nps.moves.dis7.pdus.FirePdu; import edu.nps.moves.dis7.pdus.MunitionDescriptor; import edu.nps.moves.dis7.pdus.Pdu; import edu.nps.moves.dis7.pdus.Vector3Double; import edu.nps.moves.dis7.utilities.CoordinateConversions; import edu.nps.moves.dis7.utilities.DisThreadedNetworkInterface; import edu.nps.moves.dis7.utilities.PduFactory; import edu.nps.moves.dis7.utilities.stream.PduRecorder; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; /** * The purpose of this program is to have M1Abrams Destroy A Russian T62 Tank. * simulation program that includes DIS-capable entities doing tasks and * reporting them to the network. Default settings include PDU recording turned * on by default. */ public class FrankAssignment3Simulation { private EntityID createM1Abrams() { EntityID M1AbramsID = new EntityID(); // 1.1.225.1.1.1 Platform,Ground,USA,ArmoredFightingVehicle M1 Abrams M1AbramsID.setSiteID(1); M1AbramsID.setApplicationID(13); M1AbramsID.setEntityID(25); return M1AbramsID; } private EntityType createM1AbramsType() { EntityType M1AbramsType = new EntityType(); M1AbramsType.setEntityKind(EntityKind.PLATFORM); M1AbramsType.setDomain(Domain.inst(PlatformDomain.LAND)); M1AbramsType.setCountry(Country.UNITED_STATES_OF_AMERICA_USA); M1AbramsType.setCategory(1); M1AbramsType.setSubCategory(1); M1AbramsType.setSpecific(1); return M1AbramsType; } private Vector3Double createLocationVector() { Vector3Double eloc2 = new Vector3Double(); double[] loc2 = CoordinateConversions.getXYZfromLatLonDegrees(36.599831, -121.878842, 0); //sloat delmonte intersection eloc2.setX(loc2[0]); eloc2.setY(loc2[1]); eloc2.setZ(loc2[2]); return eloc2; } private EulerAngles createOrientation() { EulerAngles orient2 = new EulerAngles(); orient2.setPhi((float) 0.0); orient2.setPsi((float) 0.0); orient2.setTheta((float) 0.0); return orient2; } private EntityID createT62ID() { EntityID T62ID = new EntityID();//1.1.45.1.7.1 Platform,Ground,China, Tank, T-62 T62ID.setSiteID(1); T62ID.setApplicationID(13); T62ID.setEntityID(2); return T62ID; } private EntityType createT62Type() { EntityType T62Type = new EntityType(); T62Type.setEntityKind(EntityKind.PLATFORM); T62Type.setDomain(Domain.inst(PlatformDomain.LAND)); T62Type.setCountry(Country.RUSSIA); T62Type.setCategory(2); T62Type.setSubCategory(41); T62Type.setSpecific(3); return T62Type; } private Vector3Double createEnemyLocation() { Vector3Double eloc1 = new Vector3Double(); double[] loc1 = CoordinateConversions.getXYZfromLatLonDegrees(36.594116, -121.877463, 0); //NPS Main Gate eloc1.setX(loc1[0]); eloc1.setY(loc1[1]); eloc1.setZ(loc1[2]); return eloc1; } private EulerAngles createEnemyOrientation() { EulerAngles orient1 = new EulerAngles(); orient1.setPhi((float) 3.1415); orient1.setPsi((float) 0.0); orient1.setTheta((float) 0.0); return orient1; } private MunitionDescriptor createM829IT(){ EntityType M829Type = new EntityType(); //2.2.225.2.13.1 M829Type.setEntityKind(EntityKind.MUNITION); M829Type.setDomain(Domain.inst(PlatformDomain.AIR)); M829Type.setCountry(Country.UNITED_STATES_OF_AMERICA_USA); M829Type.setCategory(2); M829Type.setSubCategory(13); M829Type.setSpecific(1); MunitionDescriptor M829IT = new MunitionDescriptor(); M829IT.setMunitionType(M829Type); M829IT.setQuantity(3); M829IT.setFuse(MunitionDescriptorFuse.CONTACT_GRAZE); M829IT.setRate(200); return M829IT; } /** * This runSimulation() method is for you, 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 public void runSimulation() { try { /** * seconds for real-time execution (not simulation time, which may * or may not be the same) */ final double SIMULATION_LOOP_DURATION_SECONDS = 2.0; final int SIMULATION_MAX_LOOP_COUNT = 10; // be deliberate out 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? boolean fireBool = false; boolean destBool = false; EntityID entityID_1 = new EntityID(); 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? EntityStatePdu entityStatePdu_1 = pduFactory.makeEntityStatePdu(); entityStatePdu_1.setEntityID(entityID_1); // M1Abrams entityStatePdu_1.setEntityID(createM1Abrams()); // Calling createM1Abrams Method entityStatePdu_1.setEntityType(createM1AbramsType()); // Calling createM1AbramsType Method // Location and Orientation Vector3Double eloc2 = createLocationVector(); // Calling createLocationVector Method entityStatePdu_1.setEntityLocation(eloc2); entityStatePdu_1.setEntityOrientation(createOrientation()); // Calling Create Orientation Method EntityStatePdu entityStatePdu2 = pduFactory.makeEntityStatePdu(); // //T-62 entityStatePdu2.setEntityID(createT62ID()); // Calling Create T62 ID method entityStatePdu2.setEntityType(createT62Type()); // Calling Create T62 Type method // enemy location and orientation Vector3Double eloc1 = createEnemyLocation(); // calling create enemylocation method entityStatePdu2.setEntityLocation(eloc1); // Setting em entityStatePdu2.setEntityOrientation(createEnemyOrientation()); //calling create Enemy orientation method int T62HitsReceived = 0; System.out.println(eloc2.toString()); System.out.println(eloc1.toString()); //FirePdu firePduNull = new FirePdu(); FirePdu firePdu = pduFactory.makeFirePdu(); EntityID fireID = new EntityID(); fireID.setSiteID(1); fireID.setApplicationID(13); fireID.setEntityID(25); EntityID targetID = new EntityID(); targetID.setSiteID(1); targetID.setApplicationID(13); targetID.setEntityID(2); firePdu.setFiringEntityID(fireID); firePdu.setTargetEntityID(targetID); firePdu.setDescriptor(createM829IT()); // calling create M829IT Method EntityID M829ID = new EntityID(); M829ID.setEntityID(1); firePdu.setMunitionExpendibleID(M829ID); CommentReliablePdu T62DestroyedComment = pduFactory.makeCommentReliablePdu("T62 DESTROYED BY M1 Abrams AFTER 2 rounds M829I-T ON TARGET"); CommentReliablePdu T62SightedComment = pduFactory.makeCommentReliablePdu("M1 Abrams Acquires Target - T62 with in firing distance"); //if(eloc1.getX()) EntityID MTVRID = new EntityID(); FirePdu firePdu_1a = pduFactory.makeFirePdu(); // for entity 1 first weapon (if any) while (simulationLoopCount < SIMULATION_MAX_LOOP_COUNT) // are we done yet? { simulationLoopCount++; // good practice: increment loop counter as first action in that loop // Where is my entity? entityStatePdu_1.getEntityLocation().setX(entityStatePdu_1.getEntityLocation().getX() - 20); // 1m per timestep entityStatePdu_1.getEntityLocation().setY(entityStatePdu_1.getEntityLocation().getY() - 75); // decide whether to fire, and then update the firePdu. Hmmm, you might want a target to shoort at! Double dx = eloc2.getX() - eloc1.getX(); Double dy = eloc2.getY() - eloc1.getY(); Double dz = eloc2.getZ() - eloc1.getZ(); Double range = Math.sqrt(dx * dx + dy * dy); System.out.println("range" + range + " dx:" + dx + " dy:" + dy); if (range < 100) { // Range 100 if (!fireBool) { sendSinglePdu(T62SightedComment); } fireBool = true; System.out.println("Entity#" + firePdu.getFiringEntityID().getEntityID() + " is firing " + firePdu.getDescriptor().getMunitionType().getDomain() + "." + firePdu.getDescriptor().getMunitionType().getCountry() + "." + firePdu.getDescriptor().getMunitionType().getCategory() + "." + firePdu.getDescriptor().getMunitionType().getSubCategory() + "." + firePdu.getDescriptor().getMunitionType().getSpecific() + "." + " at Entity#" + firePdu.getTargetEntityID().getEntityID()); if (firePdu.getTargetEntityID().getEntityID() == 2) { T62HitsReceived += 1; if (T62HitsReceived > 1) { // The M1 destroyst the T62 System.out.println("M1 Abrams destroys T62 after " + T62HitsReceived + "rounds hit T62 Russian Tank"); narrativeMessage4 = "Destroyed T62"; destBool = true; simulationComplete = true; } } } if (simulationLoopCount > 4) // for example { simulationComplete = true; } entityStatePdu_1.getEntityLocation().setX(entityStatePdu_1.getEntityLocation().getX() + 1.0); // 1m per timestep narrativeMessage1 = "MV3500 FrankAssignment3Simulation"; narrativeMessage2 = "runSimulation() loop " + simulationLoopCount; narrativeMessage3 = ""; // intentionally blank for testing // your loop termination condition goes here if (simulationLoopCount > 10) // 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) (SIMULATION_LOOP_DURATION_SECONDS * 1000)); // seconds * (1000 msec/sec) = milliseconds System.out.println("... [Pausing for " + SIMULATION_LOOP_DURATION_SECONDS + " 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"); sendAllPdusForLoopTimestep(entityStatePdu_1, firePdu_1a, timeStepComment, narrativeMessage1, narrativeMessage2, narrativeMessage3); System.out.println("... [PDUs successfully sent for this loop]"); // =============================== // 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("... [Termination condition met, simulationComplete=" + simulationComplete + "]"); // ", final loopCount=" + loopCount + 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 } catch (InterruptedException iex) // handle any exception that your code might choose to provoke! { Logger.getLogger(FrankAssignment3Simulation.class.getName()).log(Level.SEVERE, null, iex); } catch (Exception ex) { } /* **************************** infrastructure code, modification is seldom needed ************************* */ } private boolean verboseComments = true; String narrativeMessage1 = new String(); String narrativeMessage2 = new String(); String narrativeMessage3 = new String(); String narrativeMessage4 = 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 timeStepComment = VariableRecordType.APPLICATION_TIMESTEP; VariableRecordType otherComment = VariableRecordType.OTHER; /** * Output prefix to identify this class, helps with logging */ private final static String TRACE_PREFIX = "[" + FrankAssignment3Simulation.class.getName() + "] "; // class variables PduFactory pduFactory = new PduFactory(); DisThreadedNetworkInterface disNetworkInterface; DisThreadedNetworkInterface.PduListener pduListener; Pdu receivedPdu; static final String NETWORK_ADDRESS_DEFAULT = "239.1.2.3"; static final int NETWORK_PORT_DEFAULT = 3000; static String networkAddress = NETWORK_ADDRESS_DEFAULT; static int networkPort = NETWORK_PORT_DEFAULT; /** * Constructor design goal: additional built-in initialization conveniences * can go here to keep student efforts focused on the runSimulation() * method. */ public FrankAssignment3Simulation() { // Constructor is under consideration. Constructor is not currently needed. } /** * 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 FrankAssignment3Simulation(String address, int port) { setNetworkAddress(address); setNetworkPort(port); } /** * @return the networkAddress */ public String getNetworkAddress() { return networkAddress; } /** * @param newNetworkAddress the networkAddress to set */ public final void setNetworkAddress(String newNetworkAddress) { FrankAssignment3Simulation.networkAddress = newNetworkAddress; } /** * @return the networkPort */ public int getNetworkPort() { return networkPort; } /** * @param newNetworkPort the networkPort to set */ public final void setNetworkPort(int newNetworkPort) { FrankAssignment3Simulation.networkPort = newNetworkPort; } /** * Initialize network interface, choosing best available network interface */ public void setUpNetworkInterface() { disNetworkInterface = new DisThreadedNetworkInterface(getNetworkAddress(), getNetworkPort()); 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); } /** * All done, release network resources */ public void tearDownNetworkInterface() { disNetworkInterface.removeListener(pduListener); disNetworkInterface.close(); // disNetworkInterface.kill(); // renamed as close(), deprecated // disNetworkInterface = null; // making sure no possibility of zombie process remaining... } /** * Send a single Protocol Data Unit (PDU) of any type * * @param pdu the pdu to send */ public 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()); } } } } /** * 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) { System.out.println(TRACE_PREFIX + "started..."); FrankAssignment3Simulation thisProgram = new FrankAssignment3Simulation(); // creates instance // initial execution: can 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().getName() + " [address port]"); System.exit(-1); } // OK here we go... thisProgram.setUpNetworkInterface(); String DEFAULT_OUTPUT_DIRECTORY = "./pduLog"; String outputDirectory = DEFAULT_OUTPUT_DIRECTORY; System.out.println("Beginning pdu save to directory " + outputDirectory); PduRecorder pduRecorder = new PduRecorder(outputDirectory, networkAddress, networkPort); // assumes save thisProgram.runSimulation(); // ... your simulation execution code goes in there ... pduRecorder.stop(); thisProgram.tearDownNetworkInterface(); // make sure no processes are left lingering System.out.println(TRACE_PREFIX + "complete."); // report successful completion } /** * @return whether verboseComments mode is enabled */ public boolean isVerboseComments() { return verboseComments; } /** * @param newVerboseComments whether verboseComments mode is enabled */ public void setVerboseComments(boolean newVerboseComments) { this.verboseComments = newVerboseComments; } }