mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-07-07 16:50:36 +08:00
Outgoing Transfer ID registry
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <stdint.h>
|
||||
#include <uavcan/internal/map.hpp>
|
||||
#include <uavcan/internal/debug.hpp>
|
||||
#include <uavcan/internal/transport/transfer.hpp>
|
||||
|
||||
namespace uavcan
|
||||
{
|
||||
|
||||
#pragma pack(push, 1)
|
||||
class OutgoingTransferRegistryKey
|
||||
{
|
||||
uint16_t data_type_id_;
|
||||
uint8_t transfer_type_;
|
||||
uint8_t destination_node_id_; ///< Not applicable for message broadcasting
|
||||
|
||||
public:
|
||||
OutgoingTransferRegistryKey()
|
||||
: data_type_id_(0xFFFF)
|
||||
, transfer_type_(0xFF)
|
||||
, destination_node_id_(NODE_ID_INVALID)
|
||||
{ }
|
||||
|
||||
OutgoingTransferRegistryKey(uint16_t data_type_id, TransferType transfer_type, uint8_t destination_node_id)
|
||||
: data_type_id_(data_type_id)
|
||||
, transfer_type_(transfer_type)
|
||||
, destination_node_id_(destination_node_id)
|
||||
{
|
||||
assert(destination_node_id != NODE_ID_INVALID);
|
||||
assert((transfer_type == TRANSFER_TYPE_MESSAGE_BROADCAST) == (destination_node_id == NODE_ID_BROADCAST));
|
||||
|
||||
/* Service response transfers must use the same Transfer ID as matching service request transfer,
|
||||
* so this registry is not applicable for service response transfers at all.
|
||||
*/
|
||||
assert(transfer_type != TRANSFER_TYPE_SERVICE_RESPONSE);
|
||||
}
|
||||
|
||||
bool operator==(const OutgoingTransferRegistryKey& rhs) const
|
||||
{
|
||||
return
|
||||
(data_type_id_ == rhs.data_type_id_) &&
|
||||
(transfer_type_ == rhs.transfer_type_) &&
|
||||
(destination_node_id_ == rhs.destination_node_id_);
|
||||
}
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
class IOutgoingTransferRegistry
|
||||
{
|
||||
public:
|
||||
virtual ~IOutgoingTransferRegistry() { }
|
||||
virtual TransferID* accessOrCreate(const OutgoingTransferRegistryKey& key, uint64_t new_monotonic_deadline) = 0;
|
||||
virtual void cleanup(uint64_t monotonic_deadline) = 0;
|
||||
};
|
||||
|
||||
|
||||
template <int NUM_STATIC_ENTRIES>
|
||||
class OutgoingTransferRegistry : public IOutgoingTransferRegistry, Noncopyable
|
||||
{
|
||||
#pragma pack(push, 1)
|
||||
struct Value
|
||||
{
|
||||
uint64_t monotonic_deadline;
|
||||
TransferID tid;
|
||||
Value() : monotonic_deadline(0) { }
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
class DeadlineExpiredPredicate
|
||||
{
|
||||
const uint64_t ts_monotonic_;
|
||||
|
||||
public:
|
||||
DeadlineExpiredPredicate(uint64_t ts_monotonic)
|
||||
: ts_monotonic_(ts_monotonic)
|
||||
{ }
|
||||
|
||||
bool operator()(const OutgoingTransferRegistryKey& key, const Value& value) const
|
||||
{
|
||||
(void)key;
|
||||
assert(value.monotonic_deadline > 0);
|
||||
return value.monotonic_deadline <= ts_monotonic_;
|
||||
}
|
||||
};
|
||||
|
||||
Map<OutgoingTransferRegistryKey, Value, NUM_STATIC_ENTRIES> map_;
|
||||
|
||||
public:
|
||||
OutgoingTransferRegistry(IAllocator* allocator)
|
||||
: map_(allocator)
|
||||
{ }
|
||||
|
||||
TransferID* accessOrCreate(const OutgoingTransferRegistryKey& key, uint64_t new_monotonic_deadline)
|
||||
{
|
||||
assert(new_monotonic_deadline > 0);
|
||||
Value* p = map_.access(key);
|
||||
if (p == NULL)
|
||||
p = map_.insert(key, Value());
|
||||
if (p == NULL)
|
||||
return NULL;
|
||||
p->monotonic_deadline = new_monotonic_deadline;
|
||||
return &p->tid;
|
||||
}
|
||||
|
||||
void cleanup(uint64_t ts_monotonic)
|
||||
{
|
||||
map_.removeWhere(DeadlineExpiredPredicate(ts_monotonic));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <gtest/gtest.h>
|
||||
#include <uavcan/internal/transport/outgoing_transfer_registry.hpp>
|
||||
|
||||
|
||||
TEST(OutgoingTransferRegistry, Basic)
|
||||
{
|
||||
using uavcan::OutgoingTransferRegistryKey;
|
||||
uavcan::PoolManager<1> poolmgr; // Empty
|
||||
uavcan::OutgoingTransferRegistry<4> otr(&poolmgr);
|
||||
|
||||
otr.cleanup(1000);
|
||||
|
||||
static const int NUM_KEYS = 5;
|
||||
const OutgoingTransferRegistryKey keys[NUM_KEYS] =
|
||||
{
|
||||
OutgoingTransferRegistryKey(123, uavcan::TRANSFER_TYPE_MESSAGE_UNICAST, 42),
|
||||
OutgoingTransferRegistryKey(123, uavcan::TRANSFER_TYPE_MESSAGE_BROADCAST, 0),
|
||||
OutgoingTransferRegistryKey(123, uavcan::TRANSFER_TYPE_SERVICE_REQUEST, 2),
|
||||
OutgoingTransferRegistryKey(123, uavcan::TRANSFER_TYPE_MESSAGE_UNICAST, 4),
|
||||
OutgoingTransferRegistryKey(456, uavcan::TRANSFER_TYPE_SERVICE_REQUEST, 2)
|
||||
};
|
||||
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[0], 1000000)->get());
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[1], 1000000)->get());
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[2], 1000000)->get());
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[3], 1000000)->get());
|
||||
ASSERT_FALSE(otr.accessOrCreate(keys[4], 1000000)); // OOM
|
||||
|
||||
/*
|
||||
* Incrementing a little
|
||||
*/
|
||||
otr.accessOrCreate(keys[0], 2000000)->increment();
|
||||
otr.accessOrCreate(keys[0], 4000000)->increment();
|
||||
otr.accessOrCreate(keys[0], 3000000)->increment();
|
||||
ASSERT_EQ(3, otr.accessOrCreate(keys[0], 5000000)->get());
|
||||
|
||||
otr.accessOrCreate(keys[2], 2000000)->increment();
|
||||
otr.accessOrCreate(keys[2], 3000000)->increment();
|
||||
ASSERT_EQ(2, otr.accessOrCreate(keys[2], 6000000)->get());
|
||||
|
||||
otr.accessOrCreate(keys[3], 9000000)->increment();
|
||||
ASSERT_EQ(1, otr.accessOrCreate(keys[3], 4000000)->get());
|
||||
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[1], 4000000)->get());
|
||||
|
||||
ASSERT_FALSE(otr.accessOrCreate(keys[4], 1000000)); // Still OOM
|
||||
|
||||
/*
|
||||
* Cleaning up
|
||||
*/
|
||||
otr.cleanup(4000001); // Kills 1, 3
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[1], 1000000)->get());
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[3], 1000000)->get());
|
||||
otr.accessOrCreate(keys[1], 5000000)->increment();
|
||||
otr.accessOrCreate(keys[3], 5000000)->increment();
|
||||
|
||||
ASSERT_EQ(3, otr.accessOrCreate(keys[0], 5000000)->get());
|
||||
ASSERT_EQ(2, otr.accessOrCreate(keys[2], 6000000)->get());
|
||||
|
||||
otr.cleanup(5000001); // Kills 1, 3 (He needs a bath, Jud. He stinks of the ground you buried him in.), 0
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[0], 1000000)->get());
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[1], 1000000)->get());
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[3], 1000000)->get());
|
||||
|
||||
ASSERT_EQ(2, otr.accessOrCreate(keys[2], 1000000)->get());
|
||||
|
||||
otr.cleanup(5000001); // Frees some memory for 4
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[0], 1000000)->get());
|
||||
}
|
||||
Reference in New Issue
Block a user