more ota status stuff in python and more way to communicate status in c

This commit is contained in:
Laura Klünder 2023-12-01 14:58:47 +01:00
parent 6eaf087f71
commit 434e1edcc0
6 changed files with 87 additions and 10 deletions

View file

@ -18,7 +18,7 @@ from c3nav.mesh import messages
from c3nav.mesh.messages import (MESH_BROADCAST_ADDRESS, MESH_NONE_ADDRESS, MESH_ROOT_ADDRESS, OTA_CHUNK_SIZE,
MeshMessage, MeshMessageType)
from c3nav.mesh.models import MeshNode, MeshUplink, NodeMessage, OTAUpdate, OTAUpdateRecipient, OTARecipientStatus
from c3nav.mesh.utils import MESH_ALL_UPLINKS_GROUP, UPLINK_PING, get_mesh_uplink_group
from c3nav.mesh.utils import MESH_ALL_UPLINKS_GROUP, UPLINK_PING, get_mesh_uplink_group, MESH_ALL_OTA_GROUP
from c3nav.routing.rangelocator import RangeLocator
@ -597,9 +597,16 @@ class MeshUIConsumer(AsyncJsonWebsocketConsumer):
if content.get("subscribe", None) == "ranging":
await self.channel_layer.group_add("mesh_msg_received", self.channel_name)
self.msg_received_filter = {"msg_type": MeshMessageType.LOCATE_RANGE_RESULTS.name}
await self.dump_newest_messages(MeshMessageType.LOCATE_RANGE_RESULTS)
if content.get("subscribe", None) == "ota":
await self.channel_layer.group_add("mesh_msg_received", self.channel_name)
self.msg_received_filter = {"msg_type": MeshMessageType.OTA_STATUS.name}
update_id = content.get("update_id", 0)
self.msg_received_filter = {"msg_type": MeshMessageType.OTA_STATUS.name,
"update_id": update_id}
await self.channel_layer.group_add(MESH_ALL_OTA_GROUP, self.channel_name)
await self.dump_newest_messages(MeshMessageType.OTA_STATUS)
for recipient in await self._qs_as_list(OTAUpdateRecipient.objects.filter(update_id=update_id)):
await self.send_json(recipient.get_status_json())
if "send_msg" in content:
msg_to_send = self.scope["session"].pop("mesh_msg_%s" % content["send_msg"], None)
if not msg_to_send:
@ -619,6 +626,22 @@ class MeshUIConsumer(AsyncJsonWebsocketConsumer):
**msg_to_send["msg_data"],
}).send(sender=self.channel_name)
async def dump_newest_messages(self, msg_type: MeshMessageType, include_ota=False):
for msg in await self._get_last_messages(msg_type):
await self.mesh_msg_received({
"type": "mesh.msg_received",
"msg": msg.data
})
@database_sync_to_async
def _get_last_messages(self, msg_type):
results = (node.last_messages[msg_type] for node in MeshNode.objects.prefetch_last_messages(msg_type))
return [msg for msg in results if msg]
@database_sync_to_async
def _qs_as_list(self, qs):
return list(qs)
async def mesh_log_entry(self, data):
await self.send_json(data)

View file

@ -53,7 +53,7 @@ class MeshMessageType(EnumSchemaByNameMixin, IntEnum):
OTA_REQUEST_FRAGMENTS = 0x25
OTA_SETTING = 0x26
OTA_APPLY = 0x27
OTA_ABORT = 0x29
OTA_ABORT = 0x28
LOCATE_REQUEST_RANGE = 0x30
LOCATE_RANGE_RESULTS = 0x31
@ -269,6 +269,25 @@ class ConfigUplinkMessage(MeshMessage, msg_type=MeshMessageType.CONFIG_UPLINK):
port: int = field(metadata={"format": SimpleFormat('H')})
@unique
class OTADeviceStatus(EnumSchemaByNameMixin, IntEnum):
""" ota status, the ones >= 0x10 denote a permanent failure """
NONE = 0x00
STARTED = 0x01
APPLIED = 0x02
START_FAILED = 0x10
WRITE_FAILED = 0x12
APPLY_FAILED = 0x13
ROLLED_BACK = 0x14
@property
def pretty_name(self):
return self.name.replace('_', ' ').lower()
@dataclass
class OTAStatusMessage(MeshMessage, msg_type=MeshMessageType.OTA_STATUS):
""" report OTA status """
@ -277,6 +296,7 @@ class OTAStatusMessage(MeshMessage, msg_type=MeshMessageType.OTA_STATUS):
highest_chunk: int = field(metadata={"format": SimpleFormat('H')})
auto_apply: bool = field(metadata={"format": BoolFormat()})
auto_reboot: bool = field(metadata={"format": BoolFormat()})
status: OTADeviceStatus = field(metadata={"format": EnumFormat('B')})
@dataclass

View file

