Surface built-in primitive attributes in Galaxy browse
AttributesSql enumerated only the dynamic_attribute table (user-configured attributes), so engine/platform objects came back with zero attributes and extension sub-attributes (TestAlarm001.Acked, .AckMsg, ...) were missing. DiscoverHierarchy diverged badly from what System Platform's Object Viewer shows. AttributesSql now UNIONs dynamic_attribute with the built-in attributes every object inherits from its primitives (attribute_definition joined via primitive_instance). Built-in rows carry no category filter (the attribute_definition category numbering differs from dynamic_attribute's) and are never flagged is_historized/is_alarm, since those flags identify a configured attribute that anchors an extension, not the extension's leaves. dynamic_attribute wins on a reference collision. This raises the attribute surface ~7x (verified 2,026 -> 14,334 against the ZB database). AttributesSql no longer matches the OtOpcUa original; HierarchySql still does. Column shape, ordinals, proto, and generated code are unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
The gateway exposes a read-only browse surface over the AVEVA System Platform
|
||||
Galaxy Repository (the SQL Server database named `ZB`). Clients use it to
|
||||
enumerate the deployed object hierarchy and each object's dynamic attributes
|
||||
enumerate the deployed object hierarchy and each object's attributes
|
||||
before subscribing to runtime values via the existing `MxAccessGateway` RPCs.
|
||||
|
||||
This is a metadata layer: it never reads or writes runtime tag values, never
|
||||
@@ -19,8 +19,10 @@ ArchestrA IDE renders the deployment tree. Surfacing that data over gRPC lets
|
||||
remote clients build a navigable address space without any coupling to the
|
||||
COM layer or the host platform.
|
||||
|
||||
The query bodies are kept byte-for-byte identical to the equivalent OPC UA
|
||||
server in the OtOpcUa project so the two consumers see the same row sets.
|
||||
`HierarchySql` is the object-hierarchy query originally ported from the
|
||||
equivalent OPC UA server in the OtOpcUa project. `AttributesSql` has since
|
||||
diverged from OtOpcUa — see [Built-in vs configured attributes](#built-in-vs-configured-attributes)
|
||||
— and is no longer kept in sync with it.
|
||||
|
||||
## RPC Surface
|
||||
|
||||
@@ -32,7 +34,7 @@ The service is defined in
|
||||
|-----|---------|
|
||||
| `TestConnection` | Connectivity probe. Returns `{ ok: bool }` after a `SELECT 1`. Does not throw on SQL failure — returns `ok = false`. Always hits SQL directly so it remains a true health check. |
|
||||
| `GetLastDeployTime` | Returns the cached `galaxy.time_of_last_deploy`. Served from the shared hierarchy cache; refreshed in the background. |
|
||||
| `DiscoverHierarchy` | Returns one page of the deployed hierarchy plus each returned object's dynamic attributes. **Served from cache** — see [Hierarchy Cache](#hierarchy-cache). |
|
||||
| `DiscoverHierarchy` | Returns one page of the deployed hierarchy plus each returned object's attributes (configured and built-in — see [Built-in vs configured attributes](#built-in-vs-configured-attributes)). **Served from cache** — see [Hierarchy Cache](#hierarchy-cache). |
|
||||
| `WatchDeployEvents` | **Server-streaming.** The server emits the current state immediately on subscribe (so clients can bootstrap without waiting), then emits one event per detected deploy change. See [Deploy Notifications](#deploy-notifications). |
|
||||
|
||||
`DiscoverHierarchy` is a paged unary RPC. The raw request accepts `page_size`
|
||||
@@ -176,6 +178,43 @@ message DiscoverHierarchyReply {
|
||||
}
|
||||
```
|
||||
|
||||
### Built-in vs configured attributes
|
||||
|
||||
Each `GalaxyObject` carries two kinds of attribute, both surfaced the same way
|
||||
in the `attributes` list:
|
||||
|
||||
- **Configured (dynamic) attributes** — attributes added in the ArchestrA IDE
|
||||
attribute editor. Stored in the Galaxy `dynamic_attribute` table.
|
||||
- **Built-in attributes** — attributes every object inherits from its
|
||||
primitives: the object framework, the engine/platform primitives, and the
|
||||
per-attribute extensions (Alarm, History, Boolean, …). Stored in
|
||||
`attribute_definition` and reached through `primitive_instance`.
|
||||
|
||||
Built-in attributes are why an `AppEngine` or `WinPlatform` object reports its
|
||||
`Engine.*` and `Alarm*` attributes, and why an alarmed attribute such as
|
||||
`TestAlarm001` reports its extension leaves `TestAlarm001.Acked`,
|
||||
`TestAlarm001.AckMsg`, `TestAlarm001.ActiveAlarmState`, and so on. An earlier
|
||||
version of the browse query returned only configured attributes, so those
|
||||
objects came back empty or partial; including built-ins makes the browse
|
||||
surface match what System Platform's own Object Viewer shows. Expect roughly
|
||||
seven times as many attributes as configured-only — the dashboard attribute
|
||||
count reflects this.
|
||||
|
||||
Two rules govern the built-in rows:
|
||||
|
||||
- **No category filter.** `attribute_definition` uses a different
|
||||
`mx_attribute_category` numbering than `dynamic_attribute`, so only the
|
||||
`_`-prefixed-name and `.Description` exclusions apply to built-ins. (The
|
||||
configured-attribute category allow-list is unchanged.)
|
||||
- **`is_historized` / `is_alarm` are always `false` for built-in rows.** Those
|
||||
flags identify a configured attribute that *anchors* a history or alarm
|
||||
extension (e.g. `TestAlarm001`), not the extension's machinery leaves
|
||||
(`TestAlarm001.Acked`). `alarm_bearing_only` and `historized_only` therefore
|
||||
still select the anchor attributes, not their built-in children.
|
||||
|
||||
When a configured attribute and a built-in attribute resolve to the same
|
||||
reference, the configured attribute wins.
|
||||
|
||||
### Contained name vs tag name
|
||||
|
||||
Galaxy objects carry two names. `tag_name` is globally unique and is what
|
||||
@@ -219,10 +258,11 @@ GalaxyHierarchyRefreshService (BackgroundService)
|
||||
Component breakdown:
|
||||
|
||||
- `GalaxyRepository` (`src/MxGateway.Server/Galaxy/GalaxyRepository.cs`) holds
|
||||
the SQL. Its constants `HierarchySql` and `AttributesSql` are copied verbatim
|
||||
from the OtOpcUa project; do not edit them in isolation here. The two
|
||||
queries walk template-derivation and package-derivation chains via
|
||||
recursive CTEs and pick the most-derived attribute override per object.
|
||||
the SQL. Both `HierarchySql` and `AttributesSql` walk template-derivation and
|
||||
package-derivation chains via recursive CTEs and pick the most-derived
|
||||
override per object. `HierarchySql` still matches the OtOpcUa original;
|
||||
`AttributesSql` does not — it additionally enumerates built-in primitive
|
||||
attributes (see [Built-in vs configured attributes](#built-in-vs-configured-attributes)).
|
||||
- `GalaxyHierarchyCache`
|
||||
(`src/MxGateway.Server/Galaxy/GalaxyHierarchyCache.cs`) holds the most
|
||||
recent immutable `GalaxyHierarchyCacheEntry` (materialized objects +
|
||||
|
||||
Reference in New Issue
Block a user