introduce enums for state management

This commit is contained in:
Jakob Ketterl 2021-02-20 22:54:07 +01:00
parent dd5ab32b47
commit c2e8ac516c
6 changed files with 86 additions and 75 deletions

View File

@ -2,7 +2,7 @@ from owrx.details import ReceiverDetails
from owrx.dsp import DspManager from owrx.dsp import DspManager
from owrx.cpu import CpuUsageThread from owrx.cpu import CpuUsageThread
from owrx.sdr import SdrService from owrx.sdr import SdrService
from owrx.source import SdrSource, SdrSourceEventClient from owrx.source import SdrSourceState, SdrBusyState, SdrClientClass, SdrSourceEventClient
from owrx.client import ClientRegistry, TooManyClientsException from owrx.client import ClientRegistry, TooManyClientsException
from owrx.feature import FeatureDetector from owrx.feature import FeatureDetector
from owrx.version import openwebrx_version from owrx.version import openwebrx_version
@ -216,10 +216,10 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient):
self.configSubs.append(globalConfig.wire(writeConfig)) self.configSubs.append(globalConfig.wire(writeConfig))
writeConfig(globalConfig.__dict__()) writeConfig(globalConfig.__dict__())
def onStateChange(self, state): def onStateChange(self, state: SdrSourceState):
if state == SdrSource.STATE_RUNNING: if state is SdrSourceState.RUNNING:
self.handleSdrAvailable() self.handleSdrAvailable()
elif state == SdrSource.STATE_FAILED: elif state is SdrSourceState.FAILED:
self.handleSdrFailed() self.handleSdrFailed()
def handleSdrFailed(self): def handleSdrFailed(self):
@ -227,11 +227,11 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient):
self.write_log_message('SDR device "{0}" has failed, selecting new device'.format(self.sdr.getName())) self.write_log_message('SDR device "{0}" has failed, selecting new device'.format(self.sdr.getName()))
self.setSdr() self.setSdr()
def onBusyStateChange(self, state): def onBusyStateChange(self, state: SdrBusyState):
pass pass
def getClientClass(self): def getClientClass(self) -> SdrClientClass:
return SdrSource.CLIENT_USER return SdrClientClass.USER
def __sendProfiles(self): def __sendProfiles(self):
profiles = [ profiles = [

View File

@ -3,7 +3,7 @@ from owrx.wsjt import WsjtParser
from owrx.js8 import Js8Parser from owrx.js8 import Js8Parser
from owrx.aprs import AprsParser from owrx.aprs import AprsParser
from owrx.pocsag import PocsagParser from owrx.pocsag import PocsagParser
from owrx.source import SdrSource, SdrSourceEventClient from owrx.source import SdrSourceEventClient, SdrSourceState, SdrBusyState, SdrClientClass
from owrx.property import PropertyStack, PropertyLayer, PropertyValidator from owrx.property import PropertyStack, PropertyLayer, PropertyValidator
from owrx.property.validators import OrValidator, RegexValidator, BoolValidator from owrx.property.validators import OrValidator, RegexValidator, BoolValidator
from owrx.modes import Modes from owrx.modes import Modes
@ -198,21 +198,21 @@ class DspManager(csdr.output, SdrSourceEventClient):
def setProperty(self, prop, value): def setProperty(self, prop, value):
self.localProps[prop] = value self.localProps[prop] = value
def getClientClass(self): def getClientClass(self) -> SdrClientClass:
return SdrSource.CLIENT_USER return SdrClientClass.USER
def onStateChange(self, state): def onStateChange(self, state: SdrSourceState):
if state == SdrSource.STATE_RUNNING: if state is SdrSourceState.RUNNING:
logger.debug("received STATE_RUNNING, attempting DspSource restart") logger.debug("received STATE_RUNNING, attempting DspSource restart")
if self.startOnAvailable: if self.startOnAvailable:
self.dsp.start() self.dsp.start()
self.startOnAvailable = False self.startOnAvailable = False
elif state == SdrSource.STATE_STOPPING: elif state is SdrSourceState.STOPPING:
logger.debug("received STATE_STOPPING, shutting down DspSource") logger.debug("received STATE_STOPPING, shutting down DspSource")
self.dsp.stop() self.dsp.stop()
elif state == SdrSource.STATE_FAILED: elif state is SdrSourceState.FAILED:
logger.debug("received STATE_FAILED, shutting down DspSource") logger.debug("received STATE_FAILED, shutting down DspSource")
self.dsp.stop() self.dsp.stop()
def onBusyStateChange(self, state): def onBusyStateChange(self, state: SdrBusyState):
pass pass

View File

@ -2,7 +2,7 @@ from owrx.config.core import CoreConfig
from owrx.config import Config from owrx.config import Config
from csdr import csdr from csdr import csdr
import threading import threading
from owrx.source import SdrSource, SdrSourceEventClient from owrx.source import SdrSourceEventClient, SdrSourceState, SdrBusyState, SdrClientClass
from owrx.property import PropertyStack from owrx.property import PropertyStack
import logging import logging
@ -73,14 +73,14 @@ class SpectrumThread(csdr.output, SdrSourceEventClient):
c.cancel() c.cancel()
self.subscriptions = [] self.subscriptions = []
def getClientClass(self): def getClientClass(self) -> SdrClientClass:
return SdrSource.CLIENT_USER return SdrClientClass.USER
def onStateChange(self, state): def onStateChange(self, state: SdrSourceState):
if state in [SdrSource.STATE_STOPPING, SdrSource.STATE_FAILED]: if state in [SdrSourceState.STOPPING, SdrSourceState.FAILED]:
self.dsp.stop() self.dsp.stop()
elif state == SdrSource.STATE_RUNNING: elif state is SdrSourceState.RUNNING:
self.dsp.start() self.dsp.start()
def onBusyStateChange(self, state): def onBusyStateChange(self, state: SdrBusyState):
pass pass

View File

@ -1,5 +1,5 @@
import threading import threading
from owrx.source import SdrSource, SdrSourceEventClient from owrx.source import SdrSourceEventClient, SdrSourceState, SdrBusyState, SdrClientClass
from owrx.sdr import SdrService from owrx.sdr import SdrService
from owrx.bands import Bandplan from owrx.bands import Bandplan
from csdr.csdr import dsp, output from csdr.csdr import dsp, output
@ -75,22 +75,22 @@ class ServiceHandler(SdrSourceEventClient):
if "schedule" in props or "scheduler" in props: if "schedule" in props or "scheduler" in props:
self.scheduler = ServiceScheduler(self.source) self.scheduler = ServiceScheduler(self.source)
def getClientClass(self): def getClientClass(self) -> SdrClientClass:
return SdrSource.CLIENT_INACTIVE return SdrClientClass.INACTIVE
def onStateChange(self, state): def onStateChange(self, state: SdrSourceState):
if state == SdrSource.STATE_RUNNING: if state is SdrSourceState.RUNNING:
self.scheduleServiceStartup() self.scheduleServiceStartup()
elif state == SdrSource.STATE_STOPPING: elif state is SdrSourceState.STOPPING:
logger.debug("sdr source becoming unavailable; stopping services.") logger.debug("sdr source becoming unavailable; stopping services.")
self.stopServices() self.stopServices()
elif state == SdrSource.STATE_FAILED: elif state is SdrSourceState.FAILED:
logger.debug("sdr source failed; stopping services.") logger.debug("sdr source failed; stopping services.")
self.stopServices() self.stopServices()
if self.scheduler: if self.scheduler:
self.scheduler.shutdown() self.scheduler.shutdown()
def onBusyStateChange(self, state): def onBusyStateChange(self, state: SdrBusyState):
pass pass
def isSupported(self, mode): def isSupported(self, mode):
@ -164,7 +164,8 @@ class ServiceHandler(SdrSourceEventClient):
for dial in group: for dial in group:
self.services.append(self.setupService(dial["mode"], dial["frequency"], resampler)) self.services.append(self.setupService(dial["mode"], dial["frequency"], resampler))
# resampler goes in after the services since it must not be shutdown as long as the services are still running # resampler goes in after the services since it must not be shutdown as long as the services are
# still running
self.services.append(resampler) self.services.append(resampler)
def get_min_max(self, group): def get_min_max(self, group):

View File

@ -1,5 +1,5 @@
from datetime import datetime, timezone, timedelta from datetime import datetime, timezone, timedelta
from owrx.source import SdrSource, SdrSourceEventClient from owrx.source import SdrSourceEventClient, SdrSourceState, SdrClientClass, SdrBusyState
from owrx.config import Config from owrx.config import Config
import threading import threading
import math import math
@ -220,7 +220,7 @@ class ServiceScheduler(SdrSourceEventClient):
self.source.removeClient(self) self.source.removeClient(self)
def scheduleSelection(self, time=None): def scheduleSelection(self, time=None):
if self.source.getState() == SdrSource.STATE_FAILED: if self.source.getState() is SdrSourceState.FAILED:
return return
seconds = 10 seconds = 10
if time is not None: if time is not None:
@ -234,24 +234,24 @@ class ServiceScheduler(SdrSourceEventClient):
if self.selectionTimer: if self.selectionTimer:
self.selectionTimer.cancel() self.selectionTimer.cancel()
def getClientClass(self): def getClientClass(self) -> SdrClientClass:
return SdrSource.CLIENT_BACKGROUND return SdrClientClass.BACKGROUND
def onStateChange(self, state): def onStateChange(self, state: SdrSourceState):
if state == SdrSource.STATE_STOPPING: if state is SdrSourceState.STOPPING:
self.scheduleSelection() self.scheduleSelection()
elif state == SdrSource.STATE_FAILED: elif state is SdrSourceState.FAILED:
self.cancelTimer() self.cancelTimer()
def onBusyStateChange(self, state): def onBusyStateChange(self, state: SdrBusyState):
if state == SdrSource.BUSYSTATE_IDLE: if state is SdrBusyState.IDLE:
self.scheduleSelection() self.scheduleSelection()
def onFrequencyChange(self, changes): def onFrequencyChange(self, changes):
self.scheduleSelection() self.scheduleSelection()
def selectProfile(self): def selectProfile(self):
if self.source.hasClients(SdrSource.CLIENT_USER): if self.source.hasClients(SdrClientClass.USER):
logger.debug("source has active users; not touching") logger.debug("source has active users; 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")

View File

@ -15,40 +15,50 @@ from owrx.form.converter import IntConverter, OptionalConverter
from owrx.form.device import GainInput from owrx.form.device import GainInput
from owrx.controllers.settings import Section from owrx.controllers.settings import Section
from typing import List from typing import List
from enum import Enum, auto
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class SdrSourceState(Enum):
STOPPED = "Stopped"
STARTING = "Starting"
RUNNING = "Running"
STOPPING = "Stopping"
TUNING = "Tuning"
FAILED = "Failed"
def __str__(self):
return self.value
class SdrBusyState(Enum):
IDLE = auto()
BUSY = auto()
class SdrClientClass(Enum):
INACTIVE = auto()
BACKGROUND = auto()
USER = auto()
class SdrSourceEventClient(ABC): class SdrSourceEventClient(ABC):
@abstractmethod @abstractmethod
def onStateChange(self, state): def onStateChange(self, state: SdrSourceState):
pass pass
@abstractmethod @abstractmethod
def onBusyStateChange(self, state): def onBusyStateChange(self, state: SdrBusyState):
pass pass
def getClientClass(self): def getClientClass(self) -> SdrClientClass:
return SdrSource.CLIENT_INACTIVE return SdrClientClass.INACTIVE
class SdrSource(ABC): class SdrSource(ABC):
STATE_STOPPED = 0
STATE_STARTING = 1
STATE_RUNNING = 2
STATE_STOPPING = 3
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): def __init__(self, id, props):
self.id = id self.id = id
@ -78,8 +88,8 @@ class SdrSource(ABC):
self.process = None self.process = None
self.modificationLock = threading.Lock() self.modificationLock = threading.Lock()
self.failed = False self.failed = False
self.state = SdrSource.STATE_STOPPED self.state = SdrSourceState.STOPPED
self.busyState = SdrSource.BUSYSTATE_IDLE self.busyState = SdrBusyState.IDLE
self.validateProfiles() self.validateProfiles()
@ -218,11 +228,11 @@ class SdrSource(ABC):
rc = self.process.wait() rc = self.process.wait()
logger.debug("shut down with RC={0}".format(rc)) logger.debug("shut down with RC={0}".format(rc))
self.monitor = None self.monitor = None
if self.getState() == SdrSource.STATE_RUNNING: if self.getState() is SdrSourceState.RUNNING:
self.failed = True self.failed = True
self.setState(SdrSource.STATE_FAILED) self.setState(SdrSourceState.FAILED)
else: else:
self.setState(SdrSource.STATE_STOPPED) self.setState(SdrSourceState.STOPPED)
self.monitor = threading.Thread(target=wait_for_process_to_end, name="source_monitor") self.monitor = threading.Thread(target=wait_for_process_to_end, name="source_monitor")
self.monitor.start() self.monitor.start()
@ -250,7 +260,7 @@ class SdrSource(ABC):
logger.exception("Exception during postStart()") logger.exception("Exception during postStart()")
self.failed = True self.failed = True
self.setState(SdrSource.STATE_FAILED if self.failed else SdrSource.STATE_RUNNING) self.setState(SdrSourceState.FAILED if self.failed else SdrSourceState.RUNNING)
def preStart(self): def preStart(self):
""" """
@ -271,7 +281,7 @@ class SdrSource(ABC):
return self.failed return self.failed
def stop(self): def stop(self):
self.setState(SdrSource.STATE_STOPPING) self.setState(SdrSourceState.STOPPING)
with self.modificationLock: with self.modificationLock:
@ -291,11 +301,11 @@ class SdrSource(ABC):
def addClient(self, c: SdrSourceEventClient): def addClient(self, c: SdrSourceEventClient):
self.clients.append(c) self.clients.append(c)
c.onStateChange(self.getState()) c.onStateChange(self.getState())
hasUsers = self.hasClients(SdrSource.CLIENT_USER) hasUsers = self.hasClients(SdrClientClass.USER)
hasBackgroundTasks = self.hasClients(SdrSource.CLIENT_BACKGROUND) hasBackgroundTasks = self.hasClients(SdrClientClass.BACKGROUND)
if hasUsers or hasBackgroundTasks: if hasUsers or hasBackgroundTasks:
self.start() self.start()
self.setBusyState(SdrSource.BUSYSTATE_BUSY if hasUsers else SdrSource.BUSYSTATE_IDLE) self.setBusyState(SdrBusyState.BUSY if hasUsers else SdrBusyState.IDLE)
def removeClient(self, c: SdrSourceEventClient): def removeClient(self, c: SdrSourceEventClient):
try: try:
@ -303,14 +313,14 @@ class SdrSource(ABC):
except ValueError: except ValueError:
pass pass
hasUsers = self.hasClients(SdrSource.CLIENT_USER) hasUsers = self.hasClients(SdrClientClass.USER)
self.setBusyState(SdrSource.BUSYSTATE_BUSY if hasUsers else SdrSource.BUSYSTATE_IDLE) self.setBusyState(SdrBusyState.BUSY if hasUsers else SdrBusyState.IDLE)
# no need to check for users if we are always-on # no need to check for users if we are always-on
if self.isAlwaysOn(): if self.isAlwaysOn():
return return
hasBackgroundTasks = self.hasClients(SdrSource.CLIENT_BACKGROUND) hasBackgroundTasks = self.hasClients(SdrClientClass.BACKGROUND)
if not hasUsers and not hasBackgroundTasks: if not hasUsers and not hasBackgroundTasks:
self.stop() self.stop()
@ -341,17 +351,17 @@ class SdrSource(ABC):
for c in self.spectrumClients: for c in self.spectrumClients:
c.write_spectrum_data(data) c.write_spectrum_data(data)
def getState(self): def getState(self) -> SdrSourceState:
return self.state return self.state
def setState(self, state): def setState(self, state: SdrSourceState):
if state == self.state: if state == self.state:
return return
self.state = state self.state = state
for c in self.clients: for c in self.clients:
c.onStateChange(state) c.onStateChange(state)
def setBusyState(self, state): def setBusyState(self, state: SdrBusyState):
if state == self.busyState: if state == self.busyState:
return return
self.busyState = state self.busyState = state