diff --git a/config_reader.asv b/config_reader.asv deleted file mode 100644 index bd9c938..0000000 --- a/config_reader.asv +++ /dev/null @@ -1,159 +0,0 @@ -clear; clc; close all; - -model = 'mcu_test_r2023'; -block = [model '/MCU_UPP']; - -load_system(model); % если модель ещё не загружена - -update_mask_from_config(block); - -disp('Маска обновлена по конфигу.'); -function update_mask_from_config(blockPath) - config = load_periph_config(); - mask = Simulink.Mask.get(blockPath); - - tabPrompt = 'Config Peripheral'; % Имя вкладки (Prompt) - - allControls = mask.getDialogControls(); - tabCtrl = find_tab_by_prompt(allControls, tabPrompt); - - if isempty(tabCtrl) - error('Вкладка с названием "%s" не найдена в маске', tabPrompt); - end - - % Удаляем все контролы внутри вкладки - children = tabCtrl.DialogControls; - while ~isempty(children) - tabCtrl.removeControl(children(1)); - children = tabCtrl.DialogControls; % обновляем список после удаления - end - - periphs = fieldnames(config); - for i = 1:numel(periphs) - periph = periphs{i}; - defines = config.(periph).Defines; - defNames = fieldnames(defines); - - for j = 1:numel(defNames) - defPrompt = defNames{j}; - def = defines.(defPrompt); - - % Обрабатываем только checkbox и edit - switch lower(def.Type) - case 'checkbox' - paramType = 'checkbox'; - case 'edit' - paramType = 'edit'; - otherwise - continue; - end - - paramName = matlab.lang.makeValidName([periph '_' defPrompt]); - - val = def.Default; - if islogical(val) - if val - valStr = 'on'; - else - valStr = 'off'; - end - elseif isnumeric(val) - valStr = num2str(val); - elseif ischar(val) - valStr = val; - else - error('Unsupported default value type for %s.%s', periph, defPrompt); - end - - - % Добавляем параметр в маску (без TabName) - mask.addParameter(... - 'Type', paramType, ... - 'Prompt', defPrompt, ... - 'Name', paramName, ... - 'Value', valStr, ... - 'Container', tabPrompt); - - disp(['paramType = ', paramType]); - disp(['paramName = ', paramName]); - end - end -end - - - - - - -function config = load_periph_config() - jsonText = fileread('periph_config.json'); - config = jsondecode(jsonText); -end - - -function clear_params_from_tab(blockPath, tabPrompt) - mask = Simulink.Mask.get(blockPath); - controls = mask.getDialogControls; - tabs = controls(strcmp({controls.Type}, 'tab')); - - tabName = ''; - for i = 1:numel(tabs) - if strcmp(tabs(i).Prompt, tabPrompt) - tabName = tabs(i).Name; % внутреннее имя вкладки - break; - end - end - - if isempty(tabName) - error('Вкладка с названием "%s" не найдена.', tabPrompt); - end - - % Удаляем параметры с TabName == tabName - i = 1; - while i <= numel(mask.Parameters) - if strcmp(mask.Parameters(i).TabName, tabName) - mask.removeParameter(i); - else - i = i + 1; - end - end -end - - -%% поиск вкладки -function tab = find_tab_by_name(controls, targetName) - tab = []; - - for i = 1:numel(controls) - ctrl = controls(i); - - % Проверяем, вкладка ли это и совпадает ли имя - if isa(ctrl, 'Simulink.dialog.Tab') && strcmp(ctrl.Name, targetName) - tab = ctrl; - return; - end - - % Если это контейнер — обходим его детей - children = get_children(ctrl); - if ~isempty(children) - tab = find_tab_by_name(children, targetName); - if ~isempty(tab) - return; - end - end - end -end - - -function children = get_children(ctrl) - if isprop(ctrl, 'DialogControls') - children = ctrl.DialogControls; - elseif isprop(ctrl, 'Controls') - children = ctrl.Controls; - elseif isprop(ctrl, 'Children') - children = ctrl.Children; - else - children = []; - end -end - diff --git a/config_reader.m b/config_reader.m deleted file mode 100644 index 5915015..0000000 --- a/config_reader.m +++ /dev/null @@ -1,134 +0,0 @@ -clear; clc; close all; - -model = 'mcu_test_r2023'; -block = [model '/MCU_UPP']; - -load_system(model); % если модель ещё не загружена -config = load_periph_config(); -update_mask_from_config(block, config); - -disp('Маска обновлена по конфигу'); - - - - - -%% чтение конфигов -function config = load_periph_config() - jsonText = fileread('periph_config.json'); - config = jsondecode(jsonText); -end - -%% запись дефайнов по конфигу - - -%% обновление маски -function update_mask_from_config(blockPath, config) - mask = Simulink.Mask.get(blockPath); - - tabName = 'configTab'; % Имя вкладки (Prompt) - - allControls = mask.getDialogControls(); - tabCtrl = find_tab_by_name(allControls, tabName); - - if isempty(tabCtrl) - error('Вкладка с названием "%s" не найдена в маске', tabName); - end - - % Удаляем все контролы внутри вкладки - children = tabCtrl.DialogControls; - while ~isempty(children) - mask.removeParameter(children(1).Name); - children = tabCtrl.DialogControls; % обновляем список после удаления - end - - periphs = fieldnames(config); - for i = 1:numel(periphs) - periph = periphs{i}; - defines = config.(periph).Defines; - defNames = fieldnames(defines); - - for j = 1:numel(defNames) - defPrompt = defNames{j}; - def = defines.(defPrompt); - - % Обрабатываем только checkbox и edit - switch lower(def.Type) - case 'checkbox' - paramType = 'checkbox'; - case 'edit' - paramType = 'edit'; - otherwise - continue; - end - - paramName = matlab.lang.makeValidName([defPrompt]); - - val = def.Default; - if islogical(val) - if val - valStr = 'on'; - else - valStr = 'off'; - end - elseif isnumeric(val) - valStr = num2str(val); - elseif ischar(val) - valStr = val; - else - error('Unsupported default value type for %s.%s', periph, defPrompt); - end - - - % Добавляем параметр в маску (без TabName) - mask.addParameter(... - 'Type', paramType, ... - 'Prompt', defPrompt, ... - 'Name', paramName, ... - 'Value', valStr, ... - 'Container', tabName); - - param = mask.getParameter(paramName); - param.Alias = def.Def; - end - end -end - - -%% поиск вкладки -function tab = find_tab_by_name(controls, targetName) - tab = []; - - for i = 1:numel(controls) - ctrl = controls(i); - - % Проверяем, вкладка ли это и совпадает ли имя - if isa(ctrl, 'Simulink.dialog.Tab') && strcmp(ctrl.Name, targetName) - tab = ctrl; - return; - end - - % Если это контейнер — обходим его детей - children = get_children(ctrl); - if ~isempty(children) - tab = find_tab_by_name(children, targetName); - if ~isempty(tab) - return; - end - end - end -end - - -function children = get_children(ctrl) - if isprop(ctrl, 'DialogControls') - children = ctrl.DialogControls; - elseif isprop(ctrl, 'Controls') - children = ctrl.Controls; - elseif isprop(ctrl, 'Children') - children = ctrl.Children; - else - children = []; - end -end - diff --git a/mcu_test_r2023.slx b/mcu_test_r2023.slx index 2e50257..07ba88c 100644 Binary files a/mcu_test_r2023.slx and b/mcu_test_r2023.slx differ diff --git a/mexing.asv b/mexing.asv index 1f19a0c..384b703 100644 --- a/mexing.asv +++ b/mexing.asv @@ -1,59 +1,54 @@ % Компилирует S-function -clear, clc -close; - - -Ts = 0.00001; - -delete("*.mexw64") -delete("*.mexw64.pdb") -delete(".\MCU_Wrapper\Outputs\*.*"); -set_param(gcb, 'consoleOutput', ''); - -% Флаг режима отладки -definesWrapperArg = buildWrapperDefinesString(); -definesUserArg = parseDefinesMaskText(); -definesConfigArg = buildConfigDefinesString(); -definesAllArg = [definesUserArg + " " + definesWrapperArg + " " + definesConfigArg]; - - -if read_checkbox('enableDebug') - modeArg = "debug"; -else - modeArg = "release"; +function mexing(compile_mode) + global Ts + Ts = 0.00001; + + if compile_mode == 1 + delete("*.mexw64") + delete("*.mexw64.pdb") + delete(".\MCU_Wrapper\Outputs\*.*"); + set_param(gcb, 'consoleOutput', ''); + % Дефайны + definesWrapperArg = buildWrapperDefinesString(); + definesUserArg = parseDefinesMaskText(); + definesConfigArg = buildConfigDefinesString(); + definesAllArg = [definesUserArg + " " + definesWrapperArg + " " + definesConfigArg]; + + %режимы компиляции + if read_checkbox('enableDebug') + modeArg = "debug"; + else + modeArg = "release"; + end + if read_checkbox('fullOutput') || read_checkbox('extConsol') + echoArg = 'echo_enable'; + else + echoArg = 'echo_disable'; + end + + [includesArg, codeArg] = make_mex_arguments('incTable', 'srcTable'); + + % Вызов батника с двумя параметрами: includes и code + cmd = sprintf('.\\MCU_Wrapper\\run_mex.bat "%s" "%s" "%s" %s %s', includesArg, codeArg, definesAllArg, modeArg, echoArg); + + if read_checkbox('extConsol') + cmdout = runBatAndShowOutput(cmd); + else + [status, cmdout]= system(cmd); + end + + % Сохраним вывод в параметр маски с именем 'consoleOutput' + set_param(gcb, 'consoleOutput', cmdout); + + beep + else + blockPath = bdroot; + config = load_periph_config(); + update_mask_from_config(blockPath, config); + % set_param(gcb, 'consoleOutput', 'Peripheral configuration file loaded. Re-open Block Parameters'); + end end -if read_checkbox('fullOutput') || read_checkbox('extConsol') - echoArg = 'echo_enable'; -else - echoArg = 'echo_disable'; - -end - -[includesArg, codeArg] = make_mex_arguments('incTable', 'srcTable'); - -% Вызов батника с двумя параметрами: includes и code -cmd = sprintf('.\\MCU_Wrapper\\run_mex.bat "%s" "%s" "%s" %s %s', includesArg, codeArg, definesAllArg, modeArg, echoArg); - - - -if read_checkbox('extConsol') - cmdout = runBatAndShowOutput(cmd); -else - [status, cmdout]= system(cmd); -end - -% Сохраним вывод в параметр маски с именем 'consoleOutput' -set_param(gcb, 'consoleOutput', cmdout); - -% % Обновим Mask Display для показа -% maskDisplayStr = sprintf('disp(''%s'')', cmdout); -% set_param(gcb, 'MaskDisplay', maskDisplayStr); - -beep - - - %% COMPILE PARAMS @@ -103,45 +98,11 @@ function definesWrapperArg = buildWrapperDefinesString() definesWrapperArg = addDefineByParam(definesWrapperArg, 'enableDeinit', 0); definesWrapperArg = addDefineByParam(definesWrapperArg, 'threadCycles', 1); definesWrapperArg = addDefineByParam(definesWrapperArg, 'mcuClk', 1); - - % blockHandle = gcbh; - % - % % Получаем MaskValues и MaskNames - % maskValues = get_param(blockHandle, 'MaskValues'); - % paramNames = get_param(blockHandle, 'MaskNames'); - % - % % Индексы параметров - % idxCycles = find(strcmp(paramNames, 'threadCycles')); - % idxClk = find(strcmp(paramNames, 'mcuClk')); - % - % % Значения - % cyclesVal = maskValues{idxCycles}; - % clkMHz = str2double(maskValues{idxClk}); - % clkHz = round(clkMHz * 1e6); - % - % % Формируем defines в формате: -D"NAME=VALUE" - % if read_checkbox('enableThreading') - % def1 = ['-D"RUN_APP_MAIN_FUNC_THREAD"']; - % else - % def1 = ['']; - % end - % - % if read_checkbox('enableDeinit') - % def2 = ['-D"DEINITIALIZE_AFTER_SIM"']; - % else - % def2 = ['']; - % end - % - % def3 = ['-D"DEKSTOP_CYCLES_FOR_MCU_APP__EQ__' cyclesVal '"']; - % def4 = ['-D"MCU_CORE_CLOCK__EQ__' num2str(clkHz) '"']; - % - % definesWrapperArg = strjoin({def1, def2, def3, def4}, ' '); end function definesUserArg = parseDefinesMaskText() blockHandle = gcbh; - % Получаем MaskValues и MaskNames maskValues = get_param(blockHandle, 'MaskValues'); paramNames = get_param(blockHandle, 'MaskNames'); @@ -208,52 +169,25 @@ function definesWrapperArg = buildConfigDefinesString() children = tabCtrl.DialogControls; for i = 1:numel(children) ctrl = children(i); -isprop(ctrl, 'Type') -isprop(ctrl, 'Name') - if isprop(ctrl, 'Type') && isprop(ctrl, 'Name') - switch lower(ctrl.Type) - case 'checkbox' - definesWrapperArg = addDefineByParam(definesWrapperArg, ctrl.ParameterName, 0); - case 'edit' - definesWrapperArg = addDefineByParam(definesWrapperArg, ctrl.ParameterName, 1); - otherwise - % Пропускаем другие типы + % Получаем имя параметра из контрола + paramName = ctrl.Name; + try + % Получаем объект параметра по имени + param = mask.getParameter(paramName); + + % Определяем тип параметра + switch lower(param.Type) + case 'checkbox' + definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, 0); + case 'edit' + definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, 1); + otherwise + % Необрабатываемые типы + end + catch ME + warning('Не удалось получить параметр "%s": %s', paramName, ME.message); end - end end - - % blockHandle = gcbh; - % - % % Получаем MaskValues и MaskNames - % maskValues = get_param(blockHandle, 'MaskValues'); - % paramNames = get_param(blockHandle, 'MaskNames'); - % - % % Индексы параметров - % idxCycles = find(strcmp(paramNames, 'threadCycles')); - % idxClk = find(strcmp(paramNames, 'mcuClk')); - % - % % Значения - % cyclesVal = maskValues{idxCycles}; - % clkMHz = str2double(maskValues{idxClk}); - % clkHz = round(clkMHz * 1e6); - % - % % Формируем defines в формате: -D"NAME=VALUE" - % if read_checkbox('enableThreading') - % def1 = ['-D"RUN_APP_MAIN_FUNC_THREAD"']; - % else - % def1 = ['']; - % end - % - % if read_checkbox('enableDeinit') - % def2 = ['-D"DEINITIALIZE_AFTER_SIM"']; - % else - % def2 = ['']; - % end - % - % def3 = ['-D"DEKSTOP_CYCLES_FOR_MCU_APP__EQ__' cyclesVal '"']; - % def4 = ['-D"MCU_CORE_CLOCK__EQ__' num2str(clkHz) '"']; - % - % definesWrapperArg = strjoin({def1, def2, def3, def4}, ' '); end @@ -344,42 +278,6 @@ function checkbox_state = read_checkbox(checkboxName) end end -function tab = find_tab_by_name(controls, targetName) - tab = []; - - for i = 1:numel(controls) - ctrl = controls(i); - - % Проверяем, вкладка ли это и совпадает ли имя - if isa(ctrl, 'Simulink.dialog.Tab') && strcmp(ctrl.Name, targetName) - tab = ctrl; - return; - end - - % Если это контейнер — обходим его детей - children = get_children(ctrl); - if ~isempty(children) - tab = find_tab_by_name(children, targetName); - if ~isempty(tab) - return; - end - end - end -end - - -function children = get_children(ctrl) - if isprop(ctrl, 'DialogControls') - children = ctrl.DialogControls; - elseif isprop(ctrl, 'Controls') - children = ctrl.Controls; - elseif isprop(ctrl, 'Children') - children = ctrl.Children; - else - children = []; - end -end - %% CONSOLE FUNCTIONS function cmdret = runBatAndShowOutput(cmd) @@ -456,3 +354,284 @@ function logWindow_append(line) jTextArea.setCaretPosition(jTextArea.getDocument.getLength); drawnow; end + + +%% READ CONFIGS +function config = load_periph_config() + jsonText = fileread('periph_config.json'); + config = jsondecode(jsonText); +end + +%% CONFIG MASK TOOLS +function update_mask_from_config(blockPath, config) + blockPath = [blockPath '/MCU']; + + % Проверяем, была ли маска открыта + wasOpen = isMaskDialogOpen(blockPath); + close_system(blockPath, 0); + mask = Simulink.Mask.get(blockPath); + + tableNames = {'incTable', 'srcTable'}; + columns_backup = clear_tables(blockPath, tableNames); + + containerName = 'configTabAll'; + clear_all_from_container(mask, containerName); + + % Ищем контейнер, в который будем добавлять вкладки + allControls = mask.getDialogControls(); + container = find_container_by_name(allControls, containerName); + if isempty(container) + error('Контейнер "%s" не найден в маске.', containerName); + end + + % Проходим по каждому модулю (ADC, TIM...) + periphs = fieldnames(config); + for i = 1:numel(periphs) + periph = periphs{i}; + defines = config.(periph).Defines; + defNames = fieldnames(defines); + + % Создаём вкладку для модуля + tabCtrl = mask.addDialogControl( ... + 'Type', 'Tab', ... + 'Prompt', periph, ... + 'Name', [periph '_Tab'], ... + 'Container', containerName ... + ); + + for j = 1:numel(defNames) + defPrompt = defNames{j}; + def = defines.(defPrompt); + + % Только checkbox и edit + switch lower(def.Type) + case 'checkbox' + paramType = 'checkbox'; + case 'edit' + paramType = 'edit'; + otherwise + continue; + end + + paramName = matlab.lang.makeValidName(defPrompt); + + % Преобразуем значение по типу + val = def.Default; + if islogical(val) + valStr = logical(val) * "on" + ~val * "off"; + elseif isnumeric(val) + valStr = num2str(val); + elseif ischar(val) + valStr = val; + else + error('Unsupported default value type for %s.%s', periph, defPrompt); + end + + % Добавляем параметр в соответствующую вкладку + mask.addParameter( ... + 'Type', paramType, ... + 'Prompt', defPrompt, ... + 'Name', paramName, ... + 'Value', valStr, ... + 'Container', periph ... + ); + + param = mask.getParameter(paramName); + param.Alias = def.Def; + end + end + + % Восстанавливаем таблицы + restore_tables(blockPath, tableNames, columns_backup); + + % Повторно открываем маску, если она была открыта + if wasOpen + open_system(blockPath, 'mask'); + end +end + + + +function clear_all_from_container(mask, containerName) +% Очищает все параметры и вкладки внутри указанного контейнера + + allControls = mask.getDialogControls(); + container = find_container_by_name(allControls, containerName); + + if isempty(container) + error('Контейнер с именем "%s" не найден.', containerName); + end + + children = container.DialogControls; + + for i = numel(children):-1:1 + ctrl = children(i); + try + % Пытаемся удалить как контрол (вкладка, контейнер и пр.) + mask.removeDialogControl(ctrl.Name); + catch + try + % Если не получилось — пробуем как параметр + res = mask.removeParameter(ctrl.Name); + catch + warning('Не удалось удалить "%s".', ctrl.Name); + end + end + end +end + + +function isOpen = isMaskDialogOpen(blockPath) + isOpen = false; + + try + % Получаем имя блока + blockName = get_param(blockPath, 'Name'); + + % Получаем список окон MATLAB GUI + jWindows = java.awt.Window.getWindows(); + + for i = 1:numel(jWindows) + win = jWindows(i); + + % Проверка, что окно видимое и активно + if win.isShowing() + try + title = char(win.getTitle()); + % Проверка по ключевому слову, соответствующему заголовку маски + if contains(title, ['Mask Editor: ' blockName]) || ... + contains(title, ['Mask: ' blockName]) || ... + contains(title, blockName) + isOpen = true; + return; + end + catch + % Окно не имеет заголовка — пропускаем + end + end + end + catch + isOpen = false; + end +end + + + +function column_titles = clear_tables(block, table_names) + % Очищает столбцы в каждой таблице из массива имен table_names + % Возвращает cell-массив с названиями первых столбцов каждой таблицы + + % Получить объект маски блока + maskObj = Simulink.Mask.get(block); + + % Инициализировать cell-массив для хранения названий столбцов + column_titles = cell(size(table_names)); + + for k = 1:numel(table_names) + table_name = table_names{k}; + + % Получить объект управления таблицей + tableControl = maskObj.getDialogControl(table_name); + + % Получить количество столбцов + nCols = tableControl.getNumberOfColumns; + + if nCols > 0 + % Получить первый столбец (который будем удалять) + column = tableControl.getColumn(1); + column_titles{k} = column.Name; + + % Удаляем все столбцы + % Важно: при удалении столбцов индексы меняются, + % поэтому удаляем всегда первый столбец nCols раз + for i = 1:nCols + tableControl.removeColumn(1); + end + else + % Если столбцов нет, возвращаем пустую строку + column_titles{k} = ''; + end + end +end + +function restore_tables(block, table_names, column_titles) + % Восстанавливает первый столбец в каждой таблице из массива имен + % Использует массив column_titles для установки имени столбца + + % Получить объект маски блока + maskObj = Simulink.Mask.get(block); + + for k = 1:numel(table_names) + table_name = table_names{k}; + title = column_titles{k}; + + % Получить объект управления таблицей + tableControl = maskObj.getDialogControl(table_name); + + % Добавить новый столбец + column = tableControl.addColumn(Name='title', Type='edit'); + column.Name = title; + end +end + + + + +function tab = find_tab_by_name(controls, targetName) + tab = []; + + for i = 1:numel(controls) + ctrl = controls(i); + + % Проверяем, вкладка ли это и совпадает ли имя + if isa(ctrl, 'Simulink.dialog.Tab') && strcmp(ctrl.Name, targetName) + tab = ctrl; + return; + end + + % Если это контейнер — обходим его детей + children = get_children(ctrl); + if ~isempty(children) + tab = find_tab_by_name(children, targetName); + if ~isempty(tab) + return; + end + end + end +end + +function container = find_container_by_name(controls, targetName) + container = []; + + for i = 1:numel(controls) + ctrl = controls(i); + + % Проверяем, контейнер ли это и совпадает ли имя + if isa(ctrl, 'Simulink.dialog.Container') && strcmp(ctrl.Name, targetName) + container = ctrl; + return; + end + + % Если это вложенный контрол — обходим его детей + children = get_children(ctrl); + if ~isempty(children) + container = find_container_by_name(children, targetName); + if ~isempty(container) + return; + end + end + end +end + + +function children = get_children(ctrl) + if isprop(ctrl, 'DialogControls') + children = ctrl.DialogControls; + elseif isprop(ctrl, 'Controls') + children = ctrl.Controls; + elseif isprop(ctrl, 'Children') + children = ctrl.Children; + else + children = []; + end +end diff --git a/mexing.m b/mexing.m index dae1b07..3be5e77 100644 --- a/mexing.m +++ b/mexing.m @@ -1,59 +1,54 @@ % Компилирует S-function -clear, clc -close; - - -Ts = 0.00001; - -delete("*.mexw64") -delete("*.mexw64.pdb") -delete(".\MCU_Wrapper\Outputs\*.*"); -set_param(gcb, 'consoleOutput', ''); - -% Флаг режима отладки -definesWrapperArg = buildWrapperDefinesString(); -definesUserArg = parseDefinesMaskText(); -definesConfigArg = buildConfigDefinesString(); -definesAllArg = [definesUserArg + " " + definesWrapperArg + " " + definesConfigArg]; - - -if read_checkbox('enableDebug') - modeArg = "debug"; -else - modeArg = "release"; +function mexing(compile_mode) + global Ts + Ts = 0.00001; + + if compile_mode == 1 + delete("*.mexw64") + delete("*.mexw64.pdb") + delete(".\MCU_Wrapper\Outputs\*.*"); + set_param(gcb, 'consoleOutput', ''); + % Дефайны + definesWrapperArg = buildWrapperDefinesString(); + definesUserArg = parseDefinesMaskText(); + definesConfigArg = buildConfigDefinesString(); + definesAllArg = [definesUserArg + " " + definesWrapperArg + " " + definesConfigArg]; + + %режимы компиляции + if read_checkbox('enableDebug') + modeArg = "debug"; + else + modeArg = "release"; + end + if read_checkbox('fullOutput') || read_checkbox('extConsol') + echoArg = 'echo_enable'; + else + echoArg = 'echo_disable'; + end + + [includesArg, codeArg] = make_mex_arguments('incTable', 'srcTable'); + + % Вызов батника с двумя параметрами: includes и code + cmd = sprintf('.\\MCU_Wrapper\\run_mex.bat "%s" "%s" "%s" %s %s', includesArg, codeArg, definesAllArg, modeArg, echoArg); + + if read_checkbox('extConsol') + cmdout = runBatAndShowOutput(cmd); + else + [status, cmdout]= system(cmd); + end + + % Сохраним вывод в параметр маски с именем 'consoleOutput' + set_param(gcb, 'consoleOutput', cmdout); + + beep + else + blockPath = bdroot; + config = load_periph_config(); + update_mask_from_config(blockPath, config); + % set_param(gcb, 'consoleOutput', 'Peripheral configuration file loaded. Re-open Block Parameters'); + end end -if read_checkbox('fullOutput') || read_checkbox('extConsol') - echoArg = 'echo_enable'; -else - echoArg = 'echo_disable'; - -end - -[includesArg, codeArg] = make_mex_arguments('incTable', 'srcTable'); - -% Вызов батника с двумя параметрами: includes и code -cmd = sprintf('.\\MCU_Wrapper\\run_mex.bat "%s" "%s" "%s" %s %s', includesArg, codeArg, definesAllArg, modeArg, echoArg); - - - -if read_checkbox('extConsol') - cmdout = runBatAndShowOutput(cmd); -else - [status, cmdout]= system(cmd); -end - -% Сохраним вывод в параметр маски с именем 'consoleOutput' -set_param(gcb, 'consoleOutput', cmdout); - -% % Обновим Mask Display для показа -% maskDisplayStr = sprintf('disp(''%s'')', cmdout); -% set_param(gcb, 'MaskDisplay', maskDisplayStr); - -beep - - - %% COMPILE PARAMS @@ -103,45 +98,11 @@ function definesWrapperArg = buildWrapperDefinesString() definesWrapperArg = addDefineByParam(definesWrapperArg, 'enableDeinit', 0); definesWrapperArg = addDefineByParam(definesWrapperArg, 'threadCycles', 1); definesWrapperArg = addDefineByParam(definesWrapperArg, 'mcuClk', 1); - - % blockHandle = gcbh; - % - % % Получаем MaskValues и MaskNames - % maskValues = get_param(blockHandle, 'MaskValues'); - % paramNames = get_param(blockHandle, 'MaskNames'); - % - % % Индексы параметров - % idxCycles = find(strcmp(paramNames, 'threadCycles')); - % idxClk = find(strcmp(paramNames, 'mcuClk')); - % - % % Значения - % cyclesVal = maskValues{idxCycles}; - % clkMHz = str2double(maskValues{idxClk}); - % clkHz = round(clkMHz * 1e6); - % - % % Формируем defines в формате: -D"NAME=VALUE" - % if read_checkbox('enableThreading') - % def1 = ['-D"RUN_APP_MAIN_FUNC_THREAD"']; - % else - % def1 = ['']; - % end - % - % if read_checkbox('enableDeinit') - % def2 = ['-D"DEINITIALIZE_AFTER_SIM"']; - % else - % def2 = ['']; - % end - % - % def3 = ['-D"DEKSTOP_CYCLES_FOR_MCU_APP__EQ__' cyclesVal '"']; - % def4 = ['-D"MCU_CORE_CLOCK__EQ__' num2str(clkHz) '"']; - % - % definesWrapperArg = strjoin({def1, def2, def3, def4}, ' '); end function definesUserArg = parseDefinesMaskText() blockHandle = gcbh; - % Получаем MaskValues и MaskNames maskValues = get_param(blockHandle, 'MaskValues'); paramNames = get_param(blockHandle, 'MaskNames'); @@ -227,39 +188,6 @@ function definesWrapperArg = buildConfigDefinesString() warning('Не удалось получить параметр "%s": %s', paramName, ME.message); end end - - % blockHandle = gcbh; - % - % % Получаем MaskValues и MaskNames - % maskValues = get_param(blockHandle, 'MaskValues'); - % paramNames = get_param(blockHandle, 'MaskNames'); - % - % % Индексы параметров - % idxCycles = find(strcmp(paramNames, 'threadCycles')); - % idxClk = find(strcmp(paramNames, 'mcuClk')); - % - % % Значения - % cyclesVal = maskValues{idxCycles}; - % clkMHz = str2double(maskValues{idxClk}); - % clkHz = round(clkMHz * 1e6); - % - % % Формируем defines в формате: -D"NAME=VALUE" - % if read_checkbox('enableThreading') - % def1 = ['-D"RUN_APP_MAIN_FUNC_THREAD"']; - % else - % def1 = ['']; - % end - % - % if read_checkbox('enableDeinit') - % def2 = ['-D"DEINITIALIZE_AFTER_SIM"']; - % else - % def2 = ['']; - % end - % - % def3 = ['-D"DEKSTOP_CYCLES_FOR_MCU_APP__EQ__' cyclesVal '"']; - % def4 = ['-D"MCU_CORE_CLOCK__EQ__' num2str(clkHz) '"']; - % - % definesWrapperArg = strjoin({def1, def2, def3, def4}, ' '); end @@ -350,42 +278,6 @@ function checkbox_state = read_checkbox(checkboxName) end end -function tab = find_tab_by_name(controls, targetName) - tab = []; - - for i = 1:numel(controls) - ctrl = controls(i); - - % Проверяем, вкладка ли это и совпадает ли имя - if isa(ctrl, 'Simulink.dialog.Tab') && strcmp(ctrl.Name, targetName) - tab = ctrl; - return; - end - - % Если это контейнер — обходим его детей - children = get_children(ctrl); - if ~isempty(children) - tab = find_tab_by_name(children, targetName); - if ~isempty(tab) - return; - end - end - end -end - - -function children = get_children(ctrl) - if isprop(ctrl, 'DialogControls') - children = ctrl.DialogControls; - elseif isprop(ctrl, 'Controls') - children = ctrl.Controls; - elseif isprop(ctrl, 'Children') - children = ctrl.Children; - else - children = []; - end -end - %% CONSOLE FUNCTIONS function cmdret = runBatAndShowOutput(cmd) @@ -462,3 +354,310 @@ function logWindow_append(line) jTextArea.setCaretPosition(jTextArea.getDocument.getLength); drawnow; end + + +%% READ CONFIGS +function config = load_periph_config() + jsonText = fileread('periph_config.json'); + config = jsondecode(jsonText); +end + +%% CONFIG MASK TOOLS +function update_mask_from_config(blockPath, config) + blockPath = [blockPath '/MCU']; + + % Проверяем, была ли маска открыта + wasOpen = isMaskDialogOpen(blockPath); + close_system(blockPath, 0); + mask = Simulink.Mask.get(blockPath); + + tableNames = {'incTable', 'srcTable'}; + columns_backup = clear_tables(blockPath, tableNames); + + containerName = 'configTabAll'; + clear_all_from_container(mask, containerName); + + % Ищем контейнер, в который будем добавлять вкладки + allControls = mask.getDialogControls(); + container = find_container_by_name(allControls, containerName); + if isempty(container) + error('Контейнер "%s" не найден в маске.', containerName); + end + + % Проходим по каждому модулю (ADC, TIM...) + periphs = fieldnames(config); + for i = 1:numel(periphs) + periph = periphs{i}; + defines = config.(periph).Defines; + defNames = fieldnames(defines); + + % Создаём вкладку для модуля + tabCtrl = mask.addDialogControl( ... + 'Type', 'Tab', ... + 'Prompt', periph, ... + 'Name', [periph '_Tab'], ... + 'Container', containerName ... + ); + + for j = 1:numel(defNames) + defPrompt = defNames{j}; + def = defines.(defPrompt); + + % Только checkbox и edit + switch lower(def.Type) + case 'checkbox' + paramType = 'checkbox'; + case 'edit' + paramType = 'edit'; + otherwise + continue; + end + + paramName = matlab.lang.makeValidName(defPrompt); + + % Преобразуем значение по типу + val = def.Default; + if islogical(val) + valStr = logical(val) * "on" + ~val * "off"; + elseif isnumeric(val) + valStr = num2str(val); + elseif ischar(val) + valStr = val; + else + error('Unsupported default value type for %s.%s', periph, defPrompt); + end + + % Добавляем параметр в соответствующую вкладку + mask.addParameter( ... + 'Type', paramType, ... + 'Prompt', defPrompt, ... + 'Name', paramName, ... + 'Value', valStr, ... + 'Container', periph ... + ); + + param = mask.getParameter(paramName); + param.Alias = def.Def; + end + end + + % Восстанавливаем таблицы + restore_tables(blockPath, tableNames, columns_backup); + + % Повторно открываем маску, если она была открыта + if wasOpen + open_system(blockPath, 'mask'); + end +end + +function clear_all_from_container(mask, containerName) + allControls = mask.getDialogControls(); + container = find_container_by_name(allControls, containerName); + if isempty(container) + warning('Контейнер "%s" не найден.', containerName); + return; + end + + % Рекурсивно собрать все параметры (не вкладки) + paramsToDelete = collect_all_parameters(container); + + % Удаляем все параметры + for i = 1:numel(paramsToDelete) + try + mask.removeParameter(paramsToDelete{i}); + catch + warning('Не удалось удалить параметр %s', paramsToDelete{i}); + end + end + + % Рекурсивно удалить все вкладки внутри контейнера + delete_all_tabs(mask, container); +end + +function params = collect_all_parameters(container) + params = {}; + children = container.DialogControls; + for i = 1:numel(children) + ctrl = children(i); + if isa(ctrl, 'Simulink.dialog.Tab') + % Если вкладка — рекурсивно собираем параметры внутри неё + params = [params, collect_all_parameters(ctrl)]; + else + % Иначе это параметр — добавляем имя + params{end+1} = ctrl.Name; %#ok + end + end +end + +function delete_all_tabs(mask, container) + children = container.DialogControls; + % Идём в обратном порядке, чтобы безопасно удалять + for i = numel(children):-1:1 + ctrl = children(i); + if isa(ctrl, 'Simulink.dialog.Tab') + % Рекурсивно удаляем вкладки внутри текущей вкладки + delete_all_tabs(mask, ctrl); + try + mask.removeParameter(ctrl.Name); + catch + warning('Не удалось удалить вкладку %s', ctrl.Name); + end + end + end +end + + +function isOpen = isMaskDialogOpen(blockPath) + isOpen = false; + + try + % Получаем имя блока + blockName = get_param(blockPath, 'Name'); + + % Получаем список окон MATLAB GUI + jWindows = java.awt.Window.getWindows(); + + for i = 1:numel(jWindows) + win = jWindows(i); + + % Проверка, что окно видимое и активно + if win.isShowing() + try + title = char(win.getTitle()); + % Проверка по ключевому слову, соответствующему заголовку маски + if contains(title, ['Mask Editor: ' blockName]) || ... + contains(title, ['Mask: ' blockName]) || ... + contains(title, blockName) + isOpen = true; + return; + end + catch + % Окно не имеет заголовка — пропускаем + end + end + end + catch + isOpen = false; + end +end + + + +function column_titles = clear_tables(block, table_names) + % Очищает столбцы в каждой таблице из массива имен table_names + % Возвращает cell-массив с названиями первых столбцов каждой таблицы + + % Получить объект маски блока + maskObj = Simulink.Mask.get(block); + + % Инициализировать cell-массив для хранения названий столбцов + column_titles = cell(size(table_names)); + + for k = 1:numel(table_names) + table_name = table_names{k}; + + % Получить объект управления таблицей + tableControl = maskObj.getDialogControl(table_name); + + % Получить количество столбцов + nCols = tableControl.getNumberOfColumns; + + if nCols > 0 + % Получить первый столбец (который будем удалять) + column = tableControl.getColumn(1); + column_titles{k} = column.Name; + + % Удаляем все столбцы + % Важно: при удалении столбцов индексы меняются, + % поэтому удаляем всегда первый столбец nCols раз + for i = 1:nCols + tableControl.removeColumn(1); + end + else + % Если столбцов нет, возвращаем пустую строку + column_titles{k} = ''; + end + end +end + +function restore_tables(block, table_names, column_titles) + % Восстанавливает первый столбец в каждой таблице из массива имен + % Использует массив column_titles для установки имени столбца + + % Получить объект маски блока + maskObj = Simulink.Mask.get(block); + + for k = 1:numel(table_names) + table_name = table_names{k}; + title = column_titles{k}; + + % Получить объект управления таблицей + tableControl = maskObj.getDialogControl(table_name); + + % Добавить новый столбец + column = tableControl.addColumn(Name='title', Type='edit'); + column.Name = title; + end +end + + + + +function tab = find_tab_by_name(controls, targetName) + tab = []; + + for i = 1:numel(controls) + ctrl = controls(i); + + % Проверяем, вкладка ли это и совпадает ли имя + if isa(ctrl, 'Simulink.dialog.Tab') && strcmp(ctrl.Name, targetName) + tab = ctrl; + return; + end + + % Если это контейнер — обходим его детей + children = get_children(ctrl); + if ~isempty(children) + tab = find_tab_by_name(children, targetName); + if ~isempty(tab) + return; + end + end + end +end + +function container = find_container_by_name(controls, targetName) + container = []; + + for i = 1:numel(controls) + ctrl = controls(i); + + % Проверяем, контейнер ли это и совпадает ли имя + if isa(ctrl, 'Simulink.dialog.Container') && strcmp(ctrl.Name, targetName) + container = ctrl; + return; + end + + % Если это вложенный контрол — обходим его детей + children = get_children(ctrl); + if ~isempty(children) + container = find_container_by_name(children, targetName); + if ~isempty(container) + return; + end + end + end +end + + +function children = get_children(ctrl) + if isprop(ctrl, 'DialogControls') + children = ctrl.DialogControls; + elseif isprop(ctrl, 'Controls') + children = ctrl.Controls; + elseif isprop(ctrl, 'Children') + children = ctrl.Children; + else + children = []; + end +end diff --git a/periph_config.asv b/periph_config.asv deleted file mode 100644 index 2b420ab..0000000 --- a/periph_config.asv +++ /dev/null @@ -1,38 +0,0 @@ -{ - "ADC": { - "Defines": { - "ADC1 Enable": { - "Def": "ADC1_ENABLE", - "Type": "checkbox", - "Default": true - }, - "ADC2 Enable": { - "Def": "ADC2_ENABLE", - "Type": "checkbox", - "Default": true - }, - "SAMPLE_RATE": { - "Type": "edit", - "Default": 48000 - } - } - }, - "TIM": { - "Defines": { - "TIM1 Enable": { - "Def": "ADC1_ENABLE", - "Type": "checkbox", - "Default": true - }, - "TIM2 Enable": { - "Def": "ADC2_ENABLE", - "Type": "checkbox", - "Default": true - }, - "AHB Buf Clock Rate": { - "Type": "edit", - "Default": 72 - } - } - } -}