Main Content

Define Edit-Time Checks to Comply with Conditions That You Specify with the Model Advisor

In this example, you create three custom edit-time checks that check for compliance with certain software design standards. Custom edit-time checks help you catch issues earlier in the model design review process, but these checks also report issues in the Model Advisor.

The first check checks that Inport and Outport blocks have certain colors depending on their output data types.

The second check checks whether a Trigger block is higher than other blocks in a subsystem. This check must check edited blocks and other blocks in the same subsystem as the Trigger block.

The third check checks whether signals that connect to Outport blocks have labels.

Register and Define the Custom Edit-Time Checks

1. To register a custom edit-time check, create an sl_customization function. For this example, copy the sl_customization_cec.m file to the sl_customization.m file by entering this command at the command prompt:

copyfile sl_customization_cec.m sl_customization.m f

The sl_customization function accepts one argument, a customization manager object. To register the custom check, use the addModelAdvisorCheckFcn method. The input to this method is a handle to the check definition function. For this example, defineCheck is the check definition function. Open and inspect the sl_customization.m file.

function sl_customization(cm)
cm.addModelAdvisorCheckFcn(@defineCheck);

2. Create the check definition function. For this example, open and inspect the defineCheck function. This function contains three ModelAdvisor.Check objects with their check IDs as input arguments. The CallbackHandle property is the name of the class that you create to define the edit-time check. For this example, MyEditTimeChecks is the namespace and PortColor, TriggerBlockPosition, and SignalLabel are the class names. The mdladvRoot.publish function publishes the checks to a new folder in the Model Advisor. For this example, the folder name is DEMO: Edit Time Checks.

function defineCheck

%% Check the background color of Inport and Outport blocks.
rec = ModelAdvisor.Check("advisor.edittimecheck.PortColor");
rec.Title = 'Check color of Inport and Outport blocks';
rec.CallbackHandle = 'MyEditTimeChecks.PortColor'; 
mdladvRoot = ModelAdvisor.Root;
mdladvRoot.publish(rec,'DEMO: Edit Time Checks');

%% Check that determines whether Trigger block is the top-most block in a subsystem.
rec= ModelAdvisor.Check("advisor.edittimecheck.TriggerBlock");
rec.Title = 'Check that Trigger block position is higher than other blocks';
rec.CallbackHandle = 'MyEditTimeChecks.TriggerBlockPosition';
mdladvRoot.publish(rec,'DEMO: Edit Time Checks');

%% Check that determines whether signals with SignalPropagation 'on' have labels.
rec = ModelAdvisor.Check("advisor.edittimecheck.SignalLabel");
rec.Title = 'Check that signals have labels if they are to propagate those labels';
rec.CallbackHandle = 'MyEditTimeChecks.SignalLabels';
mdladvRoot.publish(rec,'DEMO: Edit Time Checks');

3. Create an edit-time check by creating a class that derives from the ModelAdvisor.EdittimeCheck abstract base class. Inspect the first edit-time check by opening the PortColor.m file.

classdef PortColor < ModelAdvisor.EdittimeCheck
    % Check that ports conform to software design standards for background color.
    %
    %   Background Color                Data Types
    %   orange                          Boolean
    %   green                           all floating-point
    %   cyan                            all integers
    %   Light Blue                      Enumerations and Bus Objects
    %   white                           auto
    %


    methods
        function obj=PortColor(checkId)
            obj=obj@ModelAdvisor.EdittimeCheck(checkId);
            obj.traversalType = edittimecheck.TraversalTypes.BLKITER;                       
        end
        
        function violation = blockDiscovered(obj, blk)
            violation = [];  
            if strcmp(get_param(blk,'BlockType'),'Inport') || strcmp(get_param(blk,'BlockType'),'Outport')
                
                dataType = get_param(blk,'OutDataTypeStr');
                currentBgColor = get_param(blk,'BackgroundColor');
                
                if strcmp(dataType,'boolean')
                        if ~strcmp(currentBgColor, 'orange')
                            % Create a violation object using the ModelAdvisor.ResultDetail class.
                            violation = ModelAdvisor.ResultDetail;
                            ModelAdvisor.ResultDetail.setData(violation,'SID',Simulink.ID.getSID(blk));
                            violation.CheckID = obj.checkId;
                            violation.Description = 'Inport/Outport blocks with Boolean outputs should be orange.';
                            violation.title = 'Port Block Color';
                            violation.ViolationType = 'warn';
                        end
                elseif any(strcmp({'single','double'},dataType))
                        if ~strcmp(currentBgColor, 'green')
                            violation = ModelAdvisor.ResultDetail;
                            ModelAdvisor.ResultDetail.setData(violation,'SID',Simulink.ID.getSID(blk));
                            violation.CheckID = obj.checkId;
                            violation.Description = 'Inport/Outport blocks with floating-point outputs should be green.';
                            violation.title = 'Port Block Color';
                            violation.ViolationType = 'warn';
                        end
                elseif any(strcmp({'uint8','uint16','uint32','int8','int16','int32'}, dataType))
                        if ~strcmp(currentBgColor, 'cyan')
                            violation = ModelAdvisor.ResultDetail;
                            ModelAdvisor.ResultDetail.setData(violation,'SID',Simulink.ID.getSID(blk));
                            violation.CheckID = obj.checkId;
                            violation.Description = 'Inport/Outport blocks with integer outputs should be cyan.';
                            violation.title = 'Port Block Color';
                            violation.ViolationType = 'warn';
                        end
                elseif contains(dataType,'Bus:')
                    if ~strcmp(currentBgColor, 'lightBlue')
                        violation = ModelAdvisor.ResultDetail;
                        ModelAdvisor.ResultDetail.setData(violation,'SID',Simulink.ID.getSID(blk));
                        violation.CheckID = obj.checkId;
                        violation.Description = 'Inport/Outport blocks with bus outputs should be light blue.';
                        violation.title = 'Port Block Color';
                        violation.ViolationType = 'warn';
                    end
                 elseif contains(dataType,'Enum:')
                    if ~strcmp(currentBgColor, 'lightBlue')
                        violation = ModelAdvisor.ResultDetail;
                        ModelAdvisor.ResultDetail.setData(violation,'SID',Simulink.ID.getSID(blk));
                        violation.CheckID = obj.checkId;
                        violation.Description = 'Inport/Outport blocks with enumeration outputs should be light blue.';
                        violation.title = 'Port Block Color';
                        violation.ViolationType = 'warn';
                    end
                elseif contains(dataType, 'auto')
                    if ~strcmp(currentBgColor, 'white')
                        violation = ModelAdvisor.ResultDetail;
                        ModelAdvisor.ResultDetail.setData(violation,'SID',Simulink.ID.getSID(blk));
                        violation.CheckID = obj.checkId;
                        violation.Description = 'Inport/Outport blocks with auto outputs should be white.';
                        violation.title = 'Port Block Color';
                        violation.ViolationType = 'warn';
                    end                    
                end
            end  
        end
        
        function violation = finishedTraversal(obj)            
            violation = [];
        end
        
        function success = fix(obj, violation)
            success = true;
            dataType = get_param(violation.Data,'OutDataTypeStr');
            if strcmp(dataType,'boolean')
                set_param(violation.Data,'BackgroundColor','orange');
            elseif any(strcmp({'single','double'},dataType))
                set_param(violation.Data,'BackgroundColor','green');
            elseif any(strcmp({'uint8','uint16','uint32','int8','int16','int32'}, dataType))
                set_param(violation.Data,'BackgroundColor','cyan');
            elseif contains(dataType,'Bus:')  || contains(dataType,'Enum:')
                set_param(violation.Data,'BackgroundColor','lightBlue');
            elseif contains(dataType,'auto')
                set_param(violation.Data,'BackgroundColor','white');
            end
        end
    end
end

The PortColor class defines three methods: PortColor, blockDiscovered, and fix. The PortColor method sets the CheckId and TraversalType properties. This check has a traversal type of edittimecheck.TraversalTypes.BLKITER because the check must check newly added and edited blocks, but it does not have to check for affected blocks in the same subsystem or model as the edited or newly added blocks. The blockDiscovered method contains an algorithm that checks the color of Inport and Outport blocks. Then, because the violation is on a block, the algorithm highlights a violating block by creating a ModelAdvisor.ResultDetail violation object with the Type property set to the default value of SID. The fix method updates blocks that do not have correct colors.

4. Inspect the second edit-time check by opening the TriggerBlockPosition.m file.

classdef TriggerBlockPosition < ModelAdvisor.EdittimeCheck
    properties
        TriggerBlock = [];
        position = [];
    end
    
    methods
        function obj=TriggerBlockPosition(checkId)
            obj=obj@ModelAdvisor.EdittimeCheck(checkId);
            obj.traversalType = edittimecheck.TraversalTypes.ACTIVEGRAPH;
        end
        
        function violation = blockDiscovered(obj, blk)
            violation = [];
            if strcmp(get_param(blk,'BlockType'),'TriggerPort')
                obj.TriggerBlock = blk;
            else
                h = get_param(blk,'Position');
                obj.position = [obj.position, h(2)];
            end
        end
        
        function violation = finishedTraversal(obj)
            violation = [];
            if isempty(obj.TriggerBlock)                
                return;
            end
            triggerPosition = get_param(obj.TriggerBlock,'Position');
            if min(obj.position) < triggerPosition(2)
                violation = ModelAdvisor.ResultDetail;
                ModelAdvisor.ResultDetail.setData(violation,'SID',...
                    Simulink.ID.getSID(obj.TriggerBlock));                
                violation.CheckID = obj.checkId;
                violation.title = 'Trigger Block Position';
                violation.Description = 'Trigger Block should be top block in subsystem';                
                violation.ViolationType = 'warn';                                
            end
            obj.TriggerBlock = [];
            obj.position =[];            
        end    
    end
end

The TriggerBlockPosition class defines three methods: TriggerBlockPosition, blockDiscovered, and finishedTraversal. The TriggerBlockPosition method sets the CheckId and TraversalType properties. This check has a traversal type of edittimecheck.TraversalTypes.ACTIVEGRAPH because it must check other blocks in the same subsystem as the Trigger block. The blockDiscovered method checks the position of Trigger blocks within subsystems. The finishedTraversal method checks whether the position of these Trigger blocks are higher than other blocks in a subsystem. Then, because the violation is on a block, the algorithm highlights a violating block by creating a ModelAdvisor.ResultDetail violation object with the Type property set to the default value of SID.

5. Inspect the third edit-time check by opening the SignalLabels.m file.

classdef SignalLabels < ModelAdvisor.EdittimeCheck
    methods
        function obj=SignalLabels(checkId)
            obj=obj@ModelAdvisor.EdittimeCheck(checkId);
            obj.traversalType = edittimecheck.TraversalTypes.BLKITER;                       
        end

        function violation = blockDiscovered(obj, blk)
            violation = [];
            ports    = get_param(blk,'Ports');
            lh = get_param(blk, 'LineHandles');
            if strcmp(get_param(blk,'BlockType'),'Outport')
                for j = 1 : ports(1)
                    if lh.Inport(j) ~= -1 % failure case: no connection
                        allsources = get_param(lh.Inport(j),'SrcPortHandle');
                        hiliteHandle = get_param(lh.Inport(j), 'DstPortHandle');
                        if (isempty(allsources) ~= 0) || (isempty(find(allsources==-1,1)) ~= 0)
                            lh_obj = get_param(lh.Inport(j),'Object');
                            if isempty(lh_obj.Name)
                                if strcmp(lh_obj.signalPropagation,'off') == 1
                                   allsources_parent = get_param(allsources,'Parent');
                                   if strcmp(get_param(allsources_parent,'BlockType'),'Inport')
                                        buscreator_outputs = get_param(allsources_parent,'IsBusElementPort');
                                    else
                                        buscreator_outputs = 'off';
                                    end
                                    if ~strcmp(buscreator_outputs,'on')
                                        violation = ModelAdvisor.ResultDetail;
                                        ModelAdvisor.ResultDetail.setData(violation,'Signal',hiliteHandle);
                                        violation.Description ='This signal should have a label.';
                                        violation.CheckID = obj.checkId;
                                        violation.Title = 'Signal Label Missing';                               
                                    end
                                end
                            end
                        end
                    end
                end
            end
        end
    end       
end

