Skip to content
Snippets Groups Projects
SCDclass_expcode.m 35.8 KiB
Newer Older
classdef SCDclass_expcode
    %An SCD expcode object
    %   
    %  .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
        maincode          % Expcode numerical code
        status            % Expcode development status  
    end
    
    properties (Access = protected)
        loadverbose       % Verbosity level of the loading (currently 0 or 1)                
    end
    properties (SetAccess = private)     
        name                % Expcode main name
        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
Federico Felici's avatar
Federico Felici committed
        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
Federico Felici's avatar
Federico Felici committed
        definednodes        % nodes that are defined in Simulink
        ddname = 'tcv.sldd' % main expcode data dictionary name
Federico Felici's avatar
Federico Felici committed
        ddpath              % main expcode data dicationary save path
      function help
        help(mfilename)
      end
      function build_nodes(compileslx_list)
        for mynodeslx = compileslx_list
             fprintf(' *** Buiding Node %s ***\n',mynodeslx{1});
             rtwbuild(mynodeslx{1});
        end
      end
Federico Felici's avatar
Federico Felici committed
      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
Federico Felici's avatar
Federico Felici committed
      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)
Federico Felici's avatar
Federico Felici committed
          fprintf(closemsg,bdroot);
          close_system(bdroot,saveflag)
        function obj=SCDclass_expcode(name)
            if nargin==0; name = 'template'; end
            obj.name = name;
            obj.maincode = 1;
            obj.status = 'debug';
            obj.loadverbose = 1;
            
            mainslx      = obj.getslxname;
Federico Felici's avatar
Federico Felici committed
            hardwarepath = fileparts(which(mainslx)); 
            assert(~isempty(hardwarepath),'%s not found?',mainslx)
            obj.ddpath = hardwarepath;

            obj.definednodes = [1 2 6 7 8];
                          
            obj.algonamelist = {};
            obj.algoddlist   = {};
            obj.exportedtps  = [];

            obj.mdscontainer  = SCDclass_mdsobjcontainer;
            obj.taskcontainer = SCDclass_taskcontainer;

Federico Felici's avatar
Federico Felici committed
            nnodes = numel(obj.definednodes);
Federico Felici's avatar
Federico Felici committed
            obj.nodes = cell(1,nnodes);
            for inode = 1:nnodes
Federico Felici's avatar
Federico Felici committed
              nodenr = obj.definednodes(inode);
              obj.nodes{nodenr} = SCDclass_node(nodenr);

%% 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 data dictionaries ...\n',obj.maincode,obj.name);
           obj.createmaindd;
           obj.setupnodedd;
           obj.setupmaindd;
           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 createmaindd(obj)
          % create main data dictionary from scratch to ensure it exists
          if contains(Simulink.data.dictionary.getOpenDictionaryPaths,obj.ddname)
              Simulink.data.dictionary.closeAll(obj.ddname,'-discard');
              % close if it exists and discard changes 
          delete(which(obj.ddname));
          Simulink.data.dictionary.create(fullfile(obj.ddpath,obj.ddname));
        end
        function setupmaindd(obj)
          % prepare main top-level SLDD
          dd = Simulink.data.dictionary.open(obj.ddname);

          % link data dictionaries for active nodes
Federico Felici's avatar
Federico Felici committed
          for ii=obj.definednodes
            mydatasource = obj.nodes{ii}.ddname;
Federico Felici's avatar
Federico Felici committed
            fprintf('adding data source %s to %s\n',mydatasource,obj.ddname)
            dd.addDataSource(mydatasource);
          end
          % Set up RFM bus
          fprintf('Setting up RFM main bus\n')
          addRFMbus(obj,dd)

          fprintf('Setting up variant model configuration')
          obj.setupvaralgo;
          
          dd.saveChanges;

        
        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 obj = init(obj)
          % close all data dictionaries
          Simulink.data.dictionary.closeAll('-discard')

          % Set configuration settings sldd
          SCDconf_setConf('SIM')
          
          % carry out any init tasks of algorithms
          if ~isempty(obj.initobjlist)
            for ii=1:numel(obj.initobjlist)
              obj.initobjlist{ii}.init();
              
              opendds = Simulink.data.dictionary.getOpenDictionaryPaths;
              % check that tcv.sldd is not opened by some algorithm init
              assert(~any(contains(opendds,obj.ddname)),...
                'init for %s leaves %s open - this should be avoided',...
                obj.initobjlist{ii}.getname,obj.ddname)
            end
          fprintf('\n** DONE WITH ALL SCDINITS **\n');
        function updatemdsmodel(obj, algo)
           obj.updatemds(algo, -1); 
        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
        
        function obj = setnode(obj,node,nodenr)
          node = node.setactive(true);
          assert(node.nodenr == nodenr,...
            'set node number and node property do not match while setting node %s',nodenr)
          obj.nodes{node.nodenr} = node;
          % Make lists of data dictionaries, mds objects, init functions etc
          % that come with this node.
          
          % wrapper algorithms
          for iwrap = 1:numel(node.wrappers)
            wrapperObj = node.wrappers{iwrap}.wrapperobj;
