uci-mcp/src/mcp230xxi.js

140 lines
5.6 KiB
JavaScript

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