0.2.24
interrupts: better integration with single interrupt methods via common function better event bubble up from single interrupt allow access to methods via a socket if enabled (same with single) allow consumer reply access to reset method (same with single) Interrupt: improve handling of interval reset with new methodmaster
parent
216bd6b98d
commit
48b6680585
|
@ -1,43 +1,29 @@
|
|||
import Interrupts from '../src/interrupts'
|
||||
|
||||
const PINS = [4]
|
||||
const PINS = [9,10]
|
||||
|
||||
const EDGE = process.env.EDGE || 'both'
|
||||
const REMOTE_HOST = process.env.REMOTE_HOST || 'sbc'
|
||||
const REMOTE_PORT = process.env.REMOTE_PORT || 9000
|
||||
const PORT = process.env.PORT || 9004
|
||||
const REMOTE_PORT = process.env.REMOTE_PORT || 9001
|
||||
|
||||
let interrupts = new Interrupts(PINS,{id:'multi-interrupt-example', resetInterval:1, resetEnabled:false, edge:EDGE, 4:{name:'mybutton'} })
|
||||
let interrupts = new Interrupts(PINS, { id:'multi-interrupt', cmd:'interrupt.find', resetCmd:'interrupt.reset', resetInterval:0, edge:EDGE })
|
||||
|
||||
// let hook = function (packet)
|
||||
// {
|
||||
// packet.cmd = 'interrupt.find'
|
||||
// return packet
|
||||
// }
|
||||
|
||||
// interrupts.registerHook(hook)
|
||||
interrupts.amendConsumerCommands( {reply:() => {}} )
|
||||
|
||||
interrupts.on('log', ev => {
|
||||
console.log(`LOG:'--${ev.level}--" ${ev.msg}`)}
|
||||
)
|
||||
|
||||
interrupts.on('connection:socket', ev => {
|
||||
// console.dir(ev)
|
||||
console.log(`connected to remote socket ${ev.socketName}: ${ev.state}`)
|
||||
if (ev.level !== 'debug' && ev.level !== 'trace') console.log(`LOG:'--${ev.level}-- ${ev.msg}`)
|
||||
// console.log(`LOG:'--${ev.level}-- ${ev.msg}`)
|
||||
})
|
||||
|
||||
interrupts.on('connection:socket', ev => {
|
||||
console.log(`remote socket connection event ${ev.socketName}: ${ev.state}`)
|
||||
})
|
||||
|
||||
//
|
||||
// interrupts.listenReset(function (packet) {
|
||||
// console.log(`============== ${this.id}=========`)
|
||||
// console.log('an interrupt reset request emitted')
|
||||
// console.dir(packet)
|
||||
// console.log('------------------------')
|
||||
// })
|
||||
;
|
||||
(async () => {
|
||||
|
||||
interrupts.registerSocket('inter','c','t',{host:REMOTE_HOST, port:REMOTE_PORT})
|
||||
interrupts.registerSocket('inters','s','t',{port:PORT})
|
||||
interrupts.registerSocket('interrupt','c','t',{host:REMOTE_HOST, port:REMOTE_PORT, name:'interrupt', data:{pins:PINS, type:'interrupt'}})
|
||||
// interrupts.registerSocket('listen','s','t',{port:1777})
|
||||
await interrupts.init()
|
||||
await interrupts.socketsInit()
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@uci/interrupt",
|
||||
"main": "src",
|
||||
"version": "0.2.23",
|
||||
"version": "0.2.24",
|
||||
"description": "a class for adding interrupt processesing for gpio pins on Raspberry Pi and Similar SBCs",
|
||||
"scripts": {
|
||||
"single": "node -r esm examples/single",
|
||||
|
|
|
@ -17,16 +17,15 @@ class Interrupt extends Base {
|
|||
log.debug({ pins: pin, opts: opts, method:'constructor', line:16, msg:'created interrupt with these opts'})
|
||||
this.pin_num = pin
|
||||
this.resetCmd = opts.resetCmd || 'interrupt.reset'
|
||||
this.resetEnabled = opts.reset|| opts. resetEnabled
|
||||
this.resetInterval = opts.resetInterval * 1000 // sets an interval timeout to check on status and send/emit reset command
|
||||
this.resetInterval = opts.resetInterval
|
||||
this.mock = opts.mock || process.env.MOCK
|
||||
this.wait = opts.wait || 0 // debounce is off by default
|
||||
// https://github.com/fivdi/onoff#gpiogpio-direction--edge--options
|
||||
this.edge = opts.edge || 'rising' // falling,both,none=no interrupt
|
||||
// pull down/up (down is default) can't be set here it is done by in DTOs or in RPI in config.txt
|
||||
// this is only used to monitor the status of the interrupt
|
||||
// pull down/up (down is default) can't be set here
|
||||
// it is done by in DTOs or in RPI in config.txt or via an external pullup/down resistor
|
||||
// this setting is only needed to monitor the state of the ready interrupt and should match what is set in hardware
|
||||
this.pull = opts.pull || 'down'
|
||||
this.__ready = this.edge === 'both' ? true : false // true is interrupt is ready
|
||||
this.pin = {}
|
||||
this.count = 0
|
||||
this.packet = opts.packet || {}
|
||||
|
@ -34,12 +33,13 @@ class Interrupt extends Base {
|
|||
this.packet.pin = this.pin_num
|
||||
this.packet.cmd = this.packet.cmd || opts.cmd || opts.interruptCmd || 'interrupt'
|
||||
this.packet.count = this.count
|
||||
this.commands = {
|
||||
fire:this.fire.bind(this),
|
||||
this.amendConsumerCommands({reset:this.reset.bind(this)})
|
||||
this.amendSocketCommands({
|
||||
reset:this.reset.bind(this),
|
||||
status:this.status.bind(this),
|
||||
reset: this.reset.bind(this)
|
||||
}
|
||||
this.addNamespace('commands', 's') // give access to these commands above if a socket/server is created
|
||||
fire:this.fire.bind(this),
|
||||
intervalReset:this.intervalReset.bind(this)
|
||||
})
|
||||
} // end constructor
|
||||
|
||||
async init() {
|
||||
|
@ -48,11 +48,9 @@ class Interrupt extends Base {
|
|||
// TODO devel mock versions for testing on other than sbc with gpios
|
||||
this.pin = new Gpio(this.pin_num, 'in', this.edge, { debounceTimeout:this.wait })
|
||||
|
||||
// if (this.resetEnabled) log.debug({msg:'initial connect interrupt reset packet sent', ready:await this.reset(), method:'init', line:53})
|
||||
// if (this.resetInterval && this.resetEnabled) setInterval(this.reset.bind(this),this.resetInterval)
|
||||
|
||||
DeadJim( (signal,err) => {
|
||||
log.warn({signal:signal, method:'init', line:56, error:err, msg:'Interrupt process was killed, remove watchers, unexport'})
|
||||
clearInterval(this._intervalReset)
|
||||
this.pin.unwatchAll()
|
||||
this.pin.unexport() // kill the kernel entry
|
||||
})
|
||||
|
@ -63,8 +61,9 @@ class Interrupt extends Base {
|
|||
this._interruptProcess(value,err)
|
||||
}.bind(this))
|
||||
|
||||
log.info({msg:'new interrupt pin created and watching', method:'init', line: 62, pin_num:this.pin_num, state:await this.status(), ready:this.__ready, edge:this.edge,debounce:this.wait})
|
||||
await this.intervalReset(this.resetInterval)
|
||||
|
||||
this.emit('log',{level:'info', msg:`new interrupt pin ${this.pin_num} created and watching`, state:await this.status(), edge:this.edge,debounce:this.wait})
|
||||
|
||||
} // end init
|
||||
|
||||
|
@ -74,40 +73,54 @@ class Interrupt extends Base {
|
|||
await this._interruptProcess(1)
|
||||
packet.status = 'fired'
|
||||
packet.pin = this.pin_num
|
||||
packet.cmd = 'reply'
|
||||
return packet
|
||||
}
|
||||
|
||||
// returns true if pin is ready and waiting to trigger interrupt
|
||||
async status(packet) {
|
||||
let status = await this.pin.read()
|
||||
if (this.edge !=='both') this.__ready = this.pull==='down' ? !status : !!status // ready is always true for 'both'
|
||||
let state = await this.pin.read()
|
||||
let ready = this.pull==='down' ? !state : !!state // ready is always true for 'both'
|
||||
if (packet) {
|
||||
packet.pin = this.pin_num
|
||||
packet.state = status
|
||||
if (this.edge !=='both') packet.ready = this.__ready
|
||||
packet.cmd = 'reply'
|
||||
packet.state = state
|
||||
if (this.edge !=='both') packet.ready = ready
|
||||
return packet
|
||||
}
|
||||
return status
|
||||
return this.edge ==='both' ? state : ready
|
||||
}
|
||||
|
||||
async reset(packet) {
|
||||
async intervalReset(packet) {
|
||||
// console.log('intervalReset ---- passed argument',packet)
|
||||
let interval = typeof packet === 'number'? packet : (packet || {}).interval
|
||||
console.log('----------intervalReset--------', this.pin_num, interval)
|
||||
if (!interval || interval<=0) {
|
||||
clearInterval(this._intervalReset)
|
||||
this._intervalReset = null
|
||||
console.log('---------disabled--------')
|
||||
}
|
||||
else {
|
||||
this._intervalReset = setInterval(this.reset.bind(this), interval*1000)
|
||||
console.log('----------enabled----------')
|
||||
}
|
||||
return this._intervalReset ? true : false
|
||||
}
|
||||
|
||||
async reset(packet={}) {
|
||||
let res = {}
|
||||
if (this.edge !=='both' && this.resetEnabled) {
|
||||
if (!this.__ready) {
|
||||
let reset = Object.assign({},this.packet)
|
||||
reset.cmd = this.resetCmd
|
||||
this.emit(this.resetCmd,reset) // emit locally
|
||||
await this.send(reset)
|
||||
await this.status()
|
||||
log.error({msg: `interrupt was forced reset. ready now? ${this.__ready}`})
|
||||
res = {cmd:'reply', msg:`attempted interrupt reset ${this.__ready? 'succeeded' : 'failed'}`, reset:true, ready:this.__ready}
|
||||
this.emit('log',{level:'info', msg:`interrupt reset request for pin ${this.pin_num}`})
|
||||
if (this.edge ==='both') res = {level:'info', reset:false, ready:true, msg:'interrupt triggered on rising and falling, no reset action needed'}
|
||||
else {
|
||||
if(!await this.status()) {
|
||||
delete packet._header
|
||||
this.emit('reset',packet) // emit locally
|
||||
packet.cmd = this.resetCmd
|
||||
let state = await this.status()
|
||||
res = {level:state?'debug':'error', msg:`attempted interrupt reset of pin ${this.pin_num} ${state ? 'succeeded' : 'failed'}`, reset:true, ready:state}
|
||||
}
|
||||
else res = {cmd:'reply', reset:false, ready:true, msg:'interrupt was ready, no action taken'}
|
||||
} else res = {cmd:'reply', reset:false, ready:true, msg:'reset NA or disabled'}
|
||||
if (packet) return Object.assign(packet,res)
|
||||
return this.__ready
|
||||
else res = {level:'info', reset:false, ready:true, msg:`pin ${this.pin_num} interrupt was ready, no action taken`}
|
||||
}
|
||||
this.emit('log',res)
|
||||
return res
|
||||
}
|
||||
|
||||
// use hook to do more processing
|
||||
|
@ -121,9 +134,9 @@ class Interrupt extends Base {
|
|||
packet.timeStamp = Date.now()
|
||||
packet.dateTime = new Date().toString()
|
||||
if (this._hookFunc) packet = await this._hookFunc.call(this,packet)
|
||||
log.debug({packet: packet, msg:'interrupt tripped, emit/send packet to all connected/listening'})
|
||||
this.emit('log',{packet: packet, msg:`interrupt tripped for pin ${this.pin_num}, emit/send packet to all listening`})
|
||||
this.emit('interrupt',packet) // emit locally
|
||||
this.send(packet) // will send packet via client to any connected socket
|
||||
this.send(packet) // no need to await reply
|
||||
}
|
||||
|
||||
// replace default processor function arguments are value of pin and any error
|
||||
|
|
|
@ -15,12 +15,13 @@ class Interrupts extends Base {
|
|||
this._interrupts = new Map()
|
||||
this.resetCmd = opts.resetCmd || 'interrupt.reset'
|
||||
log = logger({ name: 'interrupts', id: this.id, package:'@uci/interrupt', file:'src/interrupts.js'})
|
||||
this.commands = {
|
||||
fire:makefunc.bind(this,'fire'),
|
||||
status:makefunc.bind(this,'status'),
|
||||
this.amendConsumerCommands({reset:makefunc.bind(this,'reset')})
|
||||
this.amendSocketCommands({
|
||||
reset:makefunc.bind(this,'reset'),
|
||||
}
|
||||
this.addNamespace('commands', 's') // give access to these commands above if a socket/server is created
|
||||
status:makefunc.bind(this,'status'),
|
||||
fire:makefunc.bind(this,'fire'),
|
||||
intervalReset:makefunc.bind(this,'intervalReset')
|
||||
})
|
||||
let pinopts = {}
|
||||
pins.forEach(pin => {
|
||||
// remove per pin opts and store
|
||||
|
@ -33,8 +34,10 @@ class Interrupts extends Base {
|
|||
log.debug({ opts: pinopts[pin], method:'constructor', line:25, msg:`pin options for pin ${pin}`})
|
||||
this._interrupts.set(pin, new Interrupt(pin, pinopts[pin]))
|
||||
|
||||
// TODO add socket and add methods to namespace if set
|
||||
|
||||
// bubble up events from single interrupts
|
||||
const EVENTS=['log','connection','connection:consumer', 'connection:socket'] // that should emit up from pin base
|
||||
const EVENTS=['log','connection:consumer', 'connection:socket'] // that should emit up from pin base
|
||||
EVENTS.forEach(event => {
|
||||
this.interrupt(pin).on(event, obj => {
|
||||
if (Object.prototype.toString.call(obj) !== '[object Object]') {
|
||||
|
@ -51,13 +54,15 @@ class Interrupts extends Base {
|
|||
|
||||
this._interrupts.forEach( inter => {
|
||||
inter.on('interrupt', packet => {
|
||||
this.send(packet) // send via common consumer
|
||||
this.emit('interrupt',packet)
|
||||
})
|
||||
inter.on(this.resetCmd, packet => {
|
||||
this.send(packet) // send via common consumer
|
||||
this.emit(this.resetCmd,packet)
|
||||
inter.on('reset', packet => {
|
||||
this.emit('reset',packet)
|
||||
})
|
||||
// overwrite single interrupt send
|
||||
inter.send = async (packet) => {
|
||||
return await this.send(packet) // send via common consumer
|
||||
}
|
||||
})
|
||||
|
||||
} // end constructor
|
||||
|
@ -65,6 +70,7 @@ class Interrupts extends Base {
|
|||
interrupt(pin) { return this._interrupts.get(Number(pin)) } // get a handle to single interrupt
|
||||
|
||||
async init() {
|
||||
|
||||
return Promise.all(
|
||||
Array.from(this._interrupts).map(inter => {
|
||||
return inter[1].init()
|
||||
|
@ -94,13 +100,13 @@ class Interrupts extends Base {
|
|||
export default Interrupts
|
||||
|
||||
|
||||
async function makefunc(fn, packet) {
|
||||
async function makefunc(fn, packet={}) {
|
||||
if (!packet.pin || packet.pin==='all') {
|
||||
let res = {}
|
||||
for (let inter of this._interrupts.entries()) {
|
||||
packet[inter[0]] = await inter[1][fn]()
|
||||
res[inter[0]] = await inter[1][fn](packet)
|
||||
}
|
||||
packet.cmd='reply'
|
||||
return packet
|
||||
return Object.assign(packet,res)
|
||||
}
|
||||
let pin = isNaN(Number(packet)) ? packet.pin : packet
|
||||
if (this._interrupts.has(Number(pin))) return await this.interrupt(packet.pin)[fn](packet)
|
||||
|
|
Loading…
Reference in New Issue