Ejemplo n.º 1
0
    def test_is_all_1(self):
        profile = SigfoxProfile("UPLINK", "ACK ON ERROR", 1)
        rule_id = "0" * profile.RULE_ID_SIZE
        dtag = "0" * profile.T
        w = "0" * profile.M
        fcn = "1" * profile.N
        header = bitstring_to_bytes(rule_id + dtag + w + fcn)
        payload = bytearray.fromhex("3131313231333134313531")
        fragment = Fragment(profile, [header, payload])

        self.assertTrue(fragment.is_all_1())
Ejemplo n.º 2
0
def before_request():
    g.start = time.time()
    g.current_fragment = {}
    print('[before_request]: ' + request.endpoint)
    if request.endpoint == 'wyschc_get':
        if request.method == 'POST':
            print("[before_request]: POST RECEIVED")
            # BUCKET_NAME = config.BUCKET_NAME
            request_dict = request.get_json()
            print('[before_request]: Received Sigfox message: {}'.format(
                request_dict))
            # Get data and Sigfox Sequence Number.
            fragment = request_dict["data"]
            sigfox_sequence_number = request_dict["seqNumber"]
            device = request_dict['device']
            print('[before_request]: Data received from device id:{}, data:{}'.
                  format(device, request_dict['data']))
            # Parse fragment into "fragment = [header, payload]
            header_bytes = None
            header_first_hex = fragment[:1]
            if (header_first_hex) == '0' or '1':
                header = bytes.fromhex(fragment[:2])
                payload = bytearray.fromhex(fragment[2:])
                header_bytes = 1
            elif (header_first_hex) == '2':
                header = bytearray.fromhex(fragment[:4])
                payload = bytearray.fromhex(fragment[4:])
                header_bytes = 2
            else:
                print("Wrong header in fragment")
                return 'wrong header', 204

            data = [header, payload]
            # Initialize SCHC variables.
            profile_uplink = Sigfox("UPLINK", "ACK ON ERROR", header_bytes)
            profile_downlink = Sigfox("DOWNLINK", "NO ACK", header_bytes)
            buffer_size = profile_uplink.MTU
            n = profile_uplink.N
            m = profile_uplink.M
            # Convert to a Fragment class for easier manipulation.
            fragment_message = Fragment(profile_uplink, data)
            # Get some SCHC values from the fragment.
            rule_id = fragment_message.header.RULE_ID
            dtag = fragment_message.header.DTAG
            w = fragment_message.header.W
            g.current_fragment['s-downlink_enable'] = request_dict['ack']
            g.current_fragment['s-sending_start'] = time.time()
            g.current_fragment['s-data'] = request_dict["data"]
            g.current_fragment['FCN'] = fragment_message.header.FCN
            g.current_fragment['s-fragment_size'] = len(data)
            g.current_fragment['RULE_ID'] = fragment_message.header.RULE_ID
            g.current_fragment['W'] = fragment_message.header.W
            g.current_fragment['seqNumber'] = sigfox_sequence_number
            print('[before_request]: seqNum:{}, RULE_ID: {} W: {}, FCN: {}'.
                  format(sigfox_sequence_number,
                         fragment_message.header.RULE_ID,
                         fragment_message.header.W,
                         fragment_message.header.FCN))
            print('[before_request]: {}'.format(g.current_fragment))
Ejemplo n.º 3
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()
Ejemplo n.º 4
0
	def __init__(self, profile, schc_fragments):
		self.profile = profile

		for fragment in schc_fragments:
			if fragment != b'':
				self.schc_fragments.append(Fragment(self.profile, fragment))

		for fragment in self.schc_fragments:
			self.rule_set.add(fragment.header.RULE_ID)
			self.dtag_set.add(fragment.header.DTAG)
			self.window_set.add(fragment.header.W)
			self.fcn_set.add(fragment.header.FCN)
Ejemplo n.º 5
0
    def test_init(self):
        hex_data = "053131313231333134313531"
        header = bytes.fromhex(hex_data[:2])
        payload = bytearray.fromhex(hex_data[2:])
        data = [header, payload]
        profile = SigfoxProfile("UPLINK", "ACK ON ERROR", 1)
        fragment = Fragment(profile, data)
        abort = ReceiverAbort(profile, fragment.HEADER)

        self.assertEqual(type(abort.PROFILE), SigfoxProfile)
        self.assertEqual(abort.HEADER.RULE_ID, fragment.HEADER.RULE_ID)
        self.assertEqual(abort.HEADER.DTAG, fragment.HEADER.DTAG)
        self.assertEqual(abort.HEADER.W, fragment.HEADER.W)
        self.assertEqual(len(abort.to_string()), 64)
        self.assertTrue(issubclass(type(abort), ACK))
        self.assertTrue(abort.is_receiver_abort())
Ejemplo n.º 6
0
    def test_init(self):
        hex_data = "053131313231333134313531"
        header = bytes.fromhex(hex_data[:2])
        payload = bytearray.fromhex(hex_data[2:])
        data = [header, payload]
        profile = Sigfox("UPLINK", "ACK ON ERROR", 1)
        fragment = Fragment(profile, data)
        abort = SenderAbort(profile, fragment.header)

        self.assertEqual(type(abort.profile), Sigfox)
        self.assertEqual(abort.header.RULE_ID, fragment.header.RULE_ID)
        self.assertEqual(abort.header.DTAG, fragment.header.DTAG)
        self.assertEqual(abort.header.W, fragment.header.W)
        self.assertTrue(
            abort.header.FCN[0] == '1' and all(abort.header.FCN),
            msg=f"{abort.header.FCN[0] == '1'} and {all(abort.header.FCN)}")
        self.assertTrue(
            abort.payload.decode()[0] == '0' and all(abort.payload.decode()),
            msg=f"{abort.payload[0] == '0'} and {all(abort.payload)}")
        self.assertFalse(abort.is_all_1())
        self.assertTrue(abort.is_sender_abort())
Ejemplo n.º 7
0
    def test_init(self):
        hex_data = "053131313231333134313531"
        header = bytes.fromhex(hex_data[:2])
        payload = bytearray.fromhex(hex_data[2:])
        data = [header, payload]
        profile = SigfoxProfile("UPLINK", "ACK ON ERROR", 1)
        fragment = Fragment(profile, data)
        abort = SenderAbort(profile, fragment.HEADER)

        self.assertEqual(type(abort.PROFILE), SigfoxProfile)
        self.assertEqual(abort.HEADER.RULE_ID, fragment.HEADER.RULE_ID)
        self.assertEqual(abort.HEADER.DTAG, fragment.HEADER.DTAG)
        self.assertEqual(abort.HEADER.W, fragment.HEADER.W)
        self.assertTrue(
            abort.HEADER.FCN[0] == '1' and all(abort.HEADER.FCN),
            msg=f"{abort.HEADER.FCN[0] == '1'} and {all(abort.HEADER.FCN)}")
        self.assertTrue(
            abort.PAYLOAD.decode()[0] == '0' and all(abort.PAYLOAD.decode()),
            msg=f"{abort.PAYLOAD[0] == '0'} and {all(abort.PAYLOAD)}")
        self.assertFalse(abort.is_all_1())
        self.assertTrue(abort.is_sender_abort())

        hex_data = "1f353235"
        header = bytes.fromhex(hex_data[:2])
        payload = bytearray.fromhex(hex_data[2:])
        data = [header, payload]
        profile = SigfoxProfile("UPLINK", "ACK ON ERROR", 1)
        fragment = Fragment(profile, data)

        self.assertFalse(fragment.is_sender_abort())

        hex_string = "1f3030303030303030303030"
        fragment_sent = Fragment.from_hex(
            SigfoxProfile("UPLINK", "ACK ON ERROR", 1), hex_string)
        abort = SenderAbort(fragment_sent.PROFILE, fragment_sent.HEADER)

        self.assertTrue(abort.is_sender_abort())
Ejemplo n.º 8
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
Ejemplo n.º 9
0
		data = bytes(fragment_list[i][0] + fragment_list[i][1])

		if verbose:
			print(str(i) + "th fragment:")
			print(data)

		current_size += len(fragment_list[i][1])
		percent = round(float(current_size) / float(total_size) * 100, 2)

		# Send the data.
		print("Sending...")
		the_socket.sendto(data, address)
		print(str(current_size) + " / " + str(total_size) + ", " + str(percent) + "%")

		# Convert to a Fragment class for easier manipulation.
		fragment = Fragment(profile_uplink, fragment_list[i])

	# If a fragment is an All-0 or an All-1:
	if retransmitting or fragment.is_all_0() or fragment.is_all_1():

		# Juan Carlos dijo que al enviar un ACKREQ el contador se reinicia.
		attempts = 0

		# Set the timeout for RETRANSMISSION_TIMER_VALUE.
		the_socket.settimeout(profile_uplink.RETRANSMISSION_TIMER_VALUE)

		while attempts < profile_uplink.MAX_ACK_REQUESTS:

			# Try receiving an ACK from the receiver.
			try:
				ack, address = the_socket.recvfrom(profile_downlink.MTU)
