refactor state handling: uncouple failed and enabled flags

This commit is contained in:
Jakob Ketterl 2021-03-18 19:34:53 +01:00
parent 916f19ac60
commit b25a673829
7 changed files with 66 additions and 43 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 SdrSourceState, SdrBusyState, SdrClientClass, SdrSourceEventClient from owrx.source import SdrSourceState, 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
@ -219,7 +219,8 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient):
def onStateChange(self, state: SdrSourceState): def onStateChange(self, state: SdrSourceState):
if state is SdrSourceState.RUNNING: if state is SdrSourceState.RUNNING:
self.handleSdrAvailable() self.handleSdrAvailable()
elif state is SdrSourceState.FAILED:
def onFail(self):
self.handleSdrFailed() self.handleSdrFailed()
def handleSdrFailed(self): def handleSdrFailed(self):
@ -227,9 +228,6 @@ 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: SdrBusyState):
pass
def getClientClass(self) -> SdrClientClass: def getClientClass(self) -> SdrClientClass:
return SdrClientClass.USER return SdrClientClass.USER

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 SdrSourceEventClient, SdrSourceState, SdrBusyState, SdrClientClass from owrx.source import SdrSourceEventClient, SdrSourceState, 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
@ -210,9 +210,7 @@ class DspManager(csdr.output, SdrSourceEventClient):
elif state is SdrSourceState.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 is SdrSourceState.FAILED:
logger.debug("received STATE_FAILED, shutting down DspSource")
self.dsp.stop()
def onBusyStateChange(self, state: SdrBusyState): def onFail(self):
pass logger.debug("received onFail(), shutting down DspSource")
self.dsp.stop()

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 SdrSourceEventClient, SdrSourceState, SdrBusyState, SdrClientClass from owrx.source import SdrSourceEventClient, SdrSourceState, SdrClientClass
from owrx.property import PropertyStack from owrx.property import PropertyStack
import logging import logging
@ -77,10 +77,10 @@ class SpectrumThread(csdr.output, SdrSourceEventClient):
return SdrClientClass.USER return SdrClientClass.USER
def onStateChange(self, state: SdrSourceState): def onStateChange(self, state: SdrSourceState):
if state in [SdrSourceState.STOPPING, SdrSourceState.FAILED]: if state is SdrSourceState.STOPPING:
self.dsp.stop() self.dsp.stop()
elif state is SdrSourceState.RUNNING: elif state is SdrSourceState.RUNNING:
self.dsp.start() self.dsp.start()
def onBusyStateChange(self, state: SdrBusyState): def onFail(self):
pass self.dsp.stop()

View File

@ -1,7 +1,6 @@
from owrx.config import Config from owrx.config import Config
from owrx.property import PropertyManager, PropertyDeleted, PropertyDelegator, PropertyLayer from owrx.property import PropertyManager, PropertyDeleted, PropertyDelegator, PropertyLayer
from owrx.feature import FeatureDetector, UnknownFeatureException from owrx.feature import FeatureDetector, UnknownFeatureException
from owrx.source import SdrSourceState
from functools import partial from functools import partial
import logging import logging
@ -114,5 +113,5 @@ class SdrService(object):
return { return {
key: s key: s
for key, s in SdrService.sources.items() for key, s in SdrService.sources.items()
if s.getState() not in [SdrSourceState.FAILED, SdrSourceState.DISABLED] if not s.isFailed() and s.isEnabled()
} }

View File

@ -1,5 +1,5 @@
import threading import threading
from owrx.source import SdrSourceEventClient, SdrSourceState, SdrBusyState, SdrClientClass from owrx.source import SdrSourceEventClient, SdrSourceState, 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
@ -117,13 +117,11 @@ class ServiceHandler(SdrSourceEventClient):
elif state is SdrSourceState.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 is SdrSourceState.FAILED:
def onFail(self):
logger.debug("sdr source failed; stopping services.") logger.debug("sdr source failed; stopping services.")
self.stopServices() self.stopServices()
def onBusyStateChange(self, state: SdrBusyState):
pass
def isSupported(self, mode): def isSupported(self, mode):
configured = Config.get()["services_decoders"] configured = Config.get()["services_decoders"]
available = [m.modulation for m in Modes.getAvailableServices()] available = [m.modulation for m in Modes.getAvailableServices()]

View File

@ -231,7 +231,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() is SdrSourceState.FAILED: if self.source.isFailed():
return return
seconds = 10 seconds = 10
if time is not None: if time is not None:
@ -254,7 +254,8 @@ class ServiceScheduler(SdrSourceEventClient):
def onStateChange(self, state: SdrSourceState): def onStateChange(self, state: SdrSourceState):
if state is SdrSourceState.STOPPING: if state is SdrSourceState.STOPPING:
self.scheduleSelection() self.scheduleSelection()
elif state is SdrSourceState.FAILED:
def onFail(self):
self.shutdown() self.shutdown()
def onBusyStateChange(self, state: SdrBusyState): def onBusyStateChange(self, state: SdrBusyState):

View File

@ -30,8 +30,6 @@ class SdrSourceState(Enum):
RUNNING = "Running" RUNNING = "Running"
STOPPING = "Stopping" STOPPING = "Stopping"
TUNING = "Tuning" TUNING = "Tuning"
FAILED = "Failed"
DISABLED = "Disabled"
def __str__(self): def __str__(self):
return self.value return self.value
@ -48,15 +46,22 @@ class SdrClientClass(Enum):
USER = auto() USER = auto()
class SdrSourceEventClient(ABC): class SdrSourceEventClient(object):
@abstractmethod
def onStateChange(self, state: SdrSourceState): def onStateChange(self, state: SdrSourceState):
pass pass
@abstractmethod
def onBusyStateChange(self, state: SdrBusyState): def onBusyStateChange(self, state: SdrBusyState):
pass pass
def onFail(self):
pass
def onDisable(self):
pass
def onEnable(self):
pass
def getClientClass(self) -> SdrClientClass: def getClientClass(self) -> SdrClientClass:
return SdrClientClass.INACTIVE return SdrClientClass.INACTIVE
@ -129,14 +134,39 @@ class SdrSource(ABC):
self.spectrumLock = threading.Lock() self.spectrumLock = threading.Lock()
self.process = None self.process = None
self.modificationLock = threading.Lock() 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.busyState = SdrBusyState.IDLE
self.validateProfiles() self.validateProfiles()
if self.isAlwaysOn() and self.state is not SdrSourceState.DISABLED: if self.isAlwaysOn() and self.isEnabled():
self.start() 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): def validateProfiles(self):
props = PropertyStack() props = PropertyStack()
props.addLayer(1, self.props) props.addLayer(1, self.props)
@ -220,7 +250,7 @@ class SdrSource(ABC):
if self.monitor: if self.monitor:
return return
if self.getState() is SdrSourceState.FAILED: if self.isFailed():
return return
try: try:
@ -254,8 +284,7 @@ class SdrSource(ABC):
self.monitor = None self.monitor = None
if self.getState() is SdrSourceState.RUNNING: if self.getState() is SdrSourceState.RUNNING:
failed = True failed = True
self.setState(SdrSourceState.FAILED) self.fail()
else:
self.setState(SdrSourceState.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")
@ -284,7 +313,10 @@ class SdrSource(ABC):
logger.exception("Exception during postStart()") logger.exception("Exception during postStart()")
failed = True failed = True
self.setState(SdrSourceState.FAILED if failed else SdrSourceState.RUNNING) if failed:
self.fail()
else:
self.setState(SdrSourceState.RUNNING)
def preStart(self): def preStart(self):
""" """
@ -302,9 +334,6 @@ class SdrSource(ABC):
return self.monitor is not None return self.monitor is not None
def stop(self): 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: with self.modificationLock: