Skip to main content

Nouvelle pageBootNotification aux bornes FAULTED

/**

  • Script TURBO – Envoi BootNotification RAPIDE aux bornes FAULTED
  • Optimisations :
    • Traitement en parallèle (par lots de 10)
    • Suppression des délais artificiels
  • Utilisation :
    1. Faire un "Refresh" (F5) de la page pour nettoyer l'ancien script
    1. Coller ce nouveau script
    1. Lancer : triggerBootNotificationToFaultedStations() */

let stopBootNotificationScriptFlag = false;

function stopBootNotificationScript() { stopBootNotificationScriptFlag = true; console.log("🛑 Arrêt du script demandé..."); }

function getStationId(station) { return ( station.id ?? station.chargingStationId ?? station.stationId ?? station.idChargingStation ?? null ); }

async function triggerBootNotificationToFaultedStations(customApiUrl = null) { stopBootNotificationScriptFlag = false; console.log("🚀 Envoi BootNotification TURBO aux bornes FAULTED");

try { // 🔑 Auth & Config const token = localStorage.getItem("token"); if (!token) throw new Error("Token non trouvé");

const selectedRole = JSON.parse(localStorage.getItem("selectedRole") || "{}");
if (!selectedRole.groupId) throw new Error("groupId manquant");
const groupId = selectedRole.groupId;

const origin = window.location.origin;
let API_URL = customApiUrl || (origin.includes("localhost") ? origin.replace(":4200", ":3000") : origin + "/api");

console.log(`🔗 API: ${API_URL} | Groupe: ${groupId}`);

// =========================
// 1. Récupération (Pagination)
// =========================
const pageSize = 100; // Max souvent autorisé
let page = 1;
let hasMorePages = true;
let allStations = [];

console.log("📡 Récupération des bornes FAULTED...");

while (hasMorePages && !stopBootNotificationScriptFlag) {
  const url = `${API_URL}/charging-stations/group/${groupId}/list?offset=${(page - 1) * pageSize}&limit=${pageSize}&status=faulted`;
  
  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" }
  });
  
  if (!res.ok) throw new Error(`Erreur HTTP listing: ${res.status}`);
  
  const data = await res.json();
  const records = data.records || data; // Gérer format {records: [], count: n} ou [records]
  allStations = allStations.concat(records);
  
  const totalRecords = parseInt(res.headers.get("X-Total-Count") || "0");
  const totalPages = Math.ceil(totalRecords / pageSize);
  
  console.log(`✅ Page ${page}/${totalPages || '?'} (+${records.length} bornes)`);
  
  if (records.length === 0 || page >= totalPages) {
    hasMorePages = false;
  } else {
    page++;
  }
}

// =========================
// 2. Filtrage & Préparation
// =========================
// On garde uniquement celles avec un ID valide (le filtre status est fait par l'API)
const stationsToprocess = allStations
  .map(s => ({ ...s, _stationId: getStationId(s) }))
  .filter(s => s._stationId != null);

console.log(`📋 ${stationsToprocess.length} bornes FAULTED prêtes à être traitées.`);

if (stationsToprocess.length === 0) return console.warn("⚠️ Rien à traiter.");

if (!confirm(`🚀 GO pour envoyer BootNotification à ${stationsToprocess.length} bornes en MODE ACCÉLÉRÉ ?`)) {
  return console.log("❌ Opération annulée");
}

// =========================
// 3. Traitement Parallèle (Concurrency Queue)
// =========================
const CONCURRENCY_LIMIT = 10; // Nombre de requêtes simultanées
let finishedCount = 0;
let successCount = 0;
let errorCount = 0;

// Fonction worker : consomme la liste tant qu'il y en a
const processQueue = async (workerId) => {
    while (stationsToprocess.length > 0 && !stopBootNotificationScriptFlag) {
        const station = stationsToprocess.shift(); // Prend le prochain élément
        const name = station.name || station.chargeboxIdentity || station._stationId;

        try {
            const controller = new AbortController();
            const timeoutId = setTimeout(() => controller.abort(), 10000); // 10s timeout par requête

            const res = await fetch(`${API_URL}/ocpp/trigger-message/${station._stationId}`, {
                method: "POST",
                headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
                body: JSON.stringify({ command: "BootNotification", connector: 0 }),
                signal: controller.signal
            }).finally(() => clearTimeout(timeoutId));

            if (!res.ok) throw new Error(`HTTP ${res.status}`);
            
            const json = await res.json();
            const status = json?.result?.status || json?.status || json?.[0]?.result?.status;

            if (String(status).toLowerCase() === "accepted") {
                successCount++;
                console.log(`✅ [${++finishedCount}] ${name}`);
            } else {
                throw new Error(`Refusé (${status})`);
            }
        } catch (err) {
            errorCount++;
            console.warn(`❌ [${++finishedCount}] ${name} : ${err.message}`);
        }
    }
};

console.log(`🔥 Démarrage de ${CONCURRENCY_LIMIT} workers parallèles...`);

// Lancer les workers
const workers = [];
for (let i = 0; i < CONCURRENCY_LIMIT; i++) {
    workers.push(processQueue(i));
}

await Promise.all(workers);

console.log("\n==============================");
console.log("🏁 TERMINÉ");
console.log("==============================");
console.log(`✅ Succès: ${successCount}`);
console.log(`❌ Échecs: ${errorCount}`);
console.log("==============================");

} catch (err) { console.error("❌ Erreur fatale:", err); } }

// Exposition globale window.triggerBootNotificationToFaultedStations = triggerBootNotificationToFaultedStations; window.stopBootNotificationScript = stopBootNotificationScript;

console.log("✅ Script FAULTED TURBO chargé"); console.log("➡️ triggerBootNotificationToFaultedStations()");