}else{
Block* block = this->kernel->conveyor->queue.get_ref( this->kernel->conveyor->queue.size() - 1 );
block->append_gcode(gcode);
- gcode->queued++;
}
}
}
# Checked - Optimizations enabled with MRI debug monitor support. (Recommended Type)\r
# Release - Optimizations enabled.\r
# Debug - Optimization disabled with MRI debug monitor support.\r
-BUILD_TYPE=Checked\r
+BUILD_TYPE=Debug\r
\r
# Set to 1 to tag each heap allocation with the caller's return address.\r
# NOTE: Can't be enabled with latest build as not compatible with newlib nano.\r
new_message.stream->printf("\r\n");
new_message.stream->printf("ok\r\n");
- if (gcode->queued == 0)
+ // This is part of a pretty complicated thing, see Gcode.h for a long explanation
+ if( gcode->will_go_thru_new_block_context == true && gcode->passed_thru_new_block_context_without_deleting == true ){
delete gcode;
+ }else{
+ gcode->passed_thru_all_blocks_added_context_without_deleting = true;
+ }
+
}
}else{
//Request resend
#include <stdlib.h>
-Gcode::Gcode(const string& command, StreamOutput* stream) :
- command(command), m(0), g(0), add_nl(false),
- queued(0), stream(stream)
-{
+Gcode::Gcode(const string& command, StreamOutput* stream) : command(command), m(0), g(0), add_nl(false), will_go_thru_new_block_context(false), stream(stream), passed_thru_all_blocks_added_context_without_deleting(false), passed_thru_new_block_context_without_deleting(false){
prepare_cached_values();
+ this->millimeters_of_travel = 0L;
}
// Whether or not a Gcode has a letter
unsigned int g;
bool add_nl;
+ StreamOutput* stream;
+
+ // This requires a long explanation, unfortunately. We have the hard job to delete the gcode at the right momont.
+ // The problem is that we can not delete the gcode before no modules wants to use/read it anymore. This gets complicated because there are actually two possible end moments
+ // of the life of the gcode, and they can happen in either order. So if one deletes it first, the other can not access it. The last context to use it is the one that must delete it,
+ // but it's not always the same.
+ // Here is a resume of the life of a Gcode object :
+ // * Gets created inside GcodeDispatch
+ // * Is passed along with the on_gcode_received event to all modules. We'll ignore all other modules and just concentrate on Robot
+ // * Robot computes the distance for this gcode, if there is none, it just returns, otherwise :
+ // * If the queue is empty, we execute the gcode right away ( on_gcode_execute ), it is not attached to the queue, then we add blocks to the queue and they start getting executed
+ // * If the queue is not empty however, we attach the gcode to the last block on the queue, then start adding blocks to the queue. This does the same as the line above, but with the queue already running, things happen later instead of immediately.
+ // * Now we return to GcodeDispatch.
+ // The problem when we get there is this : if we attached the Gcode to a block in the Queue, We don't know now if it has been executed or not. Usually, it hasn't : we just added it to the queue and the queue has not gotton to it yet.
+ // In that case, we can obviously not delete it right here : the queue still needs it to execute it. So why not delete it when it gets executed ?
+ // Because there is one case with which we can get here, and the gcode *has* been executed already. That's in the case this gcode string appened so many blocks to the queue, that in order to make room for the later blocks
+ // we waited for the earlier ones to get consumed. In that case, the gcode will get executed when we get to it doing that.
+ // So, we can't *always* delete the gcode when it gets executed, and we can't *always* delete the gcode when we get back to GcodeDispatch.
+ // So, there are two places we can delete the Gcode, let's call them :
+ // * NEW BLOCK CONTEXT ( when the queue has executed this gcode and can delete it ( actually two places, one in on_idle, the other when recycling a block for re-use, see Conveyor.cpp )
+ // * ALL BLOCKS ADDED CONTEXT ( when we return to GcodeDispatch after the on_gcode_received call, we know all blocks that would be added for this gcode have been )
+ // Now the trick here is basically : In any given context, we want to delete the gcode
+ // * If we reach the current context and the other context was not supposed to happen
+ // * If we reach the current context and the other context did happen
+ // In either of these cases, we want delete the gcode, otherwise, we want to mark the gcode as having passed thru the current context so that when we reach that other context, the gcode gets deleted
+ // There is a complication/simplification here : the ALL_BLOCKS_ADDED context will always occur for a given gcode. That's a condition that will always be true, and a flag we don't need to keep track of.
+ // The other one however, will only occur if the block is added to the queue. It will not otherwise.
+ // So we use a flag to keep track of that
+ bool will_go_thru_new_block_context; // Defaults to false
+ // Now for each context, we need a flag ( defaulting to false ) that is set to true when we go thru that context without deleting the Gcode.
+ bool passed_thru_new_block_context_without_deleting;
+ bool passed_thru_all_blocks_added_context_without_deleting;
+
+
+
- int queued;
- StreamOutput* stream;
};
#endif
// Gcodes are attached to their respective blocks so that on_gcode_execute can be called with it
void Block::append_gcode(Gcode* gcode){
__disable_irq();
+ gcode->will_go_thru_new_block_context = true;
this->gcodes.push_back(gcode);
__enable_irq();
}
flush_blocks = 0;
}
-void Conveyor::on_module_loaded()
-{
+void Conveyor::on_module_loaded(){
register_for_event(ON_IDLE);
}
-void Conveyor::on_idle(void* argument)
-{
- if (flush_blocks)
- {
+void Conveyor::on_idle(void* argument){
+ if (flush_blocks){
+
Block* block = queue.get_tail_ref();
-// printf("Block: clean %p\n", block);
- while (block->gcodes.size())
- {
- Gcode* g = block->gcodes.back();
+ while (block->gcodes.size()){
+ Gcode* gcode = block->gcodes.back();
block->gcodes.pop_back();
- delete g;
+
+ // Do we just delete this gcode from this vector, or do we also actually delete the gcode ?
+ // This is pretty complex and explained in detail in Gcode.h
+ if( gcode->passed_thru_all_blocks_added_context_without_deleting == true ){
+ delete gcode;
+ }else{
+ gcode->passed_thru_new_block_context_without_deleting = false;
+ }
+
}
queue.delete_first();
// Then clean it up
if( block->conveyor == this ){
for(; block->gcodes.size(); ){
- Gcode* g = block->gcodes.back();
+ Gcode* gcode = block->gcodes.back();
block->gcodes.pop_back();
- delete g;
+
+ // Do we just delete this gcode from this vector, or do we also actually delete the gcode ?
+ // This is pretty complex and explained in detail in Gcode.h
+ if( gcode->passed_thru_all_blocks_added_context_without_deleting == true ){
+ delete gcode;
+ }else{
+ gcode->passed_thru_new_block_context_without_deleting = false;
+ }
+
}
}
if (this->arm_solution) delete this->arm_solution;
int solution_checksum = get_checksum(this->kernel->config->value(arm_solution_checksum)->by_default("cartesian")->as_string());
- // Note checksums are not const expressions when in debug mode, so don't use switch
- if(solution_checksum == rostock_checksum) {
- this->arm_solution = new RostockSolution(this->kernel->config);
-
- }else if(solution_checksum == delta_checksum) {
- // place holder for now
- this->arm_solution = new RostockSolution(this->kernel->config);
-
+ // Note checksums are not const expressions when in debug mode, so don't use switch
+ if(solution_checksum == rostock_checksum) {
+ this->arm_solution = new RostockSolution(this->kernel->config);
+ }else if(solution_checksum == delta_checksum) {
+ // place holder for now
+ this->arm_solution = new RostockSolution(this->kernel->config);
}else if(solution_checksum == rotatable_cartesian_checksum) {
this->arm_solution = new RotatableCartesianSolution(this->kernel->config);
- }else if(solution_checksum == cartesian_checksum) {
- this->arm_solution = new CartesianSolution(this->kernel->config);
-
- }else{
- this->arm_solution = new CartesianSolution(this->kernel->config);
- }
+ }else if(solution_checksum == cartesian_checksum) {
+ this->arm_solution = new CartesianSolution(this->kernel->config);
+ }else{
+ this->arm_solution = new CartesianSolution(this->kernel->config);
+ }
this->feed_rate = this->kernel->config->value(default_feed_rate_checksum )->by_default(100 )->as_number() / 60;
}
+//#pragma GCC push_options
+//#pragma GCC optimize ("O0")
+
+
//A GCode has been received
void Robot::on_gcode_received(void * argument){
Gcode* gcode = static_cast<Gcode*>(argument);
+ this->process_gcode(gcode);
+}
- // If this gcode is a movement gcode
- if( gcode->has_g && gcode->g < 4 && ( gcode->has_letter('X') || gcode->has_letter('Y') || gcode->has_letter('Z') ) ){
- gcode->call_on_gcode_execute_event_immediatly = false;
- gcode->on_gcode_execute_event_called = false;
- //If the queue is empty, execute immediatly, otherwise attach to the last added block
- if( this->kernel->conveyor->queue.size() == 0 ){
- gcode->call_on_gcode_execute_event_immediatly = true;
- this->process_gcode(gcode);
- if( gcode->on_gcode_execute_event_called == false ){
- //printf("GCODE A: %s \r\n", gcode->command.c_str() );
- this->kernel->call_event(ON_GCODE_EXECUTE, gcode );
- }
- }else{
- Block* block = this->kernel->conveyor->queue.get_ref( this->kernel->conveyor->queue.size() - 1 );
- this->process_gcode(gcode);
- block->append_gcode(gcode);
- gcode->queued++;
- }
+// We called process_gcode with a new gcode, and one of the functions
+// determined the distance for that given gcode. So now we can attach this gcode to the right block
+// and continue
+void Robot::distance_in_gcode_is_known(Gcode* gcode){
+ //If the queue is empty, execute immediatly, otherwise attach to the last added block
+ if( this->kernel->conveyor->queue.size() == 0 ){
+ this->kernel->call_event(ON_GCODE_EXECUTE, gcode );
}else{
- this->process_gcode(gcode);
+ Block* block = this->kernel->conveyor->queue.get_ref( this->kernel->conveyor->queue.size() - 1 );
+ block->append_gcode(gcode);
}
+
}
+//#pragma GCC pop_options
void Robot::reset_axis_position(double position, int axis) {
this->last_milestone[axis] = this->current_position[axis] = position;
// In cartesian robot, a high "mm_per_line_segment" setting will prevent waste.
gcode->millimeters_of_travel = sqrt( pow( target[X_AXIS]-this->current_position[X_AXIS], 2 ) + pow( target[Y_AXIS]-this->current_position[Y_AXIS], 2 ) + pow( target[Z_AXIS]-this->current_position[Z_AXIS], 2 ) );
- if( gcode->call_on_gcode_execute_event_immediatly == true ){
+ //if( gcode->call_on_gcode_execute_event_immediatly == true ){
//printf("GCODE B: %s \r\n", gcode->command.c_str() );
- this->kernel->call_event(ON_GCODE_EXECUTE, gcode );
- gcode->on_gcode_execute_event_called = true;
- }
+ // this->kernel->call_event(ON_GCODE_EXECUTE, gcode );
+ // gcode->on_gcode_execute_event_called = true;
+ //}
+ this->distance_in_gcode_is_known( gcode );
- if (gcode->millimeters_of_travel == 0.0) {
- this->append_milestone(this->current_position, 0.0);
- return;
- }
uint16_t segments = ceil( gcode->millimeters_of_travel/ this->mm_per_line_segment);
// A vector to keep track of the endpoint of each segment
gcode->millimeters_of_travel = hypot(angular_travel*radius, fabs(linear_travel));
- if( gcode->call_on_gcode_execute_event_immediatly == true ){
+ //if( gcode->call_on_gcode_execute_event_immediatly == true ){
//printf("GCODE C: %s \r\n", gcode->command.c_str() );
- this->kernel->call_event(ON_GCODE_EXECUTE, gcode );
- gcode->on_gcode_execute_event_called = true;
- }
+ // this->kernel->call_event(ON_GCODE_EXECUTE, gcode );
+ // gcode->on_gcode_execute_event_called = true;
+ //}
+ this->distance_in_gcode_is_known( gcode );
+
+ /*
if (gcode->millimeters_of_travel == 0.0) {
this->append_milestone(this->current_position, 0.0);
return;
}
+ */
uint16_t segments = floor(gcode->millimeters_of_travel/this->mm_per_arc_segment);
void reset_axis_position(double position, int axis);
private:
+ void distance_in_gcode_is_known(Gcode* gcode);
void process_gcode(Gcode* gcode);
void append_milestone( double target[], double feed_rate);
void append_line( Gcode* gcode, double target[], double feed_rate);
}else{
Block* block = this->kernel->conveyor->queue.get_ref( this->kernel->conveyor->queue.size() - 1 );
block->append_gcode(gcode);
- gcode->queued++;
}
}
this->kernel->call_event(ON_GCODE_EXECUTE, gcode );
this->append_empty_block();
}else{
- Block* block = this->kernel->conveyor->queue.get_ref( this->kernel->conveyor->queue.size() - 1 );
this->append_empty_block();
+ Block* block = this->kernel->conveyor->queue.get_ref( this->kernel->conveyor->queue.size() - 1 );
block->append_gcode(gcode);
- gcode->queued++;
}
}
+ }else{
+ // This is for follow move
+
+
}
}
block->ready();
}
+//#pragma GCC push_options
+//#pragma GCC optimize ("O0")
// Compute extrusion speed based on parameters and gcode distance of travel
void Extruder::on_gcode_execute(void* argument){
}
}
+//#pragma GCC pop_options
+
// When a new block begins, either follow the robot, or step by ourselves ( or stay back and do nothing )
void Extruder::on_block_begin(void* argument){
Block* block = static_cast<Block*>(argument);
this->stepper_motor->steps_per_second = 0;
this->stepper_motor->move( ( this->travel_distance > 0 ), steps_to_step);
+ }else{
+ this->current_block = NULL;
}
-
}else if( this->mode == FOLLOW ){
// In non-solo mode, we just follow the stepper module
-
this->current_block = block;
this->target_position = this->current_position + ( this->current_block->millimeters * this->travel_ratio );
- //int32_t steps_to_step = abs( int( floor(this->steps_per_millimeter*this->target_position) - floor(this->steps_per_millimeter*this->current_position) ) );
-
int old_steps = this->current_steps;
int target_steps = int( floor(this->steps_per_millimeter*this->target_position) );
int steps_to_step = target_steps - old_steps ;
this->current_steps = target_steps;
-
if( steps_to_step != 0 ){
-
- //printf("taken for extruder: %u \r\n", steps_to_step);
-
block->take();
-
- //printf("spm:%f td:%f steps:%d ( %f - %f ) \r\n", this->steps_per_millimeter, this->travel_distance, steps_to_step, this->target_position, this->current_position );
-
this->stepper_motor->move( ( steps_to_step > 0 ), abs(steps_to_step) );
-
-
-
+ }else{
+ this->current_block = NULL;
}
}else if( this->mode == OFF ){
// No movement means we must reset our speed
-
+ this->current_block = NULL;
//this->stepper_motor->set_speed(0);
}
this->current_position = this->target_position;
- if (this->current_block) // this should always be true, but sometimes it isn't. TODO: find out why
- this->current_block->release();
+ if (this->current_block){ // this should always be true, but sometimes it isn't. TODO: find out why
+ Block* block = this->current_block;
+ this->current_block = NULL;
+ block->release();
+ }
return 0;
}
}else{
Block* block = this->kernel->conveyor->queue.get_ref( this->kernel->conveyor->queue.size() - 1 );
block->append_gcode(gcode);
- gcode->queued++;
}
}
}
}else{
Block* block = this->kernel->conveyor->queue.get_ref( this->kernel->conveyor->queue.size() - 1 );
block->append_gcode(gcode);
- gcode->queued++;
}
+
}
}
}