% Компилирует S-function для блока микроконтроллера в Simulink % compile_mode: 1 - компиляция, 0 - обновление конфигурации function res = mexing(compile_mode) global Ts Ts = 0.00001; % Установка глобального времени дискретизации if compile_mode == 1 % === РЕЖИМ КОМПИЛЯЦИИ === setenv('VSLANG', '1033'); % Английский для Visual Studio % Обновление параметров блока 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 % Очистка предыдущих файлов компиляции delete('*.mexw64') delete('*.mexw64.pdb') delete([mcuPath.get('wrapperPath'), '\Outputs\*.*']); set_param(gcb, 'consoleOutput', ''); % Очистка консоли вывода % Обновление BAT-файла для компиляции 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(); % Имя S-функции % Вызов батника компиляции 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 % Сохранение вывода в параметр маски mcuMask.disp(1, cmdout); 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) % Формирует строки аргументов для вызова mex-компиляции через батник % % Вход: % incTableName - имя таблицы с путями включения % srcTableame - имя таблицы с исходными файлами % % Выход: % includesArg - строка с флагами включения (-I"path") % codeArg - строка с исходными файлами ("file1.c" "file2.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; 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 = get_param(blockHandle, 'MaskValues'); paramNames = get_param(blockHandle, 'MaskNames'); % Поиск параметра с пользовательскими дефайнами idxUserDefs = find(strcmp(paramNames, 'userDefs')); definesText = maskValues{idxUserDefs}; % Обработка специальных символов 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'; % Имя вкладки с конфигурацией 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 % Игнорирование ошибок для отсутствующих параметров end end end function definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, val_define) % Добавление дефайна на основе параметра маски blockHandle = gcbh; mask = Simulink.Mask.get(blockHandle); % Получение значений маски maskValues = get_param(blockHandle, 'MaskValues'); paramNames = get_param(blockHandle, 'MaskNames'); param = mask.getParameter(paramName); % Поиск индекса параметра idxParam = find(strcmp(paramNames, paramName), 1); if isempty(idxParam) error('Parameter "%s" not found in block mask parameters.', paramName); end % Определение имени дефайна (алиас или значение) 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 newDefine = ['-D"' def_name '__EQ__' val '"']; elseif ~strcmp(param.Type, 'popup') % Чекбоксы if mcuMask.read_checkbox(paramName) newDefine = ['-D"' def_name '"']; else newDefine = ''; end else % Выпадающие списки if strcmp(param.Alias, '') newDefine = ['-D"' def_name '"']; else newDefine = ['-D"' def_name '__EQ__' val '"']; end end % Добавление дефайна к результирующей строке if isempty(definesWrapperArg) || strlength(strtrim(definesWrapperArg)) == 0 definesWrapperArg = newDefine; else definesWrapperArg = definesWrapperArg + " " + newDefine; end end %% CONSOLE FUNCTIONS - функции работы с консолью function cmdret = runBatAndShowOutput(cmd) % Запуск BAT-файла с отображением вывода в реальном времени 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; 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'); % Получение Java-компонентов для управления скроллингом jScrollPane = findjobj(hEdit); jTextArea = jScrollPane.getViewport.getView; end % Добавление новой строки oldText = get(hEdit, 'String'); if ischar(oldText) oldText = {oldText}; end set(hEdit, 'String', [oldText; {line}]); drawnow; % Автоскроллинг вниз jTextArea.setCaretPosition(jTextArea.getDocument.getLength); drawnow; end function isOpen = isMaskDialogOpen(blockPath) % Проверка открыто ли диалоговое окно маски isOpen = false; try blockName = get_param(blockPath, 'Name'); % Получение всех открытых окон Java 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