2018-02-27 08:42:37 -08:00
import Base from '@uci/base'
2019-03-14 10:43:09 -07:00
import DeadJim from 'death'
import { Gpio } from 'onoff'
2018-02-27 08:42:37 -08:00
2019-03-06 13:02:08 -08:00
import logger from '@uci-utils/logger'
2019-03-14 10:43:09 -07:00
let log = logger ( { package : '@uci/interrupt' , file : '/src/interrupt.js' } )
2019-03-06 13:02:08 -08:00
// a pin makes a socket (server/listner) for each pin to which a consumer can be connected
2019-03-14 10:43:09 -07:00
// if opts .port/.path/.topic/.wport are base number/name to which pin number is added/appended (default being 9000,9100,'interrupt')
// for specific port number pass itrt.port,itrn.path,itrm.topic,itrw.port which override it if present
// conPacket is for connecting consumers. This will send this conPacket command on connect, which may needed to initilize something on related hardware
2019-01-01 20:02:16 -08:00
class Interrupt extends Base {
2019-12-23 14:15:50 -08:00
2019-01-01 20:02:16 -08:00
constructor ( pin , opts = { } ) {
2019-08-15 14:05:30 -07:00
pin = Number ( pin ) // make sure pin is a number!
2018-02-27 08:42:37 -08:00
super ( opts )
2019-08-15 14:05:30 -07:00
this . id = opts . name || ( opts . id || 'interrupt' ) + ':' + pin
2019-04-26 11:13:50 -07:00
log . debug ( { pins : pin , opts : opts , method : 'constructor' , line : 16 , msg : 'created interrupt with these opts' } )
2019-03-14 10:43:09 -07:00
this . pin _num = pin
2019-04-20 16:54:07 -07:00
this . resetCmd = opts . resetCmd || 'interrupt.reset'
2019-08-15 14:05:30 -07:00
this . resetEnabled = opts . reset || opts . resetEnabled
2019-04-20 16:54:07 -07:00
this . resetInterval = opts . resetInterval * 1000 // sets an interval timeout to check on status and send/emit reset command
2018-07-31 10:05:13 -07:00
this . mock = opts . mock || process . env . MOCK
2019-01-01 20:02:16 -08:00
this . wait = opts . wait || 0 // debounce is off by default
2019-03-14 10:43:09 -07:00
// https://github.com/fivdi/onoff#gpiogpio-direction--edge--options
this . edge = opts . edge || 'rising' // falling,both,none=no interrupt
2019-04-20 16:54:07 -07:00
// 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
this . pull = opts . pull || 'down'
2019-12-23 14:15:50 -08:00
this . _ _ready = this . edge === 'both' ? true : false // true is interrupt is ready
2019-03-14 10:43:09 -07:00
this . pin = { }
2019-08-15 14:05:30 -07:00
this . count = 0
2018-02-27 08:42:37 -08:00
this . packet = opts . packet || { }
2019-03-14 10:43:09 -07:00
this . packet . id = this . id
this . packet . pin = this . pin _num
2019-04-20 16:54:07 -07:00
this . packet . cmd = this . packet . cmd || opts . cmd || opts . interruptCmd || 'interrupt'
2019-08-15 14:05:30 -07:00
this . packet . count = this . count
2019-04-20 16:54:07 -07:00
this . commands = {
fire : this . fire . 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
2019-01-01 20:02:16 -08:00
} // end constructor
2018-02-27 08:42:37 -08:00
2019-01-01 20:02:16 -08:00
async init ( ) {
2018-07-01 00:25:15 -07:00
2019-04-20 16:54:07 -07:00
this . count = 0
// 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 } )
2019-12-23 14:15:50 -08:00
// 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)
2019-04-20 16:54:07 -07:00
2019-03-14 10:43:09 -07:00
DeadJim ( ( signal , err ) => {
2019-08-15 14:05:30 -07:00
log . warn ( { signal : signal , method : 'init' , line : 56 , error : err , msg : 'Interrupt process was killed, remove watchers, unexport' } )
2019-03-14 10:43:09 -07:00
this . pin . unwatchAll ( )
this . pin . unexport ( ) // kill the kernel entry
2018-02-27 08:42:37 -08:00
} )
2019-03-08 09:05:18 -08:00
2019-03-14 10:43:09 -07:00
this . pin . watch ( function ( err , value ) {
2019-08-15 14:05:30 -07:00
log . debug ( 'interrupt tripped, value:' , value , 'error:' , err )
2019-04-20 16:54:07 -07:00
this . count += 1
this . _interruptProcess ( value , err )
2019-03-14 10:43:09 -07:00
} . bind ( this ) )
2019-03-08 09:05:18 -08:00
2019-12-23 14:15:50 -08:00
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 } )
2019-08-15 14:05:30 -07:00
2019-03-08 09:05:18 -08:00
} // end init
2018-02-27 08:42:37 -08:00
// manual firing for testing
2019-04-20 16:54:07 -07:00
async fire ( packet = { } ) {
2019-04-26 11:13:50 -07:00
log . debug ( { method : 'fire' , line : 82 , msg : ` mock manually firing interrupt for pin ${ this . pin _num } ` } )
2019-04-20 16:54:07 -07:00
await this . _interruptProcess ( 1 )
2019-03-14 10:43:09 -07:00
packet . status = 'fired'
2019-08-15 14:05:30 -07:00
packet . pin = this . pin _num
2019-04-20 16:54:07 -07:00
packet . cmd = 'reply'
return packet
}
// returns true if pin is ready and waiting to trigger interrupt
2019-08-15 14:05:30 -07:00
async status ( packet ) {
2019-04-20 16:54:07 -07:00
let status = await this . pin . read ( )
2019-12-23 14:15:50 -08:00
if ( this . edge !== 'both' ) this . _ _ready = this . pull === 'down' ? ! status : ! ! status // ready is always true for 'both'
2019-08-15 14:05:30 -07:00
if ( packet ) {
packet . pin = this . pin _num
packet . state = status
2019-12-23 14:15:50 -08:00
if ( this . edge !== 'both' ) packet . ready = this . _ _ready
2019-08-15 14:05:30 -07:00
packet . cmd = 'reply'
return packet
2019-04-20 16:54:07 -07:00
}
2019-08-15 14:05:30 -07:00
return status
2019-04-20 16:54:07 -07:00
}
2019-08-15 14:05:30 -07:00
async reset ( packet ) {
let res = { }
if ( this . edge !== 'both' && this . resetEnabled ) {
2019-12-23 14:15:50 -08:00
if ( ! this . _ _ready ) {
2019-08-15 14:05:30 -07:00
let reset = Object . assign ( { } , this . packet )
reset . cmd = this . resetCmd
this . emit ( this . resetCmd , reset ) // emit locally
await this . send ( reset )
await this . status ( )
2019-12-23 14:15:50 -08:00
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 }
2019-08-15 14:05:30 -07:00
}
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 )
2019-12-23 14:15:50 -08:00
return this . _ _ready
2019-08-15 14:05:30 -07:00
}
2019-04-20 16:54:07 -07:00
2019-03-08 09:05:18 -08:00
// use hook to do more processing
2019-04-20 16:54:07 -07:00
async _interruptProcess ( value , err ) {
let packet = Object . assign ( { } , this . packet )
2019-08-15 14:05:30 -07:00
packet . id = this . id
packet . pin = this . pin _num
2019-03-14 10:43:09 -07:00
packet . error = err
packet . state = value
2019-04-20 16:54:07 -07:00
packet . count = this . count
packet . timeStamp = Date . now ( )
packet . dateTime = new Date ( ) . toString ( )
2019-08-15 14:05:30 -07:00
if ( this . _hookFunc ) packet = await this . _hookFunc . call ( this , packet )
2019-12-23 14:15:50 -08:00
log . debug ( { packet : packet , msg : 'interrupt tripped, emit/send packet to all connected/listening' } )
2019-04-20 16:54:07 -07:00
this . emit ( 'interrupt' , packet ) // emit locally
2019-12-23 14:15:50 -08:00
this . send ( packet ) // will send packet via client to any connected socket
2018-03-02 08:32:37 -08:00
}
2019-12-23 14:15:50 -08:00
// replace default processor function arguments are value of pin and any error
registerProcessor ( func ) {
this . _interruptProcess = func
}
// hook into default interrupt processor for custom processing including packet modification
2019-03-08 09:05:18 -08:00
registerHook ( func ) {
2019-08-15 14:05:30 -07:00
if ( func ) this . _hookFunc = func
else this . _hookFunc = defaultHook
2018-02-27 08:42:37 -08:00
}
2019-03-08 09:05:18 -08:00
2018-02-27 08:42:37 -08:00
} // end Class
2019-01-01 20:02:16 -08:00
export default Interrupt
2019-03-08 09:05:18 -08:00
//default hook
async function defaultHook ( packet ) {
// return a promise or use await if anything async happens in hook
// new Promise((resolve) => {
2019-12-23 14:15:50 -08:00
console . log ( '==========example hook fucntion =============' )
2019-03-08 09:05:18 -08:00
console . log ( ` pin ${ packet . pin } on sbc gpio bus has thrown an interrupt ` )
2019-08-15 14:05:30 -07:00
console . log ( 'can change anything in the packet in this hook' )
console . log ( 'to replace this use .registerHook(function)' )
console . log ( '============================' )
2019-03-08 09:05:18 -08:00
return packet
// resolve(packet)
// })
}