How to analyze FMU Files in MATLAB: Extracting Input and Output Information in R2024a?

21 ビュー (過去 30 日間)
Is there a way to analyze .fmu files in MATLAB? Specifically, I'm looking to gather information such as the number of inputs and outputs without running a simulation, but also port- and parameter names.

採用された回答

MathWorks Support Team
MathWorks Support Team 2024 年 4 月 19 日 0:00
編集済み: MathWorks Support Team 2024 年 3 月 21 日
Currently, MATLAB does not provide a direct function to analyze .fmu (Functional Mock-up Unit) files solely for the purpose of extracting information like the number of inputs and outputs. However, this functionality can be achieved by utilizing the Simulink API along with a custom MATLAB function. Below is a step-by-step guide on how to implement this solution.
 
Custom Function to Extract FMU Information:
The function getFmuInformation allows you to extract detailed information from an FMU file by leveraging Simulink's capabilities to handle FMU files. This function returns a structure containing the number of input and output ports along with their names.
 
function fmuInfoStruct = getFmuInformation(fmuFilePath,Options)
%GETFMUINFORMATION Extract information from an FMU file in Simulink.
%   fmuInfoStruct = GETFMUINFORMATION(fmuFilePath, Options) returns a
%   structure containing information about the FMU file with additional
%   options specified.
%
%   Inputs:
%       fmuFilePath - A text scalar that specifies the path to the FMU file.
%                     This must be a valid file path ending with the .fmu
%                     extension.
%
%   Name-Value Pair Arguments:
%       'deleteCacheFolder' - A logical value indicating whether to delete
%                             the cache folder generated during the import
%                             process. Default is true.
%
%   Outputs:
%       fmuInfoStruct - A structure with the following fields:
%           Inports - A structure containing:
%               numInput - The number of input ports.
%               names - A string array of names for each input port.
%           Outports - A structure containing:
%               numOutput - The number of output ports.
%               names - A string array of names for each output port.
%           Parameters - A structure containing:
%               names - A string array of parameter names, if available
%
%   Example:
%       % Specify the path to the FMU file
%       fmuFilePath = 'path/to/model.fmu';
%
%       % Get information about the FMU
%       info = getFmuInformation(fmuFilePath);
%
%       % Display the number of inputs and outputs
%       disp(info.Inports.numInput);
%       disp(info.Outports.numOutput);
arguments
    fmuFilePath {mustBeTextScalar, mustBeFile}
    Options.deleteCacheFolder (1,1) logical = true
end
[pathToFmu,fmuName,ext] = fileparts(fmuFilePath);
if ext ~= ".fmu"
    error("Input file is not an FMU file.")
end
state = warning('query','Simulink:Engine:MdlFileShadowing').state;
if state == "on"
    cleanUpObjWarn = onCleanup(@()warning('on','Simulink:Engine:MdlFileShadowing'));
    warning('off','Simulink:Engine:MdlFileShadowing')
end
tempMdlHdl = new_system;
cleanupObjMdl = onCleanup(@()bdclose(tempMdlHdl));
tempMdlName = get_param(tempMdlHdl,'Name');
fmuBlkPath = [tempMdlName,'/FMU'];
add_block('simulink_extras/FMU Import/FMU',fmuBlkPath);
if ~isempty(pathToFmu)
    pathCheck = isempty(which([fmuName ext]));
    if pathCheck
        addpath(pathToFmu)
        fmuPathCleanupObj = onCleanup(@()rmpath(pathToFmu));
    end
end
set_param(fmuBlkPath,'FMUName',fmuName)
portHandles = get_param(fmuBlkPath,'PortHandles');
numInput = length(portHandles.Inport);
numOutput = length(portHandles.Outport);
fmuInfoStruct.Inports.numInput = numInput;
fmuInfoStruct.Outports.numOutput = numOutput;
maskDisplayInfo = get_param(fmuBlkPath,'MaskDisplay');
maskDisplayInfo = splitlines(string(maskDisplayInfo));
[namesInput, namesOut] = getInOutInfoFromMaskDisplayInfo(maskDisplayInfo);
assert(length(namesInput) == numInput,"Number of inputs unequal to number of Inport names.")
assert(length(namesOut) == numOutput,"Number of outputs unequal to number of Outport names.")
fmuInfoStruct.Inports.names = namesInput;
fmuInfoStruct.Outports.names = namesOut;
paramData = get_param(fmuBlkPath,'DialogParameters');
if isempty(paramData)
    fmuInfoStruct.Parameters.names = string.empty(0,0);
