diff --git a/McuLib/m/periphConfig.m b/McuLib/m/periphConfig.m index b528fd2..3b7aff2 100644 --- a/McuLib/m/periphConfig.m +++ b/McuLib/m/periphConfig.m @@ -54,9 +54,11 @@ classdef periphConfig end end periphConfig.create_all_code_storage_params(blockPath, config); - periphConfig.cleanup_obsolete_code_params(blockPath, config); + % periphConfig.cleanup_obsolete_code_params(blockPath, config); + + periphConfig.update_all(); - % Восстанавливаем таблицы + % Восстанавлиperiph = allTabNamesваем таблицы customtable.restore_all_tables(tableNames, columns_backup); catch % Восстанавливаем таблицы @@ -157,65 +159,149 @@ classdef periphConfig fclose(fid); end - function update_callback() + function update_all() 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__Enable - expr = '^Tab_(\w+)_Enable$'; - tokens = regexp(name, expr, 'tokens'); - - if ~isempty(tokens) - periph = tokens{1}{1}; % Извлекаем имя вкладки - paramObj = mask.getParameter(name); + % Получаем все имена в кладок из 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; - - if strcmpi(val, 'off') - % Найдём вкладку по имени - try - tab = container.getDialogControl(periph); + + try + tab = container.getDialogControl(periph); + if strcmpi(val, 'off') tab.Enabled = 'off'; - periphConfig.clear_single_periph_code_param(mask, periph); - catch - warning('Вкладка с именем "%s" не найдена.', periph); - end - else - % Найдём вкладку по имени - try - tab = container.getDialogControl(periph); + % Рекурсивно очищаем связанные скрытые параметры + periphConfig.clear_tab_params(mask, config.(periph), periph); + else tab.Enabled = 'on'; - periphConfig.store_single_periph_code(mask, periph, config.(periph)); - catch - warning('Вкладка с именем "%s" не найдена.', periph); + 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 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); @@ -231,53 +317,162 @@ classdef periphConfig 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); + function clear_tab_params(mask, configStruct, prefix, depth) + if nargin < 4 + depth = 0; + end + maxDepth = 3; % Максимальная глубина рекурсии + + fields = fieldnames(configStruct); - periphs = fieldnames(config); - for i = 1:numel(periphs) - periph = periphs{i}; + for i = 1:numel(fields) + key = fields{i}; + value = configStruct.(key); + paramName = [prefix '_' key]; - % Проверяем, нужно ли создавать параметры - 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); + if isstruct(value) + if depth < maxDepth + % Рекурсивный вызов для вложенных структур с увеличением глубины + periphConfig.clear_tab_params(mask, value, paramName, depth + 1); 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); + else + if strcmp(key, 'Sources') || strcmp(key, 'Includes') + baseName = periphConfig.get_final_name_from_prefix(prefix); + periphConfig.clear_single_periph_code_param(mask, baseName); end end end end + + + function sync_tab_params(mask, configStruct, prefix, depth) + if nargin < 4 + depth = 0; + end + maxDepth = 3; % Максимальная глубина рекурсии + + fields = fieldnames(configStruct); + + for i = 1:numel(fields) + key = fields{i}; + value = configStruct.(key); + paramName = [prefix '_' key]; + + if isstruct(value) + if depth < maxDepth + % Рекурсивный вызов для вложенных структур с увеличением глубины + periphConfig.sync_tab_params(mask, value, paramName, depth + 1); + end + else + if strcmp(key, 'Sources') || strcmp(key, 'Includes') + baseName = periphConfig.get_final_name_from_prefix(prefix); + periphConfig.store_single_periph_code(mask, baseName, configStruct); + end + end + 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); + + containerName = 'configTabAll'; + container = mask.getDialogControl(containerName); + existingParams = mcuMask.collect_all_parameters(container); + + % Убедимся, что контейнер существует + tabName = 'hiddenCodeTab'; + tab = mask.getDialogControl(tabName); + if isempty(tab) + tab = container.addDialogControl('tab', tabName); + tab.Prompt = 'Hidden Code Settings'; + tab.Visible = 'off'; + else + tab.Visible = 'off'; + end + + % Запуск рекурсивного обхода + periphConfig.process_struct_recursive(mask, tabName, config, {}, existingParams); + end + + function process_struct_recursive(mask, tabName, currentStruct, nameStack, existingParams) + fields = fieldnames(currentStruct); + for i = 1:numel(fields) + fieldName = fields{i}; + value = currentStruct.(fieldName); + newStack = [nameStack, fieldName]; % Добавляем уровень к имени + + % Если value — структура, обходим дальше + if isstruct(value) + % Проверяем: это структура с Sources/Includes или просто промежуточный узел? + hasSources = isfield(value, 'Sources'); + hasIncludes = isfield(value, 'Includes'); + + if hasSources + periphConfig.addHiddenParam(mask, tabName, fieldName, 'Sources', existingParams); + end + if hasIncludes + periphConfig.addHiddenParam(mask, tabName, fieldName, 'Includes', existingParams); + end + + % Рекурсивно продолжаем обход + periphConfig.process_struct_recursive(mask, tabName, value, newStack, existingParams); + end + end + 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); @@ -357,8 +552,6 @@ classdef periphConfig codeStruct.Includes = allIncludes; end - - function clear_single_periph_code_param(mask, periph) % Формируем имена параметров paramNames = { @@ -380,7 +573,7 @@ classdef periphConfig function store_single_periph_code(mask, periph, code) % Сохраняем Sources, если они есть if isfield(code, 'Sources') - paramName = ['Hidden_' periph '_Sources']; + paramName = ['Hidden_' char(periph) '_Sources']; try param = mask.getParameter(paramName); param.Value = periphConfig.convert_code_value(code.Sources); @@ -411,7 +604,7 @@ classdef periphConfig elseif iscell(codeField) value = strjoin(codeField, newline); else - warning('Неподдерживаемый тип данных: сохранено как пустая строка'); + % warning('Неподдерживаемый тип данных: сохранено как пустая строка'); value = ''; end end @@ -598,7 +791,8 @@ classdef periphConfig end end - param.Callback = 'periphConfig.update_callback();'; + callback = sprintf('periphConfig.update_callback("%s");', paramName); + param.Callback = callback; end