refactor get public data, to be slightly easier to use on callee side
[clinton/Smoothieware.git] / src / modules / utils / panel / screens / WatchScreen.cpp
CommitLineData
3105a7be 1/*
35089dc7
JM
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.
3105a7be 5 You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>.
35089dc7
JM
6*/
7
8#include "libs/Kernel.h"
383c9c1c 9#include "LcdBase.h"
35089dc7
JM
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"
564cf1f0 17#include "Robot.h"
691dcad3 18#include "modules/robot/Conveyor.h"
35089dc7 19#include "modules/utils/player/PlayerPublicAccess.h"
d4ee6ee2 20#include "NetworkPublicAccess.h"
61134a65 21#include "PublicData.h"
f1aac8df 22#include "SwitchPublicAccess.h"
61134a65
JM
23#include "checksumm.h"
24#include "Pauser.h"
02e4b295
JM
25#include "TemperatureControlPool.h"
26
27
61134a65
JM
28#include <math.h>
29#include <string.h>
35089dc7 30#include <string>
383c9c1c 31#include <stdio.h>
373d0bf1 32#include <algorithm>
61134a65 33
35089dc7 34using namespace std;
ebc64506 35static const uint8_t icons[] = { // 115x19 - 3 bytes each: he1, he2, he3, bed, fan
862fc625
JM
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
ebc64506 55};
35089dc7 56
862fc625
JM
57WatchScreen::WatchScreen()
58{
59 speed_changed = false;
60 issue_change_speed = false;
383c9c1c 61 ipstr = nullptr;
c77d6dae 62 update_counts= 0;
383c9c1c
JM
63}
64
65WatchScreen::~WatchScreen()
66{
67 delete[] ipstr;
dcf86322 68}
35089dc7 69
862fc625
JM
70void WatchScreen::on_enter()
71{
cee1bb2d
JM
72 THEPANEL->lcd->clear();
73 THEPANEL->setup_menu(4);
c77d6dae 74 get_current_status();
58d6d841
JM
75 get_current_pos(this->pos);
76 get_sd_play_info();
862fc625 77 this->current_speed = lround(get_current_speed());
58d6d841 78 this->refresh_screen(false);
cee1bb2d
JM
79 THEPANEL->enter_control_mode(1, 0.5);
80 THEPANEL->set_control_value(this->current_speed);
56a6c8c1
JM
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
93static 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;
35089dc7
JM
98}
99
862fc625
JM
100void WatchScreen::on_refresh()
101{
3105a7be 102 // Exit if the button is clicked
cee1bb2d
JM
103 if ( THEPANEL->click() ) {
104 THEPANEL->enter_screen(this->parent);
35089dc7 105 return;
58d6d841
JM
106 }
107
108 // see if speed is being changed
cee1bb2d
JM
109 if (THEPANEL->control_value_change()) {
110 this->current_speed = THEPANEL->get_control_value();
862fc625
JM
111 if (this->current_speed < 10) {
112 this->current_speed = 10;
cee1bb2d
JM
113 THEPANEL->set_control_value(this->current_speed);
114 THEPANEL->reset_counter();
862fc625 115 } else {
3f038f58
JM
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
862fc625 118 this->speed_changed = true; // flag indicating speed changed
7a522ccc
JM
119 this->refresh_screen(false);
120 }
58d6d841 121 }
3105a7be 122
35089dc7 123 // Update Only every 20 refreshes, 1 a second
35089dc7 124 update_counts++;
862fc625 125 if ( update_counts % 20 == 0 ) {
58d6d841
JM
126 get_sd_play_info();
127 get_current_pos(this->pos);
c77d6dae 128 get_current_status();
862fc625
JM
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
3f038f58 133 // read it in case it was changed via M220
862fc625 134 this->current_speed = lround(get_current_speed());
cee1bb2d
JM
135 THEPANEL->set_control_value(this->current_speed);
136 THEPANEL->reset_counter();
3f038f58 137 }
7a522ccc 138
cee1bb2d 139 this->refresh_screen(THEPANEL->lcd->hasGraphics() ? true : false); // graphics screens should be cleared
91c45256
JM
140
141 // for LCDs with leds set them according to heater status
c77d6dae 142 bool bed_on= false, hotend_on= false, is_hot= false;
226d08ee 143 uint8_t heon=0, hemsk= 0x01; // bit set for which hotend is on bit0: hotend1, bit1: hotend2 etc
56a6c8c1
JM
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;
226d08ee 152 }
56a6c8c1 153 hemsk <<= 1;
c77d6dae
JM
154 }
155 }
56a6c8c1 156
c77d6dae
JM
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
cee1bb2d 161 THEPANEL->lcd->setLed(LED_FAN_ON, this->fan_state);
ebc64506 162
cee1bb2d 163 if (THEPANEL->lcd->hasGraphics()) {
ebc64506 164 // display the graphical icons below the status are
cee1bb2d 165 //THEPANEL->lcd->bltGlyph(0, 34, 115, 19, icons);
ebc64506 166 // for (int i = 0; i < 5; ++i) {
cee1bb2d 167 // THEPANEL->lcd->bltGlyph(i*24, 38, 23, 19, icons, 15, i*24, 0);
ebc64506 168 // }
226d08ee
JM
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);
ebc64506 172
c77d6dae 173 if (bed_on)
226d08ee 174 THEPANEL->lcd->bltGlyph(60, 38, 23, 19, icons, 15, 64, 0);
3105a7be 175
f1aac8df 176 if(this->fan_state)
cee1bb2d 177 THEPANEL->lcd->bltGlyph(96, 38, 23, 19, icons, 15, 96, 0);
ebc64506 178 }
35089dc7
JM
179 }
180}
181
dcf86322 182// queuing gcodes needs to be done from main loop
862fc625
JM
183void WatchScreen::on_main_loop()
184{
185 if (this->issue_change_speed) {
186 this->issue_change_speed = false;
3f038f58
JM
187 set_speed();
188 }
f15e6f4b 189 PanelScreen::on_main_loop(); // in case any queued commands left
dcf86322
JM
190}
191
c77d6dae
JM
192// fetch the data we are displaying
193void WatchScreen::get_current_status()
194{
f1aac8df 195 // get fan status
1ae7e276
JM
196 struct pad_switch s;
197 bool ok = PublicData::get_value( switch_checksum, fan_checksum, 0, &s );
f1aac8df 198 if (ok) {
f1aac8df
JM
199 this->fan_state = s.state;
200 } else {
201 // fan probably disabled
202 this->fan_state = false;
203 }
35089dc7
JM
204}
205
206// fetch the data we are displaying
1ad23cd3 207float WatchScreen::get_current_speed()
862fc625 208{
564cf1f0
JM
209 // in percent
210 return 6000.0F / THEKERNEL->robot->get_seconds_per_minute();
35089dc7
JM
211}
212
1ad23cd3 213void WatchScreen::get_current_pos(float *cp)
862fc625 214{
564cf1f0 215 THEKERNEL->robot->get_axis_position(cp);
35089dc7
JM
216}
217
862fc625
JM
218void WatchScreen::get_sd_play_info()
219{
58d6d841 220 void *returned_data;
75e6428d 221 bool ok = PublicData::get_value( player_checksum, get_progress_checksum, &returned_data );
862fc625
JM
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;
cee1bb2d 226 THEPANEL->set_playing_file(p.filename);
4c8afa75 227
862fc625
JM
228 } else {
229 this->elapsed_time = 0;
230 this->sd_pcnt_played = 0;
58d6d841 231 }
35089dc7
JM
232}
233
862fc625
JM
234void WatchScreen::display_menu_line(uint16_t line)
235{
58d6d841 236 // in menu mode
862fc625 237 switch ( line ) {
e0437082 238 case 0:
02e4b295 239 {
bab4e1bd 240 auto& tm= this->temp_controllers;
02e4b295 241 if(tm.size() > 0) {
e0437082 242 // only if we detected heaters in config
c77d6dae
JM
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;
3bfb2639
JM
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);
c77d6dae 258 THEPANEL->lcd->setCursor(off, 0); // col, row
3bfb2639 259 off += THEPANEL->lcd->printf("%s:%03d/%03d ", temp.designator.substr(0, 2).c_str(), t, tt);
c77d6dae
JM
260 }
261
e0437082
JM
262 }else{
263 //THEPANEL->lcd->printf("No Heaters");
264 }
265 break;
02e4b295 266 }
cee1bb2d
JM
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;
58d6d841 270 }
35089dc7 271}
84267f43 272
862fc625
JM
273const char *WatchScreen::get_status()
274{
5f1a896b 275 if (THEPANEL->hasMessage())
cee1bb2d 276 return THEPANEL->getMessage().c_str();
399cb110 277
5f1a896b 278 if (THEPANEL->is_halted())
76217df5 279 return "HALTED Reset or M999";
76217df5 280
862fc625 281 if (THEKERNEL->pauser->paused())
84267f43 282 return "Paused";
3105a7be 283
5f1a896b
JM
284 if (THEPANEL->is_suspended())
285 return "Suspended";
286
cee1bb2d
JM
287 if (THEPANEL->is_playing())
288 return THEPANEL->get_playing_file();
84267f43 289
5f1a896b 290 if (!THEKERNEL->conveyor->is_queue_empty())
691dcad3 291 return "Printing";
691dcad3 292
d4ee6ee2
JM
293 const char *ip = get_network();
294 if (ip == NULL) {
295 return "Smoothie ready";
296 } else {
297 return ip;
298 }
84267f43 299}
dcf86322 300
862fc625
JM
301void WatchScreen::set_speed()
302{
2fa50ca0 303 send_gcode("M220", 'S', this->current_speed);
dcf86322 304}
d4ee6ee2
JM
305
306const char *WatchScreen::get_network()
307{
308 void *returned_data;
309
75e6428d 310 bool ok = PublicData::get_value( network_checksum, get_ip_checksum, &returned_data );
d4ee6ee2
JM
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;
383c9c1c
JM
316 if (this->ipstr == nullptr) {
317 this->ipstr = new char[n + 1];
d4ee6ee2
JM
318 }
319 strcpy(this->ipstr, buf);
320
321 return this->ipstr;
322 }
323
324 return NULL;
325}