From 7c6009abd22d8461039be15d0fd069a460340585 Mon Sep 17 00:00:00 2001 From: Jake Mannens Date: Fri, 20 Sep 2024 16:13:30 +1000 Subject: v0.1 --- PagerMessage.cs | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 PagerMessage.cs (limited to 'PagerMessage.cs') diff --git a/PagerMessage.cs b/PagerMessage.cs new file mode 100644 index 0000000..dc30a05 --- /dev/null +++ b/PagerMessage.cs @@ -0,0 +1,124 @@ +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text.RegularExpressions; + +namespace PagerParser; + +public enum AlertLevel { + Code1 = 1, + Code3 = 3 +} + +[Flags] +public enum EmergencyService { + Ambulance = 1, + Fire = 2, + Police = 3, + Rescue = 4, + SES = 5 +} + +[Index(nameof(Message))] +public class PagerMessage { + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int PagerMessageId { get; set; } + public DateTime Timestamp { get; set; } + public string Message { get; set; } + + public virtual ParsedPagerMessage? ParsedMessage { get; set; } + + public override int GetHashCode() => + (Timestamp, Message).GetHashCode(); +} + +[Index(nameof(FirecomJobNo))] +public class ParsedPagerMessage { + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int ParsedPagerMessageId { get; set; } + public int FirecomJobNo { get; set; } + public string AssignmentArea { get; set; } + public string JobType { get; set; } + public AlertLevel AlertLevel { get; set; } + public string Description { get; set; } + public int? MelwaysMapNo { get; set; } + public string? MelwaysGrid { get; set; } + public int? GridReference { get; set; } + public EmergencyService AttendingServices { get; set; } + public string? Note { get; set; } + public int? FireGroundChannel { get; set; } + public string PageDestination { get; set; } + + public virtual GpsPosition? GpsPosition { get; set; } + + [ForeignKey(nameof(PagerMessage))] + public virtual PagerMessage OriginalMessage { get; set; } +} + +public interface IPagerMessageParserService { + public ParsedPagerMessage Parse(string message); + public ParsedPagerMessage? TryParse(string message); +} + +// We run the pager message parser as a service with a single instance +// as this allows us to pre-compile the parsing regular expression +// which will help when potentially processing 1000's of pager messages. +// +// Running a single instance of the service isn't an issue, as this +// service is only used by the fetch service for parsing new messages. +public class PagerMessageParserService : IPagerMessageParserService { + private const string Pattern = + @"^@@ALERT\s+([A-Z]+[0-9]+)\s+([A-Z&]+)C([13])\s+(.*)\s+M\s+(\d+)\s+([A-Z]\d+)\s+\((\d+)\)\s+(\*\s+[^*]+\*\s+)?([AFPRS]+)\s+((C[A-Z]+)\s+)+FGD(\d+)\s+(P\d+\s+)?F(\d+)\s+\[([A-Z]+)\]$"; + + private Regex pageMessageRegex = new Regex(Pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); + + public ParsedPagerMessage Parse(string message) { + ParsedPagerMessage m = new(); + + var match = pageMessageRegex.Match(message); + + if(!match.Success) + throw new ArgumentException(); + + // Parse non-optional message components + m.AssignmentArea = match.Groups[1].Value; + m.JobType = match.Groups[2].Value; + m.Description = match.Groups[4].Value; + m.FirecomJobNo = Int32.Parse(match.Groups[14].Value); + m.PageDestination = match.Groups[15].Value; + + // Parse optional message components + if(match.Groups[5].Success) m.MelwaysMapNo = Int32.Parse(match.Groups[5].Value); + if(match.Groups[6].Success) m.MelwaysGrid = match.Groups[6].Value; + if(match.Groups[7].Success) m.GridReference = Int32.Parse(match.Groups[7].Value); + if(match.Groups[8].Success) m.Note = match.Groups[8].Value; + if(match.Groups[12].Success) m.FireGroundChannel = Int32.Parse(match.Groups[12].Value); + + switch(match.Groups[3].Value) { + case "1": m.AlertLevel = AlertLevel.Code1; break; + case "3": m.AlertLevel = AlertLevel.Code3; break; + default: throw new ArgumentException(); + } + + // Handle each character to construct a bitmap of attending services + foreach(char c in match.Groups[9].Value) { + switch(c) { + case 'A': m.AttendingServices |= EmergencyService.Ambulance; break; + case 'F': m.AttendingServices |= EmergencyService.Fire; break; + case 'P': m.AttendingServices |= EmergencyService.Police; break; + case 'R': m.AttendingServices |= EmergencyService.Rescue; break; + case 'S': m.AttendingServices |= EmergencyService.SES; break; + } + } + + return m; + } + + public ParsedPagerMessage? TryParse(string message) { + try { + return Parse(message); + } catch { + return null; + } + } +} \ No newline at end of file -- cgit v1.3