camstreamerlib
Version:
Helper library for CamStreamer ACAP applications.
239 lines (238 loc) • 8.07 kB
JavaScript
"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;