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 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