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}`; }