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:
For basic MATLAB® script execution — Use the
addTask
function to create a new task and use theAction
argument to specify a function handle for a function that runs the script. See Custom Task That Runs Existing Script.For more complex tasks — Create a MATLAB class that inherits from either one of the Built-In Tasks or the superclass
padv.Task
and then override class properties and methods to fit your needs. See Custom Task for Specialized Functionality and Example Custom Tasks. To view the source code for a built-in task, use theopen
function.
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.
Custom Task for Specialized Functionality
To create a task that performs a custom functionality, you need to:
Create a new MATLAB class.
Inherit from either a built-in task or the superclass
padv.Task
.Specify the task name and, optionally, other task properties.
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.
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.
Property | Description |
---|---|
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. |
InputQueries | Inputs to the task. |
InputDependencyQuery | Artifacts that the task inputs depend on. Typically, you specify 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 |
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. |
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 Code | Appearance in Process Advisor |
---|---|
taskResult.Status = padv.TaskStatus.Pass | |
taskResult.Status = padv.TaskStatus.Fail | |
taskResult.Status = padv.TaskStatus.Error | |
taskResult.OutputPaths=string(... fullfile("PA_Results","myFile.txt")); | |
taskResult.Values.Fail = 3; taskResult.Values.Warn = 2; taskResult.Values.Pass = 1; |
Task results support three approaches to setting the task result values.
Approach | Use |
---|---|
Directly control the task result values by using the taskResult.Values.Fail = 3; taskResult.Values.Warn = 2; taskResult.Values.Pass = 1; |
|
Set the You can set the setComplianceCount(taskResult,"NonCompliant",3); setComplianceCount(taskResult,"Warning",2); setComplianceCount(taskResult,"Compliant",1); |
|
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 You can create assessments by using % --- 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); |
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. |
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
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.
Add task tools that integrate custom apps and commands into Process Advisor. For more information, see
padv.TaskTool
.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.
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.
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.
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.
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
See Also
addTask
| padv.ProcessModel
| padv.Task
| Process Advisor | runprocess