Пункт 18 ревью 2026-09-28 21:32 (docs/changes/027): после отката ROS
плата остаётся на более новой прошивке, а приложение сравнивало версии
на неравенство и показывало откат как обновление.
- devices.fw_state: unknown/update/downgrade/current; has_fw_update и
фильтры не считают откат обновлением; метка «в ROS: X».
- upgrade_firmware не понижает прошивку; общая запись — _flash_firmware.
- Задача fw_downgrade: проверка → обязательный бэкап → запись →
перезагрузка; API POST /devices/{id}/firmware/downgrade и
/batch/fw_downgrade (target_version обязателен).
- UI: «Откатить прошивку…» (строка и группа), общее окно отката с kind.
Тесты: 39 из 39. Стенд: временное недоступное устройство — 422/202,
задачи failed на проверке, бэкапов нет. Реальная запись прошивки при
откате и ручная проверка UI не выполнялись.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
32 lines
1.9 KiB
HTML
32 lines
1.9 KiB
HTML
{% import "_ui.html" as ui %}
|
||
{% set running = jobs|selectattr("status", "in", ["pending", "running"])|list|length %}
|
||
{% set done = jobs|selectattr("status", "equalto", "done")|list|length %}
|
||
{% set failed = jobs|selectattr("status", "equalto", "failed")|list|length %}
|
||
<div class="section-head">
|
||
<h2>Задачи</h2>
|
||
{% if running %}<span class="badge run">{{ running }} выполняется</span>{% endif %}
|
||
{% if done %}<span class="badge">{{ done }} завершено</span>{% endif %}
|
||
{% if failed %}<span class="badge bad">{{ failed }} с ошибкой</span>{% endif %}
|
||
</div>
|
||
{% if flash %}<p class="err">{{ flash }}</p>{% endif %}
|
||
<div class="card">
|
||
<table class="grid jobs">
|
||
<colgroup><col style="width:96px"><col style="width:16%"><col style="width:16%"><col style="width:130px"><col><col style="width:150px"></colgroup>
|
||
<thead><tr><th>ID</th><th>Устройство</th><th>Операция</th><th>Статус</th><th>Сообщение</th><th>Создана (UTC)</th></tr></thead>
|
||
<tbody>
|
||
{% for j in jobs %}
|
||
<tr>
|
||
<td class="c-mono muted" title="{{ j.id }}">{{ j.id|short_id }}</td>
|
||
<td style="font-weight:500">{{ j.device_name }}</td>
|
||
<td>{{ {"backup": "Бэкап", "ros_update": "Обновление ROS", "fw_update": "Обновление FW", "set_channel": "Смена канала", "ros_downgrade": "Откат ROS", "fw_downgrade": "Откат FW"}.get(j.type, j.type) }}</td>
|
||
<td><span class="badge {{ {'running': 'run', 'done': 'ok', 'failed': 'bad'}.get(j.status, '') }}">{{ {"pending": "В очереди", "running": "Выполняется", "done": "Готово", "failed": "Ошибка"}.get(j.status, j.status) }}</span></td>
|
||
<td class="msg">{{ j.message }}</td>
|
||
<td class="c-small">{{ j.created_at|dt }}</td>
|
||
</tr>
|
||
{% else %}
|
||
<tr><td colspan="6" class="empty">Задач пока нет.</td></tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|