Merge pull request 'Phase 2 PR 9 — thread IsAlarm discovery flag end-to-end' (#8) from phase-2-pr9-alarms into v2
This commit was merged in pull request #8.
This commit is contained in:
@@ -19,10 +19,17 @@ namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
/// <param name="ArrayDim">Declared array length when <see cref="IsArray"/> is true; null otherwise.</param>
|
||||
/// <param name="SecurityClass">Write-authorization tier for this attribute.</param>
|
||||
/// <param name="IsHistorized">True when this attribute is expected to feed historian / HistoryRead.</param>
|
||||
/// <param name="IsAlarm">
|
||||
/// True when this attribute represents an alarm condition (Galaxy: has an
|
||||
/// <c>AlarmExtension</c> primitive). The generic node-manager enriches the variable with an
|
||||
/// OPC UA <c>AlarmConditionState</c> when true. Defaults to false so existing non-Galaxy
|
||||
/// drivers aren't forced to flow a flag they don't produce.
|
||||
/// </param>
|
||||
public sealed record DriverAttributeInfo(
|
||||
string FullName,
|
||||
DriverDataType DriverDataType,
|
||||
bool IsArray,
|
||||
uint? ArrayDim,
|
||||
SecurityClassification SecurityClass,
|
||||
bool IsHistorized);
|
||||
bool IsHistorized,
|
||||
bool IsAlarm = false);
|
||||
|
||||
@@ -147,6 +147,7 @@ public sealed class DbBackedGalaxyBackend(GalaxyRepository repository) : IGalaxy
|
||||
ArrayDim = row.ArrayDimension is int d and > 0 ? (uint)d : null,
|
||||
SecurityClassification = row.SecurityClassification,
|
||||
IsHistorized = row.IsHistorized,
|
||||
IsAlarm = row.IsAlarm,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -392,6 +392,7 @@ public sealed class MxAccessGalaxyBackend : IGalaxyBackend, IDisposable
|
||||
ArrayDim = row.ArrayDimension is int d and > 0 ? (uint)d : null,
|
||||
SecurityClassification = row.SecurityClassification,
|
||||
IsHistorized = row.IsHistorized,
|
||||
IsAlarm = row.IsAlarm,
|
||||
};
|
||||
|
||||
private static string MapCategory(int categoryId) => categoryId switch
|
||||
|
||||
@@ -123,7 +123,8 @@ public sealed class GalaxyProxyDriver(GalaxyProxyOptions options)
|
||||
IsArray: attr.IsArray,
|
||||
ArrayDim: attr.ArrayDim,
|
||||
SecurityClass: MapSecurity(attr.SecurityClassification),
|
||||
IsHistorized: attr.IsHistorized));
|
||||
IsHistorized: attr.IsHistorized,
|
||||
IsAlarm: attr.IsAlarm));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,15 @@ public sealed class GalaxyAttributeInfo
|
||||
[Key(3)] public uint? ArrayDim { get; set; }
|
||||
[Key(4)] public int SecurityClassification { get; set; }
|
||||
[Key(5)] public bool IsHistorized { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True when the attribute has an AlarmExtension primitive in the Galaxy repository
|
||||
/// (<c>primitive_definition.primitive_name = 'AlarmExtension'</c>). The generic
|
||||
/// node-manager uses this to enrich the variable's OPC UA node with an
|
||||
/// <c>AlarmConditionState</c> during address-space build. Added in PR 9 as the
|
||||
/// discovery-side foundation for the alarm event wire-up that follows in PR 10+.
|
||||
/// </summary>
|
||||
[Key(6)] public bool IsAlarm { get; set; }
|
||||
}
|
||||
|
||||
[MessagePackObject]
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using MessagePack;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Shared.Contracts;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests;
|
||||
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class AlarmDiscoveryTests
|
||||
{
|
||||
/// <summary>
|
||||
/// PR 9 — IsAlarm must survive the MessagePack round-trip at Key=6 position.
|
||||
/// Regression guard: any reorder of keys in GalaxyAttributeInfo would silently corrupt
|
||||
/// the flag in the wire payload since MessagePack encodes by key number, not field name.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void GalaxyAttributeInfo_IsAlarm_round_trips_true_through_MessagePack()
|
||||
{
|
||||
var input = new GalaxyAttributeInfo
|
||||
{
|
||||
AttributeName = "TankLevel",
|
||||
MxDataType = 2,
|
||||
IsArray = false,
|
||||
ArrayDim = null,
|
||||
SecurityClassification = 1,
|
||||
IsHistorized = true,
|
||||
IsAlarm = true,
|
||||
};
|
||||
|
||||
var bytes = MessagePackSerializer.Serialize(input);
|
||||
var decoded = MessagePackSerializer.Deserialize<GalaxyAttributeInfo>(bytes);
|
||||
|
||||
decoded.IsAlarm.ShouldBeTrue();
|
||||
decoded.IsHistorized.ShouldBeTrue();
|
||||
decoded.AttributeName.ShouldBe("TankLevel");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GalaxyAttributeInfo_IsAlarm_round_trips_false_through_MessagePack()
|
||||
{
|
||||
var input = new GalaxyAttributeInfo { AttributeName = "ColorRgb", IsAlarm = false };
|
||||
var bytes = MessagePackSerializer.Serialize(input);
|
||||
var decoded = MessagePackSerializer.Deserialize<GalaxyAttributeInfo>(bytes);
|
||||
decoded.IsAlarm.ShouldBeFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wire-compat guard: payloads serialized before PR 9 (which omit Key=6) must still
|
||||
/// deserialize cleanly — MessagePack treats missing keys as default. This lets a newer
|
||||
/// Proxy talk to an older Host during a rolling upgrade without a crash.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Pre_PR9_payload_without_IsAlarm_key_deserializes_with_default_false()
|
||||
{
|
||||
// Build a 6-field payload (keys 0..5) matching the pre-PR9 shape by serializing a
|
||||
// stand-in class with the same key layout but no Key=6.
|
||||
var pre = new PrePR9Shape
|
||||
{
|
||||
AttributeName = "Legacy",
|
||||
MxDataType = 1,
|
||||
IsArray = false,
|
||||
ArrayDim = null,
|
||||
SecurityClassification = 0,
|
||||
IsHistorized = false,
|
||||
};
|
||||
var bytes = MessagePackSerializer.Serialize(pre);
|
||||
|
||||
var decoded = MessagePackSerializer.Deserialize<GalaxyAttributeInfo>(bytes);
|
||||
decoded.AttributeName.ShouldBe("Legacy");
|
||||
decoded.IsAlarm.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[MessagePackObject]
|
||||
public sealed class PrePR9Shape
|
||||
{
|
||||
[Key(0)] public string AttributeName { get; set; } = string.Empty;
|
||||
[Key(1)] public int MxDataType { get; set; }
|
||||
[Key(2)] public bool IsArray { get; set; }
|
||||
[Key(3)] public uint? ArrayDim { get; set; }
|
||||
[Key(4)] public int SecurityClassification { get; set; }
|
||||
[Key(5)] public bool IsHistorized { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user