UNPKG

camstreamerlib

Version:

Helper library for CamStreamer ACAP applications.

239 lines (238 loc) 8.07 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.CamOverlayDrawingAPI = void 0; const events_1 = __importDefault(require("events")); const WsClient_1 = require("./WsClient"); class CamOverlayDrawingAPI extends events_1.default { tls; tlsInsecure; ip; port; user; pass; cameraList; zIndex; 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.zIndex = options?.zIndex ?? 0; this.cameraList = [0]; if (options && Array.isArray(options.camera)) { this.cameraList = options.camera; } else if (typeof options?.camera === 'number') { this.cameraList = [options.camera]; } this.callId = 0; this.sendMessages = {}; this.wsConnected = false; this.createWsClient(); events_1.default.call(this); } connect() { this.ws.open(); this.startMsgsTimeoutCheck(); } disconnect() { this.ws.destroy(); this.stopMsgsTimeoutCheck(); } isConnected() { return this.wsConnected; } cairo(command, ...params) { return this.sendMessage({ command: command, params: params }); } writeText(...params) { return this.sendMessage({ command: 'write_text', params: params }); } uploadImageData(imgBuffer) { return this.sendBinaryMessage({ command: 'upload_image_data', params: [], }, imgBuffer); } uploadFontData(fontBuffer) { return this.sendBinaryMessage({ command: 'upload_font_data', params: [fontBuffer.toString('base64')], }, fontBuffer); } showCairoImage(cairoImage, posX, posY) { return this.sendMessage({ command: 'show_cairo_image_v2', params: [cairoImage, posX, posY, this.cameraList, this.zIndex], }); } showCairoImageAbsolute(cairoImage, posX, posY, width, height) { return this.sendMessage({ command: 'show_cairo_image_v2', params: [ cairoImage, -1.0 + (2.0 / width) * posX, -1.0 + (2.0 / height) * posY, this.cameraList, this.zIndex, ], }); } removeImage() { return this.sendMessage({ command: 'remove_image_v2', params: [] }); } createWsClient() { const options = { ip: this.ip, port: this.port, address: '/local/camoverlay/ws', protocol: 'cairo-api', user: this.user, pass: this.pass, tls: this.tls, tlsInsecure: this.tlsInsecure, }; this.ws = new WsClient_1.WsClient(options); this.ws.onOpen = () => { console.log('CamOverlay connection opened'); this.wsConnected = true; this.emit('open'); }; this.ws.onMessage = (data) => this.incomingWsMessageHandler(data.toString()); this.ws.onError = (error) => { this.reportError(error); }; this.ws.onClose = () => { console.log('CamOverlay connection closed'); 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)); } else { this.reportMessage(msgData.toString()); } } sendMessage(msgJson) { return new Promise((resolve, reject) => { try { if (!this.wsConnected) { throw new Error('No CamOverlay connection'); } 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); if (!this.wsConnected) { this.reportError(error); } else { this.reconnectWithError(error); } } }); } sendBinaryMessage(msgJson, data) { return new Promise((resolve, reject) => { try { if (!this.wsConnected) { throw new Error('No CamOverlay connection'); } msgJson.call_id = ++this.callId; const jsonBuffer = Buffer.from(JSON.stringify(msgJson)); const header = new ArrayBuffer(5); const headerView = new DataView(header); headerView.setInt8(0, 1); headerView.setInt32(1, jsonBuffer.byteLength); const msgBuffer = Buffer.concat([Buffer.from(header), jsonBuffer, data]); this.ws.send(msgBuffer.buffer); 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 binary message error: ${errorMessage}`); reject(error); if (!this.wsConnected) { this.reportError(error); } else { 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.reportError(err); this.ws.reconnect(); } reportMessage(msg) { this.emit('message', msg); } reportError(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'); } } exports.CamOverlayDrawingAPI = CamOverlayDrawingAPI;