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 {
constructor ( pin , opts = { } ) {
2019-03-07 07:35:08 -08:00
if ( typeof pin !== 'number' ) pin = parseInt ( pin ) // make sure pin is a number!
2019-03-14 10:43:09 -07:00
opts . conPacket = ( opts . resetCmd && ! opts . conPacket ) ? { cmd : opts . resetCmd , pin : pin } : opts . conPacket // will use either option
log . debug ( { conPacket : opts . conPacket , msg : 'connection packet for consumers' } )
2019-01-01 20:02:16 -08:00
if ( opts . path || opts . itrn ) {
2018-02-27 08:42:37 -08:00
opts . itrn = opts . itrn || { }
2019-03-06 13:02:08 -08:00
if ( opts . path && typeof opts . path !== 'boolean' ) opts . path = opts . path + ':' + pin
if ( typeof opts . path === 'boolean' ) opts . path = ''
opts . itrn . path = opts . itrn . path || opts . path || 'interrupt:' + pin
2018-07-31 17:28:02 -07:00
opts . itrn . conPacket = opts . conPacket
2019-01-01 20:02:16 -08:00
opts . sockets = ( opts . sockets ? opts . sockets + ',' : '' ) + 'itrn#s>n'
2018-07-01 00:25:15 -07:00
}
2019-03-06 13:02:08 -08:00
if ( opts . topic || opts . itrm ) {
2018-07-01 00:25:15 -07:00
opts . itrm = opts . itrm || { }
2019-03-06 13:02:08 -08:00
opts . itrm . topics = opts . itrm . topic || opts . topic + '/' + pin || 'interrupt/' + pin
2019-01-01 20:02:16 -08:00
opts . sockets = ( opts . sockets ? opts . sockets + ',' : '' ) + 'itrm#s>m'
2018-07-01 00:25:15 -07:00
}
2019-03-06 13:02:08 -08:00
if ( opts . itrw || opts . wport || opts . wport === 0 ) {
opts . itrw = opts . itrw || { }
2019-03-07 07:35:08 -08:00
if ( opts . wport ) opts . wport = opts . wport + + pin
opts . itrw . port = opts . itrw . port || opts . wport || 9100 + + pin
2019-01-01 20:02:16 -08:00
opts . sockets = ( opts . sockets ? opts . sockets + ',' : '' ) + 'itrw#s>w'
2018-02-27 08:42:37 -08:00
}
2018-07-01 00:25:15 -07:00
// default is a tcp socket server at 9000+pin
2019-03-06 13:02:08 -08:00
if ( opts . itrt || opts . port || opts . port === 0 || ! opts . sockets ) {
2018-07-01 00:25:15 -07:00
opts . itrt = opts . itrt || { }
2019-03-07 07:35:08 -08:00
if ( opts . port ) opts . port = opts . port + + pin
opts . itrt . port = opts . itrt . port || opts . port || 9000 + + pin
2018-07-31 17:28:02 -07:00
opts . itrt . conPacket = opts . conPacket
2019-01-01 20:02:16 -08:00
opts . sockets = ( opts . sockets ? opts . sockets + ',' : '' ) + 'itrt#s>t'
2018-07-01 00:25:15 -07:00
}
2018-02-27 08:42:37 -08:00
super ( opts )
2019-01-01 20:02:16 -08:00
this . id = ( opts . id || 'interrupt' ) + ':' + pin
log . info ( { pins : pin , opts : opts } , 'created interrupt with these opts' )
2019-03-14 10:43:09 -07:00
this . pin _num = pin
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
// this.pull = opts.pull || 'low' // 'high'
this . pin = { }
2019-03-08 09:05:18 -08:00
this . hook = opts . hook
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-03-07 07:35:08 -08:00
this . packet . cmd = this . packet . cmd || opts . pushCmd || 'interrupt'
2018-02-27 08:42:37 -08:00
this . packet . count = 0
2019-03-14 10:43:09 -07:00
this . _hookFunc = defaultHook
this . s = { fire : this . fire . bind ( this ) } // make fire available via consumer packet send
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-02-27 08:42:37 -08:00
await super . init ( )
2018-07-01 00:25:15 -07:00
2019-03-14 10:43:09 -07:00
DeadJim ( ( signal , err ) => {
log . warn ( { signal : signal , error : err , msg : 'Interrupt was killed' } )
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 = new Gpio ( this . pin _num , 'in' , this . edge , { debounceTimeout : this . wait } )
log . debug ( { msg : 'new interrupt pin created and watching' , pin : this . pin , edge : this . edge , debounce : this . wait } )
this . pin . watch ( function ( err , value ) {
log . debug ( 'sbc interrupt tripped' , err , value )
this . _interruptProcess ( this . packet , err , value )
} . bind ( this ) )
2019-03-08 09:05:18 -08:00
} // end init
2018-02-27 08:42:37 -08:00
// manual firing for testing
2019-03-14 10:43:09 -07:00
async fire ( packet ) {
2018-02-27 08:42:37 -08:00
console . log ( 'manually firing interrupt for pin' , this . pin _num )
2019-03-14 10:43:09 -07:00
await this . _interruptProcess ( this . packet , null , 'manual' )
packet . status = 'fired'
return packet
2018-02-27 08:42:37 -08:00
}
2019-03-08 09:05:18 -08:00
// use hook to do more processing
2019-03-14 10:43:09 -07:00
async _interruptProcess ( packet , err , value ) {
console . log ( 'from watch listener' , packet . pin , err , value )
packet . error = err
packet . state = value
2018-02-27 08:42:37 -08:00
packet . count += 1
packet . time = new Date ( ) . getTime ( )
2019-03-08 09:05:18 -08:00
if ( this . hook ) packet = await this . _hookFunc . call ( this , packet )
2019-03-14 10:43:09 -07:00
log . debug ( { pin : this . pin , packet : packet } , 'packet pushing to all clients' )
await this . push ( packet )
2018-03-02 08:32:37 -08:00
}
2019-03-08 09:05:18 -08:00
registerHook ( func ) {
this . _hookFunc = func
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) => {
console . log ( '==========default hook =============' )
console . log ( ` pin ${ packet . pin } on sbc gpio bus has thrown an interrupt ` )
console . log ( ` pushing to all connected socket client with cmd: ${ packet . cmd } ` )
2019-03-14 10:43:09 -07:00
console . dir ( packet )
2019-03-08 09:05:18 -08:00
console . log ( 'replace by a new function with .registerHook(function) to overwrite this' )
console . log ( 'Must be async/promise returning if anything async happens in your hook' )
2019-03-14 10:43:09 -07:00
console . log ( 'This hook allows you to modify/add to the packet being pushed to connected clients' )
2019-03-08 09:05:18 -08:00
console . log ( 'the function will be bound to the instance for complete access' )
console . log ( 'if you pass a hash for .hook you can use it here as this.hook' )
console . log ( 'the hook options contains' , this . hook )
console . log ( 'by default the instance id will be attached to the packet before this' )
return packet
// resolve(packet)
// })
}