159 lines
7.8 KiB
C#
159 lines
7.8 KiB
C#
namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
|
|
/// <summary>
|
|
/// Driver capability for data-change subscriptions — covers both native subscriptions
|
|
/// (Galaxy MXAccess advisory, OPC UA monitored items, TwinCAT ADS notifications) and
|
|
/// driver-internal polled subscriptions (Modbus, AB CIP, S7, FOCAS). The driver owns
|
|
/// its polling loop where applicable; the Core just sees <see cref="OnDataChange"/>
|
|
/// callbacks regardless of mechanism.
|
|
/// </summary>
|
|
public interface ISubscribable
|
|
{
|
|
/// <summary>
|
|
/// Subscribe to data changes for a batch of attributes.
|
|
/// The driver MAY fire <see cref="OnDataChange"/> immediately with the current value
|
|
/// (initial-data callback per OPC UA convention) and again on every change.
|
|
/// </summary>
|
|
/// <returns>An opaque subscription handle the caller passes to <see cref="UnsubscribeAsync"/>.</returns>
|
|
Task<ISubscriptionHandle> SubscribeAsync(
|
|
IReadOnlyList<string> fullReferences,
|
|
TimeSpan publishingInterval,
|
|
CancellationToken cancellationToken);
|
|
|
|
/// <summary>
|
|
/// Subscribe to data changes with per-tag advanced tuning (sampling interval, queue
|
|
/// size, monitoring mode, deadband filter). Drivers that don't have a native concept
|
|
/// of these knobs (e.g. polled drivers like Modbus) MAY ignore the per-tag knobs and
|
|
/// delegate to the simple
|
|
/// <see cref="SubscribeAsync(IReadOnlyList{string}, TimeSpan, CancellationToken)"/>
|
|
/// overload — the default implementation does exactly that, so existing implementers
|
|
/// compile unchanged.
|
|
/// </summary>
|
|
/// <param name="tags">Per-tag subscription specs. <see cref="MonitoredTagSpec.TagName"/> is the driver-side full reference.</param>
|
|
/// <param name="publishingInterval">Subscription publishing interval, applied to the whole batch.</param>
|
|
/// <param name="cancellationToken">Cancellation.</param>
|
|
/// <returns>Opaque subscription handle for <see cref="UnsubscribeAsync"/>.</returns>
|
|
Task<ISubscriptionHandle> SubscribeAsync(
|
|
IReadOnlyList<MonitoredTagSpec> tags,
|
|
TimeSpan publishingInterval,
|
|
CancellationToken cancellationToken)
|
|
=> SubscribeAsync(
|
|
tags.Select(t => t.TagName).ToList(),
|
|
publishingInterval,
|
|
cancellationToken);
|
|
|
|
/// <summary>Cancel a subscription returned by either <c>SubscribeAsync</c> overload.</summary>
|
|
Task UnsubscribeAsync(ISubscriptionHandle handle, CancellationToken cancellationToken);
|
|
|
|
/// <summary>
|
|
/// Server-pushed data-change notification. Fires whenever a subscribed attribute changes,
|
|
/// and (per OPC UA convention) on subscription establishment for current values.
|
|
/// </summary>
|
|
event EventHandler<DataChangeEventArgs>? OnDataChange;
|
|
}
|
|
|
|
/// <summary>Opaque subscription identity returned by <see cref="ISubscribable.SubscribeAsync(IReadOnlyList{string}, TimeSpan, CancellationToken)"/>.</summary>
|
|
public interface ISubscriptionHandle
|
|
{
|
|
/// <summary>Driver-internal subscription identifier (for diagnostics + post-mortem).</summary>
|
|
string DiagnosticId { get; }
|
|
}
|
|
|
|
/// <summary>Event payload for <see cref="ISubscribable.OnDataChange"/>.</summary>
|
|
/// <param name="SubscriptionHandle">The handle returned by the original <see cref="ISubscribable.SubscribeAsync(IReadOnlyList{string}, TimeSpan, CancellationToken)"/> call.</param>
|
|
/// <param name="FullReference">Driver-side full reference of the changed attribute.</param>
|
|
/// <param name="Snapshot">New value + quality + timestamps.</param>
|
|
public sealed record DataChangeEventArgs(
|
|
ISubscriptionHandle SubscriptionHandle,
|
|
string FullReference,
|
|
DataValueSnapshot Snapshot);
|
|
|
|
/// <summary>
|
|
/// Per-tag subscription tuning. Maps onto OPC UA <c>MonitoredItem</c> properties for the
|
|
/// OpcUaClient driver; non-OPC-UA drivers either map a subset (e.g. ADS picks up
|
|
/// <see cref="SamplingIntervalMs"/>) or ignore the knobs entirely and fall back to the
|
|
/// simple <see cref="ISubscribable.SubscribeAsync(IReadOnlyList{string}, TimeSpan, CancellationToken)"/>.
|
|
/// </summary>
|
|
/// <param name="TagName">Driver-side full reference (e.g. <c>ns=2;s=Foo</c> for OPC UA).</param>
|
|
/// <param name="SamplingIntervalMs">
|
|
/// Server-side sampling rate in milliseconds. <c>null</c> = use the publishing interval.
|
|
/// Sub-publish-interval values let a server sample faster than it publishes (queue +
|
|
/// coalesce), useful for events that change between publish ticks.
|
|
/// </param>
|
|
/// <param name="QueueSize">Server-side notification queue depth. <c>null</c> = driver default (1).</param>
|
|
/// <param name="DiscardOldest">
|
|
/// When the server-side queue overflows: <c>true</c> drops oldest, <c>false</c> drops newest.
|
|
/// <c>null</c> = driver default (true — preserve recency).
|
|
/// </param>
|
|
/// <param name="MonitoringMode">
|
|
/// Per-item monitoring mode. <c>Reporting</c> = sample + publish, <c>Sampling</c> = sample
|
|
/// but suppress publishing (useful with triggering), <c>Disabled</c> = neither.
|
|
/// </param>
|
|
/// <param name="DataChangeFilter">
|
|
/// Optional data-change filter (deadband + trigger semantics). <c>null</c> = no filter
|
|
/// (every change publishes regardless of magnitude).
|
|
/// </param>
|
|
public sealed record MonitoredTagSpec(
|
|
string TagName,
|
|
double? SamplingIntervalMs = null,
|
|
uint? QueueSize = null,
|
|
bool? DiscardOldest = null,
|
|
SubscriptionMonitoringMode? MonitoringMode = null,
|
|
DataChangeFilterSpec? DataChangeFilter = null);
|
|
|
|
/// <summary>
|
|
/// OPC UA <c>DataChangeFilter</c> spec. Mirrors the OPC UA Part 4 §7.17.2 structure but
|
|
/// lives in Core.Abstractions so non-OpcUaClient drivers (e.g. Modbus, S7) can accept it
|
|
/// as metadata even if they ignore the deadband mechanics.
|
|
/// </summary>
|
|
/// <param name="Trigger">When to fire: status only / status+value / status+value+timestamp.</param>
|
|
/// <param name="DeadbandType">Deadband mode: none / absolute (engineering units) / percent of EURange.</param>
|
|
/// <param name="DeadbandValue">
|
|
/// Magnitude of the deadband. For <see cref="OtOpcUa.Core.Abstractions.DeadbandType.Absolute"/>
|
|
/// this is in the variable's engineering units; for <see cref="OtOpcUa.Core.Abstractions.DeadbandType.Percent"/>
|
|
/// it's a 0..100 percentage of EURange (server returns BadFilterNotAllowed if EURange isn't set).
|
|
/// </param>
|
|
public sealed record DataChangeFilterSpec(
|
|
DataChangeTrigger Trigger,
|
|
DeadbandType DeadbandType,
|
|
double DeadbandValue);
|
|
|
|
/// <summary>
|
|
/// OPC UA <c>DataChangeTrigger</c> values. Wraps the SDK enum so Core.Abstractions doesn't
|
|
/// leak an OPC-UA-stack reference into every driver project.
|
|
/// </summary>
|
|
public enum DataChangeTrigger
|
|
{
|
|
/// <summary>Fire only when StatusCode changes.</summary>
|
|
Status = 0,
|
|
/// <summary>Fire when StatusCode or Value changes (the OPC UA default).</summary>
|
|
StatusValue = 1,
|
|
/// <summary>Fire when StatusCode, Value, or SourceTimestamp changes.</summary>
|
|
StatusValueTimestamp = 2,
|
|
}
|
|
|
|
/// <summary>OPC UA deadband-filter modes.</summary>
|
|
public enum DeadbandType
|
|
{
|
|
/// <summary>No deadband — every value change publishes.</summary>
|
|
None = 0,
|
|
/// <summary>Deadband expressed in the variable's engineering units.</summary>
|
|
Absolute = 1,
|
|
/// <summary>Deadband expressed as 0..100 percent of the variable's EURange.</summary>
|
|
Percent = 2,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Per-item subscription monitoring mode. Wraps the OPC UA SDK's <c>MonitoringMode</c>
|
|
/// so Core.Abstractions stays SDK-free.
|
|
/// </summary>
|
|
public enum SubscriptionMonitoringMode
|
|
{
|
|
/// <summary>Item is created but neither sampling nor publishing.</summary>
|
|
Disabled = 0,
|
|
/// <summary>Item samples and queues but does not publish (useful with triggering).</summary>
|
|
Sampling = 1,
|
|
/// <summary>Item samples and publishes — the OPC UA default.</summary>
|
|
Reporting = 2,
|
|
}
|