uci-utils-scheduler/src/runner.js

218 lines
5.9 KiB
JavaScript

import clone from 'clone'
import { EventEmitter } from 'events'
// Queue Multiple Schedulers
class UCIScheduleRunner extends EventEmitter {
constructor(opts) {
super(opts)
this.name = opts.name || 'queue'
this.schedules = []
if (opts.schedules) opts.schedules.forEach(sch => this.addSchedule(sch))
this.one = opts.one
this._toID = 0 // id of timeout
this._delayed = []
this._activeCount = 0
this._active = {} // object or lists of active schedules by timeout id
this._pausedSchedules = []
this.init()
}
get countdown() {
return (this.schedules[0]||{}).countdown
}
get names(){
return this.schedules.map(sch => {
return {name: sch.name, countdown: sch.countdown, next: sch.nextDT, simultaneous:sch.simultaneous}
})
}
get nextName(){
return (this.schedules[0]||{}).name
}
init() {
this.on('active', active=>{
if (!active && this._delayed.length) {
const sch=this._delayed.shift()
// console.log('a delayed action now proceeding====>', sch.name, sch.toid)
start.call(this,sch)
}
})
}
setTimeout () {
// clearTimeout(this._timeout)
const next = this.schedules[0] || {}
this._toID++
console.log('+++++++++++++++++++++ next timeout',this._toID, 'in', Math.round(next.countdownMS/1000),'++++++++++++++++++++++++++')
this._timeout = setTimeout(() => {
console.log('**********************timeout triggered', this._toID, '********************************')
let dur = 0
// don't mutate original queue, make copy for active list
const queue = clone(this.schedules)
this._active[this._toID]=[]
queue.forEach(sch =>{ // add first and any others set for same time
dur += sch.durationMS
// console.log('check if active', sch.name, sch.countdownMS, dur)
if (sch.countdownMS <= dur) this._active[this._toID].push(sch)
if (sch.one) this.removeSchedule(sch.id)
})
// this._active[this._toID].next = 0
this.trigger(this._active[this._toID].length,this._toID)
this.emit('active',this._activeCount)
if (!this.one) this.start()
},next.countdownMS)
}
stop (now) {
console.log('---------------------stopping runner------------------------')
clearTimeout(this._timeout)
if (now) {
console.log('also stopping all in progress schedules')
for (let list in this._active) {
this._active[list].forEach(sch=>{
clearTimeout(sch._stopTimeout)
sch.stopAction()}
)
}
}
}
start() {
this.update()
this.setTimeout()
}
trigger (count,toid) { // trigger all schedules actions that have same timestamp as first in queue
if (count < 1 || count > this._active[toid].length) {
// console.log('done: returning')
return
}
// console.log(count,toid,this._active[toid])
const idx = this._active[toid].length-count
const sch = this._active[toid][idx]
sch.toid = toid
// let next = this._active[toid].next
// console.log(idx,sch)
count--
if (idx===0 && !this._activeCount) start.call(this,sch)
if (sch.simultaneous) start.call(this,sch)
else {
// console.log('adding schedule to delay',sch.name,sch.toid)
this._delayed.push(sch)
// console.log(this._delayed.length)
}
this.trigger(count,toid)
}
get nextTS() {
return (this.schedules[0]||{}).nextTS
}
get nextDT(){
return (this.schedules[0]||{}).nextDT
}
// schedule instance
addSchedule (sch) {
if (getBaseClass(sch) === 'UCISchedule') {
this.schedules.push(sch)
this.sort()
}
else {
console.log('ERROR: passed schedule was not a instance of UCISchedule')
console.log(sch.name)
}
}
// schedule id
removeSchedule (id) {
this.schedules = this.schedules.filter(a => a.id !== id)
}
getSchedule (id) {
return this.schedules.find(a => a.id === id)
}
updateSchedule (id) {
this.getSchedule(id).update()
}
enableSchedule(id,state) {
if (state == null) state = true
if (state) {
const sch = this._pausedSchedules.find(a => a.id === id)
if (sch) {
this.addSchedule(sch)
sch.enabled = true
sch.emit('enabled',true)
}
} else {
const sch = this.schedules.find(a => a.id === id)
if (sch) {
this._pausedSchedules.push(sch)
this.removeSchedule(id)
sch.enabled = false
sch.emit('enabled',false)
}
}
}
update () {
for(let sch of this.schedules) {
// console.log('updating', sch.name)
sch.update()
}
this.sort()
}
sort () {
this.schedules.sort((a,b) => a.nextTS - b.nextTS)
}
}
export default UCIScheduleRunner
export { UCIScheduleRunner as Runner }
function start (sch, toid) {
toid = sch.toid || toid
console.log('-----timeoutid',toid, 'start action-----')
sch.startAction()
this._activeCount++
this.emit('active',1)
sch._stopTimeout = setTimeout(() =>{ // stop action timeout
console.log('---timeoutid',toid, 'stop action----')
sch.stopAction()
this._activeCount--
// done remove from active list
this._active[toid]=this._active[toid].filter(s=>s.id!==sch.id)
if (!this._active[toid].length) {
// none left on that list, remove it
console.log('===========actions for timeout', toid, 'are complete===========')
delete this._active[toid]
}
// console.log('schedule action complete', sch.name,toid, this._activeCount)
this.emit('active',this._activeCount)
},sch.durationMS)
}
function getBaseClass(targetClass){
if(targetClass instanceof Object){
let baseClass = targetClass
while (baseClass){
const newBaseClass = Object.getPrototypeOf(baseClass)
if(newBaseClass && newBaseClass !== Object && newBaseClass.name){
baseClass = newBaseClass
}else{
break
}
}
return baseClass.constructor.name
}
}