team-3/src/c3nav/mesh/models.py

121 lines
4.5 KiB
Python
Raw Normal View History

from collections import UserDict
from functools import cached_property
from django.db import models, NotSupportedError
2022-04-15 20:02:42 +02:00
from django.utils.translation import gettext_lazy as _
from c3nav.mesh.messages import MessageType, ChipType, Message as MeshMessage
class MeshNodeQuerySet(models.QuerySet):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._prefetch_last_messages = set()
self._prefetch_last_messages_done = False
def _clone(self):
clone = super()._clone()
clone._prefetch_last_messages = self._prefetch_last_messages
return clone
def prefetch_last_messages(self, *types: MessageType):
clone = self._chain()
clone._prefetch_last_messages |= (
set(types) if types else set(msgtype.value for msgtype in MessageType)
)
return clone
def _fetch_all(self):
super()._fetch_all()
if self._prefetch_last_messages and not self._prefetch_last_messages_done:
nodes = {node.pk: node for node in self._result_cache}
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'):
nodes[message.node].last_messages[message.message_type] = message
except NotSupportedError:
pass
print(tuple(nodes.values())[0].last_messages[MessageType.MESH_SIGNIN])
2022-04-15 20:02:42 +02:00
class LastMessagesByTypeLookup(UserDict):
def __init__(self, node):
super().__init__()
self.node = node
def _get_key(self, item):
if isinstance(item, MessageType):
return item
if isinstance(item, str):
try:
return getattr(MessageType, item)
except AttributeError:
pass
return MessageType(item)
def __getitem__(self, key):
key = self._get_key(key)
try:
return self.data[key]
except KeyError:
pass
msg = self.node.received_messages.filter(message_type=key).order_by('-datetime', '-pk').first()
self.data[key] = msg
return msg
def __setitem__(self, key, item):
self.data[self._get_key(key)] = item
2022-04-15 20:02:42 +02:00
class MeshNode(models.Model):
2022-04-15 20:57:11 +02:00
address = models.CharField(_('mac address'), max_length=17, primary_key=True)
2023-10-02 22:02:25 +02:00
name = models.CharField(_('name'), max_length=32, null=True, blank=True)
2022-04-15 20:02:42 +02:00
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'))
objects = models.Manager.from_queryset(MeshNodeQuerySet)()
2022-04-15 20:02:42 +02:00
def __str__(self):
2023-10-02 22:02:25 +02:00
if self.name:
return '%s (%s)' % (self.address, self.name)
2022-04-15 20:02:42 +02:00
return self.address
@cached_property
def last_messages(self):
return LastMessagesByTypeLookup(self)
2022-04-15 20:02:42 +02:00
class NodeMessage(models.Model):
MESSAGE_TYPES = [(msgtype.value, msgtype.name) for msgtype in MessageType]
src_node = models.ForeignKey('MeshNode', models.PROTECT,
related_name='received_messages', verbose_name=_('node'))
uplink_node = models.ForeignKey('MeshNode', models.PROTECT,
related_name='relayed_messages', verbose_name=_('uplink node'))
2022-04-15 20:02:42 +02:00
datetime = models.DateTimeField(_('datetime'), db_index=True, auto_now_add=True)
message_type = models.SmallIntegerField(_('message type'), db_index=True, choices=MESSAGE_TYPES)
data = models.JSONField(_('message data'))
def __str__(self):
return '(#%d) %s at %s' % (self.pk, self.get_message_type_display(), self.datetime)
@cached_property
def parsed(self):
return MeshMessage.fromjson(self.data)
2022-04-15 20:02:42 +02:00
class Firmware(models.Model):
CHIPS = [(msgtype.value, msgtype.name.replace('_', '-')) for msgtype in ChipType]
chip = models.SmallIntegerField(_('chip'), db_index=True, choices=CHIPS)
2022-04-15 20:02:42 +02:00
project_name = models.CharField(_('project name'), max_length=32)
version = models.CharField(_('firmware version'), max_length=32)
idf_version = models.CharField(_('IDF version'), max_length=32)
sha256_hash = models.CharField(_('SHA256 hash'), unique=True, max_length=64)
binary = models.FileField(_('firmware file'), null=True)
class Meta:
unique_together = [
2023-10-02 22:02:25 +02:00
('chip', 'project_name', 'version', 'idf_version', 'sha256_hash'),
2022-04-15 20:02:42 +02:00
]