Přeskočit na obsah

SEB scény a Event Player ve FW 0.12.11

Jak ve firmware 0.12.11 použít Berry SEB.land a SEB.emit pro kauzální scény a časované segmenty eventových hodnot.

SEB (Spectoda Event Batch) je malý binární soubor s diskrétními eventovými hodnotami/záznamy. Každý záznam obsahuje nativní hodnotu, label, Spectoda ID, prioritu a absolutní časový offset uvnitř segmentu. Landing nebo emit záznamu vyvolá běžné zpracování EventStateUpdate přes EventStore.

SEB není authoring ani projektový formát Playeru. Neobsahuje název scény, Tracku, loop ani seek pravidla. Tyto informace drží projektový Berry script. Statický soubor player.show v3 je malý manifest jednoho globálního mixed-ID streamu v pevných NetworkStorage slotech player.NNN.

Jeden SEB segment má nejvýše 340 záznamů, velikost nejvýše 4 092 bajtů a délku nejvýše 65 535 ms. Tím využije jeden 4 096B datový blok LittleFS; metadata souboru jsou uložena samostatně. Runtime nadále přijme v jednom atomickém příspěvku nejvýše 340 splatných Eventů, takže jedna maximální Cue Group ve stejném čase napříč všemi ID jde commitnout atomicky. Celkový 341. pending Event selže před změnou fronty, EventStore nebo cursoru. Cue Group se nikdy nerozdělí. Delší show se skládá z více seřazených segmentů. Normativní byte layout a všechna validační pravidla udržuje firmware v souboru components/spectoda-library/docs/SEB_V1.md; zde je přesná review verze pro FW 0.12.11.

Pro distribuovaný Player používejte na všech připravených kontrolerech SEB.land. Události vstoupí do lokálního EventStore se stejným síťovým časem, ale nevznikne okamžitý broadcast execute bytecode. Běžná synchronizace EventStore později dorovná kontroler, který událost ve správný okamžik nelandnul.

SEB.emit použijte jen tehdy, když existuje právě jeden autoritativní zdroj. Pouštět SEB.emit na všech kontrolerech by zbytečně násobilo síťové události.

Skalární API zůstává ve FW 0.12.11 zachované:

spectoda.landEvent(label, value, ids, vtype)
spectoda.landEvent(label, value, ids, vtype, event_millis)
spectoda.emitEvent(label, value, ids, vtype)
spectoda.emitEvent(label, value, ids, vtype, event_millis)

ids může být jedno ID, seznam ID nebo nil pro broadcast. Volitelný event_millis zachová přesný kauzální lokální čas původní události. Pro dávky používejte komponentu SEB; nejde o plural variantu skalární metody.

var result = SEB.land("segment-000.seb", {
"source": "networkStorage",
"at": segment_at,
"cursor": cursor,
"until": position
})
if result["ok"]
cursor = result["cursor"]
else
print("SEB error", result["error"])
end
  • at je lokální wrapping controller.millis() token odpovídající offsetu nula. Pro časovaný segment je povinný.
  • cursor je index prvního dosud nespotřebovaného zdrojového záznamu. Výchozí hodnota je 0.
  • until je včetně zahrnutý offset, po který se mají zpracovat splatné záznamy. Výchozí hodnota je 0.
  • source: "networkStorage" určuje, že první argument je název souboru. Bez něj zůstává první argument Berry hodnota bytes.

Jedno volání vezme maximální splatný prefix. Nejdřív jej celý ověří a rezervuje a teprve potom atomicky zapíše. Při chybě zůstane EventStore, fronta i cursor beze změny, takže Berry může volání bezpečně zopakovat.

Jeden kontrakt args.at pokrývá tři explicitní časové domény:

  • timeline.at(segmentStart) převede pozici Player timeline na lokální wrapping millis token;
  • event_millis z triggeru EVS.cb zachová Network-clock kauzalitu události při vyvolání scény;
  • controller.millis() znamená právě teď na tomto Controlleru.

Network clock zůstává autoritou pro řazení EventStore. SEB přijímá pouze lokální token a nemá žádný timeline-specific overload.

SEB si neponechá Berry pointer, filename, file handle, cursor ani Player stav. Projekt musí zachovat bitově stejné at pro přesný pár zdroje a cursoru. Při změně segmentu, seeku, loopu nebo změně časové projekce musí cursor resetovat nebo nahradit.

