Этот пример использует Simulink® и Simulink Requirements® APIs, чтобы автоматически получить и соединить структуру модели Simulink, в целях документирования проекта в Редакторе Simulink Requirements. Автоматизация также поможет восстановить или переместить данные о трассируемости требований после заменяющий или изменяющий соединенные артефакты. Использование следующих API командной строки продемонстрировано:
slreq.new
для создания нового Набора Требования
slreq.ReqSet
для добавления записей в Набор Требования
add
для добавления дочерних требований
slreq.Requirement
для заполнения - в Description
поле
slreq.createLink
для создания ссылки от SRC
к DEST
slreq.find
для определения местоположения объектов Simulink Requirements
setDestination
для повторного подключения целевого конца существующей ссылки
setSource
для перемещения существующей ссылки на новый исходный объект
isResolvedSource
для идентификации ссылок, исходный объект которых не может быть найден
slreq.show
используемый, чтобы просмотреть или источник или целевой конец данного slreq.Link
В нескольких местах мы также используем устаревший rmi
API, которые наследованы от части Requirements Management Interface (RMI) продукта SLVnV на пенсии.
Вы хотите использовать продукт Simulink Requirements, чтобы создать подробное описание вашего проекта Simulink, и вы хотите организовать свой набор Требований в иерархии, которая совпадает с вашими моделями Simulink. Вы также хотите простой способ перейти между элементами этого набора Требований и соответствующих элементов в вашем проекте.
В целях этой демонстрации рассмотрите slvnvdemo_powerwindow_vs.slx
модель спецификации, спроектированная для проверки функциональных свойств slvnvdemo_powerwindowController.slx
.
open_system('slvnvdemo_powerwindow_vs'); open_system('slvnvdemo_powerwindowController');
Мы используем устаревший API продукта VNV/RMI, rmi('getObjectsInModel',MODEL)
, получить иерархический список объектов в MODEL
, затем используйте Simulink Requirements slreq.* APIs
автоматически сгенерировать суррогат (представление) для каждой из наших моделей Simulink.
Мы можем затем предоставить связанную информацию о конструктивных требованиях в полях Description или Rational автоматически сгенерированных элементов прокси.
Ниже скрипт, который создает один Набор Требования с двумя суррогатами модели. Нижняя часть три команды обеспечивает пример как к программно временной замене Description
поле для элемента прокси, но по всей вероятности вы сделаете это в интерактивном режиме в Редакторе.
models = {'slvnvdemo_powerwindow_vs', 'slvnvdemo_powerwindowController'}; workDir = tempname; disp(['Using ' workDir ' to store generated files.']);
Using C:\Users\ahoward\AppData\Local\Temp\tp048cdc5c_b22d_44f6_bb6c_85d54e962505 to store generated files.
mkdir(workDir); addpath(workDir); for modelIdx = 1:length(models) modelName = models{modelIdx}; reqSetFile = fullfile(workDir, [modelName '.slreqx']); slProxySet = slreq.new(reqSetFile); % create separate ReqSet file with matching name open_system(modelName); % will create a proxy item for each object in this Simulink model modelNode = slProxySet.add('Id', modelName, 'Summary', [modelName ' Description']); [objHs, parentIdx, isSf, SIDs] = rmi('getobjectsInModel', modelName); for objIdx = 1:length(objHs) if parentIdx(objIdx) < 0 % top-level item is the model itself indexedReqs(objIdx) = modelNode; %#ok<SAGROW> else parentReq = indexedReqs(parentIdx(objIdx)); if isSf(objIdx) sfObj = Simulink.ID.getHandle([modelName SIDs{objIdx}]); if isa(sfObj, 'Stateflow.State') name = sf('get', objHs(objIdx), '.name'); elseif isa(sfObj, 'Stateflow.Transition') name = sf('get', objHs(objIdx), '.labelString'); else warning('SF object of type %s skipped.', class(sfObj)); continue; end type = strrep(class(sfObj), 'Stateflow.', ''); else name = get_param(objHs(objIdx), 'Name'); type = get_param(objHs(objIdx), 'BlockType'); end indexedReqs(objIdx) = parentReq.add(... 'Id', SIDs{objIdx}, 'Summary', [name ' (' type ')']); %#ok<SAGROW> end end slProxySet.save(); % save the autogenerated Requirement Set end slreq.editor(); % open editor to view the constructed Requirement Set slProxySet = slreq.find('type', 'ReqSet', 'Name', 'slvnvdemo_powerwindow_vs'); roItem = slProxySet.find('type', 'Requirement', 'Summary', 'upD (Inport)'); % will... % provide Description text for this item roItem.Description = ['Driver''s UP button should close the window all the way if...' ... ' released within 0.5 seconds'];
Теперь мы можем просмотреть структуру каждой модели в Редакторе Требования, и мы можем отредактировать поля Description, чтобы предоставить дополнительную подробную информацию о каждом элементе дизайна. То, что отсутствует, является простым способом перейти между объектами в схемах Simulink и элементами прокси/суррогатными в Simulink Requirements. Скрипт ниже демонстрирует использование slreq.createLink
API, чтобы автоматически добавить навигационные ссылки. Мы можем выбрать любой желаемый уровень гранулярности. В целях этого примера мы ограничим соединение с блоками Subsystem.
Мы можем также позволить подсветить, чтобы визуализировать, который Simulink возражает полученным навигационным ссылкам.
Переместитесь по ссылке от блока Simulink, чтобы рассмотреть соответствующий элемент прокси в Редакторе Simulink Requirements. Отметьте гиперссылку на связанный блок в Панели под Ссылками в нижней правой части.
for modelIdx = 1:length(models) modelName = models{modelIdx}; counter = 0; slProxySet = slreq.find('type', 'ReqSet', 'Name', modelName); proxyItems = slProxySet.find('type', 'Requirement'); for reqIdx = 1:numel(proxyItems) roItem = proxyItems(reqIdx); if contains(roItem.Summary, '(SubSystem)') % || contains(roItem.Summary, '(State)') sid = [modelName roItem.Id]; disp([' linking ' sid ' ..']); srcObj = Simulink.ID.getHandle(sid); link = slreq.createLink(srcObj, roItem); link.Description = 'slreq proxy item'; counter = counter + 1; end end disp(['Created ' num2str(counter) ' links for ' modelName]); rmi('highlightModel', modelName); end
linking slvnvdemo_powerwindow_vs:394 .. linking slvnvdemo_powerwindow_vs:394:224 .. linking slvnvdemo_powerwindow_vs:394:272 .. linking slvnvdemo_powerwindow_vs:394:271 .. linking slvnvdemo_powerwindow_vs:394:360 .. linking slvnvdemo_powerwindow_vs:397 .. linking slvnvdemo_powerwindow_vs:397:107 .. linking slvnvdemo_powerwindow_vs:397:300 .. linking slvnvdemo_powerwindow_vs:397:108 .. linking slvnvdemo_powerwindow_vs:397:285 .. linking slvnvdemo_powerwindow_vs:397:307 .. linking slvnvdemo_powerwindow_vs:399 .. linking slvnvdemo_powerwindow_vs:399:650 .. linking slvnvdemo_powerwindow_vs:399:214 .. linking slvnvdemo_powerwindow_vs:399:218 .. linking slvnvdemo_powerwindow_vs:399:273 .. linking slvnvdemo_powerwindow_vs:160 .. linking slvnvdemo_powerwindow_vs:160:643 .. linking slvnvdemo_powerwindow_vs:160:646 .. linking slvnvdemo_powerwindow_vs:160:590 .. linking slvnvdemo_powerwindow_vs:160:591 .. linking slvnvdemo_powerwindow_vs:160:648 .. linking slvnvdemo_powerwindow_vs:160:592 ..
Created 23 links for slvnvdemo_powerwindow_vs
linking slvnvdemo_powerwindowController:39 .. linking slvnvdemo_powerwindowController:40 .. linking slvnvdemo_powerwindowController:36 ..
Created 3 links for slvnvdemo_powerwindowController
В ходе разработки дизайн-проекта может быть потребность мигрировать на новый набор Требований. Если текущие Требования имеют ссылки, и когда существует известное правило сопоставить исходные связанные требования с соответствующими записями в новом Наборе Требования, можно хотеть автоматически переместить ссылки, если это возможно, постараться не восстанавливать все соединение вручную. Миграция существующих ссылок предпочтена по воссозданию новых ссылок, потому что вы сохраняете существующие метаданные, такие как ключевые слова, операторы объяснения, история комментария.
Чтобы быстро соединить ситуацию в качестве примера, где вы, возможно, должны переместить ссылки, мы начнем с Требований, импортированных из документа Microsoft Word, затем создадим некоторые ссылки.
Мы затем создаем некоторые ссылки, любой в интерактивном режиме (перетаскиванием в режиме Requirements Perspective) или использование API, чтобы позволить навигацию между объектами Simulink и импортированными требованиями.
Навигация от объекта Simulink сделана или через контекстное меню или одним нажатием кнопки когда в режиме Requirements Perspective.
examplesFolder = fullfile(matlabroot, 'toolbox', 'slrequirements', 'slrequirementsdemos'); docsFolder = fullfile(examplesFolder, 'powerwin_reqs'); addpath(docsFolder); % just in case externalDocName = 'PowerWindowSpecification'; externalDoc = fullfile(docsFolder, [externalDocName '.docx']); outputFile = fullfile(workDir, 'ReadOnlyImport.slreqx'); [~,~,roReqSet] = slreq.import(externalDoc, 'ReqSet', outputFile); % without extra... % arguments the default import mode is "read-only references" roReqSet.save(); roItem = roReqSet.find('CustomId', 'Simulink_requirement_item_2'); designModel = 'slvnvdemo_powerwindowController'; link1 = slreq.createLink([designModel '/Truth Table'], roItem); % link to read-only item... % in imported set link2 = slreq.createLink([designModel '/Truth Table1'], roItem); % link 2nd block to the... % same read-only item slreq.show(link1.source()); % highlight te source of 1st link slreq.show(link1.destination()); % navigate to the target of 1st link rmi('view', [designModel '/Truth Table1'], 2); % navigate 2nd link from Truth Table1... % using legacy RMI('view') API
Обратите внимание на то, что мы импортировали наш документ Word "как ссылки только для чтения", который является значением по умолчанию для slreq.import
команда, когда запущено без дополнительных аргументов. Этот режим импорта позволяет позже обновлять импортированные элементы, когда более новая версия исходного документа становится доступной. Теперь предполагаемый, что мы передумали: мы хотим, чтобы наши импортированные элементы были полностью доступны для редактирования в Редакторе Simulink Requirements, включая сложения, удаления и структурные перемещения. Несмотря на то, что можно Разблокировать и отредактировать свойства элементов, импортированных "как ссылки", вы не можете переупорядочить импортированные элементы или добавить новые единицы, и если вы удалите элемент, он вновь появится при выполнении следующего update
из модифицированного внешнего документа. Когда неограниченная возможность редактирования необходима, мы используем различный режим импорта: "как доступные для редактирования требования", путем обеспечения дополнительного AsReference
аргумент для slreq.import
команда и определение false
как значение.
Это генерирует новый Набор Требования, чтобы быть управляемым исключительно в Simulink Requirements. Нет никакой связи с исходным внешним документом, и вы свободны добавить/переместить/удалить записи по мере необходимости. Теперь вы не должны Разблокировать импортированные элементы, чтобы изменить Описание или другие свойства:
Однако существует проблема: наши ранее созданные ссылки соединяются от Simulink до исходных Ссылок только для чтения, не к позже импортированным доступным для редактирования Требованиям. Решение состоит в том, чтобы создать и запустить скрипт, который перенаправляет существующие ссылки на соответствующие элементы в недавно импортированном (доступном для редактирования) наборе. Мы используем setDestination
API, чтобы выполнить необходимые обновления.
После того, как мы циклично выполняемся по всем ссылкам в LinkSet и настраиваем затронутые ссылки, чтобы соединиться с соответствующими доступными для редактирования элементами, когда мы перешли от блока модели, правильный элемент в доступном для редактирования наборе открывается, и входящие ссылки от обоих блоков показывают.
Ниже скрипт в качестве примера, который выполняет эту задачу.
outputFile = fullfile(workDir, 'EditableImport.slreqx'); % re-import as Editable Requirements [~,~,mwReqSet] = slreq.import(externalDoc, 'ReqSet', outputFile, 'AsReference', false); mwReqSet.save(); linkSet = slreq.find('type', 'LinkSet', 'Name', designModel); % LinkSet for our design model links = linkSet.getLinks(); % all outgoing links in this LinkSet updateCount = 0; for linkIdx = 1:numel(links) link = links(linkIdx); if strcmp(link.destination.reqSet, [roReqSet.Name '.slreqx']) % if this link points to... % an item in read-only ReqSet sid = link.destination.sid; % internal identifier of linked read-only item roItem = mwReqSet.find('SID', sid); % located the linked read-only item id = roItem.Id; % document-side identifier of imported read-only item mwItem = mwReqSet.find('Id', id); % located a matching item in Editable... % Requirement Set link.setDestination(mwItem); updateCount = updateCount + 1; end end disp(['Updated ' num2str(updateCount) ' links from ' designModel]);
Updated 2 links from slvnvdemo_powerwindowController
slreq.show(link.destination()); % check updated destination of the last... % link we modified rmi('view', [designModel '/Truth Table1'], 2); % navigate again (legacy API), editable... % item selected in RE
Рассмотрите ситуацию, когда у вас есть SubSystem с большим количеством ссылок на Требования, и эта подсистема должна была быть перепроектирована или полностью заменена. Новая реализация в основном подобна, и требуется сохранить существующие ссылки, где возможный (где блоки с тем же именем существует на том же уровне иерархии структуры модели). Это позволит ограничивать ручные шаги соединения только блоками, которые не существовали в исходной реализации. Вы используете setSource
API, чтобы повторно прикрепить существующие ссылки в новых исходных объектах после заменяющий SubSystem. Обратите внимание на то, что вы не можете просто продолжить использовать старые ссылки, потому что ссылки используют уникальные персистентные независимые от сеанса идентификаторы (SIDs), чтобы сопоставить исходный объект ссылки (объект Simulink, который "владеет" ссылкой), и ваша замена, SubSystem имеет новый SIDs для каждого объекта.
Продемонстрировать использование setSource
API с нашей моделью в качестве примера, мы просто заменим два блока Таблицы истинности, которые мы соединили в предыдущем разделе с теми же точными новыми блоками. Если это сделано, ссылки становятся unresolved
, потому что новые копии Таблицы истинности имеют новый SIDs.
В Редакторе Требований нажмите Show Links и заметьте оранжевые треугольные индикаторы для всех неработающих ссылок. Существуют в общей сложности 4, потому что каждый из замененных блоков имел 2 ссылки: одна ссылка на суррогатный элемент в slvnvdemo_powerwindowController.slreqx
и другая ссылка на импортированное требование в ReadOnlyImport.slreqx
.
slreq.open('slvnvdemo_powerwindowController.slreqx')
ans = ReqSet with properties: Description: '' Name: 'slvnvdemo_powerwindowController' Filename: 'C:\Users\ahoward\AppData\Local\Temp\tp048cdc5c_b22d_44f6_bb6c_85d54e962505\slvnvdemo_powerwindowController.slreqx' Revision: 1 Dirty: 0 CustomAttributeNames: {} CreatedBy: 'ahoward' CreatedOn: 14-Jan-2021 15:22:27 ModifiedBy: 'ahoward' ModifiedOn: 14-Jan-2021 15:22:27
originalModel = 'slvnvdemo_powerwindowController'; updatedModel = 'UpdatedModel'; save_system(originalModel, fullfile(workDir, [updatedModel '.slx'])); % this also creates... % .slmx file in workDir delete_line(updatedModel, 'Mux1/1', 'Truth Table/1'); % disconnect original block delete_line(updatedModel, 'Truth Table/1', 'control/3'); % disconnect original block add_block([updatedModel '/Truth Table'], [updatedModel '/New Truth Table']); % create... % replacement block delete_block([updatedModel '/Truth Table']); % delete original block add_line(updatedModel, 'Mux1/1', 'New Truth Table/1'); % reconnect new block add_line(updatedModel, 'New Truth Table/1', 'control/3'); % reconnect new block set_param([updatedModel '/New Truth Table'], 'Name', 'Truth Table'); % restore original name delete_line(updatedModel, 'Mux4/1', 'Truth Table1/1'); % disconnect original block delete_line(updatedModel, 'Truth Table1/1', 'control/4'); % disconnect original block add_block([updatedModel '/Truth Table1'], [updatedModel '/New Truth Table1']); % create... % replacement block delete_block([updatedModel '/Truth Table1']); % delete original block add_line(updatedModel, 'Mux4/1', 'New Truth Table1/1'); % reconnect new block add_line(updatedModel, 'New Truth Table1/1', 'control/4'); % reconnect new block set_param([updatedModel '/New Truth Table1'], 'Name', 'Truth Table1'); % restore original name
Теперь мы должны выполнить итерации по всем ссылкам в новой модели, идентифицировать тех с неразрешенным источником с помощью isResolvedSource
API, затем используйте setSource
команда, чтобы зафиксировать каждую неработающую ссылку. Поскольку мы не можем использовать старый SIDs, чтобы найти необходимые новые источники ссылки, мы открываем исходную модель, чтобы обнаружить исходный блок path и имя, затем определяем местоположение соответствующего заменяющего блока в обновленной модели.
См. скрипт в качестве примера ниже. Когда вы запускаете этот скрипт, он сообщает о 4 зафиксированных ссылках. Проверяйте Представление ссылок в Редактор Simulink Requirements и подтвердите, что все ссылки теперь разрешены, нигде нет никаких индикаторов оранжевой иконки.
open_system(originalModel); updatedLinkSet = slreq.find('type', 'LinkSet', 'Name', updatedModel); links = updatedLinkSet.getLinks(); fixCount = 0; for linkIdx = 1:numel(links) link = links(linkIdx); if ~link.isResolvedSource() missingSID = link.source.id; origBlockHandle = Simulink.ID.getHandle([originalModel missingSID]); origBlockPath = getfullname(origBlockHandle); [~,blockPath] = strtok(origBlockPath, '/'); updatedBlockPath = [updatedModel blockPath]; updatedModelSID = Simulink.ID.getSID(updatedBlockPath); updatedBlockHandle = Simulink.ID.getHandle(updatedModelSID); link.setSource(updatedBlockHandle); fixCount = fixCount + 1; end end updatedLinkSet.save(); disp(['Fixed ' num2str(fixCount) ' links in ' updatedModel '.slmx']);
Fixed 4 links in UpdatedModel.slmx
К очистке после выполнения перечисленных выше шагов мы закрываем все модели и удаляем все файлы, которые были созданы скриптами в этом примере. slreq.clear
команда удалит все Требования и данные о Ссылках из памяти сеанса работы с MATLAB, чтобы не конфликтовать с тем, что вы делаете затем.
slreq.clear(); bdclose('all'); rmpath(workDir); rmpath(docsFolder); rmdir(workDir,'s'); % clear stored import location for our document to avoid prompt on rerun slreq.import.docToReqSetMap(externalDoc,'clear');