fix(dcl): apply native-alarm conditionFilter (client-side gate + OPC UA WhereClause) (#8)
conditionFilter was plumbed end-to-end but applied nowhere — a filtered source silently mirrored all conditions. Define the filter as a comma-separated, case-insensitive list of condition type names (blank = all); enforce it authoritatively client-side in DataConnectionActor routing (uniform across OPC UA + MxGateway) and, for OPC UA, additionally build a server-side EventFilter WhereClause as a bandwidth optimization.
This commit is contained in:
+76
@@ -0,0 +1,76 @@
|
||||
using Opc.Ua;
|
||||
using ZB.MOM.WW.ScadaBridge.DataConnectionLayer;
|
||||
using ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Adapters;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Tests.Adapters;
|
||||
|
||||
/// <summary>
|
||||
/// M2.4 (#8): the OPC UA EventFilter gains a server-side <see cref="ContentFilter"/>
|
||||
/// WhereClause as a bandwidth optimisation when a condition-type filter is present.
|
||||
/// The client-side gate in DataConnectionActor remains authoritative; these tests
|
||||
/// only pin the filter-shaping. No live server required — pure SDK object building.
|
||||
/// </summary>
|
||||
public class RealOpcUaClientAlarmFilterTests
|
||||
{
|
||||
[Fact]
|
||||
public void BuildAlarmEventFilter_NoFilter_HasNoWhereClause()
|
||||
{
|
||||
var filter = RealOpcUaClient.BuildAlarmEventFilter(AlarmConditionFilter.AllowAll);
|
||||
Assert.NotEmpty(filter.SelectClauses);
|
||||
Assert.Empty(filter.WhereClause.Elements);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildAlarmEventFilter_WithKnownTypes_BuildsNonEmptyWhereClause()
|
||||
{
|
||||
var parsed = AlarmConditionFilter.Parse("LimitAlarmType,DiscreteAlarmType");
|
||||
var filter = RealOpcUaClient.BuildAlarmEventFilter(parsed);
|
||||
|
||||
Assert.NotEmpty(filter.WhereClause.Elements);
|
||||
// Two known types → two OfType operands (OR'd when more than one).
|
||||
var ofTypeCount = filter.WhereClause.Elements.Count(e => e.FilterOperator == FilterOperator.OfType);
|
||||
Assert.Equal(2, ofTypeCount);
|
||||
Assert.Contains(filter.WhereClause.Elements, e => e.FilterOperator == FilterOperator.Or);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildAlarmEventFilter_SingleKnownType_BuildsSingleOfType_NoOr()
|
||||
{
|
||||
var parsed = AlarmConditionFilter.Parse("AlarmConditionType");
|
||||
var filter = RealOpcUaClient.BuildAlarmEventFilter(parsed);
|
||||
|
||||
Assert.Single(filter.WhereClause.Elements);
|
||||
Assert.Equal(FilterOperator.OfType, filter.WhereClause.Elements[0].FilterOperator);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildAlarmEventFilter_TypeMatchingIsCaseInsensitive()
|
||||
{
|
||||
var parsed = AlarmConditionFilter.Parse("limitalarmtype");
|
||||
var filter = RealOpcUaClient.BuildAlarmEventFilter(parsed);
|
||||
Assert.Single(filter.WhereClause.Elements, e => e.FilterOperator == FilterOperator.OfType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildAlarmEventFilter_AllUnknownTypes_OmitsWhereClause()
|
||||
{
|
||||
// Custom/vendor type names we cannot map to standard NodeIds are skipped
|
||||
// server-side; the client-side gate still enforces them. Omitting the
|
||||
// WhereClause is the safe choice — a partial WhereClause would drop the
|
||||
// unmapped types at the server and break correctness.
|
||||
var parsed = AlarmConditionFilter.Parse("MyVendorCustomAlarm,AnotherCustomThing");
|
||||
var filter = RealOpcUaClient.BuildAlarmEventFilter(parsed);
|
||||
Assert.Empty(filter.WhereClause.Elements);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildAlarmEventFilter_MixedKnownAndUnknown_OmitsWhereClause()
|
||||
{
|
||||
// If ANY requested type can't be mapped, a server-side WhereClause would
|
||||
// silently drop that type's events — so we omit the optimisation entirely
|
||||
// and let the (authoritative) client gate do the filtering.
|
||||
var parsed = AlarmConditionFilter.Parse("LimitAlarmType,MyVendorCustomAlarm");
|
||||
var filter = RealOpcUaClient.BuildAlarmEventFilter(parsed);
|
||||
Assert.Empty(filter.WhereClause.Elements);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user