implement property deletion handling; activate scheduler deletion
This commit is contained in:
@@ -1,13 +1,11 @@
|
||||
from owrx.config.core import CoreConfig
|
||||
from owrx.config.migration import Migrator
|
||||
from owrx.property import PropertyLayer
|
||||
from owrx.property import PropertyLayer, PropertyDeleted
|
||||
from owrx.jsons import Encoder
|
||||
import json
|
||||
|
||||
|
||||
class DynamicConfig(PropertyLayer):
|
||||
_deleted = object()
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
try:
|
||||
@@ -43,12 +41,12 @@ class DynamicConfig(PropertyLayer):
|
||||
file.write(jsonContent)
|
||||
|
||||
def __delitem__(self, key):
|
||||
self.__setitem__(key, DynamicConfig._deleted)
|
||||
self.__setitem__(key, PropertyDeleted)
|
||||
|
||||
def __contains__(self, item):
|
||||
if not super().__contains__(item):
|
||||
return False
|
||||
if super().__getitem__(item) is DynamicConfig._deleted:
|
||||
if super().__getitem__(item) is PropertyDeleted:
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -58,7 +56,7 @@ class DynamicConfig(PropertyLayer):
|
||||
raise KeyError('Key "{key}" does not exist'.format(key=item))
|
||||
|
||||
def __dict__(self):
|
||||
return {k: v for k, v in super().__dict__().items() if v is not DynamicConfig._deleted}
|
||||
return {k: v for k, v in super().__dict__().items() if v is not PropertyDeleted}
|
||||
|
||||
def keys(self):
|
||||
return [k for k in super().keys() if self.__contains__(k)]
|
||||
|
@@ -9,7 +9,7 @@ from owrx.version import openwebrx_version
|
||||
from owrx.bands import Bandplan
|
||||
from owrx.bookmarks import Bookmarks
|
||||
from owrx.map import Map
|
||||
from owrx.property import PropertyStack
|
||||
from owrx.property import PropertyStack, PropertyDeleted
|
||||
from owrx.modes import Modes, DigitalMode
|
||||
from owrx.config import Config
|
||||
from owrx.waterfall import WaterfallOptions
|
||||
@@ -176,7 +176,8 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient):
|
||||
if changes is None:
|
||||
config = configProps.__dict__()
|
||||
else:
|
||||
config = changes
|
||||
# transform deletions into Nones
|
||||
config = {k: v if v is not PropertyDeleted else None for k, v in changes.items()}
|
||||
if (
|
||||
(changes is None or "start_freq" in changes or "center_freq" in changes)
|
||||
and "start_freq" in configProps
|
||||
|
@@ -10,6 +10,15 @@ class PropertyError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class PropertyDeletion(object):
|
||||
pass
|
||||
|
||||
|
||||
# a special object that will be sent in events when a deletion occured
|
||||
# it can also represent deletion of a key in internal storage, but should not be return from standard dict apis
|
||||
PropertyDeleted = PropertyDeletion()
|
||||
|
||||
|
||||
class Subscription(object):
|
||||
def __init__(self, subscriptee, name, subscriber):
|
||||
self.subscriptee = subscriptee
|
||||
@@ -126,7 +135,8 @@ class PropertyLayer(PropertyManager):
|
||||
return {k: v for k, v in self.properties.items()}
|
||||
|
||||
def __delitem__(self, key):
|
||||
return self.properties.__delitem__(key)
|
||||
self.properties.__delitem__(key)
|
||||
self._fireCallbacks({key: PropertyDeleted})
|
||||
|
||||
def keys(self):
|
||||
return self.properties.keys()
|
||||
@@ -273,7 +283,7 @@ class PropertyStack(PropertyManager):
|
||||
if self[key] != pm[key]:
|
||||
changes[key] = self[key]
|
||||
else:
|
||||
changes[key] = None
|
||||
changes[key] = PropertyDeleted
|
||||
return changes
|
||||
|
||||
def replaceLayer(self, priority: int, pm: PropertyManager):
|
||||
@@ -289,15 +299,21 @@ class PropertyStack(PropertyManager):
|
||||
|
||||
def receiveEvent(self, layer, changes):
|
||||
changesToForward = {name: value for name, value in changes.items() if layer == self._getTopLayer(name)}
|
||||
self._fireCallbacks(changesToForward)
|
||||
# deletions need to be handled separately: only send them if deleted in all layers
|
||||
deletionsToForward = {
|
||||
name: value
|
||||
for name, value in changes.items()
|
||||
if value is PropertyDeleted and self._getTopLayer(name, False) is None
|
||||
}
|
||||
self._fireCallbacks({**changesToForward, **deletionsToForward})
|
||||
|
||||
def _getTopLayer(self, item):
|
||||
def _getTopLayer(self, item, fallback=True):
|
||||
layers = [la["props"] for la in sorted(self.layers, key=lambda l: l["priority"])]
|
||||
for m in layers:
|
||||
if item in m:
|
||||
return m
|
||||
# return top layer by default
|
||||
if layers:
|
||||
# return top layer as fallback
|
||||
if fallback and layers:
|
||||
return layers[0]
|
||||
|
||||
def __getitem__(self, item):
|
||||
|
@@ -213,7 +213,8 @@ class ServiceScheduler(SdrSourceEventClient):
|
||||
props = self.source.getProps()
|
||||
props.filter("center_freq", "samp_rate").wire(self.onFrequencyChange)
|
||||
props.wireProperty("scheduler", self.parseSchedule)
|
||||
self.parseSchedule()
|
||||
# wireProperty calls parseSchedule with the initial value
|
||||
# self.parseSchedule()
|
||||
|
||||
def parseSchedule(self, *args):
|
||||
props = self.source.getProps()
|
||||
@@ -259,6 +260,12 @@ class ServiceScheduler(SdrSourceEventClient):
|
||||
if self.source.hasClients(SdrClientClass.USER):
|
||||
logger.debug("source has active users; not touching")
|
||||
return
|
||||
|
||||
if self.schedule is None:
|
||||
logger.debug("no active schedule. releasing source...")
|
||||
self.source.removeClient(self)
|
||||
return
|
||||
|
||||
logger.debug("source seems to be idle, selecting profile for background services")
|
||||
entry = self.schedule.getCurrentEntry()
|
||||
|
||||
@@ -269,7 +276,8 @@ class ServiceScheduler(SdrSourceEventClient):
|
||||
nextEntry = self.schedule.getNextEntry()
|
||||
if nextEntry is not None:
|
||||
self.scheduleSelection(nextEntry.getNextActivation())
|
||||
logger.debug("no next entry available, scheduler standing by for external events.")
|
||||
else:
|
||||
logger.debug("no next entry available, scheduler standing by for external events.")
|
||||
return
|
||||
|
||||
self.source.addClient(self)
|
||||
|
@@ -314,10 +314,10 @@ class SdrSource(ABC):
|
||||
self.setBusyState(SdrBusyState.BUSY if hasUsers else SdrBusyState.IDLE)
|
||||
|
||||
def removeClient(self, c: SdrSourceEventClient):
|
||||
try:
|
||||
self.clients.remove(c)
|
||||
except ValueError:
|
||||
pass
|
||||
if c not in self.clients:
|
||||
return
|
||||
|
||||
self.clients.remove(c)
|
||||
|
||||
hasUsers = self.hasClients(SdrClientClass.USER)
|
||||
self.setBusyState(SdrBusyState.BUSY if hasUsers else SdrBusyState.IDLE)
|
||||
|
Reference in New Issue
Block a user