-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathIoListTestingWindow.CommissioningStatus.cs
More file actions
179 lines (157 loc) · 7.65 KB
/
Copy pathIoListTestingWindow.CommissioningStatus.cs
File metadata and controls
179 lines (157 loc) · 7.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
using ArIED61850Tester.Models;
using ArIED61850Tester.Models.IoTesting;
using ArIED61850Tester.Services.IoTesting;
namespace ArIED61850Tester;
public partial class IoListTestingWindow
{
private DispatcherTimer? _commissioningConnectionBadgeTimer;
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
if (_commissioningConnectionBadgeTimer != null)
return;
_commissioningConnectionBadgeTimer = new DispatcherTimer(DispatcherPriority.Background)
{
Interval = TimeSpan.FromMilliseconds(200)
};
_commissioningConnectionBadgeTimer.Tick += CommissioningConnectionBadgeTimer_Tick;
_commissioningConnectionBadgeTimer.Start();
Closed += (_, _) =>
{
if (_commissioningConnectionBadgeTimer == null)
return;
_commissioningConnectionBadgeTimer.Stop();
_commissioningConnectionBadgeTimer.Tick -= CommissioningConnectionBadgeTimer_Tick;
_commissioningConnectionBadgeTimer = null;
};
}
private void CommissioningConnectionBadgeTimer_Tick(object? sender, EventArgs e)
{
foreach (var plan in FatIedList.Items.OfType<IoTestIedPlan>())
{
// Older/saved FAT workspaces can contain a complete generic Value 1 / Value 2
// pair while Runtime.State still reflects the earlier transition-only contract.
// Re-assess only those complete generic digital pairs; raw evidence is untouched.
RefreshCurrentPairVerdicts(plan);
if (FatIedList.ItemContainerGenerator.ContainerFromItem(plan) is not ListBoxItem container)
continue;
var badge = FindNamedVisual<Border>(container, "StateBadge");
var text = FindNamedVisual<TextBlock>(container, "StateText");
if (badge == null || text == null)
continue;
// The FAT card must reflect the live Engineering runtime, not the last copied
// IoTestIedPlan flags. That removes the old Refresh dependency after an FO/network
// loss: as soon as the monitor marks the transport down, the card follows it.
var device = ResolveCommissioningRuntimeDevice(plan);
var state = plan.IsPreparing
? "CONNECTING"
: device != null
? device.IsConnected
? "ONLINE"
: device.IsMonitoring
? "RECONNECTING"
: "OFFLINE"
: plan.IsLiveConnected
? "ONLINE"
: plan.IsLiveMonitoring
? "RECONNECTING"
: "OFFLINE";
var palette = state switch
{
"ONLINE" => new BadgePalette("#EAF8F1", "#8FD1B1", "#16845A"),
"RECONNECTING" => new BadgePalette("#FFF8E8", "#E9C46A", "#A56D00"),
"CONNECTING" => new BadgePalette("#EDF3FF", "#9CB8F5", "#315DBF"),
_ => new BadgePalette("#FFF1F2", "#F0B7BC", "#C53A45")
};
// Connection health is independent from FAT PASS/FAIL. A previous XAML
// visibility binding hid StateBadge when all rows passed; a local value here
// intentionally keeps ONLINE / RECONNECTING / OFFLINE visible at all times.
badge.Visibility = Visibility.Visible;
badge.Background = ConnectionBadgeBrushFromHex(palette.Background);
badge.BorderBrush = ConnectionBadgeBrushFromHex(palette.Border);
var liveDetail = device == null ? plan.LiveStatusText : device.Status;
badge.ToolTip = string.IsNullOrWhiteSpace(liveDetail)
? state
: $"{state} · {liveDetail}";
text.Text = state;
text.Foreground = ConnectionBadgeBrushFromHex(palette.Foreground);
if (FindNamedVisual<Control>(container, "RelayIcon") is { } relayIcon)
relayIcon.Foreground = ConnectionBadgeBrushFromHex(palette.Foreground);
}
// Keep Boolean presentation canonical without rewriting relay evidence or persisted
// RawValue. SetCurrentValue preserves the existing WPF binding, so a new sample can
// still replace the cell normally on the next runtime update.
if (_fatSignalsGrid != null)
NormalizeFatBooleanPresentation(_fatSignalsGrid);
}
private Iec61850MonitorDevice? ResolveCommissioningRuntimeDevice(IoTestIedPlan plan)
{
if (Owner is not MainWindow engineeringWindow)
return null;
if (!string.IsNullOrWhiteSpace(plan.LiveDeviceId))
{
var byId = engineeringWindow.Devices.FirstOrDefault(device =>
device.DeviceId.Equals(plan.LiveDeviceId, StringComparison.OrdinalIgnoreCase));
if (byId != null)
return byId;
}
return engineeringWindow.Devices.FirstOrDefault(device =>
device.IpAddress.Equals(plan.IpAddress, StringComparison.OrdinalIgnoreCase) &&
(device.Name.Equals(plan.IedName, StringComparison.OrdinalIgnoreCase) ||
device.SclIedName.Equals(plan.IedName, StringComparison.OrdinalIgnoreCase)))
?? engineeringWindow.Devices.FirstOrDefault(device =>
device.IpAddress.Equals(plan.IpAddress, StringComparison.OrdinalIgnoreCase));
}
private static void RefreshCurrentPairVerdicts(IoTestIedPlan plan)
{
foreach (var point in plan.TestPoints)
{
if (point.CaptureMode != FatCaptureMode.AutomaticTransition ||
point.Runtime.Value1Evidence == null ||
point.Runtime.Value2Evidence == null ||
point.Runtime.State is IoTestPointState.Passed or IoTestPointState.Review or IoTestPointState.Failed)
{
continue;
}
FatCurrentEvidenceAssessmentService.Apply(point);
}
}
private static void NormalizeFatBooleanPresentation(DependencyObject root)
{
var count = VisualTreeHelper.GetChildrenCount(root);
for (var index = 0; index < count; index++)
{
var child = VisualTreeHelper.GetChild(root, index);
if (child is TextBlock textBlock)
{
if (textBlock.Text.Equals("true", StringComparison.OrdinalIgnoreCase))
textBlock.SetCurrentValue(TextBlock.TextProperty, "True");
else if (textBlock.Text.Equals("false", StringComparison.OrdinalIgnoreCase))
textBlock.SetCurrentValue(TextBlock.TextProperty, "False");
}
NormalizeFatBooleanPresentation(child);
}
}
private static T? FindNamedVisual<T>(DependencyObject root, string name)
where T : FrameworkElement
{
var count = VisualTreeHelper.GetChildrenCount(root);
for (var index = 0; index < count; index++)
{
var child = VisualTreeHelper.GetChild(root, index);
if (child is T element && element.Name.Equals(name, StringComparison.Ordinal))
return element;
var nested = FindNamedVisual<T>(child, name);
if (nested != null)
return nested;
}
return null;
}
private static Brush ConnectionBadgeBrushFromHex(string value)
=> new SolidColorBrush((Color)ColorConverter.ConvertFromString(value));
private sealed record BadgePalette(string Background, string Border, string Foreground);
}