Federico Felici's avatar
Federico Felici committed
            cpunr        = node.wrappers{iwrap}.cpunr;
            
            for ialgo = 1:numel(wrapperObj.algos)
              algoObj = wrapperObj.algos(ialgo);
Federico Felici's avatar
Federico Felici committed
              obj = processalgorithm(obj,algoObj,node.nodenr,cpunr);
            end
          end
          
          % node algorithms
          for ialgo = 1:numel(node.algos)
            algoObj = node.algos;
            obj = processalgorithm(obj,algoObj,node.nodenr,1);
          end
        %% Printing methods
        
Federico Felici's avatar
Federico Felici committed
        function printinfo(obj,nodenr,cpunr)
Federico Felici's avatar
Federico Felici committed
            obj.nodes{nodenr}.wrapper{cpunr}.wrapperObj.printinfo;
            if isempty(obj.nodes{nodenr}); fprintf('no node %d defined\n',nodenr); end
Federico Felici's avatar
Federico Felici committed
            obj.nodes{nodenr}.printinfo;
          fprintf('*****************************************************\n');
          fprintf('* SCD expcode: ''%s'', main code: %d\n',obj.name,obj.maincode);
          fprintf('*****************************************************\n');
Federico Felici's avatar
Federico Felici committed
          fprintf('* Configured nodes:\n')
          for node = obj.nodes
            if isempty(node{:}),continue; end
            node{:}.printinfo;
          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));
        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

        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);
Federico Felici's avatar
Federico Felici committed
        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);
Federico Felici's avatar
Federico Felici committed
        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
            addParameter(p,'ddname',obj.ddname,@(x) ischar(x));
            myddname = p.Results.ddname;
            obj.mdscontainer.printMARTe2wgbusconfig(shot, myddname, busname, frequency);               
        end

        function printparameters(obj)
            obj.mdscontainer.printparameters;
        end

        function printwavegens(obj)
            obj.mdscontainer.printwavegens;
        end
        function obj = processalgorithm(obj,algoObj,node,cpu)
          % Checking and importing algorithm name
          if(~ismember(algoObj.getname,obj.algonamelist))
            obj.algonamelist{end+1} = algoObj.getname;
            obj.algos{end+1}        = algoObj;
            algoispresent = false;
          else
            fprintf('algorithm ''%s'' already present in the expcode, importing only wavegens',algoObj.getname);
            algoispresent = true;
          end
          
          % Importing algorithm MDS objects into the main mdscontainer,
          % filling base structure name
          algomdscontainer = algoObj.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=algoObj.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
          
          % Importing algorithms data dictionary, only those with proper name
          algodd=algoObj.getdatadictionary;
          if(strcmp(algodd(1:7),'SCDalgo'))
            if(~ismember(algodd,obj.algoddlist))
              obj.algoddlist{end+1}=algodd;
              warning('SCDclass_expcode:addalgorithm','algorithm data dictionary ''%s'' already present, ignoring', algodd);
          else
            error('attempting to add algorithm data dictionary not starting with ''SCDalgo''')
          end
          
          % Importing tasks
          algotaskcontainer=algoObj.gettaskcontainer;
          obj.taskcontainer=obj.taskcontainer.importtaskobjects(algotaskcontainer);
          
          % Importing inits
          [stdinitstmp,fpinitstmp]=algoObj.getinits;
          if(numel(fpinitstmp)>0)
            toadd = ones(numel(fpinitstmp),1);
            for ii=1:numel(fpinitstmp)
              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}),fpinitstmp{ii}{2}))
                      warning('SCDclass_expcode:addalgorithm','An init driving the structure %s has already been added, ignoring this init',char(fpinitstmp{ii}{2}))
              if toadd(ii)
                temp=cell(10,1);
                temp{1}=fpinitstmp{ii}{1};
                temp{2}=fpinitstmp{ii}{2};
                obj.fpinits{end+1}=temp;
            if any(toadd) % if any inits from this algoobj were taken
              obj.initobjlist{end+1}=algoObj; %% Add the full algorithm object here, to see if it is fine
            end
          elseif(numel(stdinitstmp)>0)
            obj.initobjlist{end+1}=algoObj;
          end
        end
            
        function out = getexportedtps(obj)
            out = obj.exportedtps;
        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.nodes)
            mynode = obj.nodes{ii};
            if ~isempty(mynode)
              mynode.updatetemplatetp();
%% 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);
        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);
        function obj=printlog(obj,str,varargin)
          % printlog, allows sprintf()-like expressions
            if obj.loadverbose==1
