Skip to content

i3X Support#625

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

i3X Support#625
AlexGodbehere wants to merge 77 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.
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.
Embedded in acs-i3x process, using graphology for graph traversal
and MiniSearch for BM25 text search. 12 MCP tools across search,
graph, and analysis categories. Streamable HTTP transport at
POST /mcp.
Introduces I3xRag that mirrors all I3xObjects into a graphology
undirected graph (parent-child edges) and a MiniSearch full-text
index. Exposes init(), rebuild(), nodeCount(), and edgeCount().
Includes tests with a 13-node mock object tree.
Add BM25-based full-text search over the object tree index with prefix
matching and fuzzy tolerance. searchByType adds type filtering, and
searchRelated composes search with a neighborhood stub (to be implemented
in Task 3). Exports SearchResult and SearchRelatedResult interfaces.
Implement traverse(), findPath(), compositionTree(), and replace the
neighborhood() stub with a real BFS-based implementation. traverse()
uses graphology-traversal bfsFromNode, findPath() uses
graphology-shortest-path bidirectional, and compositionTree() builds
nested trees via the objectTree children API. Unskip the searchRelated
test that depended on neighborhood().
Add relationshipMap(), typeSchema(), valueFilter(), staleValues(), and
getHistory() methods for RAG-style value analysis and type introspection.
Includes parameterised mock value cache and 11 new tests.
Register 12 MCP tools (search, graph traversal, analysis) on an McpServer
instance, all delegating to the I3xRag class. Tests use the MCP SDK
in-memory transport to verify tool listing, invocation, and error handling.
Mount a POST /mcp endpoint using the SDK's StreamableHTTPServerTransport
in stateless JSON-response mode. Each request gets a fresh transport
connected to the underlying Server, then closes it after handling.
GET and DELETE return 405 since sessions are not used.
- Add async mutex to serialise concurrent MCP requests, preventing
  "already connected" errors under concurrent load
- Log transport errors instead of silently swallowing them
- Extract duplicated mock data from rag and tools tests into
  test/helpers/mock-rag.ts
- Implement `get_values` tool for retrieving current values from cache or InfluxDB
- Add `get_values` to MCP server, updating tool count to 13
- Introduce supporting methods in I3xRag for value retrieval
- Update tests to cover `get_values` functionality and tool registration
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