docs: backfill XML documentation across 756 files
v2-ci / build (push) Failing after 1m43s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped

Adds <summary>, <param>, <typeparam>, and <inheritdoc/> tags to public
members surfaced by commentchecker — resolves 5,847 of 5,869 issues
(99.6%) across three /fixdocs passes.
This commit is contained in:
Joseph Doherty
2026-05-28 08:10:17 -04:00
parent f9fc7dd2e1
commit 64e3fbe035
756 changed files with 9876 additions and 96 deletions
@@ -23,11 +23,20 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
private readonly IAlarmHistorianWriteBackend _backend;
/// <summary>
/// Initializes a new instance of the AahClientManagedAlarmEventWriter class.
/// </summary>
/// <param name="backend">The alarm historian write backend to delegate to.</param>
public AahClientManagedAlarmEventWriter(IAlarmHistorianWriteBackend backend)
{
_backend = backend ?? throw new ArgumentNullException(nameof(backend));
}
/// <summary>
/// Writes an array of alarm historian events asynchronously.
/// </summary>
/// <param name="events">The alarm events to write.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public async Task<bool[]> WriteAsync(AlarmHistorianEventDto[] events, CancellationToken cancellationToken)
{
if (events is null || events.Length == 0)
@@ -79,6 +88,9 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// trinary <see cref="AlarmHistorianWriteOutcome"/>. Exposed for the production
/// <see cref="SdkAlarmHistorianWriteBackend"/> to share the mapping with tests.
/// </summary>
/// <param name="hresult">The HRESULT code from the SDK call.</param>
/// <param name="isCommunicationError">Indicates whether the error is a communication-class error.</param>
/// <param name="isMalformedInput">Indicates whether the input was malformed.</param>
public static AlarmHistorianWriteOutcome MapOutcome(int hresult, bool isCommunicationError, bool isMalformedInput)
{
// Order matters: malformed input is permanent regardless of HRESULT pattern;
@@ -16,9 +16,14 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
private readonly object _lock = new object();
private readonly List<NodeEntry> _nodes;
/// <summary>Initializes the picker with default system clock.</summary>
/// <param name="config">Historian configuration.</param>
public HistorianClusterEndpointPicker(HistorianConfiguration config)
: this(config, () => DateTime.UtcNow) { }
/// <summary>Initializes the picker with custom clock function.</summary>
/// <param name="config">Historian configuration.</param>
/// <param name="clock">Clock function for testing.</param>
internal HistorianClusterEndpointPicker(HistorianConfiguration config, Func<DateTime> clock)
{
_clock = clock ?? throw new ArgumentNullException(nameof(clock));
@@ -36,11 +41,13 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
.ToList();
}
/// <summary>Gets the total count of configured nodes.</summary>
public int NodeCount
{
get { lock (_lock) return _nodes.Count; }
}
/// <summary>Gets the list of currently healthy nodes.</summary>
public IReadOnlyList<string> GetHealthyNodes()
{
lock (_lock)
@@ -50,6 +57,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
}
}
/// <summary>Gets the count of currently healthy nodes.</summary>
public int HealthyNodeCount
{
get
@@ -62,6 +70,9 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
}
}
/// <summary>Marks a node as failed and starts its cooldown.</summary>
/// <param name="node">Node name.</param>
/// <param name="error">Optional error message.</param>
public void MarkFailed(string node, string? error)
{
lock (_lock)
@@ -77,6 +88,8 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
}
}
/// <summary>Marks a node as healthy and clears its cooldown.</summary>
/// <param name="node">Node name.</param>
public void MarkHealthy(string node)
{
lock (_lock)
@@ -87,6 +100,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
}
}
/// <summary>Returns a snapshot of all node states.</summary>
public List<HistorianClusterNodeState> SnapshotNodeStates()
{
lock (_lock)
@@ -119,10 +133,15 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
private sealed class NodeEntry
{
/// <summary>Gets or sets the node name.</summary>
public string Name { get; set; } = "";
/// <summary>Gets or sets when cooldown expires.</summary>
public DateTime? CooldownUntil { get; set; }
/// <summary>Gets or sets the failure count.</summary>
public int FailureCount { get; set; }
/// <summary>Gets or sets the last error message.</summary>
public string? LastError { get; set; }
/// <summary>Gets or sets the last failure time.</summary>
public DateTime? LastFailureTime { get; set; }
}
}
@@ -8,11 +8,22 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// </summary>
public sealed class HistorianClusterNodeState
{
/// <summary>Gets or sets the node name.</summary>
public string Name { get; set; } = "";
/// <summary>Gets or sets a value indicating whether the node is healthy.</summary>
public bool IsHealthy { get; set; }
/// <summary>Gets or sets the time until the node exits cooldown mode.</summary>
public DateTime? CooldownUntil { get; set; }
/// <summary>Gets or sets the count of recent failures.</summary>
public int FailureCount { get; set; }
/// <summary>Gets or sets the last error message.</summary>
public string? LastError { get; set; }
/// <summary>Gets or sets the time of the last failure.</summary>
public DateTime? LastFailureTime { get; set; }
}
}
@@ -12,6 +12,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// </summary>
public sealed class HistorianConfiguration
{
/// <summary>Gets or sets a value indicating whether Historian integration is enabled.</summary>
public bool Enabled { get; set; } = false;
/// <summary>Single-node fallback when <see cref="ServerNames"/> is empty.</summary>
@@ -24,12 +25,19 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// </summary>
public List<string> ServerNames { get; set; } = new();
/// <summary>Gets or sets the failure cooldown period in seconds.</summary>
public int FailureCooldownSeconds { get; set; } = 60;
/// <summary>Gets or sets a value indicating whether to use integrated security.</summary>
public bool IntegratedSecurity { get; set; } = true;
/// <summary>Gets or sets the user name for authentication.</summary>
public string? UserName { get; set; }
/// <summary>Gets or sets the password for authentication.</summary>
public string? Password { get; set; }
/// <summary>Gets or sets the Historian server port.</summary>
public int Port { get; set; } = 32568;
/// <summary>Gets or sets the command timeout in seconds.</summary>
public int CommandTimeoutSeconds { get; set; } = 30;
/// <summary>Gets or sets the maximum number of values per read operation.</summary>
public int MaxValuesPerRead { get; set; } = 10000;
/// <summary>
@@ -40,9 +40,15 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
private readonly HistorianClusterEndpointPicker _picker;
/// <summary>Initializes a new instance of the <see cref="HistorianDataSource"/> class with the default connection factory.</summary>
/// <param name="config">The historian configuration.</param>
public HistorianDataSource(HistorianConfiguration config)
: this(config, new SdkHistorianConnectionFactory(), null) { }
/// <summary>Initializes a new instance of the <see cref="HistorianDataSource"/> class with the specified connection factory and endpoint picker.</summary>
/// <param name="config">The historian configuration.</param>
/// <param name="factory">The historian connection factory.</param>
/// <param name="picker">The optional cluster endpoint picker.</param>
internal HistorianDataSource(
HistorianConfiguration config,
IHistorianConnectionFactory factory,
@@ -76,6 +82,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// <em>connection</em> (rather than the query payload) is the problem and the
/// shared SDK connection should therefore be reset. Internal for unit testing.
/// </summary>
/// <param name="code">The historian access error code.</param>
internal static bool IsConnectionClassError(HistorianAccessError.ErrorValue code)
=> ConnectionErrorCodes.Contains(code);
@@ -88,6 +95,8 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// single pipe-server connection thread indefinitely. See
/// Driver.Historian.Wonderware-010.
/// </summary>
/// <param name="cfg">The historian configuration.</param>
/// <param name="ct">The cancellation token.</param>
internal static CancellationTokenSource BuildRequestCts(HistorianConfiguration cfg, CancellationToken ct)
{
var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
@@ -151,6 +160,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
};
}
/// <summary>Gets a snapshot of the current health status.</summary>
public HistorianHealthSnapshot GetHealthSnapshot()
{
var nodeStates = _picker.SnapshotNodeStates();
@@ -309,7 +319,11 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// </summary>
internal sealed class QueryClassStartQueryException : InvalidOperationException
{
/// <summary>Gets the error code that caused the exception.</summary>
public HistorianAccessError.ErrorValue Code { get; }
/// <summary>Initializes a new instance of the <see cref="QueryClassStartQueryException"/> class.</summary>
/// <param name="message">The exception message.</param>
/// <param name="code">The historian access error code.</param>
public QueryClassStartQueryException(string message, HistorianAccessError.ErrorValue code)
: base(message)
{
@@ -382,6 +396,12 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
}
}
/// <summary>Reads raw historical samples for the specified tag.</summary>
/// <param name="tagName">The tag name.</param>
/// <param name="startTime">The start time for the query.</param>
/// <param name="endTime">The end time for the query.</param>
/// <param name="maxValues">The maximum number of values to return.</param>
/// <param name="ct">Cancellation token for the operation.</param>
public Task<List<HistorianSample>> ReadRawAsync(
string tagName, DateTime startTime, DateTime endTime, int maxValues,
CancellationToken ct = default)
@@ -465,6 +485,13 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
return Task.FromResult(results);
}
/// <summary>Reads aggregate historical samples for the specified tag.</summary>
/// <param name="tagName">The tag name.</param>
/// <param name="startTime">The start time for the query.</param>
/// <param name="endTime">The end time for the query.</param>
/// <param name="intervalMs">The interval in milliseconds.</param>
/// <param name="aggregateColumn">The aggregate column name.</param>
/// <param name="ct">Cancellation token for the operation.</param>
public Task<List<HistorianAggregateSample>> ReadAggregateAsync(
string tagName, DateTime startTime, DateTime endTime,
double intervalMs, string aggregateColumn,
@@ -545,6 +572,10 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
return Task.FromResult(results);
}
/// <summary>Reads historical samples at specific timestamps for the specified tag.</summary>
/// <param name="tagName">The tag name.</param>
/// <param name="timestamps">The timestamps to read.</param>
/// <param name="ct">Cancellation token for the operation.</param>
public Task<List<HistorianSample>> ReadAtTimeAsync(
string tagName, DateTime[] timestamps,
CancellationToken ct = default)
@@ -627,6 +658,12 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
return Task.FromResult(results);
}
/// <summary>Reads historical events within the specified time range.</summary>
/// <param name="sourceName">The optional event source name filter.</param>
/// <param name="startTime">The start time for the query.</param>
/// <param name="endTime">The end time for the query.</param>
/// <param name="maxEvents">The maximum number of events to return.</param>
/// <param name="ct">Cancellation token for the operation.</param>
public Task<List<HistorianEventDto>> ReadEventsAsync(
string? sourceName, DateTime startTime, DateTime endTime, int maxEvents,
CancellationToken ct = default)
@@ -726,6 +763,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// as a string; this is a known edge case of the SDK binding.
/// </para>
/// </summary>
/// <param name="result">The history query result.</param>
internal static object? SelectValue(HistoryQueryResult result)
=> SelectValueFromPair(result.Value, result.StringValue);
@@ -735,6 +773,8 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// <see cref="HistoryQueryResult"/> (whose internal property initialisers make
/// it impractical to fake). See Driver.Historian.Wonderware-012.
/// </summary>
/// <param name="value">The numeric value.</param>
/// <param name="stringValue">The string value.</param>
internal static object? SelectValueFromPair(double value, string? stringValue)
{
if (!string.IsNullOrEmpty(stringValue) && value == 0)
@@ -742,6 +782,9 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
return value;
}
/// <summary>Extracts the specified aggregate value from an analog summary query result.</summary>
/// <param name="result">The analog summary query result.</param>
/// <param name="column">The aggregate column name.</param>
internal static double? ExtractAggregateValue(AnalogSummaryQueryResult result, string column)
{
switch (column)
@@ -757,6 +800,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
}
}
/// <summary>Disposes the historian data source and releases its resources.</summary>
public void Dispose()
{
if (_disposed) return;
@@ -8,11 +8,22 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// </summary>
public sealed class HistorianEventDto
{
/// <summary>Gets or sets the unique identifier for the event.</summary>
public Guid Id { get; set; }
/// <summary>Gets or sets the source of the event.</summary>
public string? Source { get; set; }
/// <summary>Gets or sets the time when the event occurred.</summary>
public DateTime EventTime { get; set; }
/// <summary>Gets or sets the time when the event was received.</summary>
public DateTime ReceivedTime { get; set; }
/// <summary>Gets or sets the display text for the event.</summary>
public string? DisplayText { get; set; }
/// <summary>Gets or sets the severity level of the event.</summary>
public ushort Severity { get; set; }
}
}
@@ -9,19 +9,33 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// </summary>
public sealed class HistorianHealthSnapshot
{
/// <summary>Gets or sets the total number of queries executed.</summary>
public long TotalQueries { get; set; }
/// <summary>Gets or sets the total number of successful queries.</summary>
public long TotalSuccesses { get; set; }
/// <summary>Gets or sets the total number of failed queries.</summary>
public long TotalFailures { get; set; }
/// <summary>Gets or sets the number of consecutive failures.</summary>
public int ConsecutiveFailures { get; set; }
/// <summary>Gets or sets the time of the last successful query.</summary>
public DateTime? LastSuccessTime { get; set; }
/// <summary>Gets or sets the time of the last failed query.</summary>
public DateTime? LastFailureTime { get; set; }
/// <summary>Gets or sets the last error message, if any.</summary>
public string? LastError { get; set; }
/// <summary>Gets or sets a value indicating whether the process connection is open.</summary>
public bool ProcessConnectionOpen { get; set; }
/// <summary>Gets or sets a value indicating whether the event connection is open.</summary>
public bool EventConnectionOpen { get; set; }
/// <summary>Gets or sets the name of the active process node.</summary>
public string? ActiveProcessNode { get; set; }
/// <summary>Gets or sets the name of the active event node.</summary>
public string? ActiveEventNode { get; set; }
/// <summary>Gets or sets the total number of cluster nodes.</summary>
public int NodeCount { get; set; }
/// <summary>Gets or sets the number of healthy cluster nodes.</summary>
public int HealthyNodeCount { get; set; }
/// <summary>Gets or sets the list of cluster node states.</summary>
public List<HistorianClusterNodeState> Nodes { get; set; } = new();
}
}
@@ -15,6 +15,8 @@ public static class HistorianQualityMapper
/// family bits decide the category (Good &gt;= 192, Uncertain 64-191, Bad 0-63); the
/// low-nibble subcode selects the specific code.
/// </summary>
/// <param name="q">The OPC DA quality byte.</param>
/// <returns>The corresponding OPC UA status code.</returns>
public static uint Map(byte q) => q switch
{
// Good family (192+)
@@ -11,11 +11,13 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// </summary>
public sealed class HistorianSample
{
/// <summary>Gets or sets the historical data value.</summary>
public object? Value { get; set; }
/// <summary>Raw OPC DA quality byte from the historian SDK (low 8 bits of OpcQuality).</summary>
/// <summary>Gets or sets the raw OPC DA quality byte from the historian SDK (low 8 bits of OpcQuality).</summary>
public byte Quality { get; set; }
/// <summary>Gets or sets the UTC timestamp of the historical sample.</summary>
public DateTime TimestampUtc { get; set; }
}
@@ -25,7 +27,9 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// </summary>
public sealed class HistorianAggregateSample
{
/// <summary>Gets or sets the aggregate value, or null if unavailable.</summary>
public double? Value { get; set; }
/// <summary>Gets or sets the UTC timestamp of the aggregate sample.</summary>
public DateTime TimestampUtc { get; set; }
}
}
@@ -23,6 +23,8 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// input slot in the same order — must always return an array of the same
/// length as <paramref name="events"/>.
/// </summary>
/// <param name="events">The events to write to the historian.</param>
/// <param name="cancellationToken">Token to cancel the operation.</param>
Task<AlarmHistorianWriteOutcome[]> WriteBatchAsync(
AlarmHistorianEventDto[] events,
CancellationToken cancellationToken);
@@ -16,6 +16,10 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// <c>false</c> because <c>HistorianAccess.AddStreamedValue</c> fails with
/// <c>WriteToReadOnlyFile</c> on a read-only session.
/// </summary>
/// <param name="config">The historian configuration.</param>
/// <param name="type">The type of connection to create.</param>
/// <param name="readOnly">Whether the connection should be read-only.</param>
/// <returns>An open HistorianAccess connection.</returns>
HistorianAccess CreateAndConnect(
HistorianConfiguration config, HistorianConnectionType type, bool readOnly = true);
}
@@ -23,6 +27,11 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// <summary>Production implementation — opens real Historian SDK connections.</summary>
internal sealed class SdkHistorianConnectionFactory : IHistorianConnectionFactory
{
/// <summary>Creates and connects a Historian SDK connection.</summary>
/// <param name="config">The historian configuration.</param>
/// <param name="type">The type of connection to create.</param>
/// <param name="readOnly">Whether the connection should be read-only.</param>
/// <returns>An open HistorianAccess connection.</returns>
public HistorianAccess CreateAndConnect(
HistorianConfiguration config, HistorianConnectionType type, bool readOnly = true)
{
@@ -66,6 +75,10 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// Builds the <see cref="HistorianConnectionArgs"/> for a connection. Pure (no SDK
/// side effects) so the read-only-vs-write argument shaping is unit-testable.
/// </summary>
/// <param name="config">The historian configuration.</param>
/// <param name="type">The type of connection to create.</param>
/// <param name="readOnly">Whether the connection should be read-only.</param>
/// <returns>The configured connection arguments.</returns>
internal static HistorianConnectionArgs BuildConnectionArgs(
HistorianConfiguration config, HistorianConnectionType type, bool readOnly)
{
@@ -14,23 +14,52 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// </summary>
public interface IHistorianDataSource : IDisposable
{
/// <summary>Reads raw historical samples asynchronously.</summary>
/// <param name="tagName">The tag name to read from.</param>
/// <param name="startTime">The start time of the time range.</param>
/// <param name="endTime">The end time of the time range.</param>
/// <param name="maxValues">The maximum number of values to return.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task representing the asynchronous operation that returns a list of historian samples.</returns>
Task<List<HistorianSample>> ReadRawAsync(
string tagName, DateTime startTime, DateTime endTime, int maxValues,
CancellationToken ct = default);
/// <summary>Reads aggregate historical samples asynchronously.</summary>
/// <param name="tagName">The tag name to read from.</param>
/// <param name="startTime">The start time of the time range.</param>
/// <param name="endTime">The end time of the time range.</param>
/// <param name="intervalMs">The interval in milliseconds for aggregation.</param>
/// <param name="aggregateColumn">The column to aggregate.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task representing the asynchronous operation that returns a list of aggregate samples.</returns>
Task<List<HistorianAggregateSample>> ReadAggregateAsync(
string tagName, DateTime startTime, DateTime endTime,
double intervalMs, string aggregateColumn,
CancellationToken ct = default);
/// <summary>Reads historical samples at specific times asynchronously.</summary>
/// <param name="tagName">The tag name to read from.</param>
/// <param name="timestamps">The array of timestamps at which to read values.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task representing the asynchronous operation that returns a list of historian samples.</returns>
Task<List<HistorianSample>> ReadAtTimeAsync(
string tagName, DateTime[] timestamps,
CancellationToken ct = default);
/// <summary>Reads historical events asynchronously.</summary>
/// <param name="sourceName">The source name to filter events, or null for all sources.</param>
/// <param name="startTime">The start time of the time range.</param>
/// <param name="endTime">The end time of the time range.</param>
/// <param name="maxEvents">The maximum number of events to return.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task representing the asynchronous operation that returns a list of historian events.</returns>
Task<List<HistorianEventDto>> ReadEventsAsync(
string? sourceName, DateTime startTime, DateTime endTime, int maxEvents,
CancellationToken ct = default);
/// <summary>Gets a health snapshot of the data source.</summary>
/// <returns>A HistorianHealthSnapshot containing the current health information.</returns>
HistorianHealthSnapshot GetHealthSnapshot();
}
}
@@ -84,9 +84,15 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
private string? _activeNode;
private bool _disposed;
/// <summary>Initializes a new instance using the default SDK connection factory.</summary>
/// <param name="config">The historian configuration.</param>
public SdkAlarmHistorianWriteBackend(HistorianConfiguration config)
: this(config, new SdkHistorianConnectionFactory(), null) { }
/// <summary>Initializes a new instance with injected dependencies (for testing).</summary>
/// <param name="config">The historian configuration.</param>
/// <param name="factory">The connection factory.</param>
/// <param name="picker">The cluster endpoint picker, or null to use a new instance.</param>
internal SdkAlarmHistorianWriteBackend(
HistorianConfiguration config,
IHistorianConnectionFactory factory,
@@ -97,6 +103,10 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
_picker = picker ?? new HistorianClusterEndpointPicker(config);
}
/// <summary>Writes a batch of alarm events to the historian, returning outcomes for each event.</summary>
/// <param name="events">The alarm events to write.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>An array of outcomes corresponding to each input event.</returns>
public Task<AlarmHistorianWriteOutcome[]> WriteBatchAsync(
AlarmHistorianEventDto[] events,
CancellationToken cancellationToken)
@@ -183,6 +193,8 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// event properties — operator-comment fidelity is the field the value-driven
/// fallback path cannot carry.
/// </summary>
/// <param name="dto">The alarm event data transfer object.</param>
/// <returns>The mapped HistorianEvent.</returns>
internal static HistorianEvent ToHistorianEvent(AlarmHistorianEventDto dto)
{
// The ArchestrA SDK marks these HistorianEvent members obsolete but still honours
@@ -238,6 +250,8 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
/// <see cref="AahClientManagedAlarmEventWriter.MapOutcome"/> mapping. Exposed for
/// unit tests — connection-class codes are handled separately by the batch loop.
/// </summary>
/// <param name="code">The error code to classify.</param>
/// <returns>The corresponding write outcome.</returns>
internal static AlarmHistorianWriteOutcome ClassifyOutcome(HistorianAccessError.ErrorValue code)
=> AahClientManagedAlarmEventWriter.MapOutcome(
(int)code,
@@ -365,6 +379,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend
RequestTimeoutSeconds = _config.RequestTimeoutSeconds,
};
/// <summary>Disposes the connection and releases resources.</summary>
public void Dispose()
{
if (_disposed) return;