@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.
Files changed (60) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +6 -6
  3. package/cordis.patch.yml +1 -1
  4. package/entries/netlogic.js +266 -0
  5. package/entries/optix.js +13 -0
  6. package/package.json +3 -2
  7. package/vendor/optix/SKILL.md +68 -0
  8. package/vendor/optix/netlogic/SOURCE.json +12 -0
  9. package/vendor/optix/netlogic/index.json +1987 -0
  10. package/vendor/optix/netlogic/pages/accessing-project-nodes.md +113 -0
  11. package/vendor/optix/netlogic/pages/advanced-svg.md +124 -0
  12. package/vendor/optix/netlogic/pages/alarming.md +489 -0
  13. package/vendor/optix/netlogic/pages/async-tasks.md +236 -0
  14. package/vendor/optix/netlogic/pages/audit-signature.md +91 -0
  15. package/vendor/optix/netlogic/pages/colors.md +122 -0
  16. package/vendor/optix/netlogic/pages/companion-specs.md +77 -0
  17. package/vendor/optix/netlogic/pages/creating-objects.md +370 -0
  18. package/vendor/optix/netlogic/pages/database-interaction.md +437 -0
  19. package/vendor/optix/netlogic/pages/datagrids.md +123 -0
  20. package/vendor/optix/netlogic/pages/debugging-tips.md +605 -0
  21. package/vendor/optix/netlogic/pages/designtime-tags-import.md +531 -0
  22. package/vendor/optix/netlogic/pages/dialog-boxes.md +150 -0
  23. package/vendor/optix/netlogic/pages/dispatch-events.md +200 -0
  24. package/vendor/optix/netlogic/pages/dynamic-links.md +825 -0
  25. package/vendor/optix/netlogic/pages/element-access.md +1208 -0
  26. package/vendor/optix/netlogic/pages/engineering-units.md +298 -0
  27. package/vendor/optix/netlogic/pages/events.md +340 -0
  28. package/vendor/optix/netlogic/pages/execute-command.md +202 -0
  29. package/vendor/optix/netlogic/pages/fto-studio-cli.md +99 -0
  30. package/vendor/optix/netlogic/pages/good-practices.md +50 -0
  31. package/vendor/optix/netlogic/pages/information-model.md +89 -0
  32. package/vendor/optix/netlogic/pages/log-output.md +22 -0
  33. package/vendor/optix/netlogic/pages/managing-aliases.md +22 -0
  34. package/vendor/optix/netlogic/pages/manipulate-nodes.md +72 -0
  35. package/vendor/optix/netlogic/pages/mqtt.md +240 -0
  36. package/vendor/optix/netlogic/pages/netlogic-lifecycle.md +270 -0
  37. package/vendor/optix/netlogic/pages/netlogic-overview.md +155 -0
  38. package/vendor/optix/netlogic/pages/opcua.md +325 -0
  39. package/vendor/optix/netlogic/pages/plc-tags.md +219 -0
  40. package/vendor/optix/netlogic/pages/queries.md +1297 -0
  41. package/vendor/optix/netlogic/pages/random-generation.md +90 -0
  42. package/vendor/optix/netlogic/pages/recipes.md +375 -0
  43. package/vendor/optix/netlogic/pages/recipex.md +2383 -0
  44. package/vendor/optix/netlogic/pages/regex.md +15 -0
  45. package/vendor/optix/netlogic/pages/register-observers.md +346 -0
  46. package/vendor/optix/netlogic/pages/reports.md +141 -0
  47. package/vendor/optix/netlogic/pages/resource-uri.md +308 -0
  48. package/vendor/optix/netlogic/pages/runtime-tags-import.md +280 -0
  49. package/vendor/optix/netlogic/pages/serial-port.md +357 -0
  50. package/vendor/optix/netlogic/pages/sessions.md +58 -0
  51. package/vendor/optix/netlogic/pages/system-node.md +153 -0
  52. package/vendor/optix/netlogic/pages/template-library.md +127 -0
  53. package/vendor/optix/netlogic/pages/threading.md +236 -0
  54. package/vendor/optix/netlogic/pages/translations.md +417 -0
  55. package/vendor/optix/netlogic/pages/trends.md +128 -0
  56. package/vendor/optix/netlogic/pages/ui-animations.md +115 -0
  57. package/vendor/optix/netlogic/pages/users-groups.md +416 -0
  58. package/vendor/optix/netlogic/pages/variables-formatting.md +16 -0
  59. package/vendor/optix/netlogic/pages/variables-interaction.md +557 -0
  60. package/vendor/optix/netlogic/pages/variables-structure.md +155 -0
