mcu_matlab/McuLib/m/periphConfig.m
Razvalyaev 7c2fb99908 добавлена поддержка popup в конфиге
добавлена задание src в run_bat.mex по чекбоксу
исправлены лишние абзацы в disp
2025-06-15 15:18:11 +03:00

624 lines
26 KiB
Matlab
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

classdef periphConfig
methods(Static)
function updateMask(blockPath, config)
% blockPath = [blockPath '/MCU'];
% Проверяем, была ли маска открыта
% wasOpen = isMaskDialogOpen(blockPath);
mask = Simulink.Mask.get(blockPath);
tableNames = {'incTable', 'srcTable'};
columns_backup = customtable.save_all_tables(tableNames);
try
containerName = 'configTabAll';
periphConfig.clear_all_from_container(mask, containerName);
% Ищем контейнер, в который будем добавлять вкладки
container = mask.getDialogControl(containerName);
if isempty(container)
error('Контейнер "%s" не найден в маске.', containerName);
end
if ~isempty(config)
% Проходим по каждому модулю (ADC, TIM...)
periphs = fieldnames(config);
for i = 1:numel(periphs)
periph = periphs{i};
% Сохраняем код, если он есть
periphConfig.store_single_periph_code(mask, periph, config.(periph));
% Проверяем наличие Defines
if ~isfield(config.(periph), 'Defines')
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.addConfig(mask, containerName, periph, defPrompt, def);
end
end
end
periphConfig.create_all_code_storage_params(blockPath, config);
periphConfig.cleanup_obsolete_code_params(blockPath, config);
% Восстанавливаем таблицы
customtable.restore_all_tables(tableNames, columns_backup);
catch
% Восстанавливаем таблицы
customtable.restore_all_tables(tableNames, columns_backup);
end
% % Повторно открываем маску, если она была открыта
% 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 update_callback()
blockPath = gcb;
mask = Simulink.Mask.get(blockPath);
config = periphConfig.read_config(blockPath);
periphs = fieldnames(config);
for i = 1:numel(periphs)
periph = periphs{i};
% Сохраняем код, если он есть
periphConfig.store_single_periph_code(mask, periph, config.(periph));
end
containerName = 'configTabAll';
container = mask.getDialogControl(containerName);
paramsAll = mcuMask.collect_all_parameters(container);
% Цикл по параметрам
for i = 1:length(paramsAll)
name = paramsAll{i};
% Ищем параметры вида Tab_<Periph>_Enable
expr = '^Tab_(\w+)_Enable$';
tokens = regexp(name, expr, 'tokens');
if ~isempty(tokens)
periph = tokens{1}{1}; % Извлекаем имя вкладки
paramObj = mask.getParameter(name);
val = paramObj.Value;
if strcmpi(val, 'off')
% Найдём вкладку по имени
try
tab = container.getDialogControl(periph);
tab.Enabled = 'off';
periphConfig.clear_single_periph_code_param(mask, periph);
catch
warning('Вкладка с именем "%s" не найдена.', periph);
end
else
% Найдём вкладку по имени
try
tab = container.getDialogControl(periph);
tab.Enabled = 'on';
periphConfig.store_single_periph_code(mask, periph, config.(periph));
catch
warning('Вкладка с именем "%s" не найдена.', periph);
end
end
end
end
periphConfig.getWrapperCode();
end
function getWrapperCode()
blockPath = gcb;
CodeStruct = periphConfig.restore_periph_code_from_mask(blockPath);
periphPath = mcuPath.get('periphPath');
[periphPath, ~, ~] = fileparts(periphPath);
periphConfig.addCodeBat(CodeStruct, periphPath);
end
end
methods(Static, Access=private)
function create_all_code_storage_params(blockPath, config)
mask = Simulink.Mask.get(blockPath);
existingParams = arrayfun(@(p) p.Name, mask.Parameters, 'UniformOutput', false);
periphs = fieldnames(config);
for i = 1:numel(periphs)
periph = periphs{i};
% Проверяем, нужно ли создавать параметры
hasSources = isfield(config.(periph), 'Sources');
hasIncludes = isfield(config.(periph), 'Includes');
if ~hasSources && ~hasIncludes
continue;
end
% Добавляем параметр для Sources
if hasSources
srcParamName = ['Hidden_' periph '_Sources'];
if ~ismember(srcParamName, existingParams)
mask.addParameter( ...
'Name', srcParamName, ...
'Type', 'edit', ...
'Prompt', '', ...
'Value', '', ...
'Visible', 'off' ...
);
fprintf('Создан скрытый параметр: %s\n', srcParamName);
end
end
% Добавляем параметр для Includes
if hasIncludes
incParamName = ['Hidden_' periph '_Includes'];
if ~ismember(incParamName, existingParams)
mask.addParameter( ...
'Name', incParamName, ...
'Type', 'edit', ...
'Prompt', '', ...
'Value', '', ...
'Visible', 'off' ...
);
fprintf('Создан скрытый параметр: %s\n', incParamName);
end
end
end
end
function cleanup_obsolete_code_params(blockPath, config)
mask = Simulink.Mask.get(blockPath);
maskParams = mask.Parameters;
% Получаем список актуальных периферий
validPeriphs = fieldnames(config);
for i = 1:numel(maskParams)
paramName = maskParams(i).Name;
% Проверяем, является ли параметром хранения Sources или Includes
expr = '^Hidden_(\w+)_(Sources|Includes)$';
tokens = regexp(paramName, expr, 'tokens');
if ~isempty(tokens)
periph = tokens{1}{1};
% Если периферии больше нет удаляем параметр
if ~ismember(periph, validPeriphs)
mask.removeParameter(paramName);
fprintf('Удалён устаревший параметр: %s\n', paramName);
end
end
end
end
function codeStruct = restore_periph_code_from_mask(blockPath)
mask = Simulink.Mask.get(blockPath);
maskParams = mask.Parameters;
allSources = {};
allIncludes = {};
for i = 1:numel(maskParams)
name = maskParams(i).Name;
% Ищем параметры Sources
tokensSrc = regexp(name, '^Hidden_(\w+)_Sources$', 'tokens');
if ~isempty(tokensSrc)
val = maskParams(i).Value;
if ischar(val) || isstring(val)
valStr = strtrim(char(val));
% Пропускаем пустые строки и '[]'
if isempty(valStr) || strcmp(valStr, '[]')
continue;
end
lines = splitlines(valStr);
lines = lines(~cellfun(@(x) all(isspace(x)) || isempty(x), lines));
allSources = [allSources; lines]; %#ok<AGROW>
end
continue;
end
% Ищем параметры Includes
tokensInc = regexp(name, '^Hidden_(\w+)_Includes$', 'tokens');
if ~isempty(tokensInc)
val = maskParams(i).Value;
if ischar(val) || isstring(val)
valStr = strtrim(char(val));
if isempty(valStr) || strcmp(valStr, '[]')
continue;
end
lines = splitlines(valStr);
lines = lines(~cellfun(@(x) all(isspace(x)) || isempty(x), lines));
allIncludes = [allIncludes; lines]; %#ok<AGROW>
end
continue;
end
end
codeStruct = struct();
codeStruct.Sources = allSources;
codeStruct.Includes = allIncludes;
end
function clear_single_periph_code_param(mask, periph)
% Формируем имена параметров
paramNames = {
['Hidden_' periph '_Sources'],
['Hidden_' periph '_Includes']
};
for i = 1:numel(paramNames)
paramName = paramNames{i};
try
param = mask.getParameter(paramName);
param.Value = '';
catch
% Параметр не существует — ничего не делаем
end
end
end
function store_single_periph_code(mask, periph, code)
% Сохраняем Sources, если они есть
if isfield(code, 'Sources') && isfield(code.Sources, 'Options')
paramName = ['Hidden_' periph '_Sources'];
try
param = mask.getParameter(paramName);
param.Value = periphConfig.convert_code_value(code.Sources.Options);
catch
mcuMask.disp(0, ['Параметр ' paramName ' не найден']);
end
end
% Сохраняем Includes, если они есть
if isfield(code, 'Includes') && isfield(code.Includes, 'Options')
paramName = ['Hidden_' periph '_Includes'];
try
param = mask.getParameter(paramName);
param.Value = periphConfig.convert_code_value(code.Includes.Options);
catch
mcuMask.disp(0, ['Параметр ' paramName ' не найден']);
end
end
end
function value = convert_code_value(codeField)
% Преобразует значение поля Options в строку
if ischar(codeField)
value = codeField;
elseif isstring(codeField)
value = char(codeField);
elseif iscell(codeField)
value = strjoin(codeField, newline);
else
warning('Неподдерживаемый тип данных: сохранено как пустая строка');
value = '';
end
end
function res = addCodeBat(codeConfig, codePath)
% Возвращает 0 при успехе, 1 при ошибке
try
% Формируем строки
srcText = compiler.createSourcesBat('code_PERIPH', codeConfig.Sources, codePath);
incText = compiler.createIncludesBat('includes_PERIPH', codeConfig.Includes, codePath);
% Записываем результат
res = compiler.updateRunMexBat(srcText, incText, ':: PERIPH BAT'); % Всё прошло успешно
catch
% В случае ошибки просто возвращаем 1
res = 1;
end
end
function res = addUserFunctions(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(mcuPath.get('wrapperPath'), '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 addConfig(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';
case 'popup'
paramType = 'popup';
otherwise
% Игнорируем остальные типы
return;
end
paramName = matlab.lang.makeValidName(defPrompt);
% Получаем значение
if strcmp(paramType, 'popup')
if isfield(def, 'Def') && iscell(def.Def) && ~isempty(def.Def)
choices = def.Def;
valStr = ''; % по умолчанию — ничего
else
warning('Popout параметр "%s" не содержит допустимого списка в Def.', defPrompt);
return;
end
else
val = def.Default;
if islogical(val)
valStr = periphConfig.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
end
% Добавляем параметр в маску
if strcmp(paramType, 'popup')
param = mask.addParameter( ...
'Type', paramType, ...
'Prompt', def.Prompt, ...
'Name', paramName, ...
'Container', periphName ...
);
else
param = mask.addParameter( ...
'Type', paramType, ...
'Prompt', def.Prompt, ...
'Name', paramName, ...
'Value', valStr, ...
'Container', periphName ...
);
end
param.Evaluate = 'off';
if def.NewRow
param.DialogControl.Row = 'new';
else
param.DialogControl.Row = 'current';
end
if isfield(def, 'Def')
if strcmp(paramType, 'popup')
param.TypeOptions = def.Def;
else
param.Alias = def.Def;
end
end
param.Callback = 'periphConfig.update_callback();';
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
function res = ternary(cond, valTrue, valFalse)
if cond
res = valTrue;
else
res = valFalse;
end
end
end
end