示例#1
0
class Tripod:
    """Classe principale per la gestione del tripode.

    Per la gestione del tripode, sono necessarie tre distinte attivita':

      - Abilitazione dei segnali visivi e acustici in base allo stato
      - Esecuzione del programma canopen_server per la comunicazione con i motori
      - Avvio del server TCP per la comunicazione con il programma di controllo remoto

    Il controllo dei segnali visivi ed acustici avviene tramite bus i2c, mentre il loro
    stato dipende da quanto comunicato tramite PIPE dal canopen_server.

    Il dialogo con il programma di controllo dei motori avviene tramite stdin, stdout e PIPE.

    """

    def __init__(self):

        logging.info("Initializing ALMA_Tripod class")

        self.ini = ConfigParser.ConfigParser()
        self.ini.read('/opt/spinitalia/service/config.ini')

        # Istanzio le classi
        self.canopen = Canopen(self)
        self.alarm = Alarm(self)
        self.kinematic = Kinematic(self)

        self.posTime = 0.01

        self.almaPositionProtocol = None
        self.almaPositionProtocolMulticast = None

        self.last_sim = ""
        self.last_sim_file = None
        self.last_sim_time = 0.0
        self.last_sim_timestamp = 0.0

        # All'avvio il sistema si trova nello stato 99, Non autenticato
        self.canStatus = 99
        self.oldStatus = 98    # Forzo il primo update
        self.simHash = ''

        self.userLoggedIn = False
        self.isAsyncError = False

        # Inizializza le strutture
        self.motorsAddress = []
        self.motorPos = {'120': 0L, '121': 0L, '122': 0L, '119': 0L}
        self.OpProgress = 0
        self.isCentered = False
        self.isImporting = False
        self.isReadyToStart = False
        self.currentLine = 0
        self.mex_counter = 0L
        self.almaControlProtocol = None
        self.isReading = False

        self.antenna_weight = 50

        # Per evitare che vengano eseguiti comandi errati prima che il canopen abbia comunicato lo stato tramite la
        # pipe ( si sblocca con CT0 )
        self.init_done = False

        # Annulla l'importazione
        self.stopAnalysis = False

        self.password = "******"

        # Indirizzi dei motori
        self.motor_address_list = ['119', '120', '121', '122']

        # Calcolo della velocità
        self.old_roll = 0.0
        self.old_pitch = 0.0
        self.old_yaw = 0.0

        # Compila i cercatori
        self.find_motor_position = re.compile(
            '@M([^ ]*) S([^ ]*) @M([^ ]*) S([^ ]*) @M([^ ]*) S([^ ]*) @M([^ ]*) S([^ ]*) AS([^ ]*) T([^ ]*) C([^ ]*)'
        )

        # Per prima cosa aggiorno lo stato
        self.update_output()

    def cleanup(self):

        logging.info("De-Initializing ALMA_Tripod class")

        # Invia CT6, spegnimento
        #reactor.callFromThread(self.canopen.sendCommand, 'CT6', 'local')

        # Chiude lo streamer
        if 'almaPositionProtocolMulticast' in vars():
            if self.almaPositionProtocolMulticast.posSender:
                self.almaPositionProtocolMulticast.posSender.stop()

        # Chiude lo stream_reader
        if 'receiver_thread' in vars():
            self.receiver_thread.stop()

        # Chiude il processo di comunicazione con i motori se attivo
        if self.canopen:
            self.canopen.transport.closeStdin()

        # Chiude il reattore se attivo
        if reactor.running:
            logging.info("Stopping reactor")
            reactor.stop()

        # Solo per rimuovere l'interrupt sul GPIO
        self.alarm.cleanup()

    def start_canopen(self):

        # Avvio il server CANOPEN
        # usePTY serve ad evitare l'ECHO
        # INFO: uso stdbuf per evitare il buffering dell'output se non in terminale
        if self.ini.getboolean("System", "canopen_fake"):
            reactor.spawnProcess(
                self.canopen,
                "/usr/bin/stdbuf",
                args=[
                    "stdbuf",
                    "--output=L",
                    "--input=0",
                    "{}alma3d_canopenshell".format(self.ini.get("Path", "base_path")),
                    "fake",
                    "load#libcanfestival_can_socket.so,0,1M,8"
                ],
                env=os.environ,
                usePTY=False
            )
        else:
            reactor.spawnProcess(
                self.canopen,
                "/usr/bin/stdbuf",
                args=[
                    "stdbuf",
                    "--output=L",
                    "--input=0",
                    "{}alma3d_canopenshell".format(self.ini.get("Path", "base_path")),
                    "load#libcanfestival_can_socket.so,0,1M,8"
                ],
                env=os.environ,
                usePTY=False
            )

    def update_import_progress(self, value, line_num):

        self.OpProgress = value
        self.currentLine = line_num

    def update_import_end(self, filename):

        if not self.stopAnalysis:
            self.isImporting = False
            self.last_sim = filename
            if filename != "":
                self.isReadyToStart = True

    def stream_reader(self):

        # TODO: Deve ripartire automaticamente in caso di errore ed in caso di assenza di pipe!
        logging.info("Position reader thread started!")

        is_pipe_open = False

        motor_pos = {'120': 0L, '121': 0L, '122': 0L, '119': 0L}
        is_async_error = False
        can_status = 3
        is_centered = False
        op_progress = 0
        mex_counter = 0L
        pos_time = 0.0

        while self.isReading:
            if is_pipe_open:
                try:
                    line = pipein.readline()[:-1]
                    # print 'Parent %d got "%s" at %s' % (os.getpid(), line, time.time( ))
                    # line: @M119 S0 @M120 S0 @M121 S0 @M122 S0 AS4 T9 C0
                    canopen_status = self.find_motor_position.search(line)
                    if canopen_status:
                        motor_pos[canopen_status.group(1)] = canopen_status.group(2)
                        motor_pos[canopen_status.group(3)] = canopen_status.group(4)
                        motor_pos[canopen_status.group(5)] = canopen_status.group(6)
                        motor_pos[canopen_status.group(7)] = canopen_status.group(8)
                        # Se lo stato e' zero, vuol dire che c'e' una segnalazione pendente
                        if canopen_status.group(9) == '0' and is_async_error is False:
                            is_async_error = True
                        elif canopen_status.group(9) != '0':
                            try:
                                can_status = int(canopen_status.group(9))
                            except ValueError:
                                can_status = 97
                                logging.error("Impossibile convertire lo stato {}".format(can_status))
                            if not is_centered:
                                if can_status == 6:
                                    is_centered = True
                        # pos_time = float(canopen_status.group(10))
                        pos_time = 0.01
                        op_progress = canopen_status.group(11)
                        mex_counter += 1

                    reactor.callFromThread(
                        self.update_var_from_canopen, motor_pos, is_async_error, can_status, is_centered,
                        pos_time, op_progress, mex_counter
                    )

                except Exception, e:

                    # Potrebbe essere semplicemente non disponibile un dato!
                    exc_type, exc_value, exc_traceback = sys.exc_info()
                    logging.error("Disabling Alarm, device not found! (%s)" % str(e))
                    traceback.print_exception(exc_type, exc_value, exc_traceback, limit=2, file=sys.stdout)

                    is_pipe_open = False
                    logging.info("Pipe closed!")

            else:

                # Questa devo analizzarla meglio
                try:
                    if self.ini.get("System", "canopen_fake"):
                        if os.path.exists(self.ini.get("Path", "pipe_position_fake")):
                            mode = os.stat(self.ini.get("Path", "pipe_position_fake")).st_mode
                            if stat.S_ISFIFO(mode):
                                pipein = open(self.ini.get("Path", "pipe_position_fake"), 'r')
                                is_pipe_open = True
                                continue
                    else:
                        if os.path.exists(self.ini.get("Path", "pipe_position")):
                            mode = os.stat(self.ini.get("Path", "pipe_position")).st_mode
                            if stat.S_ISFIFO(mode):
                                pipein = open(self.ini.get("Path", "pipe_position"), 'r')
                                is_pipe_open = True
                                continue
                    sleep(0.5)

                except:

                    exc_type, exc_value, exc_traceback = sys.exc_info()
                    logging.error("Impossibile inviare lo stato (%s)" % sys.exc_info()[0])
                    traceback.print_exception(exc_type, exc_value, exc_traceback, limit=2, file=sys.stdout)