refactor state handling: uncouple failed and enabled flags
This commit is contained in:
		@@ -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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								owrx/dsp.py
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								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()
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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"]
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user