feat(streaming): carry IsMesReportable on AlarmStateUpdate (25) and DebugAlarmStateDto (20) (ScadaBridge#60)

Central's Debug View live stream and Alarm Summary cache always saw false
because neither wire message had the field. Additive proto fields, vendored
C# regenerated with docker/regen-proto.sh (sitestream + sitecommand), mapped
both ways in StreamRelayActor/SiteStreamGrpcClient and SiteCommandDtoMapper.
The live stream publishes Alarm Actor events, which never carried the flag,
so InstanceActor.HandleAlarmStateChanged now stamps it before retaining and
publishing. A pre-#60 site sends the proto3 default. No UI badge yet.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
Joseph Doherty
2026-11-09 12:31:37 -05:00
parent 6bd1fd3072
commit 5310fd0e26
14 changed files with 489 additions and 294 deletions
@@ -209,6 +209,40 @@ public class StreamRelayActorTests : TestKit
SiteStreamGrpcClient.ConvertToDomainEvent(unackedProto)).AckTime);
}
/// <summary>
/// Verifies that the MES-reportable flag survives the relay → proto → central decode
/// round trip (Gitea #60, <c>AlarmStateUpdate</c> field 25), and that an unflagged alarm
/// arrives as false.
/// </summary>
[Fact]
public void RelaysAlarmStateChanged_IsMesReportable_SurvivesFullRoundTrip()
{
var channel = Channel.CreateUnbounded<SiteStreamEvent>();
var actor = Sys.ActorOf(Props.Create(() =>
new StreamRelayActor("corr-mes", channel.Writer)));
var flagged = new AlarmStateChanged(
"Site1.Oven1", "HighTemp", AlarmState.Active, 700,
new DateTimeOffset(2026, 11, 9, 12, 0, 0, TimeSpan.Zero))
{
IsMesReportable = true
};
actor.Tell(flagged);
var flaggedProto = ReadProtoEvent(channel);
Assert.True(flaggedProto.AlarmChanged.IsMesReportable);
Assert.True(
Assert.IsType<AlarmStateChanged>(
SiteStreamGrpcClient.ConvertToDomainEvent(flaggedProto)).IsMesReportable);
actor.Tell(flagged with { IsMesReportable = false });
var plainProto = ReadProtoEvent(channel);
Assert.False(plainProto.AlarmChanged.IsMesReportable);
Assert.False(
Assert.IsType<AlarmStateChanged>(
SiteStreamGrpcClient.ConvertToDomainEvent(plainProto)).IsMesReportable);
}
/// <summary>
/// Reads the next relayed proto event, retrying once after a short pause because the
/// relay actor writes to the channel asynchronously (mirrors the existing round-trip