Files
lmxopcua/gr/queries/attributes.sql
Joseph Doherty a7576ffb38 Implement LmxOpcUa server — all 6 phases complete
Full OPC UA server on .NET Framework 4.8 (x86) exposing AVEVA System
Platform Galaxy tags via MXAccess. Mirrors Galaxy object hierarchy as
OPC UA address space, translating contained-name browse paths to
tag-name runtime references.

Components implemented:
- Configuration: AppConfiguration with 4 sections, validator
- Domain: ConnectionState, Quality, Vtq, MxDataTypeMapper, error codes
- MxAccess: StaComThread, MxAccessClient (partial classes), MxProxyAdapter
  using strongly-typed ArchestrA.MxAccess COM interop
- Galaxy Repository: SQL queries (hierarchy, attributes, change detection),
  ChangeDetectionService with auto-rebuild on deploy
- OPC UA Server: LmxNodeManager (CustomNodeManager2), LmxOpcUaServer,
  OpcUaServerHost with programmatic config, SecurityPolicy None
- Status Dashboard: HTTP server with HTML/JSON/health endpoints
- Integration: Full 14-step startup, graceful shutdown, component wiring

175 tests (174 unit + 1 integration), all passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 05:55:27 -04:00

64 lines
2.6 KiB
SQL

-- Galaxy Object User-Defined Attributes/Tags for OPC UA Server
-- Returns user-defined (dynamic) attributes for automation objects.
-- These are the attributes defined on templates and inherited by instances
-- via the derived_from_gobject_id chain (e.g., MachineID, MoveInFlag).
--
-- Use full_tag_reference for read/write operations against the runtime.
-- Join with hierarchy.sql results on gobject_id to place attributes in the OPC UA browse tree.
--
-- For system/primitive attributes as well, see attributes_extended.sql.
--
-- Array dimensions are extracted from the mx_value hex string on the template's
-- dynamic_attribute row (bytes 5-6, little-endian uint16 at hex positions 13-16).
--
-- Data types (mx_data_type):
-- 1 = Boolean, 2 = Integer (Int32), 3 = Float (Single), 4 = Double,
-- 5 = String, 6 = Time (DateTime), 7 = ElapsedTime (TimeSpan),
-- 8 = (reference), 13 = (enumeration), 14 = (custom), 15 = InternationalizedString, 16 = (custom)
;WITH template_chain AS (
-- Start from each non-template instance
SELECT g.gobject_id, g.derived_from_gobject_id, 0 AS depth
FROM gobject g
WHERE g.is_template = 0
UNION ALL
-- Walk up the template derivation chain
SELECT tc.gobject_id, t.derived_from_gobject_id, tc.depth + 1
FROM template_chain tc
INNER JOIN gobject t ON t.gobject_id = tc.derived_from_gobject_id
WHERE tc.derived_from_gobject_id <> 0 AND tc.depth < 10
)
SELECT DISTINCT
g.gobject_id,
g.tag_name,
da.attribute_name,
g.tag_name + '.' + da.attribute_name
+ CASE WHEN da.is_array = 1 THEN '[]' ELSE '' END
AS full_tag_reference,
da.mx_data_type,
dt.description AS data_type_name,
da.is_array,
CASE WHEN da.is_array = 1
THEN CONVERT(int, CONVERT(varbinary(2),
SUBSTRING(da.mx_value, 15, 2) + SUBSTRING(da.mx_value, 13, 2), 2))
ELSE NULL
END AS array_dimension,
da.mx_attribute_category,
da.security_classification
FROM template_chain tc
INNER JOIN dynamic_attribute da
ON da.gobject_id = tc.derived_from_gobject_id
INNER JOIN gobject g
ON g.gobject_id = tc.gobject_id
INNER JOIN template_definition td
ON td.template_definition_id = g.template_definition_id
LEFT JOIN data_type dt
ON dt.mx_data_type = da.mx_data_type
WHERE td.category_id IN (1, 3, 4, 10, 11, 13, 17, 24, 26)
AND g.is_template = 0
AND g.deployed_package_id <> 0
AND da.attribute_name NOT LIKE '[_]%'
AND da.attribute_name NOT LIKE '%.Description'
AND da.mx_attribute_category IN (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 24)
ORDER BY g.tag_name, da.attribute_name;