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 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 demostrated:

In a few places we also use the legacy RMI(ARGS) 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 slreqProjectStart 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: consider crs_plant.slx Simulink model (click to open), that has several links to an external Microsoft® Word document crs_reqs.docx.

Navigate one of the links to open the linked document.

Word document opens to the correct 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:');
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
Original Links to Word document:
    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 Simulink Requirements Editor. You will see two Requirement Sets loaded: crs_req.slreqx and crs_req_funct_spec.slreqx. The first Requirement Set is a collection of references imported from crs_req.docx, and the 2nd was manually created in 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 documnt. 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 LinkSet.updateDocUri(ORIG_DOC, NEW_DOC) 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 Correct Update of Matching Links

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

rmi('view', 'crs_plant/status', 1); % updated document opens
links = linkSet.getLinks();
disp('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
Links to Word document after update:
    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 presense of Requirements Editor brings you directly to the external Word document.

To avoid this inconsistency we need to update the previously imported references for assotiation with the updated document name. We use the ReqSet.updateSrcFileLocation() API to accomplish this task. Additionally, because the updated document may have modified Requirements, we must use importNode.updateFromDocumet() API to pull-in the updates for reference items stored on Simulink Requirements sidde. After this is done, navigating from Simulink model will locate the correct matching imported reference.

reqSet = slreq.find('type', 'ReqSet', 'Name', 'crs_req'); % ReqSet with imported References
reqSet.updateSrcFileLocation('crs_req.docx', 'crs_req_v2.docx');
importNode = reqSet.find('CustomId', 'crs_req_v2');  % top-level Import node
importNode.updateFromDocument();
rmidotnet.MSWord.application('kill');  % close Word application
rmi('view', 'crs_plant/status', 1);    % navigate to highlight the updated reference in Requiremets Editor

Cleanup After USE CASE 1

slreq.clear();                         % discard link data changes to avoid prompts on Project close
prj = simulinkproject(); prj.close();  % close the Simulink Project (also cleans-up MATLAB path changes)
rmidotnet.MSWord.application('kill');  % close MS Word application

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 assocation is stored only in the Requirement Set that hosts the imported References. To demonstrate this workflow we restart from the same initial point by reopening the Cruise Control Project example in a new subfolder. We then use linkSet.redirectLinksToImportedReqs(reqSet) API to update all the direct links in crs_plant.slmx. After updating the LinkSet in this way, we loop over all the links to confirm the absence of "direct" links to crs_req.docx file.

slreqCCProjectStart();
copyfile(fullfile(pwd, 'documents/crs_req.docx'), fullfile(pwd, 'documents/crs_req_v2.docx'));
open_system('crs_plant');     % open the Simulink model
linkSet = slreq.find('type', 'LinkSet', 'Name', 'crs_plant'); % LinkSet for crs_plant.slx
reqSet = slreq.find('type', 'ReqSet', 'Name', 'crs_req');     % ReqSet with imported References
linkSet.redirectLinksToImportedReqs(reqSet);      % Convert all direct links to reference links
links = linkSet.getLinks();
disp('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']);  % should not happen
        counter = counter + 1;
    end
end
disp(['    Total ' num2str(counter) ' links to external document']);  % should be 0 direct links
rmi('view', 'crs_plant/status', 1); % navigate from Simulink model to updated Reference
Check for links to original external document:
    Total 0 links to external document

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 correctly 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.

reqSet = slreq.find('type', 'ReqSet', 'Name', 'crs_req'); % ReqSet with imported References
reqSet.updateSrcFileLocation('crs_req.docx', 'crs_req_v2.docx');
importNode = reqSet.find('CustomId', 'crs_req_v2');  % top-level Import node
importNode.updateFromDocument();
rmi('view', 'crs_plant/status', 1);   % navigates to a Reference in updated Requirement Set

Cleanup After USE CASE 2

slreq.clear();                         % discard link data changes to avoid prompts on Project close
prj = simulinkproject(); prj.close();  % close the Simulink Project (also cleans-up MATLAB path changes)
rmidotnet.MSWord.application('kill');  % close MS Word application

USE CASE 3: Moving Linked Artifacts to a New Project

Now suppose that we are branching an exisiting project with linked artifacts, and we need to create a new set of renamed aftifacts 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.

slreqCCProjectStart();
open_system('models/crs_plant.slx');
linkSet = slreq.find('type', 'LinkSet', 'Name', 'crs_plant'); % LinkSet for crs_plant.slx
reqSet = slreq.find('type', 'ReqSet', 'Name', 'crs_req');     % ReqSet with imported References
linkSet.redirectLinksToImportedReqs(reqSet);      % Convert all direct links to reference links
mkdir(fullfile(pwd, 'copied'));
save_system('crs_plant', fullfile(pwd, 'copied/crs_plant_v2.slx'));  % this also creates crs_plant_v2.slmx LinkSet file
reqSet.save(fullfile(pwd, 'copied/crs_req_v2.slreqx'));              % new ReqSet copy to use with crs_plant_v2.slx
copyfile('documents/crs_req.docx', 'copied/crs_req_v2.docx');        % new document copy to use with crs_req_v2.slreqx
reqSet.updateSrcFileLocation('crs_req.docx', fullfile(pwd, 'copied/crs_req_v2.docx'));  % associate renamed ReqSet with renamed Document
importNode = reqSet.find('CustomId', 'crs_req_v2');  % top-level Import node
importNode.updateFromDocument();                     % ensure contents in renamed ReqSet match the contents in renamed Document
rmi('view', 'crs_plant_v2/status', 1); % navigation from renamed Simulink model: the old item in the original ReqSet is highlighted <- WRONG

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 correct requirement in the renamed Requirement Set is selected, and the links in the Links pane at bottom-right are resolved.

linkSet = slreq.find('type', 'LinkSet', 'Name', 'crs_plant_v2'); % LinkSet for crs_plant_v2.slx (new copy)
linkSet.updateDocUri('crs_req.slreqx', 'crs_req_v2.slreqx');     % crs_plant_v2.slx should link with crs_req_v2.slreqx
rmi('view', 'crs_plant_v2/status', 1); % navigation from renamed Simulink model: correct item in renamed ReqSet is highlighted

Cleanup After USE CASE 3

slreq.clear();                         % discard link data changes to avoid prompts on Project close
close_system('crs_plant_v2');          % this Model is not in Project, hence need to close separately
prj = simulinkproject(); prj.close();  % close the Simulink Project (also cleans-up MATLAB path changes)
rmidotnet.MSWord.application('kill');  % close MS Word application