Ejemplo n.º 10
0
def hello_get(request):
    """HTTP Cloud Function.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """

    # Wait for an HTTP POST request.
    if request.method == 'POST':

        # Get request JSON.
        print("POST RECEIVED")
        request_dict = request.get_json()
        print('Received Sigfox message: {}'.format(request_dict))

        # Get data and Sigfox Sequence Number.
        fragment = request_dict["data"]
        sigfox_sequence_number = request_dict["seqNumber"]

        # Initialize Cloud Storage variables.
        BUCKET_NAME = config.BUCKET_NAME

        # Parse fragment into "fragment = [header, payload]
        header_bytes = None
        header_first_hex = fragment[:1]

        if (header_first_hex) == '0' or (header_first_hex) =='1':
            header = bytes.fromhex(fragment[:2])
            payload = bytearray.fromhex(fragment[2:])
            header_bytes = 1
        elif (header_first_hex) == '2':
            header = bytearray.fromhex(fragment[:4])
            payload = bytearray.fromhex(fragment[4:])
            header_bytes = 2
        else:
            print("Wrong header in fragment")
            return 'wrong header', 204

        if bytearray.fromhex(fragment[2:]).decode() == "CLEAN":
            try:
                _ = requests.post(
                    url=config.CLEAN_URL,
                    json={"header_bytes": header_bytes,
                          "from_lopy": "True"},
                    timeout=0.1)
            except requests.exceptions.ReadTimeout:
                pass
            return '', 204
        elif bytearray.fromhex(fragment[2:]).decode() == "CLEAN_ALL":
            try:
                _ = requests.post(
                    url=config.CLEAN_URL,
                    json={"header_bytes": header_bytes,
                          "from_lopy": "True"},
                    timeout=0.1)
            except requests.exceptions.ReadTimeout:
                pass
            return '', 204

        data = [header, payload]

        # Initialize SCHC variables.
        profile_uplink = Sigfox("UPLINK", "ACK ON ERROR", header_bytes)
        profile_downlink = Sigfox("DOWNLINK", "NO ACK", header_bytes)
        buffer_size = profile_uplink.MTU
        n = profile_uplink.N
        m = profile_uplink.M

        # If fragment size is greater than buffer size, ignore it and end function.
        if len(fragment) / 2 * 8 > buffer_size:  # Fragment is hex, 1 hex = 1/2 byte
            return json.dumps({"message": "Fragment size is greater than buffer size D:"}), 200

        # If the folder named "all windows" does not exist, create it along with all subdirectories.
        if not exists_blob("all_windows/"):
            print("INITIALIZING... (be patient)")
            create_folder("all_windows/")

            # For each window in the SCHC Profile, create its blob.
            for i in range(2 ** m):
                create_folder("all_windows/window_%d/" % i)

                # For each fragment in the SCHC Profile, create its blob.
                for j in range(2 ** n - 1):
                    upload_blob("", "all_windows/window_%d/fragment_%d_%d" % (i, i, j))

                # Create the blob for each bitmap.
                if not exists_blob("all_windows/window_%d/bitmap_%d" % (i, i) or size_blob("all_windows/window_%d/bitmap_%d" % (i, i)) == 0):
                    bitmap = ""
                    for b in range(profile_uplink.BITMAP_SIZE):
                        bitmap += "0"
                    upload_blob(bitmap, "all_windows/window_%d/bitmap_%d" % (i, i))

        print("BLOBs created")

        # Find current experiment number
        current_experiment = 0
        for blob in blob_list():
            if blob.startswith("DL_LOSSES_"):
                current_experiment += 1
        print(f"This is the {current_experiment}th experiment.")

        # Initialize empty window
        window = []
        for i in range(2 ** n - 1):
            window.append([b"", b""])

        # Compute the fragment compressed number (FCN) from the Profile
        fcn_dict = {}
        for j in range(2 ** n - 1):
            fcn_dict[zfill(bin((2 ** n - 2) - (j % (2 ** n - 1)))[2:], n)] = j


        # Convert to a Fragment class for easier manipulation.
        fragment_message = Fragment(profile_uplink, data)

        if 'enable_losses' in request_dict:
            if request_dict['enable_losses'] == "True":
                loss_rate = request_dict["loss_rate"]
                # loss_rate = 10
                coin = random.random()
                print('loss rate: {}, random toss:{}'.format(loss_rate, coin * 100))
                if coin * 100 < loss_rate:
                    print("[LOSS] The fragment was lost.")
                    if fragment_message.is_all_1():
                        last_sequence_number = read_blob("SSN")
                        print("SSN is {} and last SSN is {}".format(sigfox_sequence_number, last_sequence_number))
                        if int(sigfox_sequence_number) - int(last_sequence_number) == 1:
                            # We do that to save the last SSN value for future use (when the next All-1 Arrives)
                            # In a Real Loss Scenario we will not know the SSN...
                            upload_blob(sigfox_sequence_number, "SSN")
                    return 'fragment lost', 204

        # Get current window for this fragment.
        current_window = int(fragment_message.header.W, 2)

        # Get the current bitmap.
        bitmap = read_blob("all_windows/window_%d/bitmap_%d" % (current_window, current_window))

        # Try getting the fragment number from the FCN dictionary.
        try:
            fragment_number = fcn_dict[fragment_message.header.FCN]
            upload_blob(fragment_number, "fragment_number")

            time_received = int(request_dict["time"])
            if exists_blob("timestamp"):
                # Check time validation.
                last_time_received = int(read_blob("timestamp"))

                # If this is not the very first fragment and the inactivity timer has been reached, ignore the message.
                # TODO: Send SCHC abort message.
                if str(fragment_number) != "0" and str(
                        current_window) != "0" and time_received - last_time_received > profile_uplink.INACTIVITY_TIMER_VALUE:
                    return json.dumps({"message": "Inactivity timer reached. Message ignored."}), 200

            # Upload current timestamp.
            upload_blob(time_received, "timestamp")

            # Print some data for the user.
            print("[RECV] This corresponds to the " + str(fragment_number) + "th fragment of the " + str(
                current_window) + "th window.")
            print("[RECV] Sigfox sequence number: " + str(sigfox_sequence_number))

            #Controlled Errors check
            # losses_mask = read_blob(BUCKET_NAME, "all_windows/window_%d/losses_mask_%d" % (current_window, current_window))
            # if (losses_mask[fragment_number]) != '0':
            #     losses_mask = replace_bit(losses_mask, fragment_number, str(int(losses_mask[fragment_number])-1))
            #     upload_blob(BUCKET_NAME, losses_mask, "all_windows/window_%d/losses_mask_%d" % (current_window, current_window))
            #     print("[LOSS] The fragment was lost.")
            #     return 'fragment lost', 204

            # Update bitmap and upload it.
            bitmap = replace_bit(bitmap, fragment_number, '1')
            upload_blob(bitmap, "all_windows/window_%d/bitmap_%d" % (current_window, current_window))

            # Upload the fragment data.
            upload_blob(data[0].decode("ISO-8859-1") + data[1].decode("utf-8"),
                        "all_windows/window_%d/fragment_%d_%d" % (current_window, current_window, fragment_number))

        # If the FCN could not been found, it almost certainly is the final fragment.
        except KeyError:
            print("[RECV] This seems to be the final fragment.")
            # Upload current timestamp.
            time_received = int(request_dict["time"])
            upload_blob(time_received, "timestamp")
            print("is All-1:{}, is All-0:{}".format(fragment_message.is_all_1(), fragment_message.is_all_0()))
            # print("RULE_ID: {}, W:{}, FCN:{}".format(fragment.header.RULE_ID, fragment.header.W, fragment.header.FCN))
            # Update bitmap and upload it.
            bitmap = replace_bit(bitmap, len(bitmap) - 1, '1')
            upload_blob(bitmap, "all_windows/window_%d/bitmap_%d" % (current_window, current_window))

        # Get some SCHC values from the fragment.
        rule_id = fragment_message.header.RULE_ID
        dtag = fragment_message.header.DTAG
        w = fragment_message.header.W

        # Get last and current Sigfox sequence number (SSN)
        last_sequence_number = 0
        if exists_blob("SSN"):
            last_sequence_number = read_blob("SSN")
        upload_blob(sigfox_sequence_number, "SSN")

        # If the fragment is at the end of a window (ALL-0 or ALL-1)
        if fragment_message.is_all_0() or fragment_message.is_all_1():

            # Prepare the ACK bitmap. Find the first bitmap with a 0 in it.
            for i in range(current_window + 1):
                bitmap_ack = read_blob("all_windows/window_%d/bitmap_%d" % (i, i))
                print(bitmap_ack)
                window_ack = i
                if '0' in bitmap_ack:
                    break

            # If the ACK bitmap has a 0 at the end of a non-final window, a fragment has been lost.
            if fragment_message.is_all_0() and '0' in bitmap_ack:
                if 'enable_dl_losses' in request_dict:
                    if request_dict['enable_dl_losses'] == "True":
                        coin = random.random()
                        print('loss rate: {}, random toss:{}'.format(loss_rate, coin * 100))
                        if coin * 100 < loss_rate:
                            print("[LOSS-ALL0] The Downlink NACK was lost.")

                            upload_blob(read_blob(f"DL_LOSSES_{current_experiment}") + "\n Lost DL message in window {}".format(current_window), f"DL_LOSSES_{current_experiment}")
                            return 'Downlink lost', 204
                print("[ALL0] Sending ACK for lost fragments...")
                print("bitmap with errors -> {}".format(bitmap_ack))
                # Create an ACK message and send it.
                ack = ACK(profile_downlink, rule_id, dtag, zfill(format(window_ack, 'b'), m), bitmap_ack, '0')
                response_json = send_ack(request_dict, ack)
                print("Response content -> {}".format(response_json))
                return response_json, 200

            # If the ACK bitmap is complete and the fragment is an ALL-0, send an ACK
            # This is to be modified, as ACK-on-Error does not need an ACK for every window.
            if fragment_message.is_all_0() and bitmap[0] == '1' and all(bitmap):
                print("[ALL0] All Fragments of current window received")
                print("[ALL0] No need to send an ACK")
                # print("[ALLX] Sending ACK after window...")

                # Create an ACK message and send it.
                # ack = ACK(profile_downlink, rule_id, dtag, w, bitmap, '0')
                # response_json = send_ack(request_dict, ack)
                # print("200, Response content -> {}".format(response_json))
                # Response to continue, no ACK is sent Back.
                return '', 204
                # return response_json, 200

            # If the fragment is an ALL-1
            if fragment_message.is_all_1():
                # response = {request_dict['device']: {'downlinkData': '080fffffffffffff'}}
                # print("response -> {}".format(json.dumps(response)))
                # return json.dumps(response), 200
                # The bitmap in the last window follows the following regular expression: "1*0*1*"
                # Since the ALL-1, if received, changes the least significant bit of the bitmap.
                # For a "complete" bitmap in the last window, there shouldn't be non-consecutive zeroes:
                # 1110001 is a valid bitmap, 1101001 is not.

                pattern2 = re.compile("0*1")
                if pattern2.fullmatch(bitmap_ack):
                    if 'enable_dl_losses' in request_dict:
                        if request_dict['enable_dl_losses'] == "True":
                            coin = random.random()
                            print('loss rate: {}, random toss:{}'.format(loss_rate, coin * 100))
                            if coin * 100 < loss_rate:
                                print("[LOSS-ALL1] The Downlink ACK was lost.")
                                upload_blob(read_blob(f"DL_LOSSES_{current_experiment}") + "\n Lost DL message in window {}".format(current_window), f"DL_LOSSES_{current_experiment}")
                                return 'Downlink lost', 204
                    print("SSN is {} and last SSN is {}".format(sigfox_sequence_number, last_sequence_number))
                    # Downlink Controlled Errors
                    dl_errors = int(read_blob("dl_errors"))
                    if dl_errors == 0:
                        last_index = 0
                        upload_blob(data[0].decode("ISO-8859-1") + data[1].decode("utf-8"),
                                    "all_windows/window_%d/fragment_%d_%d" % (
                                        current_window, current_window, last_index))
                        print("Info for reassemble: last_index:{}, current_window:{}".format(last_index, current_window))
                        try:
                            print('Activating reassembly process...')
                            _ = requests.post(
                                url=config.REASSEMBLE_URL,
                                json={"last_index": last_index, "current_window": current_window,
                                      "header_bytes": header_bytes},
                                timeout=0.1)
                        except requests.exceptions.ReadTimeout:
                            pass

                        # Send last ACK to end communication.
                        print("[ALL1] Reassembled: Sending last ACK")
                        bitmap = ''
                        for k in range(profile_uplink.BITMAP_SIZE):
                            bitmap += '0'
                        last_ack = ACK(profile_downlink, rule_id, dtag, w, bitmap, '1')
                        response_json = send_ack(request_dict, last_ack)
                        # return response_json, 200
                        # response_json = send_ack(request_dict, last_ack)
                        print("200, Response content -> {}".format(response_json))
                        return response_json, 200
                    else:
                        dl_errors -= 1
                        upload_blob(dl_errors, "dl_errors")
                        print("[DL-ERROR] We simulate a downlink error. We don't send an ACK")
                        return '', 204

                pattern = re.compile("1*0*1")

                # If the bitmap matches the regex, check if the last two received fragments are consecutive.
                if pattern.fullmatch(bitmap_ack):
                    print("SSN is {} and last SSN is {}".format(sigfox_sequence_number,last_sequence_number))
                    # If the last two received fragments are consecutive, accept the ALL-1 and start reassembling
                    if 'enable_dl_losses' in request_dict:
                        if request_dict['enable_dl_losses'] == "True":
                            coin = random.random()
                            print('loss rate: {}, random toss:{}'.format(loss_rate, coin * 100))
                            if coin * 100 < loss_rate:
                                print("[LOSS-ALL1] The Downlink ACK was lost.")
                                upload_blob(read_blob(f"DL_LOSSES_{current_experiment}") + "\n Lost DL message in window {}".format(current_window), f"DL_LOSSES_{current_experiment}")
                                return 'Downlink lost', 204
                    if int(sigfox_sequence_number) - int(last_sequence_number) == 1:
                        # Downlink Controlled Errors
                        dl_errors = int(read_blob("dl_errors"))
                        if dl_errors == 0:
                            last_index = int(read_blob("fragment_number")) + 1
                            upload_blob(data[0].decode("ISO-8859-1") + data[1].decode("utf-8"),
                                        "all_windows/window_%d/fragment_%d_%d" % (
                                            current_window, current_window, last_index))
                            print("Info for reassemble: last_index:{}, current_window:{}".format(last_index,current_window))
                            try:
                                print('Activating reassembly process...')
                                _ = requests.post(url=config.REASSEMBLE_URL,
                                                  json={"last_index": last_index, "current_window": current_window, "header_bytes": header_bytes},
                                                  timeout=0.1)
                            except requests.exceptions.ReadTimeout:
                                pass

                            # Send last ACK to end communication.
                            print("[ALL1] Reassembled: Sending last ACK")
                            bitmap = ''
                            for k in range(profile_uplink.BITMAP_SIZE):
                                bitmap += '0'
                            last_ack = ACK(profile_downlink, rule_id, dtag, w, bitmap, '1')
                            response_json = send_ack(request_dict, last_ack)
                            # return response_json, 200
                            # response_json = send_ack(request_dict, last_ack)
                            print("200, Response content -> {}".format(response_json))
                            return response_json, 200
                        else:
                            dl_errors -= 1
                            upload_blob(dl_errors, "dl_errors")
                            print("[DL-ERROR] We simulate a downlink error. We don't send an ACK")
                            return '', 204
                    else:
                        # Send NACK at the end of the window.
                        if 'enable_dl_losses' in request_dict:
                            if request_dict['enable_dl_losses'] == "True":
                                coin = random.random()
                                print('loss rate: {}, random toss:{}'.format(loss_rate, coin * 100))
                                if coin * 100 < loss_rate:
                                    print("[LOSS-ALL1] The Downlink NACK was lost.")
                                    upload_blob(read_blob(f"DL_LOSSES_{current_experiment}") + "\n Lost DL message in window {}".format(current_window), f"DL_LOSSES_{current_experiment}")
                                    return 'Downlink lost', 204
                        print("[ALLX] Sending NACK for lost fragments because of SSN...")
                        ack = ACK(profile_downlink, rule_id, dtag, zfill(format(window_ack, 'b'), m), bitmap_ack, '0')
                        response_json = send_ack(request_dict, ack)
                        return response_json, 200

                    # If they are not, there is a gap between two fragments: a fragment has been lost.
                # The same happens if the bitmap doesn't match the regex.
                else:
                    # Send NACK at the end of the window.
                    if 'enable_dl_losses' in request_dict:
                        if request_dict['enable_dl_losses'] == "True":
                            coin = random.random()
                            print('loss rate: {}, random toss:{}'.format(loss_rate, coin * 100))
                            if coin * 100 < loss_rate:
                                print("[LOSS-ALL1] The Downlink NACK was lost.")
                                upload_blob(read_blob(f"DL_LOSSES_{current_experiment}") + "\n Lost DL message in window {}".format(current_window), f"DL_LOSSES_{current_experiment}")
                                return 'Downlink lost', 204
                    print("[ALLX] Sending NACK for lost fragments...")
                    ack = ACK(profile_downlink, rule_id, dtag, zfill(format(window_ack, 'b'), m), bitmap_ack, '0')
                    response_json = send_ack(request_dict, ack)
                    return response_json, 200

        return '', 204

    else:
        print('Invalid HTTP Method to invoke Cloud Function. Only POST supported')
        return abort(405)
