Main Content

使用转换表维护库模块的兼容性

如果模型是使用库模块的旧版本保存的,转换表允许您检查对库中模块的更改会不会对模型造成破坏。如果您更改库的路径或名称,或者添加、删除或重命名库模块中的参数,转换表可以帮助您保持库模块的兼容性。有关详细信息,请参阅创建自定义库

创建一个转换表条目,用于将旧库模块映射到新库模块。打开模型时,旧库模块的链接会自动更新,并且不会丢失数据或功能。在您打开模型、关闭模型或执行 add_blockreplace_block 命令时,会执行转换表。

当进行了以下操作时, 需要在转换表中创建相应的条目:

  • 重命名模块。

  • 将模块从一个库移至另一个库。

  • 向模块中添加或从其中删除参数。

  • 拆分模块的功能。

为模块重命名创建转换表条目

假设有如下示例,其中库 testlib 中模块 ss1 重命名为 ss2

  1. 打开锁定的库 Lib1

  2. 在“库”窗口中,在选项卡上,点击锁定的库。现在库已解锁,可以进行编辑。

  3. 建模选项卡上,点击库属性库属性对话框随即打开。

  4. 点击转换表选项卡。

  5. 点击“添加新条目”。转换表中将添加一个新行。

  6. 旧模块路径中指定旧模块的名称,在新模块路径中指定对应的新模块路径。如果不知道路径,请选择模型中的模块,然后点击获取 gcb 按钮。模块的路径会自动填充到对应的列中。

将在转换表中创建旧路径到新路径的映射。现在,打开一个包含库链接的模型。模型中旧库模块的链接会自动更新。您可以使用表上方的搜索栏来过滤表内容。您可以按升序或降序对列进行排序。您也可以按值对列进行分组。

为库模块分配版本号

版本列中,您可以为库模块指定版本号。

如果旧模块名称与新模块名称相同,转换表会自动填充版本号。库版本 (LibraryVersion) 的初始值从创建库链接时库的 ModelVersion 属性派生而来。对库模块的任何后续更新都会更新库版本,使其与库的模型版本匹配。

版本号:

  • 必须为数值。

  • 当新旧模块路径相同时,格式必须为 <major_version>.<minor_version>

  • 包含的圆点不能超过一个。例如,版本号 1.3 是可以接受的。版本号 1.3.1 则不行。

  • 当您使用转换表将库模块从一个库移到另一个库时,版本号的格式并不重要。

将模块从一个库移至另一个库

假设有如下示例,其中模块 ss1testlib1 移至 testlib2。这种情况的转换表条目如下所示:

moving block from one library to another

添加或删除模块参数

假设有如下示例,库 testlibss1Gain 参数被删除,然后添加了另外两个参数 Initial conditionDelay length

add or remove parameter

为了处理此变化,我们使用变换函数。变换函数可更正新旧库链接之间的参数不匹配,从而确保库链接仍然有效。您可以使用 MATLAB® 路径中的 .m 函数文件定义一个变换函数,然后在转换表变换函数列中调用该函数。

链接模块以名称-值参量形式包含模块参数的实例,以实例化该模块。此实例数据作为 InstanceData 与转换表条目一起传递给变换函数。此数据存储为 struct 字段。

当您为一个库模块创建不同的版本时,会在 InstanceData 中添加或删除参数。在此示例中,删除 增益 参数之前以及添加初始条件和延迟长度后的 InstanceData 如下所示

之前:

'RTWMemSecFuncInitTerm' 'Inherit from model'
'RTWMemSecFuncExecute' 'Inherit from model'
'RTWMemSecDataConstants' 'Inherit from model'
'RTWMemSecDataInternal' 'Inherit from model'
'RTWMemSecDataParameters' 'Inherit from model'
'ContentPreviewEnabled' 'on'
'Gain' '0'

之后:

'RTWMemSecFuncInitTerm'    'Inherit from model'
'RTWMemSecFuncExecute'    'Inherit from model'
'RTWMemSecDataConstants'    'Inherit from model'
'RTWMemSecDataInternal'    'Inherit from model'
'RTWMemSecDataParameters'    'Inherit from model'
'ContentPreviewEnabled'    'on'
'DelayLength'    '1'
'InitialCondition'    '0' 

变换函数的语法如下。

function outData = TransformationFcn(inData)

在此函数语法中:

  • inData 是一个结构体,包含字段 ForwardingTableEntryInstanceDataForwardingTableEntry 也是一个结构体。

    • ForwardingTableEntry 包含模块的旧名称、新名称、旧路径和新路径。请参考代码来访问 ForwardingTableEntry 的成员。

  • outData 是包含 NewInstanceDataNewBlockPath 字段的结构体:

要在模块中添加或删除参数,请执行以下操作:

  1. 获取实例数据和转换表条目。

  2. 提取名称-值对组和模块名称的列表。

  3. 获取新旧模块名称。

  4. 检查新旧模块名称是否相同。如果是,则提取模块的版本。

  5. 检查是否旧版本为 1.1、新版本为 1.2。然后,删除 Gain 参数,并添加参数 Delay lengthInitial condition

注意

您的库可以有多个变换函数文件。然而,为了高效维护,建议每个库有一个变换函数文件。

要添加或删除参数,请使用以下变换函数。

function [outData] = txFcnTestlib(inData)
    outData.NewBlockPath = '';
    outData.NewInstanceData = [];
    
    % Get linked block instance data and forwarding entry for which
    % function is called
    instanceData = inData.InstanceData;
    forwardingTableEntry = inData.ForwardingTableEntry;
    
    % Get list of name value pair and block names
    [ParamNames{1:length(instanceData)}] = instanceData.Name;
    
    % Get old and new block paths or simply names
    oldName = forwardingTableEntry.('__slOldName__');
    newName = forwardingTableEntry.('__slNewName__');
    
    % If block names are same and its subsys block in the lib
    if strcmp(oldName, newName)
        % Get old and new block versions from forwarding table
        oldVer = forwardingTableEntry.('__slOldVersion__');
        newVer = forwardingTableEntry.('__slNewVersion__');

        % Each forwarding entry with different version can have separate
        % entry. Here, for one single entry in forwarding table i.e.
        % 1.2->1.3 or 1.3->2.0, we have separate transformation making one
        % TX Fcn for one library or block.
        if strcmp(oldName, 'testlib/ss1')
            % Forwarding with same block name ss1
            if oldVer == '1.1' && newVer == '1.2'
                % Remove gain param
                if (ismember('Gain',ParamNames))
                    for (i = 1:length(instanceData))
                        if (strcmp(instanceData(i).Name,'Gain') == true)
                            instanceData(i) = [];
                            break;
                        end
                    end
                end
                
                % Add delay length param
                if (ismember('DelayLength',ParamNames))
                    for (i = 1:length(instanceData))
                        if (strcmp(instanceData(i).Name,'Value') == true)
                            instanceData(i).Value = '5';
                            break;
                        end
                    end
                else
                    instanceData(end+1).Name = 'DelayLength';
                    instanceData(end).Value = '1';
                end
                
                % Add initial condition param
                if (ismember('InitialCondition',ParamNames))
                    for (i = 1:length(instanceData))
                        if (strcmp(instanceData(i).Name,'InitialCondition') == true)
                            instanceData(i).Value = '0';
                            break;
                        end
                    end
                else
                    instanceData(end+1).Name = 'InitialCondition';
                    instanceData(end).Value = '0';
                end
            elseif (oldVer == '1.2' && newVer == '1.3')
                % Do version 1.2 to 1.3 specific changes
            elseif (oldVer == '1.3' && newVer == '2.0')
                % Do version 1.3 to 2.0 specific changes
            else
                % Do default changes not applicable to any version if
                % required
            end
        elseif strcmp(oldName, 'testlib/ss2')
            % Forwarding for block ss2  with version upgrade
        end
    elseif strcmp(oldName, 'testlib/oldblk') && strcmp(newName, 'testlib2/newblk')
        % Block moved to new library or just block is renamed within same
        % library, no version is checked here. Do transformation is
        % required i.e. is block has been modified while renaming or moving
        % to new lib.
    elseif strcmp(oldName, 'testlib/blkX') && isempty(newName)
        % We are splitting the block into 2 different blocks. Hence, kept
        % newName empty in fwd table. Using one of the block param from its
        % instance data (which is name-value pair of all block params) to
        % decide how and what to divide.
        
        % Get the index of param we are interested in i.e. param X
        i = find(contains({instanceData.Name},'X'), true);
        
        % Based on its value, do block splitting
        switch (instanceData(i).Value)
            case 'op1'
                newName = 'newlibX/blkX';
                % Remove existing param or add new param or modify existing
                % params etc.
            case 'op2'
                newName = 'newlibY/blkX';
            otherwise
                newName = oldName;
        end
    end
    
    % Return new instance data and new block path
    outData.NewInstanceData = instanceData;
    outData.NewBlockPath = newName;
end

拆分模块功能

拆分模块的功能涉及添加、删除或修改模块的现有参数然后将其添加到新模块。要拆分模块的功能,请执行下列步骤:

  1. newName 参量留为空。

  2. 获取参数的索引和值。

  3. 根据值拆分模块。

  4. 如果需要,将 NewBlockPath 与更新后的 InstanceData 一起返回。

创建封装参数别名

如果重命名封装参数,则必须确保使用旧参数名称的现有 MATLAB 脚本能够继续工作。要检查兼容性,您可以为封装参数名称创建别名。别名允许您在库模块中更改封装参数的名称,而无需在现有模型中重新创建模块链接。

假设有一个包含编辑参数的封装模块。此编辑参数的封装参数名称为 p1

MaskObj = Simulink.Mask.get(gcb)); 
hEdit = MaskObj.getParameter('p1');

hEdit= 
% MaskParameter with properties:

           Type: 'edit'
    TypeOptions: {0×1 cell}
           Name: 'p1'
         Prompt: 'p1'
          Value: '0'
       Evaluate: 'on'
        Tunable: 'on'
      NeverSave: 'off'
         Hidden: 'off'
        Enabled: 'on'
        Visible: 'on'
        ToolTip: 'on'
       Callback: ''
          Alias: ''

请注意,编辑封装参数没有别名。要为封装参数添加别名,请为 Alias 封装参数属性设置值。

MaskObj.Alias = 'pa'

您可以使用封装参数名称或别名对封装参数执行函数调用。例如,您可以使用 set_param(gcb, 'p1, '10)(封装参数名称)或 set_param(gcb, 'pa, '10)(封装参数别名)为编辑封装参数设置值。

相关主题