I was wrong. AVEVA Tech Note 537 ("Creating an Application Object Script
Using GRAccess", April 2008) documents the supported pattern:
ConfigurableAttributes[<script>.<field>].SetValue(MxValue) inside a
CheckOut/Save/CheckIn cycle. graccesscli's existing
FindAttributeForMutation already follows this — writes to MxCategoryPackageOnly_Lockable
script-text fields persist correctly.
The earlier "writeback gap" diagnosis was a phantom caused by a reader-side
issue. `object attribute value get` against a script body returns
"Supported: False / Attribute value is not exposed" because
MxValueDetails uses a case-sensitive `ReadProperty(attr, "Value")` lookup
plus an accessor probe (GetBoolean -> GetInteger -> GetFloat -> GetDouble
-> GetString) that can fall through silently for some MxValue shapes. The
COM-side property is exposed as `value` (lowercase), readable as
`attr.value.GetString()` -- which the live probe at
`analysis/ide-edit-investigation/probe_setvalue/` does and confirms the
post-write content matches the marker exactly.
Live verification on $TestMachine.UpdateTestChangingInt.DeclarationsText
and $DelmiaReceiver.ProcessRecipe.{ExecuteText,DeclarationsText}:
=== verdict ===
marker landed on same-proxy ConfigurableAttributes: True
marker landed on same-proxy Attributes : True
marker landed on fresh-proxy ConfigurableAttributes: True
marker landed on fresh-proxy Attributes : True
The probe also confirmed that two earlier graccesscli `object scripts set`
invocations (which I had wrongly believed failed) had persisted -- the
marker text I wrote previously was still on disk in
ProcessRecipe.{ExecuteText,DeclarationsText} when read directly via
attr.value.GetString(). The probe restored both fields to their original
values.
This commit:
- Updates the misleading [Command(...)] / [CommandOption(...)]
descriptions in GRAccessSurfaceCommands.cs back to honest versions
citing TN-537.
- Restores the --file-using examples for `object scripts set` and
`object scripts create` across script-editing.md, llm-integration.md,
usage.md, and zb-testmachine.md.
- Removes the test that asserted the (wrong) EnsureMutableViaSetValue
guard. Re-aims ScriptCommandDescriptions_… at the corrected wording.
- Removes two leftover EnsureMutableViaSetValue calls in the trigger-period
/ trigger-type write paths (both targeted MxCategoryWriteable_C_Lockable
attributes; would never have fired even if the helper still existed).
- Adds analysis/ide-edit-investigation/REPORT.md (replacing the earlier
wrong report) plus the probe sources under probe_setvalue/.
The MxValueDetails reader gap (case-sensitive ReadProperty + accessor
probe) is a real follow-up: `object attribute value get` should
case-insensitively read `value` and try GetString first when the
underlying MxValue.DataType is MxString. Out of scope here -- that's a
separate, smaller fix.
Test count delta: 67 -> 66 (-2 wrong tests, +1 corrected description test).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
12 KiB
Editing Scripts And Script Libraries
This guide describes how to edit scripts related to a template such as TestMachine.
Read script-parsing.md first so you know whether the target script is a script library, an object-level script-like attribute, an extension primitive, or content that only appears in an exported object package.
Run commands from graccess_cli. Examples assume an active session:
graccess session start --galaxy ZB --node .
Without a session, add --node . to each command.
For LLM-driven script work, read script metadata and validate guarded edits with the LLM envelope:
graccess object scripts list --galaxy ZB --name TestMachine --type template --llm-json
graccess object scripts get --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --llm-json
graccess object scripts set --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --file .\UpdateTestChangingInt.txt --confirm --confirm-target TestMachine --dry-run --llm-json
What The CLI Can Edit Today
| Area | Current command support |
|---|---|
| Script library inventory | script-library list |
| Script library export | script-library export |
| Script library import/add | script-library import, script-library add, galaxy import-script-library |
| Object script metadata | object scripts list, object scripts get |
| Object script body read/write | object scripts get (package fallback) and object scripts set --file <path> [--field <name>] for body fields like ExecuteText / DeclarationsText / StartupText / ShutdownText / OnScanText / OffScanText / Expression. See AVEVA Tech Note 537. |
| Object script mutable settings | object scripts settings set --trigger-type, --trigger-period-ms |
| Object script create/delete | object scripts create, object scripts delete |
| Script-like attributes | object attribute set, lock, security, buffer |
| Extension primitives | object extension add/delete/rename |
| Full object package script payloads | Use objects export and galaxy import-objects |
Export Before Editing
Export script libraries before changing them:
$out = '.\template-snapshots\script-libraries-before'
New-Item -ItemType Directory -Force -Path $out | Out-Null
$libs = graccess script-library list --galaxy ZB --json | ConvertFrom-Json
foreach ($lib in $libs) {
$name = $lib.Name
$path = Join-Path $out "$name.aaslib"
graccess script-library export --galaxy ZB --name $name --output $path --confirm --confirm-target $path
}
Export the template package when object-level scripts may be involved:
$pkg = '.\template-snapshots\TestMachine-before\TestMachine.aaPKG'
graccess objects export --galaxy ZB --type template --name TestMachine --output $pkg --confirm --confirm-target $pkg
Edit Script Libraries
Script library content is edited outside the CLI in the .aaslib file or source toolchain that creates it. Import the updated library after review:
graccess script-library import --galaxy ZB --path '.\scripts\CommonScripts.aaslib' --confirm --confirm-target '.\scripts\CommonScripts.aaslib'
The galaxy-level import command is also available:
graccess galaxy import-script-library --galaxy ZB --path '.\scripts\CommonScripts.aaslib' --confirm --confirm-target '.\scripts\CommonScripts.aaslib'
After import, list libraries again:
graccess script-library list --galaxy ZB --json
Edit Script-Like Attributes
Some templates store expressions, declarations, triggers, or script fragments in attributes. Find candidates:
$attrs = graccess object attributes --galaxy ZB --name TestMachine --type template --json | ConvertFrom-Json
$extended = graccess object extended-attributes --galaxy ZB --name TestMachine --type template --json | ConvertFrom-Json
$scripts = (@($attrs) + @($extended)) | Where-Object {
$_.Name -match '(?i)script|execute|trigger|expression|declaration|startup|shutdown|scan'
} | Sort-Object Name -Unique
$scripts | Select-Object Name, DataType, Category, Locked
If the script-like setting is a scalar writable attribute, edit it through the normal template edit flow. Script extension attributes are looked up through ConfigurableAttributes first, then Attributes — the pattern AVEVA Tech Note 537 ("Creating an Application Object Script Using GRAccess") prescribes for ScriptExtension text fields. object scripts set, object scripts settings set, and object attribute value set all use that ordering. A bare --script <name> writes to <name>.ExecuteText; pass --field <FieldName> (or include the suffix in --script) to target DeclarationsText, StartupText, ShutdownText, OnScanText, OffScanText, or Expression.
graccess object checkout --galaxy ZB --name TestMachine --type template --confirm --confirm-target TestMachine
graccess object scripts set --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --file .\UpdateTestChangingInt.txt --confirm --confirm-target TestMachine
graccess object save --galaxy ZB --name TestMachine --type template --confirm --confirm-target TestMachine
graccess object checkin --galaxy ZB --name TestMachine --type template --comment 'Update script body' --confirm --confirm-target TestMachine
Reader caveat.
object attribute value getagainst a script text field may reportSupported: False("Attribute value is not exposed by this GRAccess attribute") even when the value is set. The write itself persists; the limitation is inMxValueDetailsreading the COM-sidevalueaccessor for these field types. Verify post-write content withobject scripts get --llm-json(which uses the package-export readback) instead. Round-trip evidence is inanalysis/ide-edit-investigation/probe_setvalue/(commite4e5425..).
Update periodic script settings and lock the interval for deployment inheritance:
graccess object checkout --galaxy ZB --name '$TestMachine' --type template --confirm --confirm-target '$TestMachine'
graccess object scripts settings set --galaxy ZB --name '$TestMachine' --type template --script UpdateTestChangingInt --trigger-period-ms 500 --lock-trigger-period --confirm --confirm-target '$TestMachine'
graccess object save --galaxy ZB --name '$TestMachine' --type template --confirm --confirm-target '$TestMachine'
graccess object checkin --galaxy ZB --name '$TestMachine' --type template --comment 'Set UpdateTestChangingInt interval to 500ms' --confirm --confirm-target '$TestMachine'
Verify with package-backed readback:
graccess object scripts get --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --llm-json
Edit Extension Primitives
When a script is represented by an extension primitive, use the extension commands for the primitive lifecycle:
graccess object checkout --galaxy ZB --name TestMachine --type template --confirm --confirm-target TestMachine
graccess object extension add --galaxy ZB --name TestMachine --type template --extension-type ScriptExtension --primitive OnScan --object-extension --confirm --confirm-target TestMachine
graccess object save --galaxy ZB --name TestMachine --type template --confirm --confirm-target TestMachine
graccess object checkin --galaxy ZB --name TestMachine --type template --comment 'Add script extension primitive' --confirm --confirm-target TestMachine
Rename:
graccess object extension rename --galaxy ZB --name TestMachine --type template --extension-type ScriptExtension --primitive OnScan --new-name OnScan2 --object-extension --confirm --confirm-target TestMachine
Delete:
graccess object extension delete --galaxy ZB --name TestMachine --type template --extension-type ScriptExtension --primitive OnScan2 --object-extension --confirm --confirm-target TestMachine
These commands manage primitive structure. The wrapper below creates the same ScriptExtension primitive and can initialize the body and common settings in the same checkout flow (per AVEVA TN-537: AddExtensionPrimitive → Save → set body/settings via ConfigurableAttributes):
graccess object checkout --galaxy ZB --name '$MyTemplate' --type template --confirm --confirm-target '$MyTemplate'
graccess object scripts create --galaxy ZB --name '$MyTemplate' --type template --script OnScan --file .\OnScan.txt --trigger-type Periodic --trigger-period-ms 1000 --confirm --confirm-target '$MyTemplate'
graccess object save --galaxy ZB --name '$MyTemplate' --type template --confirm --confirm-target '$MyTemplate'
graccess object checkin --galaxy ZB --name '$MyTemplate' --type template --comment 'Add OnScan script' --confirm --confirm-target '$MyTemplate'
After adding a ScriptExtension primitive, set its body with object scripts set --script <primitiveName> (defaults to ExecuteText) or target a different field with --field DeclarationsText / StartupText / etc.
Full-Fidelity Object Script Edits
When script bodies are only available inside the object package, use export/import:
- Export the template package.
- Edit with the supported vendor tooling or package workflow.
- Import the updated package with explicit confirmation.
- Re-parse and validate the template.
Export:
$pkg = '.\template-work\TestMachine.aaPKG'
graccess objects export --galaxy ZB --type template --name TestMachine --output $pkg --confirm --confirm-target $pkg
Import:
graccess galaxy import-objects --galaxy ZB --file '.\template-work\TestMachine.aaPKG' --overwrite --confirm --confirm-target '.\template-work\TestMachine.aaPKG'
For conflict-aware import:
graccess galaxy import-objects-ex --galaxy ZB --file '.\template-work\TestMachine.aaPKG' --version-conflict '<E_RESOLVE_VERSION_CONFLICT_ACTION>' --name-conflict '<E_RESOLVE_NAME_CONFLICT_ACTION>' --confirm --confirm-target '.\template-work\TestMachine.aaPKG'
The exact enum values for import conflict handling must match the local GRAccess interop assembly. See graccess_operations.md and graccess_documentation.md.
Validate Script Edits
After import or checkin:
graccess object get --galaxy ZB --name TestMachine --type template --json
graccess object attributes --galaxy ZB --name TestMachine --type template --json
graccess object extended-attributes --galaxy ZB --name TestMachine --type template --json
For runtime validation, instantiate a test object and deploy only that explicitly named test instance:
graccess template instantiate --galaxy ZB --name TestMachine --type template --new-name TestMachine_ScriptTest_001 --create-contained --confirm --confirm-target TestMachine
graccess instance deploy --galaxy ZB --name TestMachine_ScriptTest_001 --type instance --confirm --confirm-target TestMachine_ScriptTest_001
Supported Object Script Command Pattern
graccess object scripts list --galaxy ZB --name TestMachine --type template --json
graccess object scripts get --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --llm-json
graccess object scripts set --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --file .\UpdateTestChangingInt.txt --confirm --confirm-target TestMachine --llm-json
The local GRAccess examples sometimes show Template.Scripts[index].ScriptString, but this repository's installed ArchestrA.GRAccess.dll does not expose a public object script collection — it exposes script extension projections as attributes (e.g. UpdateTestChangingInt.ExecuteText). The CLI writes those attributes through the AVEVA TN-537 pattern: IgObject.ConfigurableAttributes[<script>.<field>].SetValue(MxValue), surrounded by CheckOut → Save → CheckIn. Reads use the package-export fallback when the GRAccess attribute-value accessor doesn't surface the field directly.