classdef mcuPorts % Класс для управления портами ввода-вывода в маске Simulink % Генерирует конфигурационные файлы для портов на основе параметров маски methods(Static) function write() % Основная функция записи конфигурации портов в файлы % Генерирует заголовочный файл и файл реализации для портов block = gcb; mask = Simulink.Mask.get(block); % Пути к конфигурационным файлам hPath = fullfile(mcuPath.get('wrapperPath'), 'mcu_wrapper_conf.h'); cPath = fullfile(mcuPath.get('wrapperPath'), 'mcu_wrapper.c'); % Устанавливаем значения по умолчанию для неиспользуемых портов mcuPorts.defaultUnused(); %% СОЗДАНИЕ КОНФИГУРАЦИИ ДЛЯ ВХОДНЫХ ПОРТОВ prefixNumb = 'IN'; [widths, portPrefixes] = mcuPorts.getMaskNames('in'); % Генерируем текст для заголовочного файла и файла реализации headerText = mcuPorts.addPortHeaderDefines('', widths, prefixNumb, portPrefixes); cText = mcuPorts.addPortCDefines('', widths, prefixNumb, portPrefixes); %% СОЗДАНИЕ КОНФИГУРАЦИИ ДЛЯ ВЫХОДНЫХ ПОРТОВ prefixNumb = 'OUT'; [widths, portPrefixes] = mcuPorts.getMaskNames('out'); % Добавляем выходные порты к существующей конфигурации headerText = mcuPorts.addPortHeaderDefines(headerText, widths, prefixNumb, portPrefixes); cText = mcuPorts.addPortCDefines(cText, widths, prefixNumb, portPrefixes); %% ЗАПИСЬ В ФАЙЛЫ % Читаем существующее содержимое файлов hCode = fileread(hPath); hCode = regexprep(hCode, '\r\n?', '\n'); cCode = fileread(cPath); cCode = regexprep(cCode, '\r\n?', '\n'); % Вставляем сгенерированную конфигурацию в заголовочный файл code = editCode.insertSection(hCode, '// INPUT/OUTPUTS PARAMS', headerText.PARAMS); code = editCode.insertSection(code, '// INPUT/OUTPUTS AUTO-PARAMS', headerText.AUTO_PARAMS); % Записываем обновленный заголовочный файл fid = fopen(hPath, 'w', 'n', 'UTF-8'); if fid == -1 error('Не удалось открыть файл для записи'); end fwrite(fid, code); fclose(fid); % Вставляем сгенерированную конфигурацию в файл реализации code = editCode.insertSection(cCode, '// INPUT/OUTPUTS AUTO-PARAMS', cText); % Записываем обновленный файл реализации fid = fopen(cPath, 'w', 'n', 'UTF-8'); if fid == -1 error('Не удалось открыть файл для записи'); end fwrite(fid, code); fclose(fid); end function [portwidth, defnames] = getMaskNames(port_prefix) % Получение имен и ширин портов из параметров маски % port_prefix - префикс портов ('in' или 'out') % Возвращает ширины портов и их имена block = gcb; mask = Simulink.Mask.get(block); % Получаем количество портов из спиннера paramName = sprintf('%sNumb', port_prefix); param = mask.getParameter(paramName); numb = str2double(param.Value); % Инициализируем массивы для имен и ширин портов defnames = strings(1, numb); portwidth = []; % Читаем значения параметров для каждого порта for i = 1:numb % Получаем имя порта paramName = sprintf('%s_port_%d_name', port_prefix, i); param = mask.getParameter(paramName); defnames(i) = param.Value; % Получаем ширину порта paramName = sprintf('%s_port_%d_width', port_prefix, i); param = mask.getParameter(paramName); portwidth(i) = str2double(param.Value); end end function updateMask() % Обновление видимости параметров портов в маске % Вызывается при изменении количества портов mcuPorts.updateMask_prefix('in'); mcuPorts.updateMask_prefix('out'); end function defaultUnused() % Установка значений по умолчанию для неиспользуемых портов mcuPorts.defaultUnused_prefix('in'); mcuPorts.defaultUnused_prefix('out'); end end methods(Static, Access=private) function updateMask_prefix(port_prefix) % Обновление видимости параметров для конкретного типа портов % port_prefix - префикс портов ('in' или 'out') block = gcb; mask = Simulink.Mask.get(block); % Получаем текущее количество портов paramName = sprintf('%sNumb', port_prefix); param = mask.getParameter(paramName); n = str2double(param.Value); % Максимальное количество портов из диапазона параметра maxPorts = param.Range(2); % Обходим все возможные порты for i = 1:maxPorts % Формируем имена параметров для имени и ширины порта paramDefName = sprintf('%s_port_%d_name', port_prefix, i); paramWidthName = sprintf('%s_port_%d_width', port_prefix, i); paramDef = mask.getParameter(paramDefName); paramWidth = mask.getParameter(paramWidthName); if i <= n % Показываем параметры для используемых портов paramDef.Visible = 'on'; paramWidth.Visible = 'on'; % Устанавливаем значения по умолчанию если пустые if isempty(strtrim(paramDef.Value)) paramDef.Value = upper(port_prefix); end if isempty(strtrim(paramWidth.Value)) || strcmp(paramWidth.Value, '0') paramWidth.Value = '16'; end else % Скрываем параметры для неиспользуемых портов paramDef.Visible = 'off'; paramWidth.Visible = 'off'; end end end function defaultUnused_prefix(port_prefix) % Установка значений по умолчанию для неиспользуемых портов % port_prefix - префикс портов ('in' или 'out') block = gcb; mask = Simulink.Mask.get(block); % Получаем количество используемых портов paramName = sprintf('%sNumb', port_prefix); param = mask.getParameter(paramName); numb = str2double(param.Value); % Максимальное количество портов maxPorts = param.Range(2); % Устанавливаем значения по умолчанию для неиспользуемых портов for i = numb+1:maxPorts paramName = sprintf('%s_port_%d_name', port_prefix, i); param = mask.getParameter(paramName); param.Value = upper(port_prefix); % Имя по умолчанию paramName = sprintf('%s_port_%d_width', port_prefix, i); param = mask.getParameter(paramName); param.Value = '16'; % Ширина по умолчанию end end function headerText = addPortHeaderDefines(existingText, widths, prefixNumb, portPrefixes) % Генерация define-макросов для заголовочного файла % existingText - существующий текст конфигурации % widths - вектор ширин портов % prefixNumb - общий префикс ('IN' или 'OUT') % portPrefixes - массив префиксов для каждого порта % Возвращает структуру с PARAMS и AUTO_PARAMS n = numel(widths); upperPrefix = upper(prefixNumb); % === БАЗОВЫЕ ПАРАМЕТРЫ === lines = { sprintf('#define %s_PORT_NUMB %d', upperPrefix, n) }; % Добавляем define для ширины каждого порта for i = 1:n lines{end+1} = sprintf('#define %s_PORT_%d_WIDTH %d', ... upper(portPrefixes{i}), i, widths(i)); end newParams = strjoin(lines, newline); newParams = [newParams, newline]; % === АВТОМАТИЧЕСКИ ГЕНЕРИРУЕМЫЕ ПАРАМЕТРЫ === lines = {}; % Формируем выражение для общего размера буфера sumExprParts = cell(1,n); for i = 1:n sumExprParts{i} = sprintf('%s_PORT_%d_WIDTH', upper(portPrefixes{i}), i); end sumExpr = strjoin(sumExprParts, ' + '); lines{end+1} = sprintf('/// === Полный размер буфера ==='); lines{end+1} = sprintf('#define TOTAL_%s_SIZE (%s)', upperPrefix, sumExpr); lines{end+1} = ''; lines{end+1} = sprintf('/// === Смещения массивов (внутри общего буфера) ==='); % Генерируем define для смещений каждого массива в буфере for i = 1:n if i == 1 lines{end+1} = sprintf('#define OFFSET_%s_ARRAY_1 0', upperPrefix); else lines{end+1} = sprintf('#define OFFSET_%s_ARRAY_%d (OFFSET_%s_ARRAY_%d + %s_PORT_%d_WIDTH)', ... upperPrefix, i, upperPrefix, i - 1, upper(portPrefixes{i - 1}), i - 1); end end newAuto = strjoin(lines, newline); % === ОБЪЕДИНЕНИЕ С СУЩЕСТВУЮЩИМ ТЕКСТОМ === if isfield(existingText, 'PARAMS') headerText.PARAMS = [existingText.PARAMS, newline, newParams]; else headerText.PARAMS = newParams; end if isfield(existingText, 'AUTO_PARAMS') headerText.AUTO_PARAMS = [existingText.AUTO_PARAMS, newline, newAuto, newline]; else headerText.AUTO_PARAMS = [newAuto, newline]; end end function cText = addPortCDefines(existingText, widths, prefixNumb, portPrefixes) % Генерация кода для файла реализации (.c) % existingText - существующий текст % widths - вектор ширин портов % prefixNumb - общий префикс ('IN' или 'OUT') % portPrefixes - массив префиксов для каждого порта % Возвращает текст для вставки в .c файл n = numel(widths); upperPrefix = upper(prefixNumb); lowerPrefix = lower(prefixNumb); lines = {}; % === ТАБЛИЦА ДЛИН МАССИВОВ === lines{end+1} = '/**'; lines{end+1} = sprintf(' * @brief Таблица длин массивов %s', upperPrefix); lines{end+1} = ' */'; lines{end+1} = sprintf('const int %sLengths[%s_PORT_NUMB] = {', lowerPrefix, upperPrefix); % Заполняем таблицу длин for i = 1:n comma = ','; if i == n comma = ''; end lines{end+1} = sprintf(' %s_PORT_%d_WIDTH%s', upper(portPrefixes{i}), i, comma); end lines{end+1} = '};'; % === ТАБЛИЦА СМЕЩЕНИЙ === lines{end+1} = '/**'; lines{end+1} = sprintf(' * @brief Таблица смещений в выходном массиве %s', upperPrefix); lines{end+1} = ' */'; lines{end+1} = sprintf('const int %sOffsets[%s_PORT_NUMB] = {', lowerPrefix, upperPrefix); % Заполняем таблицу смещений for i = 1:n comma = ','; if i == n comma = ''; end lines{end+1} = sprintf(' OFFSET_%s_ARRAY_%d%s', upperPrefix, i, comma); end lines{end+1} = '};'; lines{end+1} = ''; newText = strjoin(lines, newline); % Объединяем с существующим текстом if nargin < 1 || isempty(existingText) cText = newText; else cText = [existingText, newline, newText]; end end function val = iff(cond, a, b) % Вспомогательная функция - тернарный оператор % cond - условие, a - значение если true, b - значение если false if cond val = a; else val = b; end end end end