diff --git a/@MyCeCryo/MyCeCryo.m b/@MyCeCryo/MyCeCryo.m index 350edec..b632d50 100644 --- a/@MyCeCryo/MyCeCryo.m +++ b/@MyCeCryo/MyCeCryo.m @@ -1,170 +1,166 @@ % Class for controlling the auto manifold of ColdEdge stinger cryostat. % The manifold is managed by an Arduino board that communicates with % computer via serial protocol. classdef MyCeCryo < MyScpiInstrument properties (GetAccess = public, SetAccess = protected, ... SetObservable = true) % If a long term operation (e.g. starting a cooldown or pumping the % capillary) is in progress operation_in_progress % Time for the recirculator to pump down helium from the capillary % before closing it off tr = 20 end properties (Access = protected) Timer end methods (Access = public) function this = MyCeCryo(interface, address, varargin) this@MyScpiInstrument(interface, address, varargin{:}); this.Timer = timer(); % Buffer size of 64 kB should be way an overkill. The labview % program provided by ColdEdge use 256 Bytes. this.Device.InputBufferSize=2^16; this.Device.OutputBufferSize=2^16; end function delete(this) try stop(this.Timer); catch ME warning(ME.message); end try delete(this.Timer); catch ME warning(ME.message); end end % Abort the current operation function abort(this) stop(this.Timer); this.operation_in_progress = false; writePropertyHedged(this, ... 'valve1', false, ... 'valve2', false, ... 'valve3', false, ... 'valve4', false, ... 'valve5', false, ... 'valve7', false, ... 'recirc', false, ... 'cryocooler', false); end function startCooldown(this) assert(~this.operation_in_progress, ['Cannot initiate' ... ' cooldown stop. Another operation is in progress.']) writePropertyHedged(this, ... 'valve2', false, ... 'valve3', false, ... 'valve5', false, ... 'valve7', false); writePropertyHedged(this, ... 'valve1', true, ... 'valve4', true, ... 'recirc', true, ... 'cryocooler', true); end function stopCooldown(this) function switchRecirculatorOff(~, ~) writePropertyHedged(this, ... 'valve1', false, ... 'valve2', false, ... 'valve3', false, ... 'valve4', false); % Switch off the recirculator after all the valves are % closed writePropertyHedged(this, 'recirc', false); this.operation_in_progress = false; end assert(~this.operation_in_progress, ['Cannot initiate' ... ' cooldown stop. Another operation is in progress.']) % Switch off the cryocooler, close the recirculator supply % valve (1) and at the same time open the valves bridging the % recirculator return and supply (2 and 3). writePropertyHedged(this, ... 'valve1', false, ... 'valve2', true, ... 'valve3', true, ... 'cryocooler', false); % Wait for the helium to be pumped out of the capillary by the % recirculator and then switch the recirculator off this.Timer.ExecutionMode = 'singleShot'; this.Timer.StartDelay = this.tr; this.Timer.TimerFcn = @switchRecirculatorOff; start(this.Timer); this.operation_in_progress = true; end end methods (Access = protected) function createCommandList(this) % Valve states for i=1:7 if i == 6 continue % No valve 6 end tag = sprintf('valve%i',i); cmd = sprintf(':VALVE%i',i); addCommand(this, tag, cmd,... 'default', false, ... 'fmt_spec', '%b', ... 'info', 'Valve open(true)/clsed(false)'); end addCommand(this, 'recirc', ':REC',... 'default', false, ... 'fmt_spec', '%b', ... 'info', 'Recirculator on/off'); addCommand(this, 'cryocooler', ':COOL',... 'default', false, ... 'fmt_spec', '%b', ... 'info', 'Cryocooler on/off'); addCommand(this, 'press', ':PRES',... 'default', false, ... 'fmt_spec', '%e', ... 'access', 'r', ... 'info', 'Supply pressure (PSI)'); end end methods - function set.operation_in_progress(this, ~) - notify(this, 'PropertyRead'); - end - function val = get.operation_in_progress(this) try val = strcmpi(this.Timer.Running, 'on'); catch ME warning(ME.message); val = false; end end end end diff --git a/GUIs/GuiCeCryo.mlapp b/GUIs/GuiCeCryo.mlapp index 481b574..927adaa 100644 Binary files a/GUIs/GuiCeCryo.mlapp and b/GUIs/GuiCeCryo.mlapp differ diff --git a/Utility functions/App utilities/linkGuiElement.m b/Utility functions/App utilities/linkGuiElement.m index c3f47ee..001c60a 100644 --- a/Utility functions/App utilities/linkGuiElement.m +++ b/Utility functions/App utilities/linkGuiElement.m @@ -1,241 +1,238 @@ % Using app.linked_elem_list, create correspondence between app % properties or sub-properties and Value fields of control elements. % The elements added to linked_elem_list are updated when updateGui is % called. % This function is applicable to any app sub-properties, but also contains % extended functionality in case the tag corresponds to a command of % MyScpiInstrument. % By default a callback to the ValueChanged event of the gui element is % assigned, for which the app needs to have createGenericCallback method function linkGuiElement(app, elem, prop_tag, varargin) p=inputParser(); % GUI control element addRequired(p,'elem'); % Instrument command to be linked to the GUI element addRequired(p,'prop_tag',@ischar); % A property of the GUI element that is updated according to the value % under prop_tag can be other than 'Value' (e.g. 'Color' in the case of % a lamp indicator) addParameter(p,'elem_prop','Value',@ischar); % If input_presc is given, the value assigned to the instrument propery % is related to the value x displayed in GUI as x/input_presc. addParameter(p,'input_presc',1,@isnumeric); % Arbitrary processing functions can be specified for input and output. % out_proc_fcn is applied to values before assigning them to gui % elements and in_proc_fcn is applied before assigning % to the linked properties addParameter(p,'out_proc_fcn',@(x)x,@(f)isa(f,'function_handle')); addParameter(p,'in_proc_fcn',@(x)x,@(f)isa(f,'function_handle')); addParameter(p,'create_callback',true,@islogical); % For drop-down menues initializes entries automatically based on the % list of values. Ignored for all the other control elements. addParameter(p,'init_val_list',false,@islogical); - addParameter(p,'lamp_on_color', MyAppColors.lampOn()); - addParameter(p,'lamp_off_color', MyAppColors.lampOff()); - parse(p,elem,prop_tag,varargin{:}); create_callback = p.Results.create_callback; if isempty(prop_tag) warning('''prop_tag'' is empty, element is not linked') return end % Make sure the property tag starts with a dot and convert to % subreference structure if prop_tag(1)~='.' PropSubref=str2substruct(['.',prop_tag]); else PropSubref=str2substruct(prop_tag); end % Check if the referenced property is accessible try target_val=subsref(app, PropSubref); catch disp(['Property corresponding to tag ',prop_tag,... ' is not accessible, element is not linked and disabled.']) elem.Enable='off'; return end % Check if the tag refers to a property of an object, which also helps % to determine is callback is to be created if (length(PropSubref)>1) && isequal(PropSubref(end).type,'.') % Potential MyInstrument object Obj=subsref(app, PropSubref(1:end-1)); % Potential command name tag=PropSubref(end).subs; % Check if the property corresponds to an instrument command try is_cmd=ismember(tag, Obj.command_names); catch % If anything goes wrong in the previous block the prop is not % a command is_cmd=false; end if is_cmd % Object is an instrument. Instr=Obj; % Never create callbacks for read-only properties. if ~contains(Instr.CommandList.(tag).access,'w') create_callback=false; end % Then check if the tag corresponds to a simple object % property (and not to a structure field) elseif isprop(Obj, tag) try % indprop may sometimes throw errors, especially on Matlab % below 2018a, therefore use try-catch mp = findprop(Obj, tag); % Newer create callbacks for the properties with % attributes listed below, as those cannot be set if mp.Constant||mp.Abstract||~strcmpi(mp.SetAccess,'public') create_callback=false; end catch end end else is_cmd=false; end % Check if the gui element is editable - if it is not, then a callback % is not assigned. This is only meaningful for uieditfieds. Drop-downs % also have 'Editable' property, but it corresponds to the editability % of elements and does not have an effect on assigning callback. if (strcmpi(elem.Type, 'uinumericeditfield') || ... strcmpi(elem.Type, 'uieditfield')) ... && strcmpi(elem.Editable, 'off') create_callback=false; end % If the gui element is disabled callback is not assigned if isprop(elem, 'Enable') && strcmpi(elem.Enable, 'off') create_callback=false; end % If create_callback is true and the element does not already have % a callback, assign genericValueChanged as ValueChangedFcn if create_callback && isprop(elem, 'ValueChangedFcn') && ... isempty(elem.ValueChangedFcn) % A public createGenericCallback method needs to intorduced in the % app, as officially Matlab apps do not support an automatic % callback assignment (as of the version of Matlab 2018a) assert(ismethod(app,'createGenericCallback'), ['App needs to ',... 'contain public createGenericCallback method to automatically'... 'assign callbacks. Use ''create_callback'',false in order to '... 'disable automatic callback']); elem.ValueChangedFcn = createGenericCallback(app); % Make callbacks non-interruptible for other callbacks % (but are still interruptible for timers) try elem.Interruptible = 'off'; elem.BusyAction = 'cancel'; catch warning('Could not make callback for %s non-interruptible',... prop_tag); end end % A step relevant for lamp indicators. It is often convenient to have a % lamp as an indicator of on/off state. If a lamp is being linked to a % logical-type variable we therefore assign OutputProcessingFcn puts % logical values in corresponcence with colors if strcmpi(elem.Type, 'uilamp') && ~iscolor(target_val) % The property of lamp that is to be updated by updateGui is not % Value but Color elem.UserData.elem_prop='Color'; % Select between the default on and off colors. Different colors % can be indicated by explicitly setting OutputProcessingFcn that % will overwrite the one assigned here. elem.UserData.OutputProcessingFcn = ... - @(x)select(x, p.Results.lamp_on_color, p.Results.lamp_off_color); + @(x)select(x, MyAppColors.lampOn(), MyAppColors.lampOff()); end % If a prescaler, input processing function or output processing % function is specified, store it in UserData of the element if p.Results.input_presc ~= 1 elem.UserData.InputPrescaler = p.Results.input_presc; end if ~ismember('in_proc_fcn',p.UsingDefaults) elem.UserData.InputProcessingFcn = p.Results.in_proc_fcn; end if ~ismember('out_proc_fcn',p.UsingDefaults) elem.UserData.OutputProcessingFcn = p.Results.out_proc_fcn; end if ~ismember('elem_prop',p.UsingDefaults) elem.UserData.elem_prop = p.Results.out_proc_fcn; end %% Linking % The link is established by storing the subreference structure % in UserData and adding elem to the list of linked elements elem.UserData.LinkSubs = PropSubref; app.linked_elem_list = [app.linked_elem_list, elem]; %% MyScpiInstrument-specific % The remaining code provides extended functionality for linking to % commands of MyScpiInstrument if is_cmd % If supplied command does not have read permission, issue warning. if ~contains(Instr.CommandList.(tag).access,'r') fprintf(['Instrument command ''%s'' does not have read permission,\n',... 'corresponding gui element will not be automatically ',... 'syncronized\n'],tag); % Try switching color of the gui element to orange try elem.BackgroundColor = MyAppColors.warning; catch try elem.FontColor = MyAppColors.warning; catch end end end % Auto initialization of entries, for dropdown menus only if p.Results.init_val_list && isequal(elem.Type, 'uidropdown') try cmd_val_list = Instr.CommandList.(tag).val_list; if all(cellfun(@ischar, cmd_val_list)) % If the command has only string values, get the list of % values ignoring abbreviations cmd_val_list = stdValueList(Instr, tag); % Capitalized the displayed values elem.Items = cellfun(@(x)[upper(x(1)),lower(x(2:end))],... cmd_val_list,'UniformOutput',false); else % Items in a dropdown should be strings, so convert if % necessary str_list=cell(length(cmd_val_list), 1); for i=1:length(cmd_val_list) if ~ischar(cmd_val_list{i}) str_list{i}=num2str(cmd_val_list{i}); end end elem.Items = str_list; end % Assign raw values list as ItemsData elem.ItemsData = cmd_val_list; catch warning(['Could not automatically assign values',... ' when linking ',tag,' property']); end end end end