This commit is contained in:
PTZOptics
2018-11-02 15:24:39 -04:00
committed by GitHub
commit b799070fd1
29 changed files with 6054 additions and 0 deletions

View File

@@ -0,0 +1,180 @@
/*jshint esversion: 6 */
const path = require('path');
const http = require('http');
const tcpPortUsed =require('tcp-port-used');
const db = require(path.resolve(__dirname, './db.js'));
const Camera = db.Camera;
module.exports = {
getDeviceModel,
checkPortUse,
createNewCameraStreamPort
};
async function checkPortUse(port) {
return tcpPortUsed.check(port, '127.0.0.1')
.then((inuse) => {
return inuse;
})
.catch(err => {
throw err;
});
}
async function getDeviceModel(ip) {
const options = {
hostname: ip,
path: '/cgi-bin/param.cgi?get_serial_number',
method: 'GET'
};
return sendCgiReq(options)
.then((res) => {
const rawData = res.toString().replace(/\n/g, '').toUpperCase();
return calcCamModel(rawData, rawData.substring(0, 1), rawData.substring(0, 2))
.then(res => {
return res;
})
.catch(err => {
throw err;
});
})
.catch(err => {
throw err;
});
}
async function calcCamModel(serial, first_letter, twoFirst_letter) {
let model = '';
// Check 12x
if (["1", "A", "B", "C", "D", "N", "O"].includes(first_letter)) {
model = "PT12X-";
(first_letter === "1") ? model += serial.slice(3, 6) + "-XX-" + serial.slice(8, 10) + checkPoe(serial.slice(10)) : model += newSerialAnsBuilder(serial);
}
// Check 20x
if (["2", "E", "F", "G", "H", "P", "Q"].includes(first_letter) && twoFirst_letter !== "PT" ) {
model = "PT20X-";
(first_letter === "2") ? model += serial.slice(3, 6) + "-XX-" + serial.slice(8, 10) + checkPoe(serial.slice(10)) : model += newSerialAnsBuilder(serial);
}
// Check 30x
if (["W", "X", "R", "S"].includes(first_letter)) {
model = "PT30X-" + newSerialAnsBuilder(serial);
}
// Check zcams
if (["J", "U", "I", "T"].includes(first_letter)) {
(["J", "U"].includes(first_letter)) ? model = "PT20X-ZCAM-" : model = "PTVL-ZCAM";
}
if ('PT' == twoFirst_letter) {
(serial.slice(0, 4) === "PTVL") ? model = "PTVL-ZCAM-" : model = "PT20X-ZCAM-";
}
return [model, serial];
}
function newSerialAnsBuilder(serial) {
let ans = '';
switch(serial.substring(0, 1)) {
// 12X
case "A":
case "B":
ans = "SDI-XX-G2" + checkPoe(serial.slice(1));
break;
case "C":
case "D":
ans = "USB-XX-G2";
break;
case "N":
case "O":
ans = "SDI-XX-G2 POE";
break;
// 20X
case "E":
case "F":
ans = "SDI-XX-G2" + checkPoe(serial.slice(1));
break;
case "G":
case "H":
ans = "USB-XX-G2";
break;
case "P":
case "Q":
ans = "SDI-XX-G2 POE";
break;
// 30X
case "W":
case "X":
ans = "SDI-XX-G2 POE";
break;
case "R":
case "S":
ans = "NDI-XX-G2";
break;
}
return ans;
}
function checkPoe (serialNum) {
if (serialNum <= "B1025000") {
return '';
} else if (serialNum >= "B1025001" && serialNum <= "D0129000") {
return "-POE";
} else if(serialNum >= "D0129001") {
return "-POE";
}
}
async function createNewCameraStreamPort() {
let port = 5000;
let cameraStreamPorts;
try {
// Returns all saved camera stream ports
cameraStreamPorts = await currentCameraStreamPorts();
} catch(err) {
throw err;
}
while (cameraStreamPorts.includes(port)) {
++port;
}
return port;
}
async function currentCameraStreamPorts() {
return Camera.find().select('streamPort').lean()
.then((cameras) => {
return cameras.map((camera) => {
return camera.streamPort;
});
})
.catch((err) => {
throw err;
});
}
function sendCgiReq(options) {
return new Promise(function(resolve, reject) {
const req = http.request(options, (res) => {
let rawData = '';
res.setEncoding('utf8');
res.on('data', (chunk) => {
rawData += chunk;
});
res.on('end', () => {
resolve(rawData);
});
});
req.on('error', function(err) {
reject(err);
});
req.end();
});
}

11
app/_helpers/db.js Normal file
View File

