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.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

View File

@ -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()

View File

@ -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()

View File

@ -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()
}

View File

@ -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"]

View File

@ -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:

View File

@ -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: