示例#1
0
 def callback(self, ch, method, properties, body):
     Mesh.consummed += 1
     if Mesh.consummed % 100 == 0:  #display mesh status each 100 models received
         Mesh.required_amount = SchedulerState.get_amount()
         Mesh.print_mesh_info()
     # uncommment to reduce the amount of frames send during the esp declaration phase
     # if Mesh.comp < Mesh.required_amount :
     #     print_flush("Not enough pixels on the mesh network to display the model")
     #     return
     if Websock.should_get_deco():
         Listen.deco = json.loads(Websock.get_deco())
     b = body.decode('ascii')
     self.model.set_from_json(b)
     tmp = Websock.get_esp_state()
     if tmp != None and eval(tmp) != self.previous_state:
         Mesh.change_esp_state = True
         print_flush("tmp != None, tmp = {}".format(tmp))
         self.previous_state = eval(tmp)
     else:
         Mesh.change_esp_state = False
     if Mesh.change_esp_state:  # A procedure has started (ie AMA.py is launched) and both the mesh network and the serveur have to be ready
         self.procedures_manager()
     elif (Mesh.ama == 1):  # A procedure is running
         self.ama_care()
     elif (Mesh.addressed):
         # Production mod : all pixels are addressed
         Mesh.sequence = (Mesh.sequence + 1) % 65536
         array = slef.msg.color(self.model._model, Mesh.sequence,
                                Mesh.pixels, Listen.unk)
         self.mesh_conn.send(array)
     else:  # Temporisation required between the launching of AMA.py and the frist model matching the procedure arrives
         print_flush("{} : It is not the time to send colors".format(
             Mesh.consummed))
