Отображение каналов MDF к модели Simulink Input port

Этот пример показывает вам, как программно сопоставить каналы MDF и использовать их данные через входные порты модели Simulink. Это выполняет сбор имен входного порта модели Simulink и коррелирует их к содержимому данного MDF-файла. Рычажное устройство между ними затем создается, который использует данные о канале, полученные от MDF-файла, когда модель запускается.

Получите детали модели

Задайте имя модели в качестве примера и откройте его.

mdlName = 'ModelForMDFInput';
open_system(mdlName);

Используйте функцию createInputDataset, чтобы получить полную информацию о модели и ее входных параметрах.

dsObj = createInputDataset(mdlName)
dsObj = 
Simulink.SimulationData.Dataset '' with 2 elements

                             Name      BlockPath 
                             ________  _________ 
    1  [1x1 timeseries]      triangle  ''       
    2  [1x1 struct    ]      busInput  ''       

  - Use braces { } to access, modify, or add elements using index.

Получите модель имена Input port

Эта модель имеет и шину и отдельный входной порт. helperGetMdlInputNames функция демонстрирует, как получить имя всех входных параметров модели независимо от того, как они заданы в модели.

mdlInputNames = helperGetMdlInputNames(mdlName)
mdlInputNames = 4×1 string array
    "triangle"
    "pwm"
    "pwm_level"
    "pwm_filtered"

Исследуйте MDF-файл

Теперь, когда у вас есть имена входного порта модели, вы видите, какие каналы существуют в MDF-файле, таким образом, можно попытаться совпадать с ними. channelList функция функции MDF предоставляет быстрый доступ к доступным каналам, существующим в MDF-файле.

mdfName = 'CANape.MF4';
mdfObj = mdf(mdfName);
mdfChannelInfo = channelList(mdfObj)
mdfChannelInfo=120×9 table
            ChannelName            ChannelGroupNumber    ChannelGroupNumSamples    ChannelGroupAcquisitionName    ChannelGroupComment    ChannelDisplayName    ChannelUnit    ChannelComment                    ChannelDescription                 
    ___________________________    __________________    ______________________    ___________________________    ___________________    __________________    ___________    ______________    ___________________________________________________

    "ampl"                                 2                       199                        100ms                      100ms                   ""               100ms           100ms         "Amplitude of channel 1-3"                         
    "channel1"                             2                       199                        100ms                      100ms                   ""               100ms           100ms         "FLOAT demo signal (sine wave)"                    
    "Counter_B4"                           1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "Single bit demo signal (bit from a byte shifting)"
    "Counter_B5"                           1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "Single bit demo signal (bit from a byte shifting)"
    "Counter_B6"                           1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "Single bit demo signal (bit from a byte shifting)"
    "Counter_B7"                           1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "Single bit demo signal (bit from a byte shifting)"
    "map1_8_8_uc_measure"                  1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
    "map1_8_8_uc_measure[0][0]"            1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
    "map1_8_8_uc_measure[0][1]"            1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
    "map1_8_8_uc_measure[0][2]"            1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
    "map1_8_8_uc_measure[0][3]"            1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
    "map1_8_8_uc_measure[0][4]"            1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
    "map1_8_8_uc_measure[0][5]"            1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
    "map1_8_8_uc_measure[0][6]"            1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
    "map1_8_8_uc_measure[0][7]"            1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
    "map1_8_8_uc_measure[1][0]"            1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
      ⋮

Создайте таблицу, чтобы управлять элементами интереса

Используйте таблицу, чтобы сопоставить входные порты модели с каналами MDF.

channelTable = table();
channelTable.PortNames = mdlInputNames;
n = size(channelTable.PortNames,1);
channelTable.ChGrpNum = NaN(n,1);
channelTable.ChNameActual = strings(n,1);
channelTable
channelTable=4×3 table
      PortNames       ChGrpNum    ChNameActual
    ______________    ________    ____________

    "triangle"          NaN            ""     
    "pwm"               NaN            ""     
    "pwm_level"         NaN            ""     
    "pwm_filtered"      NaN            ""     

Выполните Input port, чтобы образовать канал, соответствуя

helperReportChannelInfo функционируйте ищет MDF-файл названия канала, которые совпадают с именами входного порта модели. Когда найдено, детали канала зарегистрированы в таблице. А именно, номер группы канала, где данный канал находится в файле и его фактическом заданном имени. Обратите внимание на то, что фактические названия канала не являются точными совпадениями именам порта модели. В этом примере название канала, соответствующее, выполняется нечувствительное к регистру и игнорирует символы подчеркивания. Этот алгоритм может быть адаптирован по мере необходимости на основе специализированных критериев соответствия.

channelTable = helperReportChannelInfo(channelTable, mdfChannelInfo)
channelTable=4×3 table
      PortNames       ChGrpNum    ChNameActual 
    ______________    ________    _____________

    "triangle"           1        "Triangle"   
    "pwm"                1        "PWM"        
    "pwm_level"          1        "PWM_Level"  
    "pwm_filtered"       1        "PWMFiltered"

