Initial commit: Wonderware / System Platform tools and reference
Five tools under one repo, all docs organized per DOCS-GUIDE.md: - aalogcli: .NET 4.8 / x86 CliFx CLI for reading System Platform binary logs (*.aaLGX) for LLM debugging, built on aaOpenSource/aaLog. Commands: last, tail, range, unread, fields. Stable JSON envelope under --llm-json. Build template under lib/build/ for rebuilding aaLogReader.dll. - aot: ArchestrA Object Toolkit 2014 v4.0 reference material. Dev guide (Markdown converted from CHM), API reference for the ArchestrA.Toolkit namespace, and the Monitor / Watchdog VS sample solutions. - graccesscli: .NET 4.8 / x86 CliFx CLI that automates Galaxy configuration via the ArchestrA GRAccess COM interop. Includes session daemon, IPC protocol, and llm-json envelope contract. - grdb: SQL/DDL exploration of the Galaxy Repository database. DDL captures, reusable queries, hierarchy / contained-name <-> tag-name translation notes. - histdb: LLM-oriented reference for AVEVA Historian retrieval. INSQL linked-server, extension tables, every wwXxx time-domain extension, every retrieval mode, alarm/event SQL recipes, REST API. Distilled from the 243-page Historian Retrieval Guide. Root contains: - CLAUDE.md: thin index pointing into each tool's README. - DOCS-GUIDE.md: doctrine for organizing docs for LLM consumption. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
-- Galaxy Object User-Defined Attributes/Tags for OPC UA Server
|
||||
-- Returns user-defined (dynamic) attributes for deployed automation objects.
|
||||
-- These are the attributes defined on templates and inherited by instances
|
||||
-- via the deployed package derivation 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.
|
||||
--
|
||||
-- Only attributes that existed at deploy time are included. The CTE walks
|
||||
-- package.derived_from_package_id starting from each instance's deployed_package_id,
|
||||
-- then joins dynamic_attribute on package_id to filter out post-deploy additions.
|
||||
-- When the same attribute appears at multiple levels, only the shallowest
|
||||
-- (most-derived) version is kept.
|
||||
--
|
||||
-- Historization detection: an attribute is historized when a primitive_instance
|
||||
-- with a matching name exists in the deployed package chain and its primitive_definition
|
||||
-- has primitive_name = 'HistoryExtension'.
|
||||
--
|
||||
-- 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 deployed_package_chain AS (
|
||||
-- Start from each deployed instance's deployed package
|
||||
SELECT
|
||||
g.gobject_id,
|
||||
p.package_id,
|
||||
p.derived_from_package_id,
|
||||
0 AS depth
|
||||
FROM gobject g
|
||||
INNER JOIN package p
|
||||
ON p.package_id = g.deployed_package_id
|
||||
WHERE g.is_template = 0
|
||||
AND g.deployed_package_id <> 0
|
||||
UNION ALL
|
||||
-- Walk up the package derivation chain
|
||||
SELECT
|
||||
dpc.gobject_id,
|
||||
p.package_id,
|
||||
p.derived_from_package_id,
|
||||
dpc.depth + 1
|
||||
FROM deployed_package_chain dpc
|
||||
INNER JOIN package p
|
||||
ON p.package_id = dpc.derived_from_package_id
|
||||
WHERE dpc.derived_from_package_id <> 0 AND dpc.depth < 10
|
||||
)
|
||||
SELECT
|
||||
gobject_id,
|
||||
tag_name,
|
||||
attribute_name,
|
||||
full_tag_reference,
|
||||
mx_data_type,
|
||||
data_type_name,
|
||||
is_array,
|
||||
array_dimension,
|
||||
mx_attribute_category,
|
||||
security_classification,
|
||||
is_historized,
|
||||
is_alarm
|
||||
FROM (
|
||||
SELECT
|
||||
dpc.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,
|
||||
CASE WHEN EXISTS (
|
||||
SELECT 1 FROM deployed_package_chain dpc2
|
||||
INNER JOIN primitive_instance pi
|
||||
ON pi.package_id = dpc2.package_id
|
||||
AND pi.primitive_name = da.attribute_name
|
||||
INNER JOIN primitive_definition pd
|
||||
ON pd.primitive_definition_id = pi.primitive_definition_id
|
||||
AND pd.primitive_name = 'HistoryExtension'
|
||||
WHERE dpc2.gobject_id = dpc.gobject_id
|
||||
) THEN 1 ELSE 0 END AS is_historized,
|
||||
CASE WHEN EXISTS (
|
||||
SELECT 1 FROM deployed_package_chain dpc2
|
||||
INNER JOIN primitive_instance pi
|
||||
ON pi.package_id = dpc2.package_id
|
||||
AND pi.primitive_name = da.attribute_name
|
||||
INNER JOIN primitive_definition pd
|
||||
ON pd.primitive_definition_id = pi.primitive_definition_id
|
||||
AND pd.primitive_name = 'AlarmExtension'
|
||||
WHERE dpc2.gobject_id = dpc.gobject_id
|
||||
) THEN 1 ELSE 0 END AS is_alarm,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY dpc.gobject_id, da.attribute_name
|
||||
ORDER BY dpc.depth
|
||||
) AS rn
|
||||
FROM deployed_package_chain dpc
|
||||
INNER JOIN dynamic_attribute da
|
||||
ON da.package_id = dpc.package_id
|
||||
INNER JOIN gobject g
|
||||
ON g.gobject_id = dpc.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 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)
|
||||
) ranked
|
||||
WHERE rn = 1
|
||||
ORDER BY tag_name, attribute_name;
|
||||
@@ -0,0 +1,196 @@
|
||||
-- Galaxy Object Attributes/Tags for OPC UA Server
|
||||
-- Returns all runtime-readable attributes for deployed 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
|
||||
-- Joined via the instance's deployed_package_id to exclude undeployed changes.
|
||||
-- 2. dynamic_attribute — user-defined attributes (e.g., MachineID, MoveInFlag)
|
||||
-- Walked via the deployed package derivation chain (package.derived_from_package_id)
|
||||
-- to only include attributes that existed at deploy time.
|
||||
-- When the same attribute appears at multiple levels (e.g., instance override and
|
||||
-- base template), only the shallowest (most-derived) version is kept.
|
||||
--
|
||||
-- Historization detection: a dynamic attribute is historized when a primitive_instance
|
||||
-- with a matching name exists in the deployed package chain and its primitive_definition
|
||||
-- has primitive_name = 'HistoryExtension'.
|
||||
--
|
||||
-- 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 deployed_package_chain AS (
|
||||
-- Start from each deployed instance's deployed package
|
||||
SELECT
|
||||
g.gobject_id,
|
||||
p.package_id,
|
||||
p.derived_from_package_id,
|
||||
0 AS depth
|
||||
FROM gobject g
|
||||
INNER JOIN package p
|
||||
ON p.package_id = g.deployed_package_id
|
||||
WHERE g.is_template = 0
|
||||
AND g.deployed_package_id <> 0
|
||||
UNION ALL
|
||||
-- Walk up the package derivation chain
|
||||
SELECT
|
||||
dpc.gobject_id,
|
||||
p.package_id,
|
||||
p.derived_from_package_id,
|
||||
dpc.depth + 1
|
||||
FROM deployed_package_chain dpc
|
||||
INNER JOIN package p
|
||||
ON p.package_id = dpc.derived_from_package_id
|
||||
WHERE dpc.derived_from_package_id <> 0 AND dpc.depth < 10
|
||||
),
|
||||
-- Rank dynamic attributes: shallowest (most-derived) wins per object + attribute
|
||||
ranked_dynamic AS (
|
||||
SELECT
|
||||
dpc.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,
|
||||
CASE WHEN EXISTS (
|
||||
SELECT 1 FROM deployed_package_chain dpc2
|
||||
INNER JOIN primitive_instance pi
|
||||
ON pi.package_id = dpc2.package_id
|
||||
AND pi.primitive_name = da.attribute_name
|
||||
INNER JOIN primitive_definition pd
|
||||
ON pd.primitive_definition_id = pi.primitive_definition_id
|
||||
AND pd.primitive_name = 'HistoryExtension'
|
||||
WHERE dpc2.gobject_id = dpc.gobject_id
|
||||
) THEN 1 ELSE 0 END AS is_historized,
|
||||
CASE WHEN EXISTS (
|
||||
SELECT 1 FROM deployed_package_chain dpc2
|
||||
INNER JOIN primitive_instance pi
|
||||
ON pi.package_id = dpc2.package_id
|
||||
AND pi.primitive_name = da.attribute_name
|
||||
INNER JOIN primitive_definition pd
|
||||
ON pd.primitive_definition_id = pi.primitive_definition_id
|
||||
AND pd.primitive_name = 'AlarmExtension'
|
||||
WHERE dpc2.gobject_id = dpc.gobject_id
|
||||
) THEN 1 ELSE 0 END AS is_alarm,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY dpc.gobject_id, da.attribute_name
|
||||
ORDER BY dpc.depth
|
||||
) AS rn
|
||||
FROM deployed_package_chain dpc
|
||||
INNER JOIN dynamic_attribute da
|
||||
ON da.package_id = dpc.package_id
|
||||
INNER JOIN gobject g
|
||||
ON g.gobject_id = dpc.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 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)
|
||||
)
|
||||
SELECT
|
||||
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,
|
||||
is_historized,
|
||||
is_alarm,
|
||||
attribute_source
|
||||
FROM (
|
||||
-- Part 1: System/primitive attributes (from attribute_definition via deployed package)
|
||||
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,
|
||||
CAST(0 AS int) AS is_historized,
|
||||
CAST(0 AS int) AS is_alarm,
|
||||
'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.deployed_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 (shallowest override from deployed package chain)
|
||||
SELECT
|
||||
gobject_id,
|
||||
tag_name,
|
||||
'' AS primitive_name,
|
||||
attribute_name,
|
||||
full_tag_reference,
|
||||
mx_data_type,
|
||||
data_type_name,
|
||||
is_array,
|
||||
array_dimension,
|
||||
mx_attribute_category,
|
||||
security_classification,
|
||||
is_historized,
|
||||
is_alarm,
|
||||
'dynamic' AS attribute_source
|
||||
FROM ranked_dynamic
|
||||
WHERE rn = 1
|
||||
) all_attributes
|
||||
ORDER BY tag_name, primitive_name, attribute_name;
|
||||
@@ -0,0 +1,8 @@
|
||||
-- Galaxy Change Detection for OPC UA Server
|
||||
-- Poll this query on an interval to detect when the hierarchy needs refreshing.
|
||||
-- When time_of_last_deploy changes, a deployment has occurred and the OPC UA
|
||||
-- server should re-run hierarchy.sql and attributes.sql to pick up changes
|
||||
-- (new/removed objects, undeployed objects, etc.).
|
||||
|
||||
SELECT time_of_last_deploy
|
||||
FROM galaxy;
|
||||
@@ -0,0 +1,89 @@
|
||||
-- Galaxy Object Hierarchy for OPC UA Server
|
||||
-- Returns the parent-child hierarchy with both browse names (contained_name) and tag names.
|
||||
-- Use browse_name for OPC UA browse tree display.
|
||||
-- Use tag_name for runtime read/write tag references.
|
||||
--
|
||||
-- Derived from internal_automation_object_model_hierarchy_view2 logic:
|
||||
-- - gobject: provides hierarchy (contained_by_gobject_id, area_gobject_id) and names
|
||||
-- - template_definition: category_id filters to automation-relevant objects
|
||||
--
|
||||
-- Category IDs:
|
||||
-- 1 = $WinPlatform (platform objects)
|
||||
-- 3 = $AppEngine (application engines)
|
||||
-- 4 = $InTouchViewApp (InTouch view apps)
|
||||
-- 10 = $UserDefined (user-defined automation objects)
|
||||
-- 11 = $FieldReference (field reference objects)
|
||||
-- 13 = $Area (areas / folders)
|
||||
-- 17 = $DIObject (DI objects)
|
||||
-- 24 = $DDESuiteLinkClient
|
||||
-- 26 = $OPCClient
|
||||
--
|
||||
-- template_chain (new): pipe-delimited list of template tag_names walking
|
||||
-- gobject.derived_from_gobject_id from the instance upward. Index 0 is the
|
||||
-- object's own immediate template, the last entry is the most ancestral
|
||||
-- template before $Object. Mirrors the deployed_package_chain CTE pattern
|
||||
-- in attributes.sql. Consumed by AlarmObjectFilter for template-based
|
||||
-- alarm filtering with wildcard support.
|
||||
|
||||
;WITH template_chain AS (
|
||||
-- Start from each non-template deployed instance's own template
|
||||
SELECT
|
||||
g.gobject_id AS instance_gobject_id,
|
||||
t.gobject_id AS template_gobject_id,
|
||||
t.tag_name AS template_tag_name,
|
||||
t.derived_from_gobject_id,
|
||||
0 AS depth
|
||||
FROM gobject g
|
||||
INNER JOIN gobject t
|
||||
ON t.gobject_id = g.derived_from_gobject_id
|
||||
WHERE g.is_template = 0
|
||||
AND g.deployed_package_id <> 0
|
||||
AND g.derived_from_gobject_id <> 0
|
||||
UNION ALL
|
||||
-- Walk up the template derivation chain
|
||||
SELECT
|
||||
tc.instance_gobject_id,
|
||||
t.gobject_id,
|
||||
t.tag_name,
|
||||
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,
|
||||
g.contained_name,
|
||||
CASE WHEN g.contained_name IS NULL OR g.contained_name = ''
|
||||
THEN g.tag_name
|
||||
ELSE g.contained_name
|
||||
END AS browse_name,
|
||||
CASE WHEN g.contained_by_gobject_id = 0
|
||||
THEN g.area_gobject_id
|
||||
ELSE g.contained_by_gobject_id
|
||||
END AS parent_gobject_id,
|
||||
CASE WHEN td.category_id = 13
|
||||
THEN 1
|
||||
ELSE 0
|
||||
END AS is_area,
|
||||
td.category_id AS category_id,
|
||||
g.hosted_by_gobject_id AS hosted_by_gobject_id,
|
||||
ISNULL(
|
||||
STUFF((
|
||||
SELECT '|' + tc.template_tag_name
|
||||
FROM template_chain tc
|
||||
WHERE tc.instance_gobject_id = g.gobject_id
|
||||
ORDER BY tc.depth
|
||||
FOR XML PATH('')
|
||||
), 1, 1, ''),
|
||||
''
|
||||
) AS template_chain
|
||||
FROM gobject g
|
||||
INNER JOIN template_definition td
|
||||
ON g.template_definition_id = td.template_definition_id
|
||||
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
|
||||
ORDER BY parent_gobject_id, g.tag_name;
|
||||
Reference in New Issue
Block a user