Main Content

Create Custom Tasks

With the CI/CD Automation for Simulink Check support package, you can define a development and verification process for your team by adding built-in and custom tasks to your process. The support package contains several built-in tasks that you can reconfigure and use to perform steps in your process, but if you need to perform other actions or always want to use a reconfigured version of a built-in task, you can create and add custom tasks to your process model.

Depending on what you want your custom task to do, there are different approaches:

To add custom tasks to your process, you need to edit the process model file for your project. If you do not have a project or process model, see Automate and Run Tasks with Process Advisor to get started.

Custom Task That Runs Existing Script

If your custom task only needs to run an existing MATLAB script, you can edit your process model to specify which script to run by using the Action argument for the addTask function.

For example, suppose that you have a script, myScript.m, that you want a custom task to run. You can use the addTask function to add a new task to your process model. The Action argument specifies the function that the task runs. In your processmodel.m file, you can specify:

function processmodel(pm)
    % Defines the project's processmodel

    arguments
        pm padv.ProcessModel
    end

    addTask(pm,"RunMyScript", Action = @runMyScript);

end

function taskResult = runMyScript(~)
    run("myScript.m");
    taskResult = padv.TaskResult;
end
"RunMyScript" is the name for the new task. @runMyScript is the function handle for the function that you define inside the processmodel.m file. padv.TaskResult defines the results for the task.

You can run the script as a task in Process Advisor.

The task RunMyScript in the Process Advisor Tasks column

Custom Task for Specialized Functionality

To create a task that performs a custom functionality, you need to:

  1. Create a new MATLAB class.

  2. Inherit from either a built-in task or the superclass padv.Task.

  3. Specify the task name and, optionally, other task properties.

  4. Keep or override the run method that defines the action that the task performs.

Create New MATLAB Class

Create a new MATLAB class in your project.

Note that namespaces can help you organize the class definition files for your custom tasks. For example, in the root of your project you can create a folder +processLibrary with a subfolder +task and save your class in that folder.

"MyCustomTask.m" class definition file inside "+task" folder inside "+processLibrary" folder

To share your custom tasks across multiple process models in different projects, consider creating a referenced project that contains your folders and class definition files. Your main projects can then use the referenced project as a shared process library.

Choose Superclass for Custom Task

Your MATLAB class can inherit from either:

  • One of the Built-In Tasks — Use this approach when there is a built-in task that is similar to the custom task that you want to create. When you inherit from a built-in task, like padv.builtin.task.RunModelStandards, your custom task inherits the functionality of that task, but then you can override the properties and methods of the class to fit your needs. For information on the built-in tasks, see Built-In Tasks.

  • The superclass padv.Task — Use this approach if your custom task needs to perform a step that is not similar to a built-in task. padv.Task is the base class of the built-in tasks, so you must completely define the inputs, functionality, and outputs of the task.

If you are inheriting from a built-in task, you can replace the contents of your class file with the following example code. The code inherits from the built-in task padv.builtin.task.RunModelStandards, but you can replace those lines of code to inherit from a different built-in task instead.

classdef MyCustomTask < padv.builtin.task.RunModelStandards
    % task definition goes here
    methods
        function obj = MyCustomTask(options)
            arguments
                options.Name = "MyCustomTask";
                options.Title = "My Custom Task";
            end
            obj@padv.builtin.task.RunModelStandards(Name = options.Name);
            obj.Title = options.Title;
        end
    end
end

If you are inheriting from padv.Task, you can replace the contents of your class file with the following example code. The code finds the models in the project by using the iteration query padv.builtin.query.FindModels and specifies those models as task inputs by using the input query padv.builtin.query.GetIterationArtifact. The code calls the constructor of the superclass padv.Task. For information on superclass constructors, see Design Subclass Constructors.

