diff --git a/assignments/src/MV3500Cohort2019JulySeptember/homework4/Brennenstuhl/BrennenstuhlEspduSender.java b/assignments/src/MV3500Cohort2019JulySeptember/homework4/Brennenstuhl/BrennenstuhlEspduSender.java index d05532b965828d7f679a967031ef7baad2c15b08..86607d9a4eb2768d66eb0e41826e5f4bffadee89 100644 --- a/assignments/src/MV3500Cohort2019JulySeptember/homework4/Brennenstuhl/BrennenstuhlEspduSender.java +++ b/assignments/src/MV3500Cohort2019JulySeptember/homework4/Brennenstuhl/BrennenstuhlEspduSender.java @@ -275,7 +275,7 @@ public class BrennenstuhlEspduSender FirePdu firePdu = new FirePdu(); firePdu.setLocationInWorldCoordinates(espdu.getEntityLocation()); - byte[] fireArray = firePdu.marshal(); + byte[] fireArray = firePdu.marshal().array(); broadcastAddresses = getBroadcastAddresses(); Iterator iterator = broadcastAddresses.iterator(); diff --git a/assignments/src/MV3500Cohort2019JulySeptember/homework4/Schutt/SchuttESPDUSender.java b/assignments/src/MV3500Cohort2019JulySeptember/homework4/Schutt/SchuttESPDUSender.java index 4b6c630e24760b8ad271a9a52c468d21beceea7f..220daafdbb802b40371f15b25bc70b0ae8f215d2 100644 --- a/assignments/src/MV3500Cohort2019JulySeptember/homework4/Schutt/SchuttESPDUSender.java +++ b/assignments/src/MV3500Cohort2019JulySeptember/homework4/Schutt/SchuttESPDUSender.java @@ -287,7 +287,7 @@ public class SchuttESPDUSender firePdu.setLocationInWorldCoordinates(tarLocation); firePdu.setRange((float) 4000.0); - byte[] fireArray = firePdu.marshal(); + byte[] fireArray = firePdu.marshal().array(); broadcastAddresses = getBroadcastAddresses(); Iterator iterator = broadcastAddresses.iterator(); diff --git a/assignments/src/MV3500Cohort2019JulySeptember/homework4/Yurkovich/Yurk_EspduSender.java b/assignments/src/MV3500Cohort2019JulySeptember/homework4/Yurkovich/Yurk_EspduSender.java index a84dae2fa0c6065f5afae0bc0283760b9040aa1b..2721f764fca2d52b0dcc698261660ed6fe4b3a38 100644 --- a/assignments/src/MV3500Cohort2019JulySeptember/homework4/Yurkovich/Yurk_EspduSender.java +++ b/assignments/src/MV3500Cohort2019JulySeptember/homework4/Yurkovich/Yurk_EspduSender.java @@ -281,7 +281,7 @@ public class Yurk_EspduSender firePdu.setVelocity(pVelocity); - byte[] fireArray = firePdu.marshal(); + byte[] fireArray = firePdu.marshal().array(); broadcastAddresses = getBroadcastAddresses(); Iterator iterator = broadcastAddresses.iterator(); diff --git a/assignments/src/MV3500Cohort2019JulySeptember/projects/SchuttFetterolf/AllPduRoundTripTest.java b/assignments/src/MV3500Cohort2019JulySeptember/projects/SchuttFetterolf/AllPduRoundTripTest.java index bbcfe4e717dfd7db0003ab51451cca56a657e464..76baf9fddefd1b51e20bc1af32204f643af72540 100644 --- a/assignments/src/MV3500Cohort2019JulySeptember/projects/SchuttFetterolf/AllPduRoundTripTest.java +++ b/assignments/src/MV3500Cohort2019JulySeptember/projects/SchuttFetterolf/AllPduRoundTripTest.java @@ -190,7 +190,7 @@ // private void sendOne(Pdu pdu) // { // pduSentMap.put(pdu.getPduType(), pdu); -// disNetworkInterface.send(pdu); +// disNetworkInterface.sendPDU(pdu); // } // // private void setupRecorder() throws Exception diff --git a/assignments/src/MV3500Cohort2020JulySeptember/homework4/Britt/BrittSimulation.java b/assignments/src/MV3500Cohort2020JulySeptember/homework4/Britt/BrittSimulation.java index 8d00a280ae1909c005eb05b2837caf813b381deb..d71bd39fbcbdac32eb6382a3dd287aa01bc8be0d 100644 --- a/assignments/src/MV3500Cohort2020JulySeptember/homework4/Britt/BrittSimulation.java +++ b/assignments/src/MV3500Cohort2020JulySeptember/homework4/Britt/BrittSimulation.java @@ -126,7 +126,7 @@ public class BrittSimulation { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2020JulySeptember/homework4/Britt/EspduSender.java b/assignments/src/MV3500Cohort2020JulySeptember/homework4/Britt/EspduSender.java index 42c186b4f66eabbb50c61b53e4a89a2491361d66..bab47f015f1e303e10937221c8ebd7e9828c0410 100644 --- a/assignments/src/MV3500Cohort2020JulySeptember/homework4/Britt/EspduSender.java +++ b/assignments/src/MV3500Cohort2020JulySeptember/homework4/Britt/EspduSender.java @@ -315,7 +315,7 @@ public class EspduSender FirePdu firePdu = new FirePdu(); firePdu.setLocationInWorldCoordinates(espdu.getEntityLocation()); - byte[] fireArray = firePdu.marshal(); + byte[] fireArray = firePdu.marshal().array(); // CommentPdu newCommentPdu = new CommentPdu(); // ArrayList<VariableDatum> payloadList = new ArrayList<>(); diff --git a/assignments/src/MV3500Cohort2020JulySeptember/homework4/Britt/ExampleSimulationProgram.java b/assignments/src/MV3500Cohort2020JulySeptember/homework4/Britt/ExampleSimulationProgram.java index b32306d44c33083de3613cf8de4d42a994a61fe5..eef45fd5aba7ea473869233494b70b043571980b 100644 --- a/assignments/src/MV3500Cohort2020JulySeptember/homework4/Britt/ExampleSimulationProgram.java +++ b/assignments/src/MV3500Cohort2020JulySeptember/homework4/Britt/ExampleSimulationProgram.java @@ -116,7 +116,7 @@ public class ExampleSimulationProgram { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2020JulySeptember/homework4/Cannon/CannonArtillerySimulation.java b/assignments/src/MV3500Cohort2020JulySeptember/homework4/Cannon/CannonArtillerySimulation.java index 8e610d6d24cc3cfad77aa35f35693c5f9fdd9d08..7487d210eba85d4f5a1c12a43d9ddec745628215 100644 --- a/assignments/src/MV3500Cohort2020JulySeptember/homework4/Cannon/CannonArtillerySimulation.java +++ b/assignments/src/MV3500Cohort2020JulySeptember/homework4/Cannon/CannonArtillerySimulation.java @@ -121,7 +121,7 @@ public class CannonArtillerySimulation { */ private void sendSinglePdu(Pdu pdu) { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(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()); diff --git a/assignments/src/MV3500Cohort2020JulySeptember/homework4/White/test/WhiteSimulation.java b/assignments/src/MV3500Cohort2020JulySeptember/homework4/White/test/WhiteSimulation.java index e62d50be646327beb4329eb5400bbfea23af8a55..85d406f2ee4f6ec15befabfc35a526548e1b662e 100644 --- a/assignments/src/MV3500Cohort2020JulySeptember/homework4/White/test/WhiteSimulation.java +++ b/assignments/src/MV3500Cohort2020JulySeptember/homework4/White/test/WhiteSimulation.java @@ -129,7 +129,7 @@ public class WhiteSimulation { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2020JulySeptember/homework4/White/working/WhiteSimulation.java b/assignments/src/MV3500Cohort2020JulySeptember/homework4/White/working/WhiteSimulation.java index b029ce4bb10c12261b93f0a84c609f10427b4078..087f4268fc27c8b82e1a1725fa4664c92b0b0579 100644 --- a/assignments/src/MV3500Cohort2020JulySeptember/homework4/White/working/WhiteSimulation.java +++ b/assignments/src/MV3500Cohort2020JulySeptember/homework4/White/working/WhiteSimulation.java @@ -126,7 +126,7 @@ public class WhiteSimulation { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Allen/ExampleSimulationProgramAllen_3.java b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Allen/ExampleSimulationProgramAllen_3.java index 33ca72aad1f18a69ecf0914dccab93ebd16597f5..7505ffadb6dd2d49d5b2ace8f0de5ecce152a405 100644 --- a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Allen/ExampleSimulationProgramAllen_3.java +++ b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Allen/ExampleSimulationProgramAllen_3.java @@ -395,7 +395,7 @@ public class ExampleSimulationProgramAllen_3 { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Fisher/ExampleSimulationProgramFisher_2.java b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Fisher/ExampleSimulationProgramFisher_2.java index 49d8edab8812e25e584b3b947ddf575f9aed5b9a..fbd5be06f23f82e80a733ffa606ee6a39a5ec758 100644 --- a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Fisher/ExampleSimulationProgramFisher_2.java +++ b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Fisher/ExampleSimulationProgramFisher_2.java @@ -290,7 +290,7 @@ public class ExampleSimulationProgramFisher_2 { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Frank/FrankAssignmentThreeSimulation.java b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Frank/FrankAssignmentThreeSimulation.java index 836c3e84ca82f37bf215175bd6608f572814d042..da46993c1f0952ecd096c6decfe97ea9eaaa3871 100644 --- a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Frank/FrankAssignmentThreeSimulation.java +++ b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Frank/FrankAssignmentThreeSimulation.java @@ -412,7 +412,7 @@ public class FrankAssignmentThreeSimulation { */ private void sendSinglePdu(Pdu pdu) { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(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()); diff --git a/assignments/src/MV3500Cohort2021JulySeptember/homework3/HittnerDom/HittnerDom3HW.java b/assignments/src/MV3500Cohort2021JulySeptember/homework3/HittnerDom/HittnerDom3HW.java index e14bf1184e20571d213841a726439dbb686d1bf6..db30b21bc693befa207bc41dd7e31f869a2ad32e 100644 --- a/assignments/src/MV3500Cohort2021JulySeptember/homework3/HittnerDom/HittnerDom3HW.java +++ b/assignments/src/MV3500Cohort2021JulySeptember/homework3/HittnerDom/HittnerDom3HW.java @@ -274,7 +274,7 @@ public class HittnerDom3HW { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2021JulySeptember/homework3/HittnerNick/HittnerNick3.java b/assignments/src/MV3500Cohort2021JulySeptember/homework3/HittnerNick/HittnerNick3.java index 3de8dc0eae187a3ba359205c98f86fee03ec9f1c..52a396d54b4a2eb7938a5beb4ba0e27377b08192 100644 --- a/assignments/src/MV3500Cohort2021JulySeptember/homework3/HittnerNick/HittnerNick3.java +++ b/assignments/src/MV3500Cohort2021JulySeptember/homework3/HittnerNick/HittnerNick3.java @@ -270,7 +270,7 @@ public class HittnerNick3 { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Keeven/Keeven3.java b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Keeven/Keeven3.java index 2dbb6d64652f53ee4189738ae755ddcee5974664..fbaa589657dcdcf8cfa13b94e1a0df5d1dbceab7 100644 --- a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Keeven/Keeven3.java +++ b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Keeven/Keeven3.java @@ -411,7 +411,7 @@ public class Keeven3 { */ private void sendSinglePdu(Pdu pdu) { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(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()); diff --git a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Leckie/homework3Leckie.java b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Leckie/homework3Leckie.java index 3b9bf63bdc1133372cbb7e25f82f0376793158de..5cd62faaf97c906d399a4dda8b99c1d87c6dabfd 100644 --- a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Leckie/homework3Leckie.java +++ b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Leckie/homework3Leckie.java @@ -259,7 +259,7 @@ public class homework3Leckie { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2021JulySeptember/homework3/McNeely/ExampleSimulationProgramMcNeely.java b/assignments/src/MV3500Cohort2021JulySeptember/homework3/McNeely/ExampleSimulationProgramMcNeely.java index c6116b78ed2c82c912b5d000911060c6512ceab3..3777ab0d8381a21673e72248be66c8173cb0aef3 100644 --- a/assignments/src/MV3500Cohort2021JulySeptember/homework3/McNeely/ExampleSimulationProgramMcNeely.java +++ b/assignments/src/MV3500Cohort2021JulySeptember/homework3/McNeely/ExampleSimulationProgramMcNeely.java @@ -251,7 +251,7 @@ public class ExampleSimulationProgramMcNeely { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Morris/MorrisSimulationProgram.java b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Morris/MorrisSimulationProgram.java index 978e9a8cdab264f117d31fd7f96339356d9c0876..542c521eb81731e612d17ac0ee1b05eafad3375f 100644 --- a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Morris/MorrisSimulationProgram.java +++ b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Morris/MorrisSimulationProgram.java @@ -95,7 +95,7 @@ public class MorrisSimulationProgram // decide whether to fire, and then update the firePdu. Hmmm, you might want a target to shoort at! firePdu_1a.setLocationInWorldCoordinates(entityStatePdu_1.getEntityLocation()); - byte[] fireArray = firePdu_1a.marshal(); + byte[] fireArray = firePdu_1a.marshal().array(); System.out.println("FirePdu_1 #" + simulationLoopCount + " firePdu=[FireMissionIndex=" + firePdu_1a.getFireMissionIndex() + ", descriptor=" + firePdu_1a.getDescriptor() + "]"); // etc. etc. your code goes here for your simulation of interest @@ -279,7 +279,7 @@ public class MorrisSimulationProgram { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Pugh/PughSimulationProgram.java b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Pugh/PughSimulationProgram.java index 58dd7652a43e4d31cbdde92a48fad919ecbcdd96..f45e97677c45267ba7ff7122e613d8de62af5f11 100644 --- a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Pugh/PughSimulationProgram.java +++ b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Pugh/PughSimulationProgram.java @@ -279,7 +279,7 @@ public class PughSimulationProgram { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Reynolds/ExampleSimulationProgramReynolds.java b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Reynolds/ExampleSimulationProgramReynolds.java index 13ca42dfa88849cff312d39ece3de90ff9f00d68..f5041be5084b42b78a2a10aa1b753cb15e21e2ea 100644 --- a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Reynolds/ExampleSimulationProgramReynolds.java +++ b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Reynolds/ExampleSimulationProgramReynolds.java @@ -405,7 +405,7 @@ public class ExampleSimulationProgramReynolds { */ private void sendSinglePdu(Pdu pdu) { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(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()); diff --git a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Robinson/hw3Robinson.java b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Robinson/hw3Robinson.java index 64d7bd0c560082e2508a2469701d0ea69306a685..99c678a0a315b2d07a4940c05bd33f9effda0ea7 100644 --- a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Robinson/hw3Robinson.java +++ b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Robinson/hw3Robinson.java @@ -304,7 +304,7 @@ public class hw3Robinson { { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Schlessel/ExampleSimulationProgram.java b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Schlessel/ExampleSimulationProgram.java index d30e20e033263fd0dfa85a253a0f82f331fbdcfe..f3637760cb1f3e4404e5b2b2931ccc1d7d6def69 100644 --- a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Schlessel/ExampleSimulationProgram.java +++ b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Schlessel/ExampleSimulationProgram.java @@ -282,7 +282,7 @@ public class ExampleSimulationProgram { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Schlessel/SchlesselSimulationProgram.java b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Schlessel/SchlesselSimulationProgram.java index 34956e4a56dd44c556d6e7c7ad5cbad1991d4301..7957fd606116951dc9da15f59c641833c2de45c0 100644 --- a/assignments/src/MV3500Cohort2021JulySeptember/homework3/Schlessel/SchlesselSimulationProgram.java +++ b/assignments/src/MV3500Cohort2021JulySeptember/homework3/Schlessel/SchlesselSimulationProgram.java @@ -289,7 +289,7 @@ public class SchlesselSimulationProgram { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2021JulySeptember/projects/AllenReynolds/KineticFirewallSimulationProgramAllenReynolds.java b/assignments/src/MV3500Cohort2021JulySeptember/projects/AllenReynolds/KineticFirewallSimulationProgramAllenReynolds.java index 5dba8f8d2917270db8cf813be0f2dc822ecc9b0e..171cea5b8e8f7c95b154f5ac654961b6c03dc887 100644 --- a/assignments/src/MV3500Cohort2021JulySeptember/projects/AllenReynolds/KineticFirewallSimulationProgramAllenReynolds.java +++ b/assignments/src/MV3500Cohort2021JulySeptember/projects/AllenReynolds/KineticFirewallSimulationProgramAllenReynolds.java @@ -457,7 +457,7 @@ public class KineticFirewallSimulationProgramAllenReynolds { */ private void sendSinglePdu(Pdu pdu) { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(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()); diff --git a/assignments/src/MV3500Cohort2021JulySeptember/projects/Fisher/MV3500ProjectFisher.java b/assignments/src/MV3500Cohort2021JulySeptember/projects/Fisher/MV3500ProjectFisher.java index a3050a7b4f0e1ecd907986fa5bac3cb7e7166c0c..452b2e00176b02acc5456ad33a47e21873fdfc48 100644 --- a/assignments/src/MV3500Cohort2021JulySeptember/projects/Fisher/MV3500ProjectFisher.java +++ b/assignments/src/MV3500Cohort2021JulySeptember/projects/Fisher/MV3500ProjectFisher.java @@ -396,7 +396,7 @@ public class MV3500ProjectFisher { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2022MayJune/homework2/Ashmore/ExampleSimulationProgramAshmore.java b/assignments/src/MV3500Cohort2022MayJune/homework2/Ashmore/ExampleSimulationProgramAshmore.java index 256bee4813b8afe930c6cfb086f6b8fdf1b96465..9115997c159ac4a282c811873f37a1e62b3a5122 100644 --- a/assignments/src/MV3500Cohort2022MayJune/homework2/Ashmore/ExampleSimulationProgramAshmore.java +++ b/assignments/src/MV3500Cohort2022MayJune/homework2/Ashmore/ExampleSimulationProgramAshmore.java @@ -355,7 +355,7 @@ public class ExampleSimulationProgramAshmore { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2022MayJune/homework2/Ashmore/PduTrack.java b/assignments/src/MV3500Cohort2022MayJune/homework2/Ashmore/PduTrack.java index 59ed259f6e77010678f1ff5da4aee78e9b499d1b..8cded7a666c68aaf99dad80444611ca0cc6a351a 100644 --- a/assignments/src/MV3500Cohort2022MayJune/homework2/Ashmore/PduTrack.java +++ b/assignments/src/MV3500Cohort2022MayJune/homework2/Ashmore/PduTrack.java @@ -954,7 +954,7 @@ public class PduTrack EntityStatePdu espdu = (EntityStatePdu) pdu; System.out.println("espdu from pduTrack pduList"); reportPdu(espdu); - byte[] byteArray = espdu.marshal(); + byte[] byteArray = espdu.marshal().array(); System.out.println("espdu.marshal() byteArray: " + bytesToHex(byteArray)); flushBuffers(); @@ -972,7 +972,7 @@ public class PduTrack System.out.println("espdu.copyByteBuffer()"); reportPdu(espdu.copyByteBuffer()); - byte[] byteArrayCopy = espdu.copyByteBuffer().marshal(); + byte[] byteArrayCopy = espdu.copyByteBuffer().marshal().array(); System.out.println("espdu.copyByteBuffer().marshal() byteArray: " + bytesToHex(byteArrayCopy)); flushBuffers(); @@ -990,7 +990,7 @@ public class PduTrack System.out.println("espdu.copyDataOutputStream()"); reportPdu(espdu.copyDataOutputStream()); - byte[] byteArrayCopyDOS = espdu.copyDataOutputStream().marshal(); + byte[] byteArrayCopyDOS = espdu.copyDataOutputStream().marshal().array(); System.out.println("espdu.copyDataOutputStream().marshal() byteArray: " + bytesToHex(byteArrayCopyDOS)); flushBuffers(); diff --git a/assignments/src/MV3500Cohort2022MayJune/homework2/Duran/ExampleSimulationProgramDuran.java b/assignments/src/MV3500Cohort2022MayJune/homework2/Duran/ExampleSimulationProgramDuran.java index c544a0ebc8f7318dfa8be3abca3f5f51c4677ee4..9d93215fa55f4a7ba8138a75630cda5743b3535e 100644 --- a/assignments/src/MV3500Cohort2022MayJune/homework2/Duran/ExampleSimulationProgramDuran.java +++ b/assignments/src/MV3500Cohort2022MayJune/homework2/Duran/ExampleSimulationProgramDuran.java @@ -356,7 +356,7 @@ public class ExampleSimulationProgramDuran { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2022MayJune/homework2/Duran/PduTrack.java b/assignments/src/MV3500Cohort2022MayJune/homework2/Duran/PduTrack.java index 61a7159852d5a52117479d7a4428abdadd99fbf0..121ab78128723a99e57878f115e03b698985b89f 100644 --- a/assignments/src/MV3500Cohort2022MayJune/homework2/Duran/PduTrack.java +++ b/assignments/src/MV3500Cohort2022MayJune/homework2/Duran/PduTrack.java @@ -954,7 +954,7 @@ public class PduTrack EntityStatePdu espdu = (EntityStatePdu) pdu; System.out.println("espdu from pduTrack pduList"); reportPdu(espdu); - byte[] byteArray = espdu.marshal(); + byte[] byteArray = espdu.marshal().array(); System.out.println("espdu.marshal() byteArray: " + bytesToHex(byteArray)); flushBuffers(); @@ -972,7 +972,7 @@ public class PduTrack System.out.println("espdu.copyByteBuffer()"); reportPdu(espdu.copyByteBuffer()); - byte[] byteArrayCopy = espdu.copyByteBuffer().marshal(); + byte[] byteArrayCopy = espdu.copyByteBuffer().marshal().array(); System.out.println("espdu.copyByteBuffer().marshal() byteArray: " + bytesToHex(byteArrayCopy)); flushBuffers(); @@ -990,7 +990,7 @@ public class PduTrack System.out.println("espdu.copyDataOutputStream()"); reportPdu(espdu.copyDataOutputStream()); - byte[] byteArrayCopyDOS = espdu.copyDataOutputStream().marshal(); + byte[] byteArrayCopyDOS = espdu.copyDataOutputStream().marshal().array(); System.out.println("espdu.copyDataOutputStream().marshal() byteArray: " + bytesToHex(byteArrayCopyDOS)); flushBuffers(); diff --git a/assignments/src/MV3500Cohort2022MayJune/homework2/Hickey/ExampleSimulationProgramHickey.java b/assignments/src/MV3500Cohort2022MayJune/homework2/Hickey/ExampleSimulationProgramHickey.java index 6da4e4e01d4c48f6abb0a07c3b9b4b93bd15aa7a..46a48a53ec8ad47a70eae79465219a0da638d136 100644 --- a/assignments/src/MV3500Cohort2022MayJune/homework2/Hickey/ExampleSimulationProgramHickey.java +++ b/assignments/src/MV3500Cohort2022MayJune/homework2/Hickey/ExampleSimulationProgramHickey.java @@ -1,505 +1,505 @@ -/** - * 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.Hickey; - -import 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 ExampleSimulationProgramHickey -{ - 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(ExampleSimulationProgramHickey.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 ExampleSimulationProgramHickey() - { - 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 ExampleSimulationProgramHickey(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 ExampleSimulationProgramHickey 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 ExampleSimulationProgramHickey 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 = "[" + ExampleSimulationProgramHickey.class.getName() + "] "; - - System.out.println(TRACE_PREFIX + "main() started..."); - - thisProgram = new ExampleSimulationProgramHickey(); // 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 - } -} +/** + * 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.Hickey; + +import 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 ExampleSimulationProgramHickey +{ + 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(ExampleSimulationProgramHickey.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 ExampleSimulationProgramHickey() + { + 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 ExampleSimulationProgramHickey(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 ExampleSimulationProgramHickey 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.sendPDU(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 ExampleSimulationProgramHickey 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 = "[" + ExampleSimulationProgramHickey.class.getName() + "] "; + + System.out.println(TRACE_PREFIX + "main() started..."); + + thisProgram = new ExampleSimulationProgramHickey(); // 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/Hickey/PduTrack.java b/assignments/src/MV3500Cohort2022MayJune/homework2/Hickey/PduTrack.java index a64db8a31024a8c286bdc4e193479988da4c8858..00af5fe6fe9232ea8b0497e0a3455de766668670 100644 --- a/assignments/src/MV3500Cohort2022MayJune/homework2/Hickey/PduTrack.java +++ b/assignments/src/MV3500Cohort2022MayJune/homework2/Hickey/PduTrack.java @@ -1,1044 +1,1044 @@ -/* -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.Hickey; - -import 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."); - } - -} +/* +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.Hickey; + +import 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().array(); + 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().array(); + 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().array(); + 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/Marks/ExampleSimulationProgramMarks.java b/assignments/src/MV3500Cohort2022MayJune/homework2/Marks/ExampleSimulationProgramMarks.java index 0a1d79b82dd29525421d938097be39a301e07f79..de4171dc8cff2c0ced5b0a84269bf17dc23b90d6 100644 --- a/assignments/src/MV3500Cohort2022MayJune/homework2/Marks/ExampleSimulationProgramMarks.java +++ b/assignments/src/MV3500Cohort2022MayJune/homework2/Marks/ExampleSimulationProgramMarks.java @@ -364,7 +364,7 @@ public class ExampleSimulationProgramMarks { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2022MayJune/homework2/Marks/PduTrack.java b/assignments/src/MV3500Cohort2022MayJune/homework2/Marks/PduTrack.java index 0ebc34e0cb95e875786467b67a499fd4f9ef3482..7891d253427f00358a7160f512978ebced5f2959 100644 --- a/assignments/src/MV3500Cohort2022MayJune/homework2/Marks/PduTrack.java +++ b/assignments/src/MV3500Cohort2022MayJune/homework2/Marks/PduTrack.java @@ -955,7 +955,7 @@ public class PduTrack EntityStatePdu espdu = (EntityStatePdu) pdu; System.out.println("espdu from pduTrack pduList"); reportPdu(espdu); - byte[] byteArray = espdu.marshal(); + byte[] byteArray = espdu.marshal().array(); System.out.println("espdu.marshal() byteArray: " + bytesToHex(byteArray)); flushBuffers(); @@ -973,7 +973,7 @@ public class PduTrack System.out.println("espdu.copyByteBuffer()"); reportPdu(espdu.copyByteBuffer()); - byte[] byteArrayCopy = espdu.copyByteBuffer().marshal(); + byte[] byteArrayCopy = espdu.copyByteBuffer().marshal().array(); System.out.println("espdu.copyByteBuffer().marshal() byteArray: " + bytesToHex(byteArrayCopy)); flushBuffers(); @@ -991,7 +991,7 @@ public class PduTrack System.out.println("espdu.copyDataOutputStream()"); reportPdu(espdu.copyDataOutputStream()); - byte[] byteArrayCopyDOS = espdu.copyDataOutputStream().marshal(); + byte[] byteArrayCopyDOS = espdu.copyDataOutputStream().marshal().array(); System.out.println("espdu.copyDataOutputStream().marshal() byteArray: " + bytesToHex(byteArrayCopyDOS)); flushBuffers(); diff --git a/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/ExampleSimulationProgramTam.java b/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/ExampleSimulationProgramTam.java index a6ab8626861c9b6cebd7655b81eaa4763fdd30d8..49399c2f5d43948a94d2d992700195199fd64160 100644 --- a/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/ExampleSimulationProgramTam.java +++ b/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/ExampleSimulationProgramTam.java @@ -1,504 +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 - } -} +/** + * 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.sendPDU(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/PduTrack.java b/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/PduTrack.java index 504ea3db17ddc26610c304f9c2ee1cfeefa57cd1..8d54bbc547ef170a9438275b9d45d39a56ad4f1f 100644 --- a/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/PduTrack.java +++ b/assignments/src/MV3500Cohort2022MayJune/homework2/Tam/PduTrack.java @@ -1,1043 +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."); - } - -} +/* +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().array(); + 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().array(); + 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().array(); + 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/MV3500Cohort2023MarchJune/homework3/Chojnacki/ExampleSimulationProgram.java b/assignments/src/MV3500Cohort2023MarchJune/homework3/Chojnacki/ExampleSimulationProgram.java index 8b3bd05a31393d3281e2d1a682bf1fc6d7af64f4..3ac933bf2a488fbef3641ddb99c38e0ef8827db5 100644 --- a/assignments/src/MV3500Cohort2023MarchJune/homework3/Chojnacki/ExampleSimulationProgram.java +++ b/assignments/src/MV3500Cohort2023MarchJune/homework3/Chojnacki/ExampleSimulationProgram.java @@ -1,518 +1,518 @@ -/** - * 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 - * - * This Program is a modified version of ExampleSimulationProgram in order to see the - * verbose plain text pdu log. This simulates a firewall identifying a malicious packet - * and tracing its source before destroying it. - * - * and tracing its source before destroying it - updated September 12, 2021 to ensure - * correct file pushed to Gitlab. - * - * @author Bruce Chojnacki - */ -package MV3500Cohort2023MarchJune.homework3.Chojnacki; - -import edu.nps.moves.dis7.enumerations.*; // match any -import edu.nps.moves.dis7.pdus.*; // match any of the PDU classes, easier than listing individually -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 provide an easily modifiable example - * 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 ExampleSimulationProgram { - - private boolean verboseComments = true; - 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; - String DEFAULT_OUTPUT_DIRECTORY = "./pduLog"; - - private EntityID createFriendFireWall() { - EntityID FNFireWallID = new EntityID(); // 1.1.225.1.1.1 Platform,Cyber,USA,FireWall - FNFireWallID.setSiteID(13); - FNFireWallID.setApplicationID(43); - FNFireWallID.setEntityID(103); - return FNFireWallID; - } - - private EntityType createFriendFireWallType() { - EntityType FNFireWallType = new EntityType(); - FNFireWallType.setEntityKind(EntityKind.PLATFORM); - FNFireWallType.setDomain(Domain.inst(PlatformDomain.OTHER)); - FNFireWallType.setCountry(Country.UNITED_STATES_OF_AMERICA_USA); - FNFireWallType.setCategory(3); - FNFireWallType.setSubCategory(1); - FNFireWallType.setSpecific(1); - return FNFireWallType; - } - - private EntityID createMalPacket() { - EntityID MalPacketID = new EntityID(); // 1.1.45.1.7.1 Platform,Cyber,China,MaliciousPacket - MalPacketID.setSiteID(66); - MalPacketID.setApplicationID(666); - MalPacketID.setEntityID(6666); - return MalPacketID; - } - - private EntityType createMalPacketType() { - EntityType MalPacketType = new EntityType(); - MalPacketType.setEntityKind(EntityKind.PLATFORM); - MalPacketType.setDomain(Domain.inst(PlatformDomain.OTHER)); - MalPacketType.setCountry(Country.CHINA_PEOPLES_REPUBLIC_OF_CHN); - MalPacketType.setCategory(3); - MalPacketType.setSubCategory(1); - MalPacketType.setSpecific(1); - return MalPacketType; - } - - private MunitionDescriptor createTraceroute() { - - EntityType TracerouteType = new EntityType(); //2.2.225.2.13.1 - TracerouteType.setEntityKind(EntityKind.MUNITION); - TracerouteType.setDomain(Domain.inst(PlatformDomain.OTHER)); - TracerouteType.setCountry(Country.UNITED_STATES_OF_AMERICA_USA); - TracerouteType.setCategory(2); - TracerouteType.setSubCategory(8); - TracerouteType.setSpecific(1); - MunitionDescriptor Traceroute = new MunitionDescriptor(); - Traceroute.setMunitionType(TracerouteType); - Traceroute.setQuantity(1000); - Traceroute.setFuse(MunitionDescriptorFuse.CONTACT); - Traceroute.setRate(200); - return Traceroute; - } - - /** - * This runSimulationLoops() 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 runSimulationLoops() { - try { - /** - * seconds for real-time execution (not simulation time, which may - * or may not be the same) - */ - final double SIMULATION_LOOP_DURATION_SECONDS = 1.0; - final int SIMULATION_MAX_LOOP_COUNT = 15; // 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; - - // TODO reset clock to zero each time for consistent outputs - // 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. - // create PDU object for US Firewall and set its values. - //EntityID entityID_1 = new EntityID(); - //entityID_1.setSiteID(1).setApplicationID(2).setEntityID(3); // made-up example ID; - EntityStatePdu entityStatePdu_1 = pduFactory.makeEntityStatePdu(); - entityStatePdu_1.setEntityID(createFriendFireWall()); - entityStatePdu_1.setEntityType(createFriendFireWallType()); - entityStatePdu_1.getEntityLocation().setX(0); - entityStatePdu_1.setForceId(ForceID.FRIENDLY); - - // TODO someday, use enumerations; is there a unique site triplet for MOVES Institute? - // create PDU object for Malware Packet and set its values. - //EntityID entityID_2 = new EntityID(); - //entityID_2.setSiteID(4).setApplicationID(5).setEntityID(6); - EntityStatePdu entityStatePdu_2 = pduFactory.makeEntityStatePdu(); - entityStatePdu_2.setEntityID(createMalPacket()); - entityStatePdu_2.setEntityType(createMalPacketType()); - entityStatePdu_2.getEntityLocation().setX(7); - entityStatePdu_2.setForceId(ForceID.OPPOSING); - - int MalPacketPingsReceived = 0; - - FirePdu firePdu = pduFactory.makeFirePdu(); // for entity 1 first weapon (possible traceroute) - // should we customize this munition? what is it for your simulation? - EntityID fireID = new EntityID(); - fireID.setSiteID(13); - fireID.setApplicationID(43); - fireID.setEntityID(103); - EntityID targetID = new EntityID(); - targetID.setSiteID(66); - targetID.setApplicationID(666); - targetID.setEntityID(6666); - - firePdu.setFiringEntityID(fireID); - firePdu.setTargetEntityID(targetID); - - firePdu.setDescriptor(createTraceroute()); // calling create Traceroute Method - - EntityID TracerouteID = new EntityID(); - TracerouteID.setEntityID(1); - firePdu.setMunitionExpendibleID(TracerouteID); - - CommentReliablePdu MalPacketDestroyedComment = pduFactory.makeCommentReliablePdu("Malware Packet DESTROYED BY Firewall"); - CommentReliablePdu MalPacketDetectedComment = pduFactory.makeCommentReliablePdu("Firewall Detects Malware engage Traceroute"); - - // TODO simulation management PDUs for startup, planning to design special class support - //DetonationPdu detonationPdu = pduFactory.makeDetonationPdu(); - //detonationPdu.setDescriptor(pDescriptor); - // 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()); // stationary defensive posture - entityStatePdu_2.getEntityLocation().setX(entityStatePdu_2.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! - Double range = entityStatePdu_2.getEntityLocation().getX(); - System.out.println("range: " + range + " hops from our Network DMZ!"); - - if (range < 5) { // Range 5 - if (!fireBool) { - sendSinglePdu(MalPacketDetectedComment); - } - 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() == 6666) { - MalPacketPingsReceived += 1; - if (MalPacketPingsReceived > 1) { - // The Firewall destroys the MalPacket - - System.out.println("Malware Packet DESTROYED BY Firewall after " + MalPacketPingsReceived + "pings from the traceroute."); - narrativeMessage4 = "Destroyed MalPacket"; - destBool = true; - simulationComplete = true; - - } - } - } - // 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("Phase complete. Next phase initiating..."); - - // make your reports: narrative code for CommentPdu here (set all to empty strings to avoid sending) - narrativeMessage1 = "MV3500 Homework 3 - Simulation Program"; - narrativeMessage2 = "runSimulation() loop " + simulationLoopCount; - narrativeMessage3 = "Simulation Started!"; // intentionally blank for testing - - // your loop termination condition goes here - if (simulationLoopCount > 5) // 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, timeStepComment, narrativeMessage1, narrativeMessage2, narrativeMessage3); - sendSinglePdu(entityStatePdu_2); // me too i.e. 2! - 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 - { - //sendSinglePdu(detonationPdu); - 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(ExampleSimulationProgram.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(); - 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 = "[" + ExampleSimulationProgram.class.getName() + "] "; - - // class variables - PduFactory pduFactory = new PduFactory(); - 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 ExampleSimulationProgram() { - // 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 ExampleSimulationProgram(String address, int port) { - setNetworkAddress(address); - - setNetworkPort(port); - } - - /** - * get networkAddress - * - * @return the networkAddress - */ - public String getNetworkAddress() { - return networkAddress; - } - - /** - * set networkAddress - * - * @param newNetworkAddress the networkAddress to set - */ - public final void setNetworkAddress(String newNetworkAddress) { - ExampleSimulationProgram.networkAddress = newNetworkAddress; - } - - /** - * get networkPort - * - * @return the networkPort - */ - public int getNetworkPort() { - return networkPort; - } - - /** - * set networkPort - * - * @param newNetworkPort the networkPort to set - */ - public final void setNetworkPort(int newNetworkPort) { - ExampleSimulationProgram.networkPort = newNetworkPort; - } - - /** - * Initialize network interface, choosing best available network interface - */ - public void setUpNetworkInterface() { - disNetworkInterface = new DisThreadedNetworkInterface(getNetworkAddress(), getNetworkPort()); - disNetworkInterface.setDescriptor("ExampleSimulationProgramAllen_3 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.setDescriptor("ExampleSimulationProgramAllen_3 pduRecorder"); - pduRecorder.start(); // begin running - } - - /** - * All done, release network resources - */ - public void tearDownNetworkInterface() { - pduRecorder.stop(); - - 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 - */ - private 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..."); - - ExampleSimulationProgram thisProgram = new ExampleSimulationProgram(); // 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(); - - 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 - } - - /** - * get whether verbose comments are enabled - * - * @return whether verboseComments mode is enabled - */ - public boolean isVerboseComments() { - return verboseComments; - } - - /** - * set whether verbose comments are enabled - * - * @param newVerboseComments whether verboseComments mode is enabled - */ - public void setVerboseComments(boolean newVerboseComments) { - this.verboseComments = newVerboseComments; - } -} +/** + * 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 + * + * This Program is a modified version of ExampleSimulationProgram in order to see the + * verbose plain text pdu log. This simulates a firewall identifying a malicious packet + * and tracing its source before destroying it. + * + * and tracing its source before destroying it - updated September 12, 2021 to ensure + * correct file pushed to Gitlab. + * + * @author Bruce Chojnacki + */ +package MV3500Cohort2023MarchJune.homework3.Chojnacki; + +import edu.nps.moves.dis7.enumerations.*; // match any +import edu.nps.moves.dis7.pdus.*; // match any of the PDU classes, easier than listing individually +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 provide an easily modifiable example + * 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 ExampleSimulationProgram { + + private boolean verboseComments = true; + 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; + String DEFAULT_OUTPUT_DIRECTORY = "./pduLog"; + + private EntityID createFriendFireWall() { + EntityID FNFireWallID = new EntityID(); // 1.1.225.1.1.1 Platform,Cyber,USA,FireWall + FNFireWallID.setSiteID(13); + FNFireWallID.setApplicationID(43); + FNFireWallID.setEntityID(103); + return FNFireWallID; + } + + private EntityType createFriendFireWallType() { + EntityType FNFireWallType = new EntityType(); + FNFireWallType.setEntityKind(EntityKind.PLATFORM); + FNFireWallType.setDomain(Domain.inst(PlatformDomain.OTHER)); + FNFireWallType.setCountry(Country.UNITED_STATES_OF_AMERICA_USA); + FNFireWallType.setCategory(3); + FNFireWallType.setSubCategory(1); + FNFireWallType.setSpecific(1); + return FNFireWallType; + } + + private EntityID createMalPacket() { + EntityID MalPacketID = new EntityID(); // 1.1.45.1.7.1 Platform,Cyber,China,MaliciousPacket + MalPacketID.setSiteID(66); + MalPacketID.setApplicationID(666); + MalPacketID.setEntityID(6666); + return MalPacketID; + } + + private EntityType createMalPacketType() { + EntityType MalPacketType = new EntityType(); + MalPacketType.setEntityKind(EntityKind.PLATFORM); + MalPacketType.setDomain(Domain.inst(PlatformDomain.OTHER)); + MalPacketType.setCountry(Country.CHINA_PEOPLES_REPUBLIC_OF_CHN); + MalPacketType.setCategory(3); + MalPacketType.setSubCategory(1); + MalPacketType.setSpecific(1); + return MalPacketType; + } + + private MunitionDescriptor createTraceroute() { + + EntityType TracerouteType = new EntityType(); //2.2.225.2.13.1 + TracerouteType.setEntityKind(EntityKind.MUNITION); + TracerouteType.setDomain(Domain.inst(PlatformDomain.OTHER)); + TracerouteType.setCountry(Country.UNITED_STATES_OF_AMERICA_USA); + TracerouteType.setCategory(2); + TracerouteType.setSubCategory(8); + TracerouteType.setSpecific(1); + MunitionDescriptor Traceroute = new MunitionDescriptor(); + Traceroute.setMunitionType(TracerouteType); + Traceroute.setQuantity(1000); + Traceroute.setFuse(MunitionDescriptorFuse.CONTACT); + Traceroute.setRate(200); + return Traceroute; + } + + /** + * This runSimulationLoops() 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 runSimulationLoops() { + try { + /** + * seconds for real-time execution (not simulation time, which may + * or may not be the same) + */ + final double SIMULATION_LOOP_DURATION_SECONDS = 1.0; + final int SIMULATION_MAX_LOOP_COUNT = 15; // 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; + + // TODO reset clock to zero each time for consistent outputs + // 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. + // create PDU object for US Firewall and set its values. + //EntityID entityID_1 = new EntityID(); + //entityID_1.setSiteID(1).setApplicationID(2).setEntityID(3); // made-up example ID; + EntityStatePdu entityStatePdu_1 = pduFactory.makeEntityStatePdu(); + entityStatePdu_1.setEntityID(createFriendFireWall()); + entityStatePdu_1.setEntityType(createFriendFireWallType()); + entityStatePdu_1.getEntityLocation().setX(0); + entityStatePdu_1.setForceId(ForceID.FRIENDLY); + + // TODO someday, use enumerations; is there a unique site triplet for MOVES Institute? + // create PDU object for Malware Packet and set its values. + //EntityID entityID_2 = new EntityID(); + //entityID_2.setSiteID(4).setApplicationID(5).setEntityID(6); + EntityStatePdu entityStatePdu_2 = pduFactory.makeEntityStatePdu(); + entityStatePdu_2.setEntityID(createMalPacket()); + entityStatePdu_2.setEntityType(createMalPacketType()); + entityStatePdu_2.getEntityLocation().setX(7); + entityStatePdu_2.setForceId(ForceID.OPPOSING); + + int MalPacketPingsReceived = 0; + + FirePdu firePdu = pduFactory.makeFirePdu(); // for entity 1 first weapon (possible traceroute) + // should we customize this munition? what is it for your simulation? + EntityID fireID = new EntityID(); + fireID.setSiteID(13); + fireID.setApplicationID(43); + fireID.setEntityID(103); + EntityID targetID = new EntityID(); + targetID.setSiteID(66); + targetID.setApplicationID(666); + targetID.setEntityID(6666); + + firePdu.setFiringEntityID(fireID); + firePdu.setTargetEntityID(targetID); + + firePdu.setDescriptor(createTraceroute()); // calling create Traceroute Method + + EntityID TracerouteID = new EntityID(); + TracerouteID.setEntityID(1); + firePdu.setMunitionExpendibleID(TracerouteID); + + CommentReliablePdu MalPacketDestroyedComment = pduFactory.makeCommentReliablePdu("Malware Packet DESTROYED BY Firewall"); + CommentReliablePdu MalPacketDetectedComment = pduFactory.makeCommentReliablePdu("Firewall Detects Malware engage Traceroute"); + + // TODO simulation management PDUs for startup, planning to design special class support + //DetonationPdu detonationPdu = pduFactory.makeDetonationPdu(); + //detonationPdu.setDescriptor(pDescriptor); + // 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()); // stationary defensive posture + entityStatePdu_2.getEntityLocation().setX(entityStatePdu_2.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! + Double range = entityStatePdu_2.getEntityLocation().getX(); + System.out.println("range: " + range + " hops from our Network DMZ!"); + + if (range < 5) { // Range 5 + if (!fireBool) { + sendSinglePdu(MalPacketDetectedComment); + } + 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() == 6666) { + MalPacketPingsReceived += 1; + if (MalPacketPingsReceived > 1) { + // The Firewall destroys the MalPacket + + System.out.println("Malware Packet DESTROYED BY Firewall after " + MalPacketPingsReceived + "pings from the traceroute."); + narrativeMessage4 = "Destroyed MalPacket"; + destBool = true; + simulationComplete = true; + + } + } + } + // 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("Phase complete. Next phase initiating..."); + + // make your reports: narrative code for CommentPdu here (set all to empty strings to avoid sending) + narrativeMessage1 = "MV3500 Homework 3 - Simulation Program"; + narrativeMessage2 = "runSimulation() loop " + simulationLoopCount; + narrativeMessage3 = "Simulation Started!"; // intentionally blank for testing + + // your loop termination condition goes here + if (simulationLoopCount > 5) // 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, timeStepComment, narrativeMessage1, narrativeMessage2, narrativeMessage3); + sendSinglePdu(entityStatePdu_2); // me too i.e. 2! + 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 + { + //sendSinglePdu(detonationPdu); + 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(ExampleSimulationProgram.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(); + 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 = "[" + ExampleSimulationProgram.class.getName() + "] "; + + // class variables + PduFactory pduFactory = new PduFactory(); + 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 ExampleSimulationProgram() { + // 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 ExampleSimulationProgram(String address, int port) { + setNetworkAddress(address); + + setNetworkPort(port); + } + + /** + * get networkAddress + * + * @return the networkAddress + */ + public String getNetworkAddress() { + return networkAddress; + } + + /** + * set networkAddress + * + * @param newNetworkAddress the networkAddress to set + */ + public final void setNetworkAddress(String newNetworkAddress) { + ExampleSimulationProgram.networkAddress = newNetworkAddress; + } + + /** + * get networkPort + * + * @return the networkPort + */ + public int getNetworkPort() { + return networkPort; + } + + /** + * set networkPort + * + * @param newNetworkPort the networkPort to set + */ + public final void setNetworkPort(int newNetworkPort) { + ExampleSimulationProgram.networkPort = newNetworkPort; + } + + /** + * Initialize network interface, choosing best available network interface + */ + public void setUpNetworkInterface() { + disNetworkInterface = new DisThreadedNetworkInterface(getNetworkAddress(), getNetworkPort()); + disNetworkInterface.setDescriptor("ExampleSimulationProgramAllen_3 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.setDescriptor("ExampleSimulationProgramAllen_3 pduRecorder"); + pduRecorder.start(); // begin running + } + + /** + * All done, release network resources + */ + public void tearDownNetworkInterface() { + pduRecorder.stop(); + + 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 + */ + private void sendSinglePdu(Pdu pdu) { + try { + disNetworkInterface.sendPDU(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..."); + + ExampleSimulationProgram thisProgram = new ExampleSimulationProgram(); // 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(); + + 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 + } + + /** + * get whether verbose comments are enabled + * + * @return whether verboseComments mode is enabled + */ + public boolean isVerboseComments() { + return verboseComments; + } + + /** + * set whether verbose comments are enabled + * + * @param newVerboseComments whether verboseComments mode is enabled + */ + public void setVerboseComments(boolean newVerboseComments) { + this.verboseComments = newVerboseComments; + } +} diff --git a/assignments/src/MV3500Cohort2023MarchJune/homework3/Oblak/ExampleSimulationProgram.java b/assignments/src/MV3500Cohort2023MarchJune/homework3/Oblak/ExampleSimulationProgram.java index aec56e3dea2ca7e4db26950968c97b73e45313d0..deb0601ecd5c051c21821bdaa7361f3d789ca795 100644 --- a/assignments/src/MV3500Cohort2023MarchJune/homework3/Oblak/ExampleSimulationProgram.java +++ b/assignments/src/MV3500Cohort2023MarchJune/homework3/Oblak/ExampleSimulationProgram.java @@ -407,7 +407,7 @@ public class ExampleSimulationProgram { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2023MarchJune/homework3/Sloan/SloanExampleSimulationProgram.java b/assignments/src/MV3500Cohort2023MarchJune/homework3/Sloan/SloanExampleSimulationProgram.java index 62f857ba116ee9654a699234a8f0b7ae53f606de..06baaecd23744707c8c298b24cd45ec5e47de5cb 100644 --- a/assignments/src/MV3500Cohort2023MarchJune/homework3/Sloan/SloanExampleSimulationProgram.java +++ b/assignments/src/MV3500Cohort2023MarchJune/homework3/Sloan/SloanExampleSimulationProgram.java @@ -291,7 +291,7 @@ public class SloanExampleSimulationProgram { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex) diff --git a/assignments/src/MV3500Cohort2023MarchJune/homework3/Tidwell/ExampleSimulationProgram.java b/assignments/src/MV3500Cohort2023MarchJune/homework3/Tidwell/ExampleSimulationProgram.java index b9854bc3f6720420fa02eb6876045b9f746670df..8ce33fd4e806c887fd6887b77c86647c0dbaea2c 100644 --- a/assignments/src/MV3500Cohort2023MarchJune/homework3/Tidwell/ExampleSimulationProgram.java +++ b/assignments/src/MV3500Cohort2023MarchJune/homework3/Tidwell/ExampleSimulationProgram.java @@ -395,7 +395,7 @@ public class ExampleSimulationProgram { try { - disNetworkInterface.send(pdu); + disNetworkInterface.sendPDU(pdu); Thread.sleep(100); // TODO consider refactoring the wait logic and moving externally } catch (InterruptedException ex)