@aryacode/dsh-engineering 0.5.0 → 0.7.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 (77) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +9 -8
  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 +10 -10
  7. package/vendor/l5x/SKILL.md +27 -7
  8. package/vendor/l5x/lib/engine/change-set.d.ts +9 -1
  9. package/vendor/l5x/lib/engine/change-set.js +32 -7
  10. package/vendor/l5x/lib/engine/documentation.d.ts +58 -0
  11. package/vendor/l5x/lib/engine/documentation.js +237 -0
  12. package/vendor/l5x/lib/engine/instructions.d.ts +50 -0
  13. package/vendor/l5x/lib/engine/instructions.js +107 -0
  14. package/vendor/l5x/lib/engine/ladder.d.ts +74 -0
  15. package/vendor/l5x/lib/engine/ladder.js +209 -0
  16. package/vendor/l5x/lib/index.d.ts +1 -0
  17. package/vendor/l5x/lib/index.js +3 -1
  18. package/vendor/l5x/lib/model/routines.js +6 -1
  19. package/vendor/l5x/lib/session.d.ts +20 -0
  20. package/vendor/l5x/lib/session.js +36 -0
  21. package/vendor/l5x/lib/tools/document.d.ts +18 -0
  22. package/vendor/l5x/lib/tools/document.js +119 -0
  23. package/vendor/l5x/lib/tools/drafts.js +109 -3
  24. package/vendor/optix/SKILL.md +68 -0
  25. package/vendor/optix/netlogic/SOURCE.json +12 -0
  26. package/vendor/optix/netlogic/index.json +1987 -0
  27. package/vendor/optix/netlogic/pages/accessing-project-nodes.md +113 -0
  28. package/vendor/optix/netlogic/pages/advanced-svg.md +124 -0
  29. package/vendor/optix/netlogic/pages/alarming.md +489 -0
  30. package/vendor/optix/netlogic/pages/async-tasks.md +236 -0
  31. package/vendor/optix/netlogic/pages/audit-signature.md +91 -0
  32. package/vendor/optix/netlogic/pages/colors.md +122 -0
  33. package/vendor/optix/netlogic/pages/companion-specs.md +77 -0
  34. package/vendor/optix/netlogic/pages/creating-objects.md +370 -0
  35. package/vendor/optix/netlogic/pages/database-interaction.md +437 -0
  36. package/vendor/optix/netlogic/pages/datagrids.md +123 -0
  37. package/vendor/optix/netlogic/pages/debugging-tips.md +605 -0
  38. package/vendor/optix/netlogic/pages/designtime-tags-import.md +531 -0
  39. package/vendor/optix/netlogic/pages/dialog-boxes.md +150 -0
  40. package/vendor/optix/netlogic/pages/dispatch-events.md +200 -0
  41. package/vendor/optix/netlogic/pages/dynamic-links.md +825 -0
  42. package/vendor/optix/netlogic/pages/element-access.md +1208 -0
  43. package/vendor/optix/netlogic/pages/engineering-units.md +298 -0
  44. package/vendor/optix/netlogic/pages/events.md +340 -0
  45. package/vendor/optix/netlogic/pages/execute-command.md +202 -0
  46. package/vendor/optix/netlogic/pages/fto-studio-cli.md +99 -0
  47. package/vendor/optix/netlogic/pages/good-practices.md +50 -0
  48. package/vendor/optix/netlogic/pages/information-model.md +89 -0
  49. package/vendor/optix/netlogic/pages/log-output.md +22 -0
  50. package/vendor/optix/netlogic/pages/managing-aliases.md +22 -0
  51. package/vendor/optix/netlogic/pages/manipulate-nodes.md +72 -0
  52. package/vendor/optix/netlogic/pages/mqtt.md +240 -0
  53. package/vendor/optix/netlogic/pages/netlogic-lifecycle.md +270 -0
  54. package/vendor/optix/netlogic/pages/netlogic-overview.md +155 -0
  55. package/vendor/optix/netlogic/pages/opcua.md +325 -0
  56. package/vendor/optix/netlogic/pages/plc-tags.md +219 -0
  57. package/vendor/optix/netlogic/pages/queries.md +1297 -0
  58. package/vendor/optix/netlogic/pages/random-generation.md +90 -0
  59. package/vendor/optix/netlogic/pages/recipes.md +375 -0
  60. package/vendor/optix/netlogic/pages/recipex.md +2383 -0
  61. package/vendor/optix/netlogic/pages/regex.md +15 -0
  62. package/vendor/optix/netlogic/pages/register-observers.md +346 -0
  63. package/vendor/optix/netlogic/pages/reports.md +141 -0
  64. package/vendor/optix/netlogic/pages/resource-uri.md +308 -0
  65. package/vendor/optix/netlogic/pages/runtime-tags-import.md +280 -0
  66. package/vendor/optix/netlogic/pages/serial-port.md +357 -0
  67. package/vendor/optix/netlogic/pages/sessions.md +58 -0
  68. package/vendor/optix/netlogic/pages/system-node.md +153 -0
  69. package/vendor/optix/netlogic/pages/template-library.md +127 -0
  70. package/vendor/optix/netlogic/pages/threading.md +236 -0
  71. package/vendor/optix/netlogic/pages/translations.md +417 -0
  72. package/vendor/optix/netlogic/pages/trends.md +128 -0
  73. package/vendor/optix/netlogic/pages/ui-animations.md +115 -0
  74. package/vendor/optix/netlogic/pages/users-groups.md +416 -0
  75. package/vendor/optix/netlogic/pages/variables-formatting.md +16 -0
  76. package/vendor/optix/netlogic/pages/variables-interaction.md +557 -0
  77. package/vendor/optix/netlogic/pages/variables-structure.md +155 -0
