import {MCP230XX, map, changed, isPlainObject, to, merge} from './mcp230xx' import { byteFormat } from '@uci-utils/byte' // import Ready from '@uci-utils/ready' import logger from '@uci-utils/logger' let log = {} class MCP230XXi extends MCP230XX { constructor(pins, options={}) { if (!Array.isArray(pins)) { options = pins; pins = options.interrupt.pins} // if pins sent via .interrupt.pins let opts = Object.assign({},options) // don't allow passed options to mutate if (opts.interrupt) delete opts.interrupt.pins // if .interrupt was passed then .pins must be removed opts.lport = opts.lport || (opts.interrupt || {}).port || opts.iport if (!opts.lport) opts.lpath = opts.lpath || (opts.interrupt || {}).ipath || opts.ipath || 'interrupt' // must has a socket litener for interrupt process super(opts) this.pinsCfg = opts.pinsCfg || this.chip17 ? [{port:'A', pins:'all', cfg:'input_interrupt'},{port:'B', pins:'all', cfg:'input_interrupt'}] : [{pins:'all', cfg:'input_interrupt'}] log = logger({ file: 'src/mcp230xxi.js', class: 'MCP230XXi', name: 'mcp-interrupt', id: this.id }) pins.forEach((pin,index) => { this[pin] = opts['i' + pin] || {} this[pin].mport = this[pin].mport || index ? 'B' : 'A' }) this.pins = pins this.commands.interrupt = this.bindFuncs(icommands) // add interrupt pin commands to base set in "command.js" this._interruptProcess = process.bind(this) // default processor this.ready.addObserver(`${opts.interrupt.name}:process`) // will emit on received interrupt process ready packet let interruptobs = this.ready.combineObservers(`${opts.interrupt.name}`,['mcp:configure',this.getSocket(opts.interrupt.name).obsName,`${opts.interrupt.name}:process`]) this.ready.addObserver('interrupt:reset',interruptobs .pipe( map(async ready => { if (ready) { let imcpready = await this.resetInterrupt() if (imcpready){ let res = await this.send(opts.interrupt.name,{cmd:'status'}) ready = res.ready || res.pins.reduce((acc,pin)=>acc&&pin.ready,true) } } return ready }) //map )) let socket = this.getSocket(`${options.name}:${options.interrupt.remote ? 't':'n'}`) // mcp t or n socket was created this.consumerConnected(socket,{add:true,consumer:options.interrupt.name}) } // end constructor async interruptState(pin) { let istate = await this.bus.read(this.getPort(pin) !== 'B' ? 0x07 : 0x17) let pullup = (this.chipCfg||'').includes('Pullup') ? true : false let state = istate.response ? true && pullup : false || !pullup return state } async resetInterrupt(pins) { if (!Array.isArray(pins)) pins = pins ? [pins] : this.pins this._ireset = (await Promise.all(pins.map(async pin => { await this.bus.read(this.getPort(pin) !== 'B' ? 0x08 : 0x18) // 0x08 is intcap interrupt capture register return(await this.interruptState(pin)) }) )).reduce((res,val) => res && val) return this._ireset } getPort(pin) { if (pin==='A' || pin==='B') return pin return this[pin].mport } registerInterruptProcessor(func) { this._interruptProcess = func.bind(this) } } // end of MCP230XXi Class export default MCP230XXi export { MCP230XXi, map, changed, isPlainObject, to, merge} // default processor function process(details) { details.id = this.id console.log('----default interrupt processor for mcp instance----') console.log('here is where you could either locally take some action or send on a message to another process') console.log('create your own function and register it with .interruptProcesser(function)') console.log(this.id) console.dir(details) console.log('------------------------------------------') } // commands to be added to pin packet command functions const icommands = { status: async function(packet) { let pin = packet.ipin || packet.pin let state = await this.interruptState(pin) return {cmd:'reply', request:'interrupt.status', port:this.getPort(pin), ipin:pin, ready:state} }, reset: async function(packet) { let pin = packet.ipin || packet.pin let state = await this.resetInterrupt(pin) let res = {level:state ? 'debug':'error', msg:`remote reset request from ${packet._header.sender.instanceID} for pin ${pin}: ${state?'ready': 'error'}` } this.emit('log',res) return {state:state} }, // finds which mcp pin caused the interrupt find: async function(inter) { let ipin = inter.ipin || inter.pin if (!await this.interruptState(ipin)) { // if it's already reset then this is false trip let res = await this.commands.pin.status({ pins: 'all', reg: 'intf', port:this.getPort(ipin) }) // read port interrupt status let status = await this.resetInterrupt(ipin) // now reset let pin = byteFormat(res.port||[], { in: 'ARY', out: 'PLC' })[0] if (!pin) { log.warn({cmd:'interrupt.find', line:132, inter:inter, msg:'no pin associated with interrupt'}) return { error: 'no pin associated with interrupt' } } else { // avoid bad interrupt (i.e. no pin caused interrupt) delete inter.cmd; delete inter._header inter.ipin = ipin inter.pin = pin inter.port = this[ipin].mport inter.interrupt_ready = status inter.state = (await this.commands.pin.status({pin:pin, port:this.getPort(ipin)})).state inter.closed = inter.state === 'on' ? true : false this._interruptProcess(inter) // replying with reset command which is also a check return {cmd:'reset', pin:ipin} } } } } // end commands