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.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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								owrx/dsp.py
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								owrx/dsp.py
									
									
									
									
									
								
							@@ -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()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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()]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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:
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user