0.0.6 improvements and fixes while working on irrigation project
parent
67955d3260
commit
2f6901e7ae
|
@ -0,0 +1,5 @@
|
|||
# Schedule and Queue Classes
|
||||
|
||||
For reoccuring schedule on an interval with start stop actions. Queue allows multiple schedules with facility to avoid simultanteous action if desired
|
||||
|
||||
The primary use case is to scehduel multiple irrigation zones that share a common pump (not simultaneous)
|
14
package.json
14
package.json
|
@ -1,26 +1,22 @@
|
|||
{
|
||||
"name": "@uci-utils/scheduler",
|
||||
"version": "0.0.4",
|
||||
"description": "an irrigation style scheduler interfacing with Home Assistant",
|
||||
"type": "module",
|
||||
"version": "0.0.6",
|
||||
"description": "an interval reoccuring style scheduler and runner",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"start": "node ./example/scheduler.js",
|
||||
"dev": "UCI_ENV=dev nodemon -r esm ./",
|
||||
"example:sch": "nodemon examples/schedule.js",
|
||||
"example:que": "nodemon examples/queue.js",
|
||||
"dev:debug": "UCI_ENV=dev UCI_LOG_LEVEL=debug nodemon -r esm ./"
|
||||
"example:sch": "nodemon -r esm examples/schedule.js",
|
||||
"example:que": "nodemon -r esm examples/queue.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"await-to-js": "^2.1.1",
|
||||
"clone": "^2.1.2",
|
||||
"esm": "^3.2.25",
|
||||
"moment": "^2.26.0",
|
||||
"moment-duration-format": "^2.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esm": "^3.2.25",
|
||||
"nodemon": "^1.19.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import Schedule from './schedule.js'
|
||||
import Runner from './runner.js'
|
||||
|
||||
export { Schedule, Runner }
|
||||
|
||||
|
|
@ -2,33 +2,33 @@ import clone from 'clone'
|
|||
import { EventEmitter } from 'events'
|
||||
|
||||
// Queue Multiple Schedulers
|
||||
class Queue extends EventEmitter {
|
||||
class UCIScheduleRunner extends EventEmitter {
|
||||
constructor(opts) {
|
||||
super(opts)
|
||||
this.name = opts.name || 'queue'
|
||||
this.queue = []
|
||||
if (opts.schedules) opts.schedules.forEach(sch => this.add(sch))
|
||||
this.schedules = []
|
||||
if (opts.schedules) opts.schedules.forEach(sch => this.addSchedule(sch))
|
||||
this.one = opts.one
|
||||
this._active = {} // list of active schedules organized by timeout
|
||||
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.queue[0]||{}).countdown
|
||||
return (this.schedules[0]||{}).countdown
|
||||
}
|
||||
|
||||
get names(){
|
||||
return this.queue.map(sch => {
|
||||
return this.schedules.map(sch => {
|
||||
return {name: sch.name, countdown: sch.countdown, next: sch.nextDT, simultaneous:sch.simultaneous}
|
||||
})
|
||||
}
|
||||
|
||||
get nextName(){
|
||||
return (this.queue[0]||{}).name
|
||||
return (this.schedules[0]||{}).name
|
||||
}
|
||||
|
||||
init() {
|
||||
|
@ -44,14 +44,14 @@ class Queue extends EventEmitter {
|
|||
|
||||
setTimeout () {
|
||||
// clearTimeout(this._timeout)
|
||||
const next = this.queue[0] || {}
|
||||
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.queue)
|
||||
const queue = clone(this.schedules)
|
||||
this._active[this._toID]=[]
|
||||
queue.forEach(sch =>{ // add first and any others set for same time
|
||||
dur += sch.durationMS
|
||||
|
@ -66,16 +66,11 @@ class Queue extends EventEmitter {
|
|||
},next.countdownMS)
|
||||
}
|
||||
|
||||
stop () {
|
||||
console.log('---------------------stopping queue, active schedules will complete--------------------------')
|
||||
console.log(this._timeout)
|
||||
stop (now) {
|
||||
console.log('---------------------stopping runner------------------------')
|
||||
clearTimeout(this._timeout)
|
||||
}
|
||||
|
||||
kill () {
|
||||
console.log('---------------------stopping queue, active schedules will be terminated as well--------------------')
|
||||
clearTimeout(this._timeout)
|
||||
// console.log(this._active)
|
||||
if (now) {
|
||||
console.log('also stopping all in progress schedules')
|
||||
for (let list in this._active) {
|
||||
this._active[list].forEach(sch=>{
|
||||
clearTimeout(sch._stopTimeout)
|
||||
|
@ -83,6 +78,7 @@ class Queue extends EventEmitter {
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
start() {
|
||||
this.update()
|
||||
|
@ -112,16 +108,17 @@ class Queue extends EventEmitter {
|
|||
}
|
||||
|
||||
get nextTS() {
|
||||
return (this.queue[0]||{}).nextTS
|
||||
return (this.schedules[0]||{}).nextTS
|
||||
}
|
||||
|
||||
get nextDT(){
|
||||
return (this.queue[0]||{}).nextDT
|
||||
return (this.schedules[0]||{}).nextDT
|
||||
}
|
||||
|
||||
add (sch) {
|
||||
// schedule instance
|
||||
addSchedule (sch) {
|
||||
if (getBaseClass(sch) === 'UCISchedule') {
|
||||
this.queue.push(sch)
|
||||
this.schedules.push(sch)
|
||||
this.sort()
|
||||
}
|
||||
else {
|
||||
|
@ -130,20 +127,41 @@ class Queue extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
// schedule id
|
||||
removeSchedule (id) {
|
||||
this.queue = this.queue.filter(a => a.id !== id)
|
||||
this.schedules = this.schedules.filter(a => a.id !== id)
|
||||
}
|
||||
|
||||
getSchedule (id) {
|
||||
return this.queue.find(a => a.id === 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.queue) {
|
||||
for(let sch of this.schedules) {
|
||||
// console.log('updating', sch.name)
|
||||
sch.update()
|
||||
}
|
||||
|
@ -151,12 +169,12 @@ class Queue extends EventEmitter {
|
|||
}
|
||||
|
||||
sort () {
|
||||
this.queue.sort((a,b) => a.nextTS - b.nextTS)
|
||||
this.schedules.sort((a,b) => a.nextTS - b.nextTS)
|
||||
}
|
||||
}
|
||||
|
||||
export default Queue
|
||||
export {Queue}
|
||||
export default UCIScheduleRunner
|
||||
export { UCIScheduleRunner as Runner }
|
||||
|
||||
function start (sch, toid) {
|
||||
toid = sch.toid || toid
|
|
@ -13,30 +13,23 @@ class UCISchedule extends EventEmitter {
|
|||
this.id = opts.id || this.name.replace(/ /g, '_')
|
||||
this.desc = opts.desc
|
||||
this.hour = opts.hour || 0
|
||||
this.dev = opts.dev || process.env.UCI_ENV==='dev'
|
||||
this.dev = opts.dev
|
||||
this.minute = opts.minute || 0
|
||||
this.delta = opts.delta || 6 // time to next trigger in hours
|
||||
this.duration = opts.duration || 10 // in minutes
|
||||
// computed values
|
||||
this.nextTS = 0 // the next trigger time in seconds
|
||||
this.enabled = opts.enabled || true
|
||||
this.active = false
|
||||
// this.active = false // if schedule is currently active
|
||||
// this.on('active',active=>this.active=active)
|
||||
this.simultaneous = opts.simulanteous // if true delay this scheduled event until current scheduled event completes
|
||||
// this.lastActiveTS
|
||||
this._startAction = opts.startAction || defaultStartAction.bind(this) // single (or array) of function(s)
|
||||
this._stopAction = opts.stopAction || defaultStopAction.bind(this) // single (or array) of function(s)
|
||||
this.update()
|
||||
}
|
||||
|
||||
// async readyWait () {
|
||||
// return new Promise( resolve => {
|
||||
// if (!this.active) {
|
||||
// resolve()
|
||||
// return}
|
||||
// this.once('active',active=>{if (!active) resolve()})
|
||||
// })
|
||||
// }
|
||||
|
||||
get countdown() {
|
||||
return moment.duration(parseInt((this.nextTS - Math.floor(Date.now()/1000))),'seconds').format('hh:mm:ss')
|
||||
}
|
||||
|
@ -65,6 +58,7 @@ class UCISchedule extends EventEmitter {
|
|||
// }
|
||||
|
||||
update() { // all TS in seconds
|
||||
// console.log('updating',this.hour,this.minute,this.delta, this.dev)
|
||||
let baseTS = this.hour*3600+this.minute*60
|
||||
let dt = new Date()
|
||||
let intoDayTS = (dt.getSeconds() + (60 * dt.getMinutes()) + (60 * 60 * dt.getHours()))
|
||||
|
@ -81,20 +75,25 @@ class UCISchedule extends EventEmitter {
|
|||
}
|
||||
|
||||
registerStartAction(func) {
|
||||
if (!Array.isArray(func)) func = [func]
|
||||
this.startAction = func
|
||||
//
|
||||
// if (!Array.isArray(func)) func = [func]
|
||||
this._startAction = func
|
||||
}
|
||||
|
||||
registerStopAction(func) {
|
||||
if (!Array.isArray(func)) func = [func]
|
||||
this.stopAction = func
|
||||
// if (!Array.isArray(func)) func = [func]
|
||||
this._stopAction = func
|
||||
}
|
||||
|
||||
// todo support a array of functions
|
||||
async startAction (data) {
|
||||
this.active = true
|
||||
this.on('active',true)
|
||||
this._startAction(data)
|
||||
}
|
||||
|
||||
async stopAction (data) {
|
||||
this.active = false
|
||||
this.on('active',false)
|
||||
this._stopAction(data)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue