Handling signals

Hi everyone,

i come back to you with another issue. :)
I have a main thread of my device server which is communicating through rs232 serial port. In this main thread, at some point i am creating a child process that need to trigger a function call in my main process. I thought i could use the signal and os libraries but it does'nt seem to be as straightforward as i thought.

I tried this :

def init_device(self):
    signal.signal(signal.SIGUSR1, self.handler)

def handler(self, signum, frame):
    do some stuff…

def mainProcessFunction(self):
    p = Process(target=self.childProcessFunction
    p.start()

def childProcessFunction(self)
    os.kill(os.getppid(), signal.SIGUSR1)

and this :

def init_device(self):
    self.register_signal(signal.SIGUSR1)

def delete_device(self):
    self.unregister_signal(signal.SIGUSR1)

def signal_handler(self, *arg, **kwargs):
    do some stuff…

def mainProcessFunction(self):
def childProcessFunction(self) :
Same as above

Can someone please explain how to handle os signals in a device ?

Thank you for your help !
Edited 8 years ago
Hi,

I'm also having trouble with signals handling.
I want to create a Tango device for launching subprocesses and controling them.

I start my process from a command with Popen
When launched from a console, this process can catch a SIGINT signal for calling a shutdown function.
When launched from the Tango device, and when the SIGINT signal is sent from a device command emergency, the process don't catch it.

Here is the code to illustrate:


import os,sys,logging,psutil
if sys.platform == "win32":
    from win32process import DETACHED_PROCESS, CREATE_NEW_PROCESS_GROUP
from subprocess import PIPE, Popen
import signal
from PyTango import DevState
from PyTango.server import Device, DeviceMeta, run, device_property, command,\
    attribute

"""Launcher device server"""

class Launcher(Device):
    ''' Tango Device Class for Python script Launcher
    '''
    __metaclass__ = DeviceMeta
    scriptPath = device_property(dtype=str)
    
    def init_device(self):
        Device.init_device(self)
        self.process = None
        self._returnCode = 0
        self._stdout = ''
        self._stderr = ''
        if self.scriptPath != None and os.path.isfile(self.scriptPath):
            self.set_state(DevState.ON)
        else:
            self.set_state(DevState.FAULT)
            logging.error("Error finding device corresponding script. scriptPath = {}".format(self.scriptPath))
        
    @attribute(label="Return code",dtype=int)
    def returnCode(self):
        if self.process != None:
            return self.process.returncode

    @attribute(label="Stdout",dtype=str)
    def stdout(self):
        if self.get_state() == DevState.ON:
            for line in self.process.stdout.readlines():
                self._stdout = self._stdout + line
        return self._stdout

    @attribute(label="Stderr",dtype=str)
    def stderr(self):
        if self.get_state() == DevState.ON:
            for line in self.process.stderr.readlines():
                self._stderr = self._stderr + line
        return self._stderr

    @attribute(label="Return code", dtype=bool)
    def isRunning(self):
        try:
            if self.process == None: return False
            self.process.poll()
            if self.process.returncode == None: # process still running_in_idle
                p = psutil.Process(self.process.pid)
                if p.status() == 'running':
                    self.set_state(DevState.RUNNING)
                elif p.status() == 'stopped':
                    self.set_state(DevState.OFF)
            else:

                for line in self.process.stdout.readlines():
                    self._stdout = self._stdout + line
                for line in self.process.stderr.readlines():
                    self._stderr = self._stderr + line
                self.set_state(DevState.ON)
            logging.debug("returnCode = {}".format(self.process.returncode))
            return self.process.returncode == None
        except Exception as e:
            logging.warning(e)
            
    @command()
    def run(self):
        logging.info("Starting script …")
        try:
            if sys.platform == "win32":
                self.process = Popen(['python',self.scriptPath], bufsize=0, shell=True, # universal_newlines=True,
                                             creationflags=DETACHED_PROCESS, stdout=PIPE, stderr=PIPE)
            else:
                self.process = Popen(['python',self.scriptPath],
                                             stdout=PIPE, stderr=PIPE)
            self._stdout = ''
            self._stderr = ''
            self.set_state(DevState.RUNNING)
        except: # Exiting, device server can't be started
            raise
            self.set_state(DevState.FAULT)

    def is_run_allowed(self):
        return (self.process == None or self.process.returncode != None) and self.get_state() == DevState.ON

    @command()
    def emergency(self):
        logging.info("Emergency shutdown… Process {}".format(self.process.pid))
        try:
            p = psutil.Process(self.process.pid)
            p.send_signal(signal.SIGINT)
        except Exception as e:
            logging.error(e)
            self.set_state(DevState.FAULT)

def main():
    run([Launcher])

if __name__ == "__main__":
    logging.basicConfig(level=logging.DEBUG)
    
    sys.argv.append("DsTestsUnitaires")
    main()

And here is the class, interruptible, launched by the process:


import signal
from time import sleep
import logging
import sys

class Iterator(object):
    '''
    Interruptible iterator
    '''

    def __init__(self, lstSequence, fctCallback=None):
        '''
        Constructor
        :param lstSequence: sequence to iterate
        :type lstSequence: list
        '''
        self.lstSequence = lstSequence
        self.fctCallback = fctCallback
        self.bInterrupted = False
        self.iLastValue = None
        signal.signal(signal.SIGINT, self.signal_handler)
    
    def signal_handler(self, signum, frame):
        logging.debug("Signal caught")
        self.bInterrupted = True
        if self.fctCallback != None:
            self.fctCallback()
        
    def next(self):
        # Pop a value when called, start a shutdown sequence if flag bInterrupted is set
        try:
            if not self.bInterrupted:
                self.iLastValue = self.lstSequence.pop(0)
            else:
                i = len(self.lstSequence)
                while self.lstSequence[i-1] < self.lstSequence[i-2]:
                    i -= 1
                self.lstSequence = self.lstSequence[i-1:]
                self.bInterrupted = False
                self.iLastValue = self.lstSequence.pop(0)
        except IndexError:
            self.iLastValue = None
        return self.iLastValue
        
if __name__ == "__main__": # pragma: no cover
    def essaiCallback():
        print "Emergency shutdown callback started"
        
    compteur = Iterator(range(0,30,2)+range(20,0,-5), essaiCallback)

    value=compteur.next()
    while value != None:
        print value
        try:
            sleep(1)
        except IOError:
            pass
        value=compteur.next()

Have you found answers to your problem ?

Thanks by advance for your help !
I finally discovered that it was working with SIGUSR1 or SIGUSR2 instead of SIGINT. The use of these signals was apparently introduced with Tango 8.

Sorry !
 
Register or login to create to post a reply.