classdef SCDclass_expcode %An SCD expcode object % % Main methods: % .open: Open baseline .slx model % open(N): Open node N % open(N,M): Open thread M of node N % .build, build(N), build(N,M): Generate C code for node N, thread M % .compile, compile(N), compile(N,M): Compile Simulink model for node N, thread M) % .close_all: Close all Simulink block diagrams % % .printinfo: Print information about components of this expcode % .printtasks: Print configured tasks % .printwavegens: Print configured wavegen signals %. properties (Access = public) % Main properties name % Expcode main name maincode % Expcode numerical code status % Expcode development status end properties (Access = protected) loadverbose % Verbosity level of the loading (currently 0 or 1) end properties (Access = private) activenodes % numerical list of configured active nodes activecpus % numerical list of configured CPUs %nodeddlist % list of data dictionaries at node level wrapperlist % list of configured wrappers initobjlist % list of loaded algorithm objects with configured inits algonamelist % List of loaded algorithm names algoddlist % list of data dictionaries at algorithm level mdscontainer % container class for MDS+ interface objects taskcontainer % container class for generic init and term task objects exportedtps % list of tunable parameters variable to be exported fpinits % list of standard inits scripts modeltoactualize % name of the algorithm to actualize ('all' means all coonfigured) algos % list of algorithms objects nodes % array of node properties structs ddname % top level data dictionary name for the expcode ddpath % top level data dicationary save path end %% %% STATIC METHODS %% methods(Static) function help help(mfilename) end function node = defaultnode(nodenr) % [general part] node.hasrfm = 1; % all nodes have rfm node.datadict=sprintf('SCD_rtc_%02d.sldd',nodenr); node.algos = {}; % node-specific algos node.algodatadicts = {}; % data dictionaries for these algos switch nodenr case 1 node.ncpu = 1; node.type = 'std1cpu2015a'; node.timing.t_start = -4.5; node.timing.t_stop = 3; node.timing.dt = 1e-4; node.thperiod = 1.0000e-03; node.buildcfg.conffile{1} = 'standard'; node.buildcfg.initscdbeforecomp = 0; node.haswavegen = 1; node.hasethercat = 0; node.hasadc = 1; case 2 node.ncpu = 6; node.type = 'std1cpu2015a'; node.timing.t_start = -4.5; node.timing.t_stop = 3; node.timing.dt = 1e-4; node.thperiod = [1.0000e-03 1.0000e-03 1.0000e-03 1.0000e-03 1.0e-3 1.0e-3]; node.buildcfg.conffile = cell(node.ncpu,1); node.buildcfg.conffile{1} = 'standard'; node.buildcfg.conffile{2} = 'standard'; node.buildcfg.conffile{3} = 'standard'; node.buildcfg.conffile{4} = 'standard'; node.buildcfg.conffile{5} = 'standard'; node.buildcfg.conffile{6} = 'standard'; node.buildcfg.initscdbeforecomp = 0; node.haswavegen = 1; node.hasadc = 1; node.hasethercat = 1; node.algos = {'SCDcalib02'}; % direct node algorithms case 3 node.ncpu = 4; node.type = '4cpus2015a'; node.timing.t_start = -2.0; node.timing.t_stop = 2.75; node.timing.dt = 1e-3; node.thperiod = [1.0000e-03 1.0000e-03 1.0000e-03 1.0000e-03]; node.buildcfg.conffile = cell(node.ncpu,1); node.buildcfg.conffile{1} = 'standard'; node.buildcfg.conffile{2} = 'standard'; node.buildcfg.conffile{3} = 'standard'; node.buildcfg.conffile{4} = 'standard'; node.buildcfg.initscdbeforecomp = [0 0 0 0]; node.haswavegen = 1; node.hasadc = 0; node.hasethercat = 0; case 4 node.ncpu = 4; node.type = 'std1cpu2015a'; node.timing.t_start = -4.5; node.timing.t_stop = 3; node.timing.dt = 1e-4; node.thperiod = 1.0000e-04; node.buildcfg.conffile{1} = 'standard'; node.buildcfg.initscdbeforecomp = 0; node.haswavegen = 1; node.hasadc = 0; node.hasethercat = 0; case 5 node.ncpu = 1; node.type = 'std1cpu2015a'; node.timing.t_start = -4.5; node.timing.t_stop = 3; node.timing.dt = 1e-4; node.thperiod = 1.0000e-04; node.buildcfg.conffile{1} = 'standard'; node.buildcfg.initscdbeforecomp = 0; node.haswavegen = 1; node.hasadc = 0; node.hasethercat = 0; case 6 node.ncpu = 4; node.type = '4cpus2015a'; node.timing.t_start = 0.0; node.timing.t_stop = 2.75; node.timing.dt = 1e-3; node.thperiod = [1.0000e-03 1.0000e-03 1.0000e-03 1.0000e-03]; node.buildcfg.conffile = cell(node.ncpu,1); node.buildcfg.conffile{1} = 'standard'; node.buildcfg.conffile{2} = 'standard'; node.buildcfg.conffile{3} = 'standard'; node.buildcfg.conffile{4} = 'standard'; node.buildcfg.initscdbeforecomp = [0 0 0 0]; node.haswavegen = 1; node.hasadc = 0; node.hasethercat = 0; case 7 node.ncpu = 4; node.type = '4cpus2015a'; node.timing.t_start = -0.5; node.timing.t_stop = 2.5; node.timing.dt = 1e-3; node.thperiod = [1.0000e-03 1.0000e-03 1.0000e-03 1.0000e-03]; node.buildcfg.conffile = cell(node.ncpu,1); node.buildcfg.conffile{1} = 'standard'; node.buildcfg.conffile{2} = 'standard'; node.buildcfg.conffile{3} = 'standard'; node.buildcfg.conffile{4} = 'standard'; node.buildcfg.initscdbeforecomp = [0 0 0 0]; node.haswavegen = 1; node.hasadc = 1; node.hasethercat = 0; case 8 node.ncpu = 1; node.type = '4cpus2015a'; node.timing.t_start = -0.5; node.timing.t_stop = 2.5; node.timing.dt = 1e-3; node.thperiod = [1.0000e-03 1.0000e-03 1.0000e-03 1.0000e-03]; node.buildcfg.conffile = cell(node.ncpu,1); node.buildcfg.conffile{1} = 'standard'; node.buildcfg.conffile{2} = 'standard'; node.buildcfg.conffile{3} = 'standard'; node.buildcfg.conffile{4} = 'standard'; node.buildcfg.initscdbeforecomp = [0 0 0 0]; node.haswavegen = 1; node.hasadc = 1; node.hasethercat = 0; end % CPU-dependent quantities node.wrapper = cell(node.ncpu,1); node.wrapdatadicts=cell(node.ncpu,1); node.varalgo = ones(node.ncpu,1); node.cpuactive = zeros(node.ncpu,1); % Wrapper data dictionaries [node.wrapdatadicts,node.wrapper] = deal(cell(node.ncpu,1)); % init for iwrap=1:node.ncpu node.wrapper{iwrap} = sprintf('SCDwrap_template%02d%02d.slx',nodenr,iwrap); node.wrapdatadicts{iwrap} = sprintf('SCDwrap_template%02d%02d.sldd',nodenr,iwrap); end % algorithm data dictionaries for ialgo = 1:numel(node.algos) node.algodatadicts{ialgo} = [node.algos{ialgo},'.sldd']; end end function build_nodes(compileslx_list) for mynodeslx = compileslx_list fprintf(' *** Buiding Node %s ***\n',mynodeslx{1}); rtwbuild(mynodeslx{1}); end end function myslx = getslxname(inode,icpu) if nargin==0 myslx = 'tcv'; elseif nargin==1 assert(isnumeric(inode)) myslx = sprintf('SCD_rtc_%02d',inode); elseif nargin==2 myslx = sprintf('SCD_rtccode_%02d_%02d',inode,icpu); else error('invalid number of arguments'); end end function mydd = getddname(inode) if nargin==0 mydd = 'tcv.sldd'; elseif nargin==1 assert(isnumeric(inode)) mydd = sprintf('SCD_rtc_%02d.sldd',inode); else error('invalid number of arguments'); end end function close_all(saveflag) if nargin==0 saveflag = 1; end assert(saveflag == 1 || saveflag == 0,'saveflag must be 1 or 0') if saveflag==1 closemsg = 'Saving and Closing %s\n'; else closemsg = 'Dicarding changes and closing %s\n'; end while ~isempty(bdroot) fprintf(closemsg,bdroot); close_system(bdroot,saveflag) end end end %% %% PUBLIC METHODS %% methods %% Constructor function obj=SCDclass_expcode() % All templates constructor obj.name = 'template'; obj.maincode = 1; obj.status = 'debug'; obj.loadverbose = 1; obj.ddname = obj.getddname; hardwarepath = fileparts(which('tcv.slx')); assert(~isempty(hardwarepath),'tcv.slx not found?') obj.ddpath = hardwarepath; obj.activenodes=[1 2 6 7 8]; obj.activecpus=[ {1}; ... {1:6}; ... {1:6}; ... {1}; ... {1}; ... {1:4}; ... {1:4}; ... {1}]; obj.algonamelist={}; obj.algoddlist={}; obj.wrapperlist={}; obj.exportedtps = []; obj.algos={}; obj.mdscontainer = SCDclass_mdsobjcontainer; obj.taskcontainer = SCDclass_taskcontainer; nnodes = max(obj.activenodes); for inode = 1:nnodes tmp(inode) = SCDclass_expcode.defaultnode(inode); end obj.nodes = tmp; clear tmp; end %% Main expcode handling methods function open(~,varargin) openslx = SCDclass_expcode.getslxname(varargin{:}); fprintf('Opening %s.slx\n',openslx) open(openslx); end function setup(obj) % This function sets up the TCV Simulink model % to simulate this experimental code. % NOTE THAT it doesn't actualize tunable parameters % and wavegens, call actualizedata for that fprintf('Setting up expcode %d, ''%s'', configuring wrappers ...\n',obj.maincode,obj.name); obj=obj.setupwrappers; fprintf('Setting up main expcode data dictionary') obj = obj.setupmaindd; fprintf('Setting up expcode %d, ''%s'', configuring data dictionaries ...\n',obj.maincode,obj.name); obj.setupwrapdd; obj.setupalgodd; fprintf('Setting up variant model configuration') obj.setupvaralgo; fprintf('Setting up expcode %d, ''%s'', configuring default tunable parameters ...\n',obj.maincode,obj.name); obj.updatedefaulttp; fprintf('Setting up expcode %d, ''%s'', configuring global data ...\n',obj.maincode,obj.name); obj.setupmain; fprintf('Setting up expcode %d, ''%s'', configuring main workspace variables ...\n',obj.maincode,obj.name); obj.buildworkspacesimstruct; obj.buildworkspacetpstruct; % Set cache and codegen folders obj.setcachefolder; obj.setcodegenfolder; end function obj = setupmaindd(obj) % prepare main top-level SLDD ddfullpath = which(obj.ddname); if isempty(ddfullpath) dd = Simulink.data.dictionary.create(fullfile(obj.ddpath,obj.ddname)); else dd = Simulink.data.dictionary.open(obj.ddname); end % link data dictionaries for active nodes for ii=obj.activenodes mydatasource = obj.nodes(ii).datadict; fprintf('adding data source %s to %s\n',mydatasource,obj.ddname) dd.addDataSource(mydatasource); end % Set up RFM bus addRFMbus(obj,dd) end function actualize(obj, shot, varargin) p=inputParser; % if a model name is given, operations will be performed % only on it, otherwise they will be performed on all % configured models addParameter(p,'model','',@(x) ischar(x)); parse(p,varargin{:}); if(isempty(p.Results.model)) obj.modeltoactualize='all'; else obj.modeltoactualize=p.Results.model; end obj.actualizedata(shot); fprintf('Actualizing expcode %d, ''%s'', performing tasks ...\n',obj.maincode,obj.name); obj.taskcontainer.modeltoexecute=obj.modeltoactualize; obj.taskcontainer.exectasksoninit(shot); end function init(obj) % close all data dictionaries Simulink.data.dictionary.closeAll('-discard') if ~isempty(obj.initobjlist) for ii=1:numel(obj.initobjlist) if nargin==1 obj.initobjlist{ii}.init(); elseif nargin==2 error('this use is deprecated, add tasks instead') else error('invalid number of arguments for callinits'); end opendds = Simulink.data.dictionary.getOpenDictionaryPaths; assert(~any(contains(opendds,obj.ddname)),... 'init for %s leaves %s open - this should be avoided',... obj.initobjlist{ii}.getname,obj.ddname) end end end function updatemdsmodel(obj, algo) obj.updatemds(algo, -1); end function compile(~,varargin) myslx = SCDclass_expcode.getslxname(varargin{:}); fprintf('Compiling %s.slx\n',myslx) try eval(sprintf('%s([],[],[],''compile'')',myslx)); eval(sprintf('%s([],[],[],''term'')',myslx)); catch ME rethrow(ME) end end function sim(~,varargin) SCDconf_setConf('SIM') myslx = SCDclass_expcode.getslxname(varargin{:}); fprintf('Simulating %s.slx\n',myslx) sim(myslx) end function build(obj,varargin) if numel(varargin)==0 fprintf('\n=== Building all threads for %s ===\n',obj.name); % make list of nodes to build compileslx_list = cell(0); for inode=1:8 nodeinfo=obj.nodes(inode); for icpu = 1:nodeinfo.ncpu if nodeinfo.cpuactive(icpu) compileslx_list = [compileslx_list,... SCDclass_expcode.getslxname(inode,icpu)]; %#ok<AGROW> end end end if isempty(compileslx_list) fprintf('No active nodes found, no .slx to compile. Done\n'); return end else % user passed nodes,threads to compile myslx = SCDclass_expcode.getslxname(varargin{:}); fprintf('building %s.slx\n',myslx) compileslx_list{1} = myslx; end % set CodeGen folder for this expcode (allows fast rebuilding) CodeGenFolder = obj.setcodegenfolder; % check env variable assert(~isempty(getenv('RTCCODE_LIBPATH')),'RTCCODE_LIBPATH environment variable needs to be defined to compile'); % set configuration settings for compilation SCDconf_setConf('CODE'); % Build try SCDclass_expcode.build_nodes(compileslx_list); catch ME % if fails on first attempt, clear build folder and retry fprintf(' **** FIRST ATTEMPT BUILDING %s FAILED **** \n',obj.name) fprintf(' Error message:\n %s\n',getReport(ME)); fprintf(' Try again clearning build folder\n') system(sprintf('rm -rf %s/*',CodeGenFolder)); SCDclass_expcode.build_nodes(compileslx_list); end end %% Property access methods function nodes = getnodes(obj) nodes = obj.nodes; end %% Printing methods function printinfo(obj) fprintf('*****************************************************\n'); fprintf('* SCD expcode: ''%s'', main code: %d\n',obj.name,obj.maincode); fprintf('*****************************************************\n'); fprintf('* Configured wrappers:\n') if(numel(obj.wrapperlist)>0) for ii=1:numel(obj.wrapperlist) fprintf(' Node: %d, CPU: %d, varalgo: %d, wrapper: ''%s'', datadict: ''%s''\n', ... obj.wrapperlist{ii}{1}, ... obj.wrapperlist{ii}{2}, ... obj.wrapperlist{ii}{3}, ... obj.wrapperlist{ii}{4}, ... obj.wrapperlist{ii}{5} ... ); end end fprintf('* Configured algorithms:\n'); if(numel(obj.algonamelist)>0) for ii=1:numel(obj.algonamelist), fprintf(' ''%s''\n', char(obj.algonamelist{ii})); end end fprintf('* Configured algorithm data dictionaries:\n') if(numel(obj.algoddlist)>0) for ii=1:numel(obj.algoddlist), fprintf(' ''%s''\n', char(obj.algoddlist{ii})); end end fprintf('* Configured exported tunparams structures:\n') if(numel(obj.exportedtps)>0) for ii=1:numel(obj.exportedtps), fprintf(' ''%s''\n', char(obj.exportedtps{ii})); end end fprintf('* Configured MDS tunparams objects: %d (use printparameters method for details)\n', obj.mdscontainer.numparams); fprintf('* Configured MDS wavegens objects: %d (use printwavegens method for details)\n', obj.mdscontainer.numwavegens); fprintf('* Configured general purpose tasks: %d (use printtasks method for details)\n', obj.taskcontainer.numtasks); fprintf('* Configured init scripts: %d (use printinits method for details)\n', numel(obj.initobjlist)); end function printtasks(obj) obj.taskcontainer.printtasks; end function printinits(obj) % TODO: uniform the approach between stdinits and fpinits % but only if fpinits will stay ... fprintf('* Configured std init scripts:\n'); if(~isempty(obj.initobjlist)) for ii=1:numel(obj.initobjlist) [stdinitstmp,~]=obj.initobjlist{ii}.getinits; if ~isempty(stdinitstmp) for jj=1:numel(stdinitstmp) fprintf(' %s\n',char(stdinitstmp{jj})); end end end end fprintf('* Configured fp init scripts:\n'); if(~isempty(obj.fpinits)) for ii=1:numel(obj.fpinits) fprintf(' %s -> %s\n',char(obj.fpinits{ii}{1}),char(obj.fpinits{ii}{2})); end end end function printMARTe2taskconfig(obj, shot, varargin) p=inputParser; % if a model name is given, operations will be performed % only on it, otherwise they will be performed on all % configured models addParameter(p,'model','',@(x) ischar(x)); parse(p,varargin{:}); if(isempty(p.Results.model)) obj.taskcontainer.modeltogenerate='all'; else obj.taskcontainer.modeltogenerate=p.Results.model; end obj.taskcontainer.printMARTe2taskconfig(shot); end function printMARTe2parconfig(obj, shot, varargin) p=inputParser; % if a model name is given, operations will be performed % only on it, otherwise they will be performed on all % configured models addParameter(p,'model','',@(x) ischar(x)); parse(p,varargin{:}); if(isempty(p.Results.model)) obj.mdscontainer.modeltogenerate='all'; else obj.mdscontainer.modeltogenerate=p.Results.model; end obj.mdscontainer.printMARTe2parconfig(shot); end function printMARTe2wgbusconfig(obj, shot, busname, frequency, varargin) % printMARTe2wgbusconfig(obj, shot, busname, frequency, varargin) % % prints cfg file for loading busname Simulink.Bus % as a wavegen (or a set of them) in MARTe2 % shot can be -1 or a fixed shot (usually -1), but currently % the Shot= entry is populated by a fixed macro % frequency is the frequency of signal generation in MARTe2 % the optional parameter 'ddname' can be given to specify % the data dictionary where the bus definition is, if omitted % the default expcode level data dicationary is used p=inputParser; addParameter(p,'ddname',obj.ddname,@(x) ischar(x)); parse(p,varargin{:}); myddname = p.Results.ddname; dim=obj.mdscontainer.printMARTe2wgbusconfig(shot, myddname, busname, frequency); end function printparameters(obj) obj.mdscontainer.printparameters; end function printwavegens(obj) obj.mdscontainer.printwavegens; end %% Configuration methods function obj = addwrapper(obj, inode, icpu, varalgo, wrappername, varargin) p=inputParser; checknodes = @(x) ismember(x,obj.activenodes ); checkcpu = @(x) ismember(x,obj.activecpus{inode}); addRequired(p, 'node', checknodes); addRequired(p, 'cpu', checkcpu); addRequired(p, 'varalgo'); addRequired(p, 'modelname', @(x) ischar(x)); addOptional(p, 'datadictionary', '', @(x) ischar(x)); parse(p, inode, icpu, varalgo, wrappername, varargin{:}); % In data dictionary is not specified it is assumed having % the same name as the model if(isempty(p.Results.datadictionary)) datadictionary=[p.Results.modelname '.sldd']; else datadictionary=[p.Results.datadictionary '.sldd']; end % First checking for duplicates if(numel(obj.wrapperlist)>0) wrappernamelist={}; wrapperddnamelist={}; for ii=1:numel(obj.wrapperlist) wrappernamelist{end+1}=obj.wrapperlist{ii}{4}; wrapperddnamelist{end+1}=obj.wrapperlist{ii}{5}; end if(ismember(p.Results.modelname,wrappernamelist) || ismember(p.Results.datadictionary,wrapperddnamelist)) warning('SCDclass_expcode:addwrapper','wrapper ''%s'' already present, ignoring.',wrappername); return end end % update node info obj.nodes(inode).varalgo(icpu) = varalgo; obj.nodes(inode).wrapper{icpu} = wrappername; obj.nodes(inode).cpuactive(icpu) = true; obj.nodes(inode).active = true; % save in wrapper list tempwrapper=cell(10,1); tempwrapper{1}=p.Results.node; tempwrapper{2}=p.Results.cpu; tempwrapper{3}=p.Results.varalgo; tempwrapper{4}=p.Results.modelname; tempwrapper{5}=datadictionary; obj.wrapperlist{end+1}=tempwrapper; end function obj = addalgorithm(obj, in_node, in_cpu, in_algo) % TODO: import the entrire object into a container class ?? p=inputParser; checknodes = @(x) ismember(x,obj.activenodes ); checkcpu = @(x) ismember(x,obj.activecpus{in_node}); addRequired(p, 'node', checknodes); addRequired(p, 'cpu', checkcpu); addRequired(p, 'algo', @(x) isobject(x)); parse(p, in_node, in_cpu, in_algo); node = p.Results.node; cpu = p.Results.cpu; algo = p.Results.algo; % Checking and importing algorithm name if(~ismember(algo.getname,obj.algonamelist)) obj.algonamelist{end+1}=algo.getname; obj.algos{end+1}=algo; algoispresent=false; else fprintf('algorithm ''%s'' already present in the expcode, importing only wavegens',algo.getname); algoispresent=true; end % Importing algorithm MDS objects into the main mdscontainer, % filling base structure name algomdscontainer=algo.getmdscontainer; basewgstruct=sprintf('SCDsimdata.SCDnode%02d%02d_simdata.wavegen', node, cpu); algomdscontainer=algomdscontainer.setwavegenbasestruct(basewgstruct); obj.mdscontainer=obj.mdscontainer.importmdswavegens(algomdscontainer); if algoispresent, return; end % return if algo is already present % Importing exported tunable parameters obj.mdscontainer=obj.mdscontainer.importmdsparams (algomdscontainer); algoexptps=algo.getexportedtps; if numel(algoexptps)>0 for ii=1:numel(algoexptps) if ~ismember(algoexptps{ii},obj.exportedtps) obj.exportedtps{end+1}=algoexptps{ii}; else fprintf('exported tunparams sctruct ''%s'' already present, ignoring',algoexptps{ii}) end end end % Importing algorithms data dictionary, only those with proper % name algodd=algo.getdatadictionary; if(strcmp(algodd(1:7),'SCDalgo')) if(~ismember(algodd,obj.algoddlist)) obj.algoddlist{end+1}=algodd; else warning('SCDclass_expcode:addalgorithm','algorithm data dictionary ''%s'' already present, ignoring', algodd); end else % warning here ? end % Importing tasks algotaskcontainer=algo.gettaskcontainer; obj.taskcontainer=obj.taskcontainer.importtaskobjects(algotaskcontainer); % Importing inits [stdinitstmp,fpinits]=algo.getinits; if(numel(fpinits)>0) toadd = ones(numel(fpinits),1); for ii=1:numel(fpinits) if(~isempty(obj.fpinits)) for jj=1:numel(obj.fpinits) for kk=1:numel(obj.fpinits{jj}{2}) if(strcmp(char(obj.fpinits{jj}{2}{kk}),fpinits{ii}{2})) warning('SCDclass_expcode:addalgorithm','An init driving the structure %s has already been added, ignoring this init',char(fpinits{ii}{2})) toadd(ii)=0; end end end end if toadd(ii) temp=cell(10,1); temp{1}=fpinits{ii}{1}; temp{2}=fpinits{ii}{2}; obj.fpinits{end+1}=temp; end end if any(toadd) % if any inits from this algoobj were taken obj.initobjlist{end+1}=algo; %% Add the full algorithm object here, to see if it is fine end elseif(numel(stdinitstmp)>0) obj.initobjlist{end+1}=algo; end end function out = getexportedtps(obj) out = obj.exportedtps; end function obj = importexpcode(obj, expcode) % This function imports settings of an existing expcode into another warning('Not yet supported!'); return; % tunparamstruct import exportedtpstoadd = expcode.getexportedtps; for ii=1:numel(exportedtpstoadd) if ~sum(ismember(exportedtpstoadd{ii},obj.exportedtps)) obj.addtunparamstruct(exportedtpstoadd{ii}); end end % parameters import obj.mdscontainer=obj.mdscontainer.importmdsobjects(expcode.mdscontainer); end %% Default values setup methods function updatedefaulttp(obj) % updatedefaulttp() % % updates _tmpl tunparams structures % for every defined updating function % To define an update function see SCDclass_algo.addtunparamstruct for ii=1:numel(obj.algonamelist) % if strcmp(algo, obj.algonamelist{ii}) obj.algos{ii}.updatetemplatetp(); % end end end end %% PRIVATE METHODS methods (Access = private) %% Matlab environmental setup methods function CacheFolder = setcachefolder(obj) % Set Cache Folder for experimental code (avoid conflicts with other tmp files) CacheFolder = fullfile(fileparts(mfilename('fullpath')),'..','..',... 'gencodes',sprintf('CacheFolder-%d',obj.maincode)); fprintf('Setting Simulink Cache folder to %s\n',CacheFolder) Simulink.fileGenControl('set',... 'CacheFolder',CacheFolder,... 'createdir',true); end function CodeGenFolder = setcodegenfolder(obj) CodeGenFolder = fullfile(fileparts(mfilename('fullpath')),'..','..',... 'gencodes',sprintf('CodeGenFolder-%d',obj.maincode)); fprintf('Setting code generation folder to %s\n',CodeGenFolder) Simulink.fileGenControl('set',... 'CodeGenFolder',CodeGenFolder,... 'createdir',true); end function obj=printlog(obj,str,varargin) % printlog, allows sprintf()-like expressions if obj.loadverbose==1 fprintf('Expcode %d, ', obj.maincode); fprintf(str,varargin{:}); fprintf('\n'); end end function obj = buildworkspacetpstruct(obj) % this funtion builds a workspace structures containing % replicas of all tunable parameters structures in the data % dictionaries, this structure is the one actually used % for loading simulation wavegen data % It is better not to use directly data dictionaries structures % to avoid flooding dds with big sim data sets (and % conseguently the rtccode GIT repo itself dict=Simulink.data.dictionary.open(obj.ddname); dd=getSection(dict, 'Design Data'); for ii=1:numel(obj.exportedtps) simstructnamedd=sprintf('%s_tmpl',char(obj.exportedtps(ii))); simstructnamews=char(obj.exportedtps(ii)); if ~dd.exist(simstructnamedd) warning('tunable params structure %s not found', simstructnamedd); continue end Entry = dd.getEntry(simstructnamedd); assert(numel(Entry)<2,'multiple entries found for %s',structnamedd); simstruct = dd.getEntry(simstructnamedd).getValue; assignstr = sprintf('%s=temp;',simstructnamews); assignin('base','temp',simstruct); evalin('base',assignstr); end evalin('base','clear temp;'); end function obj = setupwrapdd(obj) % Sets up the data dictionaries of the wrappers to be loaded % on the nodes inside the main data dictionary % For every node, given the current status finds its dictionary % and the requred data sources, find actions to do and setup % wrappers data sources accordingly % It is assumed that there is only 1 wrapper and 1 wrapper data % dictionary active at a given time for every CPU model % Looping through every active node for idx_nodedds=1:numel(obj.activenodes) inode = obj.activenodes(idx_nodedds); % Getting node data dictionary and required data sources datadictname = obj.nodes(inode).datadict; fprintf('opening %s\n',datadictname) nodedd = Simulink.data.dictionary.open(datadictname); % Clear all existing data sources nodeddsources=nodedd.DataSources; for ii=1:numel(nodeddsources) nodedd.removeDataSource(nodeddsources{ii}); end % Add all wrapper dictionaries as datasources for node dd reqsources_to_add = [... obj.nodes(inode).wrapdatadicts; obj.nodes(inode).algodatadicts]; for ii=1:numel(reqsources_to_add) myresource = reqsources_to_add{ii}; obj.printlog('Adding wrapper data source %s to %s', myresource, datadictname); nodedd.addDataSource(myresource); end end end function obj = setupalgodd(obj) % Getting data dictionary and required data sources ddObj=Simulink.data.dictionary.open(obj.ddname); reqsources = obj.algoddlist; reqsources_toadd = reqsources(startsWith(reqsources,'SCDalgo')); for ii=1:numel(reqsources_toadd) mysource = reqsources_toadd{ii}; obj.printlog('Adding algorithm data source %s to %s',mysource,obj.ddname); ddObj.addDataSource(mysource); end end function buildworkspacesimstruct(obj) % obj.mdscontainer.buildworkspacesimstructnode(obj,node) % obj.mdscontainer.buildworkspacesimstruct; % old dd=SCDconf_getdatadict(obj.ddname); for ii = 1:numel(obj.activenodes) inode = obj.activenodes(ii); buildworkspacesimstructnode(obj,inode,dd); end evalin('base', 'SCDrefdata = SCDsimdata;'); end function obj = setupmain(obj) % sets up global configs for the expcode scd.expcode = obj.maincode; scd.timing.dt = 25e-6; % hard code for now, determine later from slowest thread Simulink.data.assigninGlobal('tcv','scd',scd); end function obj = setupwrappers(obj) % map obj.wrapperlist to wrapper list for each node for ii=1:numel(obj.wrapperlist) inode = obj.wrapperlist{ii}{1}; icpu = obj.wrapperlist{ii}{2}; varalgo = obj.wrapperlist{ii}{3}; modelname = obj.wrapperlist{ii}{4}; %#ok<NASGU> wrapper_dd = obj.wrapperlist{ii}{5}; switch inode case {1,8} assert(icpu==1,'SCDclass_expcode:setupwrappers',... 'not supported node%02d cpu',inode) obj.nodes(inode).varalgo(icpu) = varalgo; obj.nodes(inode).wrapdatadicts{icpu} = wrapper_dd; case {2,3,4,6,7} assert((icpu>=1 && icpu <=max(obj.activecpus{inode})),... 'SCDclass_expcode:setupwrappers','not supported node%02d cpu',inode); otherwise warning('SCDclass_expcode:setupwrappers','not supported node'); end obj.nodes(inode).varalgo(icpu) = varalgo; obj.nodes(inode).wrapdatadicts{icpu} = wrapper_dd; end end function addRFMbus(obj,dd) ddSection = dd.getSection('Design Data'); zeroElem = Simulink.BusElement; zeroElem.Name = 'zero'; Elems = repmat(Simulink.BusElement,numel(obj.activenodes),1); for ii = 1:numel(obj.activenodes) inode = obj.activenodes(ii); leafName = sprintf('Node%02d_RFM' ,inode); subBusName = sprintf('Bus: RFMOUT%02dbus',inode); % corresponding leaf element in main RFMIn bus Elems(ii) = Simulink.BusElement; Elems(ii).Name = leafName; Elems(ii).Description = sprintf('Node %02d RFM output',inode); Elems(ii).DataType = subBusName; end ii=ii+1; % Extra bus and leaf for Externals_RFM (non-node entities writing to RFM) [extBusObj,extBusName] = SCDconf_createRFMOUTextbus; assignin(ddSection,extBusName,extBusObj); Elems(ii) = Simulink.BusElement; Elems(ii).Name = 'Externals_RFM'; Elems(ii).DataType = ['Bus: ',extBusName]; Elems(ii).Description = 'Bus for data from external nodes writing to RFM'; % Assemble RFM input bus RFMINbus = Simulink.Bus; RFMINbus.HeaderFile = ''; RFMINbus.Description = 'RFM input bus for all nodes'; RFMINbus.DataScope = 'Auto'; RFMINbus.Alignment = -1; RFMINbus.Elements = Elems; assignin(ddSection,'RFMINbus',RFMINbus); end function obj = setupvaralgo(obj) % Sets up varalgo structure in main tcv data dictionary % according to varalgo info of the nodes % Getting data section of main tcv data dictionary fprintf('opening %s to setup variant model configurations\n',obj.ddname); d=Simulink.data.dictionary.open(obj.ddname); dd=getSection(d, 'Design Data'); s=struct(); % Looping through every active node for ii=1:numel(obj.activenodes) inode = obj.activenodes(ii); varalgo=obj.nodes(inode).varalgo'; for jj=1:numel(varalgo) fieldname = sprintf('algo%02d%02d',inode,jj); s.(fieldname)=varalgo(jj); end end % save in data dictionary if dd.exist('SCDvaralgo') ee = dd.getEntry('SCDvaralgo'); ee.setValue(s); else dd.addEntry('SCDvaralgo',s); end end function obj = buildworkspacesimstructnode(obj,inode,dd) % build simulation data structure node = obj.nodes(inode); if evalin('base','exist(''SCDsimdata'',''var'')') SCDsimdata = evalin('base','SCDsimdata'); else SCDsimdata = struct(); % assign here if empty end if node.haswavegen for ithread = 1:node.ncpu % get whatever is in data dictionary template in wrappers simstructname = sprintf('SCDnode%02d%02d_simdata',inode,ithread); wgbusname = sprintf('WG%02d%02dbus',inode,ithread); fprintf(' setting up %s',simstructname); if dd.exist(simstructname) simstruct = dd.getEntry(simstructname).getValue; mybus=dd.getEntry(wgbusname).getValue; % get from data dictionary if ~isstruct(simstruct) || ~isfield(simstruct,'wavegen') fprintf('.. loaded simstruct wavegen is not compatible'); regenerate = true; elseif SCDconf_structbuscmp(simstruct.wavegen,mybus) fprintf('... loaded WG from data dictionary\n'); regenerate=false; else fprintf('... loaded WG structure from dd does not match bus definition...'); regenerate=true; end else regenerate=true; fprintf('... could not find %s in data dictionary',simstructname); end if regenerate simstruct.wavegen = SCDconf_createstructfrombus(dd,wgbusname); % structure to match %ddsource = dd.find('Name',wgbusname).DataSource; % dd containing bus %wrapdd=Simulink.data.dictionary.open(ddsource).getSection('Design Data'); %wrapdd.addEntry(simstructname,simstruct) fprintf('... re-generated from bus %s\n',wgbusname') end SCDsimdata.(simstructname) = simstruct; end end % Node-specific simstructname = sprintf('SCDnode%02dsimdata',inode); if node.hasadc adcbusname = sprintf('ADC%02dbus',inode); fprintf(' setting up %s.adc\n',simstructname); SCDsimdata.(simstructname).adc = SCDconf_createstructfrombus(dd,adcbusname); end if node.hasethercat assert(inode==2,'Ethercat not yet implemented for other node than 2'); ethercatbusname = 'ETHCAT1IN'; fprintf(' setting up %s.ethercat\n',simstructname); SCDsimdata.(simstructname).ethercat = ... SCDconf_createstructfrombus(dd,ethercatbusname); end % add also RFM RFMbusname = 'RFMINbus'; SCDsimdata.rfm = SCDconf_createstructfrombus(dd,RFMbusname); % assign result in base workspace assignin('base','SCDsimdata',SCDsimdata); end %% Actualization methods function actualizedata(obj, shot) % This function actualizes configured % tunable parameters and wavegens timeseries % according to the given shot currentexpcode=Simulink.data.evalinGlobal('tcv','scd.expcode'); if(currentexpcode~=obj.maincode) error('SCDclass_expcode:wrongexpcodeloaded','Cannot perform data actualization, the loaded expcode is not matching, try to setup the expcode first'); end fprintf('Actualizing expcode %d, ''%s'', configuring tunable parameters ...\n',obj.maincode,obj.name); obj.actualizeparameters(shot) fprintf('Actualizing expcode %d, ''%s'', configuring wavegens ...\n',obj.maincode,obj.name); obj.actualizewavegens(shot); end function actualizeparameters(obj,shot) obj.mdscontainer.modeltoactualize=obj.modeltoactualize; obj.mdscontainer.actualizeparameters(shot); end function actualizewavegens(obj,shot) obj.mdscontainer.modeltoactualize=obj.modeltoactualize; obj.mdscontainer.actualizewavegens(shot); end %% MDS update methods function updatemds(obj, algo, shot) if shot~=-1 error('SCDclass_expcode:updatemds','update permitted only on the model shot'); end % % first update algorithm tunparams default locally % for ii=1:numel(obj.algonamelist) % if strcmp(algo, obj.algonamelist{ii}) % obj.algos{ii}.updatetemplatetp(); % end % end % % then update mds obj.mdscontainer.modeltogenerate=algo; obj.mdscontainer.autopopulateMDSparams(shot); obj.mdscontainer.autopopulateMDSwavegens(shot); end end end