UNPKG

camstreamerlib

Version:

Helper library for CamStreamer ACAP applications.

159 lines (158 loc) 4.93 kB
import EventEmitter from 'events'; import { WsClient } from './WsClient'; export class CamScripterAPICameraEventsGenerator extends EventEmitter { tls; tlsInsecure; ip; port; user; pass; callId; sendMessages; timeoutCheckTimer; wsConnected; ws; constructor(options) { super(); this.tls = options?.tls ?? false; this.tlsInsecure = options?.tlsInsecure ?? false; this.ip = options?.ip ?? '127.0.0.1'; this.port = options?.port ?? (this.tls ? 443 : 80); this.user = options?.user ?? ''; this.pass = options?.pass ?? ''; this.callId = 0; this.sendMessages = {}; this.wsConnected = false; this.createWsClient(); EventEmitter.call(this); } connect() { this.ws.open(); this.startMsgsTimeoutCheck(); } disconnect() { this.ws.destroy(); this.stopMsgsTimeoutCheck(); } declareEvent(eventDeclaration) { return this.sendMessage({ call_id: 0, command: 'declare_event', data: eventDeclaration, }); } undeclareEvent(eventUndeclaration) { return this.sendMessage({ call_id: 0, command: 'undeclare_event', data: eventUndeclaration, }); } sendEvent(event) { return this.sendMessage({ call_id: 0, command: 'send_event', data: event, }); } createWsClient() { const options = { user: this.user, pass: this.pass, tlsInsecure: this.tlsInsecure, tls: this.tls, ip: this.ip, port: this.port, address: `/local/camscripter/ws`, protocol: 'camera-events', }; this.ws = new WsClient(options); this.ws.onOpen = () => { this.wsConnected = true; this.emit('open'); }; this.ws.onMessage = (data) => this.incomingWsMessageHandler(data.toString()); this.ws.onError = (error) => { this.reportErr(error); }; this.ws.onClose = () => { this.wsConnected = false; this.reportClose(); }; } incomingWsMessageHandler(msgData) { const dataJSON = JSON.parse(msgData); let errorResponse; if ('error' in dataJSON) { errorResponse = dataJSON; } if (dataJSON.call_id !== undefined) { if (errorResponse !== undefined) { this.sendMessages[dataJSON.call_id]?.reject(new Error(errorResponse.error)); } else { this.sendMessages[dataJSON.call_id]?.resolve(dataJSON); } delete this.sendMessages[dataJSON.call_id]; } if (errorResponse !== undefined) { this.reconnectWithError(new Error(errorResponse.error)); } } sendMessage(msgJson) { return new Promise((resolve, reject) => { if (!this.wsConnected) { throw new Error("Websocket hasn't been opened yet."); } try { msgJson.call_id = ++this.callId; this.ws.send(JSON.stringify(msgJson)); this.sendMessages[this.callId] = { resolve, reject, sentTimestamp: Date.now() }; } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Unknown error'; const error = new Error(`Send message error: ${errorMessage}`); reject(error); this.reconnectWithError(error); } }); } startMsgsTimeoutCheck() { clearInterval(this.timeoutCheckTimer); this.timeoutCheckTimer = setInterval(() => { let reconnect = false; const now = Date.now(); for (const callId in this.sendMessages) { const msg = this.sendMessages[callId]; if (!msg) { continue; } if (now - msg.sentTimestamp > 10000) { reconnect = true; msg.reject(new Error('Message timeout')); delete this.sendMessages[callId]; } } if (reconnect) { this.reconnectWithError(new Error('Message timeout')); } }, 5000); } stopMsgsTimeoutCheck() { clearInterval(this.timeoutCheckTimer); } reconnectWithError(err) { this.reportErr(err); this.ws.reconnect(); } reportErr(err) { this.emit('error', err); } reportClose() { for (const callId in this.sendMessages) { this.sendMessages[callId]?.reject(new Error('Connection lost')); } this.sendMessages = {}; this.emit('close'); } }