@@ -0,0 +1,128 @@
1
+ # Trends
2
+
3
+ ## Pens
4
+
5
+ ### Add a pen to a trend
6
+
7
+ ```csharp
8
+ [ExportMethod]
9
+ public void AddPen(NodeId penId)
10
+ {
11
+ // Get the variable that will be used as a pen
12
+ var tag = InformationModel.GetVariable(penId);
13
+ // Create the pen object
14
+ var pen = InformationModel.Make<TrendPen>(tag.BrowseName + "_pen");
15
+ // Link the pen to the variable
16
+ pen.SetDynamicLink(tag, DynamicLinkMode.ReadWrite);
17
+ // Set the pen properties
18
+ pen.Color = Colors.Coral;
19
+ pen.Thickness = 3;
20
+ // Add the pen to the trend
21
+ trend.Pens.Add(pen);
22
+ // Refresh the trend to show the new pen
23
+ trend.Refresh();
24
+ }
25
+ ```
26
+
27
+ ### Remove a pen from a trend
28
+
29
+ ```csharp
30
+ /// <summary>
31
+ /// Remove Trend pens at runtime using ComboBoxes to select DataLogger and Variable.
32
+ /// </summary>
33
+ [ExportMethod]
34
+ public void RemovePen()
35
+ {
36
+ // Remove a pen from a trend by name
37
+ var nameCombo = Owner.Owner.Get<ComboBox>("ComboBox1");
38
+ var myTrend = Owner.Owner.Owner.Owner.Get<Trend>("Trend1");
39
+ myTrend.Pens.Remove(((UAManagedCore.LocalizedText)nameCombo.SelectedValue).Text);
40
+ }
41
+ ```
42
+
43
+ ## Y-axis
44
+
45
+ ### Add a Y-axis to a pen
46
+
47
+ #### FactoryTalk Optix version 1.7.1.x and later
48
+
49
+ ```csharp
50
+ [ExportMethod]
51
+ public void AddYAxisToPen()
52
+ {
53
+ // Get the pen to interact with
54
+ var trend = Owner.Get<Trend>("Trend1");
55
+
56
+ // Get the pen and add a Y-axis to it
57
+ var pen = trend.Pens.Get<TrendPen>("Variable1_pen");
58
+ var newAxis = pen.GetOrAddYAxis();
59
+
60
+ // Set the Y-axis properties
61
+ newAxis.MinValue = 0;
62
+ newAxis.MaxValue = 100;
63
+
64
+ // Refresh the trend to show the new Y-axis
65
+ trend.Refresh();
66
+ }
67
+ ```
68
+
69
+ #### FactoryTalk Optix before version 1.7.1.x
70
+
71
+ ```csharp
72
+ [ExportMethod]
73
+ public void AddYAxisToPen()
74
+ {
75
+ // Get the pen and the Y-axis
76
+ var trend = Owner.Get("Trend1") as Trend;
77
+ var pen = trend.Pens.Get<TrendPen>("TrendPen1");
78
+
79
+ // Create a new Y-axis
80
+ var uiNamespaceIndex = NamespaceMapProvider.GetNamespaceIndex("urn:FTOptix:UI");
81
+ var yAxis = InformationModel.Make<FTOptix.UI.ValueAxis>(new QualifiedName(uiNamespaceIndex, "YAxis"));
82
+
83
+ // Set the Y-axis properties
84
+ yAxis.MinValueVariable.Value = -10;
85
+ yAxis.MaxValueVariable.Value = 42;
86
+ yAxis.PositionVariable.Value = 0; // 0 = left, 1 = right
87
+
88
+ // Assign the Y-axis to the pen
89
+ pen.Add(yAxis);
90
+ }
91
+ ```
92
+
93
+ ### Remove a Y-axis from a pen
94
+
95
+ #### FactoryTalk Optix version 1.7.1.x and later
96
+
97
+ ```csharp
98
+ [ExportMethod]
99
+ public void RemoveYAxisFromPen()
100
+ {
101
+ // Get the pen to interact with
102
+ var trend = Owner.Get<Trend>("Trend1");
103
+
104
+ // Get the pen and remove its Y-axis
105
+ var pen = trend.Pens.Get<TrendPen>("Variable1_pen");
106
+ pen.RemoveYAxis();
107
+
108
+ // Refresh the trend to show the change
109
+ trend.Refresh();
110
+ }
111
+ ```
112
+
113
+ #### FactoryTalk Optix before version 1.7.1.x
114
+
115
+ ```csharp
116
+ [ExportMethod]
117
+ public void RemoveYAxisFromPen()
118
+ {
119
+ // Get the pen and its Y-axis
120
+ var trend = Owner.Get("Trend1") as Trend;
121
+ var pen = trend.Pens.Get<TrendPen>("TrendPen1");
122
+ var yAxis = pen.Children.OfType<FTOptix.UI.ValueAxis>().FirstOrDefault();
123
+
124
+ // Remove the Y-axis from the pen
125
+ if (yAxis != null)
126
+ pen.Remove(yAxis);
127
+ }
128
+ ```
@@ -0,0 +1,115 @@
1
+ # UI Animations
2
+
3
+ Animations allow variable values to be changed with a defined (or infinite) number of iterations; these animations can also be contained in parallel (run concurrently) or sequential animations.
4
+
5
+ ## Number animation
6
+
7
+ Numerical animations allow the offset to be added to the value of the variable by defining an initial value to a final value by also defining the relevant type of interpolation (the value is an enumeration called EasingCurve)
8
+
9
+ ```csharp
10
+ private NumberAnimation CreateNumberAnimation(IUANode ownerAnimationNode, NodeId targetAnimationNode, int from, int to, int loops, bool running, int duration, EasingCurve interpolation)
11
+ {
12
+ // Look at the count of current animations in the node
13
+ int animationCounter = ownerAnimationNode.GetNodesByType<NumberAnimation>().Count();
14
+ // Create a new Number Animation with a browseName with index +1 over the current present animations
15
+ var numberAnimation = InformationModel.Make<NumberAnimation>($"NumberAnimation{animationCounter + 1}");
16
+ // Set animation parameters, target animation node must be a NodeId
17
+ numberAnimation.Target = targetAnimationNode;
18
+ numberAnimation.From = from;
19
+ numberAnimation.To = to;
20
+ numberAnimation.Loops = loops;
21
+ numberAnimation.Running = running;
22
+ numberAnimation.Duration = duration;
23
+ numberAnimation.EasingCurve = interpolation;
24
+ // Add the animation node to the target node
25
+ ownerAnimationNode.Add(numberAnimation);
26
+ // Return the new animation for further future use
27
+ return numberAnimation;
28
+ }
29
+ ```
30
+
31
+ ## Sequential animation
32
+
33
+ Sequential animation allows all animations of this object to be performed sequentially
34
+
35
+ ```csharp
36
+ private SequentialAnimation CreateSequentialAnimation(IUANode ownerAnimationNode, int loops, bool running)
37
+ {
38
+ // Look at the count of current animations in the node
39
+ int animationCounter = ownerAnimationNode.GetNodesByType<SequentialAnimation>().Count();
40
+ // Create a new Sequential Animation with a browseName with index +1 over the current present animations
41
+ var sequentialAnimation = InformationModel.Make<SequentialAnimation>($"SequentialAnimation{animationCounter + 1}");
42
+ // Set animation parameters
43
+ sequentialAnimation.Loops = loops;
44
+ sequentialAnimation.Running = running;
45
+ // Add the animation node to the target node
46
+ ownerAnimationNode.Add(sequentialAnimation);
47
+ // Return the new animation for further future use
48
+ return sequentialAnimation;
49
+ }
50
+ ```
51
+
52
+ ## Parallel animation
53
+
54
+ Parallel animations allow all animations of this object to run simultaneously
55
+
56
+ ```csharp
57
+ private ParallelAnimation CreateParallelAnimation(IUANode ownerAnimationNode, int loops, bool running)
58
+ {
59
+ // Look at the count of current animations in the node
60
+ int animationCounter = ownerAnimationNode.GetNodesByType<ParallelAnimation>().Count();
61
+ // Create a new Parallel Animation with a browseName with index +1 over the current present animations
62
+ var parallelAnimation = InformationModel.Make<ParallelAnimation>($"ParallelAnimation{animationCounter + 1}");
63
+ // Set animation parameters
64
+ parallelAnimation.Loops = loops;
65
+ parallelAnimation.Running = running;
66
+ // Add the animation node to the target node
67
+ ownerAnimationNode.Add(parallelAnimation);
68
+ // Return the new animation for further future use
69
+ return parallelAnimation;
70
+ }
71
+ ```
72
+
73
+ ### Example of creating animations on a TextBox
74
+
75
+ ```csharp
76
+ [ExportMethod]
77
+ public void GenerateStuffAnimation()
78
+ {
79
+ var targetUiObject = Project.Current.Get<Item>("UI/Screens/Screen1/TextBox1");
80
+ var parallelAnimation = CreateParallelAnimation(targetUiObject, 10, true);
81
+ _ = CreateNumberAnimation(parallelAnimation, targetUiObject.OpacityVariable.NodeId, 0, 100, -1, true, 2000, EasingCurve.Linear);
82
+ var sequentialAnimation = CreateSequentialAnimation(targetUiObject, 5, true);
83
+ _ = CreateNumberAnimation(sequentialAnimation, targetUiObject.RotationVariable.NodeId, 0, 360, -1, true, 2000, EasingCurve.Linear);
84
+
85
+ }
86
+ ```
87
+
88
+ ## Value change animation
89
+
90
+ Animation on value change allows you to define the animation and duration to make the new value reach the value that is set in the variable
91
+
92
+ ```csharp
93
+ [ExportMethod]
94
+ public void GenerateStuffBehaviorAnimation()
95
+ {
96
+ var targetUiObject = Project.Current.Get<Item>("UI/Screens/Screen1/TextBox1");
97
+ CreateBehaviorAnimation(targetUiObject.WidthVariable, 5000, EasingCurve.InOutQuad);
98
+ }
99
+
100
+ private void CreateBehaviorAnimation(IUANode targetAnimationNode, int duration, EasingCurve interpolation)
101
+ {
102
+ // Check that targetNode is a variable and does not already have Behavior Animation
103
+ if (targetAnimationNode is not IUAVariable || targetAnimationNode.GetNodesByType<BehaviourAnimation>().Any())
104
+ {
105
+ return;
106
+ }
107
+ // Create new Behavior Animation
108
+ var behaviorAnimation = InformationModel.Make<BehaviourAnimation>($"Value change animation");
109
+ // Set animation parameters
110
+ behaviorAnimation.Duration = duration;
111
+ behaviorAnimation.EasingCurve = interpolation;
112
+ // Add the animation node to the target node
113
+ targetAnimationNode.Add(behaviorAnimation);
114
+ }
115
+ ```
@@ -0,0 +1,416 @@
1
+ # Users, groups and roles
2
+
3
+ ## Users
4
+
5
+ Users are represented by the `FTOptix.Core.User` type in FactoryTalk Optix. Users can be created, modified, and deleted programmatically using C# scripts.
6
+
7
+ > [!NOTE]
8
+ > Operations on users might require a valid `Session` to interact with the security system. You can simply put the piece of code somewhere in the UI (e.g., a screen) and it will work as expected.
9
+
10
+ ### Creating a new User
11
+
12
+ ```csharp
13
+ /// <summary>
14
+ /// Creates a new user with the specified username, password, and locale.
15
+ /// The method sets the result to a default value (NodeId.Empty) and checks if the username is empty.
16
+ /// If the username is empty, it shows a message and logs an error.
17
+ /// Otherwise, it generates the user and assigns the result.
18
+ /// </summary>
19
+ /// <param name="username">The username for the new user.</param>
20
+ /// <param name="password">The password for the new user.</param>
21
+ /// <param name="locale">The locale setting for the new user.</param>
22
+ /// <note>
23
+ /// The method uses <see cref="NodeId.Empty"/> as the default value for <see cref="result"/>.
24
+ /// </note>
25
+ [ExportMethod]
26
+ public void CreateUser(string username, string password, string locale, out NodeId result)
27
+ {
28
+ result = NodeId.Empty;
29
+
30
+ if (string.IsNullOrEmpty(username))
31
+ {
32
+ ShowMessage(1);
33
+ Log.Error("EditUserDetailPanelLogic", "Cannot create user with empty username");
34
+ return;
35
+ }
36
+
37
+ result = GenerateUser(username, password, locale);
38
+ }
39
+
40
+ /// <summary>
41
+ /// This method creates a new user with the specified username, password, and locale.
42
+ /// It checks if the username already exists in the user list and handles various
43
+ /// password change result codes.
44
+ /// </summary>
45
+ /// <param name="username">The username for the new user.</param>
46
+ /// <param name="password">The password for the new user.</param>
47
+ /// <param name="locale">The locale ID for the user (optional).</param>
48
+ /// <returns>
49
+ /// The NodeId of the newly created user, or NodeId.Empty if an error occurs.
50
+ /// </returns>
51
+ private NodeId GenerateUser(string username, string password, string locale)
52
+ {
53
+ var users = GetUsers();
54
+ if (users == null)
55
+ {
56
+ Log.Error("EditUserDetailPanelLogic", "Unable to get users");
57
+ return NodeId.Empty;
58
+ }
59
+
60
+ foreach (var child in users.Children.OfType<FTOptix.Core.User>())
61
+ {
62
+ if (child.BrowseName.Equals(username, StringComparison.OrdinalIgnoreCase))
63
+ {
64
+ Log.Error("EditUserDetailPanelLogic", "Username already exists");
65
+ return NodeId.Empty;
66
+ }
67
+ }
68
+
69
+ var user = InformationModel.MakeObject<FTOptix.Core.User>(username);
70
+ users.Add(user);
71
+
72
+ //Apply LocaleId
73
+ if (!string.IsNullOrEmpty(locale))
74
+ user.LocaleId = locale;
75
+
76
+ //Apply password
77
+ var result = Session.ChangePassword(username, password, string.Empty);
78
+
79
+ switch (result.ResultCode)
80
+ {
81
+ case FTOptix.Core.ChangePasswordResultCode.Success:
82
+ Log.Verbose("EditUserDetailPanelLogic", "User created successfully");
83
+ break;
84
+ case FTOptix.Core.ChangePasswordResultCode.WrongOldPassword:
85
+ //Not applicable
86
+ break;
87
+ case FTOptix.Core.ChangePasswordResultCode.PasswordAlreadyUsed:
88
+ //Not applicable
89
+ break;
90
+ case FTOptix.Core.ChangePasswordResultCode.PasswordChangedTooRecently:
91
+ //Not applicable
92
+ break;
93
+ case FTOptix.Core.ChangePasswordResultCode.PasswordTooShort:
94
+ Log.Error("EditUserDetailPanelLogic", "Password is too short");
95
+ users.Remove(user);
96
+ return NodeId.Empty;
97
+ case FTOptix.Core.ChangePasswordResultCode.UserNotFound:
98
+ //Not applicable
99
+ break;
100
+ case FTOptix.Core.ChangePasswordResultCode.UnsupportedOperation:
101
+ Log.Error("EditUserDetailPanelLogic", "Unsupported operation");
102
+ users.Remove(user);
103
+ return NodeId.Empty;
104
+
105
+ }
106
+
107
+ return user.NodeId;
108
+ }
109
+ ```
110
+
111
+ ### Change user password
112
+
113
+ ```csharp
114
+ /// <summary>
115
+ /// Changes the password of a user.
116
+ /// </summary>
117
+ /// <param name="username">The username of the user whose password is to be changed.</param>
118
+ /// <param name="newPassword">The new password to be set for the user.</param>
119
+ /// <returns>True if the password was changed successfully, otherwise false.</returns>
120
+ [ExportMethod]
121
+ public void ChangePassword(string username, string newPassword, out bool result)
122
+ {
123
+ result = false;
124
+
125
+ if (string.IsNullOrEmpty(username))
126
+ {
127
+ Log.Error("ChangePassword", "Username cannot be empty");
128
+ return;
129
+ }
130
+
131
+ var changePasswordResult = Session.ChangePassword(username, newPassword, string.Empty);
132
+
133
+ switch (changePasswordResult.ResultCode)
134
+ {
135
+ case FTOptix.Core.ChangePasswordResultCode.Success:
136
+ Log.Verbose("ChangePassword", "Password changed successfully");
137
+ result = true;
138
+ break;
139
+ case FTOptix.Core.ChangePasswordResultCode.WrongOldPassword:
140
+ // Not applicable
141
+ break;
142
+ case FTOptix.Core.ChangePasswordResultCode.PasswordAlreadyUsed:
143
+ Log.Error("ChangePassword", "Password has already been used");
144
+ break;
145
+ case FTOptix.Core.ChangePasswordResultCode.PasswordChangedTooRecently:
146
+ Log.Error("ChangePassword", "Password was changed too recently");
147
+ break;
148
+ case FTOptix.Core.ChangePasswordResultCode.PasswordTooShort:
149
+ Log.Error("ChangePassword", "New password is too short");
150
+ break;
151
+ case FTOptix.Core.ChangePasswordResultCode.UserNotFound:
152
+ Log.Error("ChangePassword", "User not found");
153
+ break;
154
+ case FTOptix.Core.ChangePasswordResultCode.UnsupportedOperation:
155
+ Log.Error("ChangePassword", "Unsupported operation");
156
+ break;
157
+ }
158
+ }
159
+ ```
160
+
161
+ ### Check if user belongs to a specific group or role
162
+
163
+ Groups and roles are used to manage user permissions in FactoryTalk Optix. Users can be assigned to groups and roles, which can then be used to control access to various parts of the system.
164
+
165
+ Both Roles and Groups are reference types, meaning that they can be assigned to users by adding a reference between the user and the group or role. This allows for flexible management of user permissions.
166
+
167
+ For example:
168
+
169
+ ```csharp
170
+ // Get all groups the user is part of
171
+ var userGroups = newUser.Refs.GetObjects(FTOptix.Core.ReferenceTypes.HasGroup, false);
172
+ // Get all roles the user is part of
173
+ var userGroups = newUser.Refs.GetObjects(FTOptix.Core.ReferenceTypes.HasRole, false);
174
+ ```
175
+
176
+ Roles can be assigned to groups and users, groups can be assigned to roles and users, you can make any combination of these to create a flexible permission system.
177
+
178
+ ### Perform a login
179
+
180
+ ```csharp
181
+ /// <summary>
182
+ /// This method performs the login operation using the provided username and password.
183
+ /// It retrieves the Users alias and PasswordExpiredDialogType from the logic object,
184
+ /// attempts to log in using the Session object,
185
+ /// and handles the result of the login attempt.
186
+ /// If the login is successful, it enables the login button;
187
+ /// if the password is expired, it opens a dialog for the user to change their password.
188
+ /// If the login fails, it sets an output message on the login form.
189
+ /// </summary>
190
+ /// <param name="username">The username to log in with.</param>
191
+ /// <param name="password">The password to log in with.</param>
192
+ [ExportMethod]
193
+ public void PerformLogin(string username, string password)
194
+ {
195
+ Button loginButton = (Button)Owner;
196
+ loginButton.Enabled = false;
197
+
198
+ try
199
+ {
200
+ var loginResult = Session.Login(username, password);
201
+ if (loginResult.ResultCode == ChangeUserResultCode.PasswordExpired)
202
+ {
203
+ var user = Project.Current.Get<User>($"Security/Users/{username}");
204
+ var ownerButton = (Button)Owner;
205
+ Log.Info("LoginButtonLogic", "Password expired, opening change password dialog");
206
+ return;
207
+ }
208
+ else if (loginResult.ResultCode != ChangeUserResultCode.Success)
209
+ {
210
+ Log.Error("LoginButtonLogic", "Authentication failed");
211
+ }
212
+ else if (loginResult.ResultCode == ChangeUserResultCode.Success)
213
+ {
214
+ Log.Info("LoginButtonLogic", "User logged in successfully");
215
+ }
216
+ else
217
+ {
218
+ Log.Error("LoginButtonLogic", "Unknown error during login");
219
+ }
220
+ }
221
+ catch (Exception e)
222
+ {
223
+ Log.Error("LoginButtonLogic", e.Message);
224
+ }
225
+ finally
226
+ {
227
+ loginButton.Enabled = true;
228
+ }
229
+ }
230
+ ```
231
+
232
+ ## Groups
233
+
234
+ ### Creating a new Group
235
+
236
+ ```csharp
237
+ /// <summary>
238
+ /// Creates a new group with the specified name.
239
+ /// The method checks if the group name is empty or if the group already exists,
240
+ /// and logs appropriate error messages.
241
+ /// If the group is successfully created, it adds the group to the "Security/Groups" folder.
242
+ /// </summary>
243
+ /// <param name="groupName">The name of the group to be created.</param>
244
+ /// <returns>True if the group was created successfully, false otherwise.</returns>
245
+ [ExportMethod]
246
+ public void CreateNewGroup(string groupName) {
247
+ // Get to the base folder to create groups
248
+ Folder userFolder = Project.Current.Get<Folder>("Security/Groups");
249
+ if (userFolder == null) {
250
+ Log.Error("CreateNewGroup", "Cannot find Groups folder");
251
+ return;
252
+ } else if (string.IsNullOrEmpty(groupName)) {
253
+ Log.Error("CreateNewGroup", "Cannot create group with empty name");
254
+ return;
255
+ } else if (userFolder.Get<Group>(groupName) != null) {
256
+ Log.Error("CreateNewGroup", "Group already exists!");
257
+ return;
258
+ }
259
+ // Create a new group
260
+ var newGroup = InformationModel.Make<Group>(groupName);
261
+ // Add the group to the folder
262
+ userFolder.Add(newGroup);
263
+ }
264
+ ```
265
+
266
+ ### Check if user is part of a group
267
+
268
+ ```csharp
269
+ /// <summary>
270
+ /// Checks if a user is part of a specified group.
271
+ /// </summary>
272
+ /// <param name="user">The user to check.</param>
273
+ /// <param name="group">The group to check against.</param>
274
+ /// <returns>True if the user is part of the group, otherwise false.</returns>
275
+ private static bool UserHasGroup(FTOptix.Core.User user, FTOptix.Core.Group group)
276
+ {
277
+ // Return false if user is null
278
+ if (user == null)
279
+ return false;
280
+
281
+ // Return false if group is null
282
+ if (group == null)
283
+ return false;
284
+
285
+ // Get all groups the user is part of
286
+ var userGroups = user.Refs.GetObjects(FTOptix.Core.ReferenceTypes.HasGroup, false);
287
+
288
+ // Iterate through the groups to check if the user is part of the specified group
289
+ foreach (var userGroup in userGroups)
290
+ {
291
+ if (userGroup.NodeId == group.NodeId)
292
+ return true;
293
+ }
294
+ // Return false if the user is not part of the specified group
295
+ return false;
296
+ }
297
+ ```
298
+
299
+ ### Assign group to a user
300
+
301
+ ```csharp
302
+ /// <summary>
303
+ /// Assigns a user to a specified group by adding a reference between the user and the group.
304
+ /// </summary>
305
+ /// <param name="user">The user to be assigned to the group.</param>
306
+ /// <param name="group">The group to which the user will be assigned.</param>
307
+ private void AssignUserToGroup(FTOptix.Core.User user, FTOptix.Core.Group group)
308
+ {
309
+ user.Refs.AddReference(FTOptix.Core.ReferenceTypes.HasGroup, group.NodeId);
310
+ }
311
+ ```
312
+
313
+ ### Remove group from a user
314
+
315
+ ```csharp
316
+ /// <summary>
317
+ /// Removes a user from the specified group by removing a reference between the user and the group.
318
+ /// </summary>
319
+ /// <param name="user">The user to be removed from the group.</param>
320
+ /// <param name="group">The group to which the user will be removed from.</param>
321
+ private void RemoveUserFromGroup(FTOptix.Core.User user, FTOptix.Core.Group group)
322
+ {
323
+ user.Refs.RemoveReference(FTOptix.Core.ReferenceTypes.HasGroup, group.NodeId);
324
+ }
325
+ ```
326
+
327
+ ## Roles
328
+
329
+ ### Creating a new Role
330
+
331
+ ```csharp
332
+ [ExportMethod]
333
+ public void CreateNewRole(string roleName) {
334
+ // Get to the base folder to create roles
335
+ Folder userFolder = Project.Current.Get<Folder>("Security/Roles");
336
+ if (userFolder == null) {
337
+ Log.Error("CreateNewRole", "Cannot find Roles folder");
338
+ return;
339
+ } else if (string.IsNullOrEmpty(roleName)) {
340
+ Log.Error("CreateNewRole", "Cannot create role with empty name");
341
+ return;
342
+ } else if (userFolder.Get<FTOptix.Core.Role>(roleName) != null) {
343
+ Log.Error("CreateNewRole", "Role already exists!");
344
+ return;
345
+ }
346
+ // Create a new role
347
+ var newRole = InformationModel.Make<FTOptix.Core.Role>(roleName);
348
+ // Add the role to the folder
349
+ userFolder.Add(newRole);
350
+ }
351
+ ```
352
+
353
+ ### Check if user is part of a role
354
+
355
+ ```csharp
356
+ /// <summary>
357
+ /// Checks if a user is part of a specified role.
358
+ /// </summary>
359
+ /// <param name="user">The user to check.</param>
360
+ /// <param name="role">The role to check against.</param>
361
+ /// <returns>True if the user is part of the role, otherwise false.</returns>
362
+ private static bool UserHasRole(FTOptix.Core.User user, FTOptix.Core.Role role)
363
+ {
364
+ // Return false if user is null
365
+ if (user == null)
366
+ return false;
367
+
368
+ // Return false if role is null
369
+ if (role == null)
370
+ return false;
371
+
372
+ // Get all roles the user is part of
373
+ var userRoles = user.Refs.GetObjects(FTOptix.Core.ReferenceTypes.HasRole, false);
374
+
375
+ // Iterate through the roles to check if the user is part of the specified role
376
+ foreach (var userRole in userRoles)
377
+ {
378
+ if (userRole.NodeId == role.NodeId)
379
+ return true;
380
+ }
381
+ // Return false if the user is not part of the specified role
382
+ return false;
383
+ }
384
+ ```
385
+
386
+ ### Assign role to a user
387
+
388
+ ```csharp
389
+ /// <summary>
390
+ /// Assigns a user to a specified role by adding a reference between the user and the role.
391
+ /// </summary>
392
+ /// <param name="user">The user to be assigned to the role.</param>
393
+ /// <param name="role">The role to which the user will be assigned.</param>
394
+ private void AssignUserToRole(FTOptix.Core.User user, FTOptix.Core.Role role)
395
+ {
396
+ user.Refs.AddReference(FTOptix.Core.ReferenceTypes.HasRole, role.NodeId);
397
+ }
398
+ ```
399
+
400
+ ### Remove role from a user
401
+
402
+ ```csharp
403
+ /// <summary>
404
+ /// Removes a user from the specified role by removing a reference between the user and the role.
405
+ /// </summary>
406
+ /// <param name="user">The user to be removed from the role.</param>
407
+ /// <param name="role">The role to which the user will be removed from.</param>
408
+ private void RemoveUserFromRole(FTOptix.Core.User user, FTOptix.Core.Role role)
409
+ {
410
+ user.Refs.RemoveReference(FTOptix.Core.ReferenceTypes.HasRole, role.NodeId);
411
+ }
412
+ ```
413
+
414
+ ## Get the list of groups associated to the user which is logging in
415
+
416
+ Please see: [Register OPC/UA observers](./register-observers.md).
@@ -0,0 +1,16 @@
1
+ # Variables formatting
2
+
3
+ ## Zero padding
4
+
5
+ Reads value of `i`, converts into a string and add a zero to the left if value is less than 10
6
+
7
+ ```csharp
8
+ text = i.ToString("D2");
9
+ ```
10
+
11
+ ## Print the hex equivalent of any character
12
+
13
+ ```csharp
14
+ keyPressed = "a";
15
+ Log.Info(BitConverter.ToString(Encoding.Default.GetBytes(keyPressed)));
16
+ ```