..
    Copyright 2014 Modelling, Simulation and Design Lab (MSDL) at 
    McGill University and the University of Antwerp (http://msdl.cs.mcgill.ca/)

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

Realtime simulation
===================

Realtime simulation is closely linked to normal simulation, with the exception that simulation will not progress as fast as possible. The value returned by the time advance will be interpreted in seconds and the simulator will actually wait (not busy loop) until the requested time has passed. Several realtime backends are supported in PyPDEVS and are mentioned below.

Example model
-------------

The example model will be something else than the *queue* from before, as this isn't really that interesting for realtime simulation. We will instead use the *trafficLight* model. It has a *trafficLight* that is either running autonomous or is in a manual mode. Normally, the traffic light will work autonomously, though it is possible to interrupt the traffic light and switch it to manual mode and back to autonomous again.

This complete model is::

    class TrafficLightMode:
        def __init__(self, current="red"):
            self.set(current)

        def set(self, value="red"):
            self.__colour=value

        def get(self):
            return self.__colour

        def __str__(self):
            return self.get()

    class TrafficLight(AtomicDEVS):
        def __init__(self, name):
            AtomicDEVS.__init__(self, name)
            self.state = TrafficLightMode("red")
            self.INTERRUPT = self.addInPort(name="INTERRUPT")
            self.OBSERVED = self.addOutPort(name="OBSERVED")

        def extTransition(self, inputs):
            input = inputs[self.INTERRUPT][0]
            if input == "toManual":
                if state == "manual":
                    # staying in manual mode
                    return TrafficLightMode("manual")
                if state in ("red", "green", "yellow"):
                    return TrafficLightMode("manual")

            elif input == "toAutonomous":
                if state == "manual":
                    return TrafficLightMode("red")

            raise DEVSException("Unkown input in TrafficLight")

        def intTransition(self):
            state = self.state.get()
            if state == "red":
                return TrafficLightMode("green")
            elif state == "green":
                return TrafficLightMode("yellow")
            elif state == "yellow":
                return TrafficLightMode("red")
            else:
                raise DEVSException("Unkown state in TrafficLight")

        def outputFnc(self):
            state = self.state.get()
            if state == "red":
                return {self.OBSERVED: ["grey"]}
            elif state == "green":
                return {self.OBSERVED: ["yellow"]}
            elif state == "yellow":
                return {self.OBSERVED: ["grey"]}
            else:
                raise DEVSException("Unknown state in TrafficLight")

        def timeAdvance(self):
            if state == "red":
                return 60
            elif state == "green":
                return 50
            elif state == "yellow":
                return 10
            elif state == "manual":
                return INFINITY
            else:
                raise DEVSException("Unknown state in TrafficLight")

With our model being set up, we could run it as-fast-as-possible by starting it like::

    model = TrafficLight("trafficLight")
    sim = Simulator(model)
    sim.simulate()

To make it run in real time, we only need to do some minor changes. First, we need to define on which port we want to put some external input. We can choose a way to address this port, but lets assume that we choose the same name as the name of the port. This gives us::

    refs = {"INTERRUPT": model.INTERRUPT}

Now we only need to pass this mapping to the simulator, together with the choice for realtime simulation. This is done as follows::

    refs = {"INTERRUPT": model.INTERRUPT}
    sim.setRealTime(True)
    sim.setRealTimePorts(refs)

That is all extra configuration that is required for real time simulation. 

As soon as the *simulate()* method is called, the simulation will be started as usual, though now several additional options are enabled. Specifically, the user now has an input prompt to input external data. This input should be of the form *portname data*.

In our example, the model will respond to both *toManual* and *toAutonomous* and we chose *INTERRUPT* in our mapping. So our model will react on the input *INTERRUPT toManual*. To exit realtime simulation, it is best to simply inject 'empty' input. Malformed input will cause an exception and simulation will be halted.

.. note:: All input that is injected will be passed to the model as a *string*. If the model is thus supposed to process integers, a string to integer processing step should happen in the model itself.

Input files
-----------

PyPDEVS also supports the use of input files together with input provided at run time. The input file will be parsed at startup and should be of the form *time port value*, with time being the simulation time at which this input should be injected. Again, this input will always be interpreted as a string. If a syntax error is detected while reading through this file, the error will immediately be shown.

.. note:: The file input closely resembles the usual prompt, though it is not possible to define a termination at a certain time by simply stating the time at the end. For this, you should use the termination time as provided by the standard interface.

An example input file for our example could be::

    10 INTERRUPT toManual
    20 INTERRUPT toAutonomous
    30 INTERRUPT toManual

Backends
--------

Several backends are provided for the realtime simulation. The default backend is the best for most people who just want to simulate in realtime. Other options are for when PyPDEVS is coupled to e.g. a Tk user interface or something with a game loop system.

The following backends are currently supported:

* Python threads: the default, provides simple threading and doesn't require any other programs. Activated with *setRealTimePlatformThreads()*.
* Tk threads: uses Tk for all of its waiting and delays (using the Tk event list). Activated with *setRealTimePlatformTk()*.
* Game loop: requires an external program to call the simulator after a certain delay. Activated with *setRealTimePlatformGameLoop(fps)*.

The game loop mechanism requires some additional explanation:
This mechanism will not block the main thread and if the main thread stops, so will the simulation. The caller has 2 methods to communicate with the threading system after the call to *simulate()*. These methods are:

* *realtime_finished()*: Checks whether or not the simulation is finished.
* *realtime_loop_call()*: Continue to the next simulation step. This advances the internal time according to the value provided in the initialisation.

The game loop mechanism is thus closely linked to the invoker. The calls to the *realtime_loop_call()* function and the initializer are the only concept of time that this mechanism uses. Note that setting the fps to low will decrease the granularity of all actions, as actions are executed during the method invocation.
