добавлена поддержка popup в конфиге

добавлена задание src в run_bat.mex по чекбоксу
исправлены лишние абзацы в disp
This commit is contained in:
Razvalyaev 2025-06-15 15:18:11 +03:00
parent 058d3a00cf
commit 7c2fb99908
4 changed files with 293 additions and 73 deletions

View File

@ -26,10 +26,11 @@ classdef compiler
% Список заголовочных файлов (.h)
includes = { '.\'
};
periphPath = mcuPath.get('wrapperPath');
wrapperPath = mcuPath.get('wrapperPath');
% [wrapperPath, ~, ~] = fileparts(wrapperPath);
% Формируем строки
wrapperSrcText = compiler.createSourcesBat('code_WRAPPER', sources, periphPath);
wrapperIncText = compiler.createIncludesBat('includes_WRAPPER', includes, periphPath);
wrapperSrcText = compiler.createSourcesBat('code_WRAPPER', sources, wrapperPath);
wrapperIncText = compiler.createIncludesBat('includes_WRAPPER', includes, wrapperPath);
% Записываем результат
res = compiler.updateRunMexBat(wrapperSrcText, wrapperIncText, ':: WRAPPER BAT'); % Всё прошло успешно

View File

@ -300,9 +300,13 @@ classdef mcuMask
out = sprintf(varargin{:});
end
out_now = get_param(gcb, 'consoleOutput');
out_now = get_param(gcb, 'consoleOutput');
if ~strcmp(out, '') && ~strcmp(out_now, '')
set_param(gcb, 'consoleOutput', [out_now newline out]);
else
set_param(gcb, 'consoleOutput', [out_now out]);
end
end
function updateModelAsync()

View File

@ -69,7 +69,7 @@ function res = mexing(compile_mode)
config = periphConfig.read_config(blockPath);
config = periphConfig.update_config(blockPath, config);
periphConfig.write_config(config);
periphConfig.update(blockPath, config);
periphConfig.updateMask(blockPath, config);
end
end
@ -202,6 +202,8 @@ function definesWrapperArg = buildConfigDefinesString()
definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, 0);
case 'edit'
definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, 1);
case 'popup'
definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, 0);
otherwise
% Необрабатываемые типы
end
@ -230,7 +232,15 @@ function definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, val_
end
% Берём alias из маски
alias = param.Alias;
if ~strcmp(param.Type, 'popup')
def_name = param.Alias;
else
def_name = param.Value;
end
if strcmp(def_name, '')
return;
end
if val_define ~= 0
% Значение параметра
@ -240,14 +250,16 @@ function definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, val_
val = num2str(val); % Преобразуем результат в строку
end
% Формируем define с кавычками и значением
newDefine = ['-D"' alias '__EQ__' val '"'];
else
newDefine = ['-D"' def_name '__EQ__' val '"'];
elseif ~strcmp(param.Type, 'popup')
if mcuMask.read_checkbox(paramName)
% Формируем define с кавычками без значения
newDefine = ['-D"' alias '"'];
newDefine = ['-D"' def_name '"'];
else
newDefine = '';
end
else
newDefine = ['-D"' def_name '"'];
end

View File

@ -1,76 +1,65 @@
classdef periphConfig
methods(Static)
function update(blockPath, config)
function updateMask(blockPath, config)
% blockPath = [blockPath '/MCU'];
% Проверяем, была ли маска открыта
% wasOpen = isMaskDialogOpen(blockPath);
mask = Simulink.Mask.get(blockPath);
periphPath = mcuPath.get('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('Ошибка: в конфигурационном файле не задан исходный код для симуляции периферии')
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);
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.addConfig(mask, containerName, periph, defPrompt, def);
% Создаём вкладку для модуля
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
% Восстанавливаем таблицы
customtable.restore_all_tables(tableNames, columns_backup);
% % Повторно открываем маску, если она была открыта
% if wasOpen
% open_system(blockPath, 'mask');
@ -169,9 +158,20 @@ classdef periphConfig
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);
paramsAll = mcuMask.collect_all_parameters(container);
% Цикл по параметрам
for i = 1:length(paramsAll)
name = paramsAll{i};
@ -190,6 +190,7 @@ classdef periphConfig
try
tab = container.getDialogControl(periph);
tab.Enabled = 'off';
periphConfig.clear_single_periph_code_param(mask, periph);
catch
warning('Вкладка с именем "%s" не найдена.', periph);
end
@ -198,6 +199,7 @@ classdef periphConfig
try
tab = container.getDialogControl(periph);
tab.Enabled = 'on';
periphConfig.store_single_periph_code(mask, periph, config.(periph));
catch
warning('Вкладка с именем "%s" не найдена.', periph);
end
@ -205,20 +207,221 @@ classdef periphConfig
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 res = addCodeConfig(codeConfig, periphPath)
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.Options, periphPath);
incText = compiler.createIncludesBat('includes_PERIPH', codeConfig.Includes.Options, periphPath);
srcText = compiler.createSourcesBat('code_PERIPH', codeConfig.Sources, codePath);
incText = compiler.createIncludesBat('includes_PERIPH', codeConfig.Includes, codePath);
% Записываем результат
res = compiler.updateRunMexBat(srcText, incText, ':: PERIPH BAT'); % Всё прошло успешно
@ -229,7 +432,7 @@ classdef periphConfig
end
function res = addUserCodeConfig(userCodeConfig)
function res = addUserFunctions(userCodeConfig)
% userCodeConfig структура config.UserCode
initFuncsText = '';
@ -258,7 +461,7 @@ classdef periphConfig
end
end
function res = updateWrapperCode(initFuncsText, simFuncsText, deinitFuncsText)
% Входные параметры:
% srcText - текст для записи set code_...
@ -378,9 +581,9 @@ classdef periphConfig
else
param.Alias = def.Def;
end
else
param.Callback = 'periphConfig.update_callback();';
end
param.Callback = 'periphConfig.update_callback();';
end