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