camstreamerlib
Version:
Helper library for CamStreamer ACAP applications.
221 lines (220 loc) • 8.43 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CamOverlayDrawingAPI = void 0;
const EventEmitter = require("events");
const WsClient_1 = require("./internal/WsClient");
class CamOverlayDrawingAPI extends EventEmitter {
constructor(options) {
var _a, _b, _c, _d, _e, _f, _g;
super();
this.tls = (_a = options === null || options === void 0 ? void 0 : options.tls) !== null && _a !== void 0 ? _a : false;
this.tlsInsecure = (_b = options === null || options === void 0 ? void 0 : options.tlsInsecure) !== null && _b !== void 0 ? _b : false;
this.ip = (_c = options === null || options === void 0 ? void 0 : options.ip) !== null && _c !== void 0 ? _c : '127.0.0.1';
this.port = (_d = options === null || options === void 0 ? void 0 : options.port) !== null && _d !== void 0 ? _d : (this.tls ? 443 : 80);
this.user = (_e = options === null || options === void 0 ? void 0 : options.user) !== null && _e !== void 0 ? _e : '';
this.pass = (_f = options === null || options === void 0 ? void 0 : options.pass) !== null && _f !== void 0 ? _f : '';
this.zIndex = (_g = options === null || options === void 0 ? void 0 : options.zIndex) !== null && _g !== void 0 ? _g : 0;
this.cameraList = [0];
if (options && Array.isArray(options.camera)) {
this.cameraList = options.camera;
}
else if (typeof (options === null || options === void 0 ? void 0 : options.camera) === 'number') {
this.cameraList = [options.camera];
}
this.callId = 0;
this.sendMessages = {};
this.wsConnected = false;
this.createWsClient();
EventEmitter.call(this);
}
connect() {
this.ws.open();
this.startMsgsTimeoutCheck();
}
disconnect() {
this.ws.close();
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.on('open', () => {
console.log('CamOverlay connection opened');
this.wsConnected = true;
this.emit('open');
});
this.ws.on('message', (msgData) => this.incomingWsMessageHandler(msgData));
this.ws.on('error', (error) => {
this.reportError(error);
});
this.ws.on('close', () => {
console.log('CamOverlay connection closed');
this.wsConnected = false;
this.reportClose();
});
}
incomingWsMessageHandler(msgData) {
const dataJSON = JSON.parse(msgData.toString());
let errorResponse;
if ('error' in dataJSON) {
errorResponse = dataJSON;
}
if (dataJSON.call_id !== undefined && this.sendMessages[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);
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 (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;