From 023506a3589436ecd2711fd1a8ade7647e38d99a Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Sun, 29 Sep 2024 10:21:33 +0100 Subject: [PATCH 1/7] fix references for better DX --- nodes/config/ui_base.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nodes/config/ui_base.js b/nodes/config/ui_base.js index f7573a283..d85f680cf 100644 --- a/nodes/config/ui_base.js +++ b/nodes/config/ui_base.js @@ -31,8 +31,8 @@ module.exports = function (RED) { statestore.setConfig(RED) /** - * @typedef {import('socket.io/dist').Socket} Socket - * @typedef {import('socket.io/dist').Server} Server + * @typedef {import('socket.io').Socket} Socket + * @typedef {import('socket.io').Server} Server */ // store state that can maintain cross re-deployments @@ -152,7 +152,7 @@ module.exports = function (RED) { const root = RED.settings.httpNodeRoot || '/' const fullPath = join(root, config.path) const socketIoPath = join('/', fullPath, 'socket.io') - /** @type {import('socket.io/dist').ServerOptions} */ + /** @type {import('socket.io').ServerOptions} */ const serverOptions = { path: socketIoPath, maxHttpBufferSize: uiShared.settings.maxHttpBufferSize || 1e6 // SocketIO default size From c1b9c207ee39e1415aaca1e57cc84d1ee92164d4 Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Sun, 29 Sep 2024 10:24:53 +0100 Subject: [PATCH 2/7] Refactor data-tracker composable to improve socket handling --- ui/src/widgets/data-tracker.mjs | 115 +++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 40 deletions(-) diff --git a/ui/src/widgets/data-tracker.mjs b/ui/src/widgets/data-tracker.mjs index 96ff66f12..f47b26217 100644 --- a/ui/src/widgets/data-tracker.mjs +++ b/ui/src/widgets/data-tracker.mjs @@ -8,7 +8,9 @@ export function useDataTracker (widgetId, onInput, onLoad, onDynamicProperties) } const store = useStore() + /** @type {import('socket.io-client').Socket} */ const socket = inject('$socket') + let emitWidgetLoadOnConnect = false function checkDynamicProperties (msg) { // set standard dynamic properties states if passed into msg @@ -45,55 +47,88 @@ export function useDataTracker (widgetId, onInput, onLoad, onDynamicProperties) } } + function onWidgetLoad (msg, state) { + // console.log('widget-load::msg-input:' + widgetId, msg, state) + // automatic handle state/dynamic updates for ALL widgets + if (state) { + store.commit('ui/widgetState', { + widgetId, + config: state + }) + } + // then see if there is custom onLoad functionality to deal with the latest data payloads + if (onLoad) { + onLoad(msg) + } else { + if (msg) { + store.commit('data/bind', { + widgetId, + msg + }) + } + } + } + + function onMsgInput (msg) { + // console.log('data-tracker::msg-input:' + widgetId, msg) + // check for common dynamic properties cross all widget types + checkDynamicProperties(msg) + + if (onInput) { + // sometimes we need to have different behaviour + onInput(msg) + } else { + // but most of the time, we just care about the value of msg + store.commit('data/bind', { + widgetId, + msg // TODO: we should sanitise what is stored in the store? + // One way to do this is to permit only keys explicitly listed in the widget's config (default to topic+payload if none are specified) + // A smarter? way to do this is to scan the template for msg.? binds and store only those keys + // For now, we'll just store the whole msg + }) + } + } + + function onDisconnect () { + console.log('socket disconnect detected in data-tracker, setting doReconnectSendWidgetLoad to true') + // To get a disconnect, we must have previously been connected. + // Set flag to inform onConnect to emit widget-load + emitWidgetLoadOnConnect = true + } + + function onConnect () { + console.log('socket connect detected in data-tracker' + (emitWidgetLoadOnConnect ? ', emitting widget-load' : '')) + if (emitWidgetLoadOnConnect) { + emitWidgetLoadOnConnect = false + socket.emit('widget-load', widgetId) + } + } + + function removeAllListeners () { + emitWidgetLoadOnConnect = false + socket?.off('disconnect', onDisconnect) + socket?.off('msg-input:' + widgetId, onMsgInput) + socket?.off('widget-load:' + widgetId, onWidgetLoad) + socket?.off('connect', onConnect) + } + // a composable can also hook into its owner component's - // lifecycle to setup and teardown side effects. + // lifecycle to setup and tear-down side effects. onMounted(() => { if (socket && widgetId) { - socket.on('widget-load:' + widgetId, (msg, state) => { - // automatic handle state/dynamic updates for ALL widgets - if (state) { - store.commit('ui/widgetState', { - widgetId, - config: state - }) - } - // then see if there is custom onLoad functionality to deal with the latest data payloads - if (onLoad) { - onLoad(msg) - } else { - if (msg) { - store.commit('data/bind', { - widgetId, - msg - }) - } - } - }) - // This will on in msg input for ALL components - socket.on('msg-input:' + widgetId, (msg) => { - // check for common dynamic properties cross all widget types - checkDynamicProperties(msg) + removeAllListeners() + + socket.on('disconnect', onDisconnect) + socket.on('msg-input:' + widgetId, onMsgInput) + socket.on('widget-load:' + widgetId, onWidgetLoad) + socket.on('connect', onConnect) - if (onInput) { - // sometimes we need to have different behaviour - onInput(msg) - } else { - // but most of the time, we just care about the value of msg - store.commit('data/bind', { - widgetId, - msg // TODO: we should sanitise what is stored in the store? - // One way to do this is to permit only keys explicitly listed in the widget's config (default to topic+payload if none are specified) - // A smarter? way to do this is to scan the template for msg.? binds and store only those keys - // For now, we'll just store the whole msg - }) - } - }) // let Node-RED know that this widget has loaded // useful as Node-RED can return (via msg-input) any stored data socket.emit('widget-load', widgetId) } }) onUnmounted(() => { - socket?.off('msg-input:' + widgetId) + removeAllListeners() }) } From 3fccbf9edea8399cd7f0a5ac2bd3b93e0fadcfec Mon Sep 17 00:00:00 2001 From: Stephen McLaughlin <44235289+Steve-Mcl@users.noreply.github.com> Date: Tue, 1 Oct 2024 17:31:13 +0100 Subject: [PATCH 3/7] Update ui/src/widgets/data-tracker.mjs Co-authored-by: Joe Pavitt <99246719+joepavitt@users.noreply.github.com> --- ui/src/widgets/data-tracker.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/src/widgets/data-tracker.mjs b/ui/src/widgets/data-tracker.mjs index f47b26217..5834062d9 100644 --- a/ui/src/widgets/data-tracker.mjs +++ b/ui/src/widgets/data-tracker.mjs @@ -90,7 +90,6 @@ export function useDataTracker (widgetId, onInput, onLoad, onDynamicProperties) } function onDisconnect () { - console.log('socket disconnect detected in data-tracker, setting doReconnectSendWidgetLoad to true') // To get a disconnect, we must have previously been connected. // Set flag to inform onConnect to emit widget-load emitWidgetLoadOnConnect = true From 6f8bfbbe9dab34722e653cc4ce6ba1bd8564d166 Mon Sep 17 00:00:00 2001 From: Stephen McLaughlin <44235289+Steve-Mcl@users.noreply.github.com> Date: Tue, 1 Oct 2024 17:31:19 +0100 Subject: [PATCH 4/7] Update ui/src/widgets/data-tracker.mjs Co-authored-by: Joe Pavitt <99246719+joepavitt@users.noreply.github.com> --- ui/src/widgets/data-tracker.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/src/widgets/data-tracker.mjs b/ui/src/widgets/data-tracker.mjs index 5834062d9..0a7457c56 100644 --- a/ui/src/widgets/data-tracker.mjs +++ b/ui/src/widgets/data-tracker.mjs @@ -96,7 +96,6 @@ export function useDataTracker (widgetId, onInput, onLoad, onDynamicProperties) } function onConnect () { - console.log('socket connect detected in data-tracker' + (emitWidgetLoadOnConnect ? ', emitting widget-load' : '')) if (emitWidgetLoadOnConnect) { emitWidgetLoadOnConnect = false socket.emit('widget-load', widgetId) From d36afea8e0b1452d8f9400c530f15c4538067c72 Mon Sep 17 00:00:00 2001 From: Stephen McLaughlin <44235289+Steve-Mcl@users.noreply.github.com> Date: Tue, 1 Oct 2024 17:31:25 +0100 Subject: [PATCH 5/7] Update ui/src/widgets/data-tracker.mjs Co-authored-by: Joe Pavitt <99246719+joepavitt@users.noreply.github.com> --- ui/src/widgets/data-tracker.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/src/widgets/data-tracker.mjs b/ui/src/widgets/data-tracker.mjs index 0a7457c56..3305101a3 100644 --- a/ui/src/widgets/data-tracker.mjs +++ b/ui/src/widgets/data-tracker.mjs @@ -70,7 +70,6 @@ export function useDataTracker (widgetId, onInput, onLoad, onDynamicProperties) } function onMsgInput (msg) { - // console.log('data-tracker::msg-input:' + widgetId, msg) // check for common dynamic properties cross all widget types checkDynamicProperties(msg) From 1d9c0fcd86f3d2baaf9d34761b536f408eece9ae Mon Sep 17 00:00:00 2001 From: Stephen McLaughlin <44235289+Steve-Mcl@users.noreply.github.com> Date: Tue, 1 Oct 2024 17:31:31 +0100 Subject: [PATCH 6/7] Update ui/src/widgets/data-tracker.mjs Co-authored-by: Joe Pavitt <99246719+joepavitt@users.noreply.github.com> --- ui/src/widgets/data-tracker.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/src/widgets/data-tracker.mjs b/ui/src/widgets/data-tracker.mjs index 3305101a3..033d0fa79 100644 --- a/ui/src/widgets/data-tracker.mjs +++ b/ui/src/widgets/data-tracker.mjs @@ -48,7 +48,6 @@ export function useDataTracker (widgetId, onInput, onLoad, onDynamicProperties) } function onWidgetLoad (msg, state) { - // console.log('widget-load::msg-input:' + widgetId, msg, state) // automatic handle state/dynamic updates for ALL widgets if (state) { store.commit('ui/widgetState', { From 649363bd97bedd31aabbfb4f6ba1dabc613fbb62 Mon Sep 17 00:00:00 2001 From: Joe Pavitt <99246719+joepavitt@users.noreply.github.com> Date: Fri, 4 Oct 2024 14:17:54 +0100 Subject: [PATCH 7/7] Update ui/src/widgets/data-tracker.mjs --- ui/src/widgets/data-tracker.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/src/widgets/data-tracker.mjs b/ui/src/widgets/data-tracker.mjs index 033d0fa79..cc6669fa8 100644 --- a/ui/src/widgets/data-tracker.mjs +++ b/ui/src/widgets/data-tracker.mjs @@ -94,6 +94,7 @@ export function useDataTracker (widgetId, onInput, onLoad, onDynamicProperties) } function onConnect () { + // when we unexpectedly disconnect, this is set to true if (emitWidgetLoadOnConnect) { emitWidgetLoadOnConnect = false socket.emit('widget-load', widgetId)