From 3066a54a4cb136ab597baafdbb836e96629ab38e Mon Sep 17 00:00:00 2001 From: STEINNI Date: Sat, 20 Jun 2026 20:10:14 +0000 Subject: [PATCH] finished actions => handlers refacto, small bux fix in maestro => Test maestro1 OK --- GPS/gpsServer.js | 2 +- .../arena/agentMotion.js | 0 GPS/{actions => handlers}/arena/index.js | 0 GPS/{actions => handlers}/arena/lifecycle.js | 0 GPS/{actions => handlers}/arena/worldline.js | 0 GPS/{actions => handlers}/system/index.js | 0 GPS/{actions => handlers}/system/utilities.js | 0 GPS/p42Gps.js | 4 +- GPS/startGps.sh | 16 ++- Maestro/{actions => handlers}/arena/index.js | 0 .../{actions => handlers}/arena/prepare.js | 0 Maestro/{actions => handlers}/system/index.js | 0 .../system/simulation.js | 0 .../{actions => handlers}/system/utilities.js | 0 Maestro/maestroServer.js | 4 +- Maestro/p42Maestro.js | 4 +- Maestro/startMaestro.sh | 16 ++- Observer/gpsStorageReader.js | 2 +- Observer/{actions => handlers}/arena/index.js | 0 .../{actions => handlers}/arena/lifecycle.js | 0 .../{actions => handlers}/system/index.js | 0 .../{actions => handlers}/system/positions.js | 0 .../{actions => handlers}/system/utilities.js | 0 Observer/p42Observer.js | 4 +- Observer/requestorRegistry.js | 2 +- Observer/startObserver.sh | 16 ++- config_test.json | 110 ++++++++++++++++++ lib/resolveConfigPath.sh | 22 ++++ startAllGods.sh | 75 ++++++++++++ stopAllGods.sh | 38 ++++++ tests/commands.txt | 13 ++- tests/modules/maestro1.js | 91 +++++++++++++-- 32 files changed, 386 insertions(+), 33 deletions(-) rename GPS/{actions => handlers}/arena/agentMotion.js (100%) rename GPS/{actions => handlers}/arena/index.js (100%) rename GPS/{actions => handlers}/arena/lifecycle.js (100%) rename GPS/{actions => handlers}/arena/worldline.js (100%) rename GPS/{actions => handlers}/system/index.js (100%) rename GPS/{actions => handlers}/system/utilities.js (100%) rename Maestro/{actions => handlers}/arena/index.js (100%) rename Maestro/{actions => handlers}/arena/prepare.js (100%) rename Maestro/{actions => handlers}/system/index.js (100%) rename Maestro/{actions => handlers}/system/simulation.js (100%) rename Maestro/{actions => handlers}/system/utilities.js (100%) rename Observer/{actions => handlers}/arena/index.js (100%) rename Observer/{actions => handlers}/arena/lifecycle.js (100%) rename Observer/{actions => handlers}/system/index.js (100%) rename Observer/{actions => handlers}/system/positions.js (100%) rename Observer/{actions => handlers}/system/utilities.js (100%) create mode 100644 config_test.json create mode 100644 lib/resolveConfigPath.sh create mode 100755 startAllGods.sh create mode 100755 stopAllGods.sh diff --git a/GPS/gpsServer.js b/GPS/gpsServer.js index 5a0c831..d0d070a 100644 --- a/GPS/gpsServer.js +++ b/GPS/gpsServer.js @@ -8,7 +8,7 @@ import { positionAt, needsPrismRefresh, advanceAgentSegment, -} from './actions/arena/worldline.js' +} from './handlers/arena/worldline.js' export class gpsServer { diff --git a/GPS/actions/arena/agentMotion.js b/GPS/handlers/arena/agentMotion.js similarity index 100% rename from GPS/actions/arena/agentMotion.js rename to GPS/handlers/arena/agentMotion.js diff --git a/GPS/actions/arena/index.js b/GPS/handlers/arena/index.js similarity index 100% rename from GPS/actions/arena/index.js rename to GPS/handlers/arena/index.js diff --git a/GPS/actions/arena/lifecycle.js b/GPS/handlers/arena/lifecycle.js similarity index 100% rename from GPS/actions/arena/lifecycle.js rename to GPS/handlers/arena/lifecycle.js diff --git a/GPS/actions/arena/worldline.js b/GPS/handlers/arena/worldline.js similarity index 100% rename from GPS/actions/arena/worldline.js rename to GPS/handlers/arena/worldline.js diff --git a/GPS/actions/system/index.js b/GPS/handlers/system/index.js similarity index 100% rename from GPS/actions/system/index.js rename to GPS/handlers/system/index.js diff --git a/GPS/actions/system/utilities.js b/GPS/handlers/system/utilities.js similarity index 100% rename from GPS/actions/system/utilities.js rename to GPS/handlers/system/utilities.js diff --git a/GPS/p42Gps.js b/GPS/p42Gps.js index c6e1920..8e73037 100644 --- a/GPS/p42Gps.js +++ b/GPS/p42Gps.js @@ -6,8 +6,8 @@ import {RedisConnexion} from '../redisConnexion.js' import { busReplyRoute } from '../bus/publishActionReply.js' import {configHelper} from '../configHelper.js' import {gpsServer} from './gpsServer.js' -import * as systemMesh from './actions/system/index.js' -import * as arenaMesh from './actions/arena/index.js' +import * as systemMesh from './handlers/system/index.js' +import * as arenaMesh from './handlers/arena/index.js' const meshModules = { system: systemMesh, diff --git a/GPS/startGps.sh b/GPS/startGps.sh index 20b49df..42cbb85 100755 --- a/GPS/startGps.sh +++ b/GPS/startGps.sh @@ -1,5 +1,11 @@ #!/bin/sh +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../lib/resolveConfigPath.sh +. "$SCRIPT_DIR/../lib/resolveConfigPath.sh" + +CONFIG="$(resolveConfigPath "$SCRIPT_DIR" "${1:-${CONFIG:-../config.json}}")" + set -a . /etc/p42/secrets.env set +a @@ -7,12 +13,16 @@ set +a daemon=p42Gps logfile=gps.log +if [ ! -f "$CONFIG" ]; then + echo "Config file not found: $CONFIG" >&2 + exit 1 +fi pid=$(pgrep -f "$daemon") if [ -z "$pid" ] then - node "${daemon}.js" --debug > "$logfile" 2>&1 & + node "${daemon}.js" --config "$CONFIG" --debug > "$logfile" 2>&1 & pid=$! sleep 1 @@ -20,16 +30,16 @@ then if kill -0 "$pid" 2>/dev/null then echo "" - echo "$daemon is now running with PID=$pid" + echo "$daemon is now running with PID=$pid (config=$CONFIG)" echo "" else echo "" echo "Failed to start $daemon. Check gps.log" echo "" + exit 1 fi else echo "" echo "$daemon is already running with PID=$pid" echo "" fi - diff --git a/Maestro/actions/arena/index.js b/Maestro/handlers/arena/index.js similarity index 100% rename from Maestro/actions/arena/index.js rename to Maestro/handlers/arena/index.js diff --git a/Maestro/actions/arena/prepare.js b/Maestro/handlers/arena/prepare.js similarity index 100% rename from Maestro/actions/arena/prepare.js rename to Maestro/handlers/arena/prepare.js diff --git a/Maestro/actions/system/index.js b/Maestro/handlers/system/index.js similarity index 100% rename from Maestro/actions/system/index.js rename to Maestro/handlers/system/index.js diff --git a/Maestro/actions/system/simulation.js b/Maestro/handlers/system/simulation.js similarity index 100% rename from Maestro/actions/system/simulation.js rename to Maestro/handlers/system/simulation.js diff --git a/Maestro/actions/system/utilities.js b/Maestro/handlers/system/utilities.js similarity index 100% rename from Maestro/actions/system/utilities.js rename to Maestro/handlers/system/utilities.js diff --git a/Maestro/maestroServer.js b/Maestro/maestroServer.js index 9f9a28a..daed119 100644 --- a/Maestro/maestroServer.js +++ b/Maestro/maestroServer.js @@ -78,9 +78,9 @@ export class maestroServer { refreshPrepareQuorum() { if(!this.arenaCnx) return - const { prepareAckChannel, readyTimeoutMs } = this.getMaestroSettings() + const { lifecycle, readyTimeoutMs } = this.getMaestroSettings() this.prepareQuorum = new PrepareQuorum({ - ackChannel: prepareAckChannel, + ackChannel: lifecycle.prepareAckChannel, timeoutMs: readyTimeoutMs, matchesChan: this.arenaCnx.matchesChan.bind(this.arenaCnx), debug: this.debug, diff --git a/Maestro/p42Maestro.js b/Maestro/p42Maestro.js index dec9d3b..0b10942 100644 --- a/Maestro/p42Maestro.js +++ b/Maestro/p42Maestro.js @@ -6,8 +6,8 @@ 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' +import * as systemMesh from './handlers/system/index.js' +import * as arenaMesh from './handlers/arena/index.js' const meshModules = { system: systemMesh, diff --git a/Maestro/startMaestro.sh b/Maestro/startMaestro.sh index 9b52442..deb4df1 100755 --- a/Maestro/startMaestro.sh +++ b/Maestro/startMaestro.sh @@ -1,5 +1,11 @@ #!/bin/sh +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../lib/resolveConfigPath.sh +. "$SCRIPT_DIR/../lib/resolveConfigPath.sh" + +CONFIG="$(resolveConfigPath "$SCRIPT_DIR" "${1:-${CONFIG:-../config.json}}")" + set -a . /etc/p42/secrets.env set +a @@ -7,11 +13,16 @@ set +a daemon=p42Maestro logfile=maestro.log +if [ ! -f "$CONFIG" ]; then + echo "Config file not found: $CONFIG" >&2 + exit 1 +fi + pid=$(pgrep -f "$daemon") if [ -z "$pid" ] then - node "${daemon}.js" --debug > "$logfile" 2>&1 & + node "${daemon}.js" --config "$CONFIG" --debug > "$logfile" 2>&1 & pid=$! sleep 1 @@ -19,12 +30,13 @@ then if kill -0 "$pid" 2>/dev/null then echo "" - echo "$daemon is now running with PID=$pid" + echo "$daemon is now running with PID=$pid (config=$CONFIG)" echo "" else echo "" echo "Failed to start $daemon. Check maestro.log" echo "" + exit 1 fi else echo "" diff --git a/Observer/gpsStorageReader.js b/Observer/gpsStorageReader.js index 422a805..763d7cc 100644 --- a/Observer/gpsStorageReader.js +++ b/Observer/gpsStorageReader.js @@ -1,4 +1,4 @@ -import { positionAt } from '../GPS/actions/arena/worldline.js' +import { positionAt } from '../GPS/handlers/arena/worldline.js' export class GpsStorageReader { diff --git a/Observer/actions/arena/index.js b/Observer/handlers/arena/index.js similarity index 100% rename from Observer/actions/arena/index.js rename to Observer/handlers/arena/index.js diff --git a/Observer/actions/arena/lifecycle.js b/Observer/handlers/arena/lifecycle.js similarity index 100% rename from Observer/actions/arena/lifecycle.js rename to Observer/handlers/arena/lifecycle.js diff --git a/Observer/actions/system/index.js b/Observer/handlers/system/index.js similarity index 100% rename from Observer/actions/system/index.js rename to Observer/handlers/system/index.js diff --git a/Observer/actions/system/positions.js b/Observer/handlers/system/positions.js similarity index 100% rename from Observer/actions/system/positions.js rename to Observer/handlers/system/positions.js diff --git a/Observer/actions/system/utilities.js b/Observer/handlers/system/utilities.js similarity index 100% rename from Observer/actions/system/utilities.js rename to Observer/handlers/system/utilities.js diff --git a/Observer/p42Observer.js b/Observer/p42Observer.js index b723ff5..70e60d2 100644 --- a/Observer/p42Observer.js +++ b/Observer/p42Observer.js @@ -6,8 +6,8 @@ import { RedisConnexion } from '../redisConnexion.js' import { busReplyRoute } from '../bus/publishActionReply.js' import { configHelper } from '../configHelper.js' import { observerServer } from './observerServer.js' -import * as systemMesh from './actions/system/index.js' -import * as arenaMesh from './actions/arena/index.js' +import * as systemMesh from './handlers/system/index.js' +import * as arenaMesh from './handlers/arena/index.js' const meshModules = { system: systemMesh, diff --git a/Observer/requestorRegistry.js b/Observer/requestorRegistry.js index 46d897a..807dd00 100644 --- a/Observer/requestorRegistry.js +++ b/Observer/requestorRegistry.js @@ -1,4 +1,4 @@ -import { positionAt } from '../GPS/actions/arena/worldline.js' +import { positionAt } from '../GPS/handlers/arena/worldline.js' import { Frustum } from './frustum.js' export class RequestorRegistry { diff --git a/Observer/startObserver.sh b/Observer/startObserver.sh index cc026a2..abf3647 100755 --- a/Observer/startObserver.sh +++ b/Observer/startObserver.sh @@ -1,5 +1,11 @@ #!/bin/sh +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../lib/resolveConfigPath.sh +. "$SCRIPT_DIR/../lib/resolveConfigPath.sh" + +CONFIG="$(resolveConfigPath "$SCRIPT_DIR" "${1:-${CONFIG:-../config.json}}")" + set -a . /etc/p42/secrets.env set +a @@ -7,11 +13,16 @@ set +a daemon=p42Observer logfile=observer.log +if [ ! -f "$CONFIG" ]; then + echo "Config file not found: $CONFIG" >&2 + exit 1 +fi + pid=$(pgrep -f "$daemon") if [ -z "$pid" ] then - node "${daemon}.js" --debug > "$logfile" 2>&1 & + node "${daemon}.js" --config "$CONFIG" --debug > "$logfile" 2>&1 & pid=$! sleep 1 @@ -19,12 +30,13 @@ then if kill -0 "$pid" 2>/dev/null then echo "" - echo "$daemon is now running with PID=$pid" + echo "$daemon is now running with PID=$pid (config=$CONFIG)" echo "" else echo "" echo "Failed to start $daemon. Check observer.log" echo "" + exit 1 fi else echo "" diff --git a/config_test.json b/config_test.json new file mode 100644 index 0000000..0e5f444 --- /dev/null +++ b/config_test.json @@ -0,0 +1,110 @@ +{ + "accessRights": [ + { + "canDo": [ + "RELOADCONFIG", + "GETCONFIG" + ], + "roles": [ + "admin" + ] + }, + { + "canDo": [ + "GETAGENTPOSITION", + "GETAGENTSINFRUSTUM", + "SUBSCRIBEFRUSTUM" + ], + "roles": "*" + }, + { + "canDo": [ + "STARTSIMULATION", + "STOPSIMULATION" + ], + "roles": "*" + } + ], + "gps": { + "primordialDaemon": true, + "gpsActionsChannel": "system:requests:gps", + "gpsActionsReply": "system:replies:[UID]", + "GPSstorage": { + "agentHashKey": "system:gps:agent:[UID]", + "agentsIndexKey": "system:gps:agents", + "positionsStream": "system:gps:positions", + "streamMaxLen": 100000 + }, + "agentVectorChangeChannel": "arena:agents:*", + "collisionsChannel": "arena:agents:[UID]", + "lifecycle": { + "arenaChannel": "arena:lifecycle", + "godsReadyChannel": "arena:gods:ready" + }, + "arenaStorage": { + "agentHashKey": "arena:agents:[UID]", + "agentsIndexKey": "arena:agents" + }, + "senderId": "gps", + "nearMissDistance": 1, + "prismTimeHeight": 60, + "collisionTickMs": 100, + "prismRefreshLeadSeconds": 1 + }, + "maestro": { + "maestroActionsChannel": "system:requests:maestro", + "maestroActionsReply": "system:replies:[UID]", + "senderId": "maestro", + "lifecycle": { + "arenaChannel": "arena:lifecycle", + "godsReadyChannel": "arena:gods:ready" + }, + "readyTimeoutMs": 30000 + }, + "mysql": { + "socketPath": "/var/run/mysqld/mysqld.sock", + "guiDatabase": "test_p42GUI", + "simDatabase": "test_p42SIM" + }, + "observer": { + "primordialDaemon": false, + "observerActionsChannel": "system:requests:observer", + "observerActionsReply": "system:replies:[UID]", + "senderId": "observer", + "scanIntervalMs": 300, + "lifecycle": { + "arenaChannel": "arena:lifecycle", + "godsReadyChannel": "arena:gods:ready" + } + }, + "systemMesh": { + "redis": [ + { + "redisId": "SYS_1", + "role": "primary", + "host": "127.0.0.1", + "tls": false, + "port": 6380, + "user": "", + "pass": "", + "chansNamespace": "system:", + "basePrefix": "messageBus:" + } + ] + }, + "arenaMesh": { + "redis": [ + { + "redisId": "ARN_1", + "role": "primary", + "host": "127.0.0.1", + "tls": false, + "port": 6379, + "user": "", + "pass": "", + "chansNamespace": "arena:", + "basePrefix": "messageBus:" + } + ] + } +} diff --git a/lib/resolveConfigPath.sh b/lib/resolveConfigPath.sh new file mode 100644 index 0000000..a3b4d83 --- /dev/null +++ b/lib/resolveConfigPath.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# resolveConfigPath BASE PATH +# Turn PATH into an absolute path; relative segments are resolved from BASE. +resolveConfigPath() { + _base=$1 + _path=$2 + + case "$_path" in + /*) + printf '%s\n' "$_path" + ;; + *) + _dir=$(dirname "$_path") + _name=$(basename "$_path") + if [ "$_dir" = . ]; then + printf '%s\n' "$_base/$_name" + else + printf '%s\n' "$(cd "$_base/$_dir" && pwd)/$_name" + fi + ;; + esac +} diff --git a/startAllGods.sh b/startAllGods.sh new file mode 100755 index 0000000..18335b5 --- /dev/null +++ b/startAllGods.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +GOD_FOLDERS="Maestro GPS Observer" + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=lib/resolveConfigPath.sh +. "$ROOT/lib/resolveConfigPath.sh" + +CONFIG="config.json" + +usage() { + echo "Usage: $(basename "$0") [-c|--config PATH]" >&2 + echo " PATH is relative to $ROOT unless absolute." >&2 +} + +while [ $# -gt 0 ]; do + case "$1" in + -c|--config) + if [ -z "${2:-}" ]; then + echo "Missing value for $1" >&2 + usage + exit 1 + fi + CONFIG="$2" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + CONFIG="$1" + shift + ;; + esac +done + +CONFIG="$(resolveConfigPath "$ROOT" "$CONFIG")" + +if [ ! -f "$CONFIG" ]; then + echo "Config file not found: $CONFIG" >&2 + exit 1 +fi + +failed=0 + +for folder in $GOD_FOLDERS; do + dir="$ROOT/$folder" + if [ ! -d "$dir" ]; then + echo "Missing daemon folder: $dir" >&2 + failed=1 + continue + fi + + shopt -s nullglob + scripts=("$dir"/start*.sh) + shopt -u nullglob + + if [ ${#scripts[@]} -eq 0 ]; then + echo "No start script in $dir" >&2 + failed=1 + continue + fi + if [ ${#scripts[@]} -gt 1 ]; then + echo "Multiple start scripts in $dir, using ${scripts[0]}" >&2 + fi + + echo "=== Starting $folder (config=$CONFIG) ===" + (cd "$dir" && bash "${scripts[0]}" "$CONFIG") || { + echo "Failed to start $folder" >&2 + failed=1 + } +done + +exit $failed diff --git a/stopAllGods.sh b/stopAllGods.sh new file mode 100755 index 0000000..73199ff --- /dev/null +++ b/stopAllGods.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +GOD_FOLDERS="Maestro GPS Observer" + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +failed=0 + +read -ra folders <<< "$GOD_FOLDERS" +for((i=${#folders[@]}-1; i>=0; i--)); do + folder="${folders[i]}" + dir="$ROOT/$folder" + if [ ! -d "$dir" ]; then + echo "Missing daemon folder: $dir" >&2 + failed=1 + continue + fi + + shopt -s nullglob + scripts=("$dir"/stop*.sh) + shopt -u nullglob + + if [ ${#scripts[@]} -eq 0 ]; then + echo "No stop script in $dir" >&2 + failed=1 + continue + fi + if [ ${#scripts[@]} -gt 1 ]; then + echo "Multiple stop scripts in $dir, using ${scripts[0]}" >&2 + fi + + echo "=== Stopping $folder ===" + (cd "$dir" && bash "${scripts[0]}") || { + echo "Failed to stop $folder" >&2 + failed=1 + } +done + +exit $failed diff --git a/tests/commands.txt b/tests/commands.txt index 88433c0..9846fa5 100644 --- a/tests/commands.txt +++ b/tests/commands.txt @@ -1,4 +1,11 @@ -clear; node test.js --guiDatabase test_p42GUI --simDatabase test_p42SIM maestro1 --userUuid a4f33373-6adf-4d2d-9a6d-7fa0abf8b01f --simulationUuid 0x019ec742e12175c685a97bf9300b6b49 - - +# Test DBs: point all gods at the same config (or edit config.json mysql section). +# Example: +# ./startAllGods.sh --config /opt/p42GodDaemons/config.json +# ./Maestro/startMaestro.sh /opt/p42GodDaemons/config.json +clear; node test.js maestro1 \ + --config ../config.json \ + --guiDatabase test_p42GUI --simDatabase test_p42SIM \ + --fakeAgentsReady --fakeGpsReady \ + --userUuid a4f33373-6adf-4d2d-9a6d-7fa0abf8b01f \ + --simulationUuid 0x019ec742e12175c685a97bf9300b6b49 diff --git a/tests/modules/maestro1.js b/tests/modules/maestro1.js index 5095951..9424b00 100644 --- a/tests/modules/maestro1.js +++ b/tests/modules/maestro1.js @@ -64,6 +64,54 @@ async function findSimulationFixture(ctx) { }) } +function godsReadyChannel(config) { + return(config.maestro?.lifecycle?.godsReadyChannel + ?? config.gps?.lifecycle?.godsReadyChannel + ?? 'arena:gods:ready') +} + +async function publishFakeReadyToStart(ctx, { sender, simulationId, agentIds }) { + const { arenaCnx } = ctx + const payload = { + success: true, + simulationId, + err: null, + } + if(agentIds) payload.agentIds = agentIds + + await arenaCnx.redisPublish(godsReadyChannel(ctx.config), { + eventType: 'readyToStart', + sender, + payload, + }) +} + +async function publishFakeReadies(ctx, simulationId, agentIds) { + const { argv, config, log } = ctx + + if(argv.fakeGpsReady) { + const senderId = config.gps?.senderId ?? 'gps' + log('action', `Faking GPS readyToStart (sender=${senderId})...`) + await publishFakeReadyToStart(ctx, { + sender: senderId, + simulationId, + agentIds, + }) + log('success', 'Published fake GPS readyToStart') + } + + if(argv.fakeAgentsReady) { + log('action', `Faking readyToStart for ${agentIds.length} agent(s)...`) + for(const agentId of agentIds) { + await publishFakeReadyToStart(ctx, { + sender: agentId, + simulationId, + }) + } + log('success', `Published fake readyToStart for ${agentIds.length} agent(s)`) + } +} + function waitForLifecycleEvent(ctx, eventType, timeoutMs) { return(new Promise((resolve, reject) => { const lifecyclePattern = ctx.config.maestro.lifecycle.arenaChannel @@ -99,6 +147,16 @@ export function configureYargs(yargsBuilder) { default: 15000, type: 'number', }, + fakeAgentsReady: { + describe: 'After onYourMarks, publish fake readyToStart for each seeded agent', + type: 'boolean', + default: false, + }, + fakeGpsReady: { + describe: 'After onYourMarks, publish fake GPS readyToStart', + type: 'boolean', + default: false, + }, })) } @@ -163,24 +221,29 @@ export async function run(ctx) { log('success', `Received onYourMarks for simulationId=${payload.simulationId}`) - log('action', 'Reading arena store...') - const indexIds = await arenaCnx.redisSmembers(arenaStorage.agentsIndexKey) - const expectedIds = [...expectedById.keys()].sort() - const actualIds = [...indexIds].sort() - - if(actualIds.length !== expectedIds.length) { - throw(new Error(`Agent index count mismatch: expected ${expectedIds.length}, got ${actualIds.length}`)) + const expectedIds = [...expectedById.keys()] + if(argv.fakeAgentsReady || argv.fakeGpsReady) { + await publishFakeReadies(ctx, payload.simulationId, expectedIds) } - for(let i = 0; i < expectedIds.length; i++) { - if(actualIds[i] !== expectedIds[i]) { - throw(new Error(`Agent index mismatch at ${i}: expected ${expectedIds[i]}, got ${actualIds[i]}`)) + log('action', 'Reading arena store...') + const indexIds = await arenaCnx.redisSmembers(arenaStorage.agentsIndexKey) + const sortedExpectedIds = [...expectedIds].sort() + const actualIds = [...indexIds].sort() + + if(actualIds.length !== sortedExpectedIds.length) { + throw(new Error(`Agent index count mismatch: expected ${sortedExpectedIds.length}, got ${actualIds.length}`)) + } + + for(let i = 0; i < sortedExpectedIds.length; i++) { + if(actualIds[i] !== sortedExpectedIds[i]) { + throw(new Error(`Agent index mismatch at ${i}: expected ${sortedExpectedIds[i]}, got ${actualIds[i]}`)) } } log('success', `Arena agents index contains ${actualIds.length} agent(s)`) - for(const agentId of expectedIds) { + for(const agentId of sortedExpectedIds) { const key = agentHashKey(arenaStorage.agentHashKey, agentId) const hash = await arenaCnx.redisHgetall(key) const expected = expectedById.get(agentId) @@ -218,5 +281,9 @@ export async function run(ctx) { log('success', `Agent ${agentId}: position, vector, and store values match MySQL`) } - log('success', 'Arena store seeded correctly from MySQL (Maestro will wait for agent + primordial daemon readyToStart until timeout)') + if(argv.fakeAgentsReady || argv.fakeGpsReady) { + log('success', 'Arena store seeded correctly from MySQL; fake prepare acks published') + } else { + log('success', 'Arena store seeded correctly from MySQL (Maestro will wait for agent + primordial daemon readyToStart until timeout)') + } }