前言:通过前面的文章可以了解到,MATLAB不仅可以作为一款开发环境编写各种脚本,也可以利用模块化的开发加速应用层的构建,并且随着MATLAB的功能越来越完善,许多行业都要求使用Simulink进行标准化的开发。那么本文便详细介绍其开发过程中的数据库管理---数据字典即sldd文件。
目录
一、Simulink的数据字典是什么?
想要了解数据字典是什么,可以从创建它开始,然后逐步了解。
第一步,按如下流程如创建字典:点击模型资源管理器-点击“模型名称”-点击新建-创建数据字典。
第二步,按如下步骤打开一个完整的数据字典学习里面包含的信息:点击模型资源管理器-选择“数据字典名字”-选择Design Data,然后可以在右边看到每一个信号的属性。
由下图可知,每一个信号都包含信号名称、信号值、信号类型等信息。这实际上就跟手写代码定义变量类似,它一部分通过解析的操作成为与底层代码的接口,另一部分则以变量的形式供内部模型进行调用(此部分也可以被底层调用,但是定位难度较大)。
数据字典还有一个重要的部分就是代码生成,如下所示,在此界面可以选择该信号的存储类与StructName。这两个属性与信号解析和底层交互有密不可分的联系。
代码生成属性与数据字典的关联性:如下所示,有两个信号TestSignal1、TestSignal3,其中,TestSignal1前有一个蓝色的鱼叉图标(这表示该信号已在此处解析,可以被底层通过结构体Simulink_API.TestSignal1的形式进行调用),而TestSignal3则仅仅作为信号线上的数据进行流动,无法直接以某种确定的形式被底层调用。
那么,综上所述,Simulink模型的数据字典相当于数据库、等同于底层的变量定义,而在经过解析后的信号可以直接充当底层与模型的接口,并且接口形式为我们确定和已知的。
二、如何快速管理数据字典?
其实,Simulink模型自带的数据字典模板已经很完善了,基本上不需要管理,只是简单的增删改查即可。但是,如果需要处理的信号较多,并且类型多种,那么依靠人为去一一创建的话无疑会浪费很多时间,此时通过脚本辅助我们完成这项工作就非常有必要了。根据项目经验,最终决定以Excel的形式管理数据字典。
2.1 数据字典转Excel进行管理
在某次项目中,我们需要修改1000多个信号的存储方式,如果一一手动修改毫无疑问是会吐血的。于是便开发此脚本代替这项工作。接下来便阐述该脚本的机制。
第一部分 数据字典转换
将数据字典转换成MAT文件后再进行处理。参考代码如下:
[Folderpart,fileName,fileExt]=fileparts('xx.sldd');
MyDictObj = Simulink.data.dictionary.open([fileName,fileExt]);% open .sldd file
DataSectObj = getSection(MyDictObj,'Design Data');%export to mat file
exportToFile(DataSectObj,'ASW.mat');% 导出到'.mat'文件中
第二部分 创建Excel表格
与以往的脚本不同的是,此处利用M语言自动创建Excel。参考代码如下:
XLSFileName = strcat('xx','.xlsx');%生成的xlsx文件名
DemoTitle_cell{1,1} = 'SignalName';
DemoTitle_cell{1,2} = 'SignalValue';
DemoTitle_cell{1,3} = 'SlddDataType';
DemoTitle_cell{1,4} = 'DataType';
DemoTitle_cell{1,5} = 'Dimensions';
DemoTitle_cell{1,6} = 'Min';
DemoTitle_cell{1,7} = 'Max';
DemoTitle_cell{1,8} = 'Unit';
DemoTitle_cell{1,9} = 'Initia Value';
DemoTitle_cell{1,10} = 'Category';
DemoTitle_cell{1,11} = 'StorageClass';
DemoTitle_cell{1,12} = 'CustomStorageClass';
DemoTitle_cell{1,13} = 'Headfile';
DemoTitle_cell{1,14} = 'StructName';
DemoTitle_cell{1,15} = 'Description';
xlswrite(XLSFileName,DemoTitle_cell,'DemoBaseSignal','A1');%写入数据
第三部分 读取MAT文件并写入Excel表格
操作逻辑为:读取mat文件所有信息,然后通过遍历,按行写入Excel中。由于代码篇幅过长,此处仅提供读取第一行的信息,转换成cell写入表格的代码片段供参考:
file_str = load('ASW.mat');% read m file.
dataCell = struct2cell(file_str);
fields = fieldnames(file_str);% 获取结构体中的字段名
sig_num=0;
index = 1;%遍历
dataCellMessages = dataCell{index};
var_name = fields{index};
className = class(dataCellMessages);%信号类
if (isequal(className,'mpt.Parameter'))||(isequal(className,'Simulink.Parameter'))
var_ParamentValue = dataCellMessages.Value;
var_datadim = dataCellMessages.Dimensions;
var_datadim = mat2str(var_datadim);
var_SlddDataType = className;
var_DataType = dataCellMessages.DataType;
var_min = '[ ]';
var_max = '[ ]';
var_Unit = dataCellMessages.DocUnits;
var_init = '[ ]';
var_class = dataCellMessages.CoderInfo.StorageClass;
var_description = (dataCellMessages.Description);
elseif (isequal(className,'mpt.Signal'))||(isequal(className,'Simulink.Signal'))
var_ParamentValue = '[ ]';
var_datadim = dataCellMessages.Dimensions;
var_datadim = mat2str(var_datadim);
var_SlddDataType = className;
var_DataType = dataCellMessages.DataType;
var_min = dataCellMessages.Min;
var_max = dataCellMessages.Max;
var_Unit = dataCellMessages.DocUnits;
var_init = dataCellMessages.InitialValue;
var_class = dataCellMessages.CoderInfo.StorageClass;
var_description = (dataCellMessages.Description);
else
var_ParamentValue = dataCellMessages;
var_SlddDataType = '';
var_DataType = className;
var_datadim = '[]';
var_min = '[ ]';
var_max = '[ ]';
var_Unit = '[ ]';
var_init = '[ ]';
var_class = '[ ]';
var_description = '[ ]';
end
...
...
...
sig_cell{index,1} = var_name;
if ~(isequal(className,'cell'))
sig_cell{index,2} = var_ParamentValue;
else
sig_cell{index,2} = str_tmp;
end
sig_cell{index,3} = var_SlddDataType;
sig_cell{index,4} = var_DataType;
sig_cell{index,5} = var_datadim;
sig_cell{index,6} = var_min;
sig_cell{index,7} = var_max;
sig_cell{index,8} = var_Unit;
sig_cell{index,9} = var_init;
sig_cell{index,11} = var_class;
sig_cell{index,12} = var_custom;
sig_cell{index,13} = var_headfile;
sig_cell{index,14} = var_structname;
sig_cell{index,15} = var_description;
writecell(sig_cell,XLSFileName,'Sheet','DemoBaseSignal','Range','A2');
第四部分 Excel表格渲染
此段只是为了渲染Excel表格,可以不使用。参考代码如下
%%文件渲染
%文件路径
excelFilePath = 'xx.xlsx'; % 替换为您的 Excel
% 启动 Excel 应用程序 %uisetcolor
hExcel = actxserver('Excel.Application');
hWorkbook = hExcel.Workbooks.Open(excelFilePath);
hSheet = hWorkbook.Sheets.Item('DemoBaseSignal'); % 替换 'Sheet1' 为需要操作的工作表名称
[~,Sheet_Cell] = xlsfinfo(excelFilePath);%遍历当前excel下的所有工作表
% 定位到需要修改颜色的单元格
SignalColor=sprintf('%s%d','A2:A',index+1);
MessageColor=sprintf('%s%d','B2:O',index+1);
% 选择单元格范围并应用格式 标题
hRange = hSheet.Range('A1:O1');%选中范围
hRange.Font.Name = 'Arial';%字体
hRange.Font.Size = 10;%大小
hRange.Interior.Color = 16764210;%颜色30 + 144 * 256+255 * 65536 ; % RGB
hRange.HorizontalAlignment = 3;%水平对齐:1-8分别代表常规、靠左、居中、靠右、填充、两边对齐、跨列居中、分散对齐
hRange.VerticalAlignment = 2;%垂直 1 - 靠上 (Top) 2 - 居中 (Center) 3 - 靠下 (Bottom) 4 - 两端对齐 (Justify)
hRange.Borders(1).LineStyle = 1;
hRange.Orientation = 0; % 设定文字角度
hRange.WrapText = 1;%自动换行
hRange.ColumnWidth = 25; %列
hRange.RowHeight = 35; %行
hRange.Font.Bold = 1; % 1 表示加粗
hRange.Font.Italic = 1; % 1 表示斜体
hRange.AutoFilter;%启用筛选
%% 信号
%hRange = hSheet.Range('A2:A1191');
hRange = hSheet.Range(SignalColor);
hRange.Font.Name = 'Arial';
hRange.Font.Size = 10;
hRange.Interior.Color = 11 * 65536 + 222 * 256 + 194;
hRange.HorizontalAlignment = 3;
hRange.VerticalAlignment = 2;
hRange.Borders(1).LineStyle = 1;
hRange.Orientation = 0; % 设定文字角度
hRange.WrapText = 1;%自动换行
hRange.ColumnWidth = 20;
hRange.RowHeight = 30;
hRange.Font.Italic = 1; % 1 表示斜体
%%消息
hRange = hSheet.Range(MessageColor);
hRange.Font.Name = 'Arial';
hRange.Font.Size = 10;
hRange.Interior.Color = 198 + 224 * 256 + 180 * 65536;%RGB
hRange.HorizontalAlignment = 3;
hRange.VerticalAlignment = 2;
hRange.Borders(1).LineStyle = 1;
hRange.Orientation = 0; % 设定文字角度
hRange.WrapText = 1;%自动换行
hRange.ColumnWidth = 20;
hRange.RowHeight = 30;
if isequal(Sheet_Cell{1},'Sheet1')
hWorkbook.Sheets.Item(1).Delete;%删除默认sheet
end
hWorkbook.Save;% 保存更改
% 关闭 Excel 应用程序并清理资源
hWorkbook.Close;
hExcel.Quit;
clear hWorkbook hExcel; % 清除句柄
pause(1);%wait system
winopen(DataSavePath);
fclose all;
2.2 Excel转sldd为模型提供数据库
前面提到的我们项目需求是修改1000多个信号的存储方式,那么通过脚本转化为Excel修改完成后需要再恢复成sldd才能使用。那么接下来便阐述Excel转sldd的方案。
第一部分 打开数据表格
将数据表格打开,获取表格相关信息,然后创建M文件以便后续转换为数据字典。参考代码如下:
%% step1:导入Excel文件
[Folderpart,fileName,~]=fileparts(app.EditField_FilePath.Value);
Excel_name = strrep(fileName,'.xlsx','');%获取Excel名字
mfile_name = strcat(Excel_name,'Var.m'); %生成的m文件名
DictName = strcat(Excel_name,'.sldd');%生成的sldd文件名
fid = fopen(mfile_name,'w');
fprintf(fid,'\n\n\n');%写入
[~,Sheet_Cell] = xlsfinfo(Excel_name);%遍历当前excel下的所有工作表
Sheet_Name = Sheet_Cell{1};%取sheet1名字 可以作为表格选择接口 后续更新
[~,Signal_Cell,BaseSignalRaw] = xlsread(Excel_name,Sheet_Name);%导入Signal工作表为单元数组 数据要转换为char类型
第二部分 读取信息并写入
以单个数据处理为例,首先根据属性配置界面将需要写入的信息逐一提取,然后使用fprintf函数将信息写入M文件中,参考代码如下:
OrgName = char(Signal_Cell(row,1)); %SignalName
SignalValue_cell = BaseSignalRaw(row,2); %SignalValue_cell
SignalValue = num2str(SignalValue_cell{1}); %SignalValue
SlddDataType = char(Signal_Cell(row,3)); %SlddDataType
DataType = char(Signal_Cell(row,4)); %DataType
if isequal(DataType,'char')
SignalValue = mat2str(SignalValue);
end
Dimensions = char(Signal_Cell(row,5)); %Dimensions 使用char时元胞元素必须为字符数组。
Min_cell = BaseSignalRaw(row,6); %Min-cell
Min = num2str(Min_cell{1}); %Min
Max_cell = BaseSignalRaw(row,7); %Max-cell
Max = num2str(Max_cell{1}); %Max
Unit = char(Signal_Cell(row,8)); %Unit
InitialValue_cell = BaseSignalRaw(row,9); %InitiaValue-cell
InitialValue = num2str(InitialValue_cell{1}); %InitialValue
StorageClass = char(Signal_Cell(row,11)); %StorageClass
if isequal(StorageClass,'Custom')
CustomStorageClass = char(Signal_Cell(row,12)); %CustomStorageClass
end
HeaderFile = char(Signal_Cell(row,13)); %HeaderFile
StructName = char(Signal_Cell(row,14)); %StructName
Description = char(Signal_Cell(row,15)); %Description
if isequal(SlddDataType,'mpt.Signal')
str_tmp = strcat(SignalName,' =',32,'mpt.Signal',';','\n'); %mpt Simulink
end
if isequal(SlddDataType,'mpt.Parameter')
str_tmp = strcat(SignalName,' =',32,'mpt.Parameter',';','\n');
end
if isequal(SlddDataType,'Simulink.Signal')
str_tmp = strcat(SignalName,' =',32,'Simulink.Signal',';','\n'); %mpt Simulink
end
if isequal(SlddDataType,'Simulink.Parameter')
str_tmp = strcat(SignalName,' =',32,'Simulink.Parameter',';','\n');
end
fprintf(fid,str_tmp);
if ~isempty(SlddDataType)
str_tmp = strcat('\n',SignalName,'.CoderInfo.StorageClass
=''',StorageClass,''';','\n');
fprintf(fid, str_tmp);
end
if ~isempty(SignalValue)
str_tmp = strcat(SignalName,'.Value = ',SignalValue,';','\n');
fprintf(fid, str_tmp);
end
...
...
...
if isequal(CustomStorageClass,'GetSet')
if ~isempty(HeaderFile)
str_tmp = strcat(SignalName,'.CoderInfo.CustomAttributes.HeaderFile = ''',HeaderFile,''';','\n');
fprintf(fid, str_tmp);
end
if isequal(DataType,'boolean')
rDataType = 'bool';
else
rDataType = DataType;
end
GetFunction = strcat('Get_',rDataType,'_',OrgName);
SetFunction = strcat('Set_',rDataType,'_',OrgName);
str_tmp = strcat(SignalName,'.CoderInfo.CustomAttributes.GetFunction = ''',GetFunction,''';','\n');
fprintf(fid, str_tmp);
str_tmp = strcat(SignalName,'.CoderInfo.CustomAttributes.SetFunction = ''',SetFunction,''';','\n');
fprintf(fid, str_tmp);
end
第三部分 生成数据字典
先判断是否已有同名数据字典,如果有则更新,没有则创建,然后将更新后的M文件的信息导入打开的数据字典中并保存。参考代码如下:
exist_flag = exist([pwd,'\',DictName],'file'); %判断Excel当前目录下是否已有同名数据字典
if 0 == exist_flag
DictObj = Simulink.data.dictionary.create(DictName);
else
DictObj = Simulink.data.dictionary.open(DictName);
end
SectObj = getSection(DictObj,'Design Data');
importFromFile(SectObj,mfile_name,'existingVarsAction','overwrite');
saveChanges(DictObj);
fclose('all');% 关闭所有文件
Simulink.data.dictionary.closeAll;
三 效果演示
3.1 Excel2Sldd
如下所示,操作流程为:
1、选择表格路径
2、选择数据字典保存路径
3、输入数据字典名字
4、点击start
3.2 Sldd2Excel
如下所示,操作流程为:
1、选择数据字典保存路径
2、选择表格路径
3、输入数据字典名字
4、点击start
3.3 转换结果
如下所示,Excel与sldd可以相互转换。并且可以正常打开。
总结
以上便是文章全部内容,旨在通过表格的形式来管理数据字典,希望在一定程度上减少重复的工作量。以下为工具链接以方便学习交流。