Skip to content

Commit 3cee03b

Browse files
committed
Add log-download program
1 parent 837c4f2 commit 3cee03b

14 files changed

+891
-8
lines changed

Makefile

+6-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ INCS = -I./util -I./ini -I./ini/cpp
88
INCS += -Ijsoncpp
99
INCS += -I.
1010

11-
WARNFLAGS= -Wall -Werror -Wextra -Wunused -Wredundant-decls -D_FORTIFY_SOURCE=2 -Wfloat-equal -fstack-protector -Wformat -Werror=format-security -Werror=pointer-arith -Wpedantic
11+
WARNFLAGS= -Wall -Werror -Wextra -Wunused -Wredundant-decls -D_FORTIFY_SOURCE=2 -Wfloat-equal -fstack-protector -Wformat -Werror=format-security -Werror=pointer-arith
1212

1313
ifeq ($(OS),Windows_NT)
1414
CXX=x86_64-w64-mingw32-g++.exe
@@ -95,6 +95,8 @@ LOG_ANALYZER = dronekit-la
9595

9696
IMAGETAGGER = imagetagger
9797

98+
LOG_DOWNLOAD = log_download
99+
98100
all: $(LOG_ANALYZER)
99101

100102
mavlink_c_library/protocol.h: modules/mavlink/message_definitions/v1.0/common.xml modules/mavlink/message_definitions/v1.0/ardupilotmega.xml
@@ -113,6 +115,9 @@ $(LOG_ANALYZER): $(OBJS) loganalyzer.cpp
113115
$(IMAGETAGGER): $(OBJS) imagetagger.cpp mh_imagetagger.cpp
114116
$(LINK.cpp) -o $(IMAGETAGGER) imagetagger.cpp mh_imagetagger.cpp $(OBJS) $(LIBS) $(DLIBS)
115117

118+
$(LOG_DOWNLOAD): $(OBJS) log_download.cpp log_download_program.cpp log_list.cpp
119+
$(LINK.cpp) -o $(LOG_DOWNLOAD) log_download.cpp log_download_program.cpp log_list.cpp $(OBJS) $(LIBS) $(DLIBS)
120+
116121
clean:
117122
$(RM) *.o *~ $(DATAFLASH_LOGGER) $(LOG_ANALYZER) $(IMAGETAGGER) analyzer/*.o jsoncpp/jsoncpp.o
118123
$(RM) -rf mavlink_c_library

log_download.cpp

+238
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
#include <signal.h>
2+
#include <stdio.h> // for snprintf
3+
#include <fcntl.h>
4+
#include <errno.h>
5+
#include <stdlib.h>
6+
#include <unistd.h>
7+
8+
#include "la-log.h"
9+
#include "util.h"
10+
#include "mavlink_c_library/ardupilotmega/mavlink.h"
11+
12+
#include "log_download.h"
13+
14+
bool Log_Download::configure(INIReader *config)
15+
{
16+
if (!MAVLink_Message_Handler::configure(config)) {
17+
return false;
18+
}
19+
return true;
20+
}
21+
22+
void Log_Download::handle_message(uint64_t timestamp, mavlink_message_t &msg)
23+
{
24+
most_recent_sender_system_id = msg.sysid;
25+
most_recent_sender_component_id = msg.compid;
26+
MAVLink_Message_Handler::handle_message(timestamp, msg);
27+
}
28+
29+
void Log_Download::handle_decoded_message(uint64_t T UNUSED, mavlink_log_entry_t &msg)
30+
{
31+
if (msg.id != id_to_fetch) {
32+
return;
33+
}
34+
35+
log_info = new LogEntry();
36+
log_info->id = msg.id;
37+
log_info->size = msg.size;
38+
log_info->time_utc = msg.time_utc;
39+
}
40+
41+
void Log_Download::write_data(mavlink_log_data_t &msg)
42+
{
43+
if (output_fh == -1) {
44+
::fprintf(stderr, "opening output_fh\n");
45+
char filename[120];
46+
snprintf(filename, sizeof(filename), "log%u.bin", id_to_fetch);
47+
output_fh = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644);
48+
if (output_fh == -1) {
49+
::fprintf(stderr, "Failed to open (%s): %s", filename, strerror(errno));
50+
abort();
51+
}
52+
// expand the file:
53+
if (lseek(output_fh, log_info->size, SEEK_SET) == -1) {
54+
::fprintf(stderr, "Failed to seek\n");
55+
abort();
56+
}
57+
}
58+
last_data_receieved = clock_gettime_us(CLOCK_MONOTONIC);
59+
if (lseek(output_fh, msg.ofs, SEEK_SET) == -1) {
60+
::fprintf(stderr, "Failed to seek\n");
61+
abort();
62+
}
63+
// ::fprintf(stderr, "Writing %u bytes at offset %u\n", msg.count, msg.ofs);
64+
if (write(output_fh, msg.data, msg.count) != msg.count) {
65+
::fprintf(stderr, "Short write\n");
66+
abort();
67+
}
68+
}
69+
70+
void Log_Download::handle_decoded_message(uint64_t T UNUSED, mavlink_log_data_t &msg)
71+
{
72+
if (msg.id != id_to_fetch) {
73+
return;
74+
}
75+
// la_log(LOG_ERR, "data: ofs=%u count=%u", msg.ofs, msg.count);
76+
// ::fprintf(stderr, "data: ofs=%u count=%u\n", msg.ofs, msg.count);
77+
78+
for (auto it=gaps.begin(); it != gaps.end(); it++) {
79+
Gap *gap = *it;
80+
if (msg.ofs >= gap->start &&
81+
msg.ofs < gap->start+gap->len) {
82+
write_data(msg);
83+
}
84+
// ::fprintf(stderr, "msg.ofs=%u gap->start=%u\n", msg.ofs, gap->start);
85+
if (msg.ofs == gap->start) {
86+
// msg is at start of gap
87+
if (msg.count >= gap->len) {
88+
// this gap is all done!
89+
gap->len = 0;
90+
} else {
91+
gap->start += msg.count;
92+
if (msg.count > gap->len) {
93+
gap->len = 0;
94+
} else {
95+
gap->len -= msg.count;
96+
}
97+
}
98+
} else if (msg.ofs + msg.count >= gap->start+gap->len) {
99+
// msg is at end of gap
100+
if (msg.ofs + msg.count == gap->start+gap->len) {
101+
// right at end of buffer
102+
gap->len -= (gap->start+gap->len)-msg.ofs;
103+
}
104+
} else {
105+
::fprintf(stderr, "Split required\n");
106+
abort();
107+
}
108+
}
109+
}
110+
111+
112+
void Log_Download::send_log_request_list(uint16_t id)
113+
{
114+
la_log(LOG_INFO, "mh-ld: sending log_request_list (%u) to %u/%u", id, target_system_id, target_component_id);
115+
116+
mavlink_message_t msg;
117+
mavlink_msg_log_request_list_pack(system_id,
118+
component_id,
119+
&msg,
120+
target_system_id,
121+
target_component_id,
122+
id,
123+
id);
124+
125+
_mavlink_writer->send_message(msg);
126+
}
127+
128+
uint64_t Log_Download::time_in_state() const
129+
{
130+
return (clock_gettime_us(CLOCK_MONOTONIC) - state_start_time);
131+
}
132+
133+
void Log_Download::send_data_request(Gap *gap)
134+
{
135+
mavlink_message_t msg;
136+
mavlink_msg_log_request_data_pack(system_id,
137+
component_id,
138+
&msg,
139+
target_system_id,
140+
target_component_id,
141+
id_to_fetch,
142+
gap->start,
143+
gap->len);
144+
145+
_mavlink_writer->send_message(msg);
146+
}
147+
148+
void Log_Download::sort_gaps()
149+
{
150+
if (gaps.front()->len == 0) {
151+
gaps.erase(gaps.begin());
152+
}
153+
}
154+
155+
156+
void Log_Download::run_statemachine()
157+
{
158+
switch(state) {
159+
case WAITING_FOR_SYSTEM:
160+
if (target_system_id == 0) {
161+
if (most_recent_sender_component_id == 0) {
162+
return;
163+
}
164+
}
165+
target_system_id = most_recent_sender_system_id;
166+
target_component_id = most_recent_sender_component_id;
167+
state = NEXT_LOG;
168+
// fall-through
169+
case NEXT_LOG: {
170+
if (logs_to_download.size() == 0) {
171+
state = DONE;
172+
return;
173+
}
174+
id_to_fetch = logs_to_download.back();
175+
logs_to_download.pop_back();
176+
::fprintf(stderr, "Downloading log %u\n", id_to_fetch);
177+
delete log_info;
178+
log_info = nullptr;
179+
state = SEND_GET_LOG_INFO;
180+
}
181+
// fall-through
182+
case SEND_GET_LOG_INFO:
183+
send_log_request_list(id_to_fetch);
184+
state = SENT_GET_LOG_INFO;
185+
return;
186+
case SENT_GET_LOG_INFO: {
187+
if (log_info == nullptr) {
188+
if (time_in_state() > 1000000) {
189+
state = SEND_GET_LOG_INFO;
190+
}
191+
return;
192+
}
193+
Gap *gap = new Gap();
194+
gap->start = 0;
195+
gap->len = log_info->size;
196+
gaps.push_back(gap);
197+
state = FILL_GAPS;
198+
download_start_time = clock_gettime_us(CLOCK_MONOTONIC);
199+
}
200+
// fall-through
201+
case FILL_GAPS: {
202+
bool new_gap = false;
203+
if (gaps.front()->len == 0) {
204+
sort_gaps();
205+
new_gap = true;
206+
}
207+
if (gaps.size() == 0) {
208+
state = DONE;
209+
close(output_fh);
210+
output_fh = -1;
211+
return;
212+
}
213+
if (new_gap ||
214+
clock_gettime_us(CLOCK_MONOTONIC) - last_data_receieved > 100000) {
215+
send_data_request(gaps.front());
216+
}
217+
return;
218+
}
219+
case DONE: {
220+
const uint64_t delta_time = clock_gettime_us(CLOCK_MONOTONIC) - download_start_time;
221+
const float rate = (log_info->size/1024.0)/(delta_time/1000000.0);
222+
::fprintf(stderr, "Download done (%u bytes) (%lus) (%fkB/s)\n",
223+
log_info->size,
224+
delta_time/1000000,
225+
rate);
226+
exit(0);
227+
}
228+
}
229+
}
230+
231+
void Log_Download::idle_10Hz()
232+
{
233+
run_statemachine();
234+
if (state != last_state) {
235+
last_state = state;
236+
state_start_time = clock_gettime_us(CLOCK_MONOTONIC);
237+
}
238+
}

log_download.h

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#ifndef LOG_DOWNLOAD_H
2+
#define LOG_DOWNLOAD_H
3+
4+
#include <vector>
5+
6+
/*
7+
* log_download
8+
*
9+
* download log data over a mavlink connection
10+
*
11+
*/
12+
13+
#include "INIReader.h"
14+
15+
#include "mavlink_message_handler.h"
16+
#include "mavlink_writer.h"
17+
18+
class Log_Download : public MAVLink_Message_Handler {
19+
20+
public:
21+
Log_Download(MAVLink_Writer *mavlink_writer, std::vector<uint16_t> _logs_to_download) :
22+
MAVLink_Message_Handler(),
23+
_mavlink_writer(mavlink_writer),
24+
target_system_id(target_system_id_default),
25+
target_component_id(target_component_id_default),
26+
logs_to_download(_logs_to_download)
27+
{ }
28+
29+
private:
30+
31+
bool configure(INIReader *config);
32+
33+
void idle_10Hz();
34+
35+
MAVLink_Writer *_mavlink_writer = NULL;
36+
uint8_t this_system_id = 58;
37+
uint8_t this_component_id = 58;
38+
39+
const uint8_t target_system_id_default = 0;
40+
const uint8_t target_component_id_default = 0;
41+
uint8_t most_recent_sender_system_id;
42+
uint8_t most_recent_sender_component_id;
43+
uint8_t target_system_id; // who to send our request-for-logs to
44+
uint8_t target_component_id; // who to send our request-for-logs to
45+
46+
int output_fh = -1;
47+
48+
void sort_gaps();
49+
50+
void handle_message(uint64_t timestamp, mavlink_message_t &msg) override;
51+
void handle_decoded_message(uint64_t T UNUSED, mavlink_log_data_t &msg) override;
52+
void handle_decoded_message(uint64_t T UNUSED, mavlink_log_entry_t &msg) override;
53+
54+
void send_log_request_list(uint16_t id);
55+
56+
void write_data(mavlink_log_data_t &msg);
57+
58+
std::vector<uint16_t> logs_to_download;
59+
uint16_t id_to_fetch;
60+
61+
class LogEntry {
62+
public:
63+
uint16_t id;
64+
uint32_t time_utc;
65+
uint32_t size;
66+
};
67+
LogEntry *log_info = nullptr;
68+
69+
class Gap {
70+
public:
71+
uint32_t start;
72+
uint32_t len;
73+
};
74+
std::vector<Gap*> gaps;
75+
76+
uint64_t download_start_time;
77+
void send_data_request(Gap *gap);
78+
79+
enum state_t {
80+
NEXT_LOG,
81+
WAITING_FOR_SYSTEM,
82+
SEND_GET_LOG_INFO,
83+
SENT_GET_LOG_INFO,
84+
FILL_GAPS,
85+
DONE,
86+
};
87+
state_t state = WAITING_FOR_SYSTEM;
88+
state_t last_state;
89+
uint64_t state_start_time;
90+
91+
uint64_t last_data_receieved;
92+
void run_statemachine();
93+
94+
uint64_t time_in_state() const;
95+
96+
97+
};
98+
99+
#endif

0 commit comments

Comments
 (0)