RELEASE V5.0.0 : Voici les modifications apportées :

-> IMPLÉMENTATION DONNÉES GTFS / GTFS-RT / SIRI LTE SNCF
-> RESTRUCTURATION DE L'APP.

FONCTIONNALITÉS EN COURS DE DÉVELOPPEMENT :

-> REFONTE DU DESIGN DES AFFICHEURS CLASSIQUES
-> AMÉLIORATION DES
This commit is contained in:
mathis committed 2026-08-30 02:26:08 +02:00
1 parent 78e0a0ef7b
commit 1729ab4831
1 file changed
+59 -18
+59 -18
View File
@@ -20,6 +20,20 @@ let siriLiteCache = {
updates: new Map() updates: new Map()
}; };
// Helper fetch avec timeout pour ne jamais bloquer l'affichage en cas de réseau lent ou de blocage
async function fetchWithTimeout(url, options = {}, timeoutMs = 7000) {
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeoutMs);
try {
const res = await fetch(url, { ...options, signal: controller.signal });
clearTimeout(id);
return res;
} catch (err) {
clearTimeout(id);
throw err;
}
}
function cleanNum(n) { function cleanNum(n) {
if (!n) return ''; if (!n) return '';
const m = String(n).match(/\d+/); const m = String(n).match(/\d+/);
@@ -39,8 +53,8 @@ async function fetchSiriLiteData() {
const updatesMap = new Map(); const updatesMap = new Map();
try { try {
const res = await fetch(SNCF_SIRI_LITE_TIMETABLE); const res = await fetchWithTimeout(SNCF_SIRI_LITE_TIMETABLE, {}, 8000);
if (res.ok) { if (res && res.ok) {
const xml = await res.text(); const xml = await res.text();
const journeys = xml.split('<EstimatedVehicleJourney>'); const journeys = xml.split('<EstimatedVehicleJourney>');
@@ -123,7 +137,7 @@ async function fetchSiriLiteData() {
updates: updatesMap updates: updatesMap
}; };
} catch (err) { } catch (err) {
console.warn('[sncfApi] Notice: SIRI-Lite refresh error:', err); console.warn('[sncfApi] Notice: SIRI-Lite refresh error/timeout:', err);
} }
return siriLiteCache; return siriLiteCache;
@@ -143,12 +157,12 @@ async function fetchGtfsRealtimeData() {
try { try {
const [rtRes, alertsRes] = await Promise.allSettled([ const [rtRes, alertsRes] = await Promise.allSettled([
fetch(SNCF_GTFS_RT_TRIP_UPDATES), fetchWithTimeout(SNCF_GTFS_RT_TRIP_UPDATES, {}, 6000),
fetch(SNCF_GTFS_RT_SERVICE_ALERTS) fetchWithTimeout(SNCF_GTFS_RT_SERVICE_ALERTS, {}, 6000)
]); ]);
// 1. Décodage Trip Updates (Retards, Suppressions) // 1. Décodage Trip Updates (Retards, Suppressions)
if (rtRes.status === 'fulfilled' && rtRes.value.ok) { if (rtRes.status === 'fulfilled' && rtRes.value && rtRes.value.ok) {
const buffer = await rtRes.value.arrayBuffer(); const buffer = await rtRes.value.arrayBuffer();
const feed = transit_realtime.FeedMessage.decode(new Uint8Array(buffer)); const feed = transit_realtime.FeedMessage.decode(new Uint8Array(buffer));
@@ -188,7 +202,7 @@ async function fetchGtfsRealtimeData() {
} }
// 2. Décodage Service Alerts (Causes de retards & perturbations) // 2. Décodage Service Alerts (Causes de retards & perturbations)
if (alertsRes.status === 'fulfilled' && alertsRes.value.ok) { if (alertsRes.status === 'fulfilled' && alertsRes.value && alertsRes.value.ok) {
const buffer = await alertsRes.value.arrayBuffer(); const buffer = await alertsRes.value.arrayBuffer();
const alertFeed = transit_realtime.FeedMessage.decode(new Uint8Array(buffer)); const alertFeed = transit_realtime.FeedMessage.decode(new Uint8Array(buffer));
@@ -294,13 +308,29 @@ async function fetchGtfsStationTimetable(uic, mode = 'departs') {
if (!uic) return null; if (!uic) return null;
try { try {
const publicUrl = (typeof process !== 'undefined' && process.env && process.env.PUBLIC_URL) ? process.env.PUBLIC_URL : '';
const stationUrl = `${publicUrl}/data/stations/${uic}.json`;
// Chargement parallèle tolérant aux pannes réseau / CORS
const [jsonRes, rtData, siriData] = await Promise.all([ const [jsonRes, rtData, siriData] = await Promise.all([
fetch(`/data/stations/${uic}.json`), fetch(stationUrl).catch(err => {
fetchGtfsRealtimeData(), console.warn(`[sncfApi] Notice: fetch station ${uic} error:`, err);
fetchSiriLiteData() return null;
}),
fetchGtfsRealtimeData().catch(err => {
console.warn('[sncfApi] Notice: GTFS-RT error:', err);
return { updates: new Map(), alerts: [] };
}),
fetchSiriLiteData().catch(err => {
console.warn('[sncfApi] Notice: SIRI-Lite error:', err);
return { platforms: new Map(), updates: new Map() };
})
]); ]);
if (!jsonRes.ok) return null; if (!jsonRes || !jsonRes.ok) {
console.warn(`[sncfApi] Fichier de gare introuvable : ${stationUrl} (statut : ${jsonRes ? jsonRes.status : 'réseau bloqué'})`);
return null;
}
const data = await jsonRes.json(); const data = await jsonRes.json();
const rawList = mode === 'arrivees' ? data.arrivees : data.departs; const rawList = mode === 'arrivees' ? data.arrivees : data.departs;
@@ -311,19 +341,19 @@ async function fetchGtfsStationTimetable(uic, mode = 'departs') {
const currentDay = now.getDay(); // 0 = Dimanche, 1 = Lundi, ..., 6 = Samedi const currentDay = now.getDay(); // 0 = Dimanche, 1 = Lundi, ..., 6 = Samedi
const currentYYYYMMDD = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}`; const currentYYYYMMDD = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}`;
// Filtrage complet de circulation GTFS // 1. Filtrage précis par date GTFS (si la date du jour est dans la plage de validité)
const circulatingList = rawList.filter(t => { let circulatingList = rawList.filter(t => {
// 1. Si la date courante figure dans les dates explicites // Si la date courante figure dans les dates explicites
if (Array.isArray(t.dates) && t.dates.length > 0 && t.dates.includes(currentYYYYMMDD)) { if (Array.isArray(t.dates) && t.dates.length > 0 && t.dates.includes(currentYYYYMMDD)) {
return true; return true;
} }
// 2. Si le jour de la semaine est actif dans la période de validité // Si le jour de la semaine est actif dans la période de validité
if (Array.isArray(t.days) && t.days.length > 0 && t.days.includes(currentDay)) { if (Array.isArray(t.days) && t.days.length > 0 && t.days.includes(currentDay)) {
if ((!t.start || currentYYYYMMDD >= t.start) && (!t.end || currentYYYYMMDD <= t.end)) { if ((!t.start || currentYYYYMMDD >= t.start) && (!t.end || currentYYYYMMDD <= t.end)) {
return true; return true;
} }
} }
// 3. Train de nuit parti la veille et arrivant aujourd'hui (heures >= 24h) // Train de nuit parti la veille et arrivant aujourd'hui (heures >= 24h)
if (Array.isArray(t.days) && t.days.length > 0) { if (Array.isArray(t.days) && t.days.length > 0) {
const prevDay = (currentDay + 6) % 7; const prevDay = (currentDay + 6) % 7;
const rawTime = mode === 'arrivees' ? (t.arr || t.dep) : (t.dep || t.arr); const rawTime = mode === 'arrivees' ? (t.arr || t.dep) : (t.dep || t.arr);
@@ -333,13 +363,24 @@ async function fetchGtfsStationTimetable(uic, mode = 'departs') {
return true; return true;
} }
} }
// 4. Si aucune règle n'est renseignée // Si aucune règle n'est renseignée
if (!t.dates?.length && !t.days?.length) return true; if (!t.dates?.length && !t.days?.length) return true;
return false; return false;
}); });
// 2. Fallback robuste : si la date actuelle est hors calendrier GTFS, filtrer par jour de la semaine
if (circulatingList.length === 0) { if (circulatingList.length === 0) {
return []; circulatingList = rawList.filter(t => {
if (Array.isArray(t.days) && t.days.length > 0) {
return t.days.includes(currentDay);
}
return true;
});
}
// 3. Fallback complet : si toujours vide, renvoyer la grille quotidienne complète
if (circulatingList.length === 0) {
circulatingList = rawList;
} }
// Helper de résolution ultra-robuste de quai par couple (train, gare) // Helper de résolution ultra-robuste de quai par couple (train, gare)