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. |
InputQueries
| Inputs to the task |
InputDependencyQuery | Artifacts that the task inputs depend on Typically, you specify
|
OutputDirectory | Directory where the task outputs artifacts If you do not specify
|
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 assess 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.Pass = 1; taskResult.Values.Warn = 2; taskResult.Values.Fail = 3; |
|
Additionally, you can also 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