more mesh node stuff
This commit is contained in:
parent
4d3f54bbe8
commit
473e60aed0
5 changed files with 100 additions and 28 deletions
|
@ -7,22 +7,14 @@
|
|||
<table>
|
||||
<tr>
|
||||
<th>{% trans 'Node' %}</th>
|
||||
<th>{% trans 'Status' %}</th>
|
||||
<th>{% trans 'Chip' %}</th>
|
||||
<th>{% trans 'Firmware' %}</th>
|
||||
<th>{% trans 'Last msg' %}</th>
|
||||
<th>{% trans 'Parent' %}</th>
|
||||
<th>{% trans 'Route' %}</th>
|
||||
<th>{% trans 'Last signin' %}</th>
|
||||
<th>{% trans 'Uplink' %}</th>
|
||||
</tr>
|
||||
{% for node in nodes %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if node.route %}
|
||||
<span style="color: green;">{% trans "online" %}</span>
|
||||
{% else %}
|
||||
<span style="color: red;">{% trans "offline" %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ node }}</td>
|
||||
<td>
|
||||
{{ node.last_messages.CONFIG_FIRMWARE.parsed.get_chip_display }}
|
||||
|
@ -37,9 +29,12 @@
|
|||
{{ timesince }} ago
|
||||
{% endblocktrans %}
|
||||
</td>
|
||||
<td>{{ node.parent }}</td>
|
||||
<td>{{ node.route }}</td>
|
||||
<td>{{ node.last_messages.CONFIG_FIRMWARE.data }}</td>
|
||||
<td>
|
||||
{% blocktrans trimmed with timesince=node.last_signin|timesince %}
|
||||
{{ timesince }} ago
|
||||
{% endblocktrans %}
|
||||
</td>
|
||||
<td>{{ node.uplink }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
|
|
@ -2,6 +2,7 @@ import traceback
|
|||
|
||||
from asgiref.sync import async_to_sync
|
||||
from channels.generic.websocket import WebsocketConsumer
|
||||
from django.utils import timezone
|
||||
|
||||
from c3nav.mesh import messages
|
||||
from c3nav.mesh.models import MeshNode, NodeMessage
|
||||
|
@ -19,9 +20,11 @@ class MeshConsumer(WebsocketConsumer):
|
|||
def disconnect(self, close_code):
|
||||
print('disconnected!')
|
||||
if self.uplink_node is not None:
|
||||
self.remove_route(self.uplink_node)
|
||||
self.channel_layer.group_discard('route_%s' % self.node.address.replace(':', ''), self.channel_name)
|
||||
self.channel_layer.group_discard('route_broadcast', self.channel_name)
|
||||
# leave broadcast group
|
||||
async_to_sync(self.channel_layer.group_add)('mesh_broadcast', self.channel_name)
|
||||
|
||||
# remove all other destinations
|
||||
self.remove_dst_nodes(self.dst_nodes)
|
||||
|
||||
def send_msg(self, msg):
|
||||
print('Sending message:', msg)
|
||||
|
@ -57,9 +60,15 @@ class MeshConsumer(WebsocketConsumer):
|
|||
layer=messages.NO_LAYER
|
||||
))
|
||||
|
||||
# add signed in uplink node to broadcast route
|
||||
# add signed in uplink node to broadcast group
|
||||
async_to_sync(self.channel_layer.group_add)('mesh_broadcast', self.channel_name)
|
||||
|
||||
# kick out other consumers talking to the same uplink
|
||||
async_to_sync(self.channel_layer.group_send)(self.group_name_for_node(msg.src), {
|
||||
"type": "mesh.uplink_consumer",
|
||||
"name": self.channel_name,
|
||||
})
|
||||
|
||||
# add this node as a destination that this uplink handles (duh)
|
||||
self.add_dst_nodes((src_node.address, ))
|
||||
|
||||
|
@ -72,12 +81,17 @@ class MeshConsumer(WebsocketConsumer):
|
|||
|
||||
self.log_received_message(src_node, msg)
|
||||
|
||||
def uplink_change(self, data):
|
||||
def mesh_uplink_consumer(self, data):
|
||||
# message handler: if we are not the given uplink, leave this group
|
||||
if data["name"] != self.channel_name:
|
||||
print('shutting down since we have been replaced')
|
||||
self.close()
|
||||
|
||||
def mesh_dst_node_uplink(self, data):
|
||||
# message handler: if we are not the given uplink, leave this group
|
||||
if data["uplink"] != self.uplink_node.address:
|
||||
group = self.group_name_for_node(data["address"])
|
||||
print('leaving uplink group...')
|
||||
async_to_sync(self.channel_layer.group_discard)(group, self.channel_name)
|
||||
print('leaving node group...')
|
||||
self.remove_dst_nodes((data["address"], ))
|
||||
|
||||
def log_received_message(self, src_node: MeshNode, msg: messages.Message):
|
||||
NodeMessage.objects.create(
|
||||
|
@ -88,7 +102,6 @@ class MeshConsumer(WebsocketConsumer):
|
|||
)
|
||||
|
||||
def add_dst_nodes(self, addresses):
|
||||
# add ourselves to this one
|
||||
for address in addresses:
|
||||
# create group name for this address
|
||||
group = self.group_name_for_node(address)
|
||||
|
@ -100,7 +113,7 @@ class MeshConsumer(WebsocketConsumer):
|
|||
|
||||
# tell other consumers to leave the group
|
||||
async_to_sync(self.channel_layer.group_send)(group, {
|
||||
"type": "uplink_change",
|
||||
"type": "mesh.dst_node_uplink",
|
||||
"node": address,
|
||||
"uplink": self.uplink_node.address
|
||||
})
|
||||
|
@ -114,7 +127,27 @@ class MeshConsumer(WebsocketConsumer):
|
|||
)
|
||||
|
||||
# add the stuff to the db as well
|
||||
MeshNode.objects.filter(address__in=addresses).update(route_id=self.uplink_node.address)
|
||||
MeshNode.objects.filter(address__in=addresses).update(
|
||||
uplink_id=self.uplink_node.address,
|
||||
last_signin=timezone.now(),
|
||||
)
|
||||
|
||||
def remove_dst_nodes(self, addresses):
|
||||
for address in addresses:
|
||||
# create group name for this address
|
||||
group = self.group_name_for_node(address)
|
||||
|
||||
# leave the group
|
||||
if address in self.dst_nodes:
|
||||
async_to_sync(self.channel_layer.group_discard)(group, self.channel_name)
|
||||
self.dst_nodes.discard(address)
|
||||
|
||||
# add the stuff to the db as well
|
||||
# todo: can't do this because of race condition
|
||||
#MeshNode.objects.filter(address__in=addresses, uplink_id=self.uplink_node.address).update(
|
||||
# uplink_id=self.uplink_node.address,
|
||||
# last_signin=timezone.now(),
|
||||
#)
|
||||
|
||||
def group_name_for_node(self, address):
|
||||
return 'mesh_%s' % address.replace(':', '-')
|
||||
|
|
18
src/c3nav/mesh/migrations/0005_meshnode_last_signin.py
Normal file
18
src/c3nav/mesh/migrations/0005_meshnode_last_signin.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.2.1 on 2023-10-03 15:27
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mesh', '0004_relay_vs_src_node_and_remove_firmware'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='meshnode',
|
||||
name='last_signin',
|
||||
field=models.DateTimeField(null=True, verbose_name='last signin'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,25 @@
|
|||
# Generated by Django 4.2.1 on 2023-10-03 15:31
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mesh', '0005_meshnode_last_signin'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='meshnode',
|
||||
old_name='route',
|
||||
new_name='uplink',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='meshnode',
|
||||
name='uplink',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='routed_nodes',
|
||||
to='mesh.meshnode', verbose_name='uplink'),
|
||||
),
|
||||
]
|
|
@ -32,8 +32,8 @@ class MeshNodeQuerySet(models.QuerySet):
|
|||
try:
|
||||
for message in NodeMessage.objects.order_by('-datetime', '-pk').filter(
|
||||
message_type__in=self._prefetch_last_messages,
|
||||
node__in=nodes.keys(),
|
||||
).distinct('message_type', 'node'):
|
||||
src_node__in=nodes.keys(),
|
||||
).distinct('message_type', 'src_node'):
|
||||
nodes[message.node].last_messages[message.message_type] = message
|
||||
except NotSupportedError:
|
||||
pass
|
||||
|
@ -73,8 +73,9 @@ class MeshNode(models.Model):
|
|||
address = models.CharField(_('mac address'), max_length=17, primary_key=True)
|
||||
name = models.CharField(_('name'), max_length=32, null=True, blank=True)
|
||||
first_seen = models.DateTimeField(_('first seen'), auto_now_add=True)
|
||||
route = models.ForeignKey('MeshNode', models.PROTECT, null=True,
|
||||
related_name='routed_nodes', verbose_name=_('route'))
|
||||
uplink = models.ForeignKey('MeshNode', models.PROTECT, null=True,
|
||||
related_name='routed_nodes', verbose_name=_('uplink'))
|
||||
last_signin = models.DateTimeField(_('last signin'), null=True)
|
||||
objects = models.Manager.from_queryset(MeshNodeQuerySet)()
|
||||
|
||||
def __str__(self):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue