title: "Display Construction — Canvas, Dashboard, Navigation Dynamics, Symbols & SymbolsCodeBehind" tags: [display, canvas, dashboard, symbol, navigation, hmi, ui, layout, dynamics, codebehind] description: "Build displays from scratch: Canvas and Dashboard layouts, symbol placement , asset navigation, CodeBehind, and ActionDynamic click handlingwith SymbolLabel binding, visual dynamics (color, visibility, rotation), ActionDynamic click handling, CodeBehind for display logic, and asset-driven navigation" version: "12.0" author: "Tatsoft"
...
Build complete FrameworX displays: choose between Canvas and Dashboard layouts, place controls and symbols with proper tag binding, wire up visual dynamics (color, visibility, rotation based on tag values), configure ActionDynamic for navigation, configure write CodeBehind for display lifecycle events, and set up asset-driven navigation patterns.
...
Do Do NOT use when:
get_objects + write_objects)list_elementsa basic starter dashboard (the New Solution skill covers that)...
| Category | Items |
|---|---|
| Tools | get_table_schema, write_objects, get_objects, list_elements, list_dynamics, browse_runtime_properties |
| Tables | DisplaysList, DisplaysSymbols, DisplaysLayouts |
Critical: Use DisplaysList for creating and editing displays. DisplaysDraw is the Designer visual editor UI — NOT a writable table.
Fetch the display schema and decide Canvas vs Dashboard:
get_table_schema('DisplaysList')
list_elements('Canvas') or list_elements('Dashboard')
Canvas — absolute positioning (Left/Top/Width/Height). Use for:
Dashboard — responsive grid (Columns/Rows/Cells). Use for:
PanelType property is REQUIRED — omitting it silently defaults to Canvas, which causes confusing results if you intended a Dashboard.
Query element schemas for available controls and their properties:
Before building any display, use these tools to discover what's available:
list_elements() -- all element types by category + library folders
list_dynamics() -- ALL dynamic types by category (Color, Visibility, Action, Animation, etc.)
list_elements('Symbol/HMI') -- browse symbol library
list_elements('Canvas') -- Canvas display structurelist_elements('TextBlock') -- specific control schema
list_elements('CircularGaugeDashboard') -- specificDashboard display controlstructure schema
list_
...
dynamics() with no parameter is the door to the entire dynamics system — it lists every available dynamic type organized by category with minimal examples.
Fetch the display schema and decide Canvas vs Dashboard:
get_table_schema('DisplaysList')
list_elements('Canvas') or list_elements('Dashboard')
Canvas — absolute positioning (Left/Top/Width/Height). Use for:
Dashboard — responsive grid (Columns/Rows/Cells). Use for:
PanelType property is REQUIRED — omitting it silently defaults to Canvas, which causes confusing results if you intended a Dashboard.
Canvas uses absolute positioning with Elements array. Each element needs Left, Top, Width, Height.
list_elements('TextBlock,CircularGauge,Rectangle')
list_elements('Shapes') -- all drawing primitives
list_elements('Interaction') -- buttons, inputs, toggles
list_elements('Charts') -- TrendChart, BarChart, PieChart
list_elements('Gauges') -- CircularGauge, LinearGauge, BulletGauge
Batch lookups with comma-separated names:
list_elements('TrendChart,CircularGauge,TextBlock')
A concrete Canvas display with shapes, text, a symbol, and a dynamic:
{
"table_type": "DisplaysList",
"data": [{
"ObjectName": "ProcessView",
"PanelType": "Canvas",
"Size": "1366 x 728",
"OnResize": "StretchFill",
"Elements": [
{
"Type": "TextBlock",
"Text": "Process Overview",
"Left": 50, "Top": 20, "Width": 300, "Height": 30,
"FontSize": 22
},
{
"Type": "Rectangle",
"Left": 50, "Top": 80, "Width": 200, "Height": 150,
"Dynamics": [
{
"Type": "FillColorDynamic",
"LinkedValue": "@Tag.Plant/Tank1/ValveOpen",
"ChangeColorItems": {
"Type": "ColorChangeList",
"Children": [
{ "Type": "ChangeColorItem", "ChangeLimit": 0, "LimitColor": "#FF808080" },
{ "Type": "ChangeColorItem", "ChangeLimit": 1, "LimitColor": "#FF4CAF50" }
]
}
}
]
},
{
"Type": "TextBlock",
"LinkedValue": "{@Tag.Plant/Tank1/Level.Value}",
"Left": 100, "Top": 160, "Width": 100, "Height": 25
},
{
"Type": "Symbol",
"SymbolName": "Wizard/PUMP",
"Left": 300, "Top": 120, "Width": 80, "Height": 80,
"SymbolLabels": [
{ "Type": "SymbolLabel", "Key": "State", "LabelName": "State", "LabelValue": "@Tag.Plant/Pump1/Running.Value", "FieldType": "Expression" }
]
},
{
"Type": "CircularGauge",
"LinkedValue": "@Tag.Plant/Tank1/Pressure.Value",
"Left": 450, "Top": 80, "Width": 180, "Height": 160,
"Minimum": 0, "Maximum": 100
}
]
}]
}
Key Canvas rules:
Size: "1366 x 728" for standard content areaOnResize: "StretchFill" scales proportionally to fill the layout regionDashboard uses a grid with DashboardDisplay (columns/rows definition) and Cells array.
list_elements('Dashboard')
{
"table_type": "DisplaysList",
"data": [{
"ObjectName": "MainPage",
"PanelType": "Dashboard",
"DashboardDisplay": {
"Columns": ["*", "*", "*"],
"Rows": ["*", "*"]
},
"Cells": [
{
"Row": 0, "Col": 0,
"Cell": { "HeaderLink": "Tank 1 Level" },
"Content": {
"Type": "CircularGauge",
"LinkedValue": "@Tag.Plant/Tank1/Level.Value",
"Minimum": 0, "Maximum": 100
}
},
{
"Row": 0, "Col": 1,
"Cell": { "HeaderLink": "Tank 1 Temp" },
"Content": {
"Type": "CircularGauge",
"LinkedValue": "@Tag.Plant/Tank1/Temperature.Value",
"Minimum": 0, "Maximum": 100
}
},
{
"Row": 0, "Col": 2,
"Cell": { "HeaderLink": "Trend" },
"Content": {
"Type": "TrendChart",
"Duration": "5m",
"Pens": {
"Type": "TrendPenList",
"Children": [
{ "Type": "TrendPen", "LinkedValue": "@Tag.Plant/Tank1/Level", "PenLabel": "Level", "Stroke": "#FF2196F3", "Auto": true }
]
}
}
},
{
"Row": 1, "Col": 0, "ColSpan": 3,
"Cell": { "HeaderLink": "Equipment Status" },
"Content": {
"Type": "Symbol",
"SymbolName": "Wizard/TANK",
"SymbolLabels": [
{ "Type": "SymbolLabel", "Key": "Value", "LabelName": "Value", "LabelValue": "@Tag.Plant/Tank1/Level.Value", "FieldType": "Expression" }
]
}
}
]
}]
}
Key Dashboard rules:
DashboardDisplay defines the grid: Columns and Rows arrays (use "*" for proportional sizing)Cell references Row/Col (0-based) and contains a Content elementColSpan/RowSpan for multi-cell elementsCell.HeaderLink adds a header label to the cellBrowse available symbols:
list_elements('Symbol') -- all symbol categories
list_elements('Symbol/HMI') -- HMI symbol library
list_elements('Symbol/HMI/Equipment') -- equipment symbols
list_elements('Library') -- all library folders
list_elements('Wizard') -- Wizard symbol schema
Symbols are reusable components with parameterized inputs. When you place a symbol, you use SymbolLabels to connect the symbol's internal @Label. parameters to actual @Tag. values. Here's how the mapping works:
@Label.State, @Label.Speed, etc.@Label. key to a real @Tag. value.Concrete example — placing a Pump symbol:
{
"Type": "Symbol",
"SymbolName": "Wizard/PUMP",
"Left": 200, "Top": 300, "Width": 80, "Height": 80,
"SymbolLabels": [
{ "Type": "SymbolLabel", "Key": "State", "LabelName": "State", "LabelValue": "@Tag.Plant/Pump1/Running.Value", "FieldType": "Expression" },
{ "Type": "SymbolLabel", "Key": "Speed", "LabelName": "Speed", "LabelValue": "@Tag.Plant/Pump1/Speed.Value", "FieldType": "Expression" }
]
}
The Key matches the symbol's internal @Label. name. The LabelValue provides the actual tag binding using @Tag. prefix. The symbol internally resolves @Label.State → the value of @Tag.Plant/Pump1/Running.Value.
CRITICAL: Use @Tag. bindings in SymbolLabels. NEVER use @Label. when placing symbols in displays — @Label. is only for symbol internal definitions (in DisplaysSymbols).
WizardSymbols — always available, no import needed: TANK, VALVE, PUMP, MOTOR, BLOWER. Place with SymbolName: "Wizard/PUMP". These provide easy customization through SymbolLabels for common industrial equipment.
Library symbols (~1,600 available) auto-import when referenced by SymbolName. No manual import step needed — just reference the path (e.g., "HMI/Equipment/CentrifugalPump").
Sizing: Symbols are vector-based and scale to any proportional size. Use 40x40 for compact, 80x80 for medium, 120x120 for large — the library default is just a starting point.
Dynamics attach runtime behaviors to ANY element. They decouple the visual effect from the data binding, so any property of any control can become live.
Discovery: Call list_dynamics() with no parameter to see all available dynamic types, or narrow by category:
list_dynamics() -- all dynamics by category
list_dynamics('Color') -- color-changing dynamics
list_dynamics('Visibility') -- show/hide dynamics
list_dynamics('Animation') -- rotation, position, scaling
list_dynamics('Action') -- click/navigation dynamics
list_dynamics('FillColorDynamic') -- full schema for a specific dynamic
The most common visual dynamic. Changes an element's fill color based on a tag value.
Pattern: A Dynamics array on the element, containing one or more dynamic objects. Each dynamic has a LinkedValue (data source) and behavior definition.
{
"Type": "Rectangle",
"Left": 100, "Top": 200, "Width": 60, "Height": 60,
"Dynamics": [
{
"Type": "FillColorDynamic",
"LinkedValue": "@Tag.Plant/Pump1/Running",
"ChangeColorItems": {
"Type": "ColorChangeList",
"Children": [
{ "Type": "ChangeColorItem", "ChangeLimit": 0, "LimitColor": "#FF808080" },
{ "Type": "ChangeColorItem", "ChangeLimit": 1, "LimitColor": "#FF00FF00" }
]
}
}
]
}
This makes the rectangle gray when Running = 0 (stopped) and green when Running = 1 (running). The ChangeLimit values define thresholds — the color applies when the tag value reaches that limit.
For analog values (temperature, pressure), use graduated thresholds:
{
"Type": "FillColorDynamic",
"LinkedValue": "@Tag.Plant/Tank1/Temperature",
"ChangeColorItems": {
"Type": "ColorChangeList",
"Children": [
{ "Type": "ChangeColorItem", "ChangeLimit": 0, "LimitColor": "#FF2196F3" },
{ "Type": "ChangeColorItem", "ChangeLimit": 60, "LimitColor": "#FFFFEB3B" },
{ "Type": "ChangeColorItem", "ChangeLimit": 80, "LimitColor": "#FFFF9800" },
{ "Type": "ChangeColorItem", "ChangeLimit": 95, "LimitColor": "#FFF44336" }
]
}
}
{
"Type": "TextBlock",
"Text": "ALARM",
"Left": 100, "Top": 50, "Width": 80, "Height": 25,
"Dynamics": [
{
"Type": "VisibilityDynamic",
"LinkedValue": "@Tag.Plant/Tank1/AlarmActive"
}
]
}
Element is visible when the linked value is non-zero/true, hidden when zero/false.
{
"Type": "Symbol",
"SymbolName": "Wizard/BLOWER",
"Left": 200, "Top": 100, "Width": 80, "Height": 80,
"Dynamics": [
{
"Type": "RotationDynamic",
"LinkedValue": "@Tag.Plant/Fan1/Speed",
"MinAngle": 0, "MaxAngle": 360,
"MinValue": 0, "MaxValue": 100
}
]
}
Elements can have multiple dynamics simultaneously:
{ "Type": "Rectangle", "Left": 100, "Top": 200, "Width": 60, "Height": 60, "Dynamics": [ { "Type": "FillColorDynamic", "LinkedValue": "@Tag.Plant/Pump1/Running", "ChangeColorItems": {'Shapes')"Type": "ColorChangeList",--alldrawingprimitives (Canvas only) list_elements('Interaction')"Children": [-- buttons, inputs, toggles list_elements('Charts'){ "Type": "ChangeColorItem", "ChangeLimit": 0, "LimitColor": "#FF808080" }, {-- TrendChart, BarChart, PieChart list_elements('Gauges')"Type": "ChangeColorItem", "ChangeLimit": 1, "LimitColor": "#FF4CAF50" }]--CircularGauge,LinearGauge,BulletGauge list_elements('Symbol/HMI/Labels') -- browse symbol library folder
Batch lookups with comma-separated names:
list_elements('TrendChart,CircularGauge,TextBlock')
Canvas elements require Left, Top, Width, Height:
} }, { "Type": "TextBlockVisibilityDynamic", "TextLinkedValue": "Tank Level", "Left": 100, "Top": 50, "Width": 200, "Height": 30 }"@Tag.Plant/Pump1/Enabled" } ] }
Always verify dynamic schemas: Call list_dynamics('FillColorDynamic') (or whichever type) for the exact property names and structure. The examples above are patterns — the schema is the source of truth.
For button/navigation behavior:
list_dynamics('ActionDynamic')
ActionDynamic supports two formats:
Simple format — single action, auto-mapped to MouseLeftButtonDownDashboard elements use Column, Row, ColumnSpan, RowSpan:
{
"Type": "TextBlockButton",
"Text": "TankGo to LevelDetails",
"ColumnLeft": 050, "RowTop": 0400, "ColumnSpanWidth": 2150, "RowSpanHeight": 1
}
Bind element properties to runtime tags using @Tag. prefix:
{
40,
"ActionDynamic": {
"Type": "TextBlockNavigateToDisplay",
"TextDisplayName": "@Tag.Plant/Tank1/Level.Value",DetailPage"
"Foreground": "#FFFFFF"}
}
Browse available symbols:
Nested format — multi-event, explicit mouse event mapping:
{ "ActionDynamic": [ { "Event": "MouseLeftButtonDown",list_elements('Symbol') -- all symbol categories list_elements('Symbol/HMI') -- HMI symbol library list_elements('Symbol/HMI/Equipment') -- equipment symbols list_elements('Library')"Actions": [--alllibraryfolders
Symbol placement pattern:
{
"Type": "SymbolNavigateToDisplay",
"SymbolNameDisplayName": "HMI/Equipment/Pump",DetailPage" }
]
"Left": 200, "Top": 300, "Width": 80, "Height": 80,
"SymbolLabels": [
{ "Key": "State", "Value": "@Tag.Plant/Pump1.Running" },
{ "Key": "Speed", "Value": "@Tag.Plant/Pump1.Speed" }
]
}
CRITICAL: Use @Tag. bindings in SymbolLabels. NEVER use @Label. — that prefix is only for symbol internal definitions (in DisplaysSymbols).
WizardSymbols — always available, no import needed: TANK, VALVE, PUMP, MOTOR, BLOWER. These provide easy customization through SymbolLabels for common industrial equipment.
Library symbols (~1,600 available) auto-import when referenced by SymbolName. No manual import step needed — just reference the path.
For button/navigation behavior:
list_dynamics('ActionDynamic')
ActionDynamic supports two formats:
Simple format — single action, auto-mapped to MouseLeftButtonDown:
}
]
}
Common pattern: navigation button = ActionDynamic + ShineDynamic for visual click feedback.
For all available dynamics:
list_dynamics() -- lists all dynamic types
Displays are document objects — full replacement on write.
Creating a new display: Write the full object as shown in Steps 2–3.
Modifying an existing display — read first, modify, write back:
get_objects('DisplaysList', names=['MainPage'], detail='full')
Then modify the content and write the full object.
MainPage is predefined in new solutions. Write main content directly into it — no need to create a new display for the landing screen.
CodeBehind is CLIENT-SIDE C# or VB.NET code embedded in each display for lifecycle events and operator interaction.
Lifecycle methods:
DisplayOpening() — runs once when the display loadsDisplayIsOpen() — runs cyclically while displayed (interval set by IsOpenInterval)DisplayClosing() — runs once when the display closesDialogOnOK() — runs when a dialog's OK button is clickedCodeBehind is stored in the Contents field of the display, formatted as {Language}\r\n{Code}.
Minimal example — a button click that writes a tag value:
{
"table_type": "DisplaysList",
"data": [{
"Type": "Button",
"Text": "Go to Details",
"ActionDynamic": {
"TypeObjectName": "NavigateToDisplayControlPage",
"DisplayNamePanelType": "DetailPageCanvas",
}
}
Nested format — multi-event, explicit mouse event mapping:
{
"ActionDynamicSize": [
"1366 {
"Event": "MouseLeftButtonDownx 728",
"ActionsContents": [
"CSharp\r\nvoid DisplayOpening()\r\n{\r\n // Initialize { "Type": "NavigateToDisplay", "DisplayName": "DetailPage" }
]
}
]
}
Common pattern: navigation button = ActionDynamic + ShineDynamic for visual click feedback.
For all available dynamics:
list_dynamics() -- lists all dynamic types
Displays are document objects — full replacement on write.
Creating a new display:
{
"table_type": "DisplaysList",
"data": [{
"ObjectName": "OverviewPage",
"PanelType": "Dashboard",
"Columns": 4,
"Rows": 3,
"Content": [display state\r\n}\r\n\r\nvoid ButtonStart_Click(object sender, EventArgs e)\r\n{\r\n @Tag.Plant/Pump1/Command.Value = 1;\r\n}\r\n\r\nvoid ButtonStop_Click(object sender, EventArgs e)\r\n{\r\n @Tag.Plant/Pump1/Command.Value = 0;\r\n}",
"Elements": [
{
"Type": "Button",
"Text": "Start Pump",
"Left": 50, "Top": 100, "Width": 120, "Height": 40,
"ClickEvent": "ButtonStart_Click"
},
{
"Type": "TextBlockButton",
"Text": "PlantStop OverviewPump",
"Column"Left": 200, "Top": 0100, "RowWidth": 0120, "ColumnSpanHeight": 4,40,
"FontSizeClickEvent": 24"ButtonStop_Click"
},
{
"Type": "CircularGaugeTextBlock",
"ValueLinkedValue": "{@Tag.Plant/Tank1Pump1/LevelRunning.Value}",
"Left": 50, "Top": 160, "ColumnWidth": 0200, "RowHeight": 1
25
}
}
]
}]
}
Modifying an existing display — read first, modify, write back:
get_objects('DisplaysList', names=['MainPage'])
Then modify the content and write the full object.
MainPage is predefined and MCP-labeled in new solutions. Write main content directly into it — no need to create a new display for the landing screen.
...
]
}]
}
Key CodeBehind rules:
Contents field format: first line is language (CSharp or VBdotNet), followed by \r\n, then code@ prefix for runtime object access in code: @Tag.Path/Name.Value, @Client.Context.AssetPathSee the Scripts and Expressions skill for full CodeBehind and server-side scripting guidance.
Layout regions: Header, Footer, Menu, Submenu, Content.
get_table_schema('DisplaysLayouts')
get_objects('DisplaysLayouts', names=['Startup']'], detail='full')
The Startup layout defines which display loads into the Content region (typically MainPage). Only modify if you need:
...
Layouts are document objects — read first, modify, write back.
Dependencies: DisplaysSymbols → DisplaysList → DisplaysLayouts
CodeBehind is CLIENT-SIDE C# code embedded in each display for lifecycle events and operator interaction:
DisplayOpening() — runs once when the display loadsDisplayIsOpen() — runs cyclically while displayedDisplayClosing() — runs once when the display closesUse for: operator UI event handlers, local display state, client-side calculations. Not part of the Script module — embedded in each display's content.
See the Scripts and Expressions skill for full CodeBehind guidance.
Common pattern for plant-wide navigation with dynamic content:
Architecture:
Client.Context updates → content displays reactStatic binding (fixed tag path):
@Tag.Area1/Line1/State
Dynamic binding (resolves based on selected asset):
Asset(Client.Context.AssetPath + "State1")
This allows a single display template to show data for whichever asset the operator selects in the navigation tree.
Plant/Line1/Motor1, Plant/Line2/Motor1 with same member names)Asset() syntax in display bindings for dynamic resolutionbrowse_runtime_properties('Client') at design time to explore the Client namespace and Context propertiesWhen setting custom colors on elements, be aware that theme overrides can silently replace your custom values at runtime.
Pattern — set color AND clear theme:
{
"Fill": "#FF3498DB",
"FillTheme": ""
}
If you only set Fill without clearing FillTheme, the theme engine may override your color. Always clear the corresponding theme property when setting custom colors.
| Mistake | Why It Happens | How to Avoid |
|---|---|---|
Using DisplaysDraw as table_type | Confused with DisplaysList | DisplaysDraw is the visual editor UI, not a writable table |
| Omitting PanelType | Defaults silently to Canvas | Always set PanelType explicitly |
Using @Label. in display elements | Confused with symbol definitions | @Label. is only for DisplaysSymbols internals |
| Screenshots for self-validation | Habit from other tools | Trust write_objects success. User sees live updates |
| Setting colors without clearing theme | Theme overrides custom colors | Set value AND clear theme: {Fill: '#FF3498DB', FillTheme: ''} |
| Sending partial display content | Forget it's a document object | Always read-modify-write for displays |
| Canvas positioning in Dashboard | Mixing layout paradigms | Canvas uses Left/Top; Dashboard uses Column/Row |
| Referencing symbols with wrong path | Incomplete library path | Browse with list_elements('Symbol/HMI') to get exact paths |
...
, modify, write back.
Dependencies: DisplaysSymbols → DisplaysList → DisplaysLayouts
Common pattern for plant-wide navigation with dynamic content:
Architecture:
Client.Context updates → content displays reactStatic binding (fixed tag path):
@Tag.Area1/Line1/State
Dynamic binding (resolves based on selected asset):
Asset(Client.Context.AssetPath + "State1")
This allows a single display template to show data for whichever asset the operator selects in the navigation tree.
Setting Up Asset Navigation:
Plant/Line1/Motor1, Plant/Line2/Motor1 with same member names)Asset() syntax in display bindings for dynamic resolutionbrowse_runtime_properties('Client') at design time to explore the Client namespace and Context propertiesWhen setting custom colors on elements, be aware that theme overrides can silently replace your custom values at runtime.
Best practice: OMIT color properties to use themed defaults. Only specify colors when intentionally overriding for process-specific meaning (alarm red, water blue, etc.).
Pattern — set color AND clear theme (when you must override):
{
"Fill": "#FF3498DB",
"FillTheme": ""
}
If you only set Fill without clearing FillTheme, the theme engine may override your color. Always clear the corresponding theme property when setting custom colors.
Use list_elements('ThemeColors') for available named theme brushes (StateOK, StateAlarm, Water, AlarmHighPriority, etc.).
| Mistake | Why It Happens | How to Avoid |
|---|---|---|
Using DisplaysDraw as table_type | Confused with DisplaysList | DisplaysDraw is the visual editor UI, not a writable table |
| Omitting PanelType | Defaults silently to Canvas | Always set PanelType explicitly |
Using @Label. in display elements | Confused with symbol definitions | @Label. is only for DisplaysSymbols internals. Use @Tag. when placing symbols |
| Not discovering dynamics first | Doesn't know what's available | Call list_dynamics() with no parameter to see all dynamic types |
| Screenshots for self-validation | Habit from other tools | Trust write_objects success. User sees live updates |
| Setting colors without clearing theme | Theme overrides custom colors | Set value AND clear theme: {Fill: '#FF3498DB', FillTheme: ''} — or just omit to use themed defaults |
| Sending partial display content | Forget it's a document object | Always read-modify-write for existing displays |
| Canvas positioning in Dashboard | Mixing layout paradigms | Canvas uses Left/Top + Elements; Dashboard uses Row/Col + Cells |
| Referencing symbols with wrong path | Incomplete library path | Browse with list_elements('Symbol/HMI') to get exact paths |
| Guessing dynamic property names | Different dynamics have different schemas | Always call list_dynamics('DynamicTypeName') for exact schema |
| CodeBehind in wrong Contents format | Missing language prefix | Contents must start with CSharp\r\n or VBdotNet\r\n before code |
| Display Action | Tool Call |
|---|---|
| Get display schema | get_table_schema('DisplaysList') |
| Get Canvas structure | list_elements('Canvas') |
| Get Dashboard structure | list_elements('Dashboard') |
| Get element schema | list_elements('ElementName') |
| Browse all dynamics | list_dynamics() |
| Get specific dynamic schema | list_dynamics('FillColorDynamic') |
| Browse symbols | list_elements('Symbol/HMI') or list_elements('Library') |
| Browse Wizard symbols | list_elements('Wizard') |
| Read existing display | get_objects('DisplaysList', names=['PageName'], detail='full') |
| Write display | write_objects('DisplaysList', data=[...]) |
| Navigate Designer to display | designer_action('navigate', 'Display.PageName') |
| Browse runtime properties | browse_runtime_properties('Client') |
search_docs('scripts', labels='skill')search_docs('new solution', labels='skill')list_protocols() to browse, then search_docs('modbus', labels='connector') for protocol-specific docssearch_docs('custom symbols', labels='tutorial') for building reusable parameterized symbols...