317 lines
15 KiB
Matlab
317 lines
15 KiB
Matlab
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 |