@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,90 @@
1
+ # Random generation
2
+
3
+ When using the random generation, if the loop is particularly fast the system tick may not be different and the same number is generated twice, a small delay may help
4
+
5
+ ## Random number generator
6
+
7
+ ```csharp
8
+ Random r = new Random();
9
+
10
+ public void Start()
11
+ {
12
+ // Create a random integer value
13
+ Int variableRandom = r.Next(min, max);
14
+ // Create a random double value
15
+ Double variableRandom = r.NextDouble()
16
+ }
17
+ ```
18
+
19
+ ## Random strings generator
20
+
21
+ ### Using custom character list
22
+
23
+ ```csharp
24
+ private static Random random = new Random();
25
+
26
+ public static string RandomString(int stringLength)
27
+ {
28
+ // Set the subset of characters to be used
29
+ const string usableCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
30
+ // Return the random string
31
+ return new string(Enumerable.Repeat(usableCharacters, stringLength)
32
+ .Select(s => s[random.Next(s.Length)]).ToArray());
33
+ }
34
+
35
+ public void Start()
36
+ {
37
+ // Call the method to generate the string
38
+ String randomString = RandomString(10);
39
+ }
40
+ ```
41
+
42
+ ### Using random GUID
43
+
44
+ Please note, this should **never** be used to generate passwords or tokens as it's not random enough. If high security is required, use the [RandomNumberGenerator](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.randomnumbergenerator?view=net-8.0) instead.
45
+
46
+ ```csharp
47
+ private string RandomString()
48
+ {
49
+ // Create a new guid
50
+ Guid newGuid = Guid.NewGuid();
51
+ // Remove dashes to keep only alphanumeric characters
52
+ return newGuid.ToString().Replace("-", "");
53
+ }
54
+ ```
55
+
56
+ ```csharp
57
+ private static string GenerateRandomString(int length = 8)
58
+ {
59
+ const string alphanumericChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
60
+
61
+ using var rng = System.Security.Cryptography.RandomNumberGenerator.Create();
62
+ var bytes = new byte[length];
63
+ rng.GetBytes(bytes);
64
+
65
+ var chars = new char[length];
66
+ for (int i = 0; i < length; i++)
67
+ {
68
+ chars[i] = alphanumericChars[bytes[i] % alphanumericChars.Length];
69
+ }
70
+
71
+ return new string(chars);
72
+ }
73
+ ```
74
+
75
+ This one should be better
76
+
77
+ ```csharp
78
+ [ExportMethod]
79
+ public static void GenerateRandomString(out string randomString)
80
+ {
81
+ // Create a random string generator using the RandomNumberGenerator class
82
+ using var rng = RandomNumberGenerator.Create();
83
+ // Create a byte array to store the random values
84
+ var randomBytes = new byte[32];
85
+ // Fill the byte array with random values
86
+ rng.GetBytes(randomBytes);
87
+ // Convert the byte array to a string
88
+ randomString = Convert.ToBase64String(randomBytes).Substring(0, 32);
89
+ }
90
+ ```
@@ -0,0 +1,375 @@
1
+ # Recipes
2
+
3
+ Recipes in FactoryTalk Optix provide a structured way to store and manage sets of parameters that can be loaded into your application. They are particularly useful for manufacturing processes where different product variants require different machine settings.
4
+
5
+ > [!WARNING]
6
+ > This section is a work in progress and might be updated with more examples and explanations in the future.
7
+ > Please report any issues or suggestions to the FactoryTalk Optix support or community forums.
8
+
9
+ > [!NOTE]
10
+ > The legacy recipes engine in FactoryTalk Optix allows a maximum of 2048 ingredients per recipe. This limit is imposed by the underlying database system and cannot be changed.
11
+
12
+ > [!NOTE]
13
+ > The content of this page can only be applied to the legacy recipe module of FactoryTalk Optix. The reference guide for the new recipe module can be accessed [here](./recipex.md).
14
+
15
+ ## Recipe System Overview
16
+
17
+ The recipe system in FactoryTalk Optix consists of three main components:
18
+
19
+ 1. **Recipe Schema**: Defines what variables are included in a recipe
20
+ 2. **Recipe Store**: The database where recipe data is stored
21
+ 3. **Recipe Controller**: Manages loading, saving, and applying recipes
22
+
23
+ > [!NOTE]
24
+ > Recipes work by mapping values between a database table and variables in your project. The schema defines this mapping.
25
+
26
+ ## Working with Recipe Schemas
27
+
28
+ A recipe schema links variables in your project model to columns in a database table. When recipes are loaded or saved, values are transferred between these locations.
29
+
30
+ ### Creating a Recipe Schema manually
31
+
32
+ 1. Add a Schema to the Recipes folder
33
+ 2. Set the target node (typically the Model folder)
34
+ 3. Set the store where recipes will be saved
35
+ 4. Use the UI to select variables to include in the schema
36
+
37
+ ### Modifying Recipe Schemas programmatically
38
+
39
+ Recipe schemas can be modified at runtime using NetLogic. This is useful when you need to dynamically change which variables are included in recipes.
40
+
41
+ ```csharp
42
+ [ExportMethod]
43
+ public void UpdateRecipeSchema()
44
+ {
45
+ // Get the recipe schema from the project
46
+ var legacyRecipeSchema = Project.Current.Get<FTOptix.Recipe.RecipeSchema>("Recipes/MyRecipeSchema");
47
+
48
+ // Get the EditModel and Root objects
49
+ var editModel = legacyRecipeSchema.GetObject("EditModel");
50
+ var root = legacyRecipeSchema.GetObject("Root");
51
+
52
+ // Clear existing schema
53
+ if (editModel != null)
54
+ editModel.Children.ToList().ForEach(child => child.Delete());
55
+ if (root != null)
56
+ root.Children.ToList().ForEach(child => child.Delete());
57
+
58
+ // Add variables to the schema
59
+ var targetNode = InformationModel.Get(legacyRecipeSchema.TargetNode);
60
+ var speedVar = targetNode.GetVariable("MotorSpeed");
61
+
62
+ if (speedVar != null)
63
+ {
64
+ // Add to EditModel and Root
65
+ var editVar = InformationModel.MakeVariable("MotorSpeed", speedVar.DataType);
66
+ var rootVar = InformationModel.MakeVariable("MotorSpeed", speedVar.DataType);
67
+ editModel.Add(editVar);
68
+ root.Add(rootVar);
69
+
70
+ // Update database columns
71
+ var store = InformationModel.Get<Store>(legacyRecipeSchema.Store);
72
+ var table = store.Tables.Get(legacyRecipeSchema.BrowseName);
73
+ if (table != null)
74
+ {
75
+ var column = InformationModel.Make<StoreColumn>("MotorSpeed");
76
+ column.DataType = speedVar.DataType;
77
+ table.Columns.Add(column);
78
+ }
79
+ }
80
+ }
81
+ ```
82
+
83
+ ### Adding hierarchical variables to a Recipe Schema
84
+
85
+ Variables in nested folders or objects require special handling. The code below demonstrates how to add variables from various locations in your project model:
86
+
87
+ ```csharp
88
+ [ExportMethod]
89
+ public void AddVariablesToRecipeSchema()
90
+ {
91
+ // Get the recipe schema
92
+ var legacyRecipeSchema = Project.Current.Get<FTOptix.Recipe.RecipeSchema>("Recipes/MyRecipeSchema");
93
+ if (legacyRecipeSchema == null)
94
+ {
95
+ Log.Error("Recipe schema not found");
96
+ return;
97
+ }
98
+
99
+ // Get or create EditModel and Root
100
+ var editModel = legacyRecipeSchema.GetObject("EditModel") ?? InformationModel.MakeObject("EditModel");
101
+ var root = legacyRecipeSchema.GetObject("Root") ?? InformationModel.MakeObject("Root");
102
+
103
+ if (legacyRecipeSchema.GetObject("EditModel") == null)
104
+ legacyRecipeSchema.Add(editModel);
105
+ if (legacyRecipeSchema.GetObject("Root") == null)
106
+ legacyRecipeSchema.Add(root);
107
+
108
+ // Get the database table
109
+ var store = InformationModel.Get<Store>(legacyRecipeSchema.Store);
110
+ var table = store.Tables.Get(legacyRecipeSchema.BrowseName);
111
+
112
+ // Variables to add
113
+ var variablePaths = new List<string>
114
+ {
115
+ "Settings/Temperature",
116
+ "Settings/Speed",
117
+ "Settings/BatchSize"
118
+ };
119
+
120
+ // Add each variable to the schema
121
+ var targetNode = InformationModel.Get(legacyRecipeSchema.TargetNode);
122
+ foreach (var path in variablePaths)
123
+ {
124
+ AddVariableToSchema(targetNode, path, editModel, root, table);
125
+ }
126
+ }
127
+
128
+ private void AddVariableToSchema(IUANode targetNode, string path, IUAObject editModel,
129
+ IUAObject root, Table table)
130
+ {
131
+ // Get the variable
132
+ var variable = targetNode.GetVariable(path);
133
+ if (variable == null)
134
+ {
135
+ Log.Error($"Variable {path} not found");
136
+ return;
137
+ }
138
+
139
+ // Create path segments
140
+ var segments = path.Split('/');
141
+ var varName = segments.Last();
142
+ var folders = segments.Take(segments.Length - 1).ToArray();
143
+
144
+ // Create folders in EditModel and Root if needed
145
+ var currentEditFolder = editModel;
146
+ var currentRootFolder = root;
147
+
148
+ foreach (var folder in folders)
149
+ {
150
+ var editChildFolder = currentEditFolder.GetObject(folder);
151
+ if (editChildFolder == null)
152
+ {
153
+ editChildFolder = InformationModel.MakeObject(folder);
154
+ currentEditFolder.Add(editChildFolder);
155
+ }
156
+ currentEditFolder = editChildFolder;
157
+
158
+ var rootChildFolder = currentRootFolder.GetObject(folder);
159
+ if (rootChildFolder == null)
160
+ {
161
+ rootChildFolder = InformationModel.MakeObject(folder);
162
+ currentRootFolder.Add(rootChildFolder);
163
+ }
164
+ currentRootFolder = rootChildFolder;
165
+ }
166
+
167
+ // Add variables to the folders
168
+ var editVar = InformationModel.MakeVariable(varName, variable.DataType);
169
+ var rootVar = InformationModel.MakeVariable(varName, variable.DataType);
170
+ currentEditFolder.Add(editVar);
171
+ currentRootFolder.Add(rootVar);
172
+
173
+ // Add column to the table
174
+ if (table != null)
175
+ {
176
+ var columnName = string.Join("/", segments);
177
+ var column = InformationModel.Make<StoreColumn>(columnName);
178
+ column.DataType = variable.DataType;
179
+ table.Columns.Add(column);
180
+ }
181
+ }
182
+ ```
183
+
184
+ ## Populating a Recipe Schema
185
+
186
+ The following example demonstrates how to populate a recipe schema with variables from different locations within your project model:
187
+
188
+ ```csharp
189
+ [ExportMethod]
190
+ public void PopulateRecipeSchema()
191
+ {
192
+ // List of tags with relative path from the root node to be added to the FTOptix.Recipe.RecipeSchema
193
+ var tagsToAdd = new List<string>
194
+ {
195
+ "Folder1/Variable3", // Variable in a folder
196
+ "Variable2", // Variable in the Model
197
+ "Object3/Object1/Variable2", // Variable in a nested object
198
+ "Object2/Variable1", // Variable in an object
199
+ "Object1" // Entire object (may contain other objects or variables)
200
+ };
201
+
202
+ //get the FTOptix.Recipe.RecipeSchema object
203
+ var legacyRecipeSchema = Project.Current.Get<FTOptix.Recipe.RecipeSchema>("Recipes/LegacyRecipeSchema1");
204
+
205
+ // Check if the FTOptix.Recipe.RecipeSchema exists and if the Store and TargetNode are set
206
+ if (legacyRecipeSchema == null ||
207
+ legacyRecipeSchema.Store == NodeId.Empty ||
208
+ legacyRecipeSchema.TargetNode == NodeId.Empty)
209
+ {
210
+ Log.Error("Invalid recipe schema configuration");
211
+ return;
212
+ }
213
+
214
+ // Get the root node of the Information Model
215
+ var rootNode = InformationModel.Get(legacyRecipeSchema.TargetNode);
216
+
217
+ // Process each tag
218
+ foreach (string path in tagsToAdd)
219
+ {
220
+ var node = rootNode.Get(path);
221
+ if (node == null)
222
+ {
223
+ Log.Error($"Node '{path}' not found");
224
+ continue;
225
+ }
226
+
227
+ if (node is IUAObject obj)
228
+ {
229
+ // Process all variables in the object
230
+ foreach (var childVar in obj.FindNodesByType<IUAVariable>())
231
+ AddNodeToSchema(childVar, legacyRecipeSchema);
232
+ }
233
+ else
234
+ {
235
+ // Process single variable
236
+ AddNodeToSchema(node, legacyRecipeSchema);
237
+ }
238
+ }
239
+ }
240
+ ```
241
+
242
+ ### Modify an existing Recipe Schema programmatically
243
+
244
+ ```csharp
245
+ /// <summary>
246
+ /// Add variables and corresponding store columns to an existing FTOptix.Recipe.RecipeSchema.
247
+ /// Inputs: two model variables present in the project (Model/Variable2, Model/Variable3).
248
+ /// This is a DesignTime helper typically invoked from a DesignTime NetLogic or exported method.
249
+ /// </summary>
250
+ [ExportMethod]
251
+ public void ModifyRecipeSchema()
252
+ {
253
+ // Get variables from the model
254
+ IUAVariable var2 = Project.Current.GetVariable("Model/Variable2");
255
+ IUAVariable var3 = Project.Current.GetVariable("Model/Variable3");
256
+
257
+ // Get the recipe schema to modify
258
+ FTOptix.Recipe.RecipeSchema legacyRecipeSchema = Project.Current.Get<FTOptix.Recipe.RecipeSchema>("Recipes/LegacyRecipeSchema1");
259
+
260
+ // Add variables to the EditModel (UI edit form)
261
+ IUAObject editModelObj = legacyRecipeSchema.GetObject("EditModel");
262
+ IUAVariable var2ToAddEditModel = InformationModel.MakeVariable(var2.BrowseName, var2.DataType);
263
+ IUAVariable var3ToAddEditModel = InformationModel.MakeVariable(var3.BrowseName, var3.DataType);
264
+ editModelObj.Add(var2ToAddEditModel);
265
+ editModelObj.Add(var3ToAddEditModel);
266
+
267
+ // Add variables to the Root (actual storage mapping)
268
+ IUAObject rootObj = legacyRecipeSchema.GetObject("Root");
269
+ IUAVariable var2ToAddRoot = InformationModel.MakeVariable(var2.BrowseName, var2.DataType);
270
+ IUAVariable var3ToAddRoot = InformationModel.MakeVariable(var3.BrowseName, var3.DataType);
271
+ rootObj.Add(var2ToAddRoot);
272
+ rootObj.Add(var3ToAddRoot);
273
+
274
+ // Create corresponding columns in the store table
275
+ var DB_Table_Columns = Project.Current.Get("DataStores/EmbeddedDatabase1/Tables/LegacyRecipeSchema1/Columns");
276
+ var TableColumn_Var2 = InformationModel.Make<StoreColumn>("/" + var2.BrowseName);
277
+ TableColumn_Var2.DataType = var2.DataType;
278
+ var TableColumn_Var3 = InformationModel.Make<StoreColumn>("/" + var3.BrowseName);
279
+ TableColumn_Var3.DataType = var3.DataType;
280
+ DB_Table_Columns.Add(TableColumn_Var2);
281
+ DB_Table_Columns.Add(TableColumn_Var3);
282
+ }
283
+ ```
284
+
285
+ ## Clear EditModel content recursively
286
+
287
+ ```csharp
288
+ /// <summary>
289
+ /// Helper methods to clear the EditModel of a recipe schema by resetting variables to default values.
290
+ /// Use with caution: this will set variables to empty/zero values depending on type.
291
+ /// </summary>
292
+ private void ClearEditModel(IUANode obj)
293
+ {
294
+ foreach (var item in obj.Children)
295
+ {
296
+ switch (item.NodeClass)
297
+ {
298
+ case NodeClass.Object:
299
+ // Recurse into nested objects
300
+ ClearEditModel(item);
301
+ break;
302
+ case NodeClass.Variable:
303
+ // Reset the variable value based on its current type
304
+ ResetVariableValue(item as IUAVariable);
305
+ break;
306
+ default:
307
+ break;
308
+ }
309
+ }
310
+ }
311
+
312
+ private void ResetVariableValue(IUAVariable iUAVariable)
313
+ {
314
+ var val = iUAVariable.Value.Value;
315
+ switch (val)
316
+ {
317
+ case string:
318
+ iUAVariable.Value = string.Empty;
319
+ break;
320
+ case int:
321
+ case short:
322
+ case long:
323
+ case float:
324
+ iUAVariable.Value = 0;
325
+ break;
326
+ case bool:
327
+ iUAVariable.Value = false;
328
+ break;
329
+ default:
330
+ iUAVariable.Value = 0;
331
+ break;
332
+ }
333
+ }
334
+ ```
335
+
336
+ > [!WARNING]
337
+ > When adding entire objects to a recipe schema, all variables within that object (including those in nested objects) will be included. Be careful with large object hierarchies as they might significantly increase the size of your recipe database.
338
+
339
+ ## Working with Recipe Data at Runtime
340
+
341
+ The following examples demonstrate common recipe operations you might need to perform in your application.
342
+
343
+ > [!NOTE]
344
+ > Please see the `LegacyRecipesController.cs` file in the `Scripts` library of the FactoryTalk Optix IDE to see how to load, apply, create or update recipes at runtime.
345
+
346
+ ### Importing/Exporting recipes to CSV
347
+
348
+ > [!NOTE]
349
+ > Please use the code in the `LegacyRecipesController.cs` file in the `Scripts` library of the FactoryTalk Optix IDE to import and export recipes to CSV format.
350
+
351
+ ### Checking if a recipe exists
352
+
353
+ ```csharp
354
+ [ExportMethod]
355
+ public bool RecipeExists(string recipeName)
356
+ {
357
+ var legacyRecipeSchema = Project.Current.Get<FTOptix.Recipe.RecipeSchema>("Recipes/LegacyRecipeSchema1");
358
+ if (legacyRecipeSchema == null || string.IsNullOrEmpty(recipeName))
359
+ return false;
360
+
361
+ var store = InformationModel.Get<Store>(legacyRecipeSchema.Store);
362
+ if (store == null)
363
+ return false;
364
+
365
+ var tableName = string.IsNullOrEmpty(legacyRecipeSchema.TableName) ? legacyRecipeSchema.BrowseName : legacyRecipeSchema.TableName;
366
+
367
+ // Query the database to check if the recipe exists
368
+ object[,] resultSet;
369
+ string[] header;
370
+ string query = $"SELECT * FROM \"{tableName}\" WHERE Name LIKE '{recipeName.Replace("'", "''")}'";
371
+ store.Query(query, out header, out resultSet);
372
+
373
+ return resultSet != null && resultSet.GetLength(0) > 0;
374
+ }
375
+ ```