179 lines
13 KiB
Markdown
179 lines
13 KiB
Markdown
# База прошивок как в SETGUI
|
||
|
||
Это общий сервис **базы файлов прошивок**: получить список опубликованных
|
||
образов, выбрать изделие/версию/транспорт, скачать файл, проверить SHA-256 и
|
||
передать его загрузчику. Также можно опубликовать новый образ в эту же базу.
|
||
Модуль не зависит от версии самого приложения и от `c/firmware-info`.
|
||
|
||
## Состав
|
||
|
||
| Файл | Ответственность |
|
||
|---|---|
|
||
| [`firmware_database.py`](../../python/setprotocol/firmware_database.py) | Готовый HTTPS-клиент, чтение базы, кэш скачанных образов, публикация в Gitea |
|
||
| [`firmware_catalog.py`](../../python/setprotocol/firmware_catalog.py) | Общий формат записей базы, используемый SETGUI |
|
||
| [`firmware_publish.py`](../../python/setprotocol/firmware_publish.py) | Метаданные образа, SHA-256, объединение записей каталога |
|
||
| [`firmware_db.py`](firmware_db.py) | Самостоятельный консольный запуск без SETGUI |
|
||
|
||
Требования: Python 3.10+ и стандартная библиотека. Qt, Windows Credential
|
||
Manager, установленный SETGUI и библиотеки МК не требуются. Это сервис и CLI;
|
||
готовое Qt-окно остаётся в SETGUI, а другой GUI подключает сервис по API ниже.
|
||
|
||
## Как устроена база
|
||
|
||
В текущей конфигурации SETGUI файлы находятся в Gitea Releases репозитория
|
||
`setcorp/SETRD12-Releases` на `https://git.rd12.ru`. Список файлов лежит в
|
||
`firmware.releases` файла `update.json` ветки `main`. Запись содержит изделие,
|
||
версию, транспорт, адрес образа, имя файла, SHA-256, описание и необязательный
|
||
адрес приложения во Flash. Это файловый каталог; SQL-сервер не нужен.
|
||
|
||
Новый сервис использует тот же JSON-контракт, поэтому выпуски видны клиентам
|
||
SETGUI, поддерживающим указанный транспорт. В текущем SETGUI транспорт `tms`
|
||
фильтруется; общий сервис его сохраняет. Публикация не добавляет новый
|
||
загрузочный протокол в приложение или МК.
|
||
|
||
```text
|
||
публикация .hex/.bin → Gitea release asset → скачать и проверить SHA-256
|
||
↓
|
||
обновить firmware.releases
|
||
↓
|
||
GUI: «Обновить каталог» → выбрать запись → «Скачать и выбрать» → загрузчик МК
|
||
```
|
||
|
||
Для своего сервера задайте `server`, `owner`, `repository`, `branch` и
|
||
`manifest_path`. Репозиторий, ветка и JSON-файл должны уже существовать.
|
||
Для пустой базы достаточно файла `{"firmware":{"catalogVersion":0,"releases":[]}}`.
|
||
Если `update.json` общий с обновлениями приложения, его другие разделы
|
||
сохраняются при добавлении прошивки.
|
||
|
||
## Консоль: проверить, опубликовать, посмотреть, скачать
|
||
|
||
Из корня `templates` в PowerShell (параметры сервера стоят перед командой):
|
||
|
||
```powershell
|
||
$db = @('--server', 'https://git.rd12.ru', '--owner', 'setcorp', '--repo', 'SETRD12-Releases')
|
||
|
||
# Без сети: проверить файл и сформировать запись каталога.
|
||
python tools/firmware-publish/firmware_db.py @db preflight --file C:/build/device.hex --product MY_DEVICE --version 1.2.3 --version-code 0x010203 --transport can --base-address 0x08003000
|
||
|
||
# Публикация образа в базу (изменяет сервер).
|
||
python tools/firmware-publish/firmware_db.py @db publish --file C:/build/device.hex --product MY_DEVICE --version 1.2.3 --version-code 0x010203 --transport can --base-address 0x08003000
|
||
|
||
# Получить список записей и скачать конкретную.
|
||
python tools/firmware-publish/firmware_db.py @db list --product MY_DEVICE
|
||
python tools/firmware-publish/firmware_db.py @db download --product MY_DEVICE --version-code 0x010203 --transport can --cache C:/firmware-cache
|
||
```
|
||
|
||
`MY_DEVICE`, версия, транспорт и адрес — примеры, замените их данными изделия.
|
||
SCI8 для TMS публикуйте как `.bin` с `--transport tms`, без `--base-address`.
|
||
Инструмент не конвертирует `.axf/.out` и не проверяет пригодность образа для МК.
|
||
|
||
CLI читает логин и пароль/токен из переменных окружения `FIRMWARE_DB_LOGIN` и
|
||
`FIRMWARE_DB_PASSWORD`. Задавайте их средствами своего секрет-хранилища/CI,
|
||
а не в файлах проекта и не в аргументах командной строки. Для интерактивного
|
||
ввода в PowerShell можно использовать:
|
||
|
||
```powershell
|
||
$fwCredential = Get-Credential -Message 'Gitea: логин и пароль либо токен вместо пароля'
|
||
$env:FIRMWARE_DB_LOGIN = $fwCredential.UserName
|
||
$env:FIRMWARE_DB_PASSWORD = $fwCredential.GetNetworkCredential().Password
|
||
# Выполнить нужные команды; после работы убрать переменные из текущего процесса.
|
||
Remove-Item Env:FIRMWARE_DB_LOGIN, Env:FIRMWARE_DB_PASSWORD
|
||
```
|
||
|
||
Публичный каталог можно читать без входа. Публикация через CLI требует обе
|
||
переменные. Сервис не извлекает сохранённый пароль SETGUI автоматически.
|
||
Команда `publish` явно разрешает запись и не запрашивает дополнительного
|
||
подтверждения; `preflight` ничего не загружает. Старый BAT и конфигурации
|
||
`firmware-release.cmd` продолжают использовать прежний путь через SETGUI.
|
||
|
||
## Портирование в другое Python-приложение
|
||
|
||
Подключите `templates/python` в import path приложения; переносите зависимость
|
||
целиком, не одну копию файла. Сеть и кэш реализованы в сервисе, UI и хранение
|
||
учётных данных задаёт приложение:
|
||
|
||
```python
|
||
from pathlib import Path
|
||
from setprotocol.firmware_database import (
|
||
Credentials, FirmwareDatabase, GiteaRepository, GiteaFirmwarePublisher,
|
||
)
|
||
from setprotocol.firmware_publish import FirmwarePublication
|
||
|
||
repo = GiteaRepository('https://git.rd12.ru', 'setcorp', 'SETRD12-Releases')
|
||
# login и password получает ваше приложение из формы/хранилища.
|
||
credentials = Credentials(login, password)
|
||
database = FirmwareDatabase(repo.manifest_url, Path('firmware'), credentials=credentials)
|
||
|
||
# «Обновить каталог»: результат заполнит список в вашем UI.
|
||
releases = database.read_catalog(product='MY_DEVICE', transport='can')
|
||
# После выбора пользователем строки списка:
|
||
selected = releases[selected_index]
|
||
path = database.download(selected, progress=lambda percent: print(percent))
|
||
# Передайте path, selected.transport и selected.base_address своему загрузчику.
|
||
|
||
# Публикация нового образа: отдельное действие оператора.
|
||
publisher = GiteaFirmwarePublisher(repo, credentials)
|
||
publication = FirmwarePublication(
|
||
Path('build/device.hex'), 'MY_DEVICE', '1.2.3', 0x010203, 'can', 0x08003000,
|
||
'Описание выпуска',
|
||
)
|
||
preview = publisher.preflight(publication) # можно показать перед выпуском
|
||
entry = publisher.publish(publication)
|
||
```
|
||
|
||
Для публичного каталога передайте `credentials=None`. Для чтения каталога,
|
||
расположенного вне стандартного Gitea URL, передайте его HTTPS-адрес прямо в
|
||
`FirmwareDatabase`; `GiteaRepository` нужен сетевому публикатору.
|
||
|
||
В Qt выполняйте `read_catalog`, `download`, `publish` в worker/QThread,
|
||
передавайте прогресс сигналами в главный поток. По аналогии с SETGUI:
|
||
|
||
1. Кнопка «Обновить каталог» вызывает `read_catalog`, заполняет combo/table.
|
||
2. В строке показываются `product`, `version`, `transport`; в деталях —
|
||
`file_name`, `notes`, `base_address`.
|
||
3. «Скачать и выбрать» вызывает `download`, возвращает проверенный путь.
|
||
4. Сигнал в основное окно передаёт путь и выбранную `FirmwareRelease`;
|
||
окно выставляет файл и транспорт на вкладке прошивки.
|
||
|
||
Скачивание не запускает прошивку устройства автоматически. Для C++/Android
|
||
можно использовать CLI как отдельный процесс на ПК или реализовать клиент
|
||
этого JSON-контракта на языке платформы; Python-модуль не является C-библиотекой.
|
||
|
||
## Поведение и ошибки
|
||
|
||
- Кэш: `<cache>/<sha256>/<fileName>`. Одинаковые имена разных образов не
|
||
конфликтуют. Проверенный кэш используется повторно; незавершённые временные
|
||
файлы удаляются. Результат появляется только после совпадения SHA-256.
|
||
- Только HTTPS. Учётные данные отправляются лишь исходному origin (схема,
|
||
хост, порт); на сторонний CDN и при смене origin в redirect не передаются.
|
||
Redirect для запросов записи отклоняется.
|
||
- Публикация использует asset с SHA-256 в имени: новые байты не заменяют файл,
|
||
на который ещё ссылается старый каталог. `fileName` в каталоге остаётся
|
||
исходным именем. В этом деталь реализации отличается от старого публикатора
|
||
SETGUI, но формат каталога совместим.
|
||
- Каталог меняется только после обратного скачивания образа. Запись проверяет
|
||
ревизию файла (`sha`). При конфликте операция завершается ошибкой: повторите
|
||
выпуск после проверки причины. Загруженный asset может остаться без записи
|
||
каталога; повторный запуск использует его и снова проверит скачивание.
|
||
- Повтор той же записи не увеличивает `catalogVersion`. Обновление одного
|
||
`product + versionCode + transport` заменяет запись. Полный rollback и
|
||
автоматическая очистка старых assets не выполняются.
|
||
- Текущий upload держит образ в памяти (лимит 128 MiB), чтобы параллельная
|
||
пересборка не подменила байты между вычислением хэша и отправкой.
|
||
|
||
## Проверка
|
||
|
||
Из `templates`:
|
||
|
||
```powershell
|
||
$env:PYTHONPATH = "$PWD/python"
|
||
python -m unittest discover -s python/tests -p test_firmware_database.py -v
|
||
python -m unittest discover -s python/tests -p test_firmware_publish.py -v
|
||
```
|
||
|
||
Тесты используют сервер в памяти: каталог → публикация → скачивание, повторный
|
||
выпуск, сохранение других разделов, ошибки SHA-256 и конфликты ревизии,
|
||
изоляцию авторизации при redirect. Проверка реальной Gitea и загрузка на МК
|
||
в эти тесты не входят. Перед производственным подключением проверьте выпуск
|
||
в тестовом репозитории вашей Gitea и чтение каталога целевым GUI.
|