Spaces:
Paused
Paused
File size: 5,292 Bytes
1c730d1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | // Copyright (c) 2025-2026, RTE (https://www.rte-france.com)
// This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0.
// If a copy of the Mozilla Public License, version 2.0 was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.
// SPDX-License-Identifier: MPL-2.0
// Game-Mode beginner-assistance wiring, extracted from App.tsx to keep the
// hub under its size ceiling. Registers a handler on `gameBridge` that drives
// the workspace from a lever hint (the "most-used levers" panel):
// - single-click ('inspect'): fill the Inspect field, center the NAD on the
// element (resolving an injection / coupling switch to its home VL), and
// open that substation's SLD so the beginner can inspect it;
// - double-click ('simulate'): run the mapped action directly — a catalogue
// branch disco/reco, or a coupling maneuver at the resolved VL. Magnitude-
// free injection / PST levers carry no action, so they degrade to inspect
// with a hint to set the amount in the SLD.
//
// App keeps ownership of the collaborators (diagrams + the two simulate
// entry points + the SLD opener) and passes them in, so this stays a thin
// orchestrator and the bare (non-game) app never touches the bridge.
import { useCallback, useEffect } from 'react';
import { api } from '../api';
import { gameBridge, type LeverInteractionMode } from '../game/gameBridge';
import { notifyError, notifyInfo } from '../utils/notifications';
import type { LeverInteraction } from '../types';
import type { DiagramsState } from './useDiagrams';
export interface LeverInteractionParams {
diagrams: DiagramsState;
/** Simulate a catalogue action (branch disco/reco) by id. */
handleSimulateUnsimulatedAction: (actionId: string) => Promise<void>;
/** Simulate a coupling maneuver (switch toggle) at a resolved VL. */
handleSimulateLever: (spec: { voltageLevelId: string; switches: Record<string, boolean> }) => Promise<void>;
/** Open the SLD overlay for a voltage level. */
handleVlOpen: (vlName: string) => void;
}
export function useLeverInteraction(params: LeverInteractionParams): void {
const { diagrams, handleSimulateUnsimulatedAction, handleSimulateLever, handleVlOpen } = params;
const handleLeverInteraction = useCallback(
async (interaction: LeverInteraction, mode: LeverInteractionMode) => {
const { inspectQuery, category, simulate } = interaction;
// The overflow tab isn't a NAD — center on the contingency tab instead.
const tab = diagrams.activeTab === 'overflow' ? 'contingency' : diagrams.activeTab;
// Fill the Inspect field with the clicked element (both modes). We
// center explicitly below, so use the plain setter that doesn't pin
// a focus tab (an injection / switch name never matches a NAD id,
// which would otherwise leave that pin dangling).
diagrams.setInspectQuery(inspectQuery);
// A catalogue action (branch disco/reco) simulates directly by id.
if (mode === 'simulate' && simulate?.actionId) {
await handleSimulateUnsimulatedAction(simulate.actionId);
return;
}
// Resolve the element's home VL for everything that isn't a branch
// (a branch spans two VLs and is centered directly by its own id).
let homeVl: string | null = null;
if (category !== 'branch') {
try {
const { voltage_level_ids } = await api.getElementVoltageLevels(inspectQuery);
if (voltage_level_ids.length === 1) homeVl = voltage_level_ids[0];
} catch {
// Resolution is best-effort — fall through to centering on the id.
}
}
// A coupling maneuver simulates directly once we know its VL.
if (mode === 'simulate' && simulate?.switches) {
if (homeVl) await handleSimulateLever({ voltageLevelId: homeVl, switches: simulate.switches });
else notifyError('Could not locate the substation for this maneuver.');
return;
}
// Magnitude-free lever double-clicked: it can't be simulated as-is,
// so guide the operator to set the amount in the SLD, then fall
// through to inspect (locate + open the substation).
if (mode === 'simulate' && !simulate) {
notifyInfo('Set the amount in the substation diagram, then Simulate.');
}
// Inspect: center the NAD, and open the SLD when the lever lives in
// one substation (an injection or a coupling switch → a single VL).
if (homeVl) {
diagrams.zoomToElement(homeVl, tab);
handleVlOpen(homeVl);
} else {
diagrams.zoomToElement(inspectQuery, tab);
}
},
[diagrams, handleSimulateUnsimulatedAction, handleSimulateLever, handleVlOpen],
);
useEffect(() => {
if (!gameBridge.isGameMode()) return;
gameBridge.registerLeverHandler(handleLeverInteraction);
}, [handleLeverInteraction]);
}
|