@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.
- package/CHANGELOG.md +52 -0
- package/README.md +9 -8
- package/cordis.patch.yml +1 -1
- package/entries/netlogic.js +266 -0
- package/entries/optix.js +13 -0
- package/package.json +10 -10
- package/vendor/l5x/SKILL.md +27 -7
- package/vendor/l5x/lib/engine/change-set.d.ts +9 -1
- package/vendor/l5x/lib/engine/change-set.js +32 -7
- package/vendor/l5x/lib/engine/documentation.d.ts +58 -0
- package/vendor/l5x/lib/engine/documentation.js +237 -0
- package/vendor/l5x/lib/engine/instructions.d.ts +50 -0
- package/vendor/l5x/lib/engine/instructions.js +107 -0
- package/vendor/l5x/lib/engine/ladder.d.ts +74 -0
- package/vendor/l5x/lib/engine/ladder.js +209 -0
- package/vendor/l5x/lib/index.d.ts +1 -0
- package/vendor/l5x/lib/index.js +3 -1
- package/vendor/l5x/lib/model/routines.js +6 -1
- package/vendor/l5x/lib/session.d.ts +20 -0
- package/vendor/l5x/lib/session.js +36 -0
- package/vendor/l5x/lib/tools/document.d.ts +18 -0
- package/vendor/l5x/lib/tools/document.js +119 -0
- package/vendor/l5x/lib/tools/drafts.js +109 -3
- 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,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
|
+
```
|