Main Content

Use Command-line API to Update or Repair Requirements Links

This example covers a set of standard situations when links between design artifacts and requirements become stale after one or more artifacts are moved or renamed. Rather then deleting broken links and creating new ones, we want to update existing links so that creation/modification history and other properties (description, keywords, comments,..) are preserved. Use of the following APIs is demonstrated:

  • slreq.find to get hold of Simulink Requirements® entries and links

  • find to locate the wanted entry in a given ReqSet

  • getLinks to query all outgoing Links in LinkSet

  • source brief information about link source

  • destination brief information about link destination

  • slreq.Link for "as stored" target info, which is different from "as resolved" Link.destination()

  • slreq.LinkSet to update link destinations when target document moved

  • slreq.ReqSet to update previously imported set when source document moved

  • updateFromDocument to update previously imported References from updated document

  • slreq.LinkSet to convert existing "direct links" to "reference links"

  • slreq.show used to view either the source or the destination end of a given slreq.Link

In a few places we also use the legacy rmi APIs that are inherited from Requirements Management Interface (RMI) part of the retired SLVnV Product.

Example Project Files

Before you begin, ensure a clean initial state by running slreq.clear command. Then type slreqCCProjectStart to open the Cruise Control Project example. This will unzip a collection of linked artifact files into a new subfolder under your MATLAB/Projects folder.

slreq.clear();
slreqCCProjectStart();

Simulink Model Linked to Requirements

We will focus on a small part of this Project's Dependency Graph: open the crs_plant.slx Simulink model, that has several links to an external Microsoft® Word document crs_reqs.docx.

open_system('crs_plant');

Navigate one of the links to open the linked document.

rmi('view', 'crs_plant/status', 1);

Word document opens to the corresponding section:

Here is how to use command-line APIs and check for links from crs_plant.slx to crs_reqs.docx.

open_system('crs_plant');
rmi('view', 'crs_plant/status', 1);
linkSet = slreq.find('type', 'LinkSet', 'Name', 'crs_plant');
links = linkSet.getLinks();
disp('Original Links to Word document:');
Original Links to Word document:
for i = 1:numel(links)
    linkTarget = links(i).getReferenceInfo();
    if contains(linkTarget.artifact, 'crs_req.docx')
        source = links(i).source;
        disp(['    found link from ' strrep(getfullname([bdroot source.id]),newline,'') ...
            ' to crs_req.docx']);
    end
end
    found link from crs_plant/Vehicle1/vehiclespeed to crs_req.docx
    found link from crs_plant/throttDrv to crs_req.docx
    found link from crs_plant/status to crs_req.docx
    found link from crs_plant/throttleCC to crs_req.docx

Navigation of Direct Links in the Presence of Imported References

Open the Simulink Requirements Editor.

slreq.editor;

You will see two Requirement Sets loaded: crs_req.slreqx and crs_req_func_spec.slreqx. The first Requirement Set is a collection of references imported from crs_req.docx, and the second was manually created in the Simulink Requirements Editor. If you now close the Word document and navigate the same link from crs_plant/status Inport block, the corresponding imported reference is highlighted in Requirements Editor, because navigation action finds the matching reference in a loaded imported Requirement Set.

You can still use the [Show in document] button to see the linked Requirement in the context of original document.

slreq.editor();
rmidotnet.MSWord.application('kill');
rmi('view', 'crs_plant/status', 1);

Use Case 1: Batch-update Links after Document Renamed

Suppose that an updated version of the requirements document is received, named crs_req_v2.docx. We now want the links in crs_plant.slx to target the corresponding sections of the updated document. For the purpose of this example, we will make a copy of the original document in same folder with a modified name. We then use slreq.LinkSet API to batch-update all links in a given LinkSet to connect with the newer copy of the document:

copyfile(fullfile(pwd, 'documents/crs_req.docx'), fullfile(pwd, 'documents/crs_req_v2.docx'));
linkSet.updateDocUri('crs_req.docx', 'crs_req_v2.docx');

Verify the Update of Matching Links

Now we can navigate the same link and confirm that the appropriate version of the external document opens. If we iterate all links as before, this confirms that all 4 links updated as intended:

