Customizing the Model Advisor

by

 

11 Nov 2008 (Updated )

Step by step instructions on how to create and manage model advisor checks.

sl_customization(cm)
function sl_customization(cm)
    % SL_CUSTOMIZATION - Model Advisor customization demonstration.
    % Copyright 2008 The MathWorks, Inc.
    % register custom checks 
    cm.addModelAdvisorCheckFcn(@defineModelAdvisorChecks);
    % register custom process callback
    cm.addModelAdvisorProcessFcn(@ModelAdvisorProcessFunction);
end
function [checkCellArray taskCellArray] = ModelAdvisorProcessFunction(stage, system, checkCellArray, taskCellArray)   %#ok
    switch stage
        case 'configure'
            for i=1:length(checkCellArray)
                % hidden all checks that do not belong to Demo group
                if (strcmp(checkCellArray{i}.Group, 'Demo Folder|Folder 1'))
                    % do nothing
                    checkCellArray{i}.Visible = true;
                    checkCellArray{i}.Value = false;
                else
                    checkCellArray{i}.Value = false;
                end
            end
        case 'process_results'
            % Runs after a check has been run.
    end % ends switch statment
end

% -----------------------------
% defines Model Advisor Checks
% -----------------------------
function defineModelAdvisorChecks
    mdladvRoot = ModelAdvisor.Root;

    %% --- sample check 1
    rec                     = ModelAdvisor.Check('dataLoggingSettings');
    rec.Title               = 'Validate data logging settings';
    rec.TitleTips           = ['Checks that signal logging is enabled with a ',...
                               'unique name and that the format is set to ',...
                               'structure with time'];
    rec.setCallbackFcn(@dataLoggingSettingsFunc,'None','StyleThree');
    % set input parameters
    rec.setInputParametersLayoutGrid([1 1]);
    inputParam1             = ModelAdvisor.InputParameter;
    inputParam1.Name        = 'Save existing data to file';
    inputParam1.Type        = 'Bool';
    inputParam1.Value       = false;
    inputParam1.Description = 'File name is based on model name and time stamp';
    inputParam1.setRowSpan([1 1]);
    inputParam1.setColSpan([1 1]);
    rec.setInputParameters({inputParam1});
    % set fix operation
    myAction             = ModelAdvisor.Action;
    myAction.Name        = 'Save and clear data';
    myAction.Description = ['Enables logging, sets format to structure with ',...
                            'if the option is selected the data is saved ',...
                            'to file.  If not selected the logging name ',...
                            'incremented'];
    myAction.setCallbackFcn(@dataLoggingSettingsAction);
    rec.setAction(myAction);
    rec.ListViewVisible = false;     % The list view explore isn't needed
    mdladvRoot.publish(rec, 'Demo Folder|Folder 1'); 
    
    %% --- sample check 2
    rec                     = ModelAdvisor.Check('inputDataConfiguration');
    rec.Title               = 'Check the models data input configuration';
    rec.TitleTips           = ['The model must have either root level ',...
                               'Inports enabled or Signal builder / ',...
                               'fromWorkspace blocks in the model'];
    rec.setCallbackFcn(@inputDataConfigurationFunc,'None','StyleThree');
    % set input parameters
    rec.ListViewVisible = false;     % The list view explore isn't needed
    %mdladvRoot.register(rec);        % Register the check.
    mdladvRoot.publish(rec,'Demo Folder|Folder 2');    
               
    %% --- sample check 3
    rec                     = ModelAdvisor.Check('signalLoggingEnabled');
    rec.Title               = 'Verify that required signals are logged';
    rec.TitleTips           = ['All named signals must be logged.  As an'...
                               ' option the user can select a prefix',...
                               ' that defines the named signals to log.'];
    rec.setCallbackFcn(@signalLoggingEnabledFunc,'None','StyleThree');
    % set input parameters
    rec.setInputParametersLayoutGrid([1 2]);
    inputParam1             = ModelAdvisor.InputParameter;
    inputParam1.Name        = 'Use prefix';
    inputParam1.Type        = 'Bool';
    inputParam1.Value       = false;
    inputParam1.Description = 'Enable signal filtering based on user define prefix';
    inputParam1.setRowSpan([1 1]);
    inputParam1.setColSpan([1 1]);
    inputParam2             = ModelAdvisor.InputParameter;
    inputParam2.Name        = 'Prefix string';
    inputParam2.Type        = 'String';
    inputParam2.Value       = 'LogMe_';
    inputParam2.Description = 'The Signal Name Prefix';
    inputParam2.setRowSpan([1 1]);
    inputParam2.setColSpan([2 2]);
    
    rec.setInputParameters({inputParam1, inputParam2});
    % set fix operation
    myAction             = ModelAdvisor.Action;
    myAction.Name        = 'Enable Logging';
    myAction.Description = 'Log selected signals';
    myAction.setCallbackFcn(@signalLoggingEnabledAction);
    rec.setAction(myAction);
    rec.ListViewVisible = true; 
    
    mdladvRoot.publish(rec,'Demo Folder|Folder 2');
    
    % How to move a check...
    % ID we want to move...'Check model and local libraries for updates'
    
    %% --- sample check 4: Stateflow
    rec                     = ModelAdvisor.Check('stateflowExample');
    rec.Title               = 'Provided information on Stateflow Charts';
    rec.TitleTips           = ['Returns chart name, use of history junctions',...
                               ' and multi-branch junctions'];
    rec.setCallbackFcn(@stateflowExampleFunc,'None','StyleThree');
    % set input parameters
    rec.ListViewVisible = false;     % The list view explore isn't needed
    %mdladvRoot.register(rec);        % Register the check.
    mdladvRoot.publish(rec,'Demo Folder|Folder 2');    
    
    