@ -7,6 +7,8 @@ from functools import cached_property
from operator import attrgetter
from typing import Any, Mapping, Optional, Self
import channels
from asgiref.sync import async_to_sync
from django.contrib.auth import get_user_model
from django.core.validators import RegexValidator
from django.db import NotSupportedError, models
@ -21,7 +23,7 @@ from c3nav.mesh.dataformats import BoardType, ChipType, FirmwareImage
from c3nav.mesh.messages import ConfigFirmwareMessage, ConfigHardwareMessage
from c3nav.mesh.messages import MeshMessage as MeshMessage
from c3nav.mesh.messages import MeshMessageType
from c3nav.mesh.utils import UPLINK_TIMEOUT
from c3nav.mesh.utils import UPLINK_TIMEOUT, MESH_ALL_OTA_GROUP
from c3nav.routing.rangelocator import RangeLocator
FirmwareLookup = namedtuple('FirmwareLookup', ('sha256_hash', 'chip', 'project_name', 'version', 'idf_version'))
@ -512,3 +514,18 @@ class OTAUpdateRecipient(models.Model):
UniqueConstraint(fields=["node"], condition=Q(status=OTARecipientStatus.RUNNING),
name='only_one_active_ota'),
)
async def send_status(self):
"""
use this for OTA stuffs
"""
channels.layers.get_channel_layer().group_send(MESH_ALL_OTA_GROUP, self.get_status_json())
def get_status_json(self):
return {
"type": "mesh.ota_recipient_status",
"node": self.node_id,
"update": self.update_id,
"status": self.status,
}

View file

@ -13,7 +13,10 @@
{% endif %}
{% if nodes_xyz %}
const nodes_xyz = JSON.parse(document.getElementById('nodes-xyz').textContent);
const nodes_distances = {}
const nodes_distances = {};
{% endif %}
{% if update %}
const update_id = {{ update.pk }};
{% endif %}
function connect() {
@ -26,7 +29,7 @@ function connect() {
{% elif ranging_form %}
ws.send(JSON.stringify({"subscribe": "ranging"}));
{% elif update %}
ws.send(JSON.stringify({"subscribe": "ota-{{ update.pk }}"}));
ws.send(JSON.stringify({"subscribe": "ota", "update_id": update_id}));
{% else %}
ws.send(JSON.stringify({"subscribe": "log"}));
{% endif %}
@ -100,6 +103,13 @@ function connect() {
{% endif %}
break;
case 'mesh.ota_recipient_status':
{% if update %}
var row = document.querySelector(`[id="ota-recipient-${data.update}-${data.node}"]`);
if (!row) break;
row.querySelector('.status').innerText = data.status;
{% endif %}
case 'mesh.msg_received':
{% if ranging_form %}
var cell, key, src_node, peer_node;
@ -158,10 +168,12 @@ function connect() {
}
{% endif %}
{% if update %}
var row = document.querySelector(`#ota-recipients-${data.msg.update_id} [id="recipient-${data.msg.src}"]`);
var row = document.querySelector(`[id="ota-recipient-${data.msg.update_id}-${data.msg.src}"]`);
if (!row) break;
row.querySelector('.received_bytes').innerText = data.msg.received_bytes;
row.querySelector('progress').value = data.msg.received_bytes;
row.querySelector('.auto-apply span').innerText = data.msg.auto_apply ? 'yes' : 'no';
row.querySelector('.auto-reboot span').innerText = data.msg.auto_reboot ? 'yes' : 'no';
{% endif %}
{% if send_uuid and msg_type == "MESH_ROUTE_REQUEST" %}
if (data.msg.route) {

View file

@ -18,16 +18,20 @@
<strong>Created:</strong> {{ update.created }}
</div>
<div>
<table id="ota-recipients-{{ update.pk }}">
<table>
<tr>
<th>{% trans 'Node' %}</th>
<th>{% trans 'Status' %}</th>
<th>{% trans 'Progress' %}</th>
</tr>
{% for recipient in update.recipients.all %}
<tr id="recipient-{{ recipient.node_id }}">
<tr id="ota-recipient-{{ update.pk }}-{{ recipient.node_id }}">
<td>{% mesh_node recipient.node %}</td>
<td>{{ recipient.get_status_display }}</td>
<td>
<span class="status"></span><br>
<span class="auto-apply"><strong>auto apply:</strong> <span></span><br></span>
<span class="auto-reboot"><strong>auto reboot:</strong> <span></span><br></span>
</td>
<td>
<span class="received_bytes">??</span> of {{ update.build.binary.size }} bytes
<br>

View file

@ -6,6 +6,7 @@ def get_mesh_uplink_group(address):
MESH_ALL_UPLINKS_GROUP = "mesh_uplink_all"
MESH_ALL_OTA_GROUP = "mesh_ota_all"
UPLINK_PING = 5
UPLINK_TIMEOUT = UPLINK_PING+5