Skip to content

Commit 3de505b

Browse files
committed
PROTOTYPE: connect to existing Matlab sessions
1 parent 88baca6 commit 3de505b

File tree

5 files changed

+60
-45
lines changed

5 files changed

+60
-45
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,6 @@ rebuildTags.sh
3737

3838
# caused py setup.py develop
3939
pymatbridge.egg-info
40+
41+
# so vim-ers can use "YouCompleteMe"
42+
.ycm_extra_conf.py

pymatbridge/matlab/matlabserver.m

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
function matlabserver(socket_address)
1+
function matlabserver(socket_address, exit_when_done)
2+
%MATLABSERVER Take Commands from Python via ZMQ
23
% This function takes a socket address as input and initiates a ZMQ session
34
% over the socket. I then enters the listen-respond mode until it gets an
45
% "exit" command
56

67
json_startup
78
messenger('init', socket_address);
89

9-
c=onCleanup(@()exit);
10+
if nargin > 1 && exit_when_done
11+
c = onCleanup(@stop_messenger_and_exit);
12+
else
13+
c = onCleanup(@stop_messenger);
14+
end
1015

1116
while(1)
1217
msg_in = messenger('listen');
@@ -17,7 +22,6 @@ function matlabserver(socket_address)
1722
messenger('respond', 'connected');
1823

1924
case {'exit'}
20-
messenger('exit');
2125
break;
2226

2327
case {'eval'}
@@ -29,3 +33,14 @@ function matlabserver(socket_address)
2933
end
3034

3135
end
36+
37+
end %matlabserver
38+
39+
function stop_messenger_and_exit()
40+
messenger('exit');
41+
exit;
42+
end
43+
44+
function stop_messenger()
45+
messenger('exit');
46+
end
-683 KB
Binary file not shown.

pymatbridge/messenger/src/messenger.c

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -48,61 +48,58 @@ void cleanup (void) {
4848
mexPrintf("Socket closed\n");
4949
zmq_term(ctx);
5050
mexPrintf("Context terminated\n");
51+
initialized = 0;
5152
}
5253

5354

