826312befe
Clears CommentChecker findings in src/ (3,280 -> 209, the remainder being false positives like PLC-5/IEEE-754/M0.0) by adding missing summaries/params/returns, normalising inheritdoc usage, and removing Gitea/review tracking IDs while keeping the rationale they annotated. Comment-only: comment-stripped sources are identical to the parent commit.
96 lines
4.4 KiB
C#
96 lines
4.4 KiB
C#
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Commons.Browsing;
|
|
|
|
/// <summary>
|
|
/// Serves a captured discovery snapshot through the standard picker session interface.
|
|
/// No I/O after open; the tree is point-in-time (picker offers Refresh = close + re-open).
|
|
/// The tree may be shared by coalesced sessions — dispose drops only this session's
|
|
/// reference. See design §4.3.
|
|
/// </summary>
|
|
public sealed class CapturedTreeBrowseSession(CapturedTree tree) : IBrowseSession
|
|
{
|
|
private CapturedTree? _tree = tree;
|
|
|
|
/// <inheritdoc />
|
|
public Guid Token { get; } = Guid.NewGuid();
|
|
|
|
/// <inheritdoc />
|
|
public DateTime LastUsedUtc { get; private set; } = DateTime.UtcNow;
|
|
|
|
/// <inheritdoc />
|
|
public Task<IReadOnlyList<BrowseNode>> RootAsync(CancellationToken cancellationToken)
|
|
{
|
|
var t = Tree();
|
|
var nodes = t.Root.Children.Select(Map).ToList();
|
|
if (t.Truncated)
|
|
nodes.Add(new BrowseNode("__truncated__",
|
|
"⚠ Results truncated — narrow the driver config or use manual entry",
|
|
BrowseNodeKind.Folder, HasChildrenHint: false));
|
|
LastUsedUtc = DateTime.UtcNow;
|
|
return Task.FromResult<IReadOnlyList<BrowseNode>>(nodes);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public Task<IReadOnlyList<BrowseNode>> ExpandAsync(string nodeId, CancellationToken cancellationToken)
|
|
{
|
|
var t = Tree();
|
|
LastUsedUtc = DateTime.UtcNow;
|
|
return Task.FromResult<IReadOnlyList<BrowseNode>>(
|
|
t.ById.TryGetValue(nodeId, out var n) ? n.Children.Select(Map).ToList() : []);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public Task<IReadOnlyList<AttributeInfo>> AttributesAsync(string nodeId, CancellationToken cancellationToken)
|
|
{
|
|
var t = Tree();
|
|
LastUsedUtc = DateTime.UtcNow;
|
|
if (!t.ById.TryGetValue(nodeId, out var n))
|
|
return Task.FromResult<IReadOnlyList<AttributeInfo>>([]);
|
|
var attrs = new List<AttributeInfo>();
|
|
if (n.Attribute is { } a)
|
|
attrs.Add(new AttributeInfo(n.Name, a.DriverDataType.ToString(), a.IsArray,
|
|
a.SecurityClass.ToString(), a.IsAlarm || n.IsAlarmMarked, NativeTypeFields(a)));
|
|
attrs.AddRange(n.Properties.Select(p =>
|
|
new AttributeInfo(p.Name, p.Type.ToString(), false, "", false)));
|
|
return Task.FromResult<IReadOnlyList<AttributeInfo>>(attrs);
|
|
}
|
|
|
|
/// <summary>Drops this session's reference to the captured tree; the tree itself may be shared.</summary>
|
|
/// <returns>A completed <see cref="ValueTask"/>.</returns>
|
|
public ValueTask DisposeAsync()
|
|
{
|
|
_tree = null; // drop this session's reference only — tree may be shared (coalescing)
|
|
return ValueTask.CompletedTask;
|
|
}
|
|
|
|
private CapturedTree Tree() => _tree ?? throw new ObjectDisposedException(nameof(CapturedTreeBrowseSession));
|
|
|
|
/// <summary>
|
|
/// Surfaces a leaf's driver-native type name as the <c>dataType</c> address field, so a
|
|
/// browse-commit can stamp the driver-typed <c>TagConfig</c> with the type the controller
|
|
/// actually reported.
|
|
/// </summary>
|
|
/// <param name="a">The captured attribute.</param>
|
|
/// <returns>The address fields, or null when the driver reported no native type.</returns>
|
|
/// <remarks>
|
|
/// It rides <see cref="AttributeInfo.AddressFields"/> rather than
|
|
/// <see cref="AttributeInfo.DriverDataType"/> because that field is the driver-AGNOSTIC
|
|
/// type and several consumers already read it as such. The commit mapper cannot recover the
|
|
/// native type from it: <c>SInt</c>/<c>Int</c>/<c>DInt</c> all arrive as <c>Int32</c>, so a
|
|
/// reverse map would bind a 16-bit INT as a 32-bit DINT and read four bytes for two.
|
|
/// <para>
|
|
/// Null when the driver stamped nothing — the mapper then leaves the model's own default in
|
|
/// place, which is exactly the previous behaviour rather than a guess.
|
|
/// </para>
|
|
/// </remarks>
|
|
private static IReadOnlyDictionary<string, string>? NativeTypeFields(DriverAttributeInfo a)
|
|
=> string.IsNullOrWhiteSpace(a.NativeDataType)
|
|
? null
|
|
: new Dictionary<string, string>(StringComparer.Ordinal) { ["dataType"] = a.NativeDataType };
|
|
|
|
private static BrowseNode Map(CapturedNode n) => n.IsLeaf
|
|
? new BrowseNode(n.Id, n.DisplayName, BrowseNodeKind.Leaf, false)
|
|
: new BrowseNode(n.Id, n.DisplayName, BrowseNodeKind.Folder, n.Children.Count > 0);
|
|
}
|