mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	Add better implementation of tcp console
This commit is contained in:
		
							parent
							
								
									11eafebd8f
								
							
						
					
					
						commit
						869b8c8c66
					
				
					 5 changed files with 303 additions and 51 deletions
				
			
		|  | @ -237,6 +237,8 @@ set(SLIC3R_GUI_SOURCES | |||
|     Utils/UndoRedo.hpp | ||||
|     Utils/HexFile.cpp | ||||
|     Utils/HexFile.hpp | ||||
|     Utils/TCPConsole.cpp | ||||
|     Utils/TCPConsole.hpp | ||||
|     Utils/MKS.cpp | ||||
|     Utils/MKS.hpp | ||||
| ) | ||||
|  |  | |||
|  | @ -32,14 +32,21 @@ namespace pt = boost::property_tree; | |||
| namespace Slic3r { | ||||
| 
 | ||||
| MKS::MKS(DynamicPrintConfig *config) : | ||||
| 	host(config->opt_string("print_host")), console_port(8080) | ||||
| 	host(config->opt_string("print_host")), console(config->opt_string("print_host"), "8080") | ||||
| {} | ||||
| 
 | ||||
| const char* MKS::get_name() const { return "MKS"; } | ||||
| 
 | ||||
| bool MKS::test(wxString &msg) const | ||||
| { | ||||
| 	return run_simple_gcode("M105", msg); | ||||
|   console.enqueue_cmd("M105"); | ||||
|   bool ret = console.run_queue(); | ||||
| 
 | ||||
|   if (!ret) { | ||||
|     msg = console.error_message(); | ||||
|   } | ||||
| 
 | ||||
|   return ret; | ||||
| } | ||||
| 
 | ||||
| wxString MKS::get_test_ok_msg () const | ||||
|  | @ -100,15 +107,7 @@ bool MKS::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn er | |||
| 		.perform_sync(); | ||||
| 
 | ||||
| 	if (res && upload_data.start_print) { | ||||
| 		// For some reason printer firmware does not want to respond on gcode commands immediately after file upload.
 | ||||
| 		// So we just introduce artificial delay to workaround it.
 | ||||
| 		std::this_thread::sleep_for(std::chrono::milliseconds(1500)); | ||||
| 
 | ||||
| 		wxString msg; | ||||
| 		res &= run_simple_gcode(std::string("M23 ") + upload_data.upload_path.string(), msg); | ||||
| 		if (res) { | ||||
| 			res &= run_simple_gcode(std::string("M24"), msg); | ||||
| 		} | ||||
|     start_print(upload_data.upload_path); | ||||
| 	} | ||||
| 
 | ||||
| 	return res; | ||||
|  | @ -123,8 +122,21 @@ std::string MKS::get_upload_url(const std::string &filename) const | |||
| 
 | ||||
| bool MKS::start_print(wxString &msg, const std::string &filename) const | ||||
| { | ||||
| 	BOOST_LOG_TRIVIAL(warning) << boost::format("MKS: start_print is not implemented yet, called stub"); | ||||
| 		return true; | ||||
|   // For some reason printer firmware does not want to respond on gcode commands immediately after file upload.
 | ||||
|   // So we just introduce artificial delay to workaround it.
 | ||||
|   // TODO: Inspect reasons
 | ||||
|   std::this_thread::sleep_for(std::chrono::milliseconds(1500)); | ||||
| 
 | ||||
|   console.enqueue_cmd("M23 " + upload_data.upload_path.string()); | ||||
|   console.enqueue_cmd("M24"); | ||||
| 
 | ||||
|   bool ret = console.run_queue(); | ||||
| 
 | ||||
|   if (!ret) { | ||||
|     msg = console.error_message(); | ||||
|   } | ||||
| 
 | ||||
|   return ret; | ||||
| } | ||||
| 
 | ||||
| int MKS::get_err_code_from_body(const std::string& body) const | ||||
|  | @ -136,38 +148,4 @@ int MKS::get_err_code_from_body(const std::string& body) const | |||
| 	return root.get<int>("err", 0); | ||||
| } | ||||
| 
 | ||||
