pre-release 1.04

This commit is contained in:
2025-11-07 14:52:52 +03:00
parent 041322a62e
commit 49be34efc9
16 changed files with 974 additions and 676 deletions

View File

@@ -1,156 +1,172 @@
classdef mcuMask
%% CALLBACKS
% Главный класс управления маской блока Simulink для микроконтроллера
% Содержит callback-функции и утилиты для работы с маской
%% CALLBACKS - функции обратного вызова для элементов маски
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,'');
% Проверяем доступность findjobj для внешней консоли
try
% Проверка наличия findjobj
findjobjAvailable = exist('findjobj', 'file') == 2;
catch
findjobjAvailable = false;
end
% Имя checkbox-параметра (укажите точное имя из маски)
checkboxParamName = 'extConsol'; % пример
findjobjLinkName = 'findjobj_link'; % пример
% Получаем параметр по имени
% Настраиваем параметры внешней консоли в зависимости от доступности findjobj
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 не найден
% Блокируем чекбокс если findjobj не найден
if ~findjobjAvailable
checkboxParam.Enabled = 'off';
checkboxParam.Value = 'off'; % и на всякий случай снимаем галочку
checkboxParam.Value = 'off';
checkboxParam.Prompt = 'External Console (requires findjobj)';
findjobjLink.Visible = 'on';
findjobjLink.Visible = 'on'; % Показываем ссылку для скачивания
else
checkboxParam.Enabled = 'on';
checkboxParam.Prompt = 'External Console';
findjobjLink.Visible = 'off';
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
%% WRAPPER PARAMS - callback-функции для параметров обёртки
function wrapperPath_add(callbackContext)
% Добавление пути к файлам обёртки
mcuPath.addPath('wrapperPath');
end
function enableThreading(callbackContext)
% Включение/выключение многопоточности
mainWrap.enableThreading();
end
function enableDeinit(callbackContext)
% Включение/выключение деинициализации
mainWrap.enableDeinit();
end
function extConsol(callbackContext)
% Управление внешней консолью
mainWrap.extConsol();
end
%% USER WRAPPER CODE
%% USER WRAPPER CODE - callback-функции для пользовательского кода
function appWrapperPath_add(callbackContext)
% Добавление пути к файлам обёртки приложения
mcuPath.addPath('appWrapperPath');
end
function appWrapperFunc(callbackContext)
% Загрузка функции обёртки для редактирования
appWrap.appWrapperFunc();
end
function saveAppWrapperCode(callbackContext)
% Сохранение отредактированного кода обёртки
appWrap.saveAppWrapperCode();
end
function openAppWrapperCode(callbackContext)
% Открытие кода обёртки во внешнем редакторе
appWrap.openAppWrapperCode();
end
%% USER CODE
%% USER CODE - callback-функции для пользовательского кода
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
%% PERIPH CONFIG - callback-функции для конфигурации периферии
function periphPath_add(callbackContext)
% Добавление пути к конфигурационному файлу периферии
mcuPath.addAnyFile('periphPath');
end
function periphUpdate(callbackContext)
modelName = bdroot(gcb); % получить имя верхнего уровня модели
% Обновление конфигурации периферии с асинхронным управлением
modelName = bdroot(gcb);
blockName = gcb;
mgr = asynchManage(modelName, blockName); % создать объект класса
mgr.updateGUIfromConfig(); % запустить сохранение и обновление
mgr = asynchManage(modelName, blockName);
mgr.updateGUIfromConfig(); % Запуск обновления через таймер
end
%% COMPILE
%% COMPILE - callback-функции для компиляции
function compile(callbackContext)
% Компиляция S-функции
compiler.compile();
end
function setSFuncName(callbackContext)
% Установка имени S-функции в исходном коде
block = gcb;
% Получаем параметр имени S-Function из маски блока
newName = mcuMask.get_name();
% Путь к файлу, в котором надо заменить строку
cFilePath = fullfile(pwd, mcuPath.get('wrapperPath'), 'MCU.c'); % <-- укажи правильный путь
% Путь к файлу MCU.c
cFilePath = fullfile(pwd, mcuPath.get('wrapperPath'), 'MCU.c');
% Считаем файл в память
% Чтение файла
try
fileText = fileread(cFilePath);
catch
return;
end
% Регулярное выражение для поиска строки с define
% Заменим строку вида: #define S_FUNCTION_NAME old_name
% Поиск и замена определения имени S-функции
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('Не удалось открыть файл для записи.');
@@ -159,39 +175,33 @@ classdef mcuMask
fclose(fid);
end
%% LINK TO EXTERNAL CONSOLE
%% LINK TO EXTERNAL CONSOLE - callback для внешней консоли
function findjobj_link(callbackContext)
web https://www.mathworks.com/matlabcentral/fileexchange/14317-findjobj-find-java-handles-of-matlab-graphic-objects;
% Открытие ссылки на утилиту findjobj
web('https://www.mathworks.com/matlabcentral/fileexchange/14317-findjobj-find-java-handles-of-matlab-graphic-objects');
end
end
%% GENERAL TOOLS
%% GENERAL TOOLS - общие утилиты для работы с маской
methods(Static, Access = public)
function updateModel()
% Обновление модели с компиляцией S-функции
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 close(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
@@ -201,24 +211,25 @@ classdef mcuMask
end
function open(blockPath, clear_flag)
% Открытие маски блока
open_system(blockPath, 'mask');
mcuMask.disp(clear_flag, '');
end
function name = get_name()
% Получение имени S-функции из параметра маски
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
@@ -226,8 +237,8 @@ classdef mcuMask
end
end
function children = get_children(ctrl)
% Получение дочерних элементов управления маски
if isprop(ctrl, 'DialogControls')
children = ctrl.DialogControls;
elseif isprop(ctrl, 'Controls')
@@ -240,27 +251,28 @@ classdef mcuMask
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>
% Добавление имени параметра
params{end+1} = ctrl.Name;
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);
@@ -271,14 +283,9 @@ classdef mcuMask
end
end
function tool(text, example)
% Устанавливает заданный текст в параметр Text Area 'toolText' через объект маски
% Получаем ссылку на текущий блок
% Установка текста подсказки и примера в элементы маски
block = gcb;
% Получаем объект маски
mask = Simulink.Mask.get(block);
toolTextArea = mask.getDialogControl('toolText');
@@ -288,15 +295,14 @@ classdef mcuMask
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
@@ -304,20 +310,18 @@ classdef mcuMask
if ~strcmp(out, '') && ~strcmp(out_now, '')
set_param(gcb, 'consoleOutput', [out_now newline out]);
else
set_param(gcb, 'consoleOutput', [out_now out]);
set_param(gcb, 'consoleOutput', [out_now out]);
end
end
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'));
@@ -325,8 +329,8 @@ classdef mcuMask
end
function v = getMyLibVersion()
v = 'pre-1.03';
% Получение версии библиотеки
v = 'pre-1.04';
end
end
end