Referenční Player řeší pause/resume jako explicitní vnější policy. S novou projekcí odvodí nový pár filename/at a přenese jen známý index prvního nespotřebovaného zdrojového záznamu. Toto číslo nepoužívejte s jiným zdrojem ani s libovolným at.

Scéna je obyčejný mixed-ID SEB s duration_ms = 0 a všemi offsety nula. Trigger Event může nést label scény. Callback EVS.cb předá také přesný lokální čas původní události; ten použijte jako at.

var scene = EVS("scene", ID255)
scene.cb = def(value, event_id, event_millis)
if event_millis == nil || !value.is(31)
return
end
var name = value.get(31) + ".seb"
var result = SEB.land(name, {
"source": "networkStorage",
"at": event_millis
})
if !result["ok"]
print("Invalid scene", result["error"])
end
end

Trigger $scene[ID255] samotný do souboru scény nekompilujte, jinak by vznikla rekurze.

Scene recall vůbec nevolá timeline.at(). Zůstává kauzální i při pozastavené timeline, protože callback dostal event_millis z trigger události.

Copy-ready Player načte player.show a polluje timeline.getState(). Exact-EOF manifest má 12bajtovou hlavičku a jednu 10bajtovou položku pro každý pevný segmentový slot:

  • přesnou požadovanou NetworkStorage verzi jako u48 little-endian;
  • absolutní začátek segmentu na show timeline jako u32 little-endian.

Položka s indexem 0 znamená player.000, index 1 znamená player.001 a tak dále až po player.999. Jména se neukládají. Berry získá lokální verzi z spectoda.listNetworkStorageData() jako přesně 12znakový lowercase hex řetězec a porovná ji bez převodu do 32bitového čísla.

Studio publikuje novější manifest jako první a změněné sloty po něm. Nejde o hot-update protokol: Controller zůstane neaktivní, dokud nemá všechny požadované verze a neověří SEB header i EOF. Chybějící soubory dodá běžná NetworkStorage synchronizace. Upload nijak nemění stav timeline.

Běžný dopředný běh vybere aktuální segment podle absolutního začátku, spočítá at přes timeline.at(segmentStart) a předá aktuální relativní pozici jako until. Každý Controller landuje všechna ID ze stejného streamu. Běžná pauza bez skoku timeline nedělá žádné SEB volání.

timeline.at(position) uchovává, že aktuální referenční pozice timeline P platila v Network clock čase C, vypočte C + (position - P) a tento clock převede na signed wrapping lokální token Controlleru. Stejný výpočet platí při běhu i pauze, takže Controllery s rozdílnou latencí zpracování příkazu získají stejný Network timestamp. Funkce vrací nil, pokud cílový clock není platný nebo leží mimo jednoznačné wrapping okno ±2^31 ms.

Timeline pozice je samostatný 24hodinový kruh 0..86 399 999; hodnota 86 400 000 se normalizuje na 0. Přechod přes půlnoc proto zůstává spojitý pro Player, Layer i animace. Projekce vybírá nejbližší výskyt v rozsahu ±12 hodin; přesný rozdíl 12 hodin je nejednoznačný a vrátí nil.

Pause/resume zachovává epoch, ale resume vytváří novou kauzální projekci; Player odvodí nové at pro aktuální segment a ponechá již spotřebovaný zdrojový prefix. Seek a loop epoch změní a znovu nastaví cursor.

Při seeku, loopu, rewind nebo 24hodinovém wrapu Player vybere Cue Group přesně v cíli, nebo bezprostředně před ním. Tuto jednu řídkou Group landne jednou v kauzálním čase seeku i při pauze a posune cursor až za ni. Resume ji proto nezopakuje a žádná budoucí Cue se nevybere.

Jde o řídké dorovnání, ne rekonstrukci kompletního stavu. Player nemaže EventStore ani nepřehrává starší historii. Eventy, které ve vybraném Cue nejsou, si ponechají existující EventStates. Tam, kde má libovolný seek obnovit celý vizuální stav, použijte kompletní Cue.

SEB nemění EventState mimo EventStore. Poslední EventStateUpdate proto zůstává rozhodující i po pozdější synchronizaci historie a externě emitovaná událost může stav Playeru změnit běžnou cestou.

Příklady používají jen syntetická ID a přesné NetworkStorage artefakty pro FW 0.12.11. Pro tvorbu show ve Studiu pokračujte na návod Event Player ve Spectoda Studiu.