UNPKG

node-labstreaminglayer

Version:
224 lines (200 loc) 6.01 kB
import { lsl_resolve_all, lsl_resolve_byprop, lsl_resolve_bypred, lsl_create_continuous_resolver, lsl_create_continuous_resolver_byprop, lsl_create_continuous_resolver_bypred, lsl_destroy_continuous_resolver, lsl_resolver_results } from './lib/index.js'; import { StreamInfo } from './streamInfo.js'; import { FOREVER } from './util.js'; /** * Find all streams on the network. * @param waitTime Time to wait for streams in seconds (default: 1.0) * @returns Array of StreamInfo objects */ export function resolveStreams(waitTime: number = 1.0): StreamInfo[] { // Create buffer for stream info pointers const bufferSize = 1024; const buffer = new Array(bufferSize).fill(null); // Resolve streams const numFound = lsl_resolve_all(buffer, bufferSize, waitTime); // Convert to StreamInfo objects const results: StreamInfo[] = []; for (let i = 0; i < numFound; i++) { const handle = buffer[i]; if (handle) { results.push(new StreamInfo('', '', 0, 0, 0, '', handle)); } } return results; } /** * Find streams by a specific property. * @param prop Property name to match (e.g., 'name', 'type') * @param value Property value to match * @param minimum Minimum number of streams to find * @param timeout Timeout in seconds * @returns Array of StreamInfo objects */ export function resolveByProp( prop: string, value: string, minimum: number = 1, timeout: number = FOREVER ): StreamInfo[] { // Create buffer for stream info pointers const bufferSize = 1024; const buffer = new Array(bufferSize).fill(null); // Resolve streams by property const numFound = lsl_resolve_byprop( buffer, bufferSize, prop, value, minimum, timeout ); // Convert to StreamInfo objects const results: StreamInfo[] = []; for (let i = 0; i < numFound; i++) { const handle = buffer[i]; if (handle) { results.push(new StreamInfo('', '', 0, 0, 0, '', handle)); } } return results; } /** * Find streams by an XPath predicate. * @param predicate XPath predicate string (e.g., "name='MyStream' and type='EEG'") * @param minimum Minimum number of streams to find * @param timeout Timeout in seconds * @returns Array of StreamInfo objects */ export function resolveByPred( predicate: string, minimum: number = 1, timeout: number = FOREVER ): StreamInfo[] { // Create buffer for stream info pointers const bufferSize = 1024; const buffer = new Array(bufferSize).fill(null); // Resolve streams by predicate const numFound = lsl_resolve_bypred( buffer, bufferSize, predicate, minimum, timeout ); // Convert to StreamInfo objects const results: StreamInfo[] = []; for (let i = 0; i < numFound; i++) { const handle = buffer[i]; if (handle) { results.push(new StreamInfo('', '', 0, 0, 0, '', handle)); } } return results; } // Legacy compatibility function export function resolveStream(...args: any[]): StreamInfo[] { if (args.length === 0) { return resolveStreams(); } else if (typeof args[0] === 'number') { return resolveStreams(args[0]); } else if (typeof args[0] === 'string') { if (args.length === 1) { return resolveByPred(args[0]); } else if (typeof args[1] === 'number') { return resolveByPred(args[0], args[1]); } else { if (args.length === 2) { return resolveByProp(args[0], args[1]); } else { return resolveByProp(args[0], args[1], args[2]); } } } throw new Error('Invalid arguments for resolveStream'); } // FinalizationRegistry for automatic cleanup const resolverRegistry = new FinalizationRegistry((obj: any) => { try { lsl_destroy_continuous_resolver(obj); } catch (e) { // Silently ignore cleanup errors } }); /** * Continuously resolves streams in the background. * Useful for monitoring streams that may appear and disappear. */ export class ContinuousResolver { private obj: any; // Pointer to the continuous resolver object constructor( prop?: string, value?: string, pred?: string, forgetAfter: number = 5.0 ) { if (pred !== undefined) { if (prop !== undefined || value !== undefined) { throw new Error( 'You can only either pass the prop/value argument or the pred argument, but not both.' ); } this.obj = lsl_create_continuous_resolver_bypred(pred, forgetAfter); } else if (prop !== undefined && value !== undefined) { this.obj = lsl_create_continuous_resolver_byprop(prop, value, forgetAfter); } else if (prop !== undefined || value !== undefined) { throw new Error( 'If prop is specified, then value must be specified too, and vice versa.' ); } else { this.obj = lsl_create_continuous_resolver(forgetAfter); } if (!this.obj) { throw new Error('Could not create continuous resolver.'); } // Register for automatic cleanup resolverRegistry.register(this, this.obj, this); } /** * Destroy the resolver and free resources. */ destroy(): void { if (this.obj) { try { resolverRegistry.unregister(this); lsl_destroy_continuous_resolver(this.obj); } catch (e) { // Silently ignore errors during destruction } this.obj = null; } } /** * Get the current list of resolved streams. * @returns Array of StreamInfo objects */ results(): StreamInfo[] { // Create buffer for stream info pointers const bufferSize = 1024; const buffer = new Array(bufferSize).fill(null); // Get resolver results const numFound = lsl_resolver_results(this.obj, buffer, bufferSize); // Convert to StreamInfo objects const results: StreamInfo[] = []; for (let i = 0; i < numFound; i++) { const handle = buffer[i]; if (handle) { results.push(new StreamInfo('', '', 0, 0, 0, '', handle)); } } return results; } }