using ZB.MOM.WW.ScadaBridge.Commons.Types.Alarms;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer;
///
/// Parsed native-alarm condition filter (M2.4 / #8).
///
///
/// A source's conditionFilter is a comma-separated, case-insensitive list
/// of alarm/condition type names, matched against
/// . A null, blank, or
/// all-empty list means "mirror every condition" (the historical behaviour),
/// represented here by .
///
///
///
/// This is the authoritative client-side gate consulted in the
/// DataConnectionActor routing path, so it applies uniformly across OPC UA
/// (whose server-side WhereClause is only a bandwidth optimisation) and the
/// MxGateway (whose single gateway-wide feed has no server-side filter at all).
/// Parse once at subscribe time; is the hot-path check.
///
///
public sealed class AlarmConditionFilter
{
/// The shared allow-all instance (empty filter set).
public static readonly AlarmConditionFilter AllowAll = new(new HashSet(StringComparer.OrdinalIgnoreCase));
private readonly HashSet _names;
private AlarmConditionFilter(HashSet names) => _names = names;
/// true when no type names are configured — every condition is allowed.
public bool IsEmpty => _names.Count == 0;
/// The normalized (trimmed) type names, for the OPC UA server-side WhereClause optimisation.
public IReadOnlyCollection Names => _names;
///
/// Parses a raw conditionFilter string into a normalized, case-insensitive
/// type-name set. null/blank/all-empty input yields an empty (allow-all) filter.
///
/// The raw comma-separated filter string, or null.
/// A parsed ; never null.
public static AlarmConditionFilter Parse(string? conditionFilter)
{
if (string.IsNullOrWhiteSpace(conditionFilter))
return AllowAll;
var names = new HashSet(StringComparer.OrdinalIgnoreCase);
foreach (var raw in conditionFilter.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
names.Add(raw);
return names.Count == 0 ? AllowAll : new AlarmConditionFilter(names);
}
///
/// Returns true when should be delivered:
/// the filter is empty (allow all), the transition is a framing sentinel
/// (, which carries no condition
/// type and must never be swallowed or the snapshot swap never completes), or its
/// is in the configured set.
///
/// The protocol-neutral transition to test.
/// true to deliver the transition; false to drop it.
public bool IsAllowed(NativeAlarmTransition transition)
{
if (_names.Count == 0)
return true;
// SnapshotComplete is pure framing (no condition payload) — never filter it.
if (transition.Kind == AlarmTransitionKind.SnapshotComplete)
return true;
return _names.Contains(transition.AlarmTypeName);
}
}