classdef MyCustomTask < padv.Task
    methods
        function obj = MyCustomTask(options)
            arguments
                % unique identifier for task
                options.Name = "MyCustomTask";
                % artifacts the task iterates over
                options.IterationQuery = "padv.builtin.query.FindModels";
                % input artifacts for the task
                options.InputQueries = "padv.builtin.query.GetIterationArtifact";
                % where the task outputs artifacts
                options.OutputDirectory = fullfile(...
                    '$DEFAULTOUTPUTDIR$','my_custom_task_results');
            end

            % Calling constructor of superclass padv.Task
            obj@padv.Task(options.Name,...
                IterationQuery=options.IterationQuery,...
                InputQueries=options.InputQueries);
            obj.OutputDirectory = options.OutputDirectory;
        end

        function taskResult = run(obj,input)
            % "input" is a cell array of input artifacts
            % length(input) = number of input queries
            
            % class definition goes here

            % specify results from task using padv.TaskResult
            taskResult = padv.TaskResult;
            taskResult.Status = padv.TaskStatus.Pass;
            % taskResult.Status = padv.TaskStatus.Fail;
            % taskResult.Status = padv.TaskStatus.Error;
        end
    end
end

Specify Task Properties

Specify the Name property for your task and, optionally, other task properties.

You must specify a Name because the Name is the unique identifier for the task. Specifying other class properties is optional, but can help you define the task behavior. The following table lists common class properties that you often specify for a custom task. For information on other class properties, see Built-In Tasks or padv.Task. For information on how tasks and queries define your process, see Overview of Process Model.

PropertyDescription
Name (required)

Unique identifier for task.

IterationQuery

Artifacts the task iterates over.

By default, custom tasks run one time for the project. But you can use queries to automatically find other artifacts, like the models in your project, for the task to iterate over.

Tasks column in Process Advisor showing My Custom Task iterating over several models

InputQueries

Inputs to the task.

Process Advisor task I/O column showing a model as an input

InputDependencyQuery

Artifacts that the task inputs depend on.

Typically, you specify InputDependencyQuery as padv.builtin.query.GetDependentArtifacts to get the dependent artifacts for each task input.

Process Advisor task I/O column showing the dependencies, mainly data dictionary dependencies, for a model

By default, the build system includes the process model as a dependency because when you change the process model, you might invalidate the task results. However, you can control this behavior by using the user setting TrackProcessModel.

OutputDirectory

Directory where the task outputs artifacts.

If you do not specify OutputDirectory for a custom task, the build system stores task outputs in the DefaultOutputDirectory specified by padv.ProcessModel.

TaskType

Type of task.

By default, tasks are automated tasks that automatically perform the task action and return a task status. But you can also create manual tasks that remind users of tasks that they must manually perform as part of their process. As well as user tasks that the user needs to perform and can mark off on a checkbox and add supporting output files to the task results.

Tasks column in Process Advisor showing an Automated Task, Manual Task, and User Task

For other task properties, see padv.Task.

Keep or Override run Method

The run method defines the action that your custom task performs. For examples of how to override the run method, see Example Custom Tasks.

Make sure to use the same method signature as the class that you inherit from. In the method signature, the input argument is a cell array that contains the input artifacts from your input queries. Each element in input corresponds to each input query that you specify.

For example, if you only specify one input query, padv.builtin.query.GetIterationArtifact, and you are iterating over each model in the project, you can use the first element of input, input{1}, to perform an action on each model in the project:

        function taskResult = run(obj,input)
            % Before the task loads models,
            % save a list of the models that are already loaded.
            loadedModels = get_param(Simulink.allBlockDiagrams(),'Name');
            
            % identify model name
            % "input" is a cell array of input artifacts
            % First input query gets iteration artifact (a model)
            model = input{1}; % get padv.Artifact from first input query
            modelName = padv.util.getModelName(model);

            % Example task that loads model and displays information
            load_system(modelName);
            disp(modelName);
            disp('Data Dictionaries:')
            disp(Simulink.data.dictionary.getOpenDictionaryPaths)
            
            % specify results from task using padv.TaskResult
            taskResult = padv.TaskResult;
            taskResult.Status = padv.TaskStatus.Pass;
            % taskResult.Status = padv.TaskStatus.Fail;
            % taskResult.Status = padv.TaskStatus.Error;

            % % Close models that were loaded by this task.
            padv.util.closeModelsLoadedByTask(...
                PreviouslyLoadedModels=loadedModels)
        end

