Skip to content
EcoSimLab
DeutschEnglish
Docs navigation
Tool docs

EcoInterfaces

How to install the display library, build a DisplayInput, render a display, load the stylesheet and pick the right export.

Part of: EcoInterfaces

This page is the guide to the display library @ecosimlab/ecodisplays. It covers the installation, the input contract, the exports and the stylesheet. What the library is for stands on the tool page.

Every statement here comes from the repository ecodisplays, branch main (commit 94b20a3), package version 1.0.2, newest tag v1.0.2. Where the README and the code disagree, the code counts, and this page names the difference.

The library draws displays. It does not talk to a simulator, a message broker, or a network. You bring the data.

Prerequisites

  • A bundler for SolidJS. The package ships compiled JSX and ES modules. A Vite project with vite-plugin-solid is the tested case.
  • Three peer dependencies: solid-js 1.9, d3 7 and lodash 4.17. The package declares them in peerDependencies and installs none of them.
  • One copy of solid-js per page. Two copies break the reactivity. If the library and your app resolve different copies, force one copy in your bundler.
  • Node or Bun with npm, bun or a compatible package manager for the installation. The package declares no engines field.

Installation

The package is not in a registry. Install it from its public GitLab repository, pinned to one tag. The built dist/ is part of the repository, so the installation runs no build. You need no token and no .npmrc.

With npm:

npm install gitlab:ecosimlab/ecodisplays#v1.0.2

With bun, as a dependency entry:

"dependencies": {
  "@ecosimlab/ecodisplays": "git+https://gitlab.com/ecosimlab/ecodisplays.git#v1.0.2"
}

Pin one exact tag. Never pin a branch.

The README still names v0.5.0 in both install examples. The package.json of main carries version 1.0.2, and v1.0.2 is the newest tag. The version in the code counts.

Quickstart

The smallest complete example: one display, one input, one stylesheet.

import { createSignal } from "solid-js"
import { AP6DefaultDisplay, displayInput } from "@ecosimlab/ecodisplays"
import "@ecosimlab/ecodisplays/styles.css"

export function Screen() {
  const [input] = createSignal(
    displayInput({ hasSimulationData: true, speedKmh: 48, gear: "D" }),
  )

  return (
    <div style={{ width: "640px", height: "400px" }}>
      <AP6DefaultDisplay input={input()} fit="contain" />
    </div>
  )
}

Every display takes the same two props:

Prop Meaning
input the current DisplayInput
fit how the display fills its parent box: "contain" (the default) fits the whole design into the box, "width" lets the width lead

InputTransparenzDisplay takes one more prop, vehiclesAheadMode, and it has no default. Set it to "all" or to "closest". The same display takes the optional prop signsMode, default "list", which sets where the traffic signs stand.

Package exports

The package.json declares four entry points.

Entry point What it carries
@ecosimlab/ecodisplays the 20 displays, DisplayInput, displayInput, defaultDisplayInput, VehicleProfile, InputPanel, DisplayFrame, the design sizes and the pure calculations
@ecosimlab/ecodisplays/input the input contract alone: DisplayInput, displayInput, defaultDisplayInput, INPUT_FIELDS, deriveDisplayGate, VehicleProfile. It loads neither SolidJS nor the DOM.
@ecosimlab/ecodisplays/styles.css the stylesheet as a file
@ecosimlab/ecodisplays/styles the same stylesheet as the strings css and fontFaceCss

Use ./input in a parser or a test that must run in plain Node or Bun.

The input contract

DisplayInput is one flat object with 31 fields. The library checks no value at runtime. The type is the contract, so check the data at your own boundary.

Build an input with displayInput(partial). It fills every field you leave out from defaultDisplayInput:

import { type DisplayInput, displayInput } from "@ecosimlab/ecodisplays/input"

const input: DisplayInput = displayInput({
  hasSimulationData: true,
  hasEnergyData: true,
  timeS: 12.5,
  wheelspeedMps: 13.3,
  batterySocPercent: 72,
})

The unit is part of the name

Every field name ends in its unit.

Suffix Meaning
Percent 0 to 100
S seconds
M metres
Mps metres per second
Kmh kilometres per hour
W, Kw, Kwh watt, kilowatt, kilowatt hours

The fields

Field Unit Default Meaning
hasSimulationData flag false false until a valid message arrived. The consumer draws the “no data” state.
hasEnergyData flag false false if the tick carries no vehicle model data
timeS s 0 time since the start of the drive. A lower value than before starts a new drive and resets the accumulators.
distanceM m 0 absolute distance driven
wheelspeedMps m/s 0 wheel speed, unsmoothed. The basis of the calculations.
speedKmh km/h 0 smoothed speed for the display
gear "P" "P", "R", "N", "D" or a gear number
batteryEnergyKwh kWh 0 energy in the battery. Consumption and range come from its decrease.
batterySocPercent % 0 state of charge
pBatteryKw kW 0 current battery power. Negative during recuperation.
pAeroW, pRollW, pUpW, pPowertrainLossW, pHydraulicW W 0 the power shares: drag, rolling resistance, gradient, drivetrain loss, hydraulic brake
optimalSpeedKmh km/h 0 the speed the optimiser recommends. 0 means “no recommendation”.
optimalPowerW W 0 the battery power the optimiser recommends
referenceSpeedLine list [] reference speed from the offline optimisation. Empty means “no reference”.
mpcSpeedLine list [] speed forecast of the MPC over the distance
mpcPowerLine, mpcPowerLineTime list [] power forecast of the MPC, over the distance and over time
mpcStateMachineState null state of the MPC state machine. null shows nothing.
vehiclesAhead list [] the vehicles ahead in the own lane, sorted by distance
speedLimitKmh km/h 0 the speed limit in force. 0 means “unknown”.
nextSpeedLimitKmh km/h null the limit of the next sign ahead
nextSpeedLimitDistanceM m null the distance to the next speed limit sign
trafficLightsAhead list [] traffic lights and stop signs ahead, sorted by distance
stopDistanceBrakeM, stopDistanceStrongRecuperationM, stopDistanceLightRecuperationM, stopDistanceCoastingM m null the four stop points of the kinetic beam, one per way to stop

INPUT_FIELDS carries the same table in code: unit, range and step per field. Read it to build a control panel, or as documentation of a plausible value.

The displays

The export names come from the work packages of the project, not from the function. The column “shows” says what to expect.

Family Export Shows
AP4 AP4ConsumptionDisplay a full-screen road with the consumption of the last 150 m as a trace
AP4 AP4VelocityDisplay the same road, with the driven speed against the line of the optimiser
AP5 AP5BaselineDisplay speed, gear, state of charge and consumption, plus a tall power bar
AP5 AP5BoxDisplay grey boxes in the sizes of the AP5 parts: a layout template, and the only display without input
AP5 AP5ComperatorDisplay the power bar panel, plus the energy score as a bar
AP5 AP5DecisionDisplay the power bar panel, with a marker for the recommended power
AP5 AP5DecisionSpeedDisplay a speed bar with a marker for the current and one for the recommended speed
AP5 AP5InputDisplay the power bar split into gradient, drag, rolling resistance, drivetrain loss and brake
AP5 AP5SpeedometerDisplay speed and gear in front of a large arc
AP6 AP6DefaultDisplay speed and gear alone, in the size of their content
AP6 AP6StatusQuoDisplay a speed panel with a two-part bar, filled by the share of the maximum power
AP6 AP6VariationDisplay the same two-part bar, filled by the consumption per distance
LEM LEMDisplay power against recommended power over 150 m back and ahead
LEM LEMTimeDisplay the same power chart over 20 seconds back and ahead
LEM LEMVelocityTimeDisplay speed over 20 seconds back and ahead, with forecast and reference line
LEM LEMDecisionDisplay an AP5 panel with the target speed in colour, next to a power bar with the brake share
InputTransparenz InputTransparenzDisplay the road ahead with vehicles, the next speed sign and the next light
EnergyScore EnergyScoreBarDisplay a small panel over the video: speed, gear and charge, next to a green efficiency bar
EnergyScore EnergyScoreGlyphDisplay the same small panel, with a smiley instead of the bar
EnergyScore MinimalGaugeDisplay speed and gear only, drawn small

Two names are not what you would guess. LEMDecisionDisplay is an AP5 panel with an LEM name, and it reads the AP5 fields. MinimalGaugeDisplay sits in the energy score family, but it calculates like AP6.

Vehicle profile

The displays scale the input against a vehicle. Every display whose calculation depends on those numbers takes the optional prop vehicle. Without the prop, defaultVehicleProfile applies, with the values of the simulator car.

import { AP6DefaultDisplay, defaultVehicleProfile } from "@ecosimlab/ecodisplays"

const smallCar = { ...defaultVehicleProfile, maxPowerKw: 80, batteryCapacityKwh: 22 }
Field Unit What it scales
maxPowerKw kW the power bar
batteryCapacityKwh kWh the state of charge in percent (LEM)
rangeAtFullBatteryKm km the range shown by LEM
consumptionMaxKwh, consumptionMinKwh kWh the top and the bottom of the consumption bar
defaultConsumptionKwhPer100Km kWh/100 km the stand-in range while the measured consumption is still 0
scorePowerMaxKw kW how hard a deviation from the recommendation hits the energy score

The AP4 displays draw the raw trace and take no profile.

Styles

The package ships its CSS twice. The text is the same in both.

As a file, if your build bundles CSS:

import "@ecosimlab/ecodisplays/styles.css"

As a string, if it does not — a single-file bundle, for example:

import { css, fontFaceCss } from "@ecosimlab/ecodisplays/styles"

const style = document.createElement("style")
style.textContent = css
document.head.append(style)

If you render a display in a shadow root, add fontFaceCss to the document as well. The browser reads @font-face from the document only, never from a shadow root. Without that step the displays fall back to a system font.

The stylesheet holds no global CSS reset. It leaves the page around it alone. If your page needs a reset, load one yourself.

The font is Inter, under the SIL Open Font License 1.1. The font files ship with the package as woff2, embedded in the CSS as data URIs. Nothing loads from the network at runtime.

Development and tests

Clone the repository and install with bun. The scripts of package.json:

Command What it does
bun run dev starts the showcase: every display with a made-up drive, one page per display
bun run build rewrites dist/
bun run test runs the tests with bun test --conditions browser
bun run typecheck runs tsc --noEmit over the source and the tests
bun run lint runs Biome over src/
bun run check builds again and fails if dist/ does not match the source, or if the version does not match the tag

A release is a commit that carries the new version, the matching dist/ and a tag. There is no publish step and no registry.

Licence and citation

MIT. The file LICENSE carries the text, AUTHORS names the people who wrote the code, and ASSETS.md lists every image and font with its source and licence. Cite the library at version 1.0.2, tag v1.0.2.

Open questions

  • The README names v0.5.0 in its install examples, and the newest tag is v1.0.2. The README is out of date at that place.
  • The consumers videosimlab-cockpit-replay and videosimlab-template pin v1.0.1, one tag behind the library.
  • The prop kineticBeam of InputTransparenzDisplay draws the four stop points on the road. The source calls it an experiment, not yet a study condition. It is off by default.