classdef periphConfig methods(Static) function update(blockPath, config) % blockPath = [blockPath '/MCU']; % Проверяем, была ли маска открыта % wasOpen = isMaskDialogOpen(blockPath); mask = Simulink.Mask.get(blockPath); periphPath = get_param(blockPath, 'periphPath'); [periphPath, ~, ~] = fileparts(periphPath); tableNames = {'incTable', 'srcTable'}; columns_backup = customtable.save_all_tables(tableNames); containerName = 'configTabAll'; periphConfig.clear_all_from_container(mask, containerName); % Ищем контейнер, в который будем добавлять вкладки container = mask.getDialogControl(containerName); if isempty(container) error('Контейнер "%s" не найден в маске.', containerName); end if ~isempty(config) if isfield(config, 'Code') res = periphConfig.addCodeConfig(config.Code, periphPath); if res == 0 error('Ошибка: неудачное добавление кода периферии. Проверьте корректность файлов и путей в конфигурационном файле') end else error('Ошибка: в конфигурационном файле не задан исходный код для симуляции периферии') end if isfield(config, 'UserCode') res = periphConfig.addUserCodeConfig(config.UserCode); if res == 0 error('Ошибка: неудачное добавление функций для симуляции. Проверьте корректность названий функций в конфигурационном файле') end else error('Ошибка: в конфигурационном файле не заданы функции для симуляции периферии') end % Проходим по каждому модулю (ADC, TIM...) periphs = fieldnames(config); for i = 1:numel(periphs) periph = periphs{i}; % Пропускаем Code и UserCode, они уже обработаны if strcmp(periph, 'Code') || strcmp(periph, 'UserCode') continue; end defines = config.(periph).Defines; defNames = fieldnames(defines); % Создаём вкладку для модуля tabCtrl = container.addDialogControl('tab', periph); tabCtrl.Prompt = [periph ' Config']; for j = 1:numel(defNames) defPrompt = defNames{j}; def = defines.(defPrompt); % Вызов функции добавления одного параметра periphConfig.addDefineConfig(mask, containerName, periph, defPrompt, def); end end end % Восстанавливаем таблицы customtable.restore_all_tables(tableNames, columns_backup); % % Повторно открываем маску, если она была открыта % if wasOpen % open_system(blockPath, 'mask'); % end end function config = update_config(blockPath, config) if isempty(config) return; end mask = Simulink.Mask.get(blockPath); maskParams = mask.Parameters; paramNames = arrayfun(@(p) p.Name, maskParams, 'UniformOutput', false); % Обработка остальных секций (с дефайнами) periphs = fieldnames(config); for i = 1:numel(periphs) periph = periphs{i}; % Пропускаем Code и UserCode, они уже обработаны if strcmp(periph, 'Code') || strcmp(periph, 'UserCode') continue; end % Проверяем есть ли Defines if ~isfield(config.(periph), 'Defines') continue; end defines = config.(periph).Defines; defNames = fieldnames(defines); for j = 1:numel(defNames) defPrompt = defNames{j}; paramName = matlab.lang.makeValidName(defPrompt); % Проверка, существует ли параметр с таким именем if ismember(paramName, paramNames) param = mask.getParameter(paramName); valStr = param.Value; % Проверяем, существует ли элемент defPrompt в структуре defines if isfield(defines, defPrompt) % Преобразуем строку в соответствующий тип if strcmpi(defines.(defPrompt).Type, 'checkbox') config.(periph).Defines.(defPrompt).Default = strcmpi(valStr, 'on'); elseif strcmpi(defines.(defPrompt).Type, 'edit') valNum = str2double(valStr); if isnan(valNum) config.(periph).Defines.(defPrompt).Default = valStr; else config.(periph).Defines.(defPrompt).Default = valNum; end end end end end end end function config = read_config(blockPath) mask = Simulink.Mask.get(blockPath); pathparam = mask.getParameter('periphPath'); config_path = pathparam.Value; if ~isempty(config_path) jsonText = fileread(config_path); config = jsondecode(jsonText); else config = []; end end function write_config(config) if isempty(config) return end blockHandle = gcbh; mask = Simulink.Mask.get(blockHandle); pathparam = mask.getParameter('periphPath'); config_path = pathparam.Value; jsonText = jsonencode(config, 'PrettyPrint', true); fid = fopen(config_path, 'w', 'n', 'UTF-8'); if fid == -1 error('Не удалось открыть файл periph_config.json для записи.'); end fwrite(fid, jsonText, 'char'); fclose(fid); end function clear_all_from_container(mask, containerName) % allControls = mask.getDialogControls(); container = mask.getDialogControl(containerName); if isempty(container) warning('Контейнер "%s" не найден.', containerName); return; end % Рекурсивно собрать все параметры (не вкладки) paramsToDelete = mcuMask.collect_all_parameters(container); % Удаляем все параметры for i = 1:numel(paramsToDelete) try mask.removeParameter(paramsToDelete{i}); catch warning('Не удалось удалить параметр %s', paramsToDelete{i}); end end % Рекурсивно удалить все вкладки внутри контейнера mcuMask.delete_all_tabs(mask, container); end end methods(Static, Access=private) function res = addCodeConfig(codeConfig, periphPath) % Возвращает 0 при успехе, 1 при ошибке try % Источники srcList = {}; if isfield(codeConfig, 'Sources') && isfield(codeConfig.Sources, 'Options') srcFiles = codeConfig.Sources.Options; for i = 1:numel(srcFiles) fullPath = fullfile(periphPath, srcFiles{i}); srcList{end+1} = [strrep(fullPath, '\', '\\')]; end end % Формируем srcText с переносами строк и ^ srcText = ''; for i = 1:numel(srcList) if i < numel(srcList) srcText = [srcText srcList{i} '^' newline ' ']; else srcText = [srcText srcList{i}]; end end % Инклуды incList = {}; if isfield(codeConfig, 'Includes') && isfield(codeConfig.Includes, 'Options') incPaths = codeConfig.Includes.Options; for i = 1:numel(incPaths) fullPath = fullfile(periphPath, incPaths{i}); incList{end+1} = ['-I"' strrep(fullPath, '\', '\\') '"']; end end % Формируем incText с переносами строк и ^ incText = ''; for i = 1:numel(incList) if i == 1 && numel(incList) ~= 1 incText = [incText incList{i} '^' newline]; elseif i < numel(incList) incText = [incText ' ' incList{i} '^' newline]; else incText = [incText ' ' incList{i}]; end end % Добавляем префиксы srcText = ['set code_PERIPH' '=' srcText]; incText = ['set includes_PERIPH' '=' incText]; % Записываем результат res = periphConfig.updateRunMexBat(srcText, incText); % Всё прошло успешно catch % В случае ошибки просто возвращаем 1 res = 1; end end function res = addUserCodeConfig(userCodeConfig) % userCodeConfig — структура config.UserCode initFuncsText = ''; simFuncsText = ''; deinitFuncsText = ''; if isfield(userCodeConfig, 'Functions') funcs = userCodeConfig.Functions; if isfield(funcs, 'PeriphInit') && isfield(funcs.PeriphInit, 'Options') initFuncs = funcs.PeriphInit.Options; initFuncsText = strjoin(strcat('\t', initFuncs, ';'), '\n'); end if isfield(funcs, 'PeriphSimulation') && isfield(funcs.PeriphSimulation, 'Options') simFuncs = funcs.PeriphSimulation.Options; simFuncsText = strjoin(strcat('\t', simFuncs, ';'), '\n'); end if isfield(funcs, 'PeriphDeinit') && isfield(funcs.PeriphDeinit, 'Options') deinitFuncs = funcs.PeriphDeinit.Options; deinitFuncsText = strjoin(strcat('\t', deinitFuncs, ';'), '\n'); end res = periphConfig.updateWrapperCode(initFuncsText, simFuncsText, deinitFuncsText); end end function res = updateWrapperCode(initFuncsText, simFuncsText, deinitFuncsText) % Входные параметры: % srcText - текст для записи set code_... % incText - текст для записи set includes_... % % Возвращает: % res - 0 при успехе, 1 при ошибке wrapPath = fullfile('.\MCU_Wrapper', 'mcu_wrapper.c'); res = 1; try code = fileread(wrapPath); code = regexprep(code, '\r\n?', '\n'); % Записываем строки initFuncsText и simFuncsText code = editCode.insertSection(code, '// PERIPH INIT', initFuncsText); code = editCode.insertSection(code, '// PERIPH SIM', simFuncsText); code = editCode.insertSection(code, '// PERIPH DEINIT', deinitFuncsText); fid = fopen(wrapPath, 'w', 'n', 'UTF-8'); if fid == -1 error('Не удалось открыть файл для записи'); end fwrite(fid, code); fclose(fid); res = 1; catch ME error('Ошибка: неудачная запись в файл при записи файла: %s', ME.message); end end function res = updateRunMexBat(srcText, incText) % Входные параметры: % srcText - текст для записи set code_... % incText - текст для записи set includes_... % % Возвращает: % res - 0 при успехе, 1 при ошибке periphBat = [srcText '\n\n' incText]; batPath = fullfile('.\MCU_Wrapper', 'run_mex.bat'); res = 1; try code = fileread(batPath); code = regexprep(code, '\r\n?', '\n'); % Записываем строки srcText и incText с переносами строк code = editCode.insertSection(code, ':: PERIPH BAT', periphBat); fid = fopen(batPath, 'w', 'n', 'UTF-8'); if fid == -1 error('Не удалось открыть файл для записи'); end fwrite(fid, code); fclose(fid); res = 1; catch ME mcuMask.disp(0, '\nОшибка: неудачная запись в файл при записи файла: %s', ME.message); end end function addDefineConfig(mask, containerName, periphName, defPrompt, def) % mask — объект маски Simulink.Mask.get(blockPath) % containerName — имя контейнера, в который добавляем параметр (например, 'configTabAll') % periphName — имя вкладки / контейнера для текущего периферийного блока (например, 'ADC') % defPrompt — имя параметра в Defines (например, 'shift_enable') % def — структура с описанием параметра (Prompt, Def, Type, Default, NewRow и т.п.) % Найдем контейнер с таким именем container = mask.getDialogControl(containerName); if isempty(container) error('Контейнер "%s" не найден в маске.', containerName); end % Проверим, есть ли вкладка с именем periphName, если нет — создадим tabCtrl = mask.getDialogControl(periphName); if isempty(tabCtrl) tabCtrl = container.addDialogControl('tab', periphName); tabCtrl.Prompt = [periphName ' Config']; end % Определяем тип параметра (checkbox или edit) switch lower(def.Type) case 'checkbox' paramType = 'checkbox'; case 'edit' paramType = 'edit'; otherwise % Игнорируем остальные типы return; end paramName = matlab.lang.makeValidName(defPrompt); % Преобразуем значение Default в строку для Value val = def.Default; if islogical(val) valStr = mcuMask.ternary(val, 'on', 'off'); elseif isnumeric(val) valStr = num2str(val); elseif ischar(val) valStr = val; else error('Unsupported default value type for %s.%s', periphName, defPrompt); end % Добавляем параметр в маску param = mask.addParameter( ... 'Type', paramType, ... 'Prompt', def.Prompt, ... 'Name', paramName, ... 'Value', valStr, ... 'Container', periphName ... ); param.Evaluate = 'off'; if def.NewRow param.DialogControl.Row = 'new'; else param.DialogControl.Row = 'current'; end param.Alias = def.Def; end end end