Block-Flow: add diagnostics and define further expectations

- fix a bug in IterExplorer: when iterating a »state core« directly,
  the helper CoreYield passed the detected type through ValueTypeBindings.
  This is logically wrong, because we never want to pick up some typedefs,
  rather we always want to use the type directly returned from CORE::yield()
  Here the iterator returns an Epoch&, which itself is again iterable
  (it inherits from std::array<Activity, N>). However, it is clear
  that we must not descent into such a "flatMap" style recursive expansion

- draft a simple scheme how to regulate Epoch lengths dynamically

- add diagnostics to pinpoint a given Activity and find out into which
  Epoch it has been allocated; used to cover the allocator behaviour
This commit is contained in:
Fischlurch 2023-07-15 18:54:59 +02:00
parent 7167ad6d96
commit cb2ee9466b
4 changed files with 213 additions and 25 deletions

View file

@ -269,9 +269,9 @@ namespace lib {
{
using Res = remove_reference_t<decltype(std::declval<COR>().yield())>;
using value_type = typename meta::ValueTypeBinding<Res>::value_type;
using reference = typename meta::ValueTypeBinding<Res>::reference;
using pointer = typename meta::ValueTypeBinding<Res>::pointer;
using value_type = typename meta::RefTraits<Res>::Value;
using reference = typename meta::RefTraits<Res>::Reference;
using pointer = typename meta::RefTraits<Res>::Pointer;
};
@ -285,8 +285,9 @@ namespace lib {
template<class SRC>
struct _DecoratorTraits<SRC, enable_if<is_StateCore<SRC>>>
{
using SrcVal = typename CoreYield<SRC>::value_type;
using SrcIter = lib::IterableDecorator<SrcVal, lib::CheckedCore<SRC>>;
using SrcRaw = typename lib::meta::Strip<SRC>::Type;
using SrcVal = typename CoreYield<SrcRaw>::value_type;
using SrcIter = lib::IterableDecorator<SrcVal, lib::CheckedCore<SrcRaw>>;
};
template<class SRC>

View file

@ -55,6 +55,9 @@
#include "vault/gear/activity.hpp"
#include "vault/mem/extent-family.hpp"
#include "lib/time/timevalue.hpp"
#include "lib/iter-explorer.hpp"
#include "lib/format-util.hpp"
#include "lib/rational.hpp"
#include "lib/nocopy.hpp"
#include "lib/util.hpp"
@ -64,6 +67,7 @@
namespace vault{
namespace gear {
using util::Rat;
using util::isnil;
using lib::time::Time;
using lib::time::TimeVar;
@ -80,6 +84,9 @@ namespace gear {
const Duration INITIAL_EPOCH_STEP{FRAMES_PER_EPOCH * FrameRate{50}.duration()};
const Rat OVERFLOW_BOOST_FACTOR = 9_r/10; ///< increase capacity on each Epoch overflow event
const size_t AVERAGE_EPOCHS = 10; ///< moving average len for exponential convergence towards average Epoch fill
/** raw allocator to provide a sequence of Extents to place Activity records */
using Allocator = mem::ExtentFamily<Activity, EPOCH_SIZ>;
}
@ -218,11 +225,17 @@ namespace gear {
{ }
Duration
currEpochStep() const
getEpochStep() const
{
return Duration{epochStep_};
}
void
adjustEpochStep (Rat factor)
{
epochStep_ = getEpochStep() * factor;
}
/** Adapted storage-extent iterator, directly exposing Extent& */
using EpochIter = lib::IterableDecorator<Epoch, StorageAdaptor>;
@ -254,6 +267,10 @@ namespace gear {
return *new(claimSlot()) Activity {std::forward<ARGS> (args)...};
}
Time currDeadline() const { return epoch_->deadline(); }
bool hasFreeSlot() const { return epoch_->gate().hasFreeSlot(); }
private:
void*
claimSlot() ///< EX_SANE
@ -306,6 +323,34 @@ namespace gear {
}
/**
* Notify and adjust Epoch capacity as consequence of exhausting an Epoch.
* Whenever some Epoch can not accommodate a required allocation, the allocation
* is placed into subsequent Epoch(s) and then this event is triggered, reducing
* the epochStep_ by #OVERFLOW_BOOST_FACTOR to increase capacity.
*/
void
markEpochOverflow()
{
UNIMPLEMENTED ("adjust size after overflow");
}
/**
* On clean-up of past Epochs, the actual fill factor is checked to guess an
* Epoch duration for optimal usage of epoch storage. Assuming that requested
* Activity deadlines are evenly spaced, for a simple heuristic we can just divide
* actual Epoch duration by the fill factor (longer Epoch => less capacity).
* To avoid control oscillations however, it seems prudent to use damping by
* an exponential moving average, nominally over #AVERAGE_EPOCHS.
* The current epochStep_ is assumed to be such a moving average, and will be
* updated accordingly.
*/
void
markEpochUnderflow (Duration actualLen, Rat fillFactor)
{
UNIMPLEMENTED ("adjust size when detecting partially filled Epochs");
}
private:
Epoch&
@ -354,6 +399,27 @@ namespace gear {
Time last() { return flow_.lastEpoch().deadline(); }
size_t cntEpochs() { return watch(flow_.alloc_).active(); }
size_t poolSize() { return watch(flow_.alloc_).size(); }
/** find out in which Epoch the given Activity was placed */
TimeValue
find (Activity& someActivity)
{
for (Epoch& epoch : flow_.allEpochs())
for (Activity& act : epoch)
if (util::isSameObject (act, someActivity))
return epoch.deadline();
return Time::NEVER;
}
/** render deadlines of all currently active Epochs */
std::string
allEpochs()
{
if (isnil (flow_.alloc_)) return "";
auto deadlines = lib::explore (flow_.allEpochs())
.transform([](Epoch& a){ return TimeValue{a.deadline()}; });
return util::join(deadlines, "|");
}
};
inline FlowDiagnostic

View file

@ -88,7 +88,7 @@ namespace test {
CHECK (tick.verb_ == Activity::TICK);
CHECK (1 == watch(bFlow).cntEpochs());
CHECK (watch(bFlow).first() > deadline);
CHECK (watch(bFlow).first() - deadline == bFlow.currEpochStep());
CHECK (watch(bFlow).first() - deadline == bFlow.getEpochStep());
bFlow.discardBefore (deadline + Time{0,5});
CHECK (0 == watch(bFlow).cntEpochs());
@ -217,22 +217,22 @@ namespace test {
Time t3 = Time{ 0,11};
auto& a1 = bFlow.until(t1).create();
CHECK (watch(bFlow).allEpochs() == "10:200"_expect);
CHECK (watch(bFlow).find(a1) == "10:200"_expect);
CHECK (watch(bFlow).allEpochs() == "10s200ms"_expect);
CHECK (watch(bFlow).find(a1) == "10s200ms"_expect);
auto& a3 = bFlow.until(t3).create();
CHECK (watch(bFlow).allEpochs() == "10:200|10:400|10:600|10:800|11:00"_expect);
CHECK (watch(bFlow).find(a3) == "11:000"_expect);
CHECK (watch(bFlow).allEpochs() == "10s200ms|10s400ms|10s600ms|10s800ms|11s0ms"_expect);
CHECK (watch(bFlow).find(a3) == "11s0ms"_expect);
auto& a2 = bFlow.until(t2).create();
CHECK (watch(bFlow).allEpochs() == "10:200|10:400|10:600|10:800|11:00"_expect);
CHECK (watch(bFlow).find(a2) == "11:600"_expect);
CHECK (watch(bFlow).allEpochs() == "10s200ms|10s400ms|10s600ms|10s800ms|11s00ms"_expect);
CHECK (watch(bFlow).find(a2) == "11s600ms"_expect);
Time t0 = Time{0,5};
auto& a0 = bFlow.until(t0).create();
CHECK (watch(bFlow).allEpochs() == "10:200|10:400|10:600|10:800|11:00"_expect);
CHECK (watch(bFlow).find(a2) == "10:200"_expect);
CHECK (watch(bFlow).allEpochs() == "10s200ms|10s400ms|10s600ms|10s800ms|11s00ms"_expect);
CHECK (watch(bFlow).find(a0) == "10s200ms"_expect);
BlockFlow::AllocatorHandle allocHandle = bFlow.until(Time{300,10});
for (uint i=1; i<Epoch::SIZ(); ++i)
@ -244,7 +244,7 @@ namespace test {
auto a4 = allocHandle.create();
CHECK (allocHandle.currDeadline() == Time(600,10));
CHECK (allocHandle.hasFreeSlot());
CHECK (watch(bFlow).find(a2) == "10:600"_expect);
CHECK (watch(bFlow).find(a4) == "10s600ms"_expect);
for (uint i=1; i<Epoch::SIZ(); ++i)
allocHandle.create();
@ -252,7 +252,7 @@ namespace test {
CHECK (allocHandle.currDeadline() == Time(800,10));
auto& a5 = bFlow.until(Time{220,10}).create();
CHECK (watch(bFlow).find(a5) == "10:600"_expect);
CHECK (watch(bFlow).find(a5) == "10s600ms"_expect);
allocHandle = bFlow.until(Time{900,10});
for (uint i=1; i<Epoch::SIZ(); ++i)
@ -260,18 +260,18 @@ namespace test {
CHECK (not allocHandle.hasFreeSlot());
auto& a6 = bFlow.until(Time{850,10}).create();
CHECK (watch(bFlow).find(a6) == "11:150"_expect);
CHECK (watch(bFlow).allEpochs() == "10:200|10:400|10:600|10:800|11:00|11:150"_expect);
CHECK (watch(bFlow).find(a6) == "11s150ms"_expect);
CHECK (watch(bFlow).allEpochs() == "10s200ms|10s400ms|10s600ms|10s800ms|11s00ms|11s150ms"_expect);
auto& a7 = bFlow.until(Time{500,11}).create();
CHECK (watch(bFlow).find(a6) == "11:600"_expect);
CHECK (watch(bFlow).allEpochs() == "10:200|10:400|10:600|10:800|11:00|11:150|11:300|11:450|11:600"_expect);
CHECK (watch(bFlow).find(a7) == "11s600ms"_expect);
CHECK (watch(bFlow).allEpochs() == "10s200ms|10s400ms|10s600ms|10s800ms|11s00ms|11s150ms|11s300ms|11s450ms|11s600ms"_expect);
bFlow.discardBefore (Time{999,10});
CHECK (watch(bFlow).allEpochs() == "11:00|11:150|11:300|11:450|11:600"_expect);
CHECK (watch(bFlow).allEpochs() == "11s00ms|11s150ms|11s300ms|11s450ms|11s600ms"_expect);
auto& a8 = bFlow.until(Time{500,10}).create();
CHECK (watch(bFlow).find(a6) == "11:150"_expect);
CHECK (watch(bFlow).find(a8) == "11s150ms"_expect);
}
@ -282,6 +282,28 @@ namespace test {
void
adjustEpochs()
{
BlockFlow bFlow;
CHECK (bFlow.getEpochStep() == INITIAL_EPOCH_STEP);
bFlow.markEpochOverflow();
CHECK (bFlow.getEpochStep() == INITIAL_EPOCH_STEP * OVERFLOW_BOOST_FACTOR);
bFlow.markEpochOverflow();
CHECK (bFlow.getEpochStep() == INITIAL_EPOCH_STEP * OVERFLOW_BOOST_FACTOR*OVERFLOW_BOOST_FACTOR);
Duration dur1 = INITIAL_EPOCH_STEP;
Duration dur2 = INITIAL_EPOCH_STEP * OVERFLOW_BOOST_FACTOR;
TimeVar step = bFlow.getEpochStep();
Rat fill = 8_r/10;
Rat N = AVERAGE_EPOCHS;
bFlow.markEpochUnderflow (dur1, fill);
CHECK (bFlow.getEpochStep() == step*((N-1)/N) + dur1*(1/N /fill));
step = bFlow.getEpochStep();
fill = 3_r/10;
bFlow.markEpochUnderflow (dur2, fill);
CHECK (bFlow.getEpochStep() == step*((N-1)/N) + dur2*(1/N /fill));
}

View file

@ -79949,6 +79949,34 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="button_ok"/>
</node>
</node>
<node COLOR="#435e98" CREATED="1689436181499" ID="ID_32560484" MODIFIED="1689439546663" TEXT="IterExplorer entgleist">
<icon BUILTIN="broken-line"/>
<node CREATED="1689436193834" ID="ID_156067579" MODIFIED="1689436224645" TEXT="als value_type wird Activity deduziert (nicht Epoch)">
<icon BUILTIN="info"/>
</node>
<node CREATED="1689436225702" ID="ID_937023531" MODIFIED="1689436231818" TEXT="passiert in CoreYield">
<node CREATED="1689436233565" ID="ID_709751117" MODIFIED="1689436248826" TEXT="CoreYield::Res ist noch korrekt (Epoch)"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1689436364852" ID="ID_1935791492" MODIFIED="1689436375515" TEXT="lib::meta::ValueTypeBinding biegt falsch ab!!!">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1689436477256" ID="ID_1404069442" MODIFIED="1689436481648" TEXT="RefTraits&lt;Yield::Res&gt;::Value ist noch Epoch"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1689436575624" ID="ID_1950477566" MODIFIED="1689436920354" TEXT="aber Epoch::value_type -&gt; Activity">
<icon BUILTIN="clanbomber"/>
<node CREATED="1689436608012" ID="ID_745086607" MODIFIED="1689436620678" TEXT="was sogar eindeutig korrekt ist">
<icon BUILTIN="ksmiletris"/>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1689436894638" ID="ID_1161969261" MODIFIED="1689436924919" TEXT="denn Activity ist selber wieder iterierbar">
<icon BUILTIN="forward"/>
</node>
</node>
<node COLOR="#5b280f" CREATED="1689436935224" ID="ID_582119395" MODIFIED="1689436963797" TEXT="es ist logich falsch, in CoreYield die ValueTypeBindings zu verwenden">
<icon BUILTIN="stop-sign"/>
</node>
</node>
<node COLOR="#338800" CREATED="1689436974307" ID="ID_1380053330" MODIFIED="1689439543632" TEXT="CoreYield umstellen auf die reinen RefTraits">
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1688516187452" ID="ID_843039397" MODIFIED="1688516195292" TEXT="Zusatz-Infos zu verwalten">
<icon BUILTIN="flag-yellow"/>
@ -80056,6 +80084,14 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1689204233030" ID="ID_649398296" MODIFIED="1689204243981" TEXT="Frage: Parametrisierung?">
<icon BUILTIN="help"/>
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1689357111293" ID="ID_1755712040" MODIFIED="1689357127978" TEXT="Alle Werte erst mal fest verdrahtet">
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
<icon BUILTIN="yes"/>
</node>
<node CREATED="1689357095214" ID="ID_242896897" MODIFIED="1689357103217" TEXT="sch&#xe4;tze 10 Activities pro Frame"/>
<node CREATED="1689357080233" ID="ID_1640937150" MODIFIED="1689357093042" TEXT="Startwert ist zu&#xe4;chst an 50fps festgemacht"/>
<node CREATED="1689357131499" ID="ID_667312087" MODIFIED="1689357148299" TEXT="versuche mal mit 100 Slots pro Epoche zu arbeiten"/>
<node CREATED="1689357384865" ID="ID_979539917" MODIFIED="1689357481624" TEXT="Tick-Ferquenz: 1 Tick pro Sekunde?"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1689204252820" ID="ID_1727328732" MODIFIED="1689204287072" TEXT="Epoch-Spacing-Mechanismus">
<icon BUILTIN="flag-yellow"/>
@ -80065,17 +80101,74 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1689204305717" ID="ID_123055064" MODIFIED="1689204325821" TEXT="sp&#xe4;ter wird auch dieses Thema auf TimingObservable aufsetzen">
<icon BUILTIN="yes"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1689204331764" ID="ID_578018270" MODIFIED="1689204425304" TEXT="vorerst einfache ged&#xe4;mpfte MIttelung">
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1689204331764" ID="ID_578018270" MODIFIED="1689204425304" TEXT="vorerst einfache ged&#xe4;mpfte Mittelung">
<icon BUILTIN="yes"/>
<node CREATED="1689354964927" ID="ID_1525931195" MODIFIED="1689354982888" TEXT="mir schwebt ein exponentielles moving-average vor"/>
<node CREATED="1689355051116" ID="ID_214862798" MODIFIED="1689355076675" TEXT="Ansatz">
<node CREATED="1689355077335" ID="ID_250434643" MODIFIED="1689355094729" TEXT="lineares moving-average: den N-ten alten Wert subtrahieren"/>
<node CREATED="1689355095725" ID="ID_917795868" MODIFIED="1689356305847" TEXT="exponentielles moving-average: ein N-tel vom gemittelten Wert subtrahieren">
<linktarget COLOR="#579ad8" DESTINATION="ID_917795868" ENDARROW="Default" ENDINCLINATION="-206;8;" ID="Arrow_ID_1287643203" SOURCE="ID_367226311" STARTARROW="None" STARTINCLINATION="292;0;"/>
</node>
</node>
<node CREATED="1689355137015" ID="ID_864577627" MODIFIED="1689355148010" TEXT="Konsequenz...">
<node CREATED="1689355149334" ID="ID_649514706" MODIFIED="1689355231758" TEXT="der &#xbb;alte Wert&#xab; wird jeweils mit (N-1)/N multipliziert">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<ul>
<li>
gewichtetes Mittel
</li>
<li>
eigentlich sind es N alte Punkte, aber wir ziehen ja jedesmal einen Punkt ab &#10233; N-1 gemittelte Beitr&#228;ge
</li>
</ul>
</body>
</html></richcontent>
</node>
<node CREATED="1689355234696" ID="ID_1358310078" MODIFIED="1689355261867" TEXT="Betrachtung des Einzelwerts in der k-ten Iteration">
<node CREATED="1689355265862" ID="ID_19716247" MODIFIED="1689355311124" TEXT="x&#x2096; = x/N &#xb7; ((N-1)/N)^k"/>
<node CREATED="1689355314360" ID="ID_778368723" MODIFIED="1689355327537" TEXT="das hei&#xdf;t: exponentielle D&#xe4;mpung des Beitrags"/>
<node CREATED="1689355328535" ID="ID_1580028196" MODIFIED="1689355346775" TEXT="kleines N &#x27f9; neue Werte wirken stark"/>
</node>
<node CREATED="1689355358657" ID="ID_1737373389" MODIFIED="1689355415526" TEXT="Wichtig: Anfangswert sollte nicht 0 sein, sondern ein Sch&#xe4;tzwert">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
denn sonst k&#246;nnte es relativ lang dauern, bis sich das System einreguliert, und w&#228;hrend dieser Zeit k&#246;nnte es bereits in S&#228;ttigung gehen, und Regelschwingungen produzieren
</p>
</body>
</html></richcontent>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1689204430524" ID="ID_1525654861" MODIFIED="1689204437235" TEXT="Signale">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1689204438866" ID="ID_586190414" MODIFIED="1689204458016" TEXT="Epochen-Overflow">
<icon BUILTIN="flag-yellow"/>
<node CREATED="1689355428408" ID="ID_1439608393" MODIFIED="1689355463591" TEXT="bei jedem Event um einen proprotionalen Faktor verk&#xfc;rzen"/>
<node CREATED="1689355466427" ID="ID_966913305" MODIFIED="1689355486380" TEXT="verk&#xfc;rzen &#x27f9; Slot-Dichte um den Kehrwert erh&#xf6;ht"/>
<node CREATED="1689355589282" ID="ID_840744" MODIFIED="1689355613393" TEXT="Faktor? &#x3a6; nat&#xfc;rlich">
<icon BUILTIN="ksmiletris"/>
<node CREATED="1689356180333" ID="ID_71577757" MODIFIED="1689356511458" TEXT="1/&#x3a6; = &#x3a6;-1"/>
<node CREATED="1689356185098" ID="ID_1224416689" MODIFIED="1689356203787" TEXT="0,6180339887498948482"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1689204448250" ID="ID_688039501" MODIFIED="1689204458017" TEXT="F&#xfc;llstand beim Aufr&#xe4;umen">
<icon BUILTIN="flag-yellow"/>
<node CREATED="1689356233875" ID="ID_1117159632" MODIFIED="1689356241731" TEXT="das wirkt mit einer gewissen Verz&#xf6;gerung"/>
<node CREATED="1689356263015" ID="ID_367226311" MODIFIED="1689356314144" TEXT="hier wird exponential-MA angewendet">
<arrowlink COLOR="#579ad8" DESTINATION="ID_917795868" ENDARROW="Default" ENDINCLINATION="-206;8;" ID="Arrow_ID_1287643203" STARTARROW="None" STARTINCLINATION="292;0;"/>
</node>
<node CREATED="1689356572198" ID="ID_1540220100" MODIFIED="1689356660985" TEXT="&#xbb;Sollwert&#xab; &#x27f9; als Signal einspeisen">
<node CREATED="1689356669065" ID="ID_492493887" MODIFIED="1689356705152" TEXT="&#xbb;Sollwert&#xab; &#x2261; die Epochen-L&#xe4;nge, die gen&#xfc;gt h&#xe4;tte"/>
<node CREATED="1689356713555" ID="ID_1631540322" MODIFIED="1689356790172" TEXT="angenommen die Deadlines sind gleichverteilt &#x27f9; direkt den F&#xfc;llstand nehmen"/>
<node CREATED="1689356905327" ID="ID_1889066607" MODIFIED="1689357557781" TEXT="&#x27f9; aktuelle L&#xe4;nge / F&#xfc;llstand-Faktor"/>
</node>
<node CREATED="1689356940427" ID="ID_1868278031" MODIFIED="1689356956670" TEXT="damit regelt dieser Mechanismus stets herunter"/>
<node CREATED="1689357043350" ID="ID_1771390424" MODIFIED="1689357058027" TEXT="aber langsamer, da er nur im Tick l&#xe4;uft"/>
</node>
</node>
</node>
@ -80187,7 +80280,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1689204644343" ID="ID_1848987574" MODIFIED="1689204801974" TEXT="oldest"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1689204650526" ID="ID_624484958" MODIFIED="1689204801974" TEXT="newest"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1689204731722" ID="ID_1494295782" MODIFIED="1689204801974" TEXT="Allokations-Zuordnung"/>
<node COLOR="#435e98" CREATED="1689204731722" ID="ID_1494295782" MODIFIED="1689439593921" TEXT="Allokations-Zuordnung"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1689204805041" ID="ID_1427200190" MODIFIED="1689204809545" TEXT="Ereignisse">
<icon BUILTIN="flag-yellow"/>
@ -80204,6 +80297,12 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1689204923473" ID="ID_756310466" MODIFIED="1689204950523" TEXT="f&#xfc;r BlockFlow">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1689439619197" ID="ID_1454011778" MODIFIED="1689439653881" TEXT="erste und letzte Epoche als Deadline">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1689439631356" ID="ID_801146515" MODIFIED="1689439653881" TEXT="Epoche finden, in die eine gegebene Activity alloziert wurde">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1689204967974" ID="ID_284088835" MODIFIED="1689204977426" TEXT="ActivityDetector">
<icon BUILTIN="flag-yellow"/>