Block,Conveyor: ensure on_block_end is only called once. co-opt is_ready flag for...
[clinton/Smoothieware.git] / src / modules / robot / Conveyor.cpp
CommitLineData
f80d18b9
L
1/*
2 This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl) with additions from Sungeun K. Jeon (https://github.com/chamnit/grbl)
3 Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
4 Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
5 You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>.
6*/
7
8using namespace std;
9#include <vector>
10#include "libs/nuts_bolts.h"
11#include "libs/RingBuffer.h"
12#include "../communication/utils/Gcode.h"
13#include "libs/Module.h"
14#include "libs/Kernel.h"
15#include "Timer.h" // mbed.h lib
16#include "wait_api.h" // mbed.h lib
17#include "Block.h"
18#include "Conveyor.h"
19#include "Planner.h"
55456577 20#include "mri.h"
f80d18b9 21
0b3e628f
MM
22#define planner_queue_size_checksum CHECKSUM("planner_queue_size")
23
edac9072
AW
24// The conveyor holds the queue of blocks, takes care of creating them, and starting the executing chain of blocks
25
f80d18b9 26Conveyor::Conveyor(){
c501670b 27 gc_pending = queue.tail_i;
2134bcf2 28 running = false;
702023f3
MM
29}
30
d149c730 31void Conveyor::on_module_loaded(){
702023f3 32 register_for_event(ON_IDLE);
0b3e628f
MM
33 register_for_event(ON_CONFIG_RELOAD);
34
35 on_config_reload(this);
702023f3
MM
36}
37
edac9072 38// Delete blocks here, because they can't be deleted in interrupt context ( see Block.cpp:release )
d149c730 39void Conveyor::on_idle(void* argument){
55456577 40 if (queue.tail_i != gc_pending)
c501670b 41 {
55456577
MM
42 if (queue.is_empty())
43 __debugbreak();
44 else
45 {
46 // Cleanly delete block
47 Block* block = queue.tail_ref();
9d005957 48// block->debug();
3ac0b99e 49 block->clear();
55456577
MM
50 queue.consume_tail();
51 }
702023f3 52 }
3facc890
MM
53 else if (queue.is_empty())
54 {
55 // if someone has appended gcodes but the queue is stopped
56 // make sure they get executed in a timely fashion
57 if (queue.head_ref()->gcodes.size())
58 {
59 queue_head_block();
60 ensure_running();
61 }
62 }
36aca284
MM
63 else
64 // queue not empty
65 ensure_running();
f80d18b9
L
66}
67
0b3e628f
MM
68void Conveyor::on_config_reload(void* argument)
69{
70 queue.resize(THEKERNEL->config->value(planner_queue_size_checksum)->by_default(32)->as_number());
71}
72
e0ee24ed
MM
73void Conveyor::append_gcode(Gcode* gcode)
74{
75 gcode->mark_as_taken();
c87f8e07 76 queue.head_ref()->append_gcode(gcode);
e0ee24ed
MM
77}
78
f80d18b9 79// Process a new block in the queue
2134bcf2
MM
80void Conveyor::on_block_end(void* block)
81{
55456577
MM
82 if (queue.is_empty())
83 __debugbreak();
0b3e628f 84
2134bcf2 85 gc_pending = queue.next(gc_pending);
f80d18b9
L
86
87 // Return if queue is empty
2134bcf2
MM
88 if (gc_pending == queue.head_i)
89 {
90 running = false;
f80d18b9
L
91 return;
92 }
702023f3 93
f80d18b9 94 // Get a new block
2134bcf2 95 Block* next = this->queue.item_ref(gc_pending);
f80d18b9 96
2134bcf2 97 next->begin();
f80d18b9
L
98}
99
edac9072 100// Wait for the queue to have a given number of free blocks
c501670b
MM
101void Conveyor::wait_for_queue(int free_blocks)
102{
103 while (queue.is_full())
2134bcf2
MM
104 {
105 ensure_running();
314ab8f7 106 THEKERNEL->call_event(ON_IDLE);
2134bcf2 107 }
f80d18b9 108}
17c68379 109
edac9072 110// Wait for the queue to be empty
c501670b
MM
111void Conveyor::wait_for_empty_queue()
112{
113 while (!queue.is_empty())
2134bcf2
MM
114 {
115 ensure_running();
314ab8f7 116 THEKERNEL->call_event(ON_IDLE);
2134bcf2 117 }
17c68379
BG
118}
119
68d16168 120// Return true if the queue is empty
c501670b
MM
121bool Conveyor::is_queue_empty()
122{
123 return queue.is_empty();
68d16168
L
124}
125
2134bcf2
MM
126void Conveyor::queue_head_block()
127{
128 while (queue.is_full())
129 {
130 ensure_running();
131 THEKERNEL->call_event(ON_IDLE, this);
132 }
133
9d005957 134 queue.head_ref()->ready();
2134bcf2
MM
135 queue.produce_head();
136}
137
138void Conveyor::ensure_running()
139{
140 if (!running)
141 {
142 running = true;
143 queue.tail_ref()->begin();
144 }
145}
c501670b 146
a617ac35
MM
147// Debug function
148void Conveyor::dump_queue()
149{
150 for (unsigned int index = queue.tail_i, i = 0; true; index = queue.next(index), i++ )
151 {
152 THEKERNEL->streams->printf("block %03d > ", i);
153 queue.item_ref(index)->debug();
154
155 if (index == queue.head_i)
156 break;
157 }
158}
159
c501670b
MM
160// feels hacky, but apparently the way to do it
161#include "HeapRing.cpp"
162template class HeapRing<Block>;