The run method must return a padv.TaskResult object. Process Advisor uses the padv.TaskResult object to determine the status of your custom task. The task result properties Status, OutputPaths, and Values correspond to the Tasks, I/O, and Details columns in Process Advisor.

Example CodeAppearance in Process Advisor
taskResult.Status = padv.TaskStatus.Pass

Tasks column shows task with green check mark

taskResult.Status = padv.TaskStatus.Fail

Tasks column shows task with red X

taskResult.Status = padv.TaskStatus.Error

Tasks column shows task with red exclamation mark

taskResult.OutputPaths=string(...
    fullfile("PA_Results","myFile.txt"));

I/O column shows "myFile.txt" file as output

taskResult.Values.Fail = 3;
taskResult.Values.Warn = 2;
taskResult.Values.Pass = 1;

Details column with 3 failing results, 2 warning results, and 1 passing result

Task results support three approaches to setting the task result values.

ApproachUse

Directly control the task result values by using the Values property.

taskResult.Values.Fail = 3;
taskResult.Values.Warn = 2;
taskResult.Values.Pass = 1;

  • Tasks without formal assessments or compliance

  • Tasks that do not require detailed information about the task result values that appear in the Details column

Details column with 3 failing results, 2 warning results, and 1 passing result

Set the ComplianceStatusSummary property of the task result. The task result object uses these values to set the Values property of the task result.

You can set the ComplianceStatusSummary property by using the setComplianceCount or addComplianceStatus functions.

setComplianceCount(taskResult,"NonCompliant",3);
setComplianceCount(taskResult,"Warning",2);
setComplianceCount(taskResult,"Compliant",1);

  • Tasks with or without formal assessments

  • Tasks where you want to start to map task result values to specific compliance statuses

Details column with 3 failing results, 2 warning results, and 1 passing result

Create formal assessments and add the assessments to the task. When you run the task, the task automatically runs the assessments and adds the assessment results to the task results. The task result object uses the assessment results to set the ComplianceStatusSummary property, which sets the Values property of the task result object.

You can create assessments by using padv.Assessment objects and store the assessment results by using padv.AssessmentResult objects. You can add assessments to a task by specifying the task property Assessments or by using the task object function addAssessments. For example:

    % --- Assessments ---
    A1 = padv.Assessment("A1",...
        Objective="Output file must be a TXT file.",...
        Action=@assessOutputIsTxt);
    A2 = padv.Assessment("A2",...
        Objective="Output file should not be empty.",...
        Action=@assessOutputFileContent);

    % --- Task ---
    T = padv.Task("MyTask",...
        Action=@myTaskAction,...
        OutputDirectory=fullfile("$PROJECTROOT$"),...
        Assessments=A1);
    T.addAssessments(A2);
For more information, see Assess Quality of Task Results.

  • Tasks with formal assessments defined by padv.Assessment objects

  • Tasks where you want to show the specific sources of the failures, warnings, and passing results in the Details column

  • Tasks where you want to integrate formal objectives into the task results

Details column with 1 failing result, 1 warning result, and 1 passing result

Starting in R2024a, when you point to the task status, you can see a breakdown of the individual assessments for a task, the assessment results, and the impact of those assessment results on the overall task status. The assessment information provides the specific objectives associated with the failures, warnings, and passing results that you see in the Details column.

Task status showing results from the individual task assessments. One assessment fails because not all requirements are linked to tests. One assessment has a warning because requirements must have unique names. One assessment passes because tests must have at least one requirement linked.

Optional Customizations for Opening Task Artifacts

If you want your tasks to open artifacts by using specific commands or programs, you can provide special handling for those artifacts by overriding the openArtifact method of the task. You must use the same method signature as the openArtifact method in the padv.Task class.

For example, suppose that you have a task that outputs PNG images. By default, when you open those images from the I/O column in Process Advisor, the build system automatically tries to import the image by using the Import Tool. But suppose that you want Process Advisor to display the image instead. Inside the class definition file for the task, you can override the openArtifact method to have Process Advisor use the imshow function to open the images that the task outputs. You can use the artifact argument to find the path to the task output, call the imshow function on the image filename, and return a status that indicates whether the function was able to successfully open the artifact. If the function returns the status as false, the build system opens the artifact by using the base implementation of the openArtifact method instead.

function status = openArtifact(~,artifact,~)
% Override default openArtifact method to open PNG files using the imshow function
% For other file types, use the default openArtifact method

    % Validate input arguments
    arguments
        ~
        artifact (1,1) padv.Artifact {mustBeNonempty}
        ~
    end
    
    % Get the absolute path of the image
    absPath = artifact.ArtifactAddress.getAbsolutePath;
    
    % Determine the file extension
    [~, ~, ext] = fileparts(absPath);
    
    % Check if the file is a PNG
    if strcmp(ext, '.png')
        % Display image
        imshow(absPath);
    
        % Set status to true indicating successful operation
        status = true;
    else
        % Unsupported file type
        status = false;
    end

end
For more information, see the padv.Task Object Functions.

Optional Customizations to Task Options Menu

Additionally, you can customize the options menu for a task by modifying your task and process model to:

  • Launch a tool from a task in Process Advisor. For more information, see LaunchToolAction.

    Options menu for custom task showing custom "Open My Tool" button

  • Add task tools that integrate custom apps and commands into Process Advisor. For more information, see padv.TaskTool.

    Task options menu showing custom command and task app buttons

  • Override the dryRun method to specify how your custom task evaluates task inputs and generates representative outputs for quick process model tests. For more information, see Dry Run Tasks to Test Process Model.

    Options menu for custom task showing "Dry Run Task" button

Add Custom Task to Process

Add your custom task to your process model by using the addTask function. For example, to add a custom task named MyCustomTask that is saved in a +task subfolder inside a +processLibrary folder:

function processmodel(pm)
    % Defines the project's processmodel

    arguments
        pm padv.ProcessModel
    end

    addTask(pm,processLibrary.task.MyCustomTask);

end

The custom task appears in the Tasks column in Process Advisor.

Process Advisor standalone window with MyCustomTask in the Tasks column

Example Custom Tasks

Perform Post-Processing on Task Results

You can use custom tasks to perform pre-processing or post-processing actions. For example, suppose you want to run Model Advisor and if checks generate a failure or a warning, you want the task to fail. There are no built-in tasks that perform this exact functionality by default, but the built-in task padv.builtin.task.RunModelStandards runs Model Advisor and the task fails if a check generates a failure.

You can use a custom task to create your own version of padv.builtin.task.RunModelStandards that overrides the results from the task to specify that if a Model Advisor check returns a warning, the task should also fail.

This example shows a custom task that inherits from the built-in task padv.builtin.task.RunModelStandards, overrides the input queries to use the file sampleChecks.json as the Model Advisor configuration file, and extends the run method of the built-in task to fail the task if Model Advisor returns warnings.

classdef MyRunModelStandards < padv.builtin.task.RunModelStandards
    % RunModelStandards, but use my Model Advisor configuration file
    % and fail the task when there are warnings from Model Advisor checks

    methods
        function obj = MyRunModelStandards(options)

            arguments
                options.Name = "MyRunModelStandards";
                options.Title = "My Check Modeling Standards";
            end

            obj@padv.builtin.task.RunModelStandards(Name = options.Name);
            obj.Title = options.Title;
            % specify current model (iteration artifact) and
            % Model Advisor configuration file as inputs to the task
            obj.addInputQueries([padv.builtin.query.GetIterationArtifact,...
                padv.builtin.query.FindFileWithAddress(...
                Type = 'ma_config_file',...
                Path = fullfile('tools','sampleChecks.json'))]);

        end
        
        function taskResult = run(obj,input)

            % use RunModelStandards to run Model Advisor
            taskResult = run@padv.builtin.task.RunModelStandards(obj,input);
            % If checks for a model fail, then the status will be
            % set to fail.

            % But you can extend the built-in task to specify that
            % if checks for a model generate a warning, then the
            % task status will also be set to fail.
            if taskResult.Values.Warn > 0
                taskResult.Status=padv.TaskStatus.Fail;
            end
            
        end

    end
    
end

Note

In this example, the run method of the custom task extends the run method of the built-in task by calling it from within the custom task run method. But you can also reimplement the run method for a custom task to implement your own version of the run method. For more information and common class designs, see Modify Inherited Methods.

Run Custom Task for Project

Suppose that you want to return a list of the data dictionaries in your project. There are no built-in tasks that perform this functionality, so you can create a custom task that inherits directly from the base class padv.Task and use the arguments to specify the behavior of the custom task.

classdef ListAllDataDictionaries < padv.Task

    methods
        function obj = ListAllDataDictionaries(options)

            arguments
                options.InputQueries = padv.builtin.query.FindArtifacts(...
                    ArtifactType="sl_data_dictionary_file");
                options.Name = "ListAllDataDictionaries";
            end
                inputQueries = options.InputQueries;
                obj@padv.Task(options.Name, ...
                    Title = "My Custom Task for SLDD files", ...
                    InputQueries = inputQueries, ...
                    DescriptionText = "My Custom Task for SLDD files", ...
                    Licenses={});
        end

        function taskResult = run(~, input)
            % Print names of SLDDs
            disp([input{1}.Alias]')
            taskResult = padv.TaskResult;
            taskResult.Status = padv.TaskStatus.Pass;
            taskResult.Values.Pass = 1;
        end
    end
end

In the custom task, you can find the data dictionaries in the project by using the query padv.builtin.query.FindArtifacts and specifying the query as one of the InputQueries for the task. In the run function, you can specify the action that the task performs and specify the task results, in a format that Process Advisor can recognize, by using a padv.TaskResult object. The input is a cell array of input artifacts that the build system automatically creates based on the InputQueries that you specify. In this example, the first cell in input is an array of padv.Artifact objects that represent the data dictionaries in the project. The disp function can display the aliases of the data dictionaries in the MATLAB Command Window. When you specify the task result Status, that sets the task status in the Tasks column in Process Advisor. Values.Pass sets the number of passing results in the Details column in Process Advisor.

Custom task in the Process Advisor tasks column and task output shown in the MATLAB Command Window

Specify Tool for Custom Task

When you point to a task in the Process Advisor app, you can click the ellipsis (...) to view more options. For built-in tasks, you have the option to launch a tool or multiple tools associated with the task. For example, the built-in task Check Modeling Standards allows you to directly open Model Advisor for the model that the task iteration runs on.

Check Modeling Standards task showing option to Open Model Advisor

You can associate a tool with the options menu for a task by specifying the property LaunchToolAction as a function handle that launches that tool. For example, suppose you have a custom task that runs on each model in the project and you want the task to launch Dependency Analyzer for the model. For LaunchToolAction, specify the handle to a function that launches Dependency Analyzer. The function that launches the tool has two inputs, obj and artifact, and must return a result structure with the status of the tool launch action, ToolLaunched.

function processmodel(pm)
    % Defines the project's processmodel

    arguments
        pm padv.ProcessModel
    end

    customTask = addTask(pm,"MyCustomTask",...
        IterationQuery = padv.builtin.query.FindModels,...
        InputQueries = padv.builtin.query.GetIterationArtifact,...
        LaunchToolAction=@openDependencyAnalyzer);

end

function result = openDependencyAnalyzer(obj, artifact)
    result = struct('ToolLaunched', false);    
    % handle non-model task iterations / abstract tasks
    if isempty(artifact)
        result.message = 'Open the tool for an artifact listed under the task title.';
        return;
    end    
    % identify model name
    modelName = padv.util.getModelName(artifact);    
    % open Dependency Analyzer for model
    depview(modelName)    
    result.ToolLaunched = true;
end

Specify Inputs That Can Make Task Outdated

Suppose that you want to create a custom task that analyzes specific Excel® files in your project and you want the task to become outdated when you make changes to those files. You can find the files by using the built-in query padv.builtin.query.FindArtifacts. In this example, the task uses the IncludePathRegex argument of the query to find Excel files (.xlsx) with file names that begin with HLR_. The task uses that query to define the task iterations (IterationQuery) and task inputs (InputQueries). The task iterates over these files and checks for the presence of specific sheets named StepUp and StepDown. If the Excel file has those sheets, the task passes. Otherwise, the task fails. The task automatically becomes outdated if you make a change to any of the Excel files that the query finds.

classdef CheckExcelSheetNames < padv.Task
    methods
        function obj = CheckExcelSheetNames(options)
            arguments
                % unique identifier for task
                options.Name = "CheckExcelSheetNames";
                % artifacts the task iterates over
                % in this case, Excel files that begin with "HLR_"
                options.IterationQuery = padv.builtin.query.FindArtifacts(...
                    IncludePathRegex = "HLR_.*\.xlsx");
                % input artifacts for the task
                % in this case, the same as the iteration artifacts
                options.InputQueries = "padv.builtin.query.GetIterationArtifact";
                % where the task outputs artifacts
                options.OutputDirectory = fullfile(...
                    '$DEFAULTOUTPUTDIR$','excel_status_results');
            end
            % Calling constructor of superclass padv.Task
            obj@padv.Task(options.Name,...
                IterationQuery=options.IterationQuery,...
                InputQueries=options.InputQueries);
            obj.OutputDirectory = options.OutputDirectory;
        end
        function taskResult = run(obj,input)
            % specify results from task using padv.TaskResult
            taskResult = padv.TaskResult;
            % Get the sheet names for the sheets in the current spreadsheet
            % "input" is a cell array of task input artifacts
            a = input{1}.ArtifactAddress;
            fa = a.getFileAddress;
            sheets = sheetnames(fa);
            % Check if sheets for both "StepUp" and "StepDown" are present in
            % the spreadsheet
            if ismember("StepUp", sheets) && ismember("StepDown", sheets)
                disp('Both the "StepUp" and "StepDown" sheets are present.');
                taskResult.Status = padv.TaskStatus.Pass;
            else
                disp('Missing "StepUp" or "StepDown" sheets.');
                taskResult.Status = padv.TaskStatus.Fail;
            end
        end
    end
end

Ignore Changes to Specific Task Outputs

You can turn off change tracking for a specific artifact by specifying the Track property of the artifact address as false. The artifact address is stored in the ArtifactAddress property of a padv.Artifact object.

For example, the following custom task inherits from the built-in task DetectDesignErrors, but overrides the run method to turn off change tracking for the output report. The custom task identifies the report by iterating over each task output, checking if the artifact has the same report format as the task, and then specifying the Track property for the artifact address.

classdef MyDetectDesignErrors < padv.builtin.task.DetectDesignErrors
    % Detect design errors, but ignore changes to generated report files
    methods

        function obj = MyDetectDesignErrors(options)
            arguments
                options.Name = "MyDetectDesignErrors";
                options.Title = "My Detect Design Errors";
            end
            obj@padv.builtin.task.DetectDesignErrors(Name = options.Name);
            obj.Title = options.Title;
        end

        function taskResult = run(obj,input)

            % use DetectDesignErrors to run Design Verifier
            taskResult = run@padv.builtin.task.DetectDesignErrors(obj,input);

            % for each task output, check if it's a report
            for i = 1:length(taskResult.OutputArtifacts)
                artifact = taskResult.OutputArtifacts(i);
                artifactAddress = artifact.ArtifactAddress;
                fileAddress = artifactAddress.getFileAddress;
                if contains(fileAddress, obj.ReportFormat, IgnoreCase=true)
                    % if the task output is a report, turn off change tracking for the report
                    artifactAddress.Track = false;
                end
            end

        end
    end
end
For more information, see Exclude Files from Change Tracking in Process Advisor.

See Also

| | | |

Related Topics