end

function [ResultDescription, ResultDetails] = dataLoggingSettingsFunc(system)
    % initilize the returns to empty
    ResultDescription = {};
    ResultDetails     = {};
    % Pass fail flags:  This check has three thing it checks, stored in "eachPass"
    allPass    = 1;
    eachPass   = {1,1,1};

    % get the model advisor handle for the model.
    mdladvObj = Simulink.ModelAdvisor.getModelAdvisor(system);
    % pass fail status to "pass" 
    mdladvObj.setCheckResultStatus(false);         
   
    % Formated Pass / Fail text
    passText = ModelAdvisor.Text('Pass:',{'bold','pass'});
    failText = ModelAdvisor.Text('Fail:',{'bold','fail'});    
    
    logEnabled = simget(system,'SignalLogging');     % Part one: Is signal logging enabled?
    if (strcmp(logEnabled,'off'))
        tempStr = ModelAdvisor.Text('The signal Logging was not enabled.');
        ResultDescription{end+1} = [failText,tempStr];                               
        ResultDetails{end+1}     = {};                                                              
        allPass                  = 0;
        eachPass{1}              = 0;
    else
        tempStr = ModelAdvisor.Text('Signal Logging is enabled.');
        ResultDescription{end+1} = [passText,tempStr];                                       
        ResultDetails{end+1}     = {};                                                              
    end
    
    % Check to see if the logging name exists in the base workspace
    logName     = simget(system,'SignalLoggingName');
    existInBase = evalin('base',['exist(''',logName,''',''var'')']);
    
    if (existInBase == 1)
  
        tempStr = ModelAdvisor.Text('The logged data already exists in the base worksace');
        ResultDescription{end+1} = [failText,tempStr];                                     
        ResultDetails{end+1}     = {};                               
        allPass                  = 0;
        eachPass{2}              = 0;        
    else
        tempStr = ModelAdvisor.Text('The logged data does not exist in the base worksace');
        ResultDescription{end+1} = [passText,tempStr];                                                              
        ResultDetails{end+1}     = {};                               
    end

    saveFormat     = simget(system,'SaveFormat');
    if (strcmp(saveFormat,'StructureWithTime') ~= 1)  
        
        tempStr = ModelAdvisor.Text('Data is being logged in the wrong format');
        ResultDescription{end+1} = [failText,tempStr];                               
        ResultDetails{end+1}     = {};                                                              
        allPass                  = 0;
        eachPass{3}              = 0;        
    else
        tempStr = ModelAdvisor.Text('Data is being logged in the correct format');
        ResultDescription{end+1} = [passText,tempStr];                               
        ResultDetails{end+1}     = {};                                                              
    end
    
    if (allPass == 1)
        mdladvObj.setActionEnable(0); % the check passed, therefore enable action button
        ResultDescription{end+1} = ModelAdvisor.Text('All Logging information is set up correct');
        ResultDetails{end+1}     = {};                                       
        mdladvObj.setCheckResultStatus(true);     
    else
        mdladvObj.setCheckResultData(eachPass);
        mdladvObj.setActionEnable(1);      
        mdladvObj.setCheckResultStatus(false);     
        mdladvObj.setCheckErrorSeverity(1); % The check failed
    end
    
end

function [ResultDescription, ResultDetails] = inputDataConfigurationFunc(system)
    % initilize the returns to empty
    ResultDescription = {};
    ResultDetails     = {};
    hasPassed         = 1;
    % get the model advisor handle for the model.
    mdladvObj = Simulink.ModelAdvisor.getModelAdvisor(system);
    % pass fail status to "fail" 
    mdladvObj.setCheckResultStatus(true);         
    passText = ModelAdvisor.Text('Pass: ',{'bold','pass'});
    failText = ModelAdvisor.Text('Fail: ',{'bold','fail'});    
    
    % first see if the model has root level inports
    % By setting the SearchDepth Parameter to 1 only the root level is
    % checked
    rootIn = find_system(system,'SearchDepth',1,'BlockType','Inport');
    % So there are no root level inports, need to check for blocks that
    % supply signals.  E.g. fromWorkspace, fromFile, or SignalBuilder
    fromFile = find_system(system,'BlockType','FromFile');
    fromWS   = find_system(system,'BlockType','FromWorkspace');
    % The Signal Builder blocks is actually a Masked Subsystem.
    % Because of this we need to search based on the mask type
    sigBuilder = find_system(system,'MaskType','Sigbuilder block');
    % Cat the signals together so it can be checked in one step.
    allSource  = [fromFile;fromWS;sigBuilder];
    if (isempty(allSource))  && (isempty(rootIn))
        % error detected: No inports or Other sources.
        ResultDescription{end+1} = ModelAdvisor.Paragraph(['No signal',...
                                   ' sources where detected in the model']);
        ResultDetails{end+1}     = {};         
        hasPassed                = 0;
    end
    if (isempty(allSource)==0)
        % if fromWorkspace blocks or fromFile blocks are found make
        % sure the data / file exists
        if (~isempty(fromFile))
            % For every fromFile block check that the mat file exists
            for inx = 1 : length(fromFile)
                matName = get_param(fromFile{inx},'FileName');
                % see if the file exists
                if (exist(matName,'file') == 0)
                    ResultDescription{end+1} = ModelAdvisor.Text(['The file ',...
                        matName,' does not exist for the following from file block']);%#ok  
                    % this will add a hyper link to the block
                    ResultDetails{end+1} = fromFile{inx};%#ok<AGROW>
                    hasPassed            = 0;                    
                end
            end % ends for loop
        end
        if (~isempty(fromWS))
            % For every fromFile block check that the mat file exists
            for inx = 1 : length(fromWS)
                varName = get_param(fromWS{inx},'VariableName');
                % see if the file exists
                if (evalin('base',['exist(''',varName,''',''var'')']) == 0)
                    ResultDescription{end+1} = ModelAdvisor.Text(['The workspace variable ',...
                        varName,' does not exist for the following from workspace block']);%#ok  
                    % this will add a hyper link to the block
                    ResultDetails{end+1} = fromWS{inx}; %#ok                                                          
                    hasPassed            = 0;                    
                end
            end % ends for loop
        end
    end 
    if (isempty(rootIn) == 0)
        % check to make sure that the root level inport data is enabled and
        % that the data exists for the the root level inports
        % This data exists as part of the configurtation set
        myObj     = getActiveConfigSet(system);
        isEnabled = get_param(myObj,'LoadExternalInput');
        if (strcmp(isEnabled,'on'))
            % check that the workspace params exist
            varStr          = get_param(myObj,'ExternalInput');
            [vars,passFail] = parseOutVars(varStr);
            if (passFail == 0)
                t1 = ModelAdvisor.Text('The following variables where not in the base workspace ');                
                t2 = ModelAdvisor.Text([vars{:}],{'bold','fail'}); % bold & red
                
                ResultDescription{end+1} = [failText t1 t2];  
                % this will add a hyper link to the block
                ResultDetails{end+1} = {};                                                         
                hasPassed            = 0;                
            end % Ends the passFail check
        else % the model dosn't have the input enabled
            t1                       = ModelAdvisor.Text('The model has root level inputs,');
            t2                       = ModelAdvisor.Text(' but the inputs where not enabled in the data logging menu');                
            ResultDescription{end+1} = [failText t1 t2];              
            ResultDetails{end+1}     = {};                                                                     
            hasPassed                = 0;                            
        end % Ends the isEnabled if
    end
    
    if (hasPassed == 1)
        mdladvObj.setActionEnable(0); % the check passed, therefore enable action button
        ResultDescription{end+1} = [passText,ModelAdvisor.Text('Input information is correctly configured')];
        ResultDetails{end+1}     = {};                                       
        mdladvObj.setCheckResultStatus(true);     
    else
        mdladvObj.setActionEnable(1);      
        mdladvObj.setCheckResultStatus(false);     
        mdladvObj.setCheckErrorSeverity(1); % The check failed
    end
        
end

function [ResultDescription, ResultDetails] = signalLoggingEnabledFunc(system)
    % initilize the returns to empty
    ResultDescription = {};
    ResultDetails     = {};

    % get the model advisor handle for the model.
    mdladvObj = Simulink.ModelAdvisor.getModelAdvisor(system);
    % pass fail status to "pass" 
    mdladvObj.setCheckResultStatus(false);         
    

    % get the model information:
    allLines = find_system(system, 'FindAll', 'on', 'type', 'line');
    % get the names off of the lines
    lineNames = get_param(allLines,'Name');
    % get the array index of just the non empty string
    index = find(strcmp(lineNames,'')==0);
    % sub-select the old arrays
    linesOfInt = allLines(index);
    lineNames  = lineNames(index);
    
    % Signal logging is enabled at the source port so we get that first
    lineSrPort = get_param(linesOfInt,'SrcPortHandle');
    lineSrPort = [lineSrPort{:}]; % get it into an easy to use format...
    logEnabled = get_param(lineSrPort,'DataLogging');
    index      = find(strcmp(logEnabled,'on') == 0); 
    % sub-select the old arrays
    linesOfInt    = allLines(index);
    lineNames     = lineNames(index);
    lineSrPortAll = lineSrPort(index);
    lineParentAll = get_param(lineSrPortAll,'Parent');
    
    % get input parameters
    inputParams = mdladvObj.getInputParameters;
    usePrefix   = inputParams{1}.Value;    
    prefixStr   = inputParams{2}.Value;
    
    lineParent = []; 
    lineSrPort = [];                 
    
    if (isempty(linesOfInt) == 0)    
        if (usePrefix == 1)
            % look at ust the signals with the desired prefix
            index = strfind(lineNames,prefixStr);
            if (isempty(index) == 0)            
                for qnx = 1 : length(index)
                    if (isempty(index{qnx}) == 0) && (index{qnx} == 1)
                        % subselect the line parents needed.
                        lineParent{end+1} = lineParentAll{qnx}; 
                        lineSrPort(end+1) = lineSrPortAll(qnx);                 
                    end
                end % ends for loop
            end % ends the isempty loop
        else % Not using prefix, so any lines are game
            lineParent = lineParentAll;
            lineSrPort = lineSrPortAll;
        end % ends the if (usePrefix == 0) struct
        
        if (isempty(lineParent) == 0) 
            ResultDescription{end+1} = ModelAdvisor.Text(['Enable signal',...
                    ' logging on the following signals']);
            % this will add a hyper link to the block
            ResultDetails{end+1}     = lineParent;                                                        
            mdladvObj.setCheckResultData(lineSrPort);
            mdladvObj.setActionEnable(1);      
            mdladvObj.setCheckResultStatus(false);     
            mdladvObj.setCheckErrorSeverity(1); % The check failed
            % Add the information to the list view 
            myLVParam            = ModelAdvisor.ListViewParameter;    
            myLVParam.Name       = 'Unlogged Signals'; % the name appeared at pull down filter
            myLVParam.Data       = get_param(lineSrPort,'Object');  % needs to be the object
            myLVParam.Attributes = {'Name','DataLogging'}; % name is default property
            mdladvObj.setListViewParameters({myLVParam});                
        else
            mdladvObj.setCheckResultStatus(true);     
            mdladvObj.setCheckErrorSeverity(0); % The check failed
        end
    end % ends (isempty(linesOfInt) == 0)    
    
    
end

function [ResultDescription, ResultDetails] = stateflowExampleFunc(system)
    % initilize the returns to empty
    ResultDescription = {};
    ResultDetails     = {};

    % get the model advisor handle for the model.
    mdladvObj = Simulink.ModelAdvisor.getModelAdvisor(system);
    % pass fail status to "pass" 
    mdladvObj.setCheckResultStatus(true);         
    
    % This check will provide information only: Information will be
    % recorded in a table:
    
    rt        = sfroot;
    chart     = rt.find('-isa','Stateflow.Chart');
    numCharts = length(chart);
    table     = ModelAdvisor.Table((numCharts * 3),2);
    chj       = ModelAdvisor.Text('Chart uses history junctions',{'bold'});
    cn        = ModelAdvisor.Text('Chart Name',{'bold'});
    cmt       = ModelAdvisor.Text('Chart uses junctions with multiple transitions',{'bold'});  
    trueTxt   = ModelAdvisor.Text('TRUE',{'fail','bold'});
    falseTxt  = ModelAdvisor.Text('FALSE',{'pass','bold'});    
    for inx = 1 : numCharts
        curChart = chart(inx);
        rowBase  = (inx - 1) * 3;
        table.setEntry(1 + rowBase,1,cn);        
        table.setEntry(1 + rowBase,2,ModelAdvisor.Text(curChart.Name,{'italic','bold'}));
        junc           = curChart.find('-isa','Stateflow.Junction');
        hasHist        = 0;
        hasMultOutJunc = 0;
        multiJuncs     = {};
        for jnx = 1 : length(junc)
            if (strcmp(junc(jnx).Type,'HISTORY'))
                hasHist = 1;
            else
                transOut = junc(jnx).sourcedTransitions;
                if (length(transOut) > 1)
                    hasMultOutJunc    = 1;
                    multiJuncs{end+1} = junc(jnx);
                end
            end
        end % end jnx loop
        table.setEntry(2 + rowBase,1,chj);
        if (hasHist == 0)
            table.setEntry(2 + rowBase,2,falseTxt);
        else
            table.setEntry(2 + rowBase,2,trueTxt);            
        end

        table.setEntry(3 + rowBase,1,cmt);
        if (hasMultOutJunc == 0)
            table.setEntry(3 + rowBase,2,falseTxt);
        else
            table.setEntry(3 + rowBase,2,trueTxt);            
        end
        if (hasMultOutJunc == 1)
            ResultDescription{end+1} = ModelAdvisor.Text('Link to Bad Junctions');
            ResultDetails{end+1}     = multiJuncs;
        end
        
    end % end inx loop
    ResultDescription{end+1} = table;
    ResultDetails{end+1}     = {};
end

function [missVars,pf] = parseOutVars(varStr)
    
    pf       = 1; % defaults to pass
    missVars = {};
    % step 1: remove the start /
    varStr = regexprep(varStr,' ','');
    varStr = varStr(2:end-1);
    varStr = regexp(varStr,',','split');
    for inx = 1 : length(varStr)
        found = evalin('base',['exist(''',varStr{inx},''',''var'')']);
        if (found == 0)
            pf              = 0; % failed
            missVars{end+1} = [varStr{inx},' ']; %#ok Note the space is added for display
        end
    end
    

end

function result = dataLoggingSettingsAction(taskobj)
    mdladvObj = taskobj.MAObj;
    % Get the name of the system:
    system    = getfullname(mdladvObj.System);
    % get input parameters
    inputParams = mdladvObj.getInputParameters;
    saveOrInc   = inputParams{1}.Value;
    % get the checks that passed / failed
    eachPass = mdladvObj.getCheckResultData(mdladvObj.getActiveCheck);
    % create the return text
    result = ModelAdvisor.Paragraph;
    
    
    if (eachPass{1} == 0)
        set_param(system,'SignalLogging','on');
        result.addItem('Signal Logging now enabled');        
    end
    if (eachPass{2} == 0)
        % first the file name        
        logName = simget(system,'SignalLoggingName');          
        if (saveOrInc == 1)
            fName   = [logName,'_',datestr(now,30)];
            evalin('base',['save ',fName,' ',logName,';']);
            evalin('base',['clear ',logName,';']);
            result.addItem('File saved: data deleted from base workspace');                
        else
            % check to see if it ends in a number
            [newName] = incrementNumber(logName);
            set_param(system,'SignalLoggingName',newName);
            result.addItem(['Signal name incremented to ',newName]);                    
        end
    end
    if (eachPass{3} == 0)
        set_param(system,'SaveFormat','StructureWithTime');
        result.addItem('Signal Logging format changed to structure with time');                
    end
    % save result
    mdladvObj.setActionEnable(false);
end

function result = signalLoggingEnabledAction(taskobj)
    mdladvObj = taskobj.MAObj;
    % get the source ports in question...
    lineSrPort = mdladvObj.getCheckResultData(mdladvObj.getActiveCheck);
    for inx = 1 : length(lineSrPort)
        set_param(lineSrPort(inx),'DataLogging','on');
    end
    % create the return text
    result = ModelAdvisor.Paragraph;
    result.addItem('Signal logging now enabled');                    
    % disable the action button
    mdladvObj.setActionEnable(false);
end

function [newName] = incrementNumber(logName)
    % The format of the extension is VarName_#
    % if there are multiple _ in the string we look for the last one
    underLoc = findstr(logName,'_');
    if (isempty(underLoc))
        % there are no post scripts... start a new version
        newName = [logName,'_1'];
    else
        endNum = logName(underLoc(end)+1:end);
        endNum = str2double(endNum);
        % check to make sure that the end was a number
        if (isnan(endNum)) 
            newName = [logName,'_1'];
        else
            endNum = endNum + 1;
            newName = [logName(1:underLoc(end)),num2str(endNum)];
        end % ends if (isempty(endNum))
    end % ends if (isempty(underLoc))
end

Contact us