-
Notifications
You must be signed in to change notification settings - Fork 13
4. Building DEVS Models
The following UML class diagram contains EVERYTHING you need to develop DEVS models with Cadmium.
First, let's look at the steps we need to follow to develop an atomic DEVS model.
Our atomic model must inherit from the cadmium::Atomic<S>
class. S
is a template argument to determine which data type is used to represent the state of the atomic model.
If you want it to be an integer number, your model must inherit from cadmium::Atomic<int>
.
However, most of the times you will want to use your custom class.
Let us assume that you want to represent the state in a phase-sigma fashion.
Then, we have to define the PhaseSigmaState
structure:
#include <limits>
struct PhaseSigmaState {
std::string phase; // phase (or state) of the atomic model
double sigma; // time to wait until the next internal transition
PhaseSigmaState(std::string phase, double sigma): phase(phase), sigma(sigma) {}
}
We must overwrite the insertion (<<
) operator for our state data structure.
The insertion operator allows us to tell Cadmium how to represent the state of our model as a string.
In our case, we define <<
as follows:
std::ostream& operator<<(std::ostream &out, const PhaseSigmaState& s) {
out << "(" << s.phase << "," << s.sigma << ")";
return out;
}
Now, let's define the behavior of our atomic model.
We will define a very simple model.
It has two input ports: inNewSigma
and inNewPhase
.
The model also has one output port: outNewPhase
.
This port only outputs strings.
include <cadmium/core/modeling/atomic.hpp>
class MyAtomic: public cadmium::Atomic<PhaseSigmaState> {
public:
std::shared_ptr<cadmium::Port<double>> inNewSigma;
std::shared_ptr<cadmium::Port<std::string>> inNewPhase;
std::shared_ptr<cadmium::Port<std::string>> outNewPhase;
MyAtomic(): cadmium::Atomic<PhaseSigmaState>(PhaseSigmaState("initial", 0.)) {
inNewSigma = addInPort<double>("inNewSigma");
inNewPhase = addInPort<std::string>("inNewPhase");
outNewPhase = addOutPort<std::string>("outNewPhase");
}
// it continues below...
We define the ports of our model as public attributes of our new class.
Note that we NEVER work with cadmium::Port<T>
data structures directly.
We ALWAYS work with std::shared_ptr<cadmium::Port<T>>
structures.
The ports are created in the constructor function.
We use the methods addInPort<T>
and addOutPort<T>
to create the new ports.
T
is a template argument that defines the data type of the messages that the port accepts.
For instance, inNewSigma
port only accepts string messages.
If you try to send an integer number via the inNewSigma
port, your code will not compile.
// ... the atomic model definition continues here
double timeAdvance(const PhaseSigmaState& state) const override {
return state.sigma;
}
void output(PhaseSigmaState& state) const override {
outNewPhase->addMessage(state.phase);
}
void internalTransition(PhaseSigmaState& state) override {
state.sigma = std::numeric_limits<double>::infinity();
}
void externalTransition(PhaseSigmaState& state, double e) override {
state.sigma = std::numeric_limits<double>::infinity();
}
// We won't override the confluentTransition function, as we want to use the default one.
}
When receiving a new message, the model sets its phase to the value of the message. When receiving a new message, the model sets its sigma to the value of the message.
To do.