% Компилирует S-function function res = mexing(compile_mode) global Ts Ts = 0.00001; if compile_mode == 1 delete('*.mexw64') delete('*.mexw64.pdb') delete([mcuPath.get('wrapperPath'), '\Outputs\*.*']); set_param(gcb, 'consoleOutput', ''); compiler.updateRunBat(); % Дефайны definesUserArg = parseDefinesMaskText(); definesWrapperConfigArg = buildWrapperDefinesString(); definesPeriphConfigArg = buildConfigDefinesString(); definesConfigArg = [definesWrapperConfigArg + " " + definesPeriphConfigArg]; %режимы компиляции if mcuMask.read_checkbox('enableDebug') modeArg = "debug"; else modeArg = "release"; end if mcuMask.read_checkbox('fullOutput') || mcuMask.read_checkbox('extConsol') echoArg = 'echo_enable'; else echoArg = 'echo_disable'; end [includesArg, codeArg] = make_mex_arguments('incTable', 'srcTable'); Name = mcuMask.get_name(); % Вызов батника с двумя параметрами: includes и code 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); else [status, cmdout]= system(cmd); end % Сохраним вывод в параметр маски с именем 'consoleOutput' mcuMask.disp(1, cmdout); block = gcb; newName = get_param(block, 'sfuncName'); oldName = get_param(block, 'FunctionName'); if ~strcmp(newName, oldName) set_param(block, 'FunctionName', newName); end newParam = get_param(block, 'sfuncParam'); oldParam = get_param(block, 'Parameters'); if ~strcmp(newParam, oldParam) set_param(block, 'Parameters', newParam); end if status == 0 res = 0; else res = 1; end beep else blockPath = gcb; config = configJs.read(blockPath); config = configJs.update(blockPath, config); configJs.write(config); periphConfig.updateMask(blockPath, config); end end %% COMPILE PARAMS function [includesArg, codeArg] = make_mex_arguments(incTableName, srcTableame) %MAKE_MEX_ARGUMENTS Формирует строки аргументов для вызова mex-компиляции через батник % % [includesArg, codeArg] = make_mex_arguments(includesCell, codeCell) % % Вход: % includesCell — ячейковый массив путей к директориям include % codeCell — ячейковый массив исходных файлов % % Выход: % includesArg — строка для передачи в батник, например: "-I"inc1" -I"inc2"" % codeArg — строка с исходниками, например: ""src1.c" "src2.cpp"" % Здесь пример получения из маски текущего блока (замени по своему) includesCell = customtable.parse(incTableName); codeCell = customtable.parse(srcTableame); % Оборачиваем пути в кавычки и добавляем -I includesStr = strjoin(cellfun(@(f) ['-I"' f '"'], includesCell, 'UniformOutput', false), ' '); % Оборачиваем имена файлов в кавычки codeStr = strjoin(cellfun(@(f) ['"' f '"'], codeCell, 'UniformOutput', false), ' '); % Удаляем символ переноса строки и пробел в конце, если вдруг попал codeStr = strtrim(codeStr); includesStr = strtrim(includesStr); % Оборачиваем всю строку в кавычки, чтобы батник корректно понял % includesArg = ['"' includesStr '"']; % codeArg = ['"' codeStr '"']; includesArg = includesStr; codeArg = codeStr; end function definesWrapperArg = buildWrapperDefinesString() definesWrapperArg = ''; definesWrapperArg = addDefineByParam(definesWrapperArg, 'enableThreading', 0); definesWrapperArg = addDefineByParam(definesWrapperArg, 'enableDeinit', 0); definesWrapperArg = addDefineByParam(definesWrapperArg, 'threadCycles', 1); definesWrapperArg = addDefineByParam(definesWrapperArg, 'mcuClk', 1); end function definesUserArg = parseDefinesMaskText() blockHandle = gcbh; % Получаем MaskValues и MaskNames maskValues = get_param(blockHandle, 'MaskValues'); paramNames = get_param(blockHandle, 'MaskNames'); % Индекс параметра userDefs idxUserDefs = find(strcmp(paramNames, 'userDefs')); definesText = maskValues{idxUserDefs}; % Текст с пользовательскими определениями % Убираем буквальные символы \n и \r definesText = strrep(definesText, '\n', ' '); definesText = strrep(definesText, '\r', ' '); % Разбиваем по переносам строк lines = split(definesText, {'\n', '\r\n', '\r'}); parts = strings(1,0); % пустой массив строк for k = 1:numel(lines) line = strtrim(lines{k}); if isempty(line) continue; end % Разбиваем по пробелам, чтобы получить отдельные определения в строке tokens = split(line); for t = 1:numel(tokens) token = strtrim(tokens{t}); if isempty(token) continue; end eqIdx = strfind(token, '='); if isempty(eqIdx) % Просто ключ без значения parts(end+1) = sprintf('-D"%s"', token); else key = strtrim(token(1:eqIdx(1)-1)); val = strtrim(token(eqIdx(1)+1:end)); parts(end+1) = sprintf('-D"%s__EQ__%s"', key, val); end end end definesUserArg = strjoin(parts, ' '); end function definesWrapperArg = buildConfigDefinesString() blockHandle = gcbh; mask = Simulink.Mask.get(blockHandle); tabName = 'configTabAll'; % Имя вкладки (Prompt) tabCtrl = mask.getDialogControl(tabName); if isempty(tabCtrl) error('Вкладка с названием "%s" не найдена в маске', tabName); end params = mcuMask.collect_all_parameters(tabCtrl); definesWrapperArg = ''; for i = 1:numel(params) % Получаем имя параметра из контрола paramName = string(params(i)); try % Получаем объект параметра по имени param = mask.getParameter(paramName); % Определяем тип параметра switch lower(param.Type) case 'checkbox' definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, 0); case 'edit' definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, 1); case 'popup' definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, 0); otherwise % Необрабатываемые типы end catch ME % warning('Не удалось получить параметр "%s": %s', paramName, ME.message); end end end %% PARSE FUNCTIONS function definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, val_define) blockHandle = gcbh; mask = Simulink.Mask.get(blockHandle); % Получаем MaskValues, MaskNames maskValues = get_param(blockHandle, 'MaskValues'); paramNames = get_param(blockHandle, 'MaskNames'); param = mask.getParameter(paramName); % для alias % Найдём индекс нужного параметра idxParam = find(strcmp(paramNames, paramName), 1); if isempty(idxParam) error('Parameter "%s" not found in block mask parameters.', paramName); end % Берём 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 % Значение параметра val = maskValues{idxParam}; if strcmp(param.Evaluate, 'on') val = evalin('base', val); % Вычисляем выражение val = num2str(val); % Преобразуем результат в строку end % Формируем define с кавычками и значением newDefine = ['-D"' def_name '__EQ__' val '"']; elseif ~strcmp(param.Type, 'popup') if mcuMask.read_checkbox(paramName) % Формируем define с кавычками без значения newDefine = ['-D"' def_name '"']; else newDefine = ''; end else newDefine = ['-D"' def_name '__EQ__' val '"']; end % Добавляем новый define к существующему (string) if isempty(definesWrapperArg) || strlength(strtrim(definesWrapperArg)) == 0 definesWrapperArg = newDefine; else definesWrapperArg = definesWrapperArg + " " + newDefine; end end %% CONSOLE FUNCTIONS function cmdret = runBatAndShowOutput(cmd) import java.io.*; import java.lang.*; cmdEnglish = ['chcp 437 > nul && ' cmd]; pb = java.lang.ProcessBuilder({'cmd.exe', '/c', cmdEnglish}); pb.redirectErrorStream(true); process = pb.start(); reader = BufferedReader(InputStreamReader(process.getInputStream())); cmdret = ""; % Здесь будем накапливать весь вывод while true if reader.ready() line = char(reader.readLine()); if isempty(line) break; end cmdret = cmdret + string(line) + newline; % сохраняем вывод % Здесь выводим только новую строку safeLine = strrep(line, '''', ''''''); % Экранируем апострофы logWindow_append(safeLine); drawnow; % обновляем GUI else if ~process.isAlive() % дочитываем оставшиеся строки while reader.ready() line = char(reader.readLine()); if isempty(line) break; end cmdret = cmdret + string(line) + newline; % сохраняем вывод safeLine = strrep(line, '''', ''''''); logWindow_append(safeLine); drawnow; end break; end pause(0.2); end end process.waitFor(); end function logWindow_append(line) persistent fig hEdit jScrollPane jTextArea if isempty(fig) || ~isvalid(fig) fig = figure('Name', 'Log Window', 'Position', [100 100 600 400]); hEdit = uicontrol('Style', 'edit', ... 'Max', 2, 'Min', 0, ... 'Enable', 'on', ... 'FontName', 'Courier New', ... 'Position', [10 10 580 380], ... 'HorizontalAlignment', 'left', ... 'BackgroundColor', 'white', ... 'Tag', 'LogWindowFigure'); jScrollPane = findjobj(hEdit); % JScrollPane jTextArea = jScrollPane.getViewport.getView; % JTextArea внутри JScrollPane end oldText = get(hEdit, 'String'); if ischar(oldText) oldText = {oldText}; end set(hEdit, 'String', [oldText; {line}]); drawnow; % Автоскролл вниз: jTextArea.setCaretPosition(jTextArea.getDocument.getLength); drawnow; end %% READ CONFIGS function isOpen = isMaskDialogOpen(blockPath) isOpen = false; try % Получаем имя блока blockName = get_param(blockPath, 'Name'); % Получаем список окон MATLAB GUI jWindows = java.awt.Window.getWindows(); for i = 1:numel(jWindows) win = jWindows(i); % Проверка, что окно видимое и активно if win.isShowing() try title = char(win.getTitle()); % Проверка по ключевому слову, соответствующему заголовку маски if contains(title, ['Mask Editor: ' blockName]) || ... contains(title, ['Mask: ' blockName]) || ... contains(title, blockName) isOpen = true; return; end catch % Окно не имеет заголовка — пропускаем end end end catch isOpen = false; end end