import { AccesRights } from '../accesRights.js' import { GpsStorageReader } from './gpsStorageReader.js' import { RequestorRegistry } from './requestorRegistry.js' import { SimState } from '../GPS/simulationState.js' export class observerServer { constructor(configHelper, allRediscnx, debug) { this.configHelper = configHelper this.observerConfig = configHelper.config this.allRediscnx = allRediscnx this.debug = debug this.accessRights = new AccesRights(this.observerConfig, debug) this.arenaCnx = null this.arenaCnxs = [] this.systemCnx = null this.gpsStorageReader = null this.requestorRegistry = null this.state = SimState.IDLE this.bigBangEpoch = null } getObserverSettings() { const observer = this.observerConfig.observer ?? {} return({ senderId: observer.senderId ?? 'observer', scanIntervalMs: observer.scanIntervalMs ?? 300, frustumEventsChannel: observer.observerFrustumEventsChannel ?? 'system:observer:subscribed[UID]:agents', lifecycle: { arenaChannel: observer.lifecycle?.arenaChannel ?? 'arena:lifecycle', godsReadyChannel: observer.lifecycle?.godsReadyChannel ?? 'arena:gods:ready', }, }) } getGpsStorageSettings() { const gps = this.observerConfig.gps ?? {} return(gps.GPSstorage ?? null) } initGpsStorageReader() { const gpsStorage = this.getGpsStorageSettings() if(gpsStorage && this.systemCnx) { this.gpsStorageReader = new GpsStorageReader(this.systemCnx, gpsStorage, this.debug) this.initRequestorRegistry() } } initRequestorRegistry() { if(!this.gpsStorageReader || this.requestorRegistry) return const { scanIntervalMs } = this.getObserverSettings() this.requestorRegistry = new RequestorRegistry( this.gpsStorageReader, () => this.now(), scanIntervalMs, (subscriberId, payload) => this.publishFrustumAgentEvents( subscriberId, payload.agents, payload.t ), this.debug ) } async publishFrustumAgentEvents(subscriberId, agents, t) { if(!this.systemCnx || !subscriberId) return if(!Array.isArray(agents) || !agents.length) return const { frustumEventsChannel } = this.getObserverSettings() const chan = frustumEventsChannel.replace(/\[UID\]/g, subscriberId) const senderId = this.getObserverSettings().senderId for(const agent of agents) { if(!agent?.id || !agent?.position) continue await this.systemCnx.redisPublish(chan, { eventType: 'move', sender: senderId, payload: { aid: agent.id, coords: { x: agent.position.x, y: agent.position.y, z: agent.position.z, }, t, }, }) } if(this.debug) { console.log(`[Observer] Frustum events: ${agents.length} agent(s) on ${chan} at t=${t}`) } } isLive() { return(this.state === SimState.LIVE) } simNow() { if(this.bigBangEpoch === null) return(null) return((performance.now() - this.bigBangEpoch) / 1000) } now() { if(this.isLive()) return(this.simNow()) return(null) } onYourMarks() { this.state = SimState.PREPARE this.bigBangEpoch = null this.requestorRegistry?.clear() } onBigBang() { this.bigBangEpoch = performance.now() this.state = SimState.LIVE } onSimulationStopped(payload = {}) { this.requestorRegistry?.clear() this.state = SimState.IDLE this.bigBangEpoch = null if(this.debug) console.log(`[Observer] simulationStopped at t=${payload.t}`) } wireSystemConnexion(cnx) { cnx.observerSrv = this cnx.accessRights = this.accessRights cnx.reloadAccessRights = () => this.reloadAccessRights() cnx.getAccessRights = () => this.getAccessRights() if(!this.systemCnx || cnx.redisConfig.role === 'primary') { this.systemCnx = cnx this.initGpsStorageReader() } } wireArenaConnexion(cnx) { cnx.observerSrv = this this.arenaCnxs.push(cnx) if(!this.arenaCnx || cnx.redisConfig.role === 'primary') { this.arenaCnx = cnx } } async reloadAccessRights() { await this.configHelper.refreshAccessRights() this.observerConfig.accessRights = this.configHelper.config.accessRights this.accessRights.refreshAccessRights(this.observerConfig) } getAccessRights() { return(this.observerConfig.accessRights) } }