sending updates

This commit is contained in:
Laura Klünder 2023-10-05 04:05:29 +02:00
parent 40c4e98816
commit c724833542
9 changed files with 154 additions and 20 deletions

View file

@ -1,25 +1,33 @@
{% if node_names %}
{{ node_names|json_script:"node-names" }}
{% endif %}
{% if send_uuid %}
{{ send_uuid|json_script:"send-uuid" }}
{% endif %}
<script type="text/javascript">
{% if node_names %}
const node_names = JSON.parse(document.getElementById('node-names').textContent);
{% endif %}
function connect() {
console.log('reconnecting websocket...');
var ws = new WebSocket((window.location.protocol=="https:"?"wss:":"ws:")+window.location.host+"/mesh/ui/ws", []);
ws.onopen = (event) => {
console.log('websocket connected.');
ws.send(JSON.stringify({"subscribe": "log"}));
{% if send_uuid %}
ws.send(JSON.stringify({"send_msg": JSON.parse(document.getElementById('send-uuid').textContent)}));
{% else %}
ws.send(JSON.stringify({"subscribe": "log"}));
{% endif %}
};
ws.onclose = (event) => {
window.setTimeout(connect, 500);
}
ws.onmessage = (event) => {
var data = JSON.parse(event.data);
var data = JSON.parse(event.data), line, text;
switch(data.type) {
case 'mesh.log_entry':
var line = document.createElement("tr"), cell, link_tag;
line = document.createElement("tr"), cell, link_tag;
cell = document.createElement("td");
cell.innerText = data.timestamp;
@ -54,6 +62,16 @@ function connect() {
line.appendChild(cell);
document.querySelector("tbody").prepend(line);
break
case 'mesh.msg_sent':
{% if send_uuid %}
line = document.createElement("p");
line.innerText = "sent via uplink ["+data.channel+"] "+data.uplink;
if (node_names[data.uplink]) {
line.innerText += " ("+node_names[data.uplink]+")";
}
document.getElementById("sending-status-"+data.recipient).appendChild(line);
{% endif %}
break;
}

View file

@ -17,6 +17,7 @@
<form method="POST" style="max-width:400px;">
{% csrf_token %}
{{ form }}
<noscript><input type="hidden" name="noscript" value"1"></noscript>
<button type="submit">{% trans 'Send' %}</button>
</form>

View file

@ -0,0 +1,26 @@
{% extends 'control/base.html' %}
{% load i18n %}
{% block heading %}
{% blocktrans trimmed with msg_type=msg_type %}
Sending {{ msg_type }} message
{% endblocktrans %}
{% endblock %}
{% block subcontent %}
<p><a class="button" href="{{ success_url }}">Go back</a></p>
<table>
<tr>
<th>Recipient</th>
<th>Status</th>
</tr>
{% for address, name in recipients %}
<tr>
<td>{{ address }}{% if name %} ({{ name }}){% endif %}</td>
<td id="sending-status-{{ address }}"></td>
</tr>
{% endfor %}
</table>
{% include "control/fragment_mesh_websocket.html" %}
{% endblock %}

View file

@ -41,5 +41,4 @@
</tr>
{% endfor %}
</table>
{% include "control/fragment_mesh_websocket.html" %}
{% endblock %}

View file

@ -1,7 +1,7 @@
from django.urls import path
from c3nav.control.views.mesh import MeshNodeListView, MeshMessageListView, MeshNodeDetailView, MeshMessageSendView, \
MeshNodeEditView, MeshLogView
MeshNodeEditView, MeshLogView, MeshMessageSendingView
from c3nav.control.views.mapupdates import map_updates
from c3nav.control.views.announcements import announcement_list, announcement_detail
from c3nav.control.views.access import grant_access, grant_access_qr
@ -21,6 +21,8 @@ urlpatterns = [
path('mesh/messages/', MeshMessageListView.as_view(), name='control.mesh_messages'),
path('mesh/<str:pk>/', MeshNodeDetailView.as_view(), name='control.mesh_node.detail'),
path('mesh/<str:pk>/edit/', MeshNodeEditView.as_view(), name='control.mesh_node.edit'),
path('mesh/<str:recipient>/message/<str:msg_type>/', MeshMessageSendView.as_view(), name='control.mesh_message.send'),
path('mesh/message/sending/<uuid:uuid>/', MeshMessageSendingView.as_view(), name='control.mesh_message.sending'),
path('mesh/message/<str:recipient>/<str:msg_type>/', MeshMessageSendView.as_view(), name='control.mesh_message.send'),
path('mesh/message/<str:msg_type>/', MeshMessageSendView.as_view(), name='control.mesh_message.send'),
path('', ControlPanelIndexView.as_view(), name='control.index'),
]

View file

@ -1,7 +1,10 @@
from uuid import uuid4
from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin
from django.db.models import Max
from django.http import Http404
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views.generic import ListView, DetailView, FormView, UpdateView, TemplateView
@ -113,9 +116,39 @@ class MeshMessageSendView(ControlPanelMixin, FormView):
return self.request.path
def form_valid(self, form):
form.send()
messages.success(self.request, _('Message sent successfully'))
return super().form_valid(form)
if 'noscript' in self.request.POST:
form.send()
messages.success(self.request, _('Message sent successfully(?)'))
super().form_valid(form)
uuid = uuid4()
self.request.session["mesh_msg_%s" % uuid] = {
"success_url": self.get_success_url(),
"recipients": form.get_recipients(),
"msg_data": form.get_msg_data(),
}
return redirect(reverse('control.mesh_message.sending', kwargs={'uuid': uuid}))
class MeshMessageSendingView(ControlPanelMixin, TemplateView):
template_name = "control/mesh_message_sending.html"
def get_context_data(self, uuid):
try:
data = self.request.session["mesh_msg_%s" % uuid]
except KeyError:
raise Http404
node_names = {
node.address: node.name for node in MeshNode.objects.all()
}
return {
**super().get_context_data(),
"node_names": node_names,
"send_uuid": uuid,
**data,
"recipients": [(address, node_names[address]) for address in data["recipients"]],
"msg_type": MeshMessageType(data["msg_data"]["msg_id"]).name,
}
class MeshLogView(ControlPanelMixin, TemplateView):
template_name = "control/mesh_logs.html"

View file

@ -28,10 +28,19 @@ class MeshConsumer(WebsocketConsumer):
# remove all other destinations
self.remove_dst_nodes(self.dst_nodes)
def send_msg(self, msg):
def send_msg(self, msg, sender=None):
# print("sending", msg)
# self.log_text(msg.dst, "sending %s" % msg)
self.send(bytes_data=msg.encode())
async_to_sync(self.channel_layer.group_send)("mesh_msg_sent", {
"type": "mesh.msg_sent",
"timestamp": timezone.now().strftime("%d.%m.%y %H:%M:%S.%f"),
"channel": self.channel_name,
"sender": sender,
"uplink": self.uplink_node.address if self.uplink_node else None,
"recipient": msg.dst,
#"msg": msg.tojson(), # not doing this part for privacy reasons
})
def receive(self, text_data=None, bytes_data=None):
if bytes_data is None:
@ -103,15 +112,23 @@ class MeshConsumer(WebsocketConsumer):
self.remove_dst_nodes((data["address"], ))
def mesh_send(self, data):
self.send_msg(MeshMessage.fromjson(data["msg"]))
print("mesh_send", data)
self.send_msg(MeshMessage.fromjson(data["msg"]), data["sender"])
def log_received_message(self, src_node: MeshNode, msg: messages.MeshMessage):
# self.log_text(msg.src, "received %s" % msg)
as_json = msg.tojson()
async_to_sync(self.channel_layer.group_send)("mesh_msg_received", {
"type": "mesh.msg_received",
"timestamp": timezone.now().strftime("%d.%m.%y %H:%M:%S.%f"),
"channel": self.channel_name,
"uplink": self.uplink_node.address if self.uplink_node else None,
"msg": as_json,
})
NodeMessage.objects.create(
uplink_node=self.uplink_node,
src_node=src_node,
message_type=msg.msg_id,
data=msg.tojson()
data=as_json,
)
def log_text(self, address, text):
@ -196,13 +213,47 @@ class MeshUIConsumer(JsonWebsocketConsumer):
def connect(self):
# todo: auth
self.accept()
self.msg_sent_filter = {}
self.msg_received_filter = {}
def receive_json(self, content, **kwargs):
if content.get("subscribe", None) == "log":
async_to_sync(self.channel_layer.group_add)("mesh_log", self.channel_name)
if content.get("subscribe", None) == "msg_sent":
async_to_sync(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":
async_to_sync(self.channel_layer.group_add)("mesh_msg_sent", self.channel_name)
self.msg_received_filter = dict(content.get("filter", {}))
if "send_msg" in content:
msg_to_send = self.scope["session"].pop("mesh_msg_%s" % content["send_msg"], None)
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}
for recipient in msg_to_send["recipients"]:
print('send to', recipient)
MeshMessage.fromjson({
'dst': recipient,
**msg_to_send["msg_data"],
}).send(sender=self.channel_name)
def mesh_log_entry(self, data):
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:
return
else:
if data.get(key, None) != 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_log", self.channel_name)
async_to_sync(self.channel_layer.group_discard)("mesh_msg_sent", self.channel_name)
async_to_sync(self.channel_layer.group_discard)("mesh_msg_received", self.channel_name)

View file

@ -63,17 +63,22 @@ class MeshMessageForm(forms.Form):
msg_data.pop('recipients', None)
return msg_data
def send(self):
def get_msg_data(self):
if not self.is_valid():
raise Exception('nope')
msg_data = {
return {
'msg_id': self.msg_type,
'src': ROOT_ADDRESS,
**self.get_cleaned_msg_data(),
}
recipients = [self.recipient] if self.recipient else self.cleaned_data['recipients']
def get_recipients(self):
return [self.recipient] if self.recipient else self.cleaned_data['recipients']
def send(self):
msg_data = self.get_msg_data()
recipients = self.get_recipients()
for recipient in recipients:
print('sending to ', recipient)
MeshMessage.fromjson({

View file

@ -16,7 +16,6 @@ PARENT_ADDRESS = '00:00:00:ff:ff:ff'
BROADCAST_ADDRESS = 'ff:ff:ff:ff:ff:ff'
NO_LAYER = 0xFF
@unique
class MeshMessageType(IntEnum):
NOOP = 0x00
@ -94,7 +93,6 @@ class MeshMessage:
kwargs = data.copy()
klass = cls.msg_types[kwargs.pop('msg_id')]
kwargs = klass.upgrade_json(kwargs)
names = set(field.name for field in fields(klass))
for field_ in fields(klass):
if is_dataclass(field_.type):
kwargs[field_.name] = field_.type.fromjson(kwargs[field_.name])
@ -104,9 +102,10 @@ class MeshMessage:
def upgrade_json(cls, data):
return data
def send(self):
def send(self, sender=None):
async_to_sync(channels.layers.get_channel_layer().group_send)(get_mesh_comm_group(self.dst), {
"type": "mesh.send",
"sender": sender,
"msg": self.tojson()
})