reorganizes commands

add new server/socket namespace "commands" for server side base module
add new namespace "commands" for consumer side for interrupt version of module
master
David Kebler 2018-07-31 10:29:11 -07:00
parent 1497491d9c
commit 95926a892c
5 changed files with 162 additions and 146 deletions

View File

@ -4,31 +4,35 @@
*/
import { MCP230XXi } from '../src'
import { MCP230XX } from '../src'
import Base from '@uci/base'
const BUS_HOST = 'sbc'
const BUS_HOST = 'sbc' // if ihost not given will be the same as host:
const BUS_PORT = 1776 // 1776 is default port for i2c-bus module
const SW_ADDRESS = 0x25 // 1776 is default port for i2c-bus module
const RY_ADDRESS = 0x27 // 1776 is default port for i2c-bus module
// let chip8 = new MCP230XX({id:'mcp8-27', address:0x27, nmcp: { path: '/opt/sockets/mcp2.sock' }})
// let sw17 = new MCP230XXi([9,10],{id:'sw17', chip17:true, host:BUS_HOST, address:SW_ADDRESS, iport:9000, 10:{mport:'B'}})
let announce = new Base({id:'switch:announce'})
let sw17 = new MCP230XXi([9,10],{id:'sw17', chip17:true, host:BUS_HOST, address:SW_ADDRESS+1, iport:true, 10:{mport:'B'}})
let sw8 = new MCP230XXi([24],{id:'sw8', host:BUS_HOST, address:SW_ADDRESS, iport:true})
let ry8 = new MCP230XX({id:'ry8', host:BUS_HOST, address:RY_ADDRESS})
sw17.reply = () => {}
ry8.reply = () => {}
sw8.reply = () => {}
let sw17 = new MCP230XXi([9,10],{id:'sw17', chip17:true, host:BUS_HOST, address:SW_ADDRESS+1})
// let sw8 = new MCP230XXi([24],{id:'sw8', host:BUS_HOST, address:SW_ADDRESS})
// let ry8 = new MCP230XX({id:'ry8', host:BUS_HOST, address:RY_ADDRESS})
const reply = { reply: () => {} }
// ry8.c = reply;
sw17.c=reply
// sw8.c= reply
function iprocess(details) {
console.log('--common interrupt processor--')
console.log(details)
action(details)
announce.push()
// action function can do whatever but best would be to push the details to any attached client (mqtt or some database that then pushes
// local_action(details)
}
async function action (details) {
// just something to do
async function local_action (details) {
if (details.id === 'sw17') {
await ry8.pin.state.toggle({pins:details.pin})
await ry8.pin.state.toggle({pins:'2,4,6,8'})
}
if (details.id === 'sw8') {
if (details.pin===1) {
@ -46,19 +50,15 @@ async function action (details) {
(async () => {
await sw17.init()
let cfg = {port:'B',pins:'1', cfg:'toggle_switch_pulldown'}
console.log(await sw17.pin.cfg(cfg))
sw17.interruptProcessor(iprocess)
await sw8.init()
sw8.interruptProcessor(iprocess)
await ry8.init()
let packet = {pins:'all', cfg:'output'}
await ry8.pin.cfg(packet)
// await sw8.init()
// sw8.interruptProcessor(iprocess)
//
//
// await ry8.init()
// let packet = {pins:'all', cfg:'output'}
// await ry8.pin.cfg(packet)
})().catch(err => {
console.error('FATAL: UNABLE TO START SYSTEM!\n',err)

View File

@ -1,10 +1,11 @@
{
"name": "@uci/mcp",
"main": "src",
"version": "0.1.4",
"version": "0.1.8",
"description": "Classes and Helper Functions for using the MCP chip on I2C Bus",
"scripts": {
"relays": "node -r esm examples/relays",
"swrl": "UCI_LOG=true node -r esm examples/mcp-switch-relay | pino-colada",
"swr": "node -r esm examples/mcp-switch-relay",
"test": "./node_modules/.bin/mocha --reporter list --timeout 30000",
"testw": "./node_modules/.bin/mocha --reporter list -- watch --timeout 30000",

View File

@ -3,102 +3,105 @@
import _ from '@uci/utils/src/byte'
import { CHIP, PIN } from './config'
export const chip = {
export default {
chip:{
// for custom chip configuration set packet.cfg='custom' then packet.setting should be a
// configuration byte with given format ('STR' by defaul).
cfg: async function(packet){
cfg: async function(packet){
// first make sure chip is in set to BANK=0 if not already
let bus = await this.bus.write(0x05,0)
if (bus.error) return bus
let setting = {}
let cfg = packet.cfg || 'default'
if (cfg === 'custom') setting = {val:packet.setting, fmt:packet.fmt || 'STR'}
else {
if (CHIP[cfg]) setting = CHIP[cfg]
else return {error:`no chip settings for ${cfg}`}
let bus = await this.bus.write(0x05,0)
if (bus.error) return bus
let setting = {}
let cfg = packet.cfg || 'default'
if (cfg === 'custom') setting = {val:packet.setting, fmt:packet.fmt || 'STR'}
else {
if (CHIP[cfg]) setting = CHIP[cfg]
else return {error:`no chip settings for ${cfg}`}
}
let byte = _.byteFormat(setting.val, { in: setting.fmt, out: 'DEC' })
if (byte < 128) byte += 128 // make sure BANK=1 remains on
let reg = this.chip17 ? 0x0A : 0x05
// console.log(this.chip17, reg, this.id, this.address)
bus = await this.bus.write(reg,byte)
if (bus.error) return bus
bus = await this.bus.read(0x05)
if (bus.error) return bus
return { cmd:'reply', response:_.byteFormat(bus.response,{in:'DEC',out:'STR'}) }
}
let byte = _.byteFormat(setting.val, { in: setting.fmt, out: 'DEC' })
if (byte < 128) byte += 128 // make sure BANK=1 remains on
let reg = this.chip17 ? 0x0A : 0x05
// console.log(this.chip17, reg, this.id, this.address)
bus = await this.bus.write(reg,byte)
if (bus.error) return bus
bus = await this.bus.read(0x05)
if (bus.error) return bus
return { cmd:'reply', response:_.byteFormat(bus.response,{in:'DEC',out:'STR'}) }
}
}
// Individual Pin Configurations
// set is for cfg: 'custom' assume a setting is zero unless given
// packet = { pin:, port:, cfg: , set:{dir: 0, ivrt: 0, pullup: 0, intr: 0, usedef: 0,defval: 0} }
// first get the current byte (pin) state for that setting
export const pin = {
cfg: async function(packet){
let cfg = {}
let reply = { cmd:'reply', status:{} }
packet.cfg = packet.cfg || 'output'
if (packet.cfg==='custom') cfg = packet.set
else cfg = PIN.cfgset[packet.cfg]
for(let name of Object.keys(PIN.setting)) {
let op = cfg[name] ? 'on' : 'off'
// console.log(name, op)
let busreply = await this.pin._state(packet,op,PIN.setting[name])
if (busreply.error) return busreply
reply.status[name] = busreply.status
}
return reply
},
// state is equivalent to read
status: async function (packet) {
let reg = packet.reg ? PIN.cmd[packet.reg] : PIN.cmd.gpio
if (!reg) return {error:`unknown register ${packet.reg}`, packet:packet }
let reply = { cmd:'reply'}
let pins = parsePins(packet.pins)
let state = new _.Byte()
let bus = await this.bus.read(sreg(reg, packet.port))
if (bus.error) return bus
state.value = bus.response
reply.status =
// Individual Pin Configurations
// set is for cfg: 'custom' assume a setting is zero unless given
// packet = { pin:, port:, cfg: , set:{dir: 0, ivrt: 0, pullup: 0, intr: 0, usedef: 0,defval: 0} }
// first get the current byte (pin) state for that setting
pin:{
cfg: async function(packet){
let cfg = {}
let reply = { cmd:'reply', status:{} }
packet.cfg = packet.cfg || 'output'
if (packet.cfg==='custom') cfg = packet.set
else cfg = PIN.cfgset[packet.cfg]
for(let name of Object.keys(PIN.setting)) {
let op = cfg[name] ? 'on' : 'off'
// console.log(name, op)
let busreply = await this.pin._state(packet,op,PIN.setting[name])
if (busreply.error) return busreply
reply.status[name] = busreply.status
}
return reply
},
// state is equivalent to read
status: async function (packet) {
let reg = packet.reg ? PIN.cmd[packet.reg] : PIN.cmd.gpio
if (!reg) return {error:`unknown register ${packet.reg}`, packet:packet }
let reply = { cmd:'reply'}
let pins = parsePins(packet.pins)
let state = new _.Byte()
let bus = await this.bus.read(sreg(reg, packet.port))
if (bus.error) return bus
state.value = bus.response
reply.status =
{ port:state.toFmt('ARY'),
pins: pins.value.map(pin => {
if (state.toFmt('PLC').indexOf(pin) !==-1) return [pin, 'on']
else return [pin,'off']
})
}
return reply
},
_state: async function(packet,op,reg){
reg = (reg!==undefined)? reg : PIN.cmd.gpio
// console.log(op, reg, packet)
let reply = { cmd:'reply'}
let pins = parsePins(packet.pins)
let state = new _.Byte()
let bus = await this.bus.read(sreg(reg,packet.port))
if (bus.error) return bus
state.value = bus.response
bus = await this.bus.write(sreg(reg,packet.port),state.bwOp(pins.value,op,{in:'PLC', out:'DEC'}))
if (bus.error) return bus
bus = await this.bus.read(sreg(reg,packet.port))
if (bus.error) return bus
state.value = bus.response
reply.status = state.bwOp(pins.value,'check',{in:'PLC', out:'PLC'})
return reply
},
// threse three only for output pins
state : {
on: async function (packet) {
return this.pin._state(packet,'on')
return reply
},
off: async function (packet) {
return this.pin._state(packet,'off')
_state: async function(packet,op,reg){
reg = (reg!==undefined)? reg : PIN.cmd.gpio
// console.log(op, reg, packet)
let reply = { cmd:'reply'}
let pins = parsePins(packet.pins)
let state = new _.Byte()
let bus = await this.bus.read(sreg(reg,packet.port))
if (bus.error) return bus
state.value = bus.response
bus = await this.bus.write(sreg(reg,packet.port),state.bwOp(pins.value,op,{in:'PLC', out:'DEC'}))
if (bus.error) return bus
bus = await this.bus.read(sreg(reg,packet.port))
if (bus.error) return bus
state.value = bus.response
reply.status = state.bwOp(pins.value,'check',{in:'PLC', out:'PLC'})
return reply
},
toggle: async function (packet) {
return this.pin._state(packet,'toggle')
// threse three only for output pins
state : {
on: async function (packet) {
return this.pin._state(packet,'on')
},
off: async function (packet) {
return this.pin._state(packet,'off')
},
toggle: async function (packet) {
return this.pin._state(packet,'toggle')
}
}
}
} // end pin.
} // end pin.
}
const parsePins = function(pins) {
if (typeof pins==='number') pins = [pins]

View File

@ -1,6 +1,6 @@
import Device from '@uci/i2c-device'
// import Device from '../../uci-i2c-device/src/device-packet'
import { pin, chip } from './commands'
import commands from './commands'
import logger from '@uci/logger'
let log = {}
@ -10,13 +10,15 @@ export default class MCP230XX extends Device {
super(opts)
log = logger({file:'src/mcp230xx.js',class:'MCP230XX',name:'mcp',id:this.id})
this.chip17 = opts.chip17
this.pin = this.bindFuncs(pin)
this.chip = this.bindFuncs(chip)
this.commands = this.bindFuncs(commands)
this.addNamespace('commands','s') // allow access to commands via socket/server
this.pin = this.commands.pin // add a simplier reference for local access
this.chipcfg = this.commands.chip.cfg // add a simplier reference for local access
}
async init(){
await super.init()
let res = await this.chip.cfg({})
let res = await this.chipcfg({})
let cfg = this.chip17 ?'10100010':'00100010'
if (res.response !==cfg ) throw `could not configure mcp chip at ${this.address}=0x${this.address.toString(16)}`
}

View File

@ -4,56 +4,64 @@ import { byteFormat } from '@uci/utils/src/byte'
import logger from '@uci/logger'
let log = {}
// sockets:'inter#s>t', inter:{port:INTERRUPT_PORT}
// if opts.iport not set then will be generated based on pin number
export default class MCP230XXi extends MCP230XX {
constructor(pins,opts) {
if (typeof opts.iport ==='number' || opts.ipath) {
if (typeof opts.iport ==='number') {
opts.sockets = (opts.sockets ? (opts.sockets + ',') : '') + 'inter#s>t'
opts.sockets = (opts.sockets ? (opts.sockets + ',') : '') + 'inter#c>t'
opts.inter = {port:opts.iport}
opts.inter.host = opts.ihost || opts.host
}
if (opts.ipath) {
opts.inter = { path: opts.ipath || 'interrupt'}
opts.sockets = (opts.sockets ? (opts.sockets + ',') : '') + 'inter#s>n'
opts.sockets = (opts.sockets ? (opts.sockets + ',') : '') + 'inter#c>n'
}
}
else {
pins.forEach( pin => {
pins.forEach( (pin,index) => {
let ipin = 'i'+pin
opts[ipin] = opts[pin] || {}
if (index===1) opts[ipin].mport = opts[ipin].mort || 'B'
delete(opts[pin])
if (opts[ipin].port || opts.iport !=='number') {
opts[ipin].port = opts[ipin].port || opts.iport
if (typeof opts[ipin].port !=='number') opts[ipin].port = 9000 + pin
opts.sockets = (opts.sockets ? (opts.sockets + ',') : '') + ipin +'#s>t'
opts[ipin].host = opts[ipin].host || opts.ihost || opts.host
opts.sockets = (opts.sockets ? (opts.sockets + ',') : '') + ipin +'#c>t'
}
// either on the same host as bus and interrupt or not - no need for both
else {
opts[pin].path = opts[pin].path || opts.ipath
if(!opts[pin].path) Object.assign(opts[pin],{path:'interrupt:'+pin})
opts.sockets = (opts.sockets ? (opts.sockets + ',') : '') + pin+'#s>n'
opts.sockets = (opts.sockets ? (opts.sockets + ',') : '') + pin+'#c>n'
}
})
}
super(opts)
log = logger({file:'src/mcp230xxi.js',class:'MCP230XXi',name:'mcp',id:this.id})
log.info({opts:opts},'mcp interrupt options after calling super() on base')
pins.forEach(pin => {
this[pin] = opts[pin] || {}
this[pin] = opts['i'+pin] || {}
})
this.pins = pins
log = logger({file:'src/mcp230xxi.js',class:'MCP230XXi',name:'mcp',id:this.id})
Object.assign(this.pin, this.bindFuncs(pin)) // add interrupt pin commands to base set in "command.js"
Object.assign(this.commands.pin, this.bindFuncs(ipincommands)) // add interrupt pin commands to base set in "command.js"
this.addNamespace('commands','c')
this._interruptProcess = null
this.ready = false
log.info({opts:opts, pins:pins}, 'options for mcp interrupt processor')
}
async init(){
await super.init()
this.pins.forEach(async pin => {
// console.log(this.id,pin,this[pin])
// let cfg = {port:this[pin].mport||'A',pins:this[pin].pins||'all', cfg:this[pin].type ||'toggle_switch'}
// console.log(cfg)
await this.pin.cfg({port:this[pin].mport||'A',pins:this[pin].pins||'all', cfg:this[pin].type ||'toggle_switch'})
this.pin.interrupt.reset(this[pin].mport)
let cfg = {port:this[pin].mport||'A',pins:this[pin].pins||'all', cfg:this[pin].type ||'toggle_switch'}
await this.pin.cfg(cfg)
log.info('resetting mcp port for corresponding gpio pin')
await this.pin.interrupt.reset(this[pin].mport)
this.ready=true
})
this.on('interrupt', function (details) {
@ -71,32 +79,34 @@ export default class MCP230XXi extends MCP230XX {
} // end of MCP230XX Class
const pin = { interrupt: {
// commands to be added to pin command functions
const ipincommands= { interrupt: {
reset: async function (port) {
console.log('resetting interrupt for port',port || 'A',this.id)
log.info(`resetting interrupt for port ${port || 'A'},${this.id}`)
return await this.bus.read(port!=='B' ? 0x08 : 0x18) // 0x08 is intcap interrupt capture register
},
find: async function (inter) {
// console.dir(inter)
let packet = {pins:'all',reg:'intf'}
packet.port = inter.port || this[inter.pin].mport || 'A'
// console.log(`${inter.count}th interrupt fired, pin:${inter.pin}`)
// console.log('packet to read port\n',packet.port)
let res = await this.pin.status(packet)
this.pin.interrupt.reset(packet.port)
if (!res.status) return {error:'no pin associated with interrupt'}
let pin = byteFormat(res.status.port, { in: 'ARY', out: 'PLC' })
// console.log('pin and port that caused the interrupt', pin, packet.port)
res.pin = pin[0]
packet.pins = pin[0]
packet.reg = null
res.state = (await this.pin.status(packet)).status.pins[0][1]
res.port = packet.port
res.count = inter.count
res.inter = inter.pin
delete res.status
this.emit('interrupt',res)
return res
// given a gpio interrupt then push a packet with cmd: 'pin.interrupt.find' and pin: the gpio pin number
find: async function (inter) { // inter is a hash packet
if(this.ready){ // protects tripped interrupt before it's fully initialized and reset
log.info({packet:inter},'finding mcp pin which caused interrupt')
let packet = {pins:'all',reg:'intf'}
packet.port = inter.port || this[inter.pin].mport || 'A'
let res = await this.pin.status(packet)
log.info('found pin now resetting mcp port interrupt')
await this.pin.interrupt.reset(packet.port)
if (!res.status) return {error:'no pin associated with interrupt'}
let pin = byteFormat(res.status.port, { in: 'ARY', out: 'PLC' })
res.pin = pin[0]
packet.pins = pin[0]
packet.reg = null
res.state = (await this.pin.status(packet)).status.pins[0][1]
res.port = packet.port
res.count = inter.count
res.inter = inter.pin
delete res.status
this.emit('interrupt',res)
return res
}
}
}
}