maestro refacto to grrom, getpositions actions transfered to Observer, changed to frustum, with subscription

This commit is contained in:
STEINNI
2026-06-14 10:16:56 +00:00
parent f3102d5fbc
commit c399f9ddb4
21 changed files with 760 additions and 185 deletions
+187
View File
@@ -0,0 +1,187 @@
import { positionAt } from '../GPS/actions/arena/worldline.js'
import { Frustum } from './frustum.js'
export class RequestorRegistry {
constructor(reader, getNow, scanIntervalMs, onPush, debug = false) {
this.reader = reader
this.getNow = getNow
this.scanIntervalMs = scanIntervalMs
this.onPush = onPush
this.debug = debug
this.requestors = new Map()
}
#tickTimer = null
#scanInFlight = false
#adjustFrequency(requestedMs) {
if(typeof(requestedMs) !== 'number' || Number.isNaN(requestedMs)) return(null)
if(requestedMs < 300 || requestedMs > 10000) return(null)
const tickMs = this.scanIntervalMs
const ticks = Math.round(requestedMs / tickMs)
const minTicks = Math.ceil(300 / tickMs)
const maxTicks = Math.floor(10000 / tickMs)
const clampedTicks = Math.max(minTicks, Math.min(maxTicks, ticks))
return(clampedTicks * tickMs)
}
async subscribeFrustum(id, { planes, frequency }) {
if(!id || typeof(id) !== 'string') return({ ok: false, err: 'Invalid requestor id' })
const frustum = Frustum.fromPlanes(planes)
if(!frustum) return({ ok: false, err: 'Invalid frustum planes' })
const frequencyMs = this.#adjustFrequency(frequency)
if(frequencyMs === null) {
return({ ok: false, err: 'Invalid frequency (expected 30010000 ms)' })
}
const t = this.getNow()
if(t === null) return({ ok: false, err: 'Simulation not live' })
const agents = await this.reader.loadAllAgents()
const matching = this.#matchAgents(agents, frustum, t)
const pushEveryNTicks = frequencyMs / this.scanIntervalMs
this.requestors.set(id, {
id,
frustum,
tMode: 'live',
subscription: true,
frequencyMs,
pushEveryNTicks,
tickCounter: 0,
agents: matching,
t,
updatedAt: Date.now(),
})
this.#ensureTick()
return({
ok: true,
frequency: frequencyMs,
agents: matching,
t,
})
}
updateRequestor(id, spec) {
const requestor = this.requestors.get(id)
if(!requestor) return({ ok: false, err: 'Unknown requestor' })
if(Array.isArray(spec?.planes)) {
const frustum = Frustum.fromPlanes(spec.planes)
if(!frustum) return({ ok: false, err: 'Invalid frustum planes' })
requestor.frustum = frustum
}
if(typeof(spec?.frequency) === 'number') {
const frequencyMs = this.#adjustFrequency(spec.frequency)
if(frequencyMs === null) return({ ok: false, err: 'Invalid frequency (expected 30010000 ms)' })
requestor.frequencyMs = frequencyMs
requestor.pushEveryNTicks = frequencyMs / this.scanIntervalMs
requestor.tickCounter = 0
}
return({ ok: true, frequency: requestor.frequencyMs })
}
unregisterRequestor(id) {
this.requestors.delete(id)
if(this.requestors.size === 0) this.#stopTick()
return({ ok: true })
}
getRequestorAgents(id) {
const requestor = this.requestors.get(id)
if(!requestor) return(null)
return({
agents: [...requestor.agents],
t: requestor.t,
frequency: requestor.frequencyMs ?? null,
updatedAt: requestor.updatedAt,
})
}
clear() {
this.requestors.clear()
this.#stopTick()
}
async evaluateOnce({ frustum, t }) {
if(!frustum || !(frustum instanceof Frustum)) {
return({ ok: false, err: 'Invalid frustum', agents: [] })
}
if(typeof(t) !== 'number' || Number.isNaN(t)) return({ ok: false, err: 'Invalid simulation time', agents: [] })
const agents = await this.reader.loadAllAgents()
const matching = this.#matchAgents(agents, frustum, t)
return({ ok: true, agents: matching, t })
}
#resolveT(requestor) {
if(requestor.tMode === 'fixed') return(requestor.fixedT)
return(this.getNow())
}
#matchAgents(agents, frustum, t) {
const matching = []
for(const agent of agents.values()) {
const position = positionAt(agent, t)
if(!frustum.containsPoint(position)) continue
matching.push(this.reader.buildAgentSnapshot(agent, t))
}
return(matching)
}
#pushUpdate(requestor) {
if(typeof(this.onPush) !== 'function') return
this.onPush(requestor.id, {
agents: [...requestor.agents],
t: requestor.t,
})
}
async #scanAll() {
if(this.#scanInFlight || this.requestors.size === 0) return
this.#scanInFlight = true
try {
const agents = await this.reader.loadAllAgents()
for(const requestor of this.requestors.values()) {
const t = this.#resolveT(requestor)
if(t === null) {
requestor.agents = []
requestor.t = null
requestor.updatedAt = Date.now()
continue
}
requestor.agents = this.#matchAgents(agents, requestor.frustum, t)
requestor.t = t
requestor.updatedAt = Date.now()
if(!requestor.subscription) continue
requestor.tickCounter++
if(requestor.tickCounter >= requestor.pushEveryNTicks) {
requestor.tickCounter = 0
this.#pushUpdate(requestor)
}
}
if(this.debug) console.log(`[Observer] Scanned ${agents.size} agent(s) for ${this.requestors.size} requestor(s)`)
} catch(err) {
console.error('[Observer] Requestor scan failed:', err)
} finally {
this.#scanInFlight = false
}
}
#ensureTick() {
if(this.#tickTimer) return
this.#tickTimer = setInterval(() => {
this.#scanAll()
}, this.scanIntervalMs)
}
#stopTick() {
if(!this.#tickTimer) return
clearInterval(this.#tickTimer)
this.#tickTimer = null
}
}