Repository navigation
Expand file tree
/
Copy pathMmsReportDiscoveryMapper.cs
More file actions
139 lines (116 loc) · 5.41 KB
/
Copy pathMmsReportDiscoveryMapper.cs
File metadata and controls
139 lines (116 loc) · 5.41 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
namespace AR.Iec61850.Mms;
public static class MmsReportDiscoveryMapper
{
private static readonly string[] ReportAttributeNames =
[
"RptID", "RptEna", "Resv", "ResvTms", "DatSet", "ConfRev", "OptFlds", "BufTm", "SqNum",
"TrgOps", "IntgPd", "GI", "PurgeBuf", "EntryID", "TimeOfEntry"
];
public static MmsReportInventory BuildInventory(MmsDiscoverySnapshot snapshot)
{
ArgumentNullException.ThrowIfNull(snapshot);
var inventory = new MmsReportInventory();
inventory.DataSets.AddRange(BuildDataSets(snapshot.DomainVariableLists));
inventory.ReportControls.AddRange(BuildReportControls(snapshot.DomainVariables));
return inventory;
}
private static IEnumerable<MmsDataSetCandidate> BuildDataSets(IReadOnlyDictionary<string, IReadOnlyList<string>> domainVariableLists)
{
foreach (var domainPair in domainVariableLists.OrderBy(x => x.Key, StringComparer.OrdinalIgnoreCase))
{
var domain = domainPair.Key?.Trim() ?? string.Empty;
if (string.IsNullOrWhiteSpace(domain))
continue;
foreach (var raw in domainPair.Value.Distinct(StringComparer.OrdinalIgnoreCase).OrderBy(x => x, StringComparer.OrdinalIgnoreCase))
{
if (string.IsNullOrWhiteSpace(raw))
continue;
var (logicalNode, name, reference) = NormalizeDataSetReference(domain, raw);
yield return new MmsDataSetCandidate
{
Domain = domain,
LogicalNode = logicalNode,
Name = name,
RawMmsName = raw,
Reference = reference
};
}
}
}
private static IEnumerable<MmsReportControlCandidate> BuildReportControls(
IReadOnlyDictionary<string, IReadOnlyList<string>> domainVariables)
{
var map = new Dictionary<string, MmsReportControlCandidate>(StringComparer.OrdinalIgnoreCase);
foreach (var domainPair in domainVariables.OrderBy(x => x.Key, StringComparer.OrdinalIgnoreCase))
{
var domain = domainPair.Key?.Trim() ?? string.Empty;
if (string.IsNullOrWhiteSpace(domain))
continue;
foreach (var raw in domainPair.Value.Distinct(StringComparer.OrdinalIgnoreCase))
{
if (!TryParseReportVariable(domain, raw, out var parsed))
continue;
if (!map.TryGetValue(parsed.Reference, out var candidate))
{
candidate = parsed;
map[candidate.Reference] = candidate;
}
foreach (var attr in parsed.Attributes)
{
if (!candidate.Attributes.Contains(attr, StringComparer.OrdinalIgnoreCase))
candidate.Attributes.Add(attr);
}
}
}
return map.Values
.OrderByDescending(x => x.Buffered)
.ThenBy(x => x.Domain, StringComparer.OrdinalIgnoreCase)
.ThenBy(x => x.LogicalNode, StringComparer.OrdinalIgnoreCase)
.ThenBy(x => x.Name, StringComparer.OrdinalIgnoreCase)
.ToArray();
}
private static bool TryParseReportVariable(string domain, string raw, out MmsReportControlCandidate candidate)
{
candidate = new MmsReportControlCandidate();
if (string.IsNullOrWhiteSpace(raw))
return false;
var parts = raw.Split('$', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (parts.Length < 3)
return false;
var fcIndex = Array.FindIndex(parts, p =>
p.Equals("RP", StringComparison.OrdinalIgnoreCase) ||
p.Equals("BR", StringComparison.OrdinalIgnoreCase));
if (fcIndex < 1 || fcIndex + 1 >= parts.Length)
return false;
var logicalNode = parts[0];
var functionalConstraint = parts[fcIndex].ToUpperInvariant();
var name = parts[fcIndex + 1];
if (string.IsNullOrWhiteSpace(logicalNode) || string.IsNullOrWhiteSpace(name))
return false;
candidate = new MmsReportControlCandidate
{
Domain = domain,
LogicalNode = logicalNode,
FunctionalConstraint = functionalConstraint,
Name = name,
Buffered = functionalConstraint.Equals("BR", StringComparison.OrdinalIgnoreCase),
Reference = $"{domain}/{logicalNode}.{functionalConstraint}.{name}",
Attributes = parts.Skip(fcIndex + 2)
.Where(p => IsKnownReportAttribute(p) || !string.IsNullOrWhiteSpace(p))
.ToList()
};
return true;
}
private static bool IsKnownReportAttribute(string text)
=> ReportAttributeNames.Contains(text, StringComparer.OrdinalIgnoreCase);
private static (string LogicalNode, string Name, string Reference) NormalizeDataSetReference(string domain, string raw)
{
var cleaned = raw.Trim().Replace('$', '.');
if (string.IsNullOrWhiteSpace(cleaned))
return ("LLN0", raw, $"{domain}/LLN0.{raw}");
var parts = cleaned.Split('.', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (parts.Length >= 2)
return (parts[0], parts[^1], $"{domain}/{cleaned}");
return ("LLN0", cleaned, $"{domain}/LLN0.{cleaned}");
}
}