Documentation

Create a Custom Model Metric

To create your own custom model metric:

  1. Use the slmetric.metric.createNewMetricClass function to create a new metric class derived from the base class slmetric.metric.Metric.

  2. Set the following properties of the class:

    • ID: Unique metric identifier that retrieves the new metric data.

    • Name: Name of the metric algorithm.

    • ComponentScope: Model components for which the metric is calculated.

    • CompileContext: Compile mode for metric calculation. If your model requires model metric requires model compilation, specify PostCompile. Collecting metric data for compiled models slows performance.

    • ResultCheckSumCoverage: Specify whether you want the metric data regenerated if source file and Version have not changed.

    • AggregationMode: How the metric algorithm aggregates metric data.

    • AggregateComponentDetails: Returns all detailed results or aggregates detailed results of the component.

    Optionally, set these additional properties:

    • Description: Description of the metric.

    • Version: Metric version.

  3. Write the metric algorithm into the slmetric.metric.Metric method, algorithm. The algorithm calculates the metric data specified by the Advisor.component.Component class. The Advisor.component.Types class specifies the types of model objects for which you can calculate metric data.

Create Model Metric for Nonvirtual Block Count

This example shows how to use the model metric API to create a custom model metric for counting nonvirtual blocks in a model. After creating the metric, you can collect data for the metric, access the results, and export the results.

Create Metric Class

Using the createNewMetricClass function, create a new metric class named nonvirtualblockcount. The function creates a file, nonvirtualblockcount.m, in the current working folder. The file contains a constructor and empty metric algorithm method. For this example, make sure you are in a writable folder.

className = 'nonvirtualblockcount';
slmetric.metric.createNewMetricClass(className);

Create Nonvirtual Block Count Metric

To write the metric algorithm, open the nonvirtualblockcount.m file and add the metric to the file. For example, to edit the file, use the command edit(className). For this example, you can create the metric algorithm by copying this logic into nonvirtualblockcount.m file.

classdef nonvirtualblockcount < slmetric.metric.Metric
    %nonvirtualblockcount calculates number of nonvirtual blocks per level.
    % BusCreator, BusSelector and BusAssign are treated as nonvirtual.
    properties
        VirtualBlockTypes = {'Demux','From','Goto','Ground', ...
            'GotoTagVisiblity','Mux','SignalSpecification', ...
            'Terminator','Inport'};
    end
    
    methods
    function this = nonvirtualblockcount()
        this.ID = 'nonvirtualblockcount';
        this.Name = 'Nonvirtual Block Count';
        this.Version = 1;
        this.CompileContext = 'None';
        this.Description = 'Algorithm that counts nonvirtual blocks per level.';
        this.ComponentScope = [Advisor.component.Types.Model, ...
            Advisor.component.Types.SubSystem];
        this.AggregationMode = slmetric.AggregationMode.Sum;
	    this.AggregateComponentDetails = true;
        this.ResultChecksumCoverage = true;
        this.SupportsResultDetails = true;
            
    end

    function res = algorithm(this, component)
        % create a result object for this component
        res = slmetric.metric.Result();	

        % set the component and metric ID
        res.ComponentID = component.ID;
        res.MetricID = this.ID;
        
        % Practice
        
        D1=slmetric.metric.ResultDetail('identifier 1','Name 1');
        D1.Value=0;
        D1.setGroup('Group1','Group1Name');
        D2=slmetric.metric.ResultDetail('identifier 2','Name 2');
        D2.Value=1;
        D2.setGroup('Group1','Group1Name');
        
        

        % use find_system to get all blocks inside this component
        blocks = find_system(getPath(component), ...
            'SearchDepth', 1, ...
            'Type', 'Block');

        isNonVirtual = true(size(blocks));

        for n=1:length(blocks)
            blockType = get_param(blocks{n}, 'BlockType');

            if any(strcmp(this.VirtualBlockTypes, blockType))
                isNonVirtual(n) = false;
            else
                switch blockType
                    case 'SubSystem'
                        % Virtual unless the block is conditionally executed
                        % or the Treat as atomic unit check box is selected.
                        if strcmp(get_param(blocks{n}, 'IsSubSystemVirtual'), ...
                                'on')
                            isNonVirtual(n) = false;
                        end
                    case 'Outport'
                        % Outport: Virtual when the block resides within
                        % any SubSystem block (conditional or not), and 
                        % does not reside in the root (top-level) Simulink window.
                        if component.Type ~= Advisor.component.Types.Model
                            isNonVirtual(n) = false;
                        end
                    case 'Selector'
                        % Virtual only when Number of input dimensions 
                        % specifies 1 and Index Option specifies Select 
                        % all, Index vector (dialog), or Starting index (dialog).
                        nod = get_param(blocks{n}, 'NumberOfDimensions');
                        ios = get_param(blocks{n}, 'IndexOptionArray');

                        ios_settings = {'Assign all', 'Index vector (dialog)', ...
                            'Starting index (dialog)'};

                        if nod == 1 && any(strcmp(ios_settings, ios))
                            isNonVirtual(n) = false;
                        end
                    case 'Trigger'
                        % Virtual when the output port is not present.
                        if strcmp(get_param(blocks{n}, 'ShowOutputPort'), 'off')
                            isNonVirtual(n) = false;
                        end
                    case 'Enable'
                        % Virtual unless connected directly to an Outport block.
                        isNonVirtual(n) = false;

                        if strcmp(get_param(blocks{n}, 'ShowOutputPort'), 'on')
                            pc = get_param(blocks{n}, 'PortConnectivity');

                            if ~isempty(pc.DstBlock) && ...
                                    strcmp(get_param(pc.DstBlock, 'BlockType'), ...
                                    'Outport')
                                isNonVirtual(n) = true;
                            end
                        end
                end
            end
        end

        blocks = blocks(isNonVirtual);

        res.Value = length(blocks);
    end
    end
