Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions MainWindow.IedBrowser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -532,9 +532,12 @@ private void RefreshIedBrowserVisibleValues()
}
else
{
row.Value = "Not read";
row.Value = IedBrowserReadPlan.UnobservedValue(
row.FunctionalConstraint);
row.ObservationSource = "";
row.ObservationTooltip = "";
row.ObservationTooltip = row.FunctionalConstraint == "CO"
? "FC CO: command parameters are not scalar MMS Read targets. Use the explicit controlled Signal Testing workflow."
: "";
}
continue;
}
Expand Down
10 changes: 10 additions & 0 deletions Models/IedBrowserReadPlan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ public static IedBrowserReadSelectionKind SelectionKind(IedBrowserTreeRow? page)
private static readonly HashSet<string> ReadableFc = new(
["ST", "MX", "CF", "DC", "SP", "SG", "SE", "EX"], StringComparer.OrdinalIgnoreCase);

/// <summary>
/// FC CO denotes command parameters, not scalar MMS Read targets.
/// Keeping that separate from a genuinely missing read is essential
/// during FAT; no control operation is implied by Browser navigation.
/// </summary>
public static string UnobservedValue(string? functionalConstraint)
=> string.Equals(functionalConstraint, "CO", StringComparison.OrdinalIgnoreCase)
? "Control only"
: "Not read";

public static IReadOnlyList<IedBrowserReadTarget> Build(
LiveIedModelDiscoveryDocument? model, IedBrowserTreeRow? page,
bool readAll, string? selectedDoReference = null)
Expand Down
3 changes: 2 additions & 1 deletion Models/IedBrowserStructuredDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@
var hasDirect = matching.attr is not null;
var attr = hasDirect ? matching.attr : groupArray[0].attr;
var descendants = groupArray.Where(x => x.parts.Length > pathIndex + 1).ToArray();
var fullPrefix = IedBrowserLiveValues.CanonicalReference(attr.ObjectReference) +

Check warning on line 313 in Models/IedBrowserStructuredDetails.cs

View workflow job for this annotation

GitHub Actions / Build, test, validate and package Windows application

Dereference of a possibly null reference.

Check warning on line 313 in Models/IedBrowserStructuredDetails.cs

View workflow job for this annotation

GitHub Actions / Build, test, validate and package Windows application

Dereference of a possibly null reference.

Check warning on line 313 in Models/IedBrowserStructuredDetails.cs

View workflow job for this annotation

GitHub Actions / Build, test, validate and package Windows application

Dereference of a possibly null reference.

Check warning on line 313 in Models/IedBrowserStructuredDetails.cs

View workflow job for this annotation

GitHub Actions / Build, test, validate and package Windows application

Dereference of a possibly null reference.

Check warning on line 313 in Models/IedBrowserStructuredDetails.cs

View workflow job for this annotation

GitHub Actions / Build, test, validate and package Windows application

Dereference of a possibly null reference.

Check warning on line 313 in Models/IedBrowserStructuredDetails.cs

View workflow job for this annotation

GitHub Actions / Build, test, validate and package Windows application

Dereference of a possibly null reference.
"." + string.Join(".", groupArray[0].parts.Take(pathIndex + 1));
var polar = allowVector
? IedBrowserEngineeringComposite.BuildPolar(
Expand All @@ -336,7 +336,8 @@
: TimeChildren(depth + 1, attr.MmsReference, attr.FunctionalConstraint);

result.Add(new IedBrowserDetailRow("Data",
$"{name} [{attr.FunctionalConstraint}]", "Not read", depth,
$"{name} [{attr.FunctionalConstraint}]",
IedBrowserReadPlan.UnobservedValue(attr.FunctionalConstraint), depth,
$"IEC declaration · {attr.MmsReference} · {attr.TypeSource}")
{
// A structured intermediate row may have its own observed
Expand Down
48 changes: 48 additions & 0 deletions tests/ARSAS.Tests/M214IedBrowserControlOnlyReadStatusTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using AR.Iec61850.Discovery;
using ArIED61850Tester.Models;

namespace ARSAS.Tests;

public sealed class M214IedBrowserControlOnlyReadStatusTests
{
[Fact]
public void CommandParameters_AreNotDisplayedAsMissingMmsReads()
{
const string objectRef = "LD/CSWI1.Pos";
var attributes = new[]
{
new LiveIedDataAttributeModel
{
ObjectReference = objectRef,
AttributePath = "Pos.Oper.ctlVal",
MmsReference = "LD/CSWI1$CO$Pos$Oper$ctlVal",
FunctionalConstraint = "CO"
},
new LiveIedDataAttributeModel
{
ObjectReference = objectRef,
AttributePath = "Pos.stVal",
MmsReference = "LD/CSWI1$ST$Pos$stVal",
FunctionalConstraint = "ST"
}
};
var roots = IedBrowserStructuredDetails.AttributeRows(attributes, "Pos");
var control = Assert.Single(roots, row =>
row.Name == "Oper [CO]");
Assert.Equal("Control only", control.Value);
Assert.True(control.HasChildren);
Assert.Equal("Control only", Assert.Single(control.LoadChildren!()).Value);
var status = Assert.Single(roots, row => row.Name == "stVal [ST]");
Assert.Equal("Not read", status.Value);
}

[Theory]
[InlineData("CO", "Control only")]
[InlineData("co", "Control only")]
[InlineData("ST", "Not read")]
[InlineData("SG", "Not read")]
[InlineData("CF", "Not read")]
[InlineData("", "Not read")]
public void UnobservedLabel_HasExactFcSemantics(string fc, string expected)
=> Assert.Equal(expected, IedBrowserReadPlan.UnobservedValue(fc));
}
Loading