Class Workbench

  • All Implemented Interfaces:
    java.lang.AutoCloseable

    public class Workbench
    extends java.lang.Object
    implements java.lang.AutoCloseable
    The Workbench is used to represent an environment where developers can test real-time and simulated digital twins.

    Quick start:

    Build a real-time digital twin model for testing real-time message processing with messages generated by a simulated digital twin.

    The real-time model will represent a car and the simulated digital twin will represent a pump increasing the real-time car's tire pressure. The real-time car will process messages from the simulated pump and send information back to the simulated pump when the tire is full.

    The quickstart will demonstrate the following:

    • Define a real-time car digital twin
    • Define a tire pressure change message
    • Define a real-time car message processor
    • Define a simulated pump digital twin
    • Define a simulated pump message processor
    • Define a simulated pump simulation processor

    Defining the real-time car model:

    Create a class that extends the DigitalTwinBase class and add an integer property for tire pressure. When constructed by the Workbench this will be our real-time car instance.

         public class RealTimeCar extends DigitalTwinBase {
             private int _tirePressure;
             public RealTimeCar() { _tirePressure=0; }
             public RealTimeCar(int startingTirePressure) {
                 _tirePressure = startingTirePressure;
             }
    
             public void incrementTirePressure(int increment) {
                 _tirePressure += increment;
             }
    
             public int getTirePressure() {
                 return _tirePressure;
             }
         }
     

    Defining the tire pressure change message:

    Implement a message to send from the simulated pump, to the real-time model. The message will tell the real-time car to increase the tire pressure by some value.

         public class TirePressureMessage {
             final int _pressureChange;
             public TirePressureMessage(int pressureChange) {
                 _pressureChange = pressureChange;
             }
    
             public int getPressureChange() {
                 return _pressureChange;
             }
         }
     

    Defining the real-time car message processor:

    Create the real-time car MessageProcessor. The message processor will apply the tire pressure change from the tire pressure message to the real-time car digital twin instance. When the tire is full, the message processor will send a message back to the simulated pump.

         public class RealTimeCarMessageProcessor extends MessageProcessor<RealTimeCar, TirePressureMessage> implements Serializable {
             final int TIRE_PRESSURE_FULL = 100;
             public ProcessingResult processMessages(ProcessingContext processingContext, RealTimeCar car, Iterable<TirePressureMessage> messages) throws Exception {
                 // apply the updates from the messages
                 for(TirePressureMessage message : messages) {
                     car.incrementTirePressure(message.getPressureChange());
                 }
                 if(car.getTirePressure() > TIRE_PRESSURE_FULL) {
                     processingContext.sendToDataSource(new TirePressureMessage(car.getTirePressure()));
                 }
                 return ProcessingResult.UpdateDigitalTwin;
             }
         }
     

    Defining the simulated pump model:

    Create a class that extends the DigitalTwinBase class and add a double property for tire pressure change. When constructed by the Workbench this will be our simulated pump instance.

         public class SimulatedPump extends DigitalTwinBase {
         private double _tirePressureChange;
         private boolean _tirePressureReached = false;
         public SimulatedPump() {}
         public SimulatedPump(double pressureChange) {
             _tirePressureChange = pressureChange;
         }
    
         public double getTirePressureChange() {
             return _tirePressureChange;
         }
    
         public void setTirePressureReached() {
             _tirePressureReached = true;
         }
    
         public boolean isTireFull() {
             return _tirePressureReached;
         }
     }
     

    Defining the simulated pump message processor:

    The simulated pump should stop when the simulated pump message processor receives a message. The simulated pump message processor will update the state of the simulated pump indicating that the tire is full.

         public class PumpMessageProcessor extends MessageProcessor<SimulatedPump, TirePressureMessage> implements Serializable {
             public ProcessingResult processMessages(ProcessingContext processingContext, SimulatedPump pump, Iterable<TirePressureMessage> messages) throws Exception {
                 // apply the updates from the messages
                 pump.setTirePressureReached();
                 return ProcessingResult.UpdateDigitalTwin;
             }
         }
     

    Defining the pump simulation processor:

    Define the simulated pump SimulationProcessor. This piece of code will be called at each simulation interval so long as the simulation has instances to run. This pump simulation processor will send a message to the real-time car with a tire pressure change derived from the state of the simulated pump. While the simulated pump has not been told to stop, it will continue sending tire pressure changes to the real-time car.

         public class PumpSimulationProcessor extends SimulationProcessor<SimulatedPump> implements Serializable {
             public ProcessingResult processModel(ProcessingContext processingContext, SimulatedPump simPump, Date date) {
                 SimulationController controller = processingContext.getSimulationController();
                 if(simPump.isTireFull()) {
                     controller.deleteThisInstance();
                 } else {
                     int change = (int) (100 * simPump.getTirePressureChange());
                     controller.emitTelemetry("RealTimeCar", new TirePressureMessage(change));
                 }
                 return ProcessingResult.UpdateDigitalTwin;
             }
         }
     

    Using the workbench:

    The real-time and simulation models are complete. The workbench can now load up the models and then run a simulation. When beginning testing, the "step loop" is used to track the state of the twins and the simulation. Instantiate the workbench and add the models.

         Workbench workbench = new Workbench();
         workbench.addRealTimeModel("RealTimeCar", new RealTimeCarMessageProcessor(), RealTimeCar.class, TirePressureMessage.class);
         workbench.addSimulationModel("SimPump", new SimulatedPumpMessageProcessor(), new PumpSimulationProcessor(), SimulationPump.class, TirePressureMessage.class);
     

    The workbench is loaded up with the models. Add a single simulated pump instance. Note that no real-time car digital twin is created and added to the workbench. The first message from the simulated pump digital twin will cause the workbench to create a new real-time instance.

         workbench.addInstance("SimPump", "23", new SimulationPump(0.29d));
     

    Initialize the simulation and then step through the simulation intervals. Start the simulation now and end the simulation in 60 seconds.

         SimulationStep step = workbench.initializeSimulation(System.currentTimeMillis(), System.currentTimeMillis()+60000, 1000);
     

    At each interval, view the state of the real-time car to ensure the tire pressure is changing as expected.

         while(step.getStatus() == SimulationStatus.Running) {
             step = workbench.step();
             HashMap<String, DigitalTwinBase> realTimeCars = workbench.getInstances("RealTimeCar");
             RealTimeCar rtCar = (RealTimeCar) realTimeCars.get("23");
             System.out.println("rtCar: " + rtCar.getTirePressure());
         }
     

    Summary:

    The simulated pump at each simulation step emits telemetry to the real-time car digital twin. With each tire pressure change message, the real-time car twin accrues state about the pressure of the tire. When the real-time car twins tire is full, it sends a message back to the simulated pump. When the simulated pump receives a message from the real-time car, it updates some internal state indicating to stop pumping. During the next simulation interval, the simulated pump deletes itself to stop pumping. This completes the simulation as there are no more remaining simulated pumps.

    • Constructor Summary

      Constructors 
      Constructor Description
      Workbench()
      Instantiate the workbench.
    • Method Summary

      Modifier and Type Method Description
      void addAlertProvider​(java.lang.String modelName, AlertProviderConfiguration configuration)
      Adds an alert provider configuration to the specified model on this workbench.
      void addInstance​(java.lang.String modelName, java.lang.String id, DigitalTwinBase instance)
      Adds a digital twin instance to the workbench.
      <T extends DigitalTwinBase,​V>
      void
      addRealTimeModel​(java.lang.String modelName, MessageProcessor<T,​V> digitalTwinMessageProcessor, java.lang.Class<T> dtType, java.lang.Class<V> messageClass)
      Adds a real-time digital twin model to the workbench.
      <T extends DigitalTwinBase,​V>
      void
      addSimulationModel​(java.lang.String modelName, MessageProcessor<T,​V> digitalTwinMessageProcessor, SimulationProcessor<T> simulationProcessor, java.lang.Class<T> dtType, java.lang.Class<V> messageClass)
      Adds a simulation digital twin model to the workbench.
      void close()  
      java.lang.String generateModelSchema​(java.lang.String modelName)
      Generates a ModelSchema for the defined model
      java.lang.String generateModelSchema​(java.lang.String modelName, java.lang.String outputDirectory)
      Generates a ModelSchema for the parameter modelName and writes the schema to a file on the file system.
      java.util.List<AlertMessage> getAlertMessages​(java.lang.String model, java.lang.String alertProvider)
      Retrieves alert messages from digital twin instances.
      java.util.HashMap<java.lang.String,​DigitalTwinBase> getInstances​(java.lang.String modelName)
      Retrieves DigitalTwin instances for a given model.
      java.util.List<LogMessage> getLoggedMessages​(java.lang.String model, long timestamp)
      Retrieves messages logged by digital twin instances for a specified mdoel.
      java.util.Date getTime()
      Retrieves the current time interval of the simulation.
      SimulationStep initializeSimulation​(long startTime, long endTime, long interval)
      Initializes the simulation so that each interval can be run separately by calling the step() function.
      java.util.Date peek()
      Retrieves the next interval time of the simulation.
      SimulationStep runSimulation​(long startTime, long endTime, double speedup, long interval)
      Runs a simulation from the given startTime until the given endTime OR there is no more work to do.
      SendingResult send​(java.lang.String modelName, java.lang.String id, java.util.List<java.lang.Object> messages)
      Send a list of messages to a real-time or simulation model.
      SimulationStep step()
      Run the next simulation interval.
      • Methods inherited from class java.lang.Object

        clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    • Constructor Detail

      • Workbench

        public Workbench()
        Instantiate the workbench.
    • Method Detail

      • addRealTimeModel

        public <T extends DigitalTwinBase,​V> void addRealTimeModel​(java.lang.String modelName,
                                                                         MessageProcessor<T,​V> digitalTwinMessageProcessor,
                                                                         java.lang.Class<T> dtType,
                                                                         java.lang.Class<V> messageClass)
                                                                  throws WorkbenchException
        Adds a real-time digital twin model to the workbench.
        Type Parameters:
        T - the type of the digital twin.
        V - the type of the message.
        Parameters:
        modelName - the name of the model.
        digitalTwinMessageProcessor - the model's MessageProcessor implementation. Must be marked as Serializable.
        dtType - the model's DigitalTwinBase implementation.
        messageClass - the model's message type.
        Throws:
        WorkbenchException - if any of the parameters are null or the model does not pass validation (the message processor must be serializable, and the digital twin implementation must have a parameterless constructor).
      • addSimulationModel

        public <T extends DigitalTwinBase,​V> void addSimulationModel​(java.lang.String modelName,
                                                                           MessageProcessor<T,​V> digitalTwinMessageProcessor,
                                                                           SimulationProcessor<T> simulationProcessor,
                                                                           java.lang.Class<T> dtType,
                                                                           java.lang.Class<V> messageClass)
                                                                    throws WorkbenchException
        Adds a simulation digital twin model to the workbench.
        Type Parameters:
        T - the type of the digital twin.
        V - the type of the message.
        Parameters:
        modelName - the name of the model.
        digitalTwinMessageProcessor - the model's MessageProcessor implementation. Must be marked as Serializable.
        simulationProcessor - the model's SimulationProcessor implementation. Must be marked as Serializable.
        dtType - the model's DigitalTwinBase implementation.
        messageClass - the model's message type.
        Throws:
        WorkbenchException - if any of the parameters are null or the model does not pass validation (the message processor must be serializable, and the digital twin implementation must have a parameterless constructor).
      • runSimulation

        public SimulationStep runSimulation​(long startTime,
                                            long endTime,
                                            double speedup,
                                            long interval)
                                     throws WorkbenchException
        Runs a simulation from the given startTime until the given endTime OR there is no more work to do. A simulation has reached the end time when the time to run the next interval is greater than the end time or there are no more simulated twins to run.
        Parameters:
        startTime - the start time of the simulation.
        endTime - the end time of the simulation.
        speedup - the speedup of the interval (in real-time).
        interval - the interval between simulation steps.
        Returns:
        a SimulationStep that details the final runtime and the SimulationStatus.
        Throws:
        WorkbenchException - if an exception is thrown by the simulated model or real-time model.
      • initializeSimulation

        public SimulationStep initializeSimulation​(long startTime,
                                                   long endTime,
                                                   long interval)
        Initializes the simulation so that each interval can be run separately by calling the step() function.
        Parameters:
        startTime - the start time of the simulation.
        endTime - the end time of the simulation.
        interval - the interval between simulation steps.
        Returns:
        a SimulationStep that details the startTime and the SimulationStatus -- which will always be SimulationStatus.Running.
      • getTime

        public java.util.Date getTime()
                               throws WorkbenchException
        Retrieves the current time interval of the simulation.
        Returns:
        a Date representation for the current interval time for the simulation.
        Throws:
        WorkbenchException - if the simulation is not started or initialized.
      • peek

        public java.util.Date peek()
                            throws WorkbenchException
        Retrieves the next interval time of the simulation.
        Returns:
        a Date representation for the next interval time for the simulation.
        Throws:
        WorkbenchException - if the simulation is not started or initialized.
      • getInstances

        public java.util.HashMap<java.lang.String,​DigitalTwinBase> getInstances​(java.lang.String modelName)
                                                                               throws WorkbenchException
        Retrieves DigitalTwin instances for a given model.
        Parameters:
        modelName - the digital twin model name
        Returns:
        the instances associated with the parameter model
        Throws:
        WorkbenchException - if an exception occurs while retrieving digital twin instances for the parameter modelName
      • getLoggedMessages

        public java.util.List<LogMessage> getLoggedMessages​(java.lang.String model,
                                                            long timestamp)
        Retrieves messages logged by digital twin instances for a specified mdoel. If the provided timestamp is 0, all messages will be returned. Timestamps greater than 0 will return a sublist of logged messages where the first message in the returned list will be greater than the provided timestamp. If no messages exist after the timestamp, the returned list will be empty.
        Parameters:
        model - the model name for the logged messages.
        timestamp - the timestamp used to filter the retrieved list.
        Returns:
        the list of messages defined by the timestamp
      • getAlertMessages

        public java.util.List<AlertMessage> getAlertMessages​(java.lang.String model,
                                                             java.lang.String alertProvider)
                                                      throws WorkbenchException
        Retrieves alert messages from digital twin instances.
        Parameters:
        model - the model to retrieve alert messages from.
        alertProvider - the alert provider that generated the alerts.
        Returns:
        the list of alert messages generated by digital twin instances.
        Throws:
        WorkbenchException - if an exception occurs while retrieving logged messages.
      • generateModelSchema

        public java.lang.String generateModelSchema​(java.lang.String modelName)
                                             throws WorkbenchException
        Generates a ModelSchema for the defined model
        Parameters:
        modelName - the digital twin model's name to generate a schema.
        Returns:
        a JSON string of the model's schema
        Throws:
        WorkbenchException - if an exception occurs while generating a model schema.
      • generateModelSchema

        public java.lang.String generateModelSchema​(java.lang.String modelName,
                                                    java.lang.String outputDirectory)
                                             throws WorkbenchException
        Generates a ModelSchema for the parameter modelName and writes the schema to a file on the file system. If the parameter outputDirectory is null the file will be written to the working directory of the JVM.
        Parameters:
        modelName - the name of the digital twin model
        outputDirectory - the directory to write the file to, or null to write the file to the current working directory.
        Returns:
        the full file path of the model.json schema file
        Throws:
        WorkbenchException - if an exception occurs while generating a model schema.
      • close

        public void close()
                   throws java.lang.Exception
        Specified by:
        close in interface java.lang.AutoCloseable
        Throws:
        java.lang.Exception