Lots of work on status representation
This commit is contained in:
+1
-1
@@ -23,5 +23,5 @@ set(CMAKE_CXX_STANDARD 17)
|
|||||||
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
||||||
|
|
||||||
|
|
||||||
add_executable(sender main.cpp grbl.h grbl.cpp grbl_test.cpp grbl_communication.h grbl_communication.cpp machine.h grbl_machine.cpp)
|
add_executable(sender main.cpp grbl.h grbl.cpp grbl_test.cpp grbl_communication.h grbl_communication.cpp machine.h grbl_machine.cpp string_utils.h)
|
||||||
target_link_libraries(sender nanogui gtest gtest_main)
|
target_link_libraries(sender nanogui gtest gtest_main)
|
||||||
+17
-6
@@ -74,6 +74,8 @@ void grbl::tcp_transport::worker() {
|
|||||||
std::string received;
|
std::string received;
|
||||||
uint8_t buffer[200];
|
uint8_t buffer[200];
|
||||||
|
|
||||||
|
auto last_report = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
while (!should_quit) {
|
while (!should_quit) {
|
||||||
if (is_connected) {
|
if (is_connected) {
|
||||||
// anything to write?
|
// anything to write?
|
||||||
@@ -103,12 +105,12 @@ void grbl::tcp_transport::worker() {
|
|||||||
if (is_eol) {
|
if (is_eol) {
|
||||||
if (!is_empty_line(received)) {
|
if (!is_empty_line(received)) {
|
||||||
// starts with? https://stackoverflow.com/questions/1878001/how-do-i-check-if-a-c-stdstring-starts-with-a-certain-string-and-convert-a
|
// starts with? https://stackoverflow.com/questions/1878001/how-do-i-check-if-a-c-stdstring-starts-with-a-certain-string-and-convert-a
|
||||||
if (received.rfind("Grbl", 0) == 0) {
|
// if (received.rfind("Grbl", 0) == 0) {
|
||||||
listener->on_banner(received, this);
|
// listener->on_banner(received, this);
|
||||||
|
//
|
||||||
} else {
|
// } else {
|
||||||
listener->on_line_received(received, this);
|
listener->on_line_received(received, this);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
received.clear();
|
received.clear();
|
||||||
} else {
|
} else {
|
||||||
@@ -117,6 +119,15 @@ void grbl::tcp_transport::worker() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
auto ms_since_last_report = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_report);
|
||||||
|
|
||||||
|
// maintain a 5Hz rate
|
||||||
|
if (ms_since_last_report.count() >= 200) {
|
||||||
|
request_realtime_report();
|
||||||
|
last_report = now;
|
||||||
|
}
|
||||||
|
|
||||||
// give some time to others
|
// give some time to others
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ struct transport_callbacks {
|
|||||||
virtual void on_connected(transport *) = 0;
|
virtual void on_connected(transport *) = 0;
|
||||||
virtual void on_disconnected(transport *) = 0;
|
virtual void on_disconnected(transport *) = 0;
|
||||||
virtual void on_line_received(std::string line, transport *) = 0;
|
virtual void on_line_received(std::string line, transport *) = 0;
|
||||||
virtual void on_banner(std::string version, transport *) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct transport {
|
struct transport {
|
||||||
|
virtual ~transport() = default;
|
||||||
virtual void open(transport_callbacks& cb) = 0;
|
virtual void open(transport_callbacks& cb) = 0;
|
||||||
virtual void close() = 0;
|
virtual void close() = 0;
|
||||||
virtual void send(std::string line) = 0;
|
virtual void send(std::string line) = 0;
|
||||||
|
|||||||
+165
-13
@@ -1,6 +1,11 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include "machine.h"
|
#include "machine.h"
|
||||||
|
#include "string_utils.h"
|
||||||
|
|
||||||
|
static bool starts_with(const std::string& line, const std::string& prefix) {
|
||||||
|
return line.rfind(prefix, 0) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
grbl::machine::machine() {
|
grbl::machine::machine() {
|
||||||
pipe = new tcp_transport("192.168.5.39", 23);
|
pipe = new tcp_transport("192.168.5.39", 23);
|
||||||
@@ -14,7 +19,7 @@ void grbl::machine::connect() {
|
|||||||
pipe->open(*this);
|
pipe->open(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void grbl::machine::run_program(grbl::program pgm) {
|
void grbl::machine::run_program(const grbl::program& pgm) {
|
||||||
std::cout << "running program with " << pgm.number_of_instructions() << " instructions" << std::endl;
|
std::cout << "running program with " << pgm.number_of_instructions() << " instructions" << std::endl;
|
||||||
running_program = pgm;
|
running_program = pgm;
|
||||||
state = grbl_machine_state::run_program;
|
state = grbl_machine_state::run_program;
|
||||||
@@ -35,23 +40,148 @@ void grbl::machine::on_disconnected(grbl::transport *transport) {
|
|||||||
std::cout << "grbl machine disconnected" << std::endl;
|
std::cout << "grbl machine disconnected" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void grbl::machine::on_line_received(std::string line, grbl::transport *transport) {
|
|
||||||
std::cout << ">> " << line << std::endl;
|
|
||||||
|
|
||||||
if (state == grbl_machine_state::run_program) {
|
grbl::realtime_status_report grbl::parse_status_report(std::string line, grbl::realtime_status_report& result) {
|
||||||
if (line.rfind("ok", 0) == 0) {
|
// grbl::realtime_status_report result;
|
||||||
continue_program();
|
|
||||||
} else if (line.rfind("error", 0) == 0) {
|
auto l = line.substr(1, -1);
|
||||||
std::cerr << "Received error" << std::endl;
|
auto pieces = split_string(l, "|");
|
||||||
|
for (auto i = 0; i < pieces.size(); i++) {
|
||||||
|
if (i == 0) {
|
||||||
|
// status
|
||||||
|
auto elements = split_string(pieces[i], ":");
|
||||||
|
result.status = status_from_string(elements[0]);
|
||||||
|
result.sub_status = elements.size() > 1 ? elements[1] : "";
|
||||||
} else {
|
} else {
|
||||||
|
auto elements = split_string(pieces[i], ":");
|
||||||
|
if (elements[0] == "WPos") {
|
||||||
|
auto axis = split_string(elements[1], ",");
|
||||||
|
result.work_pos[0] = std::stof(axis[0]);
|
||||||
|
result.work_pos[1] = std::stof(axis[1]);
|
||||||
|
result.work_pos[2] = std::stof(axis[2]);
|
||||||
|
} else if (elements[0] == "MPos") {
|
||||||
|
auto axis = split_string(elements[1], ",");
|
||||||
|
result.machine_pos[0] = std::stof(axis[0]);
|
||||||
|
result.machine_pos[1] = std::stof(axis[1]);
|
||||||
|
result.machine_pos[2] = std::stof(axis[2]);
|
||||||
|
} else if (elements[0] == "Bf") {
|
||||||
|
auto p = split_string(elements[1], ",");
|
||||||
|
result.buffers_free = std::stoi(p[0]);
|
||||||
|
result.rx_chars_free = std::stoi(p[1]);
|
||||||
|
} else {
|
||||||
|
// not implemented
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// evaluate responses when not running a program
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
grbl::machine_status grbl::status_from_string(const std::string& status) {
|
||||||
|
if (status == "Idle") return machine_status::idle;
|
||||||
|
if (status == "Run") return machine_status::run;
|
||||||
|
if (status == "Hold") return machine_status::hold;
|
||||||
|
if (status == "Jog") return machine_status::jog;
|
||||||
|
if (status == "Alarm") return machine_status::alarm;
|
||||||
|
if (status == "Door") return machine_status::door;
|
||||||
|
if (status == "Check") return machine_status::check;
|
||||||
|
if (status == "Home") return machine_status::home;
|
||||||
|
if (status == "Sleep") return machine_status::sleep;
|
||||||
|
if (status == "Tool") return machine_status::tool;
|
||||||
|
return machine_status::unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string grbl::status_to_string(const grbl::machine_status& status) {
|
||||||
|
switch (status) {
|
||||||
|
case machine_status::idle:
|
||||||
|
return "Idle";
|
||||||
|
case machine_status::run:
|
||||||
|
return "Run";
|
||||||
|
case machine_status::hold:
|
||||||
|
return "Hold";
|
||||||
|
case machine_status::jog:
|
||||||
|
return "Jog";
|
||||||
|
case machine_status::alarm:
|
||||||
|
return "Alarm";
|
||||||
|
case machine_status::door:
|
||||||
|
return "Door";
|
||||||
|
case machine_status::check:
|
||||||
|
return "Check";
|
||||||
|
case machine_status::home:
|
||||||
|
return "Home";
|
||||||
|
case machine_status::sleep:
|
||||||
|
return "Sleep";
|
||||||
|
case machine_status::tool:
|
||||||
|
return "Tool";
|
||||||
|
case machine_status::unknown:
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void grbl::machine::on_banner(std::string version, grbl::transport *transport) {
|
std::string grbl::alarm_to_string(int alarm) {
|
||||||
std::cout << "grbl machine received banner" << std::endl;
|
switch (alarm) {
|
||||||
|
case 1:
|
||||||
|
return "Hard limit has been triggered. Machine position is likely lost due to sudden halt. Re-homing is highly recommended.";
|
||||||
|
case 2:
|
||||||
|
return "Soft limit alarm. G-code motion target exceeds machine travel. Machine position retained. Alarm may be safely unlocked.";
|
||||||
|
case 3:
|
||||||
|
return "Reset while in motion. Machine position is likely lost due to sudden halt. Re-homing is highly recommended.";
|
||||||
|
case 4:
|
||||||
|
return "Probe fail. Probe is not in the expected initial state before starting probe cycle when G38.2 and G38.3 is not triggered and G38.4 and G38.5 is triggered.";
|
||||||
|
case 5:
|
||||||
|
return "Probe fail. Probe did not contact the workpiece within the programmed travel for G38.2 and G38.4.";
|
||||||
|
case 6:
|
||||||
|
return "Homing fail. The active homing cycle was reset.";
|
||||||
|
case 7:
|
||||||
|
return "Homing fail. Safety door was opened during homing cycle.";
|
||||||
|
case 8:
|
||||||
|
return "Homing fail. Pull off travel failed to clear limit switch. Try increasing pull-off setting or check wiring.";
|
||||||
|
case 9:
|
||||||
|
return "Homing fail. Could not find limit switch within search distances. Try increasing max travel, decreasing pull-off distance, or check wiring.";
|
||||||
|
case 10:
|
||||||
|
return "Homing fail. Second dual axis limit switch failed to trigger within configured search distance after first. Try increasing trigger fail distance or check wiring.";
|
||||||
|
default:
|
||||||
|
return "unknown alarm code";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void grbl::machine::on_line_received(std::string line, grbl::transport *transport) {
|
||||||
|
if (line.at(0) != '<')
|
||||||
|
std::cout << ">> " << line << std::endl;
|
||||||
|
|
||||||
|
if (starts_with(line, "ok")) {
|
||||||
|
// on_ok();
|
||||||
|
if (state == grbl_machine_state::run_program) {
|
||||||
|
continue_program();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (starts_with(line, "error")) {
|
||||||
|
size_t error = std::stoi(line.substr(6));
|
||||||
|
// on_error(error);
|
||||||
|
} else {
|
||||||
|
// we have a push message
|
||||||
|
if (starts_with(line, "Grbl")) {
|
||||||
|
listener->on_banner(line);
|
||||||
|
} else if (starts_with(line, "<")) {
|
||||||
|
last_report = parse_status_report(line, last_report);
|
||||||
|
listener->on_realtime_status_report(last_report);
|
||||||
|
} else if (starts_with(line, "[MSG:")) {
|
||||||
|
listener->on_message(line.substr(5, line.size() - 6));
|
||||||
|
} else if (starts_with(line, "ALARM:")) {
|
||||||
|
listener->on_alarm(std::stoi(line.substr(6)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (state == grbl_machine_state::run_program) {
|
||||||
|
// if (line.rfind("ok", 0) == 0) {
|
||||||
|
// continue_program();
|
||||||
|
// } else if (line.rfind("error", 0) == 0) {
|
||||||
|
// std::cerr << "Received error" << std::endl;
|
||||||
|
// } else {
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// // evaluate responses when not running a program
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
void grbl::machine::continue_program() {
|
void grbl::machine::continue_program() {
|
||||||
@@ -72,9 +202,9 @@ void grbl::machine::continue_program() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void grbl::machine::request_jog(jog_state jog) const {
|
void grbl::machine::request_jog(jog_state jog) const {
|
||||||
|
|
||||||
cancel_jog();
|
cancel_jog();
|
||||||
if (jog.no_jogging()) {
|
if (jog.no_jogging()) {
|
||||||
// cancel_jog();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,8 +239,30 @@ void grbl::machine::request_jog(jog_state jog) const {
|
|||||||
|
|
||||||
void grbl::machine::cancel_jog() const {
|
void grbl::machine::cancel_jog() const {
|
||||||
pipe->send_single_char_command(0x85);
|
pipe->send_single_char_command(0x85);
|
||||||
|
}
|
||||||
|
|
||||||
|
void grbl::machine::set_listener(grbl::machine_listener *listener) {
|
||||||
|
machine::listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
void grbl::machine::request_unlock() {
|
||||||
|
pipe->send("$X");
|
||||||
|
}
|
||||||
|
|
||||||
|
void grbl::machine::request_home() {
|
||||||
|
pipe->send("$H");
|
||||||
|
}
|
||||||
|
|
||||||
|
void grbl::machine::request_reset() {
|
||||||
|
pipe->send_single_char_command(0x18);
|
||||||
|
}
|
||||||
|
|
||||||
|
void grbl::machine::request_cycle_start() {
|
||||||
|
pipe->send_single_char_command(0x81);
|
||||||
|
}
|
||||||
|
|
||||||
|
void grbl::machine::request_feed_hold() {
|
||||||
|
pipe->send_single_char_command(0x82);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool grbl::jog_state::no_jogging() const {
|
bool grbl::jog_state::no_jogging() const {
|
||||||
|
|||||||
+23
-1
@@ -1,6 +1,8 @@
|
|||||||
#include "grbl.h"
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "grbl.h"
|
||||||
|
#include "machine.h"
|
||||||
|
|
||||||
TEST(grbl_program, default_state) {
|
TEST(grbl_program, default_state) {
|
||||||
grbl::program pgm;
|
grbl::program pgm;
|
||||||
|
|
||||||
@@ -25,4 +27,24 @@ X3.87739 Y78.52820
|
|||||||
EXPECT_EQ("", pgm.filename);
|
EXPECT_EQ("", pgm.filename);
|
||||||
EXPECT_EQ(6, pgm.number_of_instructions());
|
EXPECT_EQ(6, pgm.number_of_instructions());
|
||||||
EXPECT_EQ(true, pgm.is_loaded);
|
EXPECT_EQ(true, pgm.is_loaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(grbl_status_report, parse) {
|
||||||
|
auto content = "<Idle|MPos:0.000,1.000,2.000|Bf:35,1022|FS:0,0|Pn:Z|Ov:100,100,100>";
|
||||||
|
grbl::realtime_status_report r;
|
||||||
|
grbl::parse_status_report(content, r);
|
||||||
|
|
||||||
|
EXPECT_EQ(grbl::machine_status::idle, r.status);
|
||||||
|
EXPECT_EQ("", r.sub_status);
|
||||||
|
|
||||||
|
EXPECT_EQ(0, r.machine_pos[0]);
|
||||||
|
EXPECT_EQ(1, r.machine_pos[1]);
|
||||||
|
EXPECT_EQ(2, r.machine_pos[2]);
|
||||||
|
|
||||||
|
EXPECT_EQ(35, r.buffers_free);
|
||||||
|
|
||||||
|
//
|
||||||
|
grbl::parse_status_report("<Home|MPos:0.000,0.000,0.000|Bf:35,1022|FS:0,0|Pn:Z|Ov:100,100,100>", r);
|
||||||
|
EXPECT_EQ(grbl::machine_status::home, r.status);
|
||||||
|
EXPECT_EQ(1022, r.rx_chars_free);
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,48 @@
|
|||||||
|
|
||||||
namespace grbl {
|
namespace grbl {
|
||||||
|
|
||||||
|
enum class machine_status {
|
||||||
|
idle,
|
||||||
|
run,
|
||||||
|
hold,
|
||||||
|
jog,
|
||||||
|
alarm,
|
||||||
|
door,
|
||||||
|
check,
|
||||||
|
home,
|
||||||
|
sleep,
|
||||||
|
tool,
|
||||||
|
unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
machine_status status_from_string(const std::string& status);
|
||||||
|
std::string status_to_string(const machine_status& status);
|
||||||
|
std::string alarm_to_string(int alarm);
|
||||||
|
|
||||||
|
struct realtime_status_report {
|
||||||
|
machine_status status;
|
||||||
|
std::string sub_status;
|
||||||
|
float work_pos[3] = {0};
|
||||||
|
float machine_pos[3] = {0};
|
||||||
|
size_t buffers_free = 0;
|
||||||
|
size_t rx_chars_free = 0;
|
||||||
|
size_t line_number = 0;
|
||||||
|
float feed_rate = 0;
|
||||||
|
float programmed_rpm = 0;
|
||||||
|
float actual_rpm = 0;
|
||||||
|
std::string signals;
|
||||||
|
float axis_offsets[3] = {0};
|
||||||
|
std::string coordinate_system;
|
||||||
|
std::string overrides;
|
||||||
|
std::string accessory_status;
|
||||||
|
bool mpg = false;
|
||||||
|
bool homing_complete = false;
|
||||||
|
std::string scaled_axis;
|
||||||
|
bool tool_length_reference_offset_set = false;
|
||||||
|
std::string firmware;
|
||||||
|
};
|
||||||
|
|
||||||
|
realtime_status_report parse_status_report(std::string line, grbl::realtime_status_report& result);
|
||||||
|
|
||||||
enum class grbl_machine_state {
|
enum class grbl_machine_state {
|
||||||
init,
|
init,
|
||||||
@@ -12,17 +54,6 @@ enum class grbl_machine_state {
|
|||||||
idle,
|
idle,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class jog_direction {
|
|
||||||
up,
|
|
||||||
down,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
up_left,
|
|
||||||
up_right,
|
|
||||||
down_left,
|
|
||||||
down_right
|
|
||||||
};
|
|
||||||
|
|
||||||
struct jog_state {
|
struct jog_state {
|
||||||
bool left_pressed = false;
|
bool left_pressed = false;
|
||||||
bool right_pressed = false;
|
bool right_pressed = false;
|
||||||
@@ -33,7 +64,7 @@ struct jog_state {
|
|||||||
bool speed_fast_pressed = false;
|
bool speed_fast_pressed = false;
|
||||||
bool speed_slow_pressed = false;
|
bool speed_slow_pressed = false;
|
||||||
|
|
||||||
bool no_jogging() const;
|
[[nodiscard]] bool no_jogging() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool operator==(const jog_state& a, const jog_state& b) {
|
static bool operator==(const jog_state& a, const jog_state& b) {
|
||||||
@@ -54,21 +85,41 @@ static bool operator!=(const jog_state& a, const jog_state& b) {
|
|||||||
return !(a == b);
|
return !(a == b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct machine_listener {
|
||||||
|
virtual void on_connected() = 0;
|
||||||
|
virtual void on_disconnected() = 0;
|
||||||
|
virtual void on_realtime_status_report(realtime_status_report) = 0;
|
||||||
|
virtual void on_banner(std::string line) = 0;
|
||||||
|
virtual void on_message(std::string message) = 0;
|
||||||
|
virtual void on_alarm(int alarm) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct machine : public transport_callbacks {
|
struct machine : public transport_callbacks {
|
||||||
machine();
|
machine();
|
||||||
~machine();
|
~machine();
|
||||||
|
|
||||||
|
void set_listener(grbl::machine_listener *listener);
|
||||||
void connect();
|
void connect();
|
||||||
void run_program(grbl::program pgm);
|
void run_program(const grbl::program& pgm);
|
||||||
void request_jog(jog_state jog) const;
|
void request_jog(jog_state jog) const;
|
||||||
void cancel_jog() const;
|
void cancel_jog() const;
|
||||||
|
|
||||||
|
realtime_status_report get_status() const { return last_report; };
|
||||||
|
|
||||||
|
void request_unlock();
|
||||||
|
void request_home();
|
||||||
|
void request_reset();
|
||||||
|
|
||||||
|
void request_cycle_start();
|
||||||
|
void request_feed_hold();
|
||||||
|
|
||||||
|
protected:
|
||||||
void on_connected(transport *transport) override;
|
void on_connected(transport *transport) override;
|
||||||
void on_disconnected(transport *transport) override;
|
void on_disconnected(transport *transport) override;
|
||||||
void on_line_received(std::string line, transport *transport) override;
|
void on_line_received(std::string line, transport *transport) override;
|
||||||
|
|
||||||
|
realtime_status_report last_report{};
|
||||||
void on_banner(std::string version, transport *transport) override;
|
machine_listener* listener = nullptr;
|
||||||
transport *pipe = nullptr;
|
transport *pipe = nullptr;
|
||||||
grbl_machine_state state = grbl_machine_state::init;
|
grbl_machine_state state = grbl_machine_state::init;
|
||||||
program running_program;
|
program running_program;
|
||||||
|
|||||||
@@ -58,36 +58,76 @@ using namespace nanogui;
|
|||||||
grbl::machine cnc{};
|
grbl::machine cnc{};
|
||||||
|
|
||||||
|
|
||||||
class SenderApp : public Screen {
|
class SenderApp : public Screen, public grbl::machine_listener {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Window *window;
|
Window *window;
|
||||||
Window *left_window;
|
|
||||||
Window *right_window;
|
|
||||||
grbl::jog_state jog;
|
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") {
|
SenderApp() : Screen(Vector2i(1024, 768), "GRBL Sender") {
|
||||||
inc_ref();
|
inc_ref();
|
||||||
window = new Window(this, "Button demo");
|
window = new Window(this, "Machine status");
|
||||||
// window->set_fixed_height(Screen::size().y());
|
// window->set_fixed_height(Screen::size().y());
|
||||||
window->set_position(Vector2i(0, 0));
|
window->set_position(Vector2i(0, 0));
|
||||||
window->set_layout(new GridLayout(nanogui::Orientation::Horizontal, 2, Alignment::Fill, 0, 0));
|
window->set_layout(new GroupLayout());
|
||||||
window->set_size(Screen::size());
|
// window->set_size(Screen::size());
|
||||||
|
|
||||||
left_window = new Window(window, "Left sidebar");
|
new Label(window, "Status", "sans-bold");
|
||||||
left_window->set_modal(true);
|
Widget *status_holder = new Widget(window);
|
||||||
|
status_holder->set_layout(new GridLayout());
|
||||||
|
|
||||||
right_window = new Window(window, "Right sidebar");
|
lblStatus = new TextBox(window, grbl::status_to_string(cnc.get_status().status));
|
||||||
right_window->set_modal(true);
|
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
|
// No need to store a pointer, the data structure will be automatically
|
||||||
// freed when the parent window is deleted
|
// 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");
|
Button *btnLoadProgram = new Button(window, "Load");
|
||||||
b->set_callback([&] {
|
btnLoadProgram->set_callback([&] {
|
||||||
auto path = file_dialog(
|
auto path = file_dialog(
|
||||||
{{"nc", "G-Code files"},
|
{{"nc", "G-Code files"},
|
||||||
{"ngc", "G-Code files"}}, false);
|
{"ngc", "G-Code files"}}, false);
|
||||||
@@ -96,38 +136,38 @@ public:
|
|||||||
cnc.run_program(pgm);
|
cnc.run_program(pgm);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
b->set_tooltip("short tooltip");
|
btnLoadProgram->set_tooltip("short tooltip");
|
||||||
|
|
||||||
/* Alternative construction notation using variadic template */
|
// Alternative construction notation using variadic template
|
||||||
b = left_window->add<Button>("Styled", FA_ROCKET);
|
btnLoadProgram = window->add<Button>("Styled", FA_ROCKET);
|
||||||
b->set_background_color(Color(0, 0, 255, 25));
|
btnLoadProgram->set_background_color(Color(0, 0, 255, 25));
|
||||||
b->set_callback([] { std::cout << "pushed!" << std::endl; });
|
btnLoadProgram->set_callback([] { std::cout << "pushed!" << std::endl; });
|
||||||
b->set_tooltip("This button has a fairly long tooltip. It is so long, in "
|
btnLoadProgram->set_tooltip("This button has a fairly long tooltip. It is so long, in "
|
||||||
"fact, that the shown text will span several lines.");
|
"fact, that the shown text will span several lines.");
|
||||||
|
|
||||||
new Label(left_window, "Toggle buttons", "sans-bold");
|
new Label(window, "Toggle buttons", "sans-bold");
|
||||||
b = new Button(left_window, "Toggle me");
|
btnLoadProgram = new Button(window, "Toggle me");
|
||||||
b->set_flags(Button::ToggleButton);
|
btnLoadProgram->set_flags(Button::ToggleButton);
|
||||||
b->set_change_callback([](bool state) { std::cout << "Toggle button state: " << state << std::endl; });
|
btnLoadProgram->set_change_callback([](bool state) { std::cout << "Toggle button state: " << state << std::endl; });
|
||||||
|
|
||||||
new Label(left_window, "Radio buttons", "sans-bold");
|
new Label(window, "Radio buttons", "sans-bold");
|
||||||
b = new Button(left_window, "Radio button 1");
|
btnLoadProgram = new Button(window, "Radio button 1");
|
||||||
b->set_flags(Button::RadioButton);
|
btnLoadProgram->set_flags(Button::RadioButton);
|
||||||
b = new Button(left_window, "Radio button 2");
|
btnLoadProgram = new Button(window, "Radio button 2");
|
||||||
b->set_flags(Button::RadioButton);
|
btnLoadProgram->set_flags(Button::RadioButton);
|
||||||
|
|
||||||
new Label(left_window, "A tool palette", "sans-bold");
|
new Label(window, "A tool palette", "sans-bold");
|
||||||
Widget *tools = new Widget(left_window);
|
Widget *tools = new Widget(window);
|
||||||
tools->set_layout(new BoxLayout(Orientation::Horizontal,
|
tools->set_layout(new BoxLayout(Orientation::Horizontal,
|
||||||
Alignment::Middle, 0, 6));
|
Alignment::Middle, 0, 6));
|
||||||
|
|
||||||
b = new ToolButton(tools, FA_CLOUD);
|
btnLoadProgram = new ToolButton(tools, FA_CLOUD);
|
||||||
b = new ToolButton(tools, FA_FAST_FORWARD);
|
btnLoadProgram = new ToolButton(tools, FA_FAST_FORWARD);
|
||||||
b = new ToolButton(tools, FA_COMPASS);
|
btnLoadProgram = new ToolButton(tools, FA_COMPASS);
|
||||||
b = new ToolButton(tools, FA_UTENSILS);
|
btnLoadProgram = new ToolButton(tools, FA_UTENSILS);
|
||||||
|
|
||||||
new Label(left_window, "Popup buttons", "sans-bold");
|
new Label(window, "Popup buttons", "sans-bold");
|
||||||
PopupButton *popup_btn = new PopupButton(left_window, "Popup", FA_FLASK);
|
PopupButton *popup_btn = new PopupButton(window, "Popup", FA_FLASK);
|
||||||
Popup *popup = popup_btn->popup();
|
Popup *popup = popup_btn->popup();
|
||||||
popup->set_layout(new GroupLayout());
|
popup->set_layout(new GroupLayout());
|
||||||
new Label(popup, "Arbitrary widgets can be placed here");
|
new Label(popup, "Arbitrary widgets can be placed here");
|
||||||
@@ -144,340 +184,6 @@ public:
|
|||||||
popup_left->set_layout(new GroupLayout());
|
popup_left->set_layout(new GroupLayout());
|
||||||
new CheckBox(popup_left, "Another check box");
|
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();
|
perform_layout();
|
||||||
|
|
||||||
// All NanoGUI widgets are initialized at this point. Now
|
// All NanoGUI widgets are initialized at this point. Now
|
||||||
@@ -567,13 +273,41 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool resize_event(const Vector2i& size) override {
|
bool resize_event(const Vector2i& size) override {
|
||||||
window->set_size(size);
|
return Screen::resize_event(size);
|
||||||
Screen::resize_event(size);
|
}
|
||||||
return true;
|
|
||||||
|
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))
|
if (Screen::keyboard_event(key, scancode, action, modifiers))
|
||||||
return true;
|
return true;
|
||||||
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
|
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) {
|
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);
|
testing::InitGoogleTest(&argc, argv);
|
||||||
auto result = RUN_ALL_TESTS();
|
auto result = RUN_ALL_TESTS();
|
||||||
if (result) {
|
if (result) {
|
||||||
exit(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 {
|
try {
|
||||||
|
|
||||||
// grbl::program pgm;
|
|
||||||
// pgm.load("./program.nc");
|
|
||||||
// pgm.dump(std::cout);
|
|
||||||
|
|
||||||
nanogui::init();
|
nanogui::init();
|
||||||
|
|
||||||
// scoped variables
|
// scoped variables
|
||||||
@@ -752,6 +442,10 @@ int main(int argc, char **argv) {
|
|||||||
app->dec_ref();
|
app->dec_ref();
|
||||||
app->draw_all();
|
app->draw_all();
|
||||||
app->set_visible(true);
|
app->set_visible(true);
|
||||||
|
|
||||||
|
cnc.set_listener(app);
|
||||||
|
cnc.connect();
|
||||||
|
|
||||||
nanogui::mainloop(1 / 60.f * 1000);
|
nanogui::mainloop(1 / 60.f * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
std::vector<std::string> split_string(const std::string& in, const std::string& delimiter) {
|
||||||
|
std::vector<std::string> result{};
|
||||||
|
size_t last{0}, next;
|
||||||
|
while ((next = in.find(delimiter, last)) != std::string::npos) {
|
||||||
|
result.push_back(in.substr(last, next - last));
|
||||||
|
last = next + delimiter.size();
|
||||||
|
}
|
||||||
|
result.push_back(in.substr(last));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string& ltrim(std::string& str, const std::string& chars = "\t\n\v\f\r ") {
|
||||||
|
str.erase(0, str.find_first_not_of(chars));
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ltrim(std::string&& str, const std::string& chars = "\t\n\v\f\r ") {
|
||||||
|
str.erase(0, str.find_first_not_of(chars));
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string& rtrim(std::string& str, const std::string& chars = "\t\n\v\f\r ") {
|
||||||
|
str.erase(str.find_last_not_of(chars) + 1);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string rtrim(std::string&& str, const std::string& chars = "\t\n\v\f\r ") {
|
||||||
|
str.erase(str.find_last_not_of(chars) + 1);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string& trim(std::string& str, const std::string& chars = "\t\n\v\f\r ") {
|
||||||
|
return ltrim(rtrim(str, chars), chars);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string trim(std::string&& str, const std::string& chars = "\t\n\v\f\r ") {
|
||||||
|
return ltrim(rtrim(str, chars), chars);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user