mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	NEW: add NetworkTestDialog
test www.bing.com test bambulab host test http service test storage upload and upgrade test storage downloading Change-Id: Ia2aca1d3fdff5dd4e930df138e8c6a1b5aad2585 Signed-off-by: Stone Li <stone.li@bambulab.com>
This commit is contained in:
		
							parent
							
								
									9c00469d7f
								
							
						
					
					
						commit
						63809c63da
					
				
					 6 changed files with 830 additions and 1 deletions
				
			
		|  | @ -72,6 +72,8 @@ set(SLIC3R_GUI_SOURCES | |||
|     GUI/Widgets/wxStaticText2.hpp | ||||
|     GUI/AboutDialog.cpp | ||||
|     GUI/AboutDialog.hpp | ||||
|     GUI/NetworkTestDialog.cpp | ||||
|     GUI/NetworkTestDialog.hpp | ||||
|     GUI/AuxiliaryDialog.cpp | ||||
|     GUI/AuxiliaryDialog.hpp | ||||
|     GUI/Auxiliary.cpp | ||||
|  |  | |||
|  | @ -35,6 +35,7 @@ void UpgradeNetworkJob::on_success(std::function<void()> success) | |||
| 
 | ||||
| void UpgradeNetworkJob::update_status(int st, const wxString &msg) | ||||
| { | ||||
|     BOOST_LOG_TRIVIAL(info) << "UpgradeNetworkJob: percent = " << st << "msg = " << msg; | ||||
|     GUI::Job::update_status(st, msg); | ||||
|     wxCommandEvent event(EVT_UPGRADE_UPDATE_MESSAGE); | ||||
|     event.SetString(msg); | ||||
|  | @ -59,6 +60,8 @@ void UpgradeNetworkJob::process() | |||
|     auto path_str = tmp_path.string() + wxString::Format(".%d%s", get_current_pid(), ".tmp").ToStdString(); | ||||
|     tmp_path = fs::path(path_str); | ||||
| 
 | ||||
|     BOOST_LOG_TRIVIAL(info) << "UpgradeNetworkJob: save netowrk_plugin to " << tmp_path.string(); | ||||
| 
 | ||||
|     auto cancel_fn = [this]() { | ||||
|         return was_canceled(); | ||||
|     }; | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ | |||
| #include "GUI_ObjectList.hpp" | ||||
| #include "NotificationManager.hpp" | ||||
| #include "MarkdownTip.hpp" | ||||
| #include "NetworkTestDialog.hpp" | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| #include <dbt.h> | ||||
|  | @ -1634,6 +1635,12 @@ static wxMenu* generate_help_menu() | |||
|     append_menu_item(helpMenu, wxID_ANY, about_title, about_title, | ||||
|             [](wxCommandEvent&) { Slic3r::GUI::about(); }); | ||||
| #endif | ||||
| 
 | ||||
|     append_menu_item(helpMenu, wxID_ANY, _L("Open Network Test"), _L("Open Network Test"), [](wxCommandEvent&) { | ||||
|             NetworkTestDialog dlg(wxGetApp().mainframe); | ||||
|             dlg.ShowModal(); | ||||
|         }); | ||||
| 
 | ||||
|     return helpMenu; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										702
									
								
								src/slic3r/GUI/NetworkTestDialog.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										702
									
								
								src/slic3r/GUI/NetworkTestDialog.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,702 @@ | |||
| #include "NetworkTestDialog.hpp" | ||||
| #include "I18N.hpp" | ||||
| 
 | ||||
| #include "libslic3r/Utils.hpp" | ||||
| #include "GUI.hpp" | ||||
| #include "GUI_App.hpp" | ||||
| #include "I18N.hpp" | ||||
| #include "slic3r/Utils/Http.hpp" | ||||
| #include "libslic3r/AppConfig.hpp" | ||||
| #include <boost/asio/ip/address.hpp> | ||||
| 
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| namespace GUI { | ||||
| 
 | ||||
| wxDECLARE_EVENT(EVT_UPDATE_RESULT, wxCommandEvent); | ||||
| 
 | ||||
| wxDEFINE_EVENT(EVT_UPDATE_RESULT, wxCommandEvent); | ||||
| 
 | ||||
| static wxString NA_STR = _L("N/A"); | ||||
| 
 | ||||
| NetworkTestDialog::NetworkTestDialog(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) | ||||
|     : DPIDialog(parent,wxID_ANY,from_u8((boost::format(_utf8(L("Network Test")))).str()),wxDefaultPosition, | ||||
|             wxSize(1000, 700), | ||||
|             /*wxCAPTION*/wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxRESIZE_BORDER) | ||||
| { | ||||
| 	this->SetSizeHints(wxDefaultSize, wxDefaultSize); | ||||
| 
 | ||||
| 	wxBoxSizer* main_sizer; | ||||
| 	main_sizer = new wxBoxSizer(wxVERTICAL); | ||||
| 
 | ||||
| 	wxBoxSizer* top_sizer = create_top_sizer(this); | ||||
| 	main_sizer->Add(top_sizer, 0, wxEXPAND, 5); | ||||
| 
 | ||||
| 	wxBoxSizer* info_sizer = create_info_sizer(this); | ||||
| 	main_sizer->Add(info_sizer, 0, wxEXPAND, 5); | ||||
| 
 | ||||
| 	wxBoxSizer* content_sizer = create_content_sizer(this); | ||||
| 	main_sizer->Add(content_sizer, 0, wxEXPAND, 5); | ||||
| 
 | ||||
| 	wxBoxSizer* result_sizer = create_result_sizer(this); | ||||
| 	main_sizer->Add(result_sizer, 1, wxEXPAND, 5); | ||||
| 
 | ||||
| 	set_default(); | ||||
| 
 | ||||
| 	init_bind(); | ||||
| 
 | ||||
| 	this->SetSizer(main_sizer); | ||||
| 	this->Layout(); | ||||
| 
 | ||||
| 	this->Centre(wxBOTH); | ||||
| } | ||||
| 
 | ||||
| wxBoxSizer* NetworkTestDialog::create_top_sizer(wxWindow* parent) | ||||
| { | ||||
| 	auto sizer = new wxBoxSizer(wxVERTICAL); | ||||
| 
 | ||||
| 	btn_start = new wxButton(this, wxID_ANY, _L("Start Test"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	sizer->Add(btn_start, 0, wxALL, 5); | ||||
| 
 | ||||
| 	btn_download_log = new wxButton(this, wxID_ANY, _L("Export Log"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	sizer->Add(btn_download_log, 0, wxALL, 5); | ||||
| 	btn_download_log->Hide(); | ||||
| 
 | ||||
| 	btn_start->Bind(wxEVT_BUTTON, [this](wxCommandEvent &evt) { | ||||
| 			start_all_job(); | ||||
| 		}); | ||||
| 	return sizer; | ||||
| } | ||||
| 
 | ||||
| wxBoxSizer* NetworkTestDialog::create_info_sizer(wxWindow* parent) | ||||
| { | ||||
| 	auto sizer = new wxBoxSizer(wxVERTICAL); | ||||
| 
 | ||||
| 	text_basic_info = new wxStaticText(this, wxID_ANY, _L("Basic Info"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	text_basic_info->Wrap(-1); | ||||
| 	sizer->Add(text_basic_info, 0, wxALL, 5); | ||||
| 
 | ||||
| 	wxBoxSizer* version_sizer = new wxBoxSizer(wxHORIZONTAL); | ||||
| 	text_version_title = new wxStaticText(this, wxID_ANY, _L("Studio Version:"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	text_version_title->Wrap(-1); | ||||
| 	version_sizer->Add(text_version_title, 0, wxALL, 5); | ||||
| 
 | ||||
| 	wxString text_version = get_studio_version(); | ||||
| 	text_version_val = new wxStaticText(this, wxID_ANY, text_version, wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	text_version_val->Wrap(-1); | ||||
| 	version_sizer->Add(text_version_val, 0, wxALL, 5); | ||||
| 	sizer->Add(version_sizer, 1, wxEXPAND, 5); | ||||
| 
 | ||||
| 	wxBoxSizer* sys_sizer = new wxBoxSizer(wxHORIZONTAL); | ||||
| 
 | ||||
| 	txt_sys_info_title = new wxStaticText(this, wxID_ANY, _L("System Version:"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	txt_sys_info_title->Wrap(-1); | ||||
| 	sys_sizer->Add(txt_sys_info_title, 0, wxALL, 5); | ||||
| 
 | ||||
| 	txt_sys_info_value = new wxStaticText(this, wxID_ANY, get_os_info(), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	txt_sys_info_value->Wrap(-1); | ||||
| 	sys_sizer->Add(txt_sys_info_value, 0, wxALL, 5); | ||||
| 
 | ||||
| 	sizer->Add(sys_sizer, 1, wxEXPAND, 5); | ||||
| 
 | ||||
| 	wxBoxSizer* line_sizer = new wxBoxSizer(wxHORIZONTAL); | ||||
| 	txt_dns_info_title = new wxStaticText(this, wxID_ANY, _L("DNS Server:"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	txt_dns_info_title->Wrap(-1); | ||||
| 	txt_dns_info_title->Hide(); | ||||
| 	line_sizer->Add(txt_dns_info_title, 0, wxALL, 5); | ||||
| 
 | ||||
| 	txt_dns_info_value = new wxStaticText(this, wxID_ANY, get_dns_info(), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	txt_dns_info_value->Hide(); | ||||
| 	line_sizer->Add(txt_dns_info_value, 0, wxALL, 5); | ||||
| 	sizer->Add(line_sizer, 1, wxEXPAND, 5); | ||||
| 
 | ||||
| 	return sizer; | ||||
| } | ||||
| 
 | ||||
| wxBoxSizer* NetworkTestDialog::create_content_sizer(wxWindow* parent) | ||||
| { | ||||
| 	auto sizer = new wxBoxSizer(wxVERTICAL); | ||||
| 
 | ||||
| 	wxFlexGridSizer* grid_sizer; | ||||
| 	grid_sizer = new wxFlexGridSizer(0, 2, 0, 0); | ||||
| 	grid_sizer->SetFlexibleDirection(wxBOTH); | ||||
| 	grid_sizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); | ||||
| 
 | ||||
| 	text_link_title = new wxStaticText(this, wxID_ANY, _L("Test BambuLab:"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	text_link_title->Wrap(-1); | ||||
| 	grid_sizer->Add(text_link_title, 0, wxALIGN_RIGHT | wxALL, 5); | ||||
| 
 | ||||
| 	text_link_val = new wxStaticText(this, wxID_ANY, _L("N/A"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	text_link_val->Wrap(-1); | ||||
| 	grid_sizer->Add(text_link_val, 0, wxALL, 5); | ||||
| 
 | ||||
| 	text_bing_title = new wxStaticText(this, wxID_ANY, _L("Test bing.com:"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	text_bing_title->Wrap(-1); | ||||
| 	grid_sizer->Add(text_bing_title, 0, wxALIGN_RIGHT | wxALL, 5); | ||||
| 
 | ||||
| 	text_bing_val = new wxStaticText(this, wxID_ANY, _L("N/A"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	text_bing_val->Wrap(-1); | ||||
| 	grid_sizer->Add(text_bing_val, 0, wxALL, 5); | ||||
| 
 | ||||
| 	text_iot_title = new wxStaticText(this, wxID_ANY, _L("Test HTTP Service:"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	text_iot_title->Wrap(-1); | ||||
| 	grid_sizer->Add(text_iot_title, 0, wxALIGN_RIGHT | wxALL, 5); | ||||
| 
 | ||||
| 	text_iot_value = new wxStaticText(this, wxID_ANY, _L("N/A"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	text_iot_value->Wrap(-1); | ||||
| 	grid_sizer->Add(text_iot_value, 0, wxALL, 5); | ||||
| 
 | ||||
| 	text_oss_title = new wxStaticText(this, wxID_ANY, _L("Test Storage Upload:"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	text_oss_title->Wrap(-1); | ||||
| 	grid_sizer->Add(text_oss_title, 0, wxALIGN_RIGHT | wxALL, 5); | ||||
| 
 | ||||
| 	text_oss_value = new wxStaticText(this, wxID_ANY, _L("N/A"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	text_oss_value->Wrap(-1); | ||||
| 	grid_sizer->Add(text_oss_value, 0, wxALL, 5); | ||||
| 
 | ||||
| 	text_oss_upgrade_title = new wxStaticText(this, wxID_ANY, _L("Test Storage Upgrade:"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	text_oss_upgrade_title->Wrap(-1); | ||||
| 	grid_sizer->Add(text_oss_upgrade_title, 0, wxALIGN_RIGHT | wxALL, 5); | ||||
| 
 | ||||
| 	text_oss_upgrade_value = new wxStaticText(this, wxID_ANY, _L("N/A"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	text_oss_upgrade_value->Wrap(-1); | ||||
| 	grid_sizer->Add(text_oss_upgrade_value, 0, wxALL, 5); | ||||
| 
 | ||||
| 	text_oss_download_title = new wxStaticText(this, wxID_ANY, _L("Test Storage Download:"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	text_oss_download_title->Wrap(-1); | ||||
| 	grid_sizer->Add(text_oss_download_title, 0, wxALIGN_RIGHT | wxALL, 5); | ||||
| 
 | ||||
| 	text_oss_download_value = new wxStaticText(this, wxID_ANY, _L("N/A"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	text_oss_download_value->Wrap(-1); | ||||
| 	grid_sizer->Add(text_oss_download_value, 0, wxALL, 5); | ||||
| 
 | ||||
| 	text_oss_upload_title = new wxStaticText(this, wxID_ANY, _L("Test Storage Upload:"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	text_oss_upload_title->Wrap(-1); | ||||
| 	grid_sizer->Add(text_oss_upload_title, 0, wxALIGN_RIGHT | wxALL, 5); | ||||
| 
 | ||||
| 	text_oss_upload_value = new wxStaticText(this, wxID_ANY, _L("N/A"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	text_oss_upload_value->Wrap(-1); | ||||
| 	grid_sizer->Add(text_oss_upload_value, 0, wxALL, 5); | ||||
| 
 | ||||
| 	text_oss_upload_title->Hide(); | ||||
| 	text_oss_upload_value->Hide(); | ||||
| 
 | ||||
| 	sizer->Add(grid_sizer, 1, wxEXPAND, 5); | ||||
| 
 | ||||
| 	return sizer; | ||||
| } | ||||
| wxBoxSizer* NetworkTestDialog::create_result_sizer(wxWindow* parent) | ||||
| { | ||||
| 	auto sizer = new wxBoxSizer(wxVERTICAL); | ||||
| 	text_result = new wxStaticText(this, wxID_ANY, _L("Log Info"), wxDefaultPosition, wxDefaultSize, 0); | ||||
| 	text_result->Wrap(-1); | ||||
| 	sizer->Add(text_result, 0, wxALL, 5); | ||||
| 
 | ||||
| 	txt_log = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE); | ||||
| 	sizer->Add(txt_log, 1, wxALL | wxEXPAND, 5); | ||||
| 	return sizer; | ||||
| } | ||||
| 
 | ||||
| NetworkTestDialog::~NetworkTestDialog() | ||||
| { | ||||
|     ; | ||||
| } | ||||
| 
 | ||||
| void NetworkTestDialog::init_bind() | ||||
| { | ||||
| 	Bind(EVT_UPDATE_RESULT, [this](wxCommandEvent& evt) { | ||||
| 		if (evt.GetInt() == TEST_BAMBULAB_JOB) { | ||||
| 			text_link_val->SetLabelText(evt.GetString()); | ||||
| 		} else if (evt.GetInt() == TEST_BING_JOB) { | ||||
| 			text_bing_val->SetLabelText(evt.GetString()); | ||||
| 		} else if (evt.GetInt() == TEST_IOT_JOB) { | ||||
| 			text_iot_value->SetLabelText(evt.GetString()); | ||||
| 		} else if (evt.GetInt() == TEST_OSS_JOB) { | ||||
| 			text_oss_value->SetLabelText(evt.GetString()); | ||||
| 		} else if (evt.GetInt() == TEST_OSS_UPGRADE_JOB) { | ||||
| 			text_oss_upgrade_value->SetLabelText(evt.GetString()); | ||||
| 		} else if (evt.GetInt() == TEST_OSS_DOWNLOAD_JOB) { | ||||
| 			text_oss_download_value->SetLabelText(evt.GetString()); | ||||
| 		} else if (evt.GetInt() == TEST_OSS_UPLOAD_JOB) { | ||||
| 			text_oss_upload_value->SetLabelText(evt.GetString()); | ||||
| 		} | ||||
| 
 | ||||
| 		std::time_t t = std::time(0); | ||||
| 		std::tm* now_time = std::localtime(&t); | ||||
| 		std::stringstream buf; | ||||
| 		buf << std::put_time(now_time, "%a %b %d %H:%M:%S"); | ||||
| 		wxString info = wxString::Format("%s:", buf.str()) + evt.GetString() + "\n"; | ||||
| 		try { | ||||
| 			txt_log->AppendText(info); | ||||
| 		} | ||||
| 		catch (std::exception& e) { | ||||
| 			BOOST_LOG_TRIVIAL(error) << "Unkown Exception in print_log, exception=" << e.what(); | ||||
| 			return; | ||||
| 		} | ||||
| 		catch (...) { | ||||
| 			BOOST_LOG_TRIVIAL(error) << "Unkown Exception in print_log"; | ||||
| 			return; | ||||
| 		} | ||||
| 		return; | ||||
| 	}); | ||||
| 
 | ||||
| 	Bind(wxEVT_CLOSE_WINDOW, &NetworkTestDialog::on_close, this); | ||||
| } | ||||
| 
 | ||||
| wxString NetworkTestDialog::get_os_info() | ||||
| { | ||||
| 	int major = 0, minor = 0, micro = 0; | ||||
| 	wxGetOsVersion(&major, &minor, µ); | ||||
| 	std::string os_version = (boost::format("%1%.%2%.%3%") % major % minor % micro).str(); | ||||
| 	wxString text_sys_version = wxGetOsDescription() + wxString::Format("%d.%d.%d", major, minor, micro); | ||||
| 	return text_sys_version; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| wxString NetworkTestDialog::get_dns_info() | ||||
| { | ||||
| 	return NA_STR; | ||||
| } | ||||
| 
 | ||||
| void NetworkTestDialog::start_all_job() | ||||
| { | ||||
| 	start_test_bing_thread(); | ||||
| 	start_test_bambulab_thread(); | ||||
| 	start_test_iot_thread(); | ||||
| 	start_test_oss_thread(); | ||||
| 	start_test_oss_upgrade_thread(); | ||||
| 	start_test_oss_download_thread(); | ||||
| } | ||||
| 
 | ||||
| void NetworkTestDialog::start_test_bing_thread() | ||||
| { | ||||
| 	if (m_in_testing[TEST_BING_JOB]) return; | ||||
| 
 | ||||
| 	test_job[TEST_BING_JOB] = new boost::thread([this] { | ||||
| 		m_in_testing[TEST_BING_JOB] = true; | ||||
| 		update_status(TEST_BING_JOB, "test bing start..."); | ||||
| 
 | ||||
| 		std::string url = "https://www.bing.com"; | ||||
| 		Slic3r::Http http = Slic3r::Http::get(url); | ||||
| 
 | ||||
| 		int result = -1; | ||||
| 		http.header("accept", "application/json") | ||||
| 			.timeout_max(10) | ||||
| 			.on_complete([this, &result](std::string body, unsigned status) { | ||||
| 			try { | ||||
| 				if (status == 200) { | ||||
| 					result = 0; | ||||
| 				} | ||||
| 			} | ||||
| 			catch (...) { | ||||
| 				; | ||||
| 			} | ||||
| 				}) | ||||
| 			.on_ip_resolve([this](std::string ip) { | ||||
| 					wxString ip_report = wxString::Format("test bing ip resolved = %s", ip); | ||||
| 					update_status(TEST_BING_JOB, ip_report); | ||||
| 				}) | ||||
| 			.on_error([this](std::string body, std::string error, unsigned int status) { | ||||
| 			wxString info = wxString::Format("status=%u, body=%s, error=%s", status, body, error); | ||||
| 			this->update_status(TEST_BING_JOB, "test bing failed"); | ||||
| 			this->update_status(-1, info); | ||||
| 		}).perform_sync(); | ||||
| 		if (result == 0) { | ||||
| 			update_status(TEST_BING_JOB, "test bing ok"); | ||||
| 		} | ||||
| 		m_in_testing[TEST_BING_JOB] = false; | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| void NetworkTestDialog::start_test_bambulab_thread() | ||||
| { | ||||
| 	if (m_in_testing[TEST_BAMBULAB_JOB]) return; | ||||
| 
 | ||||
| 	test_job[TEST_BAMBULAB_JOB] = new boost::thread([this] { | ||||
| 		m_in_testing[TEST_BAMBULAB_JOB] = true; | ||||
| 		update_status(TEST_BAMBULAB_JOB, "test bambulab start..."); | ||||
| 
 | ||||
| 		std::string platform = "windows"; | ||||
| 
 | ||||
| #ifdef __WINDOWS__ | ||||
| 		platform = "windows"; | ||||
| #endif | ||||
| #ifdef __APPLE__ | ||||
| 		platform = "macos"; | ||||
| #endif | ||||
| #ifdef __LINUX__ | ||||
| 		platform = "linux"; | ||||
| #endif | ||||
| 		std::string query_params = (boost::format("?name=slicer&version=%1%&guide_version=%2%") | ||||
| 			% VersionInfo::convert_full_version(SLIC3R_VERSION) | ||||
| 			% VersionInfo::convert_full_version("0.0.0.1") | ||||
| 			).str(); | ||||
| 
 | ||||
| 		AppConfig* app_config = wxGetApp().app_config; | ||||
| 		std::string url = wxGetApp().get_http_url(app_config->get_country_code()) + query_params; | ||||
| 		Slic3r::Http http = Slic3r::Http::get(url); | ||||
| 		int result = -1; | ||||
| 		http.header("accept", "application/json") | ||||
| 			.timeout_max(10) | ||||
| 			.on_complete([this, &result](std::string body, unsigned status) { | ||||
| 				try { | ||||
| 					if (status == 200) { | ||||
| 						result = 0; | ||||
| 					} | ||||
| 				} | ||||
| 				catch (...) { | ||||
| 					; | ||||
| 				} | ||||
| 			}) | ||||
| 			.on_ip_resolve([this](std::string ip) { | ||||
| 				wxString ip_report = wxString::Format("test bambulab ip resolved = %s", ip); | ||||
| 				update_status(TEST_BAMBULAB_JOB, ip_report); | ||||
| 			}) | ||||
| 			.on_error([this](std::string body, std::string error, unsigned int status) { | ||||
| 				wxString info = wxString::Format("status=%u, body=%s, error=%s", status, body, error); | ||||
| 				this->update_status(TEST_BAMBULAB_JOB, "test bambulab failed"); | ||||
| 				this->update_status(-1, info); | ||||
| 			}).perform_sync(); | ||||
| 		if (result == 0) { | ||||
| 			update_status(TEST_BAMBULAB_JOB, "test bambulab ok"); | ||||
| 		} | ||||
| 		m_in_testing[TEST_BAMBULAB_JOB] = false; | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| void NetworkTestDialog::start_test_iot_thread() | ||||
| { | ||||
| 	if (m_in_testing[TEST_IOT_JOB]) return; | ||||
| 
 | ||||
| 	test_job[TEST_IOT_JOB] = new boost::thread([this] { | ||||
| 		m_in_testing[TEST_IOT_JOB] = true; | ||||
| 		update_status(TEST_IOT_JOB, "test http start..."); | ||||
| 		NetworkAgent* agent = wxGetApp().getAgent(); | ||||
| 		if (agent) { | ||||
| 			unsigned int http_code; | ||||
| 			std::string http_body; | ||||
| 			if (!agent->is_user_login()) { | ||||
| 				update_status(TEST_IOT_JOB, "please login first"); | ||||
| 			} else { | ||||
| 				int result = agent->get_user_print_info(&http_code, &http_body); | ||||
| 				if (result == 0) { | ||||
| 					update_status(TEST_IOT_JOB, "test http ok"); | ||||
| 				} else { | ||||
| 					update_status(TEST_IOT_JOB, "test http failed"); | ||||
| 					wxString info = wxString::Format("test http failed, status = %u, error = %s", http_code, http_body); | ||||
| 					update_status(-1, info); | ||||
| 				} | ||||
| 			} | ||||
| 		} else { | ||||
| 			update_status(TEST_IOT_JOB, "please install network module first"); | ||||
| 		} | ||||
| 		m_in_testing[TEST_IOT_JOB] = false; | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| void NetworkTestDialog::start_test_oss_thread() | ||||
| { | ||||
| 	test_job[TEST_OSS_JOB] = new boost::thread([this] { | ||||
| 		m_in_testing[TEST_OSS_JOB] = true; | ||||
| 		update_status(TEST_OSS_JOB, "test storage start..."); | ||||
| 
 | ||||
| 		std::string url = "http://upload-file.bambulab.com"; | ||||
| 
 | ||||
| 		AppConfig* config = wxGetApp().app_config; | ||||
| 		if (config) { | ||||
| 			if (config->get_country_code() == "CN") | ||||
| 				url = "http://upload-file.bambulab.cn"; | ||||
| 		} | ||||
| 
 | ||||
| 		Slic3r::Http http = Slic3r::Http::get(url); | ||||
| 
 | ||||
| 		int result = -1; | ||||
| 		http.timeout_max(15) | ||||
| 			.on_complete([this, &result](std::string body, unsigned status) { | ||||
| 			try { | ||||
| 				if (status == 200) { | ||||
| 					result = 0; | ||||
| 				} | ||||
| 			} | ||||
| 			catch (...) { | ||||
| 				; | ||||
| 			} | ||||
| 				}) | ||||
| 			.on_ip_resolve([this](std::string ip) { | ||||
| 				wxString ip_report = wxString::Format("test oss ip resolved = %s", ip); | ||||
| 				update_status(TEST_OSS_JOB, ip_report); | ||||
| 			}) | ||||
| 			.on_error([this, &result](std::string body, std::string error, unsigned int status) { | ||||
| 				if (status == 403) { | ||||
| 					result = 0; | ||||
| 				} else { | ||||
| 					wxString info = wxString::Format("status=%u, body=%s, error=%s", status, body, error); | ||||
| 					this->update_status(TEST_OSS_JOB, "test storage failed"); | ||||
| 					this->update_status(-1, info); | ||||
| 				} | ||||
| 			}).perform_sync(); | ||||
| 			if (result == 0) { | ||||
| 				update_status(TEST_OSS_JOB, "test storage ok"); | ||||
| 			} | ||||
| 		m_in_testing[TEST_OSS_JOB] = false; | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| void NetworkTestDialog::start_test_oss_upgrade_thread() | ||||
| { | ||||
| 	test_job[TEST_OSS_UPGRADE_JOB] = new boost::thread([this] { | ||||
| 		m_in_testing[TEST_OSS_UPGRADE_JOB] = true; | ||||
| 		update_status(TEST_OSS_UPGRADE_JOB, "test storage upgrade start..."); | ||||
| 
 | ||||
| 		std::string url = "http://upgrade-file.bambulab.com"; | ||||
| 
 | ||||
| 		AppConfig* config = wxGetApp().app_config; | ||||
| 		if (config) { | ||||
| 			if (config->get_country_code() == "CN") | ||||
| 				url = "http://upgrade-file.bambulab.cn"; | ||||
| 		} | ||||
| 
 | ||||
| 		Slic3r::Http http = Slic3r::Http::get(url); | ||||
| 
 | ||||
| 		int result = -1; | ||||
| 		http.timeout_max(15) | ||||
| 			.on_complete([this, &result](std::string body, unsigned status) { | ||||
| 			try { | ||||
| 				if (status == 200) { | ||||
| 					result = 0; | ||||
| 				} | ||||
| 			} | ||||
| 			catch (...) { | ||||
| 				; | ||||
| 			} | ||||
| 				}) | ||||
| 			.on_ip_resolve([this](std::string ip) { | ||||
| 					wxString ip_report = wxString::Format("test storage upgrade ip resolved = %s", ip); | ||||
| 					update_status(TEST_OSS_UPGRADE_JOB, ip_report); | ||||
| 				}) | ||||
| 			.on_error([this, &result](std::string body, std::string error, unsigned int status) { | ||||
| 				if (status == 403) { | ||||
| 					result = 0; | ||||
| 				} | ||||
| 				else { | ||||
| 					wxString info = wxString::Format("status=%u, body=%s, error=%s", status, body, error); | ||||
| 					this->update_status(TEST_OSS_UPGRADE_JOB, "test storage upgrade failed"); | ||||
| 					this->update_status(-1, info); | ||||
| 				} | ||||
| 			}).perform_sync(); | ||||
| 
 | ||||
| 		if (result == 0) { | ||||
| 			update_status(TEST_OSS_UPGRADE_JOB, "test storage upgrade ok"); | ||||
| 		} | ||||
| 		m_in_testing[TEST_OSS_UPGRADE_JOB] = false; | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| void NetworkTestDialog::start_test_oss_download_thread() | ||||
| { | ||||
| 	test_job[TEST_OSS_DOWNLOAD_JOB] = new boost::thread([this] { | ||||
| 		int result = 0; | ||||
| 		// get country_code
 | ||||
| 		AppConfig* app_config = wxGetApp().app_config; | ||||
| 		if (!app_config) { | ||||
| 			update_status(TEST_OSS_DOWNLOAD_JOB, "app config is nullptr"); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		m_in_testing[TEST_OSS_DOWNLOAD_JOB] = true; | ||||
| 		update_status(TEST_OSS_DOWNLOAD_JOB, "test storage download start..."); | ||||
| 		m_download_cancel = false; | ||||
| 		// get temp path
 | ||||
| 		fs::path target_file_path = (fs::temp_directory_path() / "test_storage_download.zip"); | ||||
| 		fs::path tmp_path = target_file_path; | ||||
| 		tmp_path += (boost::format(".%1%%2%") % get_current_pid() % ".tmp").str(); | ||||
| 
 | ||||
| 		// get_url
 | ||||
| 		std::string url = wxGetApp().get_plugin_url(app_config->get_country_code()); | ||||
| 		std::string download_url; | ||||
| 		Slic3r::Http http_url = Slic3r::Http::get(url); | ||||
| 		update_status(-1, "[test_oss_download]: url=" + url); | ||||
| 
 | ||||
| 		http_url.on_complete( | ||||
| 			[&download_url](std::string body, unsigned status) { | ||||
| 				try { | ||||
| 					json j = json::parse(body); | ||||
| 					std::string message = j["message"].get<std::string>(); | ||||
| 
 | ||||
| 					if (message == "success") { | ||||
| 						json resource = j.at("resources"); | ||||
| 						if (resource.is_array()) { | ||||
| 							for (auto iter = resource.begin(); iter != resource.end(); iter++) { | ||||
| 								Semver version; | ||||
| 								std::string url; | ||||
| 								std::string type; | ||||
| 								std::string vendor; | ||||
| 								std::string description; | ||||
| 								for (auto sub_iter = iter.value().begin(); sub_iter != iter.value().end(); sub_iter++) { | ||||
| 									if (boost::iequals(sub_iter.key(), "type")) { | ||||
| 										type = sub_iter.value(); | ||||
| 										BOOST_LOG_TRIVIAL(info) << "[test_storage_download]: get version of settings's type, " << sub_iter.value(); | ||||
| 									} | ||||
| 									else if (boost::iequals(sub_iter.key(), "version")) { | ||||
| 										version = *(Semver::parse(sub_iter.value())); | ||||
| 									} | ||||
| 									else if (boost::iequals(sub_iter.key(), "description")) { | ||||
| 										description = sub_iter.value(); | ||||
| 									} | ||||
| 									else if (boost::iequals(sub_iter.key(), "url")) { | ||||
| 										url = sub_iter.value(); | ||||
| 									} | ||||
| 								} | ||||
| 								BOOST_LOG_TRIVIAL(info) << "[test_storage_download]: get type " << type << ", version " << version.to_string() << ", url " << url; | ||||
| 								download_url = url; | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 					else { | ||||
| 						BOOST_LOG_TRIVIAL(info) << "[test_storage_download]: get version of plugin failed, body=" << body; | ||||
| 					} | ||||
| 				} | ||||
| 				catch (...) { | ||||
| 					BOOST_LOG_TRIVIAL(error) << "[test_storage_download]: catch unknown exception"; | ||||
| 					; | ||||
| 				} | ||||
| 			}).on_error( | ||||
| 				[&result, this](std::string body, std::string error, unsigned int status) { | ||||
| 					BOOST_LOG_TRIVIAL(error) << "[test_storage_download] on_error: " << error << ", body = " << body; | ||||
| 					wxString info = wxString::Format("status=%u, body=%s, error=%s", status, body, error); | ||||
| 					this->update_status(TEST_OSS_DOWNLOAD_JOB, "test storage download failed"); | ||||
| 					this->update_status(-1, info); | ||||
| 					result = -1; | ||||
| 			}).perform_sync(); | ||||
| 
 | ||||
| 		if (result < 0) { | ||||
| 			this->update_status(TEST_OSS_DOWNLOAD_JOB, "test storage download failed"); | ||||
| 			m_in_testing[TEST_OSS_DOWNLOAD_JOB] = false; | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		if (download_url.empty()) { | ||||
| 			BOOST_LOG_TRIVIAL(info) << "[test_oss_download]: no availaible plugin found for this app version: " << SLIC3R_VERSION; | ||||
| 			this->update_status(TEST_OSS_DOWNLOAD_JOB, "test storage download failed"); | ||||
| 			m_in_testing[TEST_OSS_DOWNLOAD_JOB] = false; | ||||
| 			return; | ||||
| 		} | ||||
| 		if (m_download_cancel) { | ||||
| 			this->update_status(TEST_OSS_DOWNLOAD_JOB, "test storage download canceled"); | ||||
| 			m_in_testing[TEST_OSS_DOWNLOAD_JOB] = false; | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		bool cancel = false; | ||||
| 		BOOST_LOG_TRIVIAL(info) << "[test_storage_download] get_url = " << download_url; | ||||
| 
 | ||||
| 		// download
 | ||||
| 		Slic3r::Http http = Slic3r::Http::get(download_url); | ||||
| 		int reported_percent = 0; | ||||
| 		http.on_progress( | ||||
| 			[this, &result, &reported_percent](Slic3r::Http::Progress progress, bool& cancel) { | ||||
| 				int percent = 0; | ||||
| 				if (progress.dltotal != 0) { | ||||
| 					percent = progress.dlnow * 100 / progress.dltotal; | ||||
| 				} | ||||
| 				if (percent - reported_percent >= 10) { | ||||
| 					reported_percent = percent; | ||||
| 					std::string download_progress_info = (boost::format("downloading %1%%%") % percent).str(); | ||||
| 					this->update_status(TEST_OSS_DOWNLOAD_JOB, download_progress_info); | ||||
| 				} | ||||
| 
 | ||||
| 				BOOST_LOG_TRIVIAL(info) << "[test_storage_download] progress: " << reported_percent; | ||||
| 				cancel = m_download_cancel; | ||||
| 
 | ||||
| 				if (cancel) | ||||
| 					result = -1; | ||||
| 			}) | ||||
| 			.on_complete([this, tmp_path, target_file_path](std::string body, unsigned status) { | ||||
| 				BOOST_LOG_TRIVIAL(info) << "[test_storage_download] completed"; | ||||
| 				bool cancel = false; | ||||
| 				fs::fstream file(tmp_path, std::ios::out | std::ios::binary | std::ios::trunc); | ||||
| 				file.write(body.c_str(), body.size()); | ||||
| 				file.close(); | ||||
| 				fs::rename(tmp_path, target_file_path); | ||||
| 				this->update_status(TEST_OSS_DOWNLOAD_JOB, "test storage download ok"); | ||||
| 			}) | ||||
| 			.on_error([this, &result](std::string body, std::string error, unsigned int status) { | ||||
| 				BOOST_LOG_TRIVIAL(error) << "[test_oss_download] downloading... on_error: " << error << ", body = " << body; | ||||
| 				wxString info = wxString::Format("status=%u, body=%s, error=%s", status, body, error); | ||||
| 				this->update_status(TEST_OSS_DOWNLOAD_JOB, "test storage download failed"); | ||||
| 				this->update_status(-1, info); | ||||
| 				result = -1; | ||||
| 			}); | ||||
| 		http.perform_sync(); | ||||
| 		if (result < 0) { | ||||
| 			this->update_status(TEST_OSS_DOWNLOAD_JOB, "test storage download failed"); | ||||
| 		} else { | ||||
| 			this->update_status(TEST_OSS_DOWNLOAD_JOB, "test storage download ok"); | ||||
| 		} | ||||
| 		m_in_testing[TEST_OSS_DOWNLOAD_JOB] = false; | ||||
| 		return; | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| void NetworkTestDialog::start_test_oss_upload_thread() | ||||
| { | ||||
| 	; | ||||
| } | ||||
| 
 | ||||
| void NetworkTestDialog::on_close(wxCloseEvent& event) | ||||
| { | ||||
| 	m_download_cancel = true; | ||||
| 	for (int i = 0; i < TEST_JOB_MAX; i++) { | ||||
| 		if (test_job[i]) { | ||||
| 			test_job[i]->join(); | ||||
| 			test_job[i] = nullptr; | ||||
| 		} | ||||
| 	} | ||||
| 	event.Skip(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| wxString NetworkTestDialog::get_studio_version() | ||||
| { | ||||
| 	return wxString(SLIC3R_VERSION); | ||||
| } | ||||
| 
 | ||||
| void NetworkTestDialog::set_default() | ||||
| { | ||||
| 	for (int i = 0; i < TEST_JOB_MAX; i++) { | ||||
| 		test_job[i] = nullptr; | ||||
| 		m_in_testing[i] = false; | ||||
| 	} | ||||
| 
 | ||||
| 	text_version_val->SetLabelText(get_studio_version()); | ||||
| 	txt_sys_info_value->SetLabelText(get_os_info()); | ||||
| 	txt_dns_info_value->SetLabelText(get_dns_info()); | ||||
| 	text_link_val->SetLabelText(NA_STR); | ||||
| 	text_bing_val->SetLabelText(NA_STR); | ||||
| 	text_iot_value->SetLabelText(NA_STR); | ||||
| 	text_oss_value->SetLabelText(NA_STR); | ||||
| 	text_oss_upgrade_value->SetLabelText(NA_STR); | ||||
| 	text_oss_download_value->SetLabelText(NA_STR); | ||||
| 	text_oss_upload_value->SetLabelText(NA_STR); | ||||
| 	m_download_cancel = false; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void NetworkTestDialog::on_dpi_changed(const wxRect &suggested_rect) | ||||
| { | ||||
|     ; | ||||
| } | ||||
| 
 | ||||
| void NetworkTestDialog::update_status(int job_id, wxString info) | ||||
| { | ||||
| 	auto evt = new wxCommandEvent(EVT_UPDATE_RESULT, this->GetId()); | ||||
| 	evt->SetString(info); | ||||
| 	evt->SetInt(job_id); | ||||
| 	wxQueueEvent(this, evt); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| } // namespace GUI
 | ||||
| } // namespace Slic3r
 | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										115
									
								
								src/slic3r/GUI/NetworkTestDialog.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								src/slic3r/GUI/NetworkTestDialog.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,115 @@ | |||
| #ifndef slic3r_GUI_NetworkTestDialog_hpp_ | ||||
| #define slic3r_GUI_NetworkTestDialog_hpp_ | ||||
| 
 | ||||
| #include <wx/wx.h> | ||||
| #include <boost/thread.hpp> | ||||
| 
 | ||||
| #include "GUI_Utils.hpp" | ||||
| #include "wxExtensions.hpp" | ||||
| #include <wx/artprov.h> | ||||
| #include <wx/xrc/xmlres.h> | ||||
| #include <wx/button.h> | ||||
| #include <wx/string.h> | ||||
| #include <wx/bitmap.h> | ||||
| #include <wx/image.h> | ||||
| #include <wx/icon.h> | ||||
| #include <wx/gdicmn.h> | ||||
| #include <wx/font.h> | ||||
| #include <wx/colour.h> | ||||
| #include <wx/settings.h> | ||||
| #include <wx/sizer.h> | ||||
| #include <wx/textctrl.h> | ||||
| #include <wx/grid.h> | ||||
| #include <wx/dialog.h> | ||||
| #include <wx/srchctrl.h> | ||||
| #include <wx/stattext.h> | ||||
| #include <iomanip> | ||||
| #include <sys/timeb.h> | ||||
| #include <time.h> | ||||
| #include <vector> | ||||
| #include <algorithm> | ||||
| 
 | ||||
| namespace Slic3r {  | ||||
| namespace GUI { | ||||
| 
 | ||||
| enum TestJob { | ||||
| 	TEST_BING_JOB = 0, | ||||
| 	TEST_BAMBULAB_JOB = 1, | ||||
| 	TEST_IOT_JOB = 2, | ||||
| 	TEST_OSS_JOB = 3, | ||||
| 	TEST_OSS_UPGRADE_JOB = 4, | ||||
| 	TEST_OSS_DOWNLOAD_JOB = 5, | ||||
| 	TEST_OSS_UPLOAD_JOB = 6, | ||||
| 	TEST_JOB_MAX = 7 | ||||
| }; | ||||
| 
 | ||||
| class NetworkTestDialog : public DPIDialog | ||||
| { | ||||
| protected: | ||||
| 	wxButton* btn_start; | ||||
| 	wxButton* btn_download_log; | ||||
| 	wxStaticText* text_basic_info; | ||||
| 	wxStaticText* text_version_title; | ||||
| 	wxStaticText* text_version_val; | ||||
| 	wxStaticText* txt_sys_info_title; | ||||
| 	wxStaticText* txt_sys_info_value; | ||||
| 	wxStaticText* txt_dns_info_title; | ||||
| 	wxStaticText* txt_dns_info_value; | ||||
| 	wxStaticText* text_link_title; | ||||
| 	wxStaticText* text_link_val; | ||||
| 	wxStaticText* text_bing_title; | ||||
| 	wxStaticText* text_bing_val; | ||||
| 	wxStaticText* text_iot_title; | ||||
| 	wxStaticText* text_iot_value; | ||||
| 	wxStaticText* text_oss_title; | ||||
| 	wxStaticText* text_oss_value; | ||||
| 	wxStaticText* text_oss_upgrade_title; | ||||
| 	wxStaticText* text_oss_upgrade_value; | ||||
| 	wxStaticText* text_oss_download_title; | ||||
| 	wxStaticText* text_oss_download_value; | ||||
| 	wxStaticText* text_oss_upload_title; | ||||
| 	wxStaticText* text_oss_upload_value; | ||||
| 	wxStaticText* text_result; | ||||
| 	wxTextCtrl* txt_log; | ||||
| 
 | ||||
| 	wxBoxSizer* create_top_sizer(wxWindow* parent); | ||||
| 	wxBoxSizer* create_info_sizer(wxWindow* parent); | ||||
| 	wxBoxSizer* create_content_sizer(wxWindow* parent); | ||||
| 	wxBoxSizer* create_result_sizer(wxWindow* parent); | ||||
| 
 | ||||
| 	boost::thread* test_job[TEST_JOB_MAX]; | ||||
| 	bool		   m_in_testing[TEST_JOB_MAX]; | ||||
| 	bool           m_download_cancel = false; | ||||
| 
 | ||||
| 	void init_bind(); | ||||
| 
 | ||||
| public: | ||||
| 	NetworkTestDialog(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(605, 375), long style = wxDEFAULT_DIALOG_STYLE); | ||||
| 
 | ||||
| 	~NetworkTestDialog(); | ||||
| 
 | ||||
| 	void on_dpi_changed(const wxRect& suggested_rect); | ||||
| 
 | ||||
| 	void set_default(); | ||||
| 	wxString get_studio_version(); | ||||
| 	wxString get_os_info(); | ||||
| 	wxString get_dns_info(); | ||||
| 
 | ||||
| 	void start_all_job(); | ||||
| 	void start_test_bing_thread(); | ||||
| 	void start_test_bambulab_thread(); | ||||
| 	void start_test_iot_thread(); | ||||
| 	void start_test_oss_thread(); | ||||
| 	void start_test_oss_upgrade_thread(); | ||||
| 	void start_test_oss_download_thread(); | ||||
| 	void start_test_oss_upload_thread(); | ||||
| 
 | ||||
| 	void on_close(wxCloseEvent& event); | ||||
| 
 | ||||
| 	void update_status(int job_id, wxString info); | ||||
| }; | ||||
| 
 | ||||
| } // namespace GUI
 | ||||
| } // namespace Slic3r
 | ||||
| 
 | ||||
| #endif | ||||
|  | @ -65,7 +65,7 @@ ZUserLogin::ZUserLogin() : wxDialog((wxWindow *) (wxGetApp().mainframe), wxID_AN | |||
|         TargetUrl = host_url + "/" + strlang + "/sign-in"; | ||||
|     } | ||||
| 
 | ||||
|     BOOST_LOG_TRIVIAL(trace) << "login url = " << TargetUrl.ToStdString(); | ||||
|     BOOST_LOG_TRIVIAL(info) << "login url = " << TargetUrl.ToStdString(); | ||||
| 
 | ||||
|     m_bbl_user_agent = wxString::Format("BBL-Slicer/v%s", SLIC3R_VERSION); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Stone Li
						Stone Li