mcu_matlab/McuLib/m/mexing.m
2025-11-07 14:52:52 +03:00

406 lines
15 KiB
Matlab
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

% Компилирует 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