@sayue_ltr/fleq 1.51.0 → 2.0.1
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/CHANGELOG.md +86 -0
- package/README.md +1 -1
- package/dist/config.js +2 -2
- package/dist/dmdata/telegram-parser.js +78 -5
- package/dist/engine/cli/cli-run.js +19 -4
- package/dist/engine/eew/eew-tracker.js +41 -15
- package/dist/engine/monitor/monitor.js +1 -1
- package/dist/engine/notification/notifier.js +5 -3
- package/dist/engine/notification/sound-player.js +232 -35
- package/dist/engine/presentation/processors/process-eew.js +15 -0
- package/dist/engine/presentation/processors/process-message.js +2 -2
- package/dist/engine/startup/update-checker.js +23 -2
- package/dist/engine/template/compiler.js +4 -1
- package/dist/engine/template/field-accessor.js +10 -4
- package/dist/engine/template/filters.js +14 -8
- package/dist/engine/template/parser.js +10 -15
- package/dist/types.js +1 -1
- package/dist/ui/earthquake-formatter.js +5 -0
- package/dist/ui/formatter.js +49 -0
- package/dist/ui/repl-handlers/command-definitions.js +11 -4
- package/dist/ui/repl-handlers/info-handlers.js +97 -41
- package/dist/ui/repl-handlers/settings-handlers.js +19 -15
- package/dist/ui/statistics-formatter.js +65 -15
- package/dist/ui/status-line.js +11 -0
- package/dist/ui/test-samples.js +6 -0
- package/dist/ui/theme.js +9 -0
- package/dist/ui/waiting-tips-eew.js +63 -0
- package/dist/ui/waiting-tips-info-systems.js +81 -0
- package/dist/ui/waiting-tips-seismology.js +97 -0
- package/dist/ui/waiting-tips-tsunami.js +72 -0
- package/dist/ui/waiting-tips-weather.js +189 -0
- package/dist/ui/waiting-tips.js +137 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,92 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [2.0.1](https://github.com/Lateo2580/FlEq/compare/v2.0.0...v2.0.1) (2026-05-03)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### バグ修正
|
|
9
|
+
|
|
10
|
+
* **update-checker:** scoped package 名の `/` を %2F に encode する ([6f02a0e](https://github.com/Lateo2580/FlEq/commit/6f02a0e5e9536272d1c4760575f10c7c93cc10d3))
|
|
11
|
+
* **update-checker:** scoped パッケージ名対応と personal build 検出 ([b4c75d8](https://github.com/Lateo2580/FlEq/commit/b4c75d8cbec707255e6e8d50a688dee415217f15))
|
|
12
|
+
|
|
13
|
+
## [2.0.0](https://github.com/Lateo2580/FlEq/compare/v1.51.0...v2.0.0) (2026-04-25)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### ⚠ BREAKING CHANGES
|
|
17
|
+
|
|
18
|
+
* **policy:** --event-log / --event-log-raw / --no-event-log の
|
|
19
|
+
CLI オプションが廃止された。eventLog / eventLogRaw 設定キーも廃止。
|
|
20
|
+
これらの機能に依存している外部利用者は、削除前バージョン (v1.52.x)
|
|
21
|
+
を pin するか、自身で private fork を保持して継続利用すること。
|
|
22
|
+
|
|
23
|
+
次回リリースは major bump (v2.0.0) として npm run release:major で
|
|
24
|
+
発行する想定。
|
|
25
|
+
|
|
26
|
+
関連: 段階1 (baf0b41)
|
|
27
|
+
|
|
28
|
+
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
29
|
+
|
|
30
|
+
### 機能追加
|
|
31
|
+
|
|
32
|
+
* **cli:** add --event-log and --event-log-raw flags ([f046653](https://github.com/Lateo2580/FlEq/commit/f046653df9c42d88f0362bdeb34dc87bcdf999f3))
|
|
33
|
+
* **cli:** show eventLog status in startup banner ([07d509f](https://github.com/Lateo2580/FlEq/commit/07d509fda93cfc0e4f1d83b9208e5dc0e86e9270))
|
|
34
|
+
* **cli:** 起動バナーに音声バックエンドの健康状態を表示 ([30a070e](https://github.com/Lateo2580/FlEq/commit/30a070e0fa97c8c034269c608a050500b058ce6d))
|
|
35
|
+
* clock コマンド・ヘルプ・status 表示を uptime 対応に ([f362927](https://github.com/Lateo2580/FlEq/commit/f362927c84515770b609628c2ced7ba0cf19004f))
|
|
36
|
+
* commands コマンド新設 — help から一覧表示を分離 ([0eb81c1](https://github.com/Lateo2580/FlEq/commit/0eb81c1fe335617e6346657a0d9bf1cacca0b387))
|
|
37
|
+
* **config:** add eventLog and eventLogRaw configuration ([db1f376](https://github.com/Lateo2580/FlEq/commit/db1f376648e705c61d1439f85e75fdde17a74810))
|
|
38
|
+
* EEW isWarning 判定を XML ベースに移行 (classification はフォールバック) ([c2087cd](https://github.com/Lateo2580/FlEq/commit/c2087cd931a31af603cadd4eff90a0a33bac833f))
|
|
39
|
+
* Event File Writer — write telegrams as individual JSON files ([0ebbd0c](https://github.com/Lateo2580/FlEq/commit/0ebbd0cb21e33d03cb97f7c6a78f996941dbfcad))
|
|
40
|
+
* formatUptime 関数を追加 (DDD:HH:MM:SS, dim ゼロ桁) ([a694522](https://github.com/Lateo2580/FlEq/commit/a6945227cc1c3c1d16728356075aae4e9765301b))
|
|
41
|
+
* implement EventFileWriter with atomic write ([e05e9cb](https://github.com/Lateo2580/FlEq/commit/e05e9cb66cd61e9af049b2c6e8d2beed7c1f50e8))
|
|
42
|
+
* **monitor:** apply eventLog config to EventFileWriter on startup ([12c251b](https://github.com/Lateo2580/FlEq/commit/12c251b1c298d4f8f0446ec93576d8904bed091b))
|
|
43
|
+
* ParsedEarthquakeInfo に eventId を追加し、地震情報表示に EventID 行を表示 ([7562377](https://github.com/Lateo2580/FlEq/commit/756237726821e2c48ceed4a58ba2ce62ce3270b8))
|
|
44
|
+
* **policy:** dmdata.jp 再配信ポリシー対応の段階1実装 ([baf0b41](https://github.com/Lateo2580/FlEq/commit/baf0b4135878ab6400554bc7c790ddd4c8be6cdc))
|
|
45
|
+
* processEew に suppressed kind を追加 (VXSE45 優先時) ([e65b33e](https://github.com/Lateo2580/FlEq/commit/e65b33e6d7ab7480bd13359a7da21661b78a40a7))
|
|
46
|
+
* PromptClock 型に uptime を追加 ([d7c2829](https://github.com/Lateo2580/FlEq/commit/d7c2829487b78ebf993c4188ec1054d74b8ad071))
|
|
47
|
+
* **repl:** add eventlog command ([874a679](https://github.com/Lateo2580/FlEq/commit/874a679eb4b12d8bba330a205d11a117ab8de2e9))
|
|
48
|
+
* **sound:** checkSoundBackend で実再生プローブによる健康チェックを追加 ([46c391c](https://github.com/Lateo2580/FlEq/commit/46c391cb3222e5200594e27040f2f9214aace123))
|
|
49
|
+
* **sound:** process.uptime ベースの単調時計ヘルパーを追加 ([6cfc195](https://github.com/Lateo2580/FlEq/commit/6cfc195bf2540f0c6586ad22be8159824fdde49f))
|
|
50
|
+
* **sound:** 起動直後 60 秒以内の再生失敗を 20 秒後に自動リトライ ([9b86ff9](https://github.com/Lateo2580/FlEq/commit/9b86ff96111bc18066e0f4aaba7f0f513fd07b1e))
|
|
51
|
+
* **sound:** 再生失敗系ログを debug から warn に格上げ ([fd9f1d5](https://github.com/Lateo2580/FlEq/commit/fd9f1d5dc07ba234bbdb9cb0cb76bd9015662673))
|
|
52
|
+
* StatusLine で uptime モード表示に対応 (未接続時も表示) ([55c93c7](https://github.com/Lateo2580/FlEq/commit/55c93c7733d5e31fccd5f47944e37736aaa5ec41))
|
|
53
|
+
* wire EventFileWriter into message-router and VolcanoRouteHandler ([a1107e0](https://github.com/Lateo2580/FlEq/commit/a1107e00db7adbfdd81c3e377143001c7542e8ce))
|
|
54
|
+
* wrap-up コマンド新設 + Codex CLI フラグ更新 + commit リマインド hook 追加 ([68dac86](https://github.com/Lateo2580/FlEq/commit/68dac861fc17e95ce1fb6ac9cb0c43bc5bc62592))
|
|
55
|
+
* 警報昇格通知をイベント単位の isUpgradeToWarning に変更 ([2e338dd](https://github.com/Lateo2580/FlEq/commit/2e338dd797f4c814da8209505f2d1e2a822e951b))
|
|
56
|
+
* 待機中Tipsに火山カテゴリを追加 (+78項目) ([0f619bb](https://github.com/Lateo2580/FlEq/commit/0f619bb87eb663f611ea461d2002040a0fedafe6))
|
|
57
|
+
* 待機中Tipsに気象カテゴリを追加 (警報・予報・定時報) ([deda85e](https://github.com/Lateo2580/FlEq/commit/deda85e46f1b154248a919ac623c3e81ea5d1048))
|
|
58
|
+
* 待機中Tipsを大幅拡充 — 新8カテゴリ+229件追加 (444→673件) ([bac458a](https://github.com/Lateo2580/FlEq/commit/bac458a556ddaa807f558fcf57cc0199b10c2a40))
|
|
59
|
+
* 統計表示にテーマロールベースのカラーリングを追加 ([d816b17](https://github.com/Lateo2580/FlEq/commit/d816b1766f3b25c2cbd199ee54248df995ef3121))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
### バグ修正
|
|
63
|
+
|
|
64
|
+
* **cli:** let --no-event-log override --event-log-raw ([fe5226e](https://github.com/Lateo2580/FlEq/commit/fe5226e13ccf7d3c8357c925cdff5466755a340f))
|
|
65
|
+
* **eew:** isWarning 観測ログを真の仕様不整合のみに絞る ([be1757c](https://github.com/Lateo2580/FlEq/commit/be1757cabf7ae22b7c8045e316477ad338fac049))
|
|
66
|
+
* **event-file-writer:** prevent collision and enforce maxFiles strictly ([a7227d6](https://github.com/Lateo2580/FlEq/commit/a7227d6f6b619fcd5a5ddfbf972aa143a5c5ff63))
|
|
67
|
+
* formatUptime の日部分を文字レベルで dim 表示する ([67d5c70](https://github.com/Lateo2580/FlEq/commit/67d5c700a35d48b97de2c7534392ef33109af59c))
|
|
68
|
+
* **monitor:** drain EventFileWriter queue on shutdown ([6cd0fc2](https://github.com/Lateo2580/FlEq/commit/6cd0fc2836e51c43218202ea51257a18cdb8056b))
|
|
69
|
+
* processMessage の EEW suppressed 結果ハンドリングを追加 ([3b5f48f](https://github.com/Lateo2580/FlEq/commit/3b5f48f72e627ee0e68372e892959c623bab174b))
|
|
70
|
+
* **sound:** Codex レビュー 2 の指摘を反映 ([3d3fdf1](https://github.com/Lateo2580/FlEq/commit/3d3fdf185a51c43d6519c7fb083a415ffa184249))
|
|
71
|
+
* **sound:** DoneHandle 化で launch 経路の log/bell も二重完了ガードで保護 ([cc5fd1c](https://github.com/Lateo2580/FlEq/commit/cc5fd1c126a38c39585250dd15831f436b2a02b7))
|
|
72
|
+
* **sound:** runPlay の timeout と execFile コールバックによる二重完了を防止 ([b0456dc](https://github.com/Lateo2580/FlEq/commit/b0456dc8f78ea1b7e6d6f62bcc8e4a007fb90aae))
|
|
73
|
+
* VXSE61 の複数 Coordinate ノードから十進度を正しく抽出 ([bdcfb38](https://github.com/Lateo2580/FlEq/commit/bdcfb383f0d34694714fa81da38cee875529e424))
|
|
74
|
+
* 起動メッセージをcommands案内に更新、+マーカーの位置と凡例を改善 ([f13fabd](https://github.com/Lateo2580/FlEq/commit/f13fabd7c688ef9da5b171787de51e5aa80d2c00))
|
|
75
|
+
* 抑制報の hasWarningIssued 更新と終端処理を修正 (Codex レビュー指摘) ([34676ba](https://github.com/Lateo2580/FlEq/commit/34676ba8a5c0d434de387a13b7349740abaaeafb))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
### リファクタリング
|
|
79
|
+
|
|
80
|
+
* codex-design スキルを対立的レビュー方式に刷新 ([6db5a6d](https://github.com/Lateo2580/FlEq/commit/6db5a6d6dd17ca8bcdf0192064458dc866f92752))
|
|
81
|
+
* EewTracker を byType Map ベースに再設計 ([79a606f](https://github.com/Lateo2580/FlEq/commit/79a606fbcb80fe2b15ee5887529140ec9c311f56))
|
|
82
|
+
* **policy:** dmdata.jp 再配信ポリシー対応の段階2 ([cf5c6fb](https://github.com/Lateo2580/FlEq/commit/cf5c6fbe047953ce256827e18ce5a62d74186096))
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
### ドキュメント
|
|
86
|
+
|
|
87
|
+
* EEW再設計に伴う仕様書同期 (5ファイル) ([a4f4b4a](https://github.com/Lateo2580/FlEq/commit/a4f4b4ad15f2c764599078884e50a427f678aa08))
|
|
88
|
+
* uptime モードの Tip・仕様書を同期 ([6b77ae5](https://github.com/Lateo2580/FlEq/commit/6b77ae57298584e7978bdae40cc3c2b86e4ae084))
|
|
89
|
+
* 待機中Tipsを現行機能に同期 (21件追加, 1件修正) ([078e070](https://github.com/Lateo2580/FlEq/commit/078e0708b76e2a89246dac67a2242cd3387558ea))
|
|
90
|
+
|
|
5
91
|
## [1.51.0](https://github.com/Lateo2580/FlEq/compare/v1.50.1...v1.51.0) (2026-03-29)
|
|
6
92
|
|
|
7
93
|
|
package/README.md
CHANGED
|
@@ -202,7 +202,7 @@ fleq config keys # 設定可能キー一覧を表示
|
|
|
202
202
|
| `promptClock` | プロンプト時計: `"elapsed"` (経過時間) / `"clock"` (現在時刻) |
|
|
203
203
|
| `waitTipIntervalMin` | 待機中ヒント表示間隔(分、0 で無効、デフォルト: 30) |
|
|
204
204
|
| `sound` | 通知音の有効/無効 (`true` / `false`) |
|
|
205
|
-
| `eewLog` | EEW ログ記録の有効/無効 (`true` / `false`
|
|
205
|
+
| `eewLog` | EEW ログ記録の有効/無効 (`true` / `false`、デフォルト: `false`。明示 opt-in) |
|
|
206
206
|
| `maxObservations` | 観測点の最大表示件数 (1〜999 / `"off"` で全件表示) |
|
|
207
207
|
| `backup` | EEW 副回線の有効/無効 (`true` / `false`) |
|
|
208
208
|
| `nightMode` | ナイトモードの有効/無効 (`true` / `false`) |
|
package/dist/config.js
CHANGED
|
@@ -159,7 +159,7 @@ const VALID_TEST_MODES = ["no", "including", "only"];
|
|
|
159
159
|
/** 有効な表示モード */
|
|
160
160
|
const VALID_DISPLAY_MODES = ["normal", "compact"];
|
|
161
161
|
/** 有効なプロンプト時計モード */
|
|
162
|
-
const VALID_PROMPT_CLOCKS = ["elapsed", "clock"];
|
|
162
|
+
const VALID_PROMPT_CLOCKS = ["elapsed", "clock", "uptime"];
|
|
163
163
|
/** 有効な EEW ログ記録項目 */
|
|
164
164
|
exports.VALID_EEW_LOG_FIELDS = [
|
|
165
165
|
"hypocenter",
|
|
@@ -214,7 +214,7 @@ const CONFIG_KEYS = {
|
|
|
214
214
|
tableWidth: 'テーブル表示幅 (40〜200 / "auto" でターミナル幅に自動追従)',
|
|
215
215
|
infoFullText: "お知らせ電文の全文表示 (true/false)",
|
|
216
216
|
displayMode: '表示モード: "normal" | "compact"',
|
|
217
|
-
promptClock: 'プロンプト時計: "elapsed" (経過時間) | "clock" (現在時刻)',
|
|
217
|
+
promptClock: 'プロンプト時計: "elapsed" (経過時間) | "clock" (現在時刻) | "uptime" (稼働時間)',
|
|
218
218
|
waitTipIntervalMin: "待機中ヒント表示間隔 (分, 0で無効)",
|
|
219
219
|
sound: "通知音の有効/無効 (true/false)",
|
|
220
220
|
eewLog: "EEWログ記録の有効/無効 (true/false)",
|
|
@@ -152,10 +152,15 @@ function extractEarthquake(earthquake) {
|
|
|
152
152
|
const area = first(dig(hypo, "Area"));
|
|
153
153
|
const name = str(dig(area, "Name"));
|
|
154
154
|
// 座標パース: "+35.7+139.8-10000/" 形式
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
155
|
+
// VXSE61 等では jmx_eb:Coordinate が複数 (十進度 + 度分) 存在し配列になる。
|
|
156
|
+
// type="震源位置(度分)" を除外して十進度を優先選択する。
|
|
157
|
+
const rawCoord = dig(area, "jmx_eb:Coordinate") || dig(area, "Coordinate");
|
|
158
|
+
const coordNode = Array.isArray(rawCoord)
|
|
159
|
+
? rawCoord.find((c) => str(dig(c, "@_type")) !== "震源位置(度分)") ?? rawCoord[0]
|
|
160
|
+
: rawCoord;
|
|
161
|
+
const coordStr = str(coordNode != null && typeof coordNode === "object"
|
|
162
|
+
? dig(coordNode, "#text")
|
|
163
|
+
: coordNode);
|
|
159
164
|
const { lat, lon, depth } = parseCoordinate(coordStr);
|
|
160
165
|
const magRaw = str(dig(earthquake, "jmx_eb:Magnitude", "#text") ||
|
|
161
166
|
dig(earthquake, "Magnitude", "#text") ||
|
|
@@ -238,6 +243,54 @@ function extractTsunami(body) {
|
|
|
238
243
|
return { text };
|
|
239
244
|
}
|
|
240
245
|
// ── EEW ヘルパー ──
|
|
246
|
+
/** Headline Information の Kind Code に警報コード (31) が含まれるか */
|
|
247
|
+
function hasWarningHeadlineCode(head) {
|
|
248
|
+
const headline = dig(head, "Headline");
|
|
249
|
+
const informations = dig(headline, "Information");
|
|
250
|
+
const infoList = Array.isArray(informations) ? informations : informations ? [informations] : [];
|
|
251
|
+
for (const info of infoList) {
|
|
252
|
+
const items = dig(info, "Item");
|
|
253
|
+
const itemList = Array.isArray(items) ? items : items ? [items] : [];
|
|
254
|
+
for (const item of itemList) {
|
|
255
|
+
const kinds = dig(item, "Kind");
|
|
256
|
+
const kindList = Array.isArray(kinds) ? kinds : kinds ? [kinds] : [];
|
|
257
|
+
for (const kind of kindList) {
|
|
258
|
+
const code = parseInt(str(dig(kind, "Code")), 10);
|
|
259
|
+
if (code === 31)
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
/** 予測地域の Category Kind Code に警報コード (10-19) が含まれるか */
|
|
267
|
+
function hasWarningAreaKind(body) {
|
|
268
|
+
const forecast = dig(body, "Intensity", "Forecast");
|
|
269
|
+
if (!forecast)
|
|
270
|
+
return false;
|
|
271
|
+
const prefs = dig(forecast, "Pref");
|
|
272
|
+
if (!Array.isArray(prefs))
|
|
273
|
+
return false;
|
|
274
|
+
for (const pref of prefs) {
|
|
275
|
+
const areas = dig(pref, "Area");
|
|
276
|
+
if (!Array.isArray(areas))
|
|
277
|
+
continue;
|
|
278
|
+
for (const area of areas) {
|
|
279
|
+
const categories = dig(area, "Category");
|
|
280
|
+
const catList = Array.isArray(categories) ? categories : categories ? [categories] : [];
|
|
281
|
+
for (const cat of catList) {
|
|
282
|
+
const kinds = dig(cat, "Kind");
|
|
283
|
+
const kindList = Array.isArray(kinds) ? kinds : kinds ? [kinds] : [];
|
|
284
|
+
for (const kind of kindList) {
|
|
285
|
+
const code = parseInt(str(dig(kind, "Code")), 10);
|
|
286
|
+
if (code >= 10 && code <= 19)
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
241
294
|
function parseMaxIntChangeReason(body) {
|
|
242
295
|
const raw = str(dig(body, "Intensity", "Forecast", "Appendix", "MaxIntChangeReason"));
|
|
243
296
|
if (!raw)
|
|
@@ -418,6 +471,7 @@ function parseEarthquakeTelegram(msg) {
|
|
|
418
471
|
reportDateTime: str(dig(head, "ReportDateTime")),
|
|
419
472
|
headline: str(dig(head, "Headline", "Text")) || null,
|
|
420
473
|
publishingOffice: msg.xmlReport?.control?.publishingOffice || "",
|
|
474
|
+
eventId: str(dig(head, "EventID")) || null,
|
|
421
475
|
isTest: msg.head.test,
|
|
422
476
|
};
|
|
423
477
|
// 震源
|
|
@@ -461,7 +515,7 @@ function parseEewTelegram(msg) {
|
|
|
461
515
|
serial: str(dig(head, "Serial")) || null,
|
|
462
516
|
eventId: str(dig(head, "EventID")) || null,
|
|
463
517
|
isTest: msg.head.test,
|
|
464
|
-
isWarning:
|
|
518
|
+
isWarning: false, // 仮値 — 後で XML から判定
|
|
465
519
|
isAssumedHypocenter: false,
|
|
466
520
|
};
|
|
467
521
|
info.maxIntChangeReason = parseMaxIntChangeReason(body);
|
|
@@ -480,6 +534,25 @@ function parseEewTelegram(msg) {
|
|
|
480
534
|
(info.maxIntChangeReason === 9 || hasPlumArea);
|
|
481
535
|
info.isAssumedHypocenter =
|
|
482
536
|
assumedHypocenterByCondition || assumedHypocenterByFallback;
|
|
537
|
+
// isWarning: XML ベース主判定 + classification を安全側フォールバック
|
|
538
|
+
// xmlWarning / classWarning を先に変数化し、判定と観測ログで同じ値を共用する。
|
|
539
|
+
const xmlWarning = msg.head.type === "VXSE43" ||
|
|
540
|
+
hasWarningAreaKind(body) ||
|
|
541
|
+
hasWarningHeadlineCode(head);
|
|
542
|
+
const classWarning = msg.classification === "eew.warning";
|
|
543
|
+
info.isWarning = xmlWarning || classWarning;
|
|
544
|
+
// 観測ログ (仕様不整合の検知用):
|
|
545
|
+
// (1) classification=eew.warning だが XML ベース判定で警報条件を検出できない
|
|
546
|
+
// (2) VXSE43 電文なのに classification が eew.warning ではない(契約差分・仕様変更の兆候)
|
|
547
|
+
// 逆方向の一般形 (xmlWarning && !classWarning) は VXSE44/VXSE45 警報相当の正常パターンなのでログしない。
|
|
548
|
+
if (classWarning && !xmlWarning) {
|
|
549
|
+
log.warn(`EEW classification=eew.warning だが XML ベース判定で警報条件を検出できず: ` +
|
|
550
|
+
`type=${msg.head.type} EventID=${str(dig(head, "EventID"))}`);
|
|
551
|
+
}
|
|
552
|
+
else if (msg.head.type === "VXSE43" && !classWarning) {
|
|
553
|
+
log.warn(`EEW VXSE43 電文だが classification=${msg.classification} (eew.warning ではない): ` +
|
|
554
|
+
`EventID=${str(dig(head, "EventID"))}`);
|
|
555
|
+
}
|
|
483
556
|
// NextAdvisory (最終報)
|
|
484
557
|
const nextAdvisory = str(dig(body, "NextAdvisory"));
|
|
485
558
|
if (nextAdvisory) {
|
|
@@ -50,7 +50,7 @@ const updateChecker = __importStar(require("../startup/update-checker"));
|
|
|
50
50
|
const log = __importStar(require("../../logger"));
|
|
51
51
|
const pipeline_controller_1 = require("../filter-template/pipeline-controller");
|
|
52
52
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
53
|
-
const { version: VERSION } = require("../../../package.json");
|
|
53
|
+
const { version: VERSION, name: PACKAGE_NAME } = require("../../../package.json");
|
|
54
54
|
async function runMonitor(opts) {
|
|
55
55
|
// ログレベル設定
|
|
56
56
|
if (opts.debug) {
|
|
@@ -162,8 +162,8 @@ async function runMonitor(opts) {
|
|
|
162
162
|
log.info(`定期要約: ${opts.summaryInterval}分間隔`);
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
|
-
printBanner(config);
|
|
166
|
-
updateChecker.checkForUpdates(
|
|
165
|
+
await printBanner(config);
|
|
166
|
+
updateChecker.checkForUpdates(PACKAGE_NAME, VERSION);
|
|
167
167
|
await (0, monitor_1.startMonitor)(config, pipelineController);
|
|
168
168
|
}
|
|
169
169
|
/** ターミナルタイトルを設定する (ANSI OSC sequence) */
|
|
@@ -180,12 +180,27 @@ function resetTerminalTitle() {
|
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
/** 起動バナー表示 */
|
|
183
|
-
function printBanner(config) {
|
|
183
|
+
async function printBanner(config) {
|
|
184
184
|
log.info(`受信区分: ${config.classifications.join(", ")}`);
|
|
185
185
|
log.info(`テストモード: ${config.testMode}`);
|
|
186
186
|
if (config.displayMode !== "normal") {
|
|
187
187
|
log.info(`表示モード: ${config.displayMode}`);
|
|
188
188
|
}
|
|
189
|
+
// 音声バックエンド状態 (起動バナー末尾、Linux は実再生プローブ)
|
|
190
|
+
try {
|
|
191
|
+
const { checkSoundBackend } = await Promise.resolve().then(() => __importStar(require("../notification/sound-player")));
|
|
192
|
+
const result = await checkSoundBackend();
|
|
193
|
+
// CUD パレット: blueGreen = RGB(0, 158, 115), vermillion = RGB(213, 94, 0)
|
|
194
|
+
const line = result.ok
|
|
195
|
+
? chalk_1.default.rgb(0, 158, 115)(`音声: ${result.label} OK`)
|
|
196
|
+
: chalk_1.default.rgb(213, 94, 0)(`音声: ${result.label} NG${result.reason ? ` (${result.reason})` : ""}`);
|
|
197
|
+
console.log(line);
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
if (err instanceof Error) {
|
|
201
|
+
log.warn(`音声バックエンド確認エラー: ${err.message}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
189
204
|
log.info("接続を開始します...");
|
|
190
205
|
console.log();
|
|
191
206
|
}
|
|
@@ -118,6 +118,8 @@ class EewTracker {
|
|
|
118
118
|
isNew: true,
|
|
119
119
|
isDuplicate: false,
|
|
120
120
|
isCancelled: info.infoType === "取消",
|
|
121
|
+
isSuppressed: false,
|
|
122
|
+
isUpgradeToWarning: false,
|
|
121
123
|
activeCount: this.getActiveCount(),
|
|
122
124
|
colorIndex: 0,
|
|
123
125
|
};
|
|
@@ -125,56 +127,80 @@ class EewTracker {
|
|
|
125
127
|
const serialRaw = parseInt(info.serial || "", 10);
|
|
126
128
|
const serial = Number.isFinite(serialRaw) ? serialRaw : null;
|
|
127
129
|
const isCancelled = info.infoType === "取消";
|
|
130
|
+
const headType = info.type;
|
|
128
131
|
const existing = this.events.get(eventId);
|
|
129
132
|
if (existing) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
+
const typeState = existing.byType.get(headType);
|
|
134
|
+
// 同一 type 内の重複判定
|
|
135
|
+
if (!isCancelled && serial != null && serial > 0 && typeState && serial <= typeState.lastSerial) {
|
|
133
136
|
return {
|
|
134
137
|
isNew: false,
|
|
135
138
|
isDuplicate: true,
|
|
136
139
|
isCancelled: false,
|
|
140
|
+
isSuppressed: false,
|
|
141
|
+
isUpgradeToWarning: false,
|
|
137
142
|
activeCount: this.getActiveCount(),
|
|
138
143
|
colorIndex: existing.colorIndex,
|
|
139
144
|
};
|
|
140
145
|
}
|
|
141
|
-
//
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (
|
|
146
|
-
existing.lastSerial
|
|
146
|
+
// 抑制判定: VXSE45 受信済みなら VXSE43/44 は抑制
|
|
147
|
+
const isSuppressed = existing.hasSeen45 && (headType === "VXSE43" || headType === "VXSE44");
|
|
148
|
+
// type 状態の更新 (抑制されても serial・lastUpdate は更新する)
|
|
149
|
+
const previousInfo = typeState?.previousInfo;
|
|
150
|
+
if (!typeState) {
|
|
151
|
+
existing.byType.set(headType, { lastSerial: serial ?? 0, previousInfo: info });
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
if (serial != null) {
|
|
155
|
+
typeState.lastSerial = Math.max(typeState.lastSerial, serial);
|
|
156
|
+
}
|
|
157
|
+
typeState.previousInfo = info;
|
|
158
|
+
}
|
|
159
|
+
// hasSeen45 更新
|
|
160
|
+
if (headType === "VXSE45") {
|
|
161
|
+
existing.hasSeen45 = true;
|
|
162
|
+
}
|
|
163
|
+
// 差分計算: 同一 type 内の連続更新でのみ (初めての type では diff なし)
|
|
164
|
+
const diff = previousInfo ? computeDiff(previousInfo, info) : undefined;
|
|
165
|
+
// 警報昇格判定 (イベント単位)
|
|
166
|
+
const isUpgradeToWarning = !isSuppressed && !existing.hasWarningIssued && info.isWarning;
|
|
167
|
+
if (!isSuppressed) {
|
|
168
|
+
existing.hasWarningIssued = existing.hasWarningIssued || info.isWarning;
|
|
147
169
|
}
|
|
148
|
-
existing.isWarning = existing.isWarning || info.isWarning;
|
|
149
170
|
existing.isCancelled = isCancelled;
|
|
150
171
|
existing.lastUpdate = new Date();
|
|
151
|
-
existing.previousInfo = info;
|
|
152
172
|
return {
|
|
153
173
|
isNew: false,
|
|
154
174
|
isDuplicate: false,
|
|
155
175
|
isCancelled,
|
|
176
|
+
isSuppressed,
|
|
177
|
+
isUpgradeToWarning,
|
|
156
178
|
activeCount: this.getActiveCount(),
|
|
157
|
-
diff,
|
|
179
|
+
diff: isSuppressed ? undefined : diff,
|
|
158
180
|
previousInfo,
|
|
159
181
|
colorIndex: existing.colorIndex,
|
|
160
182
|
};
|
|
161
183
|
}
|
|
162
184
|
// 新規イベント
|
|
163
185
|
const colorIndex = this.nextColorIndex();
|
|
186
|
+
const byType = new Map();
|
|
187
|
+
byType.set(headType, { lastSerial: serial ?? 0, previousInfo: info });
|
|
164
188
|
this.events.set(eventId, {
|
|
165
189
|
eventId,
|
|
166
|
-
|
|
167
|
-
|
|
190
|
+
byType,
|
|
191
|
+
hasSeen45: headType === "VXSE45",
|
|
192
|
+
hasWarningIssued: info.isWarning,
|
|
168
193
|
isCancelled,
|
|
169
194
|
isFinalized: false,
|
|
170
195
|
lastUpdate: new Date(),
|
|
171
|
-
previousInfo: info,
|
|
172
196
|
colorIndex,
|
|
173
197
|
});
|
|
174
198
|
return {
|
|
175
199
|
isNew: true,
|
|
176
200
|
isDuplicate: false,
|
|
177
201
|
isCancelled,
|
|
202
|
+
isSuppressed: false,
|
|
203
|
+
isUpgradeToWarning: false,
|
|
178
204
|
activeCount: this.getActiveCount(),
|
|
179
205
|
colorIndex,
|
|
180
206
|
};
|
|
@@ -76,7 +76,7 @@ async function startMonitor(config, pipelineController) {
|
|
|
76
76
|
}
|
|
77
77
|
log.info(chalk_1.default.green("リアルタイム受信中..."));
|
|
78
78
|
if (isFirstConnection) {
|
|
79
|
-
log.info(chalk_1.default.gray("
|
|
79
|
+
log.info(chalk_1.default.gray("commands (短縮: cmds) でコマンド一覧を表示"));
|
|
80
80
|
isFirstConnection = false;
|
|
81
81
|
}
|
|
82
82
|
(0, repl_coordinator_1.updateReplConnectionState)(replHandler, true);
|
|
@@ -173,10 +173,12 @@ class Notifier {
|
|
|
173
173
|
notifyEew(info, result) {
|
|
174
174
|
if (!this.settings.eew)
|
|
175
175
|
return;
|
|
176
|
-
//
|
|
177
|
-
|
|
176
|
+
// 抑制された報は通知しない
|
|
177
|
+
if (result.isSuppressed)
|
|
178
|
+
return;
|
|
179
|
+
// 通知条件: 第1報 / 警報昇格 / 取消報 / 最終報
|
|
178
180
|
const isFinal = info.nextAdvisory != null;
|
|
179
|
-
if (!result.isNew && !isUpgradeToWarning && !result.isCancelled && !isFinal) {
|
|
181
|
+
if (!result.isNew && !result.isUpgradeToWarning && !result.isCancelled && !isFinal) {
|
|
180
182
|
return;
|
|
181
183
|
}
|
|
182
184
|
if (result.isCancelled) {
|