rmi('view', 'crs_plant/status', 1); % updated document opens
links = linkSet.getLinks();
disp('Links to Word document after update:');
Links to Word document after update:
for i = 1:numel(links)
    source = links(i).source;
    linkTarget = links(i).getReferenceInfo();
    if contains(linkTarget.artifact, 'crs_req.docx')
        warning(['link from ' source.id ' still points to crs_req.docx']);  % should not happen
    elseif contains(linkTarget.artifact, 'crs_req_v2.docx')
        disp(['    found link from ' strrep(getfullname([bdroot source.id]),newline,' ')...
            ' to crs_req_v2.docx']);
    end
end
    found link from crs_plant/Vehicle1/vehicle speed to crs_req_v2.docx
    found link from crs_plant/throttDrv to crs_req_v2.docx
    found link from crs_plant/status to crs_req_v2.docx
    found link from crs_plant/throttleCC to crs_req_v2.docx

Navigate to Imported References After Updating Links

As demonstrated above, when imported references are available in Requirements Editor, navigating a link will select the matching reference object. However, we have just updated links for a new version of the document crs_req_v2.docx, and there are no imported references for this document. Navigation from Simulink block in the presence of Requirements Editor brings you directly to the external Word document.

To avoid this inconsistency we need to update the previously imported references for association with the updated document name. Use the slreq.ReqSet API to accomplish this task. Additionally, because the updated document may have modified Requirements, you must use updateFromDocument API to pull-in the updates for reference items stored on Simulink Requirements side. After this is done, navigating from Simulink model will locate the matching imported reference.

Find the Requirement Set with imported references. Update the source file location and find the top-level Import node.

reqSet = slreq.find('type', 'ReqSet', 'Name', 'crs_req');
reqSet.updateSrcFileLocation('crs_req.docx', 'crs_req_v2.docx'); 
importNode = reqSet.find('CustomId', 'crs_req_v2');

Update the imported references from the source file. Close Microsoft Word. Then, navigate to the updated reference in the Requirements Editor.

importNode.updateFromDocument();

rmidotnet.MSWord.application('kill');
rmi('view', 'crs_plant/status', 1);

Cleanup After Use Case 1

Discard link data changes to avoid prompts on Project close. Close the project (also cleans-up MATLAB path changes). Close Microsoft Word.

slreq.clear();                        
prj = simulinkproject(); prj.close();
rmidotnet.MSWord.application('kill'); 

Use Case 2: Batch-update Links to Fully Rely on Imported References

As demonstrated in Use Case 1 above, additional efforts are required to maintain "direct links" to external documents when documents are moved or renamed. A better workflow is to convert the existing "direct links" into "reference links", which are links that point to the imported References in *.slreqx files and no longer duplicate information about the location or name of the original document. When using this option, the external source document association is stored only in the Requirement Set that hosts the imported References.

To demonstrate this workflow, restart from the same initial point by reopening the Cruise Control Project example in a new subfolder. Copy the Simulink model to your directory and open it.

slreqCCProjectStart();
copyfile(fullfile(pwd, 'documents/crs_req.docx'), fullfile(pwd, 'documents/crs_req_v2.docx'));
open_system('crs_plant');

Find the crs_plant link set and crs_req requirement set with imported References.

linkSet = slreq.find('type', 'LinkSet', 'Name', 'crs_plant');
reqSet = slreq.find('type', 'ReqSet', 'Name', 'crs_req');

We then use slreq.LinkSet API to update all the direct links in crs_plant.slmx. Then create an array of all the links in the link set.

linkSet.redirectLinksToImportedReqs(reqSet);
links = linkSet.getLinks();

After updating the LinkSet in this way, loop over all the links to confirm the absence of "direct" links to crs_req.docx file.

disp('Check for links to original external document:');
Check for links to original external document:
counter = 0;
for i = 1:numel(links)
    linkTarget = links(i).getReferenceInfo();
    if contains(linkTarget.artifact, 'crs_req.docx')
        source = links(i).source;
        warning(['link from ' source.id ' still points to crs_req.docx']);
        counter = counter + 1;
    end
end
disp(['    Total ' num2str(counter) ' links to external document']);
    Total 0 links to external document

Navigate from the Simulink model to the updated reference.

rmi('view', 'crs_plant/status', 1);

Links to References and External Document Rename

