From 1aa3c5b9556e0f8d96accf78fc18af6c3aef5626 Mon Sep 17 00:00:00 2001 From: Razvalyaev Date: Thu, 19 Jun 2025 13:29:33 +0300 Subject: [PATCH] pre-release 1.02 --- MCU Wrapper.mltbx | Bin 72680 -> 83927 bytes McuLib/lib/McuLib.slx | Bin 23048 -> 22975 bytes McuLib/m/appWrap.m | 154 +++++ McuLib/m/asynchManage.m | 4 +- McuLib/m/compiler.m | 140 +++++ McuLib/m/configJs.m | 143 +++++ McuLib/m/customtable.m | 16 +- McuLib/m/mainConfig.m | 274 +++++++++ McuLib/m/mainWrap.m | 72 +++ McuLib/m/mcuMask.m | 442 ++----------- McuLib/m/mcuPath.m | 168 +++++ McuLib/m/mcuPorts.m | 4 +- McuLib/m/mexing.m | 44 +- McuLib/m/periphConfig.m | 749 ++++++++++++++++------- McuLib/templates/MCU_Wrapper/run_mex.bat | 18 +- mcuwrapper.prj | 49 +- 16 files changed, 1623 insertions(+), 654 deletions(-) create mode 100644 McuLib/m/appWrap.m create mode 100644 McuLib/m/compiler.m create mode 100644 McuLib/m/configJs.m create mode 100644 McuLib/m/mainConfig.m create mode 100644 McuLib/m/mainWrap.m create mode 100644 McuLib/m/mcuPath.m diff --git a/MCU Wrapper.mltbx b/MCU Wrapper.mltbx index 72aabd78f93e32142f0eec03d84d2b18a265efb5..646b7745e77e327727c8c6618a824c72c9311182 100644 GIT binary patch delta 61786 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
^!MSzaJ_36#7pJ83+>_7x)1{JHhUv^?V;Z=jl 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 b%5;Zo`U-bOMLsnKH3lhe28JC1pl}BOzD#N@ diff --git a/McuLib/lib/McuLib.slx b/McuLib/lib/McuLib.slx index 7c4a3593e59de0de2c4e821727dd942317377820..a091a4cedf7c7261245c44981c860fdf833f3190 100644 GIT binary patch delta 10108 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 Se;LvLX(luXgrW0a=>GuLw=FgR delta 10145 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 diff --git a/McuLib/m/appWrap.m b/McuLib/m/appWrap.m new file mode 100644 index 0000000..cfb7a94 --- /dev/null +++ b/McuLib/m/appWrap.m @@ -0,0 +1,154 @@ +classdef appWrap + + methods(Static) + function appWrapperFunc() + block = gcb; + % Получаем имя функции и путь к файлам + [filename, section, tool, example]= appWrap.getAppWrapperUserFile(block); + mcuMask.tool(tool, example); + + % Загружаем содержимое файла + set_param(block, 'appWrapperCode', ''); + try + code = fileread(filename); + code = regexprep(code, '\r\n?', '\n'); % нормализуем окончания строк + + includesText = editCode.extractSection(code, section); + set_param(block, 'appWrapperCode', includesText); + catch + end + % % Поиск тела обычной функции + % expr = sprintf('void %s()', sel); + % funcBody = editCode.extractSection(code, expr); + % set_param(block, 'wrapperCode', funcBody); + end + + function saveAppWrapperCode() + block = gcb; + + % Получаем имя функции и путь к файлам + [filename, section] = appWrap.getAppWrapperUserFile(block); + if ~isfile(filename) + errordlg(['Файл не найден: ', filename]); + return; + end + + sel = get_param(block, 'appWrapperFunc'); + basePath = get_param(block, 'appWrapperPath'); + if isempty(basePath) + errordlg('Не указан путь к файлам обёртки (wrapperPath).'); + return; + end + newBody = get_param(block, 'appWrapperCode'); + code = fileread(filename); + code = regexprep(code, '\r\n?', '\n'); + newBody = strrep(newBody, '\', '\\'); + code = editCode.insertSection(code, section, newBody); + % else + % % Обновляем тело функции + % expr = sprintf('void %s()', sel); + % code = editCode.insertSection(code, expr, newBody); + % end + fid = fopen(filename, 'w', 'n', 'UTF-8'); + if fid == -1 + errordlg('Не удалось открыть файл для записи'); + return; + end + fwrite(fid, code); + fclose(fid); + mcuMask.disp(1, ['Обновлено: ' sel]); + end + + function openAppWrapperCode() + block = gcb; + + % Получаем имя функции и путь к файлам + filename = mcuPath.getAbsolutePath(appWrap.getAppWrapperUserFile(block)); + if exist(filename, 'file') == 2 + % Формируем команду без кавычек + cmd = sprintf('rundll32.exe shell32.dll,OpenAs_RunDLL %s', filename); + status = system(cmd); + if status ~= 0 + errordlg('Не удалось открыть окно выбора приложения.'); + end + else + errordlg('Файл не найден'); + end + end + + +%% SPECIFIC TOOLS + function [filename, section, tool, example] = getAppWrapperUserFile(block, sel) + if (nargin < 2) + sel = get_param(block, 'appWrapperFunc'); + end + + basePath = mcuPath.get('appWrapperPath'); + if isempty(basePath) + errordlg('Не указан путь к файлам обёртки (wrapperPath).'); + return; + end + % Формируем путь к файлу в зависимости от типа запроса + if strcmp(sel, 'Includes') + filename = fullfile(basePath, 'app_includes.h'); + section = '// INCLUDES'; + tool = 'Инклюды для доступа к коду МК в коде оболочке'; + example = '#include "main.h"'; + elseif strcmp(sel, 'Dummy') + filename = fullfile(basePath, 'app_wrapper.c'); + section = '// DUMMY'; + tool = 'Заглушки для различных функций и переменных'; + example = ['CAN_HandleTypeDef hcan = {0};' newline... + 'void hardware_func(handle *huart) {}' newline... + 'int wait_for_hardware_flag(int *flag) {' newline... + ' return 1;' newline... + '}' newline... + '']; + elseif strcmp(sel, 'App Init') + filename = fullfile(basePath, 'app_init.c'); + section = '// USER APP INIT'; + tool = ['Код для инициализации приложения МК.' newline newline... + 'Вызов функций инициализации, если не используется отдельный поток для main().']; + example = 'init_func();'; + elseif strcmp(sel, 'App Step') + filename = fullfile(basePath, 'app_wrapper.c'); + section = '// USER APP STEP'; + tool = ['Код приложения МК для вызова в шаге симуляции.' newline newline ... + 'Вызов функций программы МК, если не используется отдельный поток для main().']; + example = 'step_func();'; + elseif strcmp(sel, 'App Inputs') + filename = fullfile(basePath, 'app_io.c'); + section = '// USER APP INPUT'; + tool = ['Работа с буффером для портов S-Function' newline newline ... + 'Буфер в начале хранит входные порты S-Function, далее идут выходные порты:' newline ... + 'Buffer[0:15] - входной порт, Buffer[16:31] - входной 1 порт, ' newline ... + 'Buffer[32:47] - выходной 1 порт, Buffer[48:63] - выходной 2 порт']; + example = ['// чтение 1-го элемента 0-го входного массива' newline... + 'app_variable_2 = ReadInputArray(0, 1);' newline newline... + '// запись в буфер выходов' newline ... + 'app_variable_2 = Buffer[10];']; + elseif strcmp(sel, 'App Outputs') + filename = fullfile(basePath, 'app_io.c'); + section = '// USER APP OUTPUT'; + tool = ['Работа с буффером для портов S-Function' newline newline ... + 'Буфер в начале хранит входные порты S-Function, далее идут выходные порты:' newline ... + 'Buffer[0:15] - входной порт, Buffer[16:31] - входной 1 порт, ' newline ... + 'Buffer[32:47] - выходной 1 порт, Buffer[48:63] - выходной 2 порт']; + example = ['// запись в 1-й элемент 0-го выходного массива' newline... + 'WriteOutputArray(app_variable, 0, 1);' newline newline ... + '// запись в буфер выходов' newline ... + 'Buffer[XD_OUTPUT_START + 10] = app_variable_2;']; + elseif strcmp(sel, 'App Deinit') + filename = fullfile(basePath, 'app_init.c'); + section = '// USER APP DEINIT'; + tool = ['Код для деинициализации приложения МК.' newline newline ... + 'Можно деинициализировать приложение МК, для повторного запуска.']; + example = 'memset(&htim1, sizeof(htim1), 0;'; + else + tool = ''; + mcuMask.disp(0, '\nОшибка выбора типа секции кода: неизвестное значение'); + end + + end + end +end \ No newline at end of file diff --git a/McuLib/m/asynchManage.m b/McuLib/m/asynchManage.m index 591139d..71f1834 100644 --- a/McuLib/m/asynchManage.m +++ b/McuLib/m/asynchManage.m @@ -39,7 +39,7 @@ classdef asynchManage < handle methods (Access = private) function saveCallback(obj) try - mcuMask.saveAndClose(obj.maskBlockPath); + mcuMask.close(obj.maskBlockPath); save_system(obj.modelName); catch ME warning('progr:Nneg', 'Ошибка при сохранении модели: %s', ME.message); @@ -79,7 +79,7 @@ classdef asynchManage < handle function GUIconfigCallback(obj) try - mcuMask.saveAndClose(obj.maskBlockPath); + mcuMask.close(obj.maskBlockPath); mexing(0); catch ME warning('progr:Nneg', 'Ошибка при обновлении модели: %s', ME.message); diff --git a/McuLib/m/compiler.m b/McuLib/m/compiler.m new file mode 100644 index 0000000..2a0f39f --- /dev/null +++ b/McuLib/m/compiler.m @@ -0,0 +1,140 @@ +classdef compiler + methods(Static) + + function compile() + addpath(mcuPath.get('wrapperPath')); + mexing(1); + end + + function get_availbe() + addpath(mcuPath.get('wrapperPath')); + mexing(1); + end + + function choose() + addpath(mcuPath.get('wrapperPath')); + mexing(1); + end + + + + function updateRunBat() + sources = { + 'MCU.c' + 'mcu_wrapper.c' + }; + % Список заголовочных файлов (.h) + includes = { '.\' + }; + wrapperPath = mcuPath.get('wrapperPath'); + % [wrapperPath, ~, ~] = fileparts(wrapperPath); + % Формируем строки + wrapperSrcText = compiler.createSourcesBat('code_WRAPPER', sources, wrapperPath); + wrapperIncText = compiler.createIncludesBat('includes_WRAPPER', includes, wrapperPath); + + % Записываем результат + res = compiler.updateRunMexBat(wrapperSrcText, wrapperIncText, ':: WRAPPER BAT'); % Всё прошло успешно + + if res == 0 + return + end + + sources = { + 'app_wrapper.c' + 'app_init.c' + 'app_io.c' + }; + % Список заголовочных файлов (.h) + includes = { '.\' + }; + periphPath = mcuPath.get('appWrapperPath'); + % Формируем строки + wrapperSrcText = compiler.createSourcesBat('code_APP_WRAPPER', sources, periphPath); + wrapperIncText = compiler.createIncludesBat('includes_APP_WRAPPER', includes, periphPath); + + % Записываем результат + res = compiler.updateRunMexBat(wrapperSrcText, wrapperIncText, ':: APP WRAPPER BAT'); % Всё прошло успешно + + periphConfig.updatePeriphRunMexBat(); + end + + + function res = updateRunMexBat(srcText, incText, Section) + % Входные параметры: + % srcText - текст для записи set code_... + % incText - текст для записи set includes_... + % + % Возвращает: + % res - 0 при успехе, 1 при ошибке + periphBat = [srcText '\n\n' incText]; + batPath = fullfile(mcuPath.get('wrapperPath'), 'run_mex.bat'); + res = 1; + try + code = fileread(batPath); + code = regexprep(code, '\r\n?', '\n'); + + % Записываем строки srcText и incText с переносами строк + code = editCode.insertSection(code, Section, periphBat); + + fid = fopen(batPath, 'w', 'n', 'UTF-8'); + if fid == -1 + error('Не удалось открыть файл для записи'); + end + fwrite(fid, code); + fclose(fid); + res = 1; + catch ME + mcuMask.disp(0, '\nОшибка: неудачная запись в файл при записи файла: %s', ME.message); + end + end + + function srcText = createSourcesBat(prefix_name, sources, path) + srcList = {}; + if nargin >= 2 && iscell(sources) + for i = 1:numel(sources) + fullPath = fullfile(path, sources{i}); + srcList{end+1} = strrep(fullPath, '\', '\\'); + end + end + + % Формируем srcText с переносами строк и ^ + srcText = ''; + for i = 1:numel(srcList) + if i < numel(srcList) + srcText = [srcText srcList{i} '^' newline ' ']; + else + srcText = [srcText srcList{i}]; + end + end + + % Добавляем префикс + srcText = ['set ' prefix_name '=' srcText]; + end + + function incText = createIncludesBat(prefix_name, includes, path) + incList = {}; + if nargin >= 2 && iscell(includes) + for i = 1:numel(includes) + fullPath = fullfile(path, includes{i}); + incList{end+1} = ['-I"' strrep(fullPath, '\', '\\') '"']; + end + end + + % Формируем incText с переносами строк и ^ + incText = ''; + for i = 1:numel(incList) + if i == 1 && numel(incList) ~= 1 + incText = [incText incList{i} '^' newline]; + elseif i < numel(incList) + incText = [incText ' ' incList{i} '^' newline]; + else + incText = [incText ' ' incList{i}]; + end + end + + % Добавляем префикс + incText = ['set ' prefix_name '=' incText]; + end + + end +end \ No newline at end of file diff --git a/McuLib/m/configJs.m b/McuLib/m/configJs.m new file mode 100644 index 0000000..f2cde98 --- /dev/null +++ b/McuLib/m/configJs.m @@ -0,0 +1,143 @@ +classdef configJs + + methods(Static) + + function config = update(blockPath, config) + if isempty(config) + return; + end + + mask = Simulink.Mask.get(blockPath); + maskParams = mask.Parameters; + paramNames = arrayfun(@(p) p.Name, maskParams, 'UniformOutput', false); + + % Обработка остальных секций (с дефайнами) + periphs = fieldnames(config); + for i = 1:numel(periphs) + periph = periphs{i}; + + % Проверяем есть ли Defines + if ~isfield(config.(periph), 'Defines') + continue; + end + + defines = config.(periph).Defines; + defNames = fieldnames(defines); + + for j = 1:numel(defNames) + defPrompt = defNames{j}; + paramName = matlab.lang.makeValidName(defPrompt); + + % Проверка, существует ли параметр с таким именем + if ismember(paramName, paramNames) + param = mask.getParameter(paramName); + valStr = param.Value; + + % Проверяем, существует ли элемент defPrompt в структуре defines + if isfield(defines, defPrompt) + % Преобразуем строку в соответствующий тип + if strcmpi(defines.(defPrompt).Type, 'checkbox') + config.(periph).Defines.(defPrompt).Default = strcmpi(valStr, 'on'); + elseif strcmpi(defines.(defPrompt).Type, 'edit') + valNum = str2double(valStr); + if isnan(valNum) + config.(periph).Defines.(defPrompt).Default = valStr; + else + config.(periph).Defines.(defPrompt).Default = valNum; + end + end + end + end + end + end + end + + function config = read(blockPath) + mask = Simulink.Mask.get(blockPath); + + pathparam = mask.getParameter('periphPath'); + config_path = pathparam.Value; + + if ~isempty(config_path) + jsonText = fileread(config_path); + config = jsondecode(jsonText); + else + config = []; + end + end + + function write(config) + if isempty(config) + return + end + + blockHandle = gcbh; + mask = Simulink.Mask.get(blockHandle); + + pathparam = mask.getParameter('periphPath'); + config_path = pathparam.Value; + + jsonText = jsonencode(config, 'PrettyPrint', true); + fid = fopen(config_path, 'w', 'n', 'UTF-8'); + if fid == -1 + error('Не удалось открыть файл periph_config.json для записи.'); + end + fwrite(fid, jsonText, 'char'); + fclose(fid); + 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 = configJs.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 + + + end + + + + methods(Static, Access=private) + + end +end diff --git a/McuLib/m/customtable.m b/McuLib/m/customtable.m index 43ab261..7915168 100644 --- a/McuLib/m/customtable.m +++ b/McuLib/m/customtable.m @@ -8,13 +8,15 @@ classdef customtable tableControl = mask.getDialogControl(table_name); tableParameter = mask.getParameter(table_name); nCols = tableControl.getNumberOfColumns; - % if nCols > 0 - % for i = 1:nCols - % tableControl.removeColumn(1); - % end - % end - % column = tableControl.addColumn(Name='Title', Type='edit'); - % tableControl.Sortable = 'on'; + % инициализация колонок если они пустые + % такое случается при removeParameter + if isempty(tableControl.Columns) || (nCols > 1) + for i = 1:nCols + tableControl.removeColumn(1); + end + column = tableControl.addColumn(Name='Title', Type='edit'); + tableControl.Sortable = 'on'; + end column.Name = tableParameter.Alias; end diff --git a/McuLib/m/mainConfig.m b/McuLib/m/mainConfig.m new file mode 100644 index 0000000..a588212 --- /dev/null +++ b/McuLib/m/mainConfig.m @@ -0,0 +1,274 @@ +classdef mainConfig + + methods(Static) + + function config = export() + blockPath = gcb; + mask = Simulink.Mask.get(blockPath); + wrapParamToExport = {'wrapperPath', 'enableDebug', 'mcuClk', ... + 'threadCycles', 'enableThreading', 'enableDeinit'}; + portParamToExport = {'inNumb', ... + 'in_port_1_name', 'in_port_1_width', ... + 'in_port_2_name', 'in_port_2_width', ... + 'in_port_3_name', 'in_port_3_width', ... + 'in_port_4_name', 'in_port_4_width', ... + 'in_port_5_name', 'in_port_5_width', ... + 'outNumb', ... + 'out_port_1_name', 'out_port_1_width', ... + 'out_port_2_name', 'out_port_2_width', ... + 'out_port_3_name', 'out_port_3_width', ... + 'out_port_4_name', 'out_port_4_width', ... + 'out_port_5_name', 'out_port_5_width',}; + appParamToExport = {'appWrapperPath', 'srcTable', 'incTable', 'userDefs'}; + + config = struct(); + for i = 1:numel(wrapParamToExport) + paramName = wrapParamToExport{i}; + def = mainConfig.exportParamToConfig(mask, paramName); + config.(paramName) = def; + end + + for i = 1:numel(portParamToExport) + paramName = portParamToExport{i}; + def = mainConfig.exportParamToConfig(mask, paramName); + config.(paramName) = def; + end + + for i = 1:numel(appParamToExport) + paramName = appParamToExport{i}; + def = mainConfig.exportParamToConfig(mask, paramName); + config.(paramName) = def; + end + + + + jsonStr = jsonencode(config, 'PrettyPrint', true); % 'PrettyPrint' для форматирования + + + % Диалог сохранения файла + [file, path] = uiputfile('*.json', 'Сохранить конфигурацию как', 'WrapperConfig.json'); + if isequal(file, 0) || isequal(path, 0) + disp('Сохранение отменено пользователем.'); + return; + end + + filepath = fullfile(path, file); + + + + % Сохраняем в файл + fid = fopen(filepath, 'w'); + if fid == -1 + mcuMask.disp(0, 'Не удалось открыть файл для записи: %s', filename); + end + + fwrite(fid, jsonStr, 'char'); + fclose(fid); + end + + + function import() + % Получаем путь к текущему блоку + blockPath = gcb; + mask = Simulink.Mask.get(blockPath); + + % Выбор JSON-файла через диалоговое окно + [file, path] = uigetfile('*.json', 'Выберите файл конфигурации'); + if isequal(file, 0) + mcuMask.disp(0, 'Импорт отменён пользователем.'); + return; + end + + fullpath = fullfile(path, file); + + % Чтение и декодирование JSON + try + jsonStr = fileread(fullpath); + config = jsondecode(jsonStr); + catch err + mcuMask.disp(0, 'Ошибка при чтении или разборе JSON: %s', err.message); + end + + % Применение параметров из конфигурации + paramNames = fieldnames(config); + for i = 1:numel(paramNames) + paramName = paramNames{i}; + def = config.(paramName); + + try + mainConfig.applyDefToMask(mask, paramName, def); + catch err + mcuMask.disp(0, 'Ошибка при применении параметра "%s": %s', paramName, err.message); + end + end + + mcuMask.disp(0, 'Конфигурация успешно импортирована.'); + end + + end + + + methods(Static, Access=private) + + + function def = exportParamToConfig(mask, paramName) + % mask — объект Simulink.Mask.get(blockPath) + % paramName — имя параметра (как в mask.Parameters.Name) + + param = mask.getParameter(paramName); + if isempty(param) + mcuMask.disp(0, 'Параметр "%s" не найден в маске.', paramName); + def = []; + return; + end + + def = struct(); + + % Prompt + def.Prompt = param.Prompt; + + % Тип параметра + def.Type = param.Type; + + % Значение по умолчанию + val = param.Value; + switch lower(param.Type) + case 'checkbox' + def.Default = strcmp(val, 'on'); + case {'edit', 'spinbox'} + num = str2double(val); + if ~isnan(num) + def.Default = num; + else + def.Default = val; + end + case 'customtable' + def.Default = customtable.parse(param.Name); % или можно попытаться распарсить значение позже + case 'text' + def.Default = ''; % или можно попытаться распарсить значение позже + otherwise + def.Default = val; + end + + % Alias, если есть + if ~isempty(param.Alias) + def.Def = param.Alias; + end + + % % Row (new/current) + % try + % rowSetting = param.DialogControl.Row; + % def.NewRow = strcmp(rowSetting, 'new'); + % catch + % def.NewRow = false; + % end + end + + + function applyDefToMask(mask, paramName, def) + % mask — объект Simulink.Mask.get(blockPath) + % paramName — имя параметра маски + % def — структура, полученная из extractDefFromMask + + % Получаем параметр + param = mask.getParameter(paramName); + if isempty(param) + mcuMask.disp(0, 'Параметр "%s" не найден в маске.', paramName); + return; + end + + if isempty(def) + mcuMask.disp(0, 'Параметр "%s" не найден в конфиге.', paramName); + return; + end + + switch lower(def.Type) + case 'checkbox' + % Логическое значение true/false + if islogical(def.Default) + param.Value = mainConfig.ternary(def.Default, 'on', 'off'); + else + mcuMask.disp(0, 'Ожидалось логическое значение для checkbox "%s".', paramName); + end + + case {'edit', 'spinbox'} + % Строка или число + if isnumeric(def.Default) + param.Value = num2str(def.Default); + elseif ischar(def.Default) || isstring(def.Default) + param.Value = char(def.Default); + else + mcuMask.disp(0, 'Некорректный формат значения для edit "%s".', paramName); + end + + case 'customtable' + % Массив строк + if iscell(def.Default) + customtable.collect(paramName, def.Default); + else + mcuMask.disp(0, 'customtable "%s" требует cell-массив строк.', paramName); + end + + case 'text' + % Просто текстовая строка + if ischar(def.Default) || isstring(def.Default) + param.Value = char(def.Default); + else + mcuMask.disp(0, 'text-параметр "%s" должен быть строкой.', paramName); + end + + case 'popup' + % popup — установить значение, если оно есть в списке + if ischar(def.Default) && isfield(def, 'Def') && any(strcmp(def.Default, def.Def)) + param.Value = def.Default; + else + mcuMask.disp(0, 'popup-параметр "%s" имеет неверное значение или список.', paramName); + end + + otherwise + % По умолчанию просто устанавливаем строковое значение + if ischar(def.Default) || isstring(def.Default) + param.Value = char(def.Default); + elseif isnumeric(def.Default) + param.Value = num2str(def.Default); + else + mcuMask.disp(0, 'Неизвестный формат значения параметра "%s".', paramName); + end + end + + % Применение Prompt, если нужно + if isfield(def, 'Prompt') + param.Prompt = def.Prompt; + end + + % Применение Alias, если есть + if isfield(def, 'Def') && ischar(def.Def) + param.Alias = def.Def; + end + + % % Установка Row, если поддерживается + % if isfield(def, 'NewRow') + % try + % if def.NewRow + % param.DialogControl.Row = 'new'; + % else + % param.DialogControl.Row = 'current'; + % end + % catch + % % Некоторым типам параметров Row может быть неприменим + % end + % end + end + + + function result = ternary(cond, a, b) + if cond + result = a; + else + result = b; + end + end + + end + +end \ No newline at end of file diff --git a/McuLib/m/mainWrap.m b/McuLib/m/mainWrap.m new file mode 100644 index 0000000..dcc3c17 --- /dev/null +++ b/McuLib/m/mainWrap.m @@ -0,0 +1,72 @@ +classdef mainWrap + + methods(Static) + function enableThreading() + block = gcb; + maskNames = get_param(block, 'MaskNames'); + maskValues = get_param(block, 'MaskValues'); + maskEnables = get_param(block, 'MaskEnables'); + idxEnable = find(strcmp(maskNames, 'enableThreading')); + idxEdit = find(strcmp(maskNames, 'threadCycles')); + if isempty(idxEnable) || isempty(idxEdit) + error('Параметры enableThreading или threadCycles не найдены в маске'); + end + val = maskValues{idxEnable}; + if strcmp(val, 'on') + maskEnables{idxEdit} = 'on'; + else + maskEnables{idxEdit} = 'off'; + end + set_param(block, 'MaskEnables', maskEnables); + end + + function enableDeinit() + block = gcb; + maskNames = get_param(block, 'MaskNames'); + maskValues = get_param(block, 'MaskValues'); + maskEnables = get_param(block, 'MaskEnables'); + idxEnable = find(strcmp(maskNames, 'enableThreading')); + idxEdit = find(strcmp(maskNames, 'threadCycles')); + if isempty(idxEnable) || isempty(idxEdit) + error('Параметры enableThreading или threadCycles не найдены в маске'); + end + val = maskValues{idxEnable}; + if strcmp(val, 'on') + maskEnables{idxEdit} = 'on'; + else + maskEnables{idxEdit} = 'off'; + end + set_param(block, 'MaskEnables', maskEnables); + end + + function extConsol() + block = gcb; + mask = Simulink.Mask.get(block); + fullOut = mask.getParameter('fullOutput'); + extCons = mask.getParameter('extConsol'); + if isempty(extCons) || isempty(fullOut) + error('Параметры fullOutput или extConsol не найдены в маске'); + end + + if(strcmp(extCons.Enabled, 'on')) + if strcmp(extCons.Value, 'on') + fullOut.Enabled = 'off'; + fullOut.Value = 'on'; + else + fullOut.Enabled = 'on'; + end + else + fullOut.Enabled = 'on'; + end + + end + + end + + +%% SPECIFIC TOOLS + methods(Static, Access = private) + + end + +end \ No newline at end of file diff --git a/McuLib/m/mcuMask.m b/McuLib/m/mcuMask.m index da7bd16..0947dcf 100644 --- a/McuLib/m/mcuMask.m +++ b/McuLib/m/mcuMask.m @@ -1,5 +1,5 @@ classdef mcuMask - +%% CALLBACKS methods(Static) % Following properties of 'maskInitContext' are avalaible to use: % - BlockHandle @@ -8,10 +8,10 @@ classdef mcuMask function MaskInitialization(maskInitContext) % Получаем хэндл текущего блока blk = gcbh; - set_param(blk,"MaskSelfModifiable","on") - set_param(blk, 'LinkStatus', 'none'); % Получаем объект маски текущего блока mask = Simulink.Mask.get(gcb); + set_param(blk,"MaskSelfModifiable","on") + set_param(blk, 'LinkStatus', 'none'); % mcuMask.disp(1,''); try % Проверка наличия findjobj @@ -19,8 +19,6 @@ classdef mcuMask catch findjobjAvailable = false; end - % Получаем объект маски текущего блока - mask = Simulink.Mask.get(gcb); % Имя checkbox-параметра (укажите точное имя из маски) checkboxParamName = 'extConsol'; % пример findjobjLinkName = 'findjobj_link'; % пример @@ -60,156 +58,37 @@ classdef mcuMask end %% WRAPPER PARAMS + function wrapperPath_add(callbackContext) + mcuPath.addPath('wrapperPath'); + end + function enableThreading(callbackContext) - block = gcb; - maskNames = get_param(block, 'MaskNames'); - maskValues = get_param(block, 'MaskValues'); - maskEnables = get_param(block, 'MaskEnables'); - idxEnable = find(strcmp(maskNames, 'enableThreading')); - idxEdit = find(strcmp(maskNames, 'threadCycles')); - if isempty(idxEnable) || isempty(idxEdit) - error('Параметры enableThreading или threadCycles не найдены в маске'); - end - val = maskValues{idxEnable}; - if strcmp(val, 'on') - maskEnables{idxEdit} = 'on'; - else - maskEnables{idxEdit} = 'off'; - end - set_param(block, 'MaskEnables', maskEnables); + mainWrap.enableThreading(); end function enableDeinit(callbackContext) - block = gcb; - maskNames = get_param(block, 'MaskNames'); - maskValues = get_param(block, 'MaskValues'); - maskEnables = get_param(block, 'MaskEnables'); - idxEnable = find(strcmp(maskNames, 'enableThreading')); - idxEdit = find(strcmp(maskNames, 'threadCycles')); - if isempty(idxEnable) || isempty(idxEdit) - error('Параметры enableThreading или threadCycles не найдены в маске'); - end - val = maskValues{idxEnable}; - if strcmp(val, 'on') - maskEnables{idxEdit} = 'on'; - else - maskEnables{idxEdit} = 'off'; - end - set_param(block, 'MaskEnables', maskEnables); + mainWrap.enableDeinit(); end function extConsol(callbackContext) - block = gcb; - mask = Simulink.Mask.get(block); - fullOut = mask.getParameter('fullOutput'); - extCons = mask.getParameter('extConsol'); - if isempty(extCons) || isempty(fullOut) - error('Параметры fullOutput или extConsol не найдены в маске'); - end - - if(strcmp(extCons.Enabled, 'on')) - if strcmp(extCons.Value, 'on') - fullOut.Enabled = 'off'; - fullOut.Value = 'on'; - else - fullOut.Enabled = 'on'; - end - else - fullOut.Enabled = 'on'; - end - + mainWrap.extConsol(); end - function wrapperPath_add(callbackContext) - block = gcb; - mask = Simulink.Mask.get(block); - % Открываем окно выбора папки - folderPath = uigetdir('', 'Выберите папку'); - % Проверка на отмену - if isequal(folderPath, 0) - return; - end - % Установка значения параметра маски - rel = mcuMask.absoluteToRelativePath(folderPath); - param = mask.getParameter('wrapperPath'); - param.Value = rel; - - end %% USER WRAPPER CODE - - function wrapperFunc(callbackContext) - block = gcb; - % Получаем имя функции и путь к файлам - [filename, section, tool, example]= mcuMask.getWrapperUserFile(block); - mcuMask.tool(tool, example); - - % Загружаем содержимое файла - set_param(block, 'wrapperCode', ''); - try - code = fileread(filename); - code = regexprep(code, '\r\n?', '\n'); % нормализуем окончания строк - - includesText = editCode.extractSection(code, section); - set_param(block, 'wrapperCode', includesText); - catch - end - % % Поиск тела обычной функции - % expr = sprintf('void %s()', sel); - % funcBody = editCode.extractSection(code, expr); - % set_param(block, 'wrapperCode', funcBody); - end - - function saveWrapperCode(callbackContext) - block = gcb; - - % Получаем имя функции и путь к файлам - [filename, section] = mcuMask.getWrapperUserFile(block); - if ~isfile(filename) - errordlg(['Файл не найден: ', filename]); - return; - end - - sel = get_param(block, 'wrapperFunc'); - basePath = get_param(block, 'wrapperPath'); - if isempty(basePath) - errordlg('Не указан путь к файлам обёртки (wrapperPath).'); - return; - end - newBody = get_param(block, 'wrapperCode'); - code = fileread(filename); - code = regexprep(code, '\r\n?', '\n'); - newBody = strrep(newBody, '\', '\\'); - code = editCode.insertSection(code, section, newBody); - % else - % % Обновляем тело функции - % expr = sprintf('void %s()', sel); - % code = editCode.insertSection(code, expr, newBody); - % end - fid = fopen(filename, 'w', 'n', 'UTF-8'); - if fid == -1 - errordlg('Не удалось открыть файл для записи'); - return; - end - fwrite(fid, code); - fclose(fid); - mcuMask.disp(1, ['Обновлено: ' sel]); + function appWrapperPath_add(callbackContext) + mcuPath.addPath('appWrapperPath'); end - function openWrapperCode(callbackContext) - block = gcb; + function appWrapperFunc(callbackContext) + appWrap.appWrapperFunc(); + end + + function saveAppWrapperCode(callbackContext) + appWrap.saveAppWrapperCode(); + end - % Получаем имя функции и путь к файлам - filename = mcuMask.getAbsolutePath(mcuMask.getWrapperUserFile(block)); - if exist(filename, 'file') == 2 - % Формируем команду без кавычек - cmd = sprintf('rundll32.exe shell32.dll,OpenAs_RunDLL %s', filename); - status = system(cmd); - if status ~= 0 - errordlg('Не удалось открыть окно выбора приложения.'); - end - else - errordlg('Файл не найден'); - end + function openAppWrapperCode(callbackContext) + appWrap.openAppWrapperCode(); end %% USER CODE @@ -222,106 +101,37 @@ classdef mcuMask end function btnAddSrc(callbackContext) - blockHandle = gcb; - % Открываем проводник для выбора файлов - [files, pathstr] = uigetfile({ ... - '*.c;*.cpp', 'Исходные файлы (*.c, *.cpp)'; ... - '*.obj;*.lib', 'Библиотеки (*.obj, *.lib)'; ... - '*.*', 'Все файлы (*.*)'}, ... - 'Выберите файлы', ... - 'MultiSelect', 'on'); - - if isequal(files, 0) - return; % Отмена выбора - end - if ischar(files) - files = {files}; % Один файл — в cell - end - % Парсим строку в cell-массив - oldTable = customtable.parse('srcTable'); - - % Добавляем новые пути, проверяя уникальность - for i = 1:numel(files) - fullpath = fullfile(pathstr, files{i}); - rel = mcuMask.absoluteToRelativePath(fullpath); - if ~any(strcmp(rel, oldTable)) - oldTable{end+1, 1} = rel; - end - end - - % Парсим строку в cell-массив - customtable.collect('srcTable', oldTable); - + mcuPath.addSourceFileTable('srcTable', 'Выберите исходные файлы'); end function btnAddInc(callbackContext) - blockHandle = gcb; - % Открываем проводник для выбора папок - pathstr = uigetdir(pwd, 'Выберите папку с заголовочными файлами'); - if isequal(pathstr, 0) - return; % Отмена выбора - end - % Парсим таблицу - oldTable = customtable.parse('incTable'); - - rel = mcuMask.absoluteToRelativePath(pathstr); - - % Проверяем наличие пути - if ~any(strcmp(rel, oldTable)) - oldTable{end+1, 1} = rel; - end - - % Собираем таблицу - customtable.collect('incTable', oldTable); + mcuPath.addPathTable('incTable', 'Выберите папку с заголовочными файлами'); end %% PERIPH CONFIG - function periphPath_add(callbackContext) - block = gcbh; - mask = Simulink.Mask.get(block); - [file, path] = uigetfile({'*.*','Все файлы (*.*)'}, 'Выберите файл'); - if isequal(file, 0) || isequal(path, 0) - % Отмена выбора — ничего не делаем - return; - end - fullFilePath = fullfile(path, file); - rel = mcuMask.absoluteToRelativePath(fullFilePath); - param = mask.getParameter('periphPath'); - param.Value = rel; + mcuPath.addAnyFile('periphPath'); end - function compile(callbackContext) - addpath('MCU_Wrapper'); - mexing(1); - end - - - function updateModel(callbackContext) - addpath('MCU_Wrapper'); - res = mexing(1); - if res ~= 0 - return; - end - + function periphUpdate(callbackContext) modelName = bdroot(gcb); % получить имя верхнего уровня модели blockName = gcb; mgr = asynchManage(modelName, blockName); % создать объект класса - mgr.saveAndUpdateModel(); % запустить сохранение и обновление + mgr.updateGUIfromConfig(); % запустить сохранение и обновление end - - function findjobj_link(callbackContext) - web('https://www.mathworks.com/matlabcentral/fileexchange/14317-findjobj-find-java-handles-of-matlab-graphic-objects'); + %% COMPILE + function compile(callbackContext) + compiler.compile(); end - function set_name() + function setSFuncName(callbackContext) block = gcb; % Получаем параметр имени S-Function из маски блока newName = mcuMask.get_name(); % Путь к файлу, в котором надо заменить строку - cFilePath = fullfile(pwd, './MCU_Wrapper/MCU.c'); % <-- укажи правильный путь + cFilePath = fullfile(pwd, mcuPath.get('wrapperPath'), 'MCU.c'); % <-- укажи правильный путь % Считаем файл в память try @@ -349,93 +159,29 @@ classdef mcuMask fclose(fid); end - function name = get_name() - block = gcb; - % Получаем параметр имени S-Function из маски блока - name = get_param(block, 'sfuncName'); + %% LINK TO EXTERNAL CONSOLE + function findjobj_link(callbackContext) + web https://www.mathworks.com/matlabcentral/fileexchange/14317-findjobj-find-java-handles-of-matlab-graphic-objects; end end - -%% SPECIFIC TOOLS - methods(Static, Access = private) - - function [filename, section, tool, example] = getWrapperUserFile(block) - sel = get_param(block, 'wrapperFunc'); - basePath = get_param(block, 'wrapperPath'); - if isempty(basePath) - errordlg('Не указан путь к файлам обёртки (wrapperPath).'); - return; - end - % Формируем путь к файлу в зависимости от типа запроса - if strcmp(sel, 'Includes') - filename = fullfile(basePath, 'app_includes.h'); - section = '// INCLUDES'; - tool = 'Инклюды для доступа к коду МК в коде оболочке'; - example = '#include "main.h"'; - elseif strcmp(sel, 'Dummy') - filename = fullfile(basePath, 'app_wrapper.c'); - section = '// DUMMY'; - tool = 'Заглушки для различных функций и переменных'; - example = ['CAN_HandleTypeDef hcan = {0};' newline... - 'void hardware_func(handle *huart) {}' newline... - 'int wait_for_hardware_flag(int *flag) {' newline... - ' return 1;' newline... - '}' newline... - '']; - elseif strcmp(sel, 'App Init') - filename = fullfile(basePath, 'app_init.c'); - section = '// USER APP INIT'; - tool = ['Код для инициализации приложения МК.' newline newline... - 'Вызов функций инициализации, если не используется отдельный поток для main().']; - example = 'init_func();'; - elseif strcmp(sel, 'App Step') - filename = fullfile(basePath, 'app_wrapper.c'); - section = '// USER APP STEP'; - tool = ['Код приложения МК для вызова в шаге симуляции.' newline newline ... - 'Вызов функций программы МК, если не используется отдельный поток для main().']; - example = 'step_func();'; - elseif strcmp(sel, 'App Inputs') - filename = fullfile(basePath, 'app_io.c'); - section = '// USER APP INPUT'; - tool = ['Работа с буффером для портов S-Function' newline newline ... - 'Буфер в начале хранит входные порты S-Function, далее идут выходные порты:' newline ... - 'Buffer[0:15] - входной порт, Buffer[16:31] - входной 1 порт, ' newline ... - 'Buffer[32:47] - выходной 1 порт, Buffer[48:63] - выходной 2 порт']; - example = ['// чтение 1-го элемента 0-го входного массива' newline... - 'app_variable_2 = ReadInputArray(0, 1);' newline newline... - '// запись в буфер выходов' newline ... - 'app_variable_2 = Buffer[10];']; - elseif strcmp(sel, 'App Outputs') - filename = fullfile(basePath, 'app_io.c'); - section = '// USER APP OUTPUT'; - tool = ['Работа с буффером для портов S-Function' newline newline ... - 'Буфер в начале хранит входные порты S-Function, далее идут выходные порты:' newline ... - 'Buffer[0:15] - входной порт, Buffer[16:31] - входной 1 порт, ' newline ... - 'Buffer[32:47] - выходной 1 порт, Buffer[48:63] - выходной 2 порт']; - example = ['// запись в 1-й элемент 0-го выходного массива' newline... - 'WriteOutputArray(app_variable, 0, 1);' newline newline ... - '// запись в буфер выходов' newline ... - 'Buffer[XD_OUTPUT_START + 10] = app_variable_2;']; - elseif strcmp(sel, 'App Deinit') - filename = fullfile(basePath, 'app_init.c'); - section = '// USER APP DEINIT'; - tool = ['Код для деинициализации приложения МК.' newline newline ... - 'Можно деинициализировать приложение МК, для повторного запуска.']; - example = 'memset(&htim1, sizeof(htim1), 0;'; - else - tool = ''; - mcuMask.disp(0, '\nОшибка выбора типа секции кода: неизвестное значение'); - end - - end - - end - %% GENERAL TOOLS methods(Static, Access = public) - function saveAndClose(blockPath) + function updateModel() + addpath(mcuPath.get('wrapperPath')); + res = mexing(1); + if res ~= 0 + return; + end + + % modelName = bdroot(gcb); % получить имя верхнего уровня модели + % blockName = gcb; + % mgr = asynchManage(modelName, blockName); % создать объект класса + % mgr.saveAndUpdateModel(); % запустить сохранение и обновление + end + + function close(blockPath) try % Считываем текущее имя модели modelName = bdroot(blockPath); @@ -459,81 +205,13 @@ classdef mcuMask mcuMask.disp(clear_flag, ''); end - - function absPath = getAbsolutePath(relPath) - % relativeToAbsolutePath — преобразует относительный путь в абсолютный. - % - % Если путь уже абсолютный — возвращается он же, приведённый к канонической форме. - % Если путь относительный — преобразуется относительно текущей директории. - - % Проверка: абсолютный ли путь - if ispc - isAbsolute = ~isempty(regexp(relPath, '^[a-zA-Z]:[\\/]', 'once')) || startsWith(relPath, '\\'); - else - isAbsolute = startsWith(relPath, '/'); - end - - if isAbsolute - % Канонизируем абсолютный путь (убираем ./, ../ и т.п.) - absPath = char(java.io.File(relPath).getCanonicalPath()); - else - % Строим абсолютный путь от текущей директории - cwd = pwd; - combined = fullfile(cwd, relPath); - absPath = char(java.io.File(combined).getCanonicalPath()); - end + function name = get_name() + block = gcb; + % Получаем параметр имени S-Function из маски блока + name = get_param(block, 'sfuncName'); end - - function rel = absoluteToRelativePath(pathstr) - % absoluteToRelativePath — преобразует абсолютный путь в относительный от текущей директории. - % - % Если путь находится в текущей директории или вложенной в неё — добавляется префикс './' - % Если выше — формируются переходы '..' - % Если путь совпадает с текущей директорией — возвращается '.' - - % Получаем текущую рабочую директорию - cwd = pwd; - - % Преобразуем пути в канонические абсолютные пути - fullpath = char(java.io.File(pathstr).getCanonicalPath()); - cwd = char(java.io.File(cwd).getCanonicalPath()); - - % Разбиваем пути на части - targetParts = strsplit(fullpath, filesep); - baseParts = strsplit(cwd, filesep); - - % Находим длину общего префикса - j = 1; - while j <= min(length(targetParts), length(baseParts)) && strcmpi(targetParts{j}, baseParts{j}) - j = j + 1; - end - - % Формируем количество подъемов ".." из cwd - numUps = length(baseParts) - (j - 1); - ups = repmat({'..'}, 1, numUps); - - % Оставшаяся часть пути после общего префикса - rest = targetParts(j:end); - - % Объединяем для получения относительного пути - relParts = [ups, rest]; - rel = fullfile(relParts{:}); - - % Если путь пустой — это текущая директория - if isempty(rel) - rel = '.'; - end - - % Если путь не содержит ".." и начинается внутри текущей директории — добавим './' - if ~isempty(rest) && isempty(ups) - rel = fullfile('.', rel); - end - end - - - function checkbox_state = read_checkbox(checkboxName) maskValues = get_param(gcbh, 'MaskValues'); paramNames = get_param(gcbh, 'MaskNames'); @@ -593,15 +271,7 @@ classdef mcuMask end end - function res = ternary(cond, valTrue, valFalse) - if cond - res = valTrue; - else - res = valFalse; - end - end - - + function tool(text, example) % Устанавливает заданный текст в параметр Text Area 'toolText' через объект маски @@ -630,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/mcuPath.m b/McuLib/m/mcuPath.m new file mode 100644 index 0000000..fefc8b8 --- /dev/null +++ b/McuLib/m/mcuPath.m @@ -0,0 +1,168 @@ +classdef mcuPath + methods(Static) + + %% GET PATH FROM PARAM + function path = get(paramName) + blockPath = gcb; + path = get_param(blockPath, paramName); + end + + %% ADD PATH TO TABLE + + function addSourceFileTable(targetParamName, message) + % Открываем проводник для выбора файлов + [files, pathstr] = uigetfile({ ... + '*.c;*.cpp', 'Исходные файлы (*.c, *.cpp)'; ... + '*.obj;*.lib', 'Библиотеки (*.obj, *.lib)'; ... + '*.*', 'Все файлы (*.*)'}, ... + message, ... + 'MultiSelect', 'on'); + + if isequal(files, 0) + return; % Отмена выбора + end + if ischar(files) + files = {files}; % Один файл — в cell + end + % Парсим строку в cell-массив + oldTable = customtable.parse(targetParamName); + + % Добавляем новые пути, проверяя уникальность + for i = 1:numel(files) + fullpath = fullfile(pathstr, files{i}); + rel = mcuPath.absoluteToRelativePath(fullpath); + if ~any(strcmp(rel, oldTable)) + oldTable{end+1, 1} = rel; + end + end + + % Парсим строку в cell-массив + customtable.collect(targetParamName, oldTable); + + end + + function addPathTable(targetParamName, message) + % Открываем проводник для выбора папок + pathstr = uigetdir(pwd, message); + if isequal(pathstr, 0) + return; % Отмена выбора + end + % Парсим таблицу + oldTable = customtable.parse(targetParamName); + + rel = mcuPath.absoluteToRelativePath(pathstr); + + % Проверяем наличие пути + if ~any(strcmp(rel, oldTable)) + oldTable{end+1, 1} = rel; + end + + % Собираем таблицу + customtable.collect(targetParamName, oldTable); + end + + %% ADD PATH TO EDIT + + function addPath(targetParamName, message) + block = gcb; + mask = Simulink.Mask.get(block); + % Открываем окно выбора папки + folderPath = uigetdir('', 'Выберите папку'); + % Проверка на отмену + if isequal(folderPath, 0) + return; + end + % Установка значения параметра маски + rel = mcuPath.absoluteToRelativePath(folderPath); + param = mask.getParameter(targetParamName); + param.Value = rel; + end + + function addAnyFile(targetParamName, message) + block = gcbh; + mask = Simulink.Mask.get(block); + [file, path] = uigetfile({'*.*','Все файлы (*.*)'}, 'Выберите файл'); + if isequal(file, 0) || isequal(path, 0) + % Отмена выбора — ничего не делаем + return; + end + fullFilePath = fullfile(path, file); + rel = mcuPath.absoluteToRelativePath(fullFilePath); + param = mask.getParameter(targetParamName); + param.Value = rel; + end + + %% GET PATH STRING + + function absPath = getAbsolutePath(relPath) + % relativeToAbsolutePath — преобразует относительный путь в абсолютный. + % + % Если путь уже абсолютный — возвращается он же, приведённый к канонической форме. + % Если путь относительный — преобразуется относительно текущей директории. + + % Проверка: абсолютный ли путь + if ispc + isAbsolute = ~isempty(regexp(relPath, '^[a-zA-Z]:[\\/]', 'once')) || startsWith(relPath, '\\'); + else + isAbsolute = startsWith(relPath, '/'); + end + + if isAbsolute + % Канонизируем абсолютный путь (убираем ./, ../ и т.п.) + absPath = char(java.io.File(relPath).getCanonicalPath()); + else + % Строим абсолютный путь от текущей директории + cwd = pwd; + combined = fullfile(cwd, relPath); + absPath = char(java.io.File(combined).getCanonicalPath()); + end + end + + function rel = absoluteToRelativePath(pathstr) + % absoluteToRelativePath — преобразует абсолютный путь в относительный от текущей директории. + % + % Если путь находится в текущей директории или вложенной в неё — добавляется префикс './' + % Если выше — формируются переходы '..' + % Если путь совпадает с текущей директорией — возвращается '.' + + % Получаем текущую рабочую директорию + cwd = pwd; + + % Преобразуем пути в канонические абсолютные пути + fullpath = char(java.io.File(pathstr).getCanonicalPath()); + cwd = char(java.io.File(cwd).getCanonicalPath()); + + % Разбиваем пути на части + targetParts = strsplit(fullpath, filesep); + baseParts = strsplit(cwd, filesep); + + % Находим длину общего префикса + j = 1; + while j <= min(length(targetParts), length(baseParts)) && strcmpi(targetParts{j}, baseParts{j}) + j = j + 1; + end + + % Формируем количество подъемов ".." из cwd + numUps = length(baseParts) - (j - 1); + ups = repmat({'..'}, 1, numUps); + + % Оставшаяся часть пути после общего префикса + rest = targetParts(j:end); + + % Объединяем для получения относительного пути + relParts = [ups, rest]; + rel = fullfile(relParts{:}); + + % Если путь пустой — это текущая директория + if isempty(rel) + rel = '.'; + end + + % Если путь не содержит ".." и начинается внутри текущей директории — добавим './' + if ~isempty(rest) && isempty(ups) + rel = fullfile('.', rel); + end + end + + end +end \ No newline at end of file diff --git a/McuLib/m/mcuPorts.m b/McuLib/m/mcuPorts.m index 4135698..76770b7 100644 --- a/McuLib/m/mcuPorts.m +++ b/McuLib/m/mcuPorts.m @@ -5,8 +5,8 @@ classdef mcuPorts function write() block = gcb; mask = Simulink.Mask.get(block); - hPath = fullfile('.\MCU_Wrapper', 'mcu_wrapper_conf.h'); - cPath = fullfile('.\MCU_Wrapper', 'mcu_wrapper.c'); + hPath = fullfile(mcuPath.get('wrapperPath'), 'mcu_wrapper_conf.h'); + cPath = fullfile(mcuPath.get('wrapperPath'), 'mcu_wrapper.c'); mcuPorts.defaultUnused(); %% CREATE prefixNumb = 'IN'; diff --git a/McuLib/m/mexing.m b/McuLib/m/mexing.m index 5880b4a..1b42f89 100644 --- a/McuLib/m/mexing.m +++ b/McuLib/m/mexing.m @@ -4,10 +4,11 @@ function res = mexing(compile_mode) Ts = 0.00001; if compile_mode == 1 - delete("*.mexw64") - delete("*.mexw64.pdb") - delete(".\MCU_Wrapper\Outputs\*.*"); + delete('*.mexw64') + delete('*.mexw64.pdb') + delete([mcuPath.get('wrapperPath'), '\Outputs\*.*']); set_param(gcb, 'consoleOutput', ''); + compiler.updateRunBat(); % Дефайны definesUserArg = parseDefinesMaskText(); definesWrapperConfigArg = buildWrapperDefinesString(); @@ -31,7 +32,8 @@ function res = mexing(compile_mode) Name = mcuMask.get_name(); % Вызов батника с двумя параметрами: includes и code - cmd = sprintf('.\\MCU_Wrapper\\run_mex.bat %s "%s" "%s" "%s" "%s" %s %s', Name, includesArg, codeArg, definesUserArg, definesConfigArg, modeArg, echoArg); + run_bat_mex_path = fullfile(mcuPath.get('wrapperPath'), 'run_mex.bat'); + cmd = sprintf('%s %s "%s" "%s" "%s" "%s" %s %s', run_bat_mex_path, Name, includesArg, codeArg, definesUserArg, definesConfigArg, modeArg, echoArg); if mcuMask.read_checkbox('extConsol') cmdout = runBatAndShowOutput(cmd); @@ -64,10 +66,10 @@ function res = mexing(compile_mode) beep else blockPath = gcb; - config = periphConfig.read_config(blockPath); - config = periphConfig.update_config(blockPath, config); - periphConfig.write_config(config); - periphConfig.update(blockPath, config); + config = configJs.read(blockPath); + config = configJs.update(blockPath, config); + configJs.write(config); + periphConfig.updateMask(blockPath, config); end end @@ -200,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 @@ -228,7 +232,21 @@ function definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, val_ end % Берём alias из маски - alias = param.Alias; + val = ''; + if ~strcmp(param.Type, 'popup') + def_name = param.Alias; + else + if strcmp(param.Alias, '') + def_name = param.Value; + else + def_name = param.Alias; + val = param.Value; + end + end + + if strcmp(def_name, '') + return; + end if val_define ~= 0 % Значение параметра @@ -238,14 +256,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 '__EQ__' val '"']; end diff --git a/McuLib/m/periphConfig.m b/McuLib/m/periphConfig.m index 16bd25c..ee6d26e 100644 --- a/McuLib/m/periphConfig.m +++ b/McuLib/m/periphConfig.m @@ -1,171 +1,424 @@ classdef periphConfig methods(Static) - function update(blockPath, config) + function updateMask(blockPath, config) % blockPath = [blockPath '/MCU']; % Проверяем, была ли маска открыта % wasOpen = isMaskDialogOpen(blockPath); mask = Simulink.Mask.get(blockPath); - periphPath = get_param(blockPath, '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) + try + rowWidth = str2double(get_param(blockPath, 'rowWidth')); - if isfield(config, 'Code') - res = periphConfig.addCodeConfig(config.Code, periphPath); - if res == 0 - error('Ошибка: неудачное добавление кода периферии. Проверьте корректность файлов и путей в конфигурационном файле') - end - else - error('Ошибка: в конфигурационном файле не задан исходный код для симуляции периферии') + 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.addDefineConfig(mask, containerName, periph, defPrompt, def); + % Создаём вкладку для модуля + tabCtrl = container.addDialogControl('tab', periph); + tabCtrl.Prompt = [periph ' Config']; + + rowCountMap = containers.Map(); + for j = 1:numel(defNames) + defPrompt = defNames{j}; + def = defines.(defPrompt); + + % Вызов функции добавления одного параметра + periphConfig.addConfig(mask, containerName, periph, defPrompt, def, rowCountMap, rowWidth); + end end end + periphConfig.create_all_code_storage_params(blockPath, config); + % periphConfig.cleanup_obsolete_code_params(blockPath, config); + + periphConfig.update(); + + % Восстанавлиperiph = allTabNamesваем таблицы + 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'); % end end - function config = update_config(blockPath, config) - if isempty(config) + function update() + blockPath = gcb; + mask = Simulink.Mask.get(blockPath); + + config = configJs.read(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 = configJs.read(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 - mask = Simulink.Mask.get(blockPath); - maskParams = mask.Parameters; - paramNames = arrayfun(@(p) p.Name, maskParams, 'UniformOutput', false); + % === Проверка на параметр, связанный с 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 = configJs.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 - % Обработка остальных секций (с дефайнами) - periphs = fieldnames(config); - for i = 1:numel(periphs) - periph = periphs{i}; - % Пропускаем Code и UserCode, они уже обработаны - if strcmp(periph, 'Code') || strcmp(periph, 'UserCode') - continue; - 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 - % Проверяем есть ли Defines - if ~isfield(config.(periph), 'Defines') - continue; - end + end + + + + methods(Static, Access=private) + + - defines = config.(periph).Defines; - defNames = fieldnames(defines); + function addHiddenParam(mask, containerName, nameBase, kind, existingParams) + % Преобразуем к красивому имени + prettyName = strrep(nameBase, '_', ' '); + paramName = ['Hidden_' char(nameBase) '_' kind]; + if ismember(paramName, existingParams) + return; + end - for j = 1:numel(defNames) - defPrompt = defNames{j}; - paramName = matlab.lang.makeValidName(defPrompt); + mask.addParameter( ... + 'Name', paramName, ... + 'Type', 'edit', ... + 'Prompt', ['Hidden ' prettyName ' ' kind], ... + 'Value', '', ... + 'Visible', 'off', ... + 'Container', containerName ... + ); + fprintf('Создан скрытый параметр: %s\n', paramName); + end + + function clear_tab_params(mask, configStruct, prefix, depth) + if nargin < 4 + depth = 0; + end + maxDepth = 3; % Максимальная глубина рекурсии + + fields = fieldnames(configStruct); - % Проверка, существует ли параметр с таким именем - if ismember(paramName, paramNames) - param = mask.getParameter(paramName); - valStr = param.Value; + for i = 1:numel(fields) + key = fields{i}; + value = configStruct.(key); + paramName = [prefix '_' key]; - % Проверяем, существует ли элемент defPrompt в структуре defines - if isfield(defines, defPrompt) - % Преобразуем строку в соответствующий тип - if strcmpi(defines.(defPrompt).Type, 'checkbox') - config.(periph).Defines.(defPrompt).Default = strcmpi(valStr, 'on'); - elseif strcmpi(defines.(defPrompt).Type, 'edit') - valNum = str2double(valStr); - if isnan(valNum) - config.(periph).Defines.(defPrompt).Default = valStr; - else - config.(periph).Defines.(defPrompt).Default = valNum; - end - end - end + if isstruct(value) + if depth < maxDepth + % Рекурсивный вызов для вложенных структур с увеличением глубины + periphConfig.clear_tab_params(mask, value, paramName, depth + 1); + end + else + if strcmp(key, 'Sources') || strcmp(key, 'Includes') + baseName = configJs.get_final_name_from_prefix(prefix); + periphConfig.clear_single_periph_code_param(mask, baseName); end end end end - - function config = read_config(blockPath) + + + 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 = configJs.get_final_name_from_prefix(prefix); + periphConfig.store_single_periph_code(mask, baseName, configStruct); + end + end + end + end + + function create_all_code_storage_params(blockPath, config) mask = Simulink.Mask.get(blockPath); - pathparam = mask.getParameter('periphPath'); - config_path = pathparam.Value; + containerName = 'configTabAll'; + container = mask.getDialogControl(containerName); + existingParams = mcuMask.collect_all_parameters(container); - if ~isempty(config_path) - jsonText = fileread(config_path); - config = jsondecode(jsonText); + % Убедимся, что контейнер существует + tabName = 'hiddenCodeTab'; + tab = mask.getDialogControl(tabName); + if isempty(tab) + tab = container.addDialogControl('tab', tabName); + tab.Prompt = 'Hidden Code Settings'; + tab.Visible = 'off'; else - config = []; + 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 write_config(config) - if isempty(config) - return - end - blockHandle = gcbh; - mask = Simulink.Mask.get(blockHandle); + function cleanup_obsolete_code_params(blockPath, config) + mask = Simulink.Mask.get(blockPath); + maskParams = mask.Parameters; - pathparam = mask.getParameter('periphPath'); - config_path = pathparam.Value; + % Получаем список актуальных периферий + validPeriphs = fieldnames(config); - jsonText = jsonencode(config, 'PrettyPrint', true); - fid = fopen(config_path, 'w', 'n', 'UTF-8'); - if fid == -1 - error('Не удалось открыть файл periph_config.json для записи.'); + 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 - fwrite(fid, jsonText, 'char'); - fclose(fid); 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_all_from_container(mask, containerName) % allControls = mask.getDialogControls(); container = mask.getDialogControl(containerName); @@ -190,69 +443,25 @@ classdef periphConfig mcuMask.delete_all_tabs(mask, container); end - end - - methods(Static, Access=private) - function res = addCodeConfig(codeConfig, periphPath) + function res = addCodeBat(codeConfig, codePath) + % Добавить сурсы и пути в батник % Возвращает 0 при успехе, 1 при ошибке try - % Источники - srcList = {}; - if isfield(codeConfig, 'Sources') && isfield(codeConfig.Sources, 'Options') - srcFiles = codeConfig.Sources.Options; - for i = 1:numel(srcFiles) - fullPath = fullfile(periphPath, srcFiles{i}); - srcList{end+1} = [strrep(fullPath, '\', '\\')]; - end - end - - % Формируем srcText с переносами строк и ^ - srcText = ''; - for i = 1:numel(srcList) - if i < numel(srcList) - srcText = [srcText srcList{i} '^' newline ' ']; - else - srcText = [srcText srcList{i}]; - end - end - - % Инклуды - incList = {}; - if isfield(codeConfig, 'Includes') && isfield(codeConfig.Includes, 'Options') - incPaths = codeConfig.Includes.Options; - for i = 1:numel(incPaths) - fullPath = fullfile(periphPath, incPaths{i}); - incList{end+1} = ['-I"' strrep(fullPath, '\', '\\') '"']; - end - end - - % Формируем incText с переносами строк и ^ - incText = ''; - for i = 1:numel(incList) - if i == 1 && numel(incList) ~= 1 - incText = [incText incList{i} '^' newline]; - elseif i < numel(incList) - incText = [incText ' ' incList{i} '^' newline]; - else - incText = [incText ' ' incList{i}]; - end - end - - % Добавляем префиксы - srcText = ['set code_PERIPH' '=' srcText]; - incText = ['set includes_PERIPH' '=' incText]; + % Формируем строки + srcText = compiler.createSourcesBat('code_PERIPH', codeConfig.Sources, codePath); + incText = compiler.createIncludesBat('includes_PERIPH', codeConfig.Includes, codePath); % Записываем результат - res = periphConfig.updateRunMexBat(srcText, incText); % Всё прошло успешно + res = compiler.updateRunMexBat(srcText, incText, ':: PERIPH BAT'); % Всё прошло успешно catch % В случае ошибки просто возвращаем 1 res = 1; end end - - function res = addUserCodeConfig(userCodeConfig) + function res = addUserFunctions(userCodeConfig) + % Добавить функции и дефайны в исходный код wrapper % userCodeConfig — структура config.UserCode initFuncsText = ''; @@ -262,34 +471,33 @@ classdef periphConfig if isfield(userCodeConfig, 'Functions') funcs = userCodeConfig.Functions; - if isfield(funcs, 'PeriphInit') && isfield(funcs.PeriphInit, 'Options') - initFuncs = funcs.PeriphInit.Options; + if isfield(funcs, 'PeriphInit') + initFuncs = funcs.PeriphInit; initFuncsText = strjoin(strcat('\t', initFuncs, ';'), '\n'); end - if isfield(funcs, 'PeriphSimulation') && isfield(funcs.PeriphSimulation, 'Options') - simFuncs = funcs.PeriphSimulation.Options; + if isfield(funcs, 'PeriphSimulation') + simFuncs = funcs.PeriphSimulation; simFuncsText = strjoin(strcat('\t', simFuncs, ';'), '\n'); end - if isfield(funcs, 'PeriphDeinit') && isfield(funcs.PeriphDeinit, 'Options') - deinitFuncs = funcs.PeriphDeinit.Options; + if isfield(funcs, 'PeriphDeinit') + deinitFuncs = funcs.PeriphDeinit; 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_... % % Возвращает: % res - 0 при успехе, 1 при ошибке - wrapPath = fullfile('.\MCU_Wrapper', 'mcu_wrapper.c'); + wrapPath = fullfile(mcuPath.get('wrapperPath'), 'mcu_wrapper.c'); res = 1; try code = fileread(wrapPath); @@ -311,47 +519,27 @@ classdef periphConfig error('Ошибка: неудачная запись в файл при записи файла: %s', ME.message); end end - - - - function res = updateRunMexBat(srcText, incText) - % Входные параметры: - % srcText - текст для записи set code_... - % incText - текст для записи set includes_... - % - % Возвращает: - % res - 0 при успехе, 1 при ошибке - periphBat = [srcText '\n\n' incText]; - batPath = fullfile('.\MCU_Wrapper', 'run_mex.bat'); - res = 1; - try - code = fileread(batPath); - code = regexprep(code, '\r\n?', '\n'); - - % Записываем строки srcText и incText с переносами строк - code = editCode.insertSection(code, ':: PERIPH BAT', periphBat); - - fid = fopen(batPath, 'w', 'n', 'UTF-8'); - if fid == -1 - error('Не удалось открыть файл для записи'); - end - fwrite(fid, code); - fclose(fid); - res = 1; - catch ME - mcuMask.disp(0, '\nОшибка: неудачная запись в файл при записи файла: %s', ME.message); - end - end - - - - function addDefineConfig(mask, containerName, periphName, defPrompt, def) + + function addConfig(mask, containerName, periphName, defPrompt, def, rowCountMap, rowWidth) % mask — объект маски Simulink.Mask.get(blockPath) % containerName — имя контейнера, в который добавляем параметр (например, 'configTabAll') % periphName — имя вкладки / контейнера для текущего периферийного блока (например, 'ADC') % defPrompt — имя параметра в Defines (например, 'shift_enable') % def — структура с описанием параметра (Prompt, Def, Type, Default, NewRow и т.п.) + if ~isKey(rowCountMap, periphName) + rowCountMap(periphName) = 0; + end + rowCount = rowCountMap(periphName) + 1; + + % Устанавливаем NewRow, если он не задан + if ~isfield(def, 'NewRow') + def.NewRow = mod(rowCount - 1, rowWidth) == 0; + elseif def.NewRow == true + rowCount = 1; + end + rowCountMap(periphName) = rowCount; + % Найдем контейнер с таким именем container = mask.getDialogControl(containerName); if isempty(container) @@ -371,6 +559,8 @@ classdef periphConfig paramType = 'checkbox'; case 'edit' paramType = 'edit'; + case 'popup' + paramType = 'popup'; otherwise % Игнорируем остальные типы return; @@ -378,26 +568,48 @@ classdef periphConfig paramName = matlab.lang.makeValidName(defPrompt); - % Преобразуем значение Default в строку для Value - val = def.Default; - if islogical(val) - valStr = mcuMask.ternary(val, 'on', 'off'); - elseif isnumeric(val) - valStr = num2str(val); - elseif ischar(val) - valStr = val; + % Получаем значение + if strcmp(paramType, 'popup') + if isfield(def, 'Def') && iscell(def.Def) && ~isempty(def.Def) + choices = def.Def; + valStr = ''; % по умолчанию — ничего + elseif isfield(def, 'Options') + choices = def.Def; + valStr = ''; % по умолчанию — ничего + else + warning('Popout параметр "%s" не содержит допустимого списка в Def.', defPrompt); + return; + end else - error('Unsupported default value type for %s.%s', periphName, defPrompt); + val = def.Default; + if islogical(val) + valStr = periphConfig.ternary(val, 'on', 'off'); + elseif isnumeric(val) + valStr = num2str(val); + elseif ischar(val) + valStr = val; + else + error('Unsupported default value type for %s.%s', periphName, defPrompt); + end end % Добавляем параметр в маску - param = mask.addParameter( ... - 'Type', paramType, ... - 'Prompt', def.Prompt, ... - 'Name', paramName, ... - 'Value', valStr, ... - 'Container', periphName ... - ); + if strcmp(paramType, 'popup') + param = mask.addParameter( ... + 'Type', paramType, ... + 'Prompt', def.Prompt, ... + 'Name', paramName, ... + 'Container', periphName ... + ); + else + param = mask.addParameter( ... + 'Type', paramType, ... + 'Prompt', def.Prompt, ... + 'Name', paramName, ... + 'Value', valStr, ... + 'Container', periphName ... + ); + end param.Evaluate = 'off'; @@ -406,9 +618,80 @@ classdef periphConfig else param.DialogControl.Row = 'current'; end - param.Alias = def.Def; + + if isfield(def, 'Def') + if strcmp(paramType, 'popup') + if iscell(def.Def) + param.TypeOptions = def.Def; + elseif isfield(def, 'Options') + param.Alias = def.Def; + param.TypeOptions = def.Options; + end + else + param.Alias = def.Def; + end + end + + callback = sprintf('periphConfig.periphParamCallback("%s");', paramName); + param.Callback = callback; end + + %% ELEMENTARY + + function clear_single_periph_code_param(mask, periph) + % Очистка кода одного поля конфига + paramNames = { + ['Hidden_' char(periph) '_Sources'], + ['Hidden_' char(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') + paramName = ['Hidden_' char(periph) '_Sources']; + try + param = mask.getParameter(paramName); + param.Value = configJs.convert_code_value(code.Sources); + catch + mcuMask.disp(0, ['Параметр ' paramName ' не найден']); + end + end + + % Сохраняем Includes, если они есть + if isfield(code, 'Includes') + paramName = ['Hidden_' char(periph) '_Includes']; + try + param = mask.getParameter(paramName); + param.Value = configJs.convert_code_value(code.Includes); + catch + mcuMask.disp(0, ['Параметр ' paramName ' не найден']); + end + end + end + + + + function res = ternary(cond, valTrue, valFalse) + if cond + res = valTrue; + else + res = valFalse; + end + end + end end diff --git a/McuLib/templates/MCU_Wrapper/run_mex.bat b/McuLib/templates/MCU_Wrapper/run_mex.bat index 5dcd00f..a635b0f 100644 --- a/McuLib/templates/MCU_Wrapper/run_mex.bat +++ b/McuLib/templates/MCU_Wrapper/run_mex.bat @@ -30,15 +30,17 @@ set defines_WRAPPER=-D"MATLAB"^ -D"__sizeof_ptr=8" :: -------------------------WRAPPER PATHS AND CODE--------------------------- :: оболочка, которая будет моделировать работу МК в симулинке -set includes_WRAPPER=-I"."^ - -I".\MCU_Wrapper"^ - -I".\app_wrapper" +:: WRAPPER BAT START + +:: WRAPPER BAT END + +:: APP WRAPPER BAT START + +:: APP WRAPPER BAT END + +set includes_WRAPPER= %includes_WRAPPER% %includes_APP_WRAPPER% +set code_WRAPPER= %code_WRAPPER% %code_APP_WRAPPER% -set code_WRAPPER= .\MCU_Wrapper\MCU.c^ - .\MCU_Wrapper\mcu_wrapper.c^ - .\app_wrapper\app_init.c^ - .\app_wrapper\app_io.c^ - .\app_wrapper\app_wrapper.c :: PERIPH BAT START diff --git a/mcuwrapper.prj b/mcuwrapper.prj index 9b198c6..f556f14 100644 --- a/mcuwrapper.prj +++ b/mcuwrapper.prj @@ -1,5 +1,5 @@ - + MCU Wrapper Razvalyaev wot890089@mail.ru @@ -7,7 +7,7 @@ Library for run MCU program in Simulink - 1.01 + 1.02 ${PROJECT_ROOT}\MCU Wrapper.mltbx @@ -27,7 +27,7 @@ false - findjobj - find java handles of Matlab graphic objects + findjobj - find java handles of Matlab graphic objects @@ -96,12 +96,49 @@ - E:\.WORK\MATLAB\mcu_matlab\MCU Wrapper.mltbx + F:\Work\Projects\MATLAB\mcu_matlab_lib\MCU Wrapper.mltbx - C:\Program Files\MyProgs\MATLAB\R2023a - + C:\Program Files\MATLAB\R2023a + + + + + + + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + false