#include "libs/SerialMessage.h"
#include "StreamOutputPool.h"
+#include "mbed.h"
+
// extern void setled(int, bool);
#define setled(a, b) do {} while (0)
last_char_was_dollar = false;
}
-void USBSerial::ensure_tx_space(int space)
+bool USBSerial::ensure_tx_space(int space)
{
+ // we need some kind of timeout here or it will hang if upstream stalls
+ uint32_t start = us_ticker_read();
while (txbuf.free() < space) {
+ if((us_ticker_read() - start) > 1000000) {
+ // 1 second timeout
+ return false;
+ }
usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);
usb->usbisr();
}
+ return true;
}
int USBSerial::_putc(int c)
{
if (!attached)
return 1;
- ensure_tx_space(1);
- txbuf.queue(c);
+ if(ensure_tx_space(1)) {
+ txbuf.queue(c);
+ }
usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);
return 1;
return strlen(str);
int i = 0;
while (*str) {
- ensure_tx_space(1);
+ if(!ensure_tx_space(1)) break;
txbuf.queue(*str);
if ((txbuf.available() % 64) == 0)
usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);
virtual void on_attach(void);\r
virtual void on_detach(void);\r
\r
- void ensure_tx_space(int);\r
+ bool ensure_tx_space(int);\r
\r
// keep track of number of newlines in the buffer\r
// this makes it trivial to detect if there's a new line available\r
#define panel_checksum CHECKSUM("panel")
// goes in Flash, list of Mxxx codes that are allowed when in Halted state
-static const int allowed_mcodes[]= {2,5,9,30,105,114,119,80,81,911,503,106,107}; // get temp, get pos, get endstops etc
+static const int allowed_mcodes[]= {2,5,9,30,105,114,115,119,80,81,911,503,106,107}; // get temp, get pos, get endstops etc
static bool is_allowed_mcode(int m) {
for (size_t i = 0; i < sizeof(allowed_mcodes)/sizeof(int); ++i) {
if(allowed_mcodes[i] == m) return true;
new_message.stream->printf("rs N%d\r\n", nextline);
}
+ } else if ( first_char == ';' || first_char == '(' || first_char == '\n' || first_char == '\r' ) {
+ // Ignore comments and blank lines
+ new_message.stream->printf("ok\n");
+
} else if( (n=possible_command.find_first_of("XYZF")) == 0 || (first_char == ' ' && n != string::npos) ) {
// handle pycam syntax, use last modal group 1 command and resubmit if an X Y Z or F is found on its own line
char buf[6];
possible_command.insert(0, buf);
goto try_again;
- } else if ( first_char == ';' || first_char == '(' || first_char == ' ' || first_char == '\n' || first_char == '\r' ) {
- // Ignore comments and blank lines
- new_message.stream->printf("ok\n");
} else {
// an uppercase non command word on its own (except XYZF) just returns ok, we could add an error but no hosts expect that.
void dump_queue(void);
void flush_queue(void);
float get_current_feedrate() const { return current_feedrate; }
+ void force_queue() { check_queue(true); }
friend class Planner; // for queue
if(argument == nullptr && this->playing_file ) {
abort_command("1", &(StreamOutput::NullStream));
}
-
+
if(argument == nullptr && this->suspended) {
// clean up from suspend
this->suspended= false;
this->saved_temperatures.clear();
this->was_playing_file= false;
this->suspend_loops= 0;
+ THEKERNEL->streams->printf("// Suspend cleared\n");
}
}
return;
}
- stream->printf("Suspending print, waiting for queue to empty...\n");
+ stream->printf("// Suspending print, waiting for queue to empty...\n");
// override the leave_heaters_on setting
this->override_leave_heaters_on= (parameters == "h");
// wait for queue to empty
THEKERNEL->conveyor->wait_for_idle();
+ if(THEKERNEL->is_halted()) {
+ THEKERNEL->streams->printf("Suspend aborted by kill\n");
+ suspended= false;
+ return;
+ }
+
THEKERNEL->streams->printf("// Saving current state...\n");
// save current XYZ position
return;
}
- stream->printf("resuming print...\n");
+ stream->printf("// resuming print...\n");
// wait for them to reach temp
if(!this->saved_temperatures.empty()) {
float t= h.second;
PublicData::set_value( temperature_control_checksum, h.first, &t );
}
- stream->printf("Waiting for heaters...\n");
+ stream->printf("// Waiting for heaters...\n");
bool wait= true;
uint32_t tus= us_ticker_read(); // mbed call
while(wait) {
}
}
+ // clean up
+ this->saved_temperatures.clear();
+
+ if(THEKERNEL->is_halted()) {
+ THEKERNEL->streams->printf("Resume aborted by kill\n");
+ THEROBOT->pop_state();
+ suspended= false;
+ return;
+ }
+
// execute optional gcode if defined
if(!before_resume_gcode.empty()) {
- stream->printf("Executing before resume gcode...\n");
+ stream->printf("// Executing before resume gcode...\n");
struct SerialMessage message;
message.message = before_resume_gcode;
message.stream = &(StreamOutput::NullStream);
}
// Restore position
- stream->printf("Restoring saved XYZ positions and state...\n");
+ stream->printf("// Restoring saved XYZ positions and state...\n");
THEROBOT->pop_state();
bool abs_mode= THEROBOT->absolute_mode; // what mode we were in
// force absolute mode for restoring position, then set to the saved relative/absolute mode
// restore extruder state
PublicData::set_value( extruder_checksum, restore_state_checksum, nullptr );
- stream->printf("Resuming print\n");
+ if(THEKERNEL->is_halted()) {
+ THEKERNEL->streams->printf("Resume aborted by kill\n");
+ suspended= false;
+ return;
+ }
+
+ stream->printf("// Resuming print\n");
if(this->was_playing_file) {
this->playing_file = true;
THEKERNEL->streams->printf("// action:resume\r\n");
}
- // clean up
- this->saved_temperatures.clear();
- suspended= false;
+ suspended= false;
}
#include "libs/utils.h"
#include "libs/SerialMessage.h"
#include "libs/StreamOutput.h"
-#include "modules/robot/Conveyor.h"
+#include "Conveyor.h"
#include "DirHandle.h"
#include "mri.h"
#include "version.h"
case 'G':
// issue get state
get_command("state", new_message.stream);
- new_message.stream->printf("ok\n");
+ //new_message.stream->printf("ok\n"); // sending this while printing will cause ok count to get out of sync
break;
case 'X':
new_message.stream->printf("ok\n");
break;
+ case 'J':
+ // instant jog command
+ jog(possible_command, new_message.stream);
+ break;
+
default:
new_message.stream->printf("error:Invalid statement\n");
break;
}
}
+void SimpleShell::jog(string parameters, StreamOutput *stream)
+{
+ // $J X0.1 F0.5
+ int n_motors= THEROBOT->get_number_registered_motors();
+
+ // get axis to move and amount (X0.1)
+ // for now always 1 axis
+ size_t npos= parameters.find_first_of("XYZABC");
+ if(npos == string::npos) {
+ stream->printf("usage: $J X|Y|Z|A|B|C 0.01 [F0.5]\n");
+ return;
+ }
+
+ string s = parameters.substr(npos);
+ if(s.empty() || s.size() < 2) {
+ stream->printf("usage: $J X0.01 [F0.5]\n");
+ return;
+ }
+ char ax= toupper(s[0]);
+ uint8_t a= ax >= 'X' ? ax - 'X' : ax - 'A' + 3;
+ if(a >= n_motors) {
+ stream->printf("error:bad axis\n");
+ return;
+ }
+
+ float d= strtof(s.substr(1).c_str(), NULL);
+
+ float delta[n_motors];
+ for (int i = 0; i < n_motors; ++i) {
+ delta[i]= 0;
+ }
+ delta[a]= d;
+
+ // get speed scale
+ float scale= 1.0F;
+ npos= parameters.find_first_of("F");
+ if(npos != string::npos && npos+1 < parameters.size()) {
+ scale= strtof(parameters.substr(npos+1).c_str(), NULL);
+ }
+
+ THEROBOT->push_state();
+ float rate_mm_s= THEROBOT->actuators[a]->get_max_rate() * scale;
+ THEROBOT->delta_move(delta, rate_mm_s, n_motors);
+
+ // turn off queue delay and run it now
+ THECONVEYOR->force_queue();
+ THEROBOT->pop_state();
+ //stream->printf("Jog: %c%f F%f\n", ax, d, scale);
+}
+
void SimpleShell::help_command( string parameters, StreamOutput *stream )
{
stream->printf("Commands:\r\n");
static void version_command(string parameters, StreamOutput *stream );
private:
+
+ void jog(string params, StreamOutput *stream);
+
static void ls_command(string parameters, StreamOutput *stream );
static void cd_command(string parameters, StreamOutput *stream );
static void delete_file_command(string parameters, StreamOutput *stream );