From 03430309801267ca39d047c23ee73166a3495df0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laura=20Kl=C3=BCnder?=
Date: Thu, 5 Oct 2023 05:02:01 +0200
Subject: [PATCH] mesh route results
---
.../control/fragment_mesh_websocket.html | 56 +++++++++++++++++--
.../control/mesh_message_sending.html | 51 +++++++++++++----
src/c3nav/control/views/mesh.py | 11 ++--
src/c3nav/mesh/consumers.py | 36 +++++++++---
src/c3nav/mesh/forms.py | 47 +++++++++++-----
src/c3nav/mesh/messages.py | 6 +-
src/c3nav/mesh/utils.py | 10 ++++
7 files changed, 170 insertions(+), 47 deletions(-)
diff --git a/src/c3nav/control/templates/control/fragment_mesh_websocket.html b/src/c3nav/control/templates/control/fragment_mesh_websocket.html
index 0cd36683..6d266887 100644
--- a/src/c3nav/control/templates/control/fragment_mesh_websocket.html
+++ b/src/c3nav/control/templates/control/fragment_mesh_websocket.html
@@ -24,10 +24,10 @@ function connect() {
window.setTimeout(connect, 500);
}
ws.onmessage = (event) => {
- var data = JSON.parse(event.data), line, text;
+ var data = JSON.parse(event.data), line, text, cell, link_tag;
switch(data.type) {
case 'mesh.log_entry':
- line = document.createElement("tr"), cell, link_tag;
+ line = document.createElement("tr");
cell = document.createElement("td");
cell.innerText = data.timestamp;
@@ -37,7 +37,7 @@ function connect() {
cell.innerText = data.channel;
if (data.uplink) {
cell.append(document.createElement("br"));
- link_tag = document.createElement("a")
+ link_tag = document.createElement("a");
link_tag.href = "/control/mesh/" + data.uplink;
link_tag.innerText = data.uplink;
if (node_names[data.uplink]) {
@@ -48,7 +48,7 @@ function connect() {
line.appendChild(cell);
cell = document.createElement("td");
- link_tag = document.createElement("a")
+ link_tag = document.createElement("a");
link_tag.href = "/control/mesh/" + data.node;
link_tag.innerText = data.node;
if (node_names[data.node]) {
@@ -73,6 +73,54 @@ function connect() {
document.getElementById("sending-status-"+data.recipient).appendChild(line);
{% endif %}
break;
+
+ case 'mesh.msg_received':
+ {% if send_uuid and msg_type == "MESH_ROUTE_REQUEST" %}
+ if (data.msg.route) {
+ link_tag = document.createElement("a");
+ link_tag.href = "/control/mesh/" + data.msg.src;
+ link_tag.innerText = data.msg.src;
+ if (node_names[data.msg.src]) {
+ link_tag.innerText += " ("+node_names[data.msg.src]+")";
+ }
+ if (data.msg.route === "00:00:00:00:00:00") {
+ line = document.createElement("li");
+ line.appendChild(link_tag);
+ document.getElementById("no-routes").appendChild(line);
+ } else {
+ line = document.createElement("tr");
+
+ cell = document.createElement("td");
+ cell.appendChild(link_tag);
+ line.appendChild(cell);
+
+ cell = document.createElement("td");
+ link_tag = document.createElement("a");
+ link_tag.href = "/control/mesh/" + data.msg.route;
+ link_tag.innerText = data.msg.route;
+ if (node_names[data.msg.route]) {
+ link_tag.innerText += " ("+node_names[data.msg.route]+")";
+ }
+ cell.append(link_tag);
+ line.appendChild(cell);
+
+ document.getElementById("route-responses").appendChild(line);
+ }
+ } else {
+ for (var i=0;iGo back
-
-
- Recipient |
- Status |
-
- {% for address, name in recipients %}
-
- {{ address }}{% if name %} ({{ name }}){% endif %} |
- |
-
- {% endfor %}
-
+
+
+
Sending progress
+
+
+ Recipient |
+ Status |
+
+ {% for address, name in recipients %}
+
+ {{ address }}{% if name %} ({{ name }}){% endif %} |
+ |
+
+ {% endfor %}
+
+
+ {% if msg_type == "MESH_ROUTE_REQUEST" %}
+
+
Routes
+
+
+
+ {% trans 'Node' %} |
+ {% trans 'Route' %} |
+
+
+
+
+
+
+
+ {% endif %}
+
{% include "control/fragment_mesh_websocket.html" %}
{% endblock %}
diff --git a/src/c3nav/control/views/mesh.py b/src/c3nav/control/views/mesh.py
index c548bc54..b72e6a36 100644
--- a/src/c3nav/control/views/mesh.py
+++ b/src/c3nav/control/views/mesh.py
@@ -14,6 +14,7 @@ from c3nav.control.views.base import ControlPanelMixin
from c3nav.mesh.forms import MeshMessageForm, MeshNodeForm
from c3nav.mesh.messages import MeshMessageType
from c3nav.mesh.models import MeshNode, NodeMessage
+from c3nav.mesh.utils import get_node_names
class MeshNodeListView(ControlPanelMixin, ListView):
@@ -137,9 +138,7 @@ class MeshMessageSendingView(ControlPanelMixin, TemplateView):
data = self.request.session["mesh_msg_%s" % uuid]
except KeyError:
raise Http404
- node_names = {
- node.address: node.name for node in MeshNode.objects.all()
- }
+ node_names = get_node_names()
return {
**super().get_context_data(),
"node_names": node_names,
@@ -156,7 +155,5 @@ class MeshLogView(ControlPanelMixin, TemplateView):
def get_context_data(self, **kwargs):
return {
**super().get_context_data(),
- "node_names": {
- node.address: node.name for node in MeshNode.objects.all()
- }
- }
\ No newline at end of file
+ "node_names": get_node_names(),
+ }
diff --git a/src/c3nav/mesh/consumers.py b/src/c3nav/mesh/consumers.py
index 0ce46b6c..03deb4e4 100644
--- a/src/c3nav/mesh/consumers.py
+++ b/src/c3nav/mesh/consumers.py
@@ -6,7 +6,7 @@ from django.utils import timezone
from c3nav.mesh.utils import get_mesh_comm_group
from c3nav.mesh import messages
-from c3nav.mesh.messages import MeshMessage, BROADCAST_ADDRESS
+from c3nav.mesh.messages import MeshMessage, MESH_BROADCAST_ADDRESS, MeshMessageType
from c3nav.mesh.models import MeshNode, NodeMessage
@@ -23,7 +23,9 @@ class MeshConsumer(WebsocketConsumer):
self.log_text(self.uplink_node, "mesh websocket disconnected")
if self.uplink_node is not None:
# leave broadcast group
- async_to_sync(self.channel_layer.group_add)(get_mesh_comm_group(BROADCAST_ADDRESS), self.channel_name)
+ async_to_sync(self.channel_layer.group_discard)(
+ get_mesh_comm_group(MESH_BROADCAST_ADDRESS), self.channel_name
+ )
# remove all other destinations
self.remove_dst_nodes(self.dst_nodes)
@@ -51,7 +53,7 @@ class MeshConsumer(WebsocketConsumer):
traceback.print_exc()
return
- if msg.dst != messages.ROOT_ADDRESS and msg.dst != messages.PARENT_ADDRESS:
+ if msg.dst != messages.MESH_ROOT_ADDRESS and msg.dst != messages.MESH_PARENT_ADDRESS:
print('Received message for forwarding:', msg)
# todo: this message isn't for us, forward it
return
@@ -67,13 +69,15 @@ class MeshConsumer(WebsocketConsumer):
# inform signed in uplink node about its layer
self.send_msg(messages.MeshLayerAnnounceMessage(
- src=messages.ROOT_ADDRESS,
+ src=messages.MESH_ROOT_ADDRESS,
dst=msg.src,
layer=messages.NO_LAYER
))
# add signed in uplink node to broadcast group
- async_to_sync(self.channel_layer.group_add)('mesh_broadcast', self.channel_name)
+ async_to_sync(self.channel_layer.group_add)(
+ get_mesh_comm_group(MESH_BROADCAST_ADDRESS), self.channel_name
+ )
# kick out other consumers talking to the same uplink
async_to_sync(self.channel_layer.group_send)(get_mesh_comm_group(msg.src), {
@@ -179,7 +183,7 @@ class MeshConsumer(WebsocketConsumer):
# tell the node to dump its current information
self.send_msg(
messages.ConfigDumpMessage(
- src=messages.ROOT_ADDRESS,
+ src=messages.MESH_ROOT_ADDRESS,
dst=address,
)
)
@@ -230,10 +234,15 @@ class MeshUIConsumer(JsonWebsocketConsumer):
if not msg_to_send:
return
self.scope["session"].save()
+
async_to_sync(self.channel_layer.group_add)("mesh_msg_sent", self.channel_name)
self.msg_sent_filter = {"sender": self.channel_name}
+
+ if msg_to_send["msg_data"]["msg_id"] == MeshMessageType.MESH_ROUTE_REQUEST:
+ async_to_sync(self.channel_layer.group_add)("mesh_msg_received", self.channel_name)
+ self.msg_received_filter = {"request_id": msg_to_send["msg_data"]["request_id"]}
+
for recipient in msg_to_send["recipients"]:
- print('send to', recipient)
MeshMessage.fromjson({
'dst': recipient,
**msg_to_send["msg_data"],
@@ -243,7 +252,6 @@ class MeshUIConsumer(JsonWebsocketConsumer):
self.send_json(data)
def mesh_msg_sent(self, data):
- print('got data', data)
for key, value in self.msg_sent_filter.items():
if isinstance(value, list):
if data.get(key, None) not in value:
@@ -253,6 +261,18 @@ class MeshUIConsumer(JsonWebsocketConsumer):
return
self.send_json(data)
+ def mesh_msg_received(self, data):
+ print('got received', data)
+ for key, filter_value in self.msg_received_filter.items():
+ value = data.get(key, data["msg"].get(key, None))
+ if isinstance(filter_value, list):
+ if value not in filter_value:
+ return
+ else:
+ if value != filter_value:
+ return
+ self.send_json(data)
+
def disconnect(self, code):
async_to_sync(self.channel_layer.group_discard)("mesh_log", self.channel_name)
async_to_sync(self.channel_layer.group_discard)("mesh_msg_sent", self.channel_name)
diff --git a/src/c3nav/mesh/forms.py b/src/c3nav/mesh/forms.py
index 23f0d1e9..64c6ee2c 100644
--- a/src/c3nav/mesh/forms.py
+++ b/src/c3nav/mesh/forms.py
@@ -1,4 +1,6 @@
+import time
from dataclasses import fields as dataclass_fields
+from functools import cached_property
from django import forms
from django.core.exceptions import ValidationError
@@ -6,7 +8,7 @@ from django.http import Http404
from django.utils.translation import gettext_lazy as _
from c3nav.mesh.dataformats import LedConfig
-from c3nav.mesh.messages import MeshMessageType, MeshMessage, ROOT_ADDRESS
+from c3nav.mesh.messages import MeshMessageType, MeshMessage, MESH_ROOT_ADDRESS, MESH_BROADCAST_ADDRESS
from c3nav.mesh.models import MeshNode
@@ -25,23 +27,26 @@ class MeshMessageForm(forms.Form):
super().__init__(*args, initial=initial, **kwargs)
recipient_root_choices = {
- 'ff:ff:ff:ff:ff:ff': _('broadcast')
+ MESH_BROADCAST_ADDRESS: _('broadcast')
}
- recipient_node_choices = {
+ node_choices = {
node.address: str(node) for node in MeshNode.objects.all()
}
- self.recipient_choices = {
+ self.node_choices_flat = {
**recipient_root_choices,
- **recipient_node_choices,
+ **node_choices,
}
+ self.node_choices = tuple(node_choices.items())
+ self.node_choices_with_broadcast = (
+ *recipient_root_choices.items(),
+ (_('nodes'), self.node_choices),
+ )
+
if self.recipient is None:
- self.fields['recipients'].choices = (
- *recipient_root_choices.items(),
- (_('nodes'), tuple(recipient_node_choices.items()))
- )
+ self.fields['recipients'].choices = self.node_choices_with_broadcast
else:
- if self.recipient not in self.recipient_choices:
- raise Http404
+ if self.recipient not in self.node_choices_flat:
+ raise Http404('unknown recipient')
self.fields.pop('recipients')
# noinspection PyMethodOverriding
@@ -56,7 +61,7 @@ class MeshMessageForm(forms.Form):
return cls.msg_types[msg_type]
def get_recipient_display(self):
- return self.recipient_choices[self.recipient]
+ return self.node_choices_flat[self.recipient]
def get_cleaned_msg_data(self):
msg_data = self.cleaned_data.copy()
@@ -69,7 +74,7 @@ class MeshMessageForm(forms.Form):
return {
'msg_id': self.msg_type,
- 'src': ROOT_ADDRESS,
+ 'src': MESH_ROOT_ADDRESS,
**self.get_cleaned_msg_data(),
}
@@ -87,6 +92,22 @@ class MeshMessageForm(forms.Form):
}).send()
+class MeshRouteRequestForm(MeshMessageForm):
+ msg_type = MeshMessageType.MESH_ROUTE_REQUEST
+
+ address = forms.ChoiceField(choices=())
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.fields["address"].choices = self.node_choices
+
+ def get_msg_data(self):
+ return {
+ **super().get_msg_data(),
+ "request_id": int(time.time()*100000) % 2**32,
+ }
+
+
class ConfigUplinkMessageForm(MeshMessageForm):
msg_type = MeshMessageType.CONFIG_UPLINK
diff --git a/src/c3nav/mesh/messages.py b/src/c3nav/mesh/messages.py
index e0427df9..f33c6245 100644
--- a/src/c3nav/mesh/messages.py
+++ b/src/c3nav/mesh/messages.py
@@ -11,9 +11,9 @@ from c3nav.mesh.utils import get_mesh_comm_group, indent_c
from c3nav.mesh.dataformats import (BoolFormat, FixedStrFormat, HexFormat, LedConfig, LedConfigFormat,
MacAddressesListFormat, MacAddressFormat, SimpleFormat, VarStrFormat)
-ROOT_ADDRESS = '00:00:00:00:00:00'
-PARENT_ADDRESS = '00:00:00:ff:ff:ff'
-BROADCAST_ADDRESS = 'ff:ff:ff:ff:ff:ff'
+MESH_ROOT_ADDRESS = '00:00:00:00:00:00'
+MESH_PARENT_ADDRESS = '00:00:00:ff:ff:ff'
+MESH_BROADCAST_ADDRESS = 'ff:ff:ff:ff:ff:ff'
NO_LAYER = 0xFF
@unique
diff --git a/src/c3nav/mesh/utils.py b/src/c3nav/mesh/utils.py
index 1408f6a5..393561ed 100644
--- a/src/c3nav/mesh/utils.py
+++ b/src/c3nav/mesh/utils.py
@@ -4,3 +4,13 @@ def get_mesh_comm_group(address):
def indent_c(code):
return " "+code.replace("\n", "\n ")
+
+
+def get_node_names():
+ from c3nav.mesh.models import MeshNode
+ return {
+ **{node.address: node.name for node in MeshNode.objects.all()},
+ 'ff:ff:ff:ff:ff:ff': "broadcast",
+ '00:00:00:ff:ff:ff': "direct parent",
+ '00:00:00:00:00:00': "root",
+ }