feat(mavlink): support FTP ListDirectoryWithTime (#27542)
Build all targets / Scan for Board Targets (push) Has been cancelled
Checks / Gate Checks [check_format] (push) Has been cancelled
Checks / Gate Checks [check_newlines] (push) Has been cancelled
Checks / Gate Checks [module_documentation] (push) Has been cancelled
Checks / Gate Checks [shellcheck_all] (push) Has been cancelled
Checks / Gate Checks [validate_module_configs] (push) Has been cancelled
Checks / Unit Tests (push) Has been cancelled
MacOS build / build (push) Has been cancelled
Ubuntu environment build / Build and Test (ubuntu:22.04) (push) Has been cancelled
Ubuntu environment build / Build and Test (ubuntu:24.04) (push) Has been cancelled
Container build / Set Tags and Variables (push) Has been cancelled
Docs - Orchestrator / T1: Detect Changes (push) Has been cancelled
Docs - Orchestrator / T2: Metadata Sync (push) Has been cancelled
Failsafe Simulator Build / build (failsafe_web) (push) Has been cancelled
FLASH usage analysis / Analyzing px4_fmu-v5x (push) Has been cancelled
FLASH usage analysis / Analyzing px4_fmu-v6x (push) Has been cancelled
ITCM check / Checking nxp_mr-tropic (push) Has been cancelled
ITCM check / Checking nxp_tropic-community (push) Has been cancelled
ITCM check / Checking px4_fmu-v5x (push) Has been cancelled
ITCM check / Checking px4_fmu-v6xrt (push) Has been cancelled
Python CI Checks / build (push) Has been cancelled
ROS Integration Tests / build (push) Has been cancelled
ROS Translation Node Tests / Build and test [humble] (push) Has been cancelled
ROS Translation Node Tests / Build and test [jazzy] (push) Has been cancelled
SITL Tests / Testing PX4 iris (push) Has been cancelled
Build all targets / Seed [${{ matrix.chip_family }}] (push) Has been cancelled
Build all targets / Build [${{ matrix.runner }}][${{ matrix.group }}] (push) Has been cancelled
Build all targets / Upload Artifacts (push) Has been cancelled
Container build / Build Container (amd64) (push) Has been cancelled
Container build / Build Container (arm64) (push) Has been cancelled
Container build / Deploy To Registry (push) Has been cancelled
Docs - Orchestrator / T2: PR Metadata (push) Has been cancelled
Docs - Orchestrator / T2: Link Check (push) Has been cancelled
Docs - Orchestrator / T3: Build Site (push) Has been cancelled
Docs - Orchestrator / T4: Deploy (push) Has been cancelled
FLASH usage analysis / Publish Results (push) Has been cancelled
Static Analysis / Clang-Tidy (push) Has been cancelled

Add support for the MAVLink FTP ListDirectoryWithTime command (opcode 16),
which returns directory listings with each file's last-modification time
appended as decimal seconds since the UNIX epoch (0 if unknown).

This lets a GCS retrieve file timestamps without downloading the files,
which is useful for log download. The plain ListDirectory output is
unchanged, and clients fall back to it when the server NAKs with
UnknownCommand.

Signed-off-by: Julian Oes <julian@oes.ch>
This commit is contained in:
Julian Oes
2026-06-01 21:51:47 -06:00
committed by GitHub
parent bb914860ea
commit cc6473bdb9
2 changed files with 19 additions and 4 deletions
+17 -3
View File
@@ -171,6 +171,10 @@ MavlinkFTP::_process_request(
errorCode = _workList(payload);
break;
case kCmdListDirectoryWithTime:
errorCode = _workList(payload, true);
break;
case kCmdOpenFileRO:
errorCode = _workOpen(payload, O_RDONLY);
break;
@@ -322,7 +326,7 @@ void MavlinkFTP::_constructPath(char *dst, int dst_len, const char *path) const
/// @brief Responds to a List command
MavlinkFTP::ErrorCode
MavlinkFTP::_workList(PayloadHeader *payload)
MavlinkFTP::_workList(PayloadHeader *payload, bool include_time)
{
_constructPath(_work_buffer1, _work_buffer1_len, _data_as_cstring(payload));
@@ -380,6 +384,7 @@ MavlinkFTP::_workList(PayloadHeader *payload)
}
uint32_t fileSize = 0;
uint32_t fileTime = 0; // seconds since the UNIX epoch, 0 if unknown
char direntType;
// Determine the directory entry type
@@ -401,6 +406,7 @@ MavlinkFTP::_workList(PayloadHeader *payload)
if (stat(_work_buffer2, &st) == 0) {
fileSize = st.st_size;
fileTime = st.st_mtime;
}
}
@@ -433,8 +439,16 @@ MavlinkFTP::_workList(PayloadHeader *payload)
_work_buffer2[0] = '\0';
} else if (direntType == kDirentFile) {
// Files send filename and file length
int ret = snprintf(_work_buffer2, _work_buffer2_len, "%s\t%" PRIu32, result->d_name, fileSize);
// Files send filename and file length, optionally followed by the modification time
int ret;
if (include_time) {
ret = snprintf(_work_buffer2, _work_buffer2_len, "%s\t%" PRIu32 "\t%" PRIu32, result->d_name, fileSize, fileTime);
} else {
ret = snprintf(_work_buffer2, _work_buffer2_len, "%s\t%" PRIu32, result->d_name, fileSize);
}
bool buf_is_ok = ((ret > 0) && (ret < _work_buffer2_len));
if (!buf_is_ok) {
+2 -1
View File
@@ -96,6 +96,7 @@ public:
kCmdRename, ///< Rename <path1> to <path2>
kCmdCalcFileCRC32, ///< Calculate CRC32 for file at <path>
kCmdBurstReadFile, ///< Burst download session file
kCmdListDirectoryWithTime, ///< List files in <path> from <offset>, including last-modification time
kRspAck = 128, ///< Ack response
kRspNak ///< Nak response
@@ -125,7 +126,7 @@ private:
void _reply(mavlink_file_transfer_protocol_t *ftp_req);
int _copy_file(const char *src_path, const char *dst_path, size_t length);
ErrorCode _workList(PayloadHeader *payload);
ErrorCode _workList(PayloadHeader *payload, bool include_time = false);
ErrorCode _workOpen(PayloadHeader *payload, int oflag);
ErrorCode _workRead(PayloadHeader *payload);
ErrorCode _workBurst(PayloadHeader *payload, uint8_t target_system_id, uint8_t target_component_id);