Newer
Older
classdef SCDclass_algo
%SCD algorithm handling object
% The class holds all information and
% methods for handling a Simulink
% algorithm
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
% Empty algorithm constructor
obj.modelname=name;
obj.mdscontainer = SCDclass_mdsobjcontainer;
obj.taskcontainer = SCDclass_taskcontainer;
obj.exportedtps = {};

Cristian Galperti
committed
obj.exportedtpsdefaults = {};
obj.stdinits = {};
obj.fpinits = {};
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

Cristian Galperti
committed
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)
%% 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');
fprintf('generating data dictionary %s\n',fullfile(obj.folder,obj.getdatadictionary));
Simulink.data.dictionary.create(ddpath);
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;
function obj = addtunparamstruct(obj, structname, default_function)
% obj = addtunparamstruct(structname, varargin)

Cristian Galperti
committed
%
% 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!');

Cristian Galperti
committed
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

Cristian Galperti
committed
end
end
function obj = updatetemplatetp(obj)
% obj = updatetemplatetp()

Cristian Galperti
committed
%
% For every configured tunparams structure
% if a default value function is present it is

Cristian Galperti
committed
% updated in the template version of the tun params
% in the algo data dictionary.
% The default value function of a tunparams can be given

Cristian Galperti
committed
% as an optional third argument of addtunparamstruct
% method
dict = Simulink.data.dictionary.open(obj.getdatadictionary);
DD = getSection(dict, 'Design Data');

Cristian Galperti
committed
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}));

Cristian Galperti
committed
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';

Cristian Galperti
committed
% 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)

Cristian Galperti
committed
else
fprintf(' %s: added new %s\n',obj.getname, tmplname);

Cristian Galperti
committed
DD.addEntry(tmplname,P);
end
end
function obj = buildworkspacetpstruct(obj)

Cristian Galperti
committed
% 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

Cristian Galperti
committed
% 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

Cristian Galperti
committed
% 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
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
%% 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}));
fprintf('%s -> %s in workspace %s \n',char(obj.fpinits{ii}{1}),char(obj.fpinits{ii}{2}{1}),char(obj.fpinits{ii}{3}));
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
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
% 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);
fprintf('Calling fixed parameter init function ''%s'', assigning its output to ''%s'' in %s...\n',...
char(initfunction),cell2mat(targetnames),workspace);
if ischar(initfunction)
initcmd=sprintf('%s(obj);', initfunction);
[value{1:nout}] = eval(initcmd);
elseif isa(initfunction,'function_handle')
if nargin(initfunction)==1
elseif nargin(initfunction)==0
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
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
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
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