From VLE
Each model inherits from the class vle::devs::Dynamics. This class defines the functional interface of each simulation model as well as a certain number of functions facilitating the coding of simulation models. The functional interface is conform to the abstract simulator Parallel DEVS and DSDEVS named DSDE.
// Initialization of the simulator by the information coming from the design of experiments
Modele(const devs::DynamicsInit& init,
const devs::InitEventList& event);
// Initialization of the simulator
devs::Time init(const devs::Time& time);
// Function in charge of time advance, called after
// internal or external transition functions
devs::Time timeAdvance() const;
// Output function
void output(const devs::Time& time, devs::ExternalEventList& output) const;
// Parallel DEVS function to arbitrate internal vs. external events conflicts.
devs::Event::EventType confluentTransitions(
const devs::InternalEvent& internal,
const ExternalEventList& extEventlist) const;
// Internal transition function
void internalTransition(const devs::Time& time)
// External transition function
void externalTransition(const devs::ExternalEventList& event,
const devs::Time& time)
// Simulator observation function
value::Value* observation(const devs::ObservationEvent& event) const;
void finish();
All these functions are associated to a behavior by default which results in a passive model that makes nothing during an infinite duration. If you want to develop a model, it is thus necessary to overload all methods or only a part.
Let us take the following example: upon the arrival of an event on its input ports, the model increments a counter. The purpose of this model being simply to count the number of outgoing events from a port of another model. Naturally, this counting is possible if the input port of this model is connected to the ouput port of the model generating the events.
The DEVS model is the following one:
- S = (counter) : the state vector is limited to the counter
- δext((counter),e,X) = (counter + 1)
-
All other elements of the DEVS structure are empty. It is thus necessary to overload the externalEvent and timeAdvance methods.
In the case of the external transition function, the counter is simply incremented. It is not necessary to call the functions dedicated to the analysis of the external event because only its input interests us.
void Counter::externalTransition(const devs::ExternalEventList& event,
const devs::Time& /* time */)
{
m_counter += event.size();
}
The duration function of the state is also simple. We use the constant infinity defined in the devs::Time class.
devs::Time Counter::timeAdvance() const
{
return devs::Time::infinity;
}
There is no more than a function to be overloaded so that the model can be complete: it is the initialization function.
devs::Time Counter::init(const devs::Time& time)
{
m_counter = 0;
return devs::Time::infinity;
}
The model is now complete. Naturally, you should not forget the declaration of the Counter class.
#include <vle/devs/Dynamics.hpp>
using namespace vle; // Avoids to add vle:: in front of all objects
// coming from vle.
class Counter : public devs::Dynamics
{
public:
Counter(const devs::DynamicsInit& init,
const devs::InitEventList& events) :
devs::Dynamics(init, events),
m_counter(0)
{ }
virtual ~Counter()
{ }
virtual devs::Time init(const devs::Time& time);
virtual devs::Time timeAdvance() const;
virtual void externalTransition(
const devs::ExternalEventList& event,
const devs::Time& time);
private:
long m_counter;
};
DECLARE_DYNAMICS(Counter);
We find here the definition of the functions to construct and destroy objects and the use of the DECLARE_DYNAMICS macro allowing to declare this model as a "plugin" of simulation for VLE.
VLE proposes a last mechanism which we can integrate into this simple model: the observation of its state. For that purpose, it is necessary to overload the observation function. This function is called when an observer questions the model about his state. Warning, this function is constant. The state of the model should not be modified when it is questioned.
We thus add the declaration of the function in the class and we add the following code:
value::Value* Counter::observation(const devs::ObservationEvent& event) const
{
if (event.onPort("c")) {
return buildInteger(m_counter);
}
}
What this code means? In the first place, the observation port is called c. It will thus be necessary, during the definition of the file of experiment, to attach a measure to this port. If the observation event arrived on the c port then we return the value of the counter state. We use the buildInteger function to transform the state of type int in a pointer on an object vle::value::Value. The class vle::value::Value is, in other, responsible for the serialization. If your model can be questioned about another variable of states, it is necessary to define another port and to make a new condition on the arrival of an event on this port.