title: "Scripts — Tasks, Classes, Expressions, and CodeBehind" tags: [script, expression, code, task, class, codebehind, automation, python, csharp] description: "Server-side automation with Script Tasks and Expressions, reusable Script Classes, and client-side CodeBehind" version: "1.0" author: "Tatsoft"
Implement automation logic in FrameworX: server-side Script Tasks for procedural logic, Expressions for lightweight tag-to-tag calculations, reusable Script Classes for shared methods, and client-side CodeBehind for display event handling.
Use when:
Do NOT use when:
Is the code triggered by a display event (button click, display opening)?
YES → CodeBehind (client-side, embedded in the display)
NO ↓
Is it a one-liner on many tags (scaling, unit conversion, status mapping)?
YES → Expression (server-side, one row per tag)
NO ↓
Is it procedural logic (sequences, loops, conditionals, multi-step)?
YES → Task (server-side, full method body)
Do you need shared utility methods callable from Tasks, Expressions, or CodeBehind?
→ Class (server-side or client-side, plain .NET/Python methods)
| Type | Runs On | Document Object? | Table |
|---|---|---|---|
| Task | Server | YES — read-modify-write | ScriptsTasks |
| Class | Server or Client | YES — read-modify-write | ScriptsClasses |
| Expression | Server | NO — simple merge | ScriptsExpressions |
| CodeBehind | Client | Embedded in DisplaysList | (part of display content) |
Key distinction: Tasks and Classes are document objects — they contain code as structured content. You must read the existing object, modify it, and write the full object back. Sending partial content replaces the entire document.
Expressions are simple objects — send only the fields you want to set or change.
| Category | Items |
|---|---|
| Tools | get_table_schema, write_objects, get_objects |
| Tables | ScriptsTasks, ScriptsClasses, ScriptsExpressions, ScriptsReferences |
get_table_schema('ScriptsTasks')
A Task is a method body that executes on triggers. Key properties:
{
"table_type": "ScriptsTasks",
"data": [{
"ObjectName": "CalculateEfficiency",
"Language": "CSharp",
"Execution": "Periodic",
"Period": 5000,
"Code": "double input = @Tag.Plant/Pump1/InputPower.Value;\ndouble output = @Tag.Plant/Pump1/OutputFlow.Value;\nif (input > 0)\n @Tag.Plant/Pump1/Efficiency.Value = (output / input) * 100;\nelse\n @Tag.Plant/Pump1/Efficiency.Value = 0;"
}]
}
Both are pre-created in new solutions and MCP-labeled. Modify with read-modify-write.
Tag access in C#:
// Read
double level = @Tag.Plant/Tank1/Level.Value;
bool isRunning = @Tag.Plant/Pump1/Running.Value;
// Write
@Tag.Plant/Tank1/SetPoint.Value = 75.0;
@Tag.Plant/Pump1/Running.Value = true;
// Quality and Timestamp
int quality = @Tag.Plant/Tank1/Level.Quality;
DateTime ts = @Tag.Plant/Tank1/Level.Timestamp;
Tag access in Python:
level = Tag.Plant_Tank1_Level.Value # Python uses _ instead of /
Tag.Plant_Tank1_SetPoint.Value = 75.0
Calling a Class method:
double result = @Script.Class.MathUtils.Clamp(value, 0, 100);
get_table_schema('ScriptsClasses')
A Class is a plain .NET or Python class with static methods. Key properties:
{
"table_type": "ScriptsClasses",
"data": [{
"ObjectName": "MathUtils",
"Language": "CSharp",
"Domain": "Server",
"Code": "public static double Clamp(double value, double min, double max)\n{\n if (value < min) return min;\n if (value > max) return max;\n return value;\n}\n\npublic static double Scale(double raw, double rawMin, double rawMax, double engMin, double engMax)\n{\n return engMin + (raw - rawMin) * (engMax - engMin) / (rawMax - rawMin);\n}"
}]
}
@Script.Class.MathUtils.Clamp(value, 0, 100)get_table_schema('ScriptsExpressions')
An Expression is a one-line server-side calculation bound to a tag. Key properties:
Tag.Path/Name (REQUIRED format)Use Expressions when many tags each need a simple computation — avoids creating a full Task document per tag.
{
"table_type": "ScriptsExpressions",
"data": [
{
"ObjectName": "Tag.Plant/Tank1/LevelPercent",
"Expression": "@Tag.Plant/Tank1/LevelRaw * 100 / 4095",
"Execution": "OnChange"
},
{
"ObjectName": "Tag.Plant/Tank1/TempFahrenheit",
"Expression": "@Tag.Plant/Tank1/TempCelsius * 9 / 5 + 32",
"Execution": "OnChange"
},
{
"ObjectName": "Tag.Plant/Tank1/Status",
"Expression": "@Script.Class.StatusLogic.EvaluateStatus(@Tag.Plant/Tank1/Level, @Tag.Plant/Tank1/Pressure)",
"Execution": "Periodic",
"Period": 1000
}
]
}
Critical: ObjectName must include the Tag. namespace prefix. The tag itself (in UnsTags) does NOT have the prefix — but the Expression binding DOES.
CodeBehind is NOT a separate table — it's embedded in the display content (DisplaysList). It runs client-side in the display's local runtime.
DisplayOpening() — runs once when the display loadsDisplayIsOpen() — runs cyclically while the display is visibleDisplayClosing() — runs once when the display closesCodeBehind is part of the display's document content. When writing a display via write_objects('DisplaysList', ...), include the CodeBehind in the content structure. Fetch the display first (document object — read-modify-write), then add or modify the CodeBehind section.
See the Display Construction skill for the full display writing workflow.
If your code needs external .NET assemblies:
get_table_schema('ScriptsReferences')
Add assembly references so Tasks and Classes can use additional libraries.
| Mistake | Why It Happens | How to Avoid |
|---|---|---|
| Sending partial Task code | Forget it's a document object | Always read-modify-write for Tasks and Classes |
Expression ObjectName without Tag. prefix | Confusing tag path vs expression binding | Always Tag.Path/Name for Expression ObjectName |
Using @Label. in server Tasks | Confusing with display symbol syntax | @Label. is only for symbol definitions. Use @Tag. in scripts |
Python tag paths with / | Python doesn't support / in identifiers | Use _ instead of / in Python tag access |
| CodeBehind accessing server objects | Assumes it runs on server | CodeBehind runs client-side. Use @Tag. for server tag access, but local variables are client-only |
| Creating a Task for each tag's scaling | Over-engineering | Use Expressions for bulk one-liner calculations |
| Forgetting hot_reload after Task changes | Code changes don't apply automatically | Call designer_action('hot_reload') or restart runtime |
Modify the predefined ServerStartup task:
get_objects('ScriptsTasks', names=['ServerStartup'])
Add initialization code, write back the full document.
When 50 analog tags each need raw-to-engineering conversion:
For complex logic applied to many tags: