добавлена поддержка 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) % Список заголовочных файлов (.h)
includes = { '.\' includes = { '.\'
}; };
periphPath = mcuPath.get('wrapperPath'); wrapperPath = mcuPath.get('wrapperPath');
% [wrapperPath, ~, ~] = fileparts(wrapperPath);
% Формируем строки % Формируем строки
wrapperSrcText = compiler.createSourcesBat('code_WRAPPER', sources, periphPath); wrapperSrcText = compiler.createSourcesBat('code_WRAPPER', sources, wrapperPath);
wrapperIncText = compiler.createIncludesBat('includes_WRAPPER', includes, periphPath); wrapperIncText = compiler.createIncludesBat('includes_WRAPPER', includes, wrapperPath);
% Записываем результат % Записываем результат
res = compiler.updateRunMexBat(wrapperSrcText, wrapperIncText, ':: WRAPPER BAT'); % Всё прошло успешно res = compiler.updateRunMexBat(wrapperSrcText, wrapperIncText, ':: WRAPPER BAT'); % Всё прошло успешно

View File

@ -300,9 +300,13 @@ classdef mcuMask
out = sprintf(varargin{:}); out = sprintf(varargin{:});
end 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]); set_param(gcb, 'consoleOutput', [out_now out]);
end end
end
function updateModelAsync() function updateModelAsync()

View File

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

View File

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