end
Now that your new model metric is defined in nonvirtualblockcount.m, register the new metric in the metric repository.
[id_metric,err_msg] = slmetric.metric.registerMetric(className);

Collect Metric Data

To collect metric data on models, use instances of slmetric.Engine. Using the getMetrics method, specify the metrics you want to collect. For this example, specify the nonvirtual block count metric for the sldemo_mdlref_bus model.

Load the sldemo_mdlref_bus model.

model = 'sldemo_mdlref_bus';
load_system(model);

Create a metric engine object and set the analysis root.

metric_engine = slmetric.Engine();
setAnalysisRoot(metric_engine,'Root',model,'RootType','Model');

Collect metric data for the nonvirtual block count metric.

execute(metric_engine);
rc = getMetrics(metric_engine,id_metric);

Display and Export Results

To access the metrics for your model, use instances of slmetric.metric.Result. In this example, display the nonvirtual block count metrics for the sldemo_mdlref_busmodel. For each result, display the MetricID, ComponentPath, and Value.

for n=1:length(rc)
    if rc(n).Status == 0
        results = rc(n).Results;

        for m=1:length(results)
            disp(['MetricID: ',results(m).MetricID]);
            disp(['  ComponentPath: ', results(m).ComponentPath]);
            disp(['  Value: ', num2str(results(m).Value)]);
            disp(' ');
        end
    else
        disp(['No results for:',rc(n).MetricID]);
    end
    disp(' ');
end

Here are the results.

MetricID: nonvirtualblockcount
  ComponentPath: sldemo_mdlref_bus
  Value: 15
 
MetricID: nonvirtualblockcount
  ComponentPath: sldemo_mdlref_bus/CounterA
  Value: NaN
 
MetricID: nonvirtualblockcount
  ComponentPath: sldemo_mdlref_bus/More Info3
  Value: 0
 
MetricID: nonvirtualblockcount
  ComponentPath: sldemo_mdlref_bus/More Info4
  Value: 0
 
MetricID: nonvirtualblockcount
  ComponentPath: sldemo_mdlref_bus/More Info1
  Value: 0
 
MetricID: nonvirtualblockcount
  ComponentPath: sldemo_mdlref_bus/More Info2
  Value: 0
 
MetricID: nonvirtualblockcount
  ComponentPath: sldemo_mdlref_counter_bus
  Value: 2
 
MetricID: nonvirtualblockcount
  ComponentPath: sldemo_mdlref_counter_bus/COUNTER
  Value: 6
 
MetricID: nonvirtualblockcount
  ComponentPath: sldemo_mdlref_counter_bus/COUNTER/Counter
  Value: 3
 
MetricID: nonvirtualblockcount
  ComponentPath: sldemo_mdlref_counter_bus/COUNTER/Counter/ResetCheck
  Value: 4
 
MetricID: nonvirtualblockcount
  ComponentPath: sldemo_mdlref_counter_bus/COUNTER/Counter/ResetCheck/NoReset
  Value: 2
 
MetricID: nonvirtualblockcount
  ComponentPath: sldemo_mdlref_counter_bus/COUNTER/Counter/ResetCheck/Reset
  Value: 3
 
MetricID: nonvirtualblockcount
  ComponentPath: sldemo_mdlref_counter_bus/COUNTER/Counter/SaturationCheck
  Value: 5
 
MetricID: nonvirtualblockcount
  ComponentPath: sldemo_mdlref_counter_bus/COUNTER/LimitsProcess
  Value: 1
 
MetricID: nonvirtualblockcount
  ComponentPath: sldemo_mdlref_counter_bus/More Info1
  Value: 0
 
MetricID: nonvirtualblockcount
  ComponentPath: sldemo_mdlref_counter_bus/More Info2
  Value: 0

To export the metric results to an XML file, use the exportMetrics method. For each metric result, the XML file includes the ComponentID, ComponentPath, MetricID, Value, AggregatedValue, and Measure.

filename='MyMetricData.xml';
exportMetrics(metric_engine,filename);

For this example, unregister the nonvirtual block count metric.

slmetric.metric.unregisterMetric(id_metric);

Close the model.

clear;
bdclose('all');

Limitations

Custom metric algorithms do not support the path property on component objects:

  • Linked Stateflow charts

  • MATLAB Function blocks

Custom metric algorithms do not follow library links.

See Also

| | | | |

Related Topics

Was this topic helpful?