diff --git a/@MyInstrument/MyInstrument.m b/@MyInstrument/MyInstrument.m index 1fa3016..932a169 100644 --- a/@MyInstrument/MyInstrument.m +++ b/@MyInstrument/MyInstrument.m @@ -1,243 +1,246 @@ classdef MyInstrument < dynamicprops & MyInputHandler properties (Access=public) name=''; interface=''; address=''; Device %Device communication object Trace %MyTrace object for storing data end properties (GetAccess=public, SetAccess=protected) idn_str=''; % identification string end properties (Constant=true) % Default parameters for device connection DEFAULT_INP_BUFF_SIZE = 1e7; % buffer size bytes DEFAULT_OUT_BUFF_SIZE = 1e7; % buffer size bytes DEFAULT_TIMEOUT = 10; % Timeout in s end events NewData PropertyRead end methods (Access=protected) % This function is overloaded to add more parameters to the parser function p = createConstructionParser(this) p=inputParser(); % Ignore unmatched parameters p.KeepUnmatched = true; addRequired(p,'interface',@ischar); addRequired(p,'address',@ischar); addParameter(p,'name','',@ischar); this.ConstructionParser=p; end end methods (Access=public) function this=MyInstrument(interface, address, varargin) createConstructionParser(this); %Loads parsed variables into class properties parseClassInputs(this,interface,address,varargin{:}); % Create an empty trace this.Trace=MyTrace(); % Create dummy device object that supports properties this.Device=struct(); this.Device.Status='not connected'; % Interface and address can correspond to an entry in the list % of local instruments. Read this entry in such case. if strcmpi(interface, 'instr_list') % load the InstrumentList structure InstrumentList = getLocalSettings('InstrumentList'); % In this case 'address' is the instrument name in % the list instr_name = address; if ~isfield(InstrumentList, instr_name) error('%s is not a field of InstrumentList',... instr_name); end if ~isfield(InstrumentList.(instr_name), 'interface') error(['InstrumentList entry ', instr_name,... ' has no ''interface'' field']); else this.interface = InstrumentList.(instr_name).interface; end if ~isfield(InstrumentList.(instr_name), 'address') error(['InstrumentList entry ', instr_name,... ' has no ''address'' field']); else this.address = InstrumentList.(instr_name).address; end % Assign name automatically, but not overwrite if % already specified if isempty(this.name) this.name = instr_name; end end % Connecting device creates a communication object, % but does not attempt communication connectDevice(this); end function delete(this) %Closes the connection to the device closeDevice(this); %Deletes the device object try delete(this.Device); catch warning('Device object cannot be deleted') end end %Triggers event for acquired data function triggerNewData(this) notify(this,'NewData') end %Triggers event for property read from device function triggerPropertyRead(this) notify(this,'PropertyRead') end % Read all the relevant instrument properties and return as a % MyMetadata object. % Dummy method that needs to be re-defined by a parent class function Hdr=readHeader(this) Hdr=MyMetadata(); % Generate valid field name from instrument name if present and % class name otherwise if ~isempty(this.name) field_name=genvarname(this.name); else field_name=class(this); end addField(Hdr, field_name); % Add identification string as parameter addParam(Hdr, field_name, 'idn', this.idn_str); end %% Connect, open, configure, identificate and close the device % Connects to the device, explicit indication of interface and % address is for ability to handle instr_list as interface function connectDevice(this) int_list={'constructor','visa','tcpip','serial'}; if ~ismember(lower(this.interface), int_list) warning(['Device is not connected, unknown interface ',... this.interface,'. Valid interfaces are ',... '''constructor'', ''visa'', ''tcpip'' and ''serial''']) return end try switch lower(this.interface) % Use 'constructor' interface to connect device with % more that one parameter, specifying its address case 'constructor' % in this case the 'address' is a command % (ObjectConstructorName), e.g. as returned by the % instrhwinfo, that creates communication object % when executed this.Device=eval(this.address); case 'visa' % visa brand is 'ni' by default this.Device=visa('ni', this.address); case 'tcpip' % Works only with default socket. Use 'constructor' % if socket or other options need to be specified this.Device=tcpip(this.address); case 'serial' this.Device=serial(this.address); otherwise error('Unknown interface'); end configureDeviceDefault(this); catch warning(['Device is not connected, ',... 'error while creating communication object.']); end end % Opens the device if it is not open. Does not throw error if % device is already open for communication with another object, but % tries to close existing connections instead. function openDevice(this) if ~isopen(this) try fopen(this.Device); catch % try to find and close all the devices with the same % VISA resource name try instr_list=instrfind('RsrcName',this.Device.RsrcName); fclose(instr_list); fopen(this.Device); warning('Multiple instrument objects of address %s exist',... this.address); catch error('Could not open device') end end end end % Closes the connection to the device function closeDevice(this) if isopen(this) fclose(this.Device); end end function configureDeviceDefault(this) dev_prop_list = properties(this.Device); if ismember('OutputBufferSize',dev_prop_list) this.Device.OutputBufferSize = this.DEFAULT_OUT_BUFF_SIZE; end if ismember('InputBufferSize',dev_prop_list) this.Device.InputBufferSize = this.DEFAULT_INP_BUFF_SIZE; end if ismember('Timeout',dev_prop_list) this.Device.Timeout = this.DEFAULT_TIMEOUT; end end % Checks if the connection to the device is open function bool=isopen(this) try bool=strcmp(this.Device.Status, 'open'); catch warning('Cannot access device Status property'); bool=false; end end %% Identification % Attempt communication and identification of the device function [str, msg]=idn(this) was_open=isopen(this); try openDevice(this); [str,~,msg]=query(this.Device,'*IDN?'); catch ErrorMessage str=''; msg=ErrorMessage.message; - end + end + % Remove carriage return and new line symbols from the string + newline_smb={sprintf('\n'),sprintf('\r')}; %#ok + str=replace(str, newline_smb,' '); this.idn_str=str; % Leave device in the state it was in the beginning if ~was_open try closeDevice(this); catch end end end end end \ No newline at end of file diff --git a/@MyTpg/MyTpg.m b/@MyTpg/MyTpg.m index 2483a7a..da59dbb 100644 --- a/@MyTpg/MyTpg.m +++ b/@MyTpg/MyTpg.m @@ -1,198 +1,202 @@ % Class for communication with Pfeiffer TPG single and dual pressure gauge % controllers. % Do not use visa communication objects with this instrument % Tested with TPG 262 and 362. classdef MyTpg < MyInstrument properties (Constant=true) % Named constants for communication ETX = char(3); % end of text CR = char(13); % carriage return \r LF = char(10); %#ok % line feed \n ENQ = char(5); % enquiry ACK = char(6); % acknowledge NAK = char(21); % negative acknowledge end properties (SetAccess=protected, GetAccess=public) pressure1 = 0; % numeric values of pressure pressure2 = 0; stat1; stat2; gauge_id1; gauge_id2; pressure_unit = ''; end properties (Dependent=true) pressure_str1; % display string with measurement unit pressure_str2; end methods (Access=public) function this = MyTpg(interface, address, varargin) this@MyInstrument(interface, address, varargin{:}); end % read pressure from a single channel or both channels at a time function p_arr = readPressure(this) query(this.Device,['PRX',this.CR,this.LF]); str = query(this.Device,this.ENQ); % Extract pressure and gauge status from reading. arr = sscanf(str,'%i,%e,%i,%e'); p_arr=transpose(arr(2:2:end)); this.pressure1 = p_arr(1); this.pressure2 = p_arr(2); % Status codes: % 0 –> Measurement data okay % 1 –> Underrange % 2 –> Overrange % 3 –> Sensor error % 4 –> Sensor off (IKR, PKR, IMR, PBR) % 5 –> No sensor (output: 5,2.0000E-2 [hPa]) % 6 –> Identification error this.stat1 = gaugeStatusFromCode(this, arr(1)); this.stat2 = gaugeStatusFromCode(this, arr(3)); % Trigger event notification triggerPropertyRead(this); end function pu = readPressureUnit(this) query(this.Device,['UNI',this.CR,this.LF]); str = query(this.Device,this.ENQ); % Pressure units correspondence table: % 0 –> mbar/bar % 1 –> Torr % 2 –> Pascal % 3 –> Micron % 4 –> hPascal (default) % 5 –> Volt pu_code = sscanf(str,'%i'); pu = pressureUnitFromCode(this, pu_code); this.pressure_unit = pu; % Trigger event notification triggerPropertyRead(this); end function id_list = readGaugeId(this) query(this.Device,['TID',this.CR,this.LF]); str = query(this.Device,this.ENQ); id_list = deblank(strsplit(str,{','})); this.gauge_id1 = id_list{1}; this.gauge_id2 = id_list{2}; % Trigger event notification triggerPropertyRead(this); end function p_arr = readAllHedged(this) openDevice(this); try p_arr = readPressure(this); readPressureUnit(this); readGaugeId(this); catch p_arr = [0,0]; warning('Error while communicating with gauge controller') end closeDevice(this); end % Implement instrument-specific readHeader function function Hdr=readHeader(this) Hdr=readHeader@MyInstrument(this); % Hdr should contain single field fn=Hdr.field_names{1}; readAllHedged(this); addParam(Hdr, fn, 'pressure_unit', this.pressure_unit); addParam(Hdr, fn, 'pressure1', this.pressure1); addParam(Hdr, fn, 'pressure2', this.pressure2); addParam(Hdr, fn, 'stat1', this.stat1); addParam(Hdr, fn, 'stat2', this.stat2); addParam(Hdr, fn, 'gauge_id1', this.gauge_id1); addParam(Hdr, fn, 'gauge_id2', this.gauge_id2); end % Attempt communication and identification of the device function [str, msg]=idn(this) was_open=isopen(this); try openDevice(this); query(this.Device,['AYT',this.CR,this.LF]); [str,~,msg]=query(this.Device,this.ENQ); catch ErrorMessage str=''; msg=ErrorMessage.message; end + % Remove carriage return and new line symbols from the string + newline_smb={sprintf('\n'),sprintf('\r')}; %#ok + str=replace(str, newline_smb,' '); + this.idn_str=str; % Leave device in the state it was in the beginning if ~was_open try closeDevice(this); catch end end end function code_list = turnGauge(this) query(this.Device,['SEN',char(1,1),this.CR,this.LF]); str = query(this.Device,this.ENQ); code_list = deblank(strsplit(str,{','})); end % Convert numerical code for gauge status to a string function str = gaugeStatusFromCode(~, code) switch int8(code) case 0 str = 'Measurement data ok'; case 1 str = 'Underrange'; case 2 str = 'Overrange'; case 3 str = 'Sensor error'; case 4 str = 'Sensor off'; case 5 str = 'No sensor'; case 6 str = 'Identification error'; otherwise str = ''; warning('Unknown gauge status code %i', code); end end % Convert numerical code for pressure unit to a string function str = pressureUnitFromCode(~, code) switch int8(code) case 0 str = 'mbar'; case 1 str = 'Torr'; case 2 str = 'Pa'; case 3 str = 'Micron'; case 4 str = 'hPa'; case 5 str = 'Volt'; otherwise str = ''; warning('unknown pressure unit, code=%i',pu_num) end end end %% Get functions methods function p_str = get.pressure_str1(this) p_str = sprintf('%.2e %s', this.pressure1, this.pressure_unit); end function p_str = get.pressure_str2(this) p_str = sprintf('%.2e %s', this.pressure2, this.pressure_unit); end end end