2024-06-23 19:40:39 +02:00
|
|
|
|
/*
|
|
|
|
|
|
ProcNode - Implementation of render node processing
|
|
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
Copyright (C)
|
|
|
|
|
|
2024, Hermann Vosseler <Ichthyostega@web.de>
|
2024-06-23 19:40:39 +02:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
**Lumiera** 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 2 of the License, or (at your
|
|
|
|
|
|
option) any later version. See the file COPYING for further details.
|
2024-06-23 19:40:39 +02:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
* *****************************************************************/
|
2024-06-23 19:40:39 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @file proc-node.cpp
|
|
|
|
|
|
** Translation unit to hold the actual implementation of node processing operations.
|
|
|
|
|
|
**
|
|
|
|
|
|
** @todo WIP-WIP-WIP 6/2024 not clear yet what goes here and what goes into turnout-system.cpp
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-11-02 23:54:41 +01:00
|
|
|
|
#include "steam/engine/proc-id.hpp"
|
2024-06-23 19:40:39 +02:00
|
|
|
|
#include "steam/engine/proc-node.hpp"
|
2024-11-04 23:56:16 +01:00
|
|
|
|
#include "lib/iter-explorer.hpp"
|
2024-11-03 21:03:34 +01:00
|
|
|
|
#include "lib/format-string.hpp"
|
2024-11-04 23:56:16 +01:00
|
|
|
|
#include "lib/format-util.hpp"
|
2024-11-03 21:03:34 +01:00
|
|
|
|
#include "lib/util.hpp"
|
|
|
|
|
|
|
2024-11-03 22:55:06 +01:00
|
|
|
|
#include <boost/functional/hash.hpp>
|
2024-11-03 21:03:34 +01:00
|
|
|
|
#include <unordered_set>
|
2024-11-04 23:56:16 +01:00
|
|
|
|
#include <set>
|
2024-06-23 19:40:39 +02:00
|
|
|
|
|
2024-11-03 22:55:06 +01:00
|
|
|
|
|
2024-06-23 19:40:39 +02:00
|
|
|
|
namespace steam {
|
|
|
|
|
|
namespace engine {
|
|
|
|
|
|
|
2024-11-04 23:56:16 +01:00
|
|
|
|
using lib::explore;
|
2024-11-03 23:58:25 +01:00
|
|
|
|
using util::_Fmt;
|
|
|
|
|
|
using util::isnil;
|
2024-11-03 21:03:34 +01:00
|
|
|
|
using util::unConst;
|
2024-11-03 23:58:25 +01:00
|
|
|
|
using util::contains;
|
|
|
|
|
|
using boost::hash_combine;
|
2024-06-23 19:40:39 +02:00
|
|
|
|
|
2024-11-04 23:56:16 +01:00
|
|
|
|
namespace {// Details: registration and symbol table for node spec data...
|
2024-11-03 21:03:34 +01:00
|
|
|
|
|
|
|
|
|
|
std::unordered_set<ProcID> procRegistry;
|
2024-11-04 01:04:01 +01:00
|
|
|
|
std::unordered_set<string> symbRegistry;
|
2024-06-23 19:40:39 +02:00
|
|
|
|
|
2024-11-04 23:56:16 +01:00
|
|
|
|
/** deduplicate and re-link to the entry in the symbol table */
|
2024-11-04 01:04:01 +01:00
|
|
|
|
void inline
|
|
|
|
|
|
dedupSymbol (StrView& symbol)
|
|
|
|
|
|
{
|
|
|
|
|
|
auto res = symbRegistry.emplace (symbol);
|
|
|
|
|
|
symbol = *res.first;
|
|
|
|
|
|
}
|
2024-06-23 19:40:39 +02:00
|
|
|
|
} // (END) Details...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Port::~Port() { } ///< @remark VTables for the Port-Turnout hierarchy emitted from \ref proc-node.cpp
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-11-03 21:03:34 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* @remark this is the only public access point to ProcID entries,
|
|
|
|
|
|
* which are automatically deduplicated and managed in a common registry
|
|
|
|
|
|
* and retained until end of the Lumiera process (never deleted).
|
|
|
|
|
|
*/
|
2024-11-02 23:54:41 +01:00
|
|
|
|
ProcID&
|
2024-11-03 22:55:06 +01:00
|
|
|
|
ProcID::describe (StrView nodeSymb, StrView portSpec)
|
2024-11-02 23:54:41 +01:00
|
|
|
|
{
|
2024-11-03 23:58:25 +01:00
|
|
|
|
REQUIRE (not isnil (nodeSymb));
|
|
|
|
|
|
REQUIRE (not isnil (portSpec));
|
|
|
|
|
|
REQUIRE (not contains (nodeSymb, ' '));
|
|
|
|
|
|
auto p = portSpec.find('(');
|
|
|
|
|
|
if (p == string::npos)
|
|
|
|
|
|
throw err::Invalid{_Fmt{"Spec for processing operation must contain at least one argument list. "
|
|
|
|
|
|
"Node:%s Spec:%s"}
|
|
|
|
|
|
% nodeSymb % portSpec
|
|
|
|
|
|
};
|
|
|
|
|
|
auto res = procRegistry.insert (ProcID{nodeSymb, portSpec.substr(0,p), portSpec.substr(p)});
|
2024-11-04 01:04:01 +01:00
|
|
|
|
ProcID& entry{unConst (*res.first)};
|
|
|
|
|
|
if (res.second)
|
|
|
|
|
|
{// new record placed into the registry
|
|
|
|
|
|
dedupSymbol (entry.nodeSymb_);
|
|
|
|
|
|
dedupSymbol (entry.argLists_);
|
|
|
|
|
|
if (not isnil(entry.portQual_))
|
|
|
|
|
|
dedupSymbol (entry.portQual_);
|
|
|
|
|
|
}
|
|
|
|
|
|
return entry;
|
2024-11-02 23:54:41 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-06-23 19:40:39 +02:00
|
|
|
|
/** @internal */
|
2024-11-03 22:55:06 +01:00
|
|
|
|
ProcID::ProcID (StrView nodeSymb, StrView portQual, StrView argLists)
|
2024-11-04 23:56:16 +01:00
|
|
|
|
: nodeSymb_{nodeSymb}
|
2024-11-03 22:55:06 +01:00
|
|
|
|
, portQual_{portQual}
|
|
|
|
|
|
, argLists_{argLists}
|
|
|
|
|
|
{ }
|
2024-06-23 19:40:39 +02:00
|
|
|
|
|
2024-11-04 23:56:16 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* generate registry hash value based on the distinct data in ProcID.
|
|
|
|
|
|
* This function is intended to be picked up by ADL, and should be usable
|
|
|
|
|
|
* both with `std::hash` and `<boost/functional/hash.hpp>`.
|
2024-11-03 21:03:34 +01:00
|
|
|
|
*/
|
|
|
|
|
|
HashVal
|
|
|
|
|
|
hash_value (ProcID const& procID)
|
|
|
|
|
|
{
|
2024-11-03 23:58:25 +01:00
|
|
|
|
HashVal hash = boost::hash_value (procID.nodeSymb_);
|
|
|
|
|
|
if (not isnil(procID.portQual_))
|
|
|
|
|
|
hash_combine (hash, procID.portQual_);
|
|
|
|
|
|
hash_combine (hash, procID.argLists_);
|
|
|
|
|
|
return hash;
|
2024-11-03 21:03:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-04 23:56:16 +01:00
|
|
|
|
string
|
|
|
|
|
|
ProcID::genProcName()
|
|
|
|
|
|
{
|
|
|
|
|
|
std::ostringstream buffer;
|
|
|
|
|
|
buffer << nodeSymb_;
|
|
|
|
|
|
if (not isnil(portQual_))
|
|
|
|
|
|
buffer << '.' << portQual_;
|
|
|
|
|
|
return buffer.str();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-03 23:58:25 +01:00
|
|
|
|
string
|
|
|
|
|
|
ProcID::genProcSpec()
|
|
|
|
|
|
{
|
2024-11-04 01:04:01 +01:00
|
|
|
|
std::ostringstream buffer;
|
|
|
|
|
|
buffer << nodeSymb_;
|
|
|
|
|
|
if (not isnil(portQual_))
|
|
|
|
|
|
buffer << '.' << portQual_;
|
|
|
|
|
|
buffer << argLists_;
|
|
|
|
|
|
return buffer.str();
|
2024-11-03 23:58:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-04 23:56:16 +01:00
|
|
|
|
string
|
|
|
|
|
|
ProcID::genNodeName()
|
|
|
|
|
|
{
|
|
|
|
|
|
return string{nodeSymb_};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-03 23:58:25 +01:00
|
|
|
|
|
2024-11-04 23:56:16 +01:00
|
|
|
|
namespace { // Helper to access ProcID recursively
|
|
|
|
|
|
ProcID&
|
|
|
|
|
|
procID (ProcNode& node)
|
|
|
|
|
|
{
|
|
|
|
|
|
REQUIRE (not isnil(watch(node).ports()));
|
|
|
|
|
|
return watch(node).ports().front().procID;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-11-03 21:03:34 +01:00
|
|
|
|
|
2024-11-04 23:56:16 +01:00
|
|
|
|
string
|
|
|
|
|
|
ProcID::genNodeSpec (Leads& leads)
|
|
|
|
|
|
{
|
|
|
|
|
|
std::ostringstream buffer;
|
|
|
|
|
|
buffer << nodeSymb_;
|
|
|
|
|
|
if (1 != leads.size())
|
|
|
|
|
|
buffer << genSrcSpec(leads);
|
|
|
|
|
|
else
|
|
|
|
|
|
{ // single chain....
|
|
|
|
|
|
ProcNode& p{leads.front().get()};
|
|
|
|
|
|
buffer << "◁—"
|
|
|
|
|
|
<< procID(p).genNodeName() // show immediate predecessor
|
|
|
|
|
|
<< procID(p).genSrcSpec(leads); // and behind that recursively the source(s)
|
|
|
|
|
|
}
|
|
|
|
|
|
return buffer.str();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
string
|
|
|
|
|
|
ProcID::genSrcSpec (Leads& leads)
|
|
|
|
|
|
{
|
|
|
|
|
|
return isnil(leads)? string{"-◎"} // no leads => starting point itself is a source node
|
|
|
|
|
|
: "┉┉{"
|
|
|
|
|
|
+ util::join(
|
|
|
|
|
|
explore(leads)
|
|
|
|
|
|
.expandAll([](ProcNode& n){ return explore(watch(n).leads()); }) // depth-first expand all predecessors
|
|
|
|
|
|
.filter ([](ProcNode& n){ return watch(n).isSrc(); }) // but retain only leafs (≙ source nodes)
|
|
|
|
|
|
.transform([](ProcNode& n){ return procID(n).nodeSymb_;}) // render the node-symbol of each src
|
|
|
|
|
|
.deduplicate()) // sort and deduplicate
|
|
|
|
|
|
+ "}";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @return symbolic string with format `NodeSymb--<predecessorSpec>`
|
|
|
|
|
|
* @remark connectivity information is abbreviated and foremost
|
|
|
|
|
|
* indicates the data source(s)
|
|
|
|
|
|
*/
|
2024-11-02 23:54:41 +01:00
|
|
|
|
string
|
|
|
|
|
|
ProcNodeDiagnostic::getNodeSpec()
|
|
|
|
|
|
{
|
2024-11-04 23:56:16 +01:00
|
|
|
|
REQUIRE (not isnil(ports()));
|
|
|
|
|
|
return ports().front().procID.genNodeSpec (leads());
|
2024-11-02 23:54:41 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HashVal
|
|
|
|
|
|
ProcNodeDiagnostic::getNodeHash() ///< @todo not clear yet if this has to include predecessor info
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED ("calculate an unique hash-key to designate this node");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-03 21:03:34 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* @return symbolic string with format `NodeSymb[.portQualifier](inType[/#][,inType[/#]])(outType[/#][,outType[/#]][ >N])`
|
|
|
|
|
|
* @remark information presented here is passed-through from builder Level-3, based on semantic markup present there
|
|
|
|
|
|
*/
|
2024-11-02 23:54:41 +01:00
|
|
|
|
string
|
|
|
|
|
|
ProcNodeDiagnostic::getPortSpec (uint portIdx)
|
|
|
|
|
|
{
|
2024-11-03 21:03:34 +01:00
|
|
|
|
auto& p{n_.wiring_.ports};
|
2024-11-03 23:58:25 +01:00
|
|
|
|
return portIdx < p.size()? p[portIdx].procID.genProcSpec()
|
|
|
|
|
|
: util::FAILURE_INDICATOR;
|
2024-11-02 23:54:41 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HashVal
|
|
|
|
|
|
ProcNodeDiagnostic::getPortHash (uint portIdx)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED ("calculate an unique, stable and reproducible hash-key to identify the Turnout");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-06-23 19:40:39 +02:00
|
|
|
|
|
|
|
|
|
|
}} // namespace steam::engine
|