From e0961c16c1c6b0b44e81a034b9e1c986db72dbe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Wed, 4 Oct 2023 17:03:53 +0200 Subject: [PATCH] add possibility to set LED_CONFIG message --- .../templates/control/mesh_node_detail.html | 40 +++++-- src/c3nav/mesh/consumers.py | 4 +- src/c3nav/mesh/dataformats.py | 11 -- src/c3nav/mesh/forms.py | 111 +++++++++++++++--- src/c3nav/site/static/site/css/c3nav.scss | 7 ++ 5 files changed, 137 insertions(+), 36 deletions(-) diff --git a/src/c3nav/control/templates/control/mesh_node_detail.html b/src/c3nav/control/templates/control/mesh_node_detail.html index e9d68112..7fddf26c 100644 --- a/src/c3nav/control/templates/control/mesh_node_detail.html +++ b/src/c3nav/control/templates/control/mesh_node_detail.html @@ -17,19 +17,41 @@

Uplink

- Enabled: {{ node.last_messages.CONFIG_UPLINK.parsed.enabled }}, - SSID: {{ node.last_messages.CONFIG_UPLINK.parsed.ssid }}, - Channel: {{ node.last_messages.CONFIG_UPLINK.parsed.channel }}
- Host: {{ node.last_messages.CONFIG_UPLINK.parsed.host }}, - Port: {{ node.last_messages.CONFIG_UPLINK.parsed.port }}, - UDP: {{ node.last_messages.CONFIG_UPLINK.parsed.udp }}, - SSL: {{ node.last_messages.CONFIG_UPLINK.parsed.ssl }} +

+ Enabled: {{ node.last_messages.CONFIG_UPLINK.parsed.enabled }}, + SSID: {{ node.last_messages.CONFIG_UPLINK.parsed.ssid }}, + Channel: {{ node.last_messages.CONFIG_UPLINK.parsed.channel }}
+ Host: {{ node.last_messages.CONFIG_UPLINK.parsed.host }}, + Port: {{ node.last_messages.CONFIG_UPLINK.parsed.port }}, + UDP: {{ node.last_messages.CONFIG_UPLINK.parsed.udp }}, + SSL: {{ node.last_messages.CONFIG_UPLINK.parsed.ssl }}
+

+

+ + {% trans 'Change' %} + +

+

Position

- X={{ node.last_messages.CONFIG_POSITION.parsed.x_pos }}, Y={{ node.last_messages.CONFIG_POSITION.parsed.y_pos }}, Z={{ node.last_messages.CONFIG_POSITION.parsed.z_pos }} +

+ X={{ node.last_messages.CONFIG_POSITION.parsed.x_pos }}, Y={{ node.last_messages.CONFIG_POSITION.parsed.y_pos }}, Z={{ node.last_messages.CONFIG_POSITION.parsed.z_pos }} +

+

+ + {% trans 'Change' %} + +

LED config

- {{ node.last_messages.CONFIG_LED.parsed.led_config }} +

+ {{ node.last_messages.CONFIG_LED.parsed.led_config }} +

+

+ + {% trans 'Change' %} + +

