Skip to content
Snippets Groups Projects
ExampleSimulationProgram.java 11.71 KiB
/**
 * Copyright (c) 2008-2024, MOVES Institute, Naval Postgraduate School (NPS). All rights reserved.
 * This work is provided under a BSD open-source license, see project license.html or license.txt
 * 
 * This main class is my submission for HW 3.
 * 
 * @author ethanjwilliams
 */
package MV3500Cohort2024JulySeptember.homework3.Williams;

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.DisChannel;
import edu.nps.moves.dis7.utilities.PduFactory;
import java.time.LocalDateTime;
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.
 * 
 * Homework 3 networking choices:
 * This simulation uses UDP multicast for efficient distribution of PDUs to 
 * multiple participants, which is great for large-scale simulations where 
 * state updates need to reach all networked participants. UDP is chosen over 
 * TCP due to its lower latency, which is essential for real-time simulations.
 *
 * @see <a href="https://gitlab.nps.edu/Savage/NetworkedGraphicsMV3500/-/blob/master/examples/src/OpenDis7Examples/ExampleSimulationProgramLog.txt" target="_blank">ExampleSimulationProgramLog.txt</a>
 * @see <a href="https://gitlab.nps.edu/Savage/NetworkedGraphicsMV3500/-/blob/master/examples/src/OpenDis7Examples/ExampleSimulationProgramPduCaptureLog.dislog" target="_blank">ExampleSimulationProgramPduCaptureLog.dislog</a>
 * @see <a href="https://gitlab.nps.edu/Savage/NetworkedGraphicsMV3500/-/blob/master/examples/src/OpenDis7Examples/ExampleSimulationProgramFlowDiagram.pdf" target="_blank">ExampleSimulationProgramFlowDiagram.pdf</a>
 * @see <a href="https://gitlab.nps.edu/Savage/NetworkedGraphicsMV3500/-/blob/master/examples/src/OpenDis7Examples/ExampleSimulationProgramWireshark.png" target="_blank">ExampleSimulationProgramWireshark.png</a>
 * @see <a href="https://gitlab.nps.edu/Savage/NetworkedGraphicsMV3500/-/blob/master/examples/src/OpenDis7Examples/ExampleSimulationProgramSequenceDiagram.png" target="_blank">ExampleSimulationProgramSequenceDiagram.png</a>
 */
public class ExampleSimulationProgram
{
    private   String     descriptor = this.getClass().getSimpleName();
    protected DisChannel disChannel;
    protected PduFactory pduFactory;
    
    private double  simulationTimeStepDuration  =  1.0; 
    double  simulationTimeInitial = 0.0;
    double  simulationTimeSeconds = simulationTimeInitial;
    int MAX_LOOP_COUNT = 4;
    
    String narrativeMessage1 = new String();
    String narrativeMessage2 = new String();
    String narrativeMessage3 = new String();
    
    protected EntityID           entityID_1          = new EntityID();
    protected EntityID           entityID_2          = new EntityID();
    protected EntityStatePdu     entityStatePdu_1;
    protected EntityStatePdu     entityStatePdu_2;
    protected FirePdu            firePdu_1a;
    protected FirePdu            firePdu_1b;
    protected MunitionDescriptor munitionDescriptor1;
    
    public ExampleSimulationProgram()
    {
        initialize();
    }

    public ExampleSimulationProgram(String newDescriptor)
    {
        descriptor = newDescriptor;
        initialize();
    }

    public ExampleSimulationProgram(String address, int port)
    {
        disChannel.setNetworkAddress(address);
        disChannel.setNetworkPort(port);
        disChannel.setVerboseComments(true); 
        disChannel.setVerboseDisNetworkInterface(true);
        disChannel.setVerbosePduRecorder(true);
        initialize();
    }
    
    private void initialize()
    {
        initializeDisChannel();
        initializeSimulationEntities();
        
        disChannel.join();
        
        String timeStepMessage = "Simulation timestep duration " + getSimulationTimeStepDuration() + " seconds";
        disChannel.sendCommentPdu(simulationTimeSeconds, DisChannel.COMMENTPDU_SIMULATION_TIMESTEP, timeStepMessage);
    }
    
    private void initializeDisChannel()
    {
        if (disChannel == null)
            disChannel = new DisChannel();
        else
        {
            disChannel.printlnTRACE ("*** warning, duplicate invocation of initializeDisChannel() ignored");
            return;
        }
        pduFactory     = disChannel.getPduFactory();
        disChannel.setDescriptor(this.getClass().getSimpleName());
        disChannel.setUpNetworkInterface();
        disChannel.printlnTRACE ("just checking: disChannel.getNetworkAddress()=" + disChannel.getNetworkAddress() +
                                                         ", getNetworkPort()="    + disChannel.getNetworkPort());
        disChannel.getDisNetworkInterface().setVerbose(true);
        disChannel.printlnTRACE ("just checking: hasVerboseSending()=" + disChannel.getDisNetworkInterface().hasVerboseSending() +
                                              ", hasVerboseReceipt()=" + disChannel.getDisNetworkInterface().hasVerboseReceipt());
        disChannel.getPduRecorder().setVerbose(true);
    }
    
