From c82a2fa6df8364c4fffeec13b7c1dbaaa936aef0 Mon Sep 17 00:00:00 2001 From: "b.peck" Date: Thu, 16 Jul 2026 12:16:16 -0500 Subject: [PATCH] Phase 1 UI foundation: 32 style classes, 9 shared components, tab shell, placeholders Dashboard shell: bundle expr binding + script transform, onStartup range init, hand-rolled tab bar (position.display exprs), CONTRACT.md frozen pending G1. Co-Authored-By: Claude Fable 5 --- CONTRACT.md | 225 +++++++++++++ .../config/ignition/tags/valueStore.idb | Bin 20480 -> 20480 bytes .../PrimeControls/Card/resource.json | 10 + .../PrimeControls/Card/style.json | 10 + .../PrimeControls/CardTitle/resource.json | 10 + .../PrimeControls/CardTitle/style.json | 12 + .../PrimeControls/Chip/Bad/resource.json | 10 + .../PrimeControls/Chip/Bad/style.json | 13 + .../PrimeControls/Chip/Flat/resource.json | 10 + .../PrimeControls/Chip/Flat/style.json | 13 + .../PrimeControls/Chip/Good/resource.json | 10 + .../PrimeControls/Chip/Good/style.json | 13 + .../PrimeControls/Empty/resource.json | 10 + .../PrimeControls/Empty/style.json | 9 + .../PrimeControls/Grade/A/resource.json | 10 + .../PrimeControls/Grade/A/style.json | 10 + .../PrimeControls/Grade/B/resource.json | 10 + .../PrimeControls/Grade/B/style.json | 10 + .../PrimeControls/Grade/C/resource.json | 10 + .../PrimeControls/Grade/C/style.json | 10 + .../PrimeControls/Grade/D/resource.json | 10 + .../PrimeControls/Grade/D/style.json | 10 + .../PrimeControls/Grade/F/resource.json | 10 + .../PrimeControls/Grade/F/style.json | 10 + .../PrimeControls/Grade/NA/resource.json | 10 + .../PrimeControls/Grade/NA/style.json | 10 + .../PrimeControls/PageBg/resource.json | 10 + .../PrimeControls/PageBg/style.json | 8 + .../Priority/Critical/resource.json | 10 + .../Priority/Critical/style.json | 15 + .../Priority/Diagnostic/resource.json | 10 + .../Priority/Diagnostic/style.json | 15 + .../PrimeControls/Priority/High/resource.json | 10 + .../PrimeControls/Priority/High/style.json | 15 + .../PrimeControls/Priority/Low/resource.json | 10 + .../PrimeControls/Priority/Low/style.json | 15 + .../Priority/Medium/resource.json | 10 + .../PrimeControls/Priority/Medium/style.json | 15 + .../PrioritySoft/Critical/resource.json | 10 + .../PrioritySoft/Critical/style.json | 10 + .../PrioritySoft/Diagnostic/resource.json | 10 + .../PrioritySoft/Diagnostic/style.json | 10 + .../PrioritySoft/High/resource.json | 10 + .../PrioritySoft/High/style.json | 10 + .../PrioritySoft/Low/resource.json | 10 + .../PrimeControls/PrioritySoft/Low/style.json | 10 + .../PrioritySoft/Medium/resource.json | 10 + .../PrioritySoft/Medium/style.json | 10 + .../PrimeControls/State/Acked/resource.json | 10 + .../PrimeControls/State/Acked/style.json | 8 + .../PrimeControls/State/Active/resource.json | 10 + .../PrimeControls/State/Active/style.json | 9 + .../PrimeControls/State/Cleared/resource.json | 10 + .../PrimeControls/State/Cleared/style.json | 8 + .../PrimeControls/Tab/Active/resource.json | 10 + .../PrimeControls/Tab/Active/style.json | 16 + .../PrimeControls/Tab/Inactive/resource.json | 10 + .../PrimeControls/Tab/Inactive/style.json | 23 ++ .../PrimeControls/Text/Big/resource.json | 10 + .../PrimeControls/Text/Big/style.json | 9 + .../PrimeControls/Text/Kpi/resource.json | 10 + .../PrimeControls/Text/Kpi/style.json | 9 + .../PrimeControls/Text/Muted/resource.json | 10 + .../PrimeControls/Text/Muted/style.json | 8 + .../PrimeControls/Toolbar/resource.json | 10 + .../PrimeControls/Toolbar/style.json | 10 + .../Components/EmptyState/resource.json | 10 + .../Components/EmptyState/view.json | 93 ++++++ .../Components/HealthGauge/resource.json | 10 + .../Components/HealthGauge/view.json | 137 ++++++++ .../Components/HeatmapCell/resource.json | 10 + .../Components/HeatmapCell/view.json | 94 ++++++ .../Components/InsightRow/resource.json | 10 + .../Components/InsightRow/view.json | 121 +++++++ .../Components/KpiCard/resource.json | 10 + .../Components/KpiCard/view.json | 184 ++++++++++ .../Components/PriorityCard/resource.json | 10 + .../Components/PriorityCard/view.json | 137 ++++++++ .../Components/ScoreBar/resource.json | 10 + .../Components/ScoreBar/view.json | 207 ++++++++++++ .../Components/SourceRow/resource.json | 10 + .../Components/SourceRow/view.json | 192 +++++++++++ .../Components/TabButton/resource.json | 10 + .../Components/TabButton/view.json | 69 ++++ .../Dashboard/FilterBar/resource.json | 10 + .../Dashboard/FilterBar/view.json | 68 ++++ .../Dashboard/Header/resource.json | 10 + .../PrimeControls/Dashboard/Header/view.json | 68 ++++ .../views/PrimeControls/Dashboard/view.json | 315 +++++++++++++++++- .../PrimeControls/Tabs/Analysis/resource.json | 10 + .../PrimeControls/Tabs/Analysis/view.json | 68 ++++ .../Tabs/BadActors/resource.json | 10 + .../PrimeControls/Tabs/BadActors/view.json | 68 ++++ .../PrimeControls/Tabs/Journal/resource.json | 10 + .../PrimeControls/Tabs/Journal/view.json | 68 ++++ .../PrimeControls/Tabs/Overview/resource.json | 10 + .../PrimeControls/Tabs/Overview/view.json | 68 ++++ tools/lint_project.py | 4 +- 98 files changed, 3006 insertions(+), 13 deletions(-) create mode 100644 CONTRACT.md create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Card/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Card/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/CardTitle/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/CardTitle/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Chip/Bad/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Chip/Bad/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Chip/Flat/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Chip/Flat/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Chip/Good/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Chip/Good/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Empty/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Empty/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Grade/A/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Grade/A/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Grade/B/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Grade/B/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Grade/C/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Grade/C/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Grade/D/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Grade/D/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Grade/F/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Grade/F/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Grade/NA/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Grade/NA/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/PageBg/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/PageBg/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Priority/Critical/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Priority/Critical/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Priority/Diagnostic/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Priority/Diagnostic/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Priority/High/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Priority/High/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Priority/Low/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Priority/Low/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Priority/Medium/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Priority/Medium/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/PrioritySoft/Critical/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/PrioritySoft/Critical/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/PrioritySoft/Diagnostic/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/PrioritySoft/Diagnostic/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/PrioritySoft/High/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/PrioritySoft/High/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/PrioritySoft/Low/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/PrioritySoft/Low/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/PrioritySoft/Medium/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/PrioritySoft/Medium/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/State/Acked/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/State/Acked/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/State/Active/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/State/Active/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/State/Cleared/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/State/Cleared/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Tab/Active/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Tab/Active/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Tab/Inactive/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Tab/Inactive/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Text/Big/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Text/Big/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Text/Kpi/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Text/Kpi/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Text/Muted/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Text/Muted/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Toolbar/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/style-classes/PrimeControls/Toolbar/style.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/EmptyState/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/EmptyState/view.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/HealthGauge/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/HealthGauge/view.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/HeatmapCell/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/HeatmapCell/view.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/InsightRow/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/InsightRow/view.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/KpiCard/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/KpiCard/view.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/PriorityCard/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/PriorityCard/view.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/ScoreBar/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/ScoreBar/view.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/SourceRow/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/SourceRow/view.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/TabButton/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/TabButton/view.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/FilterBar/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/FilterBar/view.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/Header/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/Header/view.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Analysis/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Analysis/view.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/BadActors/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/BadActors/view.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Journal/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Journal/view.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Overview/resource.json create mode 100644 ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Overview/view.json diff --git a/CONTRACT.md b/CONTRACT.md new file mode 100644 index 0000000..5e96298 --- /dev/null +++ b/CONTRACT.md @@ -0,0 +1,225 @@ +# CONTRACT.md — PrimeBAT build contract (FROZEN after Gate G1) + +Every builder/verifier agent works from this file. Do not deviate; changes require the +integrator to edit this file first. Repo: `/home/bpeck/git/buildathon`. Project root: +`ignition/gateway/projects/PrimeBAT/`. Reload: `python3 tools/provision.py scan-projects`. +Lint: `python3 tools/lint_project.py` (must exit 0). Component prop schemas (ground truth +for every prop name): `/tmp/claude-1000/-home-bpeck-git-buildathon/a7f89b44-360c-4f23-ab63-503f3c25b53b/scratchpad/schemas/components/.schema.json`. +Format exemplar (READ IT before writing any view.json): +`/home/bpeck/git/primebench/ignition/gateway/projects/PrimeBench/com.inductiveautomation.perspective/views/ForceRow/view.json`. + +## Hard rules (contest + platform) + +1. Jython 2.7 everywhere (project scripts, transforms, event scripts): **NO f-strings** + (`%` formatting only), no `typing`, no `statistics`. Scripts in view.json are inline + JSON strings, tab-indented (`\t`), first char of every script/transform line block is a tab. +2. **Java exceptions bypass `except Exception`** in Jython. Gateway-facing defensive code + uses bare `except:` + `sys.exc_info()` (probe-verified). +3. NO tag bindings in PrimeBAT. NO SQL / `system.db.*`. NO `journalName=` literals. + Banned strings (lint-enforced): `[default]`, `BuildathonSim`, `Buildathon_DB`, + `alarmsim`, `SELECT`, f-string prefixes. +4. All views/styles/scripts under `PrimeControls/` namespaces. Single page `/` → + `PrimeControls/Dashboard`. `sharedDocks` stays `{}` (docked views prohibited). +5. Every view tolerates `bundle: null` / empty arrays — no binding may throw on empty. + Empty-window UX = `PrimeControls/Components/EmptyState` embed, not a broken chart. +6. resource.json boilerplate for every view: + `{"scope":"G","version":1,"restricted":false,"overridable":true,"files":["view.json"],"attributes":{}}` + (style classes: `files:["style.json"]`; script modules: scope `"A"`, `attributes:{"hintScope":2}`). +7. Numbers/durations/timestamps rendered through `PrimeControls.fmt` helpers (consistency). + +## Probe-verified journal API facts (do not re-derive) + +- `system.alarm.queryJournal(startDate=, endDate=)` — epoch millis + accepted directly; omit `journalName` (uses the single configured journal); nonexistent + name raises java `IllegalArgumentException` (catch with bare except). +- Result `AlarmQueryResultImpl`: iterable, `len()` works. One entry per **transition**; + entries of one alarm instance share `str(evt.getId())` (uuid). +- `str(evt.getState())` is compound: `"Active, Unacknowledged"`, `"Cleared, Acknowledged"`, etc. + Transition kind per row: activation row state starts `"Active"` and row's + `getActiveData()` non-null; clear row starts `"Cleared"` (`getClearedData()` non-null); + ack row has `getAckData()` non-null / state contains `", Acknowledged"`. + Per-instance: `active_ms` = min ts of Active rows; `clear_ms` = min ts of Cleared rows; + `ack_ms` = min ts of rows with ack marker. +- `evt.get("eventTime")` → java Date; `.getTime()` → ms. `evt.get("eventType")` is None (unused). +- `evt.getDisplayPath()` = `""` when unconfigured → fall back to parsing `getSource()` + (`prov:default:/tag:FOLDER/SUB/TAG:/alm:ALARMNAME`). +- `evt.getPriority()` → AlarmPriority enum: `.ordinal()` → 0..4, `str()` → name. +- System rows: `source == "evt:System Startup"` etc., `evt.get("isSystemEvent")` True. + `includeSystem`-style kwargs are ACCEPTED BUT IGNORED — always filter client-side. +- Ack user field: `evt.get("ackUserName")` (unicode, may be `u''`). `ackUser` is None. +- `system.alarm.queryStatus(state=["ActiveUnacked","ActiveAcked"])` works; entries have + `getActiveData()` with eventTime. `system.alarm.getShelvedPaths()` → list. + +## Session props (only these exist; NO agent may add any) + +`session.custom.PrimeControls`: +```json +{"query": {"startMs": 0, "endMs": 0, "rangePreset": "8h", "priorities": [], "areas": [], + "states": [], "search": "", "refreshToken": 0}, + "ui": {"selectedTab": 0, "badActorFocus": ""}} +``` +- `rangePreset` ∈ `4h|8h|24h|7d|custom`. Header preset dropdown writes startMs/endMs/rangePreset. +- Filters (`priorities` = int levels 0–4, `areas` = strings, `states` ∈ + `["active","cleared","acked","unacked"]`, `search` substring) are written by FilterBar; + **Apply button bumps `refreshToken`** (filters do NOT auto-refetch). +- `ui.selectedTab`: 0 Overview · 1 Analysis · 2 Bad Actors · 3 Journal. +- `ui.badActorFocus`: source string; set by Overview top-5 click; BadActors highlights + clears. + +## Data layer (script package `PrimeControls`, resource paths `ignition/script-python/PrimeControls//code.py`) + +Modules: `calc` (pure), `fmt` (pure), `alarms` (gateway adapter). Pure modules run under +CPython 3 for pytest AND Jython 2.7 (`from __future__ import division, print_function`). + +### Perspective entry points (exact signatures) + +```python +PrimeControls.alarms.getDashboardBundle(startMs, endMs, options=None) # -> bundle dict (below) +PrimeControls.alarms.getJournalPage(startMs, endMs, filters=None, page=0, pageSize=50) +# filters: {"priorities":[int], "areas":[str], "states":[str], "search":str} +# -> {"rows":[{"id","time_ms","time_label","source","label","area","state","state_label", +# "priority","priority_name","ack_user"}], "total":int, "page":int, +# "page_size":int, "truncated":bool, "error":None|str} +PrimeControls.alarms.journalCsv(startMs, endMs, filters=None, maxRows=10000) # -> CSV string +PrimeControls.alarms.getSourceDetail(source, startMs, endMs) +# -> {"label","area","events":[journal-page rows],"daily":[{"t0","count"}], +# "stats":{"count","avg_tta_ms","avg_active_ms","fleeting_count", +# "top_ack_users":[{"user","count"}]},"error":None|str} +PrimeControls.alarms.getEventData(eventId, aroundMs, source) # -> {"props":[{"name","value"}],"error":None|str} +PrimeControls.alarms.getShiftReportText(shiftHours=8, anchorHour=0, options=None) +# -> {"text":str, "start_ms":int, "end_ms":int, "label":str} +PrimeControls.fmt.dur(ms) / num(v, dec=0) / pct(v, dec=0, signed=False) / clock(ms) / day_clock(ms) +PrimeControls.fmt.delta_chip(d) # d = a kpi delta dict -> {"arrow","text","good"} +``` + +### Bundle schema (`getDashboardBundle` return — every key ALWAYS present) + +``` +meta: {start_ms, end_ms, prior_start_ms, now_ms, window_hours, event_count, + activation_count, dropped_rows, truncated, correlation_mode, + standing_mode, areas:[str], priorities_seen:[{"value":int,"label":str}], + source_count, error:None|str, opts:{...effective thresholds...}} +kpis: {activations, rate_per_hr, active_now, unacked_now, shelved_now, mtta_ms, + mttr_ms, flood_pct, flood_count, chatter_count, standing_count, fleeting_count} + # each value = {"value", "prior", "delta_pct", "dir": "up|down|flat|new|none", "good": bool|None} +health: {grade:"A".."F"|None, score:float|None, + subs:[{key,label,score:float|None,weight,detail}]} # keys: rate,flood,chatter,standing,priority +insights: [{severity:int(0 crit..3 info), icon:str, text:str, tab:str}] +rate: {bins:[{t0,t1,count,flood:bool}], bin_ms, target_per_bin, flood_threshold, max_count} +floods: {episodes:[{start_ms,end_ms,duration_ms,event_count,peak_bin_count, + top_source_label,top_source_count,top_area}], pct_time_in_flood} +priority: {raw:[{name,level,count}], buckets:{low,medium,high,other}, + pct:{low,medium,high}, target:{low:80.0,medium:15.0,high:5.0}, sum_abs_dev} +heatmap: {rows:[[int]*24]*7, row_labels:["Mon".."Sun"], max_count, total} +mtta_mttr: {mtta:{mean_ms,median_ms,count}, mttr:{mean_ms,median_ms,count}, + trend:[{t0,mtta_ms|None,mttr_ms|None,ack_n,clear_n}]} +pareto: {total_activations, rows:[{rank,source,label,area,priority_name,count,pct,cum_pct}]} +top_sources: [first 5 pareto rows] +chattering: [{source,label,area,priority_name,count,per_hour,median_gap_s}] +fleeting: {total, sources:[{source,label,area,priority_name,count,median_s}]} +standing: {mode, count, rows:[{source,label,area,priority_name,active_ms,age_ms,age_h,unacked}]} +active_now: [{source,label,area,priority_name,active_ms,age_ms,unacked}] # capped 200 +``` + +Thresholds (surfaced in `meta.opts`): bins 10 min; flood >10/10min (end hysteresis 5); +ISA target 6/hr; chatter >10/hr AND median gap ≤120 s (min 5); fleeting <10 s; +standing >24 h; deltas flat band ±5%; caps: 50 000 events, 5000 bins, lists ≤50 rows. + +## Binding patterns (copy these shapes exactly) + +**P1 — bundle binding (Dashboard shell ONLY):** `custom.bundle` ← expr binding +```json +{"type": "expr", + "config": {"expression": "{session.custom.PrimeControls.query.refreshToken} + '|' + {session.custom.PrimeControls.query.startMs} + '|' + {session.custom.PrimeControls.query.endMs}"}, + "transforms": [{"type": "script", "code": "\tq = self.session.custom.PrimeControls.query\n\treturn PrimeControls.alarms.getDashboardBundle(q.startMs, q.endMs, {'priorities': list(q.priorities), 'areas': list(q.areas), 'states': list(q.states), 'search': q.search})"}]} +``` + +**P2 — embed a sub-view with bundle + tab visibility:** +```json +{"meta": {"name": "tabOverview"}, "position": {"grow": 1}, + "propConfig": { + "props.params.bundle": {"binding": {"type": "property", "config": {"path": "view.custom.bundle"}}}, + "position.display": {"binding": {"type": "expr", "config": {"expression": "{session.custom.PrimeControls.ui.selectedTab} = 0"}}}}, + "props": {"path": "PrimeControls/Tabs/Overview", "params": {}}, + "type": "ia.display.view"} +``` + +**P3 — bidirectional session binding (FilterBar widgets):** +```json +{"binding": {"bidirectional": true, "type": "property", + "config": {"path": "session.custom.PrimeControls.query.search"}}} +``` + +**P4 — event scripts:** buttons/dropdowns fire `events.component.onActionPerformed`; +labels/icons/containers fire `events.dom.onClick` (Designer-verified shape). Action object +is identical for both: `{"config": {"script": "\t..."}, "scope": "G", "type": "script"}`. +```json +"events": {"dom": {"onClick": {"config": {"script": "\tsystem.perspective.openPopup('PC_HealthScore', 'PrimeControls/Popups/HealthScore', params = {'health': self.view.params.health}, showCloseIcon = True, draggable = True)"}, "scope": "G", "type": "script"}}} +``` + +**P5 — flex repeater from bundle slice:** +```json +{"type": "ia.display.flex-repeater", + "propConfig": {"props.instances": {"binding": {"type": "property", "config": {"path": "view.params.bundle.top_sources"}}}}, + "props": {"path": "PrimeControls/Components/SourceRow", "direction": "column"}} +``` +Repeater instance keys become the child view's params → **bundle array element keys == +component view param names** (snake_case). + +View param declaration: `"params": {"bundle": null}` + propConfig +`"params.bundle": {"paramDirection": "input", "persistent": true}`. + +## Popups (IDs are constants) + +| ID | View | Params | +|---|---|---| +| `PC_HealthScore` | `PrimeControls/Popups/HealthScore` | `{health: bundle.health}` | +| `PC_AlarmDetail` | `PrimeControls/Popups/AlarmDetail` | `{source, label}` | +| `PC_EventDetail` | `PrimeControls/Popups/EventDetail` | `{event: }` | +| `PC_ShiftReport` | `PrimeControls/Popups/ShiftReport` | `{}` | + +Close: `system.perspective.closePopup('')`. + +## Style classes (foundation-owned; reference as `"style": {"classes": "PrimeControls/Card"}`) + +`PrimeControls/Priority/{Diagnostic,Low,Medium,High,Critical}` (solid chip bg+fg) · +`PrimeControls/PrioritySoft/{...same 5}` (tinted bg) · +`PrimeControls/Grade/{A,B,C,D,F,NA}` · `PrimeControls/Card` · `PrimeControls/CardTitle` · +`PrimeControls/Toolbar` · `PrimeControls/PageBg` · `PrimeControls/Chip/{Good,Bad,Flat}` · +`PrimeControls/Tab/{Active,Inactive}` · `PrimeControls/State/{Active,Acked,Cleared}` · +`PrimeControls/Text/{Big,Kpi,Muted}` · `PrimeControls/Empty`. +Priority scale: Diagnostic `#8A94A6` · Low `#5B9BD5` · Medium `#E5C453` · High `#E8883A` · +Critical `#D64550`. Accent: `#3B7DD8`. Agents may ADD classes only under +`style-classes/PrimeControls//...`, never modify shared ones. + +## Shared components (foundation-owned, under `views/PrimeControls/Components/`) + +| View | Params (all input) | +|---|---| +| `Components/TabButton` | `title:str, index:int` (writes `ui.selectedTab` onClick; active style expr) | +| `Components/KpiCard` | `label:str, value:str, unit:str, delta:{...kpi delta}|null` (polarity already encoded in `delta.good`) | +| `Components/PriorityCard` | `priority_name:str, count:int, pct:float` | +| `Components/InsightRow` | `severity:int, icon:str, text:str, tab:str` | +| `Components/SourceRow` | `rank:int, source:str, label:str, count:int, pct:float` (click → focus+tab 2) | +| `Components/HeatmapCell` | `count:int, max:int` (bg alpha-scaled accent) | +| `Components/ScoreBar` | `key:str, label:str, score:float|null, weight:float, detail:str` | +| `Components/EmptyState` | `message:str, icon:str` | +| `Components/HealthGauge` | `health:{grade,score,subs}|null` (own dom.onClick opens PC_HealthScore with `{'health': ...}`) | + +## View ownership (Phase 2/3) + +A: `Dashboard/Header` + `Dashboard/FilterBar` · B: `Tabs/Overview` · C: `Tabs/Analysis` · +D: `Tabs/BadActors` · E: `Tabs/Journal` · F: `Popups/HealthScore`+`Popups/ShiftReport` · +G: `Popups/AlarmDetail`+`Popups/EventDetail` · H: `Components/HealthGauge` upgrade. +Integrator-only: `Dashboard/view.json`, session-props, page-config, shared Components, +root style classes, `script-python/*`, SimHarness. + +## Verify loop (every agent, before declaring done) + +```bash +python3 tools/lint_project.py +python3 tools/provision.py scan-projects +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 +``` +Schema uncertainty → read the harvested schema file; still unsure → minimal probe view in +`SimHarness/views/Scratch/Current` (coordinate via integrator), never guess deep chart JSON. diff --git a/ignition/gateway/config/ignition/tags/valueStore.idb b/ignition/gateway/config/ignition/tags/valueStore.idb index 19bdb53697cdf335750b4b12b8dfeada68fc5123..e02c8f0a06674d21c48aa4c72246b36398c5fb2f 100644 GIT binary patch delta 764 zcmYjPOH5Ni6t!w8Gp{__AT{(=TR$j)N=ys=LaRV&AIi^vL?T_FCN>pBG+|MZ9f@MY zUAdyspbJUBC}BlnLU1EWF;Nqu$ig;#eeY>ps2hJX{SY^koO|b-JLlXPjwyy?ikagU zBpQ|!6PLvu2jQHhoe?K*Q#ARFuZB*$5lmJUe6Z+|q<=x`5?2X-EfH3jFDFQtA8n|@ zTqndG2Keg2JHwW~tGxpQp({5EHa({@jcfF5YV=iN?lc4~28ahRe7BY$sk(#eRX+o5 zjfY7+en?Piis38BR4Xj|Roa|H{&oC*(lNsyKMS8~I+#>Hri}h@OF6_{M(DGv;Zi^Y zgSBT^`OXj0lgG~b_h7CoH?|BkwgQxVM{OkESAo&yMCsR$2ud$A5VX~^;`kPV#PN$K zOqd(Yn4+p-UJu3IV)$+2QQDWFgyt4#x4%VVWQY^+>w=iy$f516n6AvN=F0=BZ{Kn7mmNVIFPY2rxPQgj`6@l=@4v zPAXux>lBjaG*o5o8r*V4S)uDXK|j&ZO3a@DR-`tH)rF*KW6}nzO+(Z~+npa4 zHFw3Poe;MsP5dnfsGr2Au}*L8*yP=NquklZYH^N?>YCJbKi=nSrIjxH#n4Q zY-u*;bNi>@E*Gbzau!!o`L}Qp5*{1ao&8Yetf;cRivL%(71-yV8FGz}P0Xe96q|se zcL;v@sbgtw@zLzk(!zs`_P{P4sLj{OcVQ}Wj$&^?iW`DrjDkPT!>C$#fm_wWPhea; zEtg*6N-pgQ4!qR~g=jz1q{uZs;SN?OoW-3B(1;qK;<^q`M^NARX(;+_u;|gl{g?@s zN0J65!{Tuz^E`TzV((yKXP^`_g2}Dz*=Te7uOZw7oCT6y)Bh<>fR+z;QpHY~={Le+N6gmdR{MB7G@mvxV~ z&>c&{A$6U|AMYBQJV>z*JI4O 0.55:\n\t\t\treturn '#FFFFFF'\n\texcept:\n\t\tpass\n\treturn 'var(--neutral-90)'", + "type": "script" + } + ], + "type": "property" + } + }, + "props.text": { + "binding": { + "config": { + "path": "view.params.count" + }, + "transforms": [ + { + "code": "\treturn '' if not value else value", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "borderRadius": "2px", + "fontSize": "10px", + "marginBottom": "1px", + "marginRight": "1px", + "textAlign": "center" + } + }, + "type": "ia.display.label" + } + ], + "meta": { + "name": "root" + }, + "props": { + "direction": "row", + "style": { + "alignItems": "stretch" + } + }, + "type": "ia.container.flex" + } +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/InsightRow/resource.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/InsightRow/resource.json new file mode 100644 index 0000000..276dced --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/InsightRow/resource.json @@ -0,0 +1,10 @@ +{ + "scope": "G", + "version": 1, + "restricted": false, + "overridable": true, + "files": [ + "view.json" + ], + "attributes": {} +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/InsightRow/view.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/InsightRow/view.json new file mode 100644 index 0000000..6ef9dc2 --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/InsightRow/view.json @@ -0,0 +1,121 @@ +{ + "custom": {}, + "params": { + "icon": null, + "severity": 3, + "tab": null, + "text": null + }, + "propConfig": { + "params.icon": { + "paramDirection": "input", + "persistent": true + }, + "params.severity": { + "paramDirection": "input", + "persistent": true + }, + "params.tab": { + "paramDirection": "input", + "persistent": true + }, + "params.text": { + "paramDirection": "input", + "persistent": true + } + }, + "props": {}, + "root": { + "children": [ + { + "meta": { + "name": "icon" + }, + "position": { + "basis": "24px", + "shrink": 0 + }, + "propConfig": { + "props.color": { + "binding": { + "config": { + "path": "view.params.severity" + }, + "transforms": [ + { + "code": "\tcolors = {0: '#D64550', 1: '#E8883A', 2: '#3B7DD8', 3: 'var(--neutral-60)'}\n\treturn colors.get(value, 'var(--neutral-60)')", + "type": "script" + } + ], + "type": "property" + } + }, + "props.path": { + "binding": { + "config": { + "path": "view.params.icon" + }, + "transforms": [ + { + "code": "\treturn 'material/' + (value or 'insights')", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "path": "material/insights", + "style": { + "fontSize": "18px" + } + }, + "type": "ia.display.icon" + }, + { + "meta": { + "name": "text" + }, + "position": { + "grow": 1 + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.text" + }, + "transforms": [ + { + "code": "\treturn value or ''", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "fontSize": "13px" + } + }, + "type": "ia.display.label" + } + ], + "meta": { + "name": "root" + }, + "props": { + "direction": "row", + "style": { + "alignItems": "center", + "gap": "8px", + "paddingBottom": "4px", + "paddingTop": "4px" + } + }, + "type": "ia.container.flex" + } +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/KpiCard/resource.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/KpiCard/resource.json new file mode 100644 index 0000000..276dced --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/KpiCard/resource.json @@ -0,0 +1,10 @@ +{ + "scope": "G", + "version": 1, + "restricted": false, + "overridable": true, + "files": [ + "view.json" + ], + "attributes": {} +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/KpiCard/view.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/KpiCard/view.json new file mode 100644 index 0000000..36c1388 --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/KpiCard/view.json @@ -0,0 +1,184 @@ +{ + "custom": {}, + "params": { + "delta": null, + "label": "KPI", + "unit": null, + "value": null + }, + "propConfig": { + "params.delta": { + "paramDirection": "input", + "persistent": true + }, + "params.label": { + "paramDirection": "input", + "persistent": true + }, + "params.unit": { + "paramDirection": "input", + "persistent": true + }, + "params.value": { + "paramDirection": "input", + "persistent": true + } + }, + "props": {}, + "root": { + "children": [ + { + "meta": { + "name": "lbl" + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.label" + }, + "type": "property" + } + } + }, + "props": { + "style": { + "classes": "PrimeControls/Text/Muted" + } + }, + "type": "ia.display.label" + }, + { + "children": [ + { + "meta": { + "name": "value" + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.value" + }, + "transforms": [ + { + "code": "\treturn value if value is not None else '--'", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "classes": "PrimeControls/Text/Kpi" + } + }, + "type": "ia.display.label" + }, + { + "meta": { + "name": "unit" + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.unit" + }, + "transforms": [ + { + "code": "\treturn value or ''", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "classes": "PrimeControls/Text/Muted" + } + }, + "type": "ia.display.label" + } + ], + "meta": { + "name": "valueRow" + }, + "props": { + "direction": "row", + "style": { + "alignItems": "baseline", + "gap": "4px" + } + }, + "type": "ia.container.flex" + }, + { + "meta": { + "name": "chip" + }, + "propConfig": { + "position.display": { + "binding": { + "config": { + "path": "view.params.delta" + }, + "transforms": [ + { + "code": "\treturn value is not None", + "type": "script" + } + ], + "type": "property" + } + }, + "props.style.classes": { + "binding": { + "config": { + "path": "view.params.delta" + }, + "transforms": [ + { + "code": "\tbase = 'PrimeControls/Chip/Flat'\n\ttry:\n\t\tif value and value['good'] == True:\n\t\t\tbase = 'PrimeControls/Chip/Good'\n\t\telif value and value['good'] == False:\n\t\t\tbase = 'PrimeControls/Chip/Bad'\n\texcept:\n\t\tpass\n\treturn base", + "type": "script" + } + ], + "type": "property" + } + }, + "props.text": { + "binding": { + "config": { + "path": "view.params.delta" + }, + "transforms": [ + { + "code": "\tif not value:\n\t\treturn ''\n\ttry:\n\t\tchip = PrimeControls.fmt.delta_chip(value)\n\t\treturn '%s %s' % (chip['arrow'], chip['text'])\n\texcept:\n\t\treturn ''", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": {}, + "type": "ia.display.label" + } + ], + "meta": { + "name": "root" + }, + "props": { + "direction": "column", + "style": { + "classes": "PrimeControls/Card", + "justifyContent": "space-between" + } + }, + "type": "ia.container.flex" + } +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/PriorityCard/resource.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/PriorityCard/resource.json new file mode 100644 index 0000000..276dced --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/PriorityCard/resource.json @@ -0,0 +1,10 @@ +{ + "scope": "G", + "version": 1, + "restricted": false, + "overridable": true, + "files": [ + "view.json" + ], + "attributes": {} +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/PriorityCard/view.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/PriorityCard/view.json new file mode 100644 index 0000000..d1e9c8e --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/PriorityCard/view.json @@ -0,0 +1,137 @@ +{ + "custom": {}, + "params": { + "count": 0, + "pct": null, + "priority_name": null + }, + "propConfig": { + "params.count": { + "paramDirection": "input", + "persistent": true + }, + "params.pct": { + "paramDirection": "input", + "persistent": true + }, + "params.priority_name": { + "paramDirection": "input", + "persistent": true + } + }, + "props": {}, + "root": { + "children": [ + { + "meta": { + "name": "name" + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.priority_name" + }, + "transforms": [ + { + "code": "\treturn value or 'Unknown'", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "fontSize": "13px", + "fontWeight": "bold" + } + }, + "type": "ia.display.label" + }, + { + "meta": { + "name": "count" + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.count" + }, + "transforms": [ + { + "code": "\treturn PrimeControls.fmt.num(value)", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "classes": "PrimeControls/Text/Big" + } + }, + "type": "ia.display.label" + }, + { + "meta": { + "name": "pct" + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.pct" + }, + "transforms": [ + { + "code": "\treturn PrimeControls.fmt.pct(value) + ' of events' if value is not None else ''", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "classes": "PrimeControls/Text/Muted" + } + }, + "type": "ia.display.label" + } + ], + "meta": { + "name": "root" + }, + "propConfig": { + "props.style.classes": { + "binding": { + "config": { + "path": "view.params.priority_name" + }, + "transforms": [ + { + "code": "\tnames = ['Diagnostic', 'Low', 'Medium', 'High', 'Critical']\n\tn = value if value in names else 'Diagnostic'\n\treturn 'PrimeControls/Card PrimeControls/PrioritySoft/' + n", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "direction": "column", + "style": { + "alignItems": "center", + "classes": "PrimeControls/Card", + "gap": "2px" + } + }, + "type": "ia.container.flex" + } +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/ScoreBar/resource.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/ScoreBar/resource.json new file mode 100644 index 0000000..276dced --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/ScoreBar/resource.json @@ -0,0 +1,10 @@ +{ + "scope": "G", + "version": 1, + "restricted": false, + "overridable": true, + "files": [ + "view.json" + ], + "attributes": {} +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/ScoreBar/view.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/ScoreBar/view.json new file mode 100644 index 0000000..48e5c38 --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/ScoreBar/view.json @@ -0,0 +1,207 @@ +{ + "custom": {}, + "params": { + "detail": null, + "key": null, + "label": null, + "score": null, + "weight": null + }, + "propConfig": { + "params.detail": { + "paramDirection": "input", + "persistent": true + }, + "params.key": { + "paramDirection": "input", + "persistent": true + }, + "params.label": { + "paramDirection": "input", + "persistent": true + }, + "params.score": { + "paramDirection": "input", + "persistent": true + }, + "params.weight": { + "paramDirection": "input", + "persistent": true + } + }, + "props": {}, + "root": { + "children": [ + { + "children": [ + { + "meta": { + "name": "lbl" + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.label" + }, + "transforms": [ + { + "code": "\treturn value or ''", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "fontSize": "13px", + "fontWeight": "bold" + } + }, + "type": "ia.display.label" + }, + { + "meta": { + "name": "score" + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.score" + }, + "transforms": [ + { + "code": "\treturn '%d' % round(value) if value is not None else 'N/A'", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "fontSize": "13px", + "fontWeight": "bold" + } + }, + "type": "ia.display.label" + } + ], + "meta": { + "name": "topRow" + }, + "props": { + "direction": "row", + "style": { + "justifyContent": "space-between" + } + }, + "type": "ia.container.flex" + }, + { + "children": [ + { + "children": [], + "meta": { + "name": "fill" + }, + "propConfig": { + "props.style.backgroundColor": { + "binding": { + "config": { + "path": "view.params.score" + }, + "transforms": [ + { + "code": "\ttry:\n\t\ts = float(value)\n\texcept:\n\t\treturn 'var(--neutral-40)'\n\tif s >= 80:\n\t\treturn '#2E9E5B'\n\tif s >= 60:\n\t\treturn '#E5C453'\n\treturn '#D64550'", + "type": "script" + } + ], + "type": "property" + } + }, + "props.style.width": { + "binding": { + "config": { + "path": "view.params.score" + }, + "transforms": [ + { + "code": "\ttry:\n\t\treturn '%d%%' % max(0, min(100, round(value)))\n\texcept:\n\t\treturn '0%'", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "direction": "row", + "style": { + "borderRadius": "4px" + } + }, + "type": "ia.container.flex" + } + ], + "meta": { + "name": "track" + }, + "position": { + "basis": "8px", + "shrink": 0 + }, + "props": { + "direction": "row", + "style": { + "backgroundColor": "var(--neutral-30)", + "borderRadius": "4px" + } + }, + "type": "ia.container.flex" + }, + { + "meta": { + "name": "detail" + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.detail" + }, + "transforms": [ + { + "code": "\treturn value or ''", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "classes": "PrimeControls/Text/Muted" + } + }, + "type": "ia.display.label" + } + ], + "meta": { + "name": "root" + }, + "props": { + "direction": "column", + "style": { + "gap": "2px", + "paddingBottom": "8px" + } + }, + "type": "ia.container.flex" + } +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/SourceRow/resource.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/SourceRow/resource.json new file mode 100644 index 0000000..276dced --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/SourceRow/resource.json @@ -0,0 +1,10 @@ +{ + "scope": "G", + "version": 1, + "restricted": false, + "overridable": true, + "files": [ + "view.json" + ], + "attributes": {} +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/SourceRow/view.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/SourceRow/view.json new file mode 100644 index 0000000..f4e38d3 --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/SourceRow/view.json @@ -0,0 +1,192 @@ +{ + "custom": {}, + "params": { + "count": 0, + "label": null, + "pct": null, + "rank": null, + "source": null + }, + "propConfig": { + "params.count": { + "paramDirection": "input", + "persistent": true + }, + "params.label": { + "paramDirection": "input", + "persistent": true + }, + "params.pct": { + "paramDirection": "input", + "persistent": true + }, + "params.rank": { + "paramDirection": "input", + "persistent": true + }, + "params.source": { + "paramDirection": "input", + "persistent": true + } + }, + "props": {}, + "root": { + "children": [ + { + "meta": { + "name": "rank" + }, + "position": { + "basis": "24px", + "shrink": 0 + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.rank" + }, + "transforms": [ + { + "code": "\treturn '#%s' % value if value is not None else ''", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "classes": "PrimeControls/Text/Muted" + } + }, + "type": "ia.display.label" + }, + { + "meta": { + "name": "lbl" + }, + "position": { + "grow": 1 + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.label" + }, + "transforms": [ + { + "code": "\treturn value or ''", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "fontSize": "13px", + "fontWeight": "bold" + } + }, + "type": "ia.display.label" + }, + { + "meta": { + "name": "count" + }, + "position": { + "basis": "56px", + "shrink": 0 + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.count" + }, + "transforms": [ + { + "code": "\treturn PrimeControls.fmt.num(value)", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "fontSize": "13px", + "fontWeight": "bold", + "textAlign": "right" + } + }, + "type": "ia.display.label" + }, + { + "meta": { + "name": "pct" + }, + "position": { + "basis": "48px", + "shrink": 0 + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.pct" + }, + "transforms": [ + { + "code": "\treturn PrimeControls.fmt.pct(value) if value is not None else ''", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "classes": "PrimeControls/Text/Muted", + "textAlign": "right" + } + }, + "type": "ia.display.label" + } + ], + "events": { + "dom": { + "onClick": { + "config": { + "script": "\tself.session.custom.PrimeControls.ui.badActorFocus = self.view.params.source\n\tself.session.custom.PrimeControls.ui.selectedTab = 2" + }, + "scope": "G", + "type": "script" + } + } + }, + "meta": { + "name": "root" + }, + "props": { + "direction": "row", + "style": { + "alignItems": "center", + "borderBottomColor": "var(--neutral-30)", + "borderBottomStyle": "solid", + "borderBottomWidth": "1px", + "cursor": "pointer", + "gap": "8px", + "paddingBottom": "4px", + "paddingTop": "4px" + } + }, + "type": "ia.container.flex" + } +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/TabButton/resource.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/TabButton/resource.json new file mode 100644 index 0000000..276dced --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/TabButton/resource.json @@ -0,0 +1,10 @@ +{ + "scope": "G", + "version": 1, + "restricted": false, + "overridable": true, + "files": [ + "view.json" + ], + "attributes": {} +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/TabButton/view.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/TabButton/view.json new file mode 100644 index 0000000..3bb1009 --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Components/TabButton/view.json @@ -0,0 +1,69 @@ +{ + "custom": {}, + "params": { + "index": 0, + "title": "Tab" + }, + "propConfig": { + "params.index": { + "paramDirection": "input", + "persistent": true + }, + "params.title": { + "paramDirection": "input", + "persistent": true + } + }, + "props": {}, + "root": { + "children": [ + { + "events": { + "dom": { + "onClick": { + "config": { + "script": "\tself.session.custom.PrimeControls.ui.selectedTab = self.view.params.index" + }, + "scope": "G", + "type": "script" + } + } + }, + "meta": { + "name": "btn" + }, + "propConfig": { + "props.style.classes": { + "binding": { + "config": { + "expression": "if({view.params.index} = {session.custom.PrimeControls.ui.selectedTab}, 'PrimeControls/Tab/Active', 'PrimeControls/Tab/Inactive')" + }, + "type": "expr" + } + }, + "props.text": { + "binding": { + "config": { + "path": "view.params.title" + }, + "type": "property" + } + } + }, + "props": {}, + "type": "ia.display.label" + } + ], + "meta": { + "name": "root" + }, + "props": { + "direction": "row", + "style": { + "alignItems": "center", + "justifyContent": "center" + } + }, + "type": "ia.container.flex" + } +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/FilterBar/resource.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/FilterBar/resource.json new file mode 100644 index 0000000..276dced --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/FilterBar/resource.json @@ -0,0 +1,10 @@ +{ + "scope": "G", + "version": 1, + "restricted": false, + "overridable": true, + "files": [ + "view.json" + ], + "attributes": {} +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/FilterBar/view.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/FilterBar/view.json new file mode 100644 index 0000000..b93af79 --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/FilterBar/view.json @@ -0,0 +1,68 @@ +{ + "custom": {}, + "params": { + "bundle": null + }, + "propConfig": { + "params.bundle": { + "paramDirection": "input", + "persistent": true + } + }, + "props": {}, + "root": { + "children": [ + { + "meta": { + "name": "ph" + }, + "props": { + "style": { + "classes": "PrimeControls/Empty" + }, + "text": "Filter bar - built in Phase 2" + }, + "type": "ia.display.label" + }, + { + "meta": { + "name": "evt" + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.bundle" + }, + "transforms": [ + { + "code": "\ttry:\n\t\treturn 'bundle: %s events in range' % value['meta']['event_count']\n\texcept:\n\t\treturn 'bundle: not loaded'", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "classes": "PrimeControls/Text/Muted" + } + }, + "type": "ia.display.label" + } + ], + "meta": { + "name": "root" + }, + "props": { + "direction": "column", + "style": { + "alignItems": "center", + "classes": "PrimeControls/Card", + "justifyContent": "center" + } + }, + "type": "ia.container.flex" + } +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/Header/resource.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/Header/resource.json new file mode 100644 index 0000000..276dced --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/Header/resource.json @@ -0,0 +1,10 @@ +{ + "scope": "G", + "version": 1, + "restricted": false, + "overridable": true, + "files": [ + "view.json" + ], + "attributes": {} +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/Header/view.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/Header/view.json new file mode 100644 index 0000000..47cf534 --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/Header/view.json @@ -0,0 +1,68 @@ +{ + "custom": {}, + "params": { + "bundle": null + }, + "propConfig": { + "params.bundle": { + "paramDirection": "input", + "persistent": true + } + }, + "props": {}, + "root": { + "children": [ + { + "meta": { + "name": "ph" + }, + "props": { + "style": { + "classes": "PrimeControls/Empty" + }, + "text": "Header - built in Phase 2" + }, + "type": "ia.display.label" + }, + { + "meta": { + "name": "evt" + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.bundle" + }, + "transforms": [ + { + "code": "\ttry:\n\t\treturn 'bundle: %s events in range' % value['meta']['event_count']\n\texcept:\n\t\treturn 'bundle: not loaded'", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "classes": "PrimeControls/Text/Muted" + } + }, + "type": "ia.display.label" + } + ], + "meta": { + "name": "root" + }, + "props": { + "direction": "column", + "style": { + "alignItems": "center", + "classes": "PrimeControls/Card", + "justifyContent": "center" + } + }, + "type": "ia.container.flex" + } +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/view.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/view.json index c7c454b..3bbfdf5 100644 --- a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/view.json +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Dashboard/view.json @@ -1,7 +1,35 @@ { - "custom": {}, + "custom": { + "bundle": null + }, + "events": { + "system": { + "onStartup": { + "config": { + "script": "\tq = self.session.custom.PrimeControls.query\n\tif not q.startMs or q.startMs == 0:\n\t\tnow = system.date.toMillis(system.date.now())\n\t\tq.endMs = now\n\t\tq.startMs = now - 8 * 3600 * 1000\n\t\tq.rangePreset = '8h'" + }, + "scope": "G", + "type": "script" + } + } + }, "params": {}, - "propConfig": {}, + "propConfig": { + "custom.bundle": { + "binding": { + "config": { + "expression": "{session.custom.PrimeControls.query.refreshToken} + '|' + {session.custom.PrimeControls.query.startMs} + '|' + {session.custom.PrimeControls.query.endMs}" + }, + "transforms": [ + { + "code": "\tq = self.session.custom.PrimeControls.query\n\tif not q.startMs or not q.endMs:\n\t\treturn None\n\treturn PrimeControls.alarms.getDashboardBundle(q.startMs, q.endMs, {'priorities': list(q.priorities), 'areas': list(q.areas), 'states': list(q.states), 'search': q.search})", + "type": "script" + } + ], + "type": "expr" + } + } + }, "props": { "defaultSize": { "height": 900, @@ -12,28 +40,291 @@ "children": [ { "meta": { - "name": "lblPlaceholder" + "name": "header" }, "position": { - "grow": 0 + "basis": "150px", + "grow": 0, + "shrink": 0 }, - "props": { - "text": "PrimeControls Dashboard - under construction (Phase 0 placeholder)", - "textStyle": { - "fontSize": "1.2em" + "propConfig": { + "props.params.bundle": { + "binding": { + "config": { + "path": "view.custom.bundle" + }, + "type": "property" + } } }, - "type": "ia.display.label" + "props": { + "params": {}, + "path": "PrimeControls/Dashboard/Header" + }, + "type": "ia.display.view" + }, + { + "meta": { + "name": "filterBar" + }, + "position": { + "basis": "56px", + "grow": 0, + "shrink": 0 + }, + "propConfig": { + "props.params.bundle": { + "binding": { + "config": { + "path": "view.custom.bundle" + }, + "type": "property" + } + } + }, + "props": { + "params": {}, + "path": "PrimeControls/Dashboard/FilterBar" + }, + "type": "ia.display.view" + }, + { + "children": [ + { + "meta": { + "name": "tabBtn0" + }, + "position": { + "basis": "140px", + "shrink": 0 + }, + "props": { + "params": { + "index": 0, + "title": "Overview" + }, + "path": "PrimeControls/Components/TabButton" + }, + "type": "ia.display.view" + }, + { + "meta": { + "name": "tabBtn1" + }, + "position": { + "basis": "140px", + "shrink": 0 + }, + "props": { + "params": { + "index": 1, + "title": "Analysis" + }, + "path": "PrimeControls/Components/TabButton" + }, + "type": "ia.display.view" + }, + { + "meta": { + "name": "tabBtn2" + }, + "position": { + "basis": "140px", + "shrink": 0 + }, + "props": { + "params": { + "index": 2, + "title": "Bad Actors" + }, + "path": "PrimeControls/Components/TabButton" + }, + "type": "ia.display.view" + }, + { + "meta": { + "name": "tabBtn3" + }, + "position": { + "basis": "140px", + "shrink": 0 + }, + "props": { + "params": { + "index": 3, + "title": "Journal" + }, + "path": "PrimeControls/Components/TabButton" + }, + "type": "ia.display.view" + } + ], + "meta": { + "name": "tabBar" + }, + "position": { + "basis": "42px", + "grow": 0, + "shrink": 0 + }, + "props": { + "direction": "row", + "style": { + "borderBottomColor": "var(--neutral-40)", + "borderBottomStyle": "solid", + "borderBottomWidth": "1px" + } + }, + "type": "ia.container.flex" + }, + { + "children": [ + { + "meta": { + "name": "tabOverview" + }, + "position": { + "grow": 1 + }, + "propConfig": { + "position.display": { + "binding": { + "config": { + "expression": "{session.custom.PrimeControls.ui.selectedTab} = 0" + }, + "type": "expr" + } + }, + "props.params.bundle": { + "binding": { + "config": { + "path": "view.custom.bundle" + }, + "type": "property" + } + } + }, + "props": { + "params": {}, + "path": "PrimeControls/Tabs/Overview" + }, + "type": "ia.display.view" + }, + { + "meta": { + "name": "tabAnalysis" + }, + "position": { + "grow": 1 + }, + "propConfig": { + "position.display": { + "binding": { + "config": { + "expression": "{session.custom.PrimeControls.ui.selectedTab} = 1" + }, + "type": "expr" + } + }, + "props.params.bundle": { + "binding": { + "config": { + "path": "view.custom.bundle" + }, + "type": "property" + } + } + }, + "props": { + "params": {}, + "path": "PrimeControls/Tabs/Analysis" + }, + "type": "ia.display.view" + }, + { + "meta": { + "name": "tabBadActors" + }, + "position": { + "grow": 1 + }, + "propConfig": { + "position.display": { + "binding": { + "config": { + "expression": "{session.custom.PrimeControls.ui.selectedTab} = 2" + }, + "type": "expr" + } + }, + "props.params.bundle": { + "binding": { + "config": { + "path": "view.custom.bundle" + }, + "type": "property" + } + } + }, + "props": { + "params": {}, + "path": "PrimeControls/Tabs/BadActors" + }, + "type": "ia.display.view" + }, + { + "meta": { + "name": "tabJournal" + }, + "position": { + "grow": 1 + }, + "propConfig": { + "position.display": { + "binding": { + "config": { + "expression": "{session.custom.PrimeControls.ui.selectedTab} = 3" + }, + "type": "expr" + } + }, + "props.params.bundle": { + "binding": { + "config": { + "path": "view.custom.bundle" + }, + "type": "property" + } + } + }, + "props": { + "params": {}, + "path": "PrimeControls/Tabs/Journal" + }, + "type": "ia.display.view" + } + ], + "meta": { + "name": "tabContent" + }, + "position": { + "grow": 1 + }, + "props": { + "direction": "column" + }, + "type": "ia.container.flex" } ], - "custom": {}, "meta": { "name": "root" }, "props": { - "alignItems": "center", "direction": "column", - "justify": "center" + "style": { + "classes": "PrimeControls/PageBg", + "gap": "10px" + } }, "type": "ia.container.flex" } diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Analysis/resource.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Analysis/resource.json new file mode 100644 index 0000000..276dced --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Analysis/resource.json @@ -0,0 +1,10 @@ +{ + "scope": "G", + "version": 1, + "restricted": false, + "overridable": true, + "files": [ + "view.json" + ], + "attributes": {} +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Analysis/view.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Analysis/view.json new file mode 100644 index 0000000..3e1220e --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Analysis/view.json @@ -0,0 +1,68 @@ +{ + "custom": {}, + "params": { + "bundle": null + }, + "propConfig": { + "params.bundle": { + "paramDirection": "input", + "persistent": true + } + }, + "props": {}, + "root": { + "children": [ + { + "meta": { + "name": "ph" + }, + "props": { + "style": { + "classes": "PrimeControls/Empty" + }, + "text": "Analysis - built in Phase 2" + }, + "type": "ia.display.label" + }, + { + "meta": { + "name": "evt" + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.bundle" + }, + "transforms": [ + { + "code": "\ttry:\n\t\treturn 'bundle: %s events in range' % value['meta']['event_count']\n\texcept:\n\t\treturn 'bundle: not loaded'", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "classes": "PrimeControls/Text/Muted" + } + }, + "type": "ia.display.label" + } + ], + "meta": { + "name": "root" + }, + "props": { + "direction": "column", + "style": { + "alignItems": "center", + "classes": "PrimeControls/Card", + "justifyContent": "center" + } + }, + "type": "ia.container.flex" + } +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/BadActors/resource.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/BadActors/resource.json new file mode 100644 index 0000000..276dced --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/BadActors/resource.json @@ -0,0 +1,10 @@ +{ + "scope": "G", + "version": 1, + "restricted": false, + "overridable": true, + "files": [ + "view.json" + ], + "attributes": {} +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/BadActors/view.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/BadActors/view.json new file mode 100644 index 0000000..70dbba3 --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/BadActors/view.json @@ -0,0 +1,68 @@ +{ + "custom": {}, + "params": { + "bundle": null + }, + "propConfig": { + "params.bundle": { + "paramDirection": "input", + "persistent": true + } + }, + "props": {}, + "root": { + "children": [ + { + "meta": { + "name": "ph" + }, + "props": { + "style": { + "classes": "PrimeControls/Empty" + }, + "text": "Bad Actors - built in Phase 2" + }, + "type": "ia.display.label" + }, + { + "meta": { + "name": "evt" + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.bundle" + }, + "transforms": [ + { + "code": "\ttry:\n\t\treturn 'bundle: %s events in range' % value['meta']['event_count']\n\texcept:\n\t\treturn 'bundle: not loaded'", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "classes": "PrimeControls/Text/Muted" + } + }, + "type": "ia.display.label" + } + ], + "meta": { + "name": "root" + }, + "props": { + "direction": "column", + "style": { + "alignItems": "center", + "classes": "PrimeControls/Card", + "justifyContent": "center" + } + }, + "type": "ia.container.flex" + } +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Journal/resource.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Journal/resource.json new file mode 100644 index 0000000..276dced --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Journal/resource.json @@ -0,0 +1,10 @@ +{ + "scope": "G", + "version": 1, + "restricted": false, + "overridable": true, + "files": [ + "view.json" + ], + "attributes": {} +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Journal/view.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Journal/view.json new file mode 100644 index 0000000..907da5c --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Journal/view.json @@ -0,0 +1,68 @@ +{ + "custom": {}, + "params": { + "bundle": null + }, + "propConfig": { + "params.bundle": { + "paramDirection": "input", + "persistent": true + } + }, + "props": {}, + "root": { + "children": [ + { + "meta": { + "name": "ph" + }, + "props": { + "style": { + "classes": "PrimeControls/Empty" + }, + "text": "Journal - built in Phase 2" + }, + "type": "ia.display.label" + }, + { + "meta": { + "name": "evt" + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.bundle" + }, + "transforms": [ + { + "code": "\ttry:\n\t\treturn 'bundle: %s events in range' % value['meta']['event_count']\n\texcept:\n\t\treturn 'bundle: not loaded'", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "classes": "PrimeControls/Text/Muted" + } + }, + "type": "ia.display.label" + } + ], + "meta": { + "name": "root" + }, + "props": { + "direction": "column", + "style": { + "alignItems": "center", + "classes": "PrimeControls/Card", + "justifyContent": "center" + } + }, + "type": "ia.container.flex" + } +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Overview/resource.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Overview/resource.json new file mode 100644 index 0000000..276dced --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Overview/resource.json @@ -0,0 +1,10 @@ +{ + "scope": "G", + "version": 1, + "restricted": false, + "overridable": true, + "files": [ + "view.json" + ], + "attributes": {} +} diff --git a/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Overview/view.json b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Overview/view.json new file mode 100644 index 0000000..0087324 --- /dev/null +++ b/ignition/gateway/projects/PrimeBAT/com.inductiveautomation.perspective/views/PrimeControls/Tabs/Overview/view.json @@ -0,0 +1,68 @@ +{ + "custom": {}, + "params": { + "bundle": null + }, + "propConfig": { + "params.bundle": { + "paramDirection": "input", + "persistent": true + } + }, + "props": {}, + "root": { + "children": [ + { + "meta": { + "name": "ph" + }, + "props": { + "style": { + "classes": "PrimeControls/Empty" + }, + "text": "Overview - built in Phase 2" + }, + "type": "ia.display.label" + }, + { + "meta": { + "name": "evt" + }, + "propConfig": { + "props.text": { + "binding": { + "config": { + "path": "view.params.bundle" + }, + "transforms": [ + { + "code": "\ttry:\n\t\treturn 'bundle: %s events in range' % value['meta']['event_count']\n\texcept:\n\t\treturn 'bundle: not loaded'", + "type": "script" + } + ], + "type": "property" + } + } + }, + "props": { + "style": { + "classes": "PrimeControls/Text/Muted" + } + }, + "type": "ia.display.label" + } + ], + "meta": { + "name": "root" + }, + "props": { + "direction": "column", + "style": { + "alignItems": "center", + "classes": "PrimeControls/Card", + "justifyContent": "center" + } + }, + "type": "ia.container.flex" + } +} diff --git a/tools/lint_project.py b/tools/lint_project.py index b39f5d0..1a53634 100644 --- a/tools/lint_project.py +++ b/tools/lint_project.py @@ -42,7 +42,9 @@ STRING_BANS = [ (re.compile(r"journalName\s*="), "hardcoded journalName= (judges' journal name unknown)"), (re.compile(r"\bsystem\.db\."), "system.db.* SQL access (journal API only)"), (re.compile(r"\bSELECT\s+", re.IGNORECASE), "raw SQL SELECT"), - (re.compile(r"""(^|[^A-Za-z0-9_])[fF]["']"""), "f-string (Jython 2.7 cannot parse)"), + # f-string prefix: f/F immediately followed by a quote, not itself preceded by a + # quote (excludes string literals like 'F') or word char (excludes identifiers). + (re.compile(r"""(^|[^A-Za-z0-9_'"])[fF]["']"""), "f-string (Jython 2.7 cannot parse)"), (re.compile(r"system\.util\.sendRequest\("), "sendRequest dev bridge leftover"), ]