367 lines
16 KiB
JavaScript
367 lines
16 KiB
JavaScript
|
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,
|
||
|
}
|
||
|
}
|