Ejemplo n.º 11
0
	fcn_dict[zfill(bin((2 ** n - 2) - (j % (2 ** n - 1)))[2:], 3)] = j

# Start receiving fragments.
while True:


	# Receive fragment
	try:
		print(bitmap)
		fragment, address = the_socket.recvfrom(buffer_size)

		# A fragment has the format "fragment = [header, payload]".
		data = [bytes([fragment[0]]), bytearray(fragment[1:])]

		# Convert to a Fragment class for easier manipulation.
		fragment_message = Fragment(profile_uplink, data)

		# This avoids All-0 and All-1 fragments being lost for testing purposes.
		# TODO: What happens when All-0 or All-1 get lost?
		if not fragment_message.is_all_0() and not fragment_message.is_all_1():

			# Lose packet with certain probability and go to the start of the loop.
			coin = random.random()
			if coin * 100 < loss_rate:
				print("[LOSS] The packet was lost.")
				continue

		# Try finding the fragment number from the FCN of the fragment.
		try:
			fragment_number = fcn_dict[fragment_message.header.FCN]
			current_window = int(fragment_message.header.W, 2)
Ejemplo n.º 12
0
def schc_post():
    """HTTP Cloud Function.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """

    REASSEMBLER_URL = "http://localhost:5000/reassembler"
    CLEANUP_URL = "http://localhost:5000/cleanup"

    # File where we will store authentication credentials after acquiring them.
    os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = config.CLIENT_SECRETS_FILE
    # Wait for an HTTP POST request.
    if request.method == 'POST':

        # Get request JSON.
        print("POST RECEIVED")
        request_dict = request.get_json()
        print('Received Sigfox message: {}'.format(request_dict))

        # Get data and Sigfox Sequence Number.
        raw_data = request_dict["data"]
        sigfox_sequence_number = request_dict["seqNumber"]
        ack_req = request_dict["ack"]

        # Initialize Cloud Storage variables.
        BUCKET_NAME = config.BUCKET_NAME

        header_first_hex = raw_data[:1]
        if header_first_hex == '0' or header_first_hex == '1':
            header = bytes.fromhex(raw_data[:2])
            payload = bytearray.fromhex(raw_data[2:])
            header_bytes = 1
        elif header_first_hex == '2':
            header = bytearray.fromhex(raw_data[:4])
            payload = bytearray.fromhex(raw_data[4:])
            header_bytes = 2
        else:
            print("Wrong header in raw_data")
            return 'wrong header', 204

        # Initialize SCHC variables.
        profile = Sigfox("UPLINK", "ACK ON ERROR", header_bytes)
        n = profile.N
        m = profile.M

        # If fragment size is greater than buffer size, ignore it and end function.
        if len(
                raw_data
        ) / 2 * 8 > profile.UPLINK_MTU:  # Fragment is hex, 1 hex = 1/2 byte
            return json.dumps(
                {"message": "Fragment size is greater than buffer size"}), 200

        # If the folder named "all windows" does not exist, create it along with all subdirectories.
        initialize_blobs(BUCKET_NAME, profile)

        # Compute the fragment compressed number (FCN) from the Profile
        fcn_dict = {}
        for j in range(2**n - 1):
            fcn_dict[zfill(bin((2**n - 2) - (j % (2**n - 1)))[2:], 3)] = j

        # Parse raw_data into "data = [header, payload]
        # Convert to a Fragment class for easier manipulation.
        header = bytes.fromhex(raw_data[:2])
        payload = bytearray.fromhex(raw_data[2:])
        data = [header, payload]
        fragment_message = Fragment(profile, data)

        if fragment_message.is_sender_abort():
            print("Sender-Abort received")
            try:
                print("Cleaning")
                _ = requests.post(url=CLEANUP_URL,
                                  json={"header_bytes": header_bytes},
                                  timeout=0.1)
            except requests.exceptions.ReadTimeout:
                pass
            return 'Sender-Abort received', 204

        # Get data from this fragment.
        fcn = fragment_message.header.FCN
        rule_id = fragment_message.header.RULE_ID
        dtag = fragment_message.header.DTAG
        current_window = int(fragment_message.header.W, 2)

        # Get the current bitmap.
        bitmap = read_blob(
            BUCKET_NAME,
            f"all_windows/window_{current_window}/bitmap_{current_window}")

        # Controlling deterministic losses. This loads the file "loss_mask.txt" which states when should a fragment be
        # lost, separated by windows.
        fd = None
        try:
            fd = open(config.LOSS_MASK_MODIFIED, "r")
        except FileNotFoundError:
            fd = open(config.LOSS_MASK, "r")
        finally:
            loss_mask = []
            for line in fd:
                if not line.startswith("#"):
                    for char in line:
                        try:
                            loss_mask.append(int(char))
                        except ValueError:
                            pass
            fd.close()

        print(f"Loss mask: {loss_mask}")

        # Controlling random losses.
        if 'enable_losses' in request_dict and not (
                fragment_message.is_all_0() or fragment_message.is_all_1()):
            if request_dict['enable_losses']:
                loss_rate = request_dict["loss_rate"]
                # loss_rate = 10
                coin = random.random()
                print(f'loss rate: {loss_rate}, random toss:{coin * 100}')
                if coin * 100 < loss_rate:
                    print("[LOSS] The fragment was lost.")
                    return 'fragment lost', 204

        # Check if the fragment is an All-1
        if is_monochar(fcn) and fcn[0] == '1':
            print("[RECV] This is an All-1.")

            # Check if fragment is to be lost (All-1 is the very last fragment)
            if loss_mask[-1] != 0:
                loss_mask[-1] -= 1
                with open("loss_mask_modified.txt", "w") as fd:
                    for i in loss_mask:
                        fd.write(str(i))
                print(f"[RECV] Fragment lost.")
                return 'fragment lost', 204

            # Inactivity timer validation
            time_received = int(request_dict["time"])

            if exists_blob(BUCKET_NAME, "timestamp"):
                # Check time validation.
                last_time_received = int(read_blob(BUCKET_NAME, "timestamp"))
                print(f"[RECV] Previous timestamp: {last_time_received}")
                print(f"[RECV] This timestamp: {time_received}")

                # If the inactivity timer has been reached, abort communication.
                if time_received - last_time_received > profile.INACTIVITY_TIMER_VALUE:
                    print("[RECV] Inactivity timer reached. Ending session.")
                    receiver_abort = ReceiverAbort(profile,
                                                   fragment_message.header)
                    print("Sending Receiver Abort")
                    response_json = send_ack(request_dict, receiver_abort)
                    print(f"Response content -> {response_json}")
                    try:
                        print("Cleaning Inactivity timer reached")
                        _ = requests.post(url=CLEANUP_URL,
                                          json={"header_bytes": header_bytes},
                                          timeout=0.1)
                    except requests.exceptions.ReadTimeout:
                        pass
                    return response_json, 200

            # Update timestamp
            upload_blob(BUCKET_NAME, time_received, "timestamp")

            # Update bitmap and upload it.
            bitmap = replace_bit(bitmap, len(bitmap) - 1, '1')
            print(f"Bitmap is now {bitmap}")
            upload_blob(
                BUCKET_NAME, bitmap,
                f"all_windows/window_{current_window}/bitmap_{current_window}")
            # Upload the fragment data.
            upload_blob(
                BUCKET_NAME, data[0].decode("utf-8") + data[1].decode("utf-8"),
                f"all_windows/window_{current_window}/fragment_{current_window}_{profile.WINDOW_SIZE - 1}"
            )

        # Else, it is a normal fragment.
        else:
            fragment_number = fcn_dict[fragment_message.header.FCN]

            # Check if fragment is to be lost
            position = current_window * profile.WINDOW_SIZE + fragment_number
            if loss_mask[position] != 0:
                loss_mask[position] -= 1
                with open(config.LOSS_MASK_MODIFIED, "w") as fd:
                    for i in loss_mask:
                        fd.write(str(i))
                print(f"[RECV] Fragment lost.")
                return 'fragment lost', 204

            # Inactivity timer validation
            time_received = int(request_dict["time"])

            if exists_blob(BUCKET_NAME, "timestamp"):
                # Check time validation.
                last_time_received = int(read_blob(BUCKET_NAME, "timestamp"))
                print(f"[RECV] Previous timestamp: {last_time_received}")
                print(f"[RECV] This timestamp: {time_received}")

                # If the inactivity timer has been reached, abort communication.
                if time_received - last_time_received > profile.INACTIVITY_TIMER_VALUE:
                    print("[RECV] Inactivity timer reached. Ending session.")
                    receiver_abort = ReceiverAbort(profile,
                                                   fragment_message.header)
                    print("Sending Receiver Abort")
                    response_json = send_ack(request_dict, receiver_abort)
                    print(f"Response content -> {response_json}")
                    try:
                        _ = requests.post(url=CLEANUP_URL,
                                          json={"header_bytes": header_bytes},
                                          timeout=0.1)
                    except requests.exceptions.ReadTimeout:
                        pass
                    return response_json, 200

            # Update timestamp
            upload_blob(BUCKET_NAME, time_received, "timestamp")

            # Update Sigfox sequence number JSON
            sequence_numbers = json.loads(read_blob(BUCKET_NAME, "SSN"))
            sequence_numbers[position] = request_dict["seqNumber"]
            print(sequence_numbers)
            upload_blob(BUCKET_NAME, json.dumps(sequence_numbers), "SSN")

            upload_blob(BUCKET_NAME, fragment_number, "fragment_number")

            # Print some data for the user.
            print(
                f"[RECV] This corresponds to the {str(fragment_number)}th fragment "
                f"of the {str(current_window)}th window.")
            print(
                f"[RECV] Sigfox sequence number: {str(sigfox_sequence_number)}"
            )

            # Update bitmap and upload it.
            bitmap = replace_bit(bitmap, fragment_number, '1')
            print(f"Bitmap is now {bitmap}")
            upload_blob(
                BUCKET_NAME, bitmap,
                f"all_windows/window_{current_window}/bitmap_{current_window}")

            # Upload the fragment data.
            upload_blob(
                BUCKET_NAME, data[0].decode("utf-8") + data[1].decode("utf-8"),
                f"all_windows/window_{current_window}/fragment_{current_window}_{fragment_number}"
            )

        # If the fragment requests an ACK...
        if ack_req:

            # Prepare the ACK bitmap. Find the first bitmap with a 0 in it.
            # This bitmap corresponds to the lowest-numered window with losses.
            bitmap_ack = None
            window_ack = None
            for i in range(current_window + 1):
                bitmap_ack = read_blob(BUCKET_NAME,
                                       f"all_windows/window_{i}/bitmap_{i}")
                print(bitmap_ack)
                window_ack = i
                if '0' in bitmap_ack:
                    break

            # The final window is only accessible through All-1.
            # If All-0, check non-final windows
            if fragment_message.is_all_0():
                # If the ACK bitmap has a 0 at a non-final window, a fragment has been lost.
                if '0' in bitmap_ack:
                    print(
                        "[ALL0] Lost fragments have been detected. Preparing ACK."
                    )
                    print(f"[ALL0] Bitmap with errors -> {bitmap_ack}")
                    ack = ACK(profile=profile,
                              rule_id=rule_id,
                              dtag=dtag,
                              w=zfill(format(window_ack, 'b'), m),
                              c='0',
                              bitmap=bitmap_ack)
                    response_json = send_ack(request_dict, ack)
                    print(f"Response content -> {response_json}")
                    print("[ALL0] ACK sent.")
                    return response_json, 200
                # If all bitmaps are complete up to this point, no losses are detected.
                else:
                    print("[ALL0] No losses have been detected.")
                    print("Response content -> ''")
                    return '', 204

            # If the fragment is All-1, the last window should be considered.
            if fragment_message.is_all_1():

                # First check for 0s in the bitmap. If the bitmap is of a non-final window, send corresponding ACK.
                if current_window != window_ack and '0' in bitmap_ack:
                    print(
                        "[ALL1] Lost fragments have been detected. Preparing ACK."
                    )
                    print(f"[ALL1] Bitmap with errors -> {bitmap_ack}")
                    ack = ACK(profile=profile,
                              rule_id=rule_id,
                              dtag=dtag,
                              w=zfill(format(window_ack, 'b'), m),
                              c='0',
                              bitmap=bitmap_ack)
                    response_json = send_ack(request_dict, ack)
                    print(f"Response content -> {response_json}")
                    print("[ALL1] ACK sent.")
                    return response_json, 200

                # If the bitmap is of the final window, check the following regex.
                else:
                    # The bitmap in the last window follows the following regular expression: "1*0*1*"
                    # Since the ALL-1, if received, changes the least significant bit of the bitmap.
                    # For a "complete" bitmap in the last window, there shouldn't be non-consecutive zeroes:
                    # 1110001 is a valid bitmap, 1101001 is not.
                    # The bitmap may or may not contain the 0s.
                    pattern = re.compile("1*0*1")

                    # If the bitmap matches the regex, check if there are still lost fragments.
                    if pattern.fullmatch(bitmap_ack) is not None:
                        # The idea is the following:
                        # Assume a fragment has been lost, but the regex has been matched.
                        # For example, we want a bitmap 1111111 but due to a loss we have 1111101.
                        # This incomplete bitmap matches the regex.
                        # We should note that here the SSN of the All-1 and the penultimate fragment received
                        # are not consecutive.
                        # Retransmitting the lost fragment and resending the All-1 solves that problem.
                        # There is another problematic case: we want a bitmap 1111111 but due to losses we have 1111001.
                        # If the second of those two lost fragments is retransmitted, the new bitmap, 1111011, does not
                        # match the regex. If, instead, the first of those fragments is retransmitted, the new bitmap
                        # 1111101 does match the regex. As the sender should retransmit these messages sequentially,
                        # the SSN of the resent All-1 and the penultimate fragment are still not consecutive.
                        # The only way for these two SSNs to be consecutive in these cases
                        # is that the penultimate fragment fills the bitmap in the 6th bit,
                        # and the last fragment is the All-1.
                        # This reasoning is still valid when the last window does not contain WINDOW_SIZE fragments.
                        # These particular cases validate the use for this regex matching.
                        # Remember that 1111011 is NOT a valid bitmap.
                        # In conclusion, AFTER the regex matching,
                        # we should check if the SSNs of the two last received fragments are consecutive.
                        # The second to last fragment has the highest SSN registered in the JSON.
                        # TODO: What happens when the All-0 prior to the last window is lost and is retransmitted with the All-1?
                        # We should consider only the SSNs of the last window. If there is a retransmission in a window
                        # prior to the last, the reasoning fails since the All-1 is always consecutive to a
                        # retransmitted fragment of a non-final window.
                        # If the All-1 is the only fragment of the last window (bitmap 0000001), and bitmap check of
                        # prior windows has passed, check the consecutiveness of the last All-0 and the All-1.

                        sequence_numbers = json.loads(
                            read_blob(BUCKET_NAME, "SSN"))

                        # This array has the SSNs of the last window.
                        # last_window_ssn = list(sequence_numbers.values())[current_window * profile.WINDOW_SIZE + 1:]

                        # If this array is empty, no messages have been received in the last window. Check if the
                        # last All-0 and the All-1 are consecutive. If they are not, there are lost fragments. If they
                        # are, the All-0 may have been retransmitted.
                        # print(last_window_ssn)

                        # The last sequence number should be the highest of these values.
                        last_sequence_number = max(
                            list(map(int, list(sequence_numbers.values()))))

                        # TODO: If the All-0 has the highest of these values, it may have been retransmitted using the All-1

                        print(
                            f"All-1 sequence number {sigfox_sequence_number}")
                        print(f"Last sequence number {last_sequence_number}")

                        if int(sigfox_sequence_number) - int(
                                last_sequence_number) == 1:
                            print(
                                "[ALL1] Integrity checking complete, launching reassembler."
                            )
                            # All-1 does not define a fragment number, so its fragment number must be the next
                            # of the higest registered fragment number.
                            last_index = max(
                                list(map(int, list(
                                    sequence_numbers.keys())))) + 1
                            upload_blob_using_threads(
                                BUCKET_NAME, data[0].decode("ISO-8859-1") +
                                data[1].decode("utf-8"),
                                f"all_windows/window_{current_window}/"
                                f"fragment_{current_window}_{last_index}")
                            try:
                                _ = requests.post(url=REASSEMBLER_URL,
                                                  json={
                                                      "last_index": last_index,
                                                      "current_window":
                                                      current_window,
                                                      "header_bytes":
                                                      header_bytes
                                                  },
                                                  timeout=0.1)
                            except requests.exceptions.ReadTimeout:
                                pass

                            # Send last ACK to end communication (on receiving an All-1, if no fragments are lost,
                            # if it has received at least one tile, return an ACK for the highest numbered window we
                            # currently have tiles for).
                            print("[ALL1] Preparing last ACK")
                            bitmap = ''
                            for k in range(profile.BITMAP_SIZE):
                                bitmap += '0'
                            last_ack = ACK(profile=profile,
                                           rule_id=rule_id,
                                           dtag=dtag,
                                           w=zfill(format(window_ack, 'b'), m),
                                           c='1',
                                           bitmap=bitmap_ack)
                            response_json = send_ack(request_dict, last_ack)
                            print(f"200, Response content -> {response_json}")
                            print("[ALL1] Last ACK has been sent.")

                            return response_json, 200
                    # If the last two fragments are not consecutive, or the bitmap didn't match the regex,
                    # send an ACK reporting losses.
                    else:
                        # Send NACK at the end of the window.
                        print("[ALLX] Sending NACK for lost fragments...")
                        ack = ACK(profile=profile,
                                  rule_id=rule_id,
                                  dtag=dtag,
                                  w=zfill(format(window_ack, 'b'), m),
                                  c='0',
                                  bitmap=bitmap_ack)
                        response_json = send_ack(request_dict, ack)
                        return response_json, 200

        return '', 204

    else:
        print(
            'Invalid HTTP Method to invoke Cloud Function. Only POST supported'
        )
        return abort(405)
