blob: dc30a051abedccdf52aff11e96b50cd0cc2f2c8d (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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;
}
}
}
|