Заполните объект набора данных Simulink с данными о канале MDF

Объект набора данных создал, ранее содержит и один объект timeseries и структуру объектов timeseries. Это делает данные о присвоении назад им несколько сложными. Вещи иметь в виду включают:

  • При определении 'TimeSeries' как типа возврата от функции чтения MDF необходимо вызвать чтение отдельно для каждого канала.

  • Поскольку объект набора данных имеет отличающиеся элементы (скалярный timeseries и скалярная структура объектов timeseries), необходимо вручную управлять набором и убедиться, что вы пишете в правильное местоположение.

for ii = 1:dsObj.numElements
    switch ii
        case {1} % [1x1 timeseries], triangle
            % Read the input port data from the MDF file one channel at a time.
            mdfData = read(mdfObj, channelTable.ChGrpNum(ii), channelTable.ChNameActual(ii), 'OutputFormat', 'TimeSeries');
            % Populate the dataset object.
            dsObj{ii} = mdfData;
            
        case {2} % [1x1 struct], busInput
            for jj = 1:numel(fieldnames(dsObj.getElement(ii)))
                % Read the input port data from the MDF file one channel at a time.
                mdfData = read(mdfObj, channelTable.ChGrpNum(jj+1), channelTable.ChNameActual(jj+1), 'OutputFormat', 'TimeSeries');
                % Populate the dataset object.
                dsObj{ii}.(channelTable.PortNames{jj+1}) = mdfData;                
            end
    end
end
dsObj
dsObj = 
Simulink.SimulationData.Dataset '' with 2 elements

                             Name      BlockPath 
                             ________  _________ 
    1  [1x1 timeseries]      Triangle  ''       
    2  [1x1 struct    ]      busInput  ''       

  - Use braces { } to access, modify, or add elements using index.

Включите набор данных как вход к модели Simulink

set_param(mdlName, 'LoadExternalInput', 'on');
set_param(mdlName, 'ExternalInput', 'dsObj');

Запустите модель

После выполнения модели обратите внимание, что данные о канале MDF правильно сопоставляют с обозначенными входными портами и графиками через Simulink как ожидалось.

open_system(mdlName);
bp = find_system(mdlName, 'BlockType', 'Scope');
open_system(bp);
pause(1)
set_param(mdlName, 'SimulationCommand', 'start');

Функции помощника

function mdlInputNames = helperGetMdlInputNames(mdlName)
% helperGetMdlInputNames Find input port names of a Simulink model.
%
% This function takes in the name of a Simulink model and returns the names of each model input. This specific model has 
% both a bus and a stand-alone input port going into it. To drive an input port that expects a bus means you need to supply 
% the signals as timeseries objects in a struct that matches the structure of the bus object attached to the input port.

% Test to see if the model is currently loaded in memory.
isLoaded = bdIsLoaded(matlab.lang.makeValidName(mdlName));

% If the model is not open then load it.
if ~isLoaded
    load_system(mdlName);
end

dsObj = createInputDataset(mdlName);
numElements = dsObj.numElements;
isStruct = zeros(1:numElements);

% Check to see if any of the elements in the returned dataset object are
% structs. If they are, assume they are for an input port that accepts a bus.
for elementIdx = 1:numElements
    isStruct(elementIdx) = isa(dsObj.getElement(elementIdx),'struct');
end

% For a port that accepts a bus, the data to be loaded must be arranged in a struct
% that matches the structure of the bus object attached to the input port.
busInportIdx = 1;
for idx = 1:numElements
    if isStruct(idx)
        % Get names of signals from a bus input port.
        inPortsBus(busInportIdx, :) = string(fieldnames(dsObj.getElement(idx)));
    else
        % Get signal name from a non-bus input port.
        inPorts(idx) = string(dsObj.getElement(idx).Name);
    end
end

mdlInputNames = [inPorts, inPortsBus]';
end


function channelTableOut = helperReportChannelInfo(channelTableIn, mdfChannelInfo)
% channelTableOut Reports if a channel is present in a set of channel names.

% Assign the output data.
channelTableOut = channelTableIn;

% Remove underscores and make everything lowercase for matching.
inPortChannelNames = lower(erase(channelTableIn.PortNames,'_'));
mdfChannelNames = lower(erase(mdfChannelInfo.ChannelName,'_'));

% Match the input channel names to the channel names in the MDF file.
[~, inPortidx] = ismember(inPortChannelNames, mdfChannelNames);

% Assign the relevant information back to the channel table.
channelTableOut.ChGrpNum = mdfChannelInfo{(inPortidx), {'ChannelGroupNumber'}};
channelTableOut.ChNameActual = mdfChannelInfo{(inPortidx), {'ChannelName'}};
end