Ejemplo n.º 13
0
# Get fragments from backend

backend_fragments = get_fragments(device_id, limit, username, password)
print("Fetched fragments:")
print(backend_fragments)

for i in range(len(backend_fragments)):

	# Initialize window
	for j in range(2 ** n - 1):
		window.append(b"")

	fragment = backend_fragments[i]
	data = [bytes([fragment[0]]), bytearray(fragment[1:])]
	fragment_message = Fragment(profile_uplink, data)

	try:
		fragment_number = fcn_dict[fragment_message.header.FCN]
		print("[RECV] This corresponds to the " + str(fragment_number) + "th fragment of the " + str(
			current_window) + "th window.")

		# Set the fragment_number-th bit of the bitmap to 1 and add the fragment to the fragment_number-th position
		# in the current window.
		bitmap = replace_bit(bitmap, fragment_number, '1')
		window[fragment_number] = data

	# If the FCN does not have a corresponding fragment number, then it almost certainly is an All-1
	except KeyError:

		# Set the rightmost bit of the bitmap to 1 (See SCHC draft).
Ejemplo n.º 14
0
    def __init__(self, profile, schc_fragments):
        self.PROFILE = profile

        self.SCHC_FRAGMENTS = [
            Fragment(self.PROFILE, fragment) for fragment in schc_fragments
        ]
