@br8kppooint/visca
Version:
Advanced nodejs library for working with VISCA-based PTZ cameras over IP and over serial connections.
386 lines • 21.4 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Camera = exports.CameraStatus = exports.CamWideDParams = exports.CamImageData = exports.CamLensData = exports.PTStatus = void 0;
const events_1 = require("events");
const command_1 = require("./command");
const constants_1 = require("./constants");
const utils = __importStar(require("./utils"));
/// PTStatus describes the pan/tilt status of a camera
class PTStatus {
constructor() { }
static fromData(data) {
let ret = new PTStatus();
let [p, q, r, s] = utils.nibbles(data);
ret.moveStatus = (q & constants_1.Constants.PAN_MOVE_FAIL) >> 2;
ret.initStatus = (p & constants_1.Constants.PAN_INIT_FAIL);
ret.atMaxL = (s & constants_1.Constants.PAN_MAXL) > 0;
ret.atMaxR = (s & constants_1.Constants.PAN_MAXR) > 0;
ret.atMaxU = (s & constants_1.Constants.PAN_MAXU) > 0;
ret.atMaxD = (s & constants_1.Constants.PAN_MAXD) > 0;
ret.moving = ret.moveStatus == 1;
ret.moveDone = ret.moveStatus == 2;
ret.moveFail = ret.moveStatus == 3;
ret.initializing = ret.initStatus == 1;
ret.ready = ret.initStatus == 2;
ret.fail = ret.initStatus == 3;
return ret;
}
}
exports.PTStatus = PTStatus;
class CamLensData {
constructor() { }
static fromData(data) {
let c = new CamLensData();
c.zoomPos = utils.v2i(data.slice(0, 4));
c.focusNearLimit = utils.v2i(data.slice(4, 6));
c.focusPos = utils.v2i(data.slice(6, 10));
// no data is in byte 10
let ww = data[11]; // byte 11 is byte 13 of the original full packet
// 0-normal, 1-interval, 2-trigger
c.autoFocusMode = (ww & 0b11000) >> 3;
// 0-slow, 1-normal
c.autoFocusSensitivity = (ww & 0b100) >> 2;
c.digitalZoomEnabled = utils.testBit(ww, 0b10);
c.autoFocusEnabled = utils.testBit(ww, 0b1);
let vv = data[12];
c.lowContrast = utils.testBit(vv, 0b1000);
c.loadingPreset = utils.testBit(vv, 0b100);
c.focusing = utils.testBit(vv, 0b10);
c.zooming = utils.testBit(vv, 0b1);
return c;
}
}
exports.CamLensData = CamLensData;
class CamImageData {
constructor() { }
static fromData(data) {
let c = new CamImageData();
c.gainr = utils.v2i(data.slice(0, 2));
c.gainb = utils.v2i(data.slice(2, 4));
c.wbMode = data[4];
c.gain = data[5];
c.exposureMode = data[6];
c.shutterPos = data[8];
c.irisPos = data[9];
c.gainPos = data[10];
c.brightness = data[11];
c.exposureCompPosition = data[12];
let aa = data[7];
c.highResEnabled = utils.testBit(aa, 0b100000);
c.wideDEnabled = utils.testBit(aa, 0b10000);
c.backlightCompEnabled = utils.testBit(aa, 0b100);
c.exposureCompEnabled = utils.testBit(aa, 0b10);
c.slowShutterAutoEnabled = utils.testBit(aa, 0b1);
return c;
}
}
exports.CamImageData = CamImageData;
class CamWideDParams {
constructor() { }
static fromData(data) {
let c = new CamWideDParams();
c.screenDisplay = data[0];
c.detectionSensitivity = data[1];
c.shadowCorrectionLevel = data[2];
c.highlightCorrectionLevel = data[3];
c.exposureRatio = (data[4] << 4) | data[5]; // 1-64
return c;
}
}
exports.CamWideDParams = CamWideDParams;
// TODO: Convert most "number" fields to semantic names
// using an enum or at least semantic map on the constants class
class CameraStatus {
constructor() { }
// takes CamImageData
updateImageData(imageData) {
this.rGain = imageData.gainr;
this.bGain = imageData.gainb;
this.apertureGain = imageData.gain;
this.wbMode = imageData.wbMode;
this.exposureMode = imageData.exposureMode;
this.shutterPos = imageData.shutterPos;
this.irisPos = imageData.irisPos;
this.gainPos = imageData.gainPos;
this.brightness = imageData.brightness;
this.exposureCompPosition = imageData.exposureCompPosition;
this.highResEnabled = imageData.highResEnabled;
this.wideDEnabled = imageData.wideDEnabled;
this.backlightCompEnabled = imageData.backlightCompEnabled;
this.exposureCompEnabled = imageData.exposureCompEnabled;
this.exposureCompPosition = imageData.exposureCompPosition;
this.slowShutterAutoEnabled = imageData.slowShutterAutoEnabled;
}
updateLensData(lensData) {
this.zooming = lensData.zooming;
this.zoomPos = lensData.zoomPos;
this.digitalZoomEnabled = lensData.digitalZoomEnabled;
this.focusing = lensData.focusing;
this.focusPos = lensData.focusPos;
this.focusNearLimit = lensData.focusNearLimit;
this.autoFocusMode = lensData.autoFocusMode;
this.autoFocusSensitivity = lensData.autoFocusSensitivity;
this.autoFocusEnabled = lensData.autoFocusEnabled;
this.lowContrast = lensData.lowContrast;
this.loadingPreset = lensData.loadingPreset;
}
}
exports.CameraStatus = CameraStatus;
class Camera extends events_1.EventEmitter {
// transport should support the socket interface => .send(ViscaCommand)
constructor(index, transport, name = '') {
var _a;
super();
// typescript only infers these if the constructor arguments
// use the public or private keyword
this.index = index;
this.name = name;
this.transport = transport;
this.cameraBuffers = {};
this.sentCommands = []; // FIFO stack for commands
this.commandQueue = [];
this.inquiryQueue = [];
this.status = new CameraStatus();
this.commandReady = true; // true when camera can receive commands
this.inquiryReady = true;
// UDPTransports provide a unique uuid
this.uuid = (_a = transport.uuid) !== null && _a !== void 0 ? _a : index.toString();
}
_clear() { this.cameraBuffers = {}; this.sentCommands = []; }
_update() {
this.updatetimer = null;
this._expireOldCommands();
this._processQueue();
this.emit('update');
}
_updateBooleans() {
this.commandReady = !('1' in this.cameraBuffers || '2' in this.cameraBuffers);
this.inquiryReady = !('0' in this.cameraBuffers);
}
// if a command in the stack is older than 200ms drop it
_expireOldCommands() {
let now = Date.now();
// the first command is always the oldest
while (this.sentCommands.length > 0) {
if (now - this.sentCommands[0].sentAt < constants_1.Constants.COMMAND_TIMEOUT)
break;
this.sentCommands.splice(0, 1);
}
// check all cameraBuffers
for (let key of Object.keys(this.cameraBuffers)) {
if (now - this.cameraBuffers[key].sentAt > constants_1.Constants.COMMAND_TIMEOUT)
delete this.cameraBuffers[key];
}
}
_processQueue() {
this._updateBooleans();
if (this.commandReady && this.commandQueue.length > 0) {
let [cmd] = this.commandQueue.splice(0, 1);
this.sendCommand(cmd);
}
if (this.inquiryReady && this.inquiryQueue.length > 0) {
let [cmd] = this.inquiryQueue.splice(0, 1);
this.sendCommand(cmd);
}
this._updateBooleans();
}
_scheduleUpdate() {
if (this.updatetimer != null)
return;
if (this.inquiryQueue.length > 0 || this.commandQueue.length > 0) {
this.updatetimer = setTimeout(this._update.bind(this), 25);
}
}
// treat commands that don't send ack as if
// they were stored in camera socket 0
// because the parsed response will have socket 0.
// other commands will be put on the stack until
// the ack tells us which socket received it
sendCommand(command) {
// update the header data
this._updateBooleans();
command.source = 0;
command.recipient = this.index;
command.broadcast = false;
command.addedAt = Date.now();
let queued = false;
// INTERFACE_DATA, ADDRESS_SET commands always get sent and aren't tracked.
// For inquiry commands, we need to keep track of them locally, so we can match replies to commands
if (command.msgType == constants_1.Constants.MSGTYPE_INQUIRY) {
// only allow one non-ack command at a time
if (this.inquiryReady) {
this.cameraBuffers['0'] = command; // no ACK, only complete / error
}
else {
this.inquiryQueue.push(command);
queued = true;
}
}
else if (command.msgType == constants_1.Constants.MSGTYPE_COMMAND) {
if (this.commandReady) {
this.sentCommands.push(command); // not in a buffer until we get ACK
}
else {
this.commandQueue.push(command);
queued = true;
}
}
if (queued) {
this._scheduleUpdate();
}
else {
command.sentAt = Date.now();
console.log(`SENDING VISCA COMMAND: ${command.toString()}`);
this.transport.write(command);
}
}
ack(viscaCommand) {
// get the first viscaCommand that expects an ACK
let [cmd] = this.sentCommands.splice(0, 1); // gets the head
if (cmd) {
cmd.handleAck(); // run the command ACK callback if it exists
this.cameraBuffers[viscaCommand.socket] = cmd;
}
this._scheduleUpdate();
}
complete(viscaCommand) {
var _a;
let key = viscaCommand.socket.toString();
(_a = this.cameraBuffers[key]) === null || _a === void 0 ? void 0 : _a.handleComplete(viscaCommand.data);
delete this.cameraBuffers[key];
this._scheduleUpdate();
}
error(viscaCommand) {
let message;
let errorType = viscaCommand.data[0];
let socketKey = viscaCommand.socket.toString();
switch (errorType) {
case constants_1.Constants.ERROR_SYNTAX:
message = `VISCA ERROR: syntax error, invalid command`;
break;
case constants_1.Constants.ERROR_BUFFER_FULL:
message = `VISCA ERROR: command buffers full`;
break;
case constants_1.Constants.ERROR_CANCELLED:
message = 'VISCA ERROR: command cancelled';
break;
case constants_1.Constants.ERROR_INVALID_BUFFER:
message = `VISCA ERROR: socket cannot be cancelled`;
break;
case constants_1.Constants.ERROR_COMMAND_FAILED:
message = `VISCA ERROR: command failed`;
break;
}
console.log(`CAMERA ERROR: id: ${this.index}, command socket: ${viscaCommand.socket}, message: ${message}\nRECEIVED: ${viscaCommand.toString()}`);
if (this.cameraBuffers[socketKey]) {
this.cameraBuffers[socketKey].handleError(message);
}
else {
console.log("Error: Camera buffer missing!!!");
}
delete (this.cameraBuffers[socketKey]);
this._update();
}
inquireAll() {
this.getPower(); // single command
this.getPTStatus(); // block inquiry command
this.getLensData(); // block inquiry
this.getImageData(); // block inquiry
// multiple individual queries
this.getPTPos();
this.getPTSpeed();
this.getICRMode();
this.getICRAutoMode();
this.getICRThreshold();
this.getGainLimit();
this.getFocusAutoMode();
this.getFocusAutoIntervalTime();
this.getFocusIRCorrection();
this.getWideDStatus();
this.getWideDParams();
this.getNoiseReductionStatus();
this.getHighSensitivityStatus();
this.getFreezeStatus();
this.getEffect();
this.getEffectDigital();
this.getEffectDigitalLevel();
this.getChromaSuppressStatus();
this.getID();
this.getColorGain();
this.getColorHue();
this.getVideoFormatNow();
this.getVideoFormatNext();
}
// camera specific inquiry commands
// ---------------- Inquiries ---------------------------
getPower() { let v = command_1.ViscaCommand.inqCameraPower(this.index, (data) => { this.status.powerStatus = data; }); this.sendCommand(v); }
getICRMode() { let v = command_1.ViscaCommand.inqCameraICRMode(this.index, (data) => { this.status.icrMode = data; }); this.sendCommand(v); }
getICRAutoMode() { let v = command_1.ViscaCommand.inqCameraICRAutoMode(this.index, (data) => { this.status.icrAutoMode = data; }); this.sendCommand(v); }
getICRThreshold() { let v = command_1.ViscaCommand.inqCameraICRThreshold(this.index, (data) => { this.status.icrThreshold = data; }); this.sendCommand(v); }
getGainLimit() { let v = command_1.ViscaCommand.inqCameraGainLimit(this.index, (data) => { this.status.gainLimit = data; }); this.sendCommand(v); }
getGain() { let v = command_1.ViscaCommand.inqCameraGain(this.index, (data) => { this.status.apertureGain = data; }); this.sendCommand(v); }
getGainR() { let v = command_1.ViscaCommand.inqCameraGainR(this.index, (data) => { this.status.rGain = data; }); this.sendCommand(v); }
getGainB() { let v = command_1.ViscaCommand.inqCameraGainB(this.index, (data) => { this.status.bGain = data; }); this.sendCommand(v); }
getDZoomMode() { let v = command_1.ViscaCommand.inqCameraDZoomMode(this.index, (data) => { this.status.digitalZoomEnabled = data; }); this.sendCommand(v); }
getZoomPos() { let v = command_1.ViscaCommand.inqCameraZoomPos(this.index, (data) => { this.status.zoomPos = data; }); this.sendCommand(v); }
getFocusAutoStatus() { let v = command_1.ViscaCommand.inqCameraFocusAutoStatus(this.index, (data) => { this.status.autoFocusEnabled = data; }); this.sendCommand(v); }
getFocusAutoMode() { let v = command_1.ViscaCommand.inqCameraFocusAutoMode(this.index, (data) => { this.status.autoFocusMode = data; }); this.sendCommand(v); }
getFocusIRCorrection() { let v = command_1.ViscaCommand.inqCameraFocusIRCorrection(this.index, (data) => { this.status.focusIRCorrection = data; }); this.sendCommand(v); }
getFocusPos() { let v = command_1.ViscaCommand.inqCameraFocusPos(this.index, (data) => { this.status.focusPos = data; }); this.sendCommand(v); }
getFocusNearLimit() { let v = command_1.ViscaCommand.inqCameraFocusNearLimit(this.index, (data) => { this.status.focusNearLimit = data; }); this.sendCommand(v); }
getFocusAutoIntervalTime() { let v = command_1.ViscaCommand.inqCameraFocusAutoIntervalTime(this.index, (data) => { this.status.autoFocusIntervalTime = data; }); this.sendCommand(v); }
getFocusSensitivity() { let v = command_1.ViscaCommand.inqCameraFocusSensitivity(this.index, (data) => { this.status.autoFocusSensitivity = data; }); this.sendCommand(v); }
getWBMode() { let v = command_1.ViscaCommand.inqCameraWBMode(this.index, (data) => { this.status.wbMode = data; }); this.sendCommand(v); }
getExposureMode() { let v = command_1.ViscaCommand.inqCameraExposureMode(this.index, (data) => { this.status.exposureMode = data; }); this.sendCommand(v); }
getShutterSlowMode() { let v = command_1.ViscaCommand.inqCameraShutterSlowMode(this.index, (data) => { this.status.slowShutterAutoEnabled = data; }); this.sendCommand(v); }
getShutter() { let v = command_1.ViscaCommand.inqCameraShutterPos(this.index, (data) => { this.status.shutterPos = data; }); this.sendCommand(v); }
getIris() { let v = command_1.ViscaCommand.inqCameraIris(this.index, (data) => { this.status.irisPos = data; }); this.sendCommand(v); }
getBrightness() { let v = command_1.ViscaCommand.inqCameraBrightness(this.index, (data) => { this.status.brightness = data; }); this.sendCommand(v); }
getExposureCompStatus() { let v = command_1.ViscaCommand.inqCameraExposureCompStatus(this.index, (data) => { this.status.exposureCompEnabled = data; }); this.sendCommand(v); }
getExposureCompPosition() { let v = command_1.ViscaCommand.inqCameraExposureCompPosition(this.index, (data) => { this.status.exposureCompPosition = data; }); this.sendCommand(v); }
getBacklightStatus() { let v = command_1.ViscaCommand.inqCameraBacklightStatus(this.index, (data) => { this.status.backlightCompEnabled = data; }); this.sendCommand(v); }
getWideDStatus() { let v = command_1.ViscaCommand.inqCameraWideDMode(this.index, (data) => { this.status.wideDMode = data; this.status.wideDEnabled = (data != 0); }); this.sendCommand(v); }
getWideDParams() { let v = command_1.ViscaCommand.inqCameraWideDParams(this.index, (data) => { this.status.wideDParams = data; }); this.sendCommand(v); }
getAperture() { let v = command_1.ViscaCommand.inqCameraAperture(this.index, (data) => { this.status.apertureGain = data; }); this.sendCommand(v); }
getHighResStatus() { let v = command_1.ViscaCommand.inqCameraHighResStatus(this.index, (data) => { this.status.highResEnabled = data; }); this.sendCommand(v); }
getNoiseReductionStatus() { let v = command_1.ViscaCommand.inqCameraNoiseReductionStatus(this.index, (data) => { this.status.noiseReductionLevel = data; }); this.sendCommand(v); }
getHighSensitivityStatus() { let v = command_1.ViscaCommand.inqCameraHighSensitivityStatus(this.index, (data) => { this.status.highSensitivityEnabled = data; }); this.sendCommand(v); }
getFreezeStatus() { let v = command_1.ViscaCommand.inqCameraFreezeStatus(this.index, (data) => { this.status.frozen = data; }); this.sendCommand(v); }
getEffect() { let v = command_1.ViscaCommand.inqCameraEffect(this.index, (data) => { this.status.effect = data; }); this.sendCommand(v); }
getEffectDigital() { let v = command_1.ViscaCommand.inqCameraEffectDigital(this.index, (data) => { this.status.digitalEffect = data; }); this.sendCommand(v); }
getEffectDigitalLevel() { let v = command_1.ViscaCommand.inqCameraEffectDigitalLevel(this.index, (data) => { this.status.digitalEffectLevel = data; }); this.sendCommand(v); }
getID() { let v = command_1.ViscaCommand.inqCameraID(this.index, (data) => { this.status.cameraID = data; }); this.sendCommand(v); }
getChromaSuppressStatus() { let v = command_1.ViscaCommand.inqCameraChromaSuppressStatus(this.index, (data) => { this.status.chromaSuppressLevel = data; }); this.sendCommand(v); }
getColorGain() { let v = command_1.ViscaCommand.inqCameraColorGain(this.index, (data) => { this.status.colorGain = data; }); this.sendCommand(v); }
getColorHue() { let v = command_1.ViscaCommand.inqCameraColorHue(this.index, (data) => { this.status.colorHue = data; }); this.sendCommand(v); }
// these use op commands
getVideoFormatNow() { let v = command_1.ViscaCommand.inqVideoFormatNow(this.index, (data) => { this.status.videoFormatNow = data; }); this.sendCommand(v); }
getVideoFormatNext() { let v = command_1.ViscaCommand.inqVideoFormatNext(this.index, (data) => { this.status.videoFormatNext = data; }); this.sendCommand(v); }
getPTPos() { let v = command_1.ViscaCommand.inqCameraPanTiltPos(this.index, (data) => { this.status.ptPos = data; }); this.sendCommand(v); }
getPTSpeed() { let v = command_1.ViscaCommand.inqCameraPanTiltSpeed(this.index, (data) => { this.status.ptSpeed = data; }); this.sendCommand(v); }
getPTStatus() { let v = command_1.ViscaCommand.inqCameraPanTiltStatus(this.index, (data) => { this.status.panTiltStatus = data; }); this.sendCommand(v); }
// block inquiry commands
getLensData() { let v = command_1.ViscaCommand.inqCameraLens(this.index, (data) => { this.status.updateLensData(data); }); this.sendCommand(v); }
getImageData() { let v = command_1.ViscaCommand.inqCameraImage(this.index, (data) => { this.status.updateImageData(data); }); this.sendCommand(v); }
}
exports.Camera = Camera;
//# sourceMappingURL=camera.js.map