Cockpit-Display-Replay
Das Replay-Plugin installieren, in eine jsPsych-Timeline hängen, Parameter und Layout setzen und die Trial-Daten auslesen.
Diese Seite ist die Anleitung zum jsPsych-Plugin
@ecosimlab/jspsych-plugin-videosimlab-cockpit-replay. Sie beschreibt
Installation, Trial-Parameter, Datenstrom und Trial-Daten. Wozu das Plugin gut
ist, steht auf der Tool-Seite.
Alle Angaben sind aus dem Repo videosimlab-cockpit-replay belegt, Branch
main (Commit 65f8d37), Paketversion 2.1.0, neuester Tag 2.1.0. Wo README und
Code sich widersprechen, gilt der Code. Die Abweichung ist jeweils benannt.
Voraussetzungen
- jsPsych 8.0.0 oder neuer. Das Plugin führt
jspsychals normale Dependency, nicht als Peer-Dependency. Darauf achten, dass eine Studie nicht zwei jsPsych-Instanzen bündelt. - Browser und jsPsych-Experiment. Das Plugin ist ein Trial-Typ. Es bringt keinen eigenen Player mit.
- Node und npm für die Installation. Das Repo hat kein
engines-Feld und nennt deshalb keine Mindestversion von Node. - Eigene Medien: eine browser-kompatible Videodatei und der dazu passende Datenstrom. Beides zeichnet das Plugin nicht auf.
Installation
Das Paket liegt nicht in der npm-Registry. Es wird aus dem öffentlichen GitLab-Repo installiert, festgelegt auf einen Tag:
npm install gitlab:ecosimlab/videosimlab-cockpit-replay#2.1.0
Die Display-Bibliothek @ecosimlab/ecodisplays kommt dabei mit. Das Plugin legt
sie in seiner package.json genauso auf einen Tag fest. Beide Repos liefern
ihren gebauten dist/-Ordner mit. Es braucht kein Token und keine .npmrc, und
die Installation baut nichts.
import jsPsychVideosimlabCockpitReplay from "@ecosimlab/jspsych-plugin-videosimlab-cockpit-replay";
Die
package.jsonvonmainlegt die Bibliothek aufv1.0.1fest. Der README nennt an derselben Stelle nochv0.5.0. Es gilt diepackage.json. Der neueste Tag der Bibliothek selbst istv1.0.2.
Das Paket hat einen zweiten Einstiegspunkt:
@ecosimlab/jspsych-plugin-videosimlab-cockpit-replay/layout. Er exportiert die
Box-Regeln resolveDisplayBoxes, resolveVideoSize, defaultBoxSize und
DEFAULT_VIDEO_SIZE. Eine Seite, die die Boxen schon vor dem Replay zeigen
muss, liest sie von dort statt aus einer Kopie.
Im Browser ohne Bundler. Der README lässt diesen Abschnitt als TODO
stehen. Der Build liefert die Datei trotzdem: dist/index.browser.min.js liegt
im Repo und setzt das globale Symbol jsPsychVideosimlabCockpitReplay. Die
Beispielseite examples/index.html lädt sie so.
Die CSS der Bibliothek bindet das Plugin selbst ein. Die Displays liegen in
einem Shadow Root, die @font-face-Blöcke landen einmal pro Seite im Kopf des
Dokuments. Kein eigenes Stylesheet der Bibliothek einbinden.
Quickstart
Der kleinste lauffähige Trial braucht drei Parameter und einen Wert für stop:
const jsPsych = initJsPsych();
const trial = {
type: jsPsychVideosimlabCockpitReplay,
display: "AP6DefaultDisplay",
cockpitVideo: "media/sector-1.mp4",
displayStream: sectorOneStream,
start: 0,
stop: 62.5,
sectorId: 1,
replayType: "efficient",
};
jsPsych.run([trial]);
cockpitVideo ist ein ParameterType.VIDEO. Das Plugin holt die Datei über
jsPsych.pluginAPI.getVideoBuffer() und fällt sonst auf den Pfad selbst zurück.
Das Video mit dem preload-Plugin vorladen, wenn der Wert aus einer
Timeline-Variablen oder aus einer Funktion kommt.
examples/index.html im Repo deklariert einen Trial ohne Parameter. Er zeichnet
nichts und ist ein Gerüst, kein Beispiel.
Displays
display nimmt den Namen, unter dem @ecosimlab/ecodisplays ein Display
exportiert. Ein unbekannter Name wirft zum Start des Trials einen Fehler. Die
Meldung listet alle gültigen Namen auf.
Das Plugin erreicht 20 Displays (src/app/displays.ts):
| Familie | Displays |
|---|---|
| AP4 | AP4ConsumptionDisplay, AP4VelocityDisplay |
| AP5 | AP5BaselineDisplay, AP5BoxDisplay, AP5ComperatorDisplay, AP5DecisionDisplay, AP5DecisionSpeedDisplay, AP5InputDisplay, AP5SpeedometerDisplay |
| AP6 | AP6DefaultDisplay, AP6StatusQuoDisplay, AP6VariationDisplay |
| Energy-Score | EnergyScoreBarDisplay, EnergyScoreGlyphDisplay, MinimalGaugeDisplay |
| LEM | LEMDecisionDisplay, LEMDisplay, LEMTimeDisplay, LEMVelocityTimeDisplay |
| Input-Transparenz | InputTransparenzDisplay |
Version 2.0.0 hat die sechs Kennungen der älteren Studien entfernt. Der README
ordnet sie zu: default wird AP6DefaultDisplay, status-quo-power wird
AP6StatusQuoDisplay, status-quo-variation wird AP6VariationDisplay,
default-minimal wird MinimalGaugeDisplay, glyphe wird
EnergyScoreGlyphDisplay, und gamification wird EnergyScoreBarDisplay.
API-Referenz
Der Default-Wert undefined bedeutet: Der Parameter ist Pflicht.
| Parameter | Typ | Default | Bedeutung |
|---|---|---|---|
display |
STRING |
undefined |
Export-Name des Displays. |
displays |
COMPLEX (Array) |
[] |
Eine Box je Display, siehe „Layout“. |
displayStream |
COMPLEX (Array) |
undefined |
Das aufgezeichnete Displayverhalten, siehe „Datenstrom“. |
cockpitVideo |
VIDEO |
undefined |
Video der Cockpit-Ansicht. |
video |
COMPLEX |
{ width: 1400, height: 787.5 } |
Größe, in der das Video gezeichnet wird. |
start |
FLOAT |
0 |
Sekunde im Video, ab der das Replay startet. |
stop |
FLOAT |
0 |
Sekunde, an der das Replay endet und der Trial schließt. |
sectorType |
STRING |
"other" |
"incoming", "outgoing" oder "other". Steuert das Ein- und Ausblenden. |
sectorId |
INT |
null |
Sektor, nur zur Zuordnung des Trials. |
replayType |
STRING |
null |
Effizienz-Typ, nur zur Zuordnung des Trials. |
Ein Trial braucht entweder display oder einen Eintrag in displays. Fehlt
beides, wirft das Plugin einen Fehler. Ein unbekannter sectorType wirft
ebenfalls (src/app/sector.ts).
Layout
displays enthält einen Eintrag je Box. Alle Felder außer dem Namen haben einen
Default:
| Feld | Default | Bedeutung |
|---|---|---|
display |
der Parameter display |
Export-Name des Displays |
left, top |
280, 560 | Ecke der Box, von der linken oberen Ecke des Videos aus |
width, height |
die Default-Box, siehe unten | Größe der Box |
fit |
"contain" |
"contain" passt das ganze Design in die Box ein, "width" passt die Breite ein |
Die Koordinaten zählen in den Pixeln aus video. Zwei Einträge zeichnen zwei
Displays nebeneinander. Das leistete früher der entfernte Parameter
hasDefaultGauge:
displays: [
{ display: "AP6StatusQuoDisplay", left: 720, top: 560, width: 440, height: 390 },
{ display: "MinimalGaugeDisplay", left: 280, top: 560, width: 440, height: 390 },
],
Ohne width und height folgt eine Box der Design-Größe ihres Displays
(src/app/layout.ts). Die Energy-Score-Familie bekommt ihre Design-Größe: 632 x
390 für EnergyScoreBarDisplay und EnergyScoreGlyphDisplay, 402 x 254 für
MinimalGaugeDisplay. Jedes andere Display bekommt 640 Pixel Breite im
Verhältnis seiner Design-Größe.
Alle Boxen hängen in einer Schicht über dem Video. Das Ein- und Ausblenden erfasst sie deshalb gemeinsam.
Datenstrom
displayStream ist eine Liste von DisplayInput-Punkten. DisplayInput ist
der Eingabevertrag der Bibliothek und einzeln importierbar aus
@ecosimlab/ecodisplays/input. Das Plugin führt keine eigene Feldliste.
Ein Punkt darf nur die Felder nennen, die die Aufzeichnung hat. Den Rest füllt
das Plugin aus defaultDisplayInput. Ein fehlendes Feld zeigt also seinen
Default und nicht den Wert des vorigen Punkts.
timeS zählt Sekunden ab Videobeginn und muss weiter steigen. Ein Punkt gilt,
bis der nächste beginnt. Der letzte Punkt gilt bis zum Ende des Videos. Vor dem
ersten Punkt zeigen die Displays die Default-Eingabe.
Der Name eines Feldes trägt seine Einheit: Percent ist 0 bis 100, Fraction
ist 0 bis 1, S Sekunden, M Meter, Mps Meter pro Sekunde, Kmh Kilometer
pro Stunde, dazu W, Kw und Kwh für Watt, Kilowatt und Kilowattstunden.
const displayStream = rows.map((row) => ({
hasSimulationData: true,
hasEnergyData: true,
timeS: row.t,
wheelspeedMps: row.v,
gear: "D",
batteryEnergyKwh: row.energy,
batterySocPercent: row.soc,
pBatteryKw: row.pBattery,
optimalSpeedKmh: row.optimalSpeed,
optimalPowerW: row.optimalPower,
referenceSpeedLine: [],
}));
hasSimulationData für jeden Punkt einer Aufzeichnung auf true setzen. Welches
Display welche Felder liest, listet der README der Bibliothek.
Das Replay fragt den Datenstrom alle 40 Millisekunden ab, also 25-mal pro
Sekunde (src/app/App.ts).
Trial-Daten
Zusätzlich zu den Standarddaten von jsPsych schreibt das Plugin je Trial diese Felder:
| Feld | Typ | Inhalt |
|---|---|---|
sectorId |
INT | der Parameter sectorId |
replayType |
STRING | der Parameter replayType |
display |
STRING | der Parameter display |
video |
STRING | der Wert von cockpitVideo als String |
start |
FLOAT | der Parameter start |
stop |
FLOAT | der Parameter stop |
src/index.tsschreibt zusätzlichsectorType, obwohl derdata-Block des Plugins dieses Feld nicht deklariert. Nicht darauf verlassen.
Das Plugin löst keine eigenen Events aus und bietet keine Callbacks. Dafür
on_finish am Trial nutzen.
Entwicklung & Tests
npm install
npm run build # rollup --config
npm test # jest
npm run tsc # nur Typecheck
- Der Build schreibt
dist/index.js,dist/index.cjs,dist/index.browser.js,dist/index.browser.min.jsund die Typen. Der zweite Einstiegspunkt ergänztdist/layout.js,dist/layout.cjsunddist/layout.d.ts. Der Ordnerdist/liegt im Repo. src/index.tsenthältcitations: "__CITATIONS__". Der Build füllt den Platzhalter ausCITATION.cff.- Die Tests decken Trial, Boxen und Datenstrom ab:
src/index.spec.ts,src/layout.spec.ts,src/app/layout.spec.tsundsrc/app/stream.spec.ts.jest.setup.cjsersetztResizeObserver, den jsdom nicht hat.
Lizenz & Zitation
MIT (LICENSE.txt, Copyright 2026 Leonardt Wagner; gleichlautend in
package.json).
CITATION.cff nennt:
- Autor: Leonardt Wagner (ORCID 0009-0009-1759-9674)
- Titel:
jsPsychVideosimlabCockpitReplay, Version 2.1.0, veröffentlicht 2026-09-10 - URL:
https://gitlab.com/ecosimlab/videosimlab-cockpit-replay - Die DOI-Zeile ist auskommentiert. Das Plugin hat also keinen DOI.
Für die Toolchain als Ganzes gilt zusätzlich das VideoSimLab-Paper. Die Tool-Seite nennt es.
Troubleshooting
Der Trial endet sofort. stop hat den Default 0, und das Replay endet,
sobald die Zeit des Videos stop erreicht. stop immer setzen.
Eine Fehlermeldung listet alle Displays auf. Der Name in display oder in
einem Eintrag von displays ist kein Export der Bibliothek. Einen Namen aus der
Tabelle oben nehmen.
Das Replay wirft einen Fehler, bevor es zeichnet. Ein Trial braucht
display oder einen Eintrag in displays. sectorType muss "incoming",
"outgoing" oder "other" sein. Ein falscher Wert ließ früher den ganzen Trial
unsichtbar laufen. Heute wirft er einen Fehler.
Alte Aufzeichnungen zeigen Unsinn. Version 2.0.0 hat den Typ
DisplayStreamDataPoint durch DisplayInput ersetzt. Die Aufzeichnungen
umwandeln.
Kein Video, obwohl der Pfad stimmt. Das Plugin liest zuerst den Puffer von
jsPsych. Die Datei mit dem preload-Plugin vorladen.
Der Browser blockiert das Autoplay. Das Plugin startet das Video unstummgeschaltet. Ohne vorherige Nutzerinteraktion kann der Browser das ablehnen. Vor das Replay einen Trial mit einem Klick setzen.
Offene Punkte
- Der README nennt zwei Versionen der Bibliothek (
v0.5.0im Text,v1.0.1in derpackage.json) und eine dritte Schreibweise der Import-Variablen (jsPsychVideoSimlabcockpitReplay). Beides ist im Repo zu klären.mainist dem Tag 2.1.0 um einen Commit voraus. Er hebt nur den Pin der Bibliothek aufv1.0.1. Der Installationsbefehl oben nimmt den Tag.- Die Installation im Browser ist nicht dokumentiert. README und Repo-Doku halten dort ein
TODO. Der Weg überdist/index.browser.min.jsist aus der Beispielseite abgeleitet.- Das Repo hat keine Beispielseiten mehr. Sie haben nie ein Display gezeigt. Das Zusammenspiel mit einem SAGAT-Freeze ist nirgends ausgeführt.