Commit 0648a2c1 authored by Gauthier Quesnel's avatar Gauthier Quesnel
Browse files

core: add back/advance run in simulation

parent b88d817e
Pipeline #52936 passed with stage
in 1 minute and 53 seconds
......@@ -10,6 +10,7 @@ set(gui_sources
simulation-window.cpp sources.cpp window-logger.cpp
${PROJECT_SOURCE_DIR}/../../lib/src/file.cpp
${PROJECT_SOURCE_DIR}/../../lib/src/modeling.cpp
${PROJECT_SOURCE_DIR}/../../lib/src/timeline.cpp
${PROJECT_SOURCE_DIR}/../../external/imgui/imgui.cpp
${PROJECT_SOURCE_DIR}/../../external/imgui/imgui.h
${PROJECT_SOURCE_DIR}/../../external/imgui/imgui_demo.cpp
......
......@@ -68,6 +68,9 @@ bool application::init() noexcept
{
c_editor.init();
// @todo DEBUG MODE: Prefer user settings or better timeline constructor
s_editor.tl.init(32768, 4096, 65536, 4096, 32768, 32768);
if (auto ret = c_editor.mod.registred_paths.init(max_component_dirs);
is_bad(ret)) {
log_w.log(2, "Fail to initialize registred dir paths");
......@@ -729,4 +732,56 @@ void application::shutdown() noexcept
// return editors.try_to_get(current);
// }
//
// Thread tasks
//
void task_simulation_back(void* param) noexcept
{
auto* g_task = reinterpret_cast<gui_task*>(param);
g_task->state = gui_task_status::started;
g_task->app->state |= application_status_read_only_simulating |
application_status_read_only_modeling;
if (g_task->app->s_editor.tl.current_bag > 0) {
auto ret = back(g_task->app->s_editor.tl,
g_task->app->s_editor.sim,
g_task->app->s_editor.simulation_current);
if (is_bad(ret)) {
auto& n =
g_task->app->notifications.alloc(notification_type::error);
n.title = "Fail to back the simulation";
format(n.message, "Advance message: {}", status_string(ret));
g_task->app->notifications.enable(n);
}
}
g_task->state = gui_task_status::finished;
}
void task_simulation_advance(void* param) noexcept
{
auto* g_task = reinterpret_cast<gui_task*>(param);
g_task->state = gui_task_status::started;
g_task->app->state |= application_status_read_only_simulating |
application_status_read_only_modeling;
if (g_task->app->s_editor.tl.current_bag < g_task->app->s_editor.tl.bag) {
auto ret = advance(g_task->app->s_editor.tl,
g_task->app->s_editor.sim,
g_task->app->s_editor.simulation_current);
if (is_bad(ret)) {
auto& n =
g_task->app->notifications.alloc(notification_type::error);
n.title = "Fail to advance the simulation";
format(n.message, "Advance message: {}", status_string(ret));
g_task->app->notifications.enable(n);
}
}
g_task->state = gui_task_status::finished;
}
} // namespace irt
......@@ -186,6 +186,8 @@ void save_component(void* param) noexcept;
void save_description(void* param) noexcept;
void load_project(void* param) noexcept;
void save_project(void* param) noexcept;
void task_simulation_back(void* param) noexcept;
void task_simulation_advance(void* param) noexcept;
enum class gui_task_id : u64;
......@@ -224,8 +226,11 @@ struct simulation_editor
void simulation_init() noexcept;
void simulation_clear() noexcept;
void simulation_start() noexcept;
void simulation_start_1() noexcept;
void simulation_pause() noexcept;
void simulation_stop() noexcept;
void simulation_advance() noexcept;
void simulation_back() noexcept;
bool force_pause = false;
bool force_stop = false;
......
......@@ -438,12 +438,26 @@ static void simulation_init(component_editor& ed,
return;
}
if (sim_ed.store_all_changes || sim_ed.allow_user_changes) {
if (auto ret =
initialize(sim_ed.tl, sim_ed.sim, sim_ed.simulation_begin);
is_bad(ret)) {
auto* app = container_of(&ed, &application::c_editor);
auto& n = app->notifications.alloc(notification_type::error);
n.title = "Simulation initialization fail";
format(n.message,
"Fail to initialize the debug mode: {}",
status_string(ret));
app->notifications.enable(n);
sim_ed.simulation_state = simulation_status::not_started;
}
}
sim_ed.simulation_state = simulation_status::initialized;
}
static void simulation_clear_impl(void* param) noexcept
{
// fmt::print("simulation_clear_impl\n");
auto* g_task = reinterpret_cast<gui_task*>(param);
g_task->state = gui_task_status::started;
g_task->app->state |= application_status_read_only_simulating |
......@@ -452,12 +466,10 @@ static void simulation_clear_impl(void* param) noexcept
simulation_clear(g_task->app->c_editor, g_task->app->s_editor);
g_task->state = gui_task_status::finished;
// fmt::print("simulation_clear_impl finished\n");
}
static void simulation_copy_impl(void* param) noexcept
{
// fmt::print("simulation_init_impl\n");
auto* g_task = reinterpret_cast<gui_task*>(param);
g_task->state = gui_task_status::started;
g_task->app->state |= application_status_read_only_simulating |
......@@ -468,12 +480,10 @@ static void simulation_copy_impl(void* param) noexcept
simulation_copy(g_task->app->c_editor, g_task->app->s_editor);
g_task->state = gui_task_status::finished;
// fmt::print("simulation_init_impl finished\n");
}
static void simulation_init_impl(void* param) noexcept
{
// fmt::print("simulation_init_impl\n");
auto* g_task = reinterpret_cast<gui_task*>(param);
g_task->state = gui_task_status::started;
g_task->app->state |= application_status_read_only_simulating |
......@@ -484,12 +494,35 @@ static void simulation_init_impl(void* param) noexcept
simulation_init(g_task->app->c_editor, g_task->app->s_editor);
g_task->state = gui_task_status::finished;
// fmt::print("simulation_init_impl finished\n");
}
static void task_simulation_store(simulation_editor& sim_ed) noexcept
static status debug_run(simulation_editor& sim_ed) noexcept
{
if (auto ret = run(sim_ed.tl, sim_ed.sim, sim_ed.simulation_current);
is_bad(ret)) {
auto* app = container_of(&sim_ed, &application::s_editor);
auto& n = app->notifications.alloc(notification_type::error);
n.title = "Debug run error";
app->notifications.enable(n);
sim_ed.simulation_state = simulation_status::finish_requiring;
return ret;
}
return status::success;
}
static status run(simulation_editor& sim_ed) noexcept
{
(void)sim_ed;
if (auto ret = sim_ed.sim.run(sim_ed.simulation_current); is_bad(ret)) {
auto* app = container_of(&sim_ed, &application::s_editor);
auto& n = app->notifications.alloc(notification_type::error);
n.title = "Run error";
app->notifications.enable(n);
sim_ed.simulation_state = simulation_status::finish_requiring;
return ret;
}
return status::success;
}
static void task_simulation_static_run(simulation_editor& sim_ed) noexcept
......@@ -510,9 +543,16 @@ static void task_simulation_static_run(simulation_editor& sim_ed) noexcept
if (sim_ed.simulation_state != simulation_status::running)
return;
if (auto ret = sim_ed.sim.run(sim_ed.simulation_current); is_bad(ret)) {
sim_ed.simulation_state = simulation_status::finish_requiring;
return;
if (sim_ed.store_all_changes) {
if (auto ret = debug_run(sim_ed); is_bad(ret)) {
sim_ed.simulation_state = simulation_status::finish_requiring;
return;
}
} else {
if (auto ret = run(sim_ed); is_bad(ret)) {
sim_ed.simulation_state = simulation_status::finish_requiring;
return;
}
}
if (!sim_ed.infinity_simulation &&
......@@ -564,9 +604,16 @@ static void task_simulation_live_run(simulation_editor& sim_ed) noexcept
if (sim_ed.simulation_state != simulation_status::running)
return;
if (auto ret = sim_ed.sim.run(sim_ed.simulation_current); is_bad(ret)) {
sim_ed.simulation_state = simulation_status::finish_requiring;
return;
if (sim_ed.store_all_changes) {
if (auto ret = debug_run(sim_ed); is_bad(ret)) {
sim_ed.simulation_state = simulation_status::finish_requiring;
return;
}
} else {
if (auto ret = run(sim_ed); is_bad(ret)) {
sim_ed.simulation_state = simulation_status::finish_requiring;
return;
}
}
const auto sim_end_at = sim_ed.simulation_current;
......@@ -610,18 +657,39 @@ static void task_simulation_live_run(simulation_editor& sim_ed) noexcept
static void task_simulation_run(simulation_editor& sim_ed) noexcept
{
if (sim_ed.real_time) {
if (sim_ed.store_all_changes)
task_simulation_store(sim_ed);
task_simulation_live_run(sim_ed);
} else {
if (sim_ed.store_all_changes)
task_simulation_store(sim_ed);
task_simulation_static_run(sim_ed);
}
}
static void task_simulation_run_1(simulation_editor& sim_ed) noexcept
{
sim_ed.simulation_state = simulation_status::running;
if (auto ret = debug_run(sim_ed); is_bad(ret)) {
sim_ed.simulation_state = simulation_status::finish_requiring;
return;
}
if (!sim_ed.infinity_simulation &&
sim_ed.simulation_current >= sim_ed.simulation_end) {
sim_ed.simulation_current = sim_ed.simulation_end;
sim_ed.simulation_state = simulation_status::finish_requiring;
return;
}
if (sim_ed.force_pause) {
sim_ed.force_pause = false;
sim_ed.simulation_state = simulation_status::pause_forced;
} else if (sim_ed.force_stop) {
sim_ed.force_stop = false;
sim_ed.simulation_state = simulation_status::finish_requiring;
} else {
sim_ed.simulation_state = simulation_status::pause_forced;
}
}
static void task_simulation_finish(component_editor& /*ed*/,
simulation_editor& sim_ed) noexcept
{
......@@ -646,6 +714,18 @@ static void task_simulation_run(void* param) noexcept
g_task->state = gui_task_status::finished;
}
static void task_simulation_run_1(void* param) noexcept
{
auto* g_task = reinterpret_cast<gui_task*>(param);
g_task->state = gui_task_status::started;
g_task->app->state |= application_status_read_only_simulating |
application_status_read_only_modeling;
task_simulation_run_1(g_task->app->s_editor);
g_task->state = gui_task_status::finished;
}
static void simulation_pause_impl(void* param) noexcept
{
auto* g_task = reinterpret_cast<gui_task*>(param);
......@@ -785,6 +865,25 @@ void simulation_editor::simulation_start() noexcept
}
}
void simulation_editor::simulation_start_1() noexcept
{
bool state = match(simulation_state,
simulation_status::initialized,
simulation_status::pause_forced);
irt_assert(state);
if (state) {
if (auto* parent = tree_nodes.try_to_get(head); parent) {
auto* app = container_of(this, &application::s_editor);
auto& task = app->gui_tasks.alloc();
task.app = app;
app->task_mgr.task_lists[0].add(task_simulation_run_1, &task);
app->task_mgr.task_lists[0].submit();
}
}
}
void simulation_editor::simulation_pause() noexcept
{
bool state = match(simulation_state, simulation_status::running);
......@@ -820,4 +919,26 @@ void simulation_editor::simulation_stop() noexcept
}
}
void simulation_editor::simulation_advance() noexcept
{
if (auto* parent = tree_nodes.try_to_get(head); parent) {
auto* app = container_of(this, &application::s_editor);
auto& task = app->gui_tasks.alloc();
task.app = app;
app->task_mgr.task_lists[0].add(task_simulation_advance, &task);
app->task_mgr.task_lists[0].submit();
}
}
void simulation_editor::simulation_back() noexcept
{
if (auto* parent = tree_nodes.try_to_get(head); parent) {
auto* app = container_of(this, &application::s_editor);
auto& task = app->gui_tasks.alloc();
task.app = app;
app->task_mgr.task_lists[0].add(task_simulation_back, &task);
app->task_mgr.task_lists[0].submit();
}
}
} // namespace irt
\ No newline at end of file
......@@ -1012,11 +1012,11 @@ void application::show_simulation_editor_widget() noexcept
ImGui::PushItemWidth(100.f);
ImGui::InputReal("Begin", &s_editor.simulation_begin);
ImGui::SameLine(ImGui::GetContentRegionAvail().x * 0.5f);
ImGui::Checkbox("Allow user changes", &s_editor.allow_user_changes);
ImGui::Checkbox("Edit", &s_editor.allow_user_changes);
ImGui::InputReal("End", &s_editor.simulation_end);
ImGui::SameLine(ImGui::GetContentRegionAvail().x * 0.5f);
ImGui::Checkbox("Store all changes", &s_editor.store_all_changes);
ImGui::Checkbox("Debug", &s_editor.store_all_changes);
ImGui::BeginDisabled(!s_editor.real_time);
ImGui::InputScalar("Micro second for 1 unit time",
......@@ -1024,7 +1024,7 @@ void application::show_simulation_editor_widget() noexcept
&s_editor.simulation_real_time_relation);
ImGui::EndDisabled();
ImGui::SameLine(ImGui::GetContentRegionAvail().x * 0.5f);
ImGui::Checkbox("Infinity simulation", &s_editor.infinity_simulation);
ImGui::Checkbox("No time limit", &s_editor.infinity_simulation);
ImGui::TextFormat("Current time {:.6f}", s_editor.simulation_current);
ImGui::SameLine(ImGui::GetContentRegionAvail().x * 0.5f);
......@@ -1048,9 +1048,8 @@ void application::show_simulation_editor_widget() noexcept
ImGui::SameLine();
ImGui::BeginDisabled(can_be_started);
if (ImGui::Button("start")) {
if (ImGui::Button("start"))
s_editor.simulation_start();
}
ImGui::EndDisabled();
ImGui::SameLine();
......@@ -1076,15 +1075,27 @@ void application::show_simulation_editor_widget() noexcept
}
ImGui::EndDisabled();
ImGui::BeginDisabled(!s_editor.store_all_changes || !can_be_restarted);
ImGui::SameLine();
ImGui::Button("<<");
const bool history_mode =
(s_editor.store_all_changes || s_editor.allow_user_changes) &&
(can_be_started || can_be_restarted);
ImGui::BeginDisabled(!history_mode);
if (s_editor.store_all_changes &&
s_editor.simulation_state != simulation_status::finished) {
ImGui::SameLine();
if (ImGui::Button("step-by-step"))
s_editor.simulation_start_1();
}
ImGui::SameLine();
ImGui::Button("<");
if (ImGui::Button("<") && s_editor.tl.current_bag > 0)
s_editor.simulation_back();
ImGui::SameLine();
ImGui::Button(">");
if (ImGui::Button(">") && s_editor.tl.current_bag < s_editor.tl.bag)
s_editor.simulation_advance();
ImGui::SameLine();
ImGui::Button(">>");
ImGui::Text("bag %d/%d", s_editor.tl.current_bag, s_editor.tl.bag);
ImGui::EndDisabled();
show_simulation_graph_editor(*this);
......
......@@ -880,6 +880,37 @@ public:
return status::success;
}
status copy_to(block_allocator& dst) const noexcept
{
dst.reset();
dst.m_size = m_size;
dst.m_max_size = m_max_size;
dst.m_capacity = m_max_size;
dst.m_free_head = nullptr;
std::copy_n(m_blocks, m_max_size, dst.m_blocks);
if (m_free_head) {
dst.m_free_head = &m_blocks[m_free_head - m_blocks];
const auto* src_free_list = m_free_head->next;
auto* dst_free_list = dst.m_free_head;
dst_free_list->next = nullptr;
while (src_free_list) {
const auto ptr_diff = src_free_list - m_blocks;
dst_free_list->next = &dst.m_blocks[ptr_diff];
src_free_list = src_free_list->next;
dst_free_list = dst_free_list->next;
}
dst_free_list->next = nullptr;
}
return status::success;
}
void reset() noexcept
{
if (m_capacity > 0) {
......
......@@ -9,11 +9,6 @@
namespace irt {
enum class simulation_timelime_point_id : u64;
enum class model_timelime_point_id : u64;
enum class connection_timelime_point_id : u64;
enum class timeline_point_id : u64;
enum class timeline_point_type
{
simulation, //! a copy of state of model (basic - deep copy for now)
......@@ -21,14 +16,7 @@ enum class timeline_point_type
connection //! user add, remove or change value in a connection
};
enum class timeline_operation_type
{
add,
remove,
change,
};
struct simulation_timelime_point
struct simulation_point
{
time t;
......@@ -37,16 +25,24 @@ struct simulation_timelime_point
block_allocator<list_view_node<message>> message_alloc;
};
struct model_timelime_point
struct model_point
{
time t;
model mdl;
model mdl;
model_id id;
timeline_operation_type type = timeline_operation_type::add;
enum class operation_type
{
add,
remove,
change,
};
operation_type type = operation_type::add;
};
struct connection_timelime_point
struct connection_point
{
time t;
......@@ -55,25 +51,26 @@ struct connection_timelime_point
i8 port_src;
i8 port_dst;
timeline_operation_type type = timeline_operation_type::add;
enum class operation_type
{
add,
remove,
};
operation_type type = operation_type::add;
};
struct timeline_point
{
timeline_point(timeline_point_type type_,
u64 id_,
i32 global_identifier_) noexcept
: id(id_)
timeline_point(timeline_point_type type_, i32 index_, i32 bag_) noexcept
: index(index_)
, bag(bag_)
, type(type_)
, global_identifier(global_identifier_)
{}
u64 id;
i32 index;
i32 bag;
timeline_point_type type = timeline_point_type::simulation;
i32 global_identifier;
timeline_point_id prev = undefined<timeline_point_id>();
timeline_point_id next = undefined<timeline_point_id>();
};
struct timeline
......@@ -82,271 +79,55 @@ struct timeline
i32 models = 0,
i32 messages = 0) noexcept;
simulation_timelime_point& alloc_simulation_timelime_point() noexcept;
model_timelime_point& alloc_model_timelime_point() noexcept;
connection_timelime_point& alloc_connection_timelime_point() noexcept;
simulation_point& alloc_simulation_point() noexcept;
model_point& alloc_model_point() noexcept;
connection_point& alloc_connection_point() noexcept;
void push_back(timeline_point_type type,
u64 id,
i32 global_identifier) noexcept;
void pop_front() noexcept;
void pop_front_same_global_identifier() noexcept;
status init(i32 simulation_points,
i32 model_points,
i32 connection_points,
i32 timeline_points,
i32 model_number,
i32 message_number) noexcept;
void clear() noexcept;
void reset() noexcept;
i32 make_next_global_identifier() noexcept;
i32 make_next_bag() noexcept;
data_array<simulation_timelime_point, simulation_timelime_point_id>
sim_points;
data_array<model_timelime_point, model_timelime_point_id> model_points;
data_array<connection_timelime_point, connection_timelime_point_id>
connection_points;
data_array<timeline_point, timeline_point_id> points;
vector<simulation_point> sim_points;
vector<model_point> model_points;
vector<connection_point> connection_points;
vector<timeline_point> points;
i32 global_identifier{};
timeline_point_id first{};
timeline_point_id last{};
i32 max_models_number = 0;
i32 max_messages_number = 0;
i32 current_models_number = 0;
i32 current_messages_number = 0;
i32 bag = 0; //! number of points @TODO replace with points.ssize()
i32 current_bag = -1; //! current point
};
//! @brief Initialize simulation and store first state.
status initialize(timeline& tl, simulation&, time t) noexcept;
status run(timeline& tl, simulation&, time& t) noexcept;
status finalize(timeline& tl, simulation&, time t) noexcept;
inline bool timeline::can_alloc(timeline_point_type type,
[[maybe_unused]] i32 models,
[[maybe_unused]] i32 messages) noexcept
{
bool ret = false;