Invocation: develop an abbreviated node spec

showing the Node-symbol and a reduced rendering of
either the predecessor or a collection of source nodes.

For this we need functionality to traverse the node graph depth-first
and collect all leaf nodes (which are the source nodes without predecessor);
such can be implemented with the help of the expandAll() functionality
of `lib::IterExplorer`. In addition we need to collect, sort and deduplicate
all the source-node specs; since this is a common requirement, a new
convenience builder was added to `lib::IterExplorer`
This commit is contained in:
Fischlurch 2024-11-04 23:56:16 +01:00
parent 85e2966975
commit a84dbd7bfb
9 changed files with 321 additions and 53 deletions

View file

@ -240,7 +240,7 @@ namespace util {
return join (stringify (args...), "-");
}
/** shortcut: join directly with dashes */
/** shortcut: join directly with dots */
template<typename...ARGS>
inline string
joinDot (ARGS const& ...args)

View file

@ -578,6 +578,52 @@ namespace lib {
/**
* Adapter to »piggy-back« a STL iterable container inline and expose it as »state core«.
* @warning be sure to understand the implications of this setup
* - when initialised by reference, the container's contents will be copied
* - when move-initialised, the container will be destroyed with this iterator
* - the container API remains visible (baseclass), which could confuse trait detection
*/
template<class CON>
class ContainerCore
: public CON
{
using Iter = typename CON::iterator;
Iter p_;
public:
ContainerCore (CON&& container)
: CON(std::forward<CON>(container))
, p_{CON::begin()}
{ }
// copy and assignment acceptable (warning!)
/* === »state core« protocol API === */
bool
checkPoint() const
{
return p_ != CON::end();
}
decltype(auto)
yield() const
{
return *p_;
}
void
iterNext()
{
++p_;
}
};
/**
* Decorator-Adapter to make a »state core« iterable as Lumiera Forward Iterator.

View file

@ -124,6 +124,7 @@
//Forward declaration to allow a default result container for IterExplorer::effuse
namespace std {
template<typename T, class A> class vector;
template<typename K, typename CMP, class A> class set;
}
@ -1881,6 +1882,24 @@ namespace lib {
}
/** preconfigured decorator to materialise, sort and deduplicate all source elements.
* @warning uses heap storage to effuse the source pipeline immediately
*/
template<template<typename> class SET =std::set>
auto
deduplicate()
{
using Value = typename meta::ValueTypeBinding<SRC>::value_type;
using ResCore = ContainerCore<SET<Value>>;
using ResIter = typename _DecoratorTraits<ResCore>::SrcIter;
SET<Value> buffer;
for (auto& val : *this)
buffer.emplace (val);
// »piggiy-back« the collected data into the result iterator
return IterExplorer<ResIter>{ResCore{move (buffer)}};
}
/** _terminal builder_ to package the processing pipeline as IterSource.
* Invoking this function moves the whole iterator compound, as assembled by the preceding

View file

@ -40,8 +40,9 @@
#define ENGINE_PROC_ID_H
#include "lib/hash-standard.hpp"
#include "lib/error.hpp"
#include "lib/hash-standard.hpp"
#include "lib/several.hpp"
//#include "steam/streamtype.hpp"
#include <string>
@ -55,6 +56,7 @@ namespace engine {
using std::string;
using StrView = std::string_view;
class ProcNode;
class ProcID
{
@ -64,14 +66,20 @@ namespace engine {
ProcID (StrView nodeSymb, StrView portQual, StrView argLists);
using ProcNodeRef = std::reference_wrapper<ProcNode>;
using Leads = lib::Several<ProcNodeRef>;
public:
/** build and register a processing ID descriptor */
static ProcID& describe (StrView nodeSymb, StrView portSpec);
/* === symbolic descriptors === */
string genProcSpec(); ///< render a descriptor for the operation (without predecessors)
string genProcName();
string genProcSpec(); ///< render a descriptor for the operation (without predecessors)
string genNodeName();
string genNodeSpec(Leads&);
string genSrcSpec (Leads&); ///< transitively enumerate all unique source nodes
friend bool
operator== (ProcID const& l, ProcID const& r)

View file

@ -30,27 +30,32 @@
#include "steam/engine/proc-id.hpp"
#include "steam/engine/proc-node.hpp"
#include "lib/iter-explorer.hpp"
#include "lib/format-string.hpp"
#include "lib/format-util.hpp"
#include "lib/util.hpp"
#include <boost/functional/hash.hpp>
#include <unordered_set>
#include <set>
namespace steam {
namespace engine {
using lib::explore;
using util::_Fmt;
using util::isnil;
using util::unConst;
using util::contains;
using boost::hash_combine;
namespace { // Details...
namespace {// Details: registration and symbol table for node spec data...
std::unordered_set<ProcID> procRegistry;
std::unordered_set<string> symbRegistry;
/** deduplicate and re-link to the entry in the symbol table */
void inline
dedupSymbol (StrView& symbol)
{
@ -60,7 +65,6 @@ namespace engine {
} // (END) Details...
// using mobject::Placement;
Port::~Port() { } ///< @remark VTables for the Port-Turnout hierarchy emitted from \ref proc-node.cpp
@ -96,14 +100,15 @@ namespace engine {
/** @internal */
ProcID::ProcID (StrView nodeSymb, StrView portQual, StrView argLists)
: nodeSymb_{nodeSymb} /////////////////////////////////////////////////////////OOO intern these strings!!
: nodeSymb_{nodeSymb}
, portQual_{portQual}
, argLists_{argLists}
{ }
/** 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>`.
/**
* 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>`.
*/
HashVal
hash_value (ProcID const& procID)
@ -115,6 +120,16 @@ namespace engine {
return hash;
}
string
ProcID::genProcName()
{
std::ostringstream buffer;
buffer << nodeSymb_;
if (not isnil(portQual_))
buffer << '.' << portQual_;
return buffer.str();
}
string
ProcID::genProcSpec()
{
@ -126,12 +141,65 @@ namespace engine {
return buffer.str();
}
string
ProcID::genNodeName()
{
return string{nodeSymb_};
}
namespace { // Helper to access ProcID recursively
ProcID&
procID (ProcNode& node)
{
REQUIRE (not isnil(watch(node).ports()));
return watch(node).ports().front().procID;
}
}
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)
*/
string
ProcNodeDiagnostic::getNodeSpec()
{
UNIMPLEMENTED ("generate a descriptive Spec of this ProcNode for diagnostics");
REQUIRE (not isnil(ports()));
return ports().front().procID.genNodeSpec (leads());
}
HashVal

View file

@ -239,6 +239,8 @@ namespace engine {
auto& leads() { return n_.wiring_.leads; }
auto& ports() { return n_.wiring_.ports; }
bool isSrc() { return n_.wiring_.leads.empty(); }
bool
isValid()
{

View file

@ -85,10 +85,9 @@ namespace test {
CHECK (watch(n1).isValid());
CHECK (watch(n1).leads().empty());
CHECK (watch(n1).ports().size() == 1);
SHOW_EXPR(watch(n1).getPortSpec(0))
// can generate a symbolic spec to describe the Port's processing functionality...
CHECK (watch(n1).getPortSpec(0) == "Test:Src.dummyFun(TestFrame)"_expect);
SHOW_EXPR(watch(n1).getPortSpec(1))
CHECK (watch(n1).getPortSpec(1) == ""_expect);
// such a symbolic spec is actually generated by a deduplicated metadata descriptor
@ -103,15 +102,19 @@ SHOW_EXPR(watch(n1).getPortSpec(1))
CHECK (hash_value(meta1) != hash_value(meta2));
CHECK (hash_value(meta1) != hash_value(meta3));
SHOW_EXPR(meta1.genProcSpec());
CHECK (meta1.genProcSpec() == "N1(arg)"_expect);
SHOW_EXPR(meta2.genProcSpec());
CHECK (meta2.genProcSpec() == "N2(arg)"_expect);
SHOW_EXPR(meta3.genProcSpec());
CHECK (meta3.genProcSpec() == "N1.uga()"_expect);
// re-generate the descriptor for the source node (n1)
auto& metaN1 = ProcID::describe("Test:Src",DUMMY_FUN_ID);
SHOW_EXPR(metaN1.genProcSpec());
CHECK (metaN1.genProcSpec() == "Test:Src.dummyFun(TestFrame)"_expect);
SHOW_EXPR(metaN1.genProcName())
CHECK (metaN1.genProcName() == "Test:Src.dummyFun"_expect);
SHOW_EXPR(metaN1.genNodeName())
CHECK (metaN1.genNodeName() == "Test:Src"_expect);
SHOW_EXPR(metaN1.genNodeSpec(con.leads))
CHECK (metaN1.genNodeSpec(con.leads) == "Test:Src-◎"_expect);
}

View file

@ -68,6 +68,7 @@
#include <string>
#include <tuple>
#include <cmath>
#include <set>
namespace lib {
@ -291,6 +292,7 @@ namespace test{
verify_IterSource();
verify_reduceVal();
verify_effuse();
verify_dedup();
verify_depthFirstExploration();
demonstrate_LayeredEvaluation();
@ -1234,6 +1236,20 @@ namespace test{
}
/** @test verify to deduplicate the iterator's results into a std::set
*/
void
verify_dedup()
{
CHECK (materialise (
explore(CountDown{23})
.transform([](uint j){ return j % 5; })
.deduplicate()
)
== "0-1-2-3-4"_expect); // note: values were also sorted ascending by std::set
}
/** @test package the resulting Iterator as automatically managed,

View file

@ -13732,9 +13732,7 @@
<node CREATED="1518660033466" ID="ID_1879301424" MODIFIED="1518660044053" TEXT="jede Klausel wird vorbehandelt"/>
<node CREATED="1518660206867" ID="ID_1357657997" MODIFIED="1518660237412">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
technische L&#246;sug <i>diskutierbar</i>
@ -14814,9 +14812,7 @@
</node>
<node CREATED="1509142651113" ID="ID_99734013" MODIFIED="1512926191818">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
Spezialbehandlung
@ -16536,9 +16532,7 @@
</node>
<node COLOR="#435e98" CREATED="1539302481080" ID="ID_126724611" MODIFIED="1576282358105" TEXT="static_assertion">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...damit es nicht versehentlich &#252;ber einen anderen Layer gelegt wird,
@ -18963,9 +18957,7 @@
<font BOLD="true" NAME="SansSerif" SIZE="15"/>
<node COLOR="#33565a" CREATED="1664723420339" HGAP="38" ID="ID_1605718107" MODIFIED="1664727861369" TEXT="Handlungsbedarf?" VSHIFT="39">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
BEDINGUNG: Constraint &lt; Vollgr&#246;&#223;e
@ -46855,9 +46847,7 @@
<node CREATED="1451177573108" ID="ID_1154342108" MODIFIED="1518487921086" TEXT="wenn dieser true zur&#xfc;ckgibt, ist der Zustand persistent"/>
<node CREATED="1451177584435" ID="ID_1792154974" MODIFIED="1518487921086">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
dann wird eine <i>state mark</i>&#160;ausgesendet
@ -47601,9 +47591,7 @@
</node>
<node CREATED="1455913785555" ID="ID_1777441123" MODIFIED="1575133325785" TEXT="transparenter Filter">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
schlechter....
@ -47994,9 +47982,7 @@
<node CREATED="1455928440366" ID="ID_33734026" MODIFIED="1457190669578" TEXT="just pop mutator"/>
<node CREATED="1457190670069" ID="ID_242879400" MODIFIED="1575133327206" TEXT="abandon mutator">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
<b>NOTE</b>: mutator need to be written in such a way
@ -48272,9 +48258,7 @@
<icon BUILTIN="help"/>
<node CREATED="1456424237672" ID="ID_811857290" MODIFIED="1575133328443" TEXT="zu Fu&#xdf; programmieren">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
was man konventionellerweise auch macht.
@ -48423,9 +48407,7 @@
</node>
<node CREATED="1456426322482" ID="ID_1330880148" MODIFIED="1456437520773" TEXT="irreduzibel">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
man kann versuchen, die beiden Elemente der Duplikation aufzul&#246;sen.
@ -91373,8 +91355,8 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="ksmiletris"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1728785840998" ID="ID_663732870" MODIFIED="1728785852800" TEXT="Funktions-ID generieren">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1728785840998" ID="ID_663732870" MODIFIED="1730774373893" TEXT="Funktions-ID generieren">
<icon BUILTIN="button_ok"/>
<node CREATED="1730426912428" ID="ID_1568293923" LINK="#ID_1347337581" MODIFIED="1730682010119" TEXT="tats&#xe4;chlich kann hier einfach die symbolische Spec aus dem im Turnout durchgereicht werden"/>
<node COLOR="#338800" CREATED="1730427234835" ID="ID_1719129337" MODIFIED="1730681997339" TEXT="diese Information mu&#xdf; durchgef&#xe4;delt werden">
<icon BUILTIN="button_ok"/>
@ -91392,15 +91374,138 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1728785867907" ID="ID_394739045" MODIFIED="1730682037506" TEXT="Spec generieren">
<icon BUILTIN="pencil"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1728785876033" ID="ID_424140873" MODIFIED="1728785881465" TEXT="Spec der Funktion">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1728785876033" ID="ID_424140873" MODIFIED="1730774363553" TEXT="Name der Funktion">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1728785882504" ID="ID_607531791" MODIFIED="1730682058719" TEXT="Spec aus dem Turnout">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1729986405645" ID="ID_1710935234" MODIFIED="1729986440374" TEXT="Spec der umschlie&#xdf;enden ProcNode">
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1729986405645" ID="ID_1710935234" MODIFIED="1730774358145" TEXT="Spec der umschlie&#xdf;enden ProcNode">
<arrowlink COLOR="#666a8d" DESTINATION="ID_1032840307" ENDARROW="Default" ENDINCLINATION="312;27;" ID="Arrow_ID_1338984517" STARTARROW="None" STARTINCLINATION="158;-13;"/>
<icon BUILTIN="flag-yellow"/>
<icon BUILTIN="pencil"/>
<node BACKGROUND_COLOR="#c8c0b6" CREATED="1730762304054" HGAP="25" ID="ID_1617701375" MODIFIED="1730774167902" VSHIFT="10">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
<u>Wunsch</u>: abgek&#252;rzte Darstellung der Vorl&#228;ufer
</p>
</body>
</html></richcontent>
<node CREATED="1730762334184" ID="ID_1202353548" MODIFIED="1730762346559" TEXT="wenn Einzel-Node: dann deren Namen und Quell-Spec"/>
<node CREATED="1730762347685" ID="ID_609608264" MODIFIED="1730762353185" TEXT="sonst direkt die Quell-Spec"/>
</node>
<node COLOR="#435e98" CREATED="1730772678964" ID="ID_1510545208" MODIFIED="1730774144647" TEXT="brauche depth-first alle Quell-Nodes">
<icon BUILTIN="yes"/>
<node CREATED="1730772719839" ID="ID_1652819533" MODIFIED="1730772733330" TEXT="das ist kein reines flatMap(), da rekursiv">
<icon BUILTIN="stop-sign"/>
</node>
<node CREATED="1730772701546" ID="ID_666196583" MODIFIED="1730772714108" TEXT="k&#xf6;nnte man mit lib::explore() + expand machen"/>
<node CREATED="1730773128936" ID="ID_1794250437" MODIFIED="1730773147814" TEXT="mu&#xdf; dann aber innere Knoten eigens ausfiltern">
<node CREATED="1730773150469" ID="ID_1989703053" MODIFIED="1730773743409" TEXT="der IterExplorer kann das bisher nicht....">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
...dar&#252;ber hab ich damals viel nachgedacht: eine generische Implementierung der Blatt-Iteration ist nicht so einfach zu realisieren, da man einem Element nicht anhand generischer Eigenschaften ansehen kann, ob es ein Blatt ist oder noch weiter expandiert werden kann. Ein Ausweg w&#228;re, das Element versuchsweise zu expandieren und es selber nur zur&#252;ckzuliefern, wenn die Expansion leer ist. Davon habe ich damals aber Abstand genommen, da es (a) erfordert, den Aufwand f&#252;r die Expansion stets und f&#252;r jedes Element zu leisten und (b) dann irgendwie eine Interaktion mit dem internen Stack stattfinden mu&#223;, und das dann auch noch rekursiv oder repretitiv&#160;&#160;(und das wurde mir dann alles zu kompliziert)
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1730773167011" ID="ID_114695925" MODIFIED="1730773887545" TEXT="aber wir wissen: keine Leads &#x27f9; ist Quell-Node">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
...und deshalb k&#246;nnen wir erst expandAll() machen, und dann die inneren Blattknoten einfach nachtr&#228;glich wegfiltern, woduch automatisch weiter repetitiv konsumiert wird.
</p>
</body>
</html>
</richcontent>
</node>
</node>
</node>
<node COLOR="#435e98" CREATED="1730773892379" ID="ID_1873949719" MODIFIED="1730774151857" TEXT="m&#xf6;chte aber auch noch deduplizieren">
<icon BUILTIN="yes"/>
<node CREATED="1730773903961" ID="ID_578892742" MODIFIED="1730773916259" TEXT="das k&#xf6;nnte man aber definitiv in IterExplorer einbauen"/>
<node CREATED="1730773917301" ID="ID_1998930932" MODIFIED="1730773929478" TEXT="das ist wie ein effuse() in eine std::map">
<node CREATED="1730773931686" ID="ID_686657925" MODIFIED="1730773941216" TEXT="geht nicht direkt mit effuse()"/>
<node CREATED="1730773942228" ID="ID_708017506" MODIFIED="1730773961826" TEXT="weil std::map kein push_back kennt, sondern nur insert bzw. emplace"/>
</node>
<node CREATED="1730773968577" ID="ID_1309038873" MODIFIED="1730773982723" TEXT="der resultierende Container w&#xe4;re wieder iterable"/>
<node CREATED="1730773983921" ID="ID_525211889" MODIFIED="1730774038666" TEXT="Problem: IterExplorer nimmt Container bisher nur per Referenz">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
....aus Sicherheits-Gr&#252;nden: der Container soll irgendwo &#8222;daneben&#8220; in sicherer Storage liegen
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1730774040094" ID="ID_361668791" MODIFIED="1730774051846" TEXT="Man k&#xf6;nnte aber den Container in eine &#xbb;state-core&#xab; packen">
<node COLOR="#435e98" CREATED="1730774059450" ID="ID_1347191239" MODIFIED="1730774091567" TEXT="da m&#xfc;&#xdf;te er per move(con) rein">
<icon BUILTIN="messagebox_warning"/>
</node>
<node COLOR="#338800" CREATED="1730774068251" ID="ID_1062996816" MODIFIED="1730774094301" TEXT="neuer Iter-Adapter: ContainerCore">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node COLOR="#338800" CREATED="1730774112045" ID="ID_1339216059" MODIFIED="1730774121428" TEXT="IterExplorer::deduplicate()">
<icon BUILTIN="button_ok"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1730774572424" ID="ID_1656589011" MODIFIED="1730774586813" TEXT="Abh&#xe4;ngigkeit auf std::set">
<icon BUILTIN="messagebox_warning"/>
</node>
<node COLOR="#435e98" CREATED="1730774588254" ID="ID_189433789" MODIFIED="1730774753080" TEXT="wieder den Trick mit Forward-Deklaration">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Der Trick ist: die Funktion nimmt ein Template-Argument, das aber als Default den forward-deklarierten STL-Container hat. Scheint mit meinem GCC zu klappen .... ich bin da aber sehr skeptisch, denn die Signatur mit den Template-Parametern k&#246;nnte sich in Zukunft schon noch erweitern.....
</p>
</body>
</html>
</richcontent>
<icon BUILTIN="idea"/>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1730774727731" ID="ID_150671538" MODIFIED="1730774857648" TEXT="bin erst k&#xfc;rzlich mit Yoshimi und Clang mit etwas &#xe4;hnlichem in Probleme geraten">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
auch da wollte ich einen pervasiven #include &lt;map&gt; vermeiden. Dann kam aber ein Defekt-Report von einem Clang-User auf BSD (und zwar der neueste Clang). Dort war der neue C++17 polymorphic-Allocator anders deklariert, so da&#223; es zu einer <i>ambiguity</i>&#160;gekommen ist.
</p>
</body>
</html></richcontent>
<icon BUILTIN="clanbomber"/>
</node>
</node>
<node COLOR="#338800" CREATED="1730774125353" ID="ID_457256918" MODIFIED="1730774132853" TEXT="Testfall dazu...">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node COLOR="#338800" CREATED="1730774185740" ID="ID_173460273" MODIFIED="1730774327276" TEXT="Darstellung">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1730774195978" ID="ID_506214252" MODIFIED="1730774325674" TEXT="erst mal stets das Node-Symbol selber">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1730774207472" ID="ID_1605389357" MODIFIED="1730774325673" TEXT="gefolgt von der neuen &#xbb;SrcSpec&#xab;">
<icon BUILTIN="button_ok"/>
<node CREATED="1730774277791" ID="ID_506373939" MODIFIED="1730774290283" TEXT="die soeben entwickelte deduplizierte Pipeline"/>
<node CREATED="1730774291308" ID="ID_1583882759" MODIFIED="1730774306623" TEXT="per util::join() kommasepariert und in geschweifte Klammern"/>
<node CREATED="1730774308601" ID="ID_388580554" MODIFIED="1730774321664" TEXT="oder ein direkter Quell-Marker wenn die Leads leer sind"/>
</node>
<node COLOR="#338800" CREATED="1730774235517" ID="ID_675649088" MODIFIED="1730774325673" TEXT="Spezialfall Single-Link">
<icon BUILTIN="button_ok"/>
<node CREATED="1730774248763" ID="ID_518275033" MODIFIED="1730774259392" TEXT="dann erst diesen einzigen Vorg&#xe4;nger auch anzeigen"/>
<node CREATED="1730774260378" ID="ID_1141098746" MODIFIED="1730774271710" TEXT="und erst auf diesen getSrcSpec() anwenden"/>
</node>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1730774330224" ID="ID_1021456970" MODIFIED="1730774355189" TEXT="mit komplexem Node-Tree testen">
<icon BUILTIN="bell"/>
</node>
</node>
</node>
</node>
@ -91665,6 +91770,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</p>
</body>
</html></richcontent>
<arrowlink COLOR="#fbfeda" DESTINATION="ID_241387326" ENDARROW="Default" ENDINCLINATION="-379;-27;" ID="Arrow_ID_1982618460" STARTARROW="None" STARTINCLINATION="-161;5;"/>
<linktarget COLOR="#c7dcf5" DESTINATION="ID_423678075" ENDARROW="Default" ENDINCLINATION="461;-17;" ID="Arrow_ID_268382631" SOURCE="ID_519915836" STARTARROW="None" STARTINCLINATION="409;26;"/>
<icon BUILTIN="idea"/>
</node>
@ -91749,7 +91855,8 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="smiley-oh"/>
</node>
<node CREATED="1730598198960" ID="ID_1268799308" MODIFIED="1730598217841" TEXT="&#x27f9; halbwegs sinnvolle Aufrufstruktur">
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1730598219853" ID="ID_241387326" MODIFIED="1730598252076" TEXT="ProcID &#x2259; separater Metadaten-Record">
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1730598219853" ID="ID_241387326" MODIFIED="1730733979784" TEXT="ProcID &#x2259; separater Metadaten-Record">
<linktarget COLOR="#fbfeda" DESTINATION="ID_241387326" ENDARROW="Default" ENDINCLINATION="-379;-27;" ID="Arrow_ID_1982618460" SOURCE="ID_423678075" STARTARROW="None" STARTINCLINATION="-161;5;"/>
<icon BUILTIN="idea"/>
<node CREATED="1730598396950" ID="ID_1069190015" MODIFIED="1730598409561" TEXT="soll nur die semantisch relevanten Infos enthalten"/>
<node CREATED="1730598414787" ID="ID_423504615" MODIFIED="1730598421854" TEXT="und zwar aufgebrochen in einzelne Felder">
@ -91847,8 +91954,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</li>
</ul>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="button_ok"/>
</node>
</node>