Ejemplo n.º 15
0
def receiver():
    """HTTP Cloud Function.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """

    # File where we will store authentication credentials after acquiring them.
    os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = config.CLIENT_SECRETS_FILE
    # Wait for an HTTP POST request.
    if request.method == 'POST':

        # Get request JSON.
        print("POST RECEIVED")
        request_dict = request.get_json()
        print('Received Sigfox message: {}'.format(request_dict))

        # Get data and Sigfox Sequence Number.
        fragment = request_dict["data"]
        sigfox_sequence_number = request_dict["seqNumber"]

        header_first_hex = fragment[0]

        if header_first_hex == '0' or header_first_hex == '1':
            header = bytes.fromhex(fragment[:2])
            payload = bytearray.fromhex(fragment[2:])
            header_bytes = 1
        elif header_first_hex == 'f' or header_first_hex == '2':
            header = bytearray.fromhex(fragment[:4])
            payload = bytearray.fromhex(fragment[4:])
            header_bytes = 2
        else:
            print("Wrong header in fragment")
            return 'wrong header', 204

        data = [header, payload]

        # Initialize SCHC variables.
        profile = SigfoxProfile("UPLINK", "ACK ON ERROR", header_bytes)
        buffer_size = profile.UPLINK_MTU
        n = profile.N
        m = profile.M

        # If fragment size is greater than buffer size, ignore it and end function.
        if len(fragment
               ) / 2 * 8 > buffer_size:  # Fragment is hex, 1 hex = 1/2 byte
            return json.dumps(
                {"message":
                 "Fragment size is greater than buffer size D:"}), 200

        # Initialize empty window
        window = []
        for i in range(2**n - 1):
            window.append([b"", b""])

        # Compute the fragment compressed number (FCN) from the Profile
        fcn_dict = {}
        for j in range(2**n - 1):
            fcn_dict[zfill(bin((2**n - 2) - (j % (2**n - 1)))[2:], n)] = j

        # Convert to a Fragment class for easier manipulation.
        fragment_message = Fragment(profile, data)

        # Get current window for this fragment.
        current_window = int(fragment_message.HEADER.W, 2)

        # Get the current bitmap.
        bitmap = read_blob("all_windows/window_%d/bitmap_%d" %
                           (current_window, current_window))

        if fragment_message.is_sender_abort():
            print("Sender-Abort received")
            return 'Sender-Abort received', 204

        try:
            fragment_number = fcn_dict[fragment_message.HEADER.FCN]
            upload_blob(fragment_number, "fragment_number")

            time_received = int(request_dict["time"])
            if exists_blob("timestamp"):
                # Check time validation.
                last_time_received = int(read_blob("timestamp"))

                # If this is not the very first fragment and the inactivity timer has been reached, ignore the message.
                if str(fragment_number) != "0" and str(
                        current_window
                ) != "0" and time_received - last_time_received > profile.INACTIVITY_TIMER_VALUE:
                    print("[RECV] Inactivity timer reached. Ending session.")
                    receiver_abort = ReceiverAbort(profile,
                                                   fragment_message.HEADER)
                    print("Sending Receiver Abort")
                    response_json = send_ack(request_dict, receiver_abort)
                    print(f"Response content -> {response_json}")
                    return response_json, 200

            # Upload current timestamp.
            upload_blob(time_received, "timestamp")

            # Print some data for the user.
            print(
                f"[RECV] This corresponds to the {ordinal(fragment_number)} fragment "
                f"of the {ordinal(current_window)} window.")
            print(f"[RECV] Sigfox sequence number: {sigfox_sequence_number}")

            # Update bitmap and upload it.
            bitmap = replace_bit(bitmap, fragment_number, '1')

        # If the FCN could not been found, it almost certainly is the final fragment.
        except KeyError:
            print("[RECV] This seems to be the final fragment.")
            fragment_number = profile.WINDOW_SIZE - 1
            # Upload current timestamp.
            time_received = int(request_dict["time"])
            upload_blob(time_received, "timestamp")
            print(
                f"is All-1:{fragment_message.is_all_1()}, is All-0:{fragment_message.is_all_0()}"
            )
            # Update bitmap and upload it.
            bitmap = replace_bit(bitmap, len(bitmap) - 1, '1')

        # Upload the fragment data.
        upload_blob(
            bitmap,
            f"all_windows/window_{current_window}/bitmap_{current_window}")
        upload_blob(
            data[0].decode("ISO-8859-1") + data[1].decode("utf-8"),
            f"all_windows/window_{current_window}/fragment_{current_window}_{fragment_number}"
        )

        # Get some SCHC values from the fragment.
        rule_id = fragment_message.HEADER.RULE_ID
        dtag = fragment_message.HEADER.DTAG

        # Get last and current Sigfox sequence number (SSN)
        last_sequence_number = 0
        if exists_blob("SSN"):
            last_sequence_number = read_blob("SSN")
        upload_blob(sigfox_sequence_number, "SSN")

        # # Controlling deterministic losses. This loads the file "loss_mask.txt" which states when should a fragment be
        # # lost, separated by windows.
        # fd = None
        # try:
        #     fd = open(config.LOSS_MASK_MODIFIED, "r")
        # except FileNotFoundError:
        #     fd = open(config.LOSS_MASK, "r")
        # finally:
        #     loss_mask = []
        #     for line in fd:
        #         if not line.startswith("#"):
        #             for char in line:
        #                 try:
        #                     loss_mask.append(int(char))
        #                 except ValueError:
        #                     pass
        #     fd.close()
        #
        # print(f"Loss mask: {loss_mask}")

        # If the fragment is at the end of a window (ALL-0 or ALL-1)
        if fragment_message.is_all_0() or fragment_message.is_all_1():

            # Prepare the ACK bitmap. Find the first bitmap with a 0 in it.
            for i in range(current_window + 1):
                bitmap_ack = read_blob("all_windows/window_%d/bitmap_%d" %
                                       (i, i))
                print(bitmap_ack)
                window_ack = i
                if '0' in bitmap_ack:
                    break

            # If the ACK bitmap has a 0 at the end of a non-final window, a fragment has been lost.
            if fragment_message.is_all_0() and '0' in bitmap_ack:
                print("[ALL0] Sending ACK for lost fragments...")
                print("bitmap with errors -> {}".format(bitmap_ack))
                # Create an ACK message and send it.
                ack = ACK(profile=profile,
                          rule_id=rule_id,
                          dtag=dtag,
                          w=zfill(format(window_ack, 'b'), m),
                          c='0',
                          bitmap=bitmap_ack)
                response_json = send_ack(request_dict, ack)
                print("Response content -> {}".format(response_json))
                return response_json, 200

            # If the ACK bitmap is complete and the fragment is an ALL-0, don't send an ACK
            if fragment_message.is_all_0() and bitmap[0] == '1' and all(
                    bitmap):
                print("[ALL0] All Fragments of current window received")
                print("[ALL0] No need to send an ACK")
                return '', 204

            # If the fragment is an ALL-1
            if fragment_message.is_all_1():

                # The bitmap in the last window follows the following regular expression: "1*0*1*"
                # Since the ALL-1, if received, changes the least significant bit of the bitmap.
                # For a "complete" bitmap in the last window, there shouldn't be non-consecutive zeroes:
                # 1110001 is a valid bitmap, 1101001 is not.

                pattern = re.compile("1*0*1")
                # If the bitmap matches the regex, check if the last two received fragments are consecutive.
                if pattern.fullmatch(bitmap_ack):
                    print("SSN is {} and last SSN is {}".format(
                        sigfox_sequence_number, last_sequence_number))
                    # If the last two received fragments are consecutive, accept the ALL-1 and start reassembling
                    if int(sigfox_sequence_number) - int(
                            last_sequence_number) == 1:
                        last_index = profile.WINDOW_SIZE - 1
                        print(
                            "Info for reassemble: last_index:{}, current_window:{}"
                            .format(last_index, current_window))
                        print('Activating reassembly process...')
                        start_request(url=config.LOCAL_REASSEMBLE_URL,
                                      body={
                                          "current_window": current_window,
                                          "header_bytes": header_bytes
                                      })

                        # Send last ACK to end communication.
                        print("[ALL1] Reassembled: Sending last ACK")
                        bitmap = ''
                        for k in range(profile.BITMAP_SIZE):
                            bitmap += '0'
                        last_ack = ACK(profile=profile,
                                       rule_id=rule_id,
                                       dtag=dtag,
                                       w=zfill(format(window_ack, 'b'), m),
                                       c='1',
                                       bitmap=bitmap)
                        response_json = send_ack(request_dict, last_ack)
                        # return response_json, 200
                        # response_json = send_ack(request_dict, last_ack)
                        print("200, Response content -> {}".format(
                            response_json))
                        return response_json, 200
                    else:
                        # Send NACK at the end of the window.
                        print(
                            "[ALLX] Sending NACK for lost fragments because of SSN..."
                        )
                        ack = ACK(profile=profile,
                                  rule_id=rule_id,
                                  dtag=dtag,
                                  w=zfill(format(window_ack, 'b'), m),
                                  c='0',
                                  bitmap=bitmap_ack)
                        response_json = send_ack(request_dict, ack)
                        return response_json, 200
                    # If they are not, there is a gap between two fragments: a fragment has been lost.
                # The same happens if the bitmap doesn't match the regex.
                else:
                    # Send NACK at the end of the window.
                    print("[ALLX] Sending NACK for lost fragments...")
                    ack = ACK(profile=profile,
                              rule_id=rule_id,
                              dtag=dtag,
                              w=zfill(format(window_ack, 'b'), m),
                              c='0',
                              bitmap=bitmap_ack)
                    response_json = send_ack(request_dict, ack)
                    return response_json, 200
        return '', 204
    else:
        print(
            'Invalid HTTP Method to invoke Cloud Function. Only POST supported'
        )
        return abort(405)
