diff --git a/viewer/index.html b/viewer/index.html
index a40b905..57f8fd3 100644
--- a/viewer/index.html
+++ b/viewer/index.html
@@ -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}`;
}