classdef SCDclass_algo %SCD algorithm handling object % The class holds all information and % methods for handling a Simulink % algorithm properties (Access = private) modelname % Name of the model mdscontainer % Container class for MDS+ interface objects taskcontainer % Container class for init and term tasks exportedtps % List of tunable parameters variable to be exported datadictionary % Name of the used data dictionary refdatadictionaries % Cell array of referenced data dictionaries stdinits % General initialization scripts fpinits % inits scripts for fixed parameters timing % Timing info structure exportedtpsdefaults % default tunable parameters defining functions inBus % name of input bus outBus % name of output bus modelslx % slx model file name folder % folder containing algorithm end methods function obj=SCDclass_algo(name) %% Constructor % Empty algorithm constructor obj.modelname=name; obj.mdscontainer = SCDclass_mdsobjcontainer; obj.taskcontainer = SCDclass_taskcontainer; obj.exportedtps = {}; obj.exportedtpsdefaults = {}; obj.stdinits = {}; obj.fpinits = {}; obj.datadictionary = ''; obj.refdatadictionaries = {}; obj.timing.t_start = 0 ; obj.timing.t_stop = 1 ; obj.timing.dt =1e-3; % buses obj.inBus = [obj.modelname,'_inBus' ]; obj.outBus = [obj.modelname,'_outBus']; obj.modelslx = [obj.modelname,'.slx']; % files % algo path depending on .slx location assert(~isempty(which(obj.modelslx)),'can''t find slx model %s',obj.modelslx); obj.folder = fileparts(which(obj.modelslx)); % Standard data dictionary equal to algorithm name, % overriddable by the setter method obj=obj.setdatadictionary([name '.sldd']); end %% Print infos function printinfo(obj) fprintf('*****************************************************\n'); fprintf('* SCD algorithm: ''%s''\n',obj.modelname); fprintf('*****************************************************\n'); if(~isempty(obj.datadictionary)) fprintf('* Data dictionary:\n %s\n',obj.datadictionary); else fprintf('* Data dictionary:\n base workspace\n'); end if numel(obj.exportedtps)>0 fprintf('* Tunable params structs:\n'); for ii=1:numel(obj.exportedtps) fprintf(' %s\n',obj.exportedtps{ii}); end end fprintf('* Tunable params objects:\n') obj.printparameters; fprintf('* Wavegen objects:\n'); obj.printwavegens; fprintf('* Tasks objects:\n'); obj.printtasks; fprintf('* Fixed parameters inits:\n'); obj.printinits; fprintf('*****************************************************\n'); end %% Setup function setup(obj) % setup() % % calls updatetemplatetp and % buildworkspacetpstruct % % an algorithm block diagram % should pass ctrl-d (instandalone opening) % after this call obj.updatetemplatetp; obj.buildworkspacetpstruct; end %% General purpose getters function out = getname(obj) out=obj.modelname; end function out = getmdscontainer(obj) out=obj.mdscontainer; end function out = gettaskcontainer(obj) out=obj.taskcontainer; end function out = gettiming(obj) out=obj.timing; end function out = getinits(obj) out=obj.fpinits; end %% Data dictionary setter and getter function obj = setdatadictionary(obj, datadict) obj.datadictionary=datadict; end function out = getdatadictionary(obj) out = obj.datadictionary; end function createdatadictionary(obj,varargin) % create data dictionary in path obj.folder/obj.getdatadictionary dd = obj.getdatadictionary; ddpath = fullfile(obj.folder,dd); % Close all open models. bdclose all % Close all open data dictionaries Simulink.data.dictionary.closeAll('-discard'); % Create if not existing already if isempty(which(ddpath)) fprintf('generating data dictionary %s\n',fullfile(obj.folder,obj.getdatadictionary)); Simulink.data.dictionary.create(ddpath); end end function obj = setreferenceddatadictionaries(obj,refdd) if ischar(refdd) && ~iscell(refdd); refdd={refdd}; end % to cell assert(iscell(refdd) && isrow(refdd),'refdd must be a row cell array of data dictionary names') obj.refdatadictionaries = refdd; end %% Tunable parameters structures handling functions function out = getexportedtps(obj) out = obj.exportedtps; end function obj = addtunparamstruct(obj, structname, default_function) % obj = addtunparamstruct(structname, varargin) % % adds a tunable parameter to the algo object % inputs: % structname: name of the tunable param (string) % varargin{1}: a function handle to get % its default value, this function takes no arguments % and must return either a struct or a Simulink.Parameter % % example: obj=obj.addtunparamstruct('SCDalgo_doublet_tp', % @()SCDalgo_doublet_loadtp()); if(~ismember(structname, obj.exportedtps)) obj.exportedtps{end+1}=structname; else error('SCDclass_algo:addtunparamsstruct','Tunable parameter struct already present, cannot add!'); end if nargin==3 assert(isa(default_function, 'function_handle'),'SCDclass_algo:addtunparamstruct', 'third argument, if present, must be a function handle'); obj.exportedtpsdefaults{end+1} = default_function; % add defaults to list else obj.exportedtpsdefaults{end+1} = []; % empty, no default assigned for this tp end end function obj = updatetemplatetp(obj) % obj = updatetemplatetp() % % For every configured tunparams structure % if a default value function is present it is % updated in the template version of the tun params % in the algo data dictionary. % The default value function of a tunparams can be given % as an optional third argument of addtunparamstruct % method dict = Simulink.data.dictionary.open(obj.getdatadictionary); DD = getSection(dict, 'Design Data'); for ii=1:numel(obj.exportedtps) if ~isempty(obj.exportedtpsdefaults{ii}) % get current value of parameters from the function % handle fprintf(' %s: calling %s\n', obj.getname, char(obj.exportedtpsdefaults{ii})); TP=obj.exportedtpsdefaults{ii}(); if ~isa(TP,'Simulink.Parameter') assert(isstruct(TP),'default function must return either a Simulink.Parameter or a structure'); P = Simulink.Parameter; P.Value = TP; else P = TP; end P.CoderInfo.StorageClass='ExportedGlobal'; % updates default tunable parameters structure in data dictionary tmplname = sprintf('%s_tmpl',obj.exportedtps{ii}); if DD.exist(tmplname) oldEntry = DD.getEntry(tmplname); if isequal(oldEntry.getValue.Value,P.Value) fprintf('%s: keep old template %s since not changed\n',obj.getname,tmplname); continue; else % replace oldEntry.setValue(P); fprintf('%s: replaced value of template %s since it changed\n',obj.getname,tmplname) end else fprintf(' %s: added new %s\n',obj.getname, tmplname); DD.addEntry(tmplname,P); end end end end function obj = buildworkspacetpstruct(obj) % obj = buildworkspacetpstruct(obj) % % this funtion builds a workspace structures containing % replicas of all tunable parameters structurea in the data % dictionaries, this structure is the one actually used % for loading simulation data from MDS % It is better not to use directly data dictionaries structures % to avoid flooding dds with big sim data sets (and % consequently the SCD GIT itself if(isempty(obj.datadictionary)) warning('SCDclass_algo:buildworkspacetpstruct','Methods ignored for this object, data dictionary isn''t configured'); return; end dd=SCDconf_getdatadict(obj.datadictionary); for ii=1:numel(obj.exportedtps) simstructnamedd=sprintf('%s_tmpl',char(obj.exportedtps(ii))); simstructnamews=char(obj.exportedtps(ii)); simstruct = dd.getEntry(simstructnamedd).getValue; assignstr=sprintf('%s=temp;',simstructnamews); assignin('base','temp',simstruct); evalin('base',assignstr); end evalin('base','clear temp;'); end function addbuses(obj) % Add buses to data dictionary from .m file descriptions, if % available. Bus names in obj.inBus, obj.outBus if ~isempty(which(obj.inBus)) && ~isempty(which(obj.outBus)) dict = Simulink.data.dictionary.open(obj.getdatadictionary); designDataObj = getSection(dict, 'Design Data'); fprintf('Add buses to data dictionary: %s, %s\n',obj.inBus,obj.outBus); eval(obj.inBus); eval(obj.outBus); % eval buses in base workspace dosave = false; dosave = obj.replaceorcreateddentry(designDataObj,obj.inBus ,evalin('base',obj.inBus )) || dosave; dosave = obj.replaceorcreateddentry(designDataObj,obj.outBus,evalin('base',obj.outBus )) || dosave; if dosave, dict.saveChanges; end % Save if necessary evalin('base',sprintf('clear %s %s',obj.inBus,obj.outBus)); else fprintf('no buses imported in datadictionary - assuming they are already there\n') end end %% MDS container methods forwarders function obj = addparameter(obj, param) obj.mdscontainer=obj.mdscontainer.addparameter(param); obj.mdscontainer=obj.mdscontainer.bindlastparameter(obj.modelname, obj.datadictionary, obj.exportedtps); end function obj = printparameters(obj) obj.mdscontainer=obj.mdscontainer.printparameters; end function obj = actualizeparameters(obj, shot) obj.mdscontainer=obj.mdscontainer.actualizeparameters(shot); end function obj = addwavegen(obj, wavegen) obj.mdscontainer=obj.mdscontainer.addwavegen(wavegen); obj.mdscontainer=obj.mdscontainer.bindlastwavegen(obj.modelname, obj.datadictionary, obj.timing); end function obj = printwavegens(obj) obj.mdscontainer=obj.mdscontainer.printwavegens; end function obj = actualizewavegens(obj, shot) obj.mdscontainer=obj.mdscontainer.actualizewavegens(shot); end function obj = cleanwavegens(obj) obj.mdscontainer=obj.mdscontainer.cleanwavegens; end %% Task container methods forwarders function obj = addtask(obj, task) obj.taskcontainer=obj.taskcontainer.addtask(task); obj.taskcontainer=obj.taskcontainer.bindlasttask(obj.modelname, obj.datadictionary); end function obj = printtasks(obj) obj.taskcontainer=obj.taskcontainer.printtasks; end %% Timing information function obj = settiming(obj, t_start, dt, t_stop) obj.timing.t_start=t_start; obj.timing.dt=dt; obj.timing.t_stop=t_stop; end %% Generic and fixed parameter inits function obj = addstdinitfcn(obj,fcnhandle) if ischar(fcnhandle) fcnhandle = str2func(fcnhandle); end assert(isa(fcnhandle,'function_handle'),'stdinit function must be a function handle'); assert(nargout(fcnhandle)==0,'stdinit functions may not have output arguments') obj.stdinits{end+1} = fcnhandle; end function obj = addfpinitfcn(obj, fcnname, targetstruct,targetworkspace) if nargin < 4 targetworkspace = 'datadictionary'; end if ~iscell(targetstruct) && ~isempty(targetstruct) targetstruct = {targetstruct}; end for ii=1:numel(obj.fpinits) for istruct = 1:numel(targetstruct) mytarget = targetstruct{istruct}; if ~isempty(mytarget) && contains(obj.fpinits{ii}{2},mytarget) warning('SCDclass_algo:addfpinitfcn',... 'A function defining the structure %s has already been added, ignoring.\d',mytarget) return end end end % FF question to CG: why is this a cell and not a struct? temp=cell(10,1); temp{1}=fcnname; temp{2}=targetstruct; temp{3}=targetworkspace; obj.fpinits{end+1}=temp; end function printinits(obj) if(~isempty(obj.fpinits)) for ii=1:numel(obj.fpinits) if isempty(obj.fpinits{ii}{2}) fprintf('%s : init function with no outputs\n',char(obj.fpinits{ii}{1})); else fprintf('%s -> %s in workspace %s \n',char(obj.fpinits{ii}{1}),char(obj.fpinits{ii}{2}{1}),char(obj.fpinits{ii}{3})); end end end end function init(obj) obj.callinits; % call generic algorithm init functions obj.setupdd; % setup data dictionary end function setupdd(obj) % setup data dictionary % generate/initialize data dictionary obj.createdatadictionary; % link data dictionary to model and enable access to base obj.load; set_param(obj.getname,'DataDictionary',obj.getdatadictionary,'EnableAccessToBaseWorkspace','on'); % call fixed parameter setup functions obj.callfpinits; % populate with template tunable parameters obj.updatetemplatetp; % add buses obj.addbuses; % add referenced data dictionaries obj.addrefdd end function callinits(obj) for ii=1:numel(obj.stdinits) fprintf('calling standard init function %s\n',func2str(obj.stdinits{ii})); initfunction = obj.stdinits{ii}; initfunction(); % call it end end function callfpinits(obj) % call initialization functions that set fixed parameters if ~isempty(obj.fpinits) for ii=1:numel(obj.fpinits) initfunction = obj.fpinits{ii}{1}; targetnames = obj.fpinits{ii}{2}; workspace = obj.fpinits{ii}{3}; nout = numel(targetnames); if ~isempty(targetnames) fprintf('Calling fixed parameter init function ''%s'', assigning its output to ''%s'' in %s...\n',... char(initfunction),cell2mat(targetnames),workspace); end if ischar(initfunction) initcmd=sprintf('%s(obj);', initfunction); [value{1:nout}] = eval(initcmd); elseif isa(initfunction,'function_handle') if nargin(initfunction)==1 argins={obj}; elseif nargin(initfunction)==0 argins = {}; else error('unexpected number of input arguments for function %s',func2str(initfunction)); end [value{1:nout}] = initfunction(argins{:}); % function has an input argument else error('initfunction must be a string or a function handle') end % assigns in target datadictionary depending % on target workspace dosave = false; dictionaryObj = Simulink.data.dictionary.open(obj.getdatadictionary); dd = getSection(dictionaryObj, 'Design Data'); for iout = 1:nout % cycle over number of output arguments of the function val = value{iout}; target = targetnames{iout}; if strcmp(workspace,'datadictionary') % assign in associated data dictionary dosave = obj.replaceorcreateddentry(dd,target,val) || dosave; elseif strcmp(workspace,'base') % assign in base workspace assignin('base',target,val) end end if dosave, dictionaryObj.saveChanges; end end end end function addrefdd(obj) % add configurations.sldd as referenced data dictionary, plus % other optional data dictionaries (e.g. used by lower-level components of an algorithm) dosave = false; for refdd = ['configurations.sldd',obj.refdatadictionaries] dictionaryObj = Simulink.data.dictionary.open(obj.getdatadictionary); if ~ismember(refdd{:},dictionaryObj.DataSources) fprintf('adding referenced data dictionary %s to %s \n',refdd{:},obj.getdatadictionary) refddpath = which(refdd{:}); assert(~isempty(refddpath),'could not find %s'); dictionaryObj.addDataSource(refdd{:}); dosave = true; end if dosave, dictionaryObj.saveChanges; end end end function changed = replaceorcreateddentry(obj,dd,entry,value) if dd.exist(entry) oldEntry = dd.getEntry(entry); if isequal(oldEntry.getValue,value) fprintf('%s: keep old value of %s since not changed\n',obj.getname,entry); changed = false; else oldEntry.setValue(value); % replace fprintf('%s: replaced value of %s since it changed\n',obj.getname,entry); changed = true; end else fprintf(' %s: added new %s\n',obj.getname, entry); DD.addEntry(entry,value); changed = true; end end %% generic operation methods function compile(obj) try eval(sprintf('%s([],[],[],''compile'')',obj.modelname)); catch ME try eval(sprintf('%s([],[],[],''term'')',obj.modelname)); % terminate if necessary catch rethrow(ME); % rethrow error so we can see it end end eval(sprintf('%s([],[],[],''term'')',obj.modelname)); % terminate is successful end function sim(obj) sim(obj.modelname); end function test_harness(obj) harnessname = sprintf('%s_harness.slx',obj.getname); if ~exist(harnessname,'file') warning('no harness %s found, skipping test',harnessname); return else fprintf('running test harness %s\n',harnessname); sim(harnessname); % add tests to check output result somehow (TBD) end end function open(obj) open(obj.modelname) end function load(obj) load_system(obj.modelname); end end end