add ranging feature in mesh control

This commit is contained in:
Laura Klünder 2023-11-25 14:42:29 +01:00
parent f07b06ec60
commit 5cf839577a
7 changed files with 91 additions and 10 deletions

View file

@ -348,19 +348,23 @@ class MeshUIConsumer(AsyncJsonWebsocketConsumer):
self.msg_received_filter = {}
async def connect(self):
if not self.scope["user_permisions"].mesh_control:
if not self.scope["user_permissions"].mesh_control:
raise DenyConnection
await self.accept()
async def receive_json(self, content, **kwargs):
if content.get("subscribe", None) == "log":
await self.channel_layer.group_add("mesh_log", self.channel_name)
if content.get("subscribe", None) == "msg_sent":
await self.channel_layer.group_add("mesh_msg_sent", self.channel_name)
self.msg_sent_filter = dict(content.get("filter", {}))
if content.get("subscribe", None) == "msg_received":
await self.channel_layer.group_add("mesh_msg_sent", self.channel_name)
self.msg_received_filter = dict(content.get("filter", {}))
# disabled because security
#if content.get("subscribe", None) == "msg_sent":
# await self.channel_layer.group_add("mesh_msg_sent", self.channel_name)
# self.msg_sent_filter = dict(content.get("filter", {}))
#if content.get("subscribe", None) == "msg_received":
# await self.channel_layer.group_add("mesh_msg_sent", self.channel_name)
# self.msg_received_filter = dict(content.get("filter", {}))
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}
if "send_msg" in content:
msg_to_send = self.scope["session"].pop("mesh_msg_%s" % content["send_msg"], None)
if not msg_to_send:

View file

@ -265,3 +265,17 @@ class MeshNodeForm(forms.ModelForm):
class Meta:
model = MeshNode
fields = ["name"]
class RangingForm(forms.Form):
msg_types = {}
range_from = forms.MultipleChoiceField(choices=(), required=True)
range_to = forms.MultipleChoiceField(choices=(), required=True)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
node_choices = tuple((node.address, str(node)) for node in MeshNode.objects.all())
self.fields['range_from'].choices = node_choices
self.fields['range_to'].choices = node_choices

View file

@ -18,6 +18,7 @@
<a href="{% url 'mesh.nodes' %}">Nodes</a> &middot;
<a href="{% url 'mesh.messages' %}">Messages</a> &middot;
<a href="{% url 'mesh.firmwares' %}">Firmwares</a> &middot;
<a href="{% url 'mesh.ranging' %}">Ranging</a> &middot;
<a href="{% url 'mesh.logs' %}">Live logs</a> &middot;
</p>
</nav>

View file

@ -16,6 +16,8 @@ function connect() {
console.log('websocket connected.');
{% if send_uuid %}
ws.send(JSON.stringify({"send_msg": JSON.parse(document.getElementById('send-uuid').textContent)}));
{% elif ranging_form %}
ws.send(JSON.stringify({"subscribe": "ranging"}));
{% else %}
ws.send(JSON.stringify({"subscribe": "log"}));
{% endif %}
@ -90,6 +92,21 @@ function connect() {
break;
case 'mesh.msg_received':
{% if ranging_form %}
var cell;
for (cell of document.querySelectorAll(`[data-range-from="${data.msg.src}"]`)) {
cell.innerText = "-";
}
for (var i=0;i<data.msg.ranges.length;i++) {
cell = document.querySelector(
`[data-range-from="${data.msg.src}"][data-range-to="${data.msg.ranges[i].peer}"]`
);
if (cell) cell.innerText = (
((data.msg.ranges[i].distance == 0xff) ? "invalid" : `${data.msg.ranges[i].distance}cm`) +
` (${data.msg.ranges[i].rssi}dBm)`
);
}
{% endif %}
{% if send_uuid and msg_type == "MESH_ROUTE_REQUEST" %}
if (data.msg.route) {
line = document.createElement('tr');

View file

@ -0,0 +1,33 @@
{% extends 'mesh/base.html' %}
{% load i18n mesh_node %}
{% block heading %}{% trans 'Mesh ranging' %}{% endblock %}
{% block subcontent %}
{% if not ranging_form.is_valid %}
<form>
{{ ranging_form }}
<button type="submit">{% trans 'Show ranging' %}</button>
</form>
{% else %}
<table>
<tr>
<th></th>
{% for range_from in ranging_form.cleaned_data.range_from %}
<th>{% mesh_node range_from %}</th>
{% endfor %}
</tr>
{% for range_to in ranging_form.cleaned_data.range_to %}
<tr>
<td>{% mesh_node range_to %}</td>
{% for range_from in ranging_form.cleaned_data.range_from %}
<td data-range-to="{{ range_to }}" data-range-from="{{ range_from }}"></td>
{% endfor %}
</tr>
{% endfor %}
</table>
{% endif %}
{% include "mesh/fragment_mesh_websocket.html" %}
{% endblock %}

View file

@ -4,7 +4,7 @@ from c3nav.mesh.consumers import MeshConsumer, MeshUIConsumer
from c3nav.mesh.views.firmware import (FirmwareBuildDetailView, FirmwareDetailView, FirmwaresCurrentListView,
FirmwaresListView)
from c3nav.mesh.views.messages import MeshMessageListView, MeshMessageSendingView, MeshMessageSendView
from c3nav.mesh.views.misc import MeshLogView
from c3nav.mesh.views.misc import MeshLogView, MeshRangingView
from c3nav.mesh.views.nodes import NodeDetailView, NodeEditView, NodeListView
urlpatterns = [
@ -15,11 +15,12 @@ urlpatterns = [
path('firmwares/current/', FirmwaresCurrentListView.as_view(), name='mesh.firmwares.current'),
path('firmwares/<int:pk>/', FirmwareDetailView.as_view(), name='mesh.firmwares.detail'),
path('firmwares/builds/<int:pk>/', FirmwareBuildDetailView.as_view(), name='mesh.firmwares.build.detail'),
path('<str:pk>/', NodeDetailView.as_view(), name='mesh.node.detail'),
path('<str:pk>/edit/', NodeEditView.as_view(), name='mesh.node.edit'),
path('nodes/<str:pk>/', NodeDetailView.as_view(), name='mesh.node.detail'),
path('nodes/<str:pk>/edit/', NodeEditView.as_view(), name='mesh.node.edit'),
path('message/sending/<uuid:uuid>/', MeshMessageSendingView.as_view(), name='mesh.sending'),
path('message/<str:recipient>/<str:msg_type>/', MeshMessageSendView.as_view(), name='mesh.send'),
path('message/<str:msg_type>/', MeshMessageSendView.as_view(), name='mesh.send'),
path('ranging/', MeshRangingView.as_view(), name='mesh.ranging'),
]
websocket_urlpatterns = [

View file

@ -1,5 +1,6 @@
from django.views.generic import TemplateView
from c3nav.mesh.forms import RangingForm
from c3nav.mesh.utils import get_node_names
from c3nav.mesh.views.base import MeshControlMixin
@ -12,3 +13,13 @@ class MeshLogView(MeshControlMixin, TemplateView):
**super().get_context_data(),
"node_names": get_node_names(),
}
class MeshRangingView(TemplateView):
template_name = "mesh/mesh_ranging.html"
def get_context_data(self, **kwargs):
return {
"ranging_form": RangingForm(self.request.GET or None),
"node_names": get_node_names(),
}