Class Workbench
- All Implemented Interfaces:
AutoCloseable
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
-
Method Summary
Modifier and TypeMethodDescriptionvoid
addAlertProvider
(String modelName, AlertProviderConfiguration configuration) Adds an alert provider configuration to the specified model on this workbench.void
addInstance
(String modelName, String id, DigitalTwinBase instance) Adds a digital twin instance to the workbench.<T extends DigitalTwinBase,
V>
voidaddRealTimeModel
(String modelName, MessageProcessor<T, V> digitalTwinMessageProcessor, Class<T> dtType, Class<V> messageClass) Adds a real-time digital twin model to the workbench.<T extends DigitalTwinBase,
V>
voidaddSimulationModel
(String modelName, MessageProcessor<T, V> digitalTwinMessageProcessor, SimulationProcessor<T> simulationProcessor, Class<T> dtType, Class<V> messageClass) Adds a simulation digital twin model to the workbench.void
close()
generateModelSchema
(String modelName) Generates a ModelSchema for the defined modelgenerateModelSchema
(String modelName, String outputDirectory) Generates a ModelSchema for the parameter modelName and writes the schema to a file on the file system.getAlertMessages
(String model, String alertProvider) Retrieves alert messages from digital twin instances.getInstances
(String modelName) Retrieves DigitalTwin instances for a given model.getLoggedMessages
(String model, long timestamp) Retrieves messages logged by digital twin instances for a specified mdoel.getTime()
Retrieves the current time interval of the simulation.initializeSimulation
(long startTime, long endTime, long interval) Initializes the simulation so that each interval can be run separately by calling thestep()
function.peek()
Retrieves the next interval time of the simulation.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.Send a list of messages to a real-time or simulation model.step()
Run the next simulation interval.
-
Constructor Details
-
Workbench
public Workbench()Instantiate the workbench.
-
-
Method Details
-
addRealTimeModel
public <T extends DigitalTwinBase,V> void addRealTimeModel(String modelName, MessageProcessor<T, V> digitalTwinMessageProcessor, Class<T> dtType, Class<V> messageClass) throws WorkbenchExceptionAdds 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'sMessageProcessor
implementation. Must be marked asSerializable
.dtType
- the model'sDigitalTwinBase
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(String modelName, MessageProcessor<T, V> digitalTwinMessageProcessor, SimulationProcessor<T> simulationProcessor, Class<T> dtType, Class<V> messageClass) throws WorkbenchExceptionAdds 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'sMessageProcessor
implementation. Must be marked asSerializable
.simulationProcessor
- the model'sSimulationProcessor
implementation. Must be marked asSerializable
.dtType
- the model'sDigitalTwinBase
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).
-
addInstance
public void addInstance(String modelName, String id, DigitalTwinBase instance) throws WorkbenchException Adds a digital twin instance to the workbench. Instances cannot be added to the workbench afterrunSimulation(long, long, double, long)
orinitializeSimulation(long, long, long)
has been called.- Parameters:
modelName
- the instances model.id
- the instance identifier.instance
- the real-time or simulation instance.- Throws:
WorkbenchException
- If the model does not exist or if a simulation is already running.
-
addAlertProvider
public void addAlertProvider(String modelName, AlertProviderConfiguration configuration) throws WorkbenchException Adds an alert provider configuration to the specified model on this workbench. Alert provider configurations cannot be added to the workbench afterrunSimulation(long, long, double, long)
orinitializeSimulation(long, long, long)
has been called.- Parameters:
modelName
- the instances model.configuration
- the alert provider configuration.- Throws:
WorkbenchException
- If the model does not exist or if a simulation is already running.
-
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 theSimulationStatus
. - Throws:
WorkbenchException
- if an exception is thrown by the simulated model or real-time model.
-
initializeSimulation
Initializes the simulation so that each interval can be run separately by calling thestep()
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 theSimulationStatus
-- which will always beSimulationStatus.Running
.
-
step
Run the next simulation interval.- Returns:
- a
SimulationStep
that shows the time interval that was run and the correspondingSimulationStatus
- Throws:
WorkbenchException
- if an exception is thrown by the simulated model or real-time model.
-
getTime
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
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
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
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 List<AlertMessage> getAlertMessages(String model, 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
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 String generateModelSchema(String modelName, 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 modeloutputDirectory
- 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.
-
send
public SendingResult send(String modelName, String id, List<Object> messages) throws WorkbenchException Send a list of messages to a real-time or simulation model.- Parameters:
modelName
- The model name.id
- the instance id.messages
- the messages to send.- Returns:
SendingResult.Handled
unless an exception is thrown.- Throws:
WorkbenchException
- if model name, id, or messages are null. Also thrown if the model'sMessageProcessor.processMessages(ProcessingContext, DigitalTwinBase, Iterable)
throws an exception.
-
close
- Specified by:
close
in interfaceAutoCloseable
- Throws:
Exception
-