Repository navigation
Create MarkerEmiter component #28
Copy link
Copy link
Closed
Description
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 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.
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
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]
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(...)
5. Szkic kodu
Producent — LifeTimeComponent
class LifeTimeComponent : public Component {
public:
/*
some code ...
*/
// Producent wystawia swój delegat, by MarkerEmitter mógł się podpiąć w fazie wiązania.
Delegate& timeOutEvent() { return onTimeOut; }
void update(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;
};Prymitywy zdarzeń
struct EventListener {
virtual ~EventListener() = default;
virtual void onEventTriggered(const Context& ctx) = 0;
};
class Delegate {
public:
void subscribe(EventListener* l);
void unsubscribe(EventListener* l);
void invoke(const Context& ctx) {
for (auto* l : listeners) l->onEventTriggered(ctx);
}
private:
std::vector<EventListener*> listeners; // referencje nievłaścicielskie
};Odbiorca — MarkerEventLink (most do ctx.markers)
class MarkerEventLink : public EventListener {
public:
explicit MarkerEventLink(std::string name) : eventName(std::move(name)) {}
void onEventTriggered(const Context& ctx) override {
if (ctx.markers) {
ctx.markers->push_back(eventName); // tylko NAZWA, bez timestampu
}
}
private:
std::string eventName;
};Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
[-]Create MarkerEmiter[/-][+]Create MarkerEmiter component[/+]