/**
* @file kernel/qss/Derivative.hpp
* @author The ARTIS Development Team
* See the AUTHORS or Authors.txt file
*/
/*
* ARTIS - the multimodeling and simulation environment
* This file is a part of the ARTIS environment
*
* Copyright (C) 2013-2022 ULCO http://www.univ-littoral.fr
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef QSS_DERIVATIVE
#define QSS_DERIVATIVE
#include
#include
namespace artis::qss {
template
class Derivative : public artis::pdevs::Dynamics {
public:
struct input {
enum values {
RESET = 0, IN
};
};
struct output {
enum values {
OUT = 0
};
};
struct var {
enum values {
VALUE
};
};
typedef Derivative type;
Derivative(const std::string &name, const artis::pdevs::Context &context)
:
artis::pdevs::Dynamics(name, context),
_external_number(0), _internal_number(0) {
DECLARE_STATES(int,
((state::PHASE, &type::_phase)));
DECLARE_STATES(unsigned int,
((state::INPUT_NUMBER, &type::_input_number)));
DECLARE_STATES(double,
((state::OUTPUT_VALUE, &type::_output_value),
(state::LAST_OUTPUT, &type::_last_output)));
this->input_ports({{input::RESET, "reset"},
{input::IN, "in"}});
this->output_port({output::OUT, "out"});
this->observable({var::VALUE, "value"});
}
virtual ~Derivative() {}
int external(const std::string &name, double Dyn::* var) {
++_external_number;
this->state_(state::LAST_OUTPUT + _external_number + 1, name, var);
this->input_port({input::IN + _external_number, name});
return input::IN + _external_number;
}
void internal(const std::string &name, double Dyn::* var) {
assert(_internal_number == 0);
++_internal_number;
this->state_(state::LAST_OUTPUT + 1, name, var);
}
virtual double compute() const = 0;
virtual void dconf(const typename Time::type &t, typename Time::type e,
const common::event::Bag &bag) {
dint(t);
dext(t, e, bag);
}
virtual void dint(const typename Time::type & /* time */) {
if (_phase == phase::RESPONSE) {
_last_output = _output_value;
}
_phase = phase::WAIT;
}
virtual void dext(const typename Time::type &t, typename Time::type e,
const common::event::Bag &bag) {
std::for_each(bag.begin(), bag.end(),
[this, t, e](const common::event::ExternalEvent &event) {
if (event.on_port(input::RESET)) {
_input_number = 0;
_phase = phase::INIT;
} else {
IntegratorData data;
event.data()(data);
this->get((event.port_index() - 1) + state::LAST_OUTPUT + 1).put(
dynamic_cast(this), data.value);
switch (_phase) {
case phase::INIT: {
++_input_number;
if (_input_number == this->state_number() - (state::LAST_OUTPUT + 1)) {
_output_value = compute();
_phase = phase::RESPONSE;
}
break;
}
case phase::WAIT:
case phase::RESPONSE: {
double value = compute();
if (value != _last_output) {
_output_value = value;
_phase = phase::RESPONSE;
} else {
_phase = phase::WAIT;
}
}
}
}
});
}
virtual void start(const typename Time::type & /* time */) {
_input_number = 0;
_phase = phase::INIT;
}
virtual typename Time::type ta(const typename Time::type & /* time */) {
switch (_phase) {
case phase::INIT:
return Time::infinity;
case phase::WAIT:
return Time::infinity;
case phase::RESPONSE:
return 0;
}
return Time::infinity;
}
virtual common::event::Bag lambda(const typename Time::type & /* time */) const {
common::event::Bag msgs;
switch (_phase) {
case phase::INIT:
break;
case phase::WAIT:
break;
case phase::RESPONSE: {
const DerivativeData data = {_output_value};
msgs.push_back(common::event::ExternalEvent(output::OUT, data));
}
}
return msgs;
}
virtual common::event::Value observe(const typename Time::type & /* t */, unsigned int index) const {
switch (index) {
case var::VALUE:
return (double) (_output_value);
default:
return common::event::Value();
}
}
private:
struct phase {
enum values {
INIT = 0, WAIT, RESPONSE
};
};
struct state {
enum values {
PHASE = 0, INPUT_NUMBER, OUTPUT_VALUE, LAST_OUTPUT
};
};
unsigned int _external_number;
unsigned int _internal_number;
// state
int _phase;
unsigned int _input_number;
double _output_value;
double _last_output;
};
}
#endif