Skip to content

Commit 16404ff

Browse files
authored
Merge pull request #171 from bitfocus/169-bug-udp-socket-bottleneck-osc-messages-not-all-being-recognized
fix variable handler bottleneck preventing variables from being set fix feedback handling
2 parents 7645d50 + c731013 commit 16404ff

25 files changed

+546
-438
lines changed

src/actions/common.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,9 @@ export function createCommonActions(self: InstanceBaseExt<WingConfig>): Companio
9595
const send = self.connection!.sendCommand.bind(self.connection)
9696
const ensureLoaded = self.stateHandler!.ensureLoaded.bind(self.stateHandler)
9797
const state = self.stateHandler?.state
98+
const logger = self.logger
9899
if (!state) {
99-
self.logger!.error('State handler or state is not available for creating common actions')
100+
logger?.error('State handler or state is not available for creating common actions')
100101
throw new Error('State handler or state is not available')
101102
}
102103
const transitions = self.transitions

src/config.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ export interface WingConfig {
2121
variableUpdateRate?: number
2222
/** When enabled, the module will request values for all variables on startup */
2323
prefetchVariablesOnStartup?: boolean
24+
startupVariableRequestChunkSize?: number
25+
startupVariableRequestChunkWait?: number
2426

2527
// Advanced Option
2628
requestTimeout?: number
@@ -128,7 +130,6 @@ export function GetConfigFields(_self: InstanceBaseExt<WingConfig>): SomeCompani
128130
width: 6,
129131
default: true,
130132
},
131-
spacer,
132133
{
133134
type: 'checkbox',
134135
id: 'show-advanced-options',
@@ -138,6 +139,7 @@ export function GetConfigFields(_self: InstanceBaseExt<WingConfig>): SomeCompani
138139
width: 12,
139140
default: false,
140141
},
142+
spacer,
141143
{
142144
type: 'number',
143145
id: 'requestTimeout',
@@ -159,6 +161,28 @@ export function GetConfigFields(_self: InstanceBaseExt<WingConfig>): SomeCompani
159161
default: false,
160162
isVisibleExpression: `$(options:show-advanced-options) == true`,
161163
},
164+
{
165+
type: 'number',
166+
id: 'startupVariableRequestChunkSize',
167+
label: `Variable Request Chunk`,
168+
tooltip: 'When are requested in chunks on startup, waiting between each chunk',
169+
width: 6,
170+
min: 200,
171+
max: 2000,
172+
default: 500,
173+
isVisibleExpression: `$(options:show-advanced-options) == true`,
174+
},
175+
{
176+
type: 'number',
177+
id: 'startupVariableRequestChunkWait',
178+
label: `Variable Request Wait Time (ms)`,
179+
tooltip: 'When are requested in chunks on startup, waiting between each chunk',
180+
width: 6,
181+
min: 20,
182+
max: 1000,
183+
default: 100,
184+
isVisibleExpression: `$(options:show-advanced-options) == true`,
185+
},
162186
{
163187
type: 'number',
164188
id: 'subscriptionInterval',
@@ -170,7 +194,6 @@ export function GetConfigFields(_self: InstanceBaseExt<WingConfig>): SomeCompani
170194
default: 9000,
171195
isVisibleExpression: `$(options:show-advanced-options) == true`,
172196
},
173-
spacer,
174197
{
175198
type: 'static-text',
176199
id: 'osc-forwarding-info',

src/handlers/connection-handler.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import osc, { OscMessage } from 'osc'
2-
import { ModuleLogger } from './logger.js'
2+
33
import { EventEmitter } from 'events'
44
import { OSCSomeArguments } from '@companion-module/base'
5+
import { ModuleLogger } from './logger.js'
56

67
/**
78
* Handles OSC UDP connection management, message sending, and event emission for the Behringer Wing module.
89
* Emits 'message', 'error', and 'close' events.
910
*/
1011
export class ConnectionHandler extends EventEmitter {
1112
private osc: osc.UDPPort
12-
private logger?: ModuleLogger
1313
private subscriptionTimer?: NodeJS.Timeout
1414
private subscriptionInterval: number = 9000
15+
private logger?: ModuleLogger
1516

1617
/**
1718
* Create a new ConnectionHandler.
@@ -60,7 +61,8 @@ export class ConnectionHandler extends EventEmitter {
6061
})
6162

6263
this.osc.on('message', (msg: OscMessage) => {
63-
this.logger?.debug(`Received ${JSON.stringify(msg)}`)
64+
const stringValue = (msg.args as osc.MetaArgument[])[0].value
65+
this.logger?.debug(`State updated for ${msg.address}: ${stringValue}`)
6466
this.emit('message', msg)
6567
})
6668

@@ -157,8 +159,7 @@ export class ConnectionHandler extends EventEmitter {
157159
args: args,
158160
}
159161
this.osc.send(command)
160-
// this.osc.send({ address: cmd, args: [] }) // a bit ugly, but needed to keep the desk state up to date in companion
161162
if (preventLog) return
162-
this.logger?.debug(`Sending OSC command: ${JSON.stringify(command)}`)
163+
this.logger?.debug(`Sending OSC command: ${command.address} ${argument ?? ''}`)
163164
}
164165
}

src/handlers/device-detector.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import osc from 'osc'
22
import { WingModel } from '../models/types.js'
3-
import { ModuleLogger } from './logger.js'
43
import { EventEmitter } from 'events'
4+
import { ModuleLogger } from './logger.js'
55

66
export interface DeviceInfo {
77
deviceName: string
@@ -14,6 +14,7 @@ export interface WingDeviceDetectorInterface {
1414
subscribe(instanceId: string): void
1515
unsubscribe(instanceId: string): void
1616
listKnown(): DeviceInfo[]
17+
addLogger(logger: ModuleLogger): void
1718
}
1819

1920
/**
@@ -25,23 +26,29 @@ export class WingDeviceDetector extends EventEmitter implements WingDeviceDetect
2526
private osc?: osc.UDPPort
2627
private knownDevices = new Map<string, DeviceInfo>()
2728
private queryTimer: NodeJS.Timeout | undefined
28-
private logger?: ModuleLogger
2929
private noDeviceTimeout: NodeJS.Timeout | undefined
30+
private logger?: ModuleLogger
3031

3132
constructor(logger?: ModuleLogger) {
3233
super()
3334
this.logger = logger
3435
}
3536

37+
/**
38+
* Add a logger
39+
* @param logger An instance of a ModuleLogger
40+
*/
41+
public addLogger(logger: ModuleLogger): void {
42+
this.logger = logger
43+
}
44+
3645
/**
3746
* Register a subscriber to start device detection.
3847
* @param instanceId Unique identifier for the subscriber.
3948
*/
4049
public subscribe(instanceId: string): void {
4150
const startListening = this.subscribers.size === 0
42-
4351
this.subscribers.add(instanceId)
44-
4552
if (startListening) {
4653
this.startListening()
4754
}
@@ -71,7 +78,6 @@ export class WingDeviceDetector extends EventEmitter implements WingDeviceDetect
7178
*/
7279
private startListening(): void {
7380
this.knownDevices.clear()
74-
7581
if (this.subscribers.size === 0) {
7682
return
7783
}
@@ -112,6 +118,7 @@ export class WingDeviceDetector extends EventEmitter implements WingDeviceDetect
112118
})
113119

114120
this.osc.on('message', (message): void => {
121+
this.logger?.debug(`received device detector message ${JSON.stringify(message)}`)
115122
const args = message.args as osc.MetaArgument[]
116123
if (!args || args.length === 0 || args[0].type !== 's') {
117124
return
@@ -141,7 +148,7 @@ export class WingDeviceDetector extends EventEmitter implements WingDeviceDetect
141148
// If a device has not been seen for over a minute, remove it
142149
this.knownDevices.set(info.address, info)
143150
for (const [id, data] of Array.from(this.knownDevices.entries())) {
144-
if (data.lastSeen < Date.now() - 60000) {
151+
if (data.lastSeen < Date.now() - 20000) {
145152
this.logger?.info(`Removing console ${data.deviceName} at ${data.address} from known devices due to timeout`)
146153
this.knownDevices.delete(id)
147154
}
@@ -152,7 +159,6 @@ export class WingDeviceDetector extends EventEmitter implements WingDeviceDetect
152159
this.noDeviceTimeout = undefined
153160
}
154161
})
155-
156162
this.osc.open()
157163
}
158164

src/handlers/feedback-handler.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import EventEmitter from 'events'
2-
import { ModuleLogger } from './logger.js'
32
import debounceFn from 'debounce-fn'
43
import { FeedbackId } from '../feedbacks.js'
54
import { OscMessage } from 'osc'
65
import { WingSubscriptions } from '../state/index.js'
6+
import { ModuleLogger } from './logger.js'
77

88
/**
99
* Handles feedback updates based on incoming OSC messages and manages feedback subscriptions.
@@ -12,9 +12,9 @@ import { WingSubscriptions } from '../state/index.js'
1212
export class FeedbackHandler extends EventEmitter {
1313
private readonly messageFeedbacks = new Set<FeedbackId>()
1414
private readonly debounceMessageFeedbacks: () => void
15-
private logger?: ModuleLogger
1615
private pollTimeout?: NodeJS.Timeout
1716
private pollInterval: number = 3000
17+
private logger: ModuleLogger | undefined
1818

1919
subscriptions?: WingSubscriptions
2020

@@ -24,8 +24,8 @@ export class FeedbackHandler extends EventEmitter {
2424
*/
2525
constructor(logger?: ModuleLogger) {
2626
super()
27-
this.logger = logger
2827

28+
this.logger = logger
2929
this.subscriptions = new WingSubscriptions()
3030

3131
this.debounceMessageFeedbacks = debounceFn(
@@ -47,16 +47,17 @@ export class FeedbackHandler extends EventEmitter {
4747
* Process an OSC message and trigger feedback checks if needed.
4848
* @param msg OSC message to process.
4949
*/
50-
processMessage(msg: OscMessage): void {
51-
this.logger?.debug(`Processing message for feedbacks: ${msg.address}`)
52-
53-
const toUpdate = this.subscriptions?.getFeedbacks(msg.address)
54-
if (toUpdate === undefined) {
55-
return
56-
}
57-
if (toUpdate.length > 0) {
58-
toUpdate.forEach((f) => this.messageFeedbacks.add(f))
59-
this.debounceMessageFeedbacks()
50+
processMessage(msgs: Set<OscMessage>): void {
51+
this.logger?.debug(`Processing messages for feedbacks`)
52+
for (const msg of msgs) {
53+
const toUpdate = this.subscriptions?.getFeedbacks(msg.address)
54+
if (toUpdate === undefined) {
55+
continue
56+
}
57+
if (toUpdate.length > 0) {
58+
toUpdate.forEach((f) => this.messageFeedbacks.add(f))
59+
this.debounceMessageFeedbacks()
60+
}
6061
}
6162
}
6263

src/handlers/logger.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,22 @@ type LoggerFunction = (level: LogLevel, message: string) => void
88
export class ModuleLogger {
99
private moduleName: string
1010
private loggerFn?: LoggerFunction
11+
private enabled: boolean
1112
debugMode: boolean
1213
timestamps: boolean
1314

1415
/**
1516
* Create a new ModuleLogger.
17+
*
1618
* @param moduleName Name to prefix log messages.
1719
* @param loggerFn Optional custom log function.
18-
* @param debugMode Whether to include source location information in logs.
19-
* @param timestamps Whether to include timestamps in logs.
2020
*/
2121
constructor(moduleName: string, loggerFn?: LoggerFunction) {
2222
this.moduleName = moduleName
2323
this.loggerFn = loggerFn
2424
this.debugMode = false
2525
this.timestamps = false
26+
this.enabled = true
2627
}
2728

2829
/**
@@ -38,6 +39,7 @@ export class ModuleLogger {
3839
* @param message Message to log.
3940
*/
4041
debug(message: string): void {
42+
if (!this.enabled) return
4143
const msg = this.formatMessage(message)
4244
if (this.loggerFn) {
4345
this.loggerFn('debug', `[${this.moduleName}] ${msg}`)
@@ -51,6 +53,7 @@ export class ModuleLogger {
5153
* @param message Message to log.
5254
*/
5355
info(message: string): void {
56+
if (!this.enabled) return
5457
const msg = this.formatMessage(message)
5558
if (this.loggerFn) {
5659
this.loggerFn('info', `[${this.moduleName}] ${msg}`)
@@ -64,6 +67,7 @@ export class ModuleLogger {
6467
* @param message Message to log.
6568
*/
6669
error(message: string): void {
70+
if (!this.enabled) return
6771
const msg = this.formatMessage(message)
6872
if (this.loggerFn) {
6973
this.loggerFn('error', `[${this.moduleName}] ${msg}`)
@@ -77,6 +81,7 @@ export class ModuleLogger {
7781
* @param message Message to log.
7882
*/
7983
warn(message: string): void {
84+
if (!this.enabled) return
8085
const msg = this.formatMessage(message)
8186
if (this.loggerFn) {
8287
this.loggerFn('warn', `[${this.moduleName}] ${msg}`)
@@ -85,6 +90,14 @@ export class ModuleLogger {
8590
}
8691
}
8792

93+
enable(): void {
94+
this.enabled = true
95+
}
96+
97+
disable(): void {
98+
this.enabled = false
99+
}
100+
88101
/**
89102
* Format the message with optional timestamp and source location.
90103
* @private

src/handlers/osc-forwarder.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,22 @@ import { ModuleLogger } from './logger.js'
44

55
export class OscForwarder {
66
private port: osc.UDPPort | undefined
7-
private logger: ModuleLogger
7+
private logger: ModuleLogger | undefined
88

9-
constructor(logger: ModuleLogger) {
9+
constructor(logger?: ModuleLogger) {
1010
this.logger = logger
1111
}
1212

13-
setup(enabled: boolean | undefined, host?: string, port?: number): void {
13+
setup(enabled: boolean | undefined, host?: string, port?: number, logger?: ModuleLogger): void {
1414
this.close()
1515

16+
this.logger = logger
17+
1618
if (!enabled || !host || !port) {
1719
return
1820
}
1921

20-
this.logger.info(`Setting up OSC forwarder to ${host}:${port}`)
22+
this.logger?.info(`Setting up OSC forwarder to ${host}:${port}`)
2123
try {
2224
this.port = new osc.UDPPort({
2325
localAddress: '0.0.0.0',
@@ -28,21 +30,21 @@ export class OscForwarder {
2830
})
2931

3032
this.port.on('error', (err: Error): void => {
31-
this.logger.warn(`OSC Forwarder Error: ${err.message}`)
33+
this.logger?.warn(`OSC Forwarder Error: ${err.message}`)
3234
})
3335

3436
this.port.open()
35-
this.logger.info(`OSC forwarding enabled to ${host}:${port}`)
37+
this.logger?.info(`OSC forwarding enabled to ${host}:${port}`)
3638
} catch (err: any) {
37-
this.logger.error(`Failed to setup OSC forwarder: ${err?.message ?? err}`)
39+
this.logger?.error(`Failed to setup OSC forwarder: ${err?.message ?? err}`)
3840
}
3941
}
4042

4143
send(message: OscMessage): void {
4244
try {
4345
this.port?.send(message)
4446
} catch (err: any) {
45-
this.logger.warn(`OSC forward send failed: ${err?.message ?? err}`)
47+
this.logger?.warn(`OSC forward send failed: ${err?.message ?? err}`)
4648
}
4749
}
4850

0 commit comments

Comments
 (0)