@atmosx/event-product-parser 3.0.61 → 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/.github/docs/docs-functions.md +1 -1
- package/.github/docs/docs-general-configurations.md +1 -1
- package/.github/docs/docs-integrations.md +1 -1
- package/.github/docs/docs-listeners.md +1 -1
- package/.github/docs/docs-source-configuration.md +1 -1
- package/.github/docs/docs-technical.md +1 -1
- package/README.md +7 -5
- 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
|
@@ -18,17 +18,23 @@
|
|
|
18
18
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
interface GetSampledPCM16Options {
|
|
22
|
+
Int16: Int16Array
|
|
23
|
+
OriginalRate: number
|
|
24
|
+
TargetRate: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const GetSampledPCM16 = ({ Int16, OriginalRate, TargetRate }: GetSampledPCM16Options): Int16Array => {
|
|
28
|
+
if (OriginalRate === TargetRate) return Int16;
|
|
29
|
+
const ratio = TargetRate / OriginalRate;
|
|
30
|
+
const outLen = Math.max(1, Math.round(Int16.length * ratio));
|
|
25
31
|
const out = new Int16Array(outLen);
|
|
26
32
|
for (let i = 0; i < outLen; i++) {
|
|
27
33
|
const pos = i / ratio;
|
|
28
34
|
const i0 = Math.floor(pos);
|
|
29
|
-
const i1 = Math.min(i0 + 1,
|
|
35
|
+
const i1 = Math.min(i0 + 1, Int16.length - 1);
|
|
30
36
|
const frac = pos - i0;
|
|
31
|
-
const v =
|
|
37
|
+
const v = Int16[i0] * (1 - frac) + Int16[i1] * frac;
|
|
32
38
|
out[i] = Math.round(v);
|
|
33
39
|
}
|
|
34
40
|
return out;
|
|
@@ -18,22 +18,27 @@
|
|
|
18
18
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import { platform } from
|
|
21
|
+
import { platform } from "os"
|
|
22
22
|
import { writeFileSync } from "fs"
|
|
23
|
-
import { execSync } from
|
|
23
|
+
import { execSync } from "child_process"
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
interface GetTTSOptions {
|
|
26
|
+
Text: string
|
|
27
|
+
OutputPath: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const GetTTS = async ({ Text, OutputPath }: GetTTSOptions): Promise<void> => {
|
|
26
31
|
const vPlatform = platform();
|
|
27
32
|
switch (vPlatform) {
|
|
28
33
|
case 'win32': {
|
|
29
34
|
try {
|
|
30
|
-
const txtPath =
|
|
31
|
-
writeFileSync(txtPath,
|
|
35
|
+
const txtPath = OutputPath + ".txt";
|
|
36
|
+
writeFileSync(txtPath, Text, "utf8");
|
|
32
37
|
const command = [
|
|
33
38
|
"Add-Type -AssemblyName System.Speech;",
|
|
34
39
|
`$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer;`,
|
|
35
40
|
`$text = Get-Content -Raw -LiteralPath '${txtPath}';`,
|
|
36
|
-
`$speak.SetOutputToWaveFile('${
|
|
41
|
+
`$speak.SetOutputToWaveFile('${OutputPath}');`,
|
|
37
42
|
`$speak.Speak($text);`,
|
|
38
43
|
`$speak.Dispose();`
|
|
39
44
|
].join(" ");
|
|
@@ -45,7 +50,7 @@ export const GetTTS = async (text: string, outputPath: string): Promise<void> =>
|
|
|
45
50
|
}
|
|
46
51
|
case 'linux':
|
|
47
52
|
try {
|
|
48
|
-
execSync(`espeak -w "${
|
|
53
|
+
execSync(`espeak -w "${OutputPath}" "${Text.replace(/"/g, '\\"')}"`);
|
|
49
54
|
} catch {}
|
|
50
55
|
break;
|
|
51
56
|
default: break;
|
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
interface getWavPCM16Response {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
Samples: Int16Array
|
|
23
|
+
SampleRate: number
|
|
24
|
+
Channels: number
|
|
25
|
+
BitsPerSample: number
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export const GetWavPCM16 = (buffer: Buffer): getWavPCM16Response => {
|
|
@@ -46,7 +46,7 @@ export const GetWavPCM16 = (buffer: Buffer): getWavPCM16Response => {
|
|
|
46
46
|
const bitsPerSample = fmt.readUInt16LE(14);
|
|
47
47
|
if (audioFormat !== 1 || bitsPerSample !== 16 || channels !== 1) { return null; }
|
|
48
48
|
const samples = new Int16Array(data.buffer, data.byteOffset, data.length / 2);
|
|
49
|
-
return {
|
|
49
|
+
return { Samples: new Int16Array(samples), SampleRate: sampleRate, Channels: channels, BitsPerSample: bitsPerSample };
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
|
|
@@ -18,7 +18,12 @@
|
|
|
18
18
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
interface SetAFSKOptions {
|
|
22
|
+
Bits: number[]
|
|
23
|
+
SampleRate: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const SetAFSK = ({ Bits, SampleRate }: SetAFSKOptions): Int16Array => {
|
|
22
27
|
const baud = 520.83;
|
|
23
28
|
const markFreq = 2083.3;
|
|
24
29
|
const spaceFreq = 1562.5;
|
|
@@ -27,20 +32,20 @@ export const SetAFSK = (bits: number[], sampleRate: number): Int16Array => {
|
|
|
27
32
|
const result = [];
|
|
28
33
|
let phase = 0;
|
|
29
34
|
let frac = 0;
|
|
30
|
-
for (let b = 0; b <
|
|
31
|
-
const bit =
|
|
35
|
+
for (let b = 0; b < Bits.length; b++) {
|
|
36
|
+
const bit = Bits[b];
|
|
32
37
|
const freq = bit ? markFreq : spaceFreq;
|
|
33
|
-
const samplesPerBit =
|
|
38
|
+
const samplesPerBit = SampleRate / baud + frac;
|
|
34
39
|
const n = Math.round(samplesPerBit);
|
|
35
40
|
frac = samplesPerBit - n;
|
|
36
|
-
const inc = twoPi * freq /
|
|
41
|
+
const inc = twoPi * freq / SampleRate;
|
|
37
42
|
for (let i = 0; i < n; i++) {
|
|
38
43
|
result.push(Math.round(Math.sin(phase) * amplitude * 32767));
|
|
39
44
|
phase += inc;
|
|
40
45
|
if (phase > twoPi) phase -= twoPi;
|
|
41
46
|
}
|
|
42
47
|
}
|
|
43
|
-
const fadeSamples = Math.floor(
|
|
48
|
+
const fadeSamples = Math.floor(SampleRate * 0.002);
|
|
44
49
|
for (let i = 0; i < fadeSamples; i++) {
|
|
45
50
|
const gain = i / fadeSamples;
|
|
46
51
|
result[i] = Math.round(result[i] * gain);
|
|
@@ -18,16 +18,21 @@
|
|
|
18
18
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
interface SetAttentionToneOptions {
|
|
22
|
+
MS: number
|
|
23
|
+
SampleRate: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const SetAttentionTone = ({ MS, SampleRate }: SetAttentionToneOptions): Int16Array => {
|
|
27
|
+
const len = Math.floor(MS * SampleRate);
|
|
23
28
|
const out = new Int16Array(len);
|
|
24
29
|
const f1 = 853;
|
|
25
30
|
const f2 = 960;
|
|
26
31
|
const twoPi = Math.PI * 2;
|
|
27
32
|
const amp = 0.1;
|
|
28
|
-
const fadeLen = Math.floor(
|
|
33
|
+
const fadeLen = Math.floor(SampleRate * 0.00);
|
|
29
34
|
for (let i = 0; i < len; i++) {
|
|
30
|
-
const t = i /
|
|
35
|
+
const t = i / SampleRate;
|
|
31
36
|
const s = Math.sin(twoPi * f1 * t) + Math.sin(twoPi * f2 * t);
|
|
32
37
|
let gain = 1;
|
|
33
38
|
if (i < fadeLen) gain = i / fadeLen;
|
|
@@ -18,12 +18,17 @@
|
|
|
18
18
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import { GetPCMToFloat } from "
|
|
22
|
-
import { GetFloatPCM16 } from "
|
|
21
|
+
import { GetPCMToFloat } from "@eas/GetPCMToFloat"
|
|
22
|
+
import { GetFloatPCM16 } from "@eas/GetFloatPCM16"
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
interface SetNoiseOptions {
|
|
25
|
+
Int16: Int16Array
|
|
26
|
+
NoiseLevel?: number
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const SetNoise = ({ Int16, NoiseLevel }: SetNoiseOptions): Int16Array => {
|
|
30
|
+
const x = GetPCMToFloat(Int16);
|
|
31
|
+
for (let i = 0; i < x.length; i++) x[i] += (Math.random() * 2 - 1) * (NoiseLevel ?? 0.02);
|
|
27
32
|
let peak = 0;
|
|
28
33
|
for (let i = 0; i < x.length; i++) peak = Math.max(peak, Math.abs(x[i]));
|
|
29
34
|
if (peak > 1) for (let i = 0; i < x.length; i++) x[i] *= 0.98 / peak;
|
|
@@ -18,14 +18,19 @@
|
|
|
18
18
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import { GetPCMToFloat } from "
|
|
22
|
-
import { GetFloatPCM16 } from "
|
|
21
|
+
import { GetPCMToFloat } from "@eas/GetPCMToFloat"
|
|
22
|
+
import { GetFloatPCM16 } from "@eas/GetFloatPCM16"
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
interface SetRadioEffectOptions {
|
|
25
|
+
Int16: Int16Array
|
|
26
|
+
SampleRate: number
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const SetRadioEffect = ({ Int16, SampleRate }: SetRadioEffectOptions): Int16Array => {
|
|
25
30
|
const hpCut = 3555;
|
|
26
31
|
const lpCut = 1600;
|
|
27
|
-
const x = GetPCMToFloat(
|
|
28
|
-
const dt = 1 /
|
|
32
|
+
const x = GetPCMToFloat(Int16);
|
|
33
|
+
const dt = 1 / SampleRate;
|
|
29
34
|
const rcHP = 1 / (2 * Math.PI * hpCut);
|
|
30
35
|
const aHP = rcHP / (rcHP + dt);
|
|
31
36
|
let yHP = 0, xPrev = 0;
|
|
@@ -18,26 +18,34 @@
|
|
|
18
18
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import { SetAFSK } from "
|
|
22
|
-
import { SetAsciiToBits } from "
|
|
23
|
-
import { GetMergedPCM16 } from "
|
|
21
|
+
import { SetAFSK } from "@eas/SetAFSK"
|
|
22
|
+
import { SetAsciiToBits } from "@eas/SetAsciiToBits"
|
|
23
|
+
import { GetMergedPCM16 } from "@eas/GetMergedPCM16"
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
interface SetSameHeaderOptions {
|
|
26
|
+
VTEC: string
|
|
27
|
+
Repeats: number
|
|
28
|
+
SampleRate?: number
|
|
29
|
+
PreMarkSec?: number
|
|
30
|
+
GapSec?: number
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const SetSameHeader = ({ VTEC, Repeats, SampleRate, PreMarkSec, GapSec }: SetSameHeaderOptions): Int16Array => {
|
|
34
|
+
const preMarkSec = PreMarkSec ?? 0.3;
|
|
35
|
+
const gapSec = GapSec ?? 0.1;
|
|
28
36
|
const bursts = [];
|
|
29
|
-
const gap = new Int16Array(Math.floor(gapSec *
|
|
30
|
-
for (let i = 0; i <
|
|
31
|
-
const bodyBits = SetAsciiToBits(
|
|
32
|
-
const body = SetAFSK(bodyBits,
|
|
33
|
-
const extendedBodyDuration = Math.round(preMarkSec *
|
|
37
|
+
const gap = new Int16Array(Math.floor(gapSec * SampleRate));
|
|
38
|
+
for (let i = 0; i < Repeats; i++) {
|
|
39
|
+
const bodyBits = SetAsciiToBits(VTEC);
|
|
40
|
+
const body = SetAFSK({ Bits: bodyBits, SampleRate: SampleRate});
|
|
41
|
+
const extendedBodyDuration = Math.round(preMarkSec * SampleRate);
|
|
34
42
|
const extendedBody = new Int16Array(extendedBodyDuration + gap.length);
|
|
35
43
|
for (let j = 0; j < extendedBodyDuration; j++) {
|
|
36
44
|
extendedBody[j] = Math.round(body[j % body.length] * 0.2);
|
|
37
45
|
}
|
|
38
46
|
extendedBody.set(gap, extendedBodyDuration);
|
|
39
47
|
bursts.push(extendedBody);
|
|
40
|
-
if (i !==
|
|
48
|
+
if (i !== Repeats - 1) bursts.push(gap);
|
|
41
49
|
}
|
|
42
50
|
return GetMergedPCM16(bursts);
|
|
43
51
|
}
|
|
@@ -17,32 +17,31 @@
|
|
|
17
17
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { TypeAttributes } from "
|
|
21
|
-
import { EnumAWIPS } from "
|
|
20
|
+
import { TypeAttributes } from "types-lower/Attributes"
|
|
21
|
+
import { EnumAWIPS } from "@enums/AWIPS"
|
|
22
22
|
|
|
23
23
|
interface GetAwipsTypeOptions {
|
|
24
|
-
|
|
24
|
+
Attributes: TypeAttributes
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
interface GetAwipsTypeResponse {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
Type: string
|
|
29
|
+
Prefix: string
|
|
30
|
+
Discovered: boolean
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export const GetAwipsType = (
|
|
34
|
-
|
|
35
|
-
if (!attributes.awipsid) {
|
|
33
|
+
export const GetAwipsType = ({ Attributes }: GetAwipsTypeOptions): GetAwipsTypeResponse => {
|
|
34
|
+
if (!Attributes.awipsid) {
|
|
36
35
|
return {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
Type: null,
|
|
37
|
+
Prefix: null,
|
|
38
|
+
Discovered: false
|
|
40
39
|
}
|
|
41
40
|
}
|
|
42
41
|
for (const [prefix, type] of Object.entries(EnumAWIPS)) {
|
|
43
|
-
if (
|
|
44
|
-
return { type, prefix,
|
|
42
|
+
if (Attributes.awipsid.startsWith(prefix)) {
|
|
43
|
+
return { Type: type, Prefix: prefix, Discovered: true }
|
|
45
44
|
}
|
|
46
45
|
}
|
|
47
|
-
return {
|
|
46
|
+
return { Type: Attributes.awipsid, Prefix: Attributes.awipsid, Discovered: false }
|
|
48
47
|
}
|
|
@@ -17,34 +17,46 @@
|
|
|
17
17
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { TypeStanza } from "
|
|
21
|
-
import { TypeStanzaCompiled } from "
|
|
22
|
-
import { EnumExpressions } from "
|
|
23
|
-
import { GetAwipsType } from "
|
|
20
|
+
import { TypeStanza } from "types-lower/Stanza"
|
|
21
|
+
import { TypeStanzaCompiled } from "types/StanzaCompiled"
|
|
22
|
+
import { EnumExpressions } from "@enums/Expressions"
|
|
23
|
+
import { GetAwipsType } from "@stanza/GetAwipsType"
|
|
24
24
|
|
|
25
25
|
interface ValidateStanzaOptions {
|
|
26
|
-
|
|
26
|
+
Stanza: TypeStanza
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
export const ValidateStanza = (
|
|
30
|
-
if (
|
|
31
|
-
const cb =
|
|
29
|
+
export const ValidateStanza = ({ Stanza }: ValidateStanzaOptions): TypeStanzaCompiled => {
|
|
30
|
+
if (Stanza.is(`message`)) {
|
|
31
|
+
const cb = Stanza.getChild(`x`) as TypeStanza;
|
|
32
32
|
if (cb && cb.children) {
|
|
33
33
|
const message = unescape(cb.children[0]);
|
|
34
34
|
const attributes = cb.attrs;
|
|
35
35
|
if (attributes.awipsid && attributes.awipsid.length > 1) {
|
|
36
36
|
const isCapEvent = message.includes(`<?xml`);
|
|
37
37
|
const isCapAreaDescription = message.includes(`<areaDesc>`)
|
|
38
|
-
const isVTEC = message.match(EnumExpressions.
|
|
38
|
+
const isVTEC = message.match(EnumExpressions.vtec) != null;
|
|
39
39
|
const isUGC = message.match(EnumExpressions.ugc1) != null;
|
|
40
|
-
const getType = GetAwipsType({
|
|
41
|
-
if (getType.
|
|
40
|
+
const getType = GetAwipsType({ Attributes: attributes})
|
|
41
|
+
if (getType.Type != null) {
|
|
42
42
|
return {
|
|
43
|
-
|
|
43
|
+
Message: message,
|
|
44
|
+
Attributes: attributes,
|
|
45
|
+
CapEvent: isCapEvent,
|
|
46
|
+
VTEC: isVTEC,
|
|
47
|
+
UGC: isUGC,
|
|
48
|
+
CapAreaDescription: isCapAreaDescription,
|
|
49
|
+
Ignored: false,
|
|
50
|
+
NWWS: true,
|
|
51
|
+
Type: {
|
|
52
|
+
Type: getType.Type,
|
|
53
|
+
Prefix: getType.Prefix,
|
|
54
|
+
Discovered: getType.Discovered
|
|
55
|
+
}
|
|
44
56
|
}
|
|
45
57
|
}
|
|
46
58
|
}
|
|
47
59
|
}
|
|
48
60
|
}
|
|
49
|
-
return {
|
|
61
|
+
return { Ignored: true}
|
|
50
62
|
}
|
|
@@ -18,27 +18,27 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
interface CreateHttpOptions {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
URL: string
|
|
22
|
+
Headers?: any
|
|
23
|
+
Timeout?: number
|
|
24
|
+
Auth?: {
|
|
25
|
+
Username: string
|
|
26
|
+
Password: string
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
Method?: `GET` | `POST` | `PUT` | `DELETE`
|
|
29
|
+
Body?: any;
|
|
30
|
+
Form?: any;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export const CreateHttp = async (
|
|
33
|
+
export const CreateHttp = async ({ URL, Headers, Timeout, Auth, Method, Body, Form }: CreateHttpOptions): Promise<any> => {
|
|
34
34
|
const requestOptions: RequestInit = {
|
|
35
|
-
method:
|
|
36
|
-
headers:
|
|
35
|
+
method: Method ?? "GET",
|
|
36
|
+
headers: Headers ?? {
|
|
37
37
|
"User-Agent": "AtmosphericX",
|
|
38
38
|
"Accept": "application/geo+json, text/plain, */*; q=0.9",
|
|
39
39
|
"Accept-Language": "en-US,en;q=0.9",
|
|
40
40
|
},
|
|
41
|
-
signal: AbortSignal.timeout(
|
|
41
|
+
signal: AbortSignal.timeout(Timeout ?? 10_000),
|
|
42
42
|
redirect: "follow"
|
|
43
43
|
};
|
|
44
44
|
|
|
@@ -47,8 +47,8 @@ export const CreateHttp = async (options: CreateHttpOptions): Promise<any> => {
|
|
|
47
47
|
return { error: error, options: requestOptions, status, message };
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
if (
|
|
51
|
-
const authString = `${
|
|
50
|
+
if (Auth) {
|
|
51
|
+
const authString = `${Auth.Username}:${Auth.Password}`;
|
|
52
52
|
const encodedAuth = Buffer.from(authString).toString("base64");
|
|
53
53
|
requestOptions.headers = {
|
|
54
54
|
...requestOptions.headers,
|
|
@@ -56,20 +56,20 @@ export const CreateHttp = async (options: CreateHttpOptions): Promise<any> => {
|
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
if (
|
|
60
|
-
requestOptions.body =
|
|
61
|
-
} else if (
|
|
59
|
+
if (Form) {
|
|
60
|
+
requestOptions.body = Form;
|
|
61
|
+
} else if (Body != undefined) {
|
|
62
62
|
requestOptions.body =
|
|
63
|
-
|
|
64
|
-
?
|
|
65
|
-
: typeof
|
|
66
|
-
?
|
|
67
|
-
: JSON.stringify(
|
|
63
|
+
Body instanceof FormData
|
|
64
|
+
? Body
|
|
65
|
+
: typeof Body === "string"
|
|
66
|
+
? Body
|
|
67
|
+
: JSON.stringify(Body);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
try {
|
|
71
71
|
const response = await fetch(
|
|
72
|
-
|
|
72
|
+
URL ?? `https://api.weather.gov/alerts/active`,
|
|
73
73
|
requestOptions
|
|
74
74
|
)
|
|
75
75
|
const body = await response.text();
|
|
@@ -17,11 +17,16 @@
|
|
|
17
17
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
interface GetMatchedOptions {
|
|
21
|
+
Strings: string[]
|
|
22
|
+
String: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const GetMatched = ({ Strings, String }: GetMatchedOptions): boolean => {
|
|
26
|
+
const isMatched = Strings.some(pattern => {
|
|
22
27
|
if (!pattern) return false;
|
|
23
28
|
const lowerP = pattern.toLowerCase();
|
|
24
|
-
const lowerS =
|
|
29
|
+
const lowerS = String.toLowerCase()
|
|
25
30
|
if (lowerP === "*" || lowerP === lowerS) return true;
|
|
26
31
|
if (lowerP.includes("*")) {
|
|
27
32
|
const regex = "^" + lowerP.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$";
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { TypeSettings } from
|
|
21
|
-
import {
|
|
20
|
+
import { TypeSettings } from "types/Settings"
|
|
21
|
+
import { Bootstrap } from "@bootstrap"
|
|
22
22
|
|
|
23
23
|
export const GetSettings = (): TypeSettings => {
|
|
24
|
-
return
|
|
24
|
+
return Bootstrap.Settings;
|
|
25
25
|
}
|
|
@@ -17,17 +17,22 @@
|
|
|
17
17
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
+
interface GetShapeNearestPointOptions {
|
|
21
|
+
Coordinates: number[][]
|
|
22
|
+
Point: [number, number]
|
|
23
|
+
}
|
|
24
|
+
|
|
20
25
|
interface GetShapeNearestPointResponse {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
Proximity: boolean
|
|
27
|
+
Point: [number, number]
|
|
28
|
+
Distance: number | null
|
|
29
|
+
DistanceKm?: number | null
|
|
30
|
+
DistanceMeters?: number | null
|
|
26
31
|
}
|
|
27
32
|
|
|
28
|
-
export const GetShapeNearestPoint = (
|
|
29
|
-
if (!
|
|
30
|
-
return {
|
|
33
|
+
export const GetShapeNearestPoint = ({ Coordinates, Point }: GetShapeNearestPointOptions): GetShapeNearestPointResponse => {
|
|
34
|
+
if (!Coordinates || !Point) {
|
|
35
|
+
return { Proximity: false, Point: [0, 0], Distance: null }
|
|
31
36
|
}
|
|
32
37
|
const normalize = (coords: any): any[] => {
|
|
33
38
|
if (!Array.isArray(coords)) return [];
|
|
@@ -43,10 +48,10 @@ export const GetShapeNearestPoint = (coordinates: number[][], point: [number, nu
|
|
|
43
48
|
}
|
|
44
49
|
return [];
|
|
45
50
|
}
|
|
46
|
-
const polys = normalize(
|
|
47
|
-
if (polys.length === 0) return {
|
|
48
|
-
const lon =
|
|
49
|
-
const lat =
|
|
51
|
+
const polys = normalize(Coordinates);
|
|
52
|
+
if (polys.length === 0) return { Proximity: false, Point: [0, 0], Distance: null };
|
|
53
|
+
const lon = Point[0];
|
|
54
|
+
const lat = Point[1];
|
|
50
55
|
const pointInRing = (pt: [number, number], ring: number[][]): boolean => {
|
|
51
56
|
let x = pt[0], y = pt[1];
|
|
52
57
|
let inside = false;
|
|
@@ -76,13 +81,13 @@ export const GetShapeNearestPoint = (coordinates: number[][], point: [number, nu
|
|
|
76
81
|
for (const poly of polys) {
|
|
77
82
|
const outer = poly[0];
|
|
78
83
|
const holes = poly.slice(1);
|
|
79
|
-
if (pointInRing(
|
|
84
|
+
if (pointInRing(Point, outer)) {
|
|
80
85
|
let inHole = false;
|
|
81
86
|
for (const hole of holes) {
|
|
82
|
-
if (pointInRing(
|
|
87
|
+
if (pointInRing(Point, hole)) { inHole = true; break; }
|
|
83
88
|
}
|
|
84
89
|
if (!inHole) {
|
|
85
|
-
return {
|
|
90
|
+
return { Proximity: true, Point, Distance: 0 };
|
|
86
91
|
}
|
|
87
92
|
}
|
|
88
93
|
for (const ring of poly) {
|
|
@@ -105,10 +110,10 @@ export const GetShapeNearestPoint = (coordinates: number[][], point: [number, nu
|
|
|
105
110
|
}
|
|
106
111
|
}
|
|
107
112
|
if (!isFinite(minDistance) || closestPoint == null) {
|
|
108
|
-
return {
|
|
113
|
+
return { Proximity: false, Point: [0,0], Distance: null };
|
|
109
114
|
}
|
|
110
115
|
const distanceMiles = minDistance;
|
|
111
116
|
const distanceKm = Number((distanceMiles * 1.609344).toFixed(3));
|
|
112
117
|
const distanceMeters = Math.round(distanceKm * 1000);
|
|
113
|
-
return {
|
|
118
|
+
return { Proximity: distanceMiles === 0, Point: closestPoint, Distance: distanceMiles, DistanceKm: distanceKm, DistanceMeters: distanceMeters };
|
|
114
119
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/*
|
|
2
|
+
_ _ _ __ __
|
|
3
|
+
/\ | | | | (_) \ \ / /
|
|
4
|
+
/ \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
|
|
5
|
+
/ /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
|
|
6
|
+
/ ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
|
|
7
|
+
/_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
|
|
8
|
+
| |
|
|
9
|
+
|_|
|
|
10
|
+
|
|
11
|
+
Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
|
|
12
|
+
Discord: https://atmosphericx-discord.scriptkitty.cafe
|
|
13
|
+
Ko-Fi: https://ko-fi.com/k3yomi
|
|
14
|
+
Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
|
|
15
|
+
|
|
16
|
+
Internal Package: @atmosx/event-product-parser
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export type QueueTask = () => Promise<void>;
|
|
20
|
+
|
|
21
|
+
export interface QueueManagerOptions {
|
|
22
|
+
Concurrency?: number;
|
|
23
|
+
Error?: (error: unknown) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class QueueManager {
|
|
27
|
+
private readonly queue: QueueTask[] = [];
|
|
28
|
+
private readonly concurrency: number;
|
|
29
|
+
private readonly onError: (error: unknown) => void;
|
|
30
|
+
|
|
31
|
+
private running = 0;
|
|
32
|
+
private draining = false;
|
|
33
|
+
|
|
34
|
+
public constructor({ Concurrency, Error }: QueueManagerOptions = {}) {
|
|
35
|
+
this.concurrency = Concurrency ?? 1;
|
|
36
|
+
this.onError = Error ?? ((error) => {
|
|
37
|
+
console.error("[QueueManager] Task failed:", error);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public enqueue(task: QueueTask): void {
|
|
42
|
+
this.queue.push(task);
|
|
43
|
+
this.drain();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private drain(): void {
|
|
47
|
+
if (this.draining) { return; }
|
|
48
|
+
this.draining = true;
|
|
49
|
+
while (this.running < this.concurrency && this.queue.length > 0) {
|
|
50
|
+
const task = this.queue.shift();
|
|
51
|
+
if (!task) { continue; }
|
|
52
|
+
this.running++;
|
|
53
|
+
void this.execute(task);
|
|
54
|
+
}
|
|
55
|
+
this.draining = false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private async execute(task: QueueTask): Promise<void> {
|
|
59
|
+
try {
|
|
60
|
+
await task();
|
|
61
|
+
} catch (error) {
|
|
62
|
+
try {
|
|
63
|
+
this.onError(error);
|
|
64
|
+
} catch (handlerError) {}
|
|
65
|
+
} finally {
|
|
66
|
+
this.running--;
|
|
67
|
+
this.drain();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|