interating temperature controllers was done wrong. refactoring that error.
[clinton/Smoothieware.git] / src / modules / utils / panel / screens / WatchScreen.cpp
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).
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
8 #include "libs/Kernel.h"
9 #include "LcdBase.h"
10 #include "Panel.h"
11 #include "PanelScreen.h"
12 #include "MainMenuScreen.h"
13 #include "WatchScreen.h"
14 #include "libs/nuts_bolts.h"
15 #include "libs/utils.h"
16 #include "modules/tools/temperaturecontrol/TemperatureControlPublicAccess.h"
17 #include "modules/robot/RobotPublicAccess.h"
18 #include "modules/robot/Conveyor.h"
19 #include "modules/utils/player/PlayerPublicAccess.h"
20 #include "NetworkPublicAccess.h"
21 #include "PublicData.h"
22 #include "SwitchPublicAccess.h"
23 #include "checksumm.h"
24 #include "Pauser.h"
25 #include "TemperatureControlPool.h"
26
27
28 #include <math.h>
29 #include <string.h>
30 #include <string>
31 #include <stdio.h>
32 #include <algorithm>
33
34 using namespace std;
35 static const uint8_t icons[] = { // 115x19 - 3 bytes each: he1, he2, he3, bed, fan
36 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0,
37 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
38 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x0C, 0x60,
39 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x0E, 0x20,
40 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x0F, 0x20,
41 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x0F, 0xA0,
42 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x07, 0xA0,
43 0x7F, 0x80, 0x00, 0x3F, 0xC0, 0x00, 0x3F, 0xC0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
44 0xFB, 0xC0, 0x00, 0x79, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
45 0xF3, 0xC0, 0x00, 0x76, 0xE0, 0x00, 0x76, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
46 0xEB, 0xC0, 0x00, 0x7E, 0xE0, 0x00, 0x7E, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
47 0x7B, 0x80, 0x00, 0x3D, 0xC0, 0x00, 0x39, 0xC0, 0x00, 0x82, 0x08, 0x00, 0x5E, 0x07, 0xA0,
48 0x7B, 0x80, 0x00, 0x3B, 0xC0, 0x00, 0x3E, 0xC0, 0x01, 0x04, 0x10, 0x00, 0x5F, 0x0F, 0xA0,
49 0xFB, 0xC0, 0x00, 0x77, 0xE0, 0x00, 0x76, 0xE0, 0x01, 0x04, 0x10, 0x00, 0x4F, 0x0F, 0x20,
50 0xFB, 0xC0, 0x00, 0x70, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x82, 0x08, 0x00, 0x47, 0x0E, 0x20,
51 0xFF, 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x7F, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x63, 0x0C, 0x60,
52 0x3F, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
53 0x1E, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x0F, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xE0,
54 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00
55 };
56
57
58
59 WatchScreen::WatchScreen()
60 {
61 speed_changed = false;
62 issue_change_speed = false;
63 ipstr = nullptr;
64 update_counts= 0;
65 }
66
67 WatchScreen::~WatchScreen()
68 {
69 delete[] ipstr;
70 }
71
72 void WatchScreen::on_enter()
73 {
74 THEPANEL->lcd->clear();
75 THEPANEL->setup_menu(4);
76 get_current_status();
77 get_current_pos(this->pos);
78 get_sd_play_info();
79 this->current_speed = lround(get_current_speed());
80 this->refresh_screen(false);
81 THEPANEL->enter_control_mode(1, 0.5);
82 THEPANEL->set_control_value(this->current_speed);
83 }
84
85 void WatchScreen::on_refresh()
86 {
87 // Exit if the button is clicked
88 if ( THEPANEL->click() ) {
89 THEPANEL->enter_screen(this->parent);
90 return;
91 }
92
93 // see if speed is being changed
94 if (THEPANEL->control_value_change()) {
95 this->current_speed = THEPANEL->get_control_value();
96 if (this->current_speed < 10) {
97 this->current_speed = 10;
98 THEPANEL->set_control_value(this->current_speed);
99 THEPANEL->reset_counter();
100 } else {
101 // flag the update to change the speed, we don't want to issue hundreds of M220s
102 // but we do want to display the change we are going to make
103 this->speed_changed = true; // flag indicating speed changed
104 this->refresh_screen(false);
105 }
106 }
107
108 // Update Only every 20 refreshes, 1 a second
109 update_counts++;
110 if ( update_counts % 20 == 0 ) {
111 get_sd_play_info();
112 get_current_pos(this->pos);
113 get_current_status();
114 if (this->speed_changed) {
115 this->issue_change_speed = true; // trigger actual command to change speed
116 this->speed_changed = false;
117 } else if (!this->issue_change_speed) { // change still queued
118 // read it in case it was changed via M220
119 this->current_speed = lround(get_current_speed());
120 THEPANEL->set_control_value(this->current_speed);
121 THEPANEL->reset_counter();
122 }
123
124 this->refresh_screen(THEPANEL->lcd->hasGraphics() ? true : false); // graphics screens should be cleared
125
126 // for LCDs with leds set them according to heater status
127 bool bed_on= false, hotend_on= false, is_hot= false;
128 uint8_t heon=0, hemsk= 0x01; // bit set for which hotend is on bit0: hotend1, bit1: hotend2 etc
129 temp_controllers.clear();
130 std::vector<struct pad_temperature*> *controllers;
131 bool ok = PublicData::get_value(temperature_control_checksum, poll_controls_checksum, (void**)&controllers);
132 if (ok) {
133 for (auto &c : *controllers) {
134 temp_controllers.push_back(c->id);
135 if(c->current_temperature > 50) is_hot= true; // anything is hot
136 if(c->designator.front() == 'B' && c->target_temperature > 0) bed_on= true; // bed on/off
137 if(c->designator.front() == 'T') { // a hotend by convention
138 if(c->target_temperature > 0){
139 hotend_on= true;// hotend on/off (anyone)
140 heon |= hemsk;
141 }
142 hemsk <<= 1;
143 }
144 }
145 }
146 THEPANEL->lcd->setLed(LED_BED_ON, bed_on);
147 THEPANEL->lcd->setLed(LED_HOTEND_ON, hotend_on);
148 THEPANEL->lcd->setLed(LED_HOT, is_hot);
149
150 THEPANEL->lcd->setLed(LED_FAN_ON, this->fan_state);
151
152 if (THEPANEL->lcd->hasGraphics()) {
153 // display the graphical icons below the status are
154 //THEPANEL->lcd->bltGlyph(0, 34, 115, 19, icons);
155 // for (int i = 0; i < 5; ++i) {
156 // THEPANEL->lcd->bltGlyph(i*24, 38, 23, 19, icons, 15, i*24, 0);
157 // }
158 if(heon&0x01) THEPANEL->lcd->bltGlyph(0, 38, 20, 19, icons, 15, 0, 0);
159 if(heon&0x02) THEPANEL->lcd->bltGlyph(20, 38, 20, 19, icons, 15, 24, 0);
160 if(heon&0x04) THEPANEL->lcd->bltGlyph(40, 38, 20, 19, icons, 15, 48, 0);
161
162 if (bed_on)
163 THEPANEL->lcd->bltGlyph(60, 38, 23, 19, icons, 15, 64, 0);
164
165 if(this->fan_state)
166 THEPANEL->lcd->bltGlyph(96, 38, 23, 19, icons, 15, 96, 0);
167 }
168 }
169 }
170
171 // queuing gcodes needs to be done from main loop
172 void WatchScreen::on_main_loop()
173 {
174 if (this->issue_change_speed) {
175 this->issue_change_speed = false;
176 set_speed();
177 }
178 PanelScreen::on_main_loop(); // in case any queued commands left
179 }
180
181 void *WatchScreen::getTemperatures(uint16_t heater_cs)
182 {
183 void *returned_data;
184 bool ok = PublicData::get_value( temperature_control_checksum, heater_cs, current_temperature_checksum, &returned_data );
185
186 if (ok) {
187 return returned_data;
188 }
189
190 return nullptr;
191 }
192
193 // fetch the data we are displaying
194 void WatchScreen::get_current_status()
195 {
196 void *returned_data;
197 bool ok;
198
199 // get fan status
200 ok = PublicData::get_value( switch_checksum, fan_checksum, 0, &returned_data );
201 if (ok) {
202 struct pad_switch s = *static_cast<struct pad_switch *>(returned_data);
203 this->fan_state = s.state;
204 } else {
205 // fan probably disabled
206 this->fan_state = false;
207 }
208 }
209
210 // fetch the data we are displaying
211 float WatchScreen::get_current_speed()
212 {
213 void *returned_data;
214
215 bool ok = PublicData::get_value( robot_checksum, speed_override_percent_checksum, &returned_data );
216 if (ok) {
217 float cs = *static_cast<float *>(returned_data);
218 return cs;
219 }
220 return 0.0;
221 }
222
223 void WatchScreen::get_current_pos(float *cp)
224 {
225 void *returned_data;
226
227 bool ok = PublicData::get_value( robot_checksum, current_position_checksum, &returned_data );
228 if (ok) {
229 float *p = static_cast<float *>(returned_data);
230 cp[0] = p[0];
231 cp[1] = p[1];
232 cp[2] = p[2];
233 }
234 }
235
236 void WatchScreen::get_sd_play_info()
237 {
238 void *returned_data;
239 bool ok = PublicData::get_value( player_checksum, get_progress_checksum, &returned_data );
240 if (ok) {
241 struct pad_progress p = *static_cast<struct pad_progress *>(returned_data);
242 this->elapsed_time = p.elapsed_secs;
243 this->sd_pcnt_played = p.percent_complete;
244 THEPANEL->set_playing_file(p.filename);
245
246 } else {
247 this->elapsed_time = 0;
248 this->sd_pcnt_played = 0;
249 }
250 }
251
252 void WatchScreen::display_menu_line(uint16_t line)
253 {
254 // in menu mode
255 switch ( line ) {
256 case 0:
257 {
258 auto& tm= this->temp_controllers;
259 if(tm.size() > 0) {
260 // only if we detected heaters in config
261 int n= 0;
262 if(tm.size() > 2) {
263 // more than two temps we need to cycle between them
264 n= update_counts/100; // increments every 5 seconds
265 int ntemps= (tm.size()+1)/2;
266 n= n%ntemps; // which of the pairs of temps to display
267 }
268
269 int off= 0;
270 for (size_t i = 0; i < 2; ++i) {
271 size_t o= i+(n*2);
272 if(o>tm.size()-1) break;
273 struct pad_temperature *temp= static_cast<struct pad_temperature *>(getTemperatures(tm[o]));
274 if(temp == nullptr) continue;
275 int t= std::min(999, (int)roundf(temp->current_temperature));
276 int tt= roundf(temp->target_temperature);
277 THEPANEL->lcd->setCursor(off, 0); // col, row
278 off += THEPANEL->lcd->printf("%s:%03d/%03d ", temp->designator.substr(0, 2).c_str(), t, tt);
279 }
280
281 }else{
282 //THEPANEL->lcd->printf("No Heaters");
283 }
284 break;
285 }
286 case 1: THEPANEL->lcd->printf("X%4d Y%4d Z%7.2f", (int)round(this->pos[0]), (int)round(this->pos[1]), this->pos[2]); break;
287 case 2: THEPANEL->lcd->printf("%3d%% %2lu:%02lu %3u%% sd", this->current_speed, this->elapsed_time / 60, this->elapsed_time % 60, this->sd_pcnt_played); break;
288 case 3: THEPANEL->lcd->printf("%19s", this->get_status()); break;
289 }
290 }
291
292 const char *WatchScreen::get_status()
293 {
294 if (THEPANEL->hasMessage())
295 return THEPANEL->getMessage().c_str();
296
297 if (THEPANEL->is_halted())
298 return "HALTED Reset or M999";
299
300 if (THEKERNEL->pauser->paused())
301 return "Paused";
302
303 if (THEPANEL->is_suspended())
304 return "Suspended";
305
306 if (THEPANEL->is_playing())
307 return THEPANEL->get_playing_file();
308
309 if (!THEKERNEL->conveyor->is_queue_empty())
310 return "Printing";
311
312 const char *ip = get_network();
313 if (ip == NULL) {
314 return "Smoothie ready";
315 } else {
316 return ip;
317 }
318 }
319
320 void WatchScreen::set_speed()
321 {
322 send_gcode("M220", 'S', this->current_speed);
323 }
324
325 const char *WatchScreen::get_network()
326 {
327 void *returned_data;
328
329 bool ok = PublicData::get_value( network_checksum, get_ip_checksum, &returned_data );
330 if (ok) {
331 uint8_t *ipaddr = (uint8_t *)returned_data;
332 char buf[20];
333 int n = snprintf(buf, sizeof(buf), "IP %d.%d.%d.%d", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]);
334 buf[n] = 0;
335 if (this->ipstr == nullptr) {
336 this->ipstr = new char[n + 1];
337 }
338 strcpy(this->ipstr, buf);
339
340 return this->ipstr;
341 }
342
343 return NULL;
344 }