diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..2bed546 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,33 @@ +module.exports = { + "ecmaFeatures": { + "modules": true, + "spread" : true, + "restParams" : true + }, + "env": { + "es6": true, + "node": true, + "mocha": true + }, + "parserOptions": { + "ecmaVersion": 2017, + "sourceType": "module" + }, + "extends": "eslint:recommended", + "rules": { + "indent": [ + "error", + 2 + ], + "no-console": 0, + "semi": ["error", "never"], + "linebreak-style": [ + "error", + "unix" + ], + "quotes": [ + "error", + "single" + ] + } +} diff --git a/examples/mock.mjs b/examples/mock.mjs new file mode 100644 index 0000000..651cf7e --- /dev/null +++ b/examples/mock.mjs @@ -0,0 +1,24 @@ +import Interrupt from '../src/interrupt-packet' +const delay = time => new Promise(res=>setTimeout(()=>res(),time)) + +let interrupts = new Interrupt([9,10,24],{id:'interrupt', mock:true, itrn:{path:'/opt/sockets/mcp.sock'}}) + +interrupts.interruptProcess = function (pin) { + let packet = {cmd:'pin.interrupt.find', pin:pin} + this.send(packet) +} + +; +(async () => { + + await interrupts.init() + interrupts.fire(9) + interrupts.fire(10) + interrupts.fire(24) + await delay(3000) + process.kill(process.pid, 'SIGTERM') + +})().catch(err => { + console.error('FATAL: UNABLE TO START SYSTEM!\n',err) + // process.kill(process.pid, 'SIGTERM') +}) diff --git a/package.json b/package.json index 273e269..67c1234 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,11 @@ { "name": "@uci/interrupt", - "main": "src/interrupt.js", - "version": "0.1.0", + "main": "src/interrupt-packet.mjs", + "version": "0.1.1", "description": "a class for adding interrupt processesing for gpio pins on Raspberry Pi and Similar SBCs", "scripts": { - "testi": "node test/interrupt.test.js", - "testw": "./node_modules/.bin/mocha --reporter list --recursive ", - "test": "istanbul cover ./node_modules/.bin/_mocha test/ --report lcovonly -- -R spec --recursive && codecov || true", - "watch": "./node_modules/.bin/npm-watch" + "mock": "sudo SOCKETS_DIR=/opt/sockets node_modules/.bin/nodemon --require @std/esm examples/mock", + "mockl": "sudo DEBUG=true SOCKETS_DIR=/opt/sockets node_modules/.bin/nodemon --require @std/esm examples/mock" }, "author": "David Kebler", "license": "MIT", @@ -29,12 +27,14 @@ "dependencies": { "pigpio": "^0.x" }, + "@std/esm": "cjs", "devDependencies": { - "chai": "^3.5.0", - "chai-as-promised": "^6.0.0", - "codecov": "^1.0.1", + "@std/esm": "^0.22.0", + "chai": "^4.1.2", + "chai-as-promised": "^7.1.1", + "codecov": "^3.0.0", "istanbul": "^0.4.5", - "mocha": "^3.2.0", - "npm-watch": "^0.1.7" + "mocha": "^5.0.1", + "nodemon": "^1.14.3" } } diff --git a/src/interrupt-packet.mjs b/src/interrupt-packet.mjs new file mode 100644 index 0000000..60a3204 --- /dev/null +++ b/src/interrupt-packet.mjs @@ -0,0 +1,105 @@ + +import bus, { Gpio as gpio } from 'pigpio' +// import btc from 'better-try-catch' +// import Base from '@uci/base' +import Base from '@uci/base' + +import logger from '@uci/logger' +let log = {} +const LOG_OPTS = (id) => { + return { + repo:'uci-mcp', + npm:'@uci/mcp', + file:'src/mcp230xx-packet.mjs', + class:'MCP230XX', + id:id, + instance_created:new Date().getTime() + }} + +export default class Interrupt extends Base { + constructor(pins,opts={}) { + + opts.sockets = opts.sockets ? (opts.sockets + ',') : '' + if (opts.path || opts.itrn) { + opts.itrn = opts.itrn || {} + opts.itrn.path = opts.path || opts.itrn.path || (process.env.SOCKETS_DIR || __dirname) + '/interrupt.sock' + opts.sockets = opts.sockets + 'itrn#c>n,' + } + if (opts.itrt || opts.host) { + opts.itrt = opts.itrt || {} + opts.itrt.host = opts.host || opts.itrt.host + opts.itrt.port = opts.itrt.port || opts.port || 1777 + opts.sockets = opts.sockets + 'itrt#c>t' + } + if (opts.sockets==='') throw ('must have at least one socket client') + console.dir(opts) + super(opts) + log = logger.child(LOG_OPTS(this.id)) + log.info({pins:pins, opts:opts},'create interrupts with these opts') + this.mock = opts.mock + this.mode = gpio.INPUT + this.pull = opts.pull || gpio.PUD_DOWN + this.edge = opts.edge || gpio.RISING_EDGE + this.interrupts = {} + for(let pin of pins) { + if (typeof pin==='number') {pin={num:pin}} + this.interrupts[pin.num] = { + pin: new gpio( + pin.num, + { + mode: pin.mode || this.mode, + pullUpDown: pin.pull || this.pull + // do not! set edge here as it will start the emitter -- see pigio js + }), + edge: pin.edge || this.edge, + socket: pin.socket, + cmd: pin.cmd + } + } + + } // end constructor + + + async init(){ + await super.init() + + // for cntrl-c exit of interrupt + process.on('SIGINT', () => { + this.exit().then((resp) => console.log('\n', resp)) // unexport on cntrl-c + .catch(err => console.log('error:', err)) + }) + for(const pin_num in this.interrupts) { + let pin = this.interrupts[pin_num].pin + let edge = this.interrupts[pin_num].edge + console.log(`starting interrupt on pin ${pin_num}`) + pin.on('interrupt', this.interruptProcess.bind(this,pin_num)) + // rock n roll!!, start the pigpio interrupt + console.log('edge=',edge) + if(!this.mock) pin.enableInterrupt(edge) + } + + } // end constructor + + // manual firing for testing + fire(pin_num) { + console.log('manually firing interrupt for pin', pin_num) + this.interrupts[pin_num].pin.emit('interrupt',null) + } + + exit() { + bus.terminate() + return Promise.reject('keyboard termination...terminating interrupts') + } + + interruptProcess (pin) { + console.log('=======================') + console.log(`pin ${pin} on rpi bus has thrown an interrupt`) + console.log('this is the default processor') + console.log('replace "interruptProcess" for your instance or extended class') + console.log('=======================') + } + +} // end Class + +// default handler just logs the pin number fired +// overwrite with actual socket send with appropriate packet