-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathMatlabServer.m
More file actions
400 lines (353 loc) · 12.9 KB
/
MatlabServer.m
File metadata and controls
400 lines (353 loc) · 12.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% MatlabServer
%
% This scripts starts a minimalistic MATLAB "server".
%
% When started, the server listens for connections at port 9999 or the
% port number specified by the environment variable 'MATLABSERVER_PORT'.
%
% Troubleshooting: If not working out of the box, add this will to the
% MATLAB path. Make sure InputStreamByteWrapper.class is in the same
% directory as this file!
%
% Requirements:
% This requires MATLAB with Java support, i.e. MATLAB v6 or higher.
%
% Author: Henrik Bengtsson, 2002-2014
%
% References:
% [1] http://www.mathworks.com/access/helpdesk/help/techdoc/
% matlab_external/ch_jav34.shtml#49439
% [2] http://staff.science.uva.nl/~horus/dox/horus2.0/user/
% html/n_installUnix.html
% [3] http://www.mathworks.com/access/helpdesk/help/toolbox/
% modelsim/a1057689278b4.html
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
disp('Running MatlabServer v3.1.2');
% addpath R/R_LIBS/linux/library/R.matlab/misc/
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% MATLAB version-dependent setup
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Identify major version of Matlab
hasMajor = eval('length(regexp(version, ''^[0-9]'')) ~= 0', '0');
if (hasMajor)
verParts = sscanf(version, '%d.');
verMajor = verParts(1);
else
verMajor = -1;
end
if (verMajor < 6)
% Java is not available/supported
error('MATLAB v5.x and below is not supported.');
elseif (verMajor == 6)
disp('MATLAB v6.x detected.');
% Default save option
saveOption = '';
% In MATLAB v6 only the static Java CLASSPATH is supported. It is
% specified by a 'classpath.txt' file. The default one can be found
% by which('classpath.txt'). If a 'classpath.txt' exists in the
% current(!) directory (that MATLAB is started from), it *replaces*
% the global one. Thus, it is not possible to add additional paths;
% the global ones has to be copied to the local 'classpath.txt' file.
%
% To do the above automatically from R, does not seem to be an option.
else
disp('MATLAB v7.x or higher detected.');
% MATLAB v7 and above saves compressed files, which is not recognized
% by R.matlab's readMat(); force saving in old format.
saveOption = '-V6';
disp('Saving with option -V6.');
% In MATLAB v7 and above both static and dynamic Java CLASSPATH:s exist.
% Using dynamic ones, it is possible to add the file
% InputStreamByteWrapper.class to CLASSPATH, given it is
% in the same directory as this script.
javaaddpath({fileparts(which('MatlabServer'))});
disp('Added InputStreamByteWrapper to dynamic Java CLASSPATH.');
end
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Import Java classes
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
import java.io.*;
import java.net.*;
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% If an old MATLAB server is running, close it
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% If a server object exists from a previous run, close it.
if (exist('server'))
close(server);
clear server;
end
% If an input stream exists from a previous run, close it.
if (exist('is'))
close(is);
clear is;
end
% If an output stream exists from a previous run, close it.
if (exist('os'))
close(os);
clear os;
end
fprintf(1, '----------------------\n');
fprintf(1, 'MATLAB server started!\n');
fprintf(1, '----------------------\n');
fprintf(1, 'MATLAB working directory: %s\n', pwd);
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Initiate server socket to which clients may connect
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
port = getenv('MATLABSERVER_PORT');
if (length(port) > 0)
port = str2num(port);
else
% Try to open a server socket on port 9999
port = 9999;
end
% Ports 1-1023 are reserved for the Internet Assigned Numbers Authority.
% Ports 49152-65535 are dynamic ports for the OS. [3]
if (port < 1023 | port > 65535)
error('Cannot not open connection. Port (''MATLABSERVER_PORT'') is out of range [1023,65535]: %d', port);
end
fprintf(1, 'Trying to open server socket (port %d)...', port);
server = java.net.ServerSocket(port);
fprintf(1, 'done.\n');
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Wait for client to connect
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Create a socket object from the ServerSocket to listen and accept
% connections.
% Open input and output streams
% Wait for the client to connect
fprintf(1, 'Waiting for client to connect (port %d)...', port);
clientSocket = accept(server);
fprintf(1, 'connected.\n');
% ...client connected.
is = java.io.DataInputStream(getInputStream(clientSocket));
%is = java.io.BufferedReader(InputStreamReader(is0));
os = java.io.DataOutputStream(getOutputStream(clientSocket));
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% The MATLAB server state machine
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Commands
commands = {'eval', 'send', 'receive', 'send-remote', 'receive-remote', 'echo'};
lasterr = [];
variables = [];
% As long as we receive data, echo that data back to the client.
state = 0;
while (state >= 0),
if (state == 0)
cmd = readByte(is);
fprintf(1, 'Received cmd: %d\n', cmd);
if (cmd < -1 | cmd > length(commands))
fprintf(1, 'Unknown command code: %d\n', cmd);
else
state = cmd;
end
%-------------------
% 'eval'
%-------------------
elseif (state == strmatch('eval', commands, 'exact'))
bfr = char(readUTF(is));
fprintf(1, '"eval" string: "%s"\n', bfr);
try
eval(bfr);
writeByte(os, 0);
fprintf(1, 'Sent byte: %d\n', 0);
flush(os);
catch
lasterr = sprintf('Failed to evaluate expression ''%s''.', bfr);
fprintf(1, 'EvaluationException: %s\n', lasterr);
writeByte(os, -1);
fprintf(1, 'Sent byte: %d\n', -1);
writeUTF(os, lasterr);
fprintf(1, 'Sent UTF: %s\n', lasterr);
flush(os);
end
flush(os);
state = 0;
%-------------------
% 'send'
%-------------------
elseif (state == strmatch('send', commands, 'exact'))
tmpname = sprintf('%s.mat', tempname);
expr = sprintf('save(tmpname, ''%s''', saveOption);
ok = 1;
for k=1:length(variables),
variable = variables{k};
if (exist(variable) ~= 1)
lasterr = sprintf('Variable ''%s'' not found.', variable);
disp(lasterr);
ok = 0;
break;
end;
expr = sprintf('%s, ''%s''', expr, variable);
end;
expr = sprintf('%s)', expr);
if (~ok)
writeInt(os, -1);
writeUTF(os, lasterr);
else
disp(expr);
eval(expr);
writeInt(os, 0); % Here anything but -1 means "success"
writeUTF(os, tmpname);
end
answer = readByte(is);
fprintf('answer=%d\n', answer);
state = 0;
%-------------------
% 'send-remote'
%-------------------
elseif (state == strmatch('send-remote', commands, 'exact'))
tmpname = sprintf('%s.mat', tempname);
expr = sprintf('save(tmpname, ''%s''', saveOption);
ok = 1;
for k=1:length(variables),
variable = variables{k};
if (exist(variable) ~= 1)
lasterr = sprintf('Variable ''%s'' not found.', variable);
disp(lasterr);
ok = 0;
break;
end;
expr = sprintf('%s, ''%s''', expr, variable);
end;
expr = sprintf('%s)', expr);
if (~ok)
writeInt(os, -1);
writeUTF(os, lasterr);
else
disp(expr);
eval(expr);
file = java.io.File(tmpname);
maxLength = length(file);
clear file;
writeInt(os, maxLength); % Here anything but -1 means "success"
fprintf(1, 'Send int: %d (maxLength)\n', maxLength);
fid = fopen(tmpname, 'r');
count = 1;
while (count ~= 0)
[bfr, count] = fread(fid, 65536, 'int8');
if (count > 0)
write(os, bfr);
% fprintf(1, 'Wrote %d byte(s).\n', length(bfr));
end;
end;
fclose(fid);
% fprintf(1, 'Wrote!\n');
fprintf(1, 'Send buffer: %d bytes.\n', maxLength);
delete(tmpname);
clear bfr, count, maxLength, fid, tmpname;
end
flush(os);
answer = readByte(is);
fprintf('answer=%d\n', answer);
state = 0;
%-------------------
% 'receive-remote'
%-------------------
elseif (state == strmatch('receive-remote', commands, 'exact'))
len = readInt(is);
fprintf(1, 'Will read MAT file structure of length: %d bytes.\n', len);
reader = InputStreamByteWrapper(4096);
bfr = [];
count = 1;
while (len > 0 & count > 0)
count = reader.read(is, min(4096, len));
if (count > 0)
bfr = [bfr; reader.bfr(1:count)];
len = len - count;
end;
end;
clear reader count len;
tmpfile = sprintf('%s.mat', tempname);
% tmpfile = 'tmp2.mat';
% disp(bfr');
% disp(tmpfile);
fh = fopen(tmpfile, 'wb');
fwrite(fh, bfr, 'int8');
fclose(fh);
clear fh, bfr;
load(tmpfile);
delete(tmpfile);
clear tmpfile;
writeByte(os, 0);
state = 0;
%-------------------
% 'receive'
%-------------------
elseif (state == strmatch('receive', commands, 'exact'))
filename = char(readUTF(is));
fprintf(1, 'Will read MAT file: "%s"\n', filename);
load(filename);
clear filename;
writeByte(os, 0);
state = 0;
end
end
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Shutting down the MATLAB server
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
fprintf(1, '-----------------------\n');
fprintf(1, 'MATLAB server shutdown!\n');
fprintf(1, '-----------------------\n');
writeByte(os, 0);
close(os);
close(is);
close(server);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% HISTORY:
% 2015-01-08 [v3.1.2]
% o BUG FIX: Matlab$getVariable() for a non-existing variable would
% crash the R-to-Matlab communication if remote=FALSE.
% 2014-06-23 [v3.0.2]
% o ROBUSTNESS: Variables 'lasterr' and 'variables' are now always
% defined. Potential bug spotted by Steven Jaffe at Morgan Stanley.
% o Added more progress/verbose output, e.g. current working directory.
% 2014-01-21 [v2.2.0]
% o BUG FIX: The MatlabServer.m script would incorrectly consider
% Matlab v8 and above as Matlab v6. Thanks to Frank Stephen at NREL
% for reporting on this and providing a patch.
% 2013-07-11 [v1.3.5]
% o Updated messages to use 'MATLAB' instead of 'Matlab'.
% 2010-10-25 [v1.3.4]
% o BUG FIX: The MatlabServer.m script incorrectly referred to the
% InputStreamByteWrapper class as java.io.InputStreamByteWrapper.
% Thanks Kenvor Cothey at GMO LCC for reporting on this.
% 2010-08-28
% o Now the MatlabServer script reports its version when started.
% 2010-08-27
% o BUG FIX: Now MatlabServer.m saves variables using the function form,
% i.e. save(). This solves the problem of having single quotation marks
% in the pathname. Thanks Michael Q. Fan at NC State University for
% reporting this problem.
% 2009-08-25
% o BUG FIX: Started to get the error "Undefined function or method
% 'ServerSocket' for input arguments of type 'double'.". It seems like
% import java.net.* etc does not work. A workaround is to specify the
% full path for all Java classes, e.g. java.net.ServerSocket.
% Thanks Nicolas Stadler for reporting this issue.
% 2006-12-28
% o Extended the accepted range of ports from [1023,49151] to [1023,66535].
% 2006-05-08
% o BUG FIX: The error message string for reporting port out of range
% was invalid and gave the error '... Line: 109 Column: 45 ")" expected,
% "identifier" found.'. Thanks Alexander Nervedi for reporting this.
% 2006-01-21
% o Now an error is thrown if port number is out of (safe) range.
% o Added option to specify the port number via the system environment
% variable MATLABSERVER_PORT, after request by Wang Yu, Iowa State Univ.
% 2005-03-08
% o BUG FIX: substring() is not recognized by MATLAB v7. Using regexp()
% which works in MATLAB 6.5 and 7. Workaround eval('try', 'catch').
% Thanks Patrick Drechsler, University of Wuerzburg for the bug report.
% 2005-02-24
% o Now the dynamic Java classpath is set for MATLAB v7 or higher. This
% will simplify life for MATLAB v7 users.
% 2005-02-22
% o Added javaaddpath() to include InputStreamByteWrapper.class.
% Thanks Yichun Wei for feedback and great suggestions.
% 2005-02-11
% o If MATLAB v7 or higher is detected, all MAT structures are saved with
% option '-V6' so readMat() in R.matlab can read them.
% 2002-09-02 [or maybe a little bit earlier]
% o Created.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%