diff --git a/Makefile b/Makefile index 425f842..6e873e8 100644 --- a/Makefile +++ b/Makefile @@ -115,7 +115,7 @@ LINKER_FLAGS += -ldl -lpthread ################################################### # Make the list of compiled files for the library # -IMPORT_FORMATS += ImportJSON.o ImportPNML.o +IMPORT_FORMATS += ImportJSON.o ImportPNML.o ImportTimedEventGraph.o ExportTimedEventGraph.o EXPORT_FORMATS += ExportJSON.o ExportPNML.o ExportSymfony.o ExportPnEditor.o EXPORT_FORMATS += ExportPetriLaTeX.o ExportJulia.o ExportGraphviz.o ExportDrawIO.o EXPORT_FORMATS += ExportGrafcetCpp.o diff --git a/data/examples/SemiHoward.teg b/data/examples/SemiHoward.teg new file mode 100644 index 0000000..bf52486 --- /dev/null +++ b/data/examples/SemiHoward.teg @@ -0,0 +1,7 @@ +TimedEventGraph 3 5 + +1 0: 0 1 +0 1: 1 0 +0 2: 0 0 +1 2: 1 0 +2 2: 2 1 \ No newline at end of file diff --git a/data/examples/SemiNetherlands.teg b/data/examples/SemiNetherlands.teg new file mode 100644 index 0000000..2ca4eb9 --- /dev/null +++ b/data/examples/SemiNetherlands.teg @@ -0,0 +1,14 @@ +TimedEventGraph 8 12 + +1 0: 61 2 +3 1: 81 1 +0 2: 58 1 +5 2: 0 0 +2 3: 86 2 +4 3: 69 2 +3 4: 69 1 +6 4: 36 1 +4 5: 35 1 +1 6: 0 0 +7 6: 58 1 +5 7: 61 1 diff --git a/doc/export.md b/doc/export.md index 7631a63..aaea961 100644 --- a/doc/export.md +++ b/doc/export.md @@ -1,4 +1,4 @@ -# Exporting the net +# Exporting and importing the net to other file format ## Export to LaTeX @@ -33,7 +33,7 @@ Can export to input files for [Draw.io](https://app.diagrams.net). Can export to input files for [Symfony workflow](https://symfony.com/doc/current/components/workflow.html). -## Export to pn-editor +## Export/Import to pn-editor Can export to input files for [pn-editor](https://gitlab.com/porky11/pn-editor). @@ -45,4 +45,19 @@ You have to git clone and install it manually. If your net is a GRAFCET (GRAphe Fonctionnel de Commande Etapes-Transitions in French, aka Sequential Function Chart (SFC) in English) you can generate the C++ code. -More details [here](grafcet.md). \ No newline at end of file +More details [here](grafcet.md). + +## Export to Timed Graph Event + +Store timed event graph in a compact format. The file format is based this initial [project](http://www.cmap.polytechnique.fr/~gaubert/HOWARD2.html). We are using the +`.teg` extension. + +The first line indicates if we are using "TimedEventGraph", the number of transitions and the number of arcs (which is equals to places / 2). Other lines define each arcs transition to transition, the number of tokens in the place relying the two transitions and the duration. All values are unsigned integer execept for duration which are float. + +Timed event graph file format: + +``` +TimedEventGraph + + : . +``` \ No newline at end of file diff --git a/src/Editor/DearImGui/Editor.cpp b/src/Editor/DearImGui/Editor.cpp index 1e77d50..1a18dc8 100644 --- a/src/Editor/DearImGui/Editor.cpp +++ b/src/Editor/DearImGui/Editor.cpp @@ -1166,7 +1166,7 @@ void Editor::PetriView::Canvas::pop() } //-------------------------------------------------------------------------- -void Editor::PetriView::Canvas::reshape() +ImVec2 Editor::PetriView::Canvas::reshape() { // ImDrawList API uses screen coordinates! corners[0] = ImGui::GetCursorScreenPos(); @@ -1179,12 +1179,13 @@ void Editor::PetriView::Canvas::reshape() // Lock scrolled origin origin = corners[0] + scrolling; + return size; } //-------------------------------------------------------------------------- -void Editor::PetriView::reshape() +ImVec2 Editor::PetriView::reshape() { - m_canvas.reshape(); + return m_canvas.reshape(); } //-------------------------------------------------------------------------- diff --git a/src/Editor/DearImGui/Editor.hpp b/src/Editor/DearImGui/Editor.hpp index 37ea15d..fef70f7 100644 --- a/src/Editor/DearImGui/Editor.hpp +++ b/src/Editor/DearImGui/Editor.hpp @@ -66,6 +66,7 @@ class Editor: public PetriNetEditor, public Application void messagebox(); void inspector(); void view(); + inline ImVec2 viewSize() const { return m_view.size(); } private: // Show results from Petri algorithms @@ -138,16 +139,17 @@ class Editor: public PetriNetEditor, public Application // ******************************************************************** struct GridLayout { - float step = 64.0f; + float step = 50.0f; bool show = true; bool menu = true; } grid; PetriView(Editor& editor); - void reshape(); + ImVec2 reshape(); void onHandleInput(); void drawPetriNet(Net& net, Simulation& simulation); inline ImVec2 const& origin() const { return m_canvas.origin; }; + inline ImVec2 const& size() const { return m_canvas.size; }; private: @@ -176,7 +178,7 @@ class Editor: public PetriNetEditor, public Application ImDrawList* draw_list; ImVec2 getMousePosition(); - void reshape(); + ImVec2 reshape(); void push(); void pop(); } m_canvas; diff --git a/src/Net/Exports/ExportTimedEventGraph.cpp b/src/Net/Exports/ExportTimedEventGraph.cpp new file mode 100644 index 0000000..5c90486 --- /dev/null +++ b/src/Net/Exports/ExportTimedEventGraph.cpp @@ -0,0 +1,63 @@ +//============================================================================= +// TimedPetriNetEditor: A timed Petri net editor. +// Copyright 2021 -- 2023 Quentin Quadrat +// +// This file is part of TimedPetriNetEditor. +// +// TimedPetriNetEditor 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 GNU Emacs. If not, see . +//============================================================================= + +#include "Net/Exports/Exports.hpp" +#include "TimedPetriNetEditor/PetriNet.hpp" +#include "TimedPetriNetEditor/Algorithms.hpp" +#include +#include + +namespace tpne { + +//------------------------------------------------------------------------------ +std::string exportToTimedEventGraph(Net const& net, std::string const& filename) +{ + std::stringstream error; + + if (!isEventGraph(net)) + { + error << "Failed to export the Petri net to '" << filename + << "'. Reason was 'the net is not an event graph'" + << std::endl; + return error.str(); + } + + std::ofstream file(filename); + if (!file) + { + error << "Failed to export the Petri net to '" << filename + << "'. Reason was " << strerror(errno) << std::endl; + return error.str(); + } + + file << "TimedEventGraph " << net.transitions().size() << " " + << net.places().size() << std::endl << std::endl; + + for (auto const& p: net.places()) + { + file << p.arcsIn[0]->from.id << " " << p.arcsOut[0]->to.id + << ": " << p.arcsIn[0]->duration << " " << p.tokens + << std::endl; + } + + return {}; +} + +} // namespace tpne \ No newline at end of file diff --git a/src/Net/Exports/Exports.cpp b/src/Net/Exports/Exports.cpp index 06e4809..b2e7cef 100644 --- a/src/Net/Exports/Exports.cpp +++ b/src/Net/Exports/Exports.cpp @@ -36,6 +36,7 @@ std::vector const& exporters() { "PN-Editor", ".pns,.pnl,.pnk,.pnkp", exportToPNEditor }, { "Petri-LaTeX", ".tex", exportToPetriLaTeX }, { "Petri Net Markup Language", ".pnml", exportToPNML }, + { "Timed Event Graph", ".teg", exportToTimedEventGraph }, //{ "Codesys", ".codesys.xml", exportToCodesys }, //{ "Grafcet-LaTeX", ".tex", exportToGrafcetLaTeX }, }; diff --git a/src/Net/Exports/Exports.hpp b/src/Net/Exports/Exports.hpp index 6fdb1fa..7f77a27 100644 --- a/src/Net/Exports/Exports.hpp +++ b/src/Net/Exports/Exports.hpp @@ -30,6 +30,8 @@ class Net; //! \brief JSON is the main format used for saving Petri by this editor. std::string exportToJSON(Net const& net, std::string const& filename); +//! \brief Import http://www.cmap.polytechnique.fr/~gaubert/HOWARD2.html +std::string exportToTimedEventGraph(Net const& net, std::string const& filename); //! \brief std::string exportToSymfony(Net const& net, std::string const& filename); //! \brief Import https://gitlab.com/porky11/pn-editor diff --git a/src/Net/Imports/ImportTimedEventGraph.cpp b/src/Net/Imports/ImportTimedEventGraph.cpp new file mode 100644 index 0000000..3703af2 --- /dev/null +++ b/src/Net/Imports/ImportTimedEventGraph.cpp @@ -0,0 +1,112 @@ +//============================================================================= +// TimedPetriNetEditor: A timed Petri net editor. +// Copyright 2021 -- 2023 Quentin Quadrat +// +// This file is part of TimedPetriNetEditor. +// +// TimedPetriNetEditor 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 GNU Emacs. If not, see . +//============================================================================= + +#include "Imports.hpp" +#include "TimedPetriNetEditor/PetriNet.hpp" +#include "Utils/Utils.hpp" + +#include +#include + +namespace tpne { + +//------------------------------------------------------------------------------ +std::string importFromTimedEventGraph(Net& net, std::string const& filename) +{ + std::stringstream error; + size_t initial_transition, final_transition, tokens; + float duration; + // windows screen. + // FIXME: get the exact dimension Editor::viewSize() + // FIXME: initial frame iteration: the screen size is not at its final size + const size_t w = 700u; const size_t h = 700u; + const size_t margin = 50u; const size_t nodes_by_line = 4u; + + net.reset(TypeOfNet::TimedPetriNet);//TimedEventGraph); + + // Check if file exists + std::ifstream file(filename); + if (!file) + { + error << "Failed opening '" << filename << "'. Reason was '" + << strerror(errno) << "'" << std::endl; + return error.str(); + } + + // Extract number of transitions and number of lines + size_t transitions, lines, rows; + char separator; + std::string type; + + if (!(file >> type >> transitions >> lines)) + { + error << "Malformed header. Needed 'TimedEventGraph number_transitions number_lines'" + << std::endl; + return error.str(); + } + if (type != "TimedEventGraph") + { + error << "Malformed token. Expected to extract token 'TimedEventGraph'" + << std::endl; + return error.str(); + } + + // Since the file does not give position, we place them as square + size_t dx = (w - 2u * margin) / (nodes_by_line - 1u); + size_t dy = (h - 2u * margin) / (nodes_by_line - 1u); + size_t x = margin, y = margin; + for (size_t id = 0u; id < transitions; ++id) + { + net.addTransition(id, Transition::to_str(id), x, y, 0); + x += dx; + if (x > w - margin) { x = margin; y += dy; } + } + + // Create arcs between created transitions. Places are automatically created + // when the arc is created. + for (size_t i = 0u; i < lines; ++i) + { + if (!(file >> final_transition >> initial_transition >> separator >> duration >> tokens)) + { + error << "Malformed line. Expected 4 values: 'initial_transition" + << " final_transition: duration tokens'" << std::endl; + return error.str(); + } + if ((initial_transition >= transitions) || (final_transition >= transitions)) + { + error << "Malformed line. Invalid transition ID" << std::endl; + return error.str(); + } + if (separator != ':') + { + error << "Malformed line. Missing ':' separator" << std::endl; + return error.str(); + } + + Transition* t0 = net.findTransition(initial_transition); + Transition* t1 = net.findTransition(final_transition); + assert((t0 != nullptr) && (t1 != nullptr)); + net.addArc(*t0, *t1, tokens, duration); + } + + return {}; +} + +} // namespace tpne diff --git a/src/Net/Imports/Imports.cpp b/src/Net/Imports/Imports.cpp index d1b239f..afa7c04 100644 --- a/src/Net/Imports/Imports.cpp +++ b/src/Net/Imports/Imports.cpp @@ -20,6 +20,7 @@ #include "Net/Imports/Imports.hpp" #include +#include namespace tpne { std::vector const& importers() @@ -27,6 +28,8 @@ std::vector const& importers() static const std::vector s_importers = { { "JSON", ".json", importFromJSON }, { "Petri Net Markup Language", ".pnml", importFromPNML }, + // FIXME add a filter to eliminate it in the case the net is not event graph + { "Timed Event Graph", ".teg", importFromTimedEventGraph } }; return s_importers; diff --git a/src/Net/Imports/Imports.hpp b/src/Net/Imports/Imports.hpp index 500f029..01d14af 100644 --- a/src/Net/Imports/Imports.hpp +++ b/src/Net/Imports/Imports.hpp @@ -30,6 +30,8 @@ class Net; //! \brief JSON is the main format used for saving Petri by this editor. std::string importFromJSON(Net& net, std::string const& filename); +//! \brief Import http://www.cmap.polytechnique.fr/~gaubert/HOWARD2.html +std::string importFromTimedEventGraph(Net& net, std::string const& filename); //! \brief Import https://gitlab.com/porky11/pn-editor std::string importFromPNML(Net& net, std::string const& filename); diff --git a/src/Net/PetriNet.cpp b/src/Net/PetriNet.cpp index 2c3cc48..6f4cf51 100644 --- a/src/Net/PetriNet.cpp +++ b/src/Net/PetriNet.cpp @@ -351,6 +351,30 @@ bool Net::sanityArc(Node const& from, Node const& to, bool const strict) const return true; } +//------------------------------------------------------------------------------ +// FIXME: faire l'equivalent de generateArcsInArcsOut +bool Net::addArc(Transition& from, Transition& to, size_t const tokens, float const duration) +{ + // Create the intermediate node + float x = from.x + (to.x - from.x) / 2.0f; + float y = from.y + (to.y - from.y) / 2.0f; + Place& n = addPlace(x, y, tokens); + + // Frist arc + m_arcs.push_back(Arc(from, n, duration)); + from.arcsOut.push_back(&m_arcs.back()); + n.arcsIn.push_back(&m_arcs.back()); + + // Second arc + m_arcs.push_back(Arc(n, to, duration)); + n.arcsOut.push_back(&m_arcs.back()); + to.arcsIn.push_back(&m_arcs.back()); + + generateArcsInArcsOut(); // FIXME a optimiser !!! + modified = true; + return true; +} + //------------------------------------------------------------------------------ // FIXME: faire l'equivalent de generateArcsInArcsOut bool Net::addArc(Node& from, Node& to, float const duration) @@ -366,8 +390,6 @@ bool Net::addArc(Node& from, Node& to, float const duration) m_arcs.push_back(Arc(from, to, duration)); from.arcsOut.push_back(&m_arcs.back()); to.arcsIn.push_back(&m_arcs.back()); - generateArcsInArcsOut(); // FIXME a optimiser !!! - return true; } else // Manage the case "Place -> Place" or "Transition -> Transition" { @@ -385,10 +407,10 @@ bool Net::addArc(Node& from, Node& to, float const duration) m_arcs.push_back(Arc(n, to, duration)); n.arcsOut.push_back(&m_arcs.back()); to.arcsIn.push_back(&m_arcs.back()); - - generateArcsInArcsOut(); // FIXME a optimiser !!! - return true; } + + generateArcsInArcsOut(); // FIXME a optimiser !!! + return true; } //------------------------------------------------------------------------------