matlab_stm_emulate/mexing.asv
Razvalyaev ead46d7d82 переделана структура задавания дефайнов
теперь для дефайна используется альтернативное имя alias. и есть функция, которая выставляет дефайн по chekcbox/edit и его альтернативной функции

для конфигов такая механика тоже реализована - и  она работает
2025-06-12 12:08:34 +03:00

459 lines
14 KiB
Plaintext
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
clear, clc
close;
Ts = 0.00001;
delete("*.mexw64")
delete("*.mexw64.pdb")
delete(".\MCU_Wrapper\Outputs\*.*");
set_param(gcb, 'consoleOutput', '');
% Флаг режима отладки
definesWrapperArg = buildWrapperDefinesString();
definesUserArg = parseDefinesMaskText();
definesConfigArg = buildConfigDefinesString();
definesAllArg = [definesUserArg + " " + definesWrapperArg + " " + definesConfigArg];
if read_checkbox('enableDebug')
modeArg = "debug";
else
modeArg = "release";
end
if read_checkbox('fullOutput') || read_checkbox('extConsol')
echoArg = 'echo_enable';
else
echoArg = 'echo_disable';
end
[includesArg, codeArg] = make_mex_arguments('incTable', 'srcTable');
% Вызов батника с двумя параметрами: includes и code
cmd = sprintf('.\\MCU_Wrapper\\run_mex.bat "%s" "%s" "%s" %s %s', includesArg, codeArg, definesAllArg, modeArg, echoArg);
if read_checkbox('extConsol')
cmdout = runBatAndShowOutput(cmd);
else
[status, cmdout]= system(cmd);
end
% Сохраним вывод в параметр маски с именем 'consoleOutput'
set_param(gcb, 'consoleOutput', cmdout);
% % Обновим Mask Display для показа
% maskDisplayStr = sprintf('disp(''%s'')', cmdout);
% set_param(gcb, 'MaskDisplay', maskDisplayStr);
beep
%% 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""
% Здесь пример получения из маски текущего блока (замени по своему)
blockHandle = gcbh; % или замени на нужный блок
includesCell = parseCellString(get_param(blockHandle, incTableName));
codeCell = parseCellString(get_param(blockHandle, 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);
% blockHandle = gcbh;
%
% % Получаем MaskValues и MaskNames
% maskValues = get_param(blockHandle, 'MaskValues');
% paramNames = get_param(blockHandle, 'MaskNames');
%
% % Индексы параметров
% idxCycles = find(strcmp(paramNames, 'threadCycles'));
% idxClk = find(strcmp(paramNames, 'mcuClk'));
%
% % Значения
% cyclesVal = maskValues{idxCycles};
% clkMHz = str2double(maskValues{idxClk});
% clkHz = round(clkMHz * 1e6);
%
% % Формируем defines в формате: -D"NAME=VALUE"
% if read_checkbox('enableThreading')
% def1 = ['-D"RUN_APP_MAIN_FUNC_THREAD"'];
% else
% def1 = [''];
% end
%
% if read_checkbox('enableDeinit')
% def2 = ['-D"DEINITIALIZE_AFTER_SIM"'];
% else
% def2 = [''];
% end
%
% def3 = ['-D"DEKSTOP_CYCLES_FOR_MCU_APP__EQ__' cyclesVal '"'];
% def4 = ['-D"MCU_CORE_CLOCK__EQ__' num2str(clkHz) '"'];
%
% definesWrapperArg = strjoin({def1, def2, def3, def4}, ' ');
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 = 'configTab'; % Имя вкладки (Prompt)
allControls = mask.getDialogControls();
tabCtrl = find_tab_by_name(allControls, tabName);
if isempty(tabCtrl)
error('Вкладка с названием "%s" не найдена в маске', tabName);
end
definesWrapperArg = '';
% Получаем все контролы внутри вкладки
children = tabCtrl.DialogControls;
for i = 1:numel(children)
ctrl = children(i);
isprop(ctrl, 'Type')
isprop(ctrl, 'Name')
if isprop(ctrl, 'Type') && isprop(ctrl, 'Name')
switch lower(ctrl.Type)
case 'checkbox'
definesWrapperArg = addDefineByParam(definesWrapperArg, ctrl.ParameterName, 0);
case 'edit'
definesWrapperArg = addDefineByParam(definesWrapperArg, ctrl.ParameterName, 1);
otherwise
% Пропускаем другие типы
end
end
end
% blockHandle = gcbh;
%
% % Получаем MaskValues и MaskNames
% maskValues = get_param(blockHandle, 'MaskValues');
% paramNames = get_param(blockHandle, 'MaskNames');
%
% % Индексы параметров
% idxCycles = find(strcmp(paramNames, 'threadCycles'));
% idxClk = find(strcmp(paramNames, 'mcuClk'));
%
% % Значения
% cyclesVal = maskValues{idxCycles};
% clkMHz = str2double(maskValues{idxClk});
% clkHz = round(clkMHz * 1e6);
%
% % Формируем defines в формате: -D"NAME=VALUE"
% if read_checkbox('enableThreading')
% def1 = ['-D"RUN_APP_MAIN_FUNC_THREAD"'];
% else
% def1 = [''];
% end
%
% if read_checkbox('enableDeinit')
% def2 = ['-D"DEINITIALIZE_AFTER_SIM"'];
% else
% def2 = [''];
% end
%
% def3 = ['-D"DEKSTOP_CYCLES_FOR_MCU_APP__EQ__' cyclesVal '"'];
% def4 = ['-D"MCU_CORE_CLOCK__EQ__' num2str(clkHz) '"'];
%
% definesWrapperArg = strjoin({def1, def2, def3, def4}, ' ');
end
%% PARSE FUNCTIONS
function out = parseCellString(str)
str = strtrim(str);
if startsWith(str, '{') && endsWith(str, '}')
str = str(2:end-1);
end
parts = split(str, ';');
out = cell(numel(parts), 1);
for i = 1:numel(parts)
el = strtrim(parts{i});
if startsWith(el, '''') && endsWith(el, '''')
el = el(2:end-1);
end
out{i} = el;
end
if isempty(out) || (numel(out) == 1 && isempty(out{1}))
out = {};
end
end
function str = cellArrayToString(cellArray)
quoted = cellfun(@(s) ['''' s ''''], cellArray, 'UniformOutput', false);
str = ['{' strjoin(quoted, ';') '}'];
end
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 из маски
alias = param.Alias;
if val_define ~= 0
% Значение параметра
val = maskValues{idxParam};
% Формируем define с кавычками и значением
newDefine = ['-D"' alias '__EQ__' val '"'];
else
if read_checkbox(paramName)
% Формируем define с кавычками без значения
newDefine = ['-D"' alias '"'];
else
newDefine = '';
end
end
% Добавляем новый define к существующему (string)
if isempty(definesWrapperArg) || strlength(strtrim(definesWrapperArg)) == 0
definesWrapperArg = newDefine;
else
definesWrapperArg = definesWrapperArg + " " + newDefine;
end
end
function checkbox_state = read_checkbox(checkboxName)
maskValues = get_param(gcbh, 'MaskValues');
paramNames = get_param(gcbh, 'MaskNames');
inxCheckBox = find(strcmp(paramNames, checkboxName));
checkbox_state_str = maskValues{inxCheckBox};
if strcmpi(checkbox_state_str, 'on')
checkbox_state = 1;
else
checkbox_state = 0;
end
end
function tab = find_tab_by_name(controls, targetName)
tab = [];
for i = 1:numel(controls)
ctrl = controls(i);
% Проверяем, вкладка ли это и совпадает ли имя
if isa(ctrl, 'Simulink.dialog.Tab') && strcmp(ctrl.Name, targetName)
tab = ctrl;
return;
end
% Если это контейнер — обходим его детей
children = get_children(ctrl);
if ~isempty(children)
tab = find_tab_by_name(children, targetName);
if ~isempty(tab)
return;
end
end
end
end
function children = get_children(ctrl)
if isprop(ctrl, 'DialogControls')
children = ctrl.DialogControls;
elseif isprop(ctrl, 'Controls')
children = ctrl.Controls;
elseif isprop(ctrl, 'Children')
children = ctrl.Children;
else
children = [];
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