From 7c2fb99908ea51a4b5b3fe223ca5b36b5191c623 Mon Sep 17 00:00:00 2001 From: Razvalyaev Date: Sun, 15 Jun 2025 15:18:11 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA?= =?UTF-8?q?=D0=B0=20popup=20=D0=B2=20=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3?= =?UTF-8?q?=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit добавлена задание src в run_bat.mex по чекбоксу исправлены лишние абзацы в disp --- McuLib/m/compiler.m | 7 +- McuLib/m/mcuMask.m | 6 +- McuLib/m/mexing.m | 22 ++- McuLib/m/periphConfig.m | 331 ++++++++++++++++++++++++++++++++-------- 4 files changed, 293 insertions(+), 73 deletions(-) diff --git a/McuLib/m/compiler.m b/McuLib/m/compiler.m index 6f2b3e7..dc05feb 100644 --- a/McuLib/m/compiler.m +++ b/McuLib/m/compiler.m @@ -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'); % Всё прошло успешно diff --git a/McuLib/m/mcuMask.m b/McuLib/m/mcuMask.m index 8e36778..6dc61b0 100644 --- a/McuLib/m/mcuMask.m +++ b/McuLib/m/mcuMask.m @@ -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() diff --git a/McuLib/m/mexing.m b/McuLib/m/mexing.m index aa10699..5ab6e58 100644 --- a/McuLib/m/mexing.m +++ b/McuLib/m/mexing.m @@ -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 diff --git a/McuLib/m/periphConfig.m b/McuLib/m/periphConfig.m index 2dfbbe0..c0e8146 100644 --- a/McuLib/m/periphConfig.m +++ b/McuLib/m/periphConfig.m @@ -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 + 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 + 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