218 lines
5.9 KiB
JavaScript
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
|
|
}
|
|
}
|