refactor get public data, to be slightly easier to use on callee side
[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 "Robot.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 WatchScreen::WatchScreen()
58 {
59 speed_changed = false;
60 issue_change_speed = false;
61 ipstr = nullptr;
62 update_counts= 0;
63 }
64
65 WatchScreen::~WatchScreen()
66 {
67 delete[] ipstr;
68 }
69
70 void WatchScreen::on_enter()
71 {
72 THEPANEL->lcd->clear();
73 THEPANEL->setup_menu(4);
74 get_current_status();
75 get_current_pos(this->pos);
76 get_sd_play_info();
77 this->current_speed = lround(get_current_speed());
78 this->refresh_screen(false);
79 THEPANEL->enter_control_mode(1, 0.5);
80 THEPANEL->set_control_value(this->current_speed);
81
82 // enumerate temperature controls
83 temp_controllers.clear();
84 std::vector<struct pad_temperature> controllers;
85 bool ok = PublicData::get_value(temperature_control_checksum, poll_controls_checksum, &controllers);
86 if (ok) {
87 for (auto &c : controllers) {
88 temp_controllers.push_back(c.id);
89 }
90 }
91 }
92
93 static struct pad_temperature getTemperatures(uint16_t heater_cs)
94 {
95 struct pad_temperature temp;
96 PublicData::get_value( temperature_control_checksum, current_temperature_checksum, heater_cs, &temp );
97 return temp;
98 }
99
100 void WatchScreen::on_refresh()
101 {
102 // Exit if the button is clicked
103 if ( THEPANEL->click() ) {
104 THEPANEL->enter_screen(this->parent);
105 return;
106 }
107
108 // see if speed is being changed
109 if (THEPANEL->control_value_change()) {
110 this->current_speed = THEPANEL->get_control_value();
111 if (this->current_speed < 10) {
112 this->current_speed = 10;
113 THEPANEL->set_control_value(this->current_speed);
114 THEPANEL->reset_counter();
115 } else {
116 // flag the update to change the speed, we don't want to issue hundreds of M220s
117 // but we do want to display the change we are going to make
118 this->speed_changed = true; // flag indicating speed changed
119 this->refresh_screen(false);
120 }
121 }
122
123 // Update Only every 20 refreshes, 1 a second
124 update_counts++;
125 if ( update_counts % 20 == 0 ) {
126 get_sd_play_info();
127 get_current_pos(this->pos);
128 get_current_status();
129 if (this->speed_changed) {
130 this->issue_change_speed = true; // trigger actual command to change speed
131 this->speed_changed = false;
132 } else if (!this->issue_change_speed) { // change still queued
133 // read it in case it was changed via M220
134 this->current_speed = lround(get_current_speed());
135 THEPANEL->set_control_value(this->current_speed);
136 THEPANEL->reset_counter();
137 }
138
139 this->refresh_screen(THEPANEL->lcd->hasGraphics() ? true : false); // graphics screens should be cleared
140
141 // for LCDs with leds set them according to heater status
142 bool bed_on= false, hotend_on= false, is_hot= false;
143 uint8_t heon=0, hemsk= 0x01; // bit set for which hotend is on bit0: hotend1, bit1: hotend2 etc
144 for(auto id : temp_controllers) {
145 struct pad_temperature c= getTemperatures(id);
146 if(c.current_temperature > 50) is_hot= true; // anything is hot
147 if(c.designator.front() == 'B' && c.target_temperature > 0) bed_on= true; // bed on/off
148 if(c.designator.front() == 'T') { // a hotend by convention
149 if(c.target_temperature > 0){
150 hotend_on= true;// hotend on/off (anyone)
151 heon |= hemsk;
152 }
153 hemsk <<= 1;
154 }
155 }
156
157 THEPANEL->lcd->setLed(LED_BED_ON, bed_on);
158 THEPANEL->lcd->setLed(LED_HOTEND_ON, hotend_on);
159 THEPANEL->lcd->setLed(LED_HOT, is_hot);
160
161 THEPANEL->lcd->setLed(LED_FAN_ON, this->fan_state);
162
163 if (THEPANEL->lcd->hasGraphics()) {
164 // display the graphical icons below the status are
165 //THEPANEL->lcd->bltGlyph(0, 34, 115, 19, icons);
166 // for (int i = 0; i < 5; ++i) {
167 // THEPANEL->lcd->bltGlyph(i*24, 38, 23, 19, icons, 15, i*24, 0);
168 // }
169 if(heon&0x01) THEPANEL->lcd->bltGlyph(0, 38, 20, 19, icons, 15, 0, 0);
170 if(heon&0x02) THEPANEL->lcd->bltGlyph(20, 38, 20, 19, icons, 15, 24, 0);
171 if(heon&0x04) THEPANEL->lcd->bltGlyph(40, 38, 20, 19, icons, 15, 48, 0);
172
173 if (bed_on)
174 THEPANEL->lcd->bltGlyph(60, 38, 23, 19, icons, 15, 64, 0);
175
176 if(this->fan_state)
177 THEPANEL->lcd->bltGlyph(96, 38, 23, 19, icons, 15, 96, 0);
178 }
179 }
180 }
181
182 // queuing gcodes needs to be done from main loop
183 void WatchScreen::on_main_loop()
184 {
185 if (this->issue_change_speed) {
186 this->issue_change_speed = false;
187 set_speed();
188 }
189 PanelScreen::on_main_loop(); // in case any queued commands left
190 }
191
192 // fetch the data we are displaying
193 void WatchScreen::get_current_status()
194 {
195 // get fan status
196 struct pad_switch s;
197 bool ok = PublicData::get_value( switch_checksum, fan_checksum, 0, &s );
198 if (ok) {
199 this->fan_state = s.state;
200 } else {
201 // fan probably disabled
202 this->fan_state = false;
203 }
204 }
205
206 // fetch the data we are displaying
207 float WatchScreen::get_current_speed()
208 {
209 // in percent
210 return 6000.0F / THEKERNEL->robot->get_seconds_per_minute();
211 }
212
213 void WatchScreen::get_current_pos(float *cp)
214 {
215 THEKERNEL->robot->get_axis_position(cp);
216 }
217
218 void WatchScreen::get_sd_play_info()
219 {
220 void *returned_data;
221 bool ok = PublicData::get_value( player_checksum, get_progress_checksum, &returned_data );
222 if (ok) {
223 struct pad_progress p = *static_cast<struct pad_progress *>(returned_data);
224 this->elapsed_time = p.elapsed_secs;
225 this->sd_pcnt_played = p.percent_complete;
226 THEPANEL->set_playing_file(p.filename);
227
228 } else {
229 this->elapsed_time = 0;
230 this->sd_pcnt_played = 0;
231 }
232 }
233
234 void WatchScreen::display_menu_line(uint16_t line)
235 {
236 // in menu mode
237 switch ( line ) {
238 case 0:
239 {
240 auto& tm= this->temp_controllers;
241 if(tm.size() > 0) {
242 // only if we detected heaters in config
243 int n= 0;
244 if(tm.size() > 2) {
245 // more than two temps we need to cycle between them
246 n= update_counts/100; // increments every 5 seconds
247 int ntemps= (tm.size()+1)/2;
248 n= n%ntemps; // which of the pairs of temps to display
249 }
250
251 int off= 0;
252 for (size_t i = 0; i < 2; ++i) {
253 size_t o= i+(n*2);
254 if(o>tm.size()-1) break;
255 struct pad_temperature temp= getTemperatures(tm[o]);
256 int t= std::min(999, (int)roundf(temp.current_temperature));
257 int tt= roundf(temp.target_temperature);
258 THEPANEL->lcd->setCursor(off, 0); // col, row
259 off += THEPANEL->lcd->printf("%s:%03d/%03d ", temp.designator.substr(0, 2).c_str(), t, tt);
260 }
261
262 }else{
263 //THEPANEL->lcd->printf("No Heaters");
264 }
265 break;
266 }
267 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;
268 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;
269 case 3: THEPANEL->lcd->printf("%19s", this->get_status()); break;
270 }
271 }
272
273 const char *WatchScreen::get_status()
274 {
275 if (THEPANEL->hasMessage())
276 return THEPANEL->getMessage().c_str();
277
278 if (THEPANEL->is_halted())
279 return "HALTED Reset or M999";
280
281 if (THEKERNEL->pauser->paused())
282 return "Paused";
283
284 if (THEPANEL->is_suspended())
285 return "Suspended";
286
287 if (THEPANEL->is_playing())
288 return THEPANEL->get_playing_file();
289
290 if (!THEKERNEL->conveyor->is_queue_empty())
291 return "Printing";
292
293 const char *ip = get_network();
294 if (ip == NULL) {
295 return "Smoothie ready";
296 } else {
297 return ip;
298 }
299 }
300
301 void WatchScreen::set_speed()
302 {
303 send_gcode("M220", 'S', this->current_speed);
304 }
305
306 const char *WatchScreen::get_network()
307 {
308 void *returned_data;
309
310 bool ok = PublicData::get_value( network_checksum, get_ip_checksum, &returned_data );
311 if (ok) {
312 uint8_t *ipaddr = (uint8_t *)returned_data;
313 char buf[20];
314 int n = snprintf(buf, sizeof(buf), "IP %d.%d.%d.%d", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]);
315 buf[n] = 0;
316 if (this->ipstr == nullptr) {
317 this->ipstr = new char[n + 1];
318 }
319 strcpy(this->ipstr, buf);
320
321 return this->ipstr;
322 }
323
324 return NULL;
325 }