From b25a67382923163ff387f30a6b009310278a6168 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Thu, 18 Mar 2021 19:34:53 +0100 Subject: [PATCH] refactor state handling: uncouple failed and enabled flags --- owrx/connection.py | 10 +++---- owrx/dsp.py | 10 +++---- owrx/fft.py | 8 +++--- owrx/sdr.py | 3 +- owrx/service/__init__.py | 10 +++---- owrx/service/schedule.py | 7 +++-- owrx/source/__init__.py | 61 +++++++++++++++++++++++++++++----------- 7 files changed, 66 insertions(+), 43 deletions(-) diff --git a/owrx/connection.py b/owrx/connection.py index 2acfbd9..b691a51 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 SdrSourceState, SdrBusyState, SdrClientClass, SdrSourceEventClient +from owrx.source import SdrSourceState, SdrClientClass, SdrSourceEventClient from owrx.client import ClientRegistry, TooManyClientsException from owrx.feature import FeatureDetector from owrx.version import openwebrx_version @@ -219,17 +219,15 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): def onStateChange(self, state: SdrSourceState): if state is SdrSourceState.RUNNING: self.handleSdrAvailable() - elif state is SdrSourceState.FAILED: - self.handleSdrFailed() + + def onFail(self): + self.handleSdrFailed() def handleSdrFailed(self): logger.warning('SDR device "%s" has failed, selecting new device', self.sdr.getName()) self.write_log_message('SDR device "{0}" has failed, selecting new device'.format(self.sdr.getName())) self.setSdr() - def onBusyStateChange(self, state: SdrBusyState): - pass - def getClientClass(self) -> SdrClientClass: return SdrClientClass.USER diff --git a/owrx/dsp.py b/owrx/dsp.py index 0f34998..84bc22e 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 SdrSourceEventClient, SdrSourceState, SdrBusyState, SdrClientClass +from owrx.source import SdrSourceEventClient, SdrSourceState, SdrClientClass from owrx.property import PropertyStack, PropertyLayer, PropertyValidator from owrx.property.validators import OrValidator, RegexValidator, BoolValidator from owrx.modes import Modes @@ -210,9 +210,7 @@ class DspManager(csdr.output, SdrSourceEventClient): elif state is SdrSourceState.STOPPING: logger.debug("received STATE_STOPPING, shutting down DspSource") self.dsp.stop() - elif state is SdrSourceState.FAILED: - logger.debug("received STATE_FAILED, shutting down DspSource") - self.dsp.stop() - def onBusyStateChange(self, state: SdrBusyState): - pass + def onFail(self): + logger.debug("received onFail(), shutting down DspSource") + self.dsp.stop() diff --git a/owrx/fft.py b/owrx/fft.py index f210313..cfef176 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 SdrSourceEventClient, SdrSourceState, SdrBusyState, SdrClientClass +from owrx.source import SdrSourceEventClient, SdrSourceState, SdrClientClass from owrx.property import PropertyStack import logging @@ -77,10 +77,10 @@ class SpectrumThread(csdr.output, SdrSourceEventClient): return SdrClientClass.USER def onStateChange(self, state: SdrSourceState): - if state in [SdrSourceState.STOPPING, SdrSourceState.FAILED]: + if state is SdrSourceState.STOPPING: self.dsp.stop() elif state is SdrSourceState.RUNNING: self.dsp.start() - def onBusyStateChange(self, state: SdrBusyState): - pass + def onFail(self): + self.dsp.stop() diff --git a/owrx/sdr.py b/owrx/sdr.py index 9def8e3..f0bbe07 100644 --- a/owrx/sdr.py +++ b/owrx/sdr.py @@ -1,7 +1,6 @@ from owrx.config import Config from owrx.property import PropertyManager, PropertyDeleted, PropertyDelegator, PropertyLayer from owrx.feature import FeatureDetector, UnknownFeatureException -from owrx.source import SdrSourceState from functools import partial import logging @@ -114,5 +113,5 @@ class SdrService(object): return { key: s for key, s in SdrService.sources.items() - if s.getState() not in [SdrSourceState.FAILED, SdrSourceState.DISABLED] + if not s.isFailed() and s.isEnabled() } diff --git a/owrx/service/__init__.py b/owrx/service/__init__.py index fe3b49d..fd75aec 100644 --- a/owrx/service/__init__.py +++ b/owrx/service/__init__.py @@ -1,5 +1,5 @@ import threading -from owrx.source import SdrSourceEventClient, SdrSourceState, SdrBusyState, SdrClientClass +from owrx.source import SdrSourceEventClient, SdrSourceState, SdrClientClass from owrx.sdr import SdrService from owrx.bands import Bandplan from csdr.csdr import dsp, output @@ -117,12 +117,10 @@ class ServiceHandler(SdrSourceEventClient): elif state is SdrSourceState.STOPPING: logger.debug("sdr source becoming unavailable; stopping services.") self.stopServices() - elif state is SdrSourceState.FAILED: - logger.debug("sdr source failed; stopping services.") - self.stopServices() - def onBusyStateChange(self, state: SdrBusyState): - pass + def onFail(self): + logger.debug("sdr source failed; stopping services.") + self.stopServices() def isSupported(self, mode): configured = Config.get()["services_decoders"] diff --git a/owrx/service/schedule.py b/owrx/service/schedule.py index 0e9ad83..5b58627 100644 --- a/owrx/service/schedule.py +++ b/owrx/service/schedule.py @@ -231,7 +231,7 @@ class ServiceScheduler(SdrSourceEventClient): self.source.removeClient(self) def scheduleSelection(self, time=None): - if self.source.getState() is SdrSourceState.FAILED: + if self.source.isFailed(): return seconds = 10 if time is not None: @@ -254,8 +254,9 @@ class ServiceScheduler(SdrSourceEventClient): def onStateChange(self, state: SdrSourceState): if state is SdrSourceState.STOPPING: self.scheduleSelection() - elif state is SdrSourceState.FAILED: - self.shutdown() + + def onFail(self): + self.shutdown() def onBusyStateChange(self, state: SdrBusyState): if state is SdrBusyState.IDLE: diff --git a/owrx/source/__init__.py b/owrx/source/__init__.py index 8c210db..0c9b5f6 100644 --- a/owrx/source/__init__.py +++ b/owrx/source/__init__.py @@ -30,8 +30,6 @@ class SdrSourceState(Enum): RUNNING = "Running" STOPPING = "Stopping" TUNING = "Tuning" - FAILED = "Failed" - DISABLED = "Disabled" def __str__(self): return self.value @@ -48,15 +46,22 @@ class SdrClientClass(Enum): USER = auto() -class SdrSourceEventClient(ABC): - @abstractmethod +class SdrSourceEventClient(object): def onStateChange(self, state: SdrSourceState): pass - @abstractmethod def onBusyStateChange(self, state: SdrBusyState): pass + def onFail(self): + pass + + def onDisable(self): + pass + + def onEnable(self): + pass + def getClientClass(self) -> SdrClientClass: return SdrClientClass.INACTIVE @@ -129,14 +134,39 @@ class SdrSource(ABC): self.spectrumLock = threading.Lock() self.process = None self.modificationLock = threading.Lock() - self.state = SdrSourceState.STOPPED if "enabled" not in props or props["enabled"] else SdrSourceState.DISABLED + self.state = SdrSourceState.STOPPED + self.enabled = "enabled" not in props or props["enabled"] + props.filter("enabled").wire(self._handleEnableChanged) + self.failed = False self.busyState = SdrBusyState.IDLE self.validateProfiles() - if self.isAlwaysOn() and self.state is not SdrSourceState.DISABLED: + if self.isAlwaysOn() and self.isEnabled(): self.start() + def isEnabled(self): + return self.enabled + + def _handleEnableChanged(self, changes): + if "enabled" in changes and changes["enabled"] is not PropertyDeleted: + self.enabled = changes["enabled"] + else: + self.enabled = True + for c in self.clients: + if self.isEnabled(): + c.onEnable() + else: + c.onDisable() + + def isFailed(self): + return self.failed + + def fail(self): + self.failed = True + for c in self.clients: + c.onFail() + def validateProfiles(self): props = PropertyStack() props.addLayer(1, self.props) @@ -220,7 +250,7 @@ class SdrSource(ABC): if self.monitor: return - if self.getState() is SdrSourceState.FAILED: + if self.isFailed(): return try: @@ -254,9 +284,8 @@ class SdrSource(ABC): self.monitor = None if self.getState() is SdrSourceState.RUNNING: failed = True - self.setState(SdrSourceState.FAILED) - else: - self.setState(SdrSourceState.STOPPED) + self.fail() + self.setState(SdrSourceState.STOPPED) self.monitor = threading.Thread(target=wait_for_process_to_end, name="source_monitor") self.monitor.start() @@ -284,7 +313,10 @@ class SdrSource(ABC): logger.exception("Exception during postStart()") failed = True - self.setState(SdrSourceState.FAILED if failed else SdrSourceState.RUNNING) + if failed: + self.fail() + else: + self.setState(SdrSourceState.RUNNING) def preStart(self): """ @@ -302,10 +334,7 @@ class SdrSource(ABC): return self.monitor is not None def stop(self): - # don't overwrite failed flag - # TODO introduce a better solution? - if self.getState() is not SdrSourceState.FAILED: - self.setState(SdrSourceState.STOPPING) + self.setState(SdrSourceState.STOPPING) with self.modificationLock: