From 7694636103c9a19d7796161dccaf191f4bd5618e Mon Sep 17 00:00:00 2001 From: Quentin Quadrat Date: Wed, 3 Jul 2024 23:35:18 +0200 Subject: [PATCH] WIP GRAFCET --- src/Editor/DearImGui/Editor.cpp | 76 +++++++++++++++++++++++++-------- src/Net/Exports/ExportJSON.cpp | 23 +++++++--- src/Net/Imports/ImportJSON.cpp | 1 + src/Net/Simulation.cpp | 47 ++++++++++++-------- src/Net/Simulation.hpp | 1 + 5 files changed, 106 insertions(+), 42 deletions(-) diff --git a/src/Editor/DearImGui/Editor.cpp b/src/Editor/DearImGui/Editor.cpp index b5f57a1..3659aea 100644 --- a/src/Editor/DearImGui/Editor.cpp +++ b/src/Editor/DearImGui/Editor.cpp @@ -911,12 +911,17 @@ void Editor::messagebox() ImGui::End(); } + //------------------------------------------------------------------------------ void Editor::inspector() { + // InputText modify callback: modified => net shall be saved ? + static bool modified = false; + // InputText callback: GRAFCET transitivities modified ? + static bool compiled = false; // Do not allow editing when running simulation const auto readonly = m_simulation.running ? - ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_None; + ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_None; // Place captions and tokens { @@ -934,7 +939,13 @@ void Editor::inspector() { ImGui::PushID(place.key.c_str()); ImGui::AlignTextToFramePadding(); - ImGui::InputText(place.key.c_str(), &place.caption, readonly); + ImGui::InputText(place.key.c_str(), &place.caption, + readonly | ImGuiInputTextFlags_CallbackEdit, + [](ImGuiInputTextCallbackData*) + { + modified = true; + return 0; + }); // Increment/decrement tokens ImGui::SameLine(); @@ -942,11 +953,13 @@ void Editor::inspector() if (ImGui::ArrowButton("##left", ImGuiDir_Left)) { place.decrement(); + modified = true; } ImGui::SameLine(); if (ImGui::ArrowButton("##right", ImGuiDir_Right)) { place.increment(); + modified = true; } ImGui::PopButtonRepeat(); @@ -971,34 +984,53 @@ void Editor::inspector() ImGui::Separator(); ImGui::Text("%s", (m_net.type() == TypeOfNet::GRAFCET) ? "Transitivities:" : "Captions:"); - // Show contents of transitivities + // Compile transitivities for GRAFCET the initial time and each time one of transitions + // have been edited (Currently: any InputText invalid the whole sensors. Slow but easier + // to implement). + if ((!compiled) && (m_net.type() == TypeOfNet::GRAFCET)) + { + compiled = m_simulation.generateSensors(); + } for (auto& t: m_net.transitions()) { - ImGui::InputText(t.key.c_str(), &t.caption, readonly); - std::vector const& receptivities = m_simulation.receptivities(); - if ((m_net.type() == TypeOfNet::GRAFCET) && (!receptivities.empty()) && (!m_simulation.running)) + // Show contents of transition + ImGui::InputText(t.key.c_str(), &t.caption, + readonly | ImGuiInputTextFlags_CallbackEdit, + [](ImGuiInputTextCallbackData*) + { + modified = true; + compiled = false; + return 0; + }); + + // For GRAFCET and show syntax error on the transitivity + if ((m_net.type() == TypeOfNet::GRAFCET) && (!m_simulation.running)) { - Receptivity const& recp = receptivities[t.id]; - // FIXME parse and clear sensors if and only if we modified entrytext - if (!recp.isValid()) // && recp.compiled() + std::vector const& receptivities = m_simulation.receptivities(); + if (!receptivities.empty()) { - ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "%s", recp.error().c_str()); + Receptivity const& recp = receptivities[t.id]; + if (!recp.isValid()) + { + ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "%s", recp.error().c_str()); + ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "%s", "See help for the syntax"); + } } } } ImGui::End(); + // For GRAFCET show sensor names from transitivities if (m_net.type() == TypeOfNet::GRAFCET) { - if (m_simulation.running) + ImGui::Begin("Sensors"); + for (auto& it: Sensors::instance().database()) { - ImGui::Begin("Sensors"); - for (auto& it: Sensors::instance().database()) - { - ImGui::SliderInt(it.first.c_str(), &it.second, 0, 1); - } - ImGui::End(); + int prev_value = it.second; + ImGui::SliderInt(it.first.c_str(), &it.second, 0, 1); + modified = (prev_value != it.second) && (!m_simulation.running); } + ImGui::End(); } } @@ -1012,7 +1044,9 @@ void Editor::inspector() if (arc.from.type == Node::Type::Transition) { std::string text(arc.from.key + " -> " + arc.to.arcsOut[0]->to.key); + float prev_value = arc.duration; ImGui::InputFloat(text.c_str(), &arc.duration, 0.01f, 1.0f, "%.3f", readonly); + modified = (prev_value != arc.duration); } } ImGui::End(); @@ -1024,10 +1058,17 @@ void Editor::inspector() for (auto& arc: m_net.arcs()) { std::string text(arc.from.key + " -> " + arc.to.key); + float prev_value = arc.duration; ImGui::InputFloat(text.c_str(), &arc.duration, 0.01f, 1.0f, "%.3f", readonly); + modified = (prev_value != arc.duration); } ImGui::End(); } + + // Modified net ? If yes, set it as dirty to force its save when the app + // is closed. + m_net.modified |= modified; + modified = false; } //------------------------------------------------------------------------------ @@ -1323,7 +1364,6 @@ std::vector const& Editor::getLogs() const void Editor::clearLogs() { m_messages.clear(); - } //-------------------------------------------------------------------------- diff --git a/src/Net/Exports/ExportJSON.cpp b/src/Net/Exports/ExportJSON.cpp index 4fcd034..ee0bb14 100644 --- a/src/Net/Exports/ExportJSON.cpp +++ b/src/Net/Exports/ExportJSON.cpp @@ -19,6 +19,7 @@ //============================================================================= #include "Net/Exports/Exports.hpp" +#include "Net/Receptivities.hpp" #include "TimedPetriNetEditor/PetriNet.hpp" #include "nlohmann/json.hpp" #include @@ -40,8 +41,6 @@ std::string exportToJSON(Net const& net, std::string const& filename) return error.str(); } - // TODO sensors - file << "{" << std::endl; file << " \"revision\": 3," << std::endl; file << " \"type\": \"" << to_str(net.type()) << "\"," << std::endl; @@ -74,17 +73,27 @@ std::string exportToJSON(Net const& net, std::string const& filename) for (auto const& a: net.arcs()) { file << separator; separator = ",\n"; - file << " { \"from\": \"" << a.from.key << "\", " << "\"to\": \"" << a.to.key - << "\""; + file << " { \"from\": \"" << a.from.key << "\", " << "\"to\": \"" << a.to.key << "\""; if (a.from.type == Node::Type::Transition) file << ", \"duration\": " << a.duration; file << " }"; } + + // GRAFCET sensors + separator = "\n"; + file << "\n ],\n \"sensors\": ["; + for (auto& it: Sensors::instance().database()) + { + file << separator; separator = ",\n"; + file << " { \"name\": \"" << it.first.c_str() << "\", " + << "\"value\": " << it.second << " }"; + } file << "\n ]" << std::endl; - file << " }" << std::endl; - file << " ]" << std::endl; - file << "}" << std::endl; + // TODO GRAFCET actions + + file << " }\n ]\n"; // nets + file << "}" << std::endl; // json document return {}; } diff --git a/src/Net/Imports/ImportJSON.cpp b/src/Net/Imports/ImportJSON.cpp index 79098cf..90f7648 100644 --- a/src/Net/Imports/ImportJSON.cpp +++ b/src/Net/Imports/ImportJSON.cpp @@ -28,6 +28,7 @@ namespace tpne { //------------------------------------------------------------------------------ +// TODO read sensor values std::string importFromJSON(Net& net, std::string const& filename) { std::stringstream error; diff --git a/src/Net/Simulation.cpp b/src/Net/Simulation.cpp index e26df7a..e90cef6 100644 --- a/src/Net/Simulation.cpp +++ b/src/Net/Simulation.cpp @@ -105,25 +105,12 @@ void Simulation::stateStarting() { a.count = 0u; } - m_net.resetReceptivies(); - // Check for GRAFCET if boolean expressions in transitivities have - // not syntaxical errors. - if (m_net.type() == TypeOfNet::GRAFCET) + // Reset values on transitivities and sensors for GRAFCET + m_net.resetReceptivies(); + if (!generateSensors()) { - Sensors::instance().clear(); - m_receptivities.clear(); - m_receptivities.resize(m_net.transitions().size()); - for (auto const& it: m_net.transitions()) - { - std::string error = m_receptivities[it.id].compile(it.caption, m_net); - if (!error.empty()) - { - m_messages.setWarning(error); - running = false; - return ; - } - } + running = false; } // @@ -144,6 +131,32 @@ void Simulation::stateStarting() m_state = Simulation::State::Simulating; } +//------------------------------------------------------------------------------ +// FIXME Sensors::instance().clear(); is violent we loose current sensor values +// => they are reset to false. +// https://github.com/Lecrapouille/TimedPetriNetEditor/issues/29 +bool Simulation::generateSensors() +{ + // Check for GRAFCET if boolean expressions in transitivities have + // not syntaxical errors. + if (m_net.type() == TypeOfNet::GRAFCET) + { + Sensors::instance().clear(); + m_receptivities.clear(); + m_receptivities.resize(m_net.transitions().size()); + for (auto const& it: m_net.transitions()) + { + std::string error = m_receptivities[it.id].compile(it.caption, m_net); + if (!error.empty()) + { + m_messages.setWarning(error); + return false; + } + } + } + return true; +} + //------------------------------------------------------------------------------ void Simulation::stateHalting() { diff --git a/src/Net/Simulation.hpp b/src/Net/Simulation.hpp index 7554fed..a91e44f 100644 --- a/src/Net/Simulation.hpp +++ b/src/Net/Simulation.hpp @@ -47,6 +47,7 @@ class Simulation Simulation(Net& net, Messages& m_messages); void step(float const dt); + bool generateSensors(); inline std::vector const& timedTokens() const { return m_timed_tokens; } inline std::vector const& receptivities() const { return m_receptivities; }