예제 #1
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))
예제 #2
0
def clean():
    """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")
        BUCKET_NAME = config.BUCKET_NAME
        request_dict = request.get_json()
        print('Received Request message: {}'.format(request_dict))
        header_bytes = int(request_dict["header_bytes"])
        profile = Sigfox("UPLINK", "ACK ON ERROR", header_bytes)
        bitmap = ''
        for i in range(2**profile.N - 1):
            bitmap += '0'
        for i in range(2**profile.M):
            upload_blob(BUCKET_NAME, bitmap,
                        "all_windows/window_%d/bitmap_%d" % (i, i))
            upload_blob(BUCKET_NAME, bitmap,
                        "all_windows/window_%d/losses_mask_%d" % (i, i))

        return '', 204
예제 #3
0
def clean(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.
        header_bytes = int(request_dict["header_bytes"])
        profile = Sigfox("UPLINK", "ACK ON ERROR", header_bytes)
        bitmap = '0' * (2 ** profile.N - 1)
        for i in range(2 ** profile.M):
            upload_blob(bitmap, "all_windows/window_%d/bitmap_%d" % (i, i))
            upload_blob(bitmap, "all_windows/window_%d/losses_mask_%d" % (i, i))
            # For each fragment in the SCHC Profile, create its blob.

            try:
                _ = requests.post(
                    url=config.CLEAN_WINDOW_URL,
                    json={"header_bytes": header_bytes,
                          "window_number": i,
                          "clear": request_dict["clear"] if "clear" in request_dict else "False"},
                    timeout=0.1)
            except requests.ReadTimeout:
                pass

        if exists_blob("Reassembled_Packet"):
            delete_blob("Reassembled_Packet")

        upload_blob("", "SSN")

        print(f"from lopy? {request_dict['from_lopy']}")

        if request_dict["from_lopy"] == "False":
            for blob in blob_list():
                if blob.startswith("DL_LOSSES_"):
                    delete_blob(blob)
        else:
            current_experiment = 1
            for blob in blob_list():
                if blob.startswith("DL_LOSSES_"):
                    current_experiment += 1
            print(f"Preparing for the {current_experiment}th experiment")
            upload_blob("", f"DL_LOSSES_{current_experiment}")

        return '', 204
예제 #4
0
def http_reassemble():

    if request.method == "POST":

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

        current_window = int(request_dict["current_window"])
        last_index = int(request_dict["last_index"])
        header_bytes = int(request_dict["header_bytes"])

        # Initialize Cloud Storage variables.
        # BUCKET_NAME = 'sigfoxschc'
        # BUCKET_NAME = 'wyschc-niclabs'
        BUCKET_NAME = config.BUCKET_NAME

        # Initialize SCHC variables.
        profile_uplink = Sigfox("UPLINK", "ACK ON ERROR", header_bytes)
        n = profile_uplink.N
        # Find the index of the first empty blob:

        print("[REASSEMBLE] Reassembling...")

        # Get all the fragments into an array in the format "fragment = [header, payload]"
        fragments = []

        # TODO: This assumes that the last received message is in the last window.
        for i in range(current_window + 1):
            for j in range(2**n - 1):
                print("Loading fragment {}".format(j))
                fragment_file = read_blob(
                    BUCKET_NAME,
                    "all_windows/window_%d/fragment_%d_%d" % (i, i, j))
                print(fragment_file)
                ultimate_header = fragment_file[:header_bytes]
                ultimate_payload = fragment_file[header_bytes:]
                ultimate_fragment = [
                    ultimate_header.encode(),
                    ultimate_payload.encode()
                ]
                fragments.append(ultimate_fragment)
                if i == current_window and j == last_index:
                    break

        # Instantiate a Reassembler and start reassembling.
        reassembler = Reassembler(profile_uplink, fragments)
        payload = bytearray(reassembler.reassemble())
        # Upload the full message.
        upload_blob(BUCKET_NAME, payload.decode("utf-8"),
                    "Reassembled_message")

        return '', 204
예제 #5
0
def clean_window(request):

    request_dict = request.get_json()
    header_bytes = int(request_dict["header_bytes"])
    window_number = int(request_dict["window_number"])
    profile = Sigfox("UPLINK", "ACK ON ERROR", header_bytes)

    for j in range(2 ** profile.N - 1):
        if request_dict["clear"] == "False":
            upload_blob("", f"all_windows/window_{window_number}/fragment_{window_number}_{j}")
        else:
            delete_blob(f"all_windows/window_{window_number}/fragment_{window_number}_{j}")
예제 #6
0
def cleanup():
    os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = config.CLIENT_SECRETS_FILE
    bucket_name = config.BUCKET_NAME
    header_bytes = request.get_json()["header_bytes"]
    profile = Sigfox("UPLINK", "ACK ON ERROR", header_bytes)

    # print("[CLN] Deleting timestamp blob")
    # delete_blob(bucket_name, "timestamp")
    #
    # print("[CLN] Deleting modified loss mask")
    # try:
    #     os.remove(config.LOSS_MASK_MODIFIED)
    # except FileNotFoundError:
    #     pass
    #
    # print("[CLN] Resetting SSN")
    # upload_blob(bucket_name, "{}", "SSN")
    #
    # print("[CLN] Initializing fragments...")
    # delete_blob(bucket_name, "all_windows/")
    # initialize_blobs(bucket_name, profile)

    return '', 204
예제 #7
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)
예제 #8
0

# Read the file to be sent.
with open(filename, "rb") as data:
	f = data.read()
	payload = bytearray(f)

# Initialize variables.
total_size = len(payload)
current_size = 0
percent = round(0, 2)
ack = None
last_ack = None
i = 0
current_window = 0
profile_uplink = Sigfox("UPLINK", "ACK ON ERROR")
profile_downlink = Sigfox("DOWNLINK", "NO ACK")

# Fragment the file.
fragmenter = Fragmenter(profile_uplink, payload)
fragment_list = fragmenter.fragment()

# The fragment sender MUST initialize the Attempts counter to 0 for that Rule ID and DTag value pair
# (a whole SCHC packet)
attempts = 0
retransmitting = False
fragment = None

if len(fragment_list) > (2 ** profile_uplink.M) * profile_uplink.WINDOW_SIZE:
	print(len(fragment_list))
	print((2 ** profile_uplink.M) * profile_uplink.WINDOW_SIZE)
예제 #9
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
예제 #10
0
def reassemble():
    CLEANUP_URL = "http://localhost:5000/cleanup"

    if request.method == "POST":
        print("[RSMB] The reassembler has been launched.")
        # Get request JSON.
        request_dict = request.get_json()
        print(f'[RSMB] Received HTTP message: {request_dict}')

        current_window = int(request_dict["current_window"])
        last_index = int(request_dict["last_index"])
        header_bytes = int(request_dict["header_bytes"])

        # Initialize Cloud Storage variables.
        BUCKET_NAME = config.BUCKET_NAME

        # Initialize SCHC variables.
        profile = Sigfox("UPLINK", "ACK ON ERROR", header_bytes)
        window_size = profile.WINDOW_SIZE

        print("[RSMB] Loading fragments")

        # Get all the fragments into an array in the format "fragment = [header, payload]"
        fragments = []

        # For each window, load every fragment into the fragments array
        for i in range(current_window + 1):
            for j in range(window_size):
                print(f"[RSMB] Loading fragment {j}")
                fragment_file = read_blob(
                    BUCKET_NAME, f"all_windows/window_{i}/fragment_{i}_{j}")
                print(f"[RSMB] Fragment data: {fragment_file}")
                header = fragment_file[:header_bytes]
                payload = fragment_file[header_bytes:]
                fragment = [header.encode(), payload.encode()]
                fragments.append(fragment)
                if i == current_window and j == last_index:
                    break

        # Instantiate a Reassembler and start reassembling.
        print("[RSMB] Reassembling")
        reassembler = Reassembler(profile, fragments)
        payload = bytearray(reassembler.reassemble()).decode("utf-8")

        print("[RSMB] Uploading result")
        with open(config.PAYLOAD, "w") as file:
            file.write(payload)
        # Upload the full message.
        upload_blob(BUCKET_NAME, payload, "PAYLOAD")

        if filecmp.cmp(config.PAYLOAD, config.MESSAGE):
            print("The reassembled file is equal to the original message.")
        else:
            print("The reassembled file is corrupt.")

        try:
            _ = requests.post(url=CLEANUP_URL,
                              json={"header_bytes": header_bytes},
                              timeout=0.1)
        except requests.exceptions.ReadTimeout:
            pass

        return '', 204
예제 #11
0
def schc_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>.
    """

    REASSEMBLER_URL = "http://localhost:5000/reassemble"
    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 == "true":

            # 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:
                            # 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) % profile.WINDOW_SIZE
                            upload_blob(
                                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}")

                            print(
                                "[ALL1] Integrity checking complete, launching reassembler."
                            )
                            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)