Zum Inhalt springen
EcoSimLab
DeutschEnglish
Docs-Navigation
Tool-Docs

Cockpit-Display-Replay

Das Replay-Plugin installieren, in eine jsPsych-Timeline hängen, Parameter und Layout setzen und die Trial-Daten auslesen.

Gehört zu: Cockpit-Display-Replay

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 jspsych als 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.json von main legt die Bibliothek auf v1.0.1 fest. Der README nennt an derselben Stelle noch v0.5.0. Es gilt die package.json. Der neueste Tag der Bibliothek selbst ist v1.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.ts schreibt zusätzlich sectorType, obwohl der data-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.js und die Typen. Der zweite Einstiegspunkt ergänzt dist/layout.js, dist/layout.cjs und dist/layout.d.ts. Der Ordner dist/ liegt im Repo.
  • src/index.ts enthält citations: "__CITATIONS__". Der Build füllt den Platzhalter aus CITATION.cff.
  • Die Tests decken Trial, Boxen und Datenstrom ab: src/index.spec.ts, src/layout.spec.ts, src/app/layout.spec.ts und src/app/stream.spec.ts. jest.setup.cjs ersetzt ResizeObserver, 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.0 im Text, v1.0.1 in der package.json) und eine dritte Schreibweise der Import-Variablen (jsPsychVideoSimlabcockpitReplay). Beides ist im Repo zu klären.
  • main ist dem Tag 2.1.0 um einen Commit voraus. Er hebt nur den Pin der Bibliothek auf v1.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 über dist/index.browser.min.js ist 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.