/**
* @file kernel/qss/MultiDerivative.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_MULTI_DERIVATIVE
#define QSS_MULTI_DERIVATIVE
#include
namespace artis::qss {
template
class MultiDerivative : public artis::pdevs::Dynamics {
public:
struct input {
enum values {
RESET = 0, INTERNAL = 1, EXTERNAL = 1000
};
};
struct var {
enum values {
VALUE
};
};
typedef MultiDerivative type;
MultiDerivative(const std::string &name,
const artis::pdevs::Context &context)
:
artis::pdevs::Dynamics(name, context),
_external_number(0), _internal_number(0), _variable_number(0) {
DECLARE_STATES(int,
((state::PHASE, &type::_phase)));
DECLARE_STATES(unsigned int,
((state::INPUT_NUMBER, &type::_input_number)));
DECLARE_STATES(std::vector < double > ,
((state::OUTPUT_VALUES, &type::_output_values),
(state::LAST_OUTPUTS, &type::_last_outputs)));
this->input_port({input::RESET, "reset"});
}
virtual ~MultiDerivative() {}
int external(const std::string &name, double Dyn::* var) {
this->state_(state::LAST_OUTPUTS + _variable_number + 1, name, var);
this->input_port({input::EXTERNAL + _external_number, name});
++_variable_number;
++_external_number;
return input::EXTERNAL + _external_number - 1;
}
void internal(const std::string &name, double Dyn::* var) {
assert(_external_number == 0);
this->state_(state::LAST_OUTPUTS + _variable_number + 1, name, var);
this->input_port({input::INTERNAL + _internal_number, name});
this->output_port({_internal_number, name});
this->observable({var::VALUE + _internal_number, name});
++_variable_number;
++_internal_number;
}
virtual std::vector compute() = 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 & /* t */) {
if (_phase == phase::RESPONSE) {
_last_outputs.assign(_output_values.begin(), _output_values.end());
}
_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);
if (event.port_index() >= input::INTERNAL
and event.port_index() < input::EXTERNAL) {
this->get((event.port_index() - input::INTERNAL) + state::LAST_OUTPUTS + 1)
.put(
static_cast(this), data.value);
} else {
assert(event.port_index() >= input::EXTERNAL);
this->get((event.port_index() - input::EXTERNAL) + state::LAST_OUTPUTS + 1)
.put(
static_cast(this), data.value);
}
switch (_phase) {
case phase::INIT: {
++_input_number;
if (_input_number == this->state_number() - (state::LAST_OUTPUTS + 1)) {
std::vector values = compute();
assign_values(values);
_phase = phase::RESPONSE;
}
break;
}
case phase::WAIT:
case phase::RESPONSE: {
std::vector values = compute();
if (is_different_values(values)) {
assign_values(values);
_phase = phase::RESPONSE;
} else {
_phase = phase::WAIT;
}
}
}
}
});
}
virtual void start(const typename Time::type & /* time */) {
_output_values = std::vector(_internal_number);
_last_outputs = std::vector(_internal_number);
_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: {
unsigned int index = 0;
for (double value: _output_values) {
if (value != _last_outputs[index]) {
const DerivativeData data = {value};
msgs.push_back(common::event::ExternalEvent(index, data));
}
++index;
}
}
}
return msgs;
}
virtual common::event::Value observe(const typename Time::type & /* t */,
unsigned int index) const {
if (index >= var::VALUE and index <= var::VALUE + _internal_number) {
return (double) (_output_values[index - var::VALUE]);
} else {
return common::event::Value();
}
}
unsigned int variable_number() const { return _internal_number; }
private:
void assign_values(const std::vector &values) {
for (unsigned int index = 0; index < _internal_number; ++index) {
_output_values[index] = values[index];
}
}
bool is_different_values(const std::vector &values) {
bool different = false;
unsigned int index = 0;
while (not different and index < _internal_number) {
if (_output_values[index] != values[index]) {
different = true;
} else {
++index;
}
}
return different;
}
struct phase {
enum values {
INIT = 0, WAIT, RESPONSE
};
};
struct state {
enum values {
PHASE = 0, INPUT_NUMBER, OUTPUT_VALUES, LAST_OUTPUTS
};
};
unsigned int _external_number;
unsigned int _internal_number;
unsigned int _variable_number;
// state
int _phase;
unsigned int _input_number;
std::vector _output_values;
std::vector _last_outputs;
};
}
#endif