matlab_stm_emulate/mexing.asv

638 lines
21 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
function mexing(compile_mode)
global Ts
Ts = 0.00001;
if compile_mode == 1
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);
beep
else
blockPath = bdroot;
config = load_periph_config();
update_mask_from_config(blockPath, config);
% set_param(gcb, 'consoleOutput', 'Peripheral configuration file loaded. Re-open Block Parameters');
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""
% Здесь пример получения из маски текущего блока (замени по своему)
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);
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);
% Получаем имя параметра из контрола
paramName = ctrl.Name;
try
% Получаем объект параметра по имени
param = mask.getParameter(paramName);
% Определяем тип параметра
switch lower(param.Type)
case 'checkbox'
definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, 0);
case 'edit'
definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, 1);
otherwise
% Необрабатываемые типы
end
catch ME
warning('Не удалось получить параметр "%s": %s', paramName, ME.message);
end
end
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
%% 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 config = load_periph_config()
jsonText = fileread('periph_config.json');
config = jsondecode(jsonText);
end
%% CONFIG MASK TOOLS
function update_mask_from_config(blockPath, config)
blockPath = [blockPath '/MCU'];
% Проверяем, была ли маска открыта
wasOpen = isMaskDialogOpen(blockPath);
close_system(blockPath, 0);
mask = Simulink.Mask.get(blockPath);
tableNames = {'incTable', 'srcTable'};
columns_backup = clear_tables(blockPath, tableNames);
containerName = 'configTabAll';
clear_all_from_container(mask, containerName);
% Ищем контейнер, в который будем добавлять вкладки
allControls = mask.getDialogControls();
container = find_container_by_name(allControls, containerName);
if isempty(container)
error('Контейнер "%s" не найден в маске.', containerName);
end
% Проходим по каждому модулю (ADC, TIM...)
periphs = fieldnames(config);
for i = 1:numel(periphs)
periph = periphs{i};
defines = config.(periph).Defines;
defNames = fieldnames(defines);
% Создаём вкладку для модуля
tabCtrl = mask.addDialogControl( ...
'Type', 'Tab', ...
'Prompt', periph, ...
'Name', [periph '_Tab'], ...
'Container', containerName ...
);
for j = 1:numel(defNames)
defPrompt = defNames{j};
def = defines.(defPrompt);
% Только checkbox и edit
switch lower(def.Type)
case 'checkbox'
paramType = 'checkbox';
case 'edit'
paramType = 'edit';
otherwise
continue;
end
paramName = matlab.lang.makeValidName(defPrompt);
% Преобразуем значение по типу
val = def.Default;
if islogical(val)
valStr = logical(val) * "on" + ~val * "off";
elseif isnumeric(val)
valStr = num2str(val);
elseif ischar(val)
valStr = val;
else
error('Unsupported default value type for %s.%s', periph, defPrompt);
end
% Добавляем параметр в соответствующую вкладку
mask.addParameter( ...
'Type', paramType, ...
'Prompt', defPrompt, ...
'Name', paramName, ...
'Value', valStr, ...
'Container', periph ...
);
param = mask.getParameter(paramName);
param.Alias = def.Def;
end
end
% Восстанавливаем таблицы
restore_tables(blockPath, tableNames, columns_backup);
% Повторно открываем маску, если она была открыта
if wasOpen
open_system(blockPath, 'mask');
end
end
function clear_all_from_container(mask, containerName)
% Очищает все параметры и вкладки внутри указанного контейнера
allControls = mask.getDialogControls();
container = find_container_by_name(allControls, containerName);
if isempty(container)
error('Контейнер с именем "%s" не найден.', containerName);
end
children = container.DialogControls;
for i = numel(children):-1:1
ctrl = children(i);
try
% Пытаемся удалить как контрол (вкладка, контейнер и пр.)
mask.removeDialogControl(ctrl.Name);
catch
try
% Если не получилось — пробуем как параметр
res = mask.removeParameter(ctrl.Name);
catch
warning('Не удалось удалить "%s".', ctrl.Name);
end
end
end
end
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
function column_titles = clear_tables(block, table_names)
% Очищает столбцы в каждой таблице из массива имен table_names
% Возвращает cell-массив с названиями первых столбцов каждой таблицы
% Получить объект маски блока
maskObj = Simulink.Mask.get(block);
% Инициализировать cell-массив для хранения названий столбцов
column_titles = cell(size(table_names));
for k = 1:numel(table_names)
table_name = table_names{k};
% Получить объект управления таблицей
tableControl = maskObj.getDialogControl(table_name);
% Получить количество столбцов
nCols = tableControl.getNumberOfColumns;
if nCols > 0
% Получить первый столбец (который будем удалять)
column = tableControl.getColumn(1);
column_titles{k} = column.Name;
% Удаляем все столбцы
% Важно: при удалении столбцов индексы меняются,
% поэтому удаляем всегда первый столбец nCols раз
for i = 1:nCols
tableControl.removeColumn(1);
end
else
% Если столбцов нет, возвращаем пустую строку
column_titles{k} = '';
end
end
end
function restore_tables(block, table_names, column_titles)
% Восстанавливает первый столбец в каждой таблице из массива имен
% Использует массив column_titles для установки имени столбца
% Получить объект маски блока
maskObj = Simulink.Mask.get(block);
for k = 1:numel(table_names)
table_name = table_names{k};
title = column_titles{k};
% Получить объект управления таблицей
tableControl = maskObj.getDialogControl(table_name);
% Добавить новый столбец
column = tableControl.addColumn(Name='title', Type='edit');
column.Name = title;
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 container = find_container_by_name(controls, targetName)
container = [];
for i = 1:numel(controls)
ctrl = controls(i);
% Проверяем, контейнер ли это и совпадает ли имя
if isa(ctrl, 'Simulink.dialog.Container') && strcmp(ctrl.Name, targetName)
container = ctrl;
return;
end
% Если это вложенный контрол — обходим его детей
children = get_children(ctrl);
if ~isempty(children)
container = find_container_by_name(children, targetName);
if ~isempty(container)
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