classdef mainConfig % Класс для экспорта и импорта конфигурации маски Simulink в JSON % Позволяет сохранять и загружать настройки блока между сессиями methods(Static) function config = export() % Экспортирует текущую конфигурацию маски в JSON файл % Сохраняет параметры обёртки, портов и приложения blockPath = gcb; mask = Simulink.Mask.get(blockPath); % Списки параметров для экспорта по категориям wrapParamToExport = {'wrapperPath', 'enableDebug', 'mcuClk', ... 'threadCycles', 'enableThreading', 'enableDeinit', ... 'periphPath'}; portParamToExport = {'inNumb', ... 'in_port_1_name', 'in_port_1_width', ... 'in_port_2_name', 'in_port_2_width', ... 'in_port_3_name', 'in_port_3_width', ... 'in_port_4_name', 'in_port_4_width', ... 'in_port_5_name', 'in_port_5_width', ... 'outNumb', ... 'out_port_1_name', 'out_port_1_width', ... 'out_port_2_name', 'out_port_2_width', ... 'out_port_3_name', 'out_port_3_width', ... 'out_port_4_name', 'out_port_4_width', ... 'out_port_5_name', 'out_port_5_width',}; appParamToExport = {'appWrapperPath', 'srcTable', 'incTable', 'userDefs'}; % Создаем структуру для конфигурации config = struct(); % Экспортируем параметры обёртки for i = 1:numel(wrapParamToExport) paramName = wrapParamToExport{i}; def = mainConfig.exportParamToConfig(mask, paramName); config.(paramName) = def; end % Экспортируем параметры портов for i = 1:numel(portParamToExport) paramName = portParamToExport{i}; def = mainConfig.exportParamToConfig(mask, paramName); config.(paramName) = def; end % Экспортируем параметры приложения for i = 1:numel(appParamToExport) paramName = appParamToExport{i}; def = mainConfig.exportParamToConfig(mask, paramName); config.(paramName) = def; end % Кодируем структуру в JSON с форматированием jsonStr = jsonencode(config, 'PrettyPrint', true); % Диалог сохранения файла [file, path] = uiputfile('*.json', 'Сохранить конфигурацию как', 'WrapperConfig.json'); if isequal(file, 0) || isequal(path, 0) disp('Сохранение отменено пользователем.'); return; end filepath = fullfile(path, file); % Сохраняем в файл fid = fopen(filepath, 'w'); if fid == -1 mcuMask.disp(0, 'Не удалось открыть файл для записи: %s', filename); end fwrite(fid, jsonStr, 'char'); fclose(fid); end function import() % Импортирует конфигурацию маски из JSON файла % Восстанавливает параметры обёртки, портов и приложения % Получаем путь к текущему блоку blockPath = gcb; mask = Simulink.Mask.get(blockPath); % Выбор JSON-файла через диалоговое окно [file, path] = uigetfile('*.json', 'Выберите файл конфигурации'); if isequal(file, 0) mcuMask.disp(0, 'Импорт отменён пользователем.'); return; end fullpath = fullfile(path, file); % Чтение и декодирование JSON try jsonStr = fileread(fullpath); config = jsondecode(jsonStr); catch err mcuMask.disp(0, 'Ошибка при чтении или разборе JSON: %s', err.message); end % Применение параметров из конфигурации paramNames = fieldnames(config); for i = 1:numel(paramNames) paramName = paramNames{i}; def = config.(paramName); try mainConfig.applyDefToMask(mask, paramName, def); catch err mcuMask.disp(0, 'Ошибка при применении параметра "%s": %s', paramName, err.message); end end mcuMask.disp(0, 'Конфигурация успешно импортирована.'); % Обновляем периферию после импорта mcuMask.periphUpdate(); end end methods(Static, Access=private) function def = exportParamToConfig(mask, paramName) % Экспортирует один параметр маски в структуру для JSON % mask - объект маски Simulink % paramName - имя параметра для экспорта % Возвращает структуру с описанием параметра param = mask.getParameter(paramName); if isempty(param) mcuMask.disp(0, 'Параметр "%s" не найден в маске.', paramName); def = []; return; end def = struct(); % Сохраняем подпись параметра def.Prompt = param.Prompt; % Сохраняем тип параметра def.Type = param.Type; % Сохраняем значение в зависимости от типа val = param.Value; switch lower(param.Type) case 'checkbox' % Для чекбоксов преобразуем 'on'/'off' в true/false def.Default = strcmp(val, 'on'); case {'edit', 'spinbox'} % Для числовых полей пробуем преобразовать в число num = str2double(val); if ~isnan(num) def.Default = num; else def.Default = val; % Оставляем строкой если не число end case 'customtable' % Для таблиц парсим содержимое в cell-массив def.Default = customtable.parse(param.Name); case 'text' % Для текстовых полей сохраняем пустую строку def.Default = ''; otherwise % Для остальных типов сохраняем как есть def.Default = val; end % Сохраняем алиас если есть if ~isempty(param.Alias) def.Def = param.Alias; end end function applyDefToMask(mask, paramName, def) % Применяет параметр из конфигурации к маске % mask - объект маски Simulink % paramName - имя параметра для применения % def - структура с данными параметра % Получаем параметр из маски param = mask.getParameter(paramName); if isempty(param) mcuMask.disp(0, 'Параметр "%s" не найден в маске.', paramName); return; end if isempty(def) mcuMask.disp(0, 'Параметр "%s" не найден в конфиге.', paramName); return; end % Устанавливаем значение в зависимости от типа параметра switch lower(def.Type) case 'checkbox' % Устанавливаем состояние чекбокса if islogical(def.Default) param.Value = mainConfig.ternary(def.Default, 'on', 'off'); else mcuMask.disp(0, 'Ожидалось логическое значение для checkbox "%s".', paramName); end case {'edit', 'spinbox'} % Устанавливаем значение для текстового поля if isnumeric(def.Default) param.Value = num2str(def.Default); elseif ischar(def.Default) || isstring(def.Default) param.Value = char(def.Default); else mcuMask.disp(0, 'Некорректный формат значения для edit "%s".', paramName); end case 'customtable' % Загружаем данные в таблицу if iscell(def.Default) customtable.collect(paramName, def.Default); else mcuMask.disp(0, 'customtable "%s" требует cell-массив строк.', paramName); end case 'text' % Устанавливаем текстовое значение if ischar(def.Default) || isstring(def.Default) param.Value = char(def.Default); else mcuMask.disp(0, 'text-параметр "%s" должен быть строкой.', paramName); end case 'popup' % Устанавливаем значение выпадающего списка if ischar(def.Default) && isfield(def, 'Def') && any(strcmp(def.Default, def.Def)) param.Value = def.Default; else mcuMask.disp(0, 'popup-параметр "%s" имеет неверное значение или список.', paramName); end otherwise % Универсальная установка значения if ischar(def.Default) || isstring(def.Default) param.Value = char(def.Default); elseif isnumeric(def.Default) param.Value = num2str(def.Default); else mcuMask.disp(0, 'Неизвестный формат значения параметра "%s".', paramName); end end % Обновляем подпись параметра если есть if isfield(def, 'Prompt') param.Prompt = def.Prompt; end % Обновляем алиас если есть if isfield(def, 'Def') && ischar(def.Def) param.Alias = def.Def; end % % Установка Row, если поддерживается % if isfield(def, 'NewRow') % try % if def.NewRow % param.DialogControl.Row = 'new'; % else % param.DialogControl.Row = 'current'; % end % catch % % Некоторым типам параметров Row может быть неприменим % end % end end function result = ternary(cond, a, b) % Вспомогательная функция - тернарный оператор % cond - условие, a - значение если true, b - значение если false if cond result = a; else result = b; end end end end