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

-> Implémentation des afficheurs "Sécurité"; "Informations" et "Correspondances" pour les afficheurs AFL

FONCTIONNALITÉS EN COURS DE DÉVELOPPEMENT :

-> REFONTE DU DESIGN DES AFFICHEURS CLASSIQUES
This commit is contained in:
mathis committed 2026-08-30 21:16:38 +02:00
1 parent 1c83953552
commit 4f3a34b101
24 files changed
+3517 -412

No files matched your search

+106 -54
View File
@@ -341,49 +341,65 @@ async function fetchGtfsStationTimetable(uic, mode = 'departs') {
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')}`;
// 1. Filtrage précis par date GTFS (si la date du jour est dans la plage de validité)
let circulatingList = rawList.filter(t => {
// Si la date courante figure dans les dates explicites
if (Array.isArray(t.dates) && t.dates.length > 0 && t.dates.includes(currentYYYYMMDD)) {
return true;
const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000);
const tomorrowDay = tomorrow.getDay();
const tomorrowYYYYMMDD = `${tomorrow.getFullYear()}${String(tomorrow.getMonth() + 1).padStart(2, '0')}${String(tomorrow.getDate()).padStart(2, '0')}`;
// Helper de vérification des règles de circulation calendaires
const circulatesOnDate = (t, targetDay, targetYYYYMMDD, targetDate) => {
const pad = (n) => String(n).padStart(2, '0');
const targetDateHyphen = `${targetDate.getFullYear()}-${pad(targetDate.getMonth() + 1)}-${pad(targetDate.getDate())}`;
// 1. Base GTFS : si t.dates existe (tableau précis de dates YYYYMMDD), c'est la source de vérité absolue
if (Array.isArray(t.dates) && t.dates.length > 0) {
return t.dates.includes(targetYYYYMMDD) || t.dates.includes(targetDateHyphen);
}
// 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 ((!t.start || currentYYYYMMDD >= t.start) && (!t.end || currentYYYYMMDD <= t.end)) {
return true;
// 2. Base Supabase : jours personnalisés
const customDaysRaw = t.jours_personnalises;
if (customDaysRaw) {
let customDays = [];
if (Array.isArray(customDaysRaw)) {
customDays = customDaysRaw;
} else if (typeof customDaysRaw === 'string') {
try { customDays = JSON.parse(customDaysRaw); } catch (e) {}
}
if (Array.isArray(customDays) && customDays.length > 0) {
const match = customDays.find(item => item.date === targetDateHyphen || item.date === targetYYYYMMDD);
if (match) return Boolean(match.circule);
}
}
// Train de nuit parti la veille et arrivant aujourd'hui (heures >= 24h)
// 3. Colonnes circule_lundi, circule_mardi...
const dayNames = ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'];
const colName = `circule_${dayNames[targetDay]}`;
if (typeof t[colName] !== 'undefined' && t[colName] !== null) {
const db = t.date_debut ? String(t.date_debut).slice(0, 10) : null;
const df = t.date_fin ? String(t.date_fin).slice(0, 10) : null;
if (db && targetDateHyphen < db) return false;
if (df && targetDateHyphen > df) return false;
return Boolean(t[colName]);
}
// 4. Jours GTFS (fallback si t.dates n'était pas fourni)
if (Array.isArray(t.days) && t.days.length > 0) {
const prevDay = (currentDay + 6) % 7;
const rawTime = mode === 'arrivees' ? (t.arr || t.dep) : (t.dep || t.arr);
const m = String(rawTime).match(/^(\d{1,2})/);
const rawHour = m ? parseInt(m[1], 10) : 0;
if (rawHour >= 24 && t.days.includes(prevDay)) {
return true;
}
}
// Si aucune règle n'est renseignée
if (!t.dates?.length && !t.days?.length) return true;
return false;
});
// 2. Fallback robuste : si la date actuelle est hors calendrier GTFS, filtrer par jour de la semaine
if (circulatingList.length === 0) {
circulatingList = rawList.filter(t => {
if (Array.isArray(t.days) && t.days.length > 0) {
return t.days.includes(currentDay);
}
if (!t.days.includes(targetDay)) return false;
if (t.start && targetYYYYMMDD < String(t.start).replace(/-/g, '')) return false;
if (t.end && targetYYYYMMDD > String(t.end).replace(/-/g, '')) return false;
return true;
});
}
}
// 3. Fallback complet : si toujours vide, renvoyer la grille quotidienne complète
if (circulatingList.length === 0) {
circulatingList = rawList;
}
// 5. Date unique fixée (date_depart, date, etc.)
const singleDate = t.date_depart || t.date || t.service_date;
if (singleDate) {
const sStr = String(singleDate).slice(0, 10);
return sStr === targetDateHyphen || sStr.replace(/-/g, '') === targetYYYYMMDD;
}
// Helper de résolution ultra-robuste de quai par couple (train, gare)
return true;
};
// Helper de résolution de quai
const lookupPlatform = (trainNum, tripId, stationUic, stationName) => {
const cNum = cleanNum(trainNum);
const cName = stationName ? formatStationName(stationName).toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/[-_/.,()'&]/g, ' ').replace(/\s+/g, ' ').trim() : '';
@@ -402,19 +418,18 @@ async function fetchGtfsStationTimetable(uic, mode = 'departs') {
);
};
// Enrichir chaque train circulant avec le GTFS-RT et le SIRI Lite
const enrichedTrains = circulatingList.map((t, idx) => {
// Helper d'enrichissement d'un train
const mapTrain = (t, idx, isTomorrow = false) => {
const rawTime = mode === 'arrivees' ? (t.arr || t.dep) : (t.dep || t.arr);
const timeHM = parseTimeHM(rawTime) || 0;
// Recherche de mise à jour GTFS-RT ou SIRI-Lite (par trip_id ou numéro de train)
const rt = rtData.updates.get(t.trip_id) || (t.num ? rtData.updates.get(t.num) : null) || (cleanNum(t.num) ? rtData.updates.get(cleanNum(t.num)) : null);
const siri = siriData.updates.get(t.num) || (cleanNum(t.num) ? siriData.updates.get(cleanNum(t.num)) : null) || (t.trip_id ? siriData.updates.get(t.trip_id) : null);
// Recherche de mise à jour GTFS-RT ou SIRI-Lite (valide pour aujourd'hui)
const rt = !isTomorrow ? (rtData.updates.get(t.trip_id) || (t.num ? rtData.updates.get(t.num) : null) || (cleanNum(t.num) ? rtData.updates.get(cleanNum(t.num)) : null)) : null;
const siri = !isTomorrow ? (siriData.updates.get(t.num) || (cleanNum(t.num) ? siriData.updates.get(cleanNum(t.num)) : null) || (t.trip_id ? siriData.updates.get(t.trip_id) : null)) : null;
const isCanceled = (rt && rt.isCanceled) || (siri && siri.isCanceled) || false;
const delayMinutes = (rt && rt.delayMinutes > 0) ? rt.delayMinutes : (siri && siri.delayMinutes > 0 ? siri.delayMinutes : 0);
// Extraction du quai/voie réel STRICTEMENT pour cette gare (uic)
const currentStationName = mode === 'arrivees' ? t.dest : t.orig;
let realVoie = lookupPlatform(t.num, t.trip_id, uic, currentStationName);
if (!realVoie && t.quai && typeof t.quai === 'string') {
@@ -431,13 +446,11 @@ async function fetchGtfsStationTimetable(uic, mode = 'departs') {
etat = 'retarde';
}
// Causes de retard issues des alertes temps réel
let cause = '';
if (delayMinutes > 0 && rtData.alerts.length > 0) {
if (!isTomorrow && delayMinutes > 0 && rtData.alerts.length > 0) {
cause = rtData.alerts[idx % rtData.alerts.length].text;
}
// Formatage des arrêts intermédiaires avec leurs voies respectives (propres à chaque gare)
const formattedStops = (t.stops || []).map((st) => {
const stopUic = st.uic || '';
const stopVoie = lookupPlatform(t.num, t.trip_id, stopUic, st.nom);
@@ -454,7 +467,7 @@ async function fetchGtfsStationTimetable(uic, mode = 'departs') {
});
return {
id: `gtfs-${t.num || idx}-${rawTime}-${idx}`,
id: `gtfs-${isTomorrow ? 'demain-' : ''}${t.num || idx}-${rawTime}-${idx}`,
trip_id: t.trip_id,
numero_train: t.num || String(1000 + idx),
type_train: t.type || 'TER',
@@ -477,6 +490,7 @@ async function fetchGtfsStationTimetable(uic, mode = 'departs') {
stops: formattedStops,
composition: (t.type || '').includes('TGV') ? 'Rame 1 (Voitures 1 à 8) + Rame 2 (Voitures 11 à 18)' : 'Train standard',
is_substitution: (t.type || '').includes('AUTOCAR'),
_isTomorrow: isTomorrow,
_displayTime: formatTimeHM(rawTime),
_displayPlatform: realVoie,
_depHM: timeHM,
@@ -489,16 +503,54 @@ async function fetchGtfsStationTimetable(uic, mode = 'departs') {
causeDetails: cause || null
}
};
});
};
// Tri chronologique : afficher les trains circulant à partir de l'heure courante
enrichedTrains.sort((a, b) => {
const aDiff = ((a._depHM || 0) - currentHM + 1440) % 1440;
const bDiff = ((b._depHM || 0) - currentHM + 1440) % 1440;
return aDiff - bDiff;
});
// 1. Filtrer les trains circulant aujourd'hui
let rawToday = rawList.filter(t => circulatesOnDate(t, currentDay, currentYYYYMMDD, now));
if (rawToday.length === 0) {
// Fallback si pas de calendrier GTFS explicite
rawToday = rawList.filter(t => Array.isArray(t.days) && t.days.includes(currentDay));
if (rawToday.length === 0) rawToday = rawList;
}
return enrichedTrains;
const enrichedToday = rawToday.map((t, idx) => mapTrain(t, idx, false));
// Filtrer les trains passés : "Une fois que l'heure de départ / Arrivée est dépassée, alors ne plus l'afficher"
const remainingToday = enrichedToday
.filter(t => {
const trainHM = (mode === 'arrivees' ? t._arrHM : t._depHM) || 0;
const delay = t.retard_minutes || 0;
return (trainHM + delay) >= currentHM;
})
.sort((a, b) => {
const aHM = (mode === 'arrivees' ? a._arrHM : a._depHM) || 0;
const bHM = (mode === 'arrivees' ? b._arrHM : b._depHM) || 0;
return aHM - bHM;
});
// 2. Filtrer les trains circulant demain
let rawTomorrow = rawList.filter(t => circulatesOnDate(t, tomorrowDay, tomorrowYYYYMMDD, tomorrow));
if (rawTomorrow.length === 0) {
rawTomorrow = rawList.filter(t => Array.isArray(t.days) && t.days.includes(tomorrowDay));
}
const enrichedTomorrow = rawTomorrow
.map((t, idx) => mapTrain(t, idx, true))
.sort((a, b) => {
const aHM = (mode === 'arrivees' ? a._arrHM : a._depHM) || 0;
const bHM = (mode === 'arrivees' ? b._arrHM : b._depHM) || 0;
return aHM - bHM;
});
// 3. Logique : s'il reste <= 5 horaires aujourd'hui, afficher ceux d'aujourd'hui + ceux du lendemain
let resultList = [];
if (remainingToday.length <= 5) {
resultList = [...remainingToday, ...enrichedTomorrow];
} else {
resultList = remainingToday;
}
return resultList;
} catch (err) {
console.warn('[sncfApi] Erreur de chargement GTFS station:', err);
return null;