This commit is contained in:
sunsets
2023-07-28 09:39:01 +08:00
parent f1b81b3d38
commit 529153d41d
5 changed files with 1672 additions and 148 deletions

View File

@@ -287,8 +287,8 @@ set(SLIC3R_GUI_SOURCES
Utils/PrintHost.hpp
Utils/Bonjour.cpp
Utils/Bonjour.hpp
Utils/UdpLinkServer.cpp
Utils/UdpLinkServer.hpp
Utils/Udp.cpp
Utils/Udp.hpp
Utils/PresetUpdater.cpp
Utils/PresetUpdater.hpp
Utils/Process.cpp

View File

@@ -1,5 +1,5 @@
#include "slic3r/Utils/Bonjour.hpp" // On Windows, boost needs to be included before wxWidgets headers
#include "slic3r/Utils/Udp.hpp"
#include "BonjourDialog.hpp"
#include <set>
@@ -19,90 +19,7 @@
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/format.hpp"
#include "slic3r/Utils/Bonjour.hpp"
// B29
#include "slic3r/Utils/UdpLinkServer.hpp"
#include <boost/thread/thread.hpp>
#include <vector>
const int MAX_BUF_LEN = 255;
#define GET_HOST_COMMAND "mkswifi\r\n"
#define CLIENT_PORT 11121
#define SERVER_PORT 8989
// B29
typedef struct tagDeviceInfo
{
unsigned short usFunction;
unsigned short usVersionFlag;
unsigned int uiCompanyId;
char szDeviceSerialNo[24];
unsigned short usServicePort;
char szExtend[38];
} DeviceInfo;
// B29
typedef struct tagWorkThreadParameter
{
boost::asio::io_service *pIoService;
UdpLinkServer * pUdpService;
} WorkThreadParameter;
bool g_WorkThreadExit = false;
int g_nBroastDataSendInteral = 3000;
DeviceInfo g_diDeviceInfo = {0};
std::vector<std::string> get_reply;
// B29
unsigned int __stdcall WorkThreadFunByDeviceServiceProcess(PVOID pParam)
{
int nn = 0;
int nDataSize = sizeof(DeviceInfo);
WorkThreadParameter *pAllParameter = (WorkThreadParameter *) pParam;
while (true) {
if (g_WorkThreadExit) {
break;
}
pAllParameter->pUdpService->SendData((char *) GET_HOST_COMMAND, nDataSize, true);
pAllParameter->pIoService->poll();
// break;
for (nn = g_nBroastDataSendInteral; nn > 0; nn -= 200) {
if (g_WorkThreadExit) {
break;
}
Sleep(200);
}
}
return 0;
}
// B29
static void WINAPI BroastDeviceInfoRecvDataCallback(const boost::system::error_code &error,
char * pData,
int nDataLength,
char * pPeerIp,
unsigned short usPeerPort,
DWORD dwUserData1,
DWORD dwUserData2)
{
SYSTEMTIME sm;
GetLocalTime(&sm);
char szInfo[256] = {0};
DeviceInfo *pDeviceInfo = (DeviceInfo *) pData;
//sprintf(szInfo, "%s %s:%d time:%04d-%02d-%0d %02d:%02d:%02d\n", pData, pPeerIp, usPeerPort, sm.wYear, sm.wMonth, sm.wDay, sm.wHour,
// sm.wMinute, sm.wSecond);
//printf(szInfo);
int i = 0;
for (i = 0; i < get_reply.size(); i++)
if (get_reply[i] == pData)
break;
if (i == get_reply.size())
get_reply.push_back(pData);
}
#include "slic3r/Utils/Udp.hpp"
namespace Slic3r {
@@ -123,13 +40,29 @@ public:
return new BonjourReplyEvent(*this);
}
};
// B29
class UdpReplyEvent : public wxEvent
{
public:
UdpReply reply;
UdpReplyEvent(wxEventType eventType, int winid, UdpReply &&reply) : wxEvent(winid, eventType), reply(std::move(reply)) {}
virtual wxEvent *Clone() const { return new UdpReplyEvent(*this); }
};
// B29
wxDEFINE_EVENT(EVT_UDP_REPLY, UdpReplyEvent);
wxDEFINE_EVENT(EVT_BONJOUR_REPLY, BonjourReplyEvent);
wxDECLARE_EVENT(EVT_UDP_COMPLETE, wxCommandEvent);
wxDEFINE_EVENT(EVT_UDP_COMPLETE, wxCommandEvent);
wxDECLARE_EVENT(EVT_BONJOUR_COMPLETE, wxCommandEvent);
wxDEFINE_EVENT(EVT_BONJOUR_COMPLETE, wxCommandEvent);
class ReplySet: public std::set<BonjourReply> {};
class UdpReplySet : public std::set<UdpReply> {};
struct LifetimeGuard
{
@@ -143,6 +76,7 @@ BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech)
: wxDialog(parent, wxID_ANY, _(L("Network lookup")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
, list(new wxListView(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxSIMPLE_BORDER))
, replies(new ReplySet)
, udp_replies(new UdpReplySet)
, label(new wxStaticText(this, wxID_ANY, ""))
, timer(new wxTimer())
, timer_state(0)
@@ -177,14 +111,11 @@ BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech)
Bind(EVT_BONJOUR_REPLY, &BonjourDialog::on_reply, this);
Bind(EVT_UDP_REPLY, &BonjourDialog::on_udp_reply, this);
// B29
Bind(EVT_BONJOUR_COMPLETE, [this](wxCommandEvent &) {
this->timer_state = 0;
g_WorkThreadExit = true;
for (int n = 0; n < get_reply.size(); n++) {
auto item = list->InsertItem(0, get_reply[n].substr(get_reply[n].find_last_of(",") + 1));
list->SetItem(item, 1, get_reply[n].substr(get_reply[n].find("mkswifi:") + 8, get_reply[n].find(",") - 8));
}
});
Bind(wxEVT_TIMER, &BonjourDialog::on_timer, this);
@@ -206,21 +137,6 @@ bool BonjourDialog::show_and_lookup()
timer->Start(1000);
on_timer_process();
// B29
g_WorkThreadExit = false;
boost::asio::io_service ioService;
UdpLinkServer usUdpService(8989, true);
usUdpService.SetRecvDataCallback(true, BroastDeviceInfoRecvDataCallback, 0, 0);
usUdpService.Start(ioService);
g_diDeviceInfo.usFunction = 1;
g_diDeviceInfo.usVersionFlag = 0x0001;
strcpy(g_diDeviceInfo.szDeviceSerialNo, "ABCDEFG111111111");
g_diDeviceInfo.usServicePort = 8989;
WorkThreadParameter wtpWorkThreadParameter;
wtpWorkThreadParameter.pIoService = &ioService;
wtpWorkThreadParameter.pUdpService = &usUdpService;
boost::thread thrd(WorkThreadFunByDeviceServiceProcess, &wtpWorkThreadParameter);
// The background thread needs to queue messages for this dialog
// and for that it needs a valid pointer to it (mandated by the wxWidgets API).
@@ -228,6 +144,32 @@ bool BonjourDialog::show_and_lookup()
// so that both threads can access it safely.
auto dguard = std::make_shared<LifetimeGuard>(this);
// B29
Udp::TxtKeys udp_txt_keys{"version", "model"};
udp = Udp("octoprint")
.set_txt_keys(std::move(udp_txt_keys))
.set_retries(3)
.set_timeout(4)
.on_udp_reply([dguard](UdpReply &&reply) {
std::lock_guard<std::mutex> lock_guard(dguard->mutex);
auto dialog = dguard->dialog;
if (dialog != nullptr) {
auto evt = new UdpReplyEvent(EVT_UDP_REPLY, dialog->GetId(), std::move(reply));
wxQueueEvent(dialog, evt);
}
})
.on_complete([dguard]() {
std::lock_guard<std::mutex> lock_guard(dguard->mutex);
auto dialog = dguard->dialog;
if (dialog != nullptr) {
auto evt = new wxCommandEvent(EVT_UDP_COMPLETE, dialog->GetId());
wxQueueEvent(dialog, evt);
}
})
.lookup();
// Note: More can be done here when we support discovery of hosts other than Octoprint and SL1
Bonjour::TxtKeys txt_keys{"version", "model"};
@@ -313,15 +255,69 @@ void BonjourDialog::on_reply(BonjourReplyEvent &e)
for (int i = 0; i < list->GetColumnCount(); i++) {
list->SetColumnWidth(i, wxLIST_AUTOSIZE);
if (list->GetColumnWidth(i) < 10 * em) { list->SetColumnWidth(i, 10 * em); }
if (list->GetColumnWidth(i) < 10 * em) {
list->SetColumnWidth(i, 10 * em);
}
}
if (!selected.IsEmpty()) {
// Attempt to preserve selection
auto hit = list->FindItem(-1, selected);
if (hit >= 0) { list->SetItemState(hit, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); }
if (hit >= 0) {
list->SetItemState(hit, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
}
}
}
// B29
void BonjourDialog::on_udp_reply(UdpReplyEvent &e)
{
if (udp_replies->find(e.reply) != udp_replies->end()) {
// We already have this reply
return;
}
//// Filter replies based on selected technology
// const auto model = e.reply.txt_data.find("model");
// const bool sl1 = model != e.reply.txt_data.end() && model->second == "SL1";
// if ((tech == ptFFF && sl1) || (tech == ptSLA && !sl1)) {
// return;
//}
udp_replies->insert(std::move(e.reply));
auto selected = get_selected();
wxWindowUpdateLocker freeze_guard(this);
(void) freeze_guard;
list->DeleteAllItems();
// The whole list is recreated so that we benefit from it already being sorted in the set.
// (And also because wxListView's sorting API is bananas.)
for (const auto &reply : *udp_replies) {
auto item = list->InsertItem(0, reply.service_name);
list->SetItem(item, 1, reply.hostname);
}
const int em = GUI::wxGetApp().em_unit();
for (int i = 0; i < list->GetColumnCount(); i++) {
list->SetColumnWidth(i, wxLIST_AUTOSIZE);
if (list->GetColumnWidth(i) < 10 * em) {
list->SetColumnWidth(i, 10 * em);
}
}
if (!selected.IsEmpty()) {
// Attempt to preserve selection
auto hit = list->FindItem(-1, selected);
if (hit >= 0) {
list->SetItemState(hit, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
}
}
}
void BonjourDialog::on_timer(wxTimerEvent &)
{

View File

@@ -20,9 +20,14 @@ class address;
namespace Slic3r {
class Bonjour;
//B29
class Udp;
class BonjourReplyEvent;
//B29
class UdpReplyEvent;
class ReplySet;
//B29
class UdpReplySet;
class BonjourDialog: public wxDialog
{
@@ -39,13 +44,19 @@ public:
private:
wxListView *list;
std::unique_ptr<ReplySet> replies;
//B29
std::unique_ptr<UdpReplySet> udp_replies;
wxStaticText *label;
std::shared_ptr<Bonjour> bonjour;
//B29
std::shared_ptr<Udp> udp;
std::unique_ptr<wxTimer> timer;
unsigned timer_state;
Slic3r::PrinterTechnology tech;
virtual void on_reply(BonjourReplyEvent &);
//B29
virtual void on_udp_reply(UdpReplyEvent &);
void on_timer(wxTimerEvent &);
void on_timer_process();
};

1228
src/slic3r/Utils/Udp.cpp Normal file

File diff suppressed because it is too large Load Diff

289
src/slic3r/Utils/Udp.hpp Normal file
View File

@@ -0,0 +1,289 @@
#ifndef slic3r_Udp_hpp_
#define slic3r_Udp_hpp_
#include <cstdint>
#include <memory>
#include <string>
#include <set>
#include <unordered_map>
#include <functional>
#include <boost/asio.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/optional.hpp>
#include <boost/system/error_code.hpp>
#include <boost/shared_ptr.hpp>
namespace Slic3r {
struct UdpReply
{
typedef std::unordered_map<std::string, std::string> TxtData;
boost::asio::ip::address ip;
uint16_t port;
std::string service_name;
std::string hostname;
std::string full_address;
//TxtData txt_data;
UdpReply() = delete;
UdpReply(boost::asio::ip::address ip,
uint16_t port,
std::string service_name,
std::string hostname);
std::string path() const;
bool operator==(const UdpReply &other) const;
bool operator<(const UdpReply &other) const;
};
std::ostream& operator<<(std::ostream &, const UdpReply &);
/// Udp lookup performer
class Udp : public std::enable_shared_from_this<Udp> {
private:
struct priv;
public:
typedef std::shared_ptr<Udp> Ptr;
typedef std::function<void(UdpReply &&)> ReplyFn;
typedef std::function<void()> CompleteFn;
typedef std::function<void(const std::vector<UdpReply>&)> ResolveFn;
typedef std::set<std::string> TxtKeys;
Udp(std::string service);
Udp(Udp &&other);
~Udp();
// Set requested service protocol, "tcp" by default
Udp& set_protocol(std::string protocol);
// Set which TXT key-values should be collected
// Note that "path" is always collected
Udp& set_txt_keys(TxtKeys txt_keys);
Udp& set_timeout(unsigned timeout);
Udp& set_retries(unsigned retries);
// ^ Note: By default there is 1 retry (meaning 1 broadcast is sent).
// Timeout is per one retry, ie. total time spent listening = retries * timeout.
// If retries > 1, then care needs to be taken as more than one reply from the same service may be received.
// sets hostname queried by resolve()
Udp& set_hostname(const std::string& hostname);
Udp& on_udp_reply(ReplyFn fn);
Udp& on_complete(CompleteFn fn);
Udp& on_resolve(ResolveFn fn);
// lookup all devices by given TxtKeys
// each correct reply is passed back in ReplyFn, finishes with CompleteFn
Ptr lookup();
// performs resolving of hostname into vector of ip adresses passed back by ResolveFn
// needs set_hostname and on_resolve to be called before.
Ptr resolve();
// resolve on the current thread
void resolve_sync();
private:
std::unique_ptr<priv> p;
};
struct UdpRequest
{
static const boost::asio::ip::address_v4 MCAST_IP4;
static const boost::asio::ip::address_v6 MCAST_IP6;
static const uint16_t MCAST_PORT;
std::vector<char> m_data;
static boost::optional<UdpRequest> make_PTR(const std::string& service, const std::string& protocol);
static boost::optional<UdpRequest> make_A(const std::string& hostname);
static boost::optional<UdpRequest> make_AAAA(const std::string& hostname);
private:
UdpRequest(std::vector<char>&& data) : m_data(std::move(data)) {}
};
class LookupUdpSocket;
class ResolveUdpUdpSocket;
// Session is created for each async_receive of socket. On receive, its handle_receive method is called (Thru io_service->post).
// ReplyFn is called if correct datagram was received.
class UdpUdpSession
{
public:
UdpUdpSession(Udp::ReplyFn rfn);
virtual void handle_receive(const boost::system::error_code &error, size_t bytes, std::string pData) = 0;
std::vector<char> buffer;
boost::asio::ip::udp::endpoint remote_endpoint;
protected:
Udp::ReplyFn replyfn;
};
typedef std::shared_ptr<UdpUdpSession> SharedUdpSession;
// Session for LookupUdpSocket
class LookupUdpSession : public UdpUdpSession
{
public:
LookupUdpSession(const LookupUdpSocket *sckt, Udp::ReplyFn rfn) : UdpUdpSession(rfn), socket(sckt) {}
void handle_receive(const boost::system::error_code &error, size_t bytes, std::string pData) override;
protected:
// const pointer to socket to get needed data as txt_keys etc.
const LookupUdpSocket *socket;
};
// Session for ResolveUdpUdpSocket
class ResolveUdpSession : public UdpUdpSession
{
public:
ResolveUdpSession(const ResolveUdpUdpSocket* sckt, Udp::ReplyFn rfn) : UdpUdpSession(rfn), socket(sckt) {}
void handle_receive(const boost::system::error_code &error, size_t bytes, std::string pData) override;
protected:
// const pointer to seocket to get hostname during handle_receive
const ResolveUdpUdpSocket* socket;
};
// Udp socket, starts receiving answers after first send() call until io_service is stopped.
class UdpUdpSocket
{
public:
// Two constructors: 1st is with interface which must be resolved before calling this
UdpUdpSocket(Udp::ReplyFn replyfn
, const boost::asio::ip::address& multicast_address
, const boost::asio::ip::address& interface_address
, std::shared_ptr< boost::asio::io_service > io_service);
UdpUdpSocket(Udp::ReplyFn replyfn
, const boost::asio::ip::address& multicast_address
, std::shared_ptr< boost::asio::io_service > io_service);
void send();
void async_receive();
void cancel() { socket.cancel(); }
protected:
void receive_handler(SharedUdpSession session, const boost::system::error_code& error, size_t bytes);
virtual SharedUdpSession create_session() const = 0;
Udp::ReplyFn replyfn;
boost::asio::ip::address multicast_address;
boost::asio::ip::udp::socket socket;
boost::asio::ip::udp::endpoint mcast_endpoint;
std::shared_ptr< boost::asio::io_service > io_service;
std::vector<UdpRequest> requests;
boost::array<char, 81920> m_recvBuf;
};
class LookupUdpSocket : public UdpUdpSocket
{
public:
LookupUdpSocket(Udp::TxtKeys txt_keys
, std::string service
, std::string service_dn
, std::string protocol
, Udp::ReplyFn replyfn
, const boost::asio::ip::address& multicast_address
, const boost::asio::ip::address& interface_address
, std::shared_ptr< boost::asio::io_service > io_service)
: UdpUdpSocket(replyfn, multicast_address, interface_address, io_service)
, txt_keys(txt_keys)
, service(service)
, service_dn(service_dn)
, protocol(protocol)
{
assert(!service.empty() && replyfn);
create_request();
}
LookupUdpSocket(Udp::TxtKeys txt_keys
, std::string service
, std::string service_dn
, std::string protocol
, Udp::ReplyFn replyfn
, const boost::asio::ip::address& multicast_address
, std::shared_ptr< boost::asio::io_service > io_service)
: UdpUdpSocket(replyfn, multicast_address, io_service)
, txt_keys(txt_keys)
, service(service)
, service_dn(service_dn)
, protocol(protocol)
{
assert(!service.empty() && replyfn);
create_request();
}
const Udp::TxtKeys get_txt_keys() const { return txt_keys; }
const std::string get_service() const { return service; }
const std::string get_service_dn() const { return service_dn; }
protected:
SharedUdpSession create_session() const override;
void create_request()
{
requests.clear();
// create PTR request
if (auto rqst = UdpRequest::make_PTR(service, protocol); rqst)
requests.push_back(std::move(rqst.get()));
}
boost::optional<UdpRequest> request;
Udp::TxtKeys txt_keys;
std::string service;
std::string service_dn;
std::string protocol;
};
class ResolveUdpUdpSocket : public UdpUdpSocket
{
public:
ResolveUdpUdpSocket(const std::string& hostname
, Udp::ReplyFn replyfn
, const boost::asio::ip::address& multicast_address
, const boost::asio::ip::address& interface_address
, std::shared_ptr< boost::asio::io_service > io_service)
: UdpUdpSocket(replyfn, multicast_address, interface_address, io_service)
, hostname(hostname)
{
assert(!hostname.empty() && replyfn);
create_requests();
}
ResolveUdpUdpSocket(const std::string& hostname
, Udp::ReplyFn replyfn
, const boost::asio::ip::address& multicast_address
, std::shared_ptr< boost::asio::io_service > io_service)
: UdpUdpSocket(replyfn, multicast_address, io_service)
, hostname(hostname)
{
assert(!hostname.empty() && replyfn);
create_requests();
}
std::string get_hostname() const { return hostname; }
protected:
SharedUdpSession create_session() const override;
void create_requests()
{
requests.clear();
// UdpRequest::make_A / AAAA is now implemented to add .local correctly after the hostname.
// If that is unsufficient, we need to change make_A / AAAA and pass full hostname.
std::string trimmed_hostname = hostname;
if (size_t dot_pos = trimmed_hostname.find_first_of('.'); dot_pos != std::string::npos)
trimmed_hostname = trimmed_hostname.substr(0, dot_pos);
if (auto rqst = UdpRequest::make_A(trimmed_hostname); rqst)
requests.push_back(std::move(rqst.get()));
trimmed_hostname = hostname;
if (size_t dot_pos = trimmed_hostname.find_first_of('.'); dot_pos != std::string::npos)
trimmed_hostname = trimmed_hostname.substr(0, dot_pos);
if (auto rqst = UdpRequest::make_AAAA(trimmed_hostname); rqst)
requests.push_back(std::move(rqst.get()));
}
std::string hostname;
};
}
#endif