release 1.0
This commit is contained in:
414
McuLib/m/periphConfig.m
Normal file
414
McuLib/m/periphConfig.m
Normal file
@@ -0,0 +1,414 @@
|
||||
classdef periphConfig
|
||||
|
||||
methods(Static)
|
||||
function update(blockPath, config)
|
||||
% blockPath = [blockPath '/MCU'];
|
||||
|
||||
% Проверяем, была ли маска открыта
|
||||
% wasOpen = isMaskDialogOpen(blockPath);
|
||||
mask = Simulink.Mask.get(blockPath);
|
||||
periphPath = get_param(blockPath, 'periphPath');
|
||||
[periphPath, ~, ~] = fileparts(periphPath);
|
||||
|
||||
tableNames = {'incTable', 'srcTable'};
|
||||
columns_backup = customtable.save_all_tables(tableNames);
|
||||
|
||||
containerName = 'configTabAll';
|
||||
periphConfig.clear_all_from_container(mask, containerName);
|
||||
|
||||
% Ищем контейнер, в который будем добавлять вкладки
|
||||
container = mask.getDialogControl(containerName);
|
||||
if isempty(container)
|
||||
error('Контейнер "%s" не найден в маске.', containerName);
|
||||
end
|
||||
|
||||
if ~isempty(config)
|
||||
|
||||
if isfield(config, 'Code')
|
||||
res = periphConfig.addCodeConfig(config.Code, periphPath);
|
||||
if res == 0
|
||||
error('Ошибка: неудачное добавление кода периферии. Проверьте корректность файлов и путей в конфигурационном файле')
|
||||
end
|
||||
else
|
||||
error('Ошибка: в конфигурационном файле не задан исходный код для симуляции периферии')
|
||||
end
|
||||
|
||||
if isfield(config, 'UserCode')
|
||||
res = periphConfig.addUserCodeConfig(config.UserCode);
|
||||
if res == 0
|
||||
error('Ошибка: неудачное добавление функций для симуляции. Проверьте корректность названий функций в конфигурационном файле')
|
||||
end
|
||||
else
|
||||
error('Ошибка: в конфигурационном файле не заданы функции для симуляции периферии')
|
||||
end
|
||||
|
||||
% Проходим по каждому модулю (ADC, TIM...)
|
||||
periphs = fieldnames(config);
|
||||
for i = 1:numel(periphs)
|
||||
periph = periphs{i};
|
||||
|
||||
% Пропускаем Code и UserCode, они уже обработаны
|
||||
if strcmp(periph, 'Code') || strcmp(periph, 'UserCode')
|
||||
continue;
|
||||
end
|
||||
|
||||
defines = config.(periph).Defines;
|
||||
defNames = fieldnames(defines);
|
||||
|
||||
% Создаём вкладку для модуля
|
||||
tabCtrl = container.addDialogControl('tab', periph);
|
||||
tabCtrl.Prompt = [periph ' Config'];
|
||||
|
||||
for j = 1:numel(defNames)
|
||||
defPrompt = defNames{j};
|
||||
def = defines.(defPrompt);
|
||||
|
||||
% Вызов функции добавления одного параметра
|
||||
periphConfig.addDefineConfig(mask, containerName, periph, defPrompt, def);
|
||||
end
|
||||
end
|
||||
end
|
||||
% Восстанавливаем таблицы
|
||||
customtable.restore_all_tables(tableNames, columns_backup);
|
||||
|
||||
% % Повторно открываем маску, если она была открыта
|
||||
% if wasOpen
|
||||
% open_system(blockPath, 'mask');
|
||||
% end
|
||||
end
|
||||
|
||||
function config = update_config(blockPath, config)
|
||||
if isempty(config)
|
||||
return;
|
||||
end
|
||||
|
||||
mask = Simulink.Mask.get(blockPath);
|
||||
maskParams = mask.Parameters;
|
||||
paramNames = arrayfun(@(p) p.Name, maskParams, 'UniformOutput', false);
|
||||
|
||||
% Обработка остальных секций (с дефайнами)
|
||||
periphs = fieldnames(config);
|
||||
for i = 1:numel(periphs)
|
||||
periph = periphs{i};
|
||||
|
||||
% Пропускаем Code и UserCode, они уже обработаны
|
||||
if strcmp(periph, 'Code') || strcmp(periph, 'UserCode')
|
||||
continue;
|
||||
end
|
||||
|
||||
% Проверяем есть ли Defines
|
||||
if ~isfield(config.(periph), 'Defines')
|
||||
continue;
|
||||
end
|
||||
|
||||
defines = config.(periph).Defines;
|
||||
defNames = fieldnames(defines);
|
||||
|
||||
for j = 1:numel(defNames)
|
||||
defPrompt = defNames{j};
|
||||
paramName = matlab.lang.makeValidName(defPrompt);
|
||||
|
||||
% Проверка, существует ли параметр с таким именем
|
||||
if ismember(paramName, paramNames)
|
||||
param = mask.getParameter(paramName);
|
||||
valStr = param.Value;
|
||||
|
||||
% Проверяем, существует ли элемент defPrompt в структуре defines
|
||||
if isfield(defines, defPrompt)
|
||||
% Преобразуем строку в соответствующий тип
|
||||
if strcmpi(defines.(defPrompt).Type, 'checkbox')
|
||||
config.(periph).Defines.(defPrompt).Default = strcmpi(valStr, 'on');
|
||||
elseif strcmpi(defines.(defPrompt).Type, 'edit')
|
||||
valNum = str2double(valStr);
|
||||
if isnan(valNum)
|
||||
config.(periph).Defines.(defPrompt).Default = valStr;
|
||||
else
|
||||
config.(periph).Defines.(defPrompt).Default = valNum;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function config = read_config(blockPath)
|
||||
mask = Simulink.Mask.get(blockPath);
|
||||
|
||||
pathparam = mask.getParameter('periphPath');
|
||||
config_path = pathparam.Value;
|
||||
|
||||
if ~isempty(config_path)
|
||||
jsonText = fileread(config_path);
|
||||
config = jsondecode(jsonText);
|
||||
else
|
||||
config = [];
|
||||
end
|
||||
end
|
||||
|
||||
function write_config(config)
|
||||
if isempty(config)
|
||||
return
|
||||
end
|
||||
|
||||
blockHandle = gcbh;
|
||||
mask = Simulink.Mask.get(blockHandle);
|
||||
|
||||
pathparam = mask.getParameter('periphPath');
|
||||
config_path = pathparam.Value;
|
||||
|
||||
jsonText = jsonencode(config, 'PrettyPrint', true);
|
||||
fid = fopen(config_path, 'w', 'n', 'UTF-8');
|
||||
if fid == -1
|
||||
error('Не удалось открыть файл periph_config.json для записи.');
|
||||
end
|
||||
fwrite(fid, jsonText, 'char');
|
||||
fclose(fid);
|
||||
end
|
||||
|
||||
function clear_all_from_container(mask, containerName)
|
||||
% allControls = mask.getDialogControls();
|
||||
container = mask.getDialogControl(containerName);
|
||||
if isempty(container)
|
||||
warning('Контейнер "%s" не найден.', containerName);
|
||||
return;
|
||||
end
|
||||
|
||||
% Рекурсивно собрать все параметры (не вкладки)
|
||||
paramsToDelete = mcuMask.collect_all_parameters(container);
|
||||
|
||||
% Удаляем все параметры
|
||||
for i = 1:numel(paramsToDelete)
|
||||
try
|
||||
mask.removeParameter(paramsToDelete{i});
|
||||
catch
|
||||
warning('Не удалось удалить параметр %s', paramsToDelete{i});
|
||||
end
|
||||
end
|
||||
|
||||
% Рекурсивно удалить все вкладки внутри контейнера
|
||||
mcuMask.delete_all_tabs(mask, container);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
methods(Static, Access=private)
|
||||
|
||||
function res = addCodeConfig(codeConfig, periphPath)
|
||||
% Возвращает 0 при успехе, 1 при ошибке
|
||||
try
|
||||
% Источники
|
||||
srcList = {};
|
||||
if isfield(codeConfig, 'Sources') && isfield(codeConfig.Sources, 'Options')
|
||||
srcFiles = codeConfig.Sources.Options;
|
||||
for i = 1:numel(srcFiles)
|
||||
fullPath = fullfile(periphPath, srcFiles{i});
|
||||
srcList{end+1} = [strrep(fullPath, '\', '\\')];
|
||||
end
|
||||
end
|
||||
|
||||
% Формируем srcText с переносами строк и ^
|
||||
srcText = '';
|
||||
for i = 1:numel(srcList)
|
||||
if i < numel(srcList)
|
||||
srcText = [srcText srcList{i} '^' newline ' '];
|
||||
else
|
||||
srcText = [srcText srcList{i}];
|
||||
end
|
||||
end
|
||||
|
||||
% Инклуды
|
||||
incList = {};
|
||||
if isfield(codeConfig, 'Includes') && isfield(codeConfig.Includes, 'Options')
|
||||
incPaths = codeConfig.Includes.Options;
|
||||
for i = 1:numel(incPaths)
|
||||
fullPath = fullfile(periphPath, incPaths{i});
|
||||
incList{end+1} = ['-I"' strrep(fullPath, '\', '\\') '"'];
|
||||
end
|
||||
end
|
||||
|
||||
% Формируем incText с переносами строк и ^
|
||||
incText = '';
|
||||
for i = 1:numel(incList)
|
||||
if i == 1 && numel(incList) ~= 1
|
||||
incText = [incText incList{i} '^' newline];
|
||||
elseif i < numel(incList)
|
||||
incText = [incText ' ' incList{i} '^' newline];
|
||||
else
|
||||
incText = [incText ' ' incList{i}];
|
||||
end
|
||||
end
|
||||
|
||||
% Добавляем префиксы
|
||||
srcText = ['set code_PERIPH' '=' srcText];
|
||||
incText = ['set includes_PERIPH' '=' incText];
|
||||
|
||||
% Записываем результат
|
||||
res = periphConfig.updateRunMexBat(srcText, incText); % Всё прошло успешно
|
||||
catch
|
||||
% В случае ошибки просто возвращаем 1
|
||||
res = 1;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function res = addUserCodeConfig(userCodeConfig)
|
||||
% userCodeConfig — структура config.UserCode
|
||||
|
||||
initFuncsText = '';
|
||||
simFuncsText = '';
|
||||
deinitFuncsText = '';
|
||||
|
||||
if isfield(userCodeConfig, 'Functions')
|
||||
funcs = userCodeConfig.Functions;
|
||||
|
||||
if isfield(funcs, 'PeriphInit') && isfield(funcs.PeriphInit, 'Options')
|
||||
initFuncs = funcs.PeriphInit.Options;
|
||||
initFuncsText = strjoin(strcat('\t', initFuncs, ';'), '\n');
|
||||
end
|
||||
|
||||
if isfield(funcs, 'PeriphSimulation') && isfield(funcs.PeriphSimulation, 'Options')
|
||||
simFuncs = funcs.PeriphSimulation.Options;
|
||||
simFuncsText = strjoin(strcat('\t', simFuncs, ';'), '\n');
|
||||
end
|
||||
|
||||
if isfield(funcs, 'PeriphDeinit') && isfield(funcs.PeriphDeinit, 'Options')
|
||||
deinitFuncs = funcs.PeriphDeinit.Options;
|
||||
deinitFuncsText = strjoin(strcat('\t', deinitFuncs, ';'), '\n');
|
||||
end
|
||||
|
||||
res = periphConfig.updateWrapperCode(initFuncsText, simFuncsText, deinitFuncsText);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function res = updateWrapperCode(initFuncsText, simFuncsText, deinitFuncsText)
|
||||
% Входные параметры:
|
||||
% srcText - текст для записи set code_...
|
||||
% incText - текст для записи set includes_...
|
||||
%
|
||||
% Возвращает:
|
||||
% res - 0 при успехе, 1 при ошибке
|
||||
wrapPath = fullfile('.\MCU_Wrapper', 'mcu_wrapper.c');
|
||||
res = 1;
|
||||
try
|
||||
code = fileread(wrapPath);
|
||||
code = regexprep(code, '\r\n?', '\n');
|
||||
|
||||
% Записываем строки initFuncsText и simFuncsText
|
||||
code = editCode.insertSection(code, '// PERIPH INIT', initFuncsText);
|
||||
code = editCode.insertSection(code, '// PERIPH SIM', simFuncsText);
|
||||
code = editCode.insertSection(code, '// PERIPH DEINIT', deinitFuncsText);
|
||||
|
||||
fid = fopen(wrapPath, 'w', 'n', 'UTF-8');
|
||||
if fid == -1
|
||||
error('Не удалось открыть файл для записи');
|
||||
end
|
||||
fwrite(fid, code);
|
||||
fclose(fid);
|
||||
res = 1;
|
||||
catch ME
|
||||
error('Ошибка: неудачная запись в файл при записи файла: %s', ME.message);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function res = updateRunMexBat(srcText, incText)
|
||||
% Входные параметры:
|
||||
% srcText - текст для записи set code_...
|
||||
% incText - текст для записи set includes_...
|
||||
%
|
||||
% Возвращает:
|
||||
% res - 0 при успехе, 1 при ошибке
|
||||
periphBat = [srcText '\n\n' incText];
|
||||
batPath = fullfile('.\MCU_Wrapper', 'run_mex.bat');
|
||||
res = 1;
|
||||
try
|
||||
code = fileread(batPath);
|
||||
code = regexprep(code, '\r\n?', '\n');
|
||||
|
||||
% Записываем строки srcText и incText с переносами строк
|
||||
code = editCode.insertSection(code, ':: PERIPH BAT', periphBat);
|
||||
|
||||
fid = fopen(batPath, 'w', 'n', 'UTF-8');
|
||||
if fid == -1
|
||||
error('Не удалось открыть файл для записи');
|
||||
end
|
||||
fwrite(fid, code);
|
||||
fclose(fid);
|
||||
res = 1;
|
||||
catch ME
|
||||
mcuMask.disp(0, '\nОшибка: неудачная запись в файл при записи файла: %s', ME.message);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function addDefineConfig(mask, containerName, periphName, defPrompt, def)
|
||||
% mask — объект маски Simulink.Mask.get(blockPath)
|
||||
% containerName — имя контейнера, в который добавляем параметр (например, 'configTabAll')
|
||||
% periphName — имя вкладки / контейнера для текущего периферийного блока (например, 'ADC')
|
||||
% defPrompt — имя параметра в Defines (например, 'shift_enable')
|
||||
% def — структура с описанием параметра (Prompt, Def, Type, Default, NewRow и т.п.)
|
||||
|
||||
% Найдем контейнер с таким именем
|
||||
container = mask.getDialogControl(containerName);
|
||||
if isempty(container)
|
||||
error('Контейнер "%s" не найден в маске.', containerName);
|
||||
end
|
||||
|
||||
% Проверим, есть ли вкладка с именем periphName, если нет — создадим
|
||||
tabCtrl = mask.getDialogControl(periphName);
|
||||
if isempty(tabCtrl)
|
||||
tabCtrl = container.addDialogControl('tab', periphName);
|
||||
tabCtrl.Prompt = [periphName ' Config'];
|
||||
end
|
||||
|
||||
% Определяем тип параметра (checkbox или edit)
|
||||
switch lower(def.Type)
|
||||
case 'checkbox'
|
||||
paramType = 'checkbox';
|
||||
case 'edit'
|
||||
paramType = 'edit';
|
||||
otherwise
|
||||
% Игнорируем остальные типы
|
||||
return;
|
||||
end
|
||||
|
||||
paramName = matlab.lang.makeValidName(defPrompt);
|
||||
|
||||
% Преобразуем значение Default в строку для Value
|
||||
val = def.Default;
|
||||
if islogical(val)
|
||||
valStr = mcuMask.ternary(val, 'on', 'off');
|
||||
elseif isnumeric(val)
|
||||
valStr = num2str(val);
|
||||
elseif ischar(val)
|
||||
valStr = val;
|
||||
else
|
||||
error('Unsupported default value type for %s.%s', periphName, defPrompt);
|
||||
end
|
||||
|
||||
% Добавляем параметр в маску
|
||||
param = mask.addParameter( ...
|
||||
'Type', paramType, ...
|
||||
'Prompt', def.Prompt, ...
|
||||
'Name', paramName, ...
|
||||
'Value', valStr, ...
|
||||
'Container', periphName ...
|
||||
);
|
||||
|
||||
param.Alias = def.Def;
|
||||
param.Evaluate = 'off';
|
||||
|
||||
if def.NewRow
|
||||
param.DialogControl.Row = 'new';
|
||||
else
|
||||
param.DialogControl.Row = 'current';
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user