Ejemplo n.º 16
0
def post_message():
    global counter_w0
    global counter_w1

    if request.method == 'POST':
        print("POST RECEIVED")
        # BUCKET_NAME = config.BUCKET_NAME
        request_dict = request.get_json()
        print('Received Sigfox message: {}'.format(request_dict))
        # Get data and Sigfox Sequence Number.
        fragment = request_dict["data"]
        sigfox_sequence_number = request_dict["seqNumber"]
        device = request_dict['device']
        print('Data received from device id:{}, data:{}'.format(
            device, request_dict['data']))
        # Parse fragment into "fragment = [header, payload]
        header = bytes.fromhex(fragment[:2])
        payload = bytearray.fromhex(fragment[2:])
        data = [header, payload]
        # Initialize SCHC variables.
        profile_uplink = Sigfox("UPLINK", "ACK ON ERROR")
        profile_downlink = Sigfox("DOWNLINK", "NO ACK")
        buffer_size = profile_uplink.MTU
        n = profile_uplink.N
        m = profile_uplink.M
        # Convert to a Fragment class for easier manipulation.
        fragment_message = Fragment(profile_uplink, data)
        # Get some SCHC values from the fragment.
        rule_id = fragment_message.header.RULE_ID
        dtag = fragment_message.header.DTAG
        w = fragment_message.header.W
        print('RULE_ID: {} W: {}, FCN: {}'.format(
            fragment_message.header.RULE_ID, fragment_message.header.W,
            fragment_message.header.FCN))
        if 'ack' in request_dict:
            if request_dict['ack'] == 'true':
                print('w:{}'.format(w))
                if w == '00':
                    # print('ACK already send for this window, move along')
                    # counter_w0 = 0
                    # return '', 204
                    if counter_w0 == 1:
                        # print('ACK already send for this window, move along')
                        print("This time send an ACK for window 1")
                        counter_w0 = 0
                        bitmap = '0000001'
                        ack = ACK(profile_downlink, rule_id, dtag, "01",
                                  bitmap, '0')
                        response_json = send_ack(request_dict, ack)
                        print("200, Response content -> {}".format(
                            response_json))
                        return 'fragment lost', 204
                    counter_w0 += 1
                    print('lets say we lost the All-0, so move along')
                    return 'fragment lost', 204
                    # return str(counter)
                    # Create an ACK message and send it.
                    bitmap = '1011111'
                    bitmap = '1000000'
                    bitmap = '0100001'
                    ack = ACK(profile_downlink, rule_id, dtag, w, bitmap, '1')
                    response_json = send_ack(request_dict, ack)
                    print("200, Response content -> {}".format(response_json))
                    # response = {request_dict['device']: {'downlinkData': '07f7ffffffffffff'}}
                    # print("response -> {}".format(response))
                    return response_json, 200
                elif w == '01':
                    if counter_w1 == 1:

                        print("This time send an ACK for window 1")
                        # counter_w0 = 0
                        counter_w1 += 1
                        bitmap = '0000001'
                        ack = ACK(profile_downlink, rule_id, dtag, "01",
                                  bitmap, '0')
                        response_json = send_ack(request_dict, ack)
                        print("200, Response content -> {}".format(
                            response_json))
                        return '', 204

                    elif counter_w1 == 2:
                        print('Resend an ACK for window 1')
                        counter_w1 += 1
                        bitmap = '0000001'
                        ack = ACK(profile_downlink, rule_id, dtag, w, bitmap,
                                  '0')
                        response_json = send_ack(request_dict, ack)
                        print("200, Response content -> {}".format(
                            response_json))
                        # response = {request_dict['device']: {'downlinkData': '07f7ffffffffffff'}}
                        # print("response -> {}".format(response))
                        return response_json, 200

                    elif counter_w1 == 3:
                        print(
                            'ACK already send for this window, send last ACK')
                        counter_w1 = 0
                        bitmap = '0100001'
                        ack = ACK(profile_downlink, rule_id, dtag, w, bitmap,
                                  '1')
                        response_json = send_ack(request_dict, ack)
                        print("200, Response content -> {}".format(
                            response_json))
                        # response = {request_dict['device']: {'downlinkData': '07f7ffffffffffff'}}
                        # print("response -> {}".format(response))
                        return response_json, 200

                        bitmap = '0100001'
                        ack = ACK(profile_downlink, rule_id, dtag, w, bitmap,
                                  '1')
                        response_json = send_ack(request_dict, ack)
                        print("200, Response content -> {}".format(
                            response_json))
                    counter_w1 += 1
                    # Create an ACK message and send it.
                    bitmap = '0000001'

                    ack = ACK(profile_downlink, rule_id, dtag, w, bitmap, '0')

                    # Test for loss of All-0 in window 0
                    bitmap = '1010110'
                    ack = ACK(profile_downlink, rule_id, dtag, '00', bitmap,
                              '0')
                    # ack = ACK(profile_downlink, rule_id, dtag, w, bitmap, '1')
                    response_json = send_ack(request_dict, ack)
                    print("200, Response content -> {}".format(response_json))
                    # response = {request_dict['device']: {'downlinkData': '07f7ffffffffffff'}}
                    # print("response -> {}".format(response))
                    return response_json, 200
                else:
                    return '', 204
            return '', 204
        else:
            return f'Not a correct format message', 404