@@ -0,0 +1,325 @@
1
+ # OPC/UA
2
+
3
+ ## Node's references
4
+
5
+ In OPC/UA, some nodes may have additional references are explicit relationship (a named pointer) from one Node to another
6
+
7
+ ```csharp
8
+ [ExportMethod]
9
+ public void ReadReference(NodeId element)
10
+ {
11
+ // Get to the element where we want to investigate the OPC/UA references
12
+ IUANode targetNode = InformationModel.Get(element);
13
+ // Assuming the node exists
14
+ if (targetNode != null)
15
+ {
16
+ // Loop per each reference and list it in the console output
17
+ foreach (var item in targetNode.Refs.GetReferences())
18
+ {
19
+ Log.Info("References", $"ReferenceTypeId: {item.ReferenceTypeId}, TargetNodeId: {item.TargetNodeId}, TargetNode: {item.TargetNode.BrowseName}");
20
+ }
21
+ }
22
+ else
23
+ {
24
+ Log.Info("References", "Cannot find such node in the current project");
25
+ }
26
+ }
27
+ ```
28
+
29
+ ## Read arbitrary tag from an OPC/UA Server
30
+
31
+ > [!WARNING]
32
+ > This method involves some low-level OPC/UA operations and may not be recommended for production usage. It is recommended to use the OPC/UA Client TagImporter instead.
33
+
34
+ > [!NOTE]
35
+ > This method can only be used at runtime.
36
+
37
+ This method allows to read any OPC/UA variables from a remote server knowing its `NameSpaceIndex` and `ID`. The following examples reads the `Server > ServerStatus > State` (0/2259) variable from the UaAnsiCServer from the OPC Foundation
38
+
39
+ ### FactoryTalk Optix 1.6.x or older
40
+
41
+ ```csharp
42
+ // This list comes from the OPC/UA specification
43
+ enum OpcAttributes
44
+ {
45
+ NodeId = 1,
46
+ NodeClass = 2,
47
+ BrowseName = 3,
48
+ DisplayName = 4,
49
+ Description = 5,
50
+ WriteMask = 6,
51
+ UserWriteMask = 7,
52
+ IsAbstract = 8,
53
+ Symmetric = 9,
54
+ InverseName = 10,
55
+ ContainsNoLoops = 11,
56
+ EventNotifier = 12,
57
+ Value = 13,
58
+ DataType = 14,
59
+ ValueRank = 15,
60
+ ArrayDimensions = 16,
61
+ AccessLevel = 17,
62
+ UserAccessLevel = 18,
63
+ MinimumSamplingInterval = 19,
64
+ Historizing = 20,
65
+ Executable = 21,
66
+ UserExecutable = 22,
67
+ DataTypeDefinition = 23,
68
+ RolePermissions = 24,
69
+ UserRolePermissions = 25,
70
+ AccessRestrictions = 26,
71
+ AccessLevelEx = 27,
72
+ BrowsePath = 64,
73
+ Quality = 65,
74
+ StatusCode = 66,
75
+ SourceTimestamp = 67,
76
+ ServerTimestamp = 68,
77
+ ActualDataType = 69,
78
+ ActualArrayDimensions = 70
79
+ };
80
+
81
+ [ExportMethod]
82
+ public void ReadArbitraryValueFromRemoteOpcUaServer(int nameSpaceIndex, uint identifier)
83
+ {
84
+ // Get the OPC/UA Client from the project
85
+ var opc = Project.Current.Get<OPCUAClient>("OPC-UA/OPCUAClient1");
86
+
87
+ // OPC/UA Variable to read from the remote server
88
+ object[] opcVariable = new object[3]
89
+ {
90
+ new NodeId(nameSpaceIndex, identifier), // NodeId of the variable to read (UaExpert can be used to get these values)
91
+ OpcAttributes.Value, // Attribute ID to read (13 = Value), see enumeration above
92
+ new uint[0] // Indexes, for simple scalar tags this is an empty array
93
+ };
94
+
95
+ // Create a Struct object that holds the information of the remote variable to be passed to the OPC/UA Client
96
+ // Syntax: new Struct(NodeId dataTypeId, object[] values)
97
+ // Where:
98
+ // - DataTypeId is the NodeId of the DataType expected as input argument of the method (50 is the ReadValues DataType)
99
+ // - Values is an array containing the information of the variable to read
100
+ UAManagedCore.Struct asd = new Struct(new NodeId(FTOptix.OPCUAClient.ObjectTypes.OPCUAClient.NamespaceIndex, 50), opcVariable);
101
+
102
+ // The ExecuteMethod method of the OPC/UA Client is used to call the ReadValues method of the OPC/UA Server
103
+ // This expects an object[] as input parameters, so we will create a mono-dimensional array with the Struct object
104
+ object[] inputArgs = new object[1];
105
+ // The first element of the array is the Struct object that we created
106
+ inputArgs[0] = new UAManagedCore.Struct[1];
107
+ // The first element of the Struct array is the variable information to read
108
+ ((UAManagedCore.Struct[])inputArgs[0])[0] = asd;
109
+
110
+ // The ExecuteMethod method returns an object[] with the output parameters of the method
111
+ object[] outputArgs = new object[1];
112
+
113
+ // Call the ReadValues method of the OPC/UA Server
114
+ opc.ExecuteMethod("ReadValues", inputArgs, out outputArgs); // OPCUA\OPCUAClient\OPCUAClient\Module.xml.in
115
+
116
+ // The outputArgs array contains the output parameters of the ReadValues method
117
+ var output = (UAManagedCore.Struct[])outputArgs[0];
118
+ Log.Info(output[0].Values[0].ToString());
119
+ }
120
+ ```
121
+
122
+ ### FactoryTalk Optix 1.7.x or newer
123
+
124
+ ```csharp
125
+ struct ReadResult
126
+ {
127
+ public object Value;
128
+ public uint StatusCode;
129
+
130
+ public ReadResult(UAManagedCore.Struct s)
131
+ {
132
+ Value = s.Values[0];
133
+ StatusCode = (uint)s.Values[1];
134
+ }
135
+
136
+ public bool IsGood => (StatusCode & 0xC0000000) == 0;
137
+
138
+ public string StatusName
139
+ {
140
+ get
141
+ {
142
+ if (Enum.IsDefined(typeof(OpcUaStatusCode), StatusCode))
143
+ return ((OpcUaStatusCode)StatusCode).ToString();
144
+ return $"0x{StatusCode:X8}";
145
+ }
146
+ }
147
+ }
148
+
149
+ enum OpcUaStatusCode : uint
150
+ {
151
+ Good = 0x00000000,
152
+ BadUnexpectedError = 0x80010000,
153
+ BadInternalError = 0x80020000,
154
+ BadNodeIdInvalid = 0x80330000,
155
+ BadAttributeIdInvalid = 0x80350000,
156
+ BadNodeIdUnknown = 0x80340000,
157
+ BadNotReadable = 0x803A0000,
158
+ BadNotWritable = 0x803B0000,
159
+ BadTypeMismatch = 0x80740000,
160
+ BadInvalidArgument = 0x80AB0000,
161
+ BadOutOfRange = 0x803C0000,
162
+ BadNoCommunication = 0x80310000,
163
+ BadTimeout = 0x800A0000,
164
+ BadServerNotConnected = 0x800D0000,
165
+ BadSessionIdInvalid = 0x80250000,
166
+ BadUserAccessDenied = 0x801F0000,
167
+ BadSecurityModeInsufficient = 0x80E60000,
168
+ BadServiceUnsupported = 0x800B0000,
169
+ }
170
+
171
+ // This list comes from the OPC/UA specification
172
+ enum OpcAttributes
173
+ {
174
+ NodeId = 1,
175
+ NodeClass = 2,
176
+ BrowseName = 3,
177
+ DisplayName = 4,
178
+ Description = 5,
179
+ WriteMask = 6,
180
+ UserWriteMask = 7,
181
+ IsAbstract = 8,
182
+ Symmetric = 9,
183
+ InverseName = 10,
184
+ ContainsNoLoops = 11,
185
+ EventNotifier = 12,
186
+ Value = 13,
187
+ DataType = 14,
188
+ ValueRank = 15,
189
+ ArrayDimensions = 16,
190
+ AccessLevel = 17,
191
+ UserAccessLevel = 18,
192
+ MinimumSamplingInterval = 19,
193
+ Historizing = 20,
194
+ Executable = 21,
195
+ UserExecutable = 22,
196
+ DataTypeDefinition = 23,
197
+ RolePermissions = 24,
198
+ UserRolePermissions = 25,
199
+ AccessRestrictions = 26,
200
+ AccessLevelEx = 27,
201
+ BrowsePath = 64,
202
+ Quality = 65,
203
+ StatusCode = 66,
204
+ SourceTimestamp = 67,
205
+ ServerTimestamp = 68,
206
+ ActualDataType = 69,
207
+ ActualArrayDimensions = 70
208
+ };
209
+
210
+ [ExportMethod]
211
+ public void ReadArbitraryValueFromRemoteOpcUaServer(string namespaceUri, string identifier)
212
+ {
213
+ // Change this to read a different OPC UA attribute
214
+ var attribute = OpcAttributes.Value;
215
+
216
+ // Get the OPC/UA Client from the project
217
+ var opc = Project.Current.Get<OPCUAClient>("OPC-UA/OPCUAClient1");
218
+
219
+ // Find the local namespace index by searching all registered namespaces.
220
+ // FTOptix builds aggregated URIs for dynamic namespaces as "{clientGuid}#{remoteNamespaceUri}",
221
+ // so we match a namespace URI that equals or ends with the provided remote namespace URI.
222
+ var context = LogicObject.Context;
223
+ var localNsIndex = context.GetNamespaceIndexes()
224
+ .Cast<int>()
225
+ .FirstOrDefault(i => {
226
+ var uri = context.GetNamespaceUri(i);
227
+ return uri == namespaceUri || uri.EndsWith("#" + namespaceUri);
228
+ }, -1);
229
+
230
+ if (localNsIndex < 0)
231
+ {
232
+ Log.Error($"Namespace URI '{namespaceUri}' not found. Have you imported some tags in the Optix project?");
233
+ return;
234
+ }
235
+
236
+ FTOptix.Core.ElementAccess ea = new FTOptix.Core.ElementAccess();
237
+ UAValue eaValue = ea; // implicit conversion to UAValue
238
+ var emptyElementAccess = (UAManagedCore.Struct)eaValue.Value;
239
+
240
+ var nodeIdStr = $"ns={localNsIndex};s={identifier}";
241
+
242
+ object[] opcVariable = new object[3]
243
+ {
244
+ new NodeId(localNsIndex, identifier),
245
+ (uint)attribute,
246
+ emptyElementAccess // ElementAccess struct, NOT uint[]
247
+ };
248
+
249
+ // Create a Struct object that holds the information of the remote variable to be passed to the OPC/UA Client
250
+ // Syntax: new Struct(NodeId dataTypeId, object[] values)
251
+ // Where:
252
+ // - DataTypeId is the NodeId of the DataType expected as input argument of the method (50 is the ReadValues DataType)
253
+ // - Values is an array containing the information of the variable to read
254
+ UAManagedCore.Struct asd = new Struct(new NodeId(FTOptix.OPCUAClient.ObjectTypes.OPCUAClient.NamespaceIndex, 50), opcVariable);
255
+
256
+ // The ExecuteMethod method of the OPC/UA Client is used to call the ReadValues method of the OPC/UA Server
257
+ // This expects an object[] as input parameters, so we will create a mono-dimensional array with the Struct object
258
+ object[] inputArgs = new object[1];
259
+ // The first element of the array is the Struct object that we created
260
+ inputArgs[0] = new UAManagedCore.Struct[1];
261
+ // The first element of the Struct array is the variable information to read
262
+ ((UAManagedCore.Struct[])inputArgs[0])[0] = asd;
263
+
264
+ // The ExecuteMethod method returns an object[] with the output parameters of the method
265
+ object[] outputArgs = new object[1];
266
+
267
+ // Call the ReadValues method of the OPC/UA Server
268
+ opc.ExecuteMethod("ReadValues", inputArgs, out outputArgs);
269
+
270
+ // The outputArgs array contains the output parameters of the ReadValues method
271
+ // ReadValueResult struct has 2 fields: [0] = Value, [1] = StatusCode (UInt32)
272
+ var output = (UAManagedCore.Struct[])outputArgs[0];
273
+ var result = new ReadResult(output[0]);
274
+
275
+ if (!result.IsGood)
276
+ {
277
+ Log.Error($"Reading attribute `{attribute}` for `{nodeIdStr}` failed: {result.StatusName} (0x{result.StatusCode:X8})");
278
+ return;
279
+ }
280
+
281
+ Log.Info($"Reading attribute `{attribute}` for `{nodeIdStr}` returned: `{result.Value}`");
282
+ }
283
+ ```
284
+
285
+ ## Change the Status property of a Tag (changing the DataValue structure)
286
+
287
+ This may be needed in some OPC/UA companion specifications where the Status of a tag is used to detect other status that are not only good or bad
288
+
289
+ > [!NOTE]
290
+ > This method can only be used at runtime.
291
+
292
+ ```csharp
293
+ [ExportMethod]
294
+ public void UpdateStatusCode()
295
+ {
296
+ var myVariable = Project.Current.GetVariable("Model/MyFolder/Test");
297
+ // The StatusCode shortcut is read-only
298
+ Log.Info($"Original status code: {myVariable.StatusCode}");
299
+ // new DataValue(UAValue value, uint statusCode, DateTime sourceTimestamp)
300
+ // new DataValue(UAValue value, uint statusCode, DateTime sourceTimestamp, DateTime serverTimestamp)
301
+ myVariable.DataValue = new DataValue(myVariable.Value, 100, DateTime.UtcNow);
302
+ // The StatusCode shortcut is read-only
303
+ Log.Info($"New status code: {myVariable.StatusCode}");
304
+ }
305
+ ```
306
+
307
+ ## Manually create an OPC/UA Client Tag
308
+
309
+ This script presumes that the OPC/UA Client is already configured in the project and at least a tag was imported (needed to get the NameSpaceIndex). It creates a new tag in the project that will be used to read a variable from the OPC/UA Server.
310
+
311
+ > [!NOTE]
312
+ > This method can only be used at designtime.
313
+
314
+ ```csharp
315
+ [ExportMethod]
316
+ public void CreateArbitraryTag()
317
+ {
318
+ // Get the NameSpaceIndex of the OPC/UA Client, the best solution would be to read it from an imported tag.
319
+ // This must be in the `.optix` file of the project, so it must be a known index.
320
+ var nxIndex = LogicObject.Context.GetNamespaceIndex("0ccdea9893ed5102b4fae754ef1732f7#http://www.unifiedautomation.com/DemoServer/");
321
+ // Create a new variable in the project with the NodeId of the OPC/UA variable to read in the remote server
322
+ var variable = LogicObject.Context.NodeFactory.MakeVariable(new NodeId(nxIndex, "Demo.Static.Scalar.Double"), "Double", OpcUa.DataTypes.Double, OpcUa.VariableTypes.BaseDataVariableType);
323
+ // Add the variable to the Objects folder of the OPC/UA client project
324
+ LogicObject.Owner.GetObject("Objects").Add(variable);
325
+ }
@@ -0,0 +1,219 @@
1
+ # Field tags
2
+
3
+ Field tags are functionally identical to Model variables (global variables), but with some additions.
4
+
5
+ Field tags can be forced to read or write a value to the field device (a.k.a. PLC) or to force the value to be synched to the controller with a specific refresh time.
6
+
7
+ > [!NOTE]
8
+ > Tags are automatically synchronized with the controller when they are used in:
9
+ > - A page being displayed in any of the UI sessions
10
+ > - Used in a DataLogger or EventLogger
11
+ > - Used as InputVariable of an Alarm
12
+ > - A ValueChange event is subscribed to the tag
13
+ >
14
+ > If none of these conditions are met, the tag will not be synchronized with the controller and will not be updated, so the following techniques to read or write values to the controller are needed.
15
+
16
+ > [!NOTE]
17
+ > Communication drivers are only loaded at runtime, so all the methods described in this page to read/write/force values to the controller can only be used at runtime, they will not work at design time.
18
+
19
+ ## Introduction
20
+
21
+ Field tags in FactoryTalk Optix provide several methods for efficiently communicating with PLCs, each with different performance characteristics and use cases. Understanding these methods helps optimize both application performance and network traffic.
22
+
23
+ ### Tags access methods
24
+
25
+ 1. **RemoteVariableSynchronizer**
26
+
27
+ - Optimizes data exchange by bundling requests and minimizing network traffic
28
+ - Efficient for continuous monitoring of multiple tags at specified intervals
29
+ - Expensive during initial setup but provides optimized ongoing communication
30
+ - After setup, exchanges tag values in the most optimized way possible
31
+ - Best for scenarios requiring regular polling of many dispersed tags
32
+
33
+ 2. **RemoteRead/RemoteWrite**
34
+
35
+ - Provides synchronous, on-demand reading/writing of specific tags
36
+ - Best for atomic operations when immediate values are needed
37
+ - Less efficient for large numbers of tags
38
+ - InformationModel.RemoteRead() can bundle multiple tag reads for better performance
39
+
40
+ ### RA Ethernet/IP Controller Array Reading (Optix 1.6+)
41
+
42
+ Starting from FactoryTalk Optix 1.6.x, a major optimization was introduced in the RA Ethernet/IP driver, which allows reading large arrays directly from the controller, significantly improving performance for bulk data operations. The user does not need to manually specify the read mode, as the system automatically optimizes the read operation for large arrays if possible.
43
+
44
+ This new algorithm:
45
+ - Is highly efficient for reading large blocks of contiguous data
46
+ - Can read large arrays (e.g., 10K DINT array) in ~200ms
47
+ - Requires data to be organized in arrays within the controller
48
+
49
+ Example:
50
+
51
+ ```csharp
52
+ var tag = Project.Current.Get<Tag>("CommDrivers/RAEtherNet_IPDriver1/RAEtherNet_IPStation1/Tags/Controller Tags/DINT_A10K");
53
+ var value = tag.RemoteRead();
54
+ var arrayInts = (int[])value.Value;
55
+ ```
56
+
57
+ ### Performance Considerations
58
+
59
+ - **Network Traffic**: The `RemoteVariableSynchronizer` drastically reduces network load. For example, reading 10K DINTs with the RA Ethernet/IP driver will require 10x less network traffic when using a `RemoteVariableSynchronizer` compared to a `RemoteRead`.
60
+ - **Cached Values**: Using `Project.Current.GetVariable("MyTag").Value` accesses the last cached value without triggering new controller communication
61
+ - **Hardware Impact**: Consider your network infrastructure, PLC capabilities and host machine capabilities when designing communication patterns
62
+ - **Synchronization Frequency**: Always specify appropriate refresh intervals in Remote Variable Synchronizer to prevent overwhelming the controller (`TimeSpan.FromSeconds()`)
63
+
64
+ Learning to use these methods effectively can greatly enhance the performance and responsiveness of your FactoryTalk Optix applications, especially in scenarios involving large datasets or frequent updates.
65
+
66
+ For example:
67
+
68
+ - A `RemoteVariableSynchronizer` can be created in a global NetLogic to continuously monitor and synchronize multiple tags, ensuring they are always up-to-date without excessive network traffic.
69
+ - All the NetLogic in the project can simply access the synchronized variables using the `Project.Current.GetVariable("MyTag").Value` (either getting or setting the value) method without needing to perform individual reads, which is particularly useful for applications with many tags that need regular updates.
70
+
71
+ ## Remote Variable synchronizer
72
+
73
+ The `RemoteVariableSynchronizer` allows users to keep some variables synched even if not in use by any current page, it will keep all the children elements updated with the field. This method handles an automatic optimization of the Tags in order to minimize the number of read/write from the controller (while the `RemoteRead` performs a request per each tag and it is much slower)
74
+
75
+ > [!NOTE]
76
+ > When the `RemoteVariableSynchronizer` is created, it will collect all the children variables and create an optimized read request to the controller, so it is important to create it only once and not every time a page is opened or a variable is needed.
77
+
78
+ > [!WARNING]
79
+ > The `RemoteVariableSynchronizer` might have significant impact on the communication driver, especially if used with a large number of tags. It is recommended to use it wisely and only for a small number of tags.
80
+
81
+ ### Providing a TimeSpan argument
82
+
83
+ The `TimeSpan` argument of the `RemoteVariableSynchronizer` should be used to avoid overloading the field device. If the `RemoteVariableSynchronizer` is created without any arguments, it defaults to full-speed synchronization, which can be too fast for the field device to handle. This can lead to performance issues because the iPC (industrial PC) is typically faster than the controller. By specifying a `TimeSpan` argument, you define a refresh interval between subsequent reads, which helps in managing the load on the field device and ensures smoother operation.
84
+
85
+ Avoid this constructor: `variableSynchronizer = new RemoteVariableSynchronizer();`
86
+ Use this instead: `variableSynchronizer = new RemoteVariableSynchronizer(new TimeSpan.FromSeconds(5));`
87
+
88
+ The argument can be configure as needed, depending on the speed of the synchronization which is required.
89
+
90
+ ### RemoteVariableSynchronizer example
91
+
92
+ ```csharp
93
+ public override void Start()
94
+ {
95
+ // Get the field variable
96
+ motorSpeed = LogicObject.Owner.GetVariable("Speed");
97
+ // Create the VariableSynchronizer object and request the update every 5 seconds
98
+ var updateRate = TimeSpan.FromSeconds(5);
99
+ variableSynchronizer = new RemoteVariableSynchronizer(updateRate);
100
+ // Add the variables to the synchronizer
101
+ variableSynchronizer.Add(motorSpeed);
102
+ // Add the event to listen to value changes
103
+ motorSpeed.VariableChange += MotorSpeed_VariableChange;
104
+ }
105
+ public override void Stop()
106
+ {
107
+ // Destroy the synchronized on stop
108
+ variableSynchronizer?.Dispose();
109
+ }
110
+ private void MotorSpeed_VariableChange(object sender, VariableChangeEventArgs e)
111
+ {
112
+ // Event listener when value change is detected
113
+ if (motorSpeed.Value > 200) {
114
+ Log.Warning("Speed limit reached!");
115
+ }
116
+ }
117
+ private IUAVariable motorSpeed;
118
+ private RemoteVariableSynchronizer variableSynchronizer;
119
+ ```
120
+
121
+ ## RemoteRead
122
+
123
+ Remote read is a inline read of the field value, code is stopped to read the value, once the variable is updated, the code keeps running
124
+
125
+ > [!NOTE]
126
+ > The `ChildrenRemoteRead` is just as "slow" as the `RemoteRead`, it is just a recursive approach to a normal `RemoteRead`
127
+
128
+ ### Read simple tags
129
+
130
+ ```csharp
131
+ [ExportMethod]
132
+ public void ReadBitFromPlcInteger(out int value)
133
+ {
134
+ // Get the plc tag to read
135
+ var tag1 = Project.Current.GetVariable("CommDrivers/RAEtherNet_IPDriver1/RAEtherNet_IPStation1/Tags/Program:CustomUdtProgram/program_dint_1D");
136
+ // Read the new values
137
+ var variableValue = tag1.RemoteRead();
138
+ // Do something with the values
139
+ Log.Info($"Value of the plc tag is: {variableValue}");
140
+ }
141
+ ```
142
+
143
+ ### Read arrays
144
+
145
+ ```csharp
146
+ [ExportMethod]
147
+ public void ReadBitFromPlcInteger(out int value)
148
+ {
149
+ // Get the plc tag to read
150
+ var tag1 = Project.Current.Get<FTOptix.RAEtherNetIP.Tag>("CommDrivers/RAEtherNet_IPDriver1/RAEtherNet_IPStation1/Tags/Program:CustomUdtProgram/program_dint_1D");
151
+ // Create the remote read object
152
+ var remoteVariables = new List<RemoteVariable>()
153
+ {
154
+ // Add the variables to read to the list
155
+ new RemoteVariable(tag1),
156
+ };
157
+ // Read the new values
158
+ var values = InformationModel.RemoteRead(remoteVariables).ToList();
159
+ // Get the field values to a C# variable
160
+ var plcArray = (int[])values[0].Value.Value;
161
+ // Do something with the values
162
+ Log.Info($"Value of first element of the array is {plcArray[0]}");
163
+ Log.Info($"Bit 5 of first element of the array is {GetBitValue(plcArray[0], 5)}");
164
+ }
165
+
166
+ private bool GetBitValue(int inputValue, int bitPosition)
167
+ {
168
+ // Read a specific bit from a word
169
+ var array = new BitArray(new int[] { inputValue });
170
+ return array[bitPosition];
171
+ }
172
+ ```
173
+
174
+ ### Adding a timeout
175
+
176
+ ```csharp
177
+ // Get the variable to update
178
+ var currentSpeed = Project.Current.GetVariable("Model/Motor/Speed");
179
+ // Try to get the most updated value with a timeout of 100ms
180
+ var newValue = currentSpeed.RemoteRead(100.0);
181
+ ```
182
+
183
+ ## RemoteWrite
184
+
185
+ Similar to the `RemoteRead`, when a value should be sent immediately to the field, a `RemoteWrite` method can be used
186
+
187
+ ### Write a simple value
188
+
189
+ ```csharp
190
+ // Which tag to update
191
+ var currentSpeed = Project.Current.GetVariable("Model/Motor/Speed");
192
+ // Write the new value (no write timeout)
193
+ currentSpeed.RemoteWrite(42);
194
+ ```
195
+
196
+ ### Write multiple values
197
+
198
+ ```csharp
199
+ // Which tags to update
200
+ var tag1 = Project.Current.Get<Tag>("CommDrivers/CodesysDriver1/CodesysStation1/Tags/Application/PLC_PRG/VAR1");
201
+ var tag3 = Project.Current.Get<Tag>("CommDrivers/CodesysDriver1/CodesysStation1/Tags/Application/PLC_PRG/VAR3");
202
+ // Create the RemoteWrite object
203
+ var remoteVariableValues = new List<RemoteVariableValue>()
204
+ {
205
+ new RemoteVariableValue(tag3, 0),
206
+ new RemoteVariableValue(tag1, 123)
207
+ };
208
+ // Perform the write operation (no write timeout)
209
+ InformationModel.RemoteWrite(remoteVariableValues);
210
+ ```
211
+
212
+ ### Adding a timeout
213
+
214
+ ```csharp
215
+ // Get the variable to update
216
+ var currentSpeed = Project.Current.GetVariable("Model/Motor/Speed");
217
+ // Try to write the value 150 with a timeout of 100ms
218
+ currentSpeed.RemoteWrite(150, 100.0);
219
+ ```