Lots of work on status representation

This commit is contained in:
2023-04-28 14:50:58 +03:00
parent 796fd57ac3
commit 30ccb20846
8 changed files with 430 additions and 457 deletions
+114 -420
View File
@@ -58,36 +58,76 @@ using namespace nanogui;
grbl::machine cnc{};
class SenderApp : public Screen {
class SenderApp : public Screen, public grbl::machine_listener {
public:
Window *window;
Window *left_window;
Window *right_window;
grbl::jog_state jog;
Label *m_pos_x, *m_pos_y, *m_pos_z;
TextBox *lblStatus, *lblSubstatus;
nanogui::Color red = nanogui::Color(255, 0, 0, 255);
int last_alarm = 0;
SenderApp() : Screen(Vector2i(1024, 768), "GRBL Sender") {
inc_ref();
window = new Window(this, "Button demo");
window = new Window(this, "Machine status");
// window->set_fixed_height(Screen::size().y());
window->set_position(Vector2i(0, 0));
window->set_layout(new GridLayout(nanogui::Orientation::Horizontal, 2, Alignment::Fill, 0, 0));
window->set_size(Screen::size());
window->set_layout(new GroupLayout());
// window->set_size(Screen::size());
left_window = new Window(window, "Left sidebar");
left_window->set_modal(true);
new Label(window, "Status", "sans-bold");
Widget *status_holder = new Widget(window);
status_holder->set_layout(new GridLayout());
right_window = new Window(window, "Right sidebar");
right_window->set_modal(true);
lblStatus = new TextBox(window, grbl::status_to_string(cnc.get_status().status));
lblSubstatus = new TextBox(window, cnc.get_status().sub_status);
left_window->set_layout(new GroupLayout());
// Machine pos
new Label(window, "Machine pos", "sans-bold");
Widget *mpos = new Widget(window);
mpos->set_layout(new GridLayout());
new Label(mpos, "X");
m_pos_x = new Label(mpos, std::to_string(cnc.get_status().machine_pos[0]));
new Label(mpos, "Y");
m_pos_y = new Label(mpos, std::to_string(cnc.get_status().machine_pos[1]));
new Label(mpos, "Z");
m_pos_z = new Label(mpos, std::to_string(cnc.get_status().machine_pos[2]));
// buttons to change state
new Label(window, "Actions", "sans-bold");
Widget *actions = new Widget(window);
actions->set_layout(new BoxLayout(Orientation::Horizontal));
Button *btnUnlock = new Button(actions, "Unlock");
btnUnlock->set_callback([&] {
cnc.request_unlock();
});
Button *btnHome = new Button(actions, "Home");
btnHome->set_callback([&] {
cnc.request_home();
});
Button *btnReset = new Button(actions, "Reset");
btnReset->set_background_color(red);
btnReset->set_callback([&] {
cnc.request_reset();
});
Button *btnCycleStart = new Button(actions, "Cycle Start");
btnCycleStart->set_callback([&] {
cnc.request_cycle_start();
});
Button *btnFeedHold = new Button(actions, "Feed Hold");
btnFeedHold->set_callback([&] {
cnc.request_feed_hold();
});
// No need to store a pointer, the data structure will be automatically
// freed when the parent window is deleted
new Label(left_window, "Push buttons", "sans-bold");
new Label(window, "Program", "sans-bold");
Button *b = new Button(left_window, "Plain button");
b->set_callback([&] {
Button *btnLoadProgram = new Button(window, "Load");
btnLoadProgram->set_callback([&] {
auto path = file_dialog(
{{"nc", "G-Code files"},
{"ngc", "G-Code files"}}, false);
@@ -96,38 +136,38 @@ public:
cnc.run_program(pgm);
}
});
b->set_tooltip("short tooltip");
btnLoadProgram->set_tooltip("short tooltip");
/* Alternative construction notation using variadic template */
b = left_window->add<Button>("Styled", FA_ROCKET);
b->set_background_color(Color(0, 0, 255, 25));
b->set_callback([] { std::cout << "pushed!" << std::endl; });
b->set_tooltip("This button has a fairly long tooltip. It is so long, in "
"fact, that the shown text will span several lines.");
// Alternative construction notation using variadic template
btnLoadProgram = window->add<Button>("Styled", FA_ROCKET);
btnLoadProgram->set_background_color(Color(0, 0, 255, 25));
btnLoadProgram->set_callback([] { std::cout << "pushed!" << std::endl; });
btnLoadProgram->set_tooltip("This button has a fairly long tooltip. It is so long, in "
"fact, that the shown text will span several lines.");
new Label(left_window, "Toggle buttons", "sans-bold");
b = new Button(left_window, "Toggle me");
b->set_flags(Button::ToggleButton);
b->set_change_callback([](bool state) { std::cout << "Toggle button state: " << state << std::endl; });
new Label(window, "Toggle buttons", "sans-bold");
btnLoadProgram = new Button(window, "Toggle me");
btnLoadProgram->set_flags(Button::ToggleButton);
btnLoadProgram->set_change_callback([](bool state) { std::cout << "Toggle button state: " << state << std::endl; });
new Label(left_window, "Radio buttons", "sans-bold");
b = new Button(left_window, "Radio button 1");
b->set_flags(Button::RadioButton);
b = new Button(left_window, "Radio button 2");
b->set_flags(Button::RadioButton);
new Label(window, "Radio buttons", "sans-bold");
btnLoadProgram = new Button(window, "Radio button 1");
btnLoadProgram->set_flags(Button::RadioButton);
btnLoadProgram = new Button(window, "Radio button 2");
btnLoadProgram->set_flags(Button::RadioButton);
new Label(left_window, "A tool palette", "sans-bold");
Widget *tools = new Widget(left_window);
new Label(window, "A tool palette", "sans-bold");
Widget *tools = new Widget(window);
tools->set_layout(new BoxLayout(Orientation::Horizontal,
Alignment::Middle, 0, 6));
b = new ToolButton(tools, FA_CLOUD);
b = new ToolButton(tools, FA_FAST_FORWARD);
b = new ToolButton(tools, FA_COMPASS);
b = new ToolButton(tools, FA_UTENSILS);
btnLoadProgram = new ToolButton(tools, FA_CLOUD);
btnLoadProgram = new ToolButton(tools, FA_FAST_FORWARD);
btnLoadProgram = new ToolButton(tools, FA_COMPASS);
btnLoadProgram = new ToolButton(tools, FA_UTENSILS);
new Label(left_window, "Popup buttons", "sans-bold");
PopupButton *popup_btn = new PopupButton(left_window, "Popup", FA_FLASK);
new Label(window, "Popup buttons", "sans-bold");
PopupButton *popup_btn = new PopupButton(window, "Popup", FA_FLASK);
Popup *popup = popup_btn->popup();
popup->set_layout(new GroupLayout());
new Label(popup, "Arbitrary widgets can be placed here");
@@ -144,340 +184,6 @@ public:
popup_left->set_layout(new GroupLayout());
new CheckBox(popup_left, "Another check box");
/*
window = new Window(this, "Basic widgets");
window->set_position(Vector2i(200, 15));
window->set_layout(new GroupLayout());
new Label(window, "Message dialog", "sans-bold");
tools = new Widget(window);
tools->set_layout(new BoxLayout(Orientation::Horizontal,
Alignment::Middle, 0, 6));
b = new Button(tools, "Info");
b->set_callback([&] {
auto dlg = new MessageDialog(this, MessageDialog::Type::Information, "Title", "This is an information message");
dlg->set_callback([](int result) { std::cout << "Dialog result: " << result << std::endl; });
});
b = new Button(tools, "Warn");
b->set_callback([&] {
auto dlg = new MessageDialog(this, MessageDialog::Type::Warning, "Title", "This is a warning message");
dlg->set_callback([](int result) { std::cout << "Dialog result: " << result << std::endl; });
});
b = new Button(tools, "Ask");
b->set_callback([&] {
auto dlg = new MessageDialog(this, MessageDialog::Type::Question, "Title", "This is a question message", "Yes", "No", true);
dlg->set_callback([](int result) { std::cout << "Dialog result: " << result << std::endl; });
});
#if defined(_WIN32)
/// Executable is in the Debug/Release/.. subdirectory
std::string resources_folder_path("../icons");
#else
std::string resources_folder_path("./icons");
#endif
std::vector<std::pair<int, std::string>> icons;
#if !defined(EMSCRIPTEN)
try {
icons = load_image_directory(m_nvg_context, resources_folder_path);
} catch (const std::exception& e) {
std::cerr << "Warning: " << e.what() << std::endl;
}
#endif
new Label(window, "Image panel & scroll panel", "sans-bold");
PopupButton *image_panel_btn = new PopupButton(window, "Image Panel");
image_panel_btn->set_icon(FA_IMAGES);
popup = image_panel_btn->popup();
VScrollPanel *vscroll = new VScrollPanel(popup);
ImagePanel *img_panel = new ImagePanel(vscroll);
img_panel->set_images(icons);
popup->set_fixed_size(Vector2i(245, 150));
auto image_window = new Window(this, "Selected image");
image_window->set_position(Vector2i(710, 15));
image_window->set_layout(new GroupLayout(3));
// Create a Texture instance for each object
for (auto& icon: icons) {
Vector2i size;
int n = 0;
ImageHolder texture_data(
stbi_load((icon.second + ".png").c_str(), &size.x(), &size.y(), &n, 0),
stbi_image_free);
assert(n == 4);
Texture *tex = new Texture(
Texture::PixelFormat::RGBA,
Texture::ComponentFormat::UInt8,
size,
Texture::InterpolationMode::Trilinear,
Texture::InterpolationMode::Nearest);
tex->upload(texture_data.get());
m_images.emplace_back(tex, std::move(texture_data));
}
ImageView *image_view = new ImageView(image_window);
if (!m_images.empty())
image_view->set_image(m_images[0].first);
image_view->center();
m_current_image = 0;
img_panel->set_callback([this, image_view](int i) {
std::cout << "Selected item " << i << std::endl;
image_view->set_image(m_images[i].first);
m_current_image = i;
});
image_view->set_pixel_callback(
[this](const Vector2i& index, char **out, size_t size) {
const Texture *texture = m_images[m_current_image].first.get();
uint8_t *data = m_images[m_current_image].second.get();
for (int ch = 0; ch < 4; ++ch) {
uint8_t value = data[(index.x() + index.y() * texture->size().x()) * 4 + ch];
snprintf(out[ch], size, "%i", (int) value);
}
}
);
new Label(window, "File dialog", "sans-bold");
tools = new Widget(window);
tools->set_layout(new BoxLayout(Orientation::Horizontal,
Alignment::Middle, 0, 6));
b = new Button(tools, "Open");
b->set_callback([&] {
std::cout << "File dialog result: " << file_dialog(
{{"png", "Portable Network Graphics"},
{"txt", "Text file"}}, false) << std::endl;
});
b = new Button(tools, "Save");
b->set_callback([&] {
std::cout << "File dialog result: " << file_dialog(
{{"png", "Portable Network Graphics"},
{"txt", "Text file"}}, true) << std::endl;
});
new Label(window, "Combo box", "sans-bold");
new ComboBox(window, {"Combo box item 1", "Combo box item 2", "Combo box item 3"});
new Label(window, "Check box", "sans-bold");
CheckBox *cb = new CheckBox(window, "Flag 1",
[](bool state) { std::cout << "Check box 1 state: " << state << std::endl; }
);
cb->set_checked(true);
cb = new CheckBox(window, "Flag 2",
[](bool state) { std::cout << "Check box 2 state: " << state << std::endl; }
);
new Label(window, "Progress bar", "sans-bold");
m_progress = new ProgressBar(window);
new Label(window, "Slider and text box", "sans-bold");
Widget *panel = new Widget(window);
panel->set_layout(new BoxLayout(Orientation::Horizontal,
Alignment::Middle, 0, 20));
Slider *slider = new Slider(panel);
slider->set_value(0.5f);
slider->set_fixed_width(80);
TextBox *text_box = new TextBox(panel);
text_box->set_fixed_size(Vector2i(60, 25));
text_box->set_value("50");
text_box->set_units("%");
slider->set_callback([text_box](float value) {
text_box->set_value(std::to_string((int) (value * 100)));
});
slider->set_final_callback([&](float value) {
std::cout << "Final slider value: " << (int) (value * 100) << std::endl;
});
text_box->set_fixed_size(Vector2i(60, 25));
text_box->set_font_size(20);
text_box->set_alignment(TextBox::Alignment::Right);
window = new Window(this, "Misc. widgets");
window->set_position(Vector2i(425, 15));
window->set_layout(new GroupLayout());
TabWidget *tab_widget = window->add<TabWidget>();
Widget *layer = new Widget(tab_widget);
layer->set_layout(new GroupLayout());
tab_widget->append_tab("Color Wheel", layer);
// Use overloaded variadic add to fill the tab widget with Different tabs.
layer->add<Label>("Color wheel widget", "sans-bold");
layer->add<ColorWheel>();
layer = new Widget(tab_widget);
layer->set_layout(new GroupLayout());
tab_widget->append_tab("Function Graph", layer);
layer->add<Label>("Function graph widget", "sans-bold");
Graph *graph = layer->add<Graph>("Some Function");
graph->set_header("E = 2.35e-3");
graph->set_footer("Iteration 89");
std::vector<float>& func = graph->values();
func.resize(100);
for (int i = 0; i < 100; ++i)
func[i] = 0.5f * (0.5f * std::sin(i / 10.f) +
0.5f * std::cos(i / 23.f) + 1);
// Dummy tab used to represent the last tab button.
int plus_id = tab_widget->append_tab("+", new Widget(tab_widget));
// A simple counter.
int counter = 1;
tab_widget->set_callback([tab_widget, this, counter, plus_id](int id) mutable {
if (id == plus_id) {
// When the "+" tab has been clicked, simply add a new tab.
std::string tab_name = "Dynamic " + std::to_string(counter);
Widget *layer_dyn = new Widget(tab_widget);
int new_id = tab_widget->insert_tab(tab_widget->tab_count() - 1,
tab_name, layer_dyn);
layer_dyn->set_layout(new GroupLayout());
layer_dyn->add<Label>("Function graph widget", "sans-bold");
Graph *graph_dyn = layer_dyn->add<Graph>("Dynamic function");
graph_dyn->set_header("E = 2.35e-3");
graph_dyn->set_footer("Iteration " + std::to_string(new_id * counter));
std::vector<float>& func_dyn = graph_dyn->values();
func_dyn.resize(100);
for (int i = 0; i < 100; ++i)
func_dyn[i] = 0.5f *
std::abs((0.5f * std::sin(i / 10.f + counter) +
0.5f * std::cos(i / 23.f + 1 + counter)));
++counter;
tab_widget->set_selected_id(new_id);
// We must invoke the layout manager after adding tabs dynamically
perform_layout();
}
});
// A button to go back to the first tab and scroll the window.
panel = window->add<Widget>();
panel->add<Label>("Jump to tab: ");
panel->set_layout(new BoxLayout(Orientation::Horizontal,
Alignment::Middle, 0, 6));
auto ib = panel->add<IntBox<int>>();
ib->set_editable(true);
b = panel->add<Button>("", FA_FORWARD);
b->set_fixed_size(Vector2i(22, 22));
ib->set_fixed_height(22);
b->set_callback([tab_widget, ib] {
int value = ib->value();
if (value >= 0 && value < tab_widget->tab_count())
tab_widget->set_selected_index(value);
});
window = new Window(this, "Grid of small widgets");
window->set_position(Vector2i(425, 300));
GridLayout *layout =
new GridLayout(Orientation::Horizontal, 2,
Alignment::Middle, 15, 5);
layout->set_col_alignment(
{Alignment::Maximum, Alignment::Fill});
layout->set_spacing(0, 10);
window->set_layout(layout);
// FP widget
{
new Label(window, "Floating point :", "sans-bold");
text_box = new TextBox(window);
text_box->set_editable(true);
text_box->set_fixed_size(Vector2i(100, 20));
text_box->set_value("50");
text_box->set_units("GiB");
text_box->set_default_value("0.0");
text_box->set_font_size(16);
text_box->set_format("[-]?[0-9]*\\.?[0-9]+");
}
// Positive integer widget
{
new Label(window, "Positive integer :", "sans-bold");
auto int_box = new IntBox<int>(window);
int_box->set_editable(true);
int_box->set_fixed_size(Vector2i(100, 20));
int_box->set_value(50);
int_box->set_units("Mhz");
int_box->set_default_value("0");
int_box->set_font_size(16);
int_box->set_format("[1-9][0-9]*");
int_box->set_spinnable(true);
int_box->set_min_value(1);
int_box->set_value_increment(2);
}
// Checkbox widget
{
new Label(window, "Checkbox :", "sans-bold");
cb = new CheckBox(window, "Check me");
cb->set_font_size(16);
cb->set_checked(true);
}
new Label(window, "Combo box :", "sans-bold");
ComboBox *cobo =
new ComboBox(window, {"Item 1", "Item 2", "Item 3"});
cobo->set_font_size(16);
cobo->set_fixed_size(Vector2i(100, 20));
new Label(window, "Color picker :", "sans-bold");
auto cp = new ColorPicker(window, {255, 120, 0, 255});
cp->set_fixed_size({100, 20});
cp->set_final_callback([](const Color& c) {
std::cout << "ColorPicker final callback: ["
<< c.r() << ", "
<< c.g() << ", "
<< c.b() << ", "
<< c.w() << "]" << std::endl;
});
// setup a fast callback for the color picker widget on a new window
// for demonstrative purposes
window = new Window(this, "Color Picker Fast Callback");
layout = new GridLayout(Orientation::Horizontal, 2,
Alignment::Middle, 15, 5);
layout->set_col_alignment(
{Alignment::Maximum, Alignment::Fill});
layout->set_spacing(0, 10);
window->set_layout(layout);
window->set_position(Vector2i(425, 500));
new Label(window, "Combined: ");
b = new Button(window, "ColorWheel", FA_INFINITY);
new Label(window, "Red: ");
auto red_int_box = new IntBox<int>(window);
red_int_box->set_editable(false);
new Label(window, "Green: ");
auto green_int_box = new IntBox<int>(window);
green_int_box->set_editable(false);
new Label(window, "Blue: ");
auto blue_int_box = new IntBox<int>(window);
blue_int_box->set_editable(false);
new Label(window, "Alpha: ");
auto alpha_int_box = new IntBox<int>(window);
cp->set_callback([b, red_int_box, blue_int_box, green_int_box, alpha_int_box](const Color& c) {
b->set_background_color(c);
b->set_text_color(c.contrasting_color());
int red = (int) (c.r() * 255.0f);
red_int_box->set_value(red);
int green = (int) (c.g() * 255.0f);
green_int_box->set_value(green);
int blue = (int) (c.b() * 255.0f);
blue_int_box->set_value(blue);
int alpha = (int) (c.w() * 255.0f);
alpha_int_box->set_value(alpha);
});
*/
perform_layout();
// All NanoGUI widgets are initialized at this point. Now
@@ -567,13 +273,41 @@ public:
}
bool resize_event(const Vector2i& size) override {
window->set_size(size);
Screen::resize_event(size);
return true;
return Screen::resize_event(size);
}
void on_connected() override {
}
void on_disconnected() override {
}
void on_realtime_status_report(grbl::realtime_status_report report) override {
lblStatus->set_value(grbl::status_to_string(cnc.get_status().status) + " ");
if (cnc.get_status().status == grbl::machine_status::alarm) {
lblSubstatus->set_value(grbl::alarm_to_string(last_alarm));
} else {
lblSubstatus->set_value(cnc.get_status().sub_status);
}
m_pos_x->set_caption(std::to_string(cnc.get_status().machine_pos[0]));
m_pos_y->set_caption(std::to_string(cnc.get_status().machine_pos[1]));
m_pos_z->set_caption(std::to_string(cnc.get_status().machine_pos[2]));
}
void on_banner(std::string line) override {
}
void on_message(std::string message) override {
set_caption(message);
}
void on_alarm(int alarm) override {
last_alarm = alarm;
}
virtual bool keyboard_event(int key, int scancode, int action, int modifiers) {
bool keyboard_event(int key, int scancode, int action, int modifiers) override {
if (Screen::keyboard_event(key, scancode, action, modifiers))
return true;
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
@@ -691,59 +425,15 @@ private:
};
struct grbl_listener : public grbl::transport_callbacks {
void on_connected(grbl::transport *t) override {
std::cout << "Listener: connected!" << std::endl;
// telnet handshake so that we get the banner. banner won't be coming otherwise
// t->send("\xff\xfd\x18\xff\xfd\x20\xff\xfd\x23\xff\xfd\x27");
t->send("\xff\xfd\x18");
}
void on_disconnected(grbl::transport *t) override {
std::cout << "Listener: disconnected!" << std::endl;
}
void on_banner(std::string version, grbl::transport *t) override {
std::cout << "Banner: " << version << std::endl;
t->send("$$");
}
void on_line_received(std::string line, grbl::transport *t) override {
std::cout << "Listener: -> " << line << std::endl;
}
};
int main(int argc, char **argv) {
// grbl_listener listener;
//
// grbl::tcp_transport transport("192.168.5.39", 23);
// transport.open(listener);
testing::InitGoogleTest(&argc, argv);
auto result = RUN_ALL_TESTS();
if (result) {
exit(result);
}
cnc.connect();
// grbl::program pgm{"./program.nc"};
//// pgm.dump(std::cout);
//
// cnc.run_program(pgm);
// transport.request_realtime_report();
// transport.request_cycle_start();
// transport.request_feed_hold();
// transport.parser_state_report();
try {
// grbl::program pgm;
// pgm.load("./program.nc");
// pgm.dump(std::cout);
nanogui::init();
// scoped variables
@@ -752,6 +442,10 @@ int main(int argc, char **argv) {
app->dec_ref();
app->draw_all();
app->set_visible(true);
cnc.set_listener(app);
cnc.connect();
nanogui::mainloop(1 / 60.f * 1000);
}