implement profile editing page

This commit is contained in:
Jakob Ketterl 2021-02-23 18:32:23 +01:00
parent ed258cc9a0
commit c5df6a1527
11 changed files with 120 additions and 16 deletions

View File

@ -5,6 +5,7 @@ from owrx.source import SdrDeviceDescription, SdrDeviceDescriptionMissing
from owrx.config import Config
from urllib.parse import quote, unquote
from owrx.sdr import SdrService
from abc import ABCMeta
class SdrDeviceListController(AuthorizationMixin, WebpageController):
@ -52,14 +53,11 @@ class SdrDeviceListController(AuthorizationMixin, WebpageController):
self.serve_template("settings/general.html", **self.template_variables())
class SdrDeviceController(SettingsFormController):
class SdrFormController(SettingsFormController, metaclass=ABCMeta):
def __init__(self, handler, request, options):
super().__init__(handler, request, options)
self.device_id, self.device = self._get_device()
def getData(self):
return self.device
def store(self):
# need to overwrite the existing key in the config since the layering won't capture the changes otherwise
config = Config.get()
@ -68,10 +66,21 @@ class SdrDeviceController(SettingsFormController):
config["sdrs"] = sdrs
super().store()
def _get_device(self):
device_id = unquote(self.request.matches.group(1))
if device_id not in Config.get()["sdrs"]:
return None
return device_id, Config.get()["sdrs"][device_id]
class SdrDeviceController(SdrFormController):
def getData(self):
return self.device
def getSections(self):
try:
description = SdrDeviceDescription.getByType(self.device["type"])
return [description.getSection()]
return [description.getDeviceSection()]
except SdrDeviceDescriptionMissing:
# TODO provide a generic interface that allows to switch the type
return []
@ -79,12 +88,6 @@ class SdrDeviceController(SettingsFormController):
def getTitle(self):
return self.device["name"]
def _get_device(self):
device_id = unquote(self.request.matches.group(1))
if device_id not in Config.get()["sdrs"]:
return None
return device_id, Config.get()["sdrs"][device_id]
def header_variables(self):
variables = super().header_variables()
variables["assets_prefix"] = "../../"
@ -123,3 +126,47 @@ class SdrDeviceController(SettingsFormController):
self.send_response("device not found", code=404)
return
self.serve_template("settings/general.html", **self.template_variables())
class SdrProfileController(SdrFormController):
def __init__(self, handler, request, options):
super().__init__(handler, request, options)
self.profile_id, self.profile = self._get_profile()
def getData(self):
return self.profile
def _get_profile(self):
profile_id = unquote(self.request.matches.group(2))
if self.device_id not in Config.get()["sdrs"]:
return None
if profile_id not in Config.get()["sdrs"][self.device_id]["profiles"]:
return None
return profile_id, Config.get()["sdrs"][self.device_id]["profiles"][profile_id]
def getSections(self):
try:
description = SdrDeviceDescription.getByType(self.device["type"])
return [description.getProfileSection()]
except SdrDeviceDescriptionMissing:
# TODO provide a generic interface that allows to switch the type
return []
def getTitle(self):
return self.profile["name"]
def header_variables(self):
variables = super().header_variables()
variables["assets_prefix"] = "../../../"
return variables
def template_variables(self):
variables = super().template_variables()
variables["assets_prefix"] = "../../../"
return variables
def indexAction(self):
if self.profile is None:
self.send_response("profile not found", code=404)
return
self.serve_template("settings/general.html", **self.template_variables())

View File

@ -313,3 +313,9 @@ class DropdownInput(Input):
class DropdownEnum(Enum):
def toOption(self):
return Option(self.name, str(self))
class ModesInput(DropdownInput):
def __init__(self, id, label):
options = [Option(m.modulation, m.name) for m in Modes.getAvailableModes()]
super().__init__(id, label, options)

View File

