Files
BAT/ONBOARDING.md

8.0 KiB

ONBOARDING — PrimeBAT Alarm Analysis Dashboard

Getting-started guide for a developer picking up this Ignition 8.3 Build-a-Thon project mid-build. Pairs with two other docs:

  • README.md — the docker stack, gateway-as-files layout, provisioning.
  • CONTRACT.md — the frozen build contract. This is the source of truth for every prop name, binding pattern, session prop, script signature, style class, and hard rule. Read it before editing any view.

1. What this is

A single Perspective dashboard (PrimeControls/Dashboard) that answers "is my alarm system healthy, and what do I fix first?" from ISA-18.2 metrics. All data comes from system.alarm.queryJournal / queryStatus (no SQL, no tag bindings) via one script package, PrimeControls.{calc, fmt, alarms}. One journal fetch per refresh produces a "bundle" that every tab/popup binds against.

Gateway state is version-controlled files under ignition/gateway/ (see README) — you edit JSON/Python on disk and tell the gateway to reload; there is no separate "deploy."

2. Get running (5 min)

docker compose up -d                       # gateway :8088 (admin/password) + MariaDB
curl -s http://localhost:8088/StatusPing   # {"state":"RUNNING"} when ready (~1-2 min)

The committed gateway files already contain the provisioned DB connection, alarm journal, API token config, and simulator tags — a plain up -d restores a working gateway. Only run python3 tools/provision.py mint-token && … provision if you rebuild from a factory-fresh gateway (README "Reset" section). The alarm simulator timer script is a Designer-side step — see test-data/README.md; without it the journal is empty and every chart shows its empty state.

Open the dashboard: http://localhost:8088/data/perspective/client/PrimeBAT

3. The edit → reload → verify loop

Everything under ignition/gateway/projects/PrimeBAT/ is a plain file. After editing:

python3 tools/lint_project.py                      # MUST exit 0 (banned strings, Jython, schema)
python3 tools/provision.py scan-projects           # hot-reload project resources into the gateway
#   (use scan-config instead after editing config resources or tags)
sleep 5; docker logs buildathon-ignition --since 1m 2>&1 | grep -iE "error|exception|traceback"
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8088/data/perspective/client/PrimeBAT

lint_project.py is the gate — it enforces the CONTRACT's banned strings (SELECT, [default], BuildathonSim, f-strings, …), resource.json boilerplate, and style-class references. Keep it at 0 failures / 0 warnings.

4. Unit tests (pure data-layer logic)

PrimeControls.calc and PrimeControls.fmt are written to run under both Jython 2.7 (in the gateway) and CPython 3 (for pytest). tests/conftest.py imports them straight from the project's code.py, so tests exercise the real gateway code.

pip install pytest        # not vendored
python3 -m pytest -q tests/     # 31 tests (calc + fmt)

PrimeControls.alarms is the gateway adapter (calls system.alarm.*) and is not unit-tested — verify it live (next section).

5. Browser verification harness (no Designer needed)

You can render the live Perspective client headless and screenshot it — how the tabs and popups in this repo were actually verified (the schema lint can't catch render-time bugs).

Chromium needs a few system libs. If you have sudo: npx playwright install --with-deps chromium. Without sudo (WSL2 dev boxes), fetch and extract them locally:

mkdir -p /tmp/pw && cd /tmp/pw && npm init -y && npm install playwright
npx playwright install chromium
mkdir libs && cd libs
apt-get download libnspr4 libnss3 libasound2t64 libasound2-data
for d in *.deb; do dpkg-deb -x "$d" root; done
export LD_LIBRARY_PATH="$PWD/root/usr/lib/x86_64-linux-gnu"   # then run node from /tmp/pw

Minimal probe (/tmp/pw/probe.js) — load, wait for the bundle to fetch, screenshot, capture console errors:

const { chromium } = require('playwright');
(async () => {
  const b = await chromium.launch();
  const p = await b.newPage({ viewport: { width: 1600, height: 1000 } });
  p.on('console', m => m.type() === 'error' && console.log('ERR', m.text().slice(0, 200)));
  p.on('pageerror', e => console.log('PAGEERR', e.message.slice(0, 200)));
  await p.goto('http://localhost:8088/data/perspective/client/PrimeBAT', { waitUntil: 'networkidle' });
  await p.waitForTimeout(11000);              // onStartup sets range -> bundle fetch -> render
  await p.screenshot({ path: 'dash.png' });
  // click a tab: await p.getByText('Bad Actors').first().click(); await p.waitForTimeout(4000);
  await b.close();
})();

node probe.js → inspect dash.png. A live gateway-side data probe (runs the real getDashboardBundle against the journal) lives at ignition/gateway/projects/SimHarness/ ignition/script-python/probe/ — enable the ProbeTick timer, scan, read ignition/gateway/projects/.probe/out.json.

6. Gotchas that already bit us (all in CONTRACT.md, repeated here)

  • Jython 2.7 in all gateway scripts/transforms: % formatting only (no f-strings), no typing/statistics, every inline-script line starts with a tab. Wrap any system.* / PrimeControls.alarms.* call in a bare except: — Java exceptions bypass except Exception. Every transform must tolerate null/empty (the bundle can be None).
  • A view cannot nest sub-views inside its own folder. Dashboard is a view, so Header/FilterBar had to move to PrimeControls/Shell/ — nesting them under Dashboard/ made them resolve as "view does not exist." Put shared/child views under a pure folder (Tabs/, Components/, Popups/, Shell/).
  • Input params must be persistent: false. With persistent: true, Perspective bakes the last-received value into view.json on render — the live bundle (with sim tag paths) got serialized into a tab and broke lint. Parents/openers always supply these params, so persistence is never needed.
  • No SQL, no tag bindings, no journalName= literals, no docked views. All resources under PrimeControls/ namespaces.
  • The gateway writes runtime churn to ignition/gateway/config/ignition/tags/ valueStore.idb and the trial-clock system properties — don't commit those. (Worth adding to .gitignore.)

7. Where things are

Path What
…/views/PrimeControls/Dashboard/view.json Main view: onStartup range init, custom.bundle binding, tab embeds
…/views/PrimeControls/Shell/{Header,FilterBar} Health score + KPI ribbon + date range; filter bar
…/views/PrimeControls/Tabs/{Overview,Analysis,BadActors,Journal} Tab content (embedded, tab-visibility bound)
…/views/PrimeControls/Popups/{HealthScore,ShiftReport,AlarmDetail,EventDetail} Popups (IDs are constants — see CONTRACT)
…/views/PrimeControls/Components/* Shared components (KpiCard, SourceRow, HeatmapCell, …)
…/script-python/PrimeControls/{calc,fmt,alarms}/code.py Data layer (calc/fmt pure; alarms = gateway adapter)
tools/lint_project.py, tools/provision.py Lint gate; gateway provisioning/reload
tests/ pytest for calc + fmt

8. Current state & what's next

Done (Phase 2, committed): all 4 tabs + 4 popups built; Overview timeline and the Bad Actors ranked "Top Sources" table are browser-verified. Lint clean.

Not yet live-verified: Analysis, Journal, and the 4 popups were schema-verified and reload-clean but never rendered/clicked — do a browser pass (section 5) before trusting them.

Next phase — the Header. It renders now (after the Shell relocation) but is slated for rework, and a benign React.cloneElement … null console warning still needs chasing (the KPI ribbon / dropdowns are the suspects, since it fires even with the header painting correctly).

See plan.md for the overall design and the remaining "build if time allows" ideas (compare-to-prior-period deltas, native drawing gauge, shift report polish).