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>
This commit is contained in:
134
gr/queries/attributes_extended.sql
Normal file
134
gr/queries/attributes_extended.sql
Normal file
@@ -0,0 +1,134 @@
|
||||
-- Galaxy Object Attributes/Tags for OPC UA Server
|
||||
-- Returns all runtime-readable attributes for automation objects.
|
||||
-- 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.
|
||||
--
|
||||
-- Two sources of attributes:
|
||||
-- 1. attribute_definition (via primitive_instance) — system/primitive attributes
|
||||
-- Derived from internal_runtime_attributes view logic.
|
||||
-- 2. dynamic_attribute — user-defined attributes (e.g., MachineID, MoveInFlag)
|
||||
-- Defined on templates, inherited by instances via derived_from_gobject_id chain.
|
||||
-- Requires recursive CTE to walk the template derivation hierarchy.
|
||||
--
|
||||
-- Attribute category filter (mx_attribute_category):
|
||||
-- 2-11, 24 = runtime readable attributes
|
||||
--
|
||||
-- Attribute names starting with '_' are internal/hidden and excluded.
|
||||
-- dynamic_attribute '.Description' suffixed entries are metadata, excluded.
|
||||
--
|
||||
-- Array dimensions are extracted from the mx_value hex string (bytes 5-6, little-endian
|
||||
-- uint16 at hex positions 13-16). Works for both attribute_definition and dynamic_attribute.
|
||||
--
|
||||
-- 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
|
||||
gobject_id,
|
||||
tag_name,
|
||||
primitive_name,
|
||||
attribute_name,
|
||||
full_tag_reference,
|
||||
mx_data_type,
|
||||
data_type_name,
|
||||
is_array,
|
||||
array_dimension,
|
||||
mx_attribute_category,
|
||||
security_classification,
|
||||
attribute_source
|
||||
FROM (
|
||||
-- Part 1: System/primitive attributes (from attribute_definition)
|
||||
SELECT
|
||||
g.gobject_id,
|
||||
g.tag_name,
|
||||
pi.primitive_name,
|
||||
ad.attribute_name,
|
||||
CASE WHEN pi.primitive_name = ''
|
||||
THEN g.tag_name + '.' + ad.attribute_name
|
||||
ELSE g.tag_name + '.' + pi.primitive_name + '.' + ad.attribute_name
|
||||
END + CASE WHEN ad.is_array = 1 THEN '[]' ELSE '' END
|
||||
AS full_tag_reference,
|
||||
ad.mx_data_type,
|
||||
dt.description AS data_type_name,
|
||||
ad.is_array,
|
||||
CASE WHEN ad.is_array = 1
|
||||
THEN CONVERT(int, CONVERT(varbinary(2),
|
||||
SUBSTRING(ad.mx_value, 15, 2) + SUBSTRING(ad.mx_value, 13, 2), 2))
|
||||
ELSE NULL
|
||||
END AS array_dimension,
|
||||
ad.mx_attribute_category,
|
||||
ad.security_classification,
|
||||
'primitive' AS attribute_source
|
||||
FROM gobject g
|
||||
INNER JOIN instance i
|
||||
ON i.gobject_id = g.gobject_id
|
||||
INNER JOIN template_definition td
|
||||
ON td.template_definition_id = g.template_definition_id
|
||||
AND td.runtime_clsid <> '{00000000-0000-0000-0000-000000000000}'
|
||||
INNER JOIN package p
|
||||
ON p.package_id = g.checked_in_package_id
|
||||
INNER JOIN primitive_instance pi
|
||||
ON pi.package_id = p.package_id
|
||||
AND pi.property_bitmask & 0x10 <> 0x10
|
||||
INNER JOIN attribute_definition ad
|
||||
ON ad.primitive_definition_id = pi.primitive_definition_id
|
||||
AND ad.attribute_name NOT LIKE '[_]%'
|
||||
AND ad.mx_attribute_category IN (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 24)
|
||||
LEFT JOIN data_type dt
|
||||
ON dt.mx_data_type = ad.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
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Part 2: User-defined attributes (from dynamic_attribute via template chain)
|
||||
SELECT
|
||||
g.gobject_id,
|
||||
g.tag_name,
|
||||
'' AS primitive_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,
|
||||
'dynamic' AS attribute_source
|
||||
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)
|
||||
) all_attributes
|
||||
ORDER BY tag_name, primitive_name, attribute_name;
|
||||
Reference in New Issue
Block a user