    public void initializeSimulationEntities()
    {        
        if (pduFactory == null)
            pduFactory      = disChannel.getPduFactory();
        entityStatePdu_1    = pduFactory.makeEntityStatePdu();
        entityStatePdu_2    = pduFactory.makeEntityStatePdu();
        firePdu_1a          = pduFactory.makeFirePdu();
        firePdu_1b          = pduFactory.makeFirePdu();
        munitionDescriptor1 = new MunitionDescriptor();
        
        entityID_1.setSiteID((int)(Math.random() * 100))
                  .setApplicationID((int)(Math.random() * 100))
                  .setEntityID((int)(Math.random() * 100));
        disChannel.addEntity(entityID_1);
        
        entityID_2.setSiteID((int)(Math.random() * 100))
                  .setApplicationID((int)(Math.random() * 100))
                  .setEntityID((int)(Math.random() * 100));
        disChannel.addEntity(entityID_2);

        entityStatePdu_1.setEntityID(entityID_1);
        entityStatePdu_1.setForceId(ForceID.FRIENDLY);
        entityStatePdu_1.setEntityType(new _001Poseidon());
        entityStatePdu_1.setMarking("Ethan's Poseidon #1");

        entityStatePdu_2.setEntityID(entityID_2);
        entityStatePdu_2.setForceId(ForceID.OPPOSING);
        entityStatePdu_2.setEntityType(new _002Triton());
        entityStatePdu_2.setMarking("Ethan's Triton #2");

        munitionDescriptor1.setQuantity(1);
        firePdu_1a.setDescriptor(munitionDescriptor1).setRange(1000.0f);

    }
                 
    @SuppressWarnings("SleepWhileInLoop")
    public void runSimulationLoops ()
    {
      try
      {              
        final int SIMULATION_MAX_LOOP_COUNT = 10;
              int simulationLoopCount = 0;        
              boolean simulationComplete = false;
        
        String timeMessage = "Simulation time " + simulationTimeSeconds + " at LocalDateTime " + LocalDateTime.now();
        disChannel.sendCommentPdu(simulationTimeSeconds, DisChannel.COMMENTPDU_TIME, timeMessage);

        while (simulationLoopCount < SIMULATION_MAX_LOOP_COUNT)
        {
            simulationLoopCount++;
            
            entityStatePdu_1.getEntityLocation().setX(entityStatePdu_1.getEntityLocation().getX() + (Math.random() * 2));

            System.out.println ("... My simulation just did something...");
            System.out.flush();
            
            narrativeMessage1 = "Ethan's Custom Simulation Program";
            narrativeMessage2 = "Loop " + simulationLoopCount + " - MV3500 Custom Version";
            narrativeMessage3 = "Simulation developed by Ethan Williams";

            if (simulationLoopCount > MAX_LOOP_COUNT) 
            {
                simulationComplete = true;
            }

            Thread.sleep((long)(getSimulationTimeStepDuration() * 1000));
            System.out.println ("... [Pausing for " + getSimulationTimeStepDuration() + " seconds]");
            
            sendAllPdusForLoopTimestep(simulationTimeSeconds,
                                       entityStatePdu_1, 
                                            firePdu_1a, 
                                         DisChannel.COMMENTPDU_APPLICATION_STATUS, 
                                           narrativeMessage1, narrativeMessage2);
            disChannel.sendSinglePdu(simulationTimeSeconds, entityStatePdu_2);
            
            System.out.println ("... [PDUs of interest successfully sent for this loop]");
            System.out.flush();

            if (simulationComplete || (simulationLoopCount > 10000))
            {
                System.out.println ("... [loop termination condition met, simulationComplete=" + simulationComplete + "]");
                System.out.flush();
                break;
            }
            simulationTimeSeconds += getSimulationTimeStepDuration();      
        }   

        narrativeMessage2 = "runSimulation() completed successfully";
        disChannel.sendCommentPdu(DisChannel.COMMENTPDU_NARRATIVE, narrativeMessage1, narrativeMessage2, narrativeMessage3);
        System.out.println ("... [final=completion CommentPdu successfully sent for simulation]");
        
        disChannel.leave();
      } 
      catch (InterruptedException iex)
      {
        Logger.getLogger(ExampleSimulationProgram.class.getSimpleName()).log(Level.SEVERE, null, iex);
      }
    }

    public void sendAllPdusForLoopTimestep(double simTimeSeconds,
                                   EntityStatePdu entityStatePdu,
                                          FirePdu firePdu,
                               VariableRecordType commentType,
                                        String... comments)
    {
        if (entityStatePdu != null)
            disChannel.sendSinglePdu(simTimeSeconds, entityStatePdu);
            
        if (firePdu != null)
            disChannel.sendSinglePdu(simTimeSeconds, firePdu); 
        
        disChannel.sendCommentPdu(simTimeSeconds, commentType, comments);
    }
    
    protected void handleArguments (String[] args)
    {
        if (args.length == 2) 
        {
            if ((args[0] != null) && !args[0].isEmpty())
                thisProgram.disChannel.setNetworkAddress(args[0]);
            if ((args[1] != null) && !args[1].isEmpty())
                thisProgram.disChannel.setNetworkPort(Integer.parseInt(args[1]));
        }
        else if (args.length != 0) 
        {
            System.err.println("Usage: " + thisProgram.getClass().getSimpleName() + " [address port]");
            System.exit(-1);
        }
    }

    public String getDescriptor() {
        return descriptor;
    }

    public void setDescriptor(String newDescriptor) {
        if (newDescriptor == null)
            newDescriptor = "";
        this.descriptor = newDescriptor;
    }

    public double getSimulationTimeStepDuration() {
        return simulationTimeStepDuration;
    }

    public void setSimulationTimeStepDuration(double timeStepDurationSeconds) {
        this.simulationTimeStepDuration = timeStepDurationSeconds;
    }
    
    protected static ExampleSimulationProgram thisProgram;
  
    public static void main(String[] args)
    {
        thisProgram = new ExampleSimulationProgram("test constructor");
        
        thisProgram.disChannel.printlnTRACE("main() started...");
        
        thisProgram.handleArguments(args); 

        thisProgram.runSimulationLoops(); 
        
        thisProgram.disChannel.tearDownNetworkInterface();
        
        thisProgram.disChannel.printlnTRACE("complete.");
        
        System.exit(0);
    }
}