-
Notifications
You must be signed in to change notification settings - Fork 4
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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="") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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") |
There was a problem hiding this comment.
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.