add source "busy state" to improve background scheduling

This commit is contained in:
Jakob Ketterl 2019-11-15 23:05:52 +01:00
parent 097f8a2b82
commit a36f106c72
2 changed files with 50 additions and 18 deletions

View File

@ -110,7 +110,6 @@ class ServiceScheduler(object):
def __init__(self, source, schedule): def __init__(self, source, schedule):
self.source = source self.source = source
self.schedule = Schedule.parse(schedule) self.schedule = Schedule.parse(schedule)
self.active = False
self.source.addClient(self) self.source.addClient(self)
self.selectionTimer = None self.selectionTimer = None
self.source.getProps().collect("center_freq", "samp_rate").wire(self.onFrequencyChange) self.source.getProps().collect("center_freq", "samp_rate").wire(self.onFrequencyChange)
@ -133,8 +132,8 @@ class ServiceScheduler(object):
if self.selectionTimer: if self.selectionTimer:
self.selectionTimer.cancel() self.selectionTimer.cancel()
def isActive(self): def getClientClass(self):
return self.active return SdrSource.CLIENT_BACKGROUND
def onStateChange(self, state): def onStateChange(self, state):
if state == SdrSource.STATE_STOPPING: if state == SdrSource.STATE_STOPPING:
@ -142,13 +141,16 @@ class ServiceScheduler(object):
elif state == SdrSource.STATE_FAILED: elif state == SdrSource.STATE_FAILED:
self.cancelTimer() self.cancelTimer()
def onBusyStateChange(self, state):
if state == SdrSource.BUSYSTATE_IDLE:
self.scheduleSelection()
def onFrequencyChange(self, name, value): def onFrequencyChange(self, name, value):
self.scheduleSelection() self.scheduleSelection()
def selectProfile(self): def selectProfile(self):
self.active = False if self.source.hasClients(SdrSource.CLIENT_USER):
if self.source.hasActiveClients(): logger.debug("source has active users; not touching")
logger.debug("source has active clients; not touching")
return return
logger.debug("source seems to be idle, selecting profile for background services") logger.debug("source seems to be idle, selecting profile for background services")
entry = self.schedule.getCurrentEntry() entry = self.schedule.getCurrentEntry()
@ -164,7 +166,6 @@ class ServiceScheduler(object):
self.scheduleSelection(entry.getScheduledEnd()) self.scheduleSelection(entry.getScheduledEnd())
try: try:
self.active = True
self.source.activateProfile(entry.getProfile()) self.source.activateProfile(entry.getProfile())
self.source.start() self.source.start()
except KeyError: except KeyError:
@ -186,8 +187,8 @@ class ServiceHandler(object):
if "schedule" in props: if "schedule" in props:
self.scheduler = ServiceScheduler(self.source, props["schedule"]) self.scheduler = ServiceScheduler(self.source, props["schedule"])
def isActive(self): def getClientClass(self):
return False return SdrSource.CLIENT_BACKGROUND
def onStateChange(self, state): def onStateChange(self, state):
if state == SdrSource.STATE_RUNNING: if state == SdrSource.STATE_RUNNING:
@ -199,6 +200,9 @@ class ServiceHandler(object):
logger.debug("sdr source failed; stopping services.") logger.debug("sdr source failed; stopping services.")
self.stopServices() self.stopServices()
def onBusyStateChange(self, state):
pass
def isSupported(self, mode): def isSupported(self, mode):
# TODO this should be in a more central place (the frontend also needs this) # TODO this should be in a more central place (the frontend also needs this)
requirements = { requirements = {

View File

@ -107,6 +107,13 @@ class SdrSource(object):
STATE_TUNING = 4 STATE_TUNING = 4
STATE_FAILED = 5 STATE_FAILED = 5
BUSYSTATE_IDLE = 0
BUSYSTATE_BUSY = 1
CLIENT_INACTIVE = 0
CLIENT_BACKGROUND = 1
CLIENT_USER = 2
def __init__(self, id, props, port): def __init__(self, id, props, port):
self.id = id self.id = id
self.props = props self.props = props
@ -126,6 +133,7 @@ class SdrSource(object):
self.modificationLock = threading.Lock() self.modificationLock = threading.Lock()
self.failed = False self.failed = False
self.state = SdrSource.STATE_STOPPED self.state = SdrSource.STATE_STOPPED
self.busyState = SdrSource.BUSYSTATE_IDLE
def getEventNames(self): def getEventNames(self):
return ["samp_rate", "nmux_memory", "center_freq", "ppm", "rf_gain", "lna_gain", "rf_amp", "antenna", "if_gain"] 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): def sleepOnRestart(self):
pass pass
def hasActiveClients(self): def hasClients(self, *args):
activeClients = [c for c in self.clients if c.isActive()] clients = [c for c in self.clients if c.getClientClass() in args]
return len(activeClients) > 0 return len(clients) > 0
def addClient(self, c): def addClient(self, c):
self.clients.append(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.start()
self.setBusyState(SdrSource.BUSYSTATE_BUSY if hasUsers else SdrSource.BUSYSTATE_IDLE)
def removeClient(self, c): def removeClient(self, c):
try: try:
self.clients.remove(c) self.clients.remove(c)
except ValueError: except ValueError:
pass 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() self.stop()
def addSpectrumClient(self, c): def addSpectrumClient(self, c):
@ -328,6 +343,13 @@ class SdrSource(object):
for c in self.clients: for c in self.clients:
c.onStateChange(state) 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): class Resampler(SdrSource):
def __init__(self, props, port, sdr): def __init__(self, props, port, sdr):
@ -575,8 +597,8 @@ class SpectrumThread(csdr.output):
c.cancel() c.cancel()
self.subscriptions = [] self.subscriptions = []
def isActive(self): def getClientClass(self):
return True return SdrSource.CLIENT_USER
def onStateChange(self, state): def onStateChange(self, state):
if state in [SdrSource.STATE_STOPPING, SdrSource.STATE_FAILED]: if state in [SdrSource.STATE_STOPPING, SdrSource.STATE_FAILED]:
@ -584,6 +606,9 @@ class SpectrumThread(csdr.output):
elif state == SdrSource.STATE_RUNNING: elif state == SdrSource.STATE_RUNNING:
self.dsp.start() self.dsp.start()
def onBusyStateChange(self, state):
pass
class DspManager(csdr.output): class DspManager(csdr.output):
def __init__(self, handler, sdrSource): def __init__(self, handler, sdrSource):
@ -707,8 +732,8 @@ class DspManager(csdr.output):
def setProperty(self, prop, value): def setProperty(self, prop, value):
self.localProps.getProperty(prop).setValue(value) self.localProps.getProperty(prop).setValue(value)
def isActive(self): def getClientClass(self):
return True return SdrSource.CLIENT_USER
def onStateChange(self, state): def onStateChange(self, state):
if state == SdrSource.STATE_RUNNING: if state == SdrSource.STATE_RUNNING:
@ -722,6 +747,9 @@ class DspManager(csdr.output):
self.dsp.stop() self.dsp.stop()
self.handler.handleSdrFailure("sdr device failed") self.handler.handleSdrFailure("sdr device failed")
def onBusyStateChange(self, state):
pass
class CpuUsageThread(threading.Thread): class CpuUsageThread(threading.Thread):
sharedInstance = None sharedInstance = None