else
    fmuInfoStruct.Parameters.names = string(fieldnames(paramData));
end
if Options.deleteCacheFolder
    clearFmuCache(fmuName)
end
    function [namesInput, namesOut] = getInOutInfoFromMaskDisplayInfo(maskDisplayInfo)
        allInputLines = maskDisplayInfo(contains(maskDisplayInfo,"port_label('input',"));
        allOutputLines = maskDisplayInfo(contains(maskDisplayInfo,"port_label('output',"));
        namesInput = extractBetween(allInputLines,"," + digitsPattern + ",'","');");
        namesOut = extractBetween(allOutputLines,"," + digitsPattern + ",'","');");
    end
    function clearFmuCache(fmuName)
        foundFmuFolder = false;
        pathToSlprjFmuFolder = fullfile(Simulink.fileGenControl('get','CacheFolder'),"slprj","_fmu");
        folderContentOfFmuCacheFolder = dir(pathToSlprjFmuFolder);
        allFolderNames = string({folderContentOfFmuCacheFolder.name});
        allFolderNames = allFolderNames([folderContentOfFmuCacheFolder.isdir]);
        allFolderNames = allFolderNames(and(allFolderNames ~= ".",allFolderNames ~= ".."));
        for numFolder = 1:length(allFolderNames)
            pathToFMUCacheFolderFromFMU = fullfile(pathToSlprjFmuFolder,allFolderNames(numFolder));
            allSubFmuCacheFolders = string({dir(pathToFMUCacheFolderFromFMU).name});
            if any(contains(allSubFmuCacheFolders,fmuName))
                foundFmuFolder = true;
                break
            end
        end
        if ~foundFmuFolder
            return
        end
        clear cleanupObjMdl
        rmdir(pathToFMUCacheFolderFromFMU,"s")
        if length(allFolderNames) > 1
            return
        end
        rmdir(pathToSlprjFmuFolder,"s")
        pathToSlprjFolder = fileparts(pathToSlprjFmuFolder);
        if length({dir(pathToSlprjFolder).name}) > 2
            return
        end
        rmdir(pathToSlprjFolder,"s")
    end
end
Important Notes:
  • Ensure the .fmu file path is correctly specified.
  • The Options parameter allows you to specify whether to delete the cache folder generated during the process. By default, this is set to true.
  • This function creates a temporary Simulink model to import the FMU block, from which it then extracts the required information.
Usage Example:
To use the getFmuInformation function, simply call it with the path to your FMU file as shown in the example within the function comment. The function will return a structure with the information about the inputs and outputs of the FMU.
 
% Specify the FMU file path
fmuFilePath = 'path/to/yourModel.fmu';
% Extract FMU information
fmuInfo = getFmuInformation(fmuFilePath);
% Display the extracted information
disp(['Number of Inputs: ', num2str(fmuInfo.Inports.numInput)]);
disp(['Number of Outputs: ', num2str(fmuInfo.Outports.numOutput)]);
Conclusion:
While MATLAB does not natively support the analysis of .fmu files for the purpose of extracting input and output information, the custom function getFmuInformation provides a workaround by utilizing Simulink's FMU import capabilities. Additionally, for those not using Simulink, parsing the modelDescription.xml file directly from the FMU package could be an alternative method to obtain this information. This approach allows users to programmatically access detailed information about their FMU files within the MATLAB environment. Please be aware that the function could break in any future release. It was tested with R2023b and R2024a.

その他の回答 (0 件)

カテゴリ

Help Center および File ExchangeSimulink Environment Customization についてさらに検索

タグ

タグが未入力です。

製品


リリース

R2022b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by