Skip to content

i3X Support#625

Draft
AlexGodbehere wants to merge 64 commits intomainfrom
ag/acs-i3x
Draft

i3X Support#625
AlexGodbehere wants to merge 64 commits intomainfrom
ag/acs-i3x

Conversation

@AlexGodbehere
Copy link
Copy Markdown
Contributor

Adds the acs-i3x service and an i3X Explorer page to acs-admin. This is ACS's first implementation of the i3X information model interface (spec v0.1.0).

acs-i3x service (acs-i3x/)

New Express/TypeScript service exposing i3X v0.1.0 endpoints at /v1/:

  • Explore - namespaces, object types, relationship types, objects (with hierarchy and related)
  • Query - current values (hybrid UNS cache + InfluxDB fallback) and historical values (InfluxDB Flux)
  • Subscribe - SSE streaming and sync polling for live value updates

Builds the i3X object hierarchy from ISA-95 structure in DeviceInformation originMaps. Deployed via Helm alongside existing ACS services.

graph TB
    Client["i3X Client<br/>(Explorer, etc.)"]
    
    subgraph acs-i3x["acs-i3x service"]
        OT["ObjectTree"]
        VC["ValueCache"]
        HI["History"]
        SM["SubscriptionMgr"]
    end
    
    ConfigDB["ConfigDB"]
    MQTT["MQTT Broker<br/>UNS/v1/#"]
    InfluxDB["InfluxDB"]
    
    Client -->|"REST / SSE"| acs-i3x
    OT -->|"startup"| ConfigDB
    VC -->|"runtime"| MQTT
    HI -->|"on request"| InfluxDB
    SM -->|"runtime"| MQTT
Loading

Current values use a hybrid strategy: UNS cache (real-time, sub-second) with InfluxDB last() as fallback (~10s delayed). History queries go directly to InfluxDB via Flux.

i3X Explorer in acs-admin (acs-admin/)

New /explorer page with a three-panel layout:

  • Hierarchy tree - lazy-loading ISA-95 hierarchy with expand/collapse and search filter
  • Relationship graph - interactive Cytoscape.js visualisation with configurable depth (1-3 hops) and click-to-navigate
  • Current value - live value with quality badges (Good/Uncertain/NoData/Bad), refresh, and composition component table
  • History - ECharts time series with data zoom, preset time ranges (1h/6h/24h/7d/custom), and CSV export
  • Node detail sidebar - element IDs, type, parent, namespace (all copyable) and subscribe button
  • Monitored items - navbar button with red badge, slide-over sheet with live-updating cards and sparkline trends via SSE

New packages: cytoscape, echarts, vue-echarts, @microsoft/fetch-event-source (all lazy-loaded).

Still To Do

  • Auth: Support Basic Auth first (breaking from spec, we don't support JWTs yet)
  • Live namespace updates: Use change-notify to keep the object tree current as devices change. Currently loads at startup then refreshes every 60s (or goes stale if I3X_DISABLE_REFRESH is set)
  • Explorer UI polish: First pass only. Some buttons don't work, the graph layout needs improvement. Should be able to add a device to the monitor list from the device page itself
  • ISA-95 hierarchy as a first-class concept: Need a native way to define ISA-95 hierarchy in the UI, not a nested hidden field in the Device_Information schema
  • Device_Information rethink: Currently reads from ConfigDB, but this contains pending config not yet applied. The non-address/path portion should be exported alongside the Edge Agent config. Also worth removing from Sparkplug as it's redundant and wasteful to send over MQTT continuously

Test Plan

  • Navigate to /#/explorer, verify hierarchy loads from acs-i3x
  • Expand tree nodes, verify lazy child loading
  • Select a leaf node, check current value, quality badge, timestamps
  • Select a composition, check components table and history guard message
  • Adjust relationship graph depth slider, click nodes to navigate
  • Load history with different presets, verify chart renders and zoom works
  • Export CSV, verify file downloads with correct data
  • Subscribe to a node, verify navbar badge and monitor dialog with live updates
  • Close monitor dialog, verify red badge increments on new data
  • Unsubscribe, verify cleanup

Shaped pitch for acs-i3x: a fully i3X-compliant northbound REST/SSE
API for ACS that translates i3X queries into Factory+ service calls.
Includes data model mapping, architecture, rabbit hole patches, and
a TID tracking known spec deviations and limitations.
Covers project structure, dependencies, core components (object tree,
value cache, history, subscriptions, mapping, quality), endpoint
mapping, response envelope, auth approach, env vars, and test strategy.
13-task TDD implementation plan covering: scaffold, types, mapping,
quality, envelope middleware, object tree, value cache, history,
subscriptions, API router, entry point, build verification, and
end-to-end compliance tests.
Wire ObjectTree, ValueCache, History, and SubscriptionManager behind
Express routes with i3X envelope middleware. Includes readiness check
(503), error handling, bulk query endpoints, and SSE stream delegation.
Install express and supertest as runtime and dev dependencies.
- Deployment + Service (Kerberos client+server, MQTT, InfluxDB, HTTP)
- Traefik IngressRoute for external HTTPS access
- Kerberos principals: sv1i3x (client) and HTTP/i3x (server)
- values.yaml: i3x section with enabled flag, image, logLevel
- Namespace URI defaults to https://<baseUrl>/i3x
New ServiceRole (I3X.Requirement.ServiceRole) grants:
- ConfigDB read for Registration, ConfigSchema, and Info apps
- ConfigDB class member and subclass listing
- Directory device links, alerts, and relationships
- Directory service advertisement for i3X
- UNS MQTT read/subscribe (inherited from UNS.Group.Reader)

Replaces the incorrect Historian group assignment.
When DEV_NO_AUTH=true, all endpoints are unauthenticated (no Kerberos
keytab required). Updated .env.example with local dev defaults.
- Info endpoint now at /info within infoRoute sub-router (was /)
- Bulk endpoints bypass envelope middleware to avoid double-wrapping
- Added rx-util and preserve-symlinks for local dev
- Tests need updating to match (23 failures, will fix next session)
…tions

- infoRoute now returns raw JSON (no envelope) per i3X spec
- Bulk endpoints bypass envelope via _originalJson
- Non-bulk endpoints wrapped once by envelope middleware
- All 228 tests pass
Sub-objects from UNS messages use Instance_UUIDs for parent links,
but device objects use ConfigDB UUIDs as elementIds. Added lazy
auto-detection that maps the device Instance_UUID to its ConfigDB
UUID by matching on schema type. Fixes broken parent-child nesting
in the i3X Explorer.
Previously the object tree only went as deep as the InstanceUUIDPath
(schema containers with Instance_UUIDs). Now every metric path segment
becomes an object in the tree, all the way down to the leaf tag.

Instance_UUIDs are used where available from the InstanceUUIDPath.
For segments without Instance_UUIDs (deeper than the schema containers),
deterministic v5 UUIDs are synthesised from parent UUID + segment name.

Leaf metrics (final segment) have isComposition: false.
Match any unmatched root device object instead of matching on schema
type (ConfigDB class != Sparkplug schema UUID). Fixes broken parent
chain where Axes had device Instance_UUID as parent instead of
ConfigDB UUID.
The device Instance_UUID to ConfigDB UUID mapping is permanent once
learned. Clearing it on refresh caused a race where UNS messages
arrived before loadDevices completed, creating orphaned composition
objects with broken parent chains.
The reference server at api.i3x.dev uses parentId '/' for root objects,
not null. The i3X Explorer builds the Hierarchy tree by looking for
objects with parentId '/'. Fixes 'No root objects found' in Explorer.
Logs each init step (ServiceClient, ObjectTree, ValueCache), ConfigDB
queries (class members, registration, schemas, device info), and MQTT
connection events. Helps diagnose hangs when connecting to different
ACS deployments.
Instead of using Registration app class (Sparkplug Device) as typeElementId,
now reads DeviceInformation app config for each device at startup:
- config.schema → typeElementId (the actual device schema)
- config.originMap.Instance_UUID → Instance→ConfigDB UUID mapping
- ISA95_Hierarchy (found by searching for Hierarchy-v1 Schema_UUID) →
  Enterprise/Site/Area/WorkCenter/WorkUnit hierarchy

Devices are placed under ISA-95 hierarchy at startup, not just when
UNS messages arrive. Devices without ISA-95 Enterprise stay at root.
Recursively walks each device's originMap to create the complete object
hierarchy (containers + leaf metrics) at startup. No longer dependent
on UNS messages for the structure — offline devices show their full
metric tree.

- Keys with Sparkplug_Type and no children → leaf objects
- Keys with child containers → composition objects
- Instance_UUID used where available, v5 synthesised otherwise
- Schema_UUIDs collected to create ObjectTypes for all schemas
- Plain value fields (no Sparkplug_Type, no children) are skipped
Update test mocks to match the refactored History class which now uses
getMetricMeta/getInstanceUuid from ObjectTree instead of elementId
string parsing, and getCurrentValue/getCompositionValue instead of
valueCache.getValue for current value lookups.
Current values now come from InfluxDB (sparkplug/default bucket) using
last() queries instead of the in-memory UNS MQTT cache. This means:
- Values available for ALL devices with historian data, not just those
  publishing to UNS
- Consistent data source for both current values and history
- MetricMeta stored per leaf object during tree building (topLevelInstance,
  metricPath, metricName, sparkplugType/typeSuffix)
- Composition values assembled from descendant leaf queries

UNS MQTT cache retained only for SSE streaming subscriptions (TID L3).
Current value queries now check the UNS MQTT cache first for real-time
data. If the cache has a value (device is publishing to UNS), it's
returned immediately — no InfluxDB round-trip, no 10s flush delay.

If the cache misses (device not on UNS, or no data yet), falls back
to InfluxDB last() query from the sparkplug bucket.

Same UNS MQTT connection continues to feed SSE streaming subscriptions.
Logs whether current values come from UNS cache (with age) or InfluxDB.
Logs subscription registration, stream open/close, and each SSE event
write. Logs UNS cache updates and listener notifications.
Return empty array for composition objects instead of querying all
child metrics mixed together. Only leaf metrics (with MetricMeta)
return history. Matches the reference implementation behaviour.
Shaped pitch for embedding i3X hierarchy browsing, node inspection,
relationship graph, history charts, and live monitoring into the
acs-admin Vue UI. Big batch, 6-week appetite.
Detailed component architecture, data flow, and implementation design
for the Explorer feature in acs-admin. Covers hierarchy tree, relationship
graph (Cytoscape.js), history charts (ECharts), current value display,
and monitored items with SSE subscriptions.
Detailed step-by-step plan covering: dependency setup, API client,
stores, page shell, tree, sidebar, current value, relationship graph,
history charts, monitor store, SSE, and navbar integration.
Connect RelationshipGraph, CurrentValue, and HistoryPanel into
the Explorer page main panel. Add MonitorButton (navbar, red badge)
and MonitorDialog (Sheet with cards, sparklines, unsubscribe).
Wire beforeunload cleanup for active subscriptions.
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