You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Komponent nie tworzy timestampów dla markerów oraz nie dotyka markerQueue ani DataWriter. timestamp
dokłada wyłącznie Renderer po SDL_RenderPresent — to gwarantuje wyrównanie
czasowe z odświeżeniem ekranu. Poniżej znajduje się diagram przedstawiający event system.
Cel: po 2 sekundach komponent „wygasa", odpala delegat, a podpięty MarkerEventLink wrzuca nazwę "stimulus_end" do ctx.markers.
3. Faza wiązania (raz, po sparsowaniu sceny)
flowchart LR
P[Parser buduje Scene] --> SR[Scene wywołuje onSceneReady na wszystkich komponentach]
SR --> ME["MarkerEmitterComponent.onSceneReady(scene)"]
ME --> RES["resolveEvent('stimulus', onTimeOut) → Delegate&"]
RES --> SUB["onTimeOut.subscribe(markerEventLink)"]
SUB --> RDY[Link gotowy: delegat zna swojego listenera]
Loading
Subskrypcja nie dzieje się w konstruktorze — cel (LifeTimeComponent) może
jeszcze nie istnieć. Dlatego potrzebny jest hook onSceneReady(Scene&)
uruchamiany po zbudowaniu całej sceny.
4. Przepływ w trakcie działania (klatka z timeoutem)
sequenceDiagram
participant R as Renderer (render loop)
participant LT as LifeTimeComponent
participant D as Delegate onTimeOut
participant L as MarkerEventLink
participant C as ctx.markers
participant MQ as markerQueue
participant DW as DataWriter
Note over R: nowa klatka
R->>R: deltaTime
R->>LT: update(ctx)
LT->>LT: timeElapsed += ctx.deltaTime
alt timeElapsed >= duration (pierwszy raz)
LT->>D: invoke(ctx)
D->>L: onEventTriggered(ctx)
L->>C: markers->push_back("stimulus_end")
end
Note over R: po update + render całej sceny
R->>R: SDL_RenderPresent()
R->>R: exactTime = lsl::local_clock()
R->>MQ: enqueue(Marker{"stimulus_end", exactTime})
MQ-->>DW: try_dequeue
DW->>DW: formatStrategy->writeMarker(...)
Loading
5. Szkic kodu
Producent — LifeTimeComponent
classLifeTimeComponent : publicComponent {
public:/* some code ...*/// Producent wystawia swój delegat, by MarkerEmitter mógł się podpiąć w fazie wiązania.
Delegate& timeOutEvent() { return onTimeOut; }
voidupdate(const Context& ctx) override {
if (hasTimedOut) {
return;
}
timeElapsed += ctx.deltaTime;
if (timeElapsed >= duration) {
hasTimedOut = true;
// WAZNE// Przekazujemy ctx, żeby listener trafił nazwą markera do bieżącej klatki// (ten sam timestamp vsync, niezależnie od kolejności update komponentów).
onTimeOut.invoke(ctx);
}
}
/* some code ...*/private:
Delegate onTimeOut;
double duration = 0.0;
double timeElapsed = 0.0;
bool hasTimedOut = false;
};
for more context check out class diagram https://mermaidviewer.com/diagrams/50hRyLmYGTKr7ixp1xZTa
Event System
1. Zasada nadrzędna
Komponent nie tworzy timestampów dla markerów oraz nie dotyka
markerQueueaniDataWriter. timestampdokłada wyłącznie Renderer po
SDL_RenderPresent— to gwarantuje wyrównanieczasowe z odświeżeniem ekranu. Poniżej znajduje się diagram przedstawiający event system.
classDiagram class Context { +double deltaTime +std::vector~std::string~* markers } class EventListener { <<interface>> +onEventTriggered(const Context& ctx) void } class Delegate { -std::vector~EventListener*~ listeners +subscribe(EventListener*) void +unsubscribe(EventListener*) void +invoke(const Context& ctx) void } class MarkerEventLink { -std::string eventName +onEventTriggered(const Context& ctx) void } class MarkerEmitterComponent { -std::vector~unique_ptr~MarkerEventLink~~ bindings -std::vector~Delegate*~ subscribedTo +onSceneReady(Scene&) void } class Component { <<abstract>> +update(const Context&)* void +render(SDL_Renderer*)* void +onSceneReady(Scene&) void } MarkerEventLink ..|> EventListener Delegate o-- EventListener : "non-owning refs" MarkerEmitterComponent *-- MarkerEventLink : "owns" MarkerEmitterComponent ..> Delegate : "subscribes links to" Component <|-- MarkerEmitterComponent MarkerEventLink ..> Context : "writes name into ctx.markers"2. Scenariusz przykładu
Obiekt sceny „stimulus" ma dwa komponenty:
LifeTimeComponent(duration = 2.0s)— producent zdarzenia, wystawiaDelegate onTimeOut.MarkerEmitterComponentz bindingiem{ target: "stimulus", event: onTimeOut, markerName: "stimulus_end" }.Cel: po 2 sekundach komponent „wygasa", odpala delegat, a podpięty
MarkerEventLinkwrzuca nazwę"stimulus_end"doctx.markers.3. Faza wiązania (raz, po sparsowaniu sceny)
flowchart LR P[Parser buduje Scene] --> SR[Scene wywołuje onSceneReady na wszystkich komponentach] SR --> ME["MarkerEmitterComponent.onSceneReady(scene)"] ME --> RES["resolveEvent('stimulus', onTimeOut) → Delegate&"] RES --> SUB["onTimeOut.subscribe(markerEventLink)"] SUB --> RDY[Link gotowy: delegat zna swojego listenera]Subskrypcja nie dzieje się w konstruktorze — cel (
LifeTimeComponent) możejeszcze nie istnieć. Dlatego potrzebny jest hook
onSceneReady(Scene&)uruchamiany po zbudowaniu całej sceny.
4. Przepływ w trakcie działania (klatka z timeoutem)
sequenceDiagram participant R as Renderer (render loop) participant LT as LifeTimeComponent participant D as Delegate onTimeOut participant L as MarkerEventLink participant C as ctx.markers participant MQ as markerQueue participant DW as DataWriter Note over R: nowa klatka R->>R: deltaTime R->>LT: update(ctx) LT->>LT: timeElapsed += ctx.deltaTime alt timeElapsed >= duration (pierwszy raz) LT->>D: invoke(ctx) D->>L: onEventTriggered(ctx) L->>C: markers->push_back("stimulus_end") end Note over R: po update + render całej sceny R->>R: SDL_RenderPresent() R->>R: exactTime = lsl::local_clock() R->>MQ: enqueue(Marker{"stimulus_end", exactTime}) MQ-->>DW: try_dequeue DW->>DW: formatStrategy->writeMarker(...)5. Szkic kodu
Producent —
LifeTimeComponentPrymitywy zdarzeń
Odbiorca —
MarkerEventLink(most doctx.markers)