import yargs from 'yargs/yargs' import { hideBin } from 'yargs/helpers' import 'node:process' import { RedisConnexion } from '../redisConnexion.js' import { busReplyRoute } from '../bus/publishActionReply.js' import { configHelper } from '../configHelper.js' import { maestroServer } from './maestroServer.js' import * as systemMesh from './actions/system/index.js' import * as arenaMesh from './actions/arena/index.js' const meshModules = { system: systemMesh, arena: arenaMesh, } const originalLog = console.log const originalWarn = console.warn const originalError = console.error function logWithTimestamp(originalFn, level, ...args) { const timestamp = new Date().toISOString() originalFn(`[${timestamp}] [${level}]`, ...args) } console.log = (...args) => logWithTimestamp(originalLog, 'LOG', ...args) console.warn = (...args) => logWithTimestamp(originalWarn, 'WARN', ...args) console.error = (...args) => logWithTimestamp(originalError, 'ERROR', ...args) const argv = yargs(hideBin(process.argv)).command('Maestro', 'Simulation orchestrator for P42', {}) .options({ 'debug': { description: 'shows debug info', alias: 'd', defaut: false, type: 'boolean' }, 'config': { description: 'Points to config file (default: ../config.json)', alias: 'c', default: '../config.json', type: 'string' }, }).help().version('1.0').argv const debug = Boolean(process.env.DEBUG) || argv.debug let cfgh = new configHelper({ localfile: argv.config, }) function meshRedisConns(mesh, meshName, debug, rootConfig) { const { redis, ...meshConfig } = mesh const busRoute = busReplyRoute(rootConfig.maestro, meshName) return redis.map(cfg => new RedisConnexion({ debug, config: { ...cfg, ...meshConfig, maestro: rootConfig.maestro }, redisId: cfg.redisId, meshName, meshModule: meshModules[meshName], senderId: busRoute?.senderId, actionsReply: busRoute?.actionsReply, }) ) } async function startAllRedis(rootConfig, cfgh) { if(debug) console.log('Starting all Redis instances...') const redisConns = [ ...meshRedisConns(rootConfig.systemMesh, 'system', debug, rootConfig), ...meshRedisConns(rootConfig.arenaMesh, 'arena', debug, rootConfig), ] const srv = new maestroServer(cfgh, redisConns, debug) for(const cnx of redisConns) { if(cnx.meshName === 'system') srv.wireSystemConnexion(cnx) else if(cnx.meshName === 'arena') srv.wireArenaConnexion(cnx) } const loginResults = await Promise.allSettled( redisConns.map(async cnx => { await cnx.redisLogin() return(cnx.redisId) }) ) const failedLogin = loginResults.filter(r => r.status !== 'fulfilled') if(failedLogin.length > 0) { console.error('Redis login failures:') failedLogin.forEach((r, i) => { const id = redisConns[i].redisId console.error(`login failed for redis:[${id}] → ${r.reason}`) }) throw new Error( `Redis login failed for ${failedLogin.length}/${redisConns.length} instances` ) } if(debug) console.log('All Redis logins OK') const chanResults = await Promise.allSettled( redisConns.map(async cnx => { await cnx.redisChansStart() return(cnx.redisId) }) ) const failedChans = chanResults.filter(r => r.status !== 'fulfilled') if(failedChans.length > 0) { console.error('Redis chansStart failures:') failedChans.forEach((r, i) => { const id = redisConns[i].redisId console.error(`chansStart failed for redis:[${id}] → ${r.reason}`) }) throw new Error( `Redis chansStart failed for ${failedChans.length}/${redisConns.length} instances` ) } if(debug) console.log('All Redis chansStart OK') return({ redisConns, srv }) } cfgh.fetchConfig().then(async rootConfig => { if(!rootConfig) { console.error('Cannot get a valid configuration ! Aaarrghhh...') process.exit() } console.log(`Debug mode : ${debug ? 'ON' : 'OFF'}`) const { srv } = await startAllRedis(rootConfig, cfgh) const dbOk = await srv.init() if(!dbOk) { console.error('Maestro MySQL init failed — exiting') process.exit(1) } })