Overview
The Scripting Reference provides comprehensive documentation for developing custom logic in FrameworX 10.1. With support for multiple programming languages including C#, VB.NET, Python, and JavaScript, you can implement complex calculations, automation logic, and integrations. This guide covers syntax, APIs, best practices, and practical examples for all scripting scenarios.
Supported Languages
Language Comparison
Language | Use Cases | Performance | Learning Curve | Key Features |
---|---|---|---|---|
C# | Complex logic, performance-critical | Excellent | Moderate | Full .NET access, strongly typed |
VB.NET | Legacy systems, simple logic | Excellent | Easy | Familiar syntax, .NET access |
Python | Data analysis, ML, scripting | Good | Easy | Libraries, readable syntax |
JavaScript | Client-side, expressions ,HTML5 (*1) | Good | Easy | Web integration, JSON |
(1) Use Portable pages instead of HTML
JavaScript is available only in HTML5-only, displays. Â Prefer always to the Portable Pages, which allows the configuration on Desktop Rich Clients (.NET WPF) and Web/Mobile clients (WebAssembly).
Keep JavaScript and HTML5 only required for code compatibility  or use of external HTML5 controls.
Language Selection Guide
Decision Tree:
Need maximum performance? => C#
Need higher level of runtime security & Reliably => C#
Legacy VB code? => VB.NET
Data science/ML? => Python
Client-side logic? => C# & Python server classes
Simple expressions => any language. Platform expression editor can parse different languages.
Script Types
Tasks (Scheduled Scripts)
Tasks execute on schedules or triggers:
csharp
// C# Task Example
// Read current values
double production = @Tag.Production.Counter;
DateTime shiftStart = @Tag.Shift.StartTime;
// Calculate metrics
    double hours = (DateTime.Now - shiftStart).TotalHours;
double rate = count / hours;
// Update tags
@Tag.KPI.ProductionRate = rate;
@Tag.KPI.LastUpdate = DateTime.Now;
// Log to database
@Script.Class.Util.LogToDatabase(rate);
 Â
Classes (Reusable Libraries)
Classes provide reusable functions:
csharp
// C# Class Library: EngineeringCalcs
// Flow calculation
public static double CalculateFlow(double dp, double k)
{
return k * Math.Sqrt(dp);
}
// Temperature conversion
public static double CelsiusToFahrenheit(double celsius)
{
return (celsius * 9.0 / 5.0) + 32;
}
// Pressure conversion
public static double PSIToBar(double psi)
{
return psi * 0.0689476;
}
// Tank volume
public static double CylindricalTankVolume(double diameter, double level)
{
double radius = diameter / 2;
return Math.PI * radius * radius * level;
}
Expressions (Inline Calculations)
Expressions are single-line calculations:
javascript
// JavaScript Expressions
// Conditional logic
@Tag.Pump.Status = @Tag.Pump.Running ? "Running" : "Stopped"
// Mathematical calculation
@Tag.Tank.Volume = Math.PI * Math.pow(@Tag.Tank.Radius, 2) * @Tag.Tank.Level
// String manipulation
@Tag.Display.Message = "Temperature: " + @Tag.Temp.Value.toFixed(1) + "°C"
// Date/time calculation
@Tag.Batch.Duration = (Date.now() - @Tag.Batch.StartTime) / 1000 / 60
Display Scripts (UI Event Handlers)
Scripts triggered by UI events:
csharp
// C# Display Script
public void OnButtonClick(object sender, EventArgs e)
{
// Validate user input
if (!ValidatePermissions())
{
ShowMessage("Access Denied");
return;
}
// Execute command
@Tag.Equipment.StartCommand = true;
// Log action
LogUserAction("Start button pressed");
// Update display
RefreshDisplay();
}
private bool ValidatePermissions()
{
return @Tag.Security.UserLevel >= 2;
}
Tag Access
Reading and Writing Tags
C# Tag Access
csharp
// Reading tags
double temperature = @Tag.Process.Temperature;
bool motorRunning = @Tag.Motor1.Running;
string batchID = @Tag.Batch.ID;
DateTime timestamp = @Tag.System.CurrentTime;
// Writing tags
@Tag.Setpoint.Value = 75.5;
@Tag.Pump.Start = true;
@Tag.Message.Text = "Process started";
// Array access
double[] values = @Tag.Data.Array;
@Tag.Data.Array[0] = 100;
// Quality and timestamp
TagQuality quality = @Tag.Process.Temperature.Quality;
DateTime lastUpdate = @Tag.Process.Temperature.Timestamp;
Python Tag Access
python
# Reading tags
temperature = @Tag.Process.Temperature
motor_running = @Tag.Motor1.Running
batch_id = @Tag.Batch.ID
# Writing tags
@Tag.Setpoint.Value = 75.5
@Tag.Pump.Start = True
@Tag.Message.Text = "Process started"
# Array operations
import numpy as np
values = np.array(@Tag.Data.Array)
mean_value = np.mean(values)
@Tag.Statistics.Mean = mean_value
# Bulk operations
tags_to_read = ['Tag1', 'Tag2', 'Tag3']
values = @Tag.ReadMultiple(tags_to_read)
Tag Properties and Methods
csharp
// Tag properties
public class TagProperties
{
// Basic properties
string Name { get; }
object Value { get; set; }
TagQuality Quality { get; }
DateTime Timestamp { get; }
// Extended properties
double Min { get; set; }
double Max { get; set; }
string Units { get; }
string Description { get; }
// Methods
void ForceValue(object value);
void Subscribe(Action<TagChangeEvent> handler);
void Unsubscribe();
bool WriteWithConfirm(object value, int timeout);
}
Database Operations
SQL Database Access
csharp
// C# Database Operations
public class DatabaseScript
{
public void ReadFromDatabase()
{
// Simple query
var result = @Dataset.Query("SELECT * FROM Production WHERE Date = TODAY");
// Parameterized query
var parameters = new Dictionary<string, object>
{
{ "StartDate", DateTime.Today },
{ "EndDate", DateTime.Now }
};
var data = @Dataset.ExecuteQuery(
"SELECT * FROM Production WHERE Timestamp BETWEEN @StartDate AND @EndDate",
parameters
);
// Process results
foreach (var row in data.Rows)
{
@Tag.Production.Count = row["Count"];
@Tag.Production.Quality = row["Quality"];
}
}
public void WriteToDatabase()
{
// Insert data
@Dataset.Execute(
"INSERT INTO BatchRecords (BatchID, StartTime, Product) VALUES (@ID, @Time, @Product)",
new {
ID = @Tag.Batch.ID,
Time = DateTime.Now,
Product = @Tag.Batch.Product
}
);
// Update data
@Dataset.Execute(
"UPDATE Equipment SET RunHours = @Hours WHERE ID = @EquipID",
new {
Hours = @Tag.Motor.RunHours,
EquipID = @Tag.Motor.ID
}
);
}
}
Transaction Management
csharp
// Transaction example
public void ExecuteTransaction()
{
using (var transaction = @Dataset.BeginTransaction())
{
try
{
// Multiple operations in transaction
@Dataset.Execute("INSERT INTO Orders ...", transaction);
@Dataset.Execute("UPDATE Inventory ...", transaction);
@Dataset.Execute("INSERT INTO Audit ...", transaction);
// Commit if all successful
transaction.Commit();
}
catch (Exception ex)
{
// Rollback on error
transaction.Rollback();
@Tag.Error.Message = ex.Message;
}
}
}
File Operations
Reading and Writing Files
csharp
// C# File Operations
public class FileOperations
{
public void ReadCSV()
{
string path = @"C:\Data\production.csv";
if (File.Exists(path))
{
var lines = File.ReadAllLines(path);
foreach (var line in lines.Skip(1)) // Skip header
{
var values = line.Split(',');
ProcessCSVLine(values);
}
}
}
public void WriteReport()
{
var report = new StringBuilder();
report.AppendLine($"Production Report - {DateTime.Now}");
report.AppendLine($"Total: {@Tag.Production.Total}");
report.AppendLine($"Good: {@Tag.Production.Good}");
report.AppendLine($"Reject: {@Tag.Production.Reject}");
File.WriteAllText(@"C:\Reports\daily.txt", report.ToString());
}
public void AppendLog()
{
string logEntry = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss},{@Tag.Process.Value}\n";
File.AppendAllText(@"C:\Logs\process.log", logEntry);
}
}
Communication Functions
Web Services and APIs
csharp
// REST API Calls
public class APIIntegration
{
private HttpClient client = new HttpClient();
public async Task GetDataFromAPI()
{
try
{
// GET request
var response = await client.GetAsync("https://api.example.com/data");
if (response.IsSuccessStatusCode)
{
string json = await response.Content.ReadAsStringAsync();
dynamic data = JsonConvert.DeserializeObject(json);
@Tag.External.Value = data.value;
@Tag.External.Status = data.status;
}
}
catch (Exception ex)
{
@Tag.Error.API = ex.Message;
}
}
public async Task PostDataToAPI()
{
var payload = new
{
timestamp = DateTime.Now,
value = @Tag.Process.Value,
quality = @Tag.Process.Quality
};
var json = JsonConvert.SerializeObject(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://api.example.com/data", content);
@Tag.API.LastPost = response.StatusCode.ToString();
}
}
Email Notifications
csharp
// Email Functions
public class EmailScript
{
public void SendAlarmEmail()
{
var email = new EmailMessage
{
To = "operator@company.com",
Subject = $"Alarm: {@Tag.Alarm.Description}",
Body = $@"
Alarm Active: {@Tag.Alarm.Description}
Time: {DateTime.Now}
Value: {@Tag.Alarm.Value}
Priority: {@Tag.Alarm.Priority}
Please acknowledge at HMI.
",
IsHtml = false
};
@System.SendEmail(email);
}
}
Error Handling
Exception Management
csharp
// Comprehensive Error Handling
public class ErrorHandling
{
public void SafeOperation()
{
try
{
// Risky operation
double result = Calculate();
@Tag.Result.Value = result;
}
catch (DivideByZeroException ex)
{
// Specific exception handling
@Tag.Error.Type = "Division Error";
@Tag.Error.Message = "Cannot divide by zero";
LogError(ex);
}
catch (Exception ex)
{
// General exception handling
@Tag.Error.Type = "General Error";
@Tag.Error.Message = ex.Message;
LogError(ex);
}
finally
{
// Always execute
@Tag.Process.Calculating = false;
}
}
private void LogError(Exception ex)
{
var errorLog = new
{
Timestamp = DateTime.Now,
Type = ex.GetType().Name,
Message = ex.Message,
StackTrace = ex.StackTrace,
User = @Tag.System.CurrentUser
};
@Dataset.Execute(
"INSERT INTO ErrorLog (Time, Type, Message, Stack, User) VALUES (@Time, @Type, @Msg, @Stack, @User)",
errorLog
);
}
}
Performance Optimization
Best Practices for Script Performance
csharp
// Performance Optimized Script
public class OptimizedScript
{
// Cache frequently used values
private static Dictionary<string, double> cache = new Dictionary<string, double>();
// Batch operations
public void BatchUpdate()
{
// Bad: Individual writes
// for (int i = 0; i < 100; i++)
// @Tag.Array[i] = Calculate(i);
// Good: Batch write
double[] values = new double[100];
for (int i = 0; i < 100; i++)
values[i] = Calculate(i);
@Tag.Array = values;
}
// Efficient loops
public void EfficientLoop()
{
// Pre-calculate invariants
double factor = @Tag.Config.Factor;
double offset = @Tag.Config.Offset;
var values = @Tag.Raw.Array;
var results = new double[values.Length];
// Use for instead of foreach for arrays
for (int i = 0; i < values.Length; i++)
{
results[i] = values[i] * factor + offset;
}
@Tag.Scaled.Array = results;
}
// Minimize tag access
public void MinimizeTagAccess()
{
// Bad: Multiple tag reads
// if (@Tag.Process.Mode == 1 && @Tag.Process.Mode != 2)
// Good: Single read
int mode = @Tag.Process.Mode;
if (mode == 1 && mode != 2)
{
// Process
}
}
}
Debugging Scripts
Debug Techniques
csharp
// Debugging Methods
public class DebugScript
{
public void DebugExample()
{
// Console output (appears in logs)
Console.WriteLine($"Debug: Starting process at {DateTime.Now}");
// Conditional debugging
#if DEBUG
Console.WriteLine("Debug mode - detailed logging enabled");
var stopwatch = Stopwatch.StartNew();
#endif
// Debug tags
@Tag.Debug.LastRun = DateTime.Now;
@Tag.Debug.ExecutionTime = stopwatch.ElapsedMilliseconds;
// Breakpoint (in Designer test mode)
System.Diagnostics.Debugger.Break();
// Trace information
Trace.WriteLine("Trace: Process completed");
Trace.Assert(@Tag.Value > 0, "Value should be positive");
}
}
Script Examples Library
Common Calculations
csharp
// Standard Industrial Calculations
public static class IndustrialCalcs
{
// PID Controller
public static double PID(double setpoint, double processValue,
ref double integral, ref double lastError,
double kp, double ki, double kd, double dt)
{
double error = setpoint - processValue;
integral += error * dt;
double derivative = (error - lastError) / dt;
lastError = error;
return kp * error + ki * integral + kd * derivative;
}
// Moving Average
public static double MovingAverage(Queue<double> window, double newValue, int windowSize)
{
window.Enqueue(newValue);
if (window.Count > windowSize)
window.Dequeue();
return window.Average();
}
// Rate of Change
public static double RateOfChange(double currentValue, double lastValue, double timeInterval)
{
return (currentValue - lastValue) / timeInterval;
}
// Totalization
public static double Totalize(double flow, double timeInterval, ref double total)
{
total += flow * timeInterval;
return total;
}
}
Data Processing
python
# Python Data Analysis Script
import numpy as np
import statistics
def analyze_batch_data():
# Get array data
temperatures = @Tag.Batch.TemperatureArray
pressures = @Tag.Batch.PressureArray
# Statistical analysis
@Tag.Stats.TempMean = np.mean(temperatures)
@Tag.Stats.TempStdDev = np.std(temperatures)
@Tag.Stats.TempMin = np.min(temperatures)
@Tag.Stats.TempMax = np.max(temperatures)
# Find outliers
z_scores = np.abs((temperatures - np.mean(temperatures)) / np.std(temperatures))
outliers = np.where(z_scores > 3)[0]
@Tag.Stats.OutlierCount = len(outliers)
# Correlation
correlation = np.corrcoef(temperatures, pressures)[0, 1]
@Tag.Stats.TempPressureCorrelation = correlation
# Quality determination
if @Tag.Stats.TempStdDev < 2.0 and len(outliers) == 0:
@Tag.Batch.Quality = "Good"
else:
@Tag.Batch.Quality = "Review Required"
Script Security
Security Best Practices
csharp
// Secure Scripting Practices
public class SecureScript
{
// Input validation
public void ValidateInput(string userInput)
{
// Sanitize input
userInput = userInput.Trim();
// Validate format
if (!Regex.IsMatch(userInput, @"^[a-zA-Z0-9_]+$"))
{
throw new ArgumentException("Invalid input format");
}
// Check length
if (userInput.Length > 50)
{
throw new ArgumentException("Input too long");
}
// SQL injection prevention (use parameters)
@Dataset.Execute(
"SELECT * FROM Table WHERE Name = @Name",
new { Name = userInput }
);
}
// Permission checking
public void SecureOperation()
{
// Check user permission
if (@Tag.Security.UserLevel < 3)
{
@Tag.Error.Message = "Insufficient permissions";
LogSecurityEvent("Unauthorized access attempt");
return;
}
// Execute privileged operation
ExecutePrivilegedOperation();
}
// Audit logging
private void LogSecurityEvent(string action)
{
@Dataset.Execute(
"INSERT INTO SecurityLog (Time, User, Action, Result) VALUES (@Time, @User, @Action, @Result)",
new {
Time = DateTime.Now,
User = @Tag.System.CurrentUser,
Action = action,
Result = "Denied"
}
);
}
}
Next Steps
Additional Resources
- .NET API Reference - Detailed .NET framework access
- Python API Reference - Python-specific functions
- Built-in Functions - Standard function library
- Code Examples - Complete script examples
AI Assistant Data
<details> <summary>Structured Information for AI Tools</summary>
json
{
"page": "Scripting Reference",
"type": "Technical Reference",
"purpose": "Comprehensive scripting documentation for FrameworX",
"languages": ["C#", "VB.NET", "Python", "JavaScript"],
"scriptTypes": {
"tasks": "Scheduled or triggered execution",
"classes": "Reusable libraries",
"expressions": "Inline calculations",
"display": "UI event handlers"
},
"keyTopics": [
"Tag access and manipulation",
"Database operations",
"File I/O",
"Web services",
"Error handling",
"Performance optimization",
"Security"
],
"commonUses": [
"Calculations",
"Data processing",
"Integration",
"Automation",
"Reporting"
]
}
</detail