diff --git a/McuLib/lib/McuLib.slx b/McuLib/lib/McuLib.slx index 7aa79c6..422bd2b 100644 Binary files a/McuLib/lib/McuLib.slx and b/McuLib/lib/McuLib.slx differ diff --git a/McuLib/m/compiler.m b/McuLib/m/compiler.m index dc05feb..2a0f39f 100644 --- a/McuLib/m/compiler.m +++ b/McuLib/m/compiler.m @@ -55,7 +55,7 @@ classdef compiler % Записываем результат res = compiler.updateRunMexBat(wrapperSrcText, wrapperIncText, ':: APP WRAPPER BAT'); % Всё прошло успешно - + periphConfig.updatePeriphRunMexBat(); end diff --git a/McuLib/m/periphConfig.m b/McuLib/m/periphConfig.m index c5c6e9a..528fce5 100644 --- a/McuLib/m/periphConfig.m +++ b/McuLib/m/periphConfig.m @@ -56,7 +56,7 @@ classdef periphConfig periphConfig.create_all_code_storage_params(blockPath, config); % periphConfig.cleanup_obsolete_code_params(blockPath, config); - periphConfig.update_all(); + periphConfig.update(); % Восстанавлиperiph = allTabNamesваем таблицы customtable.restore_all_tables(tableNames, columns_backup); @@ -70,6 +70,146 @@ classdef periphConfig % end end + function update() + blockPath = gcb; + mask = Simulink.Mask.get(blockPath); + + config = periphConfig.read_config(blockPath); + containerName = 'configTabAll'; + container = mask.getDialogControl(containerName); + paramsAll = mcuMask.collect_all_parameters(container); + % Получаем все имена в кладок из container (у вас должен быть container) + allTabs = container.DialogControls; + allTabNames = arrayfun(@(t) t.Name, allTabs, 'UniformOutput', false); + + fieldsConfig = fieldnames(config); + % Цикл по всем вкладкам в контейнере + for i = 1:length(allTabNames) + periph = fieldsConfig{i}; + + % Попытка найти параметр чекбокса Tab__Enable + checkboxName = ['Tab_' periph '_Enable']; + if ismember(checkboxName, paramsAll) + % Чекбокс есть - проверяем его состояние + paramObj = mask.getParameter(checkboxName); + val = paramObj.Value; + + try + tab = container.getDialogControl(periph); + if strcmpi(val, 'off') + tab.Enabled = 'off'; + % Рекурсивно очищаем связанные скрытые параметры + periphConfig.clear_tab_params(mask, config.(periph), periph); + else + tab.Enabled = 'on'; + periphConfig.sync_tab_params(mask, config.(periph), periph); + end + catch + warning('Вкладка с именем "%s" не найдена.', periph); + end + + else + % Чекбокса нет — просто пытаемся включить вкладку, если она есть + try + tab = container.getDialogControl(periph); + tab.Enabled = 'on'; + % Опционально можно синхронизировать параметры, если есть config поле + if isfield(config, periph) + periphConfig.sync_tab_params(mask, config.(periph), periph); + end + catch + % Можно не выводить предупреждение — вкладка может быть необязательной + % warning('Вкладка с именем "%s" не найдена.', periph); + end + end + end + + end + + function periphParamCallback(paramName) + blockPath = gcb; + mask = Simulink.Mask.get(blockPath); + hObj = mask.getParameter(paramName); + config = periphConfig.read_config(blockPath); + container = mask.getDialogControl('configTabAll'); + + % === Проверка на Tab__Enable === + exprTab = '^Tab_(\w+)_Enable$'; + tokensTab = regexp(paramName, exprTab, 'tokens'); + + if ~isempty(tokensTab) + periph = tokensTab{1}{1}; + + try + tab = container.getDialogControl(periph); + paramVal = hObj.Value; + + if strcmpi(paramVal, 'off') + tab.Enabled = 'off'; + if isfield(config, periph) + periphConfig.clear_tab_params(mask, config.(periph), periph); + end + else + tab.Enabled = 'on'; + if isfield(config, periph) + periphConfig.sync_tab_params(mask, config.(periph), periph); + end + end + catch + warning('Ошибка обработки вкладки "%s".', periph); + end + return; + end + + % === Проверка на параметр, связанный с Sources/Includes === + % Проверка: это параметр, у которого есть соответствующий Hidden__Sources или Hidden__Includes + nameBase = paramName; + + paramNames = string({mask.Parameters.Name}); + hasSources = any(paramNames == "Hidden_" + nameBase + "_Sources"); + hasIncludes = any(paramNames == "Hidden_" + nameBase + "_Includes"); + + + if hasSources || hasIncludes + useVal = hObj.Value; + + % Получаем содержимое config по nameBase — возможно, вложенное + try + valueStruct = periphConfig.get_field(config, nameBase); + catch + warning('Не удалось найти путь %s в config.', nameBase); + return; + end + + if strcmpi(useVal, 'on') + try + periphConfig.store_single_periph_code(mask, nameBase, valueStruct); + catch + warning('Не удалось сохранить параметры для %s.', nameBase); + end + else + periphConfig.clear_single_periph_code_param(mask, nameBase); + end + + return; + end + + + % === Если не подошло ни под одно из условий === + % warning('Объект "%s" не поддерживается универсальным коллбеком.', paramName); + end + + function updatePeriphRunMexBat() + % Запись run_mex.bat + blockPath = gcb; + CodeStruct = periphConfig.restore_periph_code_from_mask(blockPath); + periphPath = mcuPath.get('periphPath'); + [periphPath, ~, ~] = fileparts(periphPath); + + periphConfig.addCodeBat(CodeStruct, periphPath); + end + + function config = update_config(blockPath, config) if isempty(config) return; @@ -159,158 +299,7 @@ classdef periphConfig fclose(fid); end - function update_all() - blockPath = gcb; - mask = Simulink.Mask.get(blockPath); - config = periphConfig.read_config(blockPath); - containerName = 'configTabAll'; - container = mask.getDialogControl(containerName); - paramsAll = mcuMask.collect_all_parameters(container); - % Получаем все имена в кладок из container (у вас должен быть container) - allTabs = container.DialogControls; - allTabNames = arrayfun(@(t) t.Name, allTabs, 'UniformOutput', false); - - fieldsConfig = fieldnames(config); - % Цикл по всем вкладкам в контейнере - for i = 1:length(allTabNames) - periph = fieldsConfig{i}; - - % Попытка найти параметр чекбокса Tab__Enable - checkboxName = ['Tab_' periph '_Enable']; - if ismember(checkboxName, paramsAll) - % Чекбокс есть - проверяем его состояние - paramObj = mask.getParameter(checkboxName); - val = paramObj.Value; - - try - tab = container.getDialogControl(periph); - if strcmpi(val, 'off') - tab.Enabled = 'off'; - % Рекурсивно очищаем связанные скрытые параметры - periphConfig.clear_tab_params(mask, config.(periph), periph); - else - tab.Enabled = 'on'; - periphConfig.sync_tab_params(mask, config.(periph), periph); - end - catch - warning('Вкладка с именем "%s" не найдена.', periph); - end - - else - % Чекбокса нет — просто пытаемся включить вкладку, если она есть - try - tab = container.getDialogControl(periph); - tab.Enabled = 'on'; - % Опционально можно синхронизировать параметры, если есть config поле - if isfield(config, periph) - periphConfig.sync_tab_params(mask, config.(periph), periph); - end - catch - % Можно не выводить предупреждение — вкладка может быть необязательной - % warning('Вкладка с именем "%s" не найдена.', periph); - end - end - end - - periphConfig.getWrapperCode(); - end - - - - - - - function update_callback(paramName) - blockPath = gcb; - mask = Simulink.Mask.get(blockPath); - hObj = mask.getParameter(paramName); - config = periphConfig.read_config(blockPath); - container = mask.getDialogControl('configTabAll'); - - % === Проверка на Tab__Enable === - exprTab = '^Tab_(\w+)_Enable$'; - tokensTab = regexp(paramName, exprTab, 'tokens'); - - if ~isempty(tokensTab) - periph = tokensTab{1}{1}; - - try - tab = container.getDialogControl(periph); - paramVal = hObj.Value; - - if strcmpi(paramVal, 'off') - tab.Enabled = 'off'; - if isfield(config, periph) - periphConfig.clear_tab_params(mask, config.(periph), periph); - end - else - tab.Enabled = 'on'; - if isfield(config, periph) - periphConfig.sync_tab_params(mask, config.(periph), periph); - end - end - catch - warning('Ошибка обработки вкладки "%s".', periph); - end - - periphConfig.getWrapperCode(); - return; - end - - % === Проверка на параметр, связанный с Sources/Includes === - % Проверка: это параметр, у которого есть соответствующий Hidden__Sources или Hidden__Includes - nameBase = paramName; - - paramNames = string({mask.Parameters.Name}); - hasSources = any(paramNames == "Hidden_" + nameBase + "_Sources"); - hasIncludes = any(paramNames == "Hidden_" + nameBase + "_Includes"); - - - if hasSources || hasIncludes - useVal = hObj.Value; - - % Получаем содержимое config по nameBase — возможно, вложенное - try - valueStruct = periphConfig.get_field(config, nameBase); - catch - warning('Не удалось найти путь %s в config.', nameBase); - return; - end - - if strcmpi(useVal, 'on') - try - periphConfig.store_single_periph_code(mask, nameBase, valueStruct); - catch - warning('Не удалось сохранить параметры для %s.', nameBase); - end - else - periphConfig.clear_single_periph_code_param(mask, nameBase); - end - - periphConfig.getWrapperCode(); - return; - end - - - % === Если не подошло ни под одно из условий === - % warning('Объект "%s" не поддерживается универсальным коллбеком.', paramName); - 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 @@ -372,39 +361,6 @@ classdef periphConfig end end - - function short = get_final_name_from_prefix(prefix) - % Берёт последнее имя после "_" (читаемое имя) - parts = strsplit(prefix, '_'); - short = parts{end}; - end - - - function value = get_field(configStruct, targetConfig) - value = []; - fields = fieldnames(configStruct); - - for i = 1:numel(fields) - key = fields{i}; - if strcmp(key, targetConfig) - value = configStruct.(key); - return; % нашли и возвращаем - elseif isstruct(configStruct.(key)) - value = periphConfig.get_field(configStruct.(key), targetConfig); - if ~isempty(value) - return; % нашли во вложенной структуре - end - end - end - - % Если не нашли, можно выбросить ошибку или вернуть пустое - if isempty(value) - % error('Поле "%s" не найдено в структуре.', targetConfig); - end - end - - - function create_all_code_storage_params(blockPath, config) mask = Simulink.Mask.get(blockPath); @@ -454,26 +410,6 @@ classdef periphConfig end - - function addHiddenParam(mask, containerName, nameBase, kind, existingParams) - % Преобразуем к красивому имени - prettyName = strrep(nameBase, '_', ' '); - paramName = ['Hidden_' nameBase '_' kind]; - if ismember(paramName, existingParams) - return; - end - - mask.addParameter( ... - 'Name', paramName, ... - 'Type', 'edit', ... - 'Prompt', ['Hidden ' prettyName ' ' kind], ... - 'Value', '', ... - 'Visible', 'off', ... - 'Container', containerName ... - ); - fprintf('Создан скрытый параметр: %s\n', paramName); - end - function cleanup_obsolete_code_params(blockPath, config) mask = Simulink.Mask.get(blockPath); maskParams = mask.Parameters; @@ -552,66 +488,38 @@ classdef periphConfig 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 + function clear_all_from_container(mask, containerName) + % allControls = mask.getDialogControls(); + container = mask.getDialogControl(containerName); + if isempty(container) + warning('Контейнер "%s" не найден.', containerName); + return; end - end - - function store_single_periph_code(mask, periph, code) - % Сохраняем Sources, если они есть - if isfield(code, 'Sources') - paramName = ['Hidden_' char(periph) '_Sources']; + + % Рекурсивно собрать все параметры (не вкладки) + paramsToDelete = mcuMask.collect_all_parameters(container); + + % Удаляем все параметры + for i = 1:numel(paramsToDelete) try - param = mask.getParameter(paramName); - param.Value = periphConfig.convert_code_value(code.Sources); + mask.removeParameter(paramsToDelete{i}); catch - mcuMask.disp(0, ['Параметр ' paramName ' не найден']); + warning('Не удалось удалить параметр %s', paramsToDelete{i}); end end - % Сохраняем Includes, если они есть - if isfield(code, 'Includes') - paramName = ['Hidden_' periph '_Includes']; - try - param = mask.getParameter(paramName); - param.Value = periphConfig.convert_code_value(code.Includes); - 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 + % Рекурсивно удалить все вкладки внутри контейнера + mcuMask.delete_all_tabs(mask, container); end + + + function res = addCodeBat(codeConfig, codePath) + % Добавить сурсы и пути в батник % Возвращает 0 при успехе, 1 при ошибке try % Формируем строки @@ -626,8 +534,8 @@ classdef periphConfig end end - function res = addUserFunctions(userCodeConfig) + % Добавить функции и дефайны в исходный код wrapper % userCodeConfig — структура config.UserCode initFuncsText = ''; @@ -652,12 +560,11 @@ classdef periphConfig deinitFuncsText = strjoin(strcat('\t', deinitFuncs, ';'), '\n'); end - res = periphConfig.updateWrapperCode(initFuncsText, simFuncsText, deinitFuncsText); + res = periphConfig.writeWrapperCode(initFuncsText, simFuncsText, deinitFuncsText); end end - - function res = updateWrapperCode(initFuncsText, simFuncsText, deinitFuncsText) + function res = writeWrapperCode(initFuncsText, simFuncsText, deinitFuncsText) % Входные параметры: % srcText - текст для записи set code_... % incText - текст для записи set includes_... @@ -686,7 +593,7 @@ classdef periphConfig error('Ошибка: неудачная запись в файл при записи файла: %s', ME.message); end end - + function addConfig(mask, containerName, periphName, defPrompt, def, rowCountMap, rowWidth) % mask — объект маски Simulink.Mask.get(blockPath) % containerName — имя контейнера, в который добавляем параметр (например, 'configTabAll') @@ -791,33 +698,99 @@ classdef periphConfig end end - callback = sprintf('periphConfig.update_callback("%s");', paramName); + callback = sprintf('periphConfig.periphParamCallback("%s");', paramName); param.Callback = callback; end - function clear_all_from_container(mask, containerName) - % allControls = mask.getDialogControls(); - container = mask.getDialogControl(containerName); - if isempty(container) - warning('Контейнер "%s" не найден.', containerName); - return; - end + + %% ELEMENTARY - % Рекурсивно собрать все параметры (не вкладки) - paramsToDelete = mcuMask.collect_all_parameters(container); + function clear_single_periph_code_param(mask, periph) + % Очистка кода одного поля конфига + paramNames = { + ['Hidden_' periph '_Sources'], + ['Hidden_' periph '_Includes'] + }; - % Удаляем все параметры - for i = 1:numel(paramsToDelete) + for i = 1:numel(paramNames) + paramName = paramNames{i}; try - mask.removeParameter(paramsToDelete{i}); + param = mask.getParameter(paramName); + param.Value = ''; catch - warning('Не удалось удалить параметр %s', paramsToDelete{i}); + % Параметр не существует — ничего не делаем + end + end + end + + function store_single_periph_code(mask, periph, code) + % Запись кода одного поля конфига + % Сохраняем Sources, если они есть + if isfield(code, 'Sources') + paramName = ['Hidden_' char(periph) '_Sources']; + try + param = mask.getParameter(paramName); + param.Value = periphConfig.convert_code_value(code.Sources); + catch + mcuMask.disp(0, ['Параметр ' paramName ' не найден']); end end - % Рекурсивно удалить все вкладки внутри контейнера - mcuMask.delete_all_tabs(mask, container); + % Сохраняем Includes, если они есть + if isfield(code, 'Includes') + paramName = ['Hidden_' periph '_Includes']; + try + param = mask.getParameter(paramName); + param.Value = periphConfig.convert_code_value(code.Includes); + catch + mcuMask.disp(0, ['Параметр ' paramName ' не найден']); + end + end + end + + function value = get_field(configStruct, targetConfig) + % получить targetConfig структуру из конфига (для глубоко вложенных) + value = []; + fields = fieldnames(configStruct); + + for i = 1:numel(fields) + key = fields{i}; + if strcmp(key, targetConfig) + value = configStruct.(key); + return; % нашли и возвращаем + elseif isstruct(configStruct.(key)) + value = periphConfig.get_field(configStruct.(key), targetConfig); + if ~isempty(value) + return; % нашли во вложенной структуре + end + end + end + + % Если не нашли, можно выбросить ошибку или вернуть пустое + if isempty(value) + % error('Поле "%s" не найдено в структуре.', targetConfig); + end + end + + function short = get_final_name_from_prefix(prefix) + % Берёт последнее имя после "_" (читаемое имя) + parts = strsplit(prefix, '_'); + short = parts{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 = ternary(cond, valTrue, valFalse)