Ejemplo n.º 17
0
def post(fragment_sent, retransmit=False):
    global seqNumber, attempts, current_window, last_window, i, sent, received, retransmitted
    headers = {'content-type': 'application/json'}
    profile = fragment_sent.profile

    if fragment_sent.is_all_0() and not retransmit:
        print("[POST] This is an All-0. Using All-0 SIGFOX_DL_TIMEOUT.")
        request_timeout = profile.SIGFOX_DL_TIMEOUT
    elif fragment_sent.is_all_1():
        print(
            "[POST] This is an All-1. Using RETRANSMISSION_TIMER_VALUE. Increasing ACK attempts."
        )
        attempts += 1
        request_timeout = profile.RETRANSMISSION_TIMER_VALUE
    else:
        request_timeout = 45

    payload_dict = {
        "deviceType": "WYSCHC",
        "device": device,
        "time": str(int(time.time())),
        "data": fragment_sent.hex,
        "seqNumber": str(seqNumber),
        "ack": fragment_sent.expects_ack() and not retransmit
    }

    print(
        f"[POST] Posting fragment {fragment_sent.header.string} ({fragment_sent.hex}) to {SCHC_POST_URL}"
    )

    try:
        response = requests.post(SCHC_POST_URL,
                                 data=json.dumps(payload_dict),
                                 headers=headers,
                                 timeout=request_timeout)

        if fragment_sent.is_sender_abort():
            print("Sent Sender-Abort. Goodbye")
            exit(1)

        seqNumber += 1
        sent += 1
        if retransmit:
            retransmitted += 1
        print(f"[POST] Response: {response}")
        http_code = response.status_code

        # If 500, exit with an error
        if http_code == 500:
            print("Response: 500 Internal Server Error")
            exit(1)

        # If 204, the fragment was posted successfully
        elif http_code == 204:
            print("Response: 204 No Content")
            if fragment_sent.is_all_0() and not retransmit:
                print("Faking timeout")
                time.sleep(profile.SIGFOX_DL_TIMEOUT)
                raise Timeout
            if not retransmit:
                i += 1
            return

        # If 200, the fragment was posted and an ACK has been received.
        elif http_code == 200:
            print(
                f"Response: 200 OK, Text: {response.text}. Ressetting attempts counter to 0."
            )
            received += 1
            attempts = 0
            ack = response.json()[device]["downlinkData"]

            # Parse ACK
            ack_object = ACK.parse_from_hex(profile_uplink, ack)

            if ack_object.is_receiver_abort():
                print(
                    "ERROR: Receiver Abort received. Aborting communication.")
                exit(1)

            if not fragment_sent.expects_ack():
                print(f"ERROR: ACK received but not requested ({ack}).")
                exit(1)

            # Extract data from ACK
            ack_window = ack_object.w
            ack_window_number = ack_object.window_number
            c = ack_object.c
            bitmap = ack_object.bitmap
            print(f"ACK: {ack}")
            print(f"ACK window: {str(ack_window)}")
            print(f"ACK bitmap: {bitmap}")
            print(f"ACK C bit: {c}")
            print(f"last window: {last_window}")

            # If the W field in the SCHC ACK corresponds to the last window of the SCHC Packet:
            if ack_window_number == last_window:
                # If the C bit is set, the sender MAY exit successfully.
                if c == '1':
                    print(
                        "Last ACK received, fragments reassembled successfully. End of transmission."
                    )
                    print(
                        f"TOTAL UPLINK: {sent} ({retransmitted} retransmisiones)"
                    )
                    print(f"TOTAL DOWNLINK: {received}")
                    exit(0)
                # 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 up to the All-1 fragment.
                        last_bitmap = bitmap[:len(fragment_list) % window_size]
                        print(f"last bitmap {last_bitmap}")

                        # If the SCHC ACK shows no missing tile at the receiver, abort.
                        # (C = 0 but transmission complete)
                        if last_bitmap[0] == '1' and all(last_bitmap):
                            print(
                                "ERROR: SCHC ACK shows no missing tile at the receiver."
                            )
                            post(
                                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':
                                    print(
                                        f"The {j}th ({window_size * ack_window_number + j} / {len(fragment_list)}) fragment was lost! Sending again..."
                                    )
                                    # Try sending again the lost fragment.
                                    fragment_to_be_resent = Fragment(
                                        profile_uplink,
                                        fragment_list[window_size * ack_window
                                                      + j])
                                    print(
                                        f"Lost fragment: {fragment_to_be_resent.string}"
                                    )
                                    post(fragment_to_be_resent,
                                         retransmit=True)

                            # Send All-1 again to end communication.
                            post(fragment_sent)

                    else:
                        print(
                            "ERROR: While being at the last window, the ACK-REQ was not an All-1."
                            "This is outside of the Sigfox scope.")
                        exit(1)

            # 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':
                        print(
                            f"The {j}th ({window_size * ack_window_number + j} / {len(fragment_list)}) fragment was lost! Sending again..."
                        )
                        # Try sending again the lost fragment.
                        fragment_to_be_resent = Fragment(
                            profile_uplink,
                            fragment_list[window_size * ack_window_number + j])
                        print(f"Lost fragment: {fragment_to_be_resent.string}")
                        post(fragment_to_be_resent, retransmit=True)
                if fragment_sent.is_all_1():
                    # Send All-1 again to end communication.
                    post(fragment_sent)
                elif fragment_sent.is_all_0():
                    i += 1
                    current_window += 1

    # If the timer expires
    except Timeout:
        # If an ACK was expected
        if fragment_sent.is_all_1():
            # If the attempts counter is strictly less than MAX_ACK_REQUESTS, try again
            if attempts < profile_uplink.MAX_ACK_REQUESTS:
                print(
                    "SCHC Timeout reached while waiting for an ACK. Sending the ACK Request again..."
                )
                post(fragment_sent)
            # Else, exit with an error.
            else:
                print("ERROR: MAX_ACK_REQUESTS reached. Sending Sender-Abort.")
                header = fragment_sent.header
                abort = SenderAbort(profile, header)
                post(abort)

        # If the ACK can be not sent (Sigfox only)
        if fragment_sent.is_all_0():
            print("All-0 timeout reached. Proceeding to next window.")
            if not retransmit:
                i += 1
                current_window += 1

        # Else, HTTP communication failed.
        else:
            print("ERROR: HTTP Timeout reached.")
            exit(1)
Ejemplo n.º 18
0
# (a whole SCHC packet)
attempts = 0
fragment = None

if len(fragment_list) > (2**profile_uplink.M) * window_size:
    print(len(fragment_list))
    print((2**profile_uplink.M) * window_size)
    print(
        "ERROR: The SCHC packet cannot be fragmented in 2 ** M * WINDOW_SIZE fragments or less. A Rule ID cannot be "
        "selected.")
    exit(1)

# Start sending fragments.
while i < len(fragment_list):
    # A fragment has the format "fragment = [header, payload]".
    data = bytes(fragment_list[i][0] + fragment_list[i][1])
    current_size += len(fragment_list[i][1])
    percent = round(float(current_size) / float(total_size) * 100, 2)

    # Convert to a Fragment class for easier manipulation.
    resent = None
    timeout = False
    fragment = Fragment(profile_uplink, fragment_list[i])

    # Send the data.
    print("Sending...")

    # On All-0 fragments, this function will wait for SIGFOX_DL_TIMER to expire
    # On All-1 fragments, this function will enter retransmission phase.
    post(fragment)
Ejemplo n.º 19
0
start_sending_time = chrono.read()
# Start sending fragments.
CURRENT_STATE = STATE_SEND
while i < len(fragment_list) and tx_status_ok == False:
	current_fragment = {}
	laps.append(chrono.read())
	print("laps - > {}".format(laps))
	if not retransmitting:
		pycom.rgbled(0x7f7f00) # yellow
		# A fragment has the format "fragment = [header, payload]".
		data = bytes(fragment_list[i][0] + fragment_list[i][1])


		# Convert to a Fragment class for easier manipulation.
		fragment = Fragment(profile_uplink, fragment_list[i])
		# current_fragment['RULE_ID'] = fragment.header.RULE_ID
		# current_fragment['W'] = fragment.header.W
		# current_fragment['FCN'] = fragment.header.FCN
		# current_fragment['data'] = data
		
		if verbose:
			print("--------------------------")
			print(str(i) + "th fragment:")
			
			print("RuleID:{}, DTAG:{}, WINDOW:{}, FCN:{}".format(fragment.header.RULE_ID,fragment.header.DTAG,fragment.header.W,fragment.header.FCN))
			print("SCHC Fragment: {}".format(data))
			print("SCHC Fragment Payload: {}".format(fragment_list[i][1]))

		current_size += len(fragment_list[i][1])
		if (total_size!= 0): percent = round(float(current_size) / float(total_size) * 100, 2)
Ejemplo n.º 20
0
    def insert_index(ls, pos, elmt):
        while len(ls) < pos:
            ls.append([])
        ls.insert(pos, elmt)

    def replace_bit(string, position, value):
        return '%s%s%s' % (string[:position], value, string[position + 1:])

    def find(string, character):
        return [i for i, ltr in enumerate(string) if ltr == character]

    # ----------Fragment loss emulation

    profile_uplink = Sigfox("UPLINK", "ACK ON ERROR")
    data = [bytes([fragment[0]]), bytearray(fragment[1:])]
    fragment_message = Fragment(profile_uplink, data)
    if not fragment_message.is_all_0() and not fragment_message.is_all_1():
        coin = random.random()
        if coin * 100 < loss_rate:
            print("[LOSS] The fragment was lost.")
            continue

    # ACTUAL CODE: A fragment was received.
    # Initialize variables.

    time_received = datetime.now()
    timestamp = open("timestamp", "w")
    timestamp.write(time_received.strftime(time_format))
    timestamp.close()

    profile_uplink = Sigfox("UPLINK", "NO ACK")
Ejemplo n.º 21
0
    f = data.read()
    payload = bytearray(f)

print("The payload to be transmitted is: " + str(payload))

test_header = Header(profile, rule_id="RR", dtag="D", w="WW", fcn="000", c=0)
test_header.test()

fragmenter = Fragmenter(profile, payload)
fragment_list = fragmenter.fragment()

print("Fragments:")
for fragment in fragment_list:
    print(fragment)

if Fragment(profile, fragment_list[-1]).is_all_1() is True:
    print("All-1 condition satisfied")
else:
    print("All-1 condition NOT satisfied")

# print("Rebuilding message...")
#
# reassembler = Reassembler(profile, fragment_list)
# rebuild = reassembler.reassemble()
#
# print("Rebuilt message: \n" + rebuild)
#
# if payload in rebuild:
# 	print("The message has been rebuilt successfully :D ! Though padding bits are still present.")
# else:
# 	print("There has been an error rebuilding the packet ):")