Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

Simulators are programs that attempt to duplicate real life in software. For thi

ID: 3571967 • Letter: S

Question

Simulators are programs that attempt to duplicate real life in software. For this question, you will use well-formed techniques and design patterns to construct a portion of a simulator. As in question 1, you will need to provide a readme file making explicit the well-formed techniques and the design patterns you used to answer this question. One of the most fundamental parts of a simulator is the World and those items that populate the World. Often a simple way to represent a world is using a 2D array. This array is imagined to be the ground. Items populate this array. Some items are immoveable, others are moveable, and others move autonomously. We want to maximize good programming techniques to implement this simulator. You can use class, interface, and abstract when creating your objects. I am assuming you will use everything you have learned when designing and choosing your programming constructs. This includes inheritance, controls, and design patterns. You will need to do the following: World: It contains a 2D array that can store one Moveable, Immoveable, and Autonomous object per cell of the array. This World class has a public method called "void step()" that iterates once changing the state of the world by updating the position of all the Autonomous and Moveable objects. It also calls the method display() (see below). The World also as a "void add(item)" that is used to create the world by adding items to the array. The World constructor defines the size of the array. A third method "void display()" prints the array to the screen by calling the "char getToken()" method of the item. This permits the user to see state change. Display the world in an way that is easy to view. Immovable: An object with a string name describing what it is. It also has a character variable that stores a symbol that represents the item. The method "char getToken()" returns the character symbol. If you want a greater challenge, you can replace the symbol by a graphic picture. Moveable: is implemented exactly as Immovable, however it can be moved by one cell position if bumped into by an Autonomous object. It is displaced in the same direction as it was bumped. Autonomous: is implemented exactly like Moveable (bumped by a Moveable object or another Autonomous object causes it to shift one cell in the direction it was bumped). This object has a "void step()" method that randomly picks a direction and updates the symbol's array location to the new location by one cell. Construct a main method that builds a world and then runs a simulation for 100 iterations.

Explanation / Answer


import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.LinkedList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.logging.log4j.Logger;
import org.openlvc.disco.IPduListener;
import org.openlvc.disco.OpsCenter;
import org.openlvc.disco.configuration.DiscoConfiguration;
import org.openlvc.disco.pdu.PDU;
import org.openlvc.disco.utils.BitHelpers;

public class Recorder implements IPduListener
{
  
   private Configuration configuration;
   private Logger logger;

   // DIS Settings
   private DiscoConfiguration discoConfiguration;
   private OpsCenter opscenter;

   // Packet management
   private long openingTimestamp;
   private BlockingQueue<Track> buffer;
  
   // Session Writing
   private File sessionFile;
   private Thread sessionWriter;
   private FileOutputStream fos;
   private BufferedOutputStream bos;
   private long pdusWritten;
   private long bytesWritten;
  
  
   public Recorder( Configuration configuration )
   {
       this.configuration = configuration;
       this.logger = configuration.getLogger();
      
       // DIS Settings
       this.discoConfiguration = null; // set in execute()
       this.opscenter = null; // set in execute()
      
       // Packet management
       this.openingTimestamp = 0; // set in execute()
       this.buffer = new LinkedBlockingQueue<>();
      
       // Session Writing
       this.sessionFile = new File( configuration.getFilename() );
       this.sessionWriter = new Thread( new SessionWriter(), "SessionWriter" );
       this.fos = null; // set in execute()
       this.bos = null; // set in execute()
       this.pdusWritten = 0;
       this.bytesWritten = 0;
   }

  
   public void execute()
   {
       logger.info( "Mode: Record" );

      
       this.openSession();
      
      
       this.discoConfiguration = new DiscoConfiguration();
       discoConfiguration.getLoggingConfiguration().disable();
       discoConfiguration.getUdpConfiguration().setAddress( configuration.getDisAddress() );
       discoConfiguration.getUdpConfiguration().setPort( configuration.getDisPort() );
       discoConfiguration.getUdpConfiguration().setNetworkInterface( configuration.getDisInterface() );
       discoConfiguration.getDisConfiguration().setExerciseId( configuration.getDisExerciseId() );
       // PDU processing options
       discoConfiguration.setPduSender( configuration.getPduSender() );
       discoConfiguration.setPduReceiver( configuration.getPduReceiver() );
      
       this.opscenter = new OpsCenter( discoConfiguration );
       this.opscenter.setListener( this );
      
       //
       // Kick things off
       //
       this.openingTimestamp = System.currentTimeMillis();
       this.sessionWriter.start();
       this.opscenter.open();
       logger.info( "Recorder is active - DO YOUR WORST" );
   }

   public void shutdown()
   {
       // Shut down the DIS receiving thread
       this.opscenter.close();
      
       // Stop the Session Writer
       this.sessionWriter.interrupt();
       try
       {
           this.sessionWriter.join();
           this.fos.close();
           logger.info( "Session writer has shutdown" );
           logger.info( "Captured %,d PDUs (%,d bytes)", pdusWritten, bytesWritten );
       }
       catch( InterruptedException ie )
       {
           logger.warn( "Interrupted while waiting for SessionWriter to stop: "+ie.getMessage(), ie );
       }
       catch( IOException ioex )
       {
           logger.warn( "Exception while closing session file: "+ioex.getMessage(), ioex );
       }
   }

  
   public void receiver( PDU pdu )
   {
       this.buffer.add( new Track(pdu) ); // non-blocking call
   }

  
   private void openSession()
   {
       if( this.sessionFile.exists() )
           logger.warn( "Session file exists. Will overwrite: "+sessionFile );
      
       try
       {
           this.fos = new FileOutputStream( sessionFile );
       }
       catch( IOException ioex )
       {
           logger.error( "Could not open session file for writing: "+ioex.getMessage(), ioex );
       }
      
       this.bos = new BufferedOutputStream( this.fos );
       logger.info( "Session file is open and ready for writing" );
   }
  
  
   private class Track
   {
       public long timestamp;
       public PDU pdu;
       public Track( PDU pdu )
       {
           this.pdu = pdu;
           this.timestamp = System.currentTimeMillis() - openingTimestamp;
       }
   }

  
   private class SessionWriter implements Runnable
   {
       public void run()
       {
           LinkedList<Track> flushList = new LinkedList<>();
           while( Thread.interrupted() == false )
           {
               flushList.clear();

               //
               // Drain all available PDUs
               //
               // Get the first PDU, blocking until it turns up
               try
               {
                   flushList.add( buffer.take() );
               }
               catch( InterruptedException ie )
               {
                   // time to exit
                   return;
               }

               // We've got the first PDU, if there are more just keep going, but don't block
               while( buffer.peek() != null )
                   flushList.add( buffer.poll() );

               //
               // Flush PDUs to disk
               //
               for( Track track : flushList )
               {
                   try
                   {
                       byte[] pdubytes = track.pdu.toByteArray();
                       bos.write( BitHelpers.longToBytes(track.timestamp) );
                       bos.write( BitHelpers.shortToBytes((short)pdubytes.length) );
                       bos.write( pdubytes );
                       pdusWritten++;
                       bytesWritten += (pdubytes.length+9);
                   }
                   catch( IOException ioex )
                   {
                       logger.warn( "Failed to write PDU to log file: "+ioex.getMessage(), ioex );
                   }
               }
           }
       }
   }
}


public class Main
{
  
  
   private String[] args;
   private Recorder recorder; // store so we can access from shutdown hook
   private Replay replay; // store so we can access from shutdown hook

  
   private Main( String[] args )
   {
       this.args = args;
       this.recorder = null;
       this.replay = null;
   }

  
   private void runMain()
   {
       // register the shutdown hook so we can gracefully exit
       Runtime.getRuntime().addShutdownHook( new ShutdownHook() );
      
       // parse the full command line and start a recorder
       Configuration configuration = new Configuration( args );

       // print a welcome message
       printWelcome( configuration.getLogger() );
      
       // start recording or replaying - whichever we have been told to
       if( configuration.isRecording() )
       {
           this.recorder = new Recorder( configuration );
           this.recorder.execute();
       }
       else
       {
           this.replay = new Replay( configuration );
           this.replay.execute();
       }
   }

  
   public static void main( String[] args )
   {
       if( args.length == 0 )
       {
           Configuration.printHelp();
           return;
       }
      
       // check the args for the --help command
       for( String temp : args )
       {
           if( temp.equalsIgnoreCase("--help") )
           {
               Configuration.printHelp();
               return;
           }
       }
      
       // Run this thing
       new Main(args).runMain();
   }
  
   public static void printWelcome( Logger logger )
   {
       logger.info( " Welcome to the Open LVC DIS Duplicator " );
      
   }
  
   public class ShutdownHook extends Thread
   {
       @Override
       public void run()
       {
           if( recorder != null )
               recorder.shutdown();
          
           if( replay != null )
               replay.shutdown();
       }
   }
}

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.openlvc.disco.DiscoException;
import org.openlvc.disco.configuration.Log4jConfiguration;

public class Configuration
{
  
   private boolean recording; // recording or replaying?
   private String filename; // file to read/write

   // Logging
   private Log4jConfiguration appLoggerConfiguration;
   private Logger applicationLogger;
  
   // DIS Settings
   private String disAddress; // address or "BROADCAST"
   private int disPort; // port to listen on
   private String disInterface; // IP or nic to use or one of the symbols "LOOPBACK",
   // "LINK_LOCAL", "SITE_LOCAL", "GLOBAL"
   private short disExerciseId;
  
   // PDU Processing
   private String pduSender;
   private String pduReceiver;

   // Replay Settings
   private boolean replayRealtime; // Should replay proceed in real time (with waits between PDUs)

  
   public Configuration()
   {
       this.recording = true;
       this.filename = "duplicator.session";
      
       // Logging
       this.appLoggerConfiguration = new Log4jConfiguration( "duplicator" );
       this.appLoggerConfiguration.setFileOn( false );
       this.appLoggerConfiguration.setConsoleOn( true );
       this.appLoggerConfiguration.setLevel( "INFO" );
       this.applicationLogger = null; // set on first access
      
       // DIS Settings
       this.disAddress = "BROADCAST";
       this.disPort = 3000;
       this.disInterface = "SITE_LOCAL";
       this.disExerciseId = 1;
      
       // PDU Processing
       this.pduSender = null;
       this.pduReceiver = null;
      
       // Replay Settings
       this.replayRealtime = true;
   }

   public Configuration( String[] commandline ) throws DiscoException
   {
       this();
       applyCommandLine( commandline );
   }

  
   public void applyCommandLine( String[] args ) throws DiscoException
   {
       for( int i = 0; i < args.length; i++ )
       {
           String argument = args[i];
           if( argument.equalsIgnoreCase("--record") )
               this.setToRecording();
           else if( argument.equalsIgnoreCase("--replay") )
               this.setToReplaying();
           else if( argument.equalsIgnoreCase("--file") )
               this.setFilename( args[++i] );
           else if( argument.equalsIgnoreCase("--dis-address") )
               this.setDisAddress( args[++i] );
           else if( argument.equalsIgnoreCase("--dis-port") )
               this.setDisPort( Integer.parseInt(args[++i]) );
           else if( argument.equalsIgnoreCase("--dis-interface") )
               this.setDisInterface( args[++i] );
           else if( argument.equalsIgnoreCase("--dis-exercise-id") || argument.equalsIgnoreCase("--dis-exerciseId") )
               this.setDisExerciseId(Short.parseShort(args[++i]) );
           else if( argument.equalsIgnoreCase("--replay-realtime") )
               this.setReplayRealtime( true );
           else if( argument.equalsIgnoreCase("--replay-fast") )
               this.setReplayRealtime( false );
           else if( argument.equalsIgnoreCase("--log-level") )
               this.setLogLevel( args[++i] );
           else if( argument.equalsIgnoreCase("--pdu-sender") )
               this.setPduSender( args[++i] );
           else if( argument.equalsIgnoreCase("--pdu-receiver") )
               this.setPduReceiver( args[++i] );
           else
               throw new DiscoException( "Unknown argument: "+argument );
       }
   }
  
  
   public Logger getLogger()
   {
       if( this.applicationLogger == null )
       {
           this.appLoggerConfiguration.activateConfiguration();
           this.applicationLogger = LogManager.getFormatterLogger( "duplicator" );
       }

       return this.applicationLogger;
   }
  
   public boolean isRecording()
   {
       return recording;
   }

   public boolean isReplaying()
   {
       return this.recording == false;
   }

   public void setToRecording()
   {
       this.recording = true;
   }
  
   public void setToReplaying()
   {
       this.recording = false;
   }

   public String getFilename()
   {
       return filename;
   }

   public void setFilename( String filename )
   {
       this.filename = filename;
   }

   public String getDisAddress()
   {
       return disAddress;
   }

   public void setDisAddress( String disAddress )
   {
       this.disAddress = disAddress;
   }

   public int getDisPort()
   {
       return disPort;
   }

   public void setDisPort( int disPort )
   {
       this.disPort = disPort;
   }

   public String getDisInterface()
   {
       return disInterface;
   }

   public void setDisInterface( String disInterface )
   {
       this.disInterface = disInterface;
   }
  
   public void setDisExerciseId( short id )
   {
       this.disExerciseId = id;
   }
  
   public short getDisExerciseId()
   {
       return this.disExerciseId;
   }

   public void setPduSender( String pduSender )
   {
       this.pduSender = pduSender;
   }

   public String getPduSender()
   {
       return this.pduSender;
   }

   public void setPduReceiver( String pduReceiver )
   {
       this.pduReceiver = pduReceiver;
   }

   public String getPduReceiver()
   {
       return this.pduReceiver;
   }

   public void setReplayRealtime( boolean replayRealtime )
   {
       this.replayRealtime = replayRealtime;
   }

  
   public boolean isReplayRealtime()
   {
       return this.replayRealtime;
   }
  
   public void setLogLevel( String level )
   {
       this.appLoggerConfiguration.setLevel( level );
   }

  
}