Skip to content

Commit

Permalink
feat: Action Recorder #1001 (#2125)
Browse files Browse the repository at this point in the history
  • Loading branch information
Julusian authored Sep 28, 2022
1 parent 3e4bc57 commit a228a92
Show file tree
Hide file tree
Showing 28 changed files with 1,683 additions and 370 deletions.
460 changes: 460 additions & 0 deletions lib/Controls/ActionRecorder.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/Controls/ActionRunner.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default class ActionRunner extends CoreBase {
* @param {Registry} registry - the application core
*/
constructor(registry) {
super(registry, 'action-runner', 'Control/ActionRuner')
super(registry, 'action-runner', 'Control/ActionRunner')
}

/**
Expand Down
50 changes: 50 additions & 0 deletions lib/Controls/Button/Base.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,56 @@ export default class ButtonControlBase extends ControlBase {
return false
}

/**
* Append some actions to this button
* @param {string} setId the action_set id to update
* @param {Array} newActions actions to append
* @access public
*/
actionAppend(setId, newActions) {
const action_set = this.action_sets[setId]
if (action_set) {
// Add new actions
for (const action of newActions) {
this.action_sets[setId].push(action)

this.#actionSubscribe(action)
}

return true
}

return false
}

/**
* Replace all the actions in a set
* @param {string} setId the action_set id to update
* @param {Array} newActions actions to populate
* @access public
*/
actionReplaceAll(setId, newActions) {
const action_set = this.action_sets[setId]
if (action_set) {
// Remove the old actions
for (const action of action_set) {
this.cleanupAction(action)
}
this.action_sets[setId] = []

// Add new actions
for (const action of newActions) {
this.action_sets[setId].push(action)

this.#actionSubscribe(action)
}

return true
}

return false
}

/**
* Set the delay of an action
* @param {string} setId the action_set id
Expand Down
11 changes: 11 additions & 0 deletions lib/Controls/Controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import ControlButtonPageUp from './Button/PageUp.js'
import { CreateBankControlId, ParseControlId } from '../Resources/Util.js'
import { ControlConfigRoom } from './ControlBase.js'
import ActionRunner from './ActionRunner.js'
import ActionRecorder from './ActionRecorder.js'

/**
* The class that manages the controls
Expand Down Expand Up @@ -39,6 +40,13 @@ class ControlsController extends CoreBase {
*/
actions

/**
* Actions recorder
* @type {ActionRecorder}
* @access public
*/
actionRecorder

/**
* The currently configured controls
* @access private
Expand All @@ -52,6 +60,7 @@ class ControlsController extends CoreBase {
super(registry, 'controls', 'Controls/Controller')

this.actions = new ActionRunner(registry)
this.actionRecorder = new ActionRecorder(registry)

// Init all the control classes
const config = this.db.getKey('controls', {})
Expand Down Expand Up @@ -81,6 +90,8 @@ class ControlsController extends CoreBase {
* @access public
*/
clientConnect(client) {
this.actionRecorder.clientConnect(client)

client.onPromise('controls:subscribe', (controlId) => {
client.join(ControlConfigRoom(controlId))

Expand Down
89 changes: 21 additions & 68 deletions lib/Graphics/Preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import CoreBase from '../Core/Base.js'
import Registry from '../Registry.js'
import { ControlConfigRoom } from '../Controls/ControlBase.js'

function PreviewPageRoom(page) {
return `preview:page:${page}`
}

/**
* The class that manages bank preview generation/relay for interfaces
*
Expand Down Expand Up @@ -49,10 +53,21 @@ class GraphicsPreview extends CoreBase {
clientConnect(client) {
this.clients[client.id] = client

client.on('bank_preview_page', this.handlePreviewPage.bind(this, client))
client.onPromise('web_buttons', this.handleWebButtons.bind(this, client))
client.onPromise('preview:page:subscribe', (page) => {
client.join(PreviewPageRoom(page))

client.onPromise('web_buttons_page', this.handleWebButtonsPage.bind(this, client))
const renders = {}
for (let i = 1; i <= global.MAX_BUTTONS; ++i) {
renders[i] = this.graphics.getBank(page, i).buffer
}

return renders
})
client.onPromise('preview:page:unsubscribe', (page) => {
client.leave(PreviewPageRoom(page))
})

client.onPromise('web_buttons', this.handleWebButtons.bind(this, client))

client.on('disconnect', () => {
delete this.clients[client.id]
Expand All @@ -77,28 +92,6 @@ class GraphicsPreview extends CoreBase {
return newPage
}

/**
* Send updated previews to the client for page edit
* @param {Socket} client - the client connection
* @param {number} page - the page number
* @param {Object} cache - last update information from the UI
* @access protected
*/
handlePreviewPage(client, page, cache) {
let result = {}

client._previewPage = page

for (let i = 0; i < global.MAX_BUTTONS; ++i) {
const image = this.graphics.getBank(page, i + 1)
if (cache === undefined || cache[i + 1] === undefined || cache[i + 1] != image.updated) {
result[i + 1] = image
}
}

client.emit('preview_page_data', result)
}

/**
* Get all the pages for web buttons
* @param {Socket} client - the client connection
Expand All @@ -117,33 +110,6 @@ class GraphicsPreview extends CoreBase {
return pages
}

/**
* Get a page for web buttons
* @param {Socket} client - the client connection
* @param {number} page - the page number
* @param {Object} cache - last update information from the UI
* @param {?function} answer - the response function to the UI
* @access protected
*/
handleWebButtonsPage(client, page, cache) {
this.logger.silly('handleWebButtonsPage()', page)

let result = {}

if (cache === null) {
return
}

for (let i = 0; i < global.MAX_BUTTONS; ++i) {
const image = this.graphics.getBank(page, i + 1)
if (cache === undefined || cache[i + 1] === undefined || cache[i + 1] != image.updated) {
result[i + 1] = image
}
}

return result
}

/**
* Send a bank update to the UIs
* @param {number} page - the page number
Expand All @@ -158,22 +124,9 @@ class GraphicsPreview extends CoreBase {
this.io.emitToRoom(controlRoom, `controls:preview-${controlId}`, render.buffer)
}

for (const key in this.clients) {
let client = this.clients[key]

if (client.web_buttons) {
let result = {}
result[bank] = render

client.emit('buttons_bank_data', page, result)
} else if (client._previewPage !== undefined) {
if (client._previewPage == page) {
let result = {}
result[bank] = render

client.emit('preview_page_data', result)
}
}
const previewRoom = PreviewPageRoom(page)
if (this.io.countRoomMembers(previewRoom) > 0) {
this.io.emitToRoom(previewRoom, `preview:page-bank`, page, bank, render.buffer)
}
}

Expand Down
20 changes: 14 additions & 6 deletions lib/Instance/Controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class Instance extends CoreBase {
this.store.db = this.db.getKey('instance', {})

// Prepare for clients already
this.doSave()
this.commitChanges()

this.registry.api_router.get('/help/module/:module_id/*', (req, res, next) => {
const module_id = req.params.module_id.replace(/\.\.+/g, '')
Expand Down Expand Up @@ -206,7 +206,7 @@ class Instance extends CoreBase {
this.definitions.updateVariablePrefixesForLabel(id, newLabel)
}

this.doSave()
this.commitChanges()

const instance = this.instance.moduleHost.getChild(id)
if (newLabel) {
Expand Down Expand Up @@ -270,7 +270,7 @@ class Instance extends CoreBase {
this.activate_module(id, true)

this.logger.silly('instance_add', id)
this.doSave()
this.commitChanges()

return id
}
Expand Down Expand Up @@ -313,7 +313,7 @@ class Instance extends CoreBase {
this.activate_module(id)
}

this.doSave()
this.commitChanges()
} else {
if (state === true) {
this.logger.warn(id, 'warn', `Attempting to enable connection "${label}" that is already enabled`)
Expand All @@ -337,7 +337,7 @@ class Instance extends CoreBase {
this.status.forgetInstanceStatus(id)
delete this.store.db[id]

this.doSave()
this.commitChanges()

// forward cleanup elsewhere
this.definitions.forgetInstance(id)
Expand Down Expand Up @@ -393,7 +393,7 @@ class Instance extends CoreBase {
* Save the instances config to the db, and inform clients
* @access protected
*/
doSave() {
commitChanges() {
this.db.setKey('instance', this.store.db)

const newJson = cloneDeep(this.getClientJson())
Expand Down Expand Up @@ -556,6 +556,14 @@ class Instance extends CoreBase {
instance_type: config.instance_type,
label: config.label,
enabled: config.enabled,

// Runtime properties
hasRecordActionsHandler: false,
}

const instance = this.moduleHost.getChild(id)
if (instance) {
result[id].hasRecordActionsHandler = instance.hasRecordActionsHandler
}
}

Expand Down
8 changes: 8 additions & 0 deletions lib/Instance/Host.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@ class ModuleHost {
.init(config)
.then(() => {
child.restartCount = 0

// Make sure clients are informed about any runtime properties
this.registry.instance.commitChanges()

// Inform action recorder
this.registry.controls.actionRecorder.instanceStatusChange(connectionId, true)
})
.catch((e) => {
this.logger.warn(`Instance "${config.label || child.connectionId}" failed to init: ${e} ${e?.stack}`)
Expand Down Expand Up @@ -350,6 +356,8 @@ class ModuleHost {
child.crashed ? 'Crashed' : 'Stopped'
)
this.logger.debug(`Connection "${config.label}" stopped`)

this.registry.controls.actionRecorder.instanceStatusChange(connectionId, false)
})
monitor.on('crash', () => {
this.instanceStatus.updateInstanceStatus(connectionId, null, 'Crashed')
Expand Down
29 changes: 29 additions & 0 deletions lib/Instance/Wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class SocketEventsHandler {
this.socket = socket
this.connectionId = connectionId
this.hasHttpHandler = false
this.hasRecordActionsHandler = false

this.unsubListeners = this.#listenToEvents({
'log-message': this.#handleLogMessage.bind(this),
Expand All @@ -41,6 +42,7 @@ class SocketEventsHandler {
'send-osc': this.#handleSendOsc.bind(this),
parseVariablesInString: this.#handleParseVariablesInString.bind(this),
upgradedItems: this.#handleUpgradedItems.bind(this),
recordAction: this.#handleRecordAction.bind(this),
})
}

Expand Down Expand Up @@ -109,6 +111,7 @@ class SocketEventsHandler {

// Save the resulting values
this.hasHttpHandler = !!msg.hasHttpHandler
this.hasRecordActionsHandler = !!msg.hasRecordActionsHandler
config.lastUpgradeIndex = msg.newUpgradeIndex
this.registry.instance.setInstanceLabelAndConfig(this.connectionId, null, msg.updatedConfig, true)
}
Expand Down Expand Up @@ -599,6 +602,22 @@ class SocketEventsHandler {
}
}

/**
* Handle action recorded by the instance
*/
async #handleRecordAction(msg) {
try {
this.registry.controls.actionRecorder.receiveAction(
this.connectionId,
msg.actionId,
msg.options,
msg.uniquenessId
)
} catch (e) {
this.logger.error(`Record action failed: ${e}`)
}
}

/**
* Handle the module informing us of some actions/feedbacks which have been run through upgrade scripts
*/
Expand Down Expand Up @@ -646,6 +665,16 @@ class SocketEventsHandler {
}
}
}

/**
* Inform the child instance class to start or stop recording actions
* @param {boolean} recording
*/
async startStopRecordingActions(recording) {
await socketEmit(this.socket, 'startStopRecordActions', {
recording: recording,
})
}
}

export default SocketEventsHandler
Loading

0 comments on commit a228a92

Please sign in to comment.