Backup intval module, in case this whole thing goes awry.
This commit is contained in:
parent
70ca24c62f
commit
f953a79080
|
@ -0,0 +1,423 @@
|
|||
'use strict'
|
||||
|
||||
const db = require('../db')
|
||||
const log = require('../log')('intval')
|
||||
const storage = require('node-persist')
|
||||
const fs = require('fs')
|
||||
|
||||
let Gpio
|
||||
try {
|
||||
Gpio = require('onoff').Gpio
|
||||
} catch (e) {
|
||||
log.warn('Failed including Gpio, using sim')
|
||||
Gpio = require('../../lib/onoffsim').Gpio
|
||||
}
|
||||
|
||||
|
||||
const PINS = {
|
||||
fwd : {
|
||||
pin : 13,
|
||||
dir : 'out'
|
||||
},
|
||||
bwd : {
|
||||
pin : 19,
|
||||
dir : 'out'
|
||||
},
|
||||
micro : {
|
||||
pin : 5,
|
||||
dir : 'in',
|
||||
edge : 'both'
|
||||
},
|
||||
release : {
|
||||
pin : 6,
|
||||
dir : 'in',
|
||||
edge : 'both'
|
||||
}
|
||||
}
|
||||
|
||||
/** Object representing the intval3 features */
|
||||
const intval = {}
|
||||
|
||||
intval._frame = {
|
||||
open : 250, //delay before pausing frame in open state
|
||||
openBwd : 400,
|
||||
closed : 100, //time that frame actually remains closed for
|
||||
expected : 530 //expected length of frame, in ms
|
||||
}
|
||||
intval._release = {
|
||||
min : 20,
|
||||
seq : 1000
|
||||
}
|
||||
intval._microDelay = 10 // delay after stop signal before stopping motors
|
||||
intval._pin = {}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
intval.init = function () {
|
||||
if (!fs.existsSync('./state')) fs.mkdirSync('./state')
|
||||
storage.init({
|
||||
dir: './state',
|
||||
stringify: JSON.stringify,
|
||||
parse: JSON.parse,
|
||||
encoding: 'utf8',
|
||||
logging: false, // can also be custom logging function
|
||||
continuous: true, // continously persist to disk
|
||||
interval: false, // milliseconds, persist to disk on an interval
|
||||
ttl: false, // ttl* [NEW], can be true for 24h default or a number in MILLISECONDS
|
||||
expiredInterval: 2 * 60 * 1000, // [NEW] every 2 minutes the process will clean-up the expired cache
|
||||
forgiveParseErrors: false // [NEW]
|
||||
}).then(intval._restoreState).catch((err) => {
|
||||
log.warn('init', err)
|
||||
intval.reset()
|
||||
intval._declarePins()
|
||||
})
|
||||
|
||||
process.on('SIGINT', intval._undeclarePins)
|
||||
process.on('uncaughtException', intval._undeclarePins)
|
||||
}
|
||||
|
||||
intval._restoreState = function (res) {
|
||||
storage.getItem('_state', 'test').then(intval._setState).catch((err) => {
|
||||
intval._setState(undefined)
|
||||
log.error('_restoreState', err)
|
||||
})
|
||||
intval._declarePins()
|
||||
}
|
||||
|
||||
intval._setState = function (data) {
|
||||
if (typeof data !== 'undefined') {
|
||||
intval._state = data
|
||||
intval._state.frame.cb = () => {}
|
||||
log.info('_setState', 'Restored intval state from disk')
|
||||
return true
|
||||
}
|
||||
log.info('_setState', 'Setting state from defaults')
|
||||
intval._state = {
|
||||
frame : {
|
||||
dir : true, //forward
|
||||
start : 0, //time frame started, timestamp
|
||||
active : false, //should frame be running
|
||||
paused : false,
|
||||
exposure : 0, //length of frame exposure, in ms
|
||||
delay : 0, //delay before start of frame, in ms
|
||||
current : {}, //current settings
|
||||
cb : () => {}
|
||||
},
|
||||
release : {
|
||||
time: 0,
|
||||
active : false //is pressed
|
||||
},
|
||||
micro : {
|
||||
time : 0,
|
||||
primed : false //is ready to stop frame
|
||||
},
|
||||
counter : 0,
|
||||
sequence : false
|
||||
}
|
||||
intval._storeState()
|
||||
}
|
||||
|
||||
intval._storeState = function () {
|
||||
storage.setItem('_state', intval._state)
|
||||
.then(() => {})
|
||||
.catch((err) => {
|
||||
log.error('_storeState', err)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* (internal function) Declares all Gpio pins that will be used
|
||||
*
|
||||
*/
|
||||
intval._declarePins = function () {
|
||||
let pin
|
||||
for (let p in PINS) {
|
||||
pin = PINS[p]
|
||||
if (pin.edge) intval._pin[p] = new Gpio(pin.pin, pin.dir, pin.edge)
|
||||
if (!pin.edge) intval._pin[p] = new Gpio(pin.pin, pin.dir)
|
||||
log.info('_declarePins', { pin : pin.pin, dir : pin.dir, edge : pin.edge })
|
||||
}
|
||||
intval._pin.release.watch(intval._watchRelease)
|
||||
}
|
||||
/**
|
||||
* (internal function) Undeclares all Gpio in event of uncaught error
|
||||
* that interupts the node process
|
||||
*
|
||||
*/
|
||||
intval._undeclarePins = function (e) {
|
||||
log.error(e)
|
||||
if (!intval._pin) {
|
||||
log.warn('_undeclarePins', { reason : 'No pins'})
|
||||
return process.exit()
|
||||
}
|
||||
log.warn('_undeclarePins', { pin : PINS.fwd.pin, val : 0, reason : 'exiting'})
|
||||
intval._pin.fwd.writeSync(0)
|
||||
log.warn('_undeclarePins', { pin : PINS.bwd.pin, val : 0, reason : 'exiting'})
|
||||
intval._pin.bwd.writeSync(0)
|
||||
intval._pin.fwd.unexport()
|
||||
intval._pin.bwd.unexport()
|
||||
intval._pin.micro.unexport()
|
||||
intval._pin.release.unexport()
|
||||
process.exit()
|
||||
}
|
||||
/**
|
||||
* Start motor in forward direction by setting correct pins in h-bridge
|
||||
*
|
||||
*/
|
||||
intval._startFwd = function () {
|
||||
intval._pin.fwd.writeSync(1)
|
||||
intval._pin.bwd.writeSync(0)
|
||||
}
|
||||
/**
|
||||
* Start motor in backward direction by setting correct pins in h-bridge
|
||||
*
|
||||
*/
|
||||
intval._startBwd = function () {
|
||||
intval._pin.fwd.writeSync(0)
|
||||
intval._pin.bwd.writeSync(1)
|
||||
}
|
||||
|
||||
intval._pause = function () {
|
||||
intval._pin.fwd.writeSync(0)
|
||||
intval._pin.bwd.writeSync(0)
|
||||
//log.info('_pause', 'frame paused')
|
||||
}
|
||||
/**
|
||||
* Stop motor by setting both motor pins to 0 (LOW)
|
||||
*
|
||||
*/
|
||||
intval._stop = function () {
|
||||
const entry = {}
|
||||
const now = +new Date()
|
||||
const len = now - intval._state.frame.start
|
||||
|
||||
intval._pin.fwd.writeSync(0)
|
||||
intval._pin.bwd.writeSync(0)
|
||||
|
||||
log.info(`_stop`, { frame : len })
|
||||
|
||||
intval._pin.micro.unwatch()
|
||||
intval._state.frame.active = false
|
||||
|
||||
if (intval._state.frame.cb) intval._state.frame.cb(len)
|
||||
|
||||
entry.start = intval._state.frame.start
|
||||
entry.stop = now
|
||||
entry.len = len
|
||||
entry.dir = intval._state.frame.current.dir ? 1 : 0
|
||||
entry.exposure = intval._state.frame.current.exposure
|
||||
entry.counter = intval._state.counter
|
||||
entry.sequence = intval._state.sequence ? 1 : 0
|
||||
|
||||
db.insert(entry)
|
||||
|
||||
intval._state.frame.current = {}
|
||||
}
|
||||
/**
|
||||
* Callback for watching relese switch state changes.
|
||||
* Using GPIO 06 on Raspberry Pi Zero W.
|
||||
*
|
||||
* 1) If closed AND frame active, start timer, set state primed to `true`.
|
||||
* 1) If opened AND frame active, stop frame
|
||||
*
|
||||
* Microswitch + 10K ohm resistor
|
||||
* * 1 === open
|
||||
* * 0 === closed
|
||||
*
|
||||
*
|
||||
* @param {object} err Error object present if problem reading pin
|
||||
* @param {integer} val Current value of the pin
|
||||
*
|
||||
*/
|
||||
intval._watchMicro = function (err, val) {
|
||||
const now = +new Date()
|
||||
if (err) {
|
||||
log.error('_watchMicro', err)
|
||||
}
|
||||
//log.info(`Microswitch val: ${val}`)
|
||||
//determine when to stop
|
||||
if (val === 0 && intval._state.frame.active) {
|
||||
if (!intval._state.micro.primed) {
|
||||
intval._state.micro.primed = true
|
||||
intval._state.micro.time = now
|
||||
log.info('Microswitch primed to stop motor')
|
||||
}
|
||||
} else if (val === 1 && intval._state.frame.active) {
|
||||
if (intval._state.micro.primed && !intval._state.micro.paused && (now - intval._state.frame.start) > intval._frame.open) {
|
||||
intval._state.micro.primed = false
|
||||
intval._state.micro.time = 0
|
||||
setTimeout( () => {
|
||||
intval._stop()
|
||||
}, intval._microDelay)
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Callback for watching relese switch state changes.
|
||||
* Using GPIO 05 on Raspberry Pi Zero W.
|
||||
*
|
||||
* 1) If closed, start timer.
|
||||
* 2) If opened, check timer AND
|
||||
* 3) If `press` (`now - intval._state.release.time`) greater than minimum and less than `intval._release.seq`, start frame
|
||||
* 4) If `press` greater than `intval._release.seq`, start sequence
|
||||
*
|
||||
* Button + 10K ohm resistor
|
||||
* * 1 === open
|
||||
* * 0 === closed
|
||||
*
|
||||
* @param {object} err Error object present if problem reading pin
|
||||
* @param {integer} val Current value of the pin
|
||||
*
|
||||
*/
|
||||
intval._watchRelease = function (err, val) {
|
||||
const now = +new Date()
|
||||
let press = 0
|
||||
if (err) {
|
||||
return log.error(err)
|
||||
}
|
||||
//log.info(`Release switch val: ${val}`)
|
||||
if (val === 0) {
|
||||
//closed
|
||||
if (intval._releaseClosedState(now)) {
|
||||
intval._state.release.time = now
|
||||
intval._state.release.active = true //maybe unncecessary
|
||||
}
|
||||
} else if (val === 1) {
|
||||
//opened
|
||||
if (intval._state.release.active) {
|
||||
press = now - intval._state.release.time
|
||||
if (press > intval._release.min && press < intval._release.seq) {
|
||||
intval.frame()
|
||||
} else if (press >= intval._release.seq) {
|
||||
intval.sequence()
|
||||
}
|
||||
//log.info(`Release closed for ${press}ms`)
|
||||
intval._state.release.time = 0
|
||||
intval._state.release.active = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
intval._releaseClosedState = function (now) {
|
||||
if (!intval._state.release.active && intval._state.release.time === 0) {
|
||||
return true
|
||||
}
|
||||
if (intval._state.release.active && (now - intval._state.release.time) > (intval._release.seq * 10)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
intval.reset = function () {
|
||||
intval._setState()
|
||||
intval._storeState()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default direction of the camera.
|
||||
* * forward = true
|
||||
* * backward = false
|
||||
*
|
||||
* @param {boolean} [dir=true] Direction of the camera
|
||||
*
|
||||
*/
|
||||
intval.setDir = function (val = true) {
|
||||
if (typeof val !== 'boolean') {
|
||||
return log.warn('Direction must be represented as either true or false')
|
||||
}
|
||||
intval._state.frame.dir = val
|
||||
intval._storeState()
|
||||
log.info('setDir', { direction : val ? 'forward' : 'backward' })
|
||||
}
|
||||
|
||||
intval.setExposure = function (val = 0) {
|
||||
intval._state.frame.exposure = val
|
||||
intval._storeState()
|
||||
log.info('setExposure', { exposure : val })
|
||||
}
|
||||
|
||||
intval.setDelay = function (val = 0) {
|
||||
intval._state.frame.delay = val
|
||||
intval._storeState()
|
||||
log.info('setDelay', { delay : val })
|
||||
}
|
||||
intval.setCounter = function (val = 0) {
|
||||
intval._state.counter = val
|
||||
intval._storeState()
|
||||
log.info('setCounter', { counter : val })
|
||||
}
|
||||
/**
|
||||
* Begin a single frame with set variables or defaults
|
||||
*
|
||||
* @param {?boolean} [dir="null"] (optional) Direction of the frame
|
||||
* @param {?integer} [exposure="null"] (optional) Exposure time, 0 = minimum
|
||||
*
|
||||
*/
|
||||
intval.frame = function (dir = null, exposure = null, cb = () => {}) {
|
||||
if (dir === true || (dir === null && intval._state.frame.dir === true) ) {
|
||||
dir = true
|
||||
} else {
|
||||
dir = false
|
||||
}
|
||||
|
||||
if (exposure === null && intval._state.frame.exposure !== 0) {
|
||||
exposure = intval._state.frame.exposure
|
||||
} else if (exposure === null) {
|
||||
exposure = 0 //default speed
|
||||
}
|
||||
|
||||
intval._state.frame.start = +new Date()
|
||||
intval._state.frame.active = true
|
||||
intval._pin.micro.watch(intval._watchMicro)
|
||||
|
||||
log.info('frame', {dir : dir ? 'forward' : 'backward', exposure : exposure})
|
||||
|
||||
if (dir) {
|
||||
intval._startFwd()
|
||||
} else {
|
||||
intval._startBwd()
|
||||
}
|
||||
if (exposure !== 0) {
|
||||
intval._state.frame.paused = true
|
||||
if (dir) {
|
||||
setTimeout(intval._pause, intval._frame.open)
|
||||
//log.info('frame', { pausing : time + intval._frame.open })
|
||||
setTimeout( () => {
|
||||
intval._state.frame.paused = false
|
||||
intval._startFwd()
|
||||
}, exposure + intval._frame.closed)
|
||||
} else {
|
||||
setTimeout(intval._pause, intval._frame.openBwd)
|
||||
setTimeout( () => {
|
||||
//log.info('frame', 'restarting')
|
||||
intval._state.frame.paused = false
|
||||
intval._startBwd()
|
||||
}, exposure + intval._frame.closed)
|
||||
}
|
||||
}
|
||||
if (dir) {
|
||||
intval._state.frame.cb = (len) => {
|
||||
intval._state.counter++
|
||||
intval._storeState()
|
||||
cb(len)
|
||||
}
|
||||
} else {
|
||||
intval._state.frame.cb = (len) => {
|
||||
intval._state.counter--
|
||||
intval._storeState()
|
||||
cb(len)
|
||||
}
|
||||
}
|
||||
intval._state.frame.current = {
|
||||
dir: dir,
|
||||
exposure: exposure
|
||||
}
|
||||
}
|
||||
|
||||
intval.status = function () {
|
||||
return intval._state
|
||||
}
|
||||
|
||||
module.exports = intval
|
Loading…
Reference in New Issue