- сделан выбор папки с MCU Wrapper
- добавлен файл для работы с путями - добавлен файл для работы с компилятором (не доделан)
This commit is contained in:
parent
966ddc3bac
commit
0a2fd71422
139
McuLib/m/compiler.m
Normal file
139
McuLib/m/compiler.m
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
classdef compiler
|
||||||
|
methods(Static)
|
||||||
|
|
||||||
|
function compile()
|
||||||
|
addpath(mcuPath.get('wrapperPath'));
|
||||||
|
mexing(1);
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_availbe()
|
||||||
|
addpath(mcuPath.get('wrapperPath'));
|
||||||
|
mexing(1);
|
||||||
|
end
|
||||||
|
|
||||||
|
function choose()
|
||||||
|
addpath(mcuPath.get('wrapperPath'));
|
||||||
|
mexing(1);
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function updateRunBat()
|
||||||
|
sources = {
|
||||||
|
'MCU.c'
|
||||||
|
'mcu_wrapper.c'
|
||||||
|
};
|
||||||
|
% Список заголовочных файлов (.h)
|
||||||
|
includes = { '.\'
|
||||||
|
};
|
||||||
|
periphPath = mcuPath.get('wrapperPath');
|
||||||
|
% Формируем строки
|
||||||
|
wrapperSrcText = compiler.createSourcesBat('code_WRAPPER', sources, periphPath);
|
||||||
|
wrapperIncText = compiler.createIncludesBat('includes_WRAPPER', includes, periphPath);
|
||||||
|
|
||||||
|
% Записываем результат
|
||||||
|
res = compiler.updateRunMexBat(wrapperSrcText, wrapperIncText, ':: WRAPPER BAT'); % Всё прошло успешно
|
||||||
|
|
||||||
|
if res == 0
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
sources = {
|
||||||
|
'app_wrapper.c'
|
||||||
|
'app_init.c'
|
||||||
|
'app_io.c'
|
||||||
|
};
|
||||||
|
% Список заголовочных файлов (.h)
|
||||||
|
includes = { '.\'
|
||||||
|
};
|
||||||
|
periphPath = mcuPath.get('appWrapperPath');
|
||||||
|
% Формируем строки
|
||||||
|
wrapperSrcText = compiler.createSourcesBat('code_APP_WRAPPER', sources, periphPath);
|
||||||
|
wrapperIncText = compiler.createIncludesBat('includes_APP_WRAPPER', includes, periphPath);
|
||||||
|
|
||||||
|
% Записываем результат
|
||||||
|
res = compiler.updateRunMexBat(wrapperSrcText, wrapperIncText, ':: APP WRAPPER BAT'); % Всё прошло успешно
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function res = updateRunMexBat(srcText, incText, Section)
|
||||||
|
% Входные параметры:
|
||||||
|
% srcText - текст для записи set code_...
|
||||||
|
% incText - текст для записи set includes_...
|
||||||
|
%
|
||||||
|
% Возвращает:
|
||||||
|
% res - 0 при успехе, 1 при ошибке
|
||||||
|
periphBat = [srcText '\n\n' incText];
|
||||||
|
batPath = fullfile(mcuPath.get('wrapperPath'), 'run_mex.bat');
|
||||||
|
res = 1;
|
||||||
|
try
|
||||||
|
code = fileread(batPath);
|
||||||
|
code = regexprep(code, '\r\n?', '\n');
|
||||||
|
|
||||||
|
% Записываем строки srcText и incText с переносами строк
|
||||||
|
code = editCode.insertSection(code, Section, 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 srcText = createSourcesBat(prefix_name, sources, path)
|
||||||
|
srcList = {};
|
||||||
|
if nargin >= 2 && iscell(sources)
|
||||||
|
for i = 1:numel(sources)
|
||||||
|
fullPath = fullfile(path, sources{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
|
||||||
|
|
||||||
|
% Добавляем префикс
|
||||||
|
srcText = ['set ' prefix_name '=' srcText];
|
||||||
|
end
|
||||||
|
|
||||||
|
function incText = createIncludesBat(prefix_name, includes, path)
|
||||||
|
incList = {};
|
||||||
|
if nargin >= 2 && iscell(includes)
|
||||||
|
for i = 1:numel(includes)
|
||||||
|
fullPath = fullfile(path, includes{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
|
||||||
|
|
||||||
|
% Добавляем префикс
|
||||||
|
incText = ['set ' prefix_name '=' incText];
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
@ -8,10 +8,10 @@ classdef mcuMask
|
|||||||
function MaskInitialization(maskInitContext)
|
function MaskInitialization(maskInitContext)
|
||||||
% Получаем хэндл текущего блока
|
% Получаем хэндл текущего блока
|
||||||
blk = gcbh;
|
blk = gcbh;
|
||||||
set_param(blk,"MaskSelfModifiable","on")
|
|
||||||
set_param(blk, 'LinkStatus', 'none');
|
|
||||||
% Получаем объект маски текущего блока
|
% Получаем объект маски текущего блока
|
||||||
mask = Simulink.Mask.get(gcb);
|
mask = Simulink.Mask.get(gcb);
|
||||||
|
set_param(blk,"MaskSelfModifiable","on")
|
||||||
|
set_param(blk, 'LinkStatus', 'none');
|
||||||
% mcuMask.disp(1,'');
|
% mcuMask.disp(1,'');
|
||||||
try
|
try
|
||||||
% Проверка наличия findjobj
|
% Проверка наличия findjobj
|
||||||
@ -19,8 +19,6 @@ classdef mcuMask
|
|||||||
catch
|
catch
|
||||||
findjobjAvailable = false;
|
findjobjAvailable = false;
|
||||||
end
|
end
|
||||||
% Получаем объект маски текущего блока
|
|
||||||
mask = Simulink.Mask.get(gcb);
|
|
||||||
% Имя checkbox-параметра (укажите точное имя из маски)
|
% Имя checkbox-параметра (укажите точное имя из маски)
|
||||||
checkboxParamName = 'extConsol'; % пример
|
checkboxParamName = 'extConsol'; % пример
|
||||||
findjobjLinkName = 'findjobj_link'; % пример
|
findjobjLinkName = 'findjobj_link'; % пример
|
||||||
@ -121,19 +119,11 @@ classdef mcuMask
|
|||||||
end
|
end
|
||||||
|
|
||||||
function wrapperPath_add(callbackContext)
|
function wrapperPath_add(callbackContext)
|
||||||
block = gcb;
|
mcuPath.addPath('wrapperPath');
|
||||||
mask = Simulink.Mask.get(block);
|
end
|
||||||
% Открываем окно выбора папки
|
|
||||||
folderPath = uigetdir('', 'Выберите папку');
|
|
||||||
% Проверка на отмену
|
|
||||||
if isequal(folderPath, 0)
|
|
||||||
return;
|
|
||||||
end
|
|
||||||
% Установка значения параметра маски
|
|
||||||
rel = mcuMask.absoluteToRelativePath(folderPath);
|
|
||||||
param = mask.getParameter('wrapperPath');
|
|
||||||
param.Value = rel;
|
|
||||||
|
|
||||||
|
function appWrapperPath_add(callbackContext)
|
||||||
|
mcuPath.addPath('appWrapperPath');
|
||||||
end
|
end
|
||||||
%% USER WRAPPER CODE
|
%% USER WRAPPER CODE
|
||||||
|
|
||||||
@ -199,7 +189,7 @@ classdef mcuMask
|
|||||||
block = gcb;
|
block = gcb;
|
||||||
|
|
||||||
% Получаем имя функции и путь к файлам
|
% Получаем имя функции и путь к файлам
|
||||||
filename = mcuMask.getAbsolutePath(mcuMask.getWrapperUserFile(block));
|
filename = mcuPath.getAbsolutePath(mcuMask.getWrapperUserFile(block));
|
||||||
if exist(filename, 'file') == 2
|
if exist(filename, 'file') == 2
|
||||||
% Формируем команду без кавычек
|
% Формируем команду без кавычек
|
||||||
cmd = sprintf('rundll32.exe shell32.dll,OpenAs_RunDLL %s', filename);
|
cmd = sprintf('rundll32.exe shell32.dll,OpenAs_RunDLL %s', filename);
|
||||||
@ -222,83 +212,27 @@ classdef mcuMask
|
|||||||
end
|
end
|
||||||
|
|
||||||
function btnAddSrc(callbackContext)
|
function btnAddSrc(callbackContext)
|
||||||
blockHandle = gcb;
|
mcuPath.addSourceFileTable('srcTable', 'Выберите исходные файлы');
|
||||||
% Открываем проводник для выбора файлов
|
|
||||||
[files, pathstr] = uigetfile({ ...
|
|
||||||
'*.c;*.cpp', 'Исходные файлы (*.c, *.cpp)'; ...
|
|
||||||
'*.obj;*.lib', 'Библиотеки (*.obj, *.lib)'; ...
|
|
||||||
'*.*', 'Все файлы (*.*)'}, ...
|
|
||||||
'Выберите файлы', ...
|
|
||||||
'MultiSelect', 'on');
|
|
||||||
|
|
||||||
if isequal(files, 0)
|
|
||||||
return; % Отмена выбора
|
|
||||||
end
|
|
||||||
if ischar(files)
|
|
||||||
files = {files}; % Один файл — в cell
|
|
||||||
end
|
|
||||||
% Парсим строку в cell-массив
|
|
||||||
oldTable = customtable.parse('srcTable');
|
|
||||||
|
|
||||||
% Добавляем новые пути, проверяя уникальность
|
|
||||||
for i = 1:numel(files)
|
|
||||||
fullpath = fullfile(pathstr, files{i});
|
|
||||||
rel = mcuMask.absoluteToRelativePath(fullpath);
|
|
||||||
if ~any(strcmp(rel, oldTable))
|
|
||||||
oldTable{end+1, 1} = rel;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
% Парсим строку в cell-массив
|
|
||||||
customtable.collect('srcTable', oldTable);
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function btnAddInc(callbackContext)
|
function btnAddInc(callbackContext)
|
||||||
blockHandle = gcb;
|
mcuPath.addPathTable('incTable', 'Выберите папку с заголовочными файлами');
|
||||||
% Открываем проводник для выбора папок
|
|
||||||
pathstr = uigetdir(pwd, 'Выберите папку с заголовочными файлами');
|
|
||||||
if isequal(pathstr, 0)
|
|
||||||
return; % Отмена выбора
|
|
||||||
end
|
|
||||||
% Парсим таблицу
|
|
||||||
oldTable = customtable.parse('incTable');
|
|
||||||
|
|
||||||
rel = mcuMask.absoluteToRelativePath(pathstr);
|
|
||||||
|
|
||||||
% Проверяем наличие пути
|
|
||||||
if ~any(strcmp(rel, oldTable))
|
|
||||||
oldTable{end+1, 1} = rel;
|
|
||||||
end
|
|
||||||
|
|
||||||
% Собираем таблицу
|
|
||||||
customtable.collect('incTable', oldTable);
|
|
||||||
end
|
end
|
||||||
|
|
||||||
%% PERIPH CONFIG
|
%% PERIPH CONFIG
|
||||||
|
|
||||||
function periphPath_add(callbackContext)
|
function periphPath_add(callbackContext)
|
||||||
block = gcbh;
|
mcuPath.addAnyFile('periphPath');
|
||||||
mask = Simulink.Mask.get(block);
|
|
||||||
[file, path] = uigetfile({'*.*','Все файлы (*.*)'}, 'Выберите файл');
|
|
||||||
if isequal(file, 0) || isequal(path, 0)
|
|
||||||
% Отмена выбора — ничего не делаем
|
|
||||||
return;
|
|
||||||
end
|
|
||||||
fullFilePath = fullfile(path, file);
|
|
||||||
rel = mcuMask.absoluteToRelativePath(fullFilePath);
|
|
||||||
param = mask.getParameter('periphPath');
|
|
||||||
param.Value = rel;
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
%% COMPILE
|
||||||
function compile(callbackContext)
|
function compile(callbackContext)
|
||||||
addpath('MCU_Wrapper');
|
compiler.compile();
|
||||||
mexing(1);
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function updateModel(callbackContext)
|
function updateModel(callbackContext)
|
||||||
addpath('MCU_Wrapper');
|
addpath(mcuPath.get('wrapperPath'));
|
||||||
res = mexing(1);
|
res = mexing(1);
|
||||||
if res ~= 0
|
if res ~= 0
|
||||||
return;
|
return;
|
||||||
@ -315,13 +249,13 @@ classdef mcuMask
|
|||||||
web('https://www.mathworks.com/matlabcentral/fileexchange/14317-findjobj-find-java-handles-of-matlab-graphic-objects');
|
web('https://www.mathworks.com/matlabcentral/fileexchange/14317-findjobj-find-java-handles-of-matlab-graphic-objects');
|
||||||
end
|
end
|
||||||
|
|
||||||
function set_name()
|
function set_name(callbackContext)
|
||||||
block = gcb;
|
block = gcb;
|
||||||
% Получаем параметр имени S-Function из маски блока
|
% Получаем параметр имени S-Function из маски блока
|
||||||
newName = mcuMask.get_name();
|
newName = mcuMask.get_name();
|
||||||
|
|
||||||
% Путь к файлу, в котором надо заменить строку
|
% Путь к файлу, в котором надо заменить строку
|
||||||
cFilePath = fullfile(pwd, './MCU_Wrapper/MCU.c'); % <-- укажи правильный путь
|
cFilePath = fullfile(pwd, mcuPath.get('wrapperPath'), 'MCU.c'); % <-- укажи правильный путь
|
||||||
|
|
||||||
% Считаем файл в память
|
% Считаем файл в память
|
||||||
try
|
try
|
||||||
@ -348,12 +282,6 @@ classdef mcuMask
|
|||||||
fwrite(fid, updatedText);
|
fwrite(fid, updatedText);
|
||||||
fclose(fid);
|
fclose(fid);
|
||||||
end
|
end
|
||||||
|
|
||||||
function name = get_name()
|
|
||||||
block = gcb;
|
|
||||||
% Получаем параметр имени S-Function из маски блока
|
|
||||||
name = get_param(block, 'sfuncName');
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -459,81 +387,13 @@ classdef mcuMask
|
|||||||
mcuMask.disp(clear_flag, '');
|
mcuMask.disp(clear_flag, '');
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function name = get_name()
|
||||||
function absPath = getAbsolutePath(relPath)
|
block = gcb;
|
||||||
% relativeToAbsolutePath — преобразует относительный путь в абсолютный.
|
% Получаем параметр имени S-Function из маски блока
|
||||||
%
|
name = get_param(block, 'sfuncName');
|
||||||
% Если путь уже абсолютный — возвращается он же, приведённый к канонической форме.
|
|
||||||
% Если путь относительный — преобразуется относительно текущей директории.
|
|
||||||
|
|
||||||
% Проверка: абсолютный ли путь
|
|
||||||
if ispc
|
|
||||||
isAbsolute = ~isempty(regexp(relPath, '^[a-zA-Z]:[\\/]', 'once')) || startsWith(relPath, '\\');
|
|
||||||
else
|
|
||||||
isAbsolute = startsWith(relPath, '/');
|
|
||||||
end
|
|
||||||
|
|
||||||
if isAbsolute
|
|
||||||
% Канонизируем абсолютный путь (убираем ./, ../ и т.п.)
|
|
||||||
absPath = char(java.io.File(relPath).getCanonicalPath());
|
|
||||||
else
|
|
||||||
% Строим абсолютный путь от текущей директории
|
|
||||||
cwd = pwd;
|
|
||||||
combined = fullfile(cwd, relPath);
|
|
||||||
absPath = char(java.io.File(combined).getCanonicalPath());
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function rel = absoluteToRelativePath(pathstr)
|
|
||||||
% absoluteToRelativePath — преобразует абсолютный путь в относительный от текущей директории.
|
|
||||||
%
|
|
||||||
% Если путь находится в текущей директории или вложенной в неё — добавляется префикс './'
|
|
||||||
% Если выше — формируются переходы '..'
|
|
||||||
% Если путь совпадает с текущей директорией — возвращается '.'
|
|
||||||
|
|
||||||
% Получаем текущую рабочую директорию
|
|
||||||
cwd = pwd;
|
|
||||||
|
|
||||||
% Преобразуем пути в канонические абсолютные пути
|
|
||||||
fullpath = char(java.io.File(pathstr).getCanonicalPath());
|
|
||||||
cwd = char(java.io.File(cwd).getCanonicalPath());
|
|
||||||
|
|
||||||
% Разбиваем пути на части
|
|
||||||
targetParts = strsplit(fullpath, filesep);
|
|
||||||
baseParts = strsplit(cwd, filesep);
|
|
||||||
|
|
||||||
% Находим длину общего префикса
|
|
||||||
j = 1;
|
|
||||||
while j <= min(length(targetParts), length(baseParts)) && strcmpi(targetParts{j}, baseParts{j})
|
|
||||||
j = j + 1;
|
|
||||||
end
|
|
||||||
|
|
||||||
% Формируем количество подъемов ".." из cwd
|
|
||||||
numUps = length(baseParts) - (j - 1);
|
|
||||||
ups = repmat({'..'}, 1, numUps);
|
|
||||||
|
|
||||||
% Оставшаяся часть пути после общего префикса
|
|
||||||
rest = targetParts(j:end);
|
|
||||||
|
|
||||||
% Объединяем для получения относительного пути
|
|
||||||
relParts = [ups, rest];
|
|
||||||
rel = fullfile(relParts{:});
|
|
||||||
|
|
||||||
% Если путь пустой — это текущая директория
|
|
||||||
if isempty(rel)
|
|
||||||
rel = '.';
|
|
||||||
end
|
|
||||||
|
|
||||||
% Если путь не содержит ".." и начинается внутри текущей директории — добавим './'
|
|
||||||
if ~isempty(rest) && isempty(ups)
|
|
||||||
rel = fullfile('.', rel);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function checkbox_state = read_checkbox(checkboxName)
|
function checkbox_state = read_checkbox(checkboxName)
|
||||||
maskValues = get_param(gcbh, 'MaskValues');
|
maskValues = get_param(gcbh, 'MaskValues');
|
||||||
paramNames = get_param(gcbh, 'MaskNames');
|
paramNames = get_param(gcbh, 'MaskNames');
|
||||||
@ -593,14 +453,6 @@ classdef mcuMask
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function res = ternary(cond, valTrue, valFalse)
|
|
||||||
if cond
|
|
||||||
res = valTrue;
|
|
||||||
else
|
|
||||||
res = valFalse;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function tool(text, example)
|
function tool(text, example)
|
||||||
% Устанавливает заданный текст в параметр Text Area 'toolText' через объект маски
|
% Устанавливает заданный текст в параметр Text Area 'toolText' через объект маски
|
||||||
|
168
McuLib/m/mcuPath.m
Normal file
168
McuLib/m/mcuPath.m
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
classdef mcuPath
|
||||||
|
methods(Static)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function path = get(paramName)
|
||||||
|
blockPath = gcb;
|
||||||
|
path = get_param(blockPath, paramName);
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function addSourceFileTable(targetParamName, message)
|
||||||
|
% Открываем проводник для выбора файлов
|
||||||
|
[files, pathstr] = uigetfile({ ...
|
||||||
|
'*.c;*.cpp', 'Исходные файлы (*.c, *.cpp)'; ...
|
||||||
|
'*.obj;*.lib', 'Библиотеки (*.obj, *.lib)'; ...
|
||||||
|
'*.*', 'Все файлы (*.*)'}, ...
|
||||||
|
message, ...
|
||||||
|
'MultiSelect', 'on');
|
||||||
|
|
||||||
|
if isequal(files, 0)
|
||||||
|
return; % Отмена выбора
|
||||||
|
end
|
||||||
|
if ischar(files)
|
||||||
|
files = {files}; % Один файл — в cell
|
||||||
|
end
|
||||||
|
% Парсим строку в cell-массив
|
||||||
|
oldTable = customtable.parse(targetParamName);
|
||||||
|
|
||||||
|
% Добавляем новые пути, проверяя уникальность
|
||||||
|
for i = 1:numel(files)
|
||||||
|
fullpath = fullfile(pathstr, files{i});
|
||||||
|
rel = mcuPath.absoluteToRelativePath(fullpath);
|
||||||
|
if ~any(strcmp(rel, oldTable))
|
||||||
|
oldTable{end+1, 1} = rel;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
% Парсим строку в cell-массив
|
||||||
|
customtable.collect(targetParamName, oldTable);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function addPathTable(targetParamName, message)
|
||||||
|
% Открываем проводник для выбора папок
|
||||||
|
pathstr = uigetdir(pwd, message);
|
||||||
|
if isequal(pathstr, 0)
|
||||||
|
return; % Отмена выбора
|
||||||
|
end
|
||||||
|
% Парсим таблицу
|
||||||
|
oldTable = customtable.parse(targetParamName);
|
||||||
|
|
||||||
|
rel = mcuPath.absoluteToRelativePath(pathstr);
|
||||||
|
|
||||||
|
% Проверяем наличие пути
|
||||||
|
if ~any(strcmp(rel, oldTable))
|
||||||
|
oldTable{end+1, 1} = rel;
|
||||||
|
end
|
||||||
|
|
||||||
|
% Собираем таблицу
|
||||||
|
customtable.collect(targetParamName, oldTable);
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function addPath(targetParamName, message)
|
||||||
|
block = gcb;
|
||||||
|
mask = Simulink.Mask.get(block);
|
||||||
|
% Открываем окно выбора папки
|
||||||
|
folderPath = uigetdir('', 'Выберите папку');
|
||||||
|
% Проверка на отмену
|
||||||
|
if isequal(folderPath, 0)
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
% Установка значения параметра маски
|
||||||
|
rel = mcuPath.absoluteToRelativePath(folderPath);
|
||||||
|
param = mask.getParameter(targetParamName);
|
||||||
|
param.Value = rel;
|
||||||
|
end
|
||||||
|
|
||||||
|
function addAnyFile(targetParamName, message)
|
||||||
|
block = gcbh;
|
||||||
|
mask = Simulink.Mask.get(block);
|
||||||
|
[file, path] = uigetfile({'*.*','Все файлы (*.*)'}, 'Выберите файл');
|
||||||
|
if isequal(file, 0) || isequal(path, 0)
|
||||||
|
% Отмена выбора — ничего не делаем
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
fullFilePath = fullfile(path, file);
|
||||||
|
rel = mcuPath.absoluteToRelativePath(fullFilePath);
|
||||||
|
param = mask.getParameter(targetParamName);
|
||||||
|
param.Value = rel;
|
||||||
|
end
|
||||||
|
|
||||||
|
function absPath = getAbsolutePath(relPath)
|
||||||
|
% relativeToAbsolutePath — преобразует относительный путь в абсолютный.
|
||||||
|
%
|
||||||
|
% Если путь уже абсолютный — возвращается он же, приведённый к канонической форме.
|
||||||
|
% Если путь относительный — преобразуется относительно текущей директории.
|
||||||
|
|
||||||
|
% Проверка: абсолютный ли путь
|
||||||
|
if ispc
|
||||||
|
isAbsolute = ~isempty(regexp(relPath, '^[a-zA-Z]:[\\/]', 'once')) || startsWith(relPath, '\\');
|
||||||
|
else
|
||||||
|
isAbsolute = startsWith(relPath, '/');
|
||||||
|
end
|
||||||
|
|
||||||
|
if isAbsolute
|
||||||
|
% Канонизируем абсолютный путь (убираем ./, ../ и т.п.)
|
||||||
|
absPath = char(java.io.File(relPath).getCanonicalPath());
|
||||||
|
else
|
||||||
|
% Строим абсолютный путь от текущей директории
|
||||||
|
cwd = pwd;
|
||||||
|
combined = fullfile(cwd, relPath);
|
||||||
|
absPath = char(java.io.File(combined).getCanonicalPath());
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function rel = absoluteToRelativePath(pathstr)
|
||||||
|
% absoluteToRelativePath — преобразует абсолютный путь в относительный от текущей директории.
|
||||||
|
%
|
||||||
|
% Если путь находится в текущей директории или вложенной в неё — добавляется префикс './'
|
||||||
|
% Если выше — формируются переходы '..'
|
||||||
|
% Если путь совпадает с текущей директорией — возвращается '.'
|
||||||
|
|
||||||
|
% Получаем текущую рабочую директорию
|
||||||
|
cwd = pwd;
|
||||||
|
|
||||||
|
% Преобразуем пути в канонические абсолютные пути
|
||||||
|
fullpath = char(java.io.File(pathstr).getCanonicalPath());
|
||||||
|
cwd = char(java.io.File(cwd).getCanonicalPath());
|
||||||
|
|
||||||
|
% Разбиваем пути на части
|
||||||
|
targetParts = strsplit(fullpath, filesep);
|
||||||
|
baseParts = strsplit(cwd, filesep);
|
||||||
|
|
||||||
|
% Находим длину общего префикса
|
||||||
|
j = 1;
|
||||||
|
while j <= min(length(targetParts), length(baseParts)) && strcmpi(targetParts{j}, baseParts{j})
|
||||||
|
j = j + 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
% Формируем количество подъемов ".." из cwd
|
||||||
|
numUps = length(baseParts) - (j - 1);
|
||||||
|
ups = repmat({'..'}, 1, numUps);
|
||||||
|
|
||||||
|
% Оставшаяся часть пути после общего префикса
|
||||||
|
rest = targetParts(j:end);
|
||||||
|
|
||||||
|
% Объединяем для получения относительного пути
|
||||||
|
relParts = [ups, rest];
|
||||||
|
rel = fullfile(relParts{:});
|
||||||
|
|
||||||
|
% Если путь пустой — это текущая директория
|
||||||
|
if isempty(rel)
|
||||||
|
rel = '.';
|
||||||
|
end
|
||||||
|
|
||||||
|
% Если путь не содержит ".." и начинается внутри текущей директории — добавим './'
|
||||||
|
if ~isempty(rest) && isempty(ups)
|
||||||
|
rel = fullfile('.', rel);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
@ -5,8 +5,8 @@ classdef mcuPorts
|
|||||||
function write()
|
function write()
|
||||||
block = gcb;
|
block = gcb;
|
||||||
mask = Simulink.Mask.get(block);
|
mask = Simulink.Mask.get(block);
|
||||||
hPath = fullfile('.\MCU_Wrapper', 'mcu_wrapper_conf.h');
|
hPath = fullfile(mcuPath.get('wrapperPath'), 'mcu_wrapper_conf.h');
|
||||||
cPath = fullfile('.\MCU_Wrapper', 'mcu_wrapper.c');
|
cPath = fullfile(mcuPath.get('wrapperPath'), 'mcu_wrapper.c');
|
||||||
mcuPorts.defaultUnused();
|
mcuPorts.defaultUnused();
|
||||||
%% CREATE
|
%% CREATE
|
||||||
prefixNumb = 'IN';
|
prefixNumb = 'IN';
|
||||||
|
@ -4,10 +4,11 @@ function res = mexing(compile_mode)
|
|||||||
Ts = 0.00001;
|
Ts = 0.00001;
|
||||||
|
|
||||||
if compile_mode == 1
|
if compile_mode == 1
|
||||||
delete("*.mexw64")
|
delete('*.mexw64')
|
||||||
delete("*.mexw64.pdb")
|
delete('*.mexw64.pdb')
|
||||||
delete(".\MCU_Wrapper\Outputs\*.*");
|
delete([mcuPath.get('wrapperPath'), '\Outputs\*.*']);
|
||||||
set_param(gcb, 'consoleOutput', '');
|
set_param(gcb, 'consoleOutput', '');
|
||||||
|
compiler.updateRunBat();
|
||||||
% Дефайны
|
% Дефайны
|
||||||
definesUserArg = parseDefinesMaskText();
|
definesUserArg = parseDefinesMaskText();
|
||||||
definesWrapperConfigArg = buildWrapperDefinesString();
|
definesWrapperConfigArg = buildWrapperDefinesString();
|
||||||
@ -31,7 +32,8 @@ function res = mexing(compile_mode)
|
|||||||
Name = mcuMask.get_name();
|
Name = mcuMask.get_name();
|
||||||
|
|
||||||
% Вызов батника с двумя параметрами: includes и code
|
% Вызов батника с двумя параметрами: includes и code
|
||||||
cmd = sprintf('.\\MCU_Wrapper\\run_mex.bat %s "%s" "%s" "%s" "%s" %s %s', Name, includesArg, codeArg, definesUserArg, definesConfigArg, modeArg, echoArg);
|
run_bat_mex_path = fullfile(mcuPath.get('wrapperPath'), 'run_mex.bat');
|
||||||
|
cmd = sprintf('%s %s "%s" "%s" "%s" "%s" %s %s', run_bat_mex_path, Name, includesArg, codeArg, definesUserArg, definesConfigArg, modeArg, echoArg);
|
||||||
|
|
||||||
if mcuMask.read_checkbox('extConsol')
|
if mcuMask.read_checkbox('extConsol')
|
||||||
cmdout = runBatAndShowOutput(cmd);
|
cmdout = runBatAndShowOutput(cmd);
|
||||||
|
@ -7,7 +7,7 @@ classdef periphConfig
|
|||||||
% Проверяем, была ли маска открыта
|
% Проверяем, была ли маска открыта
|
||||||
% wasOpen = isMaskDialogOpen(blockPath);
|
% wasOpen = isMaskDialogOpen(blockPath);
|
||||||
mask = Simulink.Mask.get(blockPath);
|
mask = Simulink.Mask.get(blockPath);
|
||||||
periphPath = get_param(blockPath, 'periphPath');
|
periphPath = mcuPath.get('periphPath');
|
||||||
[periphPath, ~, ~] = fileparts(periphPath);
|
[periphPath, ~, ~] = fileparts(periphPath);
|
||||||
|
|
||||||
tableNames = {'incTable', 'srcTable'};
|
tableNames = {'incTable', 'srcTable'};
|
||||||
@ -91,7 +91,7 @@ classdef periphConfig
|
|||||||
for i = 1:numel(periphs)
|
for i = 1:numel(periphs)
|
||||||
periph = periphs{i};
|
periph = periphs{i};
|
||||||
|
|
||||||
% Пропускаем Code и UserCode, они уже обработаны
|
% Пропускаем Code и UserCode
|
||||||
if strcmp(periph, 'Code') || strcmp(periph, 'UserCode')
|
if strcmp(periph, 'Code') || strcmp(periph, 'UserCode')
|
||||||
continue;
|
continue;
|
||||||
end
|
end
|
||||||
@ -166,85 +166,23 @@ classdef periphConfig
|
|||||||
fclose(fid);
|
fclose(fid);
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
methods(Static, Access=private)
|
methods(Static, Access=private)
|
||||||
|
|
||||||
function res = addCodeConfig(codeConfig, periphPath)
|
function res = addCodeConfig(codeConfig, periphPath)
|
||||||
% Возвращает 0 при успехе, 1 при ошибке
|
% Возвращает 0 при успехе, 1 при ошибке
|
||||||
try
|
try
|
||||||
% Источники
|
% Формируем строки
|
||||||
srcList = {};
|
srcText = compiler.createSourcesBat('code_PERIPH', codeConfig.Sources.Options, periphPath);
|
||||||
if isfield(codeConfig, 'Sources') && isfield(codeConfig.Sources, 'Options')
|
incText = compiler.createIncludesBat('includes_PERIPH', codeConfig.Includes.Options, periphPath);
|
||||||
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); % Всё прошло успешно
|
res = compiler.updateRunMexBat(srcText, incText, ':: PERIPH BAT'); % Всё прошло успешно
|
||||||
catch
|
catch
|
||||||
% В случае ошибки просто возвращаем 1
|
% В случае ошибки просто возвращаем 1
|
||||||
res = 1;
|
res = 1;
|
||||||
@ -289,7 +227,7 @@ classdef periphConfig
|
|||||||
%
|
%
|
||||||
% Возвращает:
|
% Возвращает:
|
||||||
% res - 0 при успехе, 1 при ошибке
|
% res - 0 при успехе, 1 при ошибке
|
||||||
wrapPath = fullfile('.\MCU_Wrapper', 'mcu_wrapper.c');
|
wrapPath = fullfile(mcuPath.get('wrapperPath'), 'mcu_wrapper.c');
|
||||||
res = 1;
|
res = 1;
|
||||||
try
|
try
|
||||||
code = fileread(wrapPath);
|
code = fileread(wrapPath);
|
||||||
@ -314,36 +252,6 @@ classdef periphConfig
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
function addDefineConfig(mask, containerName, periphName, defPrompt, def)
|
||||||
% mask — объект маски Simulink.Mask.get(blockPath)
|
% mask — объект маски Simulink.Mask.get(blockPath)
|
||||||
@ -371,6 +279,8 @@ classdef periphConfig
|
|||||||
paramType = 'checkbox';
|
paramType = 'checkbox';
|
||||||
case 'edit'
|
case 'edit'
|
||||||
paramType = 'edit';
|
paramType = 'edit';
|
||||||
|
case 'popup'
|
||||||
|
paramType = 'popup';
|
||||||
otherwise
|
otherwise
|
||||||
% Игнорируем остальные типы
|
% Игнорируем остальные типы
|
||||||
return;
|
return;
|
||||||
@ -378,26 +288,45 @@ classdef periphConfig
|
|||||||
|
|
||||||
paramName = matlab.lang.makeValidName(defPrompt);
|
paramName = matlab.lang.makeValidName(defPrompt);
|
||||||
|
|
||||||
% Преобразуем значение Default в строку для Value
|
% Получаем значение
|
||||||
val = def.Default;
|
if strcmp(paramType, 'popup')
|
||||||
if islogical(val)
|
if isfield(def, 'Def') && iscell(def.Def) && ~isempty(def.Def)
|
||||||
valStr = mcuMask.ternary(val, 'on', 'off');
|
choices = def.Def;
|
||||||
elseif isnumeric(val)
|
valStr = ''; % по умолчанию — ничего
|
||||||
valStr = num2str(val);
|
else
|
||||||
elseif ischar(val)
|
warning('Popout параметр "%s" не содержит допустимого списка в Def.', defPrompt);
|
||||||
valStr = val;
|
return;
|
||||||
|
end
|
||||||
else
|
else
|
||||||
error('Unsupported default value type for %s.%s', periphName, defPrompt);
|
val = def.Default;
|
||||||
|
if islogical(val)
|
||||||
|
valStr = periphConfig.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
|
||||||
end
|
end
|
||||||
|
|
||||||
% Добавляем параметр в маску
|
% Добавляем параметр в маску
|
||||||
param = mask.addParameter( ...
|
if strcmp(paramType, 'popup')
|
||||||
'Type', paramType, ...
|
param = mask.addParameter( ...
|
||||||
'Prompt', def.Prompt, ...
|
'Type', paramType, ...
|
||||||
'Name', paramName, ...
|
'Prompt', def.Prompt, ...
|
||||||
'Value', valStr, ...
|
'Name', paramName, ...
|
||||||
'Container', periphName ...
|
'Container', periphName ...
|
||||||
);
|
);
|
||||||
|
else
|
||||||
|
param = mask.addParameter( ...
|
||||||
|
'Type', paramType, ...
|
||||||
|
'Prompt', def.Prompt, ...
|
||||||
|
'Name', paramName, ...
|
||||||
|
'Value', valStr, ...
|
||||||
|
'Container', periphName ...
|
||||||
|
);
|
||||||
|
end
|
||||||
|
|
||||||
param.Evaluate = 'off';
|
param.Evaluate = 'off';
|
||||||
|
|
||||||
@ -406,9 +335,46 @@ classdef periphConfig
|
|||||||
else
|
else
|
||||||
param.DialogControl.Row = 'current';
|
param.DialogControl.Row = 'current';
|
||||||
end
|
end
|
||||||
param.Alias = def.Def;
|
|
||||||
|
if strcmp(paramType, 'popup')
|
||||||
|
param.TypeOptions = def.Def;
|
||||||
|
else
|
||||||
|
param.Alias = def.Def;
|
||||||
|
end
|
||||||
end
|
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
|
||||||
|
|
||||||
|
function res = ternary(cond, valTrue, valFalse)
|
||||||
|
if cond
|
||||||
|
res = valTrue;
|
||||||
|
else
|
||||||
|
res = valFalse;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user