aboutsummaryrefslogtreecommitdiff
path: root/PagerMessage.cs
diff options
context:
space:
mode:
Diffstat (limited to 'PagerMessage.cs')
-rw-r--r--PagerMessage.cs124
1 files changed, 124 insertions, 0 deletions
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