Engine: draft a messaging interface for EngineObserver (see #1347)
For now, the `EngineObserver` is defined as an empty shell, outfitted with a low-level binary message dispatch API. Messages are keyed by a Symbol, which allows evolution of private message types. Routing and Addressing is governed by an opaque size_t hash. The `EngineEvent` data base class provides »4 Slots« of inline binary storage; concrete subclasses shall define the mapping of actual data into this space and provide a convenience constructor for events. For use by the Scheduler, a `WorkTiming`-Event is defined based on this scheme; this allows to implement the λ-work and λ-done of the Scheduler-`ExecutionCtx`. These hooks will be invoked at begin and end of any render calculations.
This commit is contained in:
parent
892099412c
commit
449b5c8f50
4 changed files with 268 additions and 18 deletions
|
|
@ -32,11 +32,14 @@
|
|||
|
||||
#include "vault/gear/activity-lang.hpp"
|
||||
#include "lib/format-obj.hpp"
|
||||
#include "lib/symbol.hpp"
|
||||
#include "vault/gear/scheduler.hpp"//////////////////////////////////////////////////////////////TODO extract -> scheduler.cpp
|
||||
|
||||
#include <string>
|
||||
#include <boost/functional/hash.hpp> ////////////////////////////////////////////////////////////TODO should be in a scheduler translation-unit / extract scheduler-API
|
||||
|
||||
using std::string;
|
||||
using lib::Symbol;
|
||||
using lib::time::Time;
|
||||
using lib::time::TimeValue;
|
||||
|
||||
|
|
@ -52,6 +55,10 @@ namespace gear {
|
|||
{
|
||||
return boost::hash_value (uint32_t{id});
|
||||
}
|
||||
|
||||
Symbol WorkTiming::WORKSTART{"WorkStart"};
|
||||
Symbol WorkTiming::WORKSTOP {"WorkStop"};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////TODO extract scheduler-API
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
** @see Activity::Verb::WORKSTART
|
||||
**
|
||||
** @todo WIP-WIP-WIP 10/2023 »Playback Vertical Slice« created as a stub
|
||||
** @todo design and implement the EngineObserver as publisher-subscriber... ////////////////////////////////TICKET #1347 : design EngineObserver
|
||||
**
|
||||
*/
|
||||
|
||||
|
|
@ -44,23 +45,78 @@
|
|||
#include "vault/gear/block-flow.hpp"
|
||||
#include "vault/gear/scheduler-commutator.hpp"
|
||||
#include "vault/gear/scheduler-invocation.hpp"
|
||||
//#include "lib/symbol.hpp"
|
||||
#include "lib/symbol.hpp"
|
||||
#include "lib/nocopy.hpp"
|
||||
//#include "lib/util.hpp"
|
||||
|
||||
//#include <string>
|
||||
#include <utility>
|
||||
#include <array>
|
||||
|
||||
|
||||
namespace vault{
|
||||
namespace gear {
|
||||
|
||||
|
||||
using lib::Symbol;
|
||||
// using util::isnil;
|
||||
// using std::string;
|
||||
using std::move;
|
||||
|
||||
/**
|
||||
* Low-level Render Engine event — abstracted storage base.
|
||||
*/
|
||||
class EngineEvent
|
||||
{
|
||||
protected:
|
||||
static size_t constexpr RAW_SIZ = 3;
|
||||
using Storage = std::array<int64_t, RAW_SIZ>;
|
||||
|
||||
template<class DAT>
|
||||
union Payload
|
||||
{
|
||||
static_assert (sizeof(DAT) <= RAW_SIZ * sizeof(int64_t));
|
||||
|
||||
Storage raw;
|
||||
DAT data;
|
||||
|
||||
Payload() : raw{0} { }
|
||||
Payload (DAT const& d) : data{d} { }
|
||||
Payload (DAT && dr) : data{move(dr)} { }
|
||||
|
||||
// default copy and assignment acceptable
|
||||
|
||||
DAT const& operator= (DAT const& d) { data = d; return data; }
|
||||
DAT const& operator= (DAT && dr) { data = move(dr); return data; }
|
||||
|
||||
operator Storage&() { return raw; }
|
||||
operator Storage&&() { return move(raw); }
|
||||
};
|
||||
|
||||
/** base init for derived classes to implant custom payload */
|
||||
EngineEvent (Symbol msgID, Storage&& payload)
|
||||
: message{msgID}
|
||||
, storage_{move (payload)}
|
||||
{ }
|
||||
|
||||
public:
|
||||
EngineEvent()
|
||||
: message{Symbol::BOTTOM}
|
||||
, storage_{0}
|
||||
{ }
|
||||
|
||||
// default copy and assignment acceptable
|
||||
|
||||
Symbol message;
|
||||
|
||||
private:
|
||||
Storage storage_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Collector and aggregator for performance data.
|
||||
* @todo WIP-WIP 10/2023 - stub as placeholder for later development
|
||||
* @todo WIP-WIP 10/2023 - stub as placeholder for later development ////////////////////////////////////TICKET #1347 : design EngineObserver
|
||||
* @see Scheduler
|
||||
*/
|
||||
class EngineObserver
|
||||
|
|
@ -71,6 +127,12 @@ namespace gear {
|
|||
explicit
|
||||
EngineObserver()
|
||||
{ }
|
||||
|
||||
void
|
||||
dispatchEvent (size_t /*address*/, EngineEvent /*event*/)
|
||||
{
|
||||
/* TICKET #1347 actually move this event into a dispatcher queue */
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@
|
|||
** @see SchedulerCommutator Layer-2
|
||||
** @see activity.hpp description of »Render Activities«
|
||||
**
|
||||
** @todo WIP-WIP 10/2023 »Playback Vertical Slice«
|
||||
** @todo WIP 11/2023 »Playback Vertical Slice«
|
||||
**
|
||||
*/
|
||||
|
||||
|
|
@ -196,7 +196,7 @@ namespace gear {
|
|||
|
||||
/******************************************************//**
|
||||
* »Scheduler-Service« : coordinate render activities.
|
||||
* @todo WIP-WIP 10/2023
|
||||
* @todo WIP 11/2023
|
||||
* @see BlockFlow
|
||||
* @see SchedulerUsage_test
|
||||
*/
|
||||
|
|
@ -387,6 +387,21 @@ namespace gear {
|
|||
|
||||
|
||||
|
||||
/** work-timing event for performance observation */
|
||||
class WorkTiming
|
||||
: public EngineEvent
|
||||
{
|
||||
using Payload = EngineEvent::Payload<Time>;
|
||||
using EngineEvent::EngineEvent;
|
||||
|
||||
static Symbol WORKSTART;
|
||||
static Symbol WORKSTOP;
|
||||
|
||||
public:
|
||||
static WorkTiming start (Time now) { return WorkTiming{WORKSTART, Payload{now}}; }
|
||||
static WorkTiming stop (Time now) { return WorkTiming{WORKSTOP, Payload{now}}; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @remark when due, the scheduled Activities are performed within the
|
||||
* [Activity-Language execution environment](\ref ActivityLang::dispatchChain());
|
||||
|
|
@ -431,16 +446,24 @@ namespace gear {
|
|||
return scheduler_.layer2_.postDispatch (chainEvent, subCtx, scheduler_.layer1_);
|
||||
}
|
||||
|
||||
/**
|
||||
* λ-work : transition Managment-Mode -> Work-Mode
|
||||
* - drop the Grooming-Token (allow concurrent execution from now on)
|
||||
* - signal start time of actual processing
|
||||
* @warning current thread is expected to hold the Grooming-Token
|
||||
*/
|
||||
void
|
||||
work (Time, size_t)
|
||||
work (Time now, size_t qualifier)
|
||||
{
|
||||
UNIMPLEMENTED ("λ-work");
|
||||
scheduler_.layer2_.dropGroomingToken();
|
||||
scheduler_.engineObserver_.dispatchEvent(qualifier, WorkTiming::start(now));
|
||||
}
|
||||
|
||||
/** λ-done : signal end time of actual processing */
|
||||
void
|
||||
done (Time, size_t)
|
||||
done (Time now, size_t qualifier)
|
||||
{
|
||||
UNIMPLEMENTED ("λ-done");
|
||||
scheduler_.engineObserver_.dispatchEvent(qualifier, WorkTiming::stop(now));
|
||||
}
|
||||
|
||||
activity::Proc
|
||||
|
|
|
|||
|
|
@ -83075,32 +83075,77 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<arrowlink COLOR="#373a76" DESTINATION="ID_1435123962" ENDARROW="Default" ENDINCLINATION="304;54;" ID="Arrow_ID_928822414" STARTARROW="None" STARTINCLINATION="-255;13;"/>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node CREATED="1699401804396" ID="ID_1620543415" MODIFIED="1699402101803" TEXT="tendiere dazu, dieses Problem „unter den Teppich zu kehren“">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Warum?
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
wie gesagt, im normalen Betrieb wird nebenbei die Eingangsqueue mit bedient
|
||||
</li>
|
||||
<li>
|
||||
wenn man den Scheduler neu startet, läuft gleich einmal der »Tick« und bedient ebenfalls diese Queue
|
||||
</li>
|
||||
<li>
|
||||
das Problem tritt also nur auf, wenn der Scheduler schon läuft, aber grade idle fällt
|
||||
</li>
|
||||
<li>
|
||||
und auch nur dann, wenn man neue Tasks von außen einstellt, nicht aus einem Management-Job
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
Und — <i>last but not least </i>— die Lösung ist hässlich und redundant: man müßte das in die work-Function ganz am Anfang reinhängen. Oder zumindest in die Kapazitäts-Behandlung (im 1. und 3.Block)
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1697763699242" ID="ID_272943268" MODIFIED="1697763810547" TEXT="EngineObserver-Schnittstelle">
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1697763699242" ID="ID_272943268" MODIFIED="1699414198627" TEXT="EngineObserver-Schnittstelle">
|
||||
<arrowlink COLOR="#4f3156" DESTINATION="ID_1642973256" ENDARROW="Default" ENDINCLINATION="-628;-472;" ID="Arrow_ID_1213898118" STARTARROW="None" STARTINCLINATION="-1771;162;"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<icon BUILTIN="hourglass"/>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1697763821714" ID="ID_1158248288" MODIFIED="1697763853485" TEXT="vorerst nur eine Senke für die WORKSTART|STOP-Activities">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1698372056954" ID="ID_70592894" MODIFIED="1698372163476" TEXT="λ-work einbinden">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1698372056954" ID="ID_70592894" MODIFIED="1699413916193" TEXT="λ-work einbinden">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1698372095829" ID="ID_259045952" MODIFIED="1698372163476" TEXT="λ-done einbinden">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1698372095829" ID="ID_259045952" MODIFIED="1699413919539" TEXT="λ-done einbinden">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1698533894187" ID="ID_1515660165" MODIFIED="1698534658603" TEXT="Mediator / Listener für Betriebszustände">
|
||||
<linktarget COLOR="#ea5540" DESTINATION="ID_1515660165" ENDARROW="Default" ENDINCLINATION="-1517;105;" ID="Arrow_ID_1696922321" SOURCE="ID_1227874220" STARTARROW="None" STARTINCLINATION="912;-77;"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1698534009356" ID="ID_538756893" MODIFIED="1698534407291" TEXT="Notwendigkeit zeichnet sich nun klar ab">
|
||||
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1698534009356" ID="ID_538756893" MODIFIED="1699402223566" TEXT="Notwendigkeit zeichnet sich nun klar ab">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
Habe einige Zeit gezögert, aber nun werden die Verknüpfungen definitiv zu vielfältig. Also stellt der EngineObserver so etwas wie einen »Rück-Kanal« bereit. Leider stellen sich hiermit erneut die gesamten (hinlänglich bekannten) Probleme bezüglich der Domäne eine Benachrichtigungs-Systems. Zu welchem Grad legt das System die Kommunikation fest? Wie offen oder verbindlich sind die Teilnehmer? Wie stark vorherbestimmt sind die Nachrichten-Inhalte? Wieviel Payload ist notwendig?
|
||||
Habe einige Zeit gezögert, aber nun werden die Verknüpfungen definitiv zu vielfältig. Also stellt der EngineObserver so etwas wie einen »Rück-Kanal« bereit. Leider stellen sich hiermit erneut die gesamten (hinlänglich bekannten) Probleme bezüglich der Domäne eines Benachrichtigungs-Systems....
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
Zu welchem Grad legt das System die Kommunikation fest?
|
||||
</li>
|
||||
<li>
|
||||
Wie offen oder verbindlich ist der Kreis der Teilnehmer?
|
||||
</li>
|
||||
<li>
|
||||
Wie stark vorherbestimmt sind die Nachrichten-Inhalte?
|
||||
</li>
|
||||
<li>
|
||||
Wieviel Payload ist notwendig?
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
|
||||
|
|
@ -83112,6 +83157,12 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<node CREATED="1698544770139" ID="ID_1280127725" MODIFIED="1698544795910" TEXT="könnte asynchron und demand-driven arbeiten">
|
||||
<linktarget COLOR="#fefdd9" DESTINATION="ID_1280127725" ENDARROW="Default" ENDINCLINATION="-176;11;" ID="Arrow_ID_187765169" SOURCE="ID_422887207" STARTARROW="None" STARTINCLINATION="-154;-113;"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1699409794378" ID="ID_1067453951" LINK="#ID_1522099049" MODIFIED="1699409857480" TEXT="#1347 EngineObserver">
|
||||
<icon BUILTIN="hourglass"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1699413952330" ID="ID_518717295" LINK="#ID_1307699016" MODIFIED="1699413989310" TEXT="Entwurf für das Event-publish-API">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1697663918107" ID="ID_210862409" MODIFIED="1697675123151" TEXT="Load-Controller vorsehen">
|
||||
|
|
@ -94866,6 +94917,113 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1699403193710" ID="ID_1522099049" MODIFIED="1699403198814" TEXT="Design-Skizze">
|
||||
<node CREATED="1699403199826" ID="ID_1873444035" MODIFIED="1699403208767" TEXT="asyncrhones Messaging"/>
|
||||
<node CREATED="1699403217120" ID="ID_120723001" MODIFIED="1699403224072" TEXT="eine Eingangs-Dispatch-Queue"/>
|
||||
<node CREATED="1699403254867" ID="ID_1562054545" MODIFIED="1699403320882" TEXT="Einspeisen von Nachrichten sehr effizient">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Aufruf  aus der Berechnung heraus möglich, d.h. darf höchstens ein paar µs kosten
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1699403383634" ID="ID_72666374" MODIFIED="1699403396396" TEXT="Addresse ist codiert in einen size_t - Hash">
|
||||
<node CREATED="1699404688180" ID="ID_1754251698" MODIFIED="1699404705526" TEXT="sie repräsentiert einen symbolischen, hierarchischen Endpunkt"/>
|
||||
<node CREATED="1699404706420" ID="ID_1195678814" MODIFIED="1699404726595" TEXT="plus eine Instanz-Nummer"/>
|
||||
</node>
|
||||
<node CREATED="1699404817133" ID="ID_1983952210" MODIFIED="1699404888413" TEXT="Jeder Endpunkt steht für einen Topic-Publisher">
|
||||
<node CREATED="1699404897438" ID="ID_1350627589" MODIFIED="1699404910213" TEXT="der Empfänger registriert sich an diesem aber mit einem Funktor"/>
|
||||
<node CREATED="1699404999507" ID="ID_1908588492" MODIFIED="1699405014485" TEXT="die Instanz-Nummer kann optional in Insanzen dieses Funktors umgesetzt werden"/>
|
||||
<node CREATED="1699405021926" ID="ID_90980190" MODIFIED="1699405045470" TEXT="es können sich mehrere Empfänger registrieren/derigistrieren (Subscriber-Pattern)"/>
|
||||
</node>
|
||||
<node CREATED="1699405149557" ID="ID_1423036678" MODIFIED="1699405164251">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Empfang/Zustellung laufen <b>single-Threaded</b>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<node CREATED="1699405165067" ID="ID_229778014" MODIFIED="1699405192201" TEXT="könnte implementiert sein als ein separater Event-Thread"/>
|
||||
<node CREATED="1699405193033" ID="ID_1200939317" MODIFIED="1699405209359" TEXT="Alternativ: läuft unter GroomingToken im Scheduler-»Tick«"/>
|
||||
</node>
|
||||
<node CREATED="1699405342581" ID="ID_1835834856" MODIFIED="1699405366988" TEXT="die Instanzen....">
|
||||
<node CREATED="1699405367834" ID="ID_1953811997" MODIFIED="1699405371486" TEXT="sind beliebige Typen"/>
|
||||
<node CREATED="1699405372065" ID="ID_1589785923" MODIFIED="1699405379612" TEXT="werden vom EngineObserver gemanaged"/>
|
||||
<node CREATED="1699405382096" ID="ID_1080836669" MODIFIED="1699405396434" TEXT="und können über die Addressierung in eine Language-Referenz übersetzt werden"/>
|
||||
<node CREATED="1699405405867" ID="ID_1288409697" MODIFIED="1699405457592" TEXT="Konsequenz: man kann Instanzen effizient ansprechen und darüber streng typisiert aufrufen"/>
|
||||
<node CREATED="1699405564607" ID="ID_561422739" MODIFIED="1699405582821" TEXT="⟹ Brücke zwischen »Funktor« und Member-Funktion">
|
||||
<node CREATED="1699405616896" ID="ID_850366101" MODIFIED="1699405632436" TEXT="Routing via Address-Hashtable"/>
|
||||
<node CREATED="1699405633054" ID="ID_1875469962" MODIFIED="1699405680754" TEXT="Registrierung mit RTTI-Match und einem indirekten Dispatch"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1699405707070" ID="ID_1718899201" MODIFIED="1699405742710">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
<u>Leitbild</u>: <font face="Monospaced">TimingObservable</font>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="info"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1699409794378" ID="ID_1622814864" MODIFIED="1699409805609" TEXT="#1347 EngineObserver">
|
||||
<icon BUILTIN="hourglass"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1699414012828" ID="ID_1909389454" MODIFIED="1699414023170" TEXT="Datenstrukturen anlegen">
|
||||
<icon BUILTIN="hourglass"/>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#435e98" CREATED="1699413973971" ID="ID_1307699016" MODIFIED="1699414043369">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
<b>EngineEvent</b>-Basisstruktur
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="info"/>
|
||||
<node CREATED="1699414045739" ID="ID_3506591" MODIFIED="1699414061765" TEXT="Symbol + binäre opaque Payload"/>
|
||||
<node CREATED="1699414063936" ID="ID_1879697605" MODIFIED="1699414079359" TEXT="für Payload einen Union-cast-Mechanismus vorsehen"/>
|
||||
<node CREATED="1699414084173" ID="ID_701544569" MODIFIED="1699414099911" TEXT="man definiert für konkrete Events eine Subklasse">
|
||||
<icon BUILTIN="idea"/>
|
||||
<node CREATED="1699414102000" ID="ID_928476961" MODIFIED="1699414117317" TEXT="diese spezialisiert das Template Payload<T>"/>
|
||||
<node CREATED="1699414118433" ID="ID_639697248" MODIFIED="1699414141250" TEXT="T ist ein Tupel / Record mit den zu publizierenden Daten"/>
|
||||
<node CREATED="1699414142638" ID="ID_529883499" MODIFIED="1699414172640">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
<u>Größenbeschränkung</u>: <b>4 »Slots«</b>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1689468998095" ID="ID_32989316" MODIFIED="1689469005279" TEXT="Selbstregulierung">
|
||||
<icon BUILTIN="hourglass"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue