@aryacode/dsh-engineering 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +6 -6
- package/cordis.patch.yml +1 -1
- package/entries/netlogic.js +266 -0
- package/entries/optix.js +13 -0
- package/package.json +3 -2
- package/vendor/optix/SKILL.md +68 -0
- package/vendor/optix/netlogic/SOURCE.json +12 -0
- package/vendor/optix/netlogic/index.json +1987 -0
- package/vendor/optix/netlogic/pages/accessing-project-nodes.md +113 -0
- package/vendor/optix/netlogic/pages/advanced-svg.md +124 -0
- package/vendor/optix/netlogic/pages/alarming.md +489 -0
- package/vendor/optix/netlogic/pages/async-tasks.md +236 -0
- package/vendor/optix/netlogic/pages/audit-signature.md +91 -0
- package/vendor/optix/netlogic/pages/colors.md +122 -0
- package/vendor/optix/netlogic/pages/companion-specs.md +77 -0
- package/vendor/optix/netlogic/pages/creating-objects.md +370 -0
- package/vendor/optix/netlogic/pages/database-interaction.md +437 -0
- package/vendor/optix/netlogic/pages/datagrids.md +123 -0
- package/vendor/optix/netlogic/pages/debugging-tips.md +605 -0
- package/vendor/optix/netlogic/pages/designtime-tags-import.md +531 -0
- package/vendor/optix/netlogic/pages/dialog-boxes.md +150 -0
- package/vendor/optix/netlogic/pages/dispatch-events.md +200 -0
- package/vendor/optix/netlogic/pages/dynamic-links.md +825 -0
- package/vendor/optix/netlogic/pages/element-access.md +1208 -0
- package/vendor/optix/netlogic/pages/engineering-units.md +298 -0
- package/vendor/optix/netlogic/pages/events.md +340 -0
- package/vendor/optix/netlogic/pages/execute-command.md +202 -0
- package/vendor/optix/netlogic/pages/fto-studio-cli.md +99 -0
- package/vendor/optix/netlogic/pages/good-practices.md +50 -0
- package/vendor/optix/netlogic/pages/information-model.md +89 -0
- package/vendor/optix/netlogic/pages/log-output.md +22 -0
- package/vendor/optix/netlogic/pages/managing-aliases.md +22 -0
- package/vendor/optix/netlogic/pages/manipulate-nodes.md +72 -0
- package/vendor/optix/netlogic/pages/mqtt.md +240 -0
- package/vendor/optix/netlogic/pages/netlogic-lifecycle.md +270 -0
- package/vendor/optix/netlogic/pages/netlogic-overview.md +155 -0
- package/vendor/optix/netlogic/pages/opcua.md +325 -0
- package/vendor/optix/netlogic/pages/plc-tags.md +219 -0
- package/vendor/optix/netlogic/pages/queries.md +1297 -0
- package/vendor/optix/netlogic/pages/random-generation.md +90 -0
- package/vendor/optix/netlogic/pages/recipes.md +375 -0
- package/vendor/optix/netlogic/pages/recipex.md +2383 -0
- package/vendor/optix/netlogic/pages/regex.md +15 -0
- package/vendor/optix/netlogic/pages/register-observers.md +346 -0
- package/vendor/optix/netlogic/pages/reports.md +141 -0
- package/vendor/optix/netlogic/pages/resource-uri.md +308 -0
- package/vendor/optix/netlogic/pages/runtime-tags-import.md +280 -0
- package/vendor/optix/netlogic/pages/serial-port.md +357 -0
- package/vendor/optix/netlogic/pages/sessions.md +58 -0
- package/vendor/optix/netlogic/pages/system-node.md +153 -0
- package/vendor/optix/netlogic/pages/template-library.md +127 -0
- package/vendor/optix/netlogic/pages/threading.md +236 -0
- package/vendor/optix/netlogic/pages/translations.md +417 -0
- package/vendor/optix/netlogic/pages/trends.md +128 -0
- package/vendor/optix/netlogic/pages/ui-animations.md +115 -0
- package/vendor/optix/netlogic/pages/users-groups.md +416 -0
- package/vendor/optix/netlogic/pages/variables-formatting.md +16 -0
- package/vendor/optix/netlogic/pages/variables-interaction.md +557 -0
- package/vendor/optix/netlogic/pages/variables-structure.md +155 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Calling or running commands
|
|
2
|
+
|
|
3
|
+
## OPC/UA Methods
|
|
4
|
+
|
|
5
|
+
See [here](./creating-objects.md)
|
|
6
|
+
|
|
7
|
+
## Run external commands (CMD or external softwares)
|
|
8
|
+
|
|
9
|
+
```csharp
|
|
10
|
+
using System.Diagnostics;
|
|
11
|
+
|
|
12
|
+
static void RunCommand(string command)
|
|
13
|
+
{
|
|
14
|
+
int exitCode;
|
|
15
|
+
ProcessStartInfo processInfo;
|
|
16
|
+
Process process;
|
|
17
|
+
// Create the new command and launch it using the command prompt
|
|
18
|
+
processInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
|
|
19
|
+
processInfo.CreateNoWindow = true;
|
|
20
|
+
processInfo.UseShellExecute = false;
|
|
21
|
+
//Redirect the output
|
|
22
|
+
processInfo.RedirectStandardError = true;
|
|
23
|
+
processInfo.RedirectStandardOutput = true;
|
|
24
|
+
// Start the process
|
|
25
|
+
process = Process.Start(processInfo);
|
|
26
|
+
process.WaitForExit();
|
|
27
|
+
// Read the streams
|
|
28
|
+
// Warning: This approach can lead to deadlocks
|
|
29
|
+
string output = process.StandardOutput.ReadToEnd();
|
|
30
|
+
string error = process.StandardError.ReadToEnd();
|
|
31
|
+
// Read the exit code
|
|
32
|
+
exitCode = process.ExitCode;
|
|
33
|
+
// Print some output
|
|
34
|
+
Log.Info("RunCommand", "output>>" + (String.IsNullOrEmpty(output) ? "(none)" : output));
|
|
35
|
+
Log.Info("RunCommand", "error>>" + (String.IsNullOrEmpty(error) ? "(none)" : error));
|
|
36
|
+
Log.Info("RunCommand", "ExitCode: " + exitCode.ToString());
|
|
37
|
+
// Close the process handler
|
|
38
|
+
process.Close();
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Open a WebBrowser to a specific page
|
|
43
|
+
|
|
44
|
+
```csharp
|
|
45
|
+
[ExportMethod]
|
|
46
|
+
public void OpenBrowser(string urlPath) {
|
|
47
|
+
//string destinationPath = new ResourceUri(urlPath).Uri;
|
|
48
|
+
string destinationPath = urlPath;
|
|
49
|
+
Process myProcess = new Process();
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
// true is the default, but it is important not to set it to false
|
|
53
|
+
myProcess.StartInfo.UseShellExecute = true;
|
|
54
|
+
myProcess.StartInfo.FileName = destinationPath;
|
|
55
|
+
// Start the process
|
|
56
|
+
myProcess.Start();
|
|
57
|
+
} catch (Exception e) {
|
|
58
|
+
// Print any exception (if needed)
|
|
59
|
+
Log.Error("OpenInBrowser", e.Message);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Invoke FT Optix Core commands
|
|
65
|
+
|
|
66
|
+
> [!NOTE]
|
|
67
|
+
> Core commands can only be invoked at runtime.
|
|
68
|
+
|
|
69
|
+
```csharp
|
|
70
|
+
// Get the core commands object (exposed by FactoryTalk Optix APIs)
|
|
71
|
+
var coreCommandsObject = InformationModel.GetObject(FTOptix.CoreBase.Objects.CoreCommands);
|
|
72
|
+
// Call a specific command
|
|
73
|
+
coreCommandsObject.ExecuteMethod("Close");
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Invoke FactoryTalk Optix Alarm commands
|
|
77
|
+
|
|
78
|
+
> [!NOTE]
|
|
79
|
+
> Alarm commands can only be invoked at runtime.
|
|
80
|
+
|
|
81
|
+
```csharp
|
|
82
|
+
// Get the alarms object
|
|
83
|
+
var alarmCommands = InformationModel.GetObject(FTOptix.Alarm.Objects.AlarmCommands);
|
|
84
|
+
// Acknowledge all alarms
|
|
85
|
+
alarmCommands.ExecuteMethod("AcknowledgeAll");
|
|
86
|
+
// Confirm all alarms
|
|
87
|
+
alarmCommands.ExecuteMethod("ConfirmAll");
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Perform system shutdown (Windows only)
|
|
91
|
+
|
|
92
|
+
```csharp
|
|
93
|
+
/// <summary>
|
|
94
|
+
/// Trigger a Windows shutdown using the system 'shutdown' command.
|
|
95
|
+
/// Use with caution; this will stop the entire machine.
|
|
96
|
+
/// </summary>
|
|
97
|
+
[ExportMethod]
|
|
98
|
+
public void SystemShutDown()
|
|
99
|
+
{
|
|
100
|
+
var psi = new ProcessStartInfo("shutdown", "/s /t 0");
|
|
101
|
+
psi.CreateNoWindow = true;
|
|
102
|
+
psi.UseShellExecute = false;
|
|
103
|
+
Process.Start(psi);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Run a .BAT or external executable
|
|
108
|
+
|
|
109
|
+
```csharp
|
|
110
|
+
/// <summary>
|
|
111
|
+
/// Start a BAT file or external program. Adjust filename and parameters as needed.
|
|
112
|
+
/// On Windows, you can call cmd with /k or /c; /k keeps the window open.
|
|
113
|
+
/// </summary>
|
|
114
|
+
private void ExecuteExternalCommand()
|
|
115
|
+
{
|
|
116
|
+
var Test = new Process();
|
|
117
|
+
string filename = @"C:\Path\To\Script.bat";
|
|
118
|
+
string parameters = $"/k \"{filename}\"";
|
|
119
|
+
Process.Start("cmd", parameters);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
```csharp
|
|
124
|
+
// Example: launch VLC with parameters
|
|
125
|
+
[ExportMethod]
|
|
126
|
+
public void OpenVLC()
|
|
127
|
+
{
|
|
128
|
+
Process VLC = new Process();
|
|
129
|
+
VLC.StartInfo.FileName = @"C:\Program Files (x86)\VideoLAN\VLC\vlc.exe";
|
|
130
|
+
VLC.StartInfo.Arguments = " --fullscreen --play-and-exit C:\\path\\to\\video.mp4";
|
|
131
|
+
VLC.Start();
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Invoke a method from a different NetLogic
|
|
136
|
+
|
|
137
|
+
Calling a NetLogic instance with `var myNetLogic = new MyNetLogic();` will create a new instance of the NetLogic, but it will not be the same as the one that is running in the project so it should be avoided. To call a method from a different NetLogic, you need to get the NetLogic object from the project and then call the method on that object.
|
|
138
|
+
|
|
139
|
+
> [!NOTE]
|
|
140
|
+
> Methods from other NetLogic instances can only be invoked at runtime.
|
|
141
|
+
|
|
142
|
+
```csharp
|
|
143
|
+
[ExportMethod]
|
|
144
|
+
public void CallMethod1()
|
|
145
|
+
{
|
|
146
|
+
// Get the NetLogic containing the method to invoke
|
|
147
|
+
var myScript = Project.Current.GetObject("NetLogic/RuntimeNetLogic1");
|
|
148
|
+
// Launch the method
|
|
149
|
+
myScript.ExecuteMethod("Method1");
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Invoke a method from a different NetLogic with parameters
|
|
154
|
+
|
|
155
|
+
#### NetLogic to be called
|
|
156
|
+
|
|
157
|
+
```csharp
|
|
158
|
+
public class AlarmGridLogic : BaseNetLogic
|
|
159
|
+
{
|
|
160
|
+
[ExportMethod]
|
|
161
|
+
public void AckAllAlarmsWithMessage(object[] arguments)
|
|
162
|
+
{
|
|
163
|
+
var ackMessage = (LocalizedText) arguments[0];
|
|
164
|
+
var alarmsList = GetAllAlarms();
|
|
165
|
+
if (alarmsList.Count == 0)
|
|
166
|
+
{
|
|
167
|
+
Log.Warning("No alarms to acknowledge");
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
ProcessAlarms(ackMessage, alarmsList, (alarm, message) => alarm.Acknowledge(message));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
#### Caller
|
|
176
|
+
|
|
177
|
+
```csharp
|
|
178
|
+
public class AckConfirmMessageLogic : BaseNetLogic
|
|
179
|
+
{
|
|
180
|
+
[ExportMethod]
|
|
181
|
+
public void PerformAction()
|
|
182
|
+
{
|
|
183
|
+
// Get the target NetLogic object
|
|
184
|
+
var alarmsNetLogic = GetNetLogicObject();
|
|
185
|
+
// Get the action name to be invoked
|
|
186
|
+
string action = Owner.GetVariable("MethodName").Value;
|
|
187
|
+
// Get the comment to be added to the alarm
|
|
188
|
+
var comment = Owner.Get<TextBox>("Comment").LocalizedText;
|
|
189
|
+
// Create the object arguments to be passed to the method
|
|
190
|
+
object[] arguments = new object[] { comment };
|
|
191
|
+
alarmsNetLogic.ExecuteMethod(action, [arguments]);
|
|
192
|
+
(Owner as Dialog)?.Close();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private IUAObject GetNetLogicObject()
|
|
196
|
+
{
|
|
197
|
+
// Here the NetLogic is passed as a variable to the owner object,
|
|
198
|
+
// but it could be retrieved in a different way (like Project.Current.GetObject)
|
|
199
|
+
return InformationModel.GetObject(Owner.GetVariable("AlarmGridLogic").Value);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# FactoryTalk Optix Studio CLI
|
|
2
|
+
|
|
3
|
+
## Export an application to be loaded in an Optix Panel via USB
|
|
4
|
+
|
|
5
|
+
This script leverages the FactoryTalk Optix Studio CLI to export an application and prepare it to be imported in an Optix Panel via USB.
|
|
6
|
+
|
|
7
|
+
> [!TIP]
|
|
8
|
+
> The `LoadApplicationWidget` should be used to load the application in the Optix Panel, this script only prepares the ZIP file with the correct structure and encryption.
|
|
9
|
+
|
|
10
|
+
```powershell
|
|
11
|
+
$projectPath = "D:\temp\NewHMIProject12\NewHMIProject12.optix"
|
|
12
|
+
$optixIdePath = "C:\Program Files\Rockwell Automation\FactoryTalk Optix\Studio 1.6.2.36\FTOptixStudio.exe"
|
|
13
|
+
$7zipPath = "C:\Program Files\7-Zip\7z.exe"
|
|
14
|
+
$outputPath = "C:\Users\LabUser\Downloads\"
|
|
15
|
+
$platform = "Yocto_arm64"
|
|
16
|
+
$optixPanelPassword = "YourOptixPanelAdminPassword"
|
|
17
|
+
|
|
18
|
+
# Create a temporary directory for the export process
|
|
19
|
+
Write-Host "Creating temporary directory for export..."
|
|
20
|
+
$tempDir = New-Item -ItemType Directory -Path "$env:TEMP\OptixExportTemp" -Force
|
|
21
|
+
Write-Host "Temporary directory created at $tempDir"
|
|
22
|
+
|
|
23
|
+
# Export the project using Optix IDE
|
|
24
|
+
Write-Host "Exporting project using Optix IDE..."
|
|
25
|
+
Start-Process -FilePath "$optixIdePath" -ArgumentList "export `"$projectPath`" --platform=`"$platform`" --location=`"$tempDir`"" -Wait
|
|
26
|
+
|
|
27
|
+
# Update the .optix file with the EncryptionMode
|
|
28
|
+
Write-Host "Updating .optix file with EncryptionMode..."
|
|
29
|
+
$optixFilePath = Get-ChildItem -Path $tempDir -Recurse -Filter "*.optix" | Select-Object -First 1
|
|
30
|
+
if ($null -eq $optixFilePath) {
|
|
31
|
+
Write-Error "No .optix file found in the exported directory."
|
|
32
|
+
Write-Host "Searching for all files in temp directory for debugging:"
|
|
33
|
+
Get-ChildItem -Path $tempDir -Recurse | ForEach-Object { Write-Host $_.FullName }
|
|
34
|
+
exit 1
|
|
35
|
+
}
|
|
36
|
+
Write-Host "Found .optix file at: $($optixFilePath.FullName)"
|
|
37
|
+
|
|
38
|
+
# Extract the CoreVersion from the .optix YAML file and add encryption settings
|
|
39
|
+
$optixFileContent = Get-Content -Path $optixFilePath.FullName
|
|
40
|
+
$coreVersionLine = $optixFileContent | Where-Object { $_ -match "CoreVersion:" }
|
|
41
|
+
if ($null -eq $coreVersionLine) {
|
|
42
|
+
Write-Error "CoreVersion not found in the .optix file."
|
|
43
|
+
exit 1
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
$coreVersion = $coreVersionLine -replace "CoreVersion:\s*", ""
|
|
47
|
+
Write-Host "CoreVersion found: $coreVersion"
|
|
48
|
+
|
|
49
|
+
# Add encryption settings after CoreVersion line
|
|
50
|
+
$updatedContent = @()
|
|
51
|
+
foreach ($line in $optixFileContent) {
|
|
52
|
+
$updatedContent += $line
|
|
53
|
+
if ($line -match "CoreVersion:") {
|
|
54
|
+
$updatedContent += " EncryptionVersion: 1"
|
|
55
|
+
$updatedContent += " EncryptionMode: YAMLFiles"
|
|
56
|
+
Write-Host "Added encryption settings after CoreVersion: $coreVersion"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Write the updated content back to the .optix file
|
|
61
|
+
$updatedContent | Set-Content -Path $optixFilePath.FullName
|
|
62
|
+
|
|
63
|
+
# Write the content of the XML file
|
|
64
|
+
Write-Host "Creating DeviceConfiguration.xml..."
|
|
65
|
+
$xmlFilePath = Join-Path -Path $tempDir -ChildPath "DeviceConfiguration.xml"
|
|
66
|
+
$xmlContent = "<?xml version=""1.0"" encoding=""utf-8""?>
|
|
67
|
+
<DeviceConfiguration>
|
|
68
|
+
<Actions>
|
|
69
|
+
<ImportFTOptixApplication EncryptProject=""true""/>
|
|
70
|
+
</Actions>
|
|
71
|
+
</DeviceConfiguration>"
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
$xmlContent | Out-File -FilePath $xmlFilePath -Encoding Ascii
|
|
75
|
+
Write-Host "DeviceConfiguration.xml created successfully at: $xmlFilePath"
|
|
76
|
+
} catch {
|
|
77
|
+
Write-Error "Failed to create XML file: $($_.Exception.Message)"
|
|
78
|
+
exit 1
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# Create a zip archive of the exported project
|
|
82
|
+
Write-Host "Creating ZIP archive..."
|
|
83
|
+
$outputZipPath = Join-Path -Path $outputPath -ChildPath "FTOptixApplication.z"
|
|
84
|
+
|
|
85
|
+
# Compress it to ZIP file with Fast method and AES-256 encryption
|
|
86
|
+
Write-Host "Compressing to ZIP with AES-256 encryption..."
|
|
87
|
+
& "$7zipPath" a -tzip -mx=3 "-p$optixPanelPassword" "$outputZipPath" "$tempDir\*"
|
|
88
|
+
|
|
89
|
+
# Check if the compression was successful
|
|
90
|
+
if ($LASTEXITCODE -ne 0) {
|
|
91
|
+
Write-Error "Failed to create ZIP archive."
|
|
92
|
+
exit 1
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
# Clean up the temporary directory
|
|
96
|
+
Remove-Item -Path $tempDir -Recurse -Force
|
|
97
|
+
|
|
98
|
+
Write-Host "Export completed. The ZIP file is located at $outputZipPath"
|
|
99
|
+
```
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Good practices
|
|
2
|
+
|
|
3
|
+
Here is a list of convention our FactoryTalk Optix technicians uses when creating new projects, feel free to choose if this can apply to your project
|
|
4
|
+
|
|
5
|
+
- Use significative names for every project element (no default names)
|
|
6
|
+
- Exception is made when creating object types, still remember to give meaningful name if possible
|
|
7
|
+
- Avoid using number at the end of item name
|
|
8
|
+
- Avoid abbreviations in object names to improve readability
|
|
9
|
+
- Examples:
|
|
10
|
+
- `Templates/SpinBox (Type)` -> BAD
|
|
11
|
+
- `Templates/SpinBoxWithGreenBorder (Type)` -> GOOD
|
|
12
|
+
- `MainWindow/SpinBox1` -> BAD
|
|
13
|
+
- `MainWindow/MotorSpeed_SpinBox` -> GOOD
|
|
14
|
+
- `MainWindow/MotorSP_SB` -> BAD
|
|
15
|
+
- Nice to use same EmbeddedDatabase for every item to be stored
|
|
16
|
+
- Multiple tables can be used in same EmbeddedDatabase with different settings (RecordLimit, Names, etc)
|
|
17
|
+
- Use `PascalCase` for object names
|
|
18
|
+
- Use `camelCase` for variable names (Model, Session and Local variables)
|
|
19
|
+
- All graphical elements should be in `UI` folder
|
|
20
|
+
- Here you can create standard folders to organize your work, such as:
|
|
21
|
+
- `UI/Templates`-> Contains all custom types used to create objects
|
|
22
|
+
- `UI/Screens` -> Contains the different pages that are used in the project
|
|
23
|
+
- Both subfolder can have children subfolder to organize elements if needed
|
|
24
|
+
- Example:
|
|
25
|
+
- `UI/Templates/MotorWidgets`
|
|
26
|
+
- `UI/Templates/PumpsWidgets`
|
|
27
|
+
- `UI/Screens/Filler`
|
|
28
|
+
- `UI/Screens/Settings`
|
|
29
|
+
- Nice to use Templates folder in any first level project folder (e.g. UI, Model, Converters, etc...) when creating custom types
|
|
30
|
+
|
|
31
|
+
- Global variables should be created in the `Model` folder (and subfolder)
|
|
32
|
+
- Nice to use HTML color palette if no special need for custom coloring
|
|
33
|
+
- Example:
|
|
34
|
+
- `red` -> GOOD
|
|
35
|
+
- `#FFA1C8` -> BAD
|
|
36
|
+
- Object names and LocalizationDictionary keys should be in English
|
|
37
|
+
- Nice to sort items by their type in project tree
|
|
38
|
+
- Exception is made when defining layers is necessary to build final interface (bottom most element is highest layer)
|
|
39
|
+
- When creating a new page/container, avoid using a `Panel` with a `Rectangle` as coloured background to avoid doubling project nodes
|
|
40
|
+
- Use instead:
|
|
41
|
+
- `Screen` if the object is a whole screen that is going to be displayed in the HMI
|
|
42
|
+
- `Rectangle` if coloured background is needed
|
|
43
|
+
- `Panel` if background should be transparent (usually to group elements)
|
|
44
|
+
- Nice to group items using Panel
|
|
45
|
+
- This is useful to reduce project complexity when assigning groups-based permissions and/or to trigger specific visual logics (enabling, visibility, etc)
|
|
46
|
+
- Nice to use same `Label` name and `LocalizationDictionary` item key
|
|
47
|
+
- Nice to use keyword in controls names to identify which variable/object is referring to
|
|
48
|
+
- Example:
|
|
49
|
+
- `VoltageText` -> Label that shows the translation key for the _Voltage_ element
|
|
50
|
+
- `VoltageValue` -> Name for a LinearGauge controlling _Voltage_ value
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# The Information Model
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
In FactoryTalk Optix, each and every object is a `node`, the definition of a node comes from the OPC/UA specifications which states that an OPC/UA node is like an entity representing information. Some Nodes are Variables, DataType, References, Object, Methods... depending of what they are used for.
|
|
6
|
+
|
|
7
|
+
The OPC/UA specifications can be found at the following link [opcfoundation resources](https://opcfoundation.org/developer-tools/documents/?type=Specification) and search for [OPC 10000-1 UA Part 1: Overview and Concepts](https://opcfoundation.org/developer-tools/documents/view/158) and [OPC 10000-3 UA Part 3: Address Space Model](https://opcfoundation.org/developer-tools/documents/view/160)
|
|
8
|
+
|
|
9
|
+
Understanding the concept of nodes is crucial when working with FactoryTalk Optix because it forms the backbone of the OPC/UA information model, which FactoryTalk Optix leverages for data representation and interaction. Nodes represent various types of information and their relationships, enabling a standardized way to access and manipulate data within the system. This allows integration and interoperability between different components and devices, ensuring that data can be efficiently managed and utilized across the entire project.
|
|
10
|
+
|
|
11
|
+
### The Information Model of OPC/UA
|
|
12
|
+
|
|
13
|
+
The OPC Unified Architecture (OPC/UA) defines a rich information model that allows for complex data representation and interaction. At the core of this model is the concept of a "node". A node is an entity that represents information and can be of various types, each serving a different purpose within the information model. The major properties of a node include:
|
|
14
|
+
|
|
15
|
+
- **NodeId**: A unique identifier for the node within the address space.
|
|
16
|
+
- **BrowseName**: A human-readable name used for browsing the node.
|
|
17
|
+
- **DisplayName**: A localized name intended for display purposes.
|
|
18
|
+
- **Description**: A localized text description of the node.
|
|
19
|
+
|
|
20
|
+
Nodes can be of different types, including:
|
|
21
|
+
|
|
22
|
+
- **Object**: Represents physical or abstract entities.
|
|
23
|
+
- **Variable**: Holds a value and can have a data type.
|
|
24
|
+
- **Method**: Represents callable functions or procedures.
|
|
25
|
+
- **ObjectType**: Defines the type of an object.
|
|
26
|
+
- **VariableType**: Defines the type of a variable.
|
|
27
|
+
- **ReferenceType**: Defines the type of a reference between nodes.
|
|
28
|
+
- **DataType**: Defines the data type of a variable.
|
|
29
|
+
- **View**: Represents a subset of the address space for browsing.
|
|
30
|
+
|
|
31
|
+
These nodes and their properties form the foundation of the OPC/UA information model, enabling a standardized way to represent and interact with data across different systems and devices.
|
|
32
|
+
|
|
33
|
+
### The Information Model of FactoryTalk Optix
|
|
34
|
+
|
|
35
|
+
As everything in FactoryTalk Optix is actually an OPC/UA node, we can implement some complex scenarios like:
|
|
36
|
+
|
|
37
|
+
- Allowing other applications (both FactoryTalk Optix or third-parties) to read and write project nodes
|
|
38
|
+
- This includes exposing project elements like Databases, PLC Tags, Model variables, UI objects, etc
|
|
39
|
+
- Allowing other applications to produce and consume alarms and events using the OPC/UA A&E protocol
|
|
40
|
+
- Manipulating parts of the project using NetLogic or an external OPC/UA client
|
|
41
|
+
|
|
42
|
+
## Project context
|
|
43
|
+
|
|
44
|
+
Accessing the path of the node will return the project-level node, this is good for global elements (Model Variables, PLC Tags, etc) but it cannot be used to access session-based elements, as each user session will have a unique instance of windows and pages with uniques NodeIds
|
|
45
|
+
|
|
46
|
+
### Overview
|
|
47
|
+
|
|
48
|
+

|
|
49
|
+
|
|
50
|
+
FactoryTalk Optix graphical interface is based on the object's oriented paradigm, this means that when defining UI elements at design-time, these are actually created as object types. When the runtime starts, and a session is created (NativePresentationEngine creates automatically one UI instance, each client of the WebPresentationEngine creates one UI instance), this session is derived from the UI type that was designed, thus becoming an instance.
|
|
51
|
+
|
|
52
|
+
Each session lives as an independent object, each session can interact with global objects (such as Model variables or PLC tags) but cannot access other sessions, when calling methods that targets a session object (such as the refresh of a DataGrid), it can only affect the session where the method was called, other instances and sessions are unaffected.
|
|
53
|
+
|
|
54
|
+
NetLogic are also affected by this functionality:
|
|
55
|
+
- When using the `Project.Current.Get` syntax, only the project definition can be accessed, if a UI element is manipulated using such syntax, the session instance must be destroyed and recreated in order to inherit the new changes (for example navigating to a different page and then coming back)
|
|
56
|
+
- When placing the NetLogic inside a page (which can be something like a Screen, a Panel, a Rectangle, or any UI object) and using the `Owner.Get` syntax, the specific instance where the NetLogic is created gets accessed and the UI is immediately refreshed. Only the instance where the NetLogic exists can be accessed, a session-based NetLogic cannot interact with other sessions, only with elements in the current session or global objects (such as PLC tags or Model variables).
|
|
57
|
+
|
|
58
|
+
### Summary
|
|
59
|
+
|
|
60
|
+
- Each session of a PresentationEngine creates an instance of the UI types
|
|
61
|
+
- Each session is completely independent from other session
|
|
62
|
+
- A session can only interact with:
|
|
63
|
+
- Elements in the same session
|
|
64
|
+
- Global objects (Model variables, PLC tags, alarms, etc)
|
|
65
|
+
- A session cannot interact with other sessions
|
|
66
|
+
- Session changes are discarded when the instance is destroyed (page is changed or reloaded)
|
|
67
|
+
- Retentivity cannot be applied to session objects
|
|
68
|
+
- Retentivity should never be applied to any UI object
|
|
69
|
+
- A session can access global objects, but global objects cannot access session objects, for example:
|
|
70
|
+
- A global NetLogic cannot refresh the DataGrid of a session, a global NetLogic can only access nodes at the project level (such as alarms, PLC tags, Model variables, etc)
|
|
71
|
+
- A session NetLogic can access all global objects, all elements of the current UI session but cannot access other sessions.
|
|
72
|
+
|
|
73
|
+
### Examples
|
|
74
|
+
|
|
75
|
+
#### Refreshing a DataGrid
|
|
76
|
+
|
|
77
|
+
As the DataGrid is a session object, the NetLogic to refresh a DataGrid must be placed at a session level
|
|
78
|
+
|
|
79
|
+
#### Creating a new alarm
|
|
80
|
+
|
|
81
|
+
As the Alarm is a global object, the NetLogic can be either at the session level or at a global level
|
|
82
|
+
|
|
83
|
+
#### Performing an SQL query
|
|
84
|
+
|
|
85
|
+
Here it depends on the final goal:
|
|
86
|
+
|
|
87
|
+
- If just inserting data to a database, the NetLogic can be either at the session level or at a global level, the data can be passed as global variable or as argument of the called method
|
|
88
|
+
- If performing a query (such as a SELECT) depending on some parameters from the user, the NetLogic must be placed at the session level (where TextBox or other controls can be accessed)
|
|
89
|
+
- If performing a query (such as a SELECT or an INSERT) depending on some values from the controller (PLC), the NetLogic can be placed either at a global or session level
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Log messages
|
|
2
|
+
|
|
3
|
+
Log messages can be displayed both at RunTime and DesignTime, at DesignTime logs will appear in `Optix Studio Output` while at RunTime they will appear in the `Emulator output` or the RunTime log
|
|
4
|
+
|
|
5
|
+
## General syntax
|
|
6
|
+
|
|
7
|
+
`Log . [level] ( [optional category] , [ message content] );`
|
|
8
|
+
|
|
9
|
+
## Examples
|
|
10
|
+
|
|
11
|
+
Different log levels can be triggered and then filtered in `Optix Studio Output` and `Emulator output`. Specifying an optional custom `Category` helps you understanding where this line was generated (for example the NetLogic name and/or the method name)
|
|
12
|
+
|
|
13
|
+
- `Log.Error(category, message);`
|
|
14
|
+
- `Log.Warning(category, message);`
|
|
15
|
+
- `Log.Info(category, message);`
|
|
16
|
+
- `Log.Debug(category, message);`
|
|
17
|
+
- `Log.Verbose1(category, message);`
|
|
18
|
+
- `Log.Verbose2(category, message);`
|
|
19
|
+
|
|
20
|
+
## Get the node path for any project node
|
|
21
|
+
|
|
22
|
+
- `Log.Node(IUANode);` -> Returns a string containing the node path for a specific element
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Managing Aliases
|
|
2
|
+
|
|
3
|
+
## Delete the value from an Alias
|
|
4
|
+
|
|
5
|
+
```csharp
|
|
6
|
+
Project.Current.Get("UI/Pages/MainPage").SetAlias("MainBoilerAlias", NodeId.Empty);
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Delete the Kind property of an Alias
|
|
10
|
+
|
|
11
|
+
```csharp
|
|
12
|
+
Project.Current.Get<Alias>("UI/Pages/MainPage/MainBoilerAlias").Kind = NodeId.Empty;
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Connect an Alias to a node
|
|
16
|
+
|
|
17
|
+
```csharp
|
|
18
|
+
// Get the target variable
|
|
19
|
+
var targetVariable = Project.Current.Get<Tag>("CommDriver/RAEthernetIP/Station1/InletPump");
|
|
20
|
+
// Set the alias to the target value
|
|
21
|
+
Project.Current.Get("UI/Pages/MainPage").SetAlias("MainBoilerAlias", targetVariable.NodeId);
|
|
22
|
+
```
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Manipulate Optix nodes
|
|
2
|
+
|
|
3
|
+
> [!WARNING]
|
|
4
|
+
> This page describes low-level features and should be used with caution as they can potentially lead to unexpected results or even break your project.
|
|
5
|
+
> These features are not officially supported and may be subject to changes in future releases.
|
|
6
|
+
|
|
7
|
+
> [!NOTE]
|
|
8
|
+
> Nodes behavior can only be manipulated at runtime.
|
|
9
|
+
|
|
10
|
+
## Stopping an Optix node
|
|
11
|
+
|
|
12
|
+
Once nodes are stopped, they stop interacting with the system for example an ODBC database node will stop processing queries, a Trend node will stop logging data, an Alarm node will stop monitoring conditions, etc.
|
|
13
|
+
|
|
14
|
+
This can be useful in some scenarios to disable some components (like the `Signing Workflow` node) or to temporarily stop some processes (like a `Trend` node) without deleting them.
|
|
15
|
+
|
|
16
|
+
```csharp
|
|
17
|
+
/// <summary>
|
|
18
|
+
/// Stops an Optix node if it is started
|
|
19
|
+
/// </summary>
|
|
20
|
+
/// <param name="node">The node to be stopped</param>
|
|
21
|
+
private void StopNode(IUANode node)
|
|
22
|
+
{
|
|
23
|
+
// Only objects can be started/stopped
|
|
24
|
+
if (node is IUAObject nodeObject && node.Status == NodeStatus.Started)
|
|
25
|
+
{
|
|
26
|
+
// Stop the node
|
|
27
|
+
nodeObject.Stop();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Starting an Optix node
|
|
34
|
+
|
|
35
|
+
```csharp
|
|
36
|
+
/// <summary>
|
|
37
|
+
/// Starts an Optix node if it is stopped
|
|
38
|
+
/// </summary>
|
|
39
|
+
/// <param name="node">The node to be started</param>
|
|
40
|
+
private void StartNode(IUANode node)
|
|
41
|
+
{
|
|
42
|
+
// Only objects can be started/stopped
|
|
43
|
+
if (node is IUAObject nodeObject && node.Status != NodeStatus.Started)
|
|
44
|
+
{
|
|
45
|
+
// Start the node
|
|
46
|
+
nodeObject.Start();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Restarting an Optix node
|
|
53
|
+
|
|
54
|
+
This is simply a combination of the previous two methods
|
|
55
|
+
|
|
56
|
+
```csharp
|
|
57
|
+
/// <summary>
|
|
58
|
+
/// Restarts an Optix node if it is started
|
|
59
|
+
/// </summary>
|
|
60
|
+
/// <param name="node">The node to be restarted</param>
|
|
61
|
+
private void RestartNode(IUANode node)
|
|
62
|
+
{
|
|
63
|
+
// Only objects can be started/stopped
|
|
64
|
+
if (node is IUAObject nodeObject && node.Status == NodeStatus.Started)
|
|
65
|
+
{
|
|
66
|
+
// Restart the node
|
|
67
|
+
nodeObject.Stop();
|
|
68
|
+
nodeObject.Start();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
}
|
|
72
|
+
```
|