@atmosx/event-product-parser 3.0.62 → 3.0.63
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/README.md +6 -4
- package/dist/cjs/index.cjs +21216 -6908
- package/dist/esm/index.mjs +21207 -6900
- package/package.json +49 -47
- package/pnpm-workspace.yaml +4 -0
- package/src/@building/CreateEvent.ts +20 -22
- package/src/@building/GetEventAttachments.ts +8 -8
- package/src/@building/GetEventEnhancedName.ts +2 -4
- package/src/@building/GetEventGeometry.ts +5 -5
- package/src/@building/GetEventHeader.ts +11 -11
- package/src/@building/GetEventNodes.ts +12 -12
- package/src/@building/GetEventOffice.ts +11 -11
- package/src/@building/GetEventProperties.ts +60 -55
- package/src/@building/GetEventSignature.ts +7 -7
- package/src/@building/GetEventTags.ts +1 -1
- package/src/@building/GetEventTracking.ts +28 -32
- package/src/@core/ClearEvents.ts +7 -7
- package/src/@core/CreateListener.ts +2 -2
- package/src/@core/GetEvents.ts +2 -2
- package/src/@core/GetNodes.ts +2 -2
- package/src/@core/GetRandomEvent.ts +2 -2
- package/src/@core/GetVersion.ts +2 -2
- package/src/@core/ManualEvent.ts +20 -21
- package/src/@core/QueryStanza.ts +7 -7
- package/src/@core/SetNode.ts +32 -26
- package/src/@core/StartService.ts +18 -18
- package/src/@core/StopService.ts +8 -8
- package/src/@enums/Expressions.ts +1 -1
- package/src/@enums/Tags.ts +1 -1
- package/src/@events/ParseAPI.ts +44 -39
- package/src/@events/ParseText.ts +19 -19
- package/src/@events/ParseUGC.ts +21 -21
- package/src/@events/ParseVTEC.ts +32 -33
- package/src/@manager/MakeEvents.ts +33 -30
- package/src/@manager/RemoveEvent.ts +38 -34
- package/src/@manager/SetHash.ts +16 -11
- package/src/@manager/UpdateEvents.ts +10 -10
- package/src/@manager/UpdateNode.ts +13 -13
- package/src/@manager/ValidateEvents.ts +36 -37
- package/src/@modules/@database/GetCachedEvents.ts +10 -12
- package/src/@modules/@database/ImportBroadcastify.ts +12 -13
- package/src/@modules/@database/ImportShapefiles.ts +17 -18
- package/src/@modules/@database/ImportStanza.ts +11 -11
- package/src/@modules/@database/InitializeDatabase.ts +16 -16
- package/src/@modules/@eas/GenerateEASMessage.ts +53 -47
- package/src/@modules/@eas/GetPCM16.ts +11 -6
- package/src/@modules/@eas/GetSampledPCM16.ts +12 -6
- package/src/@modules/@eas/GetTTS.ts +12 -7
- package/src/@modules/@eas/GetWavPCM16.ts +5 -5
- package/src/@modules/@eas/SetAFSK.ts +11 -6
- package/src/@modules/@eas/SetAttentionTone.ts +9 -4
- package/src/@modules/@eas/SetNoise.ts +10 -5
- package/src/@modules/@eas/SetRadioEffect.ts +10 -5
- package/src/@modules/@eas/SetSameHeader.ts +20 -12
- package/src/@modules/@stanza/GetAwipsType.ts +14 -15
- package/src/@modules/@stanza/ValidateStanza.ts +25 -13
- package/src/@modules/@utilities/CreateHttp.ts +24 -24
- package/src/@modules/@utilities/GetMatched.ts +8 -3
- package/src/@modules/@utilities/GetSettings.ts +3 -3
- package/src/@modules/@utilities/GetShapeNearestPoint.ts +22 -17
- package/src/@modules/@utilities/QueueManager.ts +70 -0
- package/src/@modules/@utilities/SetCronSchedule.ts +20 -20
- package/src/@modules/@utilities/SetDebug.ts +10 -10
- package/src/@modules/@utilities/SetEventEmit.ts +15 -15
- package/src/@modules/@utilities/SetListener.ts +6 -6
- package/src/@modules/@utilities/SetSettings.ts +5 -5
- package/src/@modules/@utilities/SetSleep.ts +3 -3
- package/src/@modules/@utilities/SetTimeoutAction.ts +25 -25
- package/src/@modules/@utilities/SetWarning.ts +8 -8
- package/src/@modules/@xmpp/DeployXMPP.ts +16 -16
- package/src/@modules/@xmpp/ErrorXMPP.ts +4 -4
- package/src/@modules/@xmpp/OfflineXMPP.ts +11 -11
- package/src/@modules/@xmpp/OnlineXMPP.ts +16 -16
- package/src/@modules/@xmpp/ReconnectXMPP.ts +27 -27
- package/src/@modules/@xmpp/StanzaXMPP.ts +23 -23
- package/src/@parsers/@hvtec/HVExtract.ts +9 -9
- package/src/@parsers/@text/GetDescriptionFromProduct.ts +18 -19
- package/src/@parsers/@text/GetEmbededText.ts +3 -3
- package/src/@parsers/@text/GetStringText.ts +6 -6
- package/src/@parsers/@text/GetTextFromProduct.ts +10 -10
- package/src/@parsers/@text/GetXML.ts +5 -5
- package/src/@parsers/@ugc/GetCache.ts +2 -2
- package/src/@parsers/@ugc/{GetUGCHeader.ts → GetHeader.ts} +2 -2
- package/src/@parsers/@ugc/GetLocations.ts +5 -5
- package/src/@parsers/@ugc/GetZonePolygon.ts +10 -10
- package/src/@parsers/@ugc/GetZones.ts +1 -1
- package/src/@parsers/@ugc/SetCache.ts +11 -6
- package/src/@parsers/@ugc/UGCExtract.ts +10 -10
- package/src/@parsers/{@pvtec → @vtec}/VTECExtract.ts +19 -19
- package/src/@tasks/CreateTasks.ts +110 -0
- package/src/@tasks/TaskGenerateAudio.ts +34 -0
- package/src/@tasks/TaskGenerateJSON.ts +34 -0
- package/src/@tasks/TaskGenerateText.ts +34 -0
- package/src/@tasks/TaskSendNTFY.ts +91 -0
- package/src/@tasks/TaskSendWebhook.ts +100 -0
- package/src/@types/HVTEC.ts +4 -4
- package/src/@types/Hash.ts +3 -3
- package/src/@types/Settings.ts +1 -1
- package/src/@types/StanzaCompiled.ts +13 -13
- package/src/@types/UGC.ts +3 -3
- package/src/@types/VTEC.ts +10 -10
- package/src/{@types → @types-lower}/Event.ts +4 -4
- package/src/{@types → @types-lower}/Properties.ts +2 -1
- package/src/{@types → @types-lower}/Stanza.ts +1 -1
- package/src/bootstrap.ts +26 -26
- package/src/index.ts +30 -27
- package/tools/build.js +45 -0
- package/tsconfig.json +21 -1
- package/src/@manager/Tasks.ts +0 -246
- package/tsup.config.ts +0 -16
- /package/src/@parsers/{@pvtec → @vtec}/GetExpiry.ts +0 -0
- /package/src/{@types → @types-lower}/Attributes.ts +0 -0
|
@@ -17,49 +17,45 @@
|
|
|
17
17
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { TypeAttributes } from "
|
|
21
|
-
import { TypeEventProperties } from "
|
|
22
|
-
import { TypeStanzaCompiled } from "
|
|
23
|
-
import {
|
|
20
|
+
import { TypeAttributes } from "types-lower/Attributes"
|
|
21
|
+
import { TypeEventProperties } from "types-lower/Properties"
|
|
22
|
+
import { TypeStanzaCompiled } from "types/StanzaCompiled"
|
|
23
|
+
import { TypeVTEC } from "types/VTEC"
|
|
24
24
|
|
|
25
25
|
interface GetTrackingOptions {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
Type: `RAW` | `VTEC` | `API`
|
|
27
|
+
Stanza?: TypeStanzaCompiled
|
|
28
|
+
Attributes?: TypeAttributes
|
|
29
|
+
Properties?: TypeEventProperties
|
|
30
|
+
WMO?: {
|
|
31
|
+
Identifier: string
|
|
32
|
+
ID: string
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
VTEC?: TypeVTEC
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
export const GetEventTracking = (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const stanza = options.stanza
|
|
41
|
-
const vtec = options.vtec
|
|
42
|
-
if (options.type === `RAW`) {
|
|
43
|
-
const getWatchNumber = properties.watch_parameters.watch_number ?? null
|
|
37
|
+
export const GetEventTracking = ({ Type, Stanza, Attributes, Properties, WMO, VTEC }: GetTrackingOptions): string => {
|
|
38
|
+
if (Type === `RAW`) {
|
|
39
|
+
const getWatchNumber = Properties?.watch_parameters?.watch_number ?? null
|
|
44
40
|
if (getWatchNumber) {
|
|
45
|
-
return `${
|
|
41
|
+
return `${Properties.geocode.office.office}.${Stanza.Type.Prefix}.A.${getWatchNumber}`
|
|
46
42
|
}
|
|
47
|
-
return `${
|
|
43
|
+
return `${Properties.geocode.office.office}.${Attributes.ttaaii}.${Attributes.id.slice(-4).replace(`.`, ``) ?? '0'}`
|
|
48
44
|
}
|
|
49
|
-
if (
|
|
50
|
-
return
|
|
45
|
+
if (Type === `VTEC`) {
|
|
46
|
+
return VTEC?.Tracking;
|
|
51
47
|
}
|
|
52
|
-
if (
|
|
53
|
-
if (
|
|
54
|
-
const vtecValue = Array.isArray(
|
|
55
|
-
?
|
|
56
|
-
const
|
|
57
|
-
return `${
|
|
48
|
+
if (Type === `API`) {
|
|
49
|
+
if (VTEC) {
|
|
50
|
+
const vtecValue = Array.isArray(VTEC)
|
|
51
|
+
? VTEC[0].vtec : VTEC?.Raw;
|
|
52
|
+
const splitVTEC = vtecValue.split('.');
|
|
53
|
+
return `${splitVTEC[2]}.${splitVTEC[3]}.${splitVTEC[4]}.${splitVTEC[5]}`;
|
|
58
54
|
}
|
|
59
|
-
const wmoMatch =
|
|
55
|
+
const wmoMatch = WMO?.Identifier?.match(/([A-Z]{4}\d{2})\s+([A-Z]{4})/);
|
|
60
56
|
const station = wmoMatch?.[2] ?? '---';
|
|
61
|
-
if (
|
|
62
|
-
const idMatch =
|
|
57
|
+
if (WMO?.ID) {
|
|
58
|
+
const idMatch = WMO?.ID.match(/([a-f0-9]+)\.(\d+)\.(\d+)$/);
|
|
63
59
|
return `${station}.${idMatch?.[0]?.replace(/\./g, '') ?? '---'}`;
|
|
64
60
|
}
|
|
65
61
|
const id = wmoMatch?.[1] ?? '---';
|
package/src/@core/ClearEvents.ts
CHANGED
|
@@ -17,15 +17,15 @@
|
|
|
17
17
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import {
|
|
21
|
-
import { SetEventEmit } from "
|
|
20
|
+
import { Bootstrap } from "@bootstrap"
|
|
21
|
+
import { SetEventEmit } from "@utilities/SetEventEmit"
|
|
22
22
|
|
|
23
23
|
export const ClearEvents = (): void => {
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
Bootstrap.Cache.Events.features = [];
|
|
25
|
+
Bootstrap.Cache.Hashes = [];
|
|
26
26
|
SetEventEmit({
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
Event: `onEventCache`,
|
|
28
|
+
Metadata: Bootstrap.Cache.Events,
|
|
29
|
+
Message: `Manually cleared event cache.`
|
|
30
30
|
})
|
|
31
31
|
}
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { SetListener } from "
|
|
20
|
+
import { SetListener } from "@utilities/SetListener"
|
|
21
21
|
|
|
22
22
|
export const CreateListener = (event: string, callback: () => void) => {
|
|
23
|
-
SetListener({event, callback})
|
|
23
|
+
SetListener({Event: event, Callback: callback})
|
|
24
24
|
}
|
package/src/@core/GetEvents.ts
CHANGED
package/src/@core/GetNodes.ts
CHANGED
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import { Bootstrap } from "@bootstrap"
|
|
21
21
|
|
|
22
22
|
export const GetRandomEvent = (): any => {
|
|
23
|
-
return
|
|
23
|
+
return Bootstrap.Cache.Events.features[Math.floor(Math.random() * Bootstrap.Cache.Events.features.length)]
|
|
24
24
|
}
|
package/src/@core/GetVersion.ts
CHANGED
package/src/@core/ManualEvent.ts
CHANGED
|
@@ -17,39 +17,38 @@
|
|
|
17
17
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import { CreateEvent } from "../@building/CreateEvent";
|
|
20
|
+
import { EnumExpressions } from "@enums/Expressions"
|
|
21
|
+
import { GetAwipsType } from "@stanza/GetAwipsType"
|
|
22
|
+
import { CreateEvent } from "@building/CreateEvent"
|
|
24
23
|
|
|
25
24
|
interface CreateEventOptions {
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
Message: string
|
|
26
|
+
Awipsid?: string
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
export const ManualEvent = async (
|
|
31
|
-
const isCapEvent =
|
|
32
|
-
const isCapAreaDescription =
|
|
33
|
-
const isVTEC =
|
|
34
|
-
const isUGC =
|
|
29
|
+
export const ManualEvent = async ({ Message, Awipsid }: CreateEventOptions): Promise<void> => {
|
|
30
|
+
const isCapEvent = Message.includes(`<?xml`);
|
|
31
|
+
const isCapAreaDescription = Message.includes(`<areaDesc>`)
|
|
32
|
+
const isVTEC = Message.match(EnumExpressions.vtec) != null;
|
|
33
|
+
const isUGC = Message.match(EnumExpressions.ugc1) != null;
|
|
35
34
|
const attributes = {
|
|
36
35
|
"xmlns": "@atmosx/event-product-parser",
|
|
37
36
|
"id": "manual_processor.0000",
|
|
38
37
|
"issue": new Date().toISOString(),
|
|
39
38
|
"ttaaii": "XXXXX",
|
|
40
39
|
"cccc": "XXX",
|
|
41
|
-
"awipsid":
|
|
40
|
+
"awipsid": Awipsid ?? "XXXXXX",
|
|
42
41
|
}
|
|
43
|
-
const getType = GetAwipsType({ attributes
|
|
42
|
+
const getType = GetAwipsType({ Attributes: attributes})
|
|
44
43
|
const result = {
|
|
45
|
-
|
|
46
|
-
attributes,
|
|
47
|
-
isCapEvent,
|
|
48
|
-
isVTEC,
|
|
49
|
-
isUGC,
|
|
50
|
-
isCapAreaDescription,
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
Message: Message,
|
|
45
|
+
Attributes: attributes,
|
|
46
|
+
CapEvent: isCapEvent,
|
|
47
|
+
VTEC: isVTEC,
|
|
48
|
+
UGC: isUGC,
|
|
49
|
+
CapAreaDescription: isCapAreaDescription,
|
|
50
|
+
Ignored: false,
|
|
51
|
+
NWWS: true,
|
|
53
52
|
getType
|
|
54
53
|
}
|
|
55
54
|
await CreateEvent(result);
|
package/src/@core/QueryStanza.ts
CHANGED
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { TypeStanza } from "
|
|
21
|
-
import {
|
|
20
|
+
import { TypeStanza } from "types-lower/Stanza"
|
|
21
|
+
import { Bootstrap } from "@bootstrap"
|
|
22
22
|
|
|
23
23
|
interface QueryStanzaOptions {
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
Search: string
|
|
25
|
+
Max?: number
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
type QueryStanza = {
|
|
@@ -30,10 +30,10 @@ type QueryStanza = {
|
|
|
30
30
|
stanza: string
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export const QueryStanza = async (
|
|
34
|
-
const get = await
|
|
33
|
+
export const QueryStanza = async ({ Search, Max }: QueryStanzaOptions): Promise<TypeStanza[]> => {
|
|
34
|
+
const get = await Bootstrap.Database.prepare(
|
|
35
35
|
`SELECT * FROM stanzas WHERE stanza LIKE ? LIMIT ?`)
|
|
36
|
-
.all(`%${
|
|
36
|
+
.all(`%${Search}%`, Max ?? 100) as QueryStanza[];
|
|
37
37
|
const events = get.map((row) => JSON.parse(row.stanza));
|
|
38
38
|
return events as TypeStanza[];
|
|
39
39
|
}
|
package/src/@core/SetNode.ts
CHANGED
|
@@ -17,47 +17,52 @@
|
|
|
17
17
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import {
|
|
21
|
-
import { SetWarning } from "
|
|
22
|
-
import { SetEventEmit } from "
|
|
20
|
+
import { Bootstrap } from "@bootstrap"
|
|
21
|
+
import { SetWarning } from "@utilities/SetWarning"
|
|
22
|
+
import { SetEventEmit } from "@utilities/SetEventEmit"
|
|
23
23
|
|
|
24
24
|
interface GetNodeOptions {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
Identifier: string
|
|
26
|
+
Delete?: boolean
|
|
27
|
+
Coordinates: { Longitude: number; Latitude: number }
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
export const SetNode = (
|
|
31
|
-
const nodes =
|
|
32
|
-
const exists = nodes.find((node) => node.properties.identifier ===
|
|
33
|
-
if (
|
|
30
|
+
export const SetNode = ({ Identifier, Delete, Coordinates }: GetNodeOptions) => {
|
|
31
|
+
const nodes = Bootstrap.Cache.Nodes.features;
|
|
32
|
+
const exists = nodes.find((node) => node.properties.identifier === Identifier);
|
|
33
|
+
if (Delete) {
|
|
34
34
|
if (exists) {
|
|
35
35
|
const index = nodes.indexOf(exists);
|
|
36
36
|
nodes.splice(index, 1);
|
|
37
|
+
SetWarning({ Message: `Node with identifier '${Identifier}' deleted.` })
|
|
37
38
|
return SetEventEmit({
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
Event: `onNodeDelete`,
|
|
40
|
+
Metadata: {
|
|
41
|
+
Type: `node-delete`,
|
|
42
|
+
Node: exists
|
|
42
43
|
}
|
|
43
44
|
})
|
|
44
45
|
}
|
|
45
|
-
return SetWarning({
|
|
46
|
+
return SetWarning({ Message: `Node with identifier '${Identifier}' not found.` })
|
|
46
47
|
}
|
|
47
48
|
if (exists) {
|
|
48
49
|
const index = nodes.indexOf(exists);
|
|
50
|
+
if (exists.geometry.coordinates[0] === Coordinates.Longitude && exists.geometry.coordinates[1] === Coordinates.Latitude) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
49
53
|
nodes[index] = {
|
|
50
54
|
...exists,
|
|
51
55
|
geometry: {
|
|
52
56
|
type: "Point",
|
|
53
|
-
coordinates: [
|
|
57
|
+
coordinates: [Coordinates.Longitude, Coordinates.Latitude]
|
|
54
58
|
}
|
|
55
59
|
};
|
|
60
|
+
SetWarning({ Message: `Node with identifier '${Identifier}' updated.` })
|
|
56
61
|
return SetEventEmit({
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
Event: `onNodeUpdate`,
|
|
63
|
+
Metadata: {
|
|
64
|
+
Type: `node-update`,
|
|
65
|
+
Node: nodes[index]
|
|
61
66
|
}
|
|
62
67
|
})
|
|
63
68
|
}
|
|
@@ -65,17 +70,18 @@ export const SetNode = (options: GetNodeOptions) => {
|
|
|
65
70
|
type: "Feature",
|
|
66
71
|
geometry: {
|
|
67
72
|
type: "Point",
|
|
68
|
-
coordinates: [
|
|
73
|
+
coordinates: [Coordinates.Longitude, Coordinates.Latitude]
|
|
69
74
|
},
|
|
70
75
|
properties: {
|
|
71
|
-
identifier:
|
|
76
|
+
identifier: Identifier
|
|
72
77
|
}
|
|
73
78
|
});
|
|
79
|
+
SetWarning({ Message: `Node with identifier '${Identifier}' added.` })
|
|
74
80
|
return SetEventEmit({
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
Event: `onNodeAdd`,
|
|
82
|
+
Metadata: {
|
|
83
|
+
Type: `node-add`,
|
|
84
|
+
Node: nodes[nodes.length - 1]
|
|
79
85
|
}
|
|
80
86
|
})
|
|
81
87
|
}
|
|
@@ -17,26 +17,26 @@
|
|
|
17
17
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { TypeSettings } from "
|
|
21
|
-
import {
|
|
22
|
-
import { SetWarning } from "
|
|
23
|
-
import { SetSettings } from "
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import { UpdateNode } from "
|
|
29
|
-
import { UpdateEvents } from "
|
|
30
|
-
import { Cron } from "croner"
|
|
20
|
+
import { TypeSettings } from "types/Settings"
|
|
21
|
+
import { Bootstrap } from "@bootstrap"
|
|
22
|
+
import { SetWarning } from "@utilities/SetWarning"
|
|
23
|
+
import { SetSettings } from "@utilities/SetSettings"
|
|
24
|
+
import { SetCronSchedule } from "@utilities/SetCronSchedule"
|
|
25
|
+
import { DeployXMPP } from "@xmpp/DeployXMPP"
|
|
26
|
+
import { InitializeDatabase } from "@database/InitializeDatabase"
|
|
27
|
+
import { GetCachedEvents } from "@database/GetCachedEvents"
|
|
28
|
+
import { UpdateNode } from "@manager/UpdateNode"
|
|
29
|
+
import { UpdateEvents } from "@manager/UpdateEvents"
|
|
30
|
+
import { Cron } from "croner"
|
|
31
31
|
|
|
32
32
|
export const StartService = async (configurations: TypeSettings): Promise<void> => {
|
|
33
|
-
if (!
|
|
33
|
+
if (!Bootstrap.Ready) {
|
|
34
34
|
return SetWarning({
|
|
35
|
-
|
|
35
|
+
Message: `You can not create another instance without shutting down the current one first, please make sure to call the stop() method first!`
|
|
36
36
|
})
|
|
37
37
|
}
|
|
38
38
|
const settings = SetSettings(configurations);
|
|
39
|
-
|
|
39
|
+
Bootstrap.Ready = true;
|
|
40
40
|
await InitializeDatabase();
|
|
41
41
|
if (settings.EnableWireService) {
|
|
42
42
|
(async () => {
|
|
@@ -47,14 +47,14 @@ export const StartService = async (configurations: TypeSettings): Promise<void>
|
|
|
47
47
|
await SetCronSchedule()
|
|
48
48
|
let scheduleInterval = !settings.EnableWireService ? settings.NationalWeatherServiceSettings.CallbackInterval : 1;
|
|
49
49
|
if (!settings.EnableWireService && scheduleInterval < 15) {
|
|
50
|
-
SetWarning({
|
|
51
|
-
|
|
50
|
+
SetWarning({ Message: `Schedule interval of ${scheduleInterval} seconds is too low, setting to 15 seconds` })
|
|
51
|
+
Bootstrap.Settings.NationalWeatherServiceSettings.CallbackInterval = 15;
|
|
52
52
|
scheduleInterval = 15;
|
|
53
53
|
}
|
|
54
|
-
|
|
54
|
+
Bootstrap.Job = new Cron(`*/${scheduleInterval} * * * * *`, async () => {
|
|
55
55
|
await SetCronSchedule();
|
|
56
56
|
})
|
|
57
|
-
|
|
57
|
+
Bootstrap.Job = new Cron(`* * * * * *`, async () => {
|
|
58
58
|
await UpdateNode();
|
|
59
59
|
await UpdateEvents();
|
|
60
60
|
})
|
package/src/@core/StopService.ts
CHANGED
|
@@ -17,16 +17,16 @@
|
|
|
17
17
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import { Bootstrap } from "@bootstrap"
|
|
21
21
|
|
|
22
22
|
export const StopService = async (): Promise<void> => {
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
if (
|
|
26
|
-
try { await
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
if (Bootstrap.Ready) {
|
|
24
|
+
Bootstrap.Ready = false;
|
|
25
|
+
if (Bootstrap.Session) {
|
|
26
|
+
try { await Bootstrap.Session.stop(); } catch {}
|
|
27
|
+
Bootstrap.Cache.Connected = false;
|
|
28
|
+
Bootstrap.Cache.Hault = true;
|
|
29
|
+
Bootstrap.Session = null;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
export const EnumExpressions: { [key: string]: RegExp } = {
|
|
21
21
|
location: new RegExp(/^([A-Za-z.\-' ]+),\s*([A-Z]{2})$/),
|
|
22
|
-
|
|
22
|
+
vtec: new RegExp(`[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`, "g"),
|
|
23
23
|
hvtec: new RegExp(`[a-zA-Z0-9]{4}.[A-Z0-9].[A-Z]{2}.[0-9]{6}T[0-9]{4}Z.[0-9]{6}T[0-9]{4}Z.[0-9]{6}T[0-9]{4}Z.[A-Z]{2}`, "imu"),
|
|
24
24
|
wmo: new RegExp(`[A-Z0-9]{6}\\s[A-Z]{4}\\s\\d{6}`, "imu"),
|
|
25
25
|
ugc1: new RegExp(`(\\w{2}[CZ](\\d{3}((-|>)\\s?(\\n\\n)?))+)`, "imu"),
|
package/src/@enums/Tags.ts
CHANGED
|
@@ -148,7 +148,7 @@ export const EnumTags = {
|
|
|
148
148
|
{ match: /Source\.\.\.Doppler Radar/i, tag: "Confirmed By Radar" },
|
|
149
149
|
{ match: /Source\.\.\.Automated Gauges/i, tag: "Confirmed By Automated Gauges" },
|
|
150
150
|
{ match: /Source\.\.\.Radar Confirmed/i, tag: "Confirmed By Radar" },
|
|
151
|
-
{ match: /Source\.\.\.Radar Indicated/i, tag: "Indicated
|
|
151
|
+
{ match: /Source\.\.\.Radar Indicated/i, tag: "Radar Indicated" },
|
|
152
152
|
{ match: /Source\.\.\.Public/i, tag: "Confirmed By Public" },
|
|
153
153
|
{ match: /Source\.\.\.Amateur Radio/i, tag: "Confirmed By Amateur Radio" },
|
|
154
154
|
{ match: /Source\.\.\.Emergency Management/i, tag: "Confirmed By Emergency Management" },
|
package/src/@events/ParseAPI.ts
CHANGED
|
@@ -17,22 +17,22 @@
|
|
|
17
17
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { TypeStanzaCompiled } from "
|
|
21
|
-
import {
|
|
22
|
-
import { EnumICAO } from "
|
|
23
|
-
import {
|
|
24
|
-
import { GetEventTracking } from "
|
|
25
|
-
import { VTECExtract } from "
|
|
26
|
-
import { GetEventTags } from "
|
|
27
|
-
import { GetTextFromProduct } from "
|
|
28
|
-
import { SetDebug } from "
|
|
20
|
+
import { TypeStanzaCompiled } from "types/StanzaCompiled"
|
|
21
|
+
import { TypeVTEC } from "types/VTEC"
|
|
22
|
+
import { EnumICAO } from "@enums/ICAO"
|
|
23
|
+
import { Bootstrap } from "@bootstrap"
|
|
24
|
+
import { GetEventTracking } from "@building/GetEventTracking"
|
|
25
|
+
import { VTECExtract } from "@parsers/vtec/VTECExtract"
|
|
26
|
+
import { GetEventTags } from "@building/GetEventTags"
|
|
27
|
+
import { GetTextFromProduct } from "@parsers/text/GetTextFromProduct"
|
|
28
|
+
import { SetDebug } from "@utilities/SetDebug"
|
|
29
29
|
|
|
30
|
-
export const ParseAPI = async (
|
|
31
|
-
const messages = Object.values(JSON.parse(
|
|
30
|
+
export const ParseAPI = async (Stanza: TypeStanzaCompiled): Promise<void> => {
|
|
31
|
+
const messages = Object.values(JSON.parse(Stanza.Message).features) as any;
|
|
32
32
|
for (const feature of messages) {
|
|
33
33
|
const tick = performance.now();
|
|
34
|
-
const
|
|
35
|
-
|
|
34
|
+
const VTEC = VTECExtract(feature?.properties?.parameters?.VTEC?.[0]) ?? null as TypeVTEC[]
|
|
35
|
+
Bootstrap.Cache.Parsed.push({
|
|
36
36
|
type: `Feature`,
|
|
37
37
|
geometry: {
|
|
38
38
|
type: `Point`,
|
|
@@ -45,13 +45,18 @@ export const ParseAPI = async (stanza: TypeStanzaCompiled): Promise<void> => {
|
|
|
45
45
|
issued: feature?.properties?.sent ? new Date(feature?.properties?.sent).toISOString() : null,
|
|
46
46
|
expires: feature?.properties?.expires ? new Date(feature?.properties?.expires).toISOString() : null,
|
|
47
47
|
locations: feature?.properties?.areaDesc ?? null,
|
|
48
|
-
locations_array: feature?.properties?.areaDesc ? feature?.properties?.areaDesc.split(';') : [],
|
|
48
|
+
locations_array: feature?.properties?.areaDesc ? feature?.properties?.areaDesc.split('; ') : [],
|
|
49
|
+
location_states: [...new Set(
|
|
50
|
+
feature?.properties?.areaDesc?.split(';')
|
|
51
|
+
?.map((location: string) => location.match(/,\s*([A-Z]{2})\b/)?.[1])
|
|
52
|
+
.filter(Boolean) ?? []
|
|
53
|
+
)],
|
|
49
54
|
description: feature?.properties?.description ?? null,
|
|
50
55
|
attributes: feature?.properties?.attributes ?? {},
|
|
51
56
|
geocode: {
|
|
52
57
|
office: {
|
|
53
|
-
office:
|
|
54
|
-
name: EnumICAO[
|
|
58
|
+
office: VTEC ? VTEC?.[0]?.Tracking.split(`.`)[0] : null,
|
|
59
|
+
name: EnumICAO[VTEC ? VTEC?.[0]?.Tracking.split(`.`)[0] : null] ?? null,
|
|
55
60
|
},
|
|
56
61
|
organization: feature?.properties?.parameters?.WMOidentifier?.[0],
|
|
57
62
|
ugc: feature?.properties?.geocode?.UGC ?? [],
|
|
@@ -61,9 +66,9 @@ export const ParseAPI = async (stanza: TypeStanzaCompiled): Promise<void> => {
|
|
|
61
66
|
parameters: {
|
|
62
67
|
tags: GetEventTags(feature?.properties?.description),
|
|
63
68
|
instructions: feature?.properties?.instruction ?? null,
|
|
64
|
-
source: GetTextFromProduct({
|
|
65
|
-
hazards: GetTextFromProduct({
|
|
66
|
-
impacts: GetTextFromProduct({
|
|
69
|
+
source: GetTextFromProduct({ Message: feature?.properties?.description, Find: [`SOURCE...`], Removal: [`.`]}) ?? null,
|
|
70
|
+
hazards: GetTextFromProduct({ Message: feature?.properties?.description, Find: [`HAZARD...`], Removal: [`.`]}) ?? null,
|
|
71
|
+
impacts: GetTextFromProduct({ Message: feature?.properties?.description, Find: [`IMPACT...`], Removal: [`.`]}) ?? null,
|
|
67
72
|
estimated_hail_size: feature?.properties?.parameters?.maxHailSize?.[0] ?? null,
|
|
68
73
|
estimated_wind_gusts: feature?.properties?.parameters?.maxWindGust?.[0] ?? null,
|
|
69
74
|
damage_threat: feature?.properties?.parameters?.thunderstormDamageThreat?.[0] ?? null,
|
|
@@ -73,33 +78,33 @@ export const ParseAPI = async (stanza: TypeStanzaCompiled): Promise<void> => {
|
|
|
73
78
|
hail_threat: feature?.properties?.parameters?.hailThreat?.[0] ?? null,
|
|
74
79
|
},
|
|
75
80
|
discussion_parameters: {
|
|
76
|
-
discussion_number: GetTextFromProduct({
|
|
77
|
-
discussion_concerning: GetTextFromProduct({
|
|
78
|
-
discussion_max_tornado: GetTextFromProduct({
|
|
79
|
-
discussion_max_hail: GetTextFromProduct({
|
|
80
|
-
discussion_max_wind: GetTextFromProduct({
|
|
81
|
-
discussion_watch_issuance: GetTextFromProduct({
|
|
81
|
+
discussion_number: GetTextFromProduct({ Message: feature?.properties?.description, Find: [`Mesoscale Discussion `], Removal: [`Mesoscale Discussion`, `Number`, `...`] })?.toString()?.padStart(4, "0") ?? null,
|
|
82
|
+
discussion_concerning: GetTextFromProduct({ Message: feature?.properties?.description, Find: [`Concerning...`] }) ?? null,
|
|
83
|
+
discussion_max_tornado: GetTextFromProduct({ Message: feature?.properties?.description, Find: [`MOST PROBABLE PEAK TORNADO INTENSITY...`] }) ?? null,
|
|
84
|
+
discussion_max_hail: GetTextFromProduct({ Message: feature?.properties?.description, Find: [`MOST PROBABLE PEAK HAIL SIZE...`] }) ?? null,
|
|
85
|
+
discussion_max_wind: GetTextFromProduct({ Message: feature?.properties?.description, Find: [`MOST PROBABLE PEAK WIND GUST...`] }) ?? null,
|
|
86
|
+
discussion_watch_issuance: GetTextFromProduct({ Message: feature?.properties?.description, Find: [`Probability of Watch Issuance...`], Removal: [`percent`]}) ?? null,
|
|
82
87
|
},
|
|
83
88
|
watch_parameters: {
|
|
84
|
-
watch_number: (
|
|
89
|
+
watch_number: (VTEC?.[0]?.Watch) && (GetTextFromProduct({ Message: feature?.properties?.description, Find: [`ITIES FOR`, `UPDATE FOR`, `Watch Number `], Removal: [`%`, `<`, `:`] })?.replace(/(WT|WS|)/g, '')?.trim()?.toString()?.padStart(4, "0") ?? VTEC?.[0]?.Tracking?.slice(-4)?.toString()?.padStart(4, "0") ?? null),
|
|
85
90
|
watch_type: feature?.properties?.description.includes(`TORNADO WATCH`) ? `Tornado` : feature?.properties?.description.includes(`SEVERE`) ? `Severe` : null,
|
|
86
|
-
additional_tornadoes_probability: GetTextFromProduct({
|
|
87
|
-
strong_tornadoes_probability: GetTextFromProduct({
|
|
88
|
-
severe_wind_probability: GetTextFromProduct({
|
|
89
|
-
severe_hail_probability: GetTextFromProduct({
|
|
90
|
-
hail_2in_probability: GetTextFromProduct({
|
|
91
|
-
combined_hail_wind_probability: GetTextFromProduct({
|
|
92
|
-
max_hail_in: GetTextFromProduct({
|
|
93
|
-
max_wind_surface: GetTextFromProduct({
|
|
94
|
-
max_tops_x100feet: GetTextFromProduct({
|
|
95
|
-
pds_watch: (GetTextFromProduct({
|
|
91
|
+
additional_tornadoes_probability: GetTextFromProduct({ Message: feature?.properties?.description, Find: [`PROB OF 2 OR MORE TORNADOES`], Removal: [`%`, `<`, `:`] }) ?? null,
|
|
92
|
+
strong_tornadoes_probability: GetTextFromProduct({ Message: feature?.properties?.description, Find: [`PROB OF 1 OR MORE STRONG /EF2-EF5/ TORNADOES`], Removal: [`%`, `<`, `:`] }) ?? null,
|
|
93
|
+
severe_wind_probability: GetTextFromProduct({ Message: feature?.properties?.description, Find: [`PROB OF 10 OR MORE SEVERE WIND EVENTS`], Removal: [`%`, `<`, `:`] }) ?? null,
|
|
94
|
+
severe_hail_probability: GetTextFromProduct({ Message: feature?.properties?.description, Find: [`PROB OF 10 OR MORE SEVERE HAIL EVENTS`], Removal: [`%`, `<`, `:`] }) ?? null,
|
|
95
|
+
hail_2in_probability: GetTextFromProduct({ Message: feature?.properties?.description, Find: [`PROB OF 1 OR MORE HAIL EVENTS >= 2 INCHES`], Removal: [`%`, `<`, `:`] }) ?? null,
|
|
96
|
+
combined_hail_wind_probability: GetTextFromProduct({ Message: feature?.properties?.description, Find: [`PROB OF 6 OR MORE COMBINED SEVERE HAIL/WIND EVENTS`], Removal: [`%`, `<`, `:`] }) ?? null,
|
|
97
|
+
max_hail_in: GetTextFromProduct({ Message: feature?.properties?.description, Find: [`MAX HAIL /INCHES/`], Removal: [`%`, `<`, `:`] }) ?? null,
|
|
98
|
+
max_wind_surface: GetTextFromProduct({ Message: feature?.properties?.description, Find: [`MAX WIND GUSTS SURFACE /KNOTS/`], Removal: [`%`, `<`, `:`] }) ?? null,
|
|
99
|
+
max_tops_x100feet: GetTextFromProduct({ Message: feature?.properties?.description, Find: [`MAX TOPS /X 100 FEET/`], Removal: [`%`, `<`, `:`] }) ?? null,
|
|
100
|
+
pds_watch: (GetTextFromProduct({ Message: feature?.properties?.description, Find: [`PARTICULARLY DANGEROUS SITUATION`], Removal: [`%`, `<`, `:`] }) === `YES`)
|
|
96
101
|
},
|
|
97
102
|
metadata: {
|
|
98
103
|
ms: performance.now() - tick,
|
|
99
104
|
source: `events.api`,
|
|
100
|
-
tracking: GetEventTracking({
|
|
105
|
+
tracking: GetEventTracking({ Type: `API`, WMO: { Identifier: feature?.properties?.parameters?.WMOidentifier?.[0], ID: feature?.id}, VTEC: VTEC?.[0]}),
|
|
101
106
|
header: `ZCZC-ATMOSX-${feature?.properties?.parameters?.WMOidentifier}`,
|
|
102
|
-
vtec:
|
|
107
|
+
vtec: VTEC?.[0],
|
|
103
108
|
hvtec: null,
|
|
104
109
|
raw: feature?.properties?.description,
|
|
105
110
|
history: [
|
|
@@ -112,6 +117,6 @@ export const ParseAPI = async (stanza: TypeStanzaCompiled): Promise<void> => {
|
|
|
112
117
|
}
|
|
113
118
|
}
|
|
114
119
|
})
|
|
115
|
-
SetDebug({
|
|
120
|
+
SetDebug({ Title: `ParseAPI`, Message: `${Math.round(performance.now() - tick)}ms` })
|
|
116
121
|
}
|
|
117
122
|
}
|