diff --git a/hunks/interface/jogpad.js b/hunks/interface/jogpad.js new file mode 100644 index 0000000000000000000000000000000000000000..8a5e74dd557d2d50c3679d2e9baf4b8dbc6add95 --- /dev/null +++ b/hunks/interface/jogpad.js @@ -0,0 +1,139 @@ +/* +hunks/interface/arrowpad.js + +arrowpad pressure + +Jake Read at the Center for Bits and Atoms +(c) Massachusetts Institute of Technology 2019 + +This work may be reproduced, modified, distributed, performed, and +displayed for any purpose, but must acknowledge the squidworks and cuttlefish projects. +Copyright is retained and must be preserved. The work is provided as is; +no warranty is provided, and users accept all liability. +*/ + +import { + Hunkify, + Input, + Output, + State +} from '../hunks.js' + +export default function JogPad() { + Hunkify(this) + + let pass = this.output('array', 'keysdown') + + const pairs = [{ + name: 'left', + code: 37 + }, { + name: 'right', + code: 39 + }, { + name: 'up', + code: 38 + }, { + name: 'down', + code: 40 + }, { + name: 'pgup', + code: 33 + }, { + name: 'pgdown', + code: 34 + }] + + for (let pair of pairs) { + // current state, + pair.down = false + } + + let dom = this.document() + + let activeStatus = false; + let activeColor = '#d981eb' + let idleColor = '#82aef5' + + let removeKeyBindings = () => { + document.removeEventListener('keydown', keyDownListen) + document.removeEventListener('keyup', keyUpListen) + // no sticky keys! + clearAllKeys() + } + + let setKeyBindings = () => { + document.addEventListener('keydown', keyDownListen) + document.addEventListener('keyup', keyUpListen) + } + + let keyDownListen = (evt) => { + if (evt.repeat) return + let key = pairs.find((cand) => { + return cand.code === evt.keyCode + }) + if (key) { + key.down = true + } + } + + let keyUpListen = (evt) => { + let key = pairs.find((cand) => { + return cand.code === evt.keyCode + }) + if (key) { + key.down = false + } + } + + let clearAllKeys = () => { + for (let key of pairs) { + key.down = false + } + } + + let container = $('<div>').get(0) + let msg = $('<div>').text('~ click in to activate ~').get(0) + $(msg).css('padding-top', '35px') + $(container).append(msg) + $(container).css('background-color', idleColor) + $(container).width(285).height(185) + $(container).css('text-align', 'center') + $(container).css('font-size', '18px') + $(container).css('color', 'white') + $(dom).append(container) + dom.addEventListener('click', (evt) => { + if (activeStatus) { + activeStatus = false + $(msg).text('~ click in to activate ~') + $(container).css('background-color', idleColor) + removeKeyBindings() + } else { + activeStatus = true + $(msg).text('~ push push push ~') + $(container).css('background-color', activeColor) + setKeyBindings() + } + }) + + + // puts true when transitioning to down, puts false when lifting up + // downstream evts can do whatever they like, either track state or + // also run safety timeouts + + // this is actually kind of a nontrivial statemachine, bc we have to + // potentially flow-control buffer output events in the order that they + // happened ... use pairs, setup each one as a statemachine + this.loop = () => { + if(!pass.io()){ + // collect states, + let state = [] + for(let key of pairs){ + if(key.down){ + state.push(key.name) + } + } + pass.put(JSON.parse(JSON.stringify(state))) + } + } +} diff --git a/hunks/statemachines/saturn.js b/hunks/statemachines/saturn.js index c45293272e161dfcea3057456f5e790448ccc6fb..ce366a597de969b5cfc0d64034795da66ba2a5b7 100644 --- a/hunks/statemachines/saturn.js +++ b/hunks/statemachines/saturn.js @@ -53,32 +53,43 @@ export default function Saturn() { Hunkify(this) let inpts = this.input('array', 'posn') + // jogging is a whole other thing, + let keysdown = this.input('array', 'jogkeys') + let isJogMode = this.state('boolean', 'jog mode', false) + let jogSize = this.state('number', 'jog size', 1) + jogSize.onChange = (value) => { + if (value < 0.01) value = 0.01 + jogSize.set(value) + } let outp = this.output('array', 'posn') // here's a case where we might want some type of enum ish thing let isIncrementalMode = this.state('boolean', 'incremental mode', false) - let reset = this.state('boolean', 'reset', false) - reset.onChange = (val) => { - // clip anything larger than p[0] - /* - if(positions.length > 1){ - while(positions.length > 1) positions.pop() + // these are mutually exclusive, + isIncrementalMode.onChange = (value) => { + if (value) { + isJogMode.set(false) + isIncrementalMode.set(true) + } else { + isIncrementalMode.set(false) + } + } + isJogMode.onChange = (value) => { + if (value) { + isIncrementalMode.set(true) + isJogMode.set(true) + } else { + isIncrementalMode.set(false) + isJogMode.set(false) } - */ - // or, just zero: - positions = [[0,0,0]] - ramps = [] - speed = minSpeed - reset.set(false) - updatePositionDisplay() - updateBufDisplay() } + let reset = this.state('boolean', 'reset', false) let accelState = this.state('number', 'acceleration (u/s/s)', 2) accelState.onChange = (val) => { - if(val > 30){ + if (val > 30) { val = 30 - } else if (val < 0.5){ + } else if (val < 0.5) { val = 0.5 } accelState.set(val) @@ -86,15 +97,39 @@ export default function Saturn() { } let speedState = this.state('number', 'speed (u/s)', 20) speedState.onChange = (val) => { - if (val > 200){ + if (val > 200) { val = 200 - } else if (val < 5){ + } else if (val < 5) { val = 5 } speedState.set(val) cruise = speedState.value } + let resetAllState = () => { + // clip anything larger than p[0] + /* + if(positions.length > 1){ + while(positions.length > 1) positions.pop() + } + */ + // or, just zero: + positions = [ + [0, 0, 0] + ] + ramps = [] + speed = minSpeed + reset.set(false) + updatePositionDisplay() + updateBufDisplay() + // and, modal, + isJogMode.set(false) + isIncrementalMode.set(false) + } + reset.onChange = (val) => { + resetAllState() + } + let mdmsegOut = this.output('MDmseg', 'motionSegment') // settings, @@ -430,11 +465,12 @@ export default function Saturn() { } } + let jognow = true + this.loop = () => { // handle output to stepper, if we have any existing blocks to issue: if (ramps.length > 0 && !mdmsegOut.io()) { posUpdated = true - updatePositionDisplay() // multi-dimensional mseg, mdmsegOut.put(writeMDmsegFromRamp(ramps[0])) // this means we are either inside of two old positions, @@ -454,6 +490,7 @@ export default function Saturn() { } */ positions.shift() + updatePositionDisplay() speed = ramps[0].vf ramps.shift() updateBufDisplay() @@ -462,6 +499,7 @@ export default function Saturn() { } else { if (debugRuntime) console.log(`> updating p[0]`) positions[0] = ramps[0].pf + updatePositionDisplay() speed = ramps[0].vf ramps.shift() updateBufDisplay() @@ -477,6 +515,36 @@ export default function Saturn() { let logTimes = false + // jog? + if (isJogMode.value) { + // if we have a key to execute, and are not currently aiming at anything + // i.e. our only position is the 0th, our current, + if (keysdown.io() && positions.length == 1 && jognow) { + let keys = keysdown.get() + let jt = [0, 0, 0] + let inc = jogSize.value + if (keys.includes('right')) jt[0] += inc + if (keys.includes('left')) jt[0] -= inc + if (keys.includes('up')) jt[1] += inc + if (keys.includes('down')) jt[1] -= inc + if (keys.includes('pgup')) jt[2] += inc + if (keys.includes('pgdown')) jt[2] -= inc + if (Math.abs(vLen(jt)) > 0.009) { + // we only want to do this every once and while... + jognow = false + setTimeout(() => { + jognow = true + }, 1000) + positions.push([ + positions[0][0] + jt[0], + positions[0][1] + jt[1], + positions[0][2] + jt[2], + ]) + } + } else if (keysdown.io()) { + keysdown.get() + } + } // load new pts into the array, if (evalLoad()) { // get the new pt, adding it if it is of any appreciable distance @@ -494,45 +562,45 @@ export default function Saturn() { } catch (err) { console.warn('error caught at saturn input', err) } + } - // only run lookahead -> shipments if we have ah very-full buffer - if (evalLookahead()) { - if (debugRuntime) console.log('lookahead') - // LOOKAHEAD BEGIN - if (logTimes) console.time('lookahead') - // positions[] is global, speeds is generated now - // speed[0], matching positions[0], are our current situations - let speeds = new Array(positions.length) - speeds[0] = speed - speeds[speeds.length - 1] = minSpeed - // first, set speeds such that moves can be made within single periods, - periodPass(speeds) - if (logTimes) console.timeLog('lookahead') - // jd runs an algorithm that calculates maximum allowable - // instantaneous accelerations at corners - jd(speeds) // at ~ 38ms, this is the beef of it - if (logTimes) console.timeLog('lookahead') - // revpass to link by max. accel: - reversePass(speeds) - forwardPass(speeds) - // rough check speeds after initial passes, - // TODO: if we find these throwing errs, interrupt control when we - // hit some geometry we can't deal with? - positionsCheck(speeds) - if (logTimes) console.timeLog('lookahead') - // ok, ramps: - ramps.length = 0 - ramps = rampPass(speeds) - // is this still conn. to our head? - if (ramps.length > 0 && debugRuntime) console.log('new ramps', vDist(ramps[0].pi, positions[0]).toFixed(5)) - // and a check, - rampCheck(ramps) - if (logTimes) console.timeLog('lookahead') - if (logTimes) console.timeEnd('lookahead') - //console.log('new pt\t', positions.length, np) - // LOOKAHEAD END - let rp = JSON.parse(JSON.stringify(ramps)) - } + // only run lookahead -> shipments if we have ah very-full buffer + if (evalLookahead()) { + if (debugRuntime) console.log('lookahead') + // LOOKAHEAD BEGIN + if (logTimes) console.time('lookahead') + // positions[] is global, speeds is generated now + // speed[0], matching positions[0], are our current situations + let speeds = new Array(positions.length) + speeds[0] = speed + speeds[speeds.length - 1] = minSpeed + // first, set speeds such that moves can be made within single periods, + periodPass(speeds) + if (logTimes) console.timeLog('lookahead') + // jd runs an algorithm that calculates maximum allowable + // instantaneous accelerations at corners + jd(speeds) // at ~ 38ms, this is the beef of it + if (logTimes) console.timeLog('lookahead') + // revpass to link by max. accel: + reversePass(speeds) + forwardPass(speeds) + // rough check speeds after initial passes, + // TODO: if we find these throwing errs, interrupt control when we + // hit some geometry we can't deal with? + positionsCheck(speeds) + if (logTimes) console.timeLog('lookahead') + // ok, ramps: + ramps.length = 0 + ramps = rampPass(speeds) + // is this still conn. to our head? + if (ramps.length > 0 && debugRuntime) console.log('new ramps', vDist(ramps[0].pi, positions[0]).toFixed(5)) + // and a check, + rampCheck(ramps) + if (logTimes) console.timeLog('lookahead') + if (logTimes) console.timeEnd('lookahead') + //console.log('new pt\t', positions.length, np) + // LOOKAHEAD END + let rp = JSON.parse(JSON.stringify(ramps)) } } }