From c2e8ac516c6ce1aba93d93684966217e4ace734f Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Sat, 20 Feb 2021 22:54:07 +0100 Subject: [PATCH] introduce enums for state management --- owrx/connection.py | 14 ++++---- owrx/dsp.py | 16 ++++----- owrx/fft.py | 14 ++++---- owrx/service/__init__.py | 19 +++++----- owrx/service/schedule.py | 20 +++++------ owrx/source/__init__.py | 78 ++++++++++++++++++++++------------------ 6 files changed, 86 insertions(+), 75 deletions(-) diff --git a/owrx/connection.py b/owrx/connection.py index bf0e38d..03c3350 100644 --- a/owrx/connection.py +++ b/owrx/connection.py @@ -2,7 +2,7 @@ from owrx.details import ReceiverDetails from owrx.dsp import DspManager from owrx.cpu import CpuUsageThread 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.feature import FeatureDetector from owrx.version import openwebrx_version @@ -216,10 +216,10 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): self.configSubs.append(globalConfig.wire(writeConfig)) writeConfig(globalConfig.__dict__()) - def onStateChange(self, state): - if state == SdrSource.STATE_RUNNING: + def onStateChange(self, state: SdrSourceState): + if state is SdrSourceState.RUNNING: self.handleSdrAvailable() - elif state == SdrSource.STATE_FAILED: + elif state is SdrSourceState.FAILED: self.handleSdrFailed() 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.setSdr() - def onBusyStateChange(self, state): + def onBusyStateChange(self, state: SdrBusyState): pass - def getClientClass(self): - return SdrSource.CLIENT_USER + def getClientClass(self) -> SdrClientClass: + return SdrClientClass.USER def __sendProfiles(self): profiles = [ diff --git a/owrx/dsp.py b/owrx/dsp.py index 9bceed4..0f34998 100644 --- a/owrx/dsp.py +++ b/owrx/dsp.py @@ -3,7 +3,7 @@ from owrx.wsjt import WsjtParser from owrx.js8 import Js8Parser from owrx.aprs import AprsParser 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.validators import OrValidator, RegexValidator, BoolValidator from owrx.modes import Modes @@ -198,21 +198,21 @@ class DspManager(csdr.output, SdrSourceEventClient): def setProperty(self, prop, value): self.localProps[prop] = value - def getClientClass(self): - return SdrSource.CLIENT_USER + def getClientClass(self) -> SdrClientClass: + return SdrClientClass.USER - def onStateChange(self, state): - if state == SdrSource.STATE_RUNNING: + def onStateChange(self, state: SdrSourceState): + if state is SdrSourceState.RUNNING: logger.debug("received STATE_RUNNING, attempting DspSource restart") if self.startOnAvailable: self.dsp.start() self.startOnAvailable = False - elif state == SdrSource.STATE_STOPPING: + elif state is SdrSourceState.STOPPING: logger.debug("received STATE_STOPPING, shutting down DspSource") self.dsp.stop() - elif state == SdrSource.STATE_FAILED: + elif state is SdrSourceState.FAILED: logger.debug("received STATE_FAILED, shutting down DspSource") self.dsp.stop() - def onBusyStateChange(self, state): + def onBusyStateChange(self, state: SdrBusyState): pass diff --git a/owrx/fft.py b/owrx/fft.py index 53bdc5b..f210313 100644 --- a/owrx/fft.py +++ b/owrx/fft.py @@ -2,7 +2,7 @@ from owrx.config.core import CoreConfig from owrx.config import Config from csdr import csdr import threading -from owrx.source import SdrSource, SdrSourceEventClient +from owrx.source import SdrSourceEventClient, SdrSourceState, SdrBusyState, SdrClientClass from owrx.property import PropertyStack import logging @@ -73,14 +73,14 @@ class SpectrumThread(csdr.output, SdrSourceEventClient): c.cancel() self.subscriptions = [] - def getClientClass(self): - return SdrSource.CLIENT_USER + def getClientClass(self) -> SdrClientClass: + return SdrClientClass.USER - def onStateChange(self, state): - if state in [SdrSource.STATE_STOPPING, SdrSource.STATE_FAILED]: + def onStateChange(self, state: SdrSourceState): + if state in [SdrSourceState.STOPPING, SdrSourceState.FAILED]: self.dsp.stop() - elif state == SdrSource.STATE_RUNNING: + elif state is SdrSourceState.RUNNING: self.dsp.start() - def onBusyStateChange(self, state): + def onBusyStateChange(self, state: SdrBusyState): pass diff --git a/owrx/service/__init__.py b/owrx/service/__init__.py index 3baa015..ecd4b6e 100644 --- a/owrx/service/__init__.py +++ b/owrx/service/__init__.py @@ -1,5 +1,5 @@ import threading -from owrx.source import SdrSource, SdrSourceEventClient +from owrx.source import SdrSourceEventClient, SdrSourceState, SdrBusyState, SdrClientClass from owrx.sdr import SdrService from owrx.bands import Bandplan from csdr.csdr import dsp, output @@ -75,22 +75,22 @@ class ServiceHandler(SdrSourceEventClient): if "schedule" in props or "scheduler" in props: self.scheduler = ServiceScheduler(self.source) - def getClientClass(self): - return SdrSource.CLIENT_INACTIVE + def getClientClass(self) -> SdrClientClass: + return SdrClientClass.INACTIVE - def onStateChange(self, state): - if state == SdrSource.STATE_RUNNING: + def onStateChange(self, state: SdrSourceState): + if state is SdrSourceState.RUNNING: self.scheduleServiceStartup() - elif state == SdrSource.STATE_STOPPING: + elif state is SdrSourceState.STOPPING: logger.debug("sdr source becoming unavailable; stopping services.") self.stopServices() - elif state == SdrSource.STATE_FAILED: + elif state is SdrSourceState.FAILED: logger.debug("sdr source failed; stopping services.") self.stopServices() if self.scheduler: self.scheduler.shutdown() - def onBusyStateChange(self, state): + def onBusyStateChange(self, state: SdrBusyState): pass def isSupported(self, mode): @@ -164,7 +164,8 @@ class ServiceHandler(SdrSourceEventClient): for dial in group: 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) def get_min_max(self, group): diff --git a/owrx/service/schedule.py b/owrx/service/schedule.py index c024d33..6f9b5ad 100644 --- a/owrx/service/schedule.py +++ b/owrx/service/schedule.py @@ -1,5 +1,5 @@ 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 import threading import math @@ -220,7 +220,7 @@ class ServiceScheduler(SdrSourceEventClient): self.source.removeClient(self) def scheduleSelection(self, time=None): - if self.source.getState() == SdrSource.STATE_FAILED: + if self.source.getState() is SdrSourceState.FAILED: return seconds = 10 if time is not None: @@ -234,24 +234,24 @@ class ServiceScheduler(SdrSourceEventClient): if self.selectionTimer: self.selectionTimer.cancel() - def getClientClass(self): - return SdrSource.CLIENT_BACKGROUND + def getClientClass(self) -> SdrClientClass: + return SdrClientClass.BACKGROUND - def onStateChange(self, state): - if state == SdrSource.STATE_STOPPING: + def onStateChange(self, state: SdrSourceState): + if state is SdrSourceState.STOPPING: self.scheduleSelection() - elif state == SdrSource.STATE_FAILED: + elif state is SdrSourceState.FAILED: self.cancelTimer() - def onBusyStateChange(self, state): - if state == SdrSource.BUSYSTATE_IDLE: + def onBusyStateChange(self, state: SdrBusyState): + if state is SdrBusyState.IDLE: self.scheduleSelection() def onFrequencyChange(self, changes): self.scheduleSelection() def selectProfile(self): - if self.source.hasClients(SdrSource.CLIENT_USER): + if self.source.hasClients(SdrClientClass.USER): logger.debug("source has active users; not touching") return logger.debug("source seems to be idle, selecting profile for background services") diff --git a/owrx/source/__init__.py b/owrx/source/__init__.py index 688f0de..16d9a3d 100644 --- a/owrx/source/__init__.py +++ b/owrx/source/__init__.py @@ -15,40 +15,50 @@ from owrx.form.converter import IntConverter, OptionalConverter from owrx.form.device import GainInput from owrx.controllers.settings import Section from typing import List +from enum import Enum, auto import logging 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): @abstractmethod - def onStateChange(self, state): + def onStateChange(self, state: SdrSourceState): pass @abstractmethod - def onBusyStateChange(self, state): + def onBusyStateChange(self, state: SdrBusyState): pass - def getClientClass(self): - return SdrSource.CLIENT_INACTIVE + def getClientClass(self) -> SdrClientClass: + return SdrClientClass.INACTIVE 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): self.id = id @@ -78,8 +88,8 @@ class SdrSource(ABC): self.process = None self.modificationLock = threading.Lock() self.failed = False - self.state = SdrSource.STATE_STOPPED - self.busyState = SdrSource.BUSYSTATE_IDLE + self.state = SdrSourceState.STOPPED + self.busyState = SdrBusyState.IDLE self.validateProfiles() @@ -218,11 +228,11 @@ class SdrSource(ABC): rc = self.process.wait() logger.debug("shut down with RC={0}".format(rc)) self.monitor = None - if self.getState() == SdrSource.STATE_RUNNING: + if self.getState() is SdrSourceState.RUNNING: self.failed = True - self.setState(SdrSource.STATE_FAILED) + self.setState(SdrSourceState.FAILED) 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.start() @@ -250,7 +260,7 @@ class SdrSource(ABC): logger.exception("Exception during postStart()") 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): """ @@ -271,7 +281,7 @@ class SdrSource(ABC): return self.failed def stop(self): - self.setState(SdrSource.STATE_STOPPING) + self.setState(SdrSourceState.STOPPING) with self.modificationLock: @@ -291,11 +301,11 @@ class SdrSource(ABC): def addClient(self, c: SdrSourceEventClient): self.clients.append(c) c.onStateChange(self.getState()) - hasUsers = self.hasClients(SdrSource.CLIENT_USER) - hasBackgroundTasks = self.hasClients(SdrSource.CLIENT_BACKGROUND) + hasUsers = self.hasClients(SdrClientClass.USER) + hasBackgroundTasks = self.hasClients(SdrClientClass.BACKGROUND) if hasUsers or hasBackgroundTasks: 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): try: @@ -303,14 +313,14 @@ class SdrSource(ABC): except ValueError: pass - hasUsers = self.hasClients(SdrSource.CLIENT_USER) - self.setBusyState(SdrSource.BUSYSTATE_BUSY if hasUsers else SdrSource.BUSYSTATE_IDLE) + hasUsers = self.hasClients(SdrClientClass.USER) + self.setBusyState(SdrBusyState.BUSY if hasUsers else SdrBusyState.IDLE) # no need to check for users if we are always-on if self.isAlwaysOn(): return - hasBackgroundTasks = self.hasClients(SdrSource.CLIENT_BACKGROUND) + hasBackgroundTasks = self.hasClients(SdrClientClass.BACKGROUND) if not hasUsers and not hasBackgroundTasks: self.stop() @@ -341,17 +351,17 @@ class SdrSource(ABC): for c in self.spectrumClients: c.write_spectrum_data(data) - def getState(self): + def getState(self) -> SdrSourceState: return self.state - def setState(self, state): + def setState(self, state: SdrSourceState): if state == self.state: return self.state = state for c in self.clients: c.onStateChange(state) - def setBusyState(self, state): + def setBusyState(self, state: SdrBusyState): if state == self.busyState: return self.busyState = state