The SignalLabels class defines two methods: SignalLabels and blockDiscovered. The SignalLabels method sets the CheckId and TraversalType properties. This check has a traversal type of edittimecheck.TraversalTypes.BLKITER because the check must check newly added and edited blocks, but it does not have to check for affected blocks in the same subsystem or model as the edited or newly added blocks. Because this check is for signals, the blockDiscovered method must use the parameters on the line handles, LineHandles, of blocks to find signals with violations. Specifically, for signals that connect to Outport blocks, this algorithm checks whether the Name signal parameter has a value. Then, because the violation is on a signal, the algorithm highlights the signal by creating a violation object with the Type property value set to Signal.

6. Create a folder named +MyEditTimeChecks and save the three class definition files in that folder. Classes must be in a folder that has the same name as their namespace. Enter these commands at the command prompt:

copyfile PortColor.m* +MyEditTimeChecks f
copyfile TriggerBlockPosition.m +MyEditTimeChecks
copyfile SignalLabels.m +MyEditTimeChecks

Run the Edit-Time Checks on a Model

1. In order for your custom checks to be visible in the Model Advisor, you must refresh the Model Advisor check information cache. Enter this command at the command prompt:

Advisor.Manager.refresh_customizations

2. Open the model by entering this command at the command prompt.

open_system('AdvisorCustomizationExample.slx');

advisor-customization-example-model.png

3. To create a custom configuration of checks, open the Model Advisor Configuration Editor. On the Modeling tab, click Model Advisor > Configuration Editor.

4. You can create a custom configuration consisting of just the three edit-time checks in this example by deleting every folder except for the DEMO: Edit Time Checks folder. The my_config.json file that ships with this example contains this configuration. Close the Model Advisor Configuration Editor.

5 Set the custom configuration to the my_config.json file by clicking the Modeling tab and selecting Model Advisor > Edit-Time Checks. In the Configuration Parameters dialog box, specify the path to the configuration file in the Model Advisor configuration file parameter. Alternatively, enter this command at the command prompt:

ModelAdvisor.setModelConfiguration('AdvisorCustomizationExample', 'my_config.json');

6. Turn on edit-time checking by clicking the Modeling tab and selecting Model Advisor > Edit-Time Checks. In the Configuration Parameters dialog box, select the Edit-Time Checks parameter. Alternatively, you can enter this command at the command prompt:

edittime.setAdvisorChecking('AdvisorCustomizationExample','on');

7. Open the Model Advisor by clicking the Modeling tab and selecting Model Advisor. Observe that the three edit-time checks are the only ones in the Model Advisor. Close the Model Advisor.

8. To view the edit-time warnings, click the blocks and signals highlighted in yellow.

22a_model_with_highlight.png

At the top level of the model, the two Inport blocks have an output data type of int32. They produce edit-time warnings because they should be cyan. The Outport block does not produce a violation because it has an auto data type and is white. In the Amplifier subsystem, the Inport and Outport blocks do not produce edit-time warnings because they have a data type of auto and are white. The Trigger block does not produce an edit-time warning because it is the top-most block in the model. If you move the Trigger block below another block, the Trigger block has an edit-time warning. The signal connecting to the Outport block produces a warning because it does not have a label.

9. To fix the edit-time warnings for the two Inport blocks, in the edit-time check warning window, click Fix.

10. Close the model and the Model Advisor.

bdclose;

10. Remove the files from your working directory. Refresh the Model Advisor check information cache by entering this command:

Advisor.Manager.refresh_customizations

Performance Considerations for Custom Edit-Time Checks

To help prevent custom edit-time checks from negatively impacting performance as you edit your model, the Model Advisor automatically disables custom edit-time checks if, in the current MATLAB® session, the check takes longer than 500 milliseconds to execute in at least three different Simulink® models.

If the Model Advisor disables a custom edit-time check, you will see a warning on the Simulink canvas. You can re-enable the edit-time check by either:

  • Clicking the hyperlink text in the warning.

  • Passing the check identifier, checkID, to the function edittime.enableCheck:

edittime.enableCheck(checkID)

To prevent a custom edit-time check from being disabled, author the check so that the check executes in less than 500 milliseconds on your models.

See Also

| |

Related Topics