選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

434 行
19KB

  1. classdef EquipmentClass < handle
  2. %EQUIPMENT Summary of this class goes here
  3. % Detailed explanation goes here
  4. properties (SetAccess=protected)
  5. name %name of equipment
  6. tcp %TCP-object with connection to equipment
  7. channel %GPIB-channel if equipment is connected via prologix
  8. locked %lock frontpanel
  9. manufacturer
  10. model
  11. serialnumber
  12. firmwarelevel
  13. end
  14. properties (Dependent, SetAccess=private)
  15. error
  16. end
  17. properties (Hidden)
  18. debugbool
  19. end
  20. methods
  21. function ecq = EquipmentClass(ipAddress,port,channel)
  22. %EQUIPMENT Construct an instance of this class.
  23. % This functions opens the required TCP connection
  24. % for this device. Channel is GPIB-channel on prologix
  25. % converter if used, otherwise set channel to -1.
  26. % Name is queried from device via '*IDN?' command.
  27. ecq.tcp = EquipmentClass.getTCP(ipAddress,port);
  28. ecq.locked = false;
  29. ecq.channel = channel;
  30. ecq.name = ecq.idn();
  31. ecq.debugbool = false;
  32. if channel >= 0
  33. ecq.setPrologix;
  34. end
  35. splitname = strsplit(ecq.name,',');
  36. ecq.manufacturer = splitname{1};
  37. ecq.model = splitname{2};
  38. ecq.serialnumber = splitname{3};
  39. ecq.firmwarelevel = splitname{4};
  40. end
  41. function id = idn(ecq)
  42. %IDN Queries identificantion from device via '*IDN?'.
  43. id = ecq.query_unsafe('*idn?');
  44. end
  45. function clear(ecq)
  46. %CLEAR Sends clear command to device via '*CLS'.
  47. %This function also clears the TCP inputbuffer;
  48. ecq.write_unsafe('*cls');
  49. flushinput(ecq.tcp);
  50. end
  51. function reset(ecq)
  52. %RESET Sends reset command to device via '*RST'.
  53. ecq.write_unsafe('*rst');
  54. end
  55. function debug(ecq)
  56. ecq.debugbool = ~ecq.debugbool;
  57. end
  58. function flush(ecq)
  59. flushinput(ecq.tcp);
  60. flushoutput(ecq.tcp);
  61. end
  62. function opc(ecq)
  63. %OPC executes 'operation complete query' to device.
  64. %Function holds untill device returns '1'. Must be
  65. %used to avoid interrupting busy device.
  66. ecq.write_unsafe('*OPC?');
  67. for i = 1:10
  68. ack = ecq.read;
  69. if ~isempty(ack)
  70. if strcmp(ack(1),'1')
  71. return
  72. end
  73. end
  74. end
  75. error('Device is not ready');
  76. end
  77. function unlock(ecq)
  78. %UNLOCK Sets the device back to 'local control'
  79. %This function only supports devices via the Prologix.
  80. if ecq.channel >= 0
  81. fprintf(ecq.tcp,'++loc');
  82. ecq.locked = false;
  83. end
  84. end
  85. function lock(ecq)
  86. %UNLOCK Sets the device back to 'remote control'
  87. %This function only supports devices via the Prologix.
  88. if ecq.channel >= 0
  89. fprintf(ecq.tcp,'++llo');
  90. ecq.locked = true;
  91. else
  92. warning('Device does not support locking')
  93. end
  94. end
  95. function beep(ecq)
  96. %BEEP Sends the beep command to the device
  97. ecq.write('SYST:BEEP')
  98. end
  99. function write(ecq,message)
  100. %WRITE Sends a command to the channel.
  101. %The function will first check if the device is ready to
  102. %recieve a command via the OPC function. Then send the command
  103. %and eventually check if the device has thrown an error via
  104. %the ERROR function.
  105. %
  106. %See also QUERY
  107. ecq.opc;
  108. ecq.write_unsafe(message);
  109. ecq.error;
  110. end
  111. function data = bin_read(ecq,datalength,type,typesize)
  112. try
  113. if nargin < 4
  114. typesize = 8;
  115. end
  116. data = zeros(1,datalength);
  117. totalread = 0;
  118. maxlength = floor(ecq.tcp.InputBufferSize/typesize)-16;
  119. while datalength > 0
  120. readlength = min(datalength,maxlength);
  121. lastcount = 0;
  122. [lastdata,lastcount] = fread(ecq.tcp,[1,readlength],type);
  123. if lastcount == 0
  124. error('Data stream from device shorter than expected');
  125. end
  126. data(totalread+1:totalread+lastcount) = lastdata;
  127. totalread = totalread+lastcount;
  128. datalength = datalength - lastcount;
  129. end
  130. flushinput(ecq.tcp)
  131. ecq.clear
  132. catch ME
  133. flushinput(ecq.tcp);
  134. rethrow(ME);
  135. end
  136. end
  137. function data = bin_read_float(ecq,datalength)
  138. data = bin_read(ecq,datalength,'float',4);
  139. end
  140. function output = read(ecq)
  141. %READ Extracts recieved data from the TCP-buffer.
  142. %This function sends the '++read'-command to the Prologix if
  143. %needed. Will always read the TCP-buffer in matlab.
  144. if ecq.channel >= 0
  145. ecq.write_unsafe('++read');
  146. end
  147. output = fscanf(ecq.tcp);
  148. if ecq.debugbool
  149. fprintf(['<< ',output]);
  150. end
  151. end
  152. function output = query(ecq,message)
  153. %QUERY Sends command to the device and read the respond.
  154. %The command is send via the WRITE and the respond is collected
  155. %from the device.
  156. %
  157. %See also WRITE, READ
  158. ecq.opc;
  159. output = ecq.query_unsafe(message);
  160. ecq.error;
  161. end
  162. function errorlist = get.error(ecq)
  163. %ERROR Checks for errors on device. If any, throws a warning.
  164. %The function will pull all errors from the device error stack.
  165. %This is up to a maximum of 20 errors. If errors have occured
  166. %the function will throw a warning with all the error messages.
  167. for i = 1:1
  168. output = ecq.query_unsafe('SYSTem:ERRor?'); %Query error message from device.
  169. % [msgstr, msgid] = lastwarn;
  170. % if strcmp(msgid,'instrument:fscanf:unsuccessfulRead')
  171. % error(['Lost connection with ' ecq.name]);
  172. % end
  173. errorlist(i,1:(length(output))) = output; %Store the error message in the errorlist array.
  174. %GPIB protocol states that the error code '0' means no
  175. %error. The for loop will break if the last recieved error
  176. %code is zero.
  177. if str2double(regexp(output,'\d*','match','once'))==0 %Check if last recieved error code is '0'
  178. % If the 'no error' message is the only error then no
  179. % warning will be trown. However if there is more than
  180. % one error in the list. The function gives a warning
  181. % with all error messages.
  182. if i>1 %check for more than one error message.
  183. warning('Error from device: %s %s',ecq.name,reshape(errorlist(1:i-1,:)',1,[]));
  184. end
  185. ecq.clear;
  186. break;
  187. end
  188. end
  189. end
  190. end
  191. methods% (Access = protected)
  192. function write_unsafe(ecq,message)
  193. %WRITE_UNSAFE Sends command to device.
  194. %This function does not check if device is ready or check if an
  195. %error occured after writing. If possible one should always use
  196. %the WRITE function.
  197. %
  198. %See also WRITE, QUERY
  199. if ecq.channel >= 0
  200. fprintf(ecq.tcp,['++addr ', num2str(ecq.channel)]);
  201. if ecq.debugbool
  202. fprintf(['>> ++addr ', num2str(ecq.channel),'\n']);
  203. end
  204. end
  205. fprintf(ecq.tcp, message);
  206. if ecq.debugbool
  207. fprintf(['>> ',message,'\n']);
  208. end
  209. end
  210. function write_noerror(ecq,message)
  211. %WRITE Sends a command to the channel.
  212. %The function will first check if the device is ready to
  213. %recieve a command via the OPC function. Then send the command.
  214. %
  215. %See also WRITE
  216. ecq.opc;
  217. ecq.write_unsafe(message);
  218. end
  219. function output = query_unsafe(ecq,message)
  220. %QUERY_UNSAFE Sends command to device and reads device output.
  221. %This function does not check if device is ready or check if an
  222. %error occured after writing. If possible one should always use
  223. %the QUERY function.
  224. %
  225. %See also QUERY, WRITE, READ
  226. ecq.write_unsafe(message);
  227. output = read(ecq);
  228. end
  229. function disableEOI(ecq)
  230. ecq.tcp.Terminator = '';
  231. end
  232. function enableEOI(ecq)
  233. ecq.tcp.Terminator = 'LF';
  234. end
  235. end
  236. methods (Access = protected, Hidden)
  237. function setPrologix(ecq)
  238. %SETPROLOGIX Set the correct default settings
  239. ecq.write_unsafe('++mode 1'); %set device in controller mode
  240. ecq.write_unsafe('++auto 0'); %disable automatic datapull. this avoids errors on equipment.
  241. ecq.write_unsafe('++eoi 1'); %enable end of line character
  242. ecq.write_unsafe('++eos 0'); %Set end of line character
  243. end
  244. function delete(ecq)
  245. %DELETE Destructs the current object.
  246. ecq.unlock;
  247. EquipmentClass.getTCP(ecq.tcp.RemoteHost,-ecq.tcp.RemotePort);
  248. end
  249. end
  250. methods (Static)
  251. function tcpobject = getTCP(ipAddress,port)
  252. %GETTCP Returns TCP-object for TCP-connection.
  253. %Function makes a TCP-connection for specific ipAddress and
  254. %port. If TCP-connection already exist no new connection is
  255. %made and only existing handle is returned.
  256. %For existing connections the function will store the amount of
  257. %handles in use. Via the DELETE function the connection will be
  258. %closed if the connection is not in use anymore.
  259. %The connection will be removed if the port is negative number.
  260. persistent tcpconnection; %make variable persistent to share tcp-handles across multiple function calls.
  261. if isempty(tcpconnection)
  262. %if the tcpconnection is empty (first time function
  263. %call) make a struct in the variable.
  264. tcpconnection = struct;
  265. end
  266. [ipname,ipAddress] = EquipmentClass.ip2structname(ipAddress,abs(port)); %Get a structname and a cleaned ipaddress.
  267. if port > 0 %if port number is positive a connection is made.
  268. if ~isfield(tcpconnection, ipname) %check if the handle is already made before.
  269. tcpconnection.(ipname).tcp = tcpip(ipAddress,port); %Make TCP-connection
  270. tcpconnection.(ipname).nopen = 1; %Set number of connections in use to 1
  271. tcpconnection.(ipname).tcp.InputBufferSize = 2^24; %make really large buffer size 64MB. To acquire complete waveforms.
  272. tcpconnection.(ipname).tcp.OutputBufferSize = 2^24; %make really large buffer size 64MB. To acquire complete waveforms.
  273. tcpconnection.(ipname).tcp.Timeout = 5; %set timeout to 5 seconds.
  274. fopen(tcpconnection.(ipname).tcp); %Open the TCP-connection
  275. else %If connection already exist. Increase number of connections in use by 1.
  276. tcpconnection.(ipname).nopen = tcpconnection.(ipname).nopen + 1;
  277. end
  278. tcpobject = tcpconnection.(ipname).tcp; %return the TCP-connection handle.
  279. elseif port < 0 %If the portnumber is negative the connection is removed.
  280. if ~isfield(tcpconnection, ipname)
  281. return;
  282. elseif tcpconnection.(ipname).nopen > 1
  283. %If more than one object uses this tcp-handle. Decrease
  284. %the number of connections in use by 1.
  285. tcpconnection.(ipname).nopen = tcpconnection.(ipname).nopen - 1; %Decrease the number by 1.
  286. else
  287. %If only one handle uses this connection. The
  288. %connection is closed and the field is removed from the
  289. %tcpconnection struct.
  290. flushinput(tcpconnection.(ipname).tcp);
  291. fclose(tcpconnection.(ipname).tcp);
  292. tcpconnection = rmfield(tcpconnection,ipname);
  293. end
  294. else
  295. error([num2str(port),' is not a valid port number try a value between 1 and 65535.']);
  296. end
  297. end
  298. function bool = isnum(input)
  299. %ISNUM Tests if the input is numeric scalar
  300. bool = isnumeric(input)&&isscalar(input);
  301. end
  302. function num = forceNum(input)
  303. %FORCENUM Throws an error if the input is not numeric.
  304. if ~EquipmentClass.isnum(input)
  305. error('Input should be a (single) number.');
  306. end
  307. num = input;
  308. end
  309. function iptest(ipAddress)
  310. %IPTEST Checks if the given IPv4-address is valid.
  311. validip = regexp(ipAddress,'^(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9]))$', 'once');
  312. if isempty(validip)
  313. error('Invalid IP-address');
  314. end
  315. end
  316. function output = optionnum2str(input)
  317. %OPTIONNUM2STR Change numeric or string input to string output
  318. if isnum(input)
  319. output = num2str(real(input));
  320. elseif ischar(input)
  321. output = input;
  322. else
  323. error("Expected a character array or numeric value as input");
  324. end
  325. end
  326. function [structname,cleanip] = ip2structname(ipAddress,port)
  327. %IP2STRUCTNAME Returns a structname and ip w/out leading zeros.
  328. %structname to store stuff in struct (especially for GETTCP).
  329. %cleanip is a shortened ip without leading zeros.
  330. cleanip = regexprep(ipAddress,'(?:(?<=\.)|^)(?:0+)(?=\d)','');
  331. EquipmentClass.iptest(cleanip);
  332. structname = matlab.lang.makeValidName(['ip',cleanip,'_',num2str(port)]);
  333. end
  334. function ipaddress = getInterfaceIP()
  335. %GETINTERFACEIP gets the local ipaddresses on this machine.
  336. if ispc
  337. % Code to run on Windows plaform
  338. [status,result]=system('ipconfig');
  339. if ~(status == 0)
  340. error('Failed to find local IP address');
  341. end
  342. ipaddress = regexp(result,'(?<=\n IPv4 Address[ \.]*: )([0-9]{1,3}\.?){4}','match');
  343. elseif ismac
  344. % Code to run on Mac platform
  345. [status,result]=system('ifconfig');
  346. if ~(status == 0)
  347. error('Failed to find local IP address');
  348. end
  349. ipaddress = regexp(result,'(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])(?=\/)','match');
  350. elseif isunix
  351. % Code to run on Unix platform
  352. [status,result]=system('ip addr show');
  353. if ~(status == 0)
  354. error('Failed to find local IP address');
  355. end
  356. ipaddress = regexp(result,'(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])(?=\/)','match');
  357. else
  358. error('Platform not supported');
  359. end
  360. end
  361. function prologixIP = discoverPrologix()
  362. %DISCOVERPROLOGIX attemts to find the prologix on the network.
  363. %It will broadcast on all interfaces and return the ipaddress
  364. %of the prologix. Not build for multiple prologixs on a single
  365. %network.
  366. persistent storedipaddress; %store the ipaddress of the prologix
  367. if ~isempty(storedipaddress)
  368. prologixIP = storedipaddress;
  369. return
  370. end
  371. remotehost = '255.255.255.255'; %set broadcast address
  372. remoteport = 3040; %set remoteport that is used for the prologix discoverprotocol
  373. timeout = 0.2; %set timeout on package.
  374. % magic string to request ipaddress from prologix: ['5a' '00' '5b' 'db' 'ff' 'ff' 'ff' 'ff' 'ff' 'ff' '00' '00']
  375. magic_msg = uint8([90 0 91 219 255 255 255 255 255 255 00 00]);
  376. ipaddress = EquipmentClass.getInterfaceIP(); %get all interface addresses from getInterfaceIP.
  377. %create an UDPconnection-object for each interface.
  378. udpconnection = cellfun(@(ip) udp(remotehost,remoteport,'LocalHost',ip,'Timeout',timeout),ipaddress,'UniformOutput',0);
  379. cellfun(@fopen,udpconnection); %open all udpconnections
  380. cellfun(@(udpconnection) fwrite(udpconnection,magic_msg),udpconnection); %broadcast the magic_msg via all udpconnections
  381. for i = 1:10
  382. answer = cellfun(@fread,udpconnection,'UniformOutput',0); %read all udpInputBuffers for response from prologix
  383. ack = ~cellfun(@isempty,answer); %check if prologix responded on one of the interfaces.
  384. if sum(ack) > 0 %true if recieved response
  385. msg = answer{ack}; %put message from answer cell in to msg variable
  386. prologixIP = num2str(msg(21:24)','%d.%d.%d.%d'); %get ipaddress from msg.
  387. cellfun(@fclose,udpconnection); %close all connections
  388. storedipaddress = prologixIP; %store ip-address for next functioncall
  389. return
  390. end
  391. end
  392. cellfun(@fclose,udpconnection); %close all connections
  393. prologixIP = ''; %nothing found, return empty chararray.
  394. end
  395. end
  396. end