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 KiCad Developers, see CHANGELOG.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 <eda_draw_frame.h>
31 #include <id.h>
32 
33 #include <wx/wx.h>
34 
35 static const wxString HOSTNAME( wxT( "localhost" ) );
36 
37 // buffer for read and write data in socket connections
38 #define IPC_BUF_SIZE 4096
40 
41 
42 /**********************************/
43 /* Routines related to the server */
44 /**********************************/
45 
46 /* Function to initialize a server socket
47  */
48 void EDA_DRAW_FRAME::CreateServer( int service, bool local )
49 {
50  wxIPV4address addr;
51 
52  // Set the port number
53  addr.Service( service );
54 
55  // Listen on localhost only if requested
56  if( local )
57  addr.Hostname( HOSTNAME );
58 
59  delete m_socketServer;
60  m_socketServer = new wxSocketServer( addr );
61 
62  m_socketServer->SetNotify( wxSOCKET_CONNECTION_FLAG );
63  m_socketServer->SetEventHandler( *this, ID_EDA_SOCKET_EVENT_SERV );
64  m_socketServer->Notify( true );
65 }
66 
67 
68 /* Function called on every client request.
69  */
70 void EDA_DRAW_FRAME::OnSockRequest( wxSocketEvent& evt )
71 {
72  size_t len;
73  wxSocketBase* sock = evt.GetSocket();
74 
75  switch( evt.GetSocketEvent() )
76  {
77  case wxSOCKET_INPUT:
78  sock->Read( client_ipc_buffer, 1 );
79 
80  if( sock->LastCount() == 0 )
81  break; // No data, occurs on opening connection
82 
83  sock->Read( client_ipc_buffer + 1, IPC_BUF_SIZE - 2 );
84  len = 1 + sock->LastCount();
85  client_ipc_buffer[len] = 0;
87  break;
88 
89  case wxSOCKET_LOST:
90  return;
91  break;
92 
93  default:
94  wxPrintf( wxT( "EDA_DRAW_FRAME::OnSockRequest() error: Invalid event !" ) );
95  break;
96  }
97 }
98 
99 
100 /* Function called when a connection is requested by a client.
101  */
102 void EDA_DRAW_FRAME::OnSockRequestServer( wxSocketEvent& evt )
103 {
104  wxSocketBase* socket;
105  wxSocketServer* server = (wxSocketServer*) evt.GetSocket();
106 
107  socket = server->Accept();
108 
109  if( socket == NULL )
110  return;
111 
112  m_sockets.push_back( socket );
113 
114  socket->Notify( true );
115  socket->SetEventHandler( *this, ID_EDA_SOCKET_EVENT );
116  socket->SetNotify( wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG );
117 }
118 
119 
120 /**********************************/
121 /* Routines related to the CLIENT */
122 /**********************************/
123 
133 {
134 public:
136  m_messageReady( false ),
137  m_shutdown( false )
138  {
139  // Do a dummy Connect so that wx will set up the socket stuff on the main thread, which is
140  // required even if you later make socket connections on another thread.
141  wxSocketClient* client = new wxSocketClient;
142  wxIPV4address addr;
143 
144  addr.Hostname( HOSTNAME );
145  addr.Service( KICAD_PCB_PORT_SERVICE_NUMBER );
146 
147  client->Connect( addr, false );
148 
149  client->Close();
150  client->Destroy();
151 
152  m_thread = std::thread( &ASYNC_SOCKET_HOLDER::worker, this );
153  }
154 
156  {
157  {
158  std::lock_guard<std::mutex> lock( m_mutex );
159  m_shutdown = true;
160  }
161 
162  m_cv.notify_one();
163 
164  try
165  {
166  if( m_thread.joinable() )
167  m_thread.join();
168  }
169  catch( ... )
170  {
171  }
172  }
173 
180  bool Send( int aService, const std::string& aMessage )
181  {
182  if( m_messageReady )
183  return false;
184 
185  std::lock_guard<std::mutex> lock( m_mutex );
186 
187  m_message = std::make_pair( aService, aMessage );
188  m_messageReady = true;
189  m_cv.notify_one();
190 
191  return true;
192  }
193 
194 private:
198  void worker()
199  {
200  int port;
201  std::string message;
202 
203  std::unique_lock<std::mutex> lock( m_mutex );
204 
205  while( !m_shutdown )
206  {
207  m_cv.wait( lock, [&]() { return m_messageReady || m_shutdown; } );
208 
209  if( m_shutdown )
210  break;
211 
212  port = m_message.first;
213  message = m_message.second;
214 
215  lock.unlock();
216 
217  wxSocketClient* sock_client;
218  wxIPV4address addr;
219 
220  // Create a connexion
221  addr.Hostname( HOSTNAME );
222  addr.Service( port );
223 
224  // Mini-tutorial for Connect() :-)
225  // (JP CHARRAS Note: see wxWidgets: sockets/client.cpp sample)
226  // ---------------------------
227  //
228  // There are two ways to use Connect(): blocking and non-blocking,
229  // depending on the value passed as the 'wait' (2nd) parameter.
230  //
231  // Connect(addr, true) will wait until the connection completes,
232  // returning true on success and false on failure. This call blocks
233  // the GUI (this might be changed in future releases to honor the
234  // wxSOCKET_BLOCK flag).
235  //
236  // Connect(addr, false) will issue a nonblocking connection request
237  // and return immediately. If the return value is true, then the
238  // connection has been already successfully established. If it is
239  // false, you must wait for the request to complete, either with
240  // WaitOnConnect() or by watching wxSOCKET_CONNECTION / LOST
241  // events (please read the documentation).
242  //
243  // WaitOnConnect() itself never blocks the GUI (this might change
244  // in the future to honor the wxSOCKET_BLOCK flag). This call will
245  // return false on timeout, or true if the connection request
246  // completes, which in turn might mean:
247  //
248  // a) That the connection was successfully established
249  // b) That the connection request failed (for example, because
250  // it was refused by the peer.
251  //
252  // Use IsConnected() to distinguish between these two.
253  //
254  // So, in a brief, you should do one of the following things:
255  //
256  // For blocking Connect:
257  //
258  // bool success = client->Connect(addr, true);
259  //
260  // For nonblocking Connect:
261  //
262  // client->Connect(addr, false);
263  //
264  // bool waitmore = true;
265  // while (! client->WaitOnConnect(seconds, millis) && waitmore )
266  // {
267  // // possibly give some feedback to the user,
268  // // update waitmore if needed.
269  // }
270  // bool success = client->IsConnected();
271  //
272  // And that's all :-)
273 
274  sock_client = new wxSocketClient( wxSOCKET_BLOCK );
275  sock_client->SetTimeout( 1 ); // Time out in Seconds
276  sock_client->Connect( addr, false );
277  sock_client->WaitOnConnect( 0, 250 );
278 
279  if( sock_client->Ok() && sock_client->IsConnected() )
280  {
281  sock_client->SetFlags( wxSOCKET_NOWAIT /*wxSOCKET_WAITALL*/ );
282  sock_client->Write( message.c_str(), message.length() );
283  }
284 
285  sock_client->Close();
286  sock_client->Destroy();
287 
288  m_messageReady = false;
289 
290  lock.lock();
291  }
292  }
293 
294  std::thread m_thread;
295  std::pair<int, std::string> m_message;
297  mutable std::mutex m_mutex;
298  std::condition_variable m_cv;
300 };
301 
302 
303 std::unique_ptr<ASYNC_SOCKET_HOLDER> socketHolder = nullptr;
304 
305 /* Used by a client to sent (by a socket connection) a data to a server.
306  * - Open a Socket Client connection
307  * - Send the buffer cmdline
308  * - Close the socket connection
309  *
310  * service is the service number for the TC/IP connection
311  */
312 bool SendCommand( int aService, const std::string& aMessage )
313 {
314  if( !socketHolder )
315  socketHolder.reset( new ASYNC_SOCKET_HOLDER() );
316 
317  return socketHolder->Send( aService, aMessage );
318 }
319 
320 
322 {
323  if( socketHolder )
324  socketHolder.reset();
325 }
bool Send(int aService, const std::string &aMessage)
Attempts to send a message if the thread is available.
Definition: eda_dde.cpp:180
#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:38
void OnSockRequestServer(wxSocketEvent &evt)
Definition: eda_dde.cpp:102
std::vector< wxSocketBase * > m_sockets
interprocess communication
bool SendCommand(int aService, const std::string &aMessage)
Must be called to clean up the socket thread used by SendCommand.
Definition: eda_dde.cpp:312
Spins up a thread to send messages via a socket.
Definition: eda_dde.cpp:132
static char client_ipc_buffer[IPC_BUF_SIZE]
Definition: eda_dde.cpp:39
std::mutex m_mutex
Definition: eda_dde.cpp:297
std::thread m_thread
Definition: eda_dde.cpp:294
virtual void ExecuteRemoteCommand(const char *cmdline)
Execute a remote command sent via socket (to port KICAD_PCB_PORT_SERVICE_NUMBER, currently 4242).
void SocketCleanup()
Definition: eda_dde.cpp:321
#define NULL
static const wxString HOSTNAME(wxT("localhost"))
std::condition_variable m_cv
Definition: eda_dde.cpp:298
wxSocketServer * m_socketServer
void OnSockRequest(wxSocketEvent &evt)
Definition: eda_dde.cpp:70
std::pair< int, std::string > m_message
Definition: eda_dde.cpp:295
void CreateServer(int service, bool local=true)
Definition: eda_dde.cpp:48
void worker()
Actual task that sends data to the socket server.
Definition: eda_dde.cpp:198
std::unique_ptr< ASYNC_SOCKET_HOLDER > socketHolder
Definition: eda_dde.cpp:303