diff --git a/@MyClassParser/MyClassParser.m b/@MyClassParser/MyClassParser.m index 330d988..82f55ed 100644 --- a/@MyClassParser/MyClassParser.m +++ b/@MyClassParser/MyClassParser.m @@ -1,115 +1,109 @@ % Input parser, which functionality was extended to automatically add % class properties to the scheme and assign the results after parsing is % done. % Different to inputParser, KeepUnmatched is 'true' by default. classdef MyClassParser < inputParser properties (Dependent=true) unmatched_nv % List of unmatched name-value pairs end methods (Access=public) function this = MyClassParser(varargin) this@inputParser(); % KeepUnmatched is true so that the unmatched options could be % passed to another class this.KeepUnmatched=true; if nargin()==1 % If an object is supplied via varargin, add its properties % to the parser scheme addClassProperties(this, varargin{1}); end end % Add all the properties the class which are not already present % in the scheme of the parser and which have set access % permitted for MyClassParser function addClassProperties(this, obj) objMetaclass = metaclass(obj); for i=1:length(objMetaclass.PropertyList) Tmp = objMetaclass.PropertyList(i); % If parameter is already present in the parser scheme, % skip if ismember(Tmp.Name, this.Parameters) continue end % Constant, Dependent and Abstract propeties cannot be set, % so skip in this case also. if Tmp.Constant||Tmp.Abstract||Tmp.Dependent continue end % Check if the parser has access to the property. This % can be true in two cases: 1) SetAccess is public % 2) MyClassParser class was explicitly given access sa=Tmp.SetAccess; if ischar(sa) has_access=strcmpi(sa,'public'); elseif iscell(sa) % Case when SetAcces is specified as cell array of % metaclasses has_access = any(... cellfun(@(x) strcmpi(x.Name, class(this)),sa)); else has_access=false; end % If the property has set access and default value, % add it as parameter to the parser scheme if has_access if Tmp.HasDefault def = Tmp.DefaultValue; % Create validation function based on the class of % default value val_fcn = @(x)assert(isa(x, class(def)),... ['The value must be of the class ',class(def),... ' while the present one is of the class ',... class(x),'.']); addParameter(this, Tmp.Name, def, val_fcn); end end end end % parse varargin and assign results to class properties % with the same names as parameters function processInputs(this, obj, varargin) parse(this, varargin{:}); % assign results that have associated class properties with % public set access for i=1:length(this.Parameters) par = this.Parameters{i}; if ~ismember(par, this.UsingDefaults) && isprop(obj, par) try obj.(par) = this.Results.(par); catch ME warning(['Value of the input parameter ''' par ... ''' could not be assigned to property. ' ... ME.message]) end end end end end methods function unmatched_nv=get.unmatched_nv(this) - fns=fieldnames(this.Unmatched); - vals=struct2cell(this.Unmatched); - unmatched_nv=cell(1,2*length(fns)); - for i=1:length(fns) - unmatched_nv{2*i-1}=fns{i}; - unmatched_nv{2*i}=vals{i}; - end + unmatched_nv=struct2namevalue(this.Unmatched); end end end diff --git a/@MyScpiInstrument/MyScpiInstrument.m b/@MyScpiInstrument_old/MyScpiInstrument_old.m similarity index 100% rename from @MyScpiInstrument/MyScpiInstrument.m rename to @MyScpiInstrument_old/MyScpiInstrument_old.m diff --git a/Base instrument classes/MyInstrument.m b/Base instrument classes/MyInstrument.m index a936ad9..7dad433 100644 --- a/Base instrument classes/MyInstrument.m +++ b/Base instrument classes/MyInstrument.m @@ -1,119 +1,124 @@ classdef MyInstrument < dynamicprops properties (Access = public) % Synchronize all properties after every new value auto_sync = true end properties (SetAccess = protected, GetAccess = public) CommandList = struct() end properties (Dependent = true) command_names write_command_names read_command_names command_no end methods (Access = public) function obj = MyInstrument(varargin) createCommandList(this); end % Dummy function that is redefined in subclasses to % incorporate addCommand statements function createCommandList(~) end % Read all parameters of the physical device function sync(this) wc = this.read_command_names; for i=1:length(wc) tag = wc{i}; val = this.CommandList.(tag).read_fcn(); setCommand(this, tag, val, false); end end - end - - methods (Access = protected) + function addCommand(this, tag, varargin) p=inputParser(); addRequired(p,'tag', @(x)isvarname(x)); addParameter(p,'read_fcn',[], @(x)isa(x, 'function_handle')); addParameter(p,'write_fcn',[], @(x)isa(x, 'function_handle')); addParameter(p,'valudation_fcn',[], ... @(x)isa(x, 'function_handle')); addParameter(p,'value_list',{}, @iscell); + addParameter(p,'default',[]); addParameter(p,'info','', @ischar); parse(p,tag,varargin{:}); this.CommandList.(tag) = p.Results; if ~ismember('value_list', p.UsingDefaults) assert(isempty(this.CommandList.(tag).validation_fcn), ... ['validation_fcn is already assigned, cannot ' ... 'create a new one based on value_list']); this.CommandList.(tag).valudation_fcn = ... @(x) any(cellfun(@(y) isequal(y, x),... this.CommandList.(tag).value_list)); end assert(~isprop(this,tag), ['Property named ' tag ... ' already exists in the class.']); - H = addprop(this,tag); + H = addprop(this, tag); + + H.GetAccess = 'public'; if ~isempty(this.CommandList.(tag).write_fcn) H.SetAccess = 'public'; H.SetMethod = @(x,y)this.setCommand(x,y,true); else H.SetAccess = 'protected'; end + + this.(tag) = p.Results.default; end - + end + + methods (Access = protected) % Set method shared by all the commands function setCommand(this, tag, val, enable_write) if enable_write % Write and confirm the new value by reading assert(this.CommandList.(tag).valudation_fcn(val)); this.CommandList.(tag).write_fcn(val); if this.auto_sync sync(this); end if isempty(this.CommandList.(tag).read_fcn) % Assign the nominal value if it cannot be read this.(tag) = val; end else this.(tag) = val; end end end %% Set and Get functions methods function val=get.command_names(this) val=fieldnames(this.CommandList); end function val=get.write_command_names(this) ind_w=structfun(@(x) ~isempty(x.write_fcn), this.CommandList); val=this.write_command_names(ind_w); end function val=get.read_command_names(this) ind_r=structfun(@(x) ~isempty(x.write_fcn), this.CommandList); val=this.command_names(ind_r); end function command_no=get.command_no(this) command_no=length(this.command_names); end end end diff --git a/Base instrument classes/MyScpiInstrument.m b/Base instrument classes/MyScpiInstrument.m new file mode 100644 index 0000000..4c19c5e --- /dev/null +++ b/Base instrument classes/MyScpiInstrument.m @@ -0,0 +1,62 @@ +% Class featuring a specialized framework for instruments supporting SCPI + +classdef MyScpiInstrument < MyInstrument + + methods (Access = public) + function this = MyScpiInstrument() + + end + + % Extend the functionality of base class method + function addCommand(this, tag, varargin) + p=inputParser(); + p.KeepUnmatched=true; + addParameter(p,'command','',@ischar); + addParameter(p,'access','rw',@ischar); + parse(p, varargin{:}); + + % Supply the remaining parameters to the base function + unmatched_nv=struct2namevalue(p.Unmatched); + addCommand@MyInstrument(this, tag, unmatched_nv{:}); + + % Add abbreviated forms to the list of values + this.CommandList.(tag).val_list = ... + extendValList(this, this.CommandList.(tag).val_list); + + % Create read and write functions + if ~isempty(p.Results.command) + + end + end + + % + function sync(this) + + end + end + + + methods (Access = protected) + + % Add the list of values, if needed extending it to include + % short forms. For example, for the allowed value 'AVErage' + % its short form 'AVE' also will be added. + function ext_vl = extendValList(~, vl) + short_vl={}; + for i=1:length(vl) + if ischar(vl{i}) + idx = isstrprop(vl{i},'upper'); + short_form=vl{i}(idx); + % Add the short form to the list of values if it was + % not included explicitly + if ~ismember(short_form, vl) + short_vl{end+1}=short_form; %#ok + end + end + end + ext_vl=[vl, short_vl]; + end + end + +end + diff --git a/Utility functions/struct2namevalue.m b/Utility functions/struct2namevalue.m new file mode 100644 index 0000000..5b79da7 --- /dev/null +++ b/Utility functions/struct2namevalue.m @@ -0,0 +1,12 @@ +% Convert structure S to the list of fieldname-value pairs + +function nv = struct2namevalue(S) + fns=fieldnames(S); + vals=struct2cell(S); + nv=cell(1,2*length(fns)); + for i=1:length(fns) + nv{2*i-1}=fns{i}; + nv{2*i}=vals{i}; + end +end +