@@ -0,0 +1,11 @@
/*jshint esversion: 6 */
const path = require('path');
const config = require(path.resolve(__dirname, '../config.json'));
const mongoose = require('mongoose');
mongoose.connect(config.connectionString, {useNewUrlParser: true });
mongoose.Promise = global.Promise;
module.exports = {
Camera: require('../camera/camera.model')
};

View File

@@ -0,0 +1,9 @@
module.exports = errorHandler;
function errorHandler(err, req, res, next) {
console.log(err);
if (typeof err === 'string') {
return res.status(409).json({message: err});
}
return res.status(500).json({message: err.message});
}

85
app/_helpers/ptzHelper.js Normal file
View File

@@ -0,0 +1,85 @@
/*jshint esversion: 6 */
module.exports = {
translateDirection,
hexStrToNum,
numToHexStr,
sanitizeSpeed,
getCurrentPos
};
function translateDirection(direction) {
let hexStr;
switch(direction.toLowerCase()) {
case "stop":
hexStr = "0303FF";
break;
case "up":
hexStr = "0301FF";
break;
case "down":
hexStr = "0302FF";
break;
case "right":
hexStr = "0203FF";
break;
case "left":
hexStr = "0103FF";
break;
case "upleft":
hexStr = "0101FF";
break;
case "upright":
hexStr = "0201FF";
break;
case "downleft":
hexStr = "0102FF";
break;
case "downright":
hexStr = "0202FF";
break;
default:
throw "The direction " + direction + " is not a valid movement direction";
}
return hexStr;
}
function hexStrToNum(str) {
return parseInt(str, 16);
}
function numToHexStr(num) {
if (num == 0) {
return '00';
}
if (typeof num === 'string') {
num = parseInt(num);
}
return num.toString(16).toUpperCase().split('').reduce(function(str, char) {
return '0' + char;
});
}
function sanitizeSpeed(pan, tilt) {
let sanitizedPan, sanitizedTilt;
if (pan >= 1 && pan <= 18) {
sanitizedPan = ("0" + pan).slice(-2);
} else {
throw 'The pan speed value must be greater than or equal to 1 and less than or equal to 18';
}
if (tilt >= 1 && tilt <= 14) {
sanitizedTilt = ("0" + pan).slice(-2);
} else {
throw 'The tilt speed value must be greater than or equal to 1 and less than or equal to 14';
}
return [sanitizedPan, sanitizedTilt];
}
async function getCurrentPos(id) {
return numToHexStr(await socket.sendCmd(id, "81090612FF"));
}

81
app/_helpers/socket.js Normal file
View File

@@ -0,0 +1,81 @@
/*jshint esversion: 6 */
const path = require('path');
const db = require(path.resolve(__dirname, '../_helpers/db.js'));
const Camera = db.Camera;
const net = require('net');
module.exports = {
sendCmd: _sendCmd
};
async function _sendCmd(id, cmd) {
const camera = await Camera.findById(id);
const buffer = Buffer.from(cmd, 'hex');
return new Promise((resolve, reject) => {
const socket = new net.Socket({allowHalfOpen: true});
const conn = net.createConnection(Number(camera.port), camera.ip);
conn.setNoDelay();
conn.setEncoding('hex');
conn.on('connect', () => {
conn.write(buffer);
});
conn.on('error', (error) => {
reject(error);
});
conn.on('data', (buf) => {
if (conn.bytesRead >= 3) {
conn.end();
decode(buf.toString('hex'))
.then((res) => {
resolve(res);
})
.catch(err => {
reject(err);
});
} else {
reject("Unusual Camera Response: " + buf.toString('hex') + " connection bytes Read: " + conn.bytesRead);
}
});
});
}
async function decode(hexStr) {
let decoded = '';
switch (hexStr) {
case "9041ff":
case "9042ff":
decoded = "Command Accepted";
break;
case "9051ff":
case "9041ff9051ff":
decoded = "Socket1 Cmd Done";
break;
case "9052ff":
case "9042ff9052ff":
decoded = "Socket2 Cmd Done";
break;
case "906002ff":
throw "Command Syntax Error";
case "906003ff":
throw "Command Buffer Full";
case "906104ff":
throw "Socket1 Cmd Cancelled";
case "906204ff":
throw "Socket2 Cmd Cancelled";
case "906105ff":
case "906205ff":
throw "No Socket";
case "906141ff":
throw "Socket1 Cmd Not Executable";
case "906241ff":
throw "Socket2 Cmd Not Executable";
default:
throw "Unusual Camera Response: " + hexStr;
}
return decoded;
}