}
+char *_parse_arg(char **p) {
+ char *start = *p;
+ char *next = *p;
+
+ bool inQuote = false;
+ bool escape = false;
+
+ while (**p) {
+ char c = *(*p)++;
+
+ switch (c) {
+ case '\\':
+ if (!escape) {
+ escape = true;
+ continue;
+ }
+ break;
+
+ case ' ': case '\t':
+ if (!inQuote && !escape) goto done;
+ break;
+
+ case '"':
+ if (!escape) {
+ inQuote = !inQuote;
+ continue;
+ }
+ break;
+
+ default: break;
+ }
+
+ *next++ = c;
+ escape = false;
+ }
+
+ done:
+ *next = 0;
+ return start;
+}
+
+
int command_parser(char *cmd) {
// Parse line
char *p = cmd + 1; // Skip `$`
while (*p && isspace(*p)) *p++ = 0;
// Start of token
- if (*p) argv[argc++] = p;
-
- // Find end
- while (*p && !isspace(*p)) p++;
+ char *arg = _parse_arg(&p);
+ if (*arg) argv[argc++] = arg;
}
// Exec command
#include "gcode_state.h"
+static const char INVALID_PGMSTR[] PROGMEM = "INVALID";
+
+static const char INCHES_PGMSTR[] PROGMEM = "IN";
+static const char MILLIMETERS_PGMSTR[] PROGMEM = "MM";
+static const char DEGREES_PGMSTR[] PROGMEM = "DEG";
+
+static const char INVERSE_TIME_MODE_PGMSTR[] PROGMEM = "INVERSE TIME";
+static const char UNITS_PER_MINUTE_MODE_PGMSTR[] PROGMEM = "PER MIN";
+static const char UNITS_PER_REVOLUTION_MODE_PGMSTR[] PROGMEM = "PER REV";
+
+static const char PLANE_XY_PGMSTR[] PROGMEM = "XY";
+static const char PLANE_XZ_PGMSTR[] PROGMEM = "XZ";
+static const char PLANE_YZ_PGMSTR[] PROGMEM = "YZ";
+
+static const char ABSOLUTE_COORDS_PGMSTR[] PROGMEM = "ABS";
+static const char G54_PGMSTR[] PROGMEM = "G54";
+static const char G55_PGMSTR[] PROGMEM = "G55";
+static const char G56_PGMSTR[] PROGMEM = "G56";
+static const char G57_PGMSTR[] PROGMEM = "G57";
+static const char G58_PGMSTR[] PROGMEM = "G58";
+static const char G59_PGMSTR[] PROGMEM = "G59";
+
+static const char PATH_EXACT_PATH_PGMSTR[] PROGMEM = "EXACT PATH";
+static const char PATH_EXACT_STOP_PGMSTR[] PROGMEM = "EXACT STOP";
+static const char PATH_CONTINUOUS_PGMSTR[] PROGMEM = "CONTINUOUS";
+
+static const char ABSOLUTE_MODE_PGMSTR[] PROGMEM = "ABSOLUTE";
+static const char INCREMENTAL_MODE_PGMSTR[] PROGMEM = "INCREMENTAL";
+
+
PGM_P gs_get_units_pgmstr(units_t mode) {
switch (mode) {
- case INCHES: return PSTR("IN");
- case MILLIMETERS: return PSTR("MM");
- case DEGREES: return PSTR("DEG");
+ case INCHES: return INCHES_PGMSTR;
+ case MILLIMETERS: return MILLIMETERS_PGMSTR;
+ case DEGREES: return DEGREES_PGMSTR;
}
- return PSTR("INVALID");
+ return INVALID_PGMSTR;
+}
+
+
+units_t gs_parse_units(const char *s) {
+ if (!strcmp_P(s, INCHES_PGMSTR)) return INCHES;
+ if (!strcmp_P(s, MILLIMETERS_PGMSTR)) return MILLIMETERS;
+ if (!strcmp_P(s, DEGREES_PGMSTR)) return DEGREES;
+ return -1;
}
PGM_P gs_get_feed_mode_pgmstr(feed_mode_t mode) {
switch (mode) {
- case INVERSE_TIME_MODE: return PSTR("INVERSE TIME");
- case UNITS_PER_MINUTE_MODE: return PSTR("PER MIN");
- case UNITS_PER_REVOLUTION_MODE: return PSTR("PER REV");
+ case INVERSE_TIME_MODE: return INVERSE_TIME_MODE_PGMSTR;
+ case UNITS_PER_MINUTE_MODE: return UNITS_PER_MINUTE_MODE_PGMSTR;
+ case UNITS_PER_REVOLUTION_MODE: return UNITS_PER_REVOLUTION_MODE_PGMSTR;
}
- return PSTR("INVALID");
+ return INVALID_PGMSTR;
+}
+
+
+feed_mode_t gs_parse_feed_mode(const char *s) {
+ if (!strcmp_P(s, INVERSE_TIME_MODE_PGMSTR)) return INVERSE_TIME_MODE;
+ if (!strcmp_P(s, UNITS_PER_MINUTE_MODE_PGMSTR)) return UNITS_PER_MINUTE_MODE;
+ if (!strcmp_P(s, UNITS_PER_REVOLUTION_MODE_PGMSTR))
+ return UNITS_PER_REVOLUTION_MODE;
+ return -1;
}
PGM_P gs_get_plane_pgmstr(plane_t plane) {
switch (plane) {
- case PLANE_XY: return PSTR("XY");
- case PLANE_XZ: return PSTR("XZ");
- case PLANE_YZ: return PSTR("YZ");
+ case PLANE_XY: return PLANE_XY_PGMSTR;
+ case PLANE_XZ: return PLANE_XZ_PGMSTR;
+ case PLANE_YZ: return PLANE_YZ_PGMSTR;
}
- return PSTR("INVALID");
+ return INVALID_PGMSTR;
+}
+
+
+plane_t gs_parse_plane(const char *s) {
+ if (!strcmp_P(s, PLANE_XY_PGMSTR)) return PLANE_XY;
+ if (!strcmp_P(s, PLANE_XZ_PGMSTR)) return PLANE_XZ;
+ if (!strcmp_P(s, PLANE_YZ_PGMSTR)) return PLANE_YZ;
+ return -1;
}
PGM_P gs_get_coord_system_pgmstr(coord_system_t cs) {
switch (cs) {
- case ABSOLUTE_COORDS: return PSTR("ABS");
- case G54: return PSTR("G54");
- case G55: return PSTR("G55");
- case G56: return PSTR("G56");
- case G57: return PSTR("G57");
- case G58: return PSTR("G58");
- case G59: return PSTR("G59");
+ case ABSOLUTE_COORDS: return ABSOLUTE_COORDS_PGMSTR;
+ case G54: return G54_PGMSTR;
+ case G55: return G55_PGMSTR;
+ case G56: return G56_PGMSTR;
+ case G57: return G57_PGMSTR;
+ case G58: return G58_PGMSTR;
+ case G59: return G59_PGMSTR;
}
- return PSTR("INVALID");
+ return INVALID_PGMSTR;
+}
+
+
+coord_system_t gs_parse_coord_system(const char *s) {
+ if (!strcmp_P(s, ABSOLUTE_COORDS_PGMSTR)) return ABSOLUTE_COORDS;
+ if (!strcmp_P(s, G54_PGMSTR)) return G54;
+ if (!strcmp_P(s, G55_PGMSTR)) return G55;
+ if (!strcmp_P(s, G56_PGMSTR)) return G56;
+ if (!strcmp_P(s, G57_PGMSTR)) return G57;
+ if (!strcmp_P(s, G58_PGMSTR)) return G58;
+ if (!strcmp_P(s, G59_PGMSTR)) return G59;
+ return -1;
}
PGM_P gs_get_path_mode_pgmstr(path_mode_t mode) {
switch (mode) {
- case PATH_EXACT_PATH: return PSTR("EXACT PATH");
- case PATH_EXACT_STOP: return PSTR("EXACT STOP");
- case PATH_CONTINUOUS: return PSTR("CONTINUOUS");
+ case PATH_EXACT_PATH: return PATH_EXACT_PATH_PGMSTR;
+ case PATH_EXACT_STOP: return PATH_EXACT_STOP_PGMSTR;
+ case PATH_CONTINUOUS: return PATH_CONTINUOUS_PGMSTR;
}
- return PSTR("INVALID");
+ return INVALID_PGMSTR;
+}
+
+
+path_mode_t gs_parse_path_mode(const char *s) {
+ if (!strcmp_P(s, PATH_EXACT_PATH_PGMSTR)) return PATH_EXACT_PATH;
+ if (!strcmp_P(s, PATH_EXACT_STOP_PGMSTR)) return PATH_EXACT_STOP;
+ if (!strcmp_P(s, PATH_CONTINUOUS_PGMSTR)) return PATH_CONTINUOUS;
+ return -1;
}
PGM_P gs_get_distance_mode_pgmstr(distance_mode_t mode) {
switch (mode) {
- case ABSOLUTE_MODE: return PSTR("ABSOLUTE");
- case INCREMENTAL_MODE: return PSTR("INCREMENTAL");
+ case ABSOLUTE_MODE: return ABSOLUTE_MODE_PGMSTR;
+ case INCREMENTAL_MODE: return INCREMENTAL_MODE_PGMSTR;
}
- return PSTR("INVALID");
+ return INVALID_PGMSTR;
+}
+
+
+distance_mode_t gs_parse_distance_mode(const char *s) {
+ if (!strcmp_P(s, ABSOLUTE_MODE_PGMSTR)) return ABSOLUTE_MODE;
+ if (!strcmp_P(s, INCREMENTAL_MODE_PGMSTR)) return INCREMENTAL_MODE;
+ return -1;
}
PGM_P gs_get_units_pgmstr(units_t mode);
+units_t gs_parse_units(const char *units);
PGM_P gs_get_feed_mode_pgmstr(feed_mode_t mode);
+feed_mode_t gs_parse_feed_mode(const char *mode);
PGM_P gs_get_plane_pgmstr(plane_t plane);
+plane_t gs_parse_plane(const char *plane);
PGM_P gs_get_coord_system_pgmstr(coord_system_t cs);
+coord_system_t gs_parse_coord_system(const char *cs);
PGM_P gs_get_path_mode_pgmstr(path_mode_t mode);
+path_mode_t gs_parse_path_mode(const char *mode);
PGM_P gs_get_distance_mode_pgmstr(distance_mode_t mode);
+distance_mode_t gs_parse_distance_mode(const char *mode);
// These functions assume input validation occurred upstream.
/// G17, G18, G19 select axis plane
-void mach_set_plane(plane_t plane) {mach.gm.plane = plane;}
+void mach_set_plane(plane_t plane) {
+ if (plane != (plane_t)-1) mach.gm.plane = plane;
+}
/// G20, G21
-void mach_set_units(units_t mode) {mach.gm.units = mode;}
+void mach_set_units(units_t mode) {
+ if (mode != (units_t)-1) mach.gm.units = mode;
+}
/// G90, G91
void mach_set_distance_mode(distance_mode_t mode) {
- mach.gm.distance_mode = mode;
+ if (mode != (distance_mode_t)-1) mach.gm.distance_mode = mode;
}
/// G90.1, G91.1
void mach_set_arc_distance_mode(distance_mode_t mode) {
- mach.gm.arc_distance_mode = mode;
+ if (mode != (distance_mode_t)-1) mach.gm.arc_distance_mode = mode;
}
/// G54-G59
-void mach_set_coord_system(coord_system_t coord_system) {
- mach.gm.coord_system = coord_system;
+void mach_set_coord_system(coord_system_t cs) {
+ if (cs != (coord_system_t)-1) mach.gm.coord_system = cs;
}
/// G93, G94
void mach_set_feed_mode(feed_mode_t mode) {
- if (mach.gm.feed_mode == mode) return;
+ if (mode == (feed_mode_t)-1 || mach.gm.feed_mode == mode) return;
mach.gm.feed_rate = 0; // Force setting feed rate after changing modes
mach.gm.feed_mode = mode;
}
/// G61, G61.1, G64
void mach_set_path_mode(path_mode_t mode) {
- mach.gm.path_mode = mode;
+ if (mode != (path_mode_t)-1) mach.gm.path_mode = mode;
}
* http://www.linuxcnc.org/docs/2.4/html/gcode_main.html#sec:M50:-Feed-Override
*/
+void mach_set_feed_override(float value) {
+ mach.gm.feed_override = value;
+ mach.gm.feed_override_enable = !fp_ZERO(value);
+}
+
+
+void mach_set_spindle_override(float value) {
+ mach.gm.spindle_override = value;
+ mach.gm.spindle_override_enable = !fp_ZERO(value);
+}
+
+
/// M48, M49
void mach_override_enables(bool flag) {
mach.gm.feed_override_enable = flag;
void mach_mist_coolant_control(bool mist_coolant);
void mach_flood_coolant_control(bool flood_coolant);
+void mach_set_feed_override(float override);
+void mach_set_spindle_override(float override);
void mach_override_enables(bool flag);
void mach_feed_override_enable(bool flag);
void mach_spindle_override_enable(bool flag);
bool done = true;
if (!jr.writing)
for (int axis = 0; axis < AXES; axis++) {
+ if (!axis_is_enabled(axis)) continue;
+
float Vn = jr.next_velocity[axis] * axis_get_velocity_max(axis);
float Vi = jr.velocity[axis];
float Vt = jr.target_velocity[axis];
// Compute per axis velocities
for (int axis = 0; axis < AXES; axis++) {
+ if (!axis_is_enabled(axis)) continue;
+
float V = fabs(jr.velocity[axis]);
float Vt = fabs(jr.target_velocity[axis]);
}
-// GCode
+// GCode getters
int32_t get_line() {return mp_runtime_get_line();}
PGM_P get_unit() {return gs_get_units_pgmstr(mach_get_units());}
float get_speed() {return spindle_get_speed();}
float get_feed() {return mach_get_feed_rate();} // TODO get runtime value
uint8_t get_tool() {return mp_runtime_get_tool();}
-
-
-PGM_P get_feed_mode() {
- return gs_get_feed_mode_pgmstr(mach_get_feed_mode());
-}
-
-
+PGM_P get_feed_mode() {return gs_get_feed_mode_pgmstr(mach_get_feed_mode());}
PGM_P get_plane() {return gs_get_plane_pgmstr(mach_get_plane());}
bool get_mist_coolant() {return coolant_get_mist();}
bool get_flood_coolant() {return coolant_get_flood();}
+
+// GCode setters
+void set_unit(const char *units) {mach_set_units(gs_parse_units(units));}
+void set_speed(float speed) {spindle_set_speed(speed);}
+void set_feed(float feed) {mach_set_feed_rate(feed);}
+
+
+void set_tool(uint8_t tool) {
+ mp_runtime_set_tool(tool);
+ mach_select_tool(tool);
+}
+
+
+void set_feed_mode(const char *mode) {
+ mach_set_feed_mode(gs_parse_feed_mode(mode));
+}
+
+
+void set_plane(const char *plane) {mach_set_plane(gs_parse_plane(plane));}
+
+
+void set_coord_system(const char *cs) {
+ mach_set_coord_system(gs_parse_coord_system(cs));
+}
+
+
+void set_abs_override(bool enable) {mach_set_absolute_mode(enable);}
+
+
+void set_path_mode(const char *mode) {
+ mach_set_path_mode(gs_parse_path_mode(mode));
+}
+
+
+void set_distance_mode(const char *mode) {
+ mach_set_distance_mode(gs_parse_distance_mode(mode));
+}
+
+
+void set_arc_dist_mode(const char *mode) {
+ mach_set_arc_distance_mode(gs_parse_distance_mode(mode));
+}
+
+
+void set_feed_override(float value) {mach_set_feed_override(value);}
+void set_speed_override(float value) {mach_set_spindle_override(value);}
+void set_mist_coolant(bool enable) {coolant_set_mist(enable);}
+void set_flood_coolant(bool enable) {coolant_set_flood(enable);}
+
+
// System
float get_velocity() {return mp_runtime_get_velocity();}
bool get_echo() {return usart_is_set(USART_ECHO);}
// Program string
static void var_print_pstring(pstring s) {printf_P(PSTR("\"%"PRPSTR"\""), s);}
+static const char *var_parse_pstring(const char *value) {return value;}
// Flags
// GCode
VAR(line, ln, int32_t, 0, 0, 1, "Last GCode line executed")
-VAR(unit, u, pstring, 0, 0, 1, "Current unit of measure")
-VAR(speed, s, float, 0, 0, 1, "Current spindle speed")
-VAR(feed, f, float, 0, 0, 1, "Current feed rate")
-VAR(tool, t, uint8_t, 0, 0, 1, "Current tool")
-VAR(feed_mode, fm, pstring, 0, 0, 1, "Current feed rate mode")
-VAR(plane, pa, pstring, 0, 0, 1, "Current plane")
-VAR(coord_system, cs, pstring, 0, 0, 1, "Current coordinate system")
-VAR(abs_override, ao, bool, 0, 0, 1, "Absolute override enabled")
-VAR(path_mode, pc, pstring, 0, 0, 1, "Current path control mode")
-VAR(distance_mode, dm, pstring, 0, 0, 1, "Current distance mode")
-VAR(arc_dist_mode, ad, pstring, 0, 0, 1, "Current arc distance mode")
-VAR(mist_coolant, mc, bool, 0, 0, 1, "Mist coolant enabled")
-VAR(flood_coolant, fc, bool, 0, 0, 1, "Flood coolant enabled")
-VAR(feed_override, fo, float, 0, 0, 1, "Feed rate override")
-VAR(speed_override, so, float, 0, 0, 1, "Spindle speed override")
+VAR(unit, u, pstring, 0, 1, 1, "Current unit of measure")
+VAR(speed, s, float, 0, 1, 1, "Current spindle speed")
+VAR(feed, f, float, 0, 1, 1, "Current feed rate")
+VAR(tool, t, uint8_t, 0, 1, 1, "Current tool")
+VAR(feed_mode, fm, pstring, 0, 1, 1, "Current feed rate mode")
+VAR(plane, pa, pstring, 0, 1, 1, "Current plane")
+VAR(coord_system, cs, pstring, 0, 1, 1, "Current coordinate system")
+VAR(abs_override, ao, bool, 0, 1, 1, "Absolute override enabled")
+VAR(path_mode, pc, pstring, 0, 1, 1, "Current path control mode")
+VAR(distance_mode, dm, pstring, 0, 1, 1, "Current distance mode")
+VAR(arc_dist_mode, ad, pstring, 0, 1, 1, "Current arc distance mode")
+VAR(feed_override, fo, float, 0, 1, 1, "Feed rate override")
+VAR(speed_override, so, float, 0, 1, 1, "Spindle speed override")
+VAR(mist_coolant, mc, bool, 0, 1, 1, "Mist coolant enabled")
+VAR(flood_coolant, fc, bool, 0, 1, 1, "Flood coolant enabled")
// System
VAR(velocity, v, float, 0, 0, 1, "Current velocity")
connect: function () {
- this.sock = new Sock('//' + window.location.host + '/ws');
+ this.sock = new Sock('//' + window.location.host + '/sockjs');
this.sock.onmessage = function (e) {
var msg = e.data;
I2C_ZERO = 11
+machine_state_vars = '''
+ xp yp zp ap bp cp u s f t fm pa cs ao pc dm ad fo so mc fc
+'''.split()
+
+
class AVR():
def __init__(self, ctrl):
self.ctrl = ctrl
# Reset AVR communication
self.stop();
self.ctrl.config.config_avr()
+ self._restore_machine_state()
self.report()
except Exception as e:
raise
+ def _restore_machine_state(self):
+ for var in machine_state_vars:
+ if var in self.vars:
+ value = self.vars[var]
+ if isinstance(value, str): value = '"' + value + '"'
+ if isinstance(value, bool): value = int(value)
+
+ self.queue_command('${}={}'.format(var, value))
+
+
def report(self): self._i2c_command(I2C_REPORT)
self.command = None
# Load next command from queue
- if len(self.queue): self.load_next_command(self.queue.pop())
+ if len(self.queue): self.load_next_command(self.queue.popleft())
# Load next GCode command, if running or paused
elif self.stream is not None:
self.text('%-9s' % state, 0, 0)
- if 'xp' in msg: self.text('% 10.4fX' % msg['xp'], 9, 0)
- if 'yp' in msg: self.text('% 10.4fY' % msg['yp'], 9, 1)
- if 'zp' in msg: self.text('% 10.4fZ' % msg['zp'], 9, 2)
- if 'ap' in msg: self.text('% 10.4fA' % msg['ap'], 9, 3)
+ if 'xp' in msg: self.text('% 10.3fX' % msg['xp'], 9, 0)
+ if 'yp' in msg: self.text('% 10.3fY' % msg['yp'], 9, 1)
+ if 'zp' in msg: self.text('% 10.3fZ' % msg['zp'], 9, 2)
+ if 'ap' in msg: self.text('% 10.3fA' % msg['ap'], 9, 3)
if 't' in msg: self.text('%2uT' % msg['t'], 6, 1)
- if 'u' in msg: self.text('%s' % msg['u'], 0, 1)
+ if 'u' in msg: self.text('%-6s' % msg['u'], 0, 1)
if 'f' in msg: self.text('%8uF' % msg['f'], 0, 2)
if 's' in msg: self.text('%8dS' % msg['s'], 0, 3)
def put_ok(self, value): self.ctrl.avr.override_speed(float(value))
-class Connection(sockjs.tornado.SockJSConnection):
+class WSConnection(tornado.websocket.WebSocketHandler):
+ def __init__(self, app, request, **kwargs):
+ super(WSConnection, self).__init__(app, request, **kwargs)
+ self.ctrl = app.ctrl
+ self.timer = None
+
+
+ def heartbeat(self):
+ self.timer = self.ctrl.ioloop.call_later(3, self.heartbeat)
+ self.write_message({'heartbeat': self.count})
+ self.count += 1
+
+
+ def open(self):
+ self.clients = self.ctrl.web.ws_clients
+
+ self.timer = self.ctrl.ioloop.call_later(3, self.heartbeat)
+ self.count = 0;
+
+ self.clients.append(self)
+ self.write_message(self.ctrl.avr.vars)
+
+
+ def on_close(self):
+ if self.timer is not None: self.ctrl.ioloop.remove_timeout(self.timer)
+ self.clients.remove(self)
+
+
+ def on_message(self, msg): pass
+
+
+class SockJSConnection(sockjs.tornado.SockJSConnection):
def heartbeat(self):
self.timer = self.ctrl.ioloop.call_later(3, self.heartbeat)
self.send({'heartbeat': self.count})
def on_open(self, info):
self.ctrl = self.session.server.ctrl
- self.clients = self.ctrl.web.clients
+ self.clients = self.ctrl.web.sockjs_clients
self.timer = self.ctrl.ioloop.call_later(3, self.heartbeat)
self.count = 0;
class Web(tornado.web.Application):
def __init__(self, ctrl):
self.ctrl = ctrl
- self.clients = []
+ self.ws_clients = []
+ self.sockjs_clients = []
handlers = [
+ (r'/websocket', WSConnection),
(r'/api/config/load', ConfigLoadHandler),
(r'/api/config/download', ConfigDownloadHandler),
(r'/api/config/save', ConfigSaveHandler),
"default_filename": "index.html"}),
]
- router = sockjs.tornado.SockJSRouter(Connection, '/ws')
+ router = sockjs.tornado.SockJSRouter(SockJSConnection, '/sockjs')
router.ctrl = ctrl
tornado.web.Application.__init__(self, router.urls + handlers)
def broadcast(self, msg):
- if len(self.clients):
- self.clients[0].broadcast(self.clients, msg)
+ if len(self.sockjs_clients):
+ self.sockjs_clients[0].broadcast(self.sockjs_clients, msg)
+
+ for client in self.ws_clients: client.write_message(msg)