2023-06-10 10:14:12 +08:00
# include "slic3r/Utils/Bonjour.hpp" // On Windows, boost needs to be included before wxWidgets headers
# include "BonjourDialog.hpp"
# include <set>
# include <mutex>
# include <boost/nowide/convert.hpp>
# include <wx/sizer.h>
# include <wx/button.h>
# include <wx/listctrl.h>
# include <wx/stattext.h>
# include <wx/timer.h>
# include <wx/wupdlock.h>
# include "slic3r/GUI/GUI.hpp"
# include "slic3r/GUI/GUI_App.hpp"
# include "slic3r/GUI/I18N.hpp"
# include "slic3r/GUI/format.hpp"
# include "slic3r/Utils/Bonjour.hpp"
2023-07-25 09:50:35 +08:00
// 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 ) ;
}
2023-06-10 10:14:12 +08:00
namespace Slic3r {
class BonjourReplyEvent : public wxEvent
{
public :
BonjourReply reply ;
BonjourReplyEvent ( wxEventType eventType , int winid , BonjourReply & & reply ) :
wxEvent ( winid , eventType ) ,
reply ( std : : move ( reply ) )
{ }
virtual wxEvent * Clone ( ) const
{
return new BonjourReplyEvent ( * this ) ;
}
} ;
wxDEFINE_EVENT ( EVT_BONJOUR_REPLY , BonjourReplyEvent ) ;
wxDECLARE_EVENT ( EVT_BONJOUR_COMPLETE , wxCommandEvent ) ;
wxDEFINE_EVENT ( EVT_BONJOUR_COMPLETE , wxCommandEvent ) ;
class ReplySet : public std : : set < BonjourReply > { } ;
struct LifetimeGuard
{
std : : mutex mutex ;
BonjourDialog * dialog ;
LifetimeGuard ( BonjourDialog * dialog ) : dialog ( dialog ) { }
} ;
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 )
, label ( new wxStaticText ( this , wxID_ANY , " " ) )
, timer ( new wxTimer ( ) )
, timer_state ( 0 )
, tech ( tech )
{
const int em = GUI : : wxGetApp ( ) . em_unit ( ) ;
list - > SetMinSize ( wxSize ( 80 * em , 30 * em ) ) ;
wxBoxSizer * vsizer = new wxBoxSizer ( wxVERTICAL ) ;
vsizer - > Add ( label , 0 , wxEXPAND | wxTOP | wxLEFT | wxRIGHT , em ) ;
list - > SetSingleStyle ( wxLC_SINGLE_SEL ) ;
list - > SetSingleStyle ( wxLC_SORT_DESCENDING ) ;
2023-07-25 09:50:35 +08:00
//B29
list - > AppendColumn ( _ ( L ( " Address " ) ) , wxLIST_FORMAT_LEFT , 10 * em ) ;
list - > AppendColumn ( _ ( L ( " Hostname " ) ) , wxLIST_FORMAT_LEFT , 15 * em ) ;
2023-06-10 10:14:12 +08:00
list - > AppendColumn ( _ ( L ( " Service name " ) ) , wxLIST_FORMAT_LEFT , 20 * em ) ;
if ( tech = = ptFFF ) {
list - > AppendColumn ( _ ( L ( " OctoPrint version " ) ) , wxLIST_FORMAT_LEFT , 5 * em ) ;
}
vsizer - > Add ( list , 1 , wxEXPAND | wxALL , em ) ;
wxBoxSizer * button_sizer = new wxBoxSizer ( wxHORIZONTAL ) ;
button_sizer - > Add ( new wxButton ( this , wxID_OK , " OK " ) , 0 , wxALL , em ) ;
button_sizer - > Add ( new wxButton ( this , wxID_CANCEL , " Cancel " ) , 0 , wxALL , em ) ;
// ^ Note: The Ok/Cancel labels are translated by wxWidgets
vsizer - > Add ( button_sizer , 0 , wxALIGN_CENTER ) ;
SetSizerAndFit ( vsizer ) ;
Bind ( EVT_BONJOUR_REPLY , & BonjourDialog : : on_reply , this ) ;
2023-07-25 09:50:35 +08:00
// 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 ) ) ;
}
} ) ;
2023-06-10 10:14:12 +08:00
Bind ( wxEVT_TIMER , & BonjourDialog : : on_timer , this ) ;
GUI : : wxGetApp ( ) . UpdateDlgDarkUI ( this ) ;
}
BonjourDialog : : ~ BonjourDialog ( )
{
// Needed bacuse of forward defs
}
bool BonjourDialog : : show_and_lookup ( )
{
Show ( ) ; // Because we need GetId() to work before ShowModal()
timer - > Stop ( ) ;
timer - > SetOwner ( this ) ;
timer_state = 1 ;
timer - > Start ( 1000 ) ;
on_timer_process ( ) ;
2023-07-25 09:50:35 +08:00
// 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 ) ;
2023-06-10 10:14:12 +08:00
// 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).
// Here we put the pointer under a shared_ptr and protect it by a mutex,
// so that both threads can access it safely.
auto dguard = std : : make_shared < LifetimeGuard > ( this ) ;
// Note: More can be done here when we support discovery of hosts other than Octoprint and SL1
Bonjour : : TxtKeys txt_keys { " version " , " model " } ;
bonjour = Bonjour ( " octoprint " )
. set_txt_keys ( std : : move ( txt_keys ) )
. set_retries ( 3 )
. set_timeout ( 4 )
. on_reply ( [ dguard ] ( BonjourReply & & reply ) {
std : : lock_guard < std : : mutex > lock_guard ( dguard - > mutex ) ;
auto dialog = dguard - > dialog ;
if ( dialog ! = nullptr ) {
auto evt = new BonjourReplyEvent ( EVT_BONJOUR_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_BONJOUR_COMPLETE , dialog - > GetId ( ) ) ;
wxQueueEvent ( dialog , evt ) ;
}
} )
. lookup ( ) ;
bool res = ShowModal ( ) = = wxID_OK & & list - > GetFirstSelected ( ) > = 0 ;
{
// Tell the background thread the dialog is going away...
std : : lock_guard < std : : mutex > lock_guard ( dguard - > mutex ) ;
dguard - > dialog = nullptr ;
}
return res ;
}
wxString BonjourDialog : : get_selected ( ) const
{
auto sel = list - > GetFirstSelected ( ) ;
return sel > = 0 ? list - > GetItemText ( sel ) : wxString ( ) ;
}
// Private
void BonjourDialog : : on_reply ( BonjourReplyEvent & e )
{
if ( replies - > find ( e . reply ) ! = 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 ;
}
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 : * replies ) {
auto item = list - > InsertItem ( 0 , reply . full_address ) ;
list - > SetItem ( item , 1 , reply . hostname ) ;
list - > SetItem ( item , 2 , reply . service_name ) ;
if ( tech = = ptFFF ) {
const auto it = reply . txt_data . find ( " version " ) ;
if ( it ! = reply . txt_data . end ( ) ) {
list - > SetItem ( item , 3 , GUI : : from_u8 ( it - > second ) ) ;
}
}
}
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 & )
{
on_timer_process ( ) ;
}
// This is here so the function can be bound to wxEVT_TIMER and also called
// explicitly (wxTimerEvent should not be created by user code).
void BonjourDialog : : on_timer_process ( )
{
const auto search_str = _L ( " Searching for devices " ) ;
if ( timer_state > 0 ) {
const std : : string dots ( timer_state , ' . ' ) ;
label - > SetLabel ( search_str + dots ) ;
timer_state = ( timer_state ) % 3 + 1 ;
} else {
label - > SetLabel ( search_str + " : " + _L ( " Finished " ) + " . " ) ;
timer - > Stop ( ) ;
}
}
IPListDialog : : IPListDialog ( wxWindow * parent , const wxString & hostname , const std : : vector < boost : : asio : : ip : : address > & ips , size_t & selected_index )
: wxDialog ( parent , wxID_ANY , _ ( L ( " Multiple resolved IP addresses " ) ) , wxDefaultPosition , wxDefaultSize , wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER )
, m_list ( new wxListView ( this , wxID_ANY , wxDefaultPosition , wxDefaultSize , wxLC_REPORT | wxSIMPLE_BORDER ) )
, m_selected_index ( selected_index )
{
const int em = GUI : : wxGetApp ( ) . em_unit ( ) ;
m_list - > SetMinSize ( wxSize ( 40 * em , 30 * em ) ) ;
wxBoxSizer * vsizer = new wxBoxSizer ( wxVERTICAL ) ;
auto * label = new wxStaticText ( this , wxID_ANY , GUI : : format_wxstr ( _L ( " There are several IP addresses resolving to hostname %1%. \n Please select one that should be used. " ) , hostname ) ) ;
vsizer - > Add ( label , 0 , wxEXPAND | wxTOP | wxLEFT | wxRIGHT , em ) ;
m_list - > SetSingleStyle ( wxLC_SINGLE_SEL ) ;
m_list - > AppendColumn ( _ ( L ( " Address " ) ) , wxLIST_FORMAT_LEFT , 40 * em ) ;
for ( size_t i = 0 ; i < ips . size ( ) ; i + + )
m_list - > InsertItem ( i , boost : : nowide : : widen ( ips [ i ] . to_string ( ) ) ) ;
m_list - > Select ( 0 ) ;
vsizer - > Add ( m_list , 1 , wxEXPAND | wxALL , em ) ;
wxBoxSizer * button_sizer = new wxBoxSizer ( wxHORIZONTAL ) ;
button_sizer - > Add ( new wxButton ( this , wxID_OK , " OK " ) , 0 , wxALL , em ) ;
button_sizer - > Add ( new wxButton ( this , wxID_CANCEL , " Cancel " ) , 0 , wxALL , em ) ;
vsizer - > Add ( button_sizer , 0 , wxALIGN_CENTER ) ;
SetSizerAndFit ( vsizer ) ;
GUI : : wxGetApp ( ) . UpdateDlgDarkUI ( this ) ;
}
IPListDialog : : ~ IPListDialog ( )
{
}
void IPListDialog : : EndModal ( int retCode )
{
if ( retCode = = wxID_OK ) {
m_selected_index = ( size_t ) m_list - > GetFirstSelected ( ) ;
}
wxDialog : : EndModal ( retCode ) ;
}
}