re-write progress, changing platforms

This commit is contained in:
mmcwilliams 2019-10-16 10:58:46 -04:00
parent af5e493980
commit 26655ea0a7
11 changed files with 1531 additions and 888 deletions

View File

@ -173,7 +173,7 @@ class BLE {
bleno.on('stateChange', state => {
log.info('stateChange', { state : state })
if (state === 'poweredOn') {
log.info('Starting advertising', { DEVICE_NAME: DEVICE_NAME, DEVICE_ID : process.env.BLENO_DEVICE_NAME })
log.info('Starting advertising', { DEVICE_NAME, DEVICE_ID : process.env.BLENO_DEVICE_NAME })
bleno.startAdvertising(DEVICE_NAME, [CHAR_ID])
} else {
bleno.stopAdvertising()

View File

@ -1,423 +1,461 @@
'use strict'
const db = require('../db')
const log = require('../log')('intval')
const storage = require('node-persist')
const fs = require('fs')
let Gpio
'use strict';
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const db = require('../db');
const log = require('../log')('intval');
const storage = __importStar(require("node-persist"));
const fs_extra_1 = require("fs-extra");
require("../delay");
let Gpio;
try {
Gpio = require('onoff').Gpio
} catch (e) {
log.warn('Failed including Gpio, using sim')
Gpio = require('../../lib/onoffsim').Gpio
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'
}
fwd: {
pin: 13,
dir: 'out'
},
bwd: {
pin: 19,
dir: 'out'
},
micro: {
pin: 5,
dir: 'in',
edge: 'both'
},
release: {
pin: 6,
dir: 'in',
edge: 'both'
}
};
/** class representing the intval3 features */
class Intval {
constructor() {
this.STATE_DIR = '~/state';
this._frame = {
open: 250,
openBwd: 400,
closed: 100,
expected: 530 //expected length of frame, in ms
};
this._release = {
min: 20,
seq: 1000
};
this._microDelay = 10; // delay after stop signal before stopping motors
this._pin = {};
this._state = {};
this._init();
}
/**
* Initialize the storage object and bind functions to process events.
*/
async _init() {
let dirExists;
try {
dirExists = await fs_extra_1.exists(this.STATE_DIR);
}
catch (err) {
log.error('init', `Error locating state directory ${this.STATE_DIR}`);
}
if (!dirExists) {
try {
await fs_extra_1.mkdir(this.STATE_DIR);
}
catch (err) {
log.error('init', `Error creating state directory ${this.STATE_DIR}`);
}
}
storage.init({
dir: this.STATE_DIR,
stringify: JSON.stringify,
parse: JSON.parse,
encoding: 'utf8',
logging: false,
continuous: true,
interval: false,
ttl: false,
}).then(this._restoreState).catch((err) => {
log.warn('init', err);
this.reset();
this._declarePins();
});
process.on('SIGINT', this._undeclarePins);
process.on('uncaughtException', this._undeclarePins);
}
/**
* Restore the state from the storage object
*/
_restoreState() {
storage.getItem('_state', 'test').then(this._setState).catch((err) => {
this._setState();
log.error('_restoreState', err);
});
this._declarePins();
}
/**
* Creating the state object.
*/
_setState(data = undefined) {
if (typeof data !== 'undefined') {
this._state = data;
this._state.frame.cb = () => { };
log.info('_setState', 'Restored intval state from disk');
return true;
}
log.info('_setState', 'Setting state from defaults');
this._state = {
frame: {
dir: true,
start: 0,
active: false,
paused: false,
exposure: 0,
delay: 0,
current: {},
cb: () => { }
},
release: {
time: 0,
active: false //is pressed
},
micro: {
time: 0,
primed: false //is ready to stop frame
},
counter: 0,
sequence: false
};
this._storeState();
}
/**
* Store the state object.
*/
_storeState() {
storage.setItem('_state', this._state)
.then(() => { })
.catch((err) => {
log.error('_storeState', err);
});
}
/**
* (internal function) Declares all Gpio pins that will be used.
*/
_declarePins() {
let pin;
for (let p in PINS) {
pin = PINS[p];
if (pin.edge)
this._pin[p] = new Gpio(pin.pin, pin.dir, pin.edge);
if (!pin.edge)
this._pin[p] = new Gpio(pin.pin, pin.dir);
log.info('_declarePins', { pin: pin.pin, dir: pin.dir, edge: pin.edge });
}
this._pin.release.watch(this._watchRelease);
}
/**
* (internal function) Undeclares all Gpio in event of uncaught error
* that interupts the node process.
*/
_undeclarePins(e) {
log.error('_undeclarePins', e);
if (!this._pin) {
log.warn('_undeclarePins', { reason: 'No pins' });
return process.exit();
}
log.warn('_undeclarePins', { pin: PINS.fwd.pin, val: 0, reason: 'exiting' });
this._pin.fwd.writeSync(0);
log.warn('_undeclarePins', { pin: PINS.bwd.pin, val: 0, reason: 'exiting' });
this._pin.bwd.writeSync(0);
this._pin.fwd.unexport();
this._pin.bwd.unexport();
this._pin.micro.unexport();
this._pin.release.unexport();
process.exit();
}
/**
* Start motor in forward direction by setting correct pins in h-bridge
*/
_startFwd() {
this._pin.fwd.writeSync(1);
this._pin.bwd.writeSync(0);
}
/**
* Start motor in backward direction by setting correct pins in h-bridge
*/
_startBwd() {
this._pin.fwd.writeSync(0);
this._pin.bwd.writeSync(1);
}
/**
* Turn off all directions
*/
_pause() {
this._pin.fwd.writeSync(0);
this._pin.bwd.writeSync(0);
//log.info('_pause', 'frame paused')
}
/**
* Stop motor by setting both motor pins to 0 (LOW)
*/
_stop() {
const entry = {};
const now = +new Date();
const len = now - this._state.frame.start;
this._pin.fwd.writeSync(0);
this._pin.bwd.writeSync(0);
log.info(`_stop`, { frame: len });
this._pin.micro.unwatch();
this._state.frame.active = false;
if (this._state.frame.cb)
this._state.frame.cb(len);
entry.start = this._state.frame.start;
entry.stop = now;
entry.len = len;
entry.dir = this._state.frame.current.dir ? 1 : 0;
entry.exposure = this._state.frame.current.exposure;
entry.counter = this._state.counter;
entry.sequence = this._state.sequence ? 1 : 0;
db.insert(entry);
this._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
*
*/
_watchMicro(err, val) {
const now = +new Date();
if (err) {
log.error('_watchMicro', err);
}
//log.info(`Microswitch val: ${val}`)
//determine when to stop
if (val === 0 && this._state.frame.active) {
if (!this._state.micro.primed) {
this._state.micro.primed = true;
this._state.micro.time = now;
log.info('Microswitch primed to stop motor');
}
}
else if (val === 1 && this._state.frame.active) {
if (this._state.micro.primed && !this._state.micro.paused && (now - this._state.frame.start) > this._frame.open) {
this._state.micro.primed = false;
this._state.micro.time = 0;
setTimeout(() => {
this._stop();
}, this._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 - this._state.release.time`) greater than minimum and less than `this._release.seq`, start frame
* 4) If `press` greater than `this._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
*
*/
_watchRelease(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 (this._releaseClosedState(now)) {
this._state.release.time = now;
this._state.release.active = true; //maybe unncecessary
}
}
else if (val === 1) {
//opened
if (this._state.release.active) {
press = now - this._state.release.time;
if (press > this._release.min && press < this._release.seq) {
this.frame();
}
else if (press >= this._release.seq) {
this.sequence();
}
//log.info(`Release closed for ${press}ms`)
this._state.release.time = 0;
this._state.release.active = false;
}
}
}
/**
*
*/
_releaseClosedState(now) {
if (!this._state.release.active && this._state.release.time === 0) {
return true;
}
if (this._state.release.active && (now - this._state.release.time) > (this._release.seq * 10)) {
return true;
}
return false;
}
/**
* Reset the state and store it.
*/
reset() {
this._setState();
this._storeState();
}
/**
* Set the default direction of the camera.
* * forward = true
* * backward = false
*
* @param {boolean} [dir=true] Direction of the camera
*/
setDir(val = true) {
if (typeof val !== 'boolean') {
return log.warn('Direction must be represented as either true or false');
}
this._state.frame.dir = val;
this._storeState();
log.info('setDir', { direction: val ? 'forward' : 'backward' });
}
/**
* Set the exposure value for a single frame.
*
* @param {integer} val Length in milliseconds
*/
setExposure(val = 0) {
this._state.frame.exposure = val;
this._storeState();
log.info('setExposure', { exposure: val });
}
/**
* Set the delay time between each frame.
*
* @param {integer} val Length in milliseconds
*/
setDelay(val = 0) {
this._state.frame.delay = val;
this._storeState();
log.info('setDelay', { delay: val });
}
/**
* Set the counter to the value.
*
* @param {integer} val Frame number
*/
setCounter(val = 0) {
this._state.counter = val;
this._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
*
*/
frame(dir = null, exposure = null, cb = () => { }) {
if (dir === true || (dir === null && this._state.frame.dir === true)) {
dir = true;
}
else {
dir = false;
}
if (exposure === null && this._state.frame.exposure !== 0) {
exposure = this._state.frame.exposure;
}
else if (exposure === null) {
exposure = 0; //default speed
}
this._state.frame.start = +new Date();
this._state.frame.active = true;
this._pin.micro.watch(this._watchMicro);
log.info('frame', { dir: dir ? 'forward' : 'backward', exposure: exposure });
if (dir) {
this._startFwd();
}
else {
this._startBwd();
}
if (exposure !== 0) {
this._state.frame.paused = true;
if (dir) {
setTimeout(this._pause, this._frame.open);
//log.info('frame', { pausing : time + this._frame.open })
setTimeout(() => {
this._state.frame.paused = false;
this._startFwd();
}, exposure + this._frame.closed);
}
else {
setTimeout(this._pause, this._frame.openBwd);
setTimeout(() => {
//log.info('frame', 'restarting')
this._state.frame.paused = false;
this._startBwd();
}, exposure + this._frame.closed);
}
}
if (dir) {
this._state.frame.cb = (len) => {
this._state.counter++;
this._storeState();
cb(len);
};
}
else {
this._state.frame.cb = (len) => {
this._state.counter--;
this._storeState();
cb(len);
};
}
this._state.frame.current = {
dir: dir,
exposure: exposure
};
}
/**
*
*/
status() {
return this._state;
}
}
/** 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
module.exports = new Intval();
//# sourceMappingURL=index.js.map

1
lib/intval/index.js.map Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,43 +1,50 @@
'use strict';
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const uuid = require('uuid').v4;
const v4_1 = __importDefault(require("uuid/v4"));
const log = require('../log')('seq');
require("../delay");
/** Object sequence features */
class Sequence {
constructor() {
this._state = {
arr: [],
active: false,
paused: false,
frame: false,
delay: false,
count: 0,
stop: null
arr: []
};
this.active = false;
this.paused = false;
this.frame = false;
this.delay = false;
this.count = 0;
this._stop = null;
this._loop = {
arr: [],
count: 0,
max: 0
};
this.stop = function () {
this._state.active = false;
this._state.count = 0;
this.active = false;
this.count = 0;
this._state.arr = [];
this._loop.count = 0;
this._loop.max = 0;
this._loop.arr = [];
if (this._state.stop)
this._state.stop();
this._state.stop = null;
if (this._stop)
this._stop();
this._stop = null;
};
}
/**
* Start running a "sequence" of frames. Shoots a continuous sequence
* of single frames with a delay in between each one.
**/
start(options, cb) {
if (this._state.active) {
return false;
}
this._state.active = true;
this._state.count = 0;
this.active = true;
this.count = 0;
if (options.arr) {
this._state.arr = options.arr;
}
@ -51,44 +58,44 @@ class Sequence {
else {
this._loop.max = 0;
}
this._state.stop = cb;
this._stop = cb;
this.step();
this._state.id = uuid();
return this._state.id;
this.id = v4_1.default();
return this.id;
}
setStop() {
this._state.active = false;
this.active = false;
}
pause() {
this._state.paused = true;
this.paused = true;
}
resume() {
this._state.paused = false;
this.paused = false;
this.step();
}
step() {
if (this._state.active && !this._state.paused) {
if (this.active && !this.paused) {
if (this._state.arr.length > 0) {
if (this._state.count > this._state.arr.length - 1) {
if (this.count > this._state.arr.length - 1) {
return this.stop();
}
log.info('step', { count: this._state.count, id: this._state.id });
return this._state.arr[this._state.count](() => {
this._state.count++;
log.info('step', { count: this.count, id: this._state.id });
return this._state.arr[this.count](() => {
this.count++;
this.step();
});
}
else if (this._loop.arr.length > 0) {
if (this._state.count > this._loop.arr.length - 1) {
this._state.count = 0;
if (this.count > this._loop.arr.length - 1) {
this.count = 0;
this._loop.count++;
}
if (this._loop.max > 0 && this._loop.count > this._loop.max) {
return this.stop();
}
log.info('step', { count: this._state.count, id: this._state.id });
return this._loop.arr[this._state.count](() => {
this._state.count++;
log.info('step', { count: this.count, id: this.id });
return this._loop.arr[this.count](() => {
this.count++;
this.step();
});
}
@ -96,11 +103,11 @@ class Sequence {
return this.stop();
}
}
else if (this._state.paused) {
log.info('step', 'Sequence paused', { loop: this._loop.count, count: this._state.count });
else if (this.paused) {
log.info('step', 'Sequence paused', { loop: this._loop.count, count: this.count });
}
else if (!this._state.active) {
log.info('step', 'Sequence stopped', { loop: this._loop.count, count: this._state.count });
else if (!this.active) {
log.info('step', 'Sequence stopped', { loop: this._loop.count, count: this.count });
}
}
}

View File

@ -1 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sequence/index.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sequence/index.ts"],"names":[],"mappings":"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"}

840
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -27,6 +27,7 @@
"dependencies": {
"bleno": "^0.5.0",
"cron": "^1.7.2",
"fs-extra": "^8.1.0",
"node-ipc": "^9.1.1",
"node-persist": "^3.0.5",
"onoff": "^5.0.0",
@ -37,7 +38,10 @@
"winston": "^3.2.1"
},
"devDependencies": {
"@types/fs-extra": "^8.0.0",
"@types/node": "^12.7.12",
"@types/node-persist": "0.0.33",
"@types/uuid": "^3.4.5",
"jsdoc-to-markdown": "^5.0.2",
"qunit": "^2.9.3",
"typescript": "^3.6.4"

View File

@ -1,5 +1,5 @@
#!/bin/bash
sudo -u pi -i<< EOF
cd /home/pi/intval3 && git pull
cd /home/pi/intval3 && git reset --hard && git pull && npm i
EOF

519
src/intval/index.ts Normal file
View File

@ -0,0 +1,519 @@
'use strict'
const db = require('../db');
const log = require('../log')('intval');
import * as storage from 'node-persist';
import { exists, mkdir } from 'fs-extra';
import '../delay';
let Gpio : any
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'
}
}
interface State {
}
interface Entry {
start : number;
stop : number;
len : number;
dir : number;
exposure : number;
counter : number;
sequence : number;
}
/** class representing the intval3 features */
class Intval {
private STATE_DIR : string = '~/state';
private _frame : any = {
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
}
private _release : any = {
min : 20,
seq : 1000
}
private _microDelay : number = 10; // delay after stop signal before stopping motors
private _pin : any = {};
private _state : any = {};
constructor() {
this._init();
}
/**
* Initialize the storage object and bind functions to process events.
*/
private async _init () {
let dirExists : boolean;
try {
dirExists = await exists(this.STATE_DIR);
} catch (err) {
log.error('init', `Error locating state directory ${this.STATE_DIR}`);
}
if (!dirExists) {
try {
await mkdir(this.STATE_DIR);
} catch (err) {
log.error('init', `Error creating state directory ${this.STATE_DIR}`)
}
}
storage.init({
dir: this.STATE_DIR,
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(this._restoreState).catch((err) => {
log.warn('init', err)
this.reset();
this._declarePins();
})
process.on('SIGINT', this._undeclarePins);
process.on('uncaughtException', this._undeclarePins);
}
/**
* Restore the state from the storage object
*/
private _restoreState () {
storage.getItem('_state', 'test').then(this._setState).catch((err) => {
this._setState();
log.error('_restoreState', err);
})
this._declarePins();
}
/**
* Creating the state object.
*/
private _setState (data : any = undefined) {
if (typeof data !== 'undefined') {
this._state = data;
this._state.frame.cb = () => {};
log.info('_setState', 'Restored intval state from disk');
return true;
}
log.info('_setState', 'Setting state from defaults');
this._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
}
this._storeState();
}
/**
* Store the state object.
*/
private _storeState () {
storage.setItem('_state', this._state)
.then(() => {})
.catch((err) => {
log.error('_storeState', err);
})
}
/**
* (internal function) Declares all Gpio pins that will be used.
*/
private _declarePins () {
let pin;
for (let p in PINS) {
pin = PINS[p];
if (pin.edge) this._pin[p] = new Gpio(pin.pin, pin.dir, pin.edge);
if (!pin.edge) this._pin[p] = new Gpio(pin.pin, pin.dir);
log.info('_declarePins', { pin : pin.pin, dir : pin.dir, edge : pin.edge });
}
this._pin.release.watch(this._watchRelease);
}
/**
* (internal function) Undeclares all Gpio in event of uncaught error
* that interupts the node process.
*/
private _undeclarePins (e : Error) {
log.error('_undeclarePins', e);
if (!this._pin) {
log.warn('_undeclarePins', { reason : 'No pins'});
return process.exit();
}
log.warn('_undeclarePins', { pin : PINS.fwd.pin, val : 0, reason : 'exiting'});
this._pin.fwd.writeSync(0);
log.warn('_undeclarePins', { pin : PINS.bwd.pin, val : 0, reason : 'exiting'});
this._pin.bwd.writeSync(0);
this._pin.fwd.unexport();
this._pin.bwd.unexport();
this._pin.micro.unexport();
this._pin.release.unexport();
process.exit();
}
/**
* Start motor in forward direction by setting correct pins in h-bridge
*/
private _startFwd () {
this._pin.fwd.writeSync(1);
this._pin.bwd.writeSync(0);
}
/**
* Start motor in backward direction by setting correct pins in h-bridge
*/
private _startBwd () {
this._pin.fwd.writeSync(0);
this._pin.bwd.writeSync(1);
}
/**
* Turn off all directions
*/
private _pause () {
this._pin.fwd.writeSync(0);
this._pin.bwd.writeSync(0);
//log.info('_pause', 'frame paused')
}
/**
* Stop motor by setting both motor pins to 0 (LOW)
*/
private _stop () {
const entry : any = {};
const now : number = +new Date();
const len : number = now - this._state.frame.start;
this._pin.fwd.writeSync(0);
this._pin.bwd.writeSync(0);
log.info(`_stop`, { frame : len });
this._pin.micro.unwatch();
this._state.frame.active = false;
if (this._state.frame.cb) this._state.frame.cb(len);
entry.start = this._state.frame.start;
entry.stop = now;
entry.len = len;
entry.dir = this._state.frame.current.dir ? 1 : 0;
entry.exposure = this._state.frame.current.exposure;
entry.counter = this._state.counter;
entry.sequence = this._state.sequence ? 1 : 0;
db.insert(entry);
this._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
*
*/
private _watchMicro (err : Error, val : number) {
const now : number = +new Date();
if (err) {
log.error('_watchMicro', err);
}
//log.info(`Microswitch val: ${val}`)
//determine when to stop
if (val === 0 && this._state.frame.active) {
if (!this._state.micro.primed) {
this._state.micro.primed = true;
this._state.micro.time = now;
log.info('Microswitch primed to stop motor');
}
} else if (val === 1 && this._state.frame.active) {
if (this._state.micro.primed && !this._state.micro.paused && (now - this._state.frame.start) > this._frame.open) {
this._state.micro.primed = false;
this._state.micro.time = 0;
setTimeout( () => {
this._stop();
}, this._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 - this._state.release.time`) greater than minimum and less than `this._release.seq`, start frame
* 4) If `press` greater than `this._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
*
*/
private _watchRelease (err : Error, val : number) {
const now : number = +new Date();
let press : number = 0;
if (err) {
return log.error(err);
}
//log.info(`Release switch val: ${val}`)
if (val === 0) {
//closed
if (this._releaseClosedState(now)) {
this._state.release.time = now;
this._state.release.active = true; //maybe unncecessary
}
} else if (val === 1) {
//opened
if (this._state.release.active) {
press = now - this._state.release.time;
if (press > this._release.min && press < this._release.seq) {
this.frame();
} else if (press >= this._release.seq) {
this.sequence();
}
//log.info(`Release closed for ${press}ms`)
this._state.release.time = 0;
this._state.release.active = false;
}
}
}
/**
*
*/
private _releaseClosedState (now : number) {
if (!this._state.release.active && this._state.release.time === 0) {
return true;
}
if (this._state.release.active && (now - this._state.release.time) > (this._release.seq * 10)) {
return true;
}
return false;
}
/**
* Reset the state and store it.
*/
public reset () {
this._setState();
this._storeState();
}
/**
* Set the default direction of the camera.
* * forward = true
* * backward = false
*
* @param {boolean} [dir=true] Direction of the camera
*/
public setDir (val : boolean = true) {
if (typeof val !== 'boolean') {
return log.warn('Direction must be represented as either true or false');
}
this._state.frame.dir = val;
this._storeState();
log.info('setDir', { direction : val ? 'forward' : 'backward' });
}
/**
* Set the exposure value for a single frame.
*
* @param {integer} val Length in milliseconds
*/
public setExposure (val : number = 0) {
this._state.frame.exposure = val;
this._storeState();
log.info('setExposure', { exposure : val });
}
/**
* Set the delay time between each frame.
*
* @param {integer} val Length in milliseconds
*/
public setDelay (val : number = 0) {
this._state.frame.delay = val;
this._storeState();
log.info('setDelay', { delay : val });
}
/**
* Set the counter to the value.
*
* @param {integer} val Frame number
*/
public setCounter (val : number = 0) {
this._state.counter = val;
this._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
*
*/
public frame (dir : boolean = null, exposure : number = null, cb : Function = () => {}) {
if (dir === true || (dir === null && this._state.frame.dir === true) ) {
dir = true;
} else {
dir = false;
}
if (exposure === null && this._state.frame.exposure !== 0) {
exposure = this._state.frame.exposure;
} else if (exposure === null) {
exposure = 0; //default speed
}
this._state.frame.current.exposure = exposure;
this._state.frame.current.dir = dir;
this._state.frame.start = +new Date();
this._state.frame.active = true;
this._pin.micro.watch(this._watchMicro);
log.info('frame', {dir : dir ? 'forward' : 'backward', exposure : exposure});
if (dir) {
this._startFwd();
} else {
this._startBwd();
}
if (exposure !== 0) {
this._state.frame.paused = true;
if (dir) {
setTimeout(this._pause, this._frame.open);
//log.info('frame', { pausing : time + this._frame.open })
setTimeout( () => {
this._state.frame.paused = false;
this._startFwd();
}, exposure + this._frame.closed);
} else {
setTimeout(this._pause, this._frame.openBwd);
setTimeout( () => {
//log.info('frame', 'restarting')
this._state.frame.paused = false;
this._startBwd();
}, exposure + this._frame.closed);
}
}
if (dir) {
this._state.frame.cb = (len : number) => {
this._state.counter++;
this._storeState();
cb(len);
}
} else {
this._state.frame.cb = (len : number) => {
this._state.counter--;
this._storeState();
cb(len);
}
}
}
/**
* Returns the state of the
*/
public status () {
return this._state;
}
}
module.exports = new Intval();
export default Intval;

View File

@ -1,20 +1,30 @@
'use strict'
const uuid = require('uuid').v4
const log = require('../log')('seq')
import '../delay'
import uuid from 'uuid/v4';
const log = require('../log')('seq');
import '../delay';
const MAX_INTEGER = 2147483647;
interface Options {
len? : number;
}
/** Object sequence features */
class Sequence {
public _state : any = {
arr : [],
active : false,
paused : false,
frame: false,
delay : false,
count : 0,
stop : null
arr : []
}
private id : string;
private active : boolean = false;
private paused : boolean = false;
private frame : boolean = false;
private delay : boolean = false;
private count : number = 0;
private _stop : Function = null;
public _loop : any = {
arr : [],
@ -22,17 +32,20 @@ class Sequence {
max : 0
}
constructor () {
constructor (intval : Intval) {
}
public start (options : any, cb : Function) {
/**
* Start running a "sequence" of frames. Shoots a continuous sequence
* of single frames with a delay in between each one.
**/
public startOld (options : any, cb : Function) {
if (this._state.active) {
return false
}
this._state.active = true
this._state.count = 0
this.active = true
this.count = 0
if (options.arr) {
this._state.arr = options.arr
@ -48,70 +61,74 @@ class Sequence {
} else {
this._loop.max = 0
}
this._state.stop = cb
this._stop = cb
this.step()
this._state.id = uuid()
return this._state.id
this.id = uuid()
return this.id
}
public async start (options : Options) {
}
public setStop () {
this._state.active = false
this.active = false
}
public stop = function () {
this._state.active = false
this._state.count = 0
this.active = false
this.count = 0
this._state.arr = []
this._loop.count = 0
this._loop.max = 0
this._loop.arr = []
if (this._state.stop) this._state.stop()
if (this._stop) this._stop()
this._state.stop = null
this._stop = null
}
public pause () {
this._state.paused = true
this.paused = true
}
public resume () {
this._state.paused = false
this.paused = false
this.step()
}
public step () {
if (this._state.active && !this._state.paused) {
if (this.active && !this.paused) {
if (this._state.arr.length > 0) {
if (this._state.count > this._state.arr.length - 1) {
if (this.count > this._state.arr.length - 1) {
return this.stop()
}
log.info('step', { count : this._state.count, id : this._state.id })
return this._state.arr[this._state.count](() => {
this._state.count++
log.info('step', { count : this.count, id : this._state.id })
return this._state.arr[this.count](() => {
this.count++
this.step()
})
} else if (this._loop.arr.length > 0) {
if (this._state.count > this._loop.arr.length - 1) {
this._state.count = 0
if (this.count > this._loop.arr.length - 1) {
this.count = 0
this._loop.count++
}
if (this._loop.max > 0 && this._loop.count > this._loop.max) {
return this.stop()
}
log.info('step', { count : this._state.count, id : this._state.id })
return this._loop.arr[this._state.count](() => {
this._state.count++
log.info('step', { count : this.count, id : this.id })
return this._loop.arr[this.count](() => {
this.count++
this.step()
})
} else{
return this.stop()
}
} else if (this._state.paused) {
log.info('step', 'Sequence paused', { loop : this._loop.count, count : this._state.count })
} else if (!this._state.active) {
log.info('step', 'Sequence stopped', { loop : this._loop.count, count : this._state.count })
} else if (this.paused) {
log.info('step', 'Sequence paused', { loop : this._loop.count, count : this.count })
} else if (!this.active) {
log.info('step', 'Sequence stopped', { loop : this._loop.count, count : this.count })
}
}
}

View File

@ -4,6 +4,7 @@
"esModuleInterop": true,
"target": "ES2017",
"noImplicitAny": true,
"allowSyntheticDefaultImports" : true,
"moduleResolution": "node",
"sourceMap": true,
"removeComments" : false,