Exemplo n.º 1
0
    def start_session(self):
        self.CURRENT_WINDOW = 0

        total_size = len(self.MESSAGE)
        current_size = 0

        logging = self.LOGGER is not None and self.LOGGER.JSON_FILE is not None

        if logging:
            self.LOGGER.START_SENDING_TIME = self.LOGGER.CHRONO.read()
            self.LOGGER.TOTAL_SIZE = total_size
            self.LOGGER.FINISHED = False

        while self.FRAGMENT_INDEX < len(self.FRAGMENTS):
            fragment = Fragment(self.PROFILE, self.FRAGMENTS[self.FRAGMENT_INDEX])
            current_size += len(self.FRAGMENTS[self.FRAGMENT_INDEX][1])
            percent = round(float(current_size) / float(total_size) * 100, 2)

            if logging:
                self.LOGGER.info("Sending...")
                self.LOGGER.LAPS.append(self.LOGGER.CHRONO.read())
                self.LOGGER.debug("laps - > {}".format(self.LOGGER.LAPS))

                self.LOGGER.debug("--------------------------")
                self.LOGGER.debug("{} fragment:".format(ordinal(self.FRAGMENT_INDEX)))
                self.LOGGER.debug("RuleID:{}, DTAG:{}, WINDOW:{}, FCN:{}".format(fragment.HEADER.RULE_ID,
                                                                                 fragment.HEADER.DTAG,
                                                                                 fragment.HEADER.W,
                                                                                 fragment.HEADER.FCN))
                self.LOGGER.debug("SCHC Fragment: {}".format(fragment.to_string()))
                self.LOGGER.debug("SCHC Fragment Payload: {}".format(fragment.PAYLOAD))
                self.LOGGER.debug("{} / {}, {}%".format(current_size,
                                                        total_size,
                                                        percent))

            try:
                self.schc_send(fragment)
            except SCHCError:
                if logging:
                    self.LOGGER.FINISHED = False
                break

        if logging:
            self.LOGGER.END_SENDING_TIME = self.LOGGER.CHRONO.read()
            self.LOGGER.save()
