-- 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;