fix(viewer): polylines + batched T3 load
- loadSortieData: call applyTrailAndCursor() after sortie load so map polylines appear when allPoints already populated from datebar - loadDiveData: split into Phase1 (track only, batched by 4) + Phase2 (series + sub, batched by 4) — map draws as soon as tracks load - loadShipSession split into fetchShipTrack + fetchShipSeries helpers - T3: filter ship sessions by date (sess.start.slice(0,10) === filterDate) - Pass date param from loadDate to loadDiveData Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -783,7 +783,7 @@ async function loadDate(date) {
|
||||
|
||||
for (const { mission, dives } of missionDives) {
|
||||
for (const dive of dives) {
|
||||
allFetches.push(loadDiveData(mission.id, dive.id));
|
||||
allFetches.push(loadDiveData(mission.id, dive.id, date));
|
||||
totalShip += dive.ship_session_count || 0;
|
||||
totalSub += dive.sub_session_count || 0;
|
||||
}
|
||||
@@ -848,71 +848,75 @@ async function loadDate(date) {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadDiveData(missionId, diveId) {
|
||||
async function loadDiveData(missionId, diveId, filterDate) {
|
||||
try {
|
||||
const sResp = await fetch(`${API}/api/dives/${missionId}/${diveId}/sessions`);
|
||||
const sessions = await sResp.json();
|
||||
const sessionFetches = [];
|
||||
|
||||
// Ship sessions
|
||||
if (sessions.ship) {
|
||||
sessions.ship.forEach(sess => {
|
||||
sessionFetches.push(loadShipSession(missionId, diveId, sess.id));
|
||||
});
|
||||
}
|
||||
// Sub sessions
|
||||
if (sessions.sub) {
|
||||
sessions.sub.forEach(sess => {
|
||||
sessionFetches.push(loadSubSession(missionId, diveId, sess.id));
|
||||
});
|
||||
}
|
||||
await Promise.all(sessionFetches);
|
||||
// T3 FIX: filter ship sessions by date if provided (session.start = "YYYY-MM-DD_HH-MM-SS")
|
||||
const shipSessions = (sessions.ship || []).filter(sess => {
|
||||
if (!filterDate) return true;
|
||||
// sess.start: "2026-04-17_07-43-23" → compare first 10 chars "2026-04-17" to filterDate
|
||||
return !sess.start || sess.start.slice(0, 10) === filterDate;
|
||||
});
|
||||
|
||||
// Phase 1: track-only fetches (needed for map polylines) — batch by 4
|
||||
const trackFetches = shipSessions.map(sess =>
|
||||
fetchShipTrack(missionId, diveId, sess.id)
|
||||
);
|
||||
await _batchedAll(trackFetches, 4);
|
||||
|
||||
// Phase 2: series + sub sessions in background (charts data)
|
||||
const seriesFetches = [
|
||||
...shipSessions.map(sess => fetchShipSeries(missionId, diveId, sess.id)),
|
||||
...(sessions.sub || []).map(sess => loadSubSession(missionId, diveId, sess.id)),
|
||||
];
|
||||
await _batchedAll(seriesFetches, 4);
|
||||
} catch(e) { console.warn('loadDiveData error', diveId, e); }
|
||||
}
|
||||
|
||||
async function loadShipSession(missionId, diveId, sessionId) {
|
||||
// Parallel fetch: track + series with 20s timeout each — neither blocks the other
|
||||
const [trackResult, seriesResult] = await Promise.allSettled([
|
||||
fetch(`${API}/api/ship/${missionId}/${diveId}/${sessionId}/track`,
|
||||
{ signal: AbortSignal.timeout(20000) }),
|
||||
fetch(`${API}/api/ship/${missionId}/${diveId}/${sessionId}/series`,
|
||||
{ signal: AbortSignal.timeout(20000) }),
|
||||
]);
|
||||
if (trackResult.status === 'fulfilled' && trackResult.value.ok) {
|
||||
try {
|
||||
const d = await trackResult.value.json();
|
||||
const pts = (d.points||[]).map(p => ({
|
||||
t_ms: isoToMs(p.t), lat: p.lat, lon: p.lon,
|
||||
heading: p.heading || null, source: sessionId
|
||||
}));
|
||||
allPoints.push(...pts);
|
||||
} catch(e) { console.warn('loadShipSession track json error', sessionId, e); }
|
||||
} else {
|
||||
console.warn('loadShipSession track timeout/fail', sessionId,
|
||||
trackResult.status === 'rejected' ? trackResult.reason.name : trackResult.value?.status);
|
||||
}
|
||||
if (seriesResult.status === 'fulfilled' && seriesResult.value.ok) {
|
||||
try {
|
||||
const d = await seriesResult.value.json();
|
||||
const motorKeys = Object.keys(d).filter(k => /^M\d+$/.test(k));
|
||||
motorKeys.forEach((k, i) => {
|
||||
const pts = d[k];
|
||||
if (!pts || !pts.length) return;
|
||||
pwmUsvTraces.push({
|
||||
x: pts.map(p => new Date(isoToMs(p.t))),
|
||||
y: pts.map(p => p.v),
|
||||
name: k,
|
||||
type: 'scatter', mode: 'lines',
|
||||
line: { color: COLORS[i % COLORS.length], width: 1 },
|
||||
});
|
||||
});
|
||||
} catch(e) { console.warn('loadShipSession series json error', sessionId, e); }
|
||||
} else {
|
||||
console.warn('loadShipSession series timeout/fail', sessionId,
|
||||
seriesResult.status === 'rejected' ? seriesResult.reason.name : seriesResult.value?.status);
|
||||
// Run an array of promise-factories in batches of size n
|
||||
async function _batchedAll(fns, n) {
|
||||
for (let i = 0; i < fns.length; i += n) {
|
||||
await Promise.all(fns.slice(i, i + n));
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchShipTrack(missionId, diveId, sessionId) {
|
||||
try {
|
||||
const resp = await fetch(`${API}/api/ship/${missionId}/${diveId}/${sessionId}/track`,
|
||||
{ signal: AbortSignal.timeout(20000) });
|
||||
if (!resp.ok) { console.warn('fetchShipTrack fail', sessionId, resp.status); return; }
|
||||
const d = await resp.json();
|
||||
const pts = (d.points||[]).map(p => ({
|
||||
t_ms: isoToMs(p.t), lat: p.lat, lon: p.lon,
|
||||
heading: p.heading || null, source: sessionId
|
||||
}));
|
||||
allPoints.push(...pts);
|
||||
} catch(e) { console.warn('fetchShipTrack error', sessionId, e.name); }
|
||||
}
|
||||
|
||||
async function fetchShipSeries(missionId, diveId, sessionId) {
|
||||
try {
|
||||
const resp = await fetch(`${API}/api/ship/${missionId}/${diveId}/${sessionId}/series`,
|
||||
{ signal: AbortSignal.timeout(20000) });
|
||||
if (!resp.ok) return;
|
||||
const d = await resp.json();
|
||||
const motorKeys = Object.keys(d).filter(k => /^M\d+$/.test(k));
|
||||
motorKeys.forEach((k, i) => {
|
||||
const pts = d[k];
|
||||
if (!pts || !pts.length) return;
|
||||
pwmUsvTraces.push({
|
||||
x: pts.map(p => new Date(isoToMs(p.t))),
|
||||
y: pts.map(p => p.v),
|
||||
name: k, type: 'scatter', mode: 'lines',
|
||||
line: { color: COLORS[i % COLORS.length], width: 1 },
|
||||
});
|
||||
});
|
||||
} catch(e) { console.warn('fetchShipSeries error', sessionId, e.name); }
|
||||
}
|
||||
|
||||
|
||||
async function loadSubSession(missionId, diveId, sessionId) {
|
||||
// Both usbl_track and series can hang — use parallel with timeout, non-blocking
|
||||
const [usblResult, seriesResult] = await Promise.allSettled([
|
||||
@@ -1287,6 +1291,8 @@ async function loadSortieData(sortieId) {
|
||||
prog.textContent = 'Chargement AUV…';
|
||||
await loadAuvTabs(sortieId);
|
||||
prog.textContent = `${sortieId} chargé`;
|
||||
// POLYLINE FIX: ensure map trail is drawn if allPoints already loaded from datebar
|
||||
if (allPoints.length > 0) applyTrailAndCursor();
|
||||
} catch(e) {
|
||||
prog.textContent = `Erreur: ${e.message}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user