| bool MKS::run_simple_gcode(const std::string &cmd, wxString &msg) const | ||||
| { | ||||
| 	using boost::asio::ip::tcp; | ||||
| 
 | ||||
| 	try | ||||
| 	{ | ||||
| 		boost::asio::io_context io_context; | ||||
| 		tcp::socket s(io_context); | ||||
| 
 | ||||
| 		tcp::resolver resolver(io_context); | ||||
| 		boost::asio::connect(s, resolver.resolve(host, std::to_string(console_port))); | ||||
| 		boost::asio::write(s, boost::asio::buffer(cmd + "\r\n")); | ||||
| 
 | ||||
| 		msg = "request:" + cmd + "\r\n"; | ||||
| 		 | ||||
| 		boost::asio::streambuf input_buffer; | ||||
| 		size_t reply_length = boost::asio::read_until(s, input_buffer, '\n'); | ||||
| 
 | ||||
| 		std::string response((std::istreambuf_iterator<char>(&input_buffer)), std::istreambuf_iterator<char>()); | ||||
| 		if (response.length() == 0) { | ||||
| 			msg += "Empty response"; | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		msg += "response:" + response; | ||||
| 		return true; | ||||
| 	} | ||||
| 	catch (std::exception& e) | ||||
| 	{ | ||||
| 		msg = std::string("exception:") + e.what(); | ||||
| 		return false; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| } | ||||
| } // Slic3r
 | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| #include <wx/string.h> | ||||
| 
 | ||||
| #include "PrintHost.hpp" | ||||
| #include "TCPConsole.hpp" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
|  | @ -27,16 +28,15 @@ public: | |||
| 	bool can_test() const override { return true; } | ||||
| 	bool can_start_print() const override { return true; } | ||||
| 	std::string get_host() const override { return host; } | ||||
|     | ||||
| 
 | ||||
| private: | ||||
| 	std::string host; | ||||
| 	int console_port; | ||||
|     Utils::TCPConsole console; | ||||
| 
 | ||||
| 	std::string get_upload_url(const std::string &filename) const; | ||||
| 	std::string timestamp_str() const; | ||||
| 	bool start_print(wxString &msg, const std::string &filename) const; | ||||
| 	int get_err_code_from_body(const std::string &body) const; | ||||
| 	bool run_simple_gcode(const std::string& cmd, wxString& msg) const; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
							
								
								
									
										191
									
								
								src/slic3r/Utils/TCPConsole.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								src/slic3r/Utils/TCPConsole.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,191 @@ | |||
| #include <boost/asio/buffer.hpp> | ||||
| #include <boost/asio/io_context.hpp> | ||||
| #include <boost/asio/ip/tcp.hpp> | ||||
| #include <boost/asio/read_until.hpp> | ||||
| #include <boost/asio/steady_timer.hpp> | ||||
| #include <boost/asio/write.hpp> | ||||
| #include <boost/bind.hpp> | ||||
| #include <boost/format.hpp> | ||||
| #include <boost/log/trivial.hpp> | ||||
| #include <boost/algorithm/string.hpp> | ||||
| 
 | ||||
| #include <iostream> | ||||
| #include <string> | ||||
| 
 | ||||
| #include <TCPConsole.hpp> | ||||
| 
 | ||||
| using boost::asio::steady_timer; | ||||
| using boost::asio::ip::tcp; | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| namespace Utils { | ||||
| 
 | ||||
| void TCPConsole::transmit_next_command() | ||||
| { | ||||
|   if (cmd_queue_.empty()) { | ||||
|     io_context_.stop(); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   std::string cmd = cmd_queue_.front(); | ||||
|   cmd_queue_.pop_front(); | ||||
| 
 | ||||
|   BOOST_LOG_TRIVIAL(debug) << boost::format("TCPConsole: transmitting '%3%' to %1%:%2%") | ||||
|         % host_name_ | ||||
|         % port_name_ | ||||
|         % cmd; | ||||
| 
 | ||||
|   auto data = boost::asio::buffer(cmd + newline_); | ||||
| 
 | ||||
|   boost::asio::async_write( | ||||
|     socket_, | ||||
|     data, | ||||
|     boost::bind(&TCPConsole::handle_write, this, _1, _2) | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| void TCPConsole::wait_next_line() | ||||
| { | ||||
|   boost::asio::async_read_until( | ||||
|     socket_, | ||||
|     recv_buffer_, | ||||
|     newline_, | ||||
|     boost::bind(&TCPConsole::handle_read, this, _1, _2) | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| // TODO: Use std::optional here
 | ||||
| std::string TCPConsole::extract_next_line() | ||||
| { | ||||
|   char linebuf[1024]; | ||||
| 
 | ||||
|   std::istream is(&recv_buffer_); | ||||
|   is.getline(linebuf, sizeof(linebuf)); | ||||
|   if (is.good()) { | ||||
|     return linebuf; | ||||
|   } | ||||
| 
 | ||||
|   return ""; | ||||
| } | ||||
| 
 | ||||
| void TCPConsole::handle_read( | ||||
|     const boost::system::error_code& ec, | ||||
|     std::size_t bytes_transferred) | ||||
| { | ||||
|     error_code_ = ec; | ||||
| 
 | ||||
|   if (ec) { | ||||
|     BOOST_LOG_TRIVIAL(error) << boost::format("TCPConsole: Can't read from %1%:%2%: %3%") | ||||
|       % host_name_ | ||||
|       % port_name_ | ||||
|       % ec.message(); | ||||
| 
 | ||||
|       io_context_.stop(); | ||||
|   } else { | ||||
|     std::string line = extract_next_line(); | ||||
|     boost::trim(line); | ||||
| 
 | ||||
|     BOOST_LOG_TRIVIAL(debug) << boost::format("TCPConsole: received '%3%' from %1%:%2%") | ||||
|       % host_name_ | ||||
|       % port_name_ | ||||
|       % line; | ||||
| 
 | ||||
|     boost::to_lower(line); | ||||
| 
 | ||||
|     if (line == done_string_) { | ||||
|       transmit_next_command(); | ||||
|     } else { | ||||
|       wait_next_line(); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void TCPConsole::handle_write( | ||||
|     const boost::system::error_code& ec, | ||||
|     std::size_t) | ||||
| { | ||||
|   error_code_ = ec; | ||||
|   if (ec) { | ||||
|     BOOST_LOG_TRIVIAL(error) << boost::format("TCPConsole: Can't write to %1%:%2%: %3%") | ||||
|       % host_name_ | ||||
|       % port_name_ | ||||
|       % ec.message(); | ||||
| 
 | ||||
|       io_context_.stop(); | ||||
|   } else { | ||||
|     wait_next_line(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void TCPConsole::handle_connect(const boost::system::error_code& ec) | ||||
| { | ||||
|     error_code_ = ec; | ||||
| 
 | ||||
|     if (ec) { | ||||
|     	BOOST_LOG_TRIVIAL(error) << boost::format("TCPConsole: Can't connect to %1%:%2%: %3%") | ||||
|         % host_name_ | ||||
|         % port_name_ | ||||
|         % ec.message(); | ||||
| 
 | ||||
|       io_context_.stop(); | ||||
|     } else { | ||||
|     	BOOST_LOG_TRIVIAL(info) << boost::format("TCPConsole: connected to %1%:%2%") | ||||
|         % host_name_ | ||||
|         % port_name_; | ||||
| 
 | ||||
| 
 | ||||
|       transmit_next_command(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool TCPConsole::run_queue() | ||||
| { | ||||
|   try { | ||||
|     // TODO: Add more resets and initializations after previous run
 | ||||
| 
 | ||||
|     auto endpoints = resolver_.resolve(host_name_, port_name_); | ||||
| 
 | ||||
|     socket_.async_connect(endpoints->endpoint(), | ||||
|       boost::bind(&TCPConsole::handle_connect, this, _1) | ||||
|     ); | ||||
| 
 | ||||
|     // TODO: Add error and timeout processing
 | ||||
|     io_context_.restart(); | ||||
|     while (!io_context_.stopped()) { | ||||
|       BOOST_LOG_TRIVIAL(debug) << ".\n"; | ||||
|         if (error_code_) { | ||||
|           io_context_.stop(); | ||||
|         } | ||||
|         io_context_.run_for(boost::asio::chrono::milliseconds(100)); | ||||
|     } | ||||
| 
 | ||||
|     // Socket is not closed automatically by boost
 | ||||
|     socket_.close(); | ||||
| 
 | ||||
|     if (error_code_) { | ||||
|       // We expect that message is logged in handler
 | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|     // It's expected to have empty queue after successful exchange
 | ||||
|     if (!cmd_queue_.empty()) { | ||||
|       BOOST_LOG_TRIVIAL(error) << "TCPConsole: command queue is not empty after end of exchange"; | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|   catch (std::exception& e) | ||||
|   { | ||||
|     BOOST_LOG_TRIVIAL(error) << boost::format("TCPConsole: Exception while talking with %1%:%2%: %3%") | ||||
|           % host_name_ | ||||
|           % port_name_ | ||||
|           % e.what(); | ||||
| 
 | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| } | ||||
							
								
								
									
										81
									
								
								src/slic3r/Utils/TCPConsole.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/slic3r/Utils/TCPConsole.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,81 @@ | |||
| #ifndef slic3r_Utils_TCPConsole_hpp_ | ||||
| #define slic3r_Utils_TCPConsole_hpp_ | ||||
| 
 | ||||
| #include <string> | ||||
| #include <list> | ||||
| #include <boost/system/error_code.hpp> | ||||
| #include <boost/asio/ip/tcp.hpp> | ||||
| #include <boost/asio/streambuf.hpp> | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| namespace Utils { | ||||
| 
 | ||||
| const char * default_newline = "\n"; | ||||
| const char * default_done_string = "ok"; | ||||
| 
 | ||||
| using boost::asio::ip::tcp; | ||||
| 
 | ||||
| class TCPConsole | ||||
| { | ||||
| public: | ||||
|   TCPConsole(): resolver_(io_context_), socket_(io_context_), newline_(default_newline), done_string_(default_done_string) {} | ||||
| 
 | ||||
|   TCPConsole(const std::string &host_name, const std::string &port_name): | ||||
|     resolver_(io_context_), socket_(io_context_), newline_(default_newline), done_string_(default_done_string) | ||||
|   { | ||||
|     set_remote(host_name, port_name); | ||||
|   } | ||||
|   ~TCPConsole(){} | ||||
| 
 | ||||
|   void set_line_delimiter(const std::string &newline) { | ||||
|     newline_ = newline; | ||||
|   } | ||||
|   void set_command_done_string(const std::string &done_string) { | ||||
|     done_string_ = done_string; | ||||
|   } | ||||
| 
 | ||||
|   void set_remote(const std::string &host_name, const std::string &port_name) | ||||
|   { | ||||
|     host_name_ = host_name; | ||||
|     port_name_ = port_name; | ||||
|   } | ||||
| 
 | ||||
|   bool enqueue_cmd(const std::string &cmd) { | ||||
|     // TODO: Add multithread protection to queue
 | ||||
|     cmd_queue_.push_back(cmd); | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   bool run_queue(); | ||||
|   std::string error_message() { | ||||
|     return error_code_.message(); | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   void handle_connect(const boost::system::error_code& ec); | ||||
|   void handle_read(const boost::system::error_code& ec, std::size_t bytes_transferred); | ||||
|   void handle_write(const boost::system::error_code& ec, std::size_t bytes_transferred); | ||||
| 
 | ||||
|   void transmit_next_command(); | ||||
|   void wait_next_line(); | ||||
|   std::string extract_next_line(); | ||||
| 
 | ||||
|   std::string host_name_; | ||||
|   std::string port_name_; | ||||
|   std::string newline_; | ||||
|   std::string done_string_; | ||||
| 
 | ||||
|   std::list<std::string> cmd_queue_; | ||||
| 
 | ||||
|   boost::asio::io_context io_context_; | ||||
|   tcp::resolver resolver_; | ||||
|   tcp::socket socket_; | ||||
|   boost::asio::streambuf recv_buffer_; | ||||
| 
 | ||||
|   boost::system::error_code error_code_; | ||||
| }; | ||||
| 
 | ||||
| } // Utils
 | ||||
| } // Slic3r
 | ||||
| 
 | ||||
| #endif | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Sergey Kovalev
						Sergey Kovalev