new more complicated mesh code
This commit is contained in:
parent
a84a19ec0d
commit
740b896d18
5 changed files with 109 additions and 36 deletions
|
@ -0,0 +1,48 @@
|
|||
# Generated by Django 5.0.1 on 2024-03-30 18:02
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mapdata', '0104_theme_color_css_grid_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='theme',
|
||||
name='color_background',
|
||||
field=models.CharField(blank=True, max_length=32, verbose_name='background color'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='theme',
|
||||
name='color_door_fill',
|
||||
field=models.CharField(blank=True, max_length=32, verbose_name='door fill color'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='theme',
|
||||
name='color_ground_fill',
|
||||
field=models.CharField(blank=True, max_length=32, verbose_name='ground fill color'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='theme',
|
||||
name='color_obstacles_default_border',
|
||||
field=models.CharField(blank=True, max_length=32, verbose_name='default border color for obstacles'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='theme',
|
||||
name='color_obstacles_default_fill',
|
||||
field=models.CharField(blank=True, max_length=32, verbose_name='default fill color for obstacles'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='theme',
|
||||
name='color_wall_border',
|
||||
field=models.CharField(blank=True, max_length=32, verbose_name='wall border color'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='theme',
|
||||
name='color_wall_fill',
|
||||
field=models.CharField(blank=True, max_length=32, verbose_name='wall fill color'),
|
||||
),
|
||||
]
|
|
@ -15,6 +15,7 @@ from django.conf import settings
|
|||
from django.db import transaction
|
||||
from django.utils import timezone
|
||||
from django.utils.crypto import constant_time_compare
|
||||
from pydantic_extra_types.mac_address import MacAddress
|
||||
|
||||
from c3nav.mesh import messages
|
||||
from c3nav.mesh.cformats import CFormat
|
||||
|
@ -196,7 +197,7 @@ class MeshConsumer(AsyncWebsocketConsumer):
|
|||
await self.channel_layer.group_add(MESH_ALL_OTA_GROUP, self.channel_name)
|
||||
|
||||
# add this node as a destination that this uplink handles (duh)
|
||||
await self.add_dst_nodes(nodes=(src_node, ))
|
||||
await self.add_dst_node(src_node, parent=None)
|
||||
self.dst_nodes[msg.src].last_msg[MeshMessageType.MESH_SIGNIN] = msg.content
|
||||
|
||||
return
|
||||
|
@ -215,11 +216,21 @@ class MeshConsumer(AsyncWebsocketConsumer):
|
|||
return
|
||||
node_status.last_msg[msg.content.msg_type] = msg.content
|
||||
|
||||
if isinstance(msg.content, messages.MeshAddDestinationsMessage):
|
||||
result = await self.add_dst_nodes(addresses=msg.content.addresses)
|
||||
if isinstance(msg.content, messages.MeshAddDestinationMessage):
|
||||
result = await self.add_dst_node(
|
||||
node=await MeshNode.objects.aget_or_create(address=msg.content.address),
|
||||
parent_address=msg.src,
|
||||
)
|
||||
if not result:
|
||||
print('disconnecting node that send invalid destinations', msg.content)
|
||||
await self.close()
|
||||
await self.send_msg(messages.MeshMessage(
|
||||
src=MESH_ROOT_ADDRESS,
|
||||
dst=msg.src,
|
||||
content=messages.MeshSigninConfirmMessage(
|
||||
address=msg.content.address
|
||||
)
|
||||
))
|
||||
|
||||
if isinstance(msg.content, messages.MeshRemoveDestinationsMessage):
|
||||
await self.remove_dst_nodes(addresses=msg.content.addresses)
|
||||
|
@ -554,44 +565,25 @@ class MeshConsumer(AsyncWebsocketConsumer):
|
|||
def check_valid_address(address):
|
||||
return not (address.startswith('00:00:00') or address.startswith('ff:ff:ff'))
|
||||
|
||||
async def add_dst_nodes(self, nodes=None, addresses=None):
|
||||
nodes = list(nodes) if nodes else []
|
||||
addresses = set(addresses) if addresses else set()
|
||||
async def add_dst_node(self, node: MeshNode, parent_address: MacAddress | None):
|
||||
await self.log_text(node.address, "destination added")
|
||||
|
||||
if not all(self.check_valid_address(a) for a in addresses):
|
||||
return False
|
||||
# add ourselves as uplink
|
||||
await self._add_destination(node.address, parent_address)
|
||||
|
||||
node_addresses = set(node.address for node in nodes)
|
||||
missing_addresses = addresses - set(node.address for node in nodes)
|
||||
# if we aren't handling this address yet, write it down
|
||||
if node.address not in self.dst_nodes:
|
||||
self.dst_nodes[node.address] = NodeState()
|
||||
|
||||
if missing_addresses:
|
||||
await MeshNode.objects.abulk_create(
|
||||
[MeshNode(address=address) for address in missing_addresses],
|
||||
ignore_conflicts=True
|
||||
)
|
||||
|
||||
addresses |= node_addresses
|
||||
addresses |= missing_addresses
|
||||
|
||||
for address in addresses:
|
||||
await self.log_text(address, "destination added")
|
||||
|
||||
# add ourselves as uplink
|
||||
await self._add_destination(address)
|
||||
|
||||
# if we aren't handling this address yet, write it down
|
||||
if address not in self.dst_nodes:
|
||||
self.dst_nodes[address] = NodeState()
|
||||
|
||||
await self.node_resend_ask(address)
|
||||
return True
|
||||
await self.node_resend_ask(node.address)
|
||||
|
||||
@database_sync_to_async
|
||||
def _add_destination(self, address):
|
||||
def _add_destination(self, address, parent_address: MacAddress | None):
|
||||
with transaction.atomic():
|
||||
node = MeshNode.objects.select_for_update().get(address=address)
|
||||
# update database
|
||||
node.uplink = self.uplink
|
||||
node.upstream_id = parent_address
|
||||
node.last_signin = timezone.now()
|
||||
node.save()
|
||||
|
||||
|
|
|
@ -33,12 +33,13 @@ class MeshMessageType(CEnum):
|
|||
|
||||
MESH_SIGNIN = "MESH_SIGNIN", 0x03
|
||||
MESH_LAYER_ANNOUNCE = "MESH_LAYER_ANNOUNCE", 0x04
|
||||
MESH_ADD_DESTINATIONS = "MESH_ADD_DESTINATIONS", 0x05
|
||||
MESH_ADD_DESTINATION = "MESH_ADD_DESTINATION", 0x05
|
||||
MESH_REMOVE_DESTINATIONS = "MESH_REMOVE_DESTINATIONS", 0x06
|
||||
MESH_ROUTE_REQUEST = "MESH_ROUTE_REQUEST", 0x07
|
||||
MESH_ROUTE_RESPONSE = "MESH_ROUTE_RESPONSE", 0x08
|
||||
MESH_ROUTE_TRACE = "MESH_ROUTE_TRACE", 0x09
|
||||
MESH_ROUTING_FAILED = "MESH_ROUTING_FAILED", 0x0a
|
||||
MESH_SIGNIN_CONFIRM = "MESH_SIGNIN_CONFIRM", 0x0b
|
||||
|
||||
CONFIG_DUMP = "CONFIG_DUMP", 0x10
|
||||
CONFIG_HARDWARE = "CONFIG_HARDWARE", 0x11
|
||||
|
@ -101,9 +102,9 @@ class MeshLayerAnnounceMessage(discriminator_value(msg_type=MeshMessageType.MESH
|
|||
layer: Annotated[PositiveInt, Lt(2 ** 8), CDoc("mesh layer that the sending node is on")]
|
||||
|
||||
|
||||
class MeshAddDestinationsMessage(discriminator_value(msg_type=MeshMessageType.MESH_ADD_DESTINATIONS), BaseModel):
|
||||
class MeshAddDestinationMessage(discriminator_value(msg_type=MeshMessageType.MESH_ADD_DESTINATION), BaseModel):
|
||||
""" downstream node announces served destination """
|
||||
addresses: Annotated[list[MacAddress], MaxLen(16), VarLen(), CDoc("adresses of the added destinations",)]
|
||||
address: Annotated[MacAddress, CDoc("address of the added destination",)]
|
||||
|
||||
|
||||
class MeshRemoveDestinationsMessage(discriminator_value(msg_type=MeshMessageType.MESH_REMOVE_DESTINATIONS), BaseModel):
|
||||
|
@ -134,6 +135,11 @@ class MeshRoutingFailedMessage(discriminator_value(msg_type=MeshMessageType.MESH
|
|||
address: MacAddress
|
||||
|
||||
|
||||
class MeshSigninConfirmMessage(discriminator_value(msg_type=MeshMessageType.MESH_SIGNIN_CONFIRM), BaseModel):
|
||||
""" Confirm signin from root node """
|
||||
address: MacAddress
|
||||
|
||||
|
||||
class ConfigDumpMessage(discriminator_value(msg_type=MeshMessageType.CONFIG_DUMP), BaseModel):
|
||||
""" request for the node to dump its config """
|
||||
pass
|
||||
|
@ -307,12 +313,13 @@ MeshMessageContent = Annotated[
|
|||
EchoResponseMessage,
|
||||
MeshSigninMessage,
|
||||
MeshLayerAnnounceMessage,
|
||||
MeshAddDestinationsMessage,
|
||||
MeshAddDestinationMessage,
|
||||
MeshRemoveDestinationsMessage,
|
||||
MeshRouteRequestMessage,
|
||||
MeshRouteResponseMessage,
|
||||
MeshRouteTraceMessage,
|
||||
MeshRoutingFailedMessage,
|
||||
MeshSigninConfirmMessage,
|
||||
ConfigDumpMessage,
|
||||
ConfigHardwareMessage,
|
||||
ConfigBoardMessage,
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# Generated by Django 5.0.1 on 2024-03-30 18:02
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mesh', '0012_otaupdaterecipient_status_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='meshnode',
|
||||
name='upstream',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='downstream', to='mesh.meshnode', verbose_name='parent node'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nodemessage',
|
||||
name='message_type',
|
||||
field=models.CharField(choices=[('NOOP', 'noop'), ('ECHO_REQUEST', 'echo request'), ('ECHO_RESPONSE', 'echo response'), ('MESH_SIGNIN', 'mesh signin'), ('MESH_LAYER_ANNOUNCE', 'mesh layer announce'), ('MESH_ADD_DESTINATION', 'mesh add destination'), ('MESH_REMOVE_DESTINATIONS', 'mesh remove destinations'), ('MESH_ROUTE_REQUEST', 'mesh route request'), ('MESH_ROUTE_RESPONSE', 'mesh route response'), ('MESH_ROUTE_TRACE', 'mesh route trace'), ('MESH_ROUTING_FAILED', 'mesh routing failed'), ('MESH_SIGNIN_CONFIRM', 'mesh signin confirm'), ('CONFIG_DUMP', 'dump config'), ('CONFIG_HARDWARE', 'hardware config'), ('CONFIG_BOARD', 'board config'), ('CONFIG_FIRMWARE', 'firmware config'), ('CONFIG_UPLINK', 'uplink config'), ('CONFIG_POSITION', 'position config'), ('CONFIG_NODE', 'node config'), ('CONFIG_IBEACON', 'ibeacon config'), ('OTA_STATUS', 'ota status'), ('OTA_REQUEST_STATUS', 'ota request status'), ('OTA_START', 'ota start'), ('OTA_URL', 'ota url'), ('OTA_FRAGMENT', 'ota fragment'), ('OTA_REQUEST_FRAGMENTS', 'ota request fragments'), ('OTA_SETTING', 'ota setting'), ('OTA_APPLY', 'ota apply'), ('OTA_ABORT', 'ota abort'), ('LOCATE_REQUEST_RANGE', 'locate request range'), ('LOCATE_RANGE_RESULTS', 'locate range results'), ('LOCATE_RAW_FTM_RESULTS', 'locate raw ftm results'), ('REBOOT', 'reboot'), ('REPORT_ERROR', 'report error')], db_index=True, max_length=24, verbose_name='message type'),
|
||||
),
|
||||
]
|
|
@ -260,6 +260,8 @@ class MeshNode(models.Model):
|
|||
first_seen = models.DateTimeField(_('first seen'), auto_now_add=True)
|
||||
uplink = models.ForeignKey('MeshUplink', models.PROTECT, null=True,
|
||||
related_name='routed_nodes', verbose_name=_('uplink'))
|
||||
upstream = models.ForeignKey('MeshNode', models.SET_NULL, null=True,
|
||||
related_name='downstream', verbose_name=_('parent node'))
|
||||
last_signin = models.DateTimeField(_('last signin'), null=True)
|
||||
objects = models.Manager.from_queryset(MeshNodeQuerySet)()
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue