atmosx-nwws-parser 1.0.1915 → 1.0.1917

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bootstrap.ts CHANGED
@@ -63,12 +63,25 @@ export const definitions = {
63
63
  events: { "AF": "Ashfall", "AS": "Air Stagnation", "BH": "Beach Hazard", "BW": "Brisk Wind", "BZ": "Blizzard", "CF": "Coastal Flood", "DF": "Debris Flow", "DS": "Dust Storm", "EC": "Extreme Cold", "EH": "Excessive Heat", "XH": "Extreme Heat", "EW": "Extreme Wind", "FA": "Areal Flood", "FF": "Flash Flood", "FG": "Dense Fog", "FL": "Flood", "FR": "Frost", "FW": "Fire Weather", "FZ": "Freeze", "GL": "Gale", "HF": "Hurricane Force Wind", "HT": "Heat", "HU": "Hurricane", "HW": "High Wind", "HY": "Hydrologic", "HZ": "Hard Freeze", "IS": "Ice Storm", "LE": "Lake Effect Snow", "LO": "Low Water", "LS": "Lakeshore Flood", "LW": "Lake Wind", "MA": "Special Marine", "MF": "Dense Fog", "MH": "Ashfall", "MS": "Dense Smoke", "RB": "Small Craft for Rough Bar", "RP": "Rip Current Risk", "SC": "Small Craft", "SE": "Hazardous Seas", "SI": "Small Craft for Winds", "SM": "Dense Smoke", "SQ": "Snow Squall", "SR": "Storm", "SS": "Storm Surge", "SU": "High Surf", "SV": "Severe Thunderstorm", "SW": "Small Craft for Hazardous Seas", "TO": "Tornado", "TR": "Tropical Storm", "TS": "Tsunami", "TY": "Typhoon", "UP": "Heavy Freezing Spray", "WC": "Wind Chill", "WI": "Wind", "WS": "Winter Storm", "WW": "Winter Weather", "ZF": "Freezing Fog", "ZR": "Freezing Rain", "ZY": "Freezing Spray" },
64
64
  actions: { "W": "Warning", "F": "Forecast", "A": "Watch", "O": "Outlook", "Y": "Advisory", "N": "Synopsis", "S": "Statement"},
65
65
  status: { "NEW": "Issued", "CON": "Updated", "EXT": "Extended", "EXA": "Extended", "EXB": "Extended", "UPG": "Upgraded", "COR": "Correction", "ROU": "Routine", "CAN": "Cancelled", "EXP": "Expired" },
66
+ offshore: {
67
+ "Special Weather Statement": "Special Weather Statement",
68
+ "Hurricane Warning": "Hurricane Warning",
69
+ "Hurricane Force Wind Warning": "Hurricane Force Wind Warning",
70
+ "Hurricane Watch": "Hurricane Watch",
71
+ "Tropical Storm Warning": "Tropical Storm Warning",
72
+ "Tropical Storm Watch": "Tropical Storm Watch",
73
+ "High Wind Warning": "High Wind Warning",
74
+ "Gale Warning": "Gale Warning",
75
+ "Small Craft Advisory": "Small Craft Advisory",
76
+ "Small Craft Warning": "Small Craft Warning",
77
+ },
66
78
  awips: { SWOMCD: `mesoscale-discussion`, LSR: `local-storm-report`, SPS: `special-weather-statement`},
67
79
  expressions: {
68
80
  vtec: `[OTEX].(NEW|CON|EXT|EXA|EXB|UPG|CAN|EXP|COR|ROU).[A-Z]{4}.[A-Z]{2}.[WAYSFON].[0-9]{4}.[0-9]{6}T[0-9]{4}Z-[0-9]{6}T[0-9]{4}Z`,
69
81
  wmo: `[A-Z0-9]{6}\\s[A-Z]{4}\\s\\d{6}`,
70
82
  ugc1: `(\\w{2}[CZ](\\d{3}((-|>)\\s?(\n\n)?))+)`,
71
83
  ugc2: `(\\d{6}(-|>)\\s?(\n\n)?)`,
84
+ ugc3: `(\\d{6})(?=-|$)`,
72
85
  dateline: `/\d{3,4}\s*(AM|PM)?\s*[A-Z]{2,4}\s+[A-Z]{3,}\s+[A-Z]{3,}\s+\d{1,2}\s+\d{4}`
73
86
  },
74
87
  tags: {
package/dist/bootstrap.js CHANGED
@@ -94,12 +94,25 @@ exports.definitions = {
94
94
  events: { "AF": "Ashfall", "AS": "Air Stagnation", "BH": "Beach Hazard", "BW": "Brisk Wind", "BZ": "Blizzard", "CF": "Coastal Flood", "DF": "Debris Flow", "DS": "Dust Storm", "EC": "Extreme Cold", "EH": "Excessive Heat", "XH": "Extreme Heat", "EW": "Extreme Wind", "FA": "Areal Flood", "FF": "Flash Flood", "FG": "Dense Fog", "FL": "Flood", "FR": "Frost", "FW": "Fire Weather", "FZ": "Freeze", "GL": "Gale", "HF": "Hurricane Force Wind", "HT": "Heat", "HU": "Hurricane", "HW": "High Wind", "HY": "Hydrologic", "HZ": "Hard Freeze", "IS": "Ice Storm", "LE": "Lake Effect Snow", "LO": "Low Water", "LS": "Lakeshore Flood", "LW": "Lake Wind", "MA": "Special Marine", "MF": "Dense Fog", "MH": "Ashfall", "MS": "Dense Smoke", "RB": "Small Craft for Rough Bar", "RP": "Rip Current Risk", "SC": "Small Craft", "SE": "Hazardous Seas", "SI": "Small Craft for Winds", "SM": "Dense Smoke", "SQ": "Snow Squall", "SR": "Storm", "SS": "Storm Surge", "SU": "High Surf", "SV": "Severe Thunderstorm", "SW": "Small Craft for Hazardous Seas", "TO": "Tornado", "TR": "Tropical Storm", "TS": "Tsunami", "TY": "Typhoon", "UP": "Heavy Freezing Spray", "WC": "Wind Chill", "WI": "Wind", "WS": "Winter Storm", "WW": "Winter Weather", "ZF": "Freezing Fog", "ZR": "Freezing Rain", "ZY": "Freezing Spray" },
95
95
  actions: { "W": "Warning", "F": "Forecast", "A": "Watch", "O": "Outlook", "Y": "Advisory", "N": "Synopsis", "S": "Statement" },
96
96
  status: { "NEW": "Issued", "CON": "Updated", "EXT": "Extended", "EXA": "Extended", "EXB": "Extended", "UPG": "Upgraded", "COR": "Correction", "ROU": "Routine", "CAN": "Cancelled", "EXP": "Expired" },
97
+ offshore: {
98
+ "Special Weather Statement": "Special Weather Statement",
99
+ "Hurricane Warning": "Hurricane Warning",
100
+ "Hurricane Force Wind Warning": "Hurricane Force Wind Warning",
101
+ "Hurricane Watch": "Hurricane Watch",
102
+ "Tropical Storm Warning": "Tropical Storm Warning",
103
+ "Tropical Storm Watch": "Tropical Storm Watch",
104
+ "High Wind Warning": "High Wind Warning",
105
+ "Gale Warning": "Gale Warning",
106
+ "Small Craft Advisory": "Small Craft Advisory",
107
+ "Small Craft Warning": "Small Craft Warning",
108
+ },
97
109
  awips: { SWOMCD: "mesoscale-discussion", LSR: "local-storm-report", SPS: "special-weather-statement" },
98
110
  expressions: {
99
111
  vtec: "[OTEX].(NEW|CON|EXT|EXA|EXB|UPG|CAN|EXP|COR|ROU).[A-Z]{4}.[A-Z]{2}.[WAYSFON].[0-9]{4}.[0-9]{6}T[0-9]{4}Z-[0-9]{6}T[0-9]{4}Z",
100
112
  wmo: "[A-Z0-9]{6}\\s[A-Z]{4}\\s\\d{6}",
101
113
  ugc1: "(\\w{2}[CZ](\\d{3}((-|>)\\s?(\n\n)?))+)",
102
114
  ugc2: "(\\d{6}(-|>)\\s?(\n\n)?)",
115
+ ugc3: "(\\d{6})(?=-|$)",
103
116
  dateline: "/d{3,4}s*(AM|PM)?s*[A-Z]{2,4}s+[A-Z]{3,}s+[A-Z]{3,}s+d{1,2}s+d{4}"
104
117
  },
105
118
  tags: {
@@ -385,6 +385,110 @@ var mEvents = /** @class */ (function () {
385
385
  });
386
386
  });
387
387
  };
388
+ /**
389
+ * @function newUnknownAlert
390
+ * @description Emits the 'onAlert' event with parsed unknown alert objects. (Non-CAP, Non-VTEC)
391
+ * The alert objects contain various properties such as id, tracking, action, area description, expiration time,
392
+ * issue time, event type, sender information, description, geocode, and parameters like WMO identifier,
393
+ * tornado detection, max hail size, max wind gust, and thunderstorm damage threat.
394
+ *
395
+ * @param {Object} stanza - The XMPP stanza containing the raw alert message.
396
+ *
397
+ * @emits onAlert - Emitted when a new raw alert is received and parsed.
398
+ */
399
+ mEvents.newUnknownAlert = function (stanza) {
400
+ return __awaiter(this, void 0, void 0, function () {
401
+ var messages, defaultWMO, alerts, _loop_1, i;
402
+ return __generator(this, function (_a) {
403
+ switch (_a.label) {
404
+ case 0:
405
+ messages = stanza.message.split(/(?=\$\$)/g).map(function (msg) { return msg.trim(); });
406
+ defaultWMO = stanza.message.match(new RegExp(loader.definitions.expressions.wmo, 'gimu'));
407
+ alerts = [];
408
+ _loop_1 = function (i) {
409
+ var pStartTime, message, mUgc, isOffshoreEvent, getForecastOffice, getPolygonCoordinates, getDescription, getTimeIssued, alert_3, ugcCoordinates;
410
+ return __generator(this, function (_b) {
411
+ switch (_b.label) {
412
+ case 0:
413
+ pStartTime = new Date().getTime();
414
+ message = messages[i];
415
+ return [4 /*yield*/, ugc_1.default.getUGC(message)];
416
+ case 1:
417
+ mUgc = _b.sent();
418
+ isOffshoreEvent = Object.keys(loader.definitions.offshore).some(function (event) { return message.toLowerCase().includes(event.toLowerCase()); });
419
+ if (mUgc != null && isOffshoreEvent != false) {
420
+ getForecastOffice = text_parser_1.default.getForecastOffice(message) || "NWS";
421
+ getPolygonCoordinates = text_parser_1.default.getPolygonCoordinates(message);
422
+ getDescription = text_parser_1.default.getCleanDescription(message, null);
423
+ getTimeIssued = text_parser_1.default.getString(message, "ISSUED TIME...");
424
+ if (getTimeIssued == null) {
425
+ getTimeIssued = new Date(stanza.attributes.issue).getTime();
426
+ }
427
+ if (getTimeIssued == null) {
428
+ getTimeIssued = new Date().getTime();
429
+ }
430
+ if (getTimeIssued != null) {
431
+ getTimeIssued = new Date(getTimeIssued).toLocaleString();
432
+ }
433
+ alert_3 = {
434
+ hitch: "".concat(new Date().getTime() - pStartTime, "ms"),
435
+ id: defaultWMO ? "".concat(defaultWMO[0], "-").concat(mUgc.zones.join("-")) : "N/A",
436
+ tracking: defaultWMO ? "".concat(defaultWMO[0], "-").concat(mUgc.zones.join("-")) : "N/A",
437
+ action: "Issued",
438
+ history: [{ description: getDescription, action: "Issued", issued: getTimeIssued }],
439
+ properties: {
440
+ areaDesc: mUgc.locations.join("; ") || 'N/A',
441
+ expires: new Date(mUgc.expiry != null ? mUgc.expiry : new Date().getTime() + 1 * 60 * 60 * 1000),
442
+ sent: new Date(getTimeIssued),
443
+ messageType: "Issued",
444
+ event: Object.keys(loader.definitions.offshore).find(function (event) { return message.toLowerCase().includes(event.toLowerCase()); }) || 'Unknown Event',
445
+ sender: getForecastOffice,
446
+ senderName: getForecastOffice,
447
+ description: getDescription || 'No description available.',
448
+ geocode: {
449
+ UGC: mUgc.zones || [],
450
+ },
451
+ parameters: {
452
+ WMOidentifier: (defaultWMO === null || defaultWMO === void 0 ? void 0 : defaultWMO[0]) ? [defaultWMO[0]] : ["N/A"],
453
+ tornadoDetection: "N/A",
454
+ maxHailSize: "N/A",
455
+ maxWindGust: "N/A",
456
+ thunderstormDamageThreat: ["N/A"],
457
+ },
458
+ },
459
+ geometry: { type: 'Polygon', coordinates: [getPolygonCoordinates] }
460
+ };
461
+ if (loader.settings.alertSettings.ugcPolygons) {
462
+ ugcCoordinates = ugc_1.default.getCoordinates(mUgc.zones);
463
+ if (ugcCoordinates.length > 0) {
464
+ alert_3.geometry = { type: 'Polygon', coordinates: [ugcCoordinates] };
465
+ }
466
+ ;
467
+ }
468
+ alerts.push(alert_3);
469
+ }
470
+ return [2 /*return*/];
471
+ }
472
+ });
473
+ };
474
+ i = 0;
475
+ _a.label = 1;
476
+ case 1:
477
+ if (!(i < messages.length)) return [3 /*break*/, 4];
478
+ return [5 /*yield**/, _loop_1(i)];
479
+ case 2:
480
+ _a.sent();
481
+ _a.label = 3;
482
+ case 3:
483
+ i++;
484
+ return [3 /*break*/, 1];
485
+ case 4:
486
+ this.onFinished(alerts);
487
+ return [2 /*return*/];
488
+ }
489
+ });
490
+ });
491
+ };
388
492
  /**
389
493
  * @function newSpecialWeatherStatement
390
494
  * @description Emits the 'onAlert' event with parsed special weather statement alert objects.
@@ -399,7 +503,7 @@ var mEvents = /** @class */ (function () {
399
503
  */
400
504
  mEvents.newSpecialWeatherStatement = function (stanza) {
401
505
  return __awaiter(this, void 0, void 0, function () {
402
- var messages, defaultWMO, alerts, i, pStartTime, message, mUgc, getTornado, getHailSize, getWindGusts, getDamageThreat, getForecastOffice, getPolygonCoordinates, getDescription, getTimeIssued, alert_3, ugcCoordinates;
506
+ var messages, defaultWMO, alerts, i, pStartTime, message, mUgc, getTornado, getHailSize, getWindGusts, getDamageThreat, getForecastOffice, getPolygonCoordinates, getDescription, getTimeIssued, alert_4, ugcCoordinates;
403
507
  return __generator(this, function (_a) {
404
508
  switch (_a.label) {
405
509
  case 0:
@@ -433,7 +537,7 @@ var mEvents = /** @class */ (function () {
433
537
  if (getTimeIssued != null) {
434
538
  getTimeIssued = new Date(getTimeIssued).toLocaleString();
435
539
  }
436
- alert_3 = {
540
+ alert_4 = {
437
541
  hitch: "".concat(new Date().getTime() - pStartTime, "ms"),
438
542
  id: defaultWMO ? "".concat(defaultWMO[0], "-").concat(mUgc.zones.join("-")) : "N/A",
439
543
  tracking: defaultWMO ? "".concat(defaultWMO[0], "-").concat(mUgc.zones.join("-")) : "N/A",
@@ -441,7 +545,7 @@ var mEvents = /** @class */ (function () {
441
545
  history: [{ description: getDescription, action: "Issued", issued: getTimeIssued }],
442
546
  properties: {
443
547
  areaDesc: mUgc.locations.join("; ") || 'N/A',
444
- expires: new Date(new Date().getTime() + 1 * 60 * 60 * 1000),
548
+ expires: new Date(mUgc.expiry != null ? mUgc.expiry : new Date().getTime() + 1 * 60 * 60 * 1000),
445
549
  sent: new Date(getTimeIssued),
446
550
  messageType: "Issued",
447
551
  event: "Special Weather Statement",
@@ -464,11 +568,11 @@ var mEvents = /** @class */ (function () {
464
568
  if (loader.settings.alertSettings.ugcPolygons) {
465
569
  ugcCoordinates = ugc_1.default.getCoordinates(mUgc.zones);
466
570
  if (ugcCoordinates.length > 0) {
467
- alert_3.geometry = { type: 'Polygon', coordinates: [ugcCoordinates] };
571
+ alert_4.geometry = { type: 'Polygon', coordinates: [ugcCoordinates] };
468
572
  }
469
573
  ;
470
574
  }
471
- alerts.push(alert_3);
575
+ alerts.push(alert_4);
472
576
  }
473
577
  _a.label = 3;
474
578
  case 3:
@@ -494,7 +598,7 @@ var mEvents = /** @class */ (function () {
494
598
  */
495
599
  mEvents.newMesoscaleDiscussion = function (stanza) {
496
600
  return __awaiter(this, void 0, void 0, function () {
497
- var messages, defaultWMO, i, pStartTime, message, mUgc, getForecastOffice, getDescription, getTornadoIntensity, getPeakWindGust, getPeakHailSize, getTimeIssued, alert_4;
601
+ var messages, defaultWMO, i, pStartTime, message, mUgc, getForecastOffice, getDescription, getTornadoIntensity, getPeakWindGust, getPeakHailSize, getTimeIssued, alert_5;
498
602
  return __generator(this, function (_a) {
499
603
  switch (_a.label) {
500
604
  case 0:
@@ -525,7 +629,7 @@ var mEvents = /** @class */ (function () {
525
629
  if (getTimeIssued != null) {
526
630
  getTimeIssued = new Date(getTimeIssued).toLocaleString();
527
631
  }
528
- alert_4 = {
632
+ alert_5 = {
529
633
  hitch: "".concat(new Date().getTime() - pStartTime, "ms"),
530
634
  id: defaultWMO ? "".concat(defaultWMO[0], "-").concat(mUgc.zones.join("-")) : "N/A",
531
635
  tracking: defaultWMO ? "".concat(defaultWMO[0], "-").concat(mUgc.zones.join("-")) : "N/A",
@@ -551,7 +655,7 @@ var mEvents = /** @class */ (function () {
551
655
  },
552
656
  }
553
657
  };
554
- loader.statics.events.emit('onMesoscaleDiscussion', alert_4);
658
+ loader.statics.events.emit('onMesoscaleDiscussion', alert_5);
555
659
  }
556
660
  _a.label = 3;
557
661
  case 3:
@@ -313,6 +313,7 @@ var AtmosXWireParser = /** @class */ (function () {
313
313
  var dict = [
314
314
  { file: "".concat(dir, "/category-defaults-raw-vtec.bin"), attributes: { awipsid: 'alert', isCap: false, raw: true, issue: undefined } },
315
315
  { file: "".concat(dir, "/category-defaults-cap-vtec.bin"), attributes: { awipsid: 'alert', isCap: true, raw: false, issue: undefined } },
316
+ { file: "".concat(dir, "/category-defaults-raw.bin"), attributes: { awipsid: 'alert', isCap: false, raw: true, issue: undefined } },
316
317
  { file: "".concat(dir, "/category-special-weather-statements-raw.bin"), attributes: { awipsid: 'SPS001', isCap: false, raw: true, issue: undefined } },
317
318
  { file: "".concat(dir, "/category-mesoscale-discussions-raw.bin"), attributes: { awipsid: 'SWOMCD001', isCap: false, raw: true, issue: undefined } },
318
319
  { file: "".concat(dir, "/category-local-storm-reports.bin"), attributes: { awipsid: 'LSR001', isCap: false, raw: true, issue: undefined } },
@@ -114,19 +114,22 @@ var mStanza = /** @class */ (function () {
114
114
  */
115
115
  mStanza.create = function (stanzaData) {
116
116
  if (stanzaData.getAwipsType == "default" && stanzaData.hasVTEC && stanzaData.isCap) {
117
- events_1.default.newCapAlerts(stanzaData);
117
+ return events_1.default.newCapAlerts(stanzaData);
118
118
  } // Default alert - CAP Only
119
119
  if (stanzaData.getAwipsType == "default" && stanzaData.hasVTEC && !stanzaData.isCap) {
120
- events_1.default.newRawAlerts(stanzaData);
120
+ return events_1.default.newRawAlerts(stanzaData);
121
121
  } // Default alert - NonCAP
122
+ if (stanzaData.getAwipsType == "default" && !stanzaData.hasVTEC && !stanzaData.isCap) {
123
+ return events_1.default.newUnknownAlert(stanzaData);
124
+ } // Default alert - NonCAP, NonVTEC
122
125
  if (stanzaData.getAwipsType == "special-weather-statement") {
123
- events_1.default.newSpecialWeatherStatement(stanzaData);
126
+ return events_1.default.newSpecialWeatherStatement(stanzaData);
124
127
  } // SPW (Special Weather Statement)
125
128
  if (stanzaData.getAwipsType == "mesoscale-discussion") {
126
- events_1.default.newMesoscaleDiscussion(stanzaData);
129
+ return events_1.default.newMesoscaleDiscussion(stanzaData);
127
130
  } // MD (Mesoscale Discussion)
128
131
  if (stanzaData.getAwipsType == "local-storm-report") {
129
- events_1.default.newLocalStormReport(stanzaData);
132
+ return events_1.default.newLocalStormReport(stanzaData);
130
133
  } // LSR (Local Storm Report)
131
134
  };
132
135
  /**
package/dist/src/ugc.js CHANGED
@@ -94,16 +94,17 @@ var mUgcParser = /** @class */ (function () {
94
94
  */
95
95
  mUgcParser.getUGC = function (message) {
96
96
  return __awaiter(this, void 0, void 0, function () {
97
- var header, zones, locations, ugc;
97
+ var header, zones, expiry, locations, ugc;
98
98
  return __generator(this, function (_a) {
99
99
  switch (_a.label) {
100
100
  case 0:
101
101
  header = this.getHeader(message);
102
102
  zones = this.getZones(header);
103
+ expiry = this.getExpiry(message);
103
104
  return [4 /*yield*/, this.getLocations(zones)];
104
105
  case 1:
105
106
  locations = _a.sent();
106
- ugc = zones.length > 0 ? { zones: zones, locations: locations } : null;
107
+ ugc = zones.length > 0 ? { zones: zones, locations: locations, expiry: expiry } : null;
107
108
  return [2 /*return*/, ugc];
108
109
  }
109
110
  });
@@ -121,6 +122,18 @@ var mUgcParser = /** @class */ (function () {
121
122
  var full = message.substring(start, start + end).replace(/\s+/g, '').slice(0, -1);
122
123
  return full;
123
124
  };
125
+ mUgcParser.getExpiry = function (message) {
126
+ var start = message.match(new RegExp(loader.definitions.expressions.ugc3, "gimu"));
127
+ if (start != null) {
128
+ var day = parseInt(start[0].substring(0, 2), 10);
129
+ var hour = parseInt(start[0].substring(2, 4), 10);
130
+ var minute = parseInt(start[0].substring(4, 6), 10);
131
+ var now = new Date();
132
+ var expires = new Date(now.getUTCFullYear(), now.getUTCMonth(), day, hour, minute, 0);
133
+ return expires;
134
+ }
135
+ return null;
136
+ };
124
137
  /**
125
138
  * @function getLocations
126
139
  * @description Retrieves location names from a database based on UGC zone codes.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atmosx-nwws-parser",
3
- "version": "1.0.1915",
3
+ "version": "1.0.1917",
4
4
  "description": "NOAA Weather Wire Parser - Built for standalone and Project AtmosphericX Integration.",
5
5
  "main": "dist/src/helper.js",
6
6
  "types": "src/helper.d.ts",
package/src/events.ts CHANGED
@@ -244,6 +244,75 @@ export class mEvents {
244
244
  this.onFinished(alerts);
245
245
  }
246
246
 
247
+
248
+ /**
249
+ * @function newUnknownAlert
250
+ * @description Emits the 'onAlert' event with parsed unknown alert objects. (Non-CAP, Non-VTEC)
251
+ * The alert objects contain various properties such as id, tracking, action, area description, expiration time,
252
+ * issue time, event type, sender information, description, geocode, and parameters like WMO identifier,
253
+ * tornado detection, max hail size, max wind gust, and thunderstorm damage threat.
254
+ *
255
+ * @param {Object} stanza - The XMPP stanza containing the raw alert message.
256
+ *
257
+ * @emits onAlert - Emitted when a new raw alert is received and parsed.
258
+ */
259
+
260
+ static async newUnknownAlert (stanza: any) {
261
+ const messages = stanza.message.split(/(?=\$\$)/g).map(msg => msg.trim());
262
+ let defaultWMO = stanza.message.match(new RegExp(loader.definitions.expressions.wmo, 'gimu'));
263
+ let alerts: any[] = [];
264
+ for (let i = 0; i < messages.length; i++) {
265
+ const pStartTime = new Date().getTime()
266
+ const message = messages[i];
267
+ const mUgc = await mUgcParser.getUGC(message);
268
+ const isOffshoreEvent = Object.keys(loader.definitions.offshore).some(event => message.toLowerCase().includes(event.toLowerCase()));
269
+ if (mUgc != null && isOffshoreEvent != false) {
270
+ const getForecastOffice = mTextParser.getForecastOffice(message) || `NWS`;
271
+ const getPolygonCoordinates = mTextParser.getPolygonCoordinates(message);
272
+ const getDescription = mTextParser.getCleanDescription(message, null);
273
+ let getTimeIssued : any = mTextParser.getString(message, `ISSUED TIME...`)
274
+ if (getTimeIssued == null) { getTimeIssued = new Date(stanza.attributes.issue).getTime(); }
275
+ if (getTimeIssued == null) { getTimeIssued = new Date().getTime(); }
276
+ if (getTimeIssued != null) { getTimeIssued = new Date(getTimeIssued).toLocaleString(); }
277
+ let alert = {
278
+ hitch: `${new Date().getTime() - pStartTime}ms`,
279
+ id: defaultWMO ? `${defaultWMO[0]}-${mUgc.zones.join(`-`)}` : `N/A`,
280
+ tracking: defaultWMO ? `${defaultWMO[0]}-${mUgc.zones.join(`-`)}` : `N/A`,
281
+ action: `Issued`,
282
+ history: [{description: getDescription, action: `Issued`, issued: getTimeIssued}],
283
+ properties: {
284
+ areaDesc: mUgc.locations.join(`; `) || 'N/A',
285
+ expires: new Date(mUgc.expiry != null ? mUgc.expiry : new Date().getTime() + 1 * 60 * 60 * 1000),
286
+ sent: new Date(getTimeIssued),
287
+ messageType: `Issued`,
288
+ event: Object.keys(loader.definitions.offshore).find(event => message.toLowerCase().includes(event.toLowerCase())) || 'Unknown Event',
289
+ sender: getForecastOffice,
290
+ senderName: getForecastOffice,
291
+ description: getDescription || 'No description available.',
292
+ geocode: {
293
+ UGC: mUgc.zones || [],
294
+ },
295
+ parameters: {
296
+ WMOidentifier: defaultWMO?.[0] ? [defaultWMO[0]] : [`N/A`],
297
+ tornadoDetection: `N/A`,
298
+ maxHailSize: `N/A`,
299
+ maxWindGust: `N/A`,
300
+ thunderstormDamageThreat: [`N/A`],
301
+ },
302
+ },
303
+ geometry: { type: 'Polygon', coordinates: [getPolygonCoordinates] }
304
+ };
305
+ if (loader.settings.alertSettings.ugcPolygons) {
306
+ const ugcCoordinates = mUgcParser.getCoordinates(mUgc.zones);
307
+ if (ugcCoordinates.length > 0) { alert.geometry = { type: 'Polygon', coordinates: [ugcCoordinates] }; };
308
+ }
309
+ alerts.push(alert);
310
+ }
311
+ }
312
+ this.onFinished(alerts);
313
+ }
314
+
315
+
247
316
  /**
248
317
  * @function newSpecialWeatherStatement
249
318
  * @description Emits the 'onAlert' event with parsed special weather statement alert objects.
@@ -285,7 +354,7 @@ export class mEvents {
285
354
  history: [{description: getDescription, action: `Issued`, issued: getTimeIssued}],
286
355
  properties: {
287
356
  areaDesc: mUgc.locations.join(`; `) || 'N/A',
288
- expires: new Date(new Date().getTime() + 1 * 60 * 60 * 1000),
357
+ expires: new Date(mUgc.expiry != null ? mUgc.expiry : new Date().getTime() + 1 * 60 * 60 * 1000),
289
358
  sent: new Date(getTimeIssued),
290
359
  messageType: `Issued`,
291
360
  event: `Special Weather Statement`,
package/src/helper.ts CHANGED
@@ -183,6 +183,7 @@ export class AtmosXWireParser {
183
183
  let dict = [
184
184
  { file: `${dir}/category-defaults-raw-vtec.bin`, attributes: { awipsid: 'alert', isCap: false, raw: true, issue: undefined }},
185
185
  { file: `${dir}/category-defaults-cap-vtec.bin`, attributes: { awipsid: 'alert', isCap: true, raw: false, issue: undefined }},
186
+ { file: `${dir}/category-defaults-raw.bin`, attributes: { awipsid: 'alert', isCap: false, raw: true, issue: undefined }},
186
187
  { file: `${dir}/category-special-weather-statements-raw.bin`, attributes: { awipsid: 'SPS001', isCap: false, raw: true, issue: undefined }},
187
188
  { file: `${dir}/category-mesoscale-discussions-raw.bin`, attributes: { awipsid: 'SWOMCD001', isCap: false, raw: true, issue: undefined }},
188
189
  { file: `${dir}/category-local-storm-reports.bin`, attributes: { awipsid: 'LSR001', isCap: false, raw: true, issue: undefined }},
package/src/stanza.ts CHANGED
@@ -77,11 +77,12 @@ export class mStanza {
77
77
  */
78
78
 
79
79
  static create (stanzaData: any) {
80
- if (stanzaData.getAwipsType == `default` && stanzaData.hasVTEC && stanzaData.isCap) { mEvents.newCapAlerts(stanzaData) } // Default alert - CAP Only
81
- if (stanzaData.getAwipsType == `default` && stanzaData.hasVTEC && !stanzaData.isCap) { mEvents.newRawAlerts(stanzaData) } // Default alert - NonCAP
82
- if (stanzaData.getAwipsType == `special-weather-statement`) { mEvents.newSpecialWeatherStatement(stanzaData) } // SPW (Special Weather Statement)
83
- if (stanzaData.getAwipsType == `mesoscale-discussion`) { mEvents.newMesoscaleDiscussion(stanzaData) } // MD (Mesoscale Discussion)
84
- if (stanzaData.getAwipsType == `local-storm-report`) { mEvents.newLocalStormReport(stanzaData) } // LSR (Local Storm Report)
80
+ if (stanzaData.getAwipsType == `default` && stanzaData.hasVTEC && stanzaData.isCap) { return mEvents.newCapAlerts(stanzaData) } // Default alert - CAP Only
81
+ if (stanzaData.getAwipsType == `default` && stanzaData.hasVTEC && !stanzaData.isCap) { return mEvents.newRawAlerts(stanzaData) } // Default alert - NonCAP
82
+ if (stanzaData.getAwipsType == `default` && !stanzaData.hasVTEC && !stanzaData.isCap) { return mEvents.newUnknownAlert(stanzaData) } // Default alert - NonCAP, NonVTEC
83
+ if (stanzaData.getAwipsType == `special-weather-statement`) { return mEvents.newSpecialWeatherStatement(stanzaData) } // SPW (Special Weather Statement)
84
+ if (stanzaData.getAwipsType == `mesoscale-discussion`) { return mEvents.newMesoscaleDiscussion(stanzaData) } // MD (Mesoscale Discussion)
85
+ if (stanzaData.getAwipsType == `local-storm-report`) { return mEvents.newLocalStormReport(stanzaData) } // LSR (Local Storm Report)
85
86
  }
86
87
 
87
88
  /**
package/src/ugc.ts CHANGED
@@ -26,8 +26,9 @@ export class mUgcParser {
26
26
  static async getUGC (message: string) {
27
27
  const header = this.getHeader(message);
28
28
  const zones = this.getZones(header);
29
+ const expiry = this.getExpiry(message)
29
30
  const locations = await this.getLocations(zones);
30
- const ugc = zones.length > 0 ? { zones, locations} : null;
31
+ const ugc = zones.length > 0 ? { zones, locations, expiry } : null;
31
32
  return ugc;
32
33
  }
33
34
 
@@ -39,12 +40,25 @@ export class mUgcParser {
39
40
  */
40
41
 
41
42
  static getHeader (message: string) {
42
- let start = message.search(new RegExp(loader.definitions.expressions.ugc1, "gimu"));
43
- let end = message.substring(start).search(new RegExp(loader.definitions.expressions.ugc2, "gimu"));
44
- let full = message.substring(start, start + end).replace(/\s+/g, '').slice(0, -1);
43
+ const start = message.search(new RegExp(loader.definitions.expressions.ugc1, "gimu"));
44
+ const end = message.substring(start).search(new RegExp(loader.definitions.expressions.ugc2, "gimu"));
45
+ const full = message.substring(start, start + end).replace(/\s+/g, '').slice(0, -1);
45
46
  return full;
46
47
  }
47
48
 
49
+ static getExpiry (message: string) {
50
+ const start = message.match(new RegExp(loader.definitions.expressions.ugc3, "gimu"));
51
+ if (start != null) {
52
+ const day = parseInt(start[0].substring(0, 2), 10);
53
+ const hour = parseInt(start[0].substring(2, 4), 10);
54
+ const minute = parseInt(start[0].substring(4, 6), 10);
55
+ const now = new Date();
56
+ const expires = new Date(now.getUTCFullYear(), now.getUTCMonth(), day, hour, minute, 0);
57
+ return expires;
58
+ }
59
+ return null;
60
+ }
61
+
48
62
  /**
49
63
  * @function getLocations
50
64
  * @description Retrieves location names from a database based on UGC zone codes.