Skip to content

ISA-95 hierarchy controlled vocabulary#663

Merged
AlexGodbehere merged 7 commits into
mainfrom
kp/isa95-app
Jul 9, 2026
Merged

ISA-95 hierarchy controlled vocabulary#663
AlexGodbehere merged 7 commits into
mainfrom
kp/isa95-app

Conversation

@KavanPrice

Copy link
Copy Markdown
Contributor

ISA-95 hierarchy controlled vocabulary

Summary

Introduces a controlled vocabulary system for ISA-95 hierarchy values (Enterprise -> Site -> Area -> Work Centre -> Work Unit) to prevent inconsistent free-text entries across devices.

Hierarchy nodes are first-class ConfigDB objects. Each node has a canonical name (via the existing Info app) and an optional list of aliases and child-node UUIDs (via a new ISA95Vocabulary app). The Manager UI enforces selection from this vocabulary instead of allowing free-text entry.

Changes

ConfigDB bootstrap (acs-service-setup/dumps/isa95.yaml)

  • New dump file that seeds five ISA-95 level classes (ISA95Enterprise, ISA95Site, ISA95Area, ISA95WorkCenter, ISA95WorkUnit) as members of a new ISA95Level R2 class
  • New ISA95Vocabulary app with a JSON schema defining aliases (alternative search labels) and children (UUIDs of the next level's nodes)
  • Read permission granted to all authenticated principals

Shared library (lib/js-service-client/lib/uuids.js)

  • Six new class UUIDs (ISA95Level + five level subclasses) and one new app UUID (ISA95Vocabulary) added to the shared constants

Manager UI (acs-admin)

  • useISA95Store.js - new Pinia store that reactively watches all five level classes and combines member lists with ISA95Vocabulary and Info configs via combineLatest. Exposes enterprises, by_uuid, children_of, and find_by_name (which searches both canonical name and aliases).
  • ISA95HierarchyPanel.vue - new sidebar panel on the Device page with five cascading comboboxes, one per level. Each combobox is disabled until its parent level is selected. Selecting a value clears all lower levels. Alias search is built in: typing matches against the node name and any configured aliases.
  • SchemaGroup.vue - ISA95_Hierarchy is unconditionally excluded from the schema tree so it never appears as an editable node in the left-hand metric list.
  • SparkplugMetric.vue - when a metric path resolves to an ISA-95 level field, the free-text input is replaced with the same combobox (alias search, cascade clearing) for users who navigate to those metrics directly.
  • Device.vue - wires the panel into the page; routes panel @change events to OriginMapEditor.applyISA95Selection so the save model and the tree re-render are always in sync.
  • OriginMapEditor.vue - applyISA95Selection method applies hierarchy mutations directly to this.model (the object that gets saved), bumps groupRerenderTrigger, and sets the correct null-delete values for cleared levels. The panel is only shown when the device's schema references the Device Information sub-schema.

How to test

  1. In ConfigDB, create objects of class ISA95Enterprise, ISA95Site, etc. and give each an ISA95Vocabulary config with aliases and children arrays linking them into a tree.
  2. Open a device whose schema includes the Device Information sub-schema. The ISA-95 Hierarchy panel should appear in the right sidebar.
  3. Select an Enterprise. The Site, Area, Work Centre, Work Unit dropdowns should all be disabled until their parent is set.
  4. Select a Site that has children configured. The Area dropdown should populate with those children only.
  5. Change the Site to a different value. Area and below should clear immediately, and after clicking "Save Changes" the cleared values should not reappear.
  6. Open a device with a schema that does not include Device Information. The ISA-95 Hierarchy panel should not appear.
  7. Open a fresh device (no saved origin map). Selecting an Enterprise should persist correctly and not disappear after selection.
  8. Search the combobox by alias. Typing an alias string should show the correct node even if the alias doesn't match the canonical name.

@KavanPrice KavanPrice requested a review from djnewbould June 9, 2026 16:27
@KavanPrice KavanPrice self-assigned this Jun 9, 2026
A leftover `toast.warning("Test message")` fired on every CSV import,
showing a meaningless warning to the user.
The ISA-95 levels are a controlled vocabulary, editable only through the
ISA-95 Hierarchy panel, and hidden from the metric tree. CSV import knew
nothing about this: it wrote every schema leaf it found, so exporting a
device, typing free text into the ISA-95 columns and re-importing would
bypass the vocabulary entirely.

Skip those rows before the model traversal, so an import can neither
write a hierarchy value nor create the ISA95_Hierarchy parent object as a
side effect. An empty CSV cell can no longer delete an existing value.

Export is unchanged: the columns are still emitted, so a CSV remains a
complete picture of the device. Rows whose value the CSV would have
changed are counted and reported in a toast, pointing the user at the
panel. An untouched round-trip reports nothing.
@AlexGodbehere AlexGodbehere merged commit 00e2297 into main Jul 9, 2026
1 check passed
@AlexGodbehere AlexGodbehere deleted the kp/isa95-app branch July 9, 2026 07:19
AlexGodbehere added a commit that referenced this pull request Jul 9, 2026
# 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-#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.
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.

2 participants