Now, when all links point to imported References and not to the external document, traceability data remains consistent after document rename, as long as the Import node is updated for the new external document name. As in the Use Case 1, we will pretend there is an updated version of the external requirements document, by resaving our Word document with a new name. We then perform the required update for the Import node by using the same APIs as before. Now, because the links rely on imported References, and do not store information about imported document, navigation from Simulink model brings us to the updated reference, same as after performing all the steps of Use Case 1.

The Reference is now associated with the updated external document, [Show in document] button opens the updated (renamed) document, and no further adjustment on the LinkSet side is required.

Find the Requirement Set with imported references. Update the source file location and find the top-level Import node.

reqSet = slreq.find('type', 'ReqSet', 'Name', 'crs_req');
reqSet.updateSrcFileLocation('crs_req.docx', 'crs_req_v2.docx'); 
importNode = reqSet.find('CustomId', 'crs_req_v2');

Update the imported references from the source file. Then, navigate to the updated reference in the Requirements Editor.

importNode.updateFromDocument();
rmi('view', 'crs_plant/status', 1);

Cleanup After Use Case 2

Discard link data changes to avoid prompts on Project close. Close the project (also cleans-up MATLAB path changes). Close Microsoft Word.

slreq.clear();                        
prj = simulinkproject(); prj.close();
rmidotnet.MSWord.application('kill'); 

Use Case 3: Moving Linked Artifacts to a New Project

Now suppose that we are branching an existing project with linked artifacts, and we need to create a new set of renamed artifacts with all the traceability links as in the original Project. As before, we will extract the Cruise Control Project into a new subfolder, and convert the "direct links" to "reference links", as we have done in Use Case 2 above. We then go ahead and create "new versions" of the linked artifacts by resaving each one with the _v2. name.

After creating renamed copies of Simulink model, the imported external document, and the Requirement Set with the imported Requirements, there is one problem: renamed model is linked to the references in the original Requirement set, not in the renamed Requirement set.

Open the Cruise Control Project and open the crs_plant model. Find the link set for crs_plant and the requirement set crs_req.

slreqCCProjectStart();
open_system('models/crs_plant.slx');
linkSet = slreq.find('type', 'LinkSet', 'Name', 'crs_plant');
reqSet = slreq.find('type', 'ReqSet', 'Name', 'crs_req');   

Convert the direct links to reference links. Create renamed copies of the files and save them.

linkSet.redirectLinksToImportedReqs(reqSet);     
mkdir(fullfile(pwd, 'copied'));
save_system('crs_plant', fullfile(pwd, 'copied/crs_plant_v2.slx'));  
reqSet.save(fullfile(pwd, 'copied/crs_req_v2.slreqx'));              
copyfile('documents/crs_req.docx', 'copied/crs_req_v2.docx');        

Associate the renamed requirement set with the renamed document. Find the top level Import node.

reqSet.updateSrcFileLocation('crs_req.docx', fullfile(pwd, 'copied/crs_req_v2.docx'));
importNode = reqSet.find('CustomId', 'crs_req_v2');

Ensure the contents in the renamed requirement set match the contents of the renamed document by updating the imported References. Navigate from the renamed Simulink model to the item in the renamed requirement set. The old item in the original requirement set is highlighted, which is incorrect.

importNode.updateFromDocument();
rmi('view', 'crs_plant_v2/status', 1);

Update Links in Renamed Source to Use the Renamed Destination as the Target

Similarly to Use Case 1, we can use LinkSet.updateDocUri(OLD, NEW) API to update links in crs_plant_v2.slmx to use the renamed Requirement Set crs_req_v2.slreqx as the link target, instead of the original crs_req.slreqx. Once this is done, navigate again from the block in the renamed model. The requirement in the renamed Requirement Set is selected, and the links in the Links pane at bottom-right are resolved.

Find the link set for the new copy of the model, crs_plant_v2. Update the name of the requirement set linked with the new copy of the model. Navigate from the renamed Simulink model to the item in the renamed requirement set. This time, it highlights the correct item.

linkSet = slreq.find('type', 'LinkSet', 'Name', 'crs_plant_v2');
linkSet.updateDocUri('crs_req.slreqx', 'crs_req_v2.slreqx');
rmi('view', 'crs_plant_v2/status', 1);

Cleanup After Use Case 3

Clear the open requirement sets and link sets, and close the open models and projects without saving changes. Close Microsoft Word.

slreq.clear();                        
close_system('crs_plant_v2');         
prj = simulinkproject(); prj.close(); 
rmidotnet.MSWord.application('kill');