mcu_matlab/McuLib/m/mcuMask.m
Razvalyaev 245592a821 minor
переименованы функции для app wrapper
2025-06-15 10:33:32 +03:00

507 lines
22 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.

classdef mcuMask
methods(Static)
% Following properties of 'maskInitContext' are avalaible to use:
% - BlockHandle
% - MaskObject
% - MaskWorkspace - Use get/set APIs to work with mask workspace.
function MaskInitialization(maskInitContext)
% Получаем хэндл текущего блока
blk = gcbh;
% Получаем объект маски текущего блока
mask = Simulink.Mask.get(gcb);
set_param(blk,"MaskSelfModifiable","on")
set_param(blk, 'LinkStatus', 'none');
% mcuMask.disp(1,'');
try
% Проверка наличия findjobj
findjobjAvailable = exist('findjobj', 'file') == 2;
catch
findjobjAvailable = false;
end
% Имя checkbox-параметра (укажите точное имя из маски)
checkboxParamName = 'extConsol'; % пример
findjobjLinkName = 'findjobj_link'; % пример
% Получаем параметр по имени
checkboxParam = mask.getParameter(checkboxParamName);
findjobjLink = mask.getDialogControl(findjobjLinkName);
if isempty(findjobjLink)
error('Параметр %s не найден в маске.', findjobjLinkName);
end
if isempty(checkboxParam)
error('Параметр %s не найден в маске.', checkboxParamName);
end
% Блокируем чекбокс, если findjobj не найден
if ~findjobjAvailable
checkboxParam.Enabled = 'off';
checkboxParam.Value = 'off'; % и на всякий случай снимаем галочку
checkboxParam.Prompt = 'External Console (requires findjobj)';
findjobjLink.Visible = 'on';
else
checkboxParam.Enabled = 'on';
checkboxParam.Prompt = 'External Console';
findjobjLink.Visible = 'off';
end
% формирование таблицы на всю ширину
table_names = {'srcTable', 'incTable'};
for k = 1:numel(table_names)
table_name = table_names{k};
customtable.format(table_name);
end
% запись описания блока
textDesc = ['Блок для настройки параметров симуляции микроконтроллера. ' newline ...
'Позволяет задавать параметры оболочки, приложения МК и периферии'];
% Получаем объект описания
toolTextArea = mask.getDialogControl('BlockDesc');
toolTextArea.Prompt = textDesc;
end
%% WRAPPER PARAMS
function enableThreading(callbackContext)
block = gcb;
maskNames = get_param(block, 'MaskNames');
maskValues = get_param(block, 'MaskValues');
maskEnables = get_param(block, 'MaskEnables');
idxEnable = find(strcmp(maskNames, 'enableThreading'));
idxEdit = find(strcmp(maskNames, 'threadCycles'));
if isempty(idxEnable) || isempty(idxEdit)
error('Параметры enableThreading или threadCycles не найдены в маске');
end
val = maskValues{idxEnable};
if strcmp(val, 'on')
maskEnables{idxEdit} = 'on';
else
maskEnables{idxEdit} = 'off';
end
set_param(block, 'MaskEnables', maskEnables);
end
function enableDeinit(callbackContext)
block = gcb;
maskNames = get_param(block, 'MaskNames');
maskValues = get_param(block, 'MaskValues');
maskEnables = get_param(block, 'MaskEnables');
idxEnable = find(strcmp(maskNames, 'enableThreading'));
idxEdit = find(strcmp(maskNames, 'threadCycles'));
if isempty(idxEnable) || isempty(idxEdit)
error('Параметры enableThreading или threadCycles не найдены в маске');
end
val = maskValues{idxEnable};
if strcmp(val, 'on')
maskEnables{idxEdit} = 'on';
else
maskEnables{idxEdit} = 'off';
end
set_param(block, 'MaskEnables', maskEnables);
end
function extConsol(callbackContext)
block = gcb;
mask = Simulink.Mask.get(block);
fullOut = mask.getParameter('fullOutput');
extCons = mask.getParameter('extConsol');
if isempty(extCons) || isempty(fullOut)
error('Параметры fullOutput или extConsol не найдены в маске');
end
if(strcmp(extCons.Enabled, 'on'))
if strcmp(extCons.Value, 'on')
fullOut.Enabled = 'off';
fullOut.Value = 'on';
else
fullOut.Enabled = 'on';
end
else
fullOut.Enabled = 'on';
end
end
function wrapperPath_add(callbackContext)
mcuPath.addPath('wrapperPath');
end
function appWrapperPath_add(callbackContext)
mcuPath.addPath('appWrapperPath');
end
%% USER WRAPPER CODE
function appWrapperFunc(callbackContext)
block = gcb;
% Получаем имя функции и путь к файлам
[filename, section, tool, example]= mcuMask.getWrapperUserFile(block);
mcuMask.tool(tool, example);
% Загружаем содержимое файла
set_param(block, 'appWrapperCode', '');
try
code = fileread(filename);
code = regexprep(code, '\r\n?', '\n'); % нормализуем окончания строк
includesText = editCode.extractSection(code, section);
set_param(block, 'appWrapperCode', includesText);
catch
end
% % Поиск тела обычной функции
% expr = sprintf('void %s()', sel);
% funcBody = editCode.extractSection(code, expr);
% set_param(block, 'wrapperCode', funcBody);
end
function saveAppWrapperCode(callbackContext)
block = gcb;
% Получаем имя функции и путь к файлам
[filename, section] = mcuMask.getWrapperUserFile(block);
if ~isfile(filename)
errordlg(['Файл не найден: ', filename]);
return;
end
sel = get_param(block, 'appWrapperFunc');
basePath = get_param(block, 'appWrapperPath');
if isempty(basePath)
errordlg('Не указан путь к файлам обёртки (wrapperPath).');
return;
end
newBody = get_param(block, 'appWrapperCode');
code = fileread(filename);
code = regexprep(code, '\r\n?', '\n');
newBody = strrep(newBody, '\', '\\');
code = editCode.insertSection(code, section, newBody);
% else
% % Обновляем тело функции
% expr = sprintf('void %s()', sel);
% code = editCode.insertSection(code, expr, newBody);
% end
fid = fopen(filename, 'w', 'n', 'UTF-8');
if fid == -1
errordlg('Не удалось открыть файл для записи');
return;
end
fwrite(fid, code);
fclose(fid);
mcuMask.disp(1, ['Обновлено: ' sel]);
end
function openAppWrapperCode(callbackContext)
block = gcb;
% Получаем имя функции и путь к файлам
filename = mcuPath.getAbsolutePath(mcuMask.getWrapperUserFile(block));
if exist(filename, 'file') == 2
% Формируем команду без кавычек
cmd = sprintf('rundll32.exe shell32.dll,OpenAs_RunDLL %s', filename);
status = system(cmd);
if status ~= 0
errordlg('Не удалось открыть окно выбора приложения.');
end
else
errordlg('Файл не найден');
end
end
%% USER CODE
function srcTable(callbackContext)
customtable.update('srcTable');
end
function incTable(callbackContext)
customtable.update('incTable');
end
function btnAddSrc(callbackContext)
mcuPath.addSourceFileTable('srcTable', 'Выберите исходные файлы');
end
function btnAddInc(callbackContext)
mcuPath.addPathTable('incTable', 'Выберите папку с заголовочными файлами');
end
%% PERIPH CONFIG
function periphPath_add(callbackContext)
mcuPath.addAnyFile('periphPath');
end
%% COMPILE
function compile(callbackContext)
compiler.compile();
end
function updateModel(callbackContext)
addpath(mcuPath.get('wrapperPath'));
res = mexing(1);
if res ~= 0
return;
end
modelName = bdroot(gcb); % получить имя верхнего уровня модели
blockName = gcb;
mgr = asynchManage(modelName, blockName); % создать объект класса
mgr.saveAndUpdateModel(); % запустить сохранение и обновление
end
function findjobj_link(callbackContext)
web('https://www.mathworks.com/matlabcentral/fileexchange/14317-findjobj-find-java-handles-of-matlab-graphic-objects');
end
function set_name(callbackContext)
block = gcb;
% Получаем параметр имени S-Function из маски блока
newName = mcuMask.get_name();
% Путь к файлу, в котором надо заменить строку
cFilePath = fullfile(pwd, mcuPath.get('wrapperPath'), 'MCU.c'); % <-- укажи правильный путь
% Считаем файл в память
try
fileText = fileread(cFilePath);
catch
return;
end
% Регулярное выражение для поиска строки с define
% Заменим строку вида: #define S_FUNCTION_NAME old_name
pattern = '#define\s+S_FUNCTION_NAME\s+\w+';
% Новая строка
newLine = ['#define S_FUNCTION_NAME ', newName];
% Замена
updatedText = regexprep(fileText, pattern, newLine);
% Записываем обратно в файл
fid = fopen(cFilePath, 'w', 'n', 'UTF-8');
if fid == -1
error('Не удалось открыть файл для записи.');
end
fwrite(fid, updatedText);
fclose(fid);
end
end
%% SPECIFIC TOOLS
methods(Static, Access = private)
function [filename, section, tool, example] = getWrapperUserFile(block)
sel = get_param(block, 'appWrapperFunc');
basePath = mcuPath.get('appWrapperPath');
if isempty(basePath)
errordlg('Не указан путь к файлам обёртки (wrapperPath).');
return;
end
% Формируем путь к файлу в зависимости от типа запроса
if strcmp(sel, 'Includes')
filename = fullfile(basePath, 'app_includes.h');
section = '// INCLUDES';
tool = 'Инклюды для доступа к коду МК в коде оболочке';
example = '#include "main.h"';
elseif strcmp(sel, 'Dummy')
filename = fullfile(basePath, 'app_wrapper.c');
section = '// DUMMY';
tool = 'Заглушки для различных функций и переменных';
example = ['CAN_HandleTypeDef hcan = {0};' newline...
'void hardware_func(handle *huart) {}' newline...
'int wait_for_hardware_flag(int *flag) {' newline...
' return 1;' newline...
'}' newline...
''];
elseif strcmp(sel, 'App Init')
filename = fullfile(basePath, 'app_init.c');
section = '// USER APP INIT';
tool = ['Код для инициализации приложения МК.' newline newline...
'Вызов функций инициализации, если не используется отдельный поток для main().'];
example = 'init_func();';
elseif strcmp(sel, 'App Step')
filename = fullfile(basePath, 'app_wrapper.c');
section = '// USER APP STEP';
tool = ['Код приложения МК для вызова в шаге симуляции.' newline newline ...
'Вызов функций программы МК, если не используется отдельный поток для main().'];
example = 'step_func();';
elseif strcmp(sel, 'App Inputs')
filename = fullfile(basePath, 'app_io.c');
section = '// USER APP INPUT';
tool = ['Работа с буффером для портов S-Function' newline newline ...
'Буфер в начале хранит входные порты S-Function, далее идут выходные порты:' newline ...
'Buffer[0:15] - входной порт, Buffer[16:31] - входной 1 порт, ' newline ...
'Buffer[32:47] - выходной 1 порт, Buffer[48:63] - выходной 2 порт'];
example = ['// чтение 1-го элемента 0-го входного массива' newline...
'app_variable_2 = ReadInputArray(0, 1);' newline newline...
'// запись в буфер выходов' newline ...
'app_variable_2 = Buffer[10];'];
elseif strcmp(sel, 'App Outputs')
filename = fullfile(basePath, 'app_io.c');
section = '// USER APP OUTPUT';
tool = ['Работа с буффером для портов S-Function' newline newline ...
'Буфер в начале хранит входные порты S-Function, далее идут выходные порты:' newline ...
'Buffer[0:15] - входной порт, Buffer[16:31] - входной 1 порт, ' newline ...
'Buffer[32:47] - выходной 1 порт, Buffer[48:63] - выходной 2 порт'];
example = ['// запись в 1-й элемент 0-го выходного массива' newline...
'WriteOutputArray(app_variable, 0, 1);' newline newline ...
'// запись в буфер выходов' newline ...
'Buffer[XD_OUTPUT_START + 10] = app_variable_2;'];
elseif strcmp(sel, 'App Deinit')
filename = fullfile(basePath, 'app_init.c');
section = '// USER APP DEINIT';
tool = ['Код для деинициализации приложения МК.' newline newline ...
'Можно деинициализировать приложение МК, для повторного запуска.'];
example = 'memset(&htim1, sizeof(htim1), 0;';
else
tool = '';
mcuMask.disp(0, '\nОшибка выбора типа секции кода: неизвестное значение');
end
end
end
%% GENERAL TOOLS
methods(Static, Access = public)
function saveAndClose(blockPath)
try
% Считываем текущее имя модели
modelName = bdroot(blockPath);
% Включаем возможность изменения маски
set_param(blockPath, 'MaskSelfModifiable', 'on');
% Считываем текущие значения параметров маски
currentMaskValues = get_param(blockPath, 'MaskValues');
% Применяем текущие значения заново, чтобы "применить" маску
set_param(blockPath, 'MaskValues', currentMaskValues);
save_system(modelName);
catch ME
warning('progr:Nneg', 'Ошибка при сохранении маски: %s', ME.message);
end
close_system(blockPath, 0);
end
function open(blockPath, clear_flag)
open_system(blockPath, 'mask');
mcuMask.disp(clear_flag, '');
end
function name = get_name()
block = gcb;
% Получаем параметр имени S-Function из маски блока
name = get_param(block, 'sfuncName');
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 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
function params = collect_all_parameters(container)
params = {};
children = container.DialogControls;
for i = 1:numel(children)
ctrl = children(i);
if isa(ctrl, 'Simulink.dialog.Tab')
% Если вкладка — рекурсивно собираем параметры внутри неё
params = [params, mcuMask.collect_all_parameters(ctrl)];
else
% Иначе это параметр — добавляем имя
params{end+1} = ctrl.Name; %#ok<AGROW>
end
end
end
function delete_all_tabs(mask, container)
children = container.DialogControls;
% Идём в обратном порядке, чтобы безопасно удалять
for i = numel(children):-1:1
ctrl = children(i);
if isa(ctrl, 'Simulink.dialog.Tab')
% Сначала рекурсивно удаляем вкладки внутри текущей вкладки
mcuMask.delete_all_tabs(mask, ctrl);
try
container.removeDialogControl(ctrl.Name);
catch ME
warning('Не удалось удалить вкладку %s: %s', ctrl.Name, ME.message);
end
end
end
end
function tool(text, example)
% Устанавливает заданный текст в параметр Text Area 'toolText' через объект маски
% Получаем ссылку на текущий блок
block = gcb;
% Получаем объект маски
mask = Simulink.Mask.get(block);
toolTextArea = mask.getDialogControl('toolText');
exampleTextArea = mask.getDialogControl('exampleText');
toolTextArea.Prompt = text;
exampleTextArea.Prompt = example;
end
function disp(clcFlag, varargin)
if clcFlag
set_param(gcb, 'consoleOutput', '');
end
if length(varargin) == 1 && ischar(varargin{1})
% Если передан один аргумент — просто строка, передаем напрямую
out = varargin{1};
else
% Иначе считаем, что первый аргумент — формат, остальные — параметры
out = sprintf(varargin{:});
end
out_now = get_param(gcb, 'consoleOutput');
set_param(gcb, 'consoleOutput', [out_now out]);
end
function updateModelAsync()
mdl = bdroot(gcb);
try
% Применить изменения, если есть
set_param(mdl, 'ApplyChanges', 'on');
catch
beep
% Игнорировать, если не удалось
end
t = timer('StartDelay', 0.01, 'TimerFcn', @(~,~) set_param(mdl, 'SimulationCommand', 'update'));
start(t);
end
end
end