7 #include <satcat5/eth_dispatch.h>
9 #include <satcat5/udp_dispatch.h>
13 constexpr
unsigned DEBUG_VERBOSE = 0;
18 satcat5::eth::AeroFtpServer::AeroFtpServer(
19 const char* work_folder,
27 satcat5::udp::AeroFtpServer::AeroFtpServer(
28 const char* work_folder,
44 static constexpr
unsigned WORDS_PER_BLOCK = 256;
46 static constexpr
unsigned words2blocks(
unsigned words)
49 inline std::string make_filename(
const char* work,
unsigned id,
const char* typ)
52 snprintf(temp,
sizeof(temp),
"%s/file_%08u.%s", work,
id, typ);
53 return std::string(temp);
56 AeroFtpFile::AeroFtpFile(
const char* work,
unsigned id,
unsigned len,
bool resume)
57 : m_name_data(make_filename(work, id,
"data"))
58 , m_name_meta(make_filename(work, id,
"rcvd"))
59 , m_name_part(make_filename(work, id,
"part"))
62 , m_bcount(words2blocks(len))
66 , m_pending(new u8[m_bcount])
67 , m_status(m_pending, 0)
70 FILE* temp = fopen(m_name_data.c_str(),
"rb");
71 bool done = (temp && !ferror(temp));
72 if (temp) fclose(temp);
74 log(satcat5::log::INFO,
"Already complete");
77 remove(m_name_data.c_str());
81 m_data = fopen(m_name_part.c_str(), resume ?
"ab+" :
"wb+");
82 m_meta = fopen(m_name_meta.c_str(), resume ?
"ab+" :
"wb+");
85 unsigned meta_len = (unsigned)ftell(m_meta);
86 if (!(m_data && m_meta)) {
88 log(satcat5::log::ERROR,
"File creation error");
91 }
else if (meta_len == 0) {
93 log(satcat5::log::INFO,
done ?
"Restart file" :
"New file");
95 memset(m_pending, 1, m_bcount);
96 fwrite(m_pending, 1, m_bcount, m_meta);
97 }
else if (meta_len == m_bcount) {
99 log(satcat5::log::INFO,
"Continued file");
100 fseek(m_meta, 0, SEEK_SET);
101 fread(m_pending, 1, m_bcount, m_meta);
102 for (
unsigned a = 0 ; a < m_bcount ; ++a) {
103 if (m_pending[a]) ++m_pcount;
107 log(satcat5::log::ERROR,
"Length mismatch");
113 AeroFtpFile::~AeroFtpFile()
119 void AeroFtpFile::cleanup()
121 if (m_data) fclose(m_data);
122 if (m_meta) fclose(m_meta);
127 void AeroFtpFile::log(s8 level,
const char* msg)
135 u32 file_id, u32 file_len, u32 offset, u32 rxlen,
138 if (DEBUG_VERBOSE > 1) log(satcat5::log::DEBUG,
"Frame received");
141 u32 block = words2blocks(offset);
142 u32 expected = min_u32(WORDS_PER_BLOCK, m_file_len - offset);
146 if (file_id != m_file_id)
return;
147 if (file_len != m_file_len)
return;
148 if (block >= m_bcount)
return;
149 if (block*WORDS_PER_BLOCK != offset)
return;
150 if (rxlen != expected)
return;
154 if (!m_pending[block])
return;
157 u8 temp[4*WORDS_PER_BLOCK];
159 fseek(m_data, (s64)(4ull*offset), SEEK_SET);
160 fwrite(temp, 1, 4*rxlen, m_data);
164 m_pending[block] = 0;
165 fseek(m_meta, (s64)block, SEEK_SET);
166 fwrite(m_pending + block, 1, 1, m_meta);
170 log(satcat5::log::INFO,
"Completed file");
172 rename(m_name_part.c_str(), m_name_data.c_str());
173 remove(m_name_meta.c_str());
174 }
else if (DEBUG_VERBOSE > 0) {
175 log(satcat5::log::DEBUG,
"Frame accepted");
181 if (
error())
return 0;
187 const char* work_folder,
191 , m_work_folder(work_folder)
198 AeroFtpServer::~AeroFtpServer()
201 for (
auto x = m_files.begin() ; x != m_files.end() ; ++x) {
208 auto x = m_files.find(file_id);
209 if (x != m_files.end()) {
210 return x->second->missing_blocks();
218 auto x = m_files.find(file_id);
219 if (x != m_files.end()) {
220 return x->second->done();
229 u32 file_id = src.read_u32();
230 u32 file_len = src.read_u32();
231 u32 blk_off = src.read_u32();
232 u32 blk_len = src.read_u32();
235 if (file_len == 0)
return;
236 if (blk_off % WORDS_PER_BLOCK != 0)
return;
237 if (blk_len > WORDS_PER_BLOCK)
return;
240 if (m_files.find(file_id) == m_files.end()) {
242 m_work_folder.c_str(), file_id, file_len, m_resume);
246 m_files[file_id]->frame_rcvd(file_id, file_len, blk_off, blk_len, src);
Implemention of "net::Dispatch" for Ethernet frames.
Ephemeral Readable interface for a simple array.
void read_reset(unsigned len)
Reset read position to the start of the backing array, and set the readable length to the specified v...
Limited read of next N bytes.
bool read_bytes(unsigned nbytes, void *dst) override
Read 0 or more bytes into a buffer.
unsigned get_read_ready() const override
How many bytes can be read without blocking?
Abstract API for reading byte-streams and packets.
The Log class creates and formats one log message.
Log & write10(s32 val)
Print integer as a decimal value with no leading zeros.
Log & write(const char *str)
Formatting methods for various data types.
Helper class representing a saving data to a particular file.
bool error() const
Has there been an unrecoverable file-transfer error?
bool done() const
Has the complete file been received successfully?
void frame_rcvd(u32 file_id, u32 file_len, u32 offset, u32 rxlen, satcat5::io::LimitedRead &data)
Handler for each received packet relating to this file.
satcat5::io::Readable * missing_blocks()
Get a stream of missing blocks for this file.
Server for receiving file(s) using AeroFTP.
void frame_rcvd(satcat5::io::LimitedRead &src) override
Callback for incoming packets.
satcat5::io::Readable * missing_blocks(u32 file_id)
Get a stream of missing blocks for the designated file-ID.
AeroFtpServer(const char *work_folder, satcat5::net::Dispatch *iface, const satcat5::net::Type &typ)
Constructor is only available to child classes.
bool done(u32 file_id) const
Has the complete file been received successfully?
The "Dispatch" is a generic interface that knows how to read a designated protocol layer and sort inc...
void add(satcat5::net::Protocol *proto)
Register a Protocol object.
void remove(satcat5::net::Protocol *proto)
Unregister a Protocol object.
A net::Protocol is the counterpart to net::Dispatch that handles a particular data stream,...
Dispatcher sorts incoming UDP messages by port index.
AeroCube File Transfer Protocol (AeroFTP) receiver.
Diagnostic logging to UART and/or Ethernet ports.
EtherType field (uint16) is used a protocol-ID [1536..65535].
UDP and TCP ports are both 16-bit unsigned integers.
Multipurpose filter for matching fields in network packets.
Miscellaneous mathematical utility functions.
constexpr u32 min_u32(u32 a, u32 b)
Min and max functions.
constexpr T div_ceil(T a, T b)
Integer division functions with various rounding options: