Этот пример использует Simulink® и Simulink Requirements® APIs, чтобы автоматически получить и соединить структуру модели Simulink, в целях документирования проекта в Редакторе Simulink Requirements. Автоматизация также поможет восстановить или переместить данные о трассируемости требований после заменяющий или изменяющий соединенные артефакты. Использование следующих API командной строки продемонстрировано:
slreq.new(REQSETNAME)
для создания нового Набора Требования
ReqSet.add(NAME,VALUE)
для добавления записей в Набор Требования
Requirement.add(NAME,VALUE)
для добавления дочерних требований
Requirement.Description(TEXT)
для заполнения - в Description
поле
slreq.createLink(SRC,DEST)
для создания ссылки от SRC
к DEST
slreq.find('type',TYPENAME)
для определения местоположения объектов Simulink Requirements
link.setDestination(DEST)
для повторного подключения целевого конца существующей ссылки
link.setSource(SOURCE)
для перемещения существующей ссылки на новый исходный объект
link.isResolvedSource()
для идентификации ссылок, исходный объект которых не может быть найден
slreq.show()
используемый, чтобы просмотреть или источник или целевой конец данного slreq.Link
В нескольких местах мы также используем устаревший RMI(ARGS)
API, которые наследованы от части Requirements Management Interface (RMI) продукта SLVnV на пенсии.
Вы хотите использовать продукт Simulink Requirements, чтобы создать подробное описание вашего проекта Simulink, и вы хотите к организованному свой набор Требований в иерархии, которая совпадает с вашими моделями Simulink. Вы также хотите простой способ перейти между элементами этого набора Требований и соответствующих элементов в вашем проекте.
В целях этой демонстрации считайте slvnvdemo_powerwindow_vs.slx модель спецификации спроектированной для проверки функциональных свойств slvnvdemo_powerwindowController.slx.
Мы используем устаревший API продукта VNV/RMI, rmi('getObjectsInModel',MODEL)
, чтобы получить herarchical список объектов в МОДЕЛИ, затем используйте Simulink Requirements slreq.*
API, чтобы автоматически сгенерировать суррогат (представление) для каждой из наших моделей Simulink.
Мы можем затем предоставить связанную информацию о конструктивных требованиях в полях Descritpion или Rational автоматически сгенерированных элементов прокси.
Ниже скрипт, который создает один Набор Требования с двумя суррогатами модели. Нижняя часть три команды обеспечивает пример как к программно временной замене Description
поле для элемента прокси, но по всей вероятности вы сделаете это в интерактивном режиме в Редакторе.
models = {'slvnvdemo_powerwindow_vs', 'slvnvdemo_powerwindowController'}; workDir = tempname; disp(['Using ' workDir ' 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 iteself 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';
Using C:\Users\astarovo\AppData\Local\Temp\tpa1d56261_e0a7_4d8c_94c2_52b7abc0e7ef to store generated files.
Теперь мы можем просмотреть структуру каждой модели в Редакторе Требования, и мы можем отредактировать поля Description, чтобы предоставить дополнительную подробную информацию о каждом элементе дизайна. То, что отсутствует, является простым способом перейти между объектами в схемах Simulink и элементами прокси/суррогатными в Simulink Requirements. Скрипт ниже демонстрирует использование slreq.createLink(SRC,DEST)
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(DOCFILEPATH)
команда, когда запущено без дополнительных аргументов. Этот режим импорта позволяет позже обновлять импортированные элементы, когда более новая версия исходного документа становится доступной. Теперь предполагаемый, что мы передумали: мы хотим, чтобы наши импортированные элементы были полностью доступны для редактирования в Редакторе Simulink Requirements, включая сложения, удаления и структурные перемещения. Несмотря на то, что можно Разблокировать и отредактировать свойства элементов, импортированных "как ссылки", вы не можете переупорядочить импортированные элементы или добавить новые единицы, и если вы удалите элемент, он вновь появится при выполнении следующего update
из модифицированного внешнего документа. Когда неограниченная возможность редактирования необходима, мы используем различный режим импорта: "как доступные для редактирования требования", путем обеспечения дополнительного AsReference
аргумент для slreq.import()
команда и определение false
как значение.
Это генерирует новый Набор Требования, чтобы быть управляемым исключительно в Simulink Requirements. Нет никакой связи с исходным внешним документом, и вы свободны добавить/переместить/удалить записи по мере необходимости. Теперь вы не должны Разблокировать импортированные элементы, чтобы изменить Описание или другие свойства:
Однако существует проблема: наши ранее созданные ссылки соединяются от Simulink до исходных Ссылок только для чтения, не к более недавно импортированным доступным для редактирования Требованиям. Решение состоит в том, чтобы создать и запустить скрипт, который перенаправляет существующие ссылки на соответствующие элементы в недавно импортированном (доступном для редактирования) наборе. Мы используем link.setDestination(DEST)
API, чтобы выполнить необходимые обновления.
После того, как мы циклично выполняемся по всем ссылкам в LinkSet и настраиваем затронутые ссылки, чтобы соединиться с соответствующими доступными для редактирования элементами, когда мы перешли от блока модели, правильный элемент в доступном для редактирования наборе открывается, и входящие ссылки от обоих блоков показывают.
Ниже скрипт в качестве примера, который выполняет эту задачу.
outputFile = fullfile(workDir, 'EditableImport.slreqx'); [~,~,mwReqSet] = slreq.import(externalDoc, 'ReqSet', outputFile, 'AsReference', false); % re-import as Editable Requirements 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]); 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
Updated 2 links from slvnvdemo_powerwindowController
Рассмотрите sitution, когда у вас есть SubSystem с большим количеством ссылок на Требования, и эта подсистема должна была быть перепроектирована или полностью заменена. Новая реализация в основном подобна, и требуется сохранить существующие ссылки, где возможный (где блоки с тем же именем существует на том же уровне иерархии структуры модели). Это позволит ограничивать ручные шаги соединения только блоками, которые не существовали в исходной реализации. Вы используете link.setSource(SRC)
API, чтобы повторно прикрепить существующие ссылки в новых исходных объектах после заменяющий SubSystem. Обратите внимание на то, что вы не можете просто продолжить использовать старые ссылки, потому что ссылки используют уникальные персистентные независимые от сеанса идентификаторы (SIDs), чтобы сопоставить исходный объект ссылки (объект Simulink, который "владеет" ссылкой), и ваша замена, SubSystem имеет новый SIDs для каждого объекта.
К demostrate использование link.setSource(SRC)
API с нашей моделью в качестве примера, мы просто заменим два блока Таблицы истинности, которые мы соединили в предыдущем разделе с теми же точными новыми блоками. Если это сделано, ссылки становятся unresolved
, потому что новые копии Таблицы истинности имеют новый SIDs.
Переключитесь на Представление ссылок в Редакторе Требований и заметьте оранжевые треугольные индикаторы для всех неработающих ссылок. Существуют в общей сложности 4, потому что каждый из замененных блоков имел 2 ссылки: одна ссылка на суррогатный элемент в slvnvdemo_powerwindowController.slreqx
и другая ссылка на импортированное требование в ReadOnlyImport.slreqx
.
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 oritinal 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
Теперь мы должны выполнить итерации всех ссылок в новой модели, идентифицировать тех с неразрешенным источником с помощью link.isResolvedSource()
API, затем используйте link.setSource(SRC)
команда, чтобы зафиксировать каждую неработающую ссылку. Поскольку мы не можем использовать старый 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'); slreq.import.docToReqSetMap(externalDoc,'clear'); % clear stored import location for our document to avoid prompt on rerun