From a36f106c72841a61e3e7a3c31e6037c8afefd696 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Fri, 15 Nov 2019 23:05:52 +0100 Subject: [PATCH] add source "busy state" to improve background scheduling --- owrx/service.py | 22 +++++++++++++--------- owrx/source.py | 46 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/owrx/service.py b/owrx/service.py index 2e56e6c..497d5c5 100644 --- a/owrx/service.py +++ b/owrx/service.py @@ -110,7 +110,6 @@ class ServiceScheduler(object): def __init__(self, source, schedule): self.source = source self.schedule = Schedule.parse(schedule) - self.active = False self.source.addClient(self) self.selectionTimer = None self.source.getProps().collect("center_freq", "samp_rate").wire(self.onFrequencyChange) @@ -133,8 +132,8 @@ class ServiceScheduler(object): if self.selectionTimer: self.selectionTimer.cancel() - def isActive(self): - return self.active + def getClientClass(self): + return SdrSource.CLIENT_BACKGROUND def onStateChange(self, state): if state == SdrSource.STATE_STOPPING: @@ -142,13 +141,16 @@ class ServiceScheduler(object): elif state == SdrSource.STATE_FAILED: self.cancelTimer() + def onBusyStateChange(self, state): + if state == SdrSource.BUSYSTATE_IDLE: + self.scheduleSelection() + def onFrequencyChange(self, name, value): self.scheduleSelection() def selectProfile(self): - self.active = False - if self.source.hasActiveClients(): - logger.debug("source has active clients; not touching") + if self.source.hasClients(SdrSource.CLIENT_USER): + logger.debug("source has active users; not touching") return logger.debug("source seems to be idle, selecting profile for background services") entry = self.schedule.getCurrentEntry() @@ -164,7 +166,6 @@ class ServiceScheduler(object): self.scheduleSelection(entry.getScheduledEnd()) try: - self.active = True self.source.activateProfile(entry.getProfile()) self.source.start() except KeyError: @@ -186,8 +187,8 @@ class ServiceHandler(object): if "schedule" in props: self.scheduler = ServiceScheduler(self.source, props["schedule"]) - def isActive(self): - return False + def getClientClass(self): + return SdrSource.CLIENT_BACKGROUND def onStateChange(self, state): if state == SdrSource.STATE_RUNNING: @@ -199,6 +200,9 @@ class ServiceHandler(object): logger.debug("sdr source failed; stopping services.") self.stopServices() + def onBusyStateChange(self, state): + pass + def isSupported(self, mode): # TODO this should be in a more central place (the frontend also needs this) requirements = { diff --git a/owrx/source.py b/owrx/source.py index d136aef..3e6f48c 100644 --- a/owrx/source.py +++ b/owrx/source.py @@ -107,6 +107,13 @@ class SdrSource(object): STATE_TUNING = 4 STATE_FAILED = 5 + BUSYSTATE_IDLE = 0 + BUSYSTATE_BUSY = 1 + + CLIENT_INACTIVE = 0 + CLIENT_BACKGROUND = 1 + CLIENT_USER = 2 + def __init__(self, id, props, port): self.id = id self.props = props @@ -126,6 +133,7 @@ class SdrSource(object): self.modificationLock = threading.Lock() self.failed = False self.state = SdrSource.STATE_STOPPED + self.busyState = SdrSource.BUSYSTATE_IDLE def getEventNames(self): return ["samp_rate", "nmux_memory", "center_freq", "ppm", "rf_gain", "lna_gain", "rf_amp", "antenna", "if_gain"] @@ -285,21 +293,28 @@ class SdrSource(object): def sleepOnRestart(self): pass - def hasActiveClients(self): - activeClients = [c for c in self.clients if c.isActive()] - return len(activeClients) > 0 + def hasClients(self, *args): + clients = [c for c in self.clients if c.getClientClass() in args] + return len(clients) > 0 def addClient(self, c): self.clients.append(c) - if self.hasActiveClients(): + hasUsers = self.hasClients(SdrSource.CLIENT_USER) + hasBackgroundTasks = self.hasClients(SdrSource.CLIENT_BACKGROUND) + if hasUsers or hasBackgroundTasks: self.start() + self.setBusyState(SdrSource.BUSYSTATE_BUSY if hasUsers else SdrSource.BUSYSTATE_IDLE) def removeClient(self, c): try: self.clients.remove(c) except ValueError: pass - if not self.hasActiveClients(): + + hasUsers = self.hasClients(SdrSource.CLIENT_USER) + hasBackgroundTasks = self.hasClients(SdrSource.CLIENT_BACKGROUND) + self.setBusyState(SdrSource.BUSYSTATE_BUSY if hasUsers else SdrSource.BUSYSTATE_IDLE) + if not hasUsers and not hasBackgroundTasks: self.stop() def addSpectrumClient(self, c): @@ -328,6 +343,13 @@ class SdrSource(object): for c in self.clients: c.onStateChange(state) + def setBusyState(self, state): + if state == self.busyState: + return + self.busyState = state + for c in self.clients: + c.onBusyStateChange(state) + class Resampler(SdrSource): def __init__(self, props, port, sdr): @@ -575,8 +597,8 @@ class SpectrumThread(csdr.output): c.cancel() self.subscriptions = [] - def isActive(self): - return True + def getClientClass(self): + return SdrSource.CLIENT_USER def onStateChange(self, state): if state in [SdrSource.STATE_STOPPING, SdrSource.STATE_FAILED]: @@ -584,6 +606,9 @@ class SpectrumThread(csdr.output): elif state == SdrSource.STATE_RUNNING: self.dsp.start() + def onBusyStateChange(self, state): + pass + class DspManager(csdr.output): def __init__(self, handler, sdrSource): @@ -707,8 +732,8 @@ class DspManager(csdr.output): def setProperty(self, prop, value): self.localProps.getProperty(prop).setValue(value) - def isActive(self): - return True + def getClientClass(self): + return SdrSource.CLIENT_USER def onStateChange(self, state): if state == SdrSource.STATE_RUNNING: @@ -722,6 +747,9 @@ class DspManager(csdr.output): self.dsp.stop() self.handler.handleSdrFailure("sdr device failed") + def onBusyStateChange(self, state): + pass + class CpuUsageThread(threading.Thread): sharedInstance = None