diff --git a/src/api/core/Controller.ts b/src/api/core/Controller.ts index 8725af1..00f9d52 100644 --- a/src/api/core/Controller.ts +++ b/src/api/core/Controller.ts @@ -11,7 +11,7 @@ import { getLogger } from "@logtape/logtape"; const logger = getLogger(["core", "Controller"]); export class Controller { - model_manager: ModelManager; + model_manager: ModelManager | undefined; monitor_server: MonitorServer; player_manager: PlayerManager; gama_connector: GamaConnector | undefined; @@ -22,14 +22,21 @@ export class Controller { constructor(useAdb: boolean) { // this.mDnsService = new mDnsService(process.env.WEB_HOSTNAME); - this.model_manager = new ModelManager(this); - this.monitor_server = new MonitorServer(this); - this.player_manager = new PlayerManager(this); if (ENV_GAMALESS) { - logger.info("Web platform launched in 'Gamaless' mode") + const border = "=".repeat(58); + logger.warn(border); + logger.warn("= ="); + logger.warn("= !! GAMALESS MODE ACTIVE — NO GAMA, NO MODEL MANAGER ="); + logger.warn("= Simulation features are fully disabled. ="); + logger.warn("= Only headset/player management is operational. ="); + logger.warn("= ="); + logger.warn(border); } else { + this.model_manager = new ModelManager(this); this.gama_connector = new GamaConnector(this); } + this.monitor_server = new MonitorServer(this); + this.player_manager = new PlayerManager(this); if (useAdb) { this.adb_manager = new AdbManager(this); @@ -58,10 +65,10 @@ export class Controller { this.player_manager = new PlayerManager(this); this.monitor_server = new MonitorServer(this); - if (ENV_GAMALESS) { - logger.trace("skipped restarting the gama connector, application in gamaless mode...") + logger.trace("Skipped restarting the gama connector and model manager, application in gamaless mode...") } else { + this.model_manager = new ModelManager(this); this.gama_connector = new GamaConnector(this); } @@ -77,7 +84,12 @@ export class Controller { */ getSimulationInformations(): string { - return this.model_manager.getCatalogListJSON(); + if (!ENV_GAMALESS) { + return this.model_manager!.getCatalogListJSON(); + } else { + logger.debug("[getSimulationInformations] model_manager is not available in GAMALESS mode"); + return JSON.stringify([]); + } } /* @@ -110,7 +122,7 @@ export class Controller { if (!ENV_GAMALESS) this.gama_connector!.addInGamePlayer(id_player); else - logger.warn("[addInGamePlayer] Message received to add player in GAMA, but the webplatform is in GAMALESS mode..."); + logger.debug("[addInGamePlayer] Message received to add player in GAMA, but the webplatform is in GAMALESS mode..."); } purgePlayer(id_player: string): void { @@ -119,6 +131,7 @@ export class Controller { // Remove from GAMA if (!ENV_GAMALESS) this.gama_connector!.removeInGamePlayer(id_player); + // Remove from connected list this.player_manager.removePlayer(id_player); @@ -135,14 +148,14 @@ export class Controller { if (!ENV_GAMALESS) this.gama_connector!.sendExpression(id_player, expr); else - logger.warn("[sendExpression] Message received to send to GAMA, but the webplatform is in GAMALESS mode..."); + logger.debug("[sendExpression] Message received to send to GAMA, but the webplatform is in GAMALESS mode..."); } sendAsk(json: JsonPlayerAsk) { if (!ENV_GAMALESS) this.gama_connector!.sendAsk(json); else - logger.warn("[sendAsk] Message received to send to GAMA, but the webplatform is in GAMALESS mode..."); + logger.debug("[sendAsk] Message received to send to GAMA, but the webplatform is in GAMALESS mode..."); } launchExperiment() { @@ -158,7 +171,7 @@ export class Controller { this.notifyMonitor(); }, 100); } else - logger.warn("[launchExperiment] Message received to load an experiment in GAMA, but the webplatform is in GAMALESS mode..."); + logger.debug("[launchExperiment] Message received to load an experiment in GAMA, but the webplatform is in GAMALESS mode..."); } stopExperiment() { @@ -168,21 +181,21 @@ export class Controller { this.notifyMonitor(); } else - logger.warn("[stopExperiment] Message received to close current GAMA simulation, but the webplatform is in GAMALESS mode..."); + logger.debug("[stopExperiment] Message received to close current GAMA simulation, but the webplatform is in GAMALESS mode..."); } pauseExperiment(callback?: () => void) { if (!ENV_GAMALESS) this.gama_connector!.pauseExperiment(callback); else - logger.warn("[pauseExperiment] Message received to pause current GAMA simulation, but the webplatform is in GAMALESS mode..."); + logger.debug("[pauseExperiment] Message received to pause current GAMA simulation, but the webplatform is in GAMALESS mode..."); } resumeExperiment() { if (!ENV_GAMALESS) this.gama_connector!.resumeExperiment(); else - logger.warn("[resumeExperiment] Message received to resume current GAMA simulation, but the webplatform is in GAMALESS mode..."); + logger.debug("[resumeExperiment] Message received to resume current GAMA simulation, but the webplatform is in GAMALESS mode..."); } } diff --git a/src/api/monitoring/MonitorServer.ts b/src/api/monitoring/MonitorServer.ts index 9308caa..7613a39 100644 --- a/src/api/monitoring/MonitorServer.ts +++ b/src/api/monitoring/MonitorServer.ts @@ -4,6 +4,7 @@ import { Controller } from "../core/Controller.ts"; import { JsonMonitor } from "../core/Constants.ts"; import { getLogger } from "@logtape/logtape"; import Model from "../simulation/Model.ts"; +import { ENV_GAMALESS } from "../index.ts"; const logger = getLogger(["monitor", "MonitorServer"]); @@ -52,8 +53,7 @@ export class MonitorServer { Buffer.from(message).toString(), ); const type = jsonMonitor.type; - (logger.trace("Received message on monitor server : {jsonMonitor}"), - { jsonMonitor }); + logger.trace("Received message on monitor server : {jsonMonitor}", { jsonMonitor }); switch (type) { case "launch_experiment": @@ -108,6 +108,10 @@ export class MonitorServer { case "get_simulation_informations": logger.trace("Requesting and sending back simulation information"); + if (ENV_GAMALESS) { + logger.debug("[get_simulation_informations] model_manager unavailable in GAMALESS mode"); + break; + } this.sendMessageByWs( this.controller.getSimulationInformations(), ws, @@ -116,16 +120,20 @@ export class MonitorServer { case "get_simulation_by_index": logger.trace("Requesting and sending back simulation by index"); + if (ENV_GAMALESS) { + logger.debug("[get_simulation_by_index] model_manager unavailable in GAMALESS mode"); + break; + } const index: number|undefined = jsonMonitor.simulationIndex; - if (index !== undefined && index >= 0 && index < this.controller.model_manager.getModelList().length) { + if (index !== undefined && index >= 0 && index < this.controller.model_manager!.getModelList().length) { // Retrieve the simulation based on the index - this.controller.model_manager.setActiveModelByIndex(index); + this.controller.model_manager!.setActiveModelByIndex(index); logger.debug("set active model to {modelName}", { - modelName: this.controller.model_manager.getActiveModel().toString() + modelName: this.controller.model_manager!.getActiveModel().toString() }); - const selectedSimulation = this.controller.model_manager.getActiveModel(); + const selectedSimulation = this.controller.model_manager!.getActiveModel(); logger.trace("Sending back"); this.sendMessageByWs({ @@ -141,10 +149,14 @@ export class MonitorServer { case "send_simulation": logger.trace("Sending simulation"); + if (ENV_GAMALESS) { + logger.debug("[send_simulation] model_manager unavailable in GAMALESS mode"); + break; + } const simulationFromStream = JSON.parse(Buffer.from(message).toString()); - this.controller.model_manager.setActiveModelByIndex(simulationFromStream.simulation.model_index); - const selectedSimulation: Model = this.controller.model_manager.getActiveModel(); + this.controller.model_manager!.setActiveModelByIndex(simulationFromStream.simulation.model_index); + const selectedSimulation: Model = this.controller.model_manager!.getActiveModel(); logger.debug("Selected simulation sent to gama: {json}", { json: selectedSimulation.getJsonSettings() }); this.sendMessageByWs({ type: "get_simulation_by_index", @@ -152,6 +164,13 @@ export class MonitorServer { }, ws); break; + case "try_connection": + if (ENV_GAMALESS) { + logger.debug("[try_connection] GAMA is not active (GAMALESS mode) — ignoring connection attempt"); + } + // In normal mode: silently ignore, GamaConnector manages its own connection + break; + default: logger.warn( "The last message received from the monitor had an unknown type.\n{jsonMonitor}", @@ -163,9 +182,7 @@ export class MonitorServer { close: (ws, code: number, message) => { try { this.wsClients.delete(ws); - logger.debug( - `Connection closed. Code: ${code}, Reason: ${Buffer.from(message).toString()}`, - ); + logger.debug(`Connection closed. Code: ${code}, Reason: ${Buffer.from(message).toString()}`); // Handle specific close codes switch (code) { @@ -207,8 +224,19 @@ export class MonitorServer { * Sends the json_state to the monitor */ sendMonitorGamaState(): void { + if (ENV_GAMALESS) { + const messageToSend = { + type: "json_state", + gama: {}, + player: this.controller.player_manager.getArrayPlayerList(), + }; + logger.trace("Sending monitor gama state (GAMALESS):\n{messageToSend}", { messageToSend }); + this.sendMessageByWs(messageToSend); + return; + } + if ( - this.controller.model_manager.getActiveModel() !== undefined && + this.controller.model_manager?.getActiveModel() !== undefined && this.controller.gama_connector !== undefined ) { const messageToSend = { @@ -228,7 +256,7 @@ export class MonitorServer { * Send the json_setting to the monitor */ sendMonitorJsonSettings(): void { - if (this.controller.model_manager.getActiveModel() !== undefined) { + if (this.controller.model_manager?.getActiveModel() !== undefined) { logger.trace("Sending monitor json settings:\n{json}", { json: this.controller.model_manager.getActiveModel().getJsonSettings(), }); diff --git a/src/components/SelectorSimulations/SelectorSimulations.tsx b/src/components/SelectorSimulations/SelectorSimulations.tsx index 7801ad9..e2be1ad 100644 --- a/src/components/SelectorSimulations/SelectorSimulations.tsx +++ b/src/components/SelectorSimulations/SelectorSimulations.tsx @@ -12,7 +12,7 @@ import { getLogger } from '@logtape/logtape'; import { VU_CATALOG_SETTING_JSON, VU_MODEL_SETTING_JSON } from '../../api/core/Constants'; import visibility from '/src/svg_logos/visibility.svg'; const SelectorSimulations = () => { - const { ws, isWsConnected, gama, simulationList } = useWebSocket(); + const { ws, isWsConnected, gamaless, gama, simulationList } = useWebSocket(); const [loading, setLoading] = useState(true); const [connectionStatus, setConnectionStatus] = useState('Waiting for connection ...'); const { t } = useTranslation(); @@ -138,8 +138,9 @@ const SelectorSimulations = () => { }; - // Loop which tries to connect to Gama + // Loop which tries to connect to Gama (skipped in GAMALESS mode) useEffect(() => { + if (gamaless) return; let interval: NodeJS.Timeout; if (ws && !gama.connected) { interval = setInterval(() => { @@ -150,7 +151,7 @@ const SelectorSimulations = () => { return () => { clearInterval(interval); }; - }, [ws, gama.connected]); + }, [ws, gama.connected, gamaless]); // Display connexion status useEffect(() => { @@ -167,7 +168,22 @@ const SelectorSimulations = () => {
{/* ↑ prop to specify whether it should use the small version of the navigation bar */} - {loading ? ( + {gamaless ? ( +
+
+

GAMALESS Mode

+

Simulation features are disabled. No GAMA server is connected.

+

Headset management is still operational.

+
+ + + +
+ ) : loading ? (

{t('loading')}

diff --git a/src/components/SimulationManager/SimulationManager.tsx b/src/components/SimulationManager/SimulationManager.tsx index d6e2936..ecc306c 100644 --- a/src/components/SimulationManager/SimulationManager.tsx +++ b/src/components/SimulationManager/SimulationManager.tsx @@ -22,7 +22,7 @@ export interface Player { const SimulationManager = () => { const logger = getLogger(["simulationManager", "SimulationManager"]); - const { ws, gama, playerList, selectedSimulation } = useWebSocket(); // `removePlayer` is now available + const { ws, gamaless, gama, playerList, selectedSimulation } = useWebSocket(); // `removePlayer` is now available const navigate = useNavigate(); const [simulationStarted, setSimulationStarted] = useState(false); const { t } = useTranslation(); @@ -88,10 +88,10 @@ const SimulationManager = () => { useEffect(() => { - if (!selectedSimulation) { + if (!gamaless && !selectedSimulation) { navigate('/'); } - }, [selectedSimulation, navigate]); + }, [gamaless, selectedSimulation, navigate]); // Handler for removing players @@ -149,6 +149,11 @@ const SimulationManager = () => { {/* Buttons Simulations : Play Button, Pause Button, Stop Button */} + {gamaless ? ( +
+ GAMALESS — simulation controls disabled +
+ ) : ( <>
{gama.experiment_state === 'NONE' || gama.experiment_state === 'NOTREADY' ? ( @@ -240,6 +245,7 @@ const SimulationManager = () => {
+ )} diff --git a/src/components/WebSocketManager/WebSocketManager.tsx b/src/components/WebSocketManager/WebSocketManager.tsx index b62d5e1..9f179a2 100644 --- a/src/components/WebSocketManager/WebSocketManager.tsx +++ b/src/components/WebSocketManager/WebSocketManager.tsx @@ -20,6 +20,7 @@ interface PlayerList { interface WebSocketContextType { ws: WebSocket | null; isWsConnected: boolean; + gamaless: boolean; gama: { connected: boolean; loading: 'hidden' | 'visible'; @@ -46,6 +47,7 @@ interface WebSocketManagerProps { const WebSocketManager = ({ children }: WebSocketManagerProps) => { const [ws, setWs] = useState(null); const [isWsConnected, setIsWsConnected] = useState(false); + const [gamaless, setGamaless] = useState(false); const [gama, setGama] = useState({ connected: false, loading: 'hidden' as 'hidden' | 'visible', @@ -101,7 +103,11 @@ const WebSocketManager = ({ children }: WebSocketManagerProps) => { switch (data.type) { // this case is launch too much time case 'json_state': - setGama(data.gama); + const isGamaless = Object.keys(data.gama).length === 0; + setGamaless(isGamaless); + if (!isGamaless) { + setGama(data.gama); + } setPlayerList(data.player); break; //Sets the selected simulation for the websocketManager's context @@ -137,7 +143,7 @@ const WebSocketManager = ({ children }: WebSocketManagerProps) => { return ( - + {children} );