using Google.Protobuf.WellKnownTypes; using Xunit; using ZB.MOM.WW.HistorianGateway.Contracts.Grpc; using ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Mapping; namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests.Mapping; public sealed class EventMapperTests { [Fact] public void Carries_the_alarm_type_condition_name_and_alarm_id_the_writer_sent() { // The round trip of AlarmEventMapper's shape: Type = AlarmTypeName, Properties AlarmName / AlarmId. var sent = AlarmEventMapper.ToHistorianEvent(new ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian.AlarmHistorianEvent( "Plant/Modbus/dev1/temp_hi", "Area/Line/Eq", "HiTemp", "LimitAlarm", ZB.MOM.WW.OtOpcUa.Core.Abstractions.AlarmSeverity.High, "Activated", "high", "u", null, new DateTime(2026, 1, 1, 0, 0, 0, DateTimeKind.Utc))); var h = EventMapper.ToHistoricalEvent(sent); Assert.Equal("LimitAlarm", h.EventTypeName); Assert.Equal("HiTemp", h.ConditionName); Assert.Equal("Plant/Modbus/dev1/temp_hi", h.AlarmId); // A gateway row without them (e.g. an event another writer produced) maps to nulls, never throws. var bare = new HistorianEvent { Id = "E", EventTime = Ts(2026, 1, 1, 0, 0, 0), ReceivedTime = Ts(2026, 1, 1, 0, 0, 0) }; var b = EventMapper.ToHistoricalEvent(bare); Assert.Null(b.EventTypeName); Assert.Null(b.ConditionName); Assert.Null(b.AlarmId); } [Fact] public void Maps_core_fields_and_times() { var e = new HistorianEvent { Id = "E1", SourceName = "Pump1", EventTime = Ts(2026, 1, 1, 0, 0, 0), ReceivedTime = Ts(2026, 1, 1, 0, 0, 5) }; e.Properties["Message"] = "High temp"; e.Properties["Severity"] = "700"; var h = EventMapper.ToHistoricalEvent(e); Assert.Equal("E1", h.EventId); Assert.Equal("Pump1", h.SourceName); Assert.Equal("High temp", h.Message); Assert.Equal((ushort)700, h.Severity); Assert.Equal(DateTimeKind.Utc, h.EventTimeUtc.Kind); } [Theory] [InlineData("Priority", "999", 999)] [InlineData("Severity", "0", 1)] // clamp to OPC UA min 1 [InlineData("Severity", "5000", 1000)] // clamp to OPC UA max 1000 [InlineData(null, null, 1)] // missing → default min severity public void Severity_parsed_and_clamped(string? key, string? val, int expected) { var e = new HistorianEvent { Id = "E", EventTime = Ts(2026, 1, 1, 0, 0, 0), ReceivedTime = Ts(2026, 1, 1, 0, 0, 0) }; if (key is not null) e.Properties[key] = val!; Assert.Equal((ushort)expected, EventMapper.ToHistoricalEvent(e).Severity); } // Ts(...) builds a Google.Protobuf.WellKnownTypes.Timestamp from UTC parts. private static Timestamp Ts(int y, int mo, int d, int h, int mi, int s) => Timestamp.FromDateTime(new DateTime(y, mo, d, h, mi, s, DateTimeKind.Utc)); }