Plan du planificateur HC ; dialogue avec le conseiller dans HA ; arbitre encadre (question a choix fermes, liste blanche)
This commit is contained in:
60
tools/arbitrage.mjs
Normal file
60
tools/arbitrage.mjs
Normal file
@@ -0,0 +1,60 @@
|
||||
// =====================================================================
|
||||
// ARBITRE ENCADRE (08/10/2026, decision du proprietaire)
|
||||
//
|
||||
// Pour les cas qu'aucune regle n'a prevus : le CODE detecte en direct une
|
||||
// situation incoherente et formule une question a CHOIX FERMES ; OpenClaw
|
||||
// (concierge, SPARK) l'envoie par WhatsApp ; la reponse du proprietaire est
|
||||
// appliquee par le service d'historisation, et SEULEMENT l'ordre attache au
|
||||
// choix retenu, pris dans la liste blanche du guichet d'ordres (ORDRES).
|
||||
// Le modele ne decide rien et n'invente aucun choix : il relaie.
|
||||
//
|
||||
// Chaque arbitrage est historise (table `arbitrage`) : un cas qui revient avec
|
||||
// la meme reponse deviendra une regle du planificateur, apres rejeu
|
||||
// (docs/PLAN_PLANIFICATEUR_HC.md, §7).
|
||||
//
|
||||
// `detecterSituation` est pure (tests : tests/arbitrage/run.mjs).
|
||||
// =====================================================================
|
||||
|
||||
export const DUREE_MIN = 10; // la situation doit durer 10 min
|
||||
const VOITURE_ABSENTE_OU_PLEINE = /not_connected|done|full|complete|charged/i;
|
||||
|
||||
/**
|
||||
* @param {object} e etat lu en direct
|
||||
* fenetre : 'nuit' | 'midi' | null (fenetre HC en cours)
|
||||
* finFenetre : 'HH:MM' | null
|
||||
* contacteurOn, voitureBranchee : booleens
|
||||
* borneW : puissance de la borne (W)
|
||||
* sourceMode, etat, raison : controleur de la borne
|
||||
* vehicule : selecteur (VW, JEEP, ZERO)
|
||||
* modeNuit : 'sans_edf' | 'complete'
|
||||
* @returns {null | { cas, question, choix: [{ cle, libelle, ordre: null | { ordre, valeur } }] }}
|
||||
*/
|
||||
export function detecterSituation(e) {
|
||||
// CAS 1 — voiture branchee, non pleine, bloquee, alors qu'EDF est deja ferme
|
||||
// en heures creuses. Cause de la nuit du 07-08/10 (VW bloquee 01:24-05:14).
|
||||
if (e.fenetre && e.contacteurOn && e.voitureBranchee && Number(e.borneW) < 100
|
||||
&& /bloqu/i.test(e.sourceMode || '') && !VOITURE_ABSENTE_OU_PLEINE.test(e.etat || '')) {
|
||||
const v = e.vehicule || 'la voiture';
|
||||
const nuit = e.fenetre === 'nuit';
|
||||
if (nuit && e.modeNuit === 'complete') return null; // deja autorisee : ce n'est pas un choix
|
||||
return {
|
||||
cas: 'voiture_bloquee_edf_ferme',
|
||||
question: `${v} est branchee et ne charge pas, alors qu'EDF est ferme en heures creuses (jusqu'a ${e.finFenetre || 'la fin de la fenetre'}). `
|
||||
+ `Raison du controleur : « ${String(e.raison || 'inconnue').split('|')[0].trim()} ». Que fais-je ?`,
|
||||
choix: [
|
||||
nuit
|
||||
? { cle: '1', libelle: `Charger ${v} sur EDF jusqu'a ${e.finFenetre || '07:24'} (mode de nuit « complete »)`, ordre: { ordre: 'mode_nuit', valeur: 'complete' } }
|
||||
: { cle: '1', libelle: `Charger ${v} sur EDF jusqu'a ${e.finFenetre || '14:24'} (« force_grid_now », expire en fin d'heures creuses)`, ordre: { ordre: 'strategie', valeur: 'force_grid_now' } },
|
||||
{ cle: '2', libelle: 'Laisser en attente (rien ne change)', ordre: null },
|
||||
],
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Reponse du proprietaire : "1", "2", « 1 », « choix 2 »… -> cle ou null.
|
||||
export function lireChoix(texte, choix) {
|
||||
const m = /\b([1-9])\b/.exec(String(texte || ''));
|
||||
if (!m) return null;
|
||||
return choix.find((c) => c.cle === m[1]) || null;
|
||||
}
|
||||
Reference in New Issue
Block a user