[fix](trx-frontend-http): buffer decode messages until map-data plugins are ready
Eagerly load map-data plugins (AIS, APRS, VDES, HF-APRS) on startup and buffer any decode history or live SSE messages that arrive before plugin handlers register. Each plugin drains its pending buffer on init. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -563,6 +563,41 @@ const decodeHistoryTextDecoder = typeof TextDecoder === "function" ? new TextDec
|
|||||||
let decodeHistoryReplayActive = false;
|
let decodeHistoryReplayActive = false;
|
||||||
let decodeMapSyncPending = false;
|
let decodeMapSyncPending = false;
|
||||||
|
|
||||||
|
// --- Pending decode data buffers ---
|
||||||
|
// Map-data plugins (ais.js, aprs.js, vdes.js, hf-aprs.js) are loaded eagerly
|
||||||
|
// but dynamically-inserted scripts have no guaranteed execution order. If
|
||||||
|
// decode history or live SSE messages arrive before the plugin handlers are
|
||||||
|
// registered, buffer them here and let each plugin drain on init.
|
||||||
|
const _pendingDecodeHistory = {};
|
||||||
|
const _pendingDecodeLive = {};
|
||||||
|
|
||||||
|
window._trxDrainPendingDecode = function(kind) {
|
||||||
|
const historyKey = {
|
||||||
|
ais: "restoreAisHistory",
|
||||||
|
vdes: "restoreVdesHistory",
|
||||||
|
aprs: "restoreAprsHistory",
|
||||||
|
hf_aprs: "restoreHfAprsHistory",
|
||||||
|
}[kind];
|
||||||
|
if (historyKey && _pendingDecodeHistory[kind] && window[historyKey]) {
|
||||||
|
const msgs = _pendingDecodeHistory[kind];
|
||||||
|
delete _pendingDecodeHistory[kind];
|
||||||
|
window[historyKey](msgs);
|
||||||
|
}
|
||||||
|
const liveKey = {
|
||||||
|
ais: "onServerAis",
|
||||||
|
vdes: "onServerVdes",
|
||||||
|
aprs: "onServerAprs",
|
||||||
|
hf_aprs: "onServerHfAprs",
|
||||||
|
}[kind];
|
||||||
|
if (liveKey && _pendingDecodeLive[kind] && window[liveKey]) {
|
||||||
|
const msgs = _pendingDecodeLive[kind];
|
||||||
|
delete _pendingDecodeLive[kind];
|
||||||
|
for (const msg of msgs) {
|
||||||
|
try { window[liveKey](msg); } catch (_) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function markDecodeMapSyncPending() {
|
function markDecodeMapSyncPending() {
|
||||||
decodeMapSyncPending = true;
|
decodeMapSyncPending = true;
|
||||||
}
|
}
|
||||||
@@ -5920,10 +5955,10 @@ function updateDecodeStatus(text) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
function dispatchDecodeMessage(msg, skipStats) {
|
function dispatchDecodeMessage(msg, skipStats) {
|
||||||
if (msg.type === "ais" && window.onServerAis) window.onServerAis(msg);
|
if (msg.type === "ais") { if (window.onServerAis) window.onServerAis(msg); else (_pendingDecodeLive.ais = _pendingDecodeLive.ais || []).push(msg); }
|
||||||
if (msg.type === "vdes" && window.onServerVdes) window.onServerVdes(msg);
|
if (msg.type === "vdes") { if (window.onServerVdes) window.onServerVdes(msg); else (_pendingDecodeLive.vdes = _pendingDecodeLive.vdes || []).push(msg); }
|
||||||
if (msg.type === "aprs" && window.onServerAprs) window.onServerAprs(msg);
|
if (msg.type === "aprs") { if (window.onServerAprs) window.onServerAprs(msg); else (_pendingDecodeLive.aprs = _pendingDecodeLive.aprs || []).push(msg); }
|
||||||
if (msg.type === "hf_aprs" && window.onServerHfAprs) window.onServerHfAprs(msg);
|
if (msg.type === "hf_aprs") { if (window.onServerHfAprs) window.onServerHfAprs(msg); else (_pendingDecodeLive.hf_aprs = _pendingDecodeLive.hf_aprs || []).push(msg); }
|
||||||
if (msg.type === "cw" && window.onServerCw) window.onServerCw(msg);
|
if (msg.type === "cw" && window.onServerCw) window.onServerCw(msg);
|
||||||
if (msg.type === "ft8" && window.onServerFt8) window.onServerFt8(msg);
|
if (msg.type === "ft8" && window.onServerFt8) window.onServerFt8(msg);
|
||||||
if (msg.type === "ft4" && window.onServerFt4) window.onServerFt4(msg);
|
if (msg.type === "ft4" && window.onServerFt4) window.onServerFt4(msg);
|
||||||
@@ -6036,20 +6071,24 @@ function restoreDecodeHistoryGroup(kind, messages) {
|
|||||||
}
|
}
|
||||||
window.trx.map?.scheduleStatsRender();
|
window.trx.map?.scheduleStatsRender();
|
||||||
}
|
}
|
||||||
if (kind === "ais" && window.restoreAisHistory) {
|
if (kind === "ais") {
|
||||||
window.restoreAisHistory(messages);
|
if (window.restoreAisHistory) { window.restoreAisHistory(messages); }
|
||||||
|
else { _pendingDecodeHistory.ais = (_pendingDecodeHistory.ais || []).concat(messages); }
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (kind === "vdes" && window.restoreVdesHistory) {
|
if (kind === "vdes") {
|
||||||
window.restoreVdesHistory(messages);
|
if (window.restoreVdesHistory) { window.restoreVdesHistory(messages); }
|
||||||
|
else { _pendingDecodeHistory.vdes = (_pendingDecodeHistory.vdes || []).concat(messages); }
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (kind === "aprs" && window.restoreAprsHistory) {
|
if (kind === "aprs") {
|
||||||
window.restoreAprsHistory(messages);
|
if (window.restoreAprsHistory) { window.restoreAprsHistory(messages); }
|
||||||
|
else { _pendingDecodeHistory.aprs = (_pendingDecodeHistory.aprs || []).concat(messages); }
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (kind === "hf_aprs" && window.restoreHfAprsHistory) {
|
if (kind === "hf_aprs") {
|
||||||
window.restoreHfAprsHistory(messages);
|
if (window.restoreHfAprsHistory) { window.restoreHfAprsHistory(messages); }
|
||||||
|
else { _pendingDecodeHistory.hf_aprs = (_pendingDecodeHistory.hf_aprs || []).concat(messages); }
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (kind === "cw" && window.restoreCwHistory) {
|
if (kind === "cw" && window.restoreCwHistory) {
|
||||||
@@ -6083,6 +6122,9 @@ function connectDecode() {
|
|||||||
terminateDecodeHistoryWorker();
|
terminateDecodeHistoryWorker();
|
||||||
decodeHistoryReplayActive = false;
|
decodeHistoryReplayActive = false;
|
||||||
decodeMapSyncPending = false;
|
decodeMapSyncPending = false;
|
||||||
|
// Clear any pending buffers from a previous connection cycle.
|
||||||
|
for (const k in _pendingDecodeHistory) delete _pendingDecodeHistory[k];
|
||||||
|
for (const k in _pendingDecodeLive) delete _pendingDecodeLive[k];
|
||||||
if (window.resetAisHistoryView) window.resetAisHistoryView();
|
if (window.resetAisHistoryView) window.resetAisHistoryView();
|
||||||
if (window.resetVdesHistoryView) window.resetVdesHistoryView();
|
if (window.resetVdesHistoryView) window.resetVdesHistoryView();
|
||||||
if (window.resetAprsHistoryView) window.resetAprsHistoryView();
|
if (window.resetAprsHistoryView) window.resetAprsHistoryView();
|
||||||
|
|||||||
@@ -1620,6 +1620,7 @@
|
|||||||
(function() {
|
(function() {
|
||||||
var pluginScripts = {
|
var pluginScripts = {
|
||||||
'digital-modes': ['/ft8.js', '/ft4.js', '/ft2.js', '/wspr.js', '/cw.js', '/background-decode.js', '/sat.js', '/wefax.js'],
|
'digital-modes': ['/ft8.js', '/ft4.js', '/ft2.js', '/wspr.js', '/cw.js', '/background-decode.js', '/sat.js', '/wefax.js'],
|
||||||
|
'map-data': ['/map-core.js', '/ais.js', '/vdes.js', '/aprs.js', '/hf-aprs.js'],
|
||||||
'map': ['/map-core.js', '/leaflet-ais-tracksymbol.js', '/ais.js', '/vdes.js', '/aprs.js', '/hf-aprs.js', '/sat.js', '/sat-scheduler.js'],
|
'map': ['/map-core.js', '/leaflet-ais-tracksymbol.js', '/ais.js', '/vdes.js', '/aprs.js', '/hf-aprs.js', '/sat.js', '/sat-scheduler.js'],
|
||||||
'statistics': ['/map-core.js'],
|
'statistics': ['/map-core.js'],
|
||||||
'bookmarks': ['/bookmarks.js'],
|
'bookmarks': ['/bookmarks.js'],
|
||||||
@@ -1639,8 +1640,12 @@
|
|||||||
document.body.appendChild(s);
|
document.body.appendChild(s);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Load core plugins immediately (needed on main tab)
|
// Load core plugins immediately (needed on main tab).
|
||||||
['digital-modes', 'bookmarks', 'settings'].forEach(loadPlugins);
|
// 'map-data' loads map-core.js + data handlers (ais/aprs/vdes/hf-aprs) eagerly
|
||||||
|
// so decode history replay can populate map data structures before the map
|
||||||
|
// tab is opened. The 'loaded' Set prevents double-loading when the map tab
|
||||||
|
// is later activated.
|
||||||
|
['digital-modes', 'map-data', 'bookmarks', 'settings'].forEach(loadPlugins);
|
||||||
// Load others on tab switch
|
// Load others on tab switch
|
||||||
document.addEventListener('click', function(e) {
|
document.addEventListener('click', function(e) {
|
||||||
var tab = e.target.closest('[data-tab]');
|
var tab = e.target.closest('[data-tab]');
|
||||||
|
|||||||
@@ -400,3 +400,4 @@ window.onServerAis = function(msg) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
updateAisSummary();
|
updateAisSummary();
|
||||||
|
if (window._trxDrainPendingDecode) window._trxDrainPendingDecode("ais");
|
||||||
|
|||||||
@@ -491,3 +491,4 @@ window.onServerAprs = function(pkt) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderAprsHistory();
|
renderAprsHistory();
|
||||||
|
if (window._trxDrainPendingDecode) window._trxDrainPendingDecode("aprs");
|
||||||
|
|||||||
@@ -437,3 +437,4 @@ window.onServerHfAprs = function(pkt) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderHfAprsHistory();
|
renderHfAprsHistory();
|
||||||
|
if (window._trxDrainPendingDecode) window._trxDrainPendingDecode("hf_aprs");
|
||||||
|
|||||||
@@ -345,3 +345,4 @@ window.pruneVdesHistoryView = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
updateVdesSummary();
|
updateVdesSummary();
|
||||||
|
if (window._trxDrainPendingDecode) window._trxDrainPendingDecode("vdes");
|
||||||
|
|||||||
Reference in New Issue
Block a user