{% endblock %} diff --git a/src/c3nav/mesh/consumers.py b/src/c3nav/mesh/consumers.py index 1e2590ed..2989c65e 100644 --- a/src/c3nav/mesh/consumers.py +++ b/src/c3nav/mesh/consumers.py @@ -109,7 +109,7 @@ class MeshConsumer(WebsocketConsumer): def add_dst_nodes(self, addresses): for address in addresses: # create group name for this address - group = self.comm_address_group(address) + group = get_mesh_comm_group(address) # if we aren't handling this address yet, join the group if address not in self.dst_nodes: @@ -140,7 +140,7 @@ class MeshConsumer(WebsocketConsumer): def remove_dst_nodes(self, addresses): for address in tuple(addresses): # create group name for this address - group = self.comm_address_group(address) + group = get_mesh_comm_group(address) # leave the group if address in self.dst_nodes: diff --git a/src/c3nav/mesh/dataformats.py b/src/c3nav/mesh/dataformats.py index 60f2000a..40516950 100644 --- a/src/c3nav/mesh/dataformats.py +++ b/src/c3nav/mesh/dataformats.py @@ -138,14 +138,3 @@ class LedConfigFormat: else: raise ValueError return value, data[4:] - - def from_json(self, value): - if value is None: - return None - - type_ = value.pop('type') - if type_ == 'serial': - return SerialLedConfig(**value) - elif type_ == 'multipin': - return MultipinLedConfig(**value) - raise ValueError diff --git a/src/c3nav/mesh/forms.py b/src/c3nav/mesh/forms.py index eb76f7a3..6a386565 100644 --- a/src/c3nav/mesh/forms.py +++ b/src/c3nav/mesh/forms.py @@ -1,7 +1,11 @@ +from dataclasses import fields as dataclass_fields + from django import forms +from django.core.exceptions import ValidationError from django.http import Http404 from django.utils.translation import gettext_lazy as _ +from c3nav.mesh.dataformats import LedConfig from c3nav.mesh.messages import MeshMessageType, MeshMessage, ROOT_ADDRESS from c3nav.mesh.models import MeshNode @@ -54,6 +58,29 @@ class MeshMessageForm(forms.Form): def get_recipient_display(self): return self.recipient_choices[self.recipient] + def get_cleaned_msg_data(self): + msg_data = self.cleaned_data.copy() + msg_data.pop('recipients', None) + return msg_data + + def send(self): + if not self.is_valid(): + raise Exception('nope') + + msg_data = { + 'msg_id': self.msg_type, + 'src': ROOT_ADDRESS, + **self.get_cleaned_msg_data(), + } + + recipients = [self.recipient] if self.recipient else self.cleaned_data['recipients'] + for recipient in recipients: + print('sending to ', recipient) + MeshMessage.fromjson({ + 'dst': recipient, + **msg_data, + }).send() + class ConfigUplinkMessageForm(MeshMessageForm): msg_type = MeshMessageType.CONFIG_UPLINK @@ -67,20 +94,76 @@ class ConfigUplinkMessageForm(MeshMessageForm): host = forms.CharField(required=False, label=_('host'), max_length=63) port = forms.IntegerField(min_value=1, max_value=65535, label=_('port')) - def send(self): - if not self.is_valid(): - raise Exception('nope') - msg_data = { - 'msg_id': self.msg_type, - 'src': ROOT_ADDRESS, - **self.cleaned_data, +class ConfigLedMessageForm(MeshMessageForm): + msg_type = MeshMessageType.CONFIG_LED + + led_type = forms.ChoiceField(choices=( + ('', _('no LED')), + (1, _('serial LED')), + (2, _('multipin LED')) + )) + gpio = forms.IntegerField(min_value=0, max_value=48, required=False, + label=_('gpio pin'), help_text=_('serial only')) + rmt = forms.IntegerField(min_value=0, max_value=7, required=False, + label=_('rmt'), help_text=_('serial only')) + gpio_r = forms.IntegerField(min_value=0, max_value=48, required=False, + label=_('gpio red'), help_text=_('multipin only')) + gpio_g = forms.IntegerField(min_value=0, max_value=48, required=False, + label=_('gpio green'), help_text=_('multipin only')) + gpio_b = forms.IntegerField(min_value=0, max_value=48, required=False, + label=_('gpio blue'), help_text=_('multipin only')) + + def clean(self): + cleaned_data = super().clean() + + led_type = int(cleaned_data["led_type"]) + if led_type: + required_fields = set(field.name for field in dataclass_fields(LedConfig.ledconfig_types[led_type])) + else: + required_fields = set() + + errors = {} + led_config = { + "led_type": led_type } - recipients = [self.recipient] if self.recipient else self.cleaned_data['recipients'] - for recipient in recipients: - print('sending to ', recipient) - MeshMessage.fromjson({ - 'dst': recipient, - **msg_data, - }).send() + for key, value in cleaned_data.items(): + if key == "recipients": + continue + if value and key not in required_fields: + errors[key] = _("this field is not allowed for this LED type") + + for key in required_fields: + value = cleaned_data.pop(key, "") + if value == "": + errors[key] = _("this field is required for this LED type") + led_config[key] = value + + cleaned_data["led_config"] = led_config + + if errors: + raise ValidationError(errors) + + return cleaned_data + + def get_cleaned_msg_data(self): + msg_data = super().get_cleaned_msg_data().copy() + msg_data = { + "led_config": msg_data["led_config"], + } + return msg_data + + def __init__(self, *args, initial=None, **kwargs): + if initial: + initial.update(initial.pop('led_config')) + super().__init__(*args, initial=initial, **kwargs) + + +class ConfigPositionMessageForm(MeshMessageForm): + msg_type = MeshMessageType.CONFIG_POSITION + + x_pos = forms.IntegerField(min_value=0, max_value=2**16-1, label=_('X')) + y_pos = forms.IntegerField(min_value=0, max_value=2 ** 16 - 1, label=_('Y')) + z_pos = forms.IntegerField(min_value=0, max_value=2 ** 16 - 1, label=_('Z')) + diff --git a/src/c3nav/site/static/site/css/c3nav.scss b/src/c3nav/site/static/site/css/c3nav.scss index 3e9f58eb..b8a62307 100644 --- a/src/c3nav/site/static/site/css/c3nav.scss +++ b/src/c3nav/site/static/site/css/c3nav.scss @@ -1351,3 +1351,10 @@ button + button { .material-icons { text-transform: none !important; } + +.helptext { + display: block; + margin-top: -1.5rem; + font-style: italic; + margin-bottom: 1.5rem; +} \ No newline at end of file