Skip to content

ISA-95 hierarchy editor and device import#667

Merged
AlexGodbehere merged 3 commits into
mainfrom
ago/isa95-editor
Jul 9, 2026
Merged

ISA-95 hierarchy editor and device import#667
AlexGodbehere merged 3 commits into
mainfrom
ago/isa95-editor

Conversation

@AlexGodbehere

Copy link
Copy Markdown
Contributor

ISA-95 hierarchy editor and device import

Summary

Adds a first-party editor for the ISA-95 controlled vocabulary introduced by #663, plus a one-click migration that builds the vocabulary from the free-text hierarchy values already saved on devices.

Built on #663, which merged this morning.

Changes

ISA-95 page (acs-admin/src/pages/ISA95/)

A new top-level "ISA-95" nav entry, following the same shape as the Bridges page: searchable table of all vocabulary nodes (name, level, parent, aliases) with a Level filter, and a details sidebar with edit/delete actions.

  • Create/edit dialog: level, name, parent (required for non-Enterprise levels so every node stays reachable from a root), and an alias list. Creates the ConfigDB object in the right level class, names it via the Info app, writes the ISA95Vocabulary config and links it into the parent's children.
  • Delete: unlinks the node from every parent before deleting so no vocabulary config points at a dead UUID. Nodes with children can't be deleted - prune bottom-up instead of orphaning subtrees.

Import from Devices (useISA95Migration.js, ImportISA95Dialog.vue)

Scans every device's origin map for Device_Information.ISA95_Hierarchy values, shows a preview (new nodes with their parents, aliases to add, devices skipped), then applies. Notable behaviour:

  • Scoped matching: a value only matches a node that is a child of the node matched at the level above, so "Building 1" under two different enterprises stays two nodes - no cross-tree merging.
  • Case-insensitive with alias capture: amrc matches canonical AMRC, and the typed variant is saved as an alias so the device's exact text resolves against the vocabulary.
  • Gaps: values below a missing level (an Area with no Site) can't be placed in the tree; they're counted, reported in the preview, and left on the device untouched.
  • The planning logic is a pure module with the ConfigDB writes confined to apply_plan, so it's testable without a browser.

Store (useISA95Store.js)

Exports the level-to-class map and adds a parents_of getter alongside the existing children_of.

How to test

  1. Open the new ISA-95 nav entry. With no vocabulary, you get an empty state.
  2. Create an Enterprise, then a Site under it. The parent dropdown for a Site only offers Enterprises, and is required.
  3. Open a device with hierarchy values typed in (or set some free text via the metric editor on a pre-ISA-95 hierarchy controlled vocabulary #663 build), then use Import from Devices. The preview should show the chain of new nodes; import and check the Device page's ISA-95 panel now resolves those values.
  4. Re-run the import: it should report nothing to do.
  5. Save a device value differing only in case from a canonical name, re-import: no new node, the variant appears as an alias on the existing node.
  6. Try to delete a node with children: refused with a toast. Delete a leaf: it disappears from its parent's children list in the sidebar.

Testing done

  • Migration planning/apply logic covered by a 20-case harness driven against the real module (scoped matching, alias capture and dedup, gap handling, existing-node merges, ConfigDB call contents via a mock client).
  • vite build passes; new modules confirmed present in the bundle.
  • No UI-in-browser testing - needs a review pass on a live install.

Release notes

A first-party editor for the ISA-95 hierarchy vocabulary is available under "ISA-95" in the admin UI, including a one-click import that builds the vocabulary from the hierarchy values already saved on existing devices.

The vocabulary editor needs to create objects in the right level class
and to walk the tree upwards (showing a node's parent, unlinking on
delete). Export the level-to-class map instead of keeping it local to
start(), and add a parents_of getter alongside children_of.
A new top-level ISA-95 page for authoring the controlled vocabulary
introduced by the ISA-95 hierarchy work, following the same shape as the
Bridges page: a searchable table of nodes with a details sidebar, and a
dialog for creating and editing nodes.

Nodes are ConfigDB objects in the five level classes. The dialog sets
the canonical name via the Info app and aliases via the ISA95Vocabulary
app, and links the node into its parent's children list. Deleting a
node unlinks it from every parent first so no vocabulary config is left
pointing at a dead UUID; nodes with children cannot be deleted, which
forces pruning bottom-up rather than orphaning subtrees.
Installations that predate the controlled vocabulary have free-text
hierarchy values saved on their devices. An "Import from Devices"
action on the ISA-95 page scans every device's origin map, previews the
result and builds the vocabulary from it, so nobody retypes their site
structure. The operator can then prune or rename from the editor.

Matching is scoped to the parent so the same site name under two
different enterprises stays two nodes, and it is case-insensitive:
where the typed text differs from the canonical name it is added as an
alias, which makes the device's exact value resolve to the node.
Values below a gap in a device's hierarchy (an Area with no Site)
cannot be placed in the tree; they are counted, reported and left
untouched. The planning logic is a pure module, separate from the
dialog, with the ConfigDB writes confined to apply_plan.
@AlexGodbehere AlexGodbehere merged commit adc72da into main Jul 9, 2026
1 check passed
@AlexGodbehere AlexGodbehere deleted the ago/isa95-editor branch July 9, 2026 07:59
AlexGodbehere added a commit that referenced this pull request Jul 9, 2026
# Document v6.0.0 in the release notes

## Summary

`docs/reference/release-notes.md` had not been touched since v3.4.0, so
v4 and v5 were never written up and v6 had nothing at all. This adds a
`v6.0.0` section covering the changes an operator has to act on when
upgrading from v5.1.0, and notes the v4/v5 gap rather than pretending it
isn't there.

## Why v6 is a major release

Three independently sufficient reasons, documented in that order of
importance:

1. **Nanosecond timestamps (#659).** The Sparkplug `uint64` timestamp
field now carries nanoseconds where it carried milliseconds. New code
treats values below 1e15 as milliseconds, so a v6 central service reads
a v5 edge agent fine. The reverse silently corrupts data: a v5 historian
reading a v6 edge agent writes points dated tens of millions of years in
the future. This dictates an upgrade order, which the notes spell out.
It also makes ACS deliberately non-compliant with the Sparkplug B spec,
which matters to any external consumer.
2. **Grafana authentication replaced (#646).** `auth.proxy` and the
`basic-auth` middleware are gone, Keycloak is deployed by default, and
Grafana roles now come from Factory+ permission grants.
3. **ConfigDB permissions tightened.** Creating a subclass now needs
`WriteSuperclasses` on the target class (#638), and Files needs
`ReadMembers` (#654).

Plus the v1 ConfigDB dump format being removed, and the ISA-95 hierarchy
becoming a controlled vocabulary.

## Manual upgrade steps documented

- DNS and TLS for three new external hosts (`openid`, `i3x`,
`data-access` are all enabled by default).
- Grafana role grants via `serviceSetup.config.grafanaPermissions`, and
a warning that v5 proxy-created accounts may not be reclaimed under the
new UPN-based usernames.
- `WriteSuperclasses` for any local principal that creates subclasses.
- Converting any out-of-repo v1 dumps.
- Building the ISA-95 vocabulary, now via "Import from Devices" rather
than by hand (#667).

Also covers `serviceAccountsEnabled` (#668, opt-in, no action) and
records that MetaDB ships disabled and incomplete and should stay that
way.

## Notes for the reviewer

Every factual claim was checked against the code on `main` rather than
against the PR descriptions. One claim I could **not** settle and have
flagged in the notes as something to test: whether existing Grafana user
rows created by v5's proxy auth are reclaimed by the new federated
logins, or whether those users land as fresh Viewer accounts. The
reclaim machinery exists (`oauth_allow_insecure_email_lookup`,
`email_attribute_path: preferred_username`) but only fires when the old
username matches the Kerberos UPN. Worth testing on staging before
tagging, because the failure is quiet.

`edge-openprotocol` is in the tree and the CI image build but is not in
`values.yaml` and not in the driver list, so it cannot be selected in
the Manager. I left it out of the notes; you may want to decide whether
it belongs in the v6 changelog at all.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant