add more mesh control panels and navigation
This commit is contained in:
parent
6c747803b7
commit
7e759fefd5
6 changed files with 80 additions and 19 deletions
15
src/c3nav/control/templates/control/form.html
Normal file
15
src/c3nav/control/templates/control/form.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{% extends 'control/base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block heading %}
|
||||||
|
{{ title }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block subcontent %}
|
||||||
|
<form method="POST" style="max-width:400px;">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form }}
|
||||||
|
<button type="submit">{% trans 'Save' %}</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -6,14 +6,30 @@
|
||||||
{% block subcontent %}
|
{% block subcontent %}
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div>
|
<div>
|
||||||
|
<h4>General</h4>
|
||||||
|
<p>
|
||||||
|
<strong>Address:</strong> {{ node.address }}<br>
|
||||||
|
<strong>Name:</strong> {% if node.name %}{{ node.name }}{% else %}<em>{% trans '(no name)' %}</em>{% endif %}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a class="button" href="{% url "control.mesh_node.edit" pk=node.pk %}">
|
||||||
|
{% trans 'Edit' %}
|
||||||
|
</a>
|
||||||
|
<a class="button" href="{% url "control.mesh_messages" %}?src_nodes={{ node.address }}">
|
||||||
|
{% trans 'View messages' %}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
<h4>Firmware</h4>
|
<h4>Firmware</h4>
|
||||||
<strong>Chip:</strong> {{ node.last_messages.CONFIG_FIRMWARE.parsed.get_chip_display }} rev{{ node.last_messages.CONFIG_FIRMWARE.parsed.revision|join:"." }}
|
<p>
|
||||||
<br>
|
<strong>Chip:</strong> {{ node.last_messages.CONFIG_FIRMWARE.parsed.get_chip_display }} rev{{ node.last_messages.CONFIG_FIRMWARE.parsed.revision|join:"." }}
|
||||||
<strong>Firmware:</strong> {{ node.last_messages.CONFIG_FIRMWARE.parsed.project_name }} {{ node.last_messages.CONFIG_FIRMWARE.parsed.version }} (IDF {{ node.last_messages.CONFIG_FIRMWARE.parsed.idf_version }})
|
<br>
|
||||||
<br>
|
<strong>Firmware:</strong> {{ node.last_messages.CONFIG_FIRMWARE.parsed.project_name }} {{ node.last_messages.CONFIG_FIRMWARE.parsed.version }} (IDF {{ node.last_messages.CONFIG_FIRMWARE.parsed.idf_version }})
|
||||||
<strong>Compile Date:</strong> {{ node.last_messages.CONFIG_FIRMWARE.parsed.compile_date }} {{ node.last_messages.CONFIG_FIRMWARE.parsed.compile_time }}
|
<br>
|
||||||
<br>
|
<strong>Compile Date:</strong> {{ node.last_messages.CONFIG_FIRMWARE.parsed.compile_date }} {{ node.last_messages.CONFIG_FIRMWARE.parsed.compile_time }}
|
||||||
<strong>SHA256:</strong> <small>{{ node.last_messages.CONFIG_FIRMWARE.parsed.app_elf_sha256 }}</small>
|
<br>
|
||||||
|
<strong>SHA256:</strong> <small>{{ node.last_messages.CONFIG_FIRMWARE.parsed.app_elf_sha256 }}</small>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h4>Uplink</h4>
|
<h4>Uplink</h4>
|
||||||
|
@ -32,7 +48,6 @@
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
<h4>Position</h4>
|
<h4>Position</h4>
|
||||||
<p>
|
<p>
|
||||||
<strong>X=</strong>{{ node.last_messages.CONFIG_POSITION.parsed.x_pos }}, <strong>Y=</strong>{{ node.last_messages.CONFIG_POSITION.parsed.y_pos }}, <strong>Z=</strong>{{ node.last_messages.CONFIG_POSITION.parsed.z_pos }}
|
<strong>X=</strong>{{ node.last_messages.CONFIG_POSITION.parsed.x_pos }}, <strong>Y=</strong>{{ node.last_messages.CONFIG_POSITION.parsed.y_pos }}, <strong>Z=</strong>{{ node.last_messages.CONFIG_POSITION.parsed.z_pos }}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from c3nav.control.views.mesh import MeshNodeListView, MeshMessageListView, MeshNodeDetailView, MeshMessageSendView
|
from c3nav.control.views.mesh import MeshNodeListView, MeshMessageListView, MeshNodeDetailView, MeshMessageSendView, \
|
||||||
|
MeshNodeEditView
|
||||||
from c3nav.control.views.mapupdates import map_updates
|
from c3nav.control.views.mapupdates import map_updates
|
||||||
from c3nav.control.views.announcements import announcement_list, announcement_detail
|
from c3nav.control.views.announcements import announcement_list, announcement_detail
|
||||||
from c3nav.control.views.access import grant_access, grant_access_qr
|
from c3nav.control.views.access import grant_access, grant_access_qr
|
||||||
|
@ -18,6 +19,7 @@ urlpatterns = [
|
||||||
path('mesh/', MeshNodeListView.as_view(), name='control.mesh_nodes'),
|
path('mesh/', MeshNodeListView.as_view(), name='control.mesh_nodes'),
|
||||||
path('mesh/messages/', MeshMessageListView.as_view(), name='control.mesh_messages'),
|
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>/', 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/<str:recipient>/message/<str:msg_type>/', MeshMessageSendView.as_view(), name='control.mesh_message.send'),
|
||||||
path('', ControlPanelIndexView.as_view(), name='control.index'),
|
path('', ControlPanelIndexView.as_view(), name='control.index'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
from django.db.models import Max
|
from django.db.models import Max
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views.generic import ListView, DetailView, FormView
|
from django.views.generic import ListView, DetailView, FormView, UpdateView
|
||||||
|
|
||||||
from c3nav.control.forms import MeshMessageFilterForm
|
from c3nav.control.forms import MeshMessageFilterForm
|
||||||
from c3nav.control.views.base import ControlPanelMixin
|
from c3nav.control.views.base import ControlPanelMixin
|
||||||
from c3nav.mesh.forms import MeshMessageForm
|
from c3nav.mesh.forms import MeshMessageForm, MeshNodeForm
|
||||||
from c3nav.mesh.messages import MeshMessageType
|
from c3nav.mesh.messages import MeshMessageType
|
||||||
from c3nav.mesh.models import MeshNode, NodeMessage
|
from c3nav.mesh.models import MeshNode, NodeMessage
|
||||||
|
|
||||||
|
@ -32,6 +33,22 @@ class MeshNodeDetailView(ControlPanelMixin, DetailView):
|
||||||
return super().get_queryset().annotate(last_msg=Max('received_messages__datetime')).prefetch_last_messages()
|
return super().get_queryset().annotate(last_msg=Max('received_messages__datetime')).prefetch_last_messages()
|
||||||
|
|
||||||
|
|
||||||
|
class MeshNodeEditView(ControlPanelMixin, SuccessMessageMixin, UpdateView):
|
||||||
|
model = MeshNode
|
||||||
|
form_class = MeshNodeForm
|
||||||
|
template_name = "control/form.html"
|
||||||
|
success_message = _('Name updated successfully')
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
return {
|
||||||
|
**super().get_context_data(),
|
||||||
|
'title': _('Editing mesh node: %s') % self.get_object(),
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse('control.mesh_node.detail', kwargs={'pk': self.get_object().pk})
|
||||||
|
|
||||||
|
|
||||||
class MeshMessageListView(ControlPanelMixin, ListView):
|
class MeshMessageListView(ControlPanelMixin, ListView):
|
||||||
model = NodeMessage
|
model = NodeMessage
|
||||||
template_name = "control/mesh_messages.html"
|
template_name = "control/mesh_messages.html"
|
||||||
|
|
|
@ -166,3 +166,9 @@ class ConfigPositionMessageForm(MeshMessageForm):
|
||||||
x_pos = forms.IntegerField(min_value=0, max_value=2**16-1, label=_('X'))
|
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'))
|
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'))
|
z_pos = forms.IntegerField(min_value=0, max_value=2 ** 16 - 1, label=_('Z'))
|
||||||
|
|
||||||
|
|
||||||
|
class MeshNodeForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = MeshNode
|
||||||
|
fields = ["name"]
|
|
@ -90,11 +90,17 @@ class MeshMessage:
|
||||||
def fromjson(cls, data) -> M:
|
def fromjson(cls, data) -> M:
|
||||||
kwargs = data.copy()
|
kwargs = data.copy()
|
||||||
klass = cls.msg_types[kwargs.pop('msg_id')]
|
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):
|
for field_ in fields(klass):
|
||||||
if is_dataclass(field_.type):
|
if is_dataclass(field_.type):
|
||||||
kwargs[field_.name] = field_.type.fromjson(kwargs[field_.name])
|
kwargs[field_.name] = field_.type.fromjson(kwargs[field_.name])
|
||||||
return klass(**kwargs)
|
return klass(**kwargs)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def upgrade_json(cls, data):
|
||||||
|
return data
|
||||||
|
|
||||||
def send(self):
|
def send(self):
|
||||||
async_to_sync(channels.layers.get_channel_layer().group_send)(get_mesh_comm_group(self.dst), {
|
async_to_sync(channels.layers.get_channel_layer().group_send)(get_mesh_comm_group(self.dst), {
|
||||||
"type": "mesh.send",
|
"type": "mesh.send",
|
||||||
|
@ -256,14 +262,14 @@ class ConfigFirmwareMessage(MeshMessage, msg_id=MeshMessageType.CONFIG_FIRMWARE)
|
||||||
app_elf_sha256: str = field(metadata={"format": HexFormat(32)})
|
app_elf_sha256: str = field(metadata={"format": HexFormat(32)})
|
||||||
reserv2: list[int] = field(metadata={"format": SimpleFormat('20I')}, repr=False)
|
reserv2: list[int] = field(metadata={"format": SimpleFormat('20I')}, repr=False)
|
||||||
|
|
||||||
def to_model_data(self):
|
@classmethod
|
||||||
return {
|
def upgrade_json(cls, data):
|
||||||
'chip': self.chip,
|
data = data.copy() # todo: deepcopy?
|
||||||
'project_name': self.project_name,
|
print(data)
|
||||||
'version': self.version,
|
if 'revision' in data:
|
||||||
'idf_version': self.idf_version,
|
data['revision_major'], data['revision_minor'] = data.pop('revision')
|
||||||
'sha256_hash': self.app_elf_sha256,
|
print(data)
|
||||||
}
|
return data
|
||||||
|
|
||||||
def get_chip_display(self):
|
def get_chip_display(self):
|
||||||
return ChipType(self.chip).name.replace('_', '-')
|
return ChipType(self.chip).name.replace('_', '-')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue