more ota status stuff in python and more way to communicate status in c
This commit is contained in:
parent
6eaf087f71
commit
434e1edcc0
6 changed files with 87 additions and 10 deletions
|
@ -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,
|
from c3nav.mesh.messages import (MESH_BROADCAST_ADDRESS, MESH_NONE_ADDRESS, MESH_ROOT_ADDRESS, OTA_CHUNK_SIZE,
|
||||||
MeshMessage, MeshMessageType)
|
MeshMessage, MeshMessageType)
|
||||||
from c3nav.mesh.models import MeshNode, MeshUplink, NodeMessage, OTAUpdate, OTAUpdateRecipient, OTARecipientStatus
|
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
|
from c3nav.routing.rangelocator import RangeLocator
|
||||||
|
|
||||||
|
|
||||||
|
@ -597,9 +597,16 @@ class MeshUIConsumer(AsyncJsonWebsocketConsumer):
|
||||||
if content.get("subscribe", None) == "ranging":
|
if content.get("subscribe", None) == "ranging":
|
||||||
await self.channel_layer.group_add("mesh_msg_received", self.channel_name)
|
await self.channel_layer.group_add("mesh_msg_received", self.channel_name)
|
||||||
self.msg_received_filter = {"msg_type": MeshMessageType.LOCATE_RANGE_RESULTS.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":
|
if content.get("subscribe", None) == "ota":
|
||||||
await self.channel_layer.group_add("mesh_msg_received", self.channel_name)
|
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:
|
if "send_msg" in content:
|
||||||
msg_to_send = self.scope["session"].pop("mesh_msg_%s" % content["send_msg"], None)
|
msg_to_send = self.scope["session"].pop("mesh_msg_%s" % content["send_msg"], None)
|
||||||
if not msg_to_send:
|
if not msg_to_send:
|
||||||
|
@ -619,6 +626,22 @@ class MeshUIConsumer(AsyncJsonWebsocketConsumer):
|
||||||
**msg_to_send["msg_data"],
|
**msg_to_send["msg_data"],
|
||||||
}).send(sender=self.channel_name)
|
}).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):
|
async def mesh_log_entry(self, data):
|
||||||
await self.send_json(data)
|
await self.send_json(data)
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ class MeshMessageType(EnumSchemaByNameMixin, IntEnum):
|
||||||
OTA_REQUEST_FRAGMENTS = 0x25
|
OTA_REQUEST_FRAGMENTS = 0x25
|
||||||
OTA_SETTING = 0x26
|
OTA_SETTING = 0x26
|
||||||
OTA_APPLY = 0x27
|
OTA_APPLY = 0x27
|
||||||
OTA_ABORT = 0x29
|
OTA_ABORT = 0x28
|
||||||
|
|
||||||
LOCATE_REQUEST_RANGE = 0x30
|
LOCATE_REQUEST_RANGE = 0x30
|
||||||
LOCATE_RANGE_RESULTS = 0x31
|
LOCATE_RANGE_RESULTS = 0x31
|
||||||
|
@ -269,6 +269,25 @@ class ConfigUplinkMessage(MeshMessage, msg_type=MeshMessageType.CONFIG_UPLINK):
|
||||||
port: int = field(metadata={"format": SimpleFormat('H')})
|
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
|
@dataclass
|
||||||
class OTAStatusMessage(MeshMessage, msg_type=MeshMessageType.OTA_STATUS):
|
class OTAStatusMessage(MeshMessage, msg_type=MeshMessageType.OTA_STATUS):
|
||||||
""" report 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')})
|
highest_chunk: int = field(metadata={"format": SimpleFormat('H')})
|
||||||
auto_apply: bool = field(metadata={"format": BoolFormat()})
|
auto_apply: bool = field(metadata={"format": BoolFormat()})
|
||||||
auto_reboot: bool = field(metadata={"format": BoolFormat()})
|
auto_reboot: bool = field(metadata={"format": BoolFormat()})
|
||||||
|
status: OTADeviceStatus = field(metadata={"format": EnumFormat('B')})
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|
|
@ -7,6 +7,8 @@ from functools import cached_property
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
from typing import Any, Mapping, Optional, Self
|
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.contrib.auth import get_user_model
|
||||||
from django.core.validators import RegexValidator
|
from django.core.validators import RegexValidator
|
||||||
from django.db import NotSupportedError, models
|
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 ConfigFirmwareMessage, ConfigHardwareMessage
|
||||||
from c3nav.mesh.messages import MeshMessage as MeshMessage
|
from c3nav.mesh.messages import MeshMessage as MeshMessage
|
||||||
from c3nav.mesh.messages import MeshMessageType
|
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
|
from c3nav.routing.rangelocator import RangeLocator
|
||||||
|
|
||||||
FirmwareLookup = namedtuple('FirmwareLookup', ('sha256_hash', 'chip', 'project_name', 'version', 'idf_version'))
|
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),
|
UniqueConstraint(fields=["node"], condition=Q(status=OTARecipientStatus.RUNNING),
|
||||||
name='only_one_active_ota'),
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,10 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if nodes_xyz %}
|
{% if nodes_xyz %}
|
||||||
const nodes_xyz = JSON.parse(document.getElementById('nodes-xyz').textContent);
|
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 %}
|
{% endif %}
|
||||||
|
|
||||||
function connect() {
|
function connect() {
|
||||||
|
@ -26,7 +29,7 @@ function connect() {
|
||||||
{% elif ranging_form %}
|
{% elif ranging_form %}
|
||||||
ws.send(JSON.stringify({"subscribe": "ranging"}));
|
ws.send(JSON.stringify({"subscribe": "ranging"}));
|
||||||
{% elif update %}
|
{% elif update %}
|
||||||
ws.send(JSON.stringify({"subscribe": "ota-{{ update.pk }}"}));
|
ws.send(JSON.stringify({"subscribe": "ota", "update_id": update_id}));
|
||||||
{% else %}
|
{% else %}
|
||||||
ws.send(JSON.stringify({"subscribe": "log"}));
|
ws.send(JSON.stringify({"subscribe": "log"}));
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -100,6 +103,13 @@ function connect() {
|
||||||
{% endif %}
|
{% endif %}
|
||||||
break;
|
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':
|
case 'mesh.msg_received':
|
||||||
{% if ranging_form %}
|
{% if ranging_form %}
|
||||||
var cell, key, src_node, peer_node;
|
var cell, key, src_node, peer_node;
|
||||||
|
@ -158,10 +168,12 @@ function connect() {
|
||||||
}
|
}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if update %}
|
{% 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;
|
if (!row) break;
|
||||||
row.querySelector('.received_bytes').innerText = data.msg.received_bytes;
|
row.querySelector('.received_bytes').innerText = data.msg.received_bytes;
|
||||||
row.querySelector('progress').value = 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 %}
|
{% endif %}
|
||||||
{% if send_uuid and msg_type == "MESH_ROUTE_REQUEST" %}
|
{% if send_uuid and msg_type == "MESH_ROUTE_REQUEST" %}
|
||||||
if (data.msg.route) {
|
if (data.msg.route) {
|
||||||
|
|
|
@ -18,16 +18,20 @@
|
||||||
<strong>Created:</strong> {{ update.created }}
|
<strong>Created:</strong> {{ update.created }}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<table id="ota-recipients-{{ update.pk }}">
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans 'Node' %}</th>
|
<th>{% trans 'Node' %}</th>
|
||||||
<th>{% trans 'Status' %}</th>
|
<th>{% trans 'Status' %}</th>
|
||||||
<th>{% trans 'Progress' %}</th>
|
<th>{% trans 'Progress' %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for recipient in update.recipients.all %}
|
{% 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>{% 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>
|
<td>
|
||||||
<span class="received_bytes">??</span> of {{ update.build.binary.size }} bytes
|
<span class="received_bytes">??</span> of {{ update.build.binary.size }} bytes
|
||||||
<br>
|
<br>
|
||||||
|
|
|
@ -6,6 +6,7 @@ def get_mesh_uplink_group(address):
|
||||||
|
|
||||||
|
|
||||||
MESH_ALL_UPLINKS_GROUP = "mesh_uplink_all"
|
MESH_ALL_UPLINKS_GROUP = "mesh_uplink_all"
|
||||||
|
MESH_ALL_OTA_GROUP = "mesh_ota_all"
|
||||||
UPLINK_PING = 5
|
UPLINK_PING = 5
|
||||||
UPLINK_TIMEOUT = UPLINK_PING+5
|
UPLINK_TIMEOUT = UPLINK_PING+5
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue