You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

363 line
14KB

  1. classdef Equipment < handle
  2. %EQUIPMENT Summary of this class goes here
  3. % Detailed explanation goes here
  4. properties (SetAccess=protected)
  5. name
  6. tcp
  7. channel
  8. locked
  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 = Equipment(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 = Equipment.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 strcmp(ack(1),'1')
  70. return
  71. end
  72. end
  73. error('Device is not ready');
  74. end
  75. function unlock(ecq)
  76. %UNLOCK Sets the device back to 'local control'
  77. %This function only supports devices via the Prologix.
  78. if ecq.channel >= 0
  79. fprintf(ecq.tcp,'++loc');
  80. ecq.locked = false;
  81. end
  82. end
  83. function lock(ecq)
  84. %UNLOCK Sets the device back to 'remote control'
  85. %This function only supports devices via the Prologix.
  86. if ecq.channel >= 0
  87. fprintf(ecq.tcp,'++llo');
  88. ecq.locked = true;
  89. else
  90. warning('Device does not support locking')
  91. end
  92. end
  93. function beep(ecq)
  94. %BEEP Sends the beep command to the device
  95. ecq.write('SYST:BEEP')
  96. end
  97. function write(ecq,message)
  98. %WRITE Sends a command to the channel.
  99. %The function will first check if the device is ready to
  100. %recieve a command via the OPC function. Then send the command
  101. %and eventually check if the device has thrown an error via
  102. %the ERROR function.
  103. %
  104. %See also QUERY
  105. ecq.opc;
  106. ecq.write_unsafe(message);
  107. ecq.error;
  108. end
  109. function data = bin_read(ecq,datalength,type,typesize)
  110. try
  111. if nargin < 4
  112. typesize = 8;
  113. end
  114. data = zeros(1,datalength);
  115. totalread = 0;
  116. maxlength = floor(ecq.tcp.InputBufferSize/typesize)-16;
  117. while datalength > 0
  118. readlength = min(datalength,maxlength);
  119. lastcount = 0;
  120. [lastdata,lastcount] = fread(ecq.tcp,[1,readlength],type);
  121. if lastcount == 0
  122. error('Data stream from device shorter than expected');
  123. end
  124. data(totalread+1:totalread+lastcount) = lastdata;
  125. totalread = totalread+lastcount;
  126. datalength = datalength - lastcount;
  127. end
  128. flushinput(ecq.tcp)
  129. ecq.clear
  130. catch ME
  131. flushinput(ecq.tcp);
  132. rethrow(ME);
  133. end
  134. end
  135. function data = bin_read_float(ecq,datalength)
  136. data = bin_read(ecq,datalength,'float',4);
  137. end
  138. function output = read(ecq)
  139. %READ Extracts recieved data from the TCP-buffer.
  140. %This function sends the '++read'-command to the Prologix if
  141. %needed. Will always read the TCP-buffer in matlab.
  142. if ecq.channel >= 0
  143. ecq.write_unsafe('++read');
  144. end
  145. output = fscanf(ecq.tcp);
  146. if ecq.debugbool
  147. fprintf(['<< ',output]);
  148. end
  149. end
  150. function output = query(ecq,message)
  151. %QUERY Sends command to the device and read the respond.
  152. %The command is send via the WRITE and the respond is collected
  153. %from the device.
  154. %
  155. %See also WRITE, READ
  156. ecq.opc;
  157. output = ecq.query_unsafe(message);
  158. ecq.error;
  159. end
  160. function errorlist = get.error(ecq)
  161. %ERROR Checks for errors on device. If any, throws a warning.
  162. %The function will pull all errors from the device error stack.
  163. %This is up to a maximum of 20 errors. If errors have occured
  164. %the function will throw a warning with all the error messages.
  165. for i = 1:20
  166. output = ecq.query_unsafe('SYSTem:ERRor?'); %Query error message from device.
  167. % [msgstr, msgid] = lastwarn;
  168. % if strcmp(msgid,'instrument:fscanf:unsuccessfulRead')
  169. % error(['Lost connection with ' ecq.name]);
  170. % end
  171. errorlist(i,1:(length(output))) = output; %Store the error message in the errorlist array.
  172. %GPIB protocol states that the error code '0' means no
  173. %error. The for loop will break if the last recieved error
  174. %code is zero.
  175. if str2double(regexp(output,'\d*','match','once'))==0 %Check if last recieved error code is '0'
  176. % If the 'no error' message is the only error then no
  177. % warning will be trown. However if there is more than
  178. % one error in the list. The function gives a warning
  179. % with all error messages.
  180. if i>1 %check for more than one error message.
  181. warning('Error from device: %s %s',ecq.name,reshape(errorlist(1:i-1,:)',1,[]));
  182. end
  183. ecq.clear;
  184. break;
  185. end
  186. end
  187. end
  188. end
  189. methods% (Access = protected)
  190. function write_unsafe(ecq,message)
  191. %WRITE_UNSAFE Sends command to device.
  192. %This function does not check if device is ready or check if an
  193. %error occured after writing. If possible one should always use
  194. %the WRITE function.
  195. %
  196. %See also WRITE, QUERY
  197. if ecq.channel >= 0
  198. fprintf(ecq.tcp,['++addr ', num2str(ecq.channel)]);
  199. if ecq.debugbool
  200. fprintf(['>> ++addr ', num2str(ecq.channel),'\n']);
  201. end
  202. end
  203. fprintf(ecq.tcp, message);
  204. if ecq.debugbool
  205. fprintf(['>> ',message,'\n']);
  206. end
  207. end
  208. function write_noerror(ecq,message)
  209. %WRITE Sends a command to the channel.
  210. %The function will first check if the device is ready to
  211. %recieve a command via the OPC function. Then send the command.
  212. %
  213. %See also WRITE
  214. ecq.opc;
  215. ecq.write_unsafe(message);
  216. end
  217. function output = query_unsafe(ecq,message)
  218. %QUERY_UNSAFE Sends command to device and reads device output.
  219. %This function does not check if device is ready or check if an
  220. %error occured after writing. If possible one should always use
  221. %the QUERY function.
  222. %
  223. %See also QUERY, WRITE, READ
  224. ecq.write_unsafe(message);
  225. output = read(ecq);
  226. end
  227. function disableEOI(ecq)
  228. ecq.tcp.Terminator = '';
  229. end
  230. function enableEOI(ecq)
  231. ecq.tcp.Terminator = 'LF';
  232. end
  233. end
  234. methods (Access = protected, Hidden)
  235. function setPrologix(ecq)
  236. ecq.write_unsafe('++mode 1'); %set device in controller mode
  237. ecq.write_unsafe('++auto 0'); %disable automatic datapull. this avoids errors on equipment.
  238. end
  239. function delete(ecq)
  240. %DELETE Destructs the current object.
  241. ecq.unlock;
  242. Equipment.getTCP(ecq.tcp.RemoteHost,-ecq.tcp.RemotePort);
  243. end
  244. end
  245. methods (Static)
  246. function tcpobject = getTCP(ipAddress,port)
  247. %GETTCP Returns TCP-object for TCP-connection.
  248. %Function makes a TCP-connection for specific ipAddress and
  249. %port. If TCP-connection already exist no new connection is
  250. %made and only existing handle is returned.
  251. %For existing connections the function will store the amount of
  252. %handles in use. Via the DELETE function the connection will be
  253. %closed if the connection is not in use anymore.
  254. %The connection will be removed if the port is negative number.
  255. persistent tcpconnection; %make variable persistent to share tcp-handles across multiple function calls.
  256. if isempty(tcpconnection)
  257. %if the tcpconnection is empty (first time function
  258. %call) make a struct in the variable.
  259. tcpconnection = struct;
  260. end
  261. [ipname,ipAddress] = Equipment.ip2structname(ipAddress,abs(port)); %Get a structname and a cleaned ipaddress.
  262. if port > 0 %if port number is positive a connection is made.
  263. if ~isfield(tcpconnection, ipname) %check if the handle is already made before.
  264. tcpconnection.(ipname).tcp = tcpip(ipAddress,port); %Make TCP-connection
  265. tcpconnection.(ipname).nopen = 1; %Set number of connections in use to 1
  266. tcpconnection.(ipname).tcp.InputBufferSize = 2^24; %make really large buffer size 64MB. To acquire complete waveforms.
  267. tcpconnection.(ipname).tcp.OutputBufferSize = 2^24; %make really large buffer size 64MB. To acquire complete waveforms.
  268. tcpconnection.(ipname).tcp.Timeout = 5; %set timeout to 5 seconds.
  269. fopen(tcpconnection.(ipname).tcp); %Open the TCP-connection
  270. else %If connection already exist. Increase number of connections in use by 1.
  271. tcpconnection.(ipname).nopen = tcpconnection.(ipname).nopen + 1;
  272. end
  273. tcpobject = tcpconnection.(ipname).tcp; %return the TCP-connection handle.
  274. elseif port < 0 %If the portnumber is negative the connection is removed.
  275. if ~isfield(tcpconnection, ipname)
  276. return;
  277. elseif tcpconnection.(ipname).nopen > 1
  278. %If more than one object uses this tcp-handle. Decrease
  279. %the number of connections in use by 1.
  280. tcpconnection.(ipname).nopen = tcpconnection.(ipname).nopen - 1; %Decrease the number by 1.
  281. else
  282. %If only one handle uses this connection. The
  283. %connection is closed and the field is removed from the
  284. %tcpconnection struct.
  285. flushinput(tcpconnection.(ipname).tcp);
  286. fclose(tcpconnection.(ipname).tcp);
  287. tcpconnection = rmfield(tcpconnection,ipname);
  288. end
  289. else
  290. error([num2str(port),' is not a valid port number try a value between 1 and 65535.']);
  291. end
  292. end
  293. function bool = isnum(input)
  294. %ISNUM Tests if the input is numeric scalar
  295. bool = isnumeric(input)&&isscalar(input);
  296. end
  297. function num = forceNum(input)
  298. %FORCENUM Throws an error if the input is not numeric.
  299. if ~Equipment.isnum(input)
  300. error('Input should be a (single) number.');
  301. end
  302. num = input;
  303. end
  304. function iptest(ipAddress)
  305. %IPTEST Checks if the given IPv4-address is valid.
  306. 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');
  307. if isempty(validip)
  308. error('Invalid IP-address');
  309. end
  310. end
  311. function output = optionnum2str(input)
  312. if isnum(input)
  313. output = num2str(input);
  314. else
  315. output = input;
  316. end
  317. end
  318. function [structname,cleanip] = ip2structname(ipAddress,port)
  319. %IP2STRUCTNAME Returns a structname and ip w/out leading zeros.
  320. %structname to store stuff in struct (especially for GETTCP).
  321. %cleanip is a shortened ip without leading zeros.
  322. cleanip = regexprep(ipAddress,'(?:(?<=\.)|^)(?:0+)(?=\d)','');
  323. Equipment.iptest(cleanip);
  324. structname = matlab.lang.makeValidName(['ip',cleanip,'_',num2str(port)]);
  325. end
  326. end
  327. end