@ -6,7 +6,7 @@ from owrx.controllers.api import ApiController
from owrx.controllers.metrics import MetricsController
from owrx.controllers.settings import SettingsController
from owrx.controllers.settings.general import GeneralSettingsController
from owrx.controllers.settings.sdr import SdrDeviceListController, SdrDeviceController
from owrx.controllers.settings.sdr import SdrDeviceListController, SdrDeviceController, SdrProfileController
from owrx.controllers.settings.reporting import ReportingController
from owrx.controllers.settings.backgrounddecoding import BackgroundDecodingController
from owrx.controllers.settings.decoding import DecodingSettingsController
@ -117,11 +117,22 @@ class Router(object):
),
StaticRoute("/settings/sdr", SdrDeviceListController),
RegexRoute("^/settings/sdr/([^/]+)$", SdrDeviceController),
RegexRoute("^/settings/sdr/([^/]+)$", SdrDeviceController, method="POST", options={"action": "processFormData"}),
RegexRoute(
"^/settings/sdr/([^/]+)$", SdrDeviceController, method="POST", options={"action": "processFormData"}
),
RegexRoute("^/settings/sdr/([^/]+)/([^/]+)$", SdrProfileController),
RegexRoute(
"^/settings/sdr/([^/]+)/([^/]+)$",
SdrProfileController,
method="POST",
options={"action": "processFormData"},
),
StaticRoute("/settings/bookmarks", BookmarksController),
StaticRoute("/settings/bookmarks", BookmarksController, method="POST", options={"action": "new"}),
RegexRoute("^/settings/bookmarks/(.+)$", BookmarksController, method="POST", options={"action": "update"}),
RegexRoute("^/settings/bookmarks/(.+)$", BookmarksController, method="DELETE", options={"action": "delete"}),
RegexRoute(
"^/settings/bookmarks/(.+)$", BookmarksController, method="DELETE", options={"action": "delete"}
),
StaticRoute("/settings/reporting", ReportingController),
StaticRoute(
"/settings/reporting", ReportingController, method="POST", options={"action": "processFormData"}

View File

@ -10,7 +10,7 @@ from abc import ABC, abstractmethod
from owrx.command import CommandMapper
from owrx.socket import getAvailablePort
from owrx.property import PropertyStack, PropertyLayer
from owrx.form import Input, TextInput, NumberInput, CheckboxInput
from owrx.form import Input, TextInput, NumberInput, CheckboxInput, ModesInput
from owrx.form.converter import OptionalConverter
from owrx.form.device import GainInput
from owrx.controllers.settings import Section
@ -492,6 +492,11 @@ class SdrDeviceDescription(object):
NumberInput("waterfall_min_level", "Lowest waterfall level", append="dBFS"),
NumberInput("waterfall_max_level", "Highest waterfall level", append="dBFS"),
# TODO `schedule`
NumberInput("center_freq", "Center frequency", append="Hz"),
NumberInput("samp_rate", "Sample rate", append="S/s"),
NumberInput("start_freq", "Initial frequency", append="Hz"),
ModesInput("start_mod", "Initial modulation"),
NumberInput("initial_squelch_level", "Initial squelch level", append="dBFS"),
]
def getMandatoryKeys(self):
@ -500,5 +505,19 @@ class SdrDeviceDescription(object):
def getOptionalKeys(self):
return ["ppm", "always-on", "services", "rf_gain", "lfo_offset", "waterfall_min_level", "waterfall_max_level"]
def getSection(self):
def getProfileMandatoryKeys(self):
return ["center_freq", "samp_rate", "start_freq", "start_mod"]
def getProfileOptionalKeys(self):
return ["initial_squelch_level", "rf_gain", "lfo_offset", "waterfall_min_level", "waterfall_max_level"]
def getDeviceSection(self):
return OptionalSection("Device settings", self.getInputs(), self.getMandatoryKeys(), self.getOptionalKeys())
def getProfileSection(self):
return OptionalSection(
"Profile settings",
self.getInputs(),
self.getProfileMandatoryKeys(),
self.getProfileOptionalKeys(),
)

View File

@ -34,5 +34,8 @@ class AirspyDeviceDescription(SoapyConnectorDeviceDescription):
def getOptionalKeys(self):
return super().getOptionalKeys() + ["bias_tee", "bitpack"]
def getProfileOptionalKeys(self):
return super().getProfileOptionalKeys() + ["bias_tee"]
def getGainStages(self):
return ["LNA", "MIX", "VGA"]

View File

@ -92,3 +92,6 @@ class ConnectorDeviceDescription(SdrDeviceDescription):
def getOptionalKeys(self):
return super().getOptionalKeys() + ["rtltcp_compat", "iqswap"]
def getProfileOptionalKeys(self):
return super().getProfileOptionalKeys() + ["iqswap"]

View File

@ -21,5 +21,8 @@ class HackrfDeviceDescription(SoapyConnectorDeviceDescription):
def getOptionalKeys(self):
return super().getOptionalKeys() + ["bias_tee"]
def getProfileOptionalKeys(self):
return super().getProfileOptionalKeys() + ["bias_tee"]
def getGainStages(self):
return ["LNA", "AMP", "VGA"]

View File

@ -29,3 +29,6 @@ class RtlSdrDeviceDescription(ConnectorDeviceDescription):
def getOptionalKeys(self):
return super().getOptionalKeys() + ["device", "bias_tee", "direct_sampling"]
def getProfileOptionalKeys(self):
return super().getProfileOptionalKeys() + ["bias_tee", "direct_sampling"]

View File

@ -20,3 +20,6 @@ class RtlSdrSoapyDeviceDescription(SoapyConnectorDeviceDescription):
def getOptionalKeys(self):
return super().getOptionalKeys() + ["bias_tee", "direct_sampling"]
def getProfileOptionalKeys(self):
return super().getProfileOptionalKeys() + ["bias_tee", "direct_sampling"]

View File

@ -56,3 +56,6 @@ class SdrplayDeviceDescription(SoapyConnectorDeviceDescription):
def getOptionalKeys(self):
return super().getOptionalKeys() + ["bias_tee", "rf_notch", "dab_notch", "if_mode"]
def getProfileOptionalKeys(self):
return super().getProfileOptionalKeys() + ["bias_tee", "rf_notch", "dab_notch", "if_mode"]

View File

@ -100,5 +100,8 @@ class SoapyConnectorDeviceDescription(ConnectorDeviceDescription):
def getOptionalKeys(self):
return super().getOptionalKeys() + ["device", "rf_gain", "antenna"]
def getProfileOptionalKeys(self):
return super().getProfileOptionalKeys() + ["antenna"]
def getGainStages(self):
return None