Commit 26bbd9bf authored by Jake Read's avatar Jake Read
Browse files

ready for sww, some improvements to go

parent b08b0c28
......@@ -6,7 +6,6 @@ const Programs = require('./programs.js')
// the program object: real simple, just has a description, and a 'modules'
var program = Programs.new('new program')
var canvas = Programs.loadModuleFromSource(program, './modules/ui/threeCanvas.js')
Programs.setUI(canvas, 200, 200)
......
// business
const Reps = require('./reps.js')
const Programs = require('./programs.js')
// the program object: real simple, just has a description, and a 'modules'
var program = Programs.new('new program')
/* ok
- new plotter
- ack four
- go to pos block
- neil:mm adjust
- git push
- add y axis network wait ?
*/
// das link
var link = Programs.loadModuleFromSource(program, './modules/hardware/atkseriallink.js')
link.startUp()
link.state.log = true
let steps = 10
// x motor
var xm = Programs.loadModuleFromSource(program, './modules/hardware/atkstepper.js')
xm.route.route = '0,0'
xm.state.route = '0,0'
xm.state.spu = steps
xm.state.axis = 'X'
// y left and right
var ylm = Programs.loadModuleFromSource(program, './modules/hardware/atkstepper.js')
ylm.route.route = '0,1'
ylm.state.route = '0,1'
ylm.state.spu = steps
ylm.state.axis = 'Y'
var ylr = Programs.loadModuleFromSource(program, './modules/hardware/atkstepper.js')
ylr.route.route = '0,2'
ylr.state.route = '0,2'
ylr.state.spu = -steps
ylr.state.axis = 'Y'
// z motor
var zm = Programs.loadModuleFromSource(program, './modules/hardware/atkstepper.js')
zm.route.route = '0,3'
zm.state.route = '0,3'
zm.state.spu = -steps
zm.state.axis = 'Z'
var planner = Programs.loadModuleFromSource(program, './modules/motion/simplanner.js')
planner.state.minSpeed = 1
planner.state.accel = 300
planner.netWindow = 1
/*
var simplanner = Programs.loadModuelFromSource(program, './modules/motion/simplanner.js')
let ahMove = {
axes: ['X', 'Y', 'Z'],
p1: [10, 10, 10],
p2: [20, 20, 20],
vector: [10, 10, 10],
cruise: 100,
entry: 10,
exit: 10,
accel: 100
}
*/
// what doth planner output ?
/*
var newMove = {
axes: axes,
p1: p1,
p2: p2,
cruise: move.speed,
entry: 0,
exit: 0,
accel: state.accel
}
*/
// can we write something that just steps through moves one by one ?
// HOOKUP
planner.outputs.moves.attach(xm.inputs.trapezoid)
planner.outputs.moves.attach(ylm.inputs.trapezoid)
planner.outputs.moves.attach(ylr.inputs.trapezoid)
planner.outputs.moves.attach(zm.inputs.trapezoid)
xm.outputs.ack.attach(planner.inputs.acks)
ylm.outputs.ack.attach(planner.inputs.acks)
zm.outputs.ack.attach(planner.inputs.acks)
let moveToPlanner = {
position: {
X: 20,
Y: 10,
Z: 5
},
speed: 150
}
link.onOpen = function(){
// planner.inputs.instruction.fn(moveToPlanner)
//xm.inputs.trapezoid.fn(ahMove)
}
// ws input
var sockit = Programs.loadModuleFromSource(program, './modules/pipes/websocket.js')
var np = Programs.loadModuleFromSource(program, './modules/motion/neilparse.js')
sockit.outputs.data.attach(np.inputs.array)
np.outputs.move.attach(planner.inputs.instruction)
planner.outputs.moveComplete.attach(np.inputs.shift)
// UI HOOKUP
Programs.setView(program, {
scale: 0.5,
translate: [-160, -30],
origin: [200, 120]
})
let top = 100
let motorbar = 1200
let motorspace = 400
Programs.setUI(sockit, top, top)
Programs.setUI(np, 600, top)
Programs.setUI(link, 1800, top)
Programs.setUI(xm, motorbar, top)
Programs.setUI(ylm, motorbar, top + motorspace)
Programs.setUI(ylr, motorbar, top + 2 * motorspace)
Programs.setUI(zm, motorbar, top + 3 * motorspace)
Programs.setUI(planner, 600, 450)
// UI
const View = require('./views.js')
View.startHttp()
View.startWs()
Programs.assignSocket(View.uiSocket)
View.assignProgram(program)
\ No newline at end of file
......@@ -94,6 +94,7 @@ function ATKSerialLink() {
//state.portStatus = 'opening'
serialport.on('open', function() {
state.portStatus = 'open'
atkSerialLink.onOpen()
})
serialport.on('error', function(err) {
state.portStatus = err.message
......@@ -103,6 +104,10 @@ function ATKSerialLink() {
}
}
atkSerialLink.onOpen = function() {
// null 4 now ...
}
/*
------------------------------------------------------
PACKETS TO HARDWARE
......
......@@ -47,7 +47,7 @@ function Stepper() {
})
state.axis = 'X'
state.spu = 200 // steps per unit
state.spu = 80 // steps per unit
state.rawMove = -10
var ui = stepper.ui
......@@ -191,7 +191,7 @@ function Stepper() {
console.log(dMove)
}
if(verbose) console.log('------------------- DMOVE', state.axis, dMove.steps)
if(true) console.log('------------------- DMOVE', state.axis, dMove)
var packet = new Array()
// step blocks are #131
......
// boilerplate atkapi header
const JSUnit = require('../../src/jsunit.js')
let Input = JSUnit.Input
let Output = JSUnit.Output
let State = JSUnit.State
// unit to convert neil's mods-output arrays into human readable move objects
// probably do flow control here also ?
function NeilParser() {
var neilParser = {
description: {
name: 'neilParser Parser',
alt: 'line of neilParser -> points'
}
}
// log more
var verbose = false
let npList = null
// one caveat here is that we can't dynamically add objects to this,
// or they don't get getter / settered when we do
neilParser.state = State()
var state = neilParser.state
state.lift = 50
state.moveSpeed = 200
state.plungeSpeed = 50
state.jogSpeed = 600
state.listLength = 0
neilParser.inputs = {
array: Input('array', (arr) => {
let list = writeMoveObjects(arr)
console.log('have list', list)
npList = list
state.listLength = npList.length
// throw it out
neilParser.outputs.newList.emit(1)
onShift()
}),
shift: Input('event', (arg) => {
onShift()
})
}
neilParser.outputs = {
move: Output('move'),
newList: Output('event')
}
function writeMoveObjects(arr) {
// arr[i] are segments,
// between arr[i] instances, we put a z-lift and then
// a z-drop
if (Array.isArray(arr)) {
let flat = new Array()
for (var i = 0; i < arr.length; i++) {
flat.push(arr[i][0])
flat.push('z down move')
flat = flat.concat(arr[i].slice(1))
flat.push('z up move')
}
// console.log('FLATTENED TO', flat)
// add header
flat.splice(0, 0, 'z up move')
// and return home
flat.push([0, 0])
// make a list of objs
let list = new Array()
// this is actually stateful ... jogging or not
let upState = false
for (var j = 0; j < flat.length; j++) {
if (Array.isArray(flat[j])) {
let move = {
position: {
X: flat[j][0],
Y: flat[j][1]
}
}
if (upState) {
move.speed = state.jogSpeed
} else {
move.speed = state.moveSpeed
}
list.push(move)
} else if (flat[j] === 'z up move') {
upState = true
list.push({
position: {
Z: state.lift
},
speed: state.plungeSpeed
})
} else if (flat[j] === 'z down move') {
upState = false
list.push({
position: {
Z: 0
},
speed: state.plungeSpeed
})
}
}
return list
} else {
console.log('NEIL PARSE: err: not an array')
}
}
function onShift() {
if (npList != null && npList.length > 0) {
neilParser.outputs.move.emit(npList.shift())
state.listLength = npList.length
} else {
console.log('NP Completes Moves')
}
}
return neilParser
}
// export the module
module.exports = NeilParser
\ No newline at end of file
// boilerplate atkapi header
const JSUnit = require('../../src/jsunit.js')
let Input = JSUnit.Input
let Output = JSUnit.Output
let State = JSUnit.State
// interface elements
const JSUI = require('../../src/jsui.js')
let UI = JSUI.UI
// descartes, to you
const DCRT = require('../../src/cartesian.js')
const MJS = require('mathjs')
// planner consumes target moves (i.e. segments having uniform speed throughout)
// and subjects them to acceleration constraints
function Planner() {
var planner = {
description: {
name: 'Lookahead-Motion-Planner',
alt: 'movements -> acceleration planned moves'
}
}
// log more
var verbose = false
planner.state = State()
var state = planner.state // reference pass attempt?
state.axisIDs = 'X,Y,Z'
state.onUiChange('axisIDs', axisIDUpdate)
state.accel = 200 // units/s/s
state.minSpeed = 1 // units/s
state.position = [0, 0, 0]
// should be grn / red button ...
state.isRunning = 1
state.onUiChange('isRunning', netStateRefresh)
state.netWindow = 1
state.netState = [0, 0, 0]
planner.ui = UI()
var ui = planner.ui
ui.addElement('startStopButton', 'ui/uiButton.js')
ui.startStopButton.subscribe('onload', function(msg) {
ui.startStopButton.send({
calls: 'setText',
argument: 'start / stop planner'
})
})
ui.startStopButton.subscribe('onclick', onStartStop)
planner.inputs = {
instruction: Input('move instruction', onNewInstruction),
acks: Input('move acknowledgement', onAck),
run: Input('boolean', onRunInstruction)
}
planner.outputs = {
moves: Output('move instruction'),
moveComplete: Output('number')
}
// we'll use one of these to assert / do things
// after the module is loaded, and state is copied etc
// i.e. one thing we can do is assert a starting value
planner.init = function() {
state.isRunning = 0
state.netState = [0, 0, 0]
}
/*
------------------------------------------------------
UPDATING / SETUP
------------------------------------------------------
*/
function onAck(msg) {
//console.log("Planner onAck:", msg)
// update position, net states, run netCheck
var axes = state.axisIDs.split(',')
var match = axes.indexOf(msg.axis)
if (match !== -1) {
// HERE is a hack, for our state-updating system doesn't yet accomodate arrays
var newPos = state.position.slice(0)
newPos[match] += msg.increment
state.position = newPos
var newNetState = state.netState.slice(0)
newNetState[match] -= 1
state.netState = newNetState
if (verbose) console.log('NEW NET STATE', newNetState)
} else {
console.log('ERR in PLANNER: missed axis on ack from stepper')
}
netStateRefresh()
}
function netStateRefresh() {
// check if received all four,
// send a packet
var ns = state.netState
var i = 0
// check equality
while (ns[i] == ns[i + 1]) {
i++
// console.log('EQUALITY LOOP')
if (i > ns.length - 1) {
break
}
}
if (i == ns.length - 1) {
console.log('Planner confirms Move Complete')
planner.outputs.moveComplete.emit(1)
}
}
function onRunInstruction(boolean) {
if (boolean) {
if (state.isRunning) {
// already running, do nothing
} else {
state.isRunning = 1
netStateRefresh()
}
} else {
if (state.isRunning) {
state.isRunning = 0
} else {
// oy
}
}
}
function axisIDUpdate() {
var axes = state.axisIDs.split(',')
var position = new Array(axes.length)
position.fill(0)
var packetState = new Array(axes.length)
for (i in axes) {
position.push(0)
packetState.push(0)
// could do
//planner.outputs[axes[i]] = Output('move instruction')
}
state.position = position
state.packetState = packetState
console.log(planner)
}
function onStartStop() {
if (state.isRunning) {
state.isRunning = 0
} else {
state.isRunning = 1
netStateRefresh()
}
}
function sendMoveToNetwork(move) {
for (i in state.netState) {
state.netState[i]++
}
state.netState = state.netState
planner.outputs.moves.emit(move)
}
/*
------------------------------------------------------
ENTRY POINTS
------------------------------------------------------
*/
function onNewInstruction(move) {
// our axis
var axes = state.axisIDs.split(',')
// we'll make a new move object
// start and end points, and axes to track
var p1 = []
var p2 = new Array(axes.length)
p2.fill(0)
// start point is current pos ... one at a time in simplanner
p1 = state.position
// now pick out new deltas
for (i in axes) {
var key = axes[i]
if (move.position[key] != null) {
// some new pos,
p2[i] = move.position[key]
} else {
// or none, this axis stays
p2[i] = p1[i]
}
}
// check for zero-length vector
if (MJS.distance(p1, p2) == 0) {
console.log('------------------ !ACHTUNG! -------------------')
console.log('------------------ !ACHTUNG! -------------------')
console.log('planner throwing zero length vector')
zlcounter++
console.log(zlcounter)
// this means we need another one from the queue
planner.outputs.moveComplete.emit(1)
} else {
// starting with basics
let mv = {
axes: axes,
p1: p1,
p2: p2,
cruise: move.speed,
entry: state.minSpeed,
exit: state.minSpeed,
accel: state.accel
}
// run it once
runSimpleAccel(mv)
sendMoveToNetwork(mv)
}
}
/*
------------------------------------------------------
JUNCTION DEVIATION
walk mq to generate permissible entry / exit speeds
by 'junction deviation' algorithm
------------------------------------------------------
*/
function runSimpleAccel(move) {
// useful
var accel = state.accel
var ms = state.minSpeed
// should reference axis ids, not these moves ?
var numAxis = move.p1.length
// have entry and exit speeds already because we're simplanner
// now that we know permissible cornering speeds,
// we'll calculate the trapezoid shapes
// this becomes very useful for turning moves over to stepper motors
// and will give us a time estimate for each move as well,
// we need / want that so that we can set a network buffer length
calcTrap(move, accel, false)
let time = moveTime(move)
if (true) console.log('PLANNER: trapezoid time to', move.p2, time)
if (time < 0.1) {
console.log('------------------ !ACHTUNG! -------------------')
console.log('------------------ !ACHTUNG! -------------------')
console.log("WARN! move time here", time)
}
}
/*
------------------------------------------------------
TRAPEZOIDS FROM JUNCTION DEVIATION ENTRY / EXITS
------------------------------------------------------
*/
function calcTrap(move, a, debug) {
// have p1, p2, cruise, entry, exit
// add length, figure if full-accel, full-deccel, triangle, trapezoid, cruise ...
move.vector = MJS.subtract(move.p2, move.p1)
var d = DCRT.length(move.vector)
var vi = move.entry
var v = move.cruise
var vf = move.exit
// limits
var maxExit = Math.sqrt(Math.pow(vi, 2) + 2 * a * d)
var maxEntry = Math.sqrt(Math.pow(vf, 2) + 2 * a * d)
// seven possible cases
// HERE set move with type, accel / deccel lengths, calculate time for each, and total time
// then get on with writing a larger gcode window, bringing in some 'real' moves, and getting these
// out / back from steppers
if (d <= 0) {
console.log('ZERO LENGTH MOVE')
} else if (maxExit <= vf) {
if (debug) console.log('TRAP: full ramp up');
move.type = 'ramp up'
// full ramp up
// accel, cruise, and deccel time
move.t1 = (vf - vi) / a