events.js, s.triggerEvent : light cleanup on function declaration/execution

+ remove use of hotswap recording method, Traditional Recording is recommended.
+ add timed lock for webhook
+ begin layering wraps on fields to make reading easier (Webhook and Command)
mitchross-coral-installer-update
Moe 2021-01-01 22:17:37 -08:00
parent b272794399
commit c48ed6d6ef
5 changed files with 574 additions and 461 deletions

1
.gitignore vendored
View File

@ -1,6 +1,5 @@
node_modules
videos
events
frames
web.old
.DS_Store

View File

@ -2239,10 +2239,6 @@ module.exports = function(s,config,lang){
"name": lang['Traditional (Watch-Only, Includes Buffer)'],
"value": "sip"
},
{
"name": lang['Hotswap Modes (Watch-Only)'],
"value": "hot"
},
{
"name": lang['Delete Motionless Videos (Record)'],
"value": "del"
@ -2392,100 +2388,134 @@ module.exports = function(s,config,lang){
},
],
},
{
"name": "detail=detector_webhook",
"field": "Webhook",
"description": "Send a GET request to a URL with some values from the event.",
"default": "0",
"example": "",
"selector": "h_det_web",
"fieldType": "select",
"form-group-class-pre-layer": "h_det_input h_det_1",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
},
{
hidden: true,
"name": "detail=detector_webhook_url",
"field": lang['Webhook URL'],
"description": "",
"default": "",
"example": "http://111.111.111.111?mid={{MONITOR_ID}}&group={{GROUP_KEY}}&confidence={{CONFIDENCE}}",
"form-group-class": "h_det_web_input h_det_web_1",
"form-group-class-pre-layer": "h_det_input h_det_1",
"possible": ""
},
{
"name": "detail=detector_webhook_method",
"field": lang['Call Method'],
"description": "",
"default": "GET",
"example": "",
"form-group-class": "h_det_web_input h_det_web_1",
"form-group-class-pre-layer": "h_det_input h_det_1",
"fieldType": "select",
"possible": [
{
"name": `GET (${lang.Default})`,
"value": "GET"
},
{
"name": "PUT",
"value": "PUT"
},
{
"name": "POST",
"value": "POST"
}
"name": lang['Webhook'],
"color": "orange",
id: "monSectionDetectorWebhook",
isSection: true,
isAdvanced: true,
isFormGroupGroup: true,
"section-class": "h_det_input h_det_1",
"info": [
{
"name": "detail=detector_webhook",
"field": "Webhook",
"description": "Send a GET request to a URL with some values from the event.",
"default": "0",
"example": "",
"selector": "h_det_web",
"fieldType": "select",
"form-group-class-pre-layer": "h_det_input h_det_1",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
},
{
"name": "detail=detector_webhook_timeout",
"field": lang['Allow Next Webhook'],
"description": "This value is a timer to allow the next running of your Webhook. This value is in minutes.",
"default": "10",
"example": "",
"form-group-class": "h_det_web_input h_det_web_1",
"form-group-class-pre-layer": "h_det_input h_det_1",
"possible": ""
},
{
hidden: true,
"name": "detail=detector_webhook_url",
"field": lang['Webhook URL'],
"description": "",
"default": "",
"example": "http://111.111.111.111?mid={{MONITOR_ID}}&group={{GROUP_KEY}}&confidence={{CONFIDENCE}}",
"form-group-class": "h_det_web_input h_det_web_1",
"form-group-class-pre-layer": "h_det_input h_det_1",
"possible": ""
},
{
"name": "detail=detector_webhook_method",
"field": lang['Call Method'],
"description": "",
"default": "GET",
"example": "",
"form-group-class": "h_det_web_input h_det_web_1",
"form-group-class-pre-layer": "h_det_input h_det_1",
"fieldType": "select",
"possible": [
{
"name": `GET (${lang.Default})`,
"value": "GET"
},
{
"name": "PUT",
"value": "PUT"
},
{
"name": "POST",
"value": "POST"
}
]
},
]
},
{
"name": "detail=detector_command_enable",
"field": lang['Command on Trigger'],
"description": "",
"default": "0",
"example": "",
"selector": "h_det_com",
"fieldType": "select",
"form-group-class-pre-layer": "h_det_input h_det_1",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
},
{
"name": "detail=detector_command",
"field": lang['Command'],
"description": "The command that will run. This is the equivalent of running a shell command from terminal.",
"default": "",
"form-group-class": "h_det_com_input h_det_com_1",
"example": "/home/script.sh {{MONITOR_ID}} {{GROUP_KEY}} {{CONFIDENCE}}",
"form-group-class-pre-layer": "h_det_input h_det_1",
"possible": ""
},
{
"name": "detail=detector_command_timeout",
"field": lang['Allow Next Command'],
"description": "This value is a timer to allow the next running of your script. This value is in minutes.",
"default": "10",
"example": "",
"form-group-class": "h_det_com_input h_det_com_1",
"form-group-class-pre-layer": "h_det_input h_det_1",
"possible": ""
hidden: true,
"name": lang['Command'],
"color": "orange",
id: "monSectionDetectorCommand",
isSection: true,
isAdvanced: true,
isFormGroupGroup: true,
"section-class": "h_det_input h_det_1",
"info": [
{
"name": "detail=detector_command_enable",
"field": lang['Command on Trigger'],
"description": "",
"default": "0",
"example": "",
"selector": "h_det_com",
"fieldType": "select",
"form-group-class-pre-layer": "h_det_input h_det_1",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
},
{
"name": "detail=detector_command",
"field": lang['Command'],
"description": "The command that will run. This is the equivalent of running a shell command from terminal.",
"default": "",
"form-group-class": "h_det_com_input h_det_com_1",
"example": "/home/script.sh {{MONITOR_ID}} {{GROUP_KEY}} {{CONFIDENCE}}",
"form-group-class-pre-layer": "h_det_input h_det_1",
"possible": ""
},
{
"name": "detail=detector_command_timeout",
"field": lang['Allow Next Command'],
"description": "This value is a timer to allow the next running of your script. This value is in minutes.",
"default": "10",
"example": "",
"form-group-class": "h_det_com_input h_det_com_1",
"form-group-class-pre-layer": "h_det_input h_det_1",
"possible": ""
},
]
},
{
"name": "detail=snap_seconds_inward",
@ -2884,15 +2914,6 @@ module.exports = function(s,config,lang){
}
]
},
{
"name": "detail=detector_notrigger_command_timeout",
"field": lang['Allow Next Webhook'],
"description": "This value is a timer to allow the next running of your webhook. This value is in minutes.",
"default": "10",
"example": "",
"form-group-class": "h_det_web_notrig_input h_det_web_notrig_1",
"possible": ""
},
{
"name": "detail=detector_notrigger_command_enable",
"field": lang['Command on Trigger'],

View File

@ -2,87 +2,27 @@ var moment = require('moment');
var execSync = require('child_process').execSync;
var exec = require('child_process').exec;
var spawn = require('child_process').spawn;
var request = require('request');
// Matrix In Region Libs >
var SAT = require('sat')
var V = SAT.Vector;
var P = SAT.Polygon;
var B = SAT.Box;
// Matrix In Region Libs />
module.exports = function(s,config,lang){
const {
moveCameraPtzToMatrix,
splitForFFPMEG
} = require('./ffmpeg/utils.js')(s,config,lang)
const {
moveCameraPtzToMatrix
} = require('./control/ptz.js')(s,config,lang)
const {
splitForFFPMEG,
} = require('./ffmpeg/utils.js')(s,config,lang)
const countObjects = async (event) => {
const matrices = event.details.matrices
const eventsCounted = s.group[event.ke].activeMonitors[event.id].eventsCounted || {}
if(matrices){
matrices.forEach((matrix)=>{
const id = matrix.tag
if(!eventsCounted[id])eventsCounted[id] = {times: [], count: {}, tag: matrix.tag}
if(!isNaN(matrix.id))eventsCounted[id].count[matrix.id] = 1
eventsCounted[id].times.push(new Date().getTime())
})
}
return eventsCounted
}
const isAtleastOneMatrixInRegion = function(regions,matrices,callback){
var regionPolys = []
var matrixPoints = []
regions.forEach(function(region,n){
var polyPoints = []
region.points.forEach(function(point){
polyPoints.push(new V(parseInt(point[0]),parseInt(point[1])))
})
regionPolys[n] = new P(new V(0,0), polyPoints)
})
var collisions = []
var foundInRegion = false
matrices.forEach(function(matrix){
var matrixPoly = new B(new V(matrix.x, matrix.y), matrix.width, matrix.height).toPolygon()
regionPolys.forEach(function(region,n){
var response = new SAT.Response()
var collided = SAT.testPolygonPolygon(matrixPoly, region, response)
if(collided === true){
collisions.push({
matrix: matrix,
region: regions[n]
})
foundInRegion = true
}
})
})
if(callback)callback(foundInRegion,collisions)
return foundInRegion
}
const scanMatricesforCollisions = function(region,matrices){
var matrixPoints = []
var collisions = []
if (!region || !matrices){
if(callback)callback(collisions)
return collisions
}
var polyPoints = []
region.points.forEach(function(point){
polyPoints.push(new V(parseInt(point[0]),parseInt(point[1])))
})
var regionPoly = new P(new V(0,0), polyPoints)
matrices.forEach(function(matrix){
if (matrix){
var matrixPoly = new B(new V(matrix.x, matrix.y), matrix.width, matrix.height).toPolygon()
var response = new SAT.Response()
var collided = SAT.testPolygonPolygon(matrixPoly, regionPoly, response)
if(collided === true){
collisions.push(matrix)
}
}
})
return collisions
}
const nonEmpty = (element) => element.length !== 0;
countObjects,
isAtleastOneMatrixInRegion,
scanMatricesforCollisions,
getLargestMatrix,
addToEventCounter,
hasMatrices,
checkEventFilters,
checkMotionLock,
runMultiTrigger,
checkForObjectsInRegions,
runEventExecutions,
} = require('./events/utils.js')(s,config,lang)
s.addEventDetailsToString = function(eventData,string,addOps){
//d = event data
if(!addOps)addOps = {}
@ -123,10 +63,13 @@ module.exports = function(s,config,lang){
exec(d.execute,{detached: true})
break;
}
s.onEventTriggerBeforeFilterExtensions.forEach(function(extender){
extender(x,d)
})
}
s.triggerEvent = async (d,forceSave) => {
var didCountingAlready = false
var filter = {
const filter = {
halt : false,
addToMotionCounter : true,
useLock : true,
@ -137,188 +80,60 @@ module.exports = function(s,config,lang){
indifference : false,
countObjects : true
}
var detailString = JSON.stringify(d.details);
if(!s.group[d.ke]||!s.group[d.ke].activeMonitors[d.id]){
if(!s.group[d.ke] || !s.group[d.ke].activeMonitors[d.id]){
return s.systemLog(lang['No Monitor Found, Ignoring Request'])
}
d.mon=s.group[d.ke].rawMonitorConfigurations[d.id];
var currentConfig = s.group[d.ke].rawMonitorConfigurations[d.id].details
const monitorConfig = s.group[d.ke].rawMonitorConfigurations[d.id]
if(!monitorConfig){
return s.systemLog(lang['No Monitor Found, Ignoring Request'])
}
const monitorDetails = monitorConfig.details
s.onEventTriggerBeforeFilterExtensions.forEach(function(extender){
extender(d,filter)
})
var hasMatrices = (d.details.matrices && d.details.matrices.length > 0)
//read filters
const passedEventFilters = checkEventFilters(d,monitorDetails,filter)
if(!passedEventFilters)return
const eventDetails = d.details
const detailString = JSON.stringify(eventDetails)
const eventTime = new Date()
if(
currentConfig.use_detector_filters === '1' &&
((currentConfig.use_detector_filters_object === '1' && d.details.matrices) ||
currentConfig.use_detector_filters_object !== '1')
filter.addToMotionCounter &&
filter.record &&
(
monitorConfig.mode === 'record' ||
monitorConfig.mode === 'start' &&
(
(
monitorDetails.detector_record_method === 'sip' &&
monitorDetails.detector_trigger === '1'
) ||
(
monitorDetails.detector_record_method === 'del' &&
monitorDetails.detector_delete_motionless_videos === '1'
)
)
)
){
var parseValue = function(key,val){
var newVal
switch(val){
case'':
newVal = filter[key]
break;
case'0':
newVal = false
break;
case'1':
newVal = true
break;
default:
newVal = val
break;
}
return newVal
}
var filters = currentConfig.detector_filters
Object.keys(filters).forEach(function(key){
var conditionChain = {}
var dFilter = filters[key]
dFilter.where.forEach(function(condition,place){
conditionChain[place] = {ok:false,next:condition.p4,matrixCount:0}
if(d.details.matrices)conditionChain[place].matrixCount = d.details.matrices.length
var modifyFilters = function(toCheck,matrixPosition){
var param = toCheck[condition.p1]
var pass = function(){
if(matrixPosition && dFilter.actions.halt === '1'){
delete(d.details.matrices[matrixPosition])
}else{
conditionChain[place].ok = true
}
}
switch(condition.p2){
case'indexOf':
if(param.indexOf(condition.p3) > -1){
pass()
}
break;
case'!indexOf':
if(param.indexOf(condition.p3) === -1){
pass()
}
break;
default:
if(eval('param '+condition.p2+' "'+condition.p3.replace(/"/g,'\\"')+'"')){
pass()
}
break;
}
}
switch(condition.p1){
case'tag':
case'x':
case'y':
case'height':
case'width':
case'confidence':
if(d.details.matrices){
d.details.matrices.forEach(function(matrix,position){
modifyFilters(matrix,position)
})
}
break;
case'time':
var timeNow = new Date()
var timeCondition = new Date()
var doAtTime = condition.p3.split(':')
var atHour = parseInt(doAtTime[0]) - 1
var atHourNow = timeNow.getHours()
var atMinuteNow = timeNow.getMinutes()
var atSecondNow = timeNow.getSeconds()
if(atHour){
var atMinute = parseInt(doAtTime[1]) - 1 || timeNow.getMinutes()
var atSecond = parseInt(doAtTime[2]) - 1 || timeNow.getSeconds()
var nowAddedInSeconds = atHourNow * 60 * 60 + atMinuteNow * 60 + atSecondNow
var conditionAddedInSeconds = atHour * 60 * 60 + atMinute * 60 + atSecond
if(eval('nowAddedInSeconds '+condition.p2+' conditionAddedInSeconds')){
conditionChain[place].ok = true
}
}
break;
default:
modifyFilters(d.details)
break;
}
})
var conditionArray = Object.values(conditionChain)
var validationString = ''
conditionArray.forEach(function(condition,number){
validationString += condition.ok+' '
if(conditionArray.length-1 !== number){
validationString += condition.next+' '
}
})
if(eval(validationString)){
if(dFilter.actions.halt !== '1'){
delete(dFilter.actions.halt)
Object.keys(dFilter.actions).forEach(function(key){
var value = dFilter.actions[key]
filter[key] = parseValue(key,value)
})
}else{
filter.halt = true
}
}
})
if(d.details.matrices && d.details.matrices.length === 0 || filter.halt === true){
return
}else if(hasMatrices){
var reviewedMatrix = []
d.details.matrices.forEach(function(matrix){
if(matrix)reviewedMatrix.push(matrix)
})
d.details.matrices = reviewedMatrix
}
addToEventCounter(d)
}
var eventTime = new Date()
//motion counter
if(filter.addToMotionCounter && filter.record){
s.group[d.ke].activeMonitors[d.id].detector_motion_count.push(d)
}
if(filter.countObjects && currentConfig.detector_obj_count === '1' && currentConfig.detector_obj_count_in_region !== '1'){
if(
filter.countObjects &&
monitorDetails.detector_obj_count === '1' &&
monitorDetails.detector_obj_count_in_region !== '1'
){
didCountingAlready = true
countObjects(d)
}
if(currentConfig.detector_ptz_follow === '1'){
moveCameraPtzToMatrix(d,currentConfig.detector_ptz_follow_target)
if(monitorDetails.detector_ptz_follow === '1'){
moveCameraPtzToMatrix(d,monitorDetails.detector_ptz_follow_target)
}
if(filter.useLock){
if(s.group[d.ke].activeMonitors[d.id].motion_lock){
return
}
var detector_lock_timeout
if(!currentConfig.detector_lock_timeout||currentConfig.detector_lock_timeout===''){
detector_lock_timeout = 2000
}
detector_lock_timeout = parseFloat(currentConfig.detector_lock_timeout);
if(!s.group[d.ke].activeMonitors[d.id].detector_lock_timeout){
s.group[d.ke].activeMonitors[d.id].detector_lock_timeout=setTimeout(function(){
clearTimeout(s.group[d.ke].activeMonitors[d.id].detector_lock_timeout)
delete(s.group[d.ke].activeMonitors[d.id].detector_lock_timeout)
},detector_lock_timeout)
}else{
return
}
}
// check if object should be in region
if(hasMatrices && currentConfig.detector_obj_region === '1'){
var regions = s.group[d.ke].activeMonitors[d.id].parsedObjects.cords
var isMatrixInRegions = isAtleastOneMatrixInRegion(regions,d.details.matrices)
if(isMatrixInRegions){
s.debugLog('Matrix in region!')
if(filter.countObjects && currentConfig.detector_obj_count === '1' && currentConfig.detector_obj_count_in_region === '1' && !didCountingAlready){
countObjects(d)
}
}else{
return
}
}
// check modified indifference
if(filter.indifference !== false && d.details.confidence < parseFloat(filter.indifference)){
// fails indifference check for modified indifference
return
const passedMotionLock = checkMotionLock(d,monitorDetails)
if(!passedMotionLock)return
}
const passedObjectInRegionCheck = checkForObjectsInRegions(monitorConfig,filter,d,didCountingAlready)
if(!passedObjectInRegionCheck)return
//
if(d.doObjectDetection === true){
s.ocvTx({
@ -331,124 +146,35 @@ module.exports = function(s,config,lang){
})
}
//
if(currentConfig.detector_use_motion === '0' || d.doObjectDetection !== true ){
if(currentConfig.det_multi_trig === '1'){
s.getCamerasForMultiTrigger(d.mon).forEach(function(monitor){
if(monitor.mid !== d.id){
s.triggerEvent({
id: monitor.mid,
ke: monitor.ke,
details: {
confidence: 100,
name: "multiTrigger",
plug: d.details.plug,
reason: d.details.reason
}
})
}
})
}
//save this detection result in SQL, only coords. not image.
if(forceSave || (filter.save && currentConfig.detector_save === '1')){
s.knexQuery({
action: "insert",
table: "Events",
insert: {
ke: d.ke,
mid: d.id,
details: detailString,
time: eventTime,
}
})
}
if(currentConfig.detector === '1' && currentConfig.detector_notrigger === '1'){
s.setNoEventsDetector(s.group[d.ke].rawMonitorConfigurations[d.id])
}
var detector_timeout
if(!currentConfig.detector_timeout||currentConfig.detector_timeout===''){
detector_timeout = 10
}else{
detector_timeout = parseFloat(currentConfig.detector_timeout)
}
if(filter.record && d.mon.mode=='start'&&currentConfig.detector_trigger==='1'&&currentConfig.detector_record_method==='sip'){
s.createEventBasedRecording(d,moment(eventTime).subtract(5,'seconds').format('YYYY-MM-DDTHH-mm-ss'))
}else if(filter.record && d.mon.mode!=='stop'&&currentConfig.detector_trigger=='1'&&currentConfig.detector_record_method==='hot'){
if(!d.auth){
d.auth=s.gid();
}
if(!s.group[d.ke].users[d.auth]){
s.group[d.ke].users[d.auth]={system:1,details:{},lang:lang}
}
d.urlQuery = []
d.url = 'http://'+config.ip+':'+config.port+'/'+d.auth+'/monitor/'+d.ke+'/'+d.id+'/record/'+detector_timeout+'/min';
if(currentConfig.watchdog_reset!=='0'){
d.urlQuery.push('reset=1')
}
if(currentConfig.detector_trigger_record_fps&&currentConfig.detector_trigger_record_fps!==''&&currentConfig.detector_trigger_record_fps!=='0'){
d.urlQuery.push('fps='+currentConfig.detector_trigger_record_fps)
}
if(d.urlQuery.length>0){
d.url+='?'+d.urlQuery.join('&')
}
request({url:d.url,method:'GET'},function(err,data){
if(err){
//could not start hotswap
}else{
delete(s.group[d.ke].users[d.auth])
d.cx.f='detector_record_engaged';
d.cx.msg = JSON.parse(data.body)
s.tx(d.cx,'GRP_'+d.ke);
}
})
}
d.currentTime = new Date()
d.currentTimestamp = s.timeObject(d.currentTime).format()
d.screenshotName = d.details.reason + '_'+(d.mon.name.replace(/[^\w\s]/gi,''))+'_'+d.id+'_'+d.ke+'_'+s.formattedTime()
d.screenshotBuffer = null
if(filter.webhook && currentConfig.detector_webhook === '1'){
var detector_webhook_url = s.addEventDetailsToString(d,currentConfig.detector_webhook_url)
var webhookMethod = currentConfig.detector_webhook_method
if(!webhookMethod || webhookMethod === '')webhookMethod = 'GET'
request(detector_webhook_url,{method: webhookMethod,encoding:null},function(err,data){
if(err){
s.userLog(d,{type:lang["Event Webhook Error"],msg:{error:err,data:data}})
}
})
}
if(filter.command && currentConfig.detector_command_enable === '1' && !s.group[d.ke].activeMonitors[d.id].detector_command){
s.group[d.ke].activeMonitors[d.id].detector_command = s.createTimeout('detector_command',s.group[d.ke].activeMonitors[d.id],currentConfig.detector_command_timeout,10)
var detector_command = s.addEventDetailsToString(d,currentConfig.detector_command)
if(detector_command === '')return
exec(detector_command,{detached: true},function(err){
if(err)s.debugLog(err)
})
}
for (var i = 0; i < s.onEventTriggerExtensions.length; i++) {
const extender = s.onEventTriggerExtensions[i]
await extender(d,filter)
}
if(
monitorDetails.detector_use_motion === '0' ||
d.doObjectDetection !== true
){
runEventExecutions(eventTime,monitorConfig,eventDetails,forceSave,filter,d)
}
//show client machines the event
d.cx={f:'detector_trigger',id:d.id,ke:d.ke,details:d.details,doObjectDetection:d.doObjectDetection};
s.tx(d.cx,'DETECTOR_'+d.ke+d.id);
s.tx({
f: 'detector_trigger',
id: d.id,
ke: d.ke,
details: eventDetails,
doObjectDetection: d.doObjectDetection
},`DETECTOR_${monitorConfig.ke}${monitorConfig.mid}`);
}
s.createEventBasedRecording = function(d,fileTime){
if(!fileTime)fileTime = s.formattedTime()
d.mon = s.group[d.ke].rawMonitorConfigurations[d.id]
var currentConfig = s.group[d.ke].activeMonitors[d.id].details
if(currentConfig.detector !== '1'){
const monitorConfig = s.group[d.ke].rawMonitorConfigurations[d.id]
const monitorDetails = s.group[d.ke].activeMonitors[d.id].details
if(monitorDetails.detector !== '1'){
return
}
var detector_timeout
if(!currentConfig.detector_timeout||currentConfig.detector_timeout===''){
if(!monitorDetails.detector_timeout||monitorDetails.detector_timeout===''){
detector_timeout = 10
}else{
detector_timeout = parseFloat(currentConfig.detector_timeout)
detector_timeout = parseFloat(monitorDetails.detector_timeout)
}
if(currentConfig.watchdog_reset === '1' || !s.group[d.ke].activeMonitors[d.id].eventBasedRecording.timeout){
if(monitorDetails.watchdog_reset === '1' || !s.group[d.ke].activeMonitors[d.id].eventBasedRecording.timeout){
clearTimeout(s.group[d.ke].activeMonitors[d.id].eventBasedRecording.timeout)
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.timeout = setTimeout(function(){
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.allowEnd = true
@ -460,11 +186,11 @@ module.exports = function(s,config,lang){
}
if(!s.group[d.ke].activeMonitors[d.id].eventBasedRecording.process){
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.allowEnd = false;
var runRecord = function(){
const runRecord = function(){
var filename = fileTime+'.mp4'
s.userLog(d,{type:lang["Traditional Recording"],msg:lang["Started"]})
//-t 00:'+s.timeObject(new Date(detector_timeout * 1000 * 60)).format('mm:ss')+'
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.process = spawn(config.ffmpegDir,splitForFFPMEG(('-loglevel warning -analyzeduration 1000000 -probesize 1000000 -re -i "'+s.dir.streams+d.ke+'/'+d.id+'/detectorStream.m3u8" -c:v copy -strftime 1 "'+s.getVideoDirectory(d.mon) + filename + '"')))
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.process = spawn(config.ffmpegDir,splitForFFPMEG(('-loglevel warning -analyzeduration 1000000 -probesize 1000000 -re -i "'+s.dir.streams+d.ke+'/'+d.id+'/detectorStream.m3u8" -c:v copy -strftime 1 "'+s.getVideoDirectory(monitorConfig) + filename + '"')))
var ffmpegError='';
var error
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.process.stderr.on('data',function(data){
@ -476,9 +202,8 @@ module.exports = function(s,config,lang){
runRecord()
return
}
s.insertCompletedVideo(d.mon,{
s.insertCompletedVideo(monitorConfig,{
file : filename,
events: s.group[d.ke].activeMonitors[d.id].detector_motion_count
})
s.userLog(d,{type:lang["Traditional Recording"],msg:lang["Detector Recording Complete"]})
s.userLog(d,{type:lang["Traditional Recording"],msg:lang["Clear Recorder Process"]})

366
libs/events/utils.js Normal file
View File

@ -0,0 +1,366 @@
const moment = require('moment');
const execSync = require('child_process').execSync;
const exec = require('child_process').exec;
const spawn = require('child_process').spawn;
const request = require('request');
// Matrix In Region Libs >
const SAT = require('sat')
const V = SAT.Vector;
const P = SAT.Polygon;
const B = SAT.Box;
// Matrix In Region Libs />
module.exports = (s,config,lang,app,io) => {
const countObjects = async (event) => {
const matrices = event.details.matrices
const eventsCounted = s.group[event.ke].activeMonitors[event.id].eventsCounted || {}
if(matrices){
matrices.forEach((matrix)=>{
const id = matrix.tag
if(!eventsCounted[id])eventsCounted[id] = {times: [], count: {}, tag: matrix.tag}
if(!isNaN(matrix.id))eventsCounted[id].count[matrix.id] = 1
eventsCounted[id].times.push(new Date().getTime())
})
}
return eventsCounted
}
const isAtleastOneMatrixInRegion = function(regions,matrices,callback){
var regionPolys = []
var matrixPoints = []
regions.forEach(function(region,n){
var polyPoints = []
region.points.forEach(function(point){
polyPoints.push(new V(parseInt(point[0]),parseInt(point[1])))
})
regionPolys[n] = new P(new V(0,0), polyPoints)
})
var collisions = []
var foundInRegion = false
matrices.forEach(function(matrix){
var matrixPoly = new B(new V(matrix.x, matrix.y), matrix.width, matrix.height).toPolygon()
regionPolys.forEach(function(region,n){
var response = new SAT.Response()
var collided = SAT.testPolygonPolygon(matrixPoly, region, response)
if(collided === true){
collisions.push({
matrix: matrix,
region: regions[n]
})
foundInRegion = true
}
})
})
if(callback)callback(foundInRegion,collisions)
return foundInRegion
}
const scanMatricesforCollisions = function(region,matrices){
var matrixPoints = []
var collisions = []
if (!region || !matrices){
if(callback)callback(collisions)
return collisions
}
var polyPoints = []
region.points.forEach(function(point){
polyPoints.push(new V(parseInt(point[0]),parseInt(point[1])))
})
var regionPoly = new P(new V(0,0), polyPoints)
matrices.forEach(function(matrix){
if (matrix){
var matrixPoly = new B(new V(matrix.x, matrix.y), matrix.width, matrix.height).toPolygon()
var response = new SAT.Response()
var collided = SAT.testPolygonPolygon(matrixPoly, regionPoly, response)
if(collided === true){
collisions.push(matrix)
}
}
})
return collisions
}
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 addToEventCounter = (eventData) => {
const eventsCounted = s.group[eventData.ke].activeMonitors[eventData.id].detector_motion_count
s.debugLog(`addToEventCounter`,eventData,eventsCounted.length)
eventsCounted.push(eventData)
}
const clearEventCounter = (groupKey,monitorId) => {
s.group[eventData.ke].activeMonitors[eventData.id].detector_motion_count = []
}
const getEventsCounted = (groupKey,monitorId) => {
return s.group[eventData.ke].activeMonitors[eventData.id].detector_motion_count.length
}
const hasMatrices = (monitorDetails) => {
return (monitorDetails.matrices && monitorDetails.matrices.length > 0)
}
const checkEventFilters = (d,monitorDetails,filter) => {
if(
monitorDetails.use_detector_filters === '1' &&
((monitorDetails.use_detector_filters_object === '1' && d.details.matrices) ||
monitorDetails.use_detector_filters_object !== '1')
){
const parseValue = function(key,val){
var newVal
switch(val){
case'':
newVal = filter[key]
break;
case'0':
newVal = false
break;
case'1':
newVal = true
break;
default:
newVal = val
break;
}
return newVal
}
const filters = monitorDetails.detector_filters
Object.keys(filters).forEach(function(key){
var conditionChain = {}
var dFilter = filters[key]
dFilter.where.forEach(function(condition,place){
conditionChain[place] = {ok:false,next:condition.p4,matrixCount:0}
if(d.details.matrices)conditionChain[place].matrixCount = d.details.matrices.length
var modifyFilters = function(toCheck,matrixPosition){
var param = toCheck[condition.p1]
var pass = function(){
if(matrixPosition && dFilter.actions.halt === '1'){
delete(d.details.matrices[matrixPosition])
}else{
conditionChain[place].ok = true
}
}
switch(condition.p2){
case'indexOf':
if(param.indexOf(condition.p3) > -1){
pass()
}
break;
case'!indexOf':
if(param.indexOf(condition.p3) === -1){
pass()
}
break;
default:
if(eval('param '+condition.p2+' "'+condition.p3.replace(/"/g,'\\"')+'"')){
pass()
}
break;
}
}
switch(condition.p1){
case'tag':
case'x':
case'y':
case'height':
case'width':
case'confidence':
if(d.details.matrices){
d.details.matrices.forEach(function(matrix,position){
modifyFilters(matrix,position)
})
}
break;
case'time':
var timeNow = new Date()
var timeCondition = new Date()
var doAtTime = condition.p3.split(':')
var atHour = parseInt(doAtTime[0]) - 1
var atHourNow = timeNow.getHours()
var atMinuteNow = timeNow.getMinutes()
var atSecondNow = timeNow.getSeconds()
if(atHour){
var atMinute = parseInt(doAtTime[1]) - 1 || timeNow.getMinutes()
var atSecond = parseInt(doAtTime[2]) - 1 || timeNow.getSeconds()
var nowAddedInSeconds = atHourNow * 60 * 60 + atMinuteNow * 60 + atSecondNow
var conditionAddedInSeconds = atHour * 60 * 60 + atMinute * 60 + atSecond
if(eval('nowAddedInSeconds '+condition.p2+' conditionAddedInSeconds')){
conditionChain[place].ok = true
}
}
break;
default:
modifyFilters(d.details)
break;
}
})
var conditionArray = Object.values(conditionChain)
var validationString = ''
conditionArray.forEach(function(condition,number){
validationString += condition.ok+' '
if(conditionArray.length-1 !== number){
validationString += condition.next+' '
}
})
if(eval(validationString)){
if(dFilter.actions.halt !== '1'){
delete(dFilter.actions.halt)
Object.keys(dFilter.actions).forEach(function(key){
var value = dFilter.actions[key]
filter[key] = parseValue(key,value)
})
}else{
filter.halt = true
}
}
})
if(d.details.matrices && d.details.matrices.length === 0 || filter.halt === true){
return false
}else if(hasMatrices(monitorDetails)){
var reviewedMatrix = []
d.details.matrices.forEach(function(matrix){
if(matrix)reviewedMatrix.push(matrix)
})
d.details.matrices = reviewedMatrix
}
}
// check modified indifference
if(
filter.indifference !== false &&
eventDetails.confidence < parseFloat(filter.indifference)
){
// fails indifference check for modified indifference
return
}
return true
}
const checkMotionLock = (eventData,monitorDetails) => {
if(s.group[eventData.ke].activeMonitors[eventData.id].motion_lock){
return false
}
var detector_lock_timeout
if(!monitorDetails.detector_lock_timeout||monitorDetails.detector_lock_timeout===''){
detector_lock_timeout = 2000
}
detector_lock_timeout = parseFloat(monitorDetails.detector_lock_timeout);
if(!s.group[eventData.ke].activeMonitors[eventData.id].detector_lock_timeout){
s.group[eventData.ke].activeMonitors[eventData.id].detector_lock_timeout=setTimeout(function(){
clearTimeout(s.group[eventData.ke].activeMonitors[eventData.id].detector_lock_timeout)
delete(s.group[eventData.ke].activeMonitors[eventData.id].detector_lock_timeout)
},detector_lock_timeout)
}else{
return false
}
return true
}
const runMultiTrigger = (monitorConfig,eventDetails) => {
s.getCamerasForMultiTrigger(monitorConfig).forEach(function(monitor){
if(monitor.mid !== d.id){
s.triggerEvent({
id: monitor.mid,
ke: monitor.ke,
details: {
confidence: 100,
name: "multiTrigger",
plug: eventDetails.plug,
reason: eventDetails.reason
}
})
}
})
}
const runEventExecutions = async (eventTime,monitorConfig,eventDetails,forceSave,filter,d) => {
const monitorDetails = monitorConfig.details
if(monitorDetails.det_multi_trig === '1'){
runMultiTrigger(monitorConfig,eventDetails)
}
//save this detection result in SQL, only coords. not image.
if(forceSave || (filter.save && monitorDetails.detector_save === '1')){
s.knexQuery({
action: "insert",
table: "Events",
insert: {
ke: d.ke,
mid: d.id,
details: detailString,
time: eventTime,
}
})
}
if(monitorDetails.detector === '1' && monitorDetails.detector_notrigger === '1'){
s.setNoEventsDetector(monitorConfig)
}
var detector_timeout
if(!monitorDetails.detector_timeout||monitorDetails.detector_timeout===''){
detector_timeout = 10
}else{
detector_timeout = parseFloat(monitorDetails.detector_timeout)
}
if(
filter.record &&
monitorConfig.mode === 'start' &&
monitorDetails.detector_trigger === '1' &&
(monitorDetails.detector_record_method === 'sip' || monitorDetails.detector_record_method === 'hot')
){
s.createEventBasedRecording(d,moment(eventTime).subtract(5,'seconds').format('YYYY-MM-DDTHH-mm-ss'))
}
d.currentTime = eventTime
d.currentTimestamp = s.timeObject(d.currentTime).format()
d.screenshotName = eventDetails.reason + '_'+(monitorConfig.name.replace(/[^\w\s]/gi,''))+'_'+d.id+'_'+d.ke+'_'+s.formattedTime()
d.screenshotBuffer = null
if(filter.webhook && monitorDetails.detector_webhook === '1' && !s.group[d.ke].activeMonitors[d.id].detector_webhook){
s.group[d.ke].activeMonitors[d.id].detector_webhook = s.createTimeout('detector_webhook',s.group[d.ke].activeMonitors[d.id],monitorDetails.detector_webhook_timeout,10)
var detector_webhook_url = s.addEventDetailsToString(d,monitorDetails.detector_webhook_url)
var webhookMethod = monitorDetails.detector_webhook_method
if(!webhookMethod || webhookMethod === '')webhookMethod = 'GET'
request(detector_webhook_url,{method: webhookMethod,encoding:null},function(err,data){
if(err){
s.userLog(d,{type:lang["Event Webhook Error"],msg:{error:err,data:data}})
}
})
}
if(filter.command && monitorDetails.detector_command_enable === '1' && !s.group[d.ke].activeMonitors[d.id].detector_command){
s.group[d.ke].activeMonitors[d.id].detector_command = s.createTimeout('detector_command',s.group[d.ke].activeMonitors[d.id],monitorDetails.detector_command_timeout,10)
var detector_command = s.addEventDetailsToString(d,monitorDetails.detector_command)
if(detector_command === '')return
exec(detector_command,{detached: true},function(err){
if(err)s.debugLog(err)
})
}
for (var i = 0; i < s.onEventTriggerExtensions.length; i++) {
const extender = s.onEventTriggerExtensions[i]
await extender(d,filter)
}
}
const checkForObjectsInRegions = (monitorConfig,filter,d,didCountingAlready) => {
const monitorDetails = monitorConfig.details
if(hasMatrices(monitorDetails) && monitorDetails.detector_obj_region === '1'){
var regions = s.group[monitorConfig.ke].activeMonitors[monitorConfig.mid].parsedObjects.cords
var isMatrixInRegions = isAtleastOneMatrixInRegion(regions,eventDetails.matrices)
if(isMatrixInRegions){
s.debugLog('Matrix in region!')
if(filter.countObjects && monitorDetails.detector_obj_count === '1' && monitorDetails.detector_obj_count_in_region === '1' && !didCountingAlready){
countObjects(d)
}
}else{
return false
}
}
return true
}
return {
countObjects: countObjects,
isAtleastOneMatrixInRegion: isAtleastOneMatrixInRegion,
scanMatricesforCollisions: scanMatricesforCollisions,
getLargestMatrix: getLargestMatrix,
addToEventCounter: addToEventCounter,
clearEventCounter: clearEventCounter,
getEventsCounted: getEventsCounted,
hasMatrices: hasMatrices,
checkEventFilters: checkEventFilters,
checkMotionLock: checkMotionLock,
runMultiTrigger: runMultiTrigger,
checkForObjectsInRegions: checkForObjectsInRegions,
runEventExecutions: runEventExecutions,
}
}

View File

@ -162,6 +162,7 @@ module.exports = function(s,config,lang){
}else{
var href = '/videos/'+e.ke+'/'+e.mid+'/'+k.filename
if(config.useUTC === true)href += '?isUTC=true';
const monitorEventsCounted = s.group[e.ke].activeMonitors[e.mid].detector_motion_count
s.txWithSubPermissions({
f: 'video_build_success',
hrefNoAuth: href,
@ -171,7 +172,7 @@ module.exports = function(s,config,lang){
time: k.startTime,
size: k.filesize,
end: k.endTime,
events: k.events && k.events.length > 0 ? k.events : null
events: monitorEventsCounted && monitorEventsCounted.length > 0 ? monitorEventsCounted : null
},'GRP_'+e.ke,'video_view')
//purge over max
s.purgeDiskForGroup(e.ke)
@ -195,6 +196,7 @@ module.exports = function(s,config,lang){
}
}
}
s.group[e.ke].activeMonitors[e.mid].detector_motion_count = []
}
s.deleteVideo = function(e){
//e = video object