KiCad PCB EDA Suite
eda_dde.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2014-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include <condition_variable>
26 #include <mutex>
27 #include <thread>
28 
29 #include <eda_dde.h>
30 #include <kiway_player.h>
31 #include <id.h>
32 
33 #include <wx/crt.h>
34 
35 
36 static const wxString HOSTNAME( wxT( "localhost" ) );
37 
38 // buffer for read and write data in socket connections
39 #define IPC_BUF_SIZE 4096
41 
42 
43 void KIWAY_PLAYER::CreateServer( int service, bool local )
44 {
45  wxIPV4address addr;
46 
47  // Set the port number
48  addr.Service( service );
49 
50  // Listen on localhost only if requested
51  if( local )
52  addr.Hostname( HOSTNAME );
53 
54  delete m_socketServer;
55  m_socketServer = new wxSocketServer( addr );
56 
57  m_socketServer->SetNotify( wxSOCKET_CONNECTION_FLAG );
58  m_socketServer->SetEventHandler( *this, ID_EDA_SOCKET_EVENT_SERV );
59  m_socketServer->Notify( true );
60 }
61 
62 
63 void KIWAY_PLAYER::OnSockRequest( wxSocketEvent& evt )
64 {
65  size_t len;
66  wxSocketBase* sock = evt.GetSocket();
67 
68  switch( evt.GetSocketEvent() )
69  {
70  case wxSOCKET_INPUT:
71  sock->Read( client_ipc_buffer, 1 );
72 
73  if( sock->LastCount() == 0 )
74  break; // No data, occurs on opening connection
75 
76  sock->Read( client_ipc_buffer + 1, IPC_BUF_SIZE - 2 );
77  len = 1 + sock->LastCount();
78  client_ipc_buffer[len] = 0;
80  break;
81 
82  case wxSOCKET_LOST:
83  return;
84  break;
85 
86  default:
87  wxPrintf( wxT( "EDA_DRAW_FRAME::OnSockRequest() error: Invalid event !" ) );
88  break;
89  }
90 }
91 
92 
93 void KIWAY_PLAYER::OnSockRequestServer( wxSocketEvent& evt )
94 {
95  wxSocketBase* socket;
96  wxSocketServer* server = (wxSocketServer*) evt.GetSocket();
97 
98  socket = server->Accept();
99 
100  if( socket == nullptr )
101  return;
102 
103  m_sockets.push_back( socket );
104 
105  socket->Notify( true );
106  socket->SetEventHandler( *this, ID_EDA_SOCKET_EVENT );
107  socket->SetNotify( wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG );
108 }
109 
110 
121 {
122 public:
124  m_messageReady( false ),
125  m_shutdown( false )
126  {
127  // Do a dummy Connect so that wx will set up the socket stuff on the main thread, which is
128  // required even if you later make socket connections on another thread.
129  wxSocketClient* client = new wxSocketClient;
130  wxIPV4address addr;
131 
132  addr.Hostname( HOSTNAME );
133  addr.Service( KICAD_PCB_PORT_SERVICE_NUMBER );
134 
135  client->Connect( addr, false );
136 
137  client->Close();
138  client->Destroy();
139 
140  m_thread = std::thread( &ASYNC_SOCKET_HOLDER::worker, this );
141  }
142 
144  {
145  {
146  std::lock_guard<std::mutex> lock( m_mutex );
147  m_shutdown = true;
148  }
149 
150  m_cv.notify_one();
151 
152  try
153  {
154  if( m_thread.joinable() )
155  m_thread.join();
156  }
157  catch( ... )
158  {
159  }
160  }
161 
169  bool Send( int aService, const std::string& aMessage )
170  {
171  if( m_messageReady )
172  return false;
173 
174  std::lock_guard<std::mutex> lock( m_mutex );
175 
176  m_message = std::make_pair( aService, aMessage );
177  m_messageReady = true;
178  m_cv.notify_one();
179 
180  return true;
181  }
182 
183 private:
187  void worker()
188  {
189  int port;
190  std::string message;
191 
192  std::unique_lock<std::mutex> lock( m_mutex );
193 
194  while( !m_shutdown )
195  {
196  m_cv.wait( lock, [&]() { return m_messageReady || m_shutdown; } );
197 
198  if( m_shutdown )
199  break;
200 
201  port = m_message.first;
202  message = m_message.second;
203 
204  lock.unlock();
205 
206  wxSocketClient* sock_client;
207  wxIPV4address addr;
208 
209  // Create a connection
210  addr.Hostname( HOSTNAME );
211  addr.Service( port );
212 
213  // Mini-tutorial for Connect() :-)
214  // (JP CHARRAS Note: see wxWidgets: sockets/client.cpp sample)
215  // ---------------------------
216  //
217  // There are two ways to use Connect(): blocking and non-blocking,
218  // depending on the value passed as the 'wait' (2nd) parameter.
219  //
220  // Connect(addr, true) will wait until the connection completes,
221  // returning true on success and false on failure. This call blocks
222  // the GUI (this might be changed in future releases to honor the
223  // wxSOCKET_BLOCK flag).
224  //
225  // Connect(addr, false) will issue a nonblocking connection request
226  // and return immediately. If the return value is true, then the
227  // connection has been already successfully established. If it is
228  // false, you must wait for the request to complete, either with
229  // WaitOnConnect() or by watching wxSOCKET_CONNECTION / LOST
230  // events (please read the documentation).
231  //
232  // WaitOnConnect() itself never blocks the GUI (this might change
233  // in the future to honor the wxSOCKET_BLOCK flag). This call will
234  // return false on timeout, or true if the connection request
235  // completes, which in turn might mean:
236  //
237  // a) That the connection was successfully established
238  // b) That the connection request failed (for example, because
239  // it was refused by the peer.
240  //
241  // Use IsConnected() to distinguish between these two.
242  //
243  // So, in a brief, you should do one of the following things:
244  //
245  // For blocking Connect:
246  //
247  // bool success = client->Connect(addr, true);
248  //
249  // For nonblocking Connect:
250  //
251  // client->Connect(addr, false);
252  //
253  // bool waitmore = true;
254  // while (! client->WaitOnConnect(seconds, millis) && waitmore )
255  // {
256  // // possibly give some feedback to the user,
257  // // update waitmore if needed.
258  // }
259  // bool success = client->IsConnected();
260  //
261  // And that's all :-)
262 
263  sock_client = new wxSocketClient( wxSOCKET_BLOCK );
264  sock_client->SetTimeout( 1 ); // Time out in Seconds
265  sock_client->Connect( addr, false );
266  sock_client->WaitOnConnect( 0, 250 );
267 
268  if( sock_client->Ok() && sock_client->IsConnected() )
269  {
270  sock_client->SetFlags( wxSOCKET_NOWAIT /*wxSOCKET_WAITALL*/ );
271  sock_client->Write( message.c_str(), message.length() );
272  }
273 
274  sock_client->Close();
275  sock_client->Destroy();
276 
277  m_messageReady = false;
278 
279  lock.lock();
280  }
281  }
282 
283  std::thread m_thread;
284  std::pair<int, std::string> m_message;
286  mutable std::mutex m_mutex;
287  std::condition_variable m_cv;
289 };
290 
291 
292 std::unique_ptr<ASYNC_SOCKET_HOLDER> socketHolder = nullptr;
293 
294 
304 bool SendCommand( int aService, const std::string& aMessage )
305 {
306  if( !socketHolder )
307  socketHolder.reset( new ASYNC_SOCKET_HOLDER() );
308 
309  return socketHolder->Send( aService, aMessage );
310 }
311 
312 
314 {
315  if( socketHolder )
316  socketHolder.reset();
317 }
bool Send(int aService, const std::string &aMessage)
Attempt to send a message if the thread is available.
Definition: eda_dde.cpp:169
#define KICAD_PCB_PORT_SERVICE_NUMBER
< Pcbnew listens on this port for commands from Eeschema
Definition: eda_dde.h:40
DDE server & client.
#define IPC_BUF_SIZE
Definition: eda_dde.cpp:39
bool SendCommand(int aService, const std::string &aMessage)
Used by a client to sent (by a socket connection) a data to a server.
Definition: eda_dde.cpp:304
Spin up a thread to send messages via a socket.
Definition: eda_dde.cpp:120
static char client_ipc_buffer[IPC_BUF_SIZE]
Definition: eda_dde.cpp:40
void CreateServer(int service, bool local=true)
Definition: eda_dde.cpp:43
std::mutex m_mutex
Definition: eda_dde.cpp:286
std::thread m_thread
Definition: eda_dde.cpp:283
void SocketCleanup()
Definition: eda_dde.cpp:313
virtual void ExecuteRemoteCommand(const char *cmdline)
Execute a remote command sent via socket (to port KICAD_PCB_PORT_SERVICE_NUMBER, currently 4242).
Definition: kiway_player.h:196
static const wxString HOSTNAME(wxT("localhost"))
void OnSockRequest(wxSocketEvent &evt)
Definition: eda_dde.cpp:63
std::condition_variable m_cv
Definition: eda_dde.cpp:287
void OnSockRequestServer(wxSocketEvent &evt)
Definition: eda_dde.cpp:93
std::pair< int, std::string > m_message
Definition: eda_dde.cpp:284
wxSocketServer * m_socketServer
Definition: kiway_player.h:218
void worker()
Actual task that sends data to the socket server.
Definition: eda_dde.cpp:187
std::vector< wxSocketBase * > m_sockets
interprocess communication
Definition: kiway_player.h:219
std::unique_ptr< ASYNC_SOCKET_HOLDER > socketHolder
Definition: eda_dde.cpp:292