Commit 81e4cd0f authored by Gauthier Quesnel's avatar Gauthier Quesnel
Browse files

core: update external sources

parent 8fcc6401
This diff is collapsed.
......@@ -20,6 +20,25 @@
namespace irt {
// Helper to display a little (?) mark which shows a tooltip when hovered.
// In your own code you may want to display an actual icon if you are using a
// merged icon fonts (see docs/FONTS.md)
inline void
HelpMarker(const char* desc) noexcept
{
try {
ImGui::TextDisabled("(?)");
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextUnformatted(desc);
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
} catch (const std::exception& /*e*/) {
}
}
inline const char*
status_string(const status s) noexcept
{
......@@ -44,6 +63,8 @@ status_string(const status s) noexcept
"vector_init_capacity_zero",
"vector_init_capacity_too_big",
"vector_init_not_enough_memory",
"source_unknown_id",
"source_empty",
"dynamics_unknown_id",
"dynamics_unknown_port_id",
"dynamics_not_enough_memory",
......@@ -80,6 +101,8 @@ status_string(const status s) noexcept
"gui_not_enough_memory",
"io_not_enough_memory",
"io_file_format_error",
"io_file_format_source_number_error",
"io_file_source_full",
"io_file_format_model_error",
"io_file_format_model_number_error",
"io_file_format_model_unknown",
......@@ -338,23 +361,6 @@ using observation_output = std::variant<std::monostate,
file_output_id,
file_discrete_output_id>;
struct sources
{
std::map<int, irt::source::constant> csts;
std::map<int, irt::source::binary_file> bins;
std::map<int, irt::source::text_file> texts;
int csts_next_id = 1;
int bins_next_id = 1;
int texts_next_id = 1;
irt::source::constant* new_constant() noexcept;
irt::source::binary_file* new_binary_file() noexcept;
irt::source::text_file* new_text_file() noexcept;
void show(bool* is_show);
void show_menu(const char* title, external_source& src);
};
struct editor
{
small_string<16> name;
......@@ -518,9 +524,9 @@ struct editor
{
gport() noexcept = default;
gport(irt::model* model_, const int port_index_) noexcept
: model(model_)
, port_index(port_index_)
gport(irt::model* model_, const int port_index_) noexcept
: model(model_)
, port_index(port_index_)
{}
irt::model* model = nullptr;
......@@ -572,14 +578,16 @@ struct application
void show(bool* is_open);
} settings;
sources srcs;
external_source srcs;
void show_sources(bool* is_show);
void show_menu_sources(const char* title, source& src);
bool show_log = true;
bool show_simulation = true;
bool show_demo = false;
bool show_plot = true;
bool show_settings = false;
bool show_sources = false;
bool show_sources_window = false;
editor* alloc_editor();
void free_editor(editor& ed);
......
This diff is collapsed.
......@@ -312,6 +312,8 @@ enum class status
vector_init_capacity_zero,
vector_init_capacity_too_big,
vector_init_not_enough_memory,
source_unknown,
source_empty,
dynamics_unknown_id,
dynamics_unknown_port_id,
dynamics_not_enough_memory,
......@@ -348,6 +350,8 @@ enum class status
gui_not_enough_memory,
io_not_enough_memory,
io_file_format_error,
io_file_format_source_number_error,
io_file_source_full,
io_file_format_model_error,
io_file_format_model_number_error,
io_file_format_model_unknown,
......@@ -2923,6 +2927,71 @@ private:
}
};
struct simulation;
/*****************************************************************************
*
* @c source and @c source_id are data from files or random generators.
*
****************************************************************************/
struct source
{
enum class operation_type
{
initialize, // Use to initialize the buffer at simulation init step.
update, // Use to update the buffer when all values are read.
finalize // Use to clear the buffer at simulation finalize step.
};
double* buffer = nullptr;
u64 id = 0; // The identifier of the external source (see operation())
int type = -1; // The type of the external source (see operation())
int size = 0;
int index = 0;
void reset() noexcept
{
buffer = nullptr;
size = 0;
index = 0;
type = -1;
id = 0;
}
void clear() noexcept
{
buffer = nullptr;
size = 0;
index = 0;
}
bool next(double& value) noexcept
{
if (index >= size)
return false;
value = buffer[index++];
return true;
}
};
/**
* @brief Call in the initialize function of the models.
* @param sim The simulation.
* @param src The sources.
* @return
*/
inline status
initialize_source(simulation& sim, source& src) noexcept;
inline status
update_source(simulation& sim, source& src, double& val) noexcept;
inline status
finalize_source(simulation& sim, source& src) noexcept;
/*****************************************************************************
*
* DEVS Model / Simulation entities
......@@ -3085,8 +3154,6 @@ using has_init_port_t = decltype(&T::init);
template<typename T>
using has_sim_attribute_t = decltype(&T::sim);
struct simulation;
struct node
{
node() = default;
......@@ -5052,122 +5119,59 @@ struct counter
}
};
struct external_source;
struct constant_external_source
{
bool operator()(external_source& src) noexcept;
double value = 0.0;
};
inline static constant_external_source default_external_source;
struct external_source
{
function_ref<bool(external_source& src)> expand = default_external_source;
double* data = nullptr; // @todo use a std::span<double> instead
sz index = 0; // of data and size.
double value = 0.0;
sz size = 0;
u32 id = 0;
u32 type = 0;
bool init() noexcept
{
data = nullptr;
index = 0;
size = 0;
id = 0;
type = 0;
if (expand.empty())
return false;
return expand(*this);
}
bool next() noexcept
{
irt_assert(data);
if (index >= size) {
if (expand.empty() || !expand(*this))
return false;
index = 0;
} else {
++index;
}
return true;
}
};
inline bool
constant_external_source::operator()(external_source& src) noexcept
{
src.data = &value;
src.index = 0;
src.size = 1;
src.id = 0;
src.type = 0;
return true;
}
struct generator
{
port y[1];
time sigma;
double value;
external_source default_ta_source;
external_source default_value_source;
double default_offset = 1.0;
simulation* sim = nullptr;
double default_offset = 0.0;
source default_source_ta;
source default_source_value;
bool stop_on_error = false;
status initialize() noexcept
{
sigma = default_offset;
if (!default_ta_source.data)
irt_bad_return(status::model_generator_empty_ta_source);
if (!default_value_source.data)
irt_bad_return(status::model_generator_empty_value_source);
if (stop_on_error) {
irt_return_if_bad(initialize_source(*sim, default_source_ta));
irt_return_if_bad(initialize_source(*sim, default_source_value));
} else {
(void)initialize_source(*sim, default_source_ta);
(void)initialize_source(*sim, default_source_value);
}
return status::success;
}
status transition(time /*t*/, time /*e*/, time /*r*/) noexcept
{
if (!default_ta_source.data)
irt_bad_return(status::model_generator_null_ta_source);
if (!default_ta_source.next())
irt_bad_return(status::model_generator_empty_ta_source);
if (!default_value_source.data)
irt_bad_return(status::model_generator_null_value_source);
if (!default_value_source.next())
irt_bad_return(status::model_generator_empty_value_source);
sigma = *default_ta_source.data;
if (stop_on_error) {
irt_return_if_bad(update_source(*sim, default_source_ta, sigma));
irt_return_if_bad(update_source(*sim, default_source_value, value));
} else {
if (is_bad(update_source(*sim, default_source_ta, sigma)))
sigma = time_domain<time>::infinity;
if (is_bad(update_source(*sim, default_source_value, value)))
value = 0.0;
}
return status::success;
}
status lambda() noexcept
{
y[0].messages.emplace_front(*default_value_source.data);
y[0].messages.emplace_front(value);
return status::success;
}
message observation(const time /*e*/) const noexcept
{
return { *default_value_source.data };
return { value };
}
};
......@@ -5722,7 +5726,7 @@ struct queue
return status::success;
}
status transition(time t, time /*e*/, time r) noexcept
status transition(time t, time /*e*/, time /*r*/) noexcept
{
while (!queue.empty() && queue.front().real[0] <= t)
queue.pop_front();
......@@ -5767,23 +5771,24 @@ struct dynamic_queue
time sigma;
flat_double_list<dated_message> queue;
external_source default_ta_source;
simulation* sim = nullptr;
source default_source_ta;
bool stop_on_error = false;
status initialize() noexcept
{
if (!default_ta_source.init())
irt_bad_return(status::model_dynamic_queue_source_is_null);
if (!queue.get_allocator())
irt_bad_return(status::model_dynamic_queue_empty_allocator);
sigma = time_domain<time>::infinity;
queue.clear();
if (stop_on_error)
irt_return_if_bad(initialize_source(*sim, default_source_ta));
else
(void)initialize_source(*sim, default_source_ta);
return status::success;
}
status transition(time t, time /*e*/, time r) noexcept
status transition(time t, time /*e*/, time /*r*/) noexcept
{
while (!queue.empty() && queue.front().real[0] <= t)
queue.pop_front();
......@@ -5792,11 +5797,15 @@ struct dynamic_queue
if (!queue.get_allocator()->can_alloc(1u))
irt_bad_return(status::model_dynamic_queue_full);
queue.emplace_back(
*default_ta_source.data + t, msg[0], msg[1], msg[2], msg[3]);
double ta;
if (stop_on_error) {
irt_return_if_bad(update_source(*sim, default_source_ta, ta));
queue.emplace_back(t + ta, msg[0], msg[1], msg[2], msg[3]);
} else {
if (is_success(update_source(*sim, default_source_ta, ta)))
queue.emplace_back(t + ta, msg[0], msg[1], msg[2], msg[3]);
if (!default_ta_source.next())
irt_bad_return(status::model_dynamic_queue_source_is_null);
}
}
if (!queue.empty()) {
......@@ -5831,6 +5840,11 @@ struct priority_queue
port y[1];
time sigma;
flat_double_list<dated_message> queue;
double default_ta = 1.0;
simulation* sim = nullptr;
source default_source_ta;
bool stop_on_error = false;
private:
status try_to_insert(const time t, const message& msg) noexcept
......@@ -5857,34 +5871,42 @@ private:
}
public:
external_source default_ta_source;
status initialize() noexcept
{
if (!default_ta_source.init())
irt_bad_return(status::model_priority_queue_source_is_null);
if (!queue.get_allocator())
irt_bad_return(status::model_priority_queue_empty_allocator);
if (stop_on_error)
irt_return_if_bad(initialize_source(*sim, default_source_ta));
else
(void)initialize_source(*sim, default_source_ta);
sigma = time_domain<time>::infinity;
queue.clear();
return status::success;
}
status transition(time t, time /*e*/, time r) noexcept
status transition(time t, time /*e*/, time /*r*/) noexcept
{
while (!queue.empty() && queue.front().real[0] <= t)
queue.pop_front();
for (const auto& msg : x[0].messages) {
if (auto ret = try_to_insert(*default_ta_source.data + t, msg);
is_bad(ret))
irt_bad_return(status::model_priority_queue_full);
double value;
if (!default_ta_source.next())
irt_bad_return(status::model_priority_queue_source_is_null);
if (stop_on_error) {
irt_return_if_bad(
update_source(*sim, default_source_ta, value));
if (auto ret = try_to_insert(value + t, msg); is_bad(ret))
irt_bad_return(status::model_priority_queue_full);
} else {
if (is_success(update_source(*sim, default_source_ta, value))) {
if (auto ret = try_to_insert(value + t, msg); is_bad(ret))
irt_bad_return(status::model_priority_queue_full);
}
}
}
if (!queue.empty()) {
......@@ -6274,13 +6296,18 @@ struct simulation
flat_double_list<record>::allocator_type flat_double_list_shared_allocator;
data_array<model, model_id> models;
data_array<message, message_id> messages;
data_array<observer, observer_id> observers;
scheduller sched;
/**
* @brief Use initialize, generate or finalize data from a source.
*
* See the @c external_source class for an implementation.
*/
function_ref<status(source&, const source::operation_type)> source_dispatch;
time begin = time_domain<time>::zero;
time end = time_domain<time>::infinity;
......@@ -6698,7 +6725,6 @@ public:
irt_return_if_bad(models.init(model_capacity));
irt_return_if_bad(messages.init(messages_capacity));
irt_return_if_bad(observers.init(model_capacity));
irt_return_if_bad(
flat_double_list_shared_allocator.init(model_capacity * ten));
......@@ -7214,6 +7240,11 @@ public:
std::is_same_v<Dynamics, priority_queue>)
dyn.queue.set_allocator(&dated_message_allocator);
if constexpr (std::is_same_v<Dynamics, generator> ||
std::is_same_v<Dynamics, dynamic_queue> ||
std::is_same_v<Dynamics, priority_queue>)
dyn.sim = this;
if constexpr (is_detected_v<initialize_function_t, Dynamics>)
irt_return_if_bad(dyn.initialize());
......@@ -7328,6 +7359,31 @@ public:
}
};
inline status
initialize_source(simulation& sim, source& src) noexcept
{
return sim.source_dispatch(src, source::operation_type::initialize);
}
inline status
update_source(simulation& sim, source& src, double& val) noexcept
{
if (src.next(val))
return status::success;
if (auto ret = sim.source_dispatch(src, source::operation_type::update);
is_bad(ret))
return ret;
return src.next(val) ? status::success : status::source_empty;
}
inline status
finalize_source(simulation& sim, source& src) noexcept
{
return sim.source_dispatch(src, source::operation_type::finalize);
}
} // namespace irt
#endif
// Copyright (c) 2020 INRA Distributed under the Boost Software License,
// Copyright (c) 2020-2021 INRA Distributed under the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
......@@ -7,195 +7,606 @@
#include <irritator/core.hpp>
#include <array>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <map>
#include <random>
#include <vector>
namespace irt::source {
namespace irt {
struct constant
enum class external_source_type
{
double value;
binary_file,
constant,
random,
text_file
};
enum class block_vector_policy
{
not_reuse_free_list,
reuse_free_list
};
template<block_vector_policy P>
struct block_vector_base
{
double* buffer = nullptr;
sz size = 0;
sz max_size = 0;
sz capacity = 0;
sz block_size = 0;
sz free_head = static_cast<sz>(-1);
block_vector_base() = default;
bool init(external_source& src)
block_vector_base(const block_vector_base&) = delete;
block_vector_base& operator=(const block_vector_base&) = delete;
~block_vector_base() noexcept
{
src.type = 0;
src.id = 0;
src.data = &value;
src.index = 0;
if (buffer)
g_free_fn(buffer);
}
return true;
bool empty() const noexcept
{
return size == 0;
}
bool operator()(external_source& src)
status init(sz block_size_, sz capacity_) noexcept
{
if (capacity_ == 0)
return status::block_allocator_bad_capacity;
if (capacity_ != capacity) {