diff --git a/McuLib/m/compiler.m b/McuLib/m/compiler.m new file mode 100644 index 0000000..6f2b3e7 --- /dev/null +++ b/McuLib/m/compiler.m @@ -0,0 +1,139 @@ +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 = { '.\' + }; + periphPath = mcuPath.get('wrapperPath'); + % Формируем строки + wrapperSrcText = compiler.createSourcesBat('code_WRAPPER', sources, periphPath); + wrapperIncText = compiler.createIncludesBat('includes_WRAPPER', includes, periphPath); + + % Записываем результат + 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'); % Всё прошло успешно + + + 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/mcuMask.m b/McuLib/m/mcuMask.m index da7bd16..f6fe0c7 100644 --- a/McuLib/m/mcuMask.m +++ b/McuLib/m/mcuMask.m @@ -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'; % пример @@ -121,19 +119,11 @@ classdef mcuMask 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; - + mcuPath.addPath('wrapperPath'); + end + + function appWrapperPath_add(callbackContext) + mcuPath.addPath('appWrapperPath'); end %% USER WRAPPER CODE @@ -199,7 +189,7 @@ classdef mcuMask block = gcb; % Получаем имя функции и путь к файлам - filename = mcuMask.getAbsolutePath(mcuMask.getWrapperUserFile(block)); + filename = mcuPath.getAbsolutePath(mcuMask.getWrapperUserFile(block)); if exist(filename, 'file') == 2 % Формируем команду без кавычек cmd = sprintf('rundll32.exe shell32.dll,OpenAs_RunDLL %s', filename); @@ -222,83 +212,27 @@ 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 + %% COMPILE function compile(callbackContext) - addpath('MCU_Wrapper'); - mexing(1); + compiler.compile(); end function updateModel(callbackContext) - addpath('MCU_Wrapper'); + addpath(mcuPath.get('wrapperPath')); res = mexing(1); if res ~= 0 return; @@ -315,13 +249,13 @@ classdef mcuMask web('https://www.mathworks.com/matlabcentral/fileexchange/14317-findjobj-find-java-handles-of-matlab-graphic-objects'); end - function set_name() + function set_name(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 @@ -348,12 +282,6 @@ classdef mcuMask fwrite(fid, updatedText); fclose(fid); end - - function name = get_name() - block = gcb; - % Получаем параметр имени S-Function из маски блока - name = get_param(block, 'sfuncName'); - end end @@ -459,81 +387,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 +453,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' через объект маски diff --git a/McuLib/m/mcuPath.m b/McuLib/m/mcuPath.m new file mode 100644 index 0000000..94ba3e9 --- /dev/null +++ b/McuLib/m/mcuPath.m @@ -0,0 +1,168 @@ +classdef mcuPath + methods(Static) + + + + function path = get(paramName) + blockPath = gcb; + path = get_param(blockPath, paramName); + end + + + 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 + + + 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 + + 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..aa10699 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); diff --git a/McuLib/m/periphConfig.m b/McuLib/m/periphConfig.m index 16bd25c..47decb0 100644 --- a/McuLib/m/periphConfig.m +++ b/McuLib/m/periphConfig.m @@ -7,7 +7,7 @@ classdef periphConfig % Проверяем, была ли маска открыта % wasOpen = isMaskDialogOpen(blockPath); mask = Simulink.Mask.get(blockPath); - periphPath = get_param(blockPath, 'periphPath'); + periphPath = mcuPath.get('periphPath'); [periphPath, ~, ~] = fileparts(periphPath); tableNames = {'incTable', 'srcTable'}; @@ -91,7 +91,7 @@ classdef periphConfig for i = 1:numel(periphs) periph = periphs{i}; - % Пропускаем Code и UserCode, они уже обработаны + % Пропускаем Code и UserCode if strcmp(periph, 'Code') || strcmp(periph, 'UserCode') continue; end @@ -165,86 +165,24 @@ classdef periphConfig fwrite(fid, jsonText, 'char'); fclose(fid); end - - function clear_all_from_container(mask, containerName) - % allControls = mask.getDialogControls(); - container = mask.getDialogControl(containerName); - if isempty(container) - warning('Контейнер "%s" не найден.', containerName); - return; - end - - % Рекурсивно собрать все параметры (не вкладки) - paramsToDelete = mcuMask.collect_all_parameters(container); - - % Удаляем все параметры - for i = 1:numel(paramsToDelete) - try - mask.removeParameter(paramsToDelete{i}); - catch - warning('Не удалось удалить параметр %s', paramsToDelete{i}); - end - end - - % Рекурсивно удалить все вкладки внутри контейнера - mcuMask.delete_all_tabs(mask, container); - end + + end + + methods(Static, Access=private) - + function res = addCodeConfig(codeConfig, periphPath) % Возвращает 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.Options, periphPath); + incText = compiler.createIncludesBat('includes_PERIPH', codeConfig.Includes.Options, periphPath); % Записываем результат - res = periphConfig.updateRunMexBat(srcText, incText); % Всё прошло успешно + res = compiler.updateRunMexBat(srcText, incText, ':: PERIPH BAT'); % Всё прошло успешно catch % В случае ошибки просто возвращаем 1 res = 1; @@ -289,7 +227,7 @@ classdef periphConfig % % Возвращает: % res - 0 при успехе, 1 при ошибке - wrapPath = fullfile('.\MCU_Wrapper', 'mcu_wrapper.c'); + wrapPath = fullfile(mcuPath.get('wrapperPath'), 'mcu_wrapper.c'); res = 1; try code = fileread(wrapPath); @@ -314,36 +252,6 @@ classdef periphConfig - 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) % mask — объект маски Simulink.Mask.get(blockPath) @@ -371,6 +279,8 @@ classdef periphConfig paramType = 'checkbox'; case 'edit' paramType = 'edit'; + case 'popup' + paramType = 'popup'; otherwise % Игнорируем остальные типы return; @@ -378,26 +288,45 @@ 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 = ''; % по умолчанию — ничего + 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 +335,46 @@ classdef periphConfig else param.DialogControl.Row = 'current'; end - param.Alias = def.Def; + + if strcmp(paramType, 'popup') + param.TypeOptions = def.Def; + else + param.Alias = def.Def; + end end + function clear_all_from_container(mask, containerName) + % allControls = mask.getDialogControls(); + container = mask.getDialogControl(containerName); + if isempty(container) + warning('Контейнер "%s" не найден.', containerName); + return; + end + + % Рекурсивно собрать все параметры (не вкладки) + paramsToDelete = mcuMask.collect_all_parameters(container); + + % Удаляем все параметры + for i = 1:numel(paramsToDelete) + try + mask.removeParameter(paramsToDelete{i}); + catch + warning('Не удалось удалить параметр %s', paramsToDelete{i}); + end + end + + % Рекурсивно удалить все вкладки внутри контейнера + mcuMask.delete_all_tabs(mask, container); + end + + function res = ternary(cond, valTrue, valFalse) + if cond + res = valTrue; + else + res = valFalse; + end + end + end end