class ManageSimView extends WindozDomContent { #lifecycleEventTypes = [ 'onYourMarks', 'bigBang', 'simulationPaused', 'simulationResumed', 'simulationStopped', 'simulationPrepareFailed', ] constructor() { super() Object.assign(this, app.helpers.activeAttributes, app.helpers.basicDialogs) this.lifecycleChan = app.Assets.Store.json.busChannels.maestro.lifecycleChannel this.lifecycleChan = this.lifecycleChan.replace(/\[UID\]/g, app.User.identity.uuid) } async DOMContentLoaded(options) { this.models = options.models this.wasBlured = false this._refreshSeq = 0 this.lifecycleSubscribed = false ui.eicfy(this.el) const view = this this.simGrid = new DataGrid(this.find('.sim-list'), { headers: [ { label: 'Simulation', sortable: true }, { label: 'Primordial frame', sortable: true }, { label: 'Owner', sortable: true }, { label: 'Status', sortable: true }, { label: '', sortable: false }, ], height: '480px', rowActions: [ { icon: '', title: 'Start simulation', severity: 'success', callback: async function(event) { await view.onPlaySim(this, event.currentTarget) }, }, { icon: '', title: 'Pause simulation', severity: 'secondary', callback: async function(event) { await view.onPauseSim(this, event.currentTarget) }, }, { icon: '', title: 'Stop simulation', severity: 'danger', callback: async function(event) { await view.onStopSim(this, event.currentTarget) }, }, ], }) this.simGrid.enableFooter = false await this.refreshSimList() } async DOMContentFocused() { await this.subscribeLifecycle() if(!this.wasBlured) return this.wasBlured = false this.refreshSimList() } async DOMContentBlured() { this.wasBlured = true await this.unsubscribeLifecycle() } async subscribeLifecycle() { if(this.lifecycleSubscribed || !app.MessageBus?.connected) return await app.MessageBus.subscribe([this.lifecycleChan]) for(const eventType of this.#lifecycleEventTypes) { app.MessageBus.addBusListener(eventType, [this.lifecycleChan], this.lifecycleListener, 'ManageSimView') } this.lifecycleSubscribed = true } async unsubscribeLifecycle() { if(!this.lifecycleSubscribed || !app.MessageBus?.connected) return for(const eventType of this.#lifecycleEventTypes) { app.MessageBus.removeBusListener(eventType, this.lifecycleListener, 'ManageSimView') } await app.MessageBus.unSubscribe([this.lifecycleChan]) this.lifecycleSubscribed = false } lifecycleListener(realChan, payload, sender) { console.log('[ManageSimView] maestro lifecycle', { realChan, payload, sender }) } async refreshSimList() { const seq = ++this._refreshSeq this.simGrid.loading = true this.simGrid.clear() try { const data = await this.models.sims.list() if(seq !== this._refreshSeq) return const sims = data?.payload ?? [] this.simGrid.clear() for(const sim of sims) { const row = this.simGrid.addRow( { simulationUuid: sim.simulationUuid }, [ sim.sim_name ?? '', sim.ekf_name ?? '', sim.usr_name ?? '', sim.state ?? 'idle', ], true ) this.updateSimButtons(sim.state ?? 'idle', row) } this.simGrid.updateFilters() } finally { if(seq === this._refreshSeq) this.simGrid.loading = false } } updateSimButtons(state, rowEl) { const buttons = rowEl?.querySelectorAll('.cell.actions button[eicbutton]') if(!buttons || buttons.length < 3) return const enabledByState = { idle: [true, false, false], preparing: [false, false, false], live: [false, true, true], paused: [true, false, true], } const enabled = enabledByState[state] ?? [false, false, false] for(let i = 0; i < 3; i++) { buttons[i].disabled = !enabled[i] } } async onPlaySim(rowId, button) { const simulationUuid = rowId?.simulationUuid if(!simulationUuid) return const row = button.closest('.row') button.disabled = true try { const result = await this.models.sims.startSimulation(simulationUuid) ui.growl.append( `Simulation started (${result.agentIds?.length ?? 0} agent(s))`, 'success', 4000 ) await this.refreshSimList() } catch(err) { ui.growl.append(String(err), 'error', 6000) const state = row?.querySelectorAll('.cell')[4]?.innerText?.trim() || 'idle' this.updateSimButtons(state, row) } } async onPauseSim(rowId, button) { const simulationUuid = rowId?.simulationUuid if(!simulationUuid) return const row = button.closest('.row') button.disabled = true try { await this.models.sims.pauseSimulation(simulationUuid) ui.growl.append('Simulation paused', 'success', 3000) await this.refreshSimList() } catch(err) { ui.growl.append(String(err), 'error', 6000) const state = row?.querySelectorAll('.cell')[4]?.innerText?.trim() || 'idle' this.updateSimButtons(state, row) } } async onStopSim(rowId, button) { const simulationUuid = rowId?.simulationUuid if(!simulationUuid) return const row = button.closest('.row') button.disabled = true try { await this.models.sims.stopSimulation(simulationUuid) ui.growl.append('Simulation stopped', 'success', 3000) await this.refreshSimList() } catch(err) { ui.growl.append(String(err), 'error', 6000) const state = row?.querySelectorAll('.cell')[4]?.innerText?.trim() || 'idle' this.updateSimButtons(state, row) } } } app.registerClass('ManageSimView', ManageSimView)