mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-04-14 10:07:39 +08:00
File server implementation with test
This commit is contained in:
parent
cd41840f59
commit
58ca7319dd
@ -3,16 +3,17 @@
|
||||
# File operation result code.
|
||||
#
|
||||
|
||||
int16 OK = 0
|
||||
int16 UNKNOWN_ERROR = 32767
|
||||
int16 OK = 0
|
||||
int16 UNKNOWN_ERROR = 32767
|
||||
|
||||
# These error codes match some standard UNIX errno values
|
||||
int16 NOT_FOUND = 2
|
||||
int16 IO_ERROR = 5
|
||||
int16 ACCESS_DENIED = 13
|
||||
int16 IS_DIRECTORY = 21 # I.e. attempt to read/write on a path that points to a directory
|
||||
int16 INVALID_VALUE = 22 # E.g. file name is not valid for the target file system
|
||||
int16 FILE_TOO_LARGE = 27
|
||||
int16 OUT_OF_SPACE = 28
|
||||
int16 NOT_FOUND = 2
|
||||
int16 IO_ERROR = 5
|
||||
int16 ACCESS_DENIED = 13
|
||||
int16 IS_DIRECTORY = 21 # I.e. attempt to read/write on a path that points to a directory
|
||||
int16 INVALID_VALUE = 22 # E.g. file name is not valid for the target file system
|
||||
int16 FILE_TOO_LARGE = 27
|
||||
int16 OUT_OF_SPACE = 28
|
||||
int16 NOT_IMPLEMENTED = 38
|
||||
|
||||
int16 value
|
||||
|
||||
180
libuavcan/include/uavcan/protocol/file_server.hpp
Normal file
180
libuavcan/include/uavcan/protocol/file_server.hpp
Normal file
@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef UAVCAN_PROTOCOL_FILE_SERVER_HPP_INCLUDED
|
||||
#define UAVCAN_PROTOCOL_FILE_SERVER_HPP_INCLUDED
|
||||
|
||||
#include <uavcan/build_config.hpp>
|
||||
#include <uavcan/debug.hpp>
|
||||
#include <uavcan/node/service_server.hpp>
|
||||
#include <uavcan/util/method_binder.hpp>
|
||||
// UAVCAN types
|
||||
#include <uavcan/protocol/file/GetInfo.hpp>
|
||||
#include <uavcan/protocol/file/GetDirectoryEntryInfo.hpp>
|
||||
#include <uavcan/protocol/file/Read.hpp>
|
||||
#include <uavcan/protocol/file/Write.hpp>
|
||||
#include <uavcan/protocol/file/Delete.hpp>
|
||||
|
||||
namespace uavcan
|
||||
{
|
||||
/**
|
||||
* The file server backend should implement this interface.
|
||||
*/
|
||||
class UAVCAN_EXPORT IFileServerBackend
|
||||
{
|
||||
public:
|
||||
typedef protocol::file::Path::FieldTypes::path Path;
|
||||
typedef protocol::file::EntryType EntryType;
|
||||
typedef protocol::file::Error Error;
|
||||
|
||||
/**
|
||||
* Use this class to compute CRC64 for uavcan.protocol.file.GetInfo.
|
||||
*/
|
||||
typedef DataTypeSignatureCRC FileCRC;
|
||||
|
||||
/**
|
||||
* All read operations must return this number of bytes, unless end of file is reached.
|
||||
*/
|
||||
enum { ReadSize = protocol::file::Read::Response::FieldTypes::data::MaxSize };
|
||||
|
||||
/**
|
||||
* Shortcut for uavcan.protocol.file.Path.SEPARATOR.
|
||||
*/
|
||||
static char getPathSeparator() { return static_cast<char>(protocol::file::Path::SEPARATOR); }
|
||||
|
||||
/**
|
||||
* Backend for uavcan.protocol.file.GetInfo.
|
||||
* Implementation of this method is required.
|
||||
* On success the method must return zero.
|
||||
*/
|
||||
virtual int16_t getInfo(const Path& path, uint64_t& out_crc64, uint32_t& out_size, EntryType& out_type) = 0;
|
||||
|
||||
/**
|
||||
* Backend for uavcan.protocol.file.Read.
|
||||
* Implementation of this method is required.
|
||||
* @ref inout_size is set to @ref ReadSize; read operation is required to return exactly this amount, except
|
||||
* if the end of file is reached.
|
||||
* On success the method must return zero.
|
||||
*/
|
||||
virtual int16_t read(const Path& path, const uint32_t offset, uint8_t* out_buffer, uint16_t& inout_size) = 0;
|
||||
|
||||
// Methods below are optional.
|
||||
|
||||
/**
|
||||
* Backend for uavcan.protocol.file.Write.
|
||||
* Implementation of this method is NOT required; by default it returns uavcan.protocol.file.Error.NOT_IMPLEMENTED.
|
||||
* On success the method must return zero.
|
||||
*/
|
||||
virtual int16_t write(const Path& path, const uint32_t offset, const uint8_t* buffer, const uint16_t size)
|
||||
{
|
||||
(void)path;
|
||||
(void)offset;
|
||||
(void)buffer;
|
||||
(void)size;
|
||||
return Error::NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backend for uavcan.protocol.file.Delete. ('delete' is a C++ keyword, so 'remove' is used instead)
|
||||
* Implementation of this method is NOT required; by default it returns uavcan.protocol.file.Error.NOT_IMPLEMENTED.
|
||||
* On success the method must return zero.
|
||||
*/
|
||||
virtual int16_t remove(const Path& path)
|
||||
{
|
||||
(void)path;
|
||||
return Error::NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backend for uavcan.protocol.file.GetDirectoryEntryInfo.
|
||||
* Implementation of this method is NOT required; by default it returns uavcan.protocol.file.Error.NOT_IMPLEMENTED.
|
||||
* On success the method must return zero.
|
||||
*/
|
||||
virtual int16_t getDirectoryEntryInfo(const Path& directory_path, const uint32_t entry_index,
|
||||
EntryType& out_type, Path& out_entry_full_path)
|
||||
{
|
||||
(void)directory_path;
|
||||
(void)entry_index;
|
||||
(void)out_type;
|
||||
(void)out_entry_full_path;
|
||||
return Error::NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
virtual ~IFileServerBackend() { }
|
||||
};
|
||||
|
||||
/**
|
||||
* Basic file server implements only the following services:
|
||||
* uavcan.protocol.file.GetInfo
|
||||
* uavcan.protocol.file.Read
|
||||
* Also see @ref IFileServerBackend.
|
||||
*/
|
||||
class BasicFileServer
|
||||
{
|
||||
typedef MethodBinder<BasicFileServer*,
|
||||
void (BasicFileServer::*)(const protocol::file::GetInfo::Request&, protocol::file::GetInfo::Response&)>
|
||||
GetInfoCallback;
|
||||
|
||||
typedef MethodBinder<BasicFileServer*,
|
||||
void (BasicFileServer::*)(const protocol::file::Read::Request&, protocol::file::Read::Response&)>
|
||||
ReadCallback;
|
||||
|
||||
ServiceServer<protocol::file::GetInfo, GetInfoCallback> get_info_srv_;
|
||||
ServiceServer<protocol::file::Read, ReadCallback> read_srv_;
|
||||
|
||||
void handleGetInfo(const protocol::file::GetInfo::Request& req, protocol::file::GetInfo::Response& resp)
|
||||
{
|
||||
resp.error.value = backend_.getInfo(req.path.path, resp.crc64, resp.size, resp.entry_type);
|
||||
}
|
||||
|
||||
void handleRead(const protocol::file::Read::Request& req, protocol::file::Read::Response& resp)
|
||||
{
|
||||
uint16_t inout_size = resp.data.capacity();
|
||||
|
||||
resp.data.resize(inout_size);
|
||||
|
||||
resp.error.value = backend_.read(req.path.path, req.offset, resp.data.begin(), inout_size);
|
||||
|
||||
if (inout_size > resp.data.capacity())
|
||||
{
|
||||
UAVCAN_ASSERT(0);
|
||||
resp.error.value = protocol::file::Error::UNKNOWN_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
resp.data.resize(inout_size);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
IFileServerBackend& backend_; ///< Derived types can use it
|
||||
|
||||
public:
|
||||
BasicFileServer(INode& node, IFileServerBackend& backend)
|
||||
: get_info_srv_(node)
|
||||
, read_srv_(node)
|
||||
, backend_(backend)
|
||||
{ }
|
||||
|
||||
int start()
|
||||
{
|
||||
int res = get_info_srv_.start(GetInfoCallback(this, &BasicFileServer::handleGetInfo));
|
||||
if (res < 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
res = read_srv_.start(ReadCallback(this, &BasicFileServer::handleRead));
|
||||
if (res < 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // Include guard
|
||||
118
libuavcan/test/protocol/file_server.cpp
Normal file
118
libuavcan/test/protocol/file_server.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <uavcan/protocol/file_server.hpp>
|
||||
#include "helpers.hpp"
|
||||
|
||||
|
||||
class TestFileServerBackend : public uavcan::IFileServerBackend
|
||||
{
|
||||
public:
|
||||
static const std::string file_name;
|
||||
static const std::string file_data;
|
||||
|
||||
virtual int16_t getInfo(const Path& path, uint64_t& out_crc64, uint32_t& out_size, EntryType& out_type)
|
||||
{
|
||||
if (path == file_name)
|
||||
{
|
||||
{
|
||||
FileCRC crc;
|
||||
crc.add(reinterpret_cast<const uavcan::uint8_t*>(file_data.c_str()), unsigned(file_data.length()));
|
||||
out_crc64 = crc.get();
|
||||
}
|
||||
out_size = uint16_t(file_data.length());
|
||||
out_type.flags |= EntryType::FLAG_FILE;
|
||||
out_type.flags |= EntryType::FLAG_READABLE;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Error::NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
virtual int16_t read(const Path& path, const uint32_t offset, uint8_t* out_buffer, uint16_t& inout_size)
|
||||
{
|
||||
if (path == file_name)
|
||||
{
|
||||
if (offset < file_data.length())
|
||||
{
|
||||
inout_size = uint16_t(file_data.length() - offset);
|
||||
std::memcpy(out_buffer, file_data.c_str() + offset, inout_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
inout_size = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Error::NOT_FOUND;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const std::string TestFileServerBackend::file_name = "test";
|
||||
const std::string TestFileServerBackend::file_data = "123456789";
|
||||
|
||||
TEST(BasicFileServer, Basic)
|
||||
{
|
||||
using namespace uavcan::protocol::file;
|
||||
|
||||
uavcan::GlobalDataTypeRegistry::instance().reset();
|
||||
uavcan::DefaultDataTypeRegistrator<GetInfo> _reg1;
|
||||
uavcan::DefaultDataTypeRegistrator<Read> _reg2;
|
||||
|
||||
InterlinkedTestNodesWithSysClock nodes;
|
||||
|
||||
TestFileServerBackend backend;
|
||||
|
||||
uavcan::BasicFileServer serv(nodes.a, backend);
|
||||
std::cout << "sizeof(uavcan::BasicFileServer): " << sizeof(uavcan::BasicFileServer) << std::endl;
|
||||
|
||||
ASSERT_LE(0, serv.start());
|
||||
|
||||
/*
|
||||
* GetInfo, existing file
|
||||
*/
|
||||
{
|
||||
ServiceClientWithCollector<GetInfo> get_info(nodes.b);
|
||||
|
||||
GetInfo::Request get_info_req;
|
||||
get_info_req.path.path = "test";
|
||||
|
||||
ASSERT_LE(0, get_info.call(1, get_info_req));
|
||||
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(10));
|
||||
|
||||
ASSERT_TRUE(get_info.collector.result.get());
|
||||
ASSERT_TRUE(get_info.collector.result->isSuccessful());
|
||||
|
||||
ASSERT_EQ(0x62EC59E3F1A4F00A, get_info.collector.result->getResponse().crc64);
|
||||
ASSERT_EQ(0, get_info.collector.result->getResponse().error.value);
|
||||
ASSERT_EQ(9, get_info.collector.result->getResponse().size);
|
||||
ASSERT_EQ(EntryType::FLAG_FILE | EntryType::FLAG_READABLE,
|
||||
get_info.collector.result->getResponse().entry_type.flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read, existing file
|
||||
*/
|
||||
{
|
||||
ServiceClientWithCollector<Read> read(nodes.b);
|
||||
|
||||
Read::Request read_req;
|
||||
read_req.path.path = "test";
|
||||
|
||||
ASSERT_LE(0, read.call(1, read_req));
|
||||
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(10));
|
||||
|
||||
ASSERT_TRUE(read.collector.result.get());
|
||||
ASSERT_TRUE(read.collector.result->isSuccessful());
|
||||
|
||||
ASSERT_EQ("123456789", read.collector.result->getResponse().data);
|
||||
ASSERT_EQ(0, read.collector.result->getResponse().error.value);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user