Exemplo n.º 2
0
    def schc_send(self, fragment_sent, retransmit=False):

        ack = None
        current_fragment = {}
        logging = self.LOGGER is not None and self.LOGGER.JSON_FILE is not None
        self.TIMER.wait(timeout=self.DELAY, raise_exception=False)

        if logging:
            self.LOGGER.LOGGING_TIME += self.DELAY
            current_fragment = {'RULE_ID': fragment_sent.HEADER.RULE_ID,
                                'W': fragment_sent.HEADER.W,
                                'FCN': fragment_sent.HEADER.FCN,
                                'data': fragment_sent.to_bytes(),
                                'fragment_size': len(fragment_sent.to_bytes()),
                                'abort': False,
                                'sending_start': 0,
                                'sending_end': 0,
                                'send_time': 0,
                                'downlink_enable': False,
                                'timeout': 0,
                                'ack_received': False,
                                'ack': "",
                                'rssi': 0,
                                'receiver_abort_received': False,
                                'receiver_abort_message': ""}

        if fragment_sent.is_all_0() and not retransmit:
            self.LOGGER.debug("[POST] This is an All-0. Using All-0 SIGFOX_DL_TIMEOUT.")
            self.set_timeout(self.PROFILE.SIGFOX_DL_TIMEOUT)
            if logging:
                current_fragment["timeout"] = self.PROFILE.SIGFOX_DL_TIMEOUT
        elif fragment_sent.is_all_1():
            self.LOGGER.debug("[POST] This is an All-1. Using RETRANSMISSION_TIMER_VALUE. Increasing ACK attempts.")
            self.ATTEMPTS += 1
            self.set_timeout(self.PROFILE.RETRANSMISSION_TIMER_VALUE)
            if logging:
                current_fragment["timeout"] = self.PROFILE.RETRANSMISSION_TIMER_VALUE
        else:
            self.set_timeout(60)
            if logging:
                current_fragment["timeout"] = 60

        # LoPy should use to_bytes()
        data = fragment_sent.to_hex().decode()

        self.LOGGER.info("[POST] Posting fragment {} ({})".format(fragment_sent.HEADER.to_string(),
                                                                  fragment_sent.to_hex()))

        if fragment_sent.expects_ack() and not retransmit:
            current_fragment['downlink_enable'] = True
        else:
            current_fragment['downlink_enable'] = False

        try:
            if logging:
                current_fragment['sending_start'] = self.LOGGER.CHRONO.read()

            self.send(data, loss=True)
            # self.send_mask(fragment_sent)

            if fragment_sent.expects_ack() and not retransmit:
                if logging:
                    current_fragment['ack_received'] = False

                ack = self.recv(self.PROFILE.DOWNLINK_MTU // 8, loss=True)
                # ack = self.recv_mask()

            if logging:
                current_fragment['sending_end'] = self.LOGGER.CHRONO.read()
                current_fragment['send_time'] = current_fragment['sending_end'] - current_fragment['sending_start']
                if ack is not None:
                    current_fragment['rssi'] = self.PROTOCOL.rssi()
                    self.LOGGER.debug("Response received at: {}: ".format(self.LOGGER.CHRONO.read()))
                    self.LOGGER.debug('ack -> {}'.format(ack))
                    self.LOGGER.debug('message RSSI: {}'.format(self.PROTOCOL.rssi()))

            if fragment_sent.is_sender_abort():
                self.LOGGER.debug("--- senderAbort:{}".format(fragment_sent.to_string()))
                self.LOGGER.debug("--- senderAbort:{}".format(fragment_sent.to_bytes()))
                if logging:
                    current_fragment['abort'] = True
                    self.LOGGER.FRAGMENTS_INFO_ARRAY.append(current_fragment)
                self.LOGGER.error("Sent Sender-Abort. Goodbye")
                self.LOGGER.SENDER_ABORTED = True
                raise SenderAbortError

            if not fragment_sent.expects_ack():
                if not retransmit:
                    self.FRAGMENT_INDEX += 1
                if logging:
                    self.LOGGER.FRAGMENTS_INFO_ARRAY.append(current_fragment)
                return

            if ack is not None:
                if logging:
                    current_fragment['ack'] = ack
                    current_fragment['ack_received'] = True
                self.LOGGER.info("[ACK] Bytes: {}. Ressetting attempts counter to 0.".format(ack))
                self.ATTEMPTS = 0

                # Parse ACK
                # ack_object = ACK.parse_from_bytes(self.PROFILE, ack)
                ack_object = ACK.parse_from_hex(self.PROFILE, ack)

                if ack_object.is_receiver_abort():
                    if logging:
                        current_fragment['receiver_abort_message'] = ack
                        current_fragment['receiver_abort_received'] = True
                        self.LOGGER.FRAGMENTS_INFO_ARRAY.append(current_fragment)
                    self.LOGGER.error("ERROR: Receiver Abort received. Aborting communication.")
                    self.LOGGER.RECEIVER_ABORTED = True
                    raise ReceiverAbortError

                if not fragment_sent.expects_ack():
                    self.LOGGER.error("ERROR: ACK received but not requested ({}).".format(ack))
                    self.LOGGER.FRAGMENTS_INFO_ARRAY.append(current_fragment)
                    raise UnrequestedACKError

                # Extract data from ACK
                ack_window = ack_object.HEADER.W
                ack_window_number = ack_object.HEADER.WINDOW_NUMBER
                c = ack_object.HEADER.C
                bitmap = ack_object.BITMAP
                self.LOGGER.debug("ACK: {}".format(ack))
                self.LOGGER.debug("ACK window: {}".format(ack_window))
                self.LOGGER.debug("ACK bitmap: {}".format(bitmap))
                self.LOGGER.debug("ACK C bit: {}".format(c))
                self.LOGGER.debug("last window: {}".format(self.LAST_WINDOW))

                # Save ACKREQ data
                self.LOGGER.FRAGMENTS_INFO_ARRAY.append(current_fragment)

                # If the W field in the SCHC ACK corresponds to the last window of the SCHC Packet:
                if ack_window_number == self.LAST_WINDOW:
                    # If the C bit is set, the sender MAY exit successfully.
                    if c == '1':
                        self.LOGGER.info("Last ACK received, fragments reassembled successfully. End of transmission.")
                        self.LOGGER.FINISHED = True
                        self.FRAGMENT_INDEX += 1
                        return
                    # Otherwise,
                    else:
                        # If the Profile mandates that the last tile be sent in an All-1 SCHC Fragment
                        # (we are in the last window), .is_all_1() should be true:
                        if fragment_sent.is_all_1():
                            # This is the last bitmap, it contains the data before the All-1 fragment.
                            # The leftmost bit of this bitmap should always be 1, as the All-1 gets to the network
                            # to request the ACK.
                            last_bitmap = bitmap[:(len(self.FRAGMENTS) - 1) % self.PROFILE.WINDOW_SIZE]
                            self.LOGGER.debug("last bitmap {}".format(last_bitmap))
                            # If the SCHC ACK shows no missing tile at the receiver, abort.
                            # (C = 0 but transmission complete)
                            if last_bitmap == '' or (last_bitmap[0] == '1' and is_monochar(last_bitmap)):
                                self.LOGGER.error("ERROR: SCHC ACK shows no missing tile at the receiver.")
                                self.schc_send(SenderAbort(fragment_sent.PROFILE, fragment_sent.HEADER))

                            # Otherwise (fragments are lost),
                            else:
                                # Check for lost fragments.
                                for j in range(len(last_bitmap)):
                                    # If the j-th bit of the bitmap is 0, then the j-th fragment was lost.
                                    if last_bitmap[j] == '0':
                                        self.LOGGER.info("The {} ({} / {}) fragment was lost! "
                                                         "Sending again...".format(ordinal(j),
                                                                                   self.PROFILE.WINDOW_SIZE * ack_window_number + j,
                                                                                   len(self.FRAGMENTS)))
                                        # Try sending again the lost fragment.
                                        fragment_to_be_resent = Fragment(self.PROFILE,
                                                                         self.FRAGMENTS[
                                                                             self.PROFILE.WINDOW_SIZE * ack_window_number + j])
                                        self.LOGGER.debug("Lost fragment: {}".format(fragment_to_be_resent.to_string()))
                                        self.schc_send(fragment_to_be_resent, retransmit=True)

                                # Send All-1 again to end communication or check again for lost data.
                                self.schc_send(fragment_sent)

                        else:
                            self.LOGGER.error("ERROR: While being at the last window, the ACK-REQ was not an All-1. "
                                              "This is outside of the Sigfox scope.")
                            raise BadProfileError
                # Otherwise, there are lost fragments in a non-final window.
                else:

                    # Check for lost fragments.
                    for j in range(len(bitmap)):
                        # If the j-th bit of the bitmap is 0, then the j-th fragment was lost.
                        if bitmap[j] == '0':
                            self.LOGGER.info("The {} ({} / {}) fragment was lost! "
                                             "Sending again...".format(ordinal(j),
                                                                       self.PROFILE.WINDOW_SIZE * ack_window_number + j,
                                                                       len(self.FRAGMENTS)))
                            # Try sending again the lost fragment.
                            fragment_to_be_resent = Fragment(self.PROFILE,
                                                             self.FRAGMENTS[
                                                                 self.PROFILE.WINDOW_SIZE * ack_window_number + j])
                            self.LOGGER.debug("Lost fragment: {}".format(fragment_to_be_resent.to_string()))
                            self.schc_send(fragment_to_be_resent, retransmit=True)

                    if fragment_sent.is_all_1():
                        # Send All-1 again to end communication.
                        self.schc_send(fragment_sent)
                    elif fragment_sent.is_all_0():
                        # Continue with next window
                        self.FRAGMENT_INDEX += 1
                        self.CURRENT_WINDOW += 1

        except SCHCTimeoutError as e:
            if logging:
                current_fragment['sending_end'] = self.LOGGER.CHRONO.read()
                current_fragment['send_time'] = current_fragment['sending_end'] - current_fragment['sending_start']
                current_fragment['rssi'] = self.PROTOCOL.rssi()
                current_fragment['ack'] = ""
                current_fragment['ack_received'] = False
                self.LOGGER.info("OSError at: {}: ".format(self.LOGGER.CHRONO.read()))
                self.LOGGER.info('OSError number {}, {}'.format(e.args[0], e))

            # Save ACKREQ data
            if logging:
                self.LOGGER.FRAGMENTS_INFO_ARRAY.append(current_fragment)

            # If an ACK was expected
            if fragment_sent.is_all_1():
                # If the attempts counter is strictly less than MAX_ACK_REQUESTS, try again
                self.LOGGER.debug("attempts:{}".format(self.ATTEMPTS))
                if self.ATTEMPTS < self.PROFILE.MAX_ACK_REQUESTS:
                    self.LOGGER.info("SCHC Timeout reached while waiting for an ACK. Sending the ACK Request again...")
                    self.schc_send(fragment_sent, retransmit=False)
                # Else, exit with an error.
                else:
                    self.LOGGER.error("ERROR: MAX_ACK_REQUESTS reached. Sending Sender-Abort.")
                    abort = SenderAbort(self.PROFILE, fragment_sent.HEADER)
                    self.schc_send(abort)

            # If the ACK can be not sent (Sigfox only)
            if fragment_sent.is_all_0():
                self.LOGGER.info("All-0 timeout reached. Proceeding to next window.")
                self.FRAGMENT_INDEX += 1
                self.CURRENT_WINDOW += 1

            # Else, Sigfox communication failed.
            else:
                self.LOGGER.error("ERROR: Timeout reached.")
                raise NetworkDownError