From ad007b6f8f365ca33129c0fb2f6dc2d18db37a2b Mon Sep 17 00:00:00 2001 From: Moe Alam Date: Sat, 19 Sep 2020 23:28:35 -0700 Subject: [PATCH 1/3] Improve Automatic PTZ (Return Home when Object lost) + Add Inverting Y-Axis + Add ONVIF Scanning Port 8899 + Cleanup some ptz.js function uses --- definitions/en_CA.js | 37 +++++++++++++ languages/en_CA.json | 2 + libs/control.js | 2 +- libs/control/onvif.js | 22 ++++++-- libs/control/ptz.js | 115 +++++++++++++++++++++++++++++++++++++++-- libs/events.js | 64 ++++++----------------- libs/monitor.js | 23 ++++++++- libs/scanners.js | 2 +- libs/socketio.js | 5 +- libs/webServerPaths.js | 5 +- 10 files changed, 214 insertions(+), 63 deletions(-) diff --git a/definitions/en_CA.js b/definitions/en_CA.js index ce385e99..46e62eff 100644 --- a/definitions/en_CA.js +++ b/definitions/en_CA.js @@ -420,6 +420,25 @@ module.exports = function(s,config,lang){ } ] }, + { + "name": "detail=onvif_non_standard", + "field": lang['Non-Standard ONVIF'], + "description": "Is this a Non-Standard ONVIF camera?", + "default": "0", + "example": "", + "form-group-class": "h_onvif_input h_onvif_1", + "fieldType": "select", + "possible": [ + { + "name": lang.No, + "value": "0" + }, + { + "name": lang.Yes, + "value": "1" + } + ] + }, { hidden: true, "name": "detail=onvif_port", @@ -3608,6 +3627,24 @@ module.exports = function(s,config,lang){ "form-group-class": "h_control_call_input h_control_call_GET h_control_call_PUT h_control_call_POST", "possible": "" }, + { + "name": "detail=control_invert_y", + "field": lang["Invert Y-Axis"], + "description": "For When your camera is mounted upside down or uses inverted vertical controls.", + "default": "0", + "example": "", + "fieldType": "select", + "possible": [ + { + "name": lang.No, + "value": "0" + }, + { + "name": lang.Yes, + "value": "1" + } + ] + }, ] }, "Grouping": { diff --git a/languages/en_CA.json b/languages/en_CA.json index e346a099..6b9c63d7 100644 --- a/languages/en_CA.json +++ b/languages/en_CA.json @@ -37,6 +37,7 @@ "Token": "Token", "OAuth Code": "OAuth Code", "Google Drive": "Google Drive", + "Invert Y-Axis": "Invert Y-Axis", "Get Code": "Get Code", "PTZ Tracking": "PTZ Tracking", "PTZ Tracking Target": "PTZ Tracking Target", @@ -88,6 +89,7 @@ "Never": "Never", "API": "API", "ONVIF": "ONVIF", + "Non-Standard ONVIF": "Non-Standard ONVIF", "FFprobe": "Probe", "Monitor States": "Monitor States", "Schedule": "Schedule", diff --git a/libs/control.js b/libs/control.js index d564c85b..812a231c 100644 --- a/libs/control.js +++ b/libs/control.js @@ -2,5 +2,5 @@ var os = require('os'); var exec = require('child_process').exec; module.exports = function(s,config,lang,app,io){ require('./control/onvif.js')(s,config,lang,app,io) - const ptz = require('./control/ptz.js')(s,config,lang,app,io) + // const ptz = require('./control/ptz.js')(s,config,lang,app,io) } diff --git a/libs/control/onvif.js b/libs/control/onvif.js index bd668524..14c6b222 100644 --- a/libs/control/onvif.js +++ b/libs/control/onvif.js @@ -17,12 +17,25 @@ module.exports = function(s,config,lang,app,io){ try{ const info = await device.init() response.ok = true + response.device = device }catch(err){ response.msg = 'Device responded with an error' - response.error = error + response.error = err } return response } + const replaceDynamicInOptions = (Camera,options) => { + const newOptions = {} + Object.keys(options).forEach((key) => { + const value = options[key] + if(typeof value === 'string'){ + newOptions[key] = value.replace(/__CURRENT_TOKEN/g,Camera.current_profile.token) + }else if(value !== undefined && value !== null){ + newOptions[key] = value + } + }) + return newOptions + } const runOnvifMethod = (onvifOptions,callback) => { var onvifAuth = onvifOptions.auth var response = {ok: false} @@ -83,7 +96,8 @@ module.exports = function(s,config,lang,app,io){ var options var command if(argNames[0] === 'options' || argNames[0] === 'params'){ - options = onvifOptions.options || {} + options = replaceDynamicInOptions(Camera,onvifOptions.options || {}) + response.options = options } if(onvifAuth.service){ command = Camera.services[onvifAuth.service][onvifAuth.action](options) @@ -112,15 +126,15 @@ module.exports = function(s,config,lang,app,io){ config.webPaths.apiPrefix+':auth/onvif/:ke/:id/:service/:action' ],function (req,res){ s.auth(req.params,function(user){ + const options = s.getPostData(req,'options',true) || s.getPostData(req,'params',true) runOnvifMethod({ auth: { ke: req.params.ke, id: req.params.id, - auth: req.params.auth, action: req.params.action, service: req.params.service, }, - options: s.getPostData(req,'options',true) || s.getPostData(req,'params',true), + options: options, },(endData) => { s.closeJsonResponse(res,endData) }) diff --git a/libs/control/ptz.js b/libs/control/ptz.js index 566371b8..3d1f3cbd 100644 --- a/libs/control/ptz.js +++ b/libs/control/ptz.js @@ -1,7 +1,7 @@ var os = require('os'); var exec = require('child_process').exec; var request = require('request') -module.exports = function(s,config,lang,app,io){ +module.exports = function(s,config,lang){ const moveLock = {} const startMove = async function(options,callback){ const device = s.group[options.ke].activeMonitors[options.id].onvifConnection @@ -41,6 +41,7 @@ module.exports = function(s,config,lang,app,io){ } const moveOnvifCamera = function(options,callback){ const monitorConfig = s.group[options.ke].rawMonitorConfigurations[options.id] + const invertedVerticalAxis = monitorConfig.control_invert_y === '1' const controlUrlStopTimeout = parseInt(monitorConfig.details.control_url_stop_timeout) || 1000 switch(options.direction){ case'center': @@ -68,8 +69,8 @@ module.exports = function(s,config,lang,app,io){ var onvifDirections = { "left": [-1.0,'x'], "right": [1.0,'x'], - "down": [-1.0,'y'], - "up": [1.0,'y'], + "down": [invertedVerticalAxis ? 1.0 : -1.0,'y'], + "up": [invertedVerticalAxis ? -1.0 : 1.0,'y'], "zoom_in": [1.0,'z'], "zoom_out": [-1.0,'z'] } @@ -258,10 +259,114 @@ module.exports = function(s,config,lang,app,io){ } } } - s.cameraControl = ptzControl + const getPresetPositions = (options,callback) => { + const profileToken = options.ProfileToken || "__CURRENT_TOKEN" + return s.runOnvifMethod({ + auth: { + ke: options.ke, + id: options.id, + service: 'ptz', + action: 'getPresets', + }, + options: { + ProfileToken: profileToken + }, + },callback) + } + const setPresetForCurrentPosition = (options,callback) => { + const nonStandardOnvif = s.group[options.ke].rawMonitorConfigurations[options.id].details.onvif_non_standard === '1' + const profileToken = options.ProfileToken || "__CURRENT_TOKEN" + s.runOnvifMethod({ + auth: { + ke: options.ke, + id: options.id, + service: 'ptz', + action: 'setPreset', + }, + options: { + ProfileToken: profileToken, + PresetToken: nonStandardOnvif ? null : options.PresetToken || profileToken, + PresetName: options.PresetName || nonStandardOnvif ? '1' : profileToken + }, + },(endData) => { + callback(endData) + }) + } + const moveToPresetPosition = (options,callback) => { + const nonStandardOnvif = s.group[options.ke].rawMonitorConfigurations[options.id].details.onvif_non_standard === '1' + const profileToken = options.ProfileToken || "__CURRENT_TOKEN" + return s.runOnvifMethod({ + auth: { + ke: options.ke, + id: options.id, + service: 'ptz', + action: 'gotoPreset', + }, + options: { + ProfileToken: profileToken, + PresetToken: options.PresetToken || nonStandardOnvif ? '1' : profileToken, + Speed: { + "x": 1, + "y": 1, + "z": 1 + }, + }, + },callback) + } + const getLargestMatrix = (matrices) => { + var largestMatrix = {width: 0, height: 0} + matrices.forEach((matrix) => { + if(matrix.width > largestMatrix.width && matrix.height > largestMatrix.height)largestMatrix = matrix + }) + return largestMatrix.x ? largestMatrix : null + } + const moveCameraPtzToMatrix = function(event,trackingTarget){ + if(moveLock[event.ke + event.id])return; + clearTimeout(moveLock[event.ke + event.id]) + moveLock[event.ke + event.id] = setTimeout(() => { + delete(moveLock[event.ke + event.id]) + },1000) + const imgHeight = event.details.imgHeight + const imgWidth = event.details.imgWidth + const thresholdX = imgWidth * 0.125 + const thresholdY = imgHeight * 0.125 + const imageCenterX = imgWidth / 2 + const imageCenterY = imgHeight / 2 + const matrices = event.details.matrices + const largestMatrix = getLargestMatrix(matrices.filter(matrix => matrix.tag === (trackingTarget || 'person'))) + // console.log(matrices.find(matrix => matrix.tag === 'person')) + if(!largestMatrix)return; + const matrixCenterX = largestMatrix.x + (largestMatrix.width / 2) + const matrixCenterY = largestMatrix.y + (largestMatrix.height / 2) + const rawDistanceX = (matrixCenterX - imageCenterX) + const rawDistanceY = (matrixCenterY - imageCenterY) + const distanceX = imgWidth / rawDistanceX + const distanceY = imgHeight / rawDistanceY + const axisX = rawDistanceX > thresholdX || rawDistanceX < -thresholdX ? distanceX : 0 + const axisY = largestMatrix.y < 30 && largestMatrix.height > imgHeight * 0.8 ? 0.5 : rawDistanceY > thresholdY || rawDistanceY < -thresholdY ? -distanceY : 0 + if(axisX !== 0 || axisY !== 0){ + ptzControl({ + axis: [ + {direction: 'x', amount: axisX === 0 ? 0 : axisX > 0 ? 0.01 : -0.01}, + {direction: 'y', amount: axisY === 0 ? 0 : axisY > 0 ? 0.01 : -0.01}, + {direction: 'z', amount: 0}, + ], + // axis: [{direction: 'x', amount: 1.0}], + id: event.id, + ke: event.ke + },(msg) => { + s.userLog(event,msg) + // console.log(msg) + }) + } + } return { - control: ptzControl, + ptzControl: ptzControl, startMove: startMove, stopMove: stopMove, + getPresetPositions: getPresetPositions, + setPresetForCurrentPosition: setPresetForCurrentPosition, + moveToPresetPosition: moveToPresetPosition, + moveCameraPtzToMatrix: moveCameraPtzToMatrix } } diff --git a/libs/events.js b/libs/events.js index a1378dbd..8a05ef9e 100644 --- a/libs/events.js +++ b/libs/events.js @@ -10,7 +10,11 @@ var P = SAT.Polygon; var B = SAT.Box; // Matrix In Region Libs /> module.exports = function(s,config,lang){ - const ptz = require('./control/ptz.js')(s,config,lang) + const { + moveCameraPtzToMatrix, + moveToPresetPosition + } = require('./control/ptz.js')(s,config,lang) + const ptzTimeoutsUntilResetToHome = {} const countObjects = async (event) => { const matrices = event.details.matrices const eventsCounted = s.group[event.ke].activeMonitors[event.id].eventsCounted || {} @@ -78,54 +82,6 @@ module.exports = function(s,config,lang){ return collisions } const nonEmpty = (element) => element.length !== 0; - const moveLock = {} - const getLargestMatrix = (matrices) => { - var largestMatrix = {width: 0, height: 0} - matrices.forEach((matrix) => { - if(matrix.width > largestMatrix.width && matrix.height > largestMatrix.height)largestMatrix = matrix - }) - return largestMatrix.x ? largestMatrix : null - } - const moveCameraPtzToMatrix = function(event,trackingTarget){ - if(moveLock[event.ke + event.id])return; - clearTimeout(moveLock[event.ke + event.id]) - moveLock[event.ke + event.id] = setTimeout(() => { - delete(moveLock[event.ke + event.id]) - },1000) - const imgHeight = event.details.imgHeight - const imgWidth = event.details.imgWidth - const thresholdX = imgWidth * 0.125 - const thresholdY = imgHeight * 0.125 - const imageCenterX = imgWidth / 2 - const imageCenterY = imgHeight / 2 - const matrices = event.details.matrices - const largestMatrix = getLargestMatrix(matrices.filter(matrix => matrix.tag === (trackingTarget || 'person'))) - // console.log(matrices.find(matrix => matrix.tag === 'person')) - if(!largestMatrix)return; - const matrixCenterX = largestMatrix.x + (largestMatrix.width / 2) - const matrixCenterY = largestMatrix.y + (largestMatrix.height / 2) - const rawDistanceX = (matrixCenterX - imageCenterX) - const rawDistanceY = (matrixCenterY - imageCenterY) - const distanceX = imgWidth / rawDistanceX - const distanceY = imgHeight / rawDistanceY - const axisX = rawDistanceX > thresholdX || rawDistanceX < -thresholdX ? distanceX : 0 - const axisY = largestMatrix.y < 30 && largestMatrix.height > imgHeight * 0.8 ? 0.5 : rawDistanceY > thresholdY || rawDistanceY < -thresholdY ? -distanceY : 0 - if(axisX !== 0 || axisY !== 0){ - ptz.control({ - axis: [ - {direction: 'x', amount: axisX === 0 ? 0 : axisX > 0 ? 0.01 : -0.01}, - {direction: 'y', amount: axisY === 0 ? 0 : axisY > 0 ? 0.01 : -0.01}, - {direction: 'z', amount: 0}, - ], - // axis: [{direction: 'x', amount: 1.0}], - id: event.id, - ke: event.ke - },(msg) => { - s.userLog(event,msg) - // console.log(msg) - }) - } - } s.addEventDetailsToString = function(eventData,string,addOps){ //d = event data if(!addOps)addOps = {} @@ -327,6 +283,16 @@ module.exports = function(s,config,lang){ countObjects(d) } if(currentConfig.detector_ptz_follow === '1'){ + // moveToPresetPosition + clearTimeout(ptzTimeoutsUntilResetToHome[d.ke + d.id]) + ptzTimeoutsUntilResetToHome[d.ke + d.id] = setTimeout(() => { + moveToPresetPosition({ + ke: d.ke, + id: d.id, + },(endData) => { + console.log(endData) + }) + },7000) moveCameraPtzToMatrix(d,currentConfig.detector_ptz_follow_target) } if(filter.useLock){ diff --git a/libs/monitor.js b/libs/monitor.js index 85fe63b9..51b20bf3 100644 --- a/libs/monitor.js +++ b/libs/monitor.js @@ -13,6 +13,9 @@ const URL = require('url') const { copyObject, createQueue } = require('./common.js') module.exports = function(s,config,lang){ const { cameraDestroy } = require('./monitor/utils.js')(s,config,lang) + const { + setPresetForCurrentPosition + } = require('./control/ptz.js')(s,config,lang) const startMonitorInQueue = createQueue(1, 3) s.initiateMonitorObject = function(e){ if(!s.group[e.ke]){s.group[e.ke]={}}; @@ -455,7 +458,6 @@ module.exports = function(s,config,lang){ ke: e.ke },'GRP_'+e.ke) }else{ - console.log('not image') s.tx({f:'monitor_snapshot',snapshot:e.mon.name,snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke) } }else{ @@ -1607,6 +1609,25 @@ module.exports = function(s,config,lang){ break; } } + if(e.details.detector_ptz_follow === '1'){ + setTimeout(() => { + setPresetForCurrentPosition({ + ke: e.ke, + id: e.id + },(endData) => { + if(endData.ok === false){ + setTimeout(() => { + setPresetForCurrentPosition({ + ke: e.ke, + id: e.id + },(endData) => { + console.log(endData) + }) + },5000) + } + }) + },5000) + } launchMonitorProcesses(e) break; default: diff --git a/libs/scanners.js b/libs/scanners.js index 4238560f..d99a0210 100644 --- a/libs/scanners.js +++ b/libs/scanners.js @@ -54,7 +54,7 @@ module.exports = function(s,config,lang,app,io){ ip = addressRange.join(',') } if(ports === ''){ - ports = '80,8080,8000,7575,8081,9080,8090,8999' + ports = '80,8080,8000,7575,8081,9080,8090,8999,8899' } if(ports.indexOf('-') > -1){ ports = ports.split('-') diff --git a/libs/socketio.js b/libs/socketio.js index 34bddd9d..2109541c 100644 --- a/libs/socketio.js +++ b/libs/socketio.js @@ -4,6 +4,9 @@ var exec = require('child_process').exec; var spawn = require('child_process').spawn; var jsonfile = require("jsonfile"); module.exports = function(s,config,lang,io){ + const { + ptzControl + } = require('./control/ptz.js')(s,config,lang) s.clientSocketConnection = {} //send data to socket client function s.tx = function(z,y,x){ @@ -649,7 +652,7 @@ module.exports = function(s,config,lang,io){ } break; case'control': - s.cameraControl(d,function(msg){ + ptzControl(d,function(msg){ s.userLog(d,msg) tx({f:'control',response:msg}) }) diff --git a/libs/webServerPaths.js b/libs/webServerPaths.js index 9e48ba66..8b587e23 100644 --- a/libs/webServerPaths.js +++ b/libs/webServerPaths.js @@ -12,6 +12,9 @@ var proxy = httpProxy.createProxyServer({}) var ejs = require('ejs'); var fileupload = require("express-fileupload"); module.exports = function(s,config,lang,app,io){ + const { + ptzControl + } = require('./control/ptz.js')(s,config,lang,app,io) if(config.productType === 'Pro'){ var LdapAuth = require('ldapauth-fork'); } @@ -1655,7 +1658,7 @@ module.exports = function(s,config,lang,app,io){ app.get(config.webPaths.apiPrefix+':auth/control/:ke/:id/:direction', function (req,res){ res.setHeader('Content-Type', 'application/json'); s.auth(req.params,function(user){ - s.cameraControl(req.params,function(msg){ + ptzControl(req.params,function(msg){ s.userLog({ id: req.params.id, ke: req.params.ke, From e5646e5fb1277461a1c1a260348c776e181fe9f2 Mon Sep 17 00:00:00 2001 From: Moe Alam Date: Sat, 19 Sep 2020 23:31:55 -0700 Subject: [PATCH 2/3] Update ptz.js --- libs/control/ptz.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/control/ptz.js b/libs/control/ptz.js index 3d1f3cbd..1c368dda 100644 --- a/libs/control/ptz.js +++ b/libs/control/ptz.js @@ -41,7 +41,7 @@ module.exports = function(s,config,lang){ } const moveOnvifCamera = function(options,callback){ const monitorConfig = s.group[options.ke].rawMonitorConfigurations[options.id] - const invertedVerticalAxis = monitorConfig.control_invert_y === '1' + const invertedVerticalAxis = monitorConfig.details.control_invert_y === '1' const controlUrlStopTimeout = parseInt(monitorConfig.details.control_url_stop_timeout) || 1000 switch(options.direction){ case'center': From a2e8de4386bbc01cd0057ed19b62ff0029bb9a34 Mon Sep 17 00:00:00 2001 From: Moe Alam Date: Sun, 20 Sep 2020 10:45:10 -0700 Subject: [PATCH 3/3] auto ptz : cleanup return home --- libs/control/onvif.js | 4 ++-- libs/control/ptz.js | 10 ++++++++++ libs/events.js | 12 ------------ libs/monitor.js | 11 ++++++++++- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/libs/control/onvif.js b/libs/control/onvif.js index 14c6b222..9b6abab4 100644 --- a/libs/control/onvif.js +++ b/libs/control/onvif.js @@ -36,7 +36,7 @@ module.exports = function(s,config,lang,app,io){ }) return newOptions } - const runOnvifMethod = (onvifOptions,callback) => { + const runOnvifMethod = async (onvifOptions,callback) => { var onvifAuth = onvifOptions.auth var response = {ok: false} var errorMessage = function(msg,error){ @@ -108,7 +108,7 @@ module.exports = function(s,config,lang,app,io){ } } if(!s.group[onvifAuth.ke].activeMonitors[onvifAuth.id].onvifConnection){ - const response = createOnvifDevice(onvifAuth) + const response = await createOnvifDevice(onvifAuth) if(response.ok){ doAction(response.device) }else{ diff --git a/libs/control/ptz.js b/libs/control/ptz.js index 1c368dda..f023750f 100644 --- a/libs/control/ptz.js +++ b/libs/control/ptz.js @@ -3,6 +3,7 @@ var exec = require('child_process').exec; var request = require('request') module.exports = function(s,config,lang){ const moveLock = {} + const ptzTimeoutsUntilResetToHome = {} const startMove = async function(options,callback){ const device = s.group[options.ke].activeMonitors[options.id].onvifConnection if(!device){ @@ -357,6 +358,15 @@ module.exports = function(s,config,lang){ },(msg) => { s.userLog(event,msg) // console.log(msg) + clearTimeout(ptzTimeoutsUntilResetToHome[event.ke + event.id]) + ptzTimeoutsUntilResetToHome[event.ke + event.id] = setTimeout(() => { + moveToPresetPosition({ + ke: event.ke, + id: event.id, + },(endData) => { + console.log(endData) + }) + },7000) }) } } diff --git a/libs/events.js b/libs/events.js index 8a05ef9e..3424e133 100644 --- a/libs/events.js +++ b/libs/events.js @@ -12,9 +12,7 @@ var B = SAT.Box; module.exports = function(s,config,lang){ const { moveCameraPtzToMatrix, - moveToPresetPosition } = require('./control/ptz.js')(s,config,lang) - const ptzTimeoutsUntilResetToHome = {} const countObjects = async (event) => { const matrices = event.details.matrices const eventsCounted = s.group[event.ke].activeMonitors[event.id].eventsCounted || {} @@ -283,16 +281,6 @@ module.exports = function(s,config,lang){ countObjects(d) } if(currentConfig.detector_ptz_follow === '1'){ - // moveToPresetPosition - clearTimeout(ptzTimeoutsUntilResetToHome[d.ke + d.id]) - ptzTimeoutsUntilResetToHome[d.ke + d.id] = setTimeout(() => { - moveToPresetPosition({ - ke: d.ke, - id: d.id, - },(endData) => { - console.log(endData) - }) - },7000) moveCameraPtzToMatrix(d,currentConfig.detector_ptz_follow_target) } if(filter.useLock){ diff --git a/libs/monitor.js b/libs/monitor.js index 51b20bf3..5d18b10b 100644 --- a/libs/monitor.js +++ b/libs/monitor.js @@ -1621,7 +1621,16 @@ module.exports = function(s,config,lang){ ke: e.ke, id: e.id },(endData) => { - console.log(endData) + if(endData.ok === false){ + setTimeout(() => { + setPresetForCurrentPosition({ + ke: e.ke, + id: e.id + },(endData) => { + console.log(endData) + }) + },5000) + } }) },5000) }