5455
/* Gateway function with Matlab */
5556
void mexFunction(int nlhs, mxArray *plhs[],
5657
int nrhs, const mxArray *prhs[]) {
5758
char *cmd;
59+
mxLogical *p;
5860
/* If no input argument, print out the usage */
5961
if (nrhs == 0) {
6062
mexErrMsgTxt("Usage: messenger('init|listen|respond', extra1, extra2, ...)");
6163
}
62-
64+
if (nlhs > 1) {
65+
mexErrMsgTxt("messenger: too many outputs, I return a single bool");
66+
}
6367
/* Get the input command */
6468
if(!(cmd = mxArrayToString(prhs[0]))) {
6569
mexErrMsgTxt("Cannot read the command");
6670
}
67-
71+
/* we'll return true on completion of valid command, else false */
72+
plhs[0] = mxCreateLogicalMatrix(1, 1);
73+
p = mxGetLogicals(plhs[0]);
74+
p[0] = 0;
6875
/* Initialize a new server session */
6976
if (strcmp(cmd, "init") == 0) {
7077
char *socket_addr;
71-
mxLogical *p;
72-
7378
/* Check if the input format is valid */
7479
if (nrhs != 2) {
75-
mexErrMsgTxt("Missing argument: socket address");
80+
mexErrMsgTxt("Usage: messenger('init', socket_addr)");
7681
}
7782
if (!(socket_addr = mxArrayToString(prhs[1]))) {
7883
mexErrMsgTxt("Cannot read socket address");
7984
}
80-
81-
plhs[0] = mxCreateLogicalMatrix(1, 1);
82-
p = mxGetLogicals(plhs[0]);
83-
8485
if (!initialized) {
8586
if (!initialize(socket_addr)) {
8687
p[0] = 1;
8788
mexPrintf("Socket created at: %s\n", socket_addr);
8889
} else {
89-
p[0] = 0;
9090
mexErrMsgTxt("Socket creation failed.");
9191
}
9292
} else {
9393
mexErrMsgTxt("One socket has already been initialized.");
9494
}
95-
96-
return;
97-
9895
/* Listen over an existing socket */
9996
} else if (strcmp(cmd, "listen") == 0) {
10097
int byte_recvd;
10198
char *recv_buffer = mxCalloc(BUFLEN, sizeof(char));
10299
zmq_pollitem_t polls[] = {{socket_ptr, 0, ZMQ_POLLIN, 0}};
103-
100+
104101
if (!checkInitialized()) return;
105-
102+
106103
/* allow MATLAB to draw its graphics every 20ms */
107104
while (zmq_poll(polls, 1, 20000) == 0) {
108105
mexEvalString("drawnow");
@@ -116,12 +113,10 @@ void mexFunction(int nlhs, mxArray *plhs[],
116113
} else if (byte_recvd > BUFLEN){
117114
mexErrMsgTxt("Receiver buffer overflow. Message truncated");
118115
} else {
119-
sprintf(recv_buffer, "Failed to receive a message due to ZMQ error %s", strerror(errno));
116+
sprintf(recv_buffer, "Failed to receive a message due to ZMQ error %s", strerror(errno));
120117
mexErrMsgTxt(recv_buffer);
121118
}
122-
123-
return;
124-
119+
p[0] = 1;
125120
/* Send a message out */
126121
} else if (strcmp(cmd, "respond") == 0) {
127122
size_t msglen;
@@ -141,21 +136,17 @@ void mexFunction(int nlhs, mxArray *plhs[],
141136
plhs[0] = mxCreateLogicalMatrix(1, 1);
142137
p = mxGetLogicals(plhs[0]);
143138

144-
if (msglen == zmq_send(socket_ptr, msg_out, msglen, 0)) {
139+
if (msglen == (size_t) zmq_send(socket_ptr, msg_out, msglen, 0)) {
145140
p[0] = 1;
146141
} else {
147-
p[0] = 0;
148142
mexErrMsgTxt("Failed to send message due to ZMQ error");
149143
}
150-
151-
return;
152-
153144
/* Close the socket and context */
154145
} else if (strcmp(cmd, "exit") == 0) {
155146
cleanup();
156-
157-
return;
147+
p[0] = 1;
158148
} else {
159149
mexErrMsgTxt("Unidentified command");
160150
}
151+
return;
161152
}

pymatbridge/pymatbridge.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ class _Session(object):
120120
this directly; rather, use the Matlab or Octave subclasses.
121121
"""
122122

123-
def __init__(self, executable, socket_addr=None,
124-
id='python-matlab-bridge', log=False, maxtime=60,
123+
def __init__(self, executable=None, socket_addr=None,
124+
id='python-matlab-bridge', log=False, maxtime=15,
125125
platform=None, startup_options=None,
126126
loglevel=logging.WARNING):
127127
"""
@@ -146,7 +146,7 @@ def __init__(self, executable, socket_addr=None,
146146
147147
maxtime : float
148148
The maximal time to wait for a response from the session (optional,
149-
Default is 10 sec)
149+
Default is 15 sec)
150150
151151
platform : string
152152
The OS of the machine on which this is running. Per default this
@@ -186,7 +186,7 @@ def __init__(self, executable, socket_addr=None,
186186
atexit.register(self.stop)
187187

188188
def _program_name(self): # pragma: no cover
189-
raise NotImplemented
189+
raise NotImplementedError
190190

191191
def _preamble_code(self):
192192
# suppress warnings while loading the path, in the case of
@@ -199,7 +199,7 @@ def _preamble_code(self):
199199
"cd('%s');" % os.getcwd()]
200200

201201
def _execute_flag(self): # pragma: no cover
202-
raise NotImplemented
202+
raise NotImplementedError
203203

204204
def _run_server(self):
205205
code = self._preamble_code()
@@ -221,10 +221,14 @@ def start(self):
221221
self.socket_addr = self.socket_addr + ":%s"%rndport
222222

223223
# Start the MATLAB server in a new process
224-
self.logger.info("Starting %s on ZMQ socket %s" \
225-
% (self._program_name(), self.socket_addr))
224+
self.logger.info("Starting ZMQ socket %s" \
225+
% (self.socket_addr, ))
226226
self.logger.info("Send 'exit' command to kill the server")
227-
self._run_server()
227+
228+
if self.executable is not None:
229+
self.logger.info("Launching %s, sending ZMQ 'exit' will kill it." \
230+
% (self._program_name(), ))
231+
self._run_server()
228232

229233
# Start the client
230234
self.socket.connect(self.socket_addr)
@@ -426,8 +430,9 @@ def _bind_method(self, name, unconditionally=False):
426430

427431
class Matlab(_Session):
428432
def __init__(self, executable='matlab', socket_addr=None,
429-
id='python-matlab-bridge', log=False, maxtime=60,
430-
platform=None, startup_options=None):
433+
id='python-matlab-bridge', log=False, maxtime=15,
434+
platform=None, startup_options=None,
435+
loglevel=logging.WARNING):
431436
"""
432437
Initialize this thing.
433438
@@ -436,7 +441,7 @@ def __init__(self, executable='matlab', socket_addr=None,
436441
437442
executable : str
438443
A string that would start Matlab at the terminal. Per default, this
439-
is set to 'matlab', so that you can alias in your bash setup
444+
is set to 'matlab'.
440445
441446
socket_addr : str
442447
A string that represents a valid ZMQ socket address, such as
@@ -450,7 +455,7 @@ def __init__(self, executable='matlab', socket_addr=None,
450455
451456
maxtime : float
452457
The maximal time to wait for a response from matlab (optional,
453-
Default is 10 sec)
458+
Default is 15 sec)
454459
455460
platform : string
456461
The OS of the machine on which this is running. Per default this
@@ -470,7 +475,7 @@ def __init__(self, executable='matlab', socket_addr=None,
470475
if log:
471476
startup_options += ' -logfile ./pymatbridge/logs/matlablog_%s.txt' % id
472477
super(Matlab, self).__init__(executable, socket_addr, id, log, maxtime,
473-
platform, startup_options)
478+
platform, startup_options, loglevel)
474479

475480
def _program_name(self):
476481
return 'MATLAB'
@@ -481,8 +486,9 @@ def _execute_flag(self):
481486

482487
class Octave(_Session):
483488
def __init__(self, executable='octave', socket_addr=None,
484-
id='python-matlab-bridge', log=False, maxtime=60,
485-
platform=None, startup_options=None):
489+
id='python-matlab-bridge', log=False, maxtime=15,
490+
platform=None, startup_options=None,
491+
loglevel=logging.WARNING):
486492
"""
487493
Initialize this thing.
488494
@@ -505,7 +511,7 @@ def __init__(self, executable='octave', socket_addr=None,
505511
506512
maxtime : float
507513
The maximal time to wait for a response from octave (optional,
508-
Default is 10 sec)
514+
Default is 15 sec)
509515
510516
platform : string
511517
The OS of the machine on which this is running. Per default this

0 commit comments

Comments
 (0)