Cristian Galperti's avatar
Cristian Galperti committed
               fprintf('Expcode %d, ', obj.maincode); 
               fprintf(str,varargin{:}); fprintf('\n');
        
        function add_remove_sources(obj,ddsources_to_add,ddname,prefix)
          % add and remove data sources from data dictionary ddname
          % ddsources_to_add is cell array with sources
          % prefix is an optional filter to only remove those with the correct
          % prefix.
          
          ddObj = Simulink.data.dictionary.open(ddname);
          prevsources = ddObj.DataSources;
          prevsources_prefix = prevsources(startsWith(prefix,prevsources));
          for ii=1:numel(prevsources_prefix)
            myddsource = prevsources_prefix{ii};
            if ~contains(ddsources_to_add,myddsource)
              obj.printlog('Removing wrapper data source %s from %s', myddsource, ddname);
              ddObj.removeDataSource(myddsource)
            end
          end
          
          for ii=1:numel(ddsources_to_add)
            myddsource = ddsources_to_add{ii};
            if contains(prevsources_prefix,myddsource)
              obj.printlog('Not Adding wrapper data source %s from %s - already there',...
                myddsource, ddname);
            else
              obj.printlog('Adding wrapper data source %s to %s',...
                myddsource, ddname);
              ddObj.addDataSource(myddsource);
            end
          end
        end
        
        function obj = buildworkspacetpstruct(obj)
            % this funtion builds a workspace structures containing
Federico Felici's avatar
Federico Felici committed
            % 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
Federico Felici's avatar
Federico Felici committed
            % 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
                Entry = dd.getEntry(simstructnamedd);
                assert(numel(Entry)<2,'multiple entries found for %s',simstructnamedd);
                simstruct = dd.getEntry(simstructnamedd).getValue;
                assignstr = sprintf('%s=temp;',simstructnamews);
                assignin('base','temp',simstruct);
                evalin('base',assignstr);
            end
            evalin('base','clear temp;');            
        end
        function setupnodedd(obj)
            % For every node, add required wrapper datadicationaries as
            % sources for the main data dicationary
            % Getting main data dictionary and required data sources
            datadictname = obj.ddname;
            fprintf('opening %s\n',datadictname)
            
            % Looping through every active node
Federico Felici's avatar
Federico Felici committed
            for idx_nodedds=1:numel(obj.definednodes)
               inode = obj.definednodes(idx_nodedds);
               mynode = obj.nodes{inode};

               % Add all wrapper dictionaries as datasources for main dd
               wrapperdd_to_add = cell(numel(mynode.wrappers),1); % init
               for iwrap=1:numel(mynode.wrappers)
                 wrapperdd_to_add{iwrap} = mynode.wrappers{iwrap}.wrapperobj.ddname;
               end
               ddsources_to_add = wrapperdd_to_add;
               
               % remove ones we don't need
               obj.add_remove_sources(ddsources_to_add,datadictname,'SCDwrap')
               
               %%               
               % Add also node algorithm dd to node dd directly
               if ~isempty(mynode.algos)
                 algodd_to_add = {mynode.algos.getdatadictionary};
               else
                 algodd_to_add = {};
               end
               obj.add_remove_sources(algodd_to_add,mynode.ddname,'SCDalgo')
        function setupwrapperdd(obj)
          % Set up the wrapper data dictionary links
          
          for inode = 1:numel(obj.nodes)
            mynode = obj.nodes{inode};
            if isempty(mynode), continue; end
            for ii=1:numel(mynode.wrappers)
              wrapperObj = mynode.wrappers{ii}.wrapperobj;
              % link to algorithms contained in the wrappers
              wrapperObj.linkalgodd;
            end
          
          % link to hosting node
        end
        
        function buildworkspacesimstruct(obj)
%           obj.mdscontainer.buildworkspacesimstructnode(obj,node)
%           obj.mdscontainer.buildworkspacesimstruct; % old
          
          dd=SCDconf_getdatadict(obj.ddname);
Federico Felici's avatar
Federico Felici committed
            for ii = 1:numel(obj.definednodes)
              inode = obj.definednodes(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(obj.getslxname,'scd',scd);
            
            % add configurations.sldd as reference, contains simulation parameters
            ddObj = Simulink.data.dictionary.open(obj.ddname);
            ddObj.addDataSource('configurations.sldd');
        function addRFMbus(obj,dd)
          ddSection = dd.getSection('Design Data');
          zeroElem = Simulink.BusElement;
          zeroElem.Name = 'zero';
          
Federico Felici's avatar
Federico Felici committed
          Elems = repmat(Simulink.BusElement,numel(obj.definednodes),1);
Federico Felici's avatar
Federico Felici committed
          for ii = 1:numel(obj.definednodes)
            inode = obj.definednodes(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
Federico Felici's avatar
Federico Felici committed
            for ii=1:numel(obj.definednodes)
              inode = obj.definednodes(ii);
              mynode = obj.nodes{inode};
              for jj=1:numel(mynode.wrappers)
                fieldname = sprintf('algo%02d%02d',inode,jj);
                s.(fieldname) = mynode.wrappers{jj}.varalgo;
            % save in data dictionary
            if dd.exist('SCDvaralgo')
              ee = dd.getEntry('SCDvaralgo'); ee.setValue(s);
            else
              dd.addEntry('SCDvaralgo',s);
            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)
                ddentry = dd.getEntry(simstructname);
                assert(numel(ddentry)==1,'multiple entries found for %s',simstructname)

                simstruct = ddentry.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