Добавить защищённое обновление приложения через RS и CAN с сохранением загрузчика, проверкой CRC, восстановлением передачи и защитой EEPROM. Подключить общий процесс сборки C2000 и автоматический счётчик версии для согласованных RAM, Flash, CAN и паспортов бинарных образов. Уточнить запуск ЦАП, обработку измерений и параметров нагрузки, начальную индикацию и документацию регистров GAS. Дополнить модели аппаратуры и изолировать заголовки CAN в тестах. Проверка: 26 тестов выпуска, 4 теста восстановления CAN и восемь программных моделей CAN, Flash, EEPROM, ЦАП, параметров и TESTLAMP.
81 lines
9.8 KiB
Python
81 lines
9.8 KiB
Python
# Проверка load_control на модели пк. Комментарии и исходный текст в UTF-8.
|
|
# Продолжает текущий вызов, список или выражение: """Compile the production DAC.c against GPIO/IRQ mocks; no hardware writes."""; порядок аргументов сохраняется.
|
|
"""Compile the production DAC.c against GPIO/IRQ mocks; no hardware writes."""
|
|
# Подключает модуль os для этой проверки или подготовки сборки.
|
|
import os
|
|
import argparse # Позволяет независимо запускать регрессию EEPROM без аппаратных заглушек полного автомата нагрузки.
|
|
# Разбирает только границы настоящих функций message.c, сохраняя их неизменённое тело в регрессии параметров.
|
|
import re
|
|
# Импортирует необходимые имена: from pathlib import Path.
|
|
from pathlib import Path
|
|
# Подключает модуль shutil для этой проверки или подготовки сборки.
|
|
import shutil
|
|
# Подключает модуль subprocess для этой проверки или подготовки сборки.
|
|
import subprocess
|
|
# Подключает модуль tempfile для этой проверки или подготовки сборки.
|
|
import tempfile
|
|
|
|
parser = argparse.ArgumentParser(description=__doc__) # Описывает существующую полную проверку и отдельный независимый сценарий применения параметров.
|
|
parser.add_argument('--parameters-only', action='store_true', help='Проверить только Load/Default параметров и стартовую индикацию задатчика') # Ограничивает запуск функциями message.c, не подключая аппаратный DAC.c.
|
|
arguments = parser.parse_args() # Проверяет режим до компиляции временных моделей.
|
|
|
|
# Задаёт root по выражению справа; значение используется последующими операциями.
|
|
root = Path(__file__).resolve().parents[2]
|
|
# Задаёт compiler по выражению справа; значение используется последующими операциями.
|
|
compiler = os.environ.get('CC') or shutil.which('gcc') or 'C:/msys64/mingw64/bin/gcc.exe'
|
|
# Задаёт os.environ['PATH'] по выражению справа; значение используется последующими операциями.
|
|
os.environ['PATH'] = str(Path(compiler).resolve().parent) + os.pathsep + os.environ['PATH']
|
|
# Открывает управляемый ресурс tempfile.TemporaryDirectory(prefix='load-control-') as tmp с автоматическим освобождением.
|
|
with tempfile.TemporaryDirectory(prefix='load-control-') as tmp:
|
|
if not arguments.parameters_only: # Полный автомат проверяется по умолчанию; его аппаратные зависимости не блокируют независимый запуск EEPROM-регрессии.
|
|
# Задаёт exe по выражению справа; значение используется последующими операциями.
|
|
exe = Path(tmp) / 'test.exe'
|
|
flash = (root / 'Source/Internal/flash_rs.c').read_text(encoding='utf-8') # Проверяет настоящий запрет активной нагрузки перед переходом к Flash API.
|
|
guard = re.search(r'^static unsigned int flash_load_guard\([^\n]*\n\{[^\n]*\n[^\n]*\n\}', flash, re.M) # Извлекает короткий production-helper без переписывания его условия в тестовой модели.
|
|
assert guard, 'Production Flash load guard missing' # Переименование или удаление защиты не должно незаметно отключить тест перед erase.
|
|
(Path(tmp) / 'production_flash_guard.inc').write_text(guard.group() + '\n', encoding='utf-8') # Включает реальный helper в тот же C-модуль, который исполняет production DAC.c.
|
|
# Запускает внешний компилятор, генератор или тест; ненулевой результат проверяется параметрами вызова.
|
|
subprocess.run([compiler, '-Wall', '-Wextra', '-std=c99',
|
|
# Продолжает текущий вызов, список или выражение: '-Itests/load_control/stubs', '-ISource/Internal/Include',; порядок аргументов сохраняется.
|
|
'-Itests/load_control/stubs', '-ISource/Internal/Include', '-I' + tmp, # Добавляет только каталог извлечённого рабочего Flash-helper этой проверки.
|
|
# Задаёт 'tests/load_control/load_control_test.c', '-o', str(exe)], cwd по выражению справа; значение используется последующими операциями.
|
|
'tests/load_control/load_control_test.c', '-o', str(exe)], cwd=root, check=True)
|
|
# Запускает внешний компилятор, генератор или тест; ненулевой результат проверяется параметрами вызова.
|
|
subprocess.run([str(exe)], check=True, timeout=10)
|
|
# Читает рабочие загрузку и заводской сброс параметров для отдельной проверки живых START/RESET.
|
|
message = (root / 'Source/Internal/message.c').read_text(encoding='utf-8')
|
|
# Маскирует комментарии пробелами той же длины, чтобы фигурные скобки пояснений не влияли на поиск границ функций.
|
|
masked = re.sub(r'/\*.*?\*/|//[^\n]*', lambda match: ' ' * len(match.group()), message, flags=re.S)
|
|
# Собирает только проверяемые рабочие функции: EEPROM и GPIO заменяются моделью, сам алгоритм загрузки остаётся исходным.
|
|
functions = []
|
|
# Извлекает helper и обе ветки применения параметров независимо от форматирования вложенных блоков в исходнике.
|
|
for name in ('reset_parameter_commands', 'Default_params', 'Load_params'):
|
|
# Находит объявление функции с настоящим именем и необязательным static.
|
|
match = re.search(r'^(?:static )?void ' + name + r'\(', masked, re.M)
|
|
# Не допускает молчаливого запуска модели после удаления или переименования рабочего обработчика.
|
|
assert match, name
|
|
# Начинает подсчёт вложенных блоков с первой открывающей скобки тела найденной функции.
|
|
end, depth = masked.index('{', match.start()), 0
|
|
# Идёт до парной закрывающей скобки, сохраняя все вложенные условия и циклы рабочего кода.
|
|
while end < len(masked):
|
|
# Учитывает открывающую скобку только после удаления комментариев.
|
|
depth += masked[end] == '{'
|
|
# Учитывает соответствующую закрывающую скобку в том же уровне вложенности.
|
|
depth -= masked[end] == '}'
|
|
# Переходит к следующему символу, включая последнюю закрывающую скобку в извлекаемый текст.
|
|
end += 1
|
|
# Нулевой уровень означает завершение всей функции, а не первого вложенного if.
|
|
if depth == 0:
|
|
# Завершает поиск, чтобы не включить соседний обработчик Modbus.
|
|
break
|
|
# Копирует исходный текст функции вместе с русскими комментариями без изменения условий и присваиваний.
|
|
functions.append(message[match.start():end])
|
|
# Передаёт извлечённый production-код компилятору из временного каталога, не записывая generated-файлы в репозиторий.
|
|
(Path(tmp) / 'production_parameters.inc').write_text('\n'.join(functions), encoding='utf-8')
|
|
# Выбирает отдельный исполняемый файл, чтобы проверка параметров не подменяла прежние регрессии автомата DAC.
|
|
parameters_exe = Path(tmp) / 'parameters.exe'
|
|
# Компилирует реальную загрузку параметров с контролем IRQ, модели EEPROM и содержимого R127.
|
|
subprocess.run([compiler, '-Wall', '-Wextra', '-std=c99', '-I' + tmp, 'tests/load_control/parameter_commands_test.c', '-o', str(parameters_exe)], cwd=root, check=True)
|
|
# Выполняет комбинации живых START/RESET, сохранённых команд, неверной CRC, холодного старта и CAN во время чтения.
|
|
subprocess.run([str(parameters_exe)], check=True, timeout=10)
|