Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

groundside script to open and read data from files in pixhawk file system #69

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions modules/groundside_scripts.py
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this file to modules/mavlink folder. Also rename the file to ftp_example.py. We probably won't be using this as the communication method for comp, but it may be useful for logging in the future.

Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
"""
Groundside scripts to receive GPS location messages
"""

import struct
import sys
from pymavlink import mavutil

# connection params
CONNECTION_ADDRESS = "tcp:127.0.0.1:14550"
TIMEOUT = 5.0
DELAY_TIME = 1.0

# file path to read from
# 1. start up connection on mission planner
# 2. mavlink and establish write tcp connection
# 3. config menu -> MAVFtp -> simulation drone file paths
FILE_PATH = b"/@ROMFS/locations.txt"
SEQ_NUM = 0


def send_ftp_command(
connection: mavutil.mavlink_connection,
seq_num: int,
opcode: int,
req_opcode: int,
session: int,
offset: int,
size: int,
payload: bytes,
Comment on lines +24 to +30
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you re-order these so that it follows the byte order as specified in the docs

) -> None:
"""
Send an FTP command to the vehicle.

Args:
connection (mavutil.mavlink_connection): MAVLink connection object.
opcode (int): FTP command opcode.
req_opcode (int): Requested opcode.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you will never be sending a ACK or NAK message, this probably shouldn't be a parameter. You can always set it to 0 or whatever

session (int): Session ID.
offset (int): Offset in the file.
size (int): Size of the payload.
payload (bytes): Payload data.
Comment on lines +37 to +42
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add comment giving the range (and/or size) of all of these numbers

"""
ftp_payload = bytearray(251) # ftp payload size

# packing payload in file system
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please input validate all the parameters

ftp_payload[0:2] = struct.pack("<H", seq_num)
ftp_payload[2] = session
ftp_payload[3] = opcode
ftp_payload[4] = size
ftp_payload[5] = req_opcode
ftp_payload[6] = 0 # burst_complete
ftp_payload[7] = 0 # padding
ftp_payload[8:12] = struct.pack("<I", offset)
ftp_payload[12 : 12 + len(payload)] = payload
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check to make sure this doesn't overflow


connection.mav.file_transfer_protocol_send(
target_network=0,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put a comment saying this is broadcast to all (I believe it is? you can look more into what this is for)

target_system=connection.target_system,
target_component=connection.target_component,
payload=ftp_payload,
)


vehicle = mavutil.mavlink_connection(CONNECTION_ADDRESS, baud=57600)
vehicle.wait_heartbeat()
print("heartbeat received")
if vehicle:
print("CONNECTED...")
else:
print("DISCONNECTED...")

# open file for reading session
send_ftp_command(
vehicle,
seq_num=SEQ_NUM,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be a constant, it should increment by 1 (mod 65535+1) every time you send a message (ie if you receive a message that has seq_num=123, you should send the next message with seq_num=124).

opcode=4,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make an enum for opcode so that people don't have to constantly look up the FTP docs

req_opcode=0,
session=0,
offset=0,
size=len(FILE_PATH),
payload=FILE_PATH,
)
SEQ_NUM += 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You increment this from the value you received in the response FTP message. Also, capital variables are meant to be constants and shouldn't change. You can rename this to lower case, and write out the full name.

response = vehicle.recv_match(type="FILE_TRANSFER_PROTOCOL", blocking=True, timeout=TIMEOUT)

if response is None:
print("NO RESPONSE RECEIVED")
sys.exit()

response_payload = bytes(response.payload)
if response_payload[3] != 128: # Check for error - NAK Response
print("ERROR RECEIVED")
print("ERROR CODE: ", response_payload[12])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This thing could be 2 bytes, you should check the size byte. Also, make an enum for error codes so that you don't print a number but the actual error message.

sys.exit()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is ok for now, but in practice we never just quit the program, that may cause drone to fall out of the sky.


print("FILE OPENED: ")

# retrieve session id, file size, and sequence number from ACK response
SESSION_ID = response_payload[2]
DATA_OFFSET = struct.unpack("<I", response_payload[8:12])[0]
FILE_SIZE = struct.unpack("<I", response_payload[12 : 12 + response_payload[4]])[0]
SEQ_NUM = struct.unpack("<H", response_payload[0:2])[0]
CHUNK_SIZE = 239 # Max data size per chunk
FILE_DATA = b""
Comment on lines +99 to +105
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of doing this, make a function that returns a "FTP_Response" object with these as fields. You should also do sequence_num += 1 mod 2^16, so people don't have to remember to do that


# read file in chunks
while DATA_OFFSET < FILE_SIZE:
send_ftp_command(
vehicle,
seq_num=SEQ_NUM,
opcode=5,
req_opcode=0,
session=SESSION_ID,
offset=DATA_OFFSET,
size=CHUNK_SIZE,
payload=b"",
)
SEQ_NUM += 1
response = vehicle.recv_match(type="FILE_TRANSFER_PROTOCOL", blocking=True, timeout=TIMEOUT)

if response is None:
print("ERROR: NO RESPONSE RECEIVED")
break

response_payload = bytes(response.payload)
if response_payload[3] != 128:
print("ERROR CODE: ", response_payload[12])
break

chunk_data = response_payload[12 : 12 + response_payload[4]]
FILE_DATA += chunk_data
DATA_OFFSET += len(chunk_data)

print(chunk_data.decode("utf-8", errors="ignore"), end="")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than printing things one chunk at a time, you should print the entire file at once at the very end. Python print statements add newline characters, which will make your file look like it has random newlines everywhere. Also, if the bytes broke in a bad spot (ie if there was an integer in the file which is 4 bytes but you only received 1, you're going to print garbage)


# Send the next read command while waiting for the current response
if DATA_OFFSET < FILE_SIZE:
send_ftp_command(
vehicle,
seq_num=SEQ_NUM,
opcode=5,
req_opcode=0,
session=SESSION_ID,
offset=DATA_OFFSET,
size=CHUNK_SIZE,
payload=b"",
)
SEQ_NUM += 1

# Terminate read session
send_ftp_command(
vehicle,
seq_num=SEQ_NUM,
opcode=1,
req_opcode=0,
session=SESSION_ID,
offset=0,
size=0,
payload=b"",
)
print("\nEND OF FILE")