var fs = require('fs'); var events = require('events'); var spawn = require('child_process').spawn; var exec = require('child_process').exec; var Mp4Frag = require('mp4frag'); var onvif = require('node-onvif'); var request = require('request'); var connectionTester = require('connection-tester'); module.exports = function(s,config,lang){ s.initiateMonitorObject = function(e){ if(!s.group[e.ke]){s.group[e.ke]={}}; if(!s.group[e.ke].mon){s.group[e.ke].mon={}} if(!s.group[e.ke].mon[e.mid]){s.group[e.ke].mon[e.mid]={}} if(!s.group[e.ke].mon[e.mid].streamIn){s.group[e.ke].mon[e.mid].streamIn={}}; if(!s.group[e.ke].mon[e.mid].emitterChannel){s.group[e.ke].mon[e.mid].emitterChannel={}}; if(!s.group[e.ke].mon[e.mid].mp4frag){s.group[e.ke].mon[e.mid].mp4frag={}}; if(!s.group[e.ke].mon[e.mid].firstStreamChunk){s.group[e.ke].mon[e.mid].firstStreamChunk={}}; if(!s.group[e.ke].mon[e.mid].contentWriter){s.group[e.ke].mon[e.mid].contentWriter={}}; if(!s.group[e.ke].mon[e.mid].childNodeStreamWriters){s.group[e.ke].mon[e.mid].childNodeStreamWriters={}}; if(!s.group[e.ke].mon[e.mid].eventBasedRecording){s.group[e.ke].mon[e.mid].eventBasedRecording={}}; if(!s.group[e.ke].mon[e.mid].watch){s.group[e.ke].mon[e.mid].watch={}}; if(!s.group[e.ke].mon[e.mid].fixingVideos){s.group[e.ke].mon[e.mid].fixingVideos={}}; if(!s.group[e.ke].mon[e.mid].record){s.group[e.ke].mon[e.mid].record={yes:e.record}}; if(!s.group[e.ke].mon[e.mid].started){s.group[e.ke].mon[e.mid].started=0}; if(s.group[e.ke].mon[e.mid].delete){clearTimeout(s.group[e.ke].mon[e.mid].delete)} if(!s.group[e.ke].mon_conf){s.group[e.ke].mon_conf={}} } s.sendMonitorStatus = function(e){ s.group[e.ke].mon[e.id].monitorStatus = e.status s.tx(Object.assign(e,{f:'monitor_status'}),'GRP_'+e.ke) } s.buildMonitorUrl = function(e,noPath){ var authd = '' var url if(e.details.muser&&e.details.muser!==''&&e.host.indexOf('@')===-1) { e.username = e.details.muser e.password = e.details.mpass authd = e.details.muser+':'+e.details.mpass+'@' } if(e.port==80&&e.details.port_force!=='1'){e.porty=''}else{e.porty=':'+e.port} url = e.protocol+'://'+authd+e.host+e.porty if(noPath !== true)url += e.path return url } s.cleanMonitorObject = function(e){ x={keys:Object.keys(e),ar:{}}; x.keys.forEach(function(v){ if(v!=='last_frame'&&v!=='record'&&v!=='spawn'&&v!=='running'&&(v!=='time'&&typeof e[v]!=='function')){x.ar[v]=e[v];} }); return x.ar; } s.getRawSnapshotFromMonitor = function(monitor,options,callback){ if(!callback){ callback = options var options = '' }else{ options = ' '+options } var url var runExtraction = function(){ var snapBuffer = [] var snapProcess = spawn(config.ffmpegDir,('-loglevel quiet -re -i '+url+options+' -frames:v 1 -f singlejpeg pipe:1').split(' '),{detached: true}) snapProcess.stdout.on('data',function(data){ snapBuffer.push(data) }); snapProcess.on('close',function(data){ snapBuffer = Buffer.concat(snapBuffer) callback(snapBuffer) }) } var checkExists = function(localStream,callback){ fs.stat(localStream,function(err){ if(err){ callback(false) }else{ callback(true) } }) } var localStream = s.dir.streams+monitor.ke+'/'+monitor.mid+'/' checkExists(localStream+'s.jpg',function(success){ if(success === false){ checkExists(localStream+'detectorStream.m3u8',function(success){ if(success === false){ checkExists(localStream+'s.m3u8',function(success){ if(success === false){ url = s.buildMonitorUrl(monitor) }else{ url = localStream+'s.m3u8' } runExtraction() }) }else{ url = localStream+'detectorStream.m3u8' runExtraction() } }) }else{ fs.readFile(localStream+'s.jpg',function(err,snapBuffer){ callback(snapBuffer) }) } }) } s.mergeDetectorBufferChunks = function(monitor,callback){ var pathDir = s.dir.streams+monitor.ke+'/'+monitor.id+'/' var mergedFile = s.formattedTime()+'.mp4' var mergedFilepath = pathDir+mergedFile var streamDirItems = fs.readdirSync(pathDir) var items = [] var copiedItems = [] var createMerged = function(copiedItems){ var allts = pathDir+items.join('_') fs.stat(allts,function(err,stats){ if(err){ //not exist var cat = 'cat '+copiedItems.join(' ')+' > '+allts exec(cat,function(){ var merger = spawn(config.ffmpegDir,s.splitForFFPMEG(('-re -i '+allts+' -acodec copy -vcodec copy '+pathDir+mergedFile))) merger.stderr.on('data',function(data){ s.log(monitor,{type:"Buffer Merge",msg:data.toString()}) }) merger.on('close',function(){ s.file('delete',allts) copiedItems.forEach(function(copiedItem){ s.file('delete',copiedItem) }) setTimeout(function(){ s.file('delete',mergedFilepath) },1000 * 60 * 3) delete(merger) callback(mergedFilepath,mergedFile) }) }) }else{ //file exist callback(mergedFilepath,mergedFile) } }) } streamDirItems.forEach(function(filename){ if(filename.indexOf('detectorStream') > -1 && filename.indexOf('.m3u8') === -1){ items.push(filename) } }) items.sort() items = items.slice(items.length - 5,items.length) items.forEach(function(filename){ try{ var tempFilename = filename.split('.') tempFilename[0] = tempFilename[0] + 'm' tempFilename = tempFilename.join('.') var tempWriteStream = fs.createWriteStream(pathDir+tempFilename) tempWriteStream.on('finish', function(){ copiedItems.push(pathDir+tempFilename) if(copiedItems.length === items.length){ createMerged(copiedItems.sort()) } }) fs.createReadStream(pathDir+filename).pipe(tempWriteStream) }catch(err){ } }) return items } s.kill = function(x,e,p){ if(s.group[e.ke]&&s.group[e.ke].mon[e.id]&&s.group[e.ke].mon[e.id].spawn !== undefined){ if(s.group[e.ke].mon[e.id].spawn){ s.group[e.ke].mon[e.id].allowStdinWrite = false s.txToDashcamUsers({ f : 'disable_stream', ke : e.ke, mid : e.id },e.ke) s.group[e.ke].mon[e.id].spawn.stdio[3].unpipe(); // if(s.group[e.ke].mon[e.id].p2pStream){s.group[e.ke].mon[e.id].p2pStream.unpipe();} if(s.group[e.ke].mon[e.id].p2p){s.group[e.ke].mon[e.id].p2p.unpipe();} delete(s.group[e.ke].mon[e.id].p2pStream) delete(s.group[e.ke].mon[e.id].p2p) delete(s.group[e.ke].mon[e.id].pamDiff) try{ s.group[e.ke].mon[e.id].spawn.removeListener('end',s.group[e.ke].mon[e.id].spawn_exit); s.group[e.ke].mon[e.id].spawn.removeListener('exit',s.group[e.ke].mon[e.id].spawn_exit); delete(s.group[e.ke].mon[e.id].spawn_exit); }catch(er){} } s.group[e.ke].mon[e.id].firstStreamChunk = {} clearTimeout(s.group[e.ke].mon[e.id].checker); delete(s.group[e.ke].mon[e.id].checker); clearTimeout(s.group[e.ke].mon[e.id].checkStream); delete(s.group[e.ke].mon[e.id].checkStream); clearTimeout(s.group[e.ke].mon[e.id].checkSnap); delete(s.group[e.ke].mon[e.id].checkSnap); clearTimeout(s.group[e.ke].mon[e.id].watchdog_stop); delete(s.group[e.ke].mon[e.id].watchdog_stop); delete(s.group[e.ke].mon[e.id].lastJpegDetectorFrame); if(e&&s.group[e.ke].mon[e.id].record){ clearTimeout(s.group[e.ke].mon[e.id].record.capturing); // if(s.group[e.ke].mon[e.id].record.request){s.group[e.ke].mon[e.id].record.request.abort();delete(s.group[e.ke].mon[e.id].record.request);} }; // if(s.group[e.ke].mon[e.id].casper){ // s.group[e.ke].mon[e.id].casper.done() // delete(s.group[e.ke].mon[e.id].casper) // clearInterval(s.group[e.ke].mon[e.id].casperCapture) // } if(s.group[e.ke].mon[e.id].childNode){ s.cx({f:'kill',d:s.cleanMonitorObject(e)},s.group[e.ke].mon[e.id].childNodeId) }else{ if(!x||x===1){return}; p=x.pid; if(s.group[e.ke].mon_conf[e.id].type===('dashcam'||'socket'||'jpeg'||'pipe')){ x.stdin.pause();setTimeout(function(){x.kill('SIGTERM');},500) }else{ try{ x.stdin.setEncoding('utf8');x.stdin.write('q'); }catch(er){} } setTimeout(function(){exec('kill -9 '+p,{detached: true})},1000) } } } s.camera=function(x,e,cn,tx){ if(x!=='motion'){ var ee=s.cleanMonitorObject(e); if(!e){e={}};if(cn&&cn.ke&&!e.ke){e.ke=cn.ke}; if(!e.mode){e.mode=x;} if(!e.id&&e.mid){e.id=e.mid} } if(e.details&&(e.details instanceof Object)===false){ try{e.details=JSON.parse(e.details)}catch(err){} } //parse Objects (['detector_cascades','cords','detector_filters','input_map_choices']).forEach(function(v){ if(e.details&&e.details[v]&&(e.details[v] instanceof Object)===false){ try{ if(e.details[v] === '') e.details[v] = '{}' e.details[v]=JSON.parse(e.details[v]); if(!e.details[v])e.details[v]={}; s.group[e.ke].mon[e.id].details = e.details; }catch(err){ } } }); //parse Arrays (['stream_channels','input_maps']).forEach(function(v){ if(e.details&&e.details[v]&&(e.details[v] instanceof Array)===false){ try{ e.details[v]=JSON.parse(e.details[v]); if(!e.details[v])e.details[v]=[]; }catch(err){ e.details[v]=[]; } } }); s.initiateMonitorObject({ke:e.ke,mid:e.id}) switch(x){ case'buildOptionsFromUrl': var monitorConfig = cn URLobject=URL.parse(e) if(monitorConfig.details.control_url_method === 'ONVIF' && monitorConfig.details.control_base_url === ''){ if(monitorConfig.details.onvif_port === ''){ monitorConfig.details.onvif_port = 8000 } URLobject.port = monitorConfig.details.onvif_port }else if(!URLobject.port){ URLobject.port = 80 } options = { host: URLobject.hostname, port: URLobject.port, method: monitorConfig.details.control_url_method, path: URLobject.pathname, }; if(URLobject.query){ options.path=options.path+'?'+URLobject.query } if(URLobject.username&&URLobject.password){ options.username = URLobject.username options.password = URLobject.password options.auth=URLobject.username+':'+URLobject.password }else if(URLobject.auth){ var auth = URLobject.auth.split(':') options.auth=URLobject.auth options.username = auth[0] options.password = auth[1] } return options break; case'control': if(!s.group[e.ke]||!s.group[e.ke].mon[e.id]){return} var monitorConfig = s.group[e.ke].mon_conf[e.id]; if(monitorConfig.details.control!=="1"){s.log(e,{type:lang['Control Error'],msg:lang.ControlErrorText1});return} if(!monitorConfig.details.control_base_url||monitorConfig.details.control_base_url===''){ e.base = s.buildMonitorUrl(monitorConfig, true); }else{ e.base = monitorConfig.details.control_base_url; } if(!monitorConfig.details.control_url_stop_timeout || monitorConfig.details.control_url_stop_timeout === ''){ monitorConfig.details.control_url_stop_timeout = 1000 } if(!monitorConfig.details.control_url_method||monitorConfig.details.control_url_method===''){monitorConfig.details.control_url_method="GET"} var controlURL = e.base+monitorConfig.details['control_url_'+e.direction] var controlURLOptions = s.camera('buildOptionsFromUrl',controlURL,monitorConfig) if(monitorConfig.details.control_url_stop_timeout === '0' && monitorConfig.details.control_stop === '1' && s.group[e.ke].mon[e.id].ptzMoving === true){ e.direction = 'stopMove' s.group[e.ke].mon[e.id].ptzMoving = false }else{ s.group[e.ke].mon[e.id].ptzMoving = true } if(monitorConfig.details.control_url_method === 'ONVIF'){ try{ var move = function(device){ var stopOptions = {ProfileToken : device.current_profile.token,'PanTilt': true,'Zoom': true} switch(e.direction){ case'center': // device.services.ptz.gotoHomePosition() msg = {type:'Center button inactive'} s.log(e,msg) cn(msg) break; case'stopMove': msg = {type:'Control Trigger Ended'} s.log(e,msg) cn(msg) device.services.ptz.stop(stopOptions).then((result) => { // console.log(JSON.stringify(result['data'], null, ' ')); }).catch((error) => { // console.error(error); }); break; default: var controlOptions = { ProfileToken : device.current_profile.token, Velocity : {} } var onvifDirections = { "left" : [-1.0,'x'], "right" : [1.0,'x'], "down" : [-1.0,'y'], "up" : [1.0,'y'], "zoom_in" : [1.0,'zoom'], "zoom_out" : [-1.0,'zoom'] } var direction = onvifDirections[e.direction] controlOptions.Velocity[direction[1]] = direction[0]; (['x','y','z']).forEach(function(axis){ if(!controlOptions.Velocity[axis]) controlOptions.Velocity[axis] = 0 }) if(monitorConfig.details.control_stop=='1'){ device.services.ptz.continuousMove(controlOptions).then(function(err){ s.log(e,{type:'Control Trigger Started'}); if(monitorConfig.details.control_url_stop_timeout !== '0'){ setTimeout(function(){ msg = {type:'Control Trigger Ended'} s.log(e,msg) cn(msg) device.services.ptz.stop(stopOptions).then((result) => { // console.log(JSON.stringify(result['data'], null, ' ')); }).catch((error) => { console.log(error); }); },monitorConfig.details.control_url_stop_timeout) } }).catch(function(err){ console.log(err) }); }else{ device.services.ptz.absoluteMove(controlOptions).then(function(err){ msg = {type:'Control Triggered'} s.log(e,msg); cn(msg) }).catch(function(err){ console.log(err) }); } break; } } //create onvif connection if(!s.group[e.ke].mon[e.id].onvifConnection){ s.group[e.ke].mon[e.id].onvifConnection = new onvif.OnvifDevice({ xaddr : 'http://' + controlURLOptions.host + ':' + controlURLOptions.port + '/onvif/device_service', user : controlURLOptions.username, pass : controlURLOptions.password }) s.group[e.ke].mon[e.id].onvifConnection.init().then((info) => { move(s.group[e.ke].mon[e.id].onvifConnection) }).catch(function(error){ console.log(error) s.log(e,{type:lang['Control Error'],msg:error}) }) }else{ move(s.group[e.ke].mon[e.id].onvifConnection) } }catch(err){ console.log(err) msg = {type:lang['Control Error'],msg:{msg:lang.ControlErrorText2,error:err,options:controlURLOptions,direction:e.direction}} s.log(e,msg) cn(msg) } }else{ var stopCamera = function(){ var stopURL = e.base+monitorConfig.details['control_url_'+e.direction+'_stop'] var options = s.camera('buildOptionsFromUrl',stopURL,monitorConfig) var requestOptions = { url : stopURL, method : options.method, auth : { user : options.username, pass : options.password } } if(monitorConfig.details.control_digest_auth === '1'){ requestOptions.sendImmediately = true } request(requestOptions,function(err,data){ if(err){ msg = {ok:false,type:'Control Error',msg:err} }else{ msg = {ok:true,type:'Control Trigger Ended'} } cn(msg) s.log(e,msg); }) } if(e.direction === 'stopMove'){ stopCamera() }else{ var requestOptions = { url : controlURL, method : controlURLOptions.method, auth : { user : controlURLOptions.username, pass : controlURLOptions.password } } if(monitorConfig.details.control_digest_auth === '1'){ requestOptions.sendImmediately = true } request(requestOptions,function(err,data){ if(err){ msg = {ok:false,type:'Control Error',msg:err}; cn(msg) s.log(e,msg); return } if(monitorConfig.details.control_stop=='1'&&e.direction!=='center'){ s.log(e,{type:'Control Triggered Started'}); if(monitorConfig.details.control_url_stop_timeout > 0){ setTimeout(function(){ stopCamera() },monitorConfig.details.control_url_stop_timeout) } }else{ msg = {ok:true,type:'Control Triggered'}; cn(msg) s.log(e,msg); } }) } } break; case'snapshot'://get snapshot from monitor URL if(config.doSnapshot===true){ if(e.mon.mode!=='stop'){ var pathDir = s.dir.streams+e.ke+'/'+e.mid+'/' if(e.mon.details.snap==='1'){ fs.readFile(pathDir+'s.jpg',function(err,data){ if(err){s.tx({f:'monitor_snapshot',snapshot:e.mon.name,snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke);return}; s.tx({f:'monitor_snapshot',snapshot:data,snapshot_format:'ab',mid:e.mid,ke:e.ke},'GRP_'+e.ke) }) }else{ e.url=s.buildMonitorUrl(e.mon); switch(e.mon.type){ case'mjpeg':case'h264':case'local': if(e.mon.type==='local'){e.url=e.mon.path;} s.getRawSnapshotFromMonitor(e.mon,'-s 400x400',function(data){ if((data[data.length-2] === 0xFF && data[data.length-1] === 0xD9)){ s.tx({ f:'monitor_snapshot', snapshot:data.toString('base64'), snapshot_format:'b64', mid:e.mid, ke:e.ke },'GRP_'+e.ke) }else{ s.tx({f:'monitor_snapshot',snapshot:e.mon.name,snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke) } }) break; case'jpeg': request({url:e.url,method:'GET',encoding:null},function(err,data){ if(err){s.tx({f:'monitor_snapshot',snapshot:e.mon.name,snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke);return}; s.tx({f:'monitor_snapshot',snapshot:data.body,snapshot_format:'ab',mid:e.mid,ke:e.ke},'GRP_'+e.ke) }) break; default: s.tx({f:'monitor_snapshot',snapshot:'...',snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke) break; } } }else{ s.tx({f:'monitor_snapshot',snapshot:'Disabled',snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke) } }else{ s.tx({f:'monitor_snapshot',snapshot:e.mon.name,snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke) } break; case'record_off'://stop recording and start if(!s.group[e.ke].mon[e.id].record){s.group[e.ke].mon[e.id].record={}} s.group[e.ke].mon[e.id].record.yes=0; s.camera('start',e); break; case'watch_on'://live streamers - join // if(s.group[e.ke].mon[e.id].watch[cn.id]){s.camera('watch_off',e,cn,tx);return} if(!cn.monitor_watching){cn.monitor_watching={}} if(!cn.monitor_watching[e.id]){cn.monitor_watching[e.id]={ke:e.ke}} s.group[e.ke].mon[e.id].watch[cn.id]={}; // if(Object.keys(s.group[e.ke].mon[e.id].watch).length>0){ // s.sqlQuery('SELECT * FROM Monitors WHERE ke=? AND mid=?',[e.ke,e.id],function(err,r) { // if(r&&r[0]){ // r=r[0]; // r.url=s.buildMonitorUrl(r); // s.group[e.ke].mon.type=r.type; // } // }) // } break; case'watch_off'://live streamers - leave if(cn.monitor_watching){delete(cn.monitor_watching[e.id])} if(s.group[e.ke].mon[e.id]&&s.group[e.ke].mon[e.id].watch){ delete(s.group[e.ke].mon[e.id].watch[cn.id]),e.ob=Object.keys(s.group[e.ke].mon[e.id].watch).length if(e.ob===0){ delete(s.group[e.ke].mon[e.id].watch) } }else{ e.ob=0; } if(tx){tx({f:'monitor_watch_off',ke:e.ke,id:e.id,cnid:cn.id})}; s.tx({viewers:e.ob,ke:e.ke,id:e.id},'MON_'+e.id); break; case'restart'://restart monitor s.sendMonitorStatus({id:e.id,ke:e.ke,status:'Restarting'}); s.camera('stop',e) setTimeout(function(){ s.camera(e.mode,e) },1300) break; case'idle':case'stop'://stop monitor if(!s.group[e.ke]||!s.group[e.ke].mon[e.id]){return} if(config.childNodes.enabled === true && config.childNodes.mode === 'master' && s.group[e.ke].mon[e.id].childNode && s.childNodes[s.group[e.ke].mon[e.id].childNode].activeCameras[e.ke+e.id]){ s.group[e.ke].mon[e.id].started = 0 s.cx({ //function f : 'cameraStop', //data, options d : s.group[e.ke].mon_conf[e.id] },s.group[e.ke].mon[e.id].childNodeId) s.cx({f:'sync',sync:s.group[e.ke].mon_conf[e.id],ke:e.ke,mid:e.id},s.group[e.ke].mon[e.id].childNodeId); }else{ if(s.group[e.ke].mon[e.id].eventBasedRecording.process){ clearTimeout(s.group[e.ke].mon[e.id].eventBasedRecording.timeout) s.group[e.ke].mon[e.id].eventBasedRecording.allowEnd=true; s.group[e.ke].mon[e.id].eventBasedRecording.process.kill('SIGTERM'); } if(s.group[e.ke].mon[e.id].fswatch){s.group[e.ke].mon[e.id].fswatch.close();delete(s.group[e.ke].mon[e.id].fswatch)} if(s.group[e.ke].mon[e.id].fswatchStream){s.group[e.ke].mon[e.id].fswatchStream.close();delete(s.group[e.ke].mon[e.id].fswatchStream)} if(s.group[e.ke].mon[e.id].last_frame){delete(s.group[e.ke].mon[e.id].last_frame)} if(s.group[e.ke].mon[e.id].started!==1){return} s.kill(s.group[e.ke].mon[e.id].spawn,e); if(e.neglectTriggerTimer===1){ delete(e.neglectTriggerTimer); }else{ clearTimeout(s.group[e.ke].mon[e.id].trigger_timer) delete(s.group[e.ke].mon[e.id].trigger_timer) } clearInterval(s.group[e.ke].mon[e.id].running); clearInterval(s.group[e.ke].mon[e.id].detector_notrigger_timeout) clearTimeout(s.group[e.ke].mon[e.id].err_fatal_timeout); s.group[e.ke].mon[e.id].started=0; if(s.group[e.ke].mon[e.id].record){s.group[e.ke].mon[e.id].record.yes=0} s.tx({f:'monitor_stopping',mid:e.id,ke:e.ke,time:s.formattedTime()},'GRP_'+e.ke); s.camera('snapshot',{mid:e.id,ke:e.ke,mon:e}) if(x==='stop'){ s.log(e,{type:lang['Monitor Stopped'],msg:lang.MonitorStoppedText}); clearTimeout(s.group[e.ke].mon[e.id].delete) if(e.delete===1){ s.group[e.ke].mon[e.id].delete=setTimeout(function(){ delete(s.group[e.ke].mon[e.id]); delete(s.group[e.ke].mon_conf[e.id]); },1000*60); } }else{ s.tx({f:'monitor_idle',mid:e.id,ke:e.ke,time:s.formattedTime()},'GRP_'+e.ke); s.log(e,{type:lang['Monitor Idling'],msg:lang.MonitorIdlingText}); } } var wantedStatus = lang.Stopped if(x==='idle'){ var wantedStatus = lang.Idle } s.sendMonitorStatus({id:e.id,ke:e.ke,status:wantedStatus}); break; case'start':case'record'://watch or record monitor url s.initiateMonitorObject({ke:e.ke,mid:e.id}) if(!s.group[e.ke].mon_conf[e.id]){s.group[e.ke].mon_conf[e.id]=s.cleanMonitorObject(e);} e.url = s.buildMonitorUrl(e); if(s.group[e.ke].mon[e.id].started===1){ //stop action, monitor already started or recording return } //lock this function s.sendMonitorStatus({id:e.id,ke:e.ke,status:lang.Starting}); s.group[e.ke].mon[e.id].started = 1; //create host string without username and password e.hosty = e.host.split('@'); if(e.hosty[1]){ //username and password found e.hosty = e.hosty[1] }else{ //no username or password in `host` string e.hosty = e.hosty[0] } //set recording status var wantedStatus = lang.Watching if(x==='record'){ var wantedStatus = lang.Recording s.group[e.ke].mon[e.id].record.yes=1; }else{ s.group[e.ke].mon[e.mid].record.yes=0; } //set the recording directory if(e.details && e.details.dir && e.details.dir !== '' && config.childNodes.mode !== 'child'){ //addStorage choice e.dir=s.checkCorrectPathEnding(e.details.dir)+e.ke+'/'; if (!fs.existsSync(e.dir)){ fs.mkdirSync(e.dir); } e.dir=e.dir+e.id+'/'; if (!fs.existsSync(e.dir)){ fs.mkdirSync(e.dir); } }else{ //MAIN videos dir e.dir=s.dir.videos+e.ke+'/'; if (!fs.existsSync(e.dir)){ fs.mkdirSync(e.dir); } e.dir=s.dir.videos+e.ke+'/'+e.id+'/'; if (!fs.existsSync(e.dir)){ fs.mkdirSync(e.dir); } } //set the temporary files directory var setStreamDir = function(){ //stream dir e.sdir=s.dir.streams+e.ke+'/'; if (!fs.existsSync(e.sdir)){ fs.mkdirSync(e.sdir); } e.sdir=s.dir.streams+e.ke+'/'+e.id+'/'; if (!fs.existsSync(e.sdir)){ fs.mkdirSync(e.sdir); }else{ s.file('deleteFolder',e.sdir+'*') } } setStreamDir() //try to create HawkEye Onvif Object // if(e.details.is_onvif === '1'){ // console.log('onvifHawk',e.hosty, e.porty.replace(':',''), e.username, e.password) // var doOnvifHawk = true // var errorCount = 0 // var hawkFail = function(msg,callback){ // ++errorCount // if(errorCount > 2){ // callback() // s.log(e,msg); // } // } // var createHawkOnvif = function(){ // if(doOnvifHawk === false){ // return false // } // if(!e.details.onvif_port || e.details.onvif_port === ''){ // e.details.onvif_port = 8000 // } // onvifHawk.connect(e.hosty, e.details.onvif_port, e.username, e.password).then(function(results){ // var camera = results // // if the camera supports events, the module will already be loaded. // if (camera.events) { // camera.events.soap.username = e.username // camera.events.soap.password = e.password // camera.events.on('messages', messages => { // console.log(messages.data.PullMessagesResponse.NotificationMessage.Message.Message.Data) // }) // camera.events.on('messages:error', error => { // if(error.body.indexOf('anonymous') > -1){ // hawkFail({type:lang.ONVIFEventsNotAvailable,msg:{msg:lang.ONVIFnotCompliantProfileT}},function(){ // camera.events.stopPull() // }) // } // }) // // start a pull event loop // setTimeout(function(){ // camera.events.startPull() // },3000) // // call stopPull() to end the event loop // // camera.events.stopPull() // s.group[e.ke].mon[e.id].HawkEyeOnvifConnection = camera // } // if(s.group[e.ke].mon[e.id].HawkEyeOnvifConnection){ // console.log('Found') // }else{ // console.log('Not Found') // } // }).catch(function(err){ // console.log('Error Connecting') // console.log(err.code) // hawkFail({type:lang.ONVIFEventsNotAvailable,msg:{msg:lang.ONVIFnotCompliantProfileT}},function(){ // doOnvifHawk = false // }) // // setTimeout(function(){ // // createHawkOnvif() // // },3000) // }) // } // createHawkOnvif() // } //set up fatal error handler if(e.details.fatal_max===''){ e.details.fatal_max = 10 }else{ e.details.fatal_max = parseFloat(e.details.fatal_max) } var errorFatal = function(errorMessage){ s.debugLog(errorMessage) clearTimeout(s.group[e.ke].mon[e.id].err_fatal_timeout); ++errorFatalCount; if(s.group[e.ke].mon[e.id].started===1){ s.group[e.ke].mon[e.id].err_fatal_timeout=setTimeout(function(){ if(e.details.fatal_max!==0&&errorFatalCount>e.details.fatal_max){ s.camera('stop',{id:e.id,ke:e.ke}) }else{ launchMonitorProcesses() }; },5000); }else{ s.kill(s.group[e.ke].mon[e.id].spawn,e); } s.sendMonitorStatus({id:e.id,ke:e.ke,status:lang.Died}); } var errorFatalCount = 0; //cutoff time and recording check interval if(!e.details.cutoff||e.details.cutoff===''){e.cutoff=15}else{e.cutoff=parseFloat(e.details.cutoff)}; if(isNaN(e.cutoff)===true){e.cutoff=15} //set master based process launcher var launchMonitorProcesses = function(){ s.group[e.ke].mon[e.id].allowStdinWrite = false s.txToDashcamUsers({ f : 'disable_stream', ke : e.ke, mid : e.id },e.ke) if(e.details.detector_trigger=='1'){ s.group[e.ke].mon[e.id].motion_lock=setTimeout(function(){ clearTimeout(s.group[e.ke].mon[e.id].motion_lock); delete(s.group[e.ke].mon[e.id].motion_lock); },15000) } //start "no motion" checker if(e.details.detector=='1'&&e.details.detector_notrigger=='1'){ if(!e.details.detector_notrigger_timeout||e.details.detector_notrigger_timeout===''){ e.details.detector_notrigger_timeout=10 } e.detector_notrigger_timeout=parseFloat(e.details.detector_notrigger_timeout)*1000*60; s.sqlQuery('SELECT mail FROM Users WHERE ke=? AND details NOT LIKE ?',[e.ke,'%"sub"%'],function(err,r){ r=r[0]; s.group[e.ke].mon[e.id].detector_notrigger_timeout_function=function(){ if(config.mail&&e.details.detector_notrigger_mail=='1'){ e.mailOptions = { from: config.mail.from, // sender address to: r.mail, // list of receivers subject: lang.NoMotionEmailText1+' '+e.name+' ('+e.id+')', // Subject line html: ''+lang.NoMotionEmailText2+' '+e.details.detector_notrigger_timeout+' '+lang.minutes+'.', }; e.mailOptions.html+='
'+lang['Monitor Name']+' : '+e.name+'
' e.mailOptions.html+='
'+lang['Monitor ID']+' : '+e.id+'
' s.nodemailer.sendMail(e.mailOptions, (error, info) => { if (error) { s.systemLog('detector:notrigger:sendMail',error) s.tx({f:'error',ff:'detector_notrigger_mail',id:e.id,ke:e.ke,error:error},'GRP_'+e.ke); return ; } s.tx({f:'detector_notrigger_mail',id:e.id,ke:e.ke,info:info},'GRP_'+e.ke); }); } } clearInterval(s.group[e.ke].mon[e.id].detector_notrigger_timeout) s.group[e.ke].mon[e.id].detector_notrigger_timeout=setInterval(s.group[e.ke].mon[e.id].detector_notrigger_timeout_function,s.group[e.ke].mon[e.id].detector_notrigger_timeout) }) } var resetRecordingCheck = function(){ clearTimeout(s.group[e.ke].mon[e.id].checker) var cutoff = e.cutoff + 0 if(e.type === 'dashcam'){ cutoff *= 100 } s.group[e.ke].mon[e.id].checker=setTimeout(function(){ if(s.group[e.ke].mon[e.id].started === 1 && s.group[e.ke].mon_conf[e.id].mode === 'record'){ launchMonitorProcesses(); s.sendMonitorStatus({id:e.id,ke:e.ke,status:lang.Restarting}); s.log(e,{type:lang['Camera is not recording'],msg:{msg:lang['Restarting Process']}}); } },60000 * cutoff * 1.1); } var resetStreamCheck=function(){ clearTimeout(s.group[e.ke].mon[e.id].checkStream) s.group[e.ke].mon[e.id].checkStream = setTimeout(function(){ if(s.group[e.ke].mon[e.id].started===1){ launchMonitorProcesses(); s.log(e,{type:lang['Camera is not streaming'],msg:{msg:lang['Restarting Process']}}); } },60000*1); } if(e.details.snap === '1'){ var resetSnapCheck = function(){ clearTimeout(s.group[e.ke].mon[e.id].checkSnap) s.group[e.ke].mon[e.id].checkSnap = setTimeout(function(){ if(s.group[e.ke].mon[e.id].started === 1){ fs.stat(e.sdir+'s.jpg',function(err,snap){ var notStreaming = function(){ launchMonitorProcesses() s.log(e,{type:lang['Camera is not streaming'],msg:{msg:lang['Restarting Process']}}); } if(err){ notStreaming() }else{ if(!e.checkSnapTime)e.checkSnapTime = snap.mtime if(err || e.checkSnapTime === snap.mtime){ e.checkSnapTime = snap.mtime notStreaming() }else{ resetSnapCheck() } } }) } },60000*1); } resetSnapCheck() } if(config.childNodes.mode !== 'child' && s.platform!=='darwin' && (x==='record' || (x==='start'&&e.details.detector_record_method==='sip'))){ //check if ffmpeg is recording s.group[e.ke].mon[e.id].fswatch = fs.watch(e.dir, {encoding : 'utf8'}, (event, filename) => { switch(event){ case'rename': s.group[e.ke].mon[e.id].open = filename.split('.')[0] break; case'change': resetRecordingCheck() break; } }); } if( //is MacOS s.platform !== 'darwin' && //is Watch-Only or Record (x === 'start' || x === 'record') && //if JPEG API enabled or Stream Type is HLS (e.details.stream_type === 'jpeg' || e.details.stream_type === 'hls' || e.details.snap === '1') ){ s.group[e.ke].mon[e.id].fswatchStream = fs.watch(e.sdir, {encoding : 'utf8'}, () => { resetStreamCheck() }) } s.camera('snapshot',{mid:e.id,ke:e.ke,mon:e}) //check host to see if has password and user in it setStreamDir() clearTimeout(s.group[e.ke].mon[e.id].checker) if(s.group[e.ke].mon[e.id].started===1){ e.error_count=0; s.group[e.ke].mon[e.id].error_socket_timeout_count=0; s.kill(s.group[e.ke].mon[e.id].spawn,e); startVideoProcessor=function(err,o){ if(o.success===true){ e.frames=0; if(!s.group[e.ke].mon[e.id].record){s.group[e.ke].mon[e.id].record={yes:1}}; //launch ffmpeg (main) s.group[e.ke].mon[e.id].spawn = s.ffmpeg(e) s.group[e.ke].mon[e.id].spawn.stdio[5].on('data',function(data){ resetStreamCheck() // var progress = {} // data.toString().split('\n').forEach(function(v){ // var split = v.split('=') // var val = split[1] // if(val)progress[split[0]] = val // }) }) if(e.type === 'dashcam'){ setTimeout(function(){ s.group[e.ke].mon[e.id].allowStdinWrite = true s.txToDashcamUsers({ f : 'enable_stream', ke : e.ke, mid : e.id },e.ke) },30000) } s.sendMonitorStatus({id:e.id,ke:e.ke,status:wantedStatus}); //on unexpected exit restart s.group[e.ke].mon[e.id].spawn_exit=function(){ if(s.group[e.ke].mon[e.id].started===1){ if(e.details.loglevel!=='quiet'){ s.log(e,{type:lang['Process Unexpected Exit'],msg:{msg:lang['Process Crashed for Monitor'],cmd:s.group[e.ke].mon[e.id].ffmpeg}}); } errorFatal('Process Unexpected Exit'); } } s.group[e.ke].mon[e.id].spawn.on('end',s.group[e.ke].mon[e.id].spawn_exit) s.group[e.ke].mon[e.id].spawn.on('exit',s.group[e.ke].mon[e.id].spawn_exit) //emitter for mjpeg if(!e.details.stream_mjpeg_clients||e.details.stream_mjpeg_clients===''||isNaN(e.details.stream_mjpeg_clients)===false){e.details.stream_mjpeg_clients=20;}else{e.details.stream_mjpeg_clients=parseInt(e.details.stream_mjpeg_clients)} s.group[e.ke].mon[e.id].emitter = new events.EventEmitter().setMaxListeners(e.details.stream_mjpeg_clients); s.log(e,{type:lang['Process Started'],msg:{cmd:s.group[e.ke].mon[e.id].ffmpeg}}); s.tx({f:'monitor_starting',mode:x,mid:e.id,time:s.formattedTime()},'GRP_'+e.ke); //start workers if(e.type==='jpeg'){ if(!e.details.sfps||e.details.sfps===''){ e.details.sfps = 1 } var capture_fps = parseFloat(e.details.sfps); if(isNaN(capture_fps)){capture_fps = 1} if(s.group[e.ke].mon[e.id].spawn){ s.group[e.ke].mon[e.id].spawn.stdin.on('error',function(err){ if(err&&e.details.loglevel!=='quiet'){ s.log(e,{type:'STDIN ERROR',msg:err}); } }) }else{ if(x==='record'){ s.log(e,{type:lang.FFmpegCantStart,msg:lang.FFmpegCantStartText}); return } } e.captureOne=function(f){ s.group[e.ke].mon[e.id].record.request=request({url:e.url,method:'GET',encoding: null,timeout:15000},function(err,data){ if(err){ return; } }).on('data',function(d){ if(!e.buffer0){ e.buffer0=[d] }else{ e.buffer0.push(d); } if((d[d.length-2] === 0xFF && d[d.length-1] === 0xD9)){ e.buffer0=Buffer.concat(e.buffer0); ++e.frames; if(s.group[e.ke].mon[e.id].spawn&&s.group[e.ke].mon[e.id].spawn.stdin){ s.group[e.ke].mon[e.id].spawn.stdin.write(e.buffer0); } if(s.group[e.ke].mon[e.id].started===1){ s.group[e.ke].mon[e.id].record.capturing=setTimeout(function(){ e.captureOne() },1000/capture_fps); } e.buffer0=null; } if(!e.timeOut){ e.timeOut=setTimeout(function(){e.error_count=0;delete(e.timeOut);},3000); } }).on('error', function(err){ ++e.error_count; clearTimeout(e.timeOut);delete(e.timeOut); if(e.details.loglevel!=='quiet'){ s.log(e,{type:lang['JPEG Error'],msg:{msg:lang.JPEGErrorText,info:err}}); switch(err.code){ case'ESOCKETTIMEDOUT': case'ETIMEDOUT': ++s.group[e.ke].mon[e.id].error_socket_timeout_count if(e.details.fatal_max!==0&&s.group[e.ke].mon[e.id].error_socket_timeout_count>e.details.fatal_max){ s.log(e,{type:lang['Fatal Maximum Reached'],msg:{code:'ESOCKETTIMEDOUT',msg:lang.FatalMaximumReachedText}}); s.camera('stop',e) }else{ s.log(e,{type:lang['Restarting Process'],msg:{code:'ESOCKETTIMEDOUT',msg:lang.FatalMaximumReachedText}}); s.camera('restart',e) } return; break; } } if(e.details.fatal_max!==0&&e.error_count>e.details.fatal_max){ clearTimeout(s.group[e.ke].mon[e.id].record.capturing); launchMonitorProcesses(); } }); } e.captureOne() } // else if(e.type === 'webpage'){ // var capture_fps = parseFloat(e.details.sfps); // if(isNaN(capture_fps)){capture_fps = 1} // var browser = casper.create() // page.goto(e.url) // s.group[e.ke].mon[e.id].casper = browser // s.group[e.ke].mon[e.id].casperCapture = setInterval(function(){ // var buffer = page.screenshot() // console.log(buffer) // // s.group[e.ke].mon[e.id].spawn.stdin.write(buffer); // },1000/capture_fps) // } if(!s.group[e.ke]||!s.group[e.ke].mon[e.id]){s.initiateMonitorObject(e)} s.group[e.ke].mon[e.id].spawn.on('error',function(er){ s.log(e,{type:'Spawn Error',msg:er});errorFatal('Spawn Error') }); if(e.details.detector==='1'){ s.ocvTx({f:'init_monitor',id:e.id,ke:e.ke}) //frames from motion detect if(e.details.detector_pam==='1'){ s.createPamDiffEngine(e) s.group[e.ke].mon[e.id].spawn.stdio[3].pipe(s.group[e.ke].mon[e.id].p2p).pipe(s.group[e.ke].mon[e.id].pamDiff) if(e.details.detector_use_detect_object === '1'){ s.group[e.ke].mon[e.id].spawn.stdio[4].on('data',function(d){ s.group[e.ke].mon[e.id].lastJpegDetectorFrame = d }) } }else{ s.group[e.ke].mon[e.id].spawn.stdio[3].on('data',function(d){ s.ocvTx({f:'frame',mon:s.group[e.ke].mon_conf[e.id].details,ke:e.ke,id:e.id,time:s.formattedTime(),frame:d}); }) } } //frames to stream switch(e.details.stream_type){ case'mp4': s.group[e.ke].mon[e.id].mp4frag['MAIN'] = new Mp4Frag(); s.group[e.ke].mon[e.id].mp4frag['MAIN'].on('error',function(error){ s.log(e,{type:lang['Mp4Frag'],msg:{error:error}}) }) s.group[e.ke].mon[e.id].spawn.stdio[1].pipe(s.group[e.ke].mon[e.id].mp4frag['MAIN']) break; case'flv': e.frame_to_stream=function(d){ if(!s.group[e.ke].mon[e.id].firstStreamChunk['MAIN'])s.group[e.ke].mon[e.id].firstStreamChunk['MAIN'] = d; e.frame_to_stream=function(d){ resetStreamCheck() s.group[e.ke].mon[e.id].emitter.emit('data',d); } e.frame_to_stream(d) } break; case'mjpeg': e.frame_to_stream=function(d){ resetStreamCheck() s.group[e.ke].mon[e.id].emitter.emit('data',d); } break; case'h265': e.frame_to_stream=function(d){ resetStreamCheck() s.group[e.ke].mon[e.id].emitter.emit('data',d); } break; case'b64':case undefined:case null:case'': var buffer e.frame_to_stream=function(d){ resetStreamCheck() if(!buffer){ buffer=[d] }else{ buffer.push(d); } if((d[d.length-2] === 0xFF && d[d.length-1] === 0xD9)){ s.group[e.ke].mon[e.id].emitter.emit('data',Buffer.concat(buffer)); buffer=null; } } break; } if(e.frame_to_stream){ s.group[e.ke].mon[e.id].spawn.stdout.on('data',e.frame_to_stream) } if(e.details.stream_channels&&e.details.stream_channels!==''){ var createStreamEmitter = function(channel,number){ var pipeNumber = number+config.pipeAddition; if(!s.group[e.ke].mon[e.id].emitterChannel[pipeNumber]){ s.group[e.ke].mon[e.id].emitterChannel[pipeNumber] = new events.EventEmitter().setMaxListeners(0); } var frame_to_stream switch(channel.stream_type){ case'mp4': s.group[e.ke].mon[e.id].mp4frag[pipeNumber] = new Mp4Frag(); s.group[e.ke].mon[e.id].spawn.stdio[pipeNumber].pipe(s.group[e.ke].mon[e.id].mp4frag[pipeNumber]) break; case'mjpeg': frame_to_stream=function(d){ s.group[e.ke].mon[e.id].emitterChannel[pipeNumber].emit('data',d); } break; case'flv': frame_to_stream=function(d){ if(!s.group[e.ke].mon[e.id].firstStreamChunk[pipeNumber])s.group[e.ke].mon[e.id].firstStreamChunk[pipeNumber] = d; frame_to_stream=function(d){ s.group[e.ke].mon[e.id].emitterChannel[pipeNumber].emit('data',d); } frame_to_stream(d) } break; case'h264': frame_to_stream=function(d){ s.group[e.ke].mon[e.id].emitterChannel[pipeNumber].emit('data',d); } break; } if(frame_to_stream){ s.group[e.ke].mon[e.id].spawn.stdio[pipeNumber].on('data',frame_to_stream); } } e.details.stream_channels.forEach(createStreamEmitter) } if(x==='record'||e.type==='mjpeg'||e.type==='h264'||e.type==='local'){ var checkLog = function(d,x){return d.indexOf(x)>-1;} s.group[e.ke].mon[e.id].spawn.stderr.on('data',function(d){ d=d.toString(); switch(true){ case checkLog(d,'[hls @'): case checkLog(d,'Past duration'): case checkLog(d,'Last message repeated'): case checkLog(d,'pkt->duration = 0'): case checkLog(d,'Non-monotonous DTS'): case checkLog(d,'NULL @'): case checkLog(d,'RTP: missed'): case checkLog(d,'deprecated pixel format used'): return break; //mp4 output with webm encoder chosen case checkLog(d,'Could not find tag for vp8'): case checkLog(d,'Only VP8 or VP9 Video'): case checkLog(d,'Could not write header'): // switch(e.ext){ // case'mp4': // e.details.vcodec='libx264' // e.details.acodec='none' // break; // case'webm': // e.details.vcodec='libvpx' // e.details.acodec='none' // break; // } // if(e.details.stream_type==='hls'){ // e.details.stream_vcodec='libx264' // e.details.stream_acodec='no' // } // s.camera('restart',e) return s.log(e,{type:lang['Incorrect Settings Chosen'],msg:{msg:d}}) break; // case checkLog(d,'av_interleaved_write_frame'): case checkLog(d,'Connection refused'): case checkLog(d,'Connection timed out'): //restart setTimeout(function(){ s.log(e,{type:lang['Connection timed out'],msg:lang['Retrying...']}); errorFatal('Connection timed out'); },1000) break; // case checkLog(d,'No such file or directory'): // case checkLog(d,'Unable to open RTSP for listening'): // case checkLog(d,'timed out'): // case checkLog(d,'Invalid data found when processing input'): // case checkLog(d,'reset by peer'): // if(e.frames===0&&x==='record'){s.deleteVideo(e)}; // setTimeout(function(){ // if(!s.group[e.ke].mon[e.id].spawn){launchMonitorProcesses()} // },2000) // break; case checkLog(d,'Immediate exit requested'): case checkLog(d,'mjpeg_decode_dc'): case checkLog(d,'bad vlc'): case checkLog(d,'error dc'): launchMonitorProcesses() break; case /T[0-9][0-9]-[0-9][0-9]-[0-9][0-9]./.test(d): var filename = d.split('.')[0]+'.'+e.ext s.insertCompletedVideo(e,{ file : filename }) s.log(e,{type:lang['Video Finished'],msg:{filename:d}}) if( e.details.detector==='1'&& s.group[e.ke].mon[e.id].started===1&& e.details&& e.details.detector_record_method==='del'&& e.details.detector_delete_motionless_videos==='1'&& s.group[e.ke].mon[e.id].detector_motion_count===0 ){ if(e.details.loglevel!=='quiet'){ s.log(e,{type:lang['Delete Motionless Video'],msg:filename}); } s.deleteVideo({ filename : filename, ke : e.ke, id : e.id }) } s.group[e.ke].mon[e.id].detector_motion_count = 0 resetRecordingCheck() return; break; } s.log(e,{type:"FFMPEG STDERR",msg:d}) }); } }else{ s.log(e,{type:lang["Ping Failed"],msg:lang.skipPingText1}); errorFatal("Ping Failed");return; } } if(e.type!=='socket'&&e.type!=='dashcam'&&e.protocol!=='udp'&&e.type!=='local' && e.details.skip_ping !== '1'){ connectionTester.test(e.hosty,e.port,2000,startVideoProcessor); }else{ startVideoProcessor(null,{success:true}) } }else{ s.kill(s.group[e.ke].mon[e.id].spawn,e); } } //start drawing files delete(s.group[e.ke].mon[e.id].childNode) if(config.childNodes.enabled === true && config.childNodes.mode === 'master'){ var childNodeList = Object.keys(s.childNodes) if(childNodeList.length>0){ e.ch_stop = 0; launchMonitorProcesses = function(){ startVideoProcessor = function(){ s.cx({ //function f : 'cameraStart', //mode mode : x, //data, options d : s.group[e.ke].mon_conf[e.id] },s.group[e.ke].mon[e.id].childNodeId) } if(e.type!=='socket'&&e.type!=='dashcam'&&e.protocol!=='udp'&&e.type!=='local' && e.details.skip_ping !== '1'){ console.log(e.hosty,e.port) connectionTester.test(e.hosty,e.port,2000,function(err,o){ if(o.success===true){ startVideoProcessor() }else{ s.log(e,{type:lang["Ping Failed"],msg:lang.skipPingText1}); errorFatal("Ping Failed");return; } }) }else{ startVideoProcessor() } } childNodeList.forEach(function(ip){ if(e.ch_stop===0&&s.childNodes[ip].cpu<80){ e.ch_stop=1; s.childNodes[ip].activeCameras[e.ke+e.id] = s.cleanMonitorObject(s.group[e.ke].mon_conf[e.id]); s.group[e.ke].mon[e.id].childNode = ip; s.group[e.ke].mon[e.id].childNodeId = s.childNodes[ip].cnid; s.cx({f:'sync',sync:s.group[e.ke].mon_conf[e.id],ke:e.ke,mid:e.id},s.group[e.ke].mon[e.id].childNodeId); launchMonitorProcesses(); } }) }else{ launchMonitorProcesses(); } }else{ launchMonitorProcesses(); } break; } if(typeof cn==='function'){setTimeout(function(){cn()},1000);} } }