• No results found

Simulating Physical Systems

In document OpenSCADA Documentation (Page 34-38)

OpenSCADA PLCs can communicate with simulations of physical systems or processes. To interact with PLCs, a proxy Sensor/Actuator driver can be built using the GRPC API already discussed in the Section PLC I/O.

An abstract python class called PhysicalSystemSim is defined inphysical_system_sim.pyand every physical system simulation must implement an object derived from this class:

class PhysicalSystemSim:

"""Generic Physical system simulation abstract class.

Any physical system simulator which needs to interact with OpenSCADA PLCs needs to implement this Class.

(continues on next page)

OpenSCADA Documentation, Release 1.0

(continued from previous page)

"""

__metaclass__ = ABCMeta def __init__(self, **kwargs):

pass

@abstractmethod

def progress(self, timestep_secs):

"""This method is called internally to advance the simulation by a step size.

The OpenSCADA emulation_driver.py invokes this function. A physical system simulator should implement the logic to advance its simulation by

timestep_secs. During this progress, it may set sensor input's of PLCs and get Actuator outputs from PLC's and act accordingly.

Args:

timestep_secs (float): Timestep to advance in secs.

Returns:

A specific implementation of a physical system simulation must inherit from this abstract class and register itself with an object of type EmulationDriver which is a built-in class defined insideemulation_driver.py. The progress method needs to be implemented by the inherited object. It is called implicitly by the emulation_driver and when upon each call, the logic embedded in the physical system simulation should be advance the simulation for the specified duration in seconds and pause it. During this progress, the logic may set sensor input’s of PLCs and get Actuator outputs from PLCs.

An example pendulum simulator is included with the installation for reference. It is implemented inpendulum_sim.py file located inside examples/inverted_pendulum. This example implements a cart pole which can move left or right inresponse to an applied force. A PLC reads the current angle of the pendulum and applies force on the cart to move it left or right so that the pendulum stays upright.

The pendulum simulator implements the progress method. Inside this method, it gets the actuator output (which is force applied on the cart) and applies it in the simulation. It then sets the current pendulum angle as a sensor input to the PLC. It uses the GRPC API described in Section PLC I/O to interact with the attached PLC.

In the next section, we build on this discussion and show how to use the built-in EmulationDriver class to configure and run time synchronized OpenSCADA PLCs and physical system simulations.

3.10 Kronos driven co-simulation

In this section, we discuss how Kronos can be used to run time synchronized OpenSCADA PLCs and physical system simulations. This discussion is based on theinverted_pendulumexample included with the installation. We will refer the scriptsimulation.pyin the rest of this discussion.

3.10.1 Initializing the co-simulation

• The first step in running a time synchronized co-simulation involves creating a physical system simulator ob-ject. As discussed in Section, Simulating Physical Systems, this object must inherit from PhysicalSystemSim

3.10. Kronos driven co-simulation 31

abstract class defined inphysical_system_sim.py. In the running example, a class called PendulumSimulator is implemented and an object of this class is instantiated and used for this purpose:

pendulum_sim = PendulumSimulator()

• The next step involves initializing an EmulationDriver object (defined inemulation_driver.py) and register the previously created physical system simulator object. This step also assumes that Kronos module is installed and loaded:

emulation = EmulationDriver(number_dilated_nodes=num_dilated_nodes, \ is_virtual=is_virtual, \

n_insns_per_round=num_insns_per_round, rel_cpu_speed=rel_cpu_speed, \

physical_system_sim_driver=pendulum_sim)

The emulation driver class takes in some Kronos specific arguments which are briefly described here. For a more thorough discussion, please referKronosdocumentation.

• is_virtual: If True Kronos is initialized

• physical_system_driver: An object which implements PhysicalSystemSim abstract class defined in con-tib/physical_system_sim.py. If it is None, then it denotes that this co-simulation has no attached physical simulator.

• number_dilated_nodes: Ignored unless is_virtual is True. Denotes number of nodes under Kronos control.

Note that each PLC’s CPU counts as a separate node. So if there are two PLCs each with 2 CPUs, then there are 4 dilated_nodes in total.

• rel_cpu_speed: Ignored unless is_virtual is True. Denotes the relative cpu speed / (equivalent to TDF). In Kronos it represents the number of instructions that can be executed in 1ns of virtual time.

• n_insns_per_round: Number of instructions to execute per round. When divided by the rel_cpu_speed, it denotes amount of time the co-simulation would advance in one-round. In the running example, n_insns_per_round is 1000000 and rel_cpu_speed is 1 which implies that in each round, the co-simulation would run for 1000000 ns or 1ms.

• The next step involves starting the GRPC server to initialize all memory mapped files and serve as interface to query PLCs which would be subsequently started:

print "Starting PC GRPC Server ..."

grpc_server_pid = start_grpc_server(args.plc_spec_dir, fd1)

Note: The GRPC server does not count as a dilated node and should not be added to Kronos’s control

3.10.2 Starting dilated processes

In this subsection, we discuss the next stage of building a time synchronized co-simulation which involves launching PLCs, communication modules and HMI clients and adding them to Kronos’s control. The rest of this discussion assumes that Kronos is installed and loaded and the previous stage is complete.

To launch any process under Kronos’s control, it has to launched by a tracer binary which ships with Kronos instal-lation. For example, let us consider a simple command with arguments ls -al. It can be added to Kronos’s control as follows:

tracer -c "ls -al" -r <rel_cpu_speed> -n <n_insns_per_round>

OpenSCADA Documentation, Release 1.0

Using the tracer binary, OpenSCADA PLCs, communication modules and HMIs are added to Kronos’ control. The relevant portions of this stage from the running example are included here:

print "Starting PLC ..."

plc_pid = start_plc(args.plc_spec_file, is_virtual, rel_cpu_speed, \ num_insns_per_round, fd2)

print "Starting Modbus Comm module ..."

comm_module_pid = start_comm_module(args.plc_spec_file, \ args.comm_module_bind_ip, args.comm_module_listen_port, \

args.comm_module_attached_resource, is_virtual, rel_cpu_speed, \ num_insns_per_round, fd3)

print "Starting HMI ..."

example_hmi_pid = start_example_hmi(is_virtual, rel_cpu_speed, \ num_insns_per_round, fd4)

Note: The start_plc function does not invoke a tracer explicitly. Instead it passes rome arguments to plc_runner which implicitly tell it to mimic multiple tracers (one for each CPU) and register each one with Kronos.

Subsequently, the next step involves waiting for all started process to register themselves with Kronos:

# Wait until all processes have started and registered themselves emulation.wait_for_initialization()

After the completion of this stage, the experiment is frozen and ready to run in a time synchronized fashion.

3.10.3 Running the co-simulation

The co-simulation can be run using the emulation_driver object. It provides a method run_for(time_secs) which takes in as input the number of virtual time seconds to run. In running_example, since the co-simulation advances by 1ms each round (rel_cpu_speed is 1.0 and n_insns_per_round is 1000000), the argument provided to the run_for method is implicitly converted into the number of rounds to run. In each round, all the dilated processes are triggered and progress method of the physical system simulator object is invoked once:

total_time_elapsed = 0.0

while total_time_elapsed <= run_time:

# Run for 10ms or 10 rounds.

emulation.run_for(0.01) total_time_elapsed += 0.01 if is_virtual:

print "Time Elapsed: ", total_time_elapsed if stop == True:

break

3.10.4 Stopping the co-simulation

The co-simulation can be stopped by invoking the stop_exp() method provided by the emulation_driver:

emulation.stop_exp()

3.10. Kronos driven co-simulation 33

In document OpenSCADA Documentation (Page 34-38)

Related documents