-
Notifications
You must be signed in to change notification settings - Fork 4
fix(v1.6.36): real IED process feedback and smooth analyzers #312
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a6628a4
cd1e172
7d81322
579b781
cd24a3b
e442ecf
7f6a63a
f1700cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,8 @@ | ||
| using System.Diagnostics; | ||
| using System.Globalization; | ||
| using System.Windows; | ||
| using System.Windows.Media; | ||
| using ArIED61850Tester.Services; | ||
|
|
||
| namespace ArIED61850Tester.Controls; | ||
|
|
||
|
|
@@ -34,9 +36,13 @@ public sealed class ComtradePhasorView : FrameworkElement | |
|
|
||
| private PreparedPhasorPanel _voltagePanel = PreparedPhasorPanel.Empty; | ||
| private PreparedPhasorPanel _currentPanel = PreparedPhasorPanel.Empty; | ||
| private const double PresentationTimeConstantMs = 78.0; | ||
| private string _headerLabel = "Fundamental phasors at C1"; | ||
| private string _referenceDetail = "Select a valid analysis reference"; | ||
| private string _message = string.Empty; | ||
| private ComtradePhasorVector[] _smoothedVoltageVectors = Array.Empty<ComtradePhasorVector>(); | ||
| private ComtradePhasorVector[] _smoothedCurrentVectors = Array.Empty<ComtradePhasorVector>(); | ||
| private long _lastPresentationTimestamp; | ||
|
|
||
| internal void ShowPhasors( | ||
| string referenceLabel, | ||
|
|
@@ -47,8 +53,17 @@ internal void ShowPhasors( | |
| var resolvedReference = string.IsNullOrWhiteSpace(referenceLabel) ? "Reference" : referenceLabel; | ||
| _headerLabel = $"Fundamental phasors at {resolvedReference}"; | ||
| _referenceDetail = referenceDetail ?? string.Empty; | ||
| _voltagePanel = PreparePanel(voltageVectors); | ||
| _currentPanel = PreparePanel(currentVectors); | ||
|
|
||
| var now = Stopwatch.GetTimestamp(); | ||
| var elapsedMilliseconds = _lastPresentationTimestamp == 0 | ||
| ? double.PositiveInfinity | ||
| : Stopwatch.GetElapsedTime(_lastPresentationTimestamp, now).TotalMilliseconds; | ||
| _lastPresentationTimestamp = now; | ||
|
|
||
| _smoothedVoltageVectors = SmoothVectors(_smoothedVoltageVectors, voltageVectors, elapsedMilliseconds); | ||
| _smoothedCurrentVectors = SmoothVectors(_smoothedCurrentVectors, currentVectors, elapsedMilliseconds); | ||
| _voltagePanel = PreparePanel(_smoothedVoltageVectors); | ||
| _currentPanel = PreparePanel(_smoothedCurrentVectors); | ||
|
Comment on lines
+63
to
+66
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When consecutive cursor results have the same vector topology, this replaces the authoritative target with one exponentially blended sample, but no animation/render callback invokes AGENTS.md reference: AGENTS.md:L94-L106 Useful? React with 👍 / 👎. |
||
| _message = string.Empty; | ||
| InvalidateVisual(); | ||
| } | ||
|
|
@@ -60,6 +75,9 @@ internal void ShowMessage(string title, string message) | |
| _referenceDetail = message ?? string.Empty; | ||
| _voltagePanel = PreparedPhasorPanel.Empty; | ||
| _currentPanel = PreparedPhasorPanel.Empty; | ||
| _smoothedVoltageVectors = Array.Empty<ComtradePhasorVector>(); | ||
| _smoothedCurrentVectors = Array.Empty<ComtradePhasorVector>(); | ||
| _lastPresentationTimestamp = 0; | ||
| _message = message ?? string.Empty; | ||
| InvalidateVisual(); | ||
| } | ||
|
|
@@ -111,6 +129,51 @@ protected override void OnRender(DrawingContext dc) | |
| } | ||
| } | ||
|
|
||
| private static ComtradePhasorVector[] SmoothVectors( | ||
| IReadOnlyList<ComtradePhasorVector> previous, | ||
| IReadOnlyList<ComtradePhasorVector>? target, | ||
| double elapsedMilliseconds) | ||
| { | ||
| if (target is null || target.Count == 0) | ||
| return Array.Empty<ComtradePhasorVector>(); | ||
|
|
||
| var topologyMatches = previous.Count == target.Count && previous.Count > 0; | ||
| if (topologyMatches) | ||
| { | ||
| for (var index = 0; index < target.Count; index++) | ||
| { | ||
| if (!string.Equals(previous[index].Label, target[index].Label, StringComparison.Ordinal) || | ||
| !string.Equals(previous[index].Phase, target[index].Phase, StringComparison.Ordinal) || | ||
| !string.Equals(previous[index].Units, target[index].Units, StringComparison.Ordinal)) | ||
| { | ||
| topologyMatches = false; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| var output = new ComtradePhasorVector[target.Count]; | ||
| if (!topologyMatches) | ||
| { | ||
| for (var index = 0; index < target.Count; index++) | ||
| output[index] = target[index]; | ||
| return output; // First sample/topology change snaps: no artificial ramp from zero. | ||
| } | ||
|
|
||
| for (var index = 0; index < target.Count; index++) | ||
| { | ||
| var before = previous[index]; | ||
| var next = target[index]; | ||
| output[index] = new ComtradePhasorVector( | ||
| next.Label, | ||
| next.Phase, | ||
| next.Units, | ||
| PresentationEasingMath.Smooth(before.MagnitudeRms, next.MagnitudeRms, elapsedMilliseconds, PresentationTimeConstantMs), | ||
| PresentationEasingMath.SmoothAngleDegrees(before.AngleDegrees, next.AngleDegrees, elapsedMilliseconds, PresentationTimeConstantMs)); | ||
| } | ||
| return output; | ||
| } | ||
|
|
||
| private static PreparedPhasorPanel PreparePanel(IReadOnlyList<ComtradePhasorVector>? source) | ||
| { | ||
| if (source is null || source.Count == 0) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the live preflight finds the process already at the requested state,
NativeIec61850Client.ControlAlreadyAtRequestedStatereturnsIsSuccess = true,ServiceAccepted = false, andCompletionState = "NotSent". This branch nevertheless replaces its accurate “Already at requested state” stage with “Command accepted” and asserts that the control service accepted a command, misleading operators even though no SBO/Operate request was sent. Gate this wording on actual service acceptance rather than general success.AGENTS.md reference: AGENTS.md:L143-L153
Useful? React with 👍 / 👎.