示例#2
0
class Mesh(Thread):
    socket = None  #socket bind to mesh network through AP
    mac_root = ''  #esp32 root mac address
    sequence = 0  # sequence number of the COLOR frame
    pixels = SchedulerState.get_pixels_dic()  #pixels addressed and connected
    required_amount = SchedulerState.get_amount(
    )  #number of pixel required by the administrator
    consummed = 0  #model consummed on RabbitMQ
    comp = 0  # pixel amount
    #Manage adressing procedures
    addressed = None  #Tels if the pixels are addressed or not
    ama = 0  #fluctuates between 0 and 3 : 0 => NEVER_addressed; 1 => AMA_INIT; 2 => AMA_COLOR; 3 => HAR
    change_esp_state = False  #Order from ama.py to shift ESP in other state

    # Initialisation of Mesh instance requires seting up several connections to be in operating mode
    # A TCP connection for communication with the mesh network
    # A RabbitMQ connection for models recepetion
    # Reddis connection is managed by Websock class and static methods
    def __init__(self, conn, addr):
        Thread.__init__(self)
        print_flush("Pixels :", Mesh.pixels)
        #Manage adressing procedures
        Mesh.addressed = not (Mesh.pixels == {})
        self.ama_check = 0
        self.previous_state = 1
        self.model = Model(SchedulerState.get_rows(),
                           SchedulerState.get_cols())
        #Communication with mesh network config
        self.msg = Frame()
        self.mesh_conn = conn
        self.mesh_addr = addr
        self.l = Listen(conn)
        self.l.start()
        self.stopped = False
        #Communication with RabbitMQ config
        self.channel = None
        self.connection = None
        credentials = pika.PlainCredentials(
            os.environ['RABBITMQ_DEFAULT_USER'],
            os.environ['RABBITMQ_DEFAULT_PASS'])
        self.params = pika.ConnectionParameters(host='rabbit',
                                                credentials=credentials,
                                                heartbeat=0)

    #Determine if the current model has a pattern matching with the expected one.
    # This function is used only in AMA or HAR procedure. It is called in ama_care function which handle models during those procedures.
    #The pattern to match is set in the instance attribut ama_check.
    # If ama_check is set to 0, Mesh class is looking for models having : 1 red pixel, x green pixel (0 <= x < required_amount ) and special pixels (set at -1)
    # else Mesh is looking for models  having : x green pixels and the rest is colored in black
    def ama_model(self):
        i = self.model.get_height() - 1
        if (self.ama_check == 0):
            green = red = 0
            while (i >= 0):
                j = self.model.get_width() - 1
                while (j >= 0):
                    tmp = self.model.get_pixel(i, j)  # tmp = (R, G, B)
                    if ((tmp[0] + tmp[1] +
                         tmp[2]) == -3):  # special "color" -1
                        return True
                    elif (tmp[0] == 0 and tmp[1] == 1
                          and tmp[2] == 0):  # green
                        green += 1
                    elif (tmp[0] == 1 and tmp[1] == 0 and tmp[2] == 0):  # red
                        red += 1
                    else:
                        return False
                    j -= 1
                i -= 1
            return (green == Mesh.required_amount - 1 and red == 1)
        elif (self.ama_check == 1):
            while (i >= 0):
                j = self.model.get_width() - 1
                while (j >= 0):
                    tmp = self.model.get_pixel(i, j)
                    if (((tmp[0] + tmp[1] + tmp[2]) != 0)
                            and (tmp[0] != 1 and tmp[1] + tmp[2] != 0)
                            and (tmp[1] != 1 and tmp[0] + tmp[2] != 0)):
                        return False
                    j -= 1
                i -= 1
                return True

    #Is called to manage the addressing procedures.
    # Get from Reddis the newly update pixel dictionnary and unknown dictionnary
    #     this is a prerequise to build the COLOR frame and ensure that addressing procedures are well executed.
    # The type of model expected is also get from Reddis as it is set by the F-app AMA.py
    # The model received from RabbitMQ is check by ama_model. It necessary because RabbitMQ is refreshed at a frequency of 6Hz,
    # which introduce a delay between the reception of the expected model and the first model matching.
    def ama_care(self):
        #Get the new pixel addressed positions
        tmp = Websock.get_pixels()
        if tmp != None and tmp != {}:
            Mesh.pixels = json.loads(tmp)
        tmp = json.loads(Websock.get_pos_unk())
        if tmp != None:
            Listen.unk = tmp
        #Get the model format to check
        tmp = Websock.get_ama_model()
        if tmp != None:
            self.ama_check = eval(tmp)['ama']
        if self.ama_model():
            # send a COLOR frame only if the model match the expected model
            Mesh.sequence = (Mesh.sequence + 1) % 65536
            array = self.msg.color(self.model._model, Mesh.sequence,
                                   Mesh.pixels, Listen.unk, self.ama_check)
            self.mesh_conn.send(array)

    # This function is the manager of the different procedures implemented. It puts the server and the mesh network in the
    # right configuration for the required procedure by putting esp in the right state and manage the dictionnary
    def procedures_manager(self):
        Mesh.ama += 1
        if Mesh.ama == 1:  #AMA procedure starts
            print_flush("START AMA")
            Mesh.addressed = False
            Mesh.print_mesh_info()
            array = self.msg.ama(c.AMA_INIT)
            self.mesh_conn.send(array)
        elif Mesh.ama == 2:  # Ends adressing procedures
            Mesh.addressed = True
            Mesh.print_mesh_info()
            array = self.msg.ama(c.AMA_COLOR)
            self.mesh_conn.send(array)
            print_flush("END addressing procedure")
        else:  # HAR procedure starts
            print_flush("START HAR")
            Mesh.ama = 1
            Mesh.addressed = False
            Mesh.print_mesh_info()
            array = self.msg.har(Mesh.mac_root, c.STATE_CONF)
            self.mesh_conn.send(array)
            print_flush(Listen.unk.keys(), Listen.deco)
            # The pixel in deco are one by one being forgotten and their index is attributed to one of the unknown
            for mac in Listen.unk.keys():
                if len(Listen.deco) > 0:
                    pixel_deco = Listen.deco.popitem()
                    print_flush("Adding new element")
                    print_flush(pixel_deco)
                    print_flush("Inserted unknwon card at {0}".format(
                        pixel_deco[1][1]))
                    Listen.unk[mac] = ((-1, -1), pixel_deco[1][1])
                    array = self.msg.install_from_mac(mac, pixel_deco[1][1])
                    self.mesh_conn.send(array)
            Websock.send_pos_unk(Listen.unk)
            Mesh.print_mesh_info()
            array = slef.msg.ama(c.AMA_INIT)
            self.mesh_conn.send(array)

    #Invoque whenever a model is received from RabbitMQ, the callback function is the core.
    #Due to the absence of Reddis notification the callnack funciton is used to check if something have changed
    # - The deconnected pixel dictionnary has to be get from Reddis if a HAR procedure is at stake, because the unknown pixel have taken
    # their indexes in the routing table.
    # - The esp_state is the boolean that determines if a pocedure has started
    def callback(self, ch, method, properties, body):
        Mesh.consummed += 1
        if Mesh.consummed % 100 == 0:  #display mesh status each 100 models received
            Mesh.required_amount = SchedulerState.get_amount()
            Mesh.print_mesh_info()
        # uncommment to reduce the amount of frames send during the esp declaration phase
        # if Mesh.comp < Mesh.required_amount :
        #     print_flush("Not enough pixels on the mesh network to display the model")
        #     return
        if Websock.should_get_deco():
            Listen.deco = json.loads(Websock.get_deco())
        b = body.decode('ascii')
        self.model.set_from_json(b)
        tmp = Websock.get_esp_state()
        if tmp != None and eval(tmp) != self.previous_state:
            Mesh.change_esp_state = True
            print_flush("tmp != None, tmp = {}".format(tmp))
            self.previous_state = eval(tmp)
        else:
            Mesh.change_esp_state = False
        if Mesh.change_esp_state:  # A procedure has started (ie AMA.py is launched) and both the mesh network and the serveur have to be ready
            self.procedures_manager()
        elif (Mesh.ama == 1):  # A procedure is running
            self.ama_care()
        elif (Mesh.addressed):
            # Production mod : all pixels are addressed
            Mesh.sequence = (Mesh.sequence + 1) % 65536
            array = slef.msg.color(self.model._model, Mesh.sequence,
                                   Mesh.pixels, Listen.unk)
            self.mesh_conn.send(array)
        else:  # Temporisation required between the launching of AMA.py and the frist model matching the procedure arrives
            print_flush("{} : It is not the time to send colors".format(
                Mesh.consummed))

    #prints information relative to the mesh current state (server point of view)
    @staticmethod
    def print_mesh_info():
        print_flush(" ========== Mesh ==========")
        print_flush("-------- Is mesh initialized :")
        print_flush(Mesh.addressed)
        print_flush("-------- Color frame sent : ")
        print_flush(Mesh.consummed)
        print_flush("-------- Pixels amount declared ?")
        print_flush(Mesh.comp)
        print_flush("-------- Pixels amount required ?")
        print_flush(Mesh.required_amount)
        print_flush("-------- Pixels?")
        print_flush(Mesh.pixels)
        print_flush("-------- Pixels deconnected?")
        print_flush(Listen.deco)
        print_flush("-------- Pixels unknown?")
        print_flush(Listen.unk)

    #starts the connection to RabbitMQ
    def run(self):
        try:
            self.connection = pika.BlockingConnection(self.params)
            self.channel = self.connection.channel()
            self.channel.exchange_declare(exchange='pixels',
                                          exchange_type='fanout')

            result = self.channel.queue_declare(exclusive=True,
                                                arguments={"x-max-length": 1})
            queue_name = result.method.queue

            self.channel.queue_bind(exchange='pixels', queue=queue_name)
            self.channel.basic_consume(self.callback,
                                       queue=queue_name,
                                       no_ack=True)
            Mesh.print_mesh_info()
            print_flush(
                'Waiting for pixel data on queue "{}".'.format(queue_name))

            self.channel.start_consuming()
        except Exception as e:
            if self.channel is not None:
                self.channel.close()
            if self.connection is not None:
                self.connection.close()
            raise e

    #Close nicely the connections with other services before killing the thread
    def close_socket(self):
        print_flush("exiting thread, closing connection")
        if self.mesh_conn is not None:
            self.mesh_conn.close()
        if self.channel is not None:
            self.channel.close()
        if self.connection is not None:
            self.connection.close()
        print_flush("Closed connection, exiting thread...")
        self.stopped = True