libxr
Embedded C++ framework for bare-metal, RTOS and Linux
Loading...
Searching...
No Matches
topic.hpp
1#pragma once
2
3#include <atomic>
4#include <concepts>
5#include <cstddef>
6#include <cstdint>
7
8#include "libxr_cb.hpp"
9#include "libxr_def.hpp"
10#include "libxr_time.hpp"
11#include "libxr_type.hpp"
12#include "lockfree_list.hpp"
13#include "mutex.hpp"
14#include "queue.hpp"
15#include "rbt.hpp"
16#include "semaphore.hpp"
17#include "thread.hpp"
18
19namespace LibXR
20{
26template <typename Data>
27concept TopicPayload =
28 !std::is_reference_v<Data> && !std::is_const_v<Data> && !std::is_volatile_v<Data> &&
29 std::is_object_v<Data> && std::is_default_constructible_v<Data> &&
30 std::is_copy_assignable_v<Data> && std::is_trivially_destructible_v<Data>;
31
37template <typename Data>
38constexpr void CheckTopicPayload()
39{
40 static_assert(TopicPayload<Data>,
41 "LibXR::Topic typed payload must be a non-cv/ref object type that is "
42 "default-constructible, copy-assignable, and trivially destructible.");
43}
44
55class Topic
56{
61 enum class LockState : uint32_t
62 {
63 UNLOCKED = 0,
64 LOCKED = 1,
66 USE_MUTEX = UINT32_MAX
68 };
69
70 public:
81 struct Block
82 {
83 std::atomic<LockState>
86 TypeID::ID
88 uint32_t payload_size;
92 uint32_t crc32;
94 };
95
96#ifndef __DOXYGEN__
101 struct PackedDataHeader;
102
109 template <typename Data>
110 class PackedData;
111 static constexpr uint8_t PACKET_PREFIX =
112 0x5A;
113 static constexpr uint8_t PACKET_VERSION =
114 0x01;
115 static constexpr size_t PACK_BASE_SIZE =
116 17;
118#endif
119
126
133 template <typename Data>
135 {
136 static_assert(TopicPayload<Data>);
138 Data* data;
140 };
141
159
166 template <typename Data>
167 struct Message
168 {
169 static_assert(TopicPayload<Data>);
171 Data data;
172 };
173
179 static void Lock(TopicHandle topic);
180
186 static void Unlock(TopicHandle topic);
187
193 static void LockFromCallback(TopicHandle topic);
194
200 static void UnlockFromCallback(TopicHandle topic);
201
206 class Domain
207 {
208 public:
213 Domain(const char* name);
214
218 };
219
224 enum class SuberType : uint8_t
225 {
226 SYNC,
227 ASYNC,
228 QUEUE,
229 CALLBACK,
230 };
231
237 {
239 };
240
246 struct SyncBlock;
247
254 template <typename Data>
256
261 enum class ASyncSubscriberState : uint32_t;
262
268 struct ASyncBlock;
269
276 template <typename Data>
278
284 struct QueueBlock;
285
290 class QueuedSubscriber;
291
297 class Callback;
298
304 struct CallbackBlock;
305
311 class Server;
312
317 void RegisterCallback(Callback& cb);
318
324
328 Topic();
329
348 Topic(const char* name, TypeID::ID payload_type_id, size_t payload_size,
349 size_t payload_alignment, Domain* domain = nullptr, bool multi_publisher = false);
350
361 template <typename Data>
362 static Topic CreateTopic(const char* name, Domain* domain = nullptr,
363 bool multi_publisher = false)
364 {
366 return Topic(name, TypeID::GetID<Data>(), sizeof(Data), alignof(Data), domain,
367 multi_publisher);
368 }
369
375 Topic(TopicHandle topic);
376
383 static TopicHandle Find(const char* name, Domain* domain = nullptr);
384
398 template <typename Data>
399 static TopicHandle FindOrCreate(const char* name, Domain* domain = nullptr,
400 bool multi_publisher = false)
401 {
403 auto topic = Find(name, domain);
404 if (topic != nullptr)
405 {
406 RequireTypeContract(topic, TypeID::GetID<Data>(), sizeof(Data), alignof(Data));
407 if (multi_publisher)
408 {
409 RequireMultiPublisher(topic);
410 }
411 }
412 else
413 {
414 topic = CreateTopic<Data>(name, domain, multi_publisher).block_;
415 }
416 return topic;
417 }
418
423 [[nodiscard]] size_t PayloadSize() const
424 {
425 ASSERT(block_ != nullptr);
426 return block_->data_.payload_size;
427 }
428
434 [[nodiscard]] size_t PayloadAlignment() const
435 {
436 ASSERT(block_ != nullptr);
437 return block_->data_.payload_alignment;
438 }
439
446 template <typename Data>
447 void Publish(Data& data)
448 {
449 PublishTyped(data, NowTimestamp(), false, false);
450 }
451
459 template <typename Data>
460 void Publish(Data& data, MicrosecondTimestamp timestamp)
461 {
462 PublishTyped(data, timestamp, false, false);
463 }
464
472 template <typename Data>
473 void PublishFromCallback(Data& data, bool in_isr)
474 {
475 PublishTyped(data, NowTimestamp(), true, in_isr);
476 }
477
486 template <typename Data>
487 void PublishFromCallback(Data& data, MicrosecondTimestamp timestamp, bool in_isr)
488 {
489 PublishTyped(data, timestamp, true, in_isr);
490 }
491
501 void PublishBytesFromServer(void* payload_addr, size_t payload_size,
502 MicrosecondTimestamp timestamp)
503 {
504 PublishServerBytes(payload_addr, payload_size, timestamp, false, false);
505 }
506
518 void PublishBytesFromServerCallback(void* payload_addr, size_t payload_size,
519 MicrosecondTimestamp timestamp, bool in_isr)
520 {
521 PublishServerBytes(payload_addr, payload_size, timestamp, true, in_isr);
522 }
523
532 template <typename Data>
533 ErrorCode PackData(const Data& data, PackedData<Data>& packet)
534 {
535 return PackData(data, packet, NowTimestamp());
536 }
537
547 template <typename Data>
548 ErrorCode PackData(const Data& data, PackedData<Data>& packet,
549 MicrosecondTimestamp timestamp);
550
560 {
561 return PackRaw(data, packet, NowTimestamp());
562 }
563
574 {
575 if (block_ == nullptr || data.addr_ == nullptr || packet.addr_ == nullptr)
576 {
577 return ErrorCode::PTR_NULL;
578 }
579
580 if (data.size_ != block_->data_.payload_size)
581 {
582 return ErrorCode::SIZE_ERR;
583 }
584
585 if (packet.size_ < PACK_BASE_SIZE + data.size_)
586 {
587 return ErrorCode::NO_BUFF;
588 }
589
590 PackBytes(block_->data_.crc32, packet, timestamp, data);
591 return ErrorCode::OK;
592 }
593
601 static TopicHandle WaitTopic(const char* name, uint32_t timeout = UINT32_MAX,
602 Domain* domain = nullptr);
603
609 operator TopicHandle() { return block_; }
610
615 uint32_t GetKey() const;
616
617 private:
618 TopicHandle block_ = nullptr;
620
621 static inline RBTree<uint32_t>* domain_ =
622 nullptr;
623 static inline Domain* def_domain_ = nullptr;
624
628 static void EnsureDomainRegistry();
629
634 static Domain* EnsureDefaultDomain();
635
643 static void CheckServerPublishContract(TopicHandle topic, void* payload_addr,
644 size_t payload_size)
645 {
646 ASSERT(topic != nullptr);
647 ASSERT(payload_addr != nullptr);
648 ASSERT(payload_size == topic->data_.payload_size);
649 ASSERT(topic->data_.payload_alignment != 0);
650 ASSERT(reinterpret_cast<uintptr_t>(payload_addr) % topic->data_.payload_alignment ==
651 0);
652 }
653
662 template <typename Data>
663 static void CheckSubscriberType(Topic topic)
664 {
666 ASSERT(topic.block_ != nullptr);
667 RequireTypeContract(topic.block_, TypeID::GetID<Data>(), sizeof(Data), alignof(Data));
668 }
669
684 static void RequireTypeContract(TopicHandle topic, TypeID::ID payload_type_id,
685 size_t payload_size, size_t payload_alignment);
686
697 static void RequireCallbackType(TopicHandle topic, TypeID::ID payload_type_id);
698
708 static void RequireMultiPublisher(TopicHandle topic);
709
716 template <typename Data>
717 static void* AllocateSubscriberBuffer()
718 {
720 return new Data;
721 }
722
731 template <typename Data>
732 static void CopyPayload(void* dst, void* payload_addr)
733 {
735 DEV_ASSERT(dst != nullptr);
736 DEV_ASSERT(payload_addr != nullptr);
737 *reinterpret_cast<Data*>(dst) = *reinterpret_cast<Data*>(payload_addr);
738 }
739
750 template <typename Data>
751 void PublishTyped(Data& data, MicrosecondTimestamp timestamp, bool from_callback,
752 bool in_isr)
753 {
755
756 if (from_callback)
757 {
758 LockFromCallback(block_);
759 }
760 else
761 {
762 Lock(block_);
763 }
764
765 CheckPublishContract(block_, TypeID::GetID<Data>(), sizeof(Data), alignof(Data));
766 DispatchSubscribers(block_, timestamp, &data, from_callback, in_isr);
767
768 if (from_callback)
769 {
770 UnlockFromCallback(block_);
771 }
772 else
773 {
774 Unlock(block_);
775 }
776 }
777
789 static void CheckPublishContract(TopicHandle topic, TypeID::ID payload_type_id,
790 size_t payload_size, size_t payload_alignment);
791
800 static void PackBytes(uint32_t topic_name_crc32, RawData buffer,
801 MicrosecondTimestamp timestamp, ConstRawData data);
802
813 static void DispatchSubscriber(SuberBlock& block, MicrosecondTimestamp timestamp,
814 void* payload_addr, bool from_callback, bool in_isr);
815
827 static void DispatchSubscribers(TopicHandle topic, MicrosecondTimestamp timestamp,
828 void* payload_addr, bool from_callback, bool in_isr);
829
840 void PublishServerBytes(void* payload_addr, size_t payload_size,
841 MicrosecondTimestamp timestamp, bool from_callback, bool in_isr)
842 {
843 CheckServerPublishContract(block_, payload_addr, payload_size);
844
845 if (from_callback)
846 {
847 LockFromCallback(block_);
848 }
849 else
850 {
851 Lock(block_);
852 }
853
854 DispatchSubscribers(block_, timestamp, payload_addr, from_callback, in_isr);
855
856 if (from_callback)
857 {
858 UnlockFromCallback(block_);
859 }
860 else
861 {
862 Unlock(block_);
863 }
864 }
865};
866} // namespace LibXR
867
868#include "packet/packet.hpp"
869#include "server/server.hpp"
870#include "subscriber/async.hpp"
871#include "subscriber/callback.hpp"
872#include "subscriber/queue.hpp"
873#include "subscriber/sync.hpp"
只读原始数据视图 / Immutable raw data view
size_t size_
数据字节数 / Data size in bytes
const void * addr_
数据起始地址 / Data start address
链表实现,用于存储和管理数据节点。 A linked list implementation for storing and managing data nodes.
微秒时间戳 / Microsecond timestamp
互斥锁类,提供线程同步机制 Mutex class providing thread synchronization mechanisms.
Definition mutex.hpp:18
红黑树的泛型数据节点,继承自 BaseNode Generic data node for Red-Black Tree, inheriting from BaseNode.
Definition rbt.hpp:63
Data data_
存储的数据 / Stored data.
Definition rbt.hpp:98
红黑树实现,支持泛型键和值,并提供线程安全操作 Red-Black Tree implementation supporting generic keys and values with thread-...
Definition rbt.hpp:23
可写原始数据视图 / Mutable raw data view
size_t size_
数据字节数 / Data size in bytes
void * addr_
数据起始地址 / Data start address
先 StartWaiting(),再自己来取数据的订阅者 / Subscriber that first calls StartWaiting() and later pulls the data it...
Definition topic.hpp:277
每次发布时直接执行函数的订阅句柄 / Subscription handle that runs a function on each publish
Definition callback.hpp:16
topic 所属的命名域 / Naming domain that groups topics
Definition topic.hpp:207
Domain(const char *name)
构造一个 topic 域 / Construct one topic domain
Definition topic.cpp:92
RBTree< uint32_t >::Node< RBTree< uint32_t > > * node_
Definition topic.hpp:216
每次发布都往队列里塞一份数据的订阅者 / Subscriber that pushes one entry into a queue on each publish
Definition queue.hpp:25
将字节流解析成 packet 并发布到已注册 topic 的状态机 / State machine that parses byte streams into packets and publishes...
Definition server.hpp:28
调用 Wait() 收消息的订阅者 / Subscriber that receives messages by calling Wait()
Definition topic.hpp:255
发布订阅主题 / Publish-subscribe topic
Definition topic.hpp:56
SuberType
topic 支持的订阅者种类 / Subscriber kinds supported by a topic
Definition topic.hpp:225
@ SYNC
同步等待型订阅者 / Synchronous wait-based subscriber.
@ ASYNC
异步本地缓冲型订阅者 / Asynchronous local-buffer subscriber.
@ QUEUE
队列转发型订阅者 / Queue-forwarding subscriber.
@ CALLBACK
回调执行型订阅者 / Callback-executing subscriber.
void Publish(Data &data)
在普通上下文里发布一条消息,并自动取当前时间戳 / Publish one message in normal context and stamp it with the current time
Definition topic.hpp:447
void PublishFromCallback(Data &data, MicrosecondTimestamp timestamp, bool in_isr)
在回调或 ISR 路径里按指定时间戳发布一条消息 / Publish one message from callback or ISR context with an explicit timestam...
Definition topic.hpp:487
static void Unlock(TopicHandle topic)
在普通上下文里释放一个 topic 发布路径 / Unlock one topic publish path in normal context
Definition topic.cpp:50
static TopicHandle FindOrCreate(const char *name, Domain *domain=nullptr, bool multi_publisher=false)
按精确类型查找或创建一个 topic / Find or create one topic with an exact payload type
Definition topic.hpp:399
RBTree< uint32_t >::Node< Block > * TopicHandle
指向一个 topic 运行时状态块的句柄 / Handle pointing to one topic runtime state block
Definition topic.hpp:125
size_t PayloadSize() const
获取该 topic 固定 payload 字节数 / Get the fixed payload size of this topic
Definition topic.hpp:423
ASyncSubscriberState
异步订阅者本地缓冲区的状态 / State of the async subscriber's local buffer
Definition async.hpp:12
ErrorCode PackRaw(ConstRawData data, RawData packet)
将一段 raw payload 按当前 topic 元数据打包成 packet / Pack a raw payload byte view into one packet using the curr...
Definition topic.hpp:559
static MicrosecondTimestamp NowTimestamp()
读取当前时间戳 / Read the current timestamp
Definition publish.cpp:80
static Topic CreateTopic(const char *name, Domain *domain=nullptr, bool multi_publisher=false)
用精确类型创建或查找一个 topic / Create or look up one topic using one exact payload type
Definition topic.hpp:362
static void Lock(TopicHandle topic)
在普通上下文里锁住一个 topic 发布路径 / Lock one topic publish path in normal context
Definition topic.cpp:32
static void UnlockFromCallback(TopicHandle topic)
在回调或 ISR 路径里释放一个 topic 发布路径 / Unlock one topic publish path from callback or ISR context
Definition topic.cpp:80
static void LockFromCallback(TopicHandle topic)
在回调或 ISR 路径里锁住一个 topic 发布路径 / Lock one topic publish path from callback or ISR context
Definition topic.cpp:62
void PublishBytesFromServerCallback(void *payload_addr, size_t payload_size, MicrosecondTimestamp timestamp, bool in_isr)
供回调/ISR 上下文里的 packet/server 路径按字节发布一条消息 / Publish one packet/server message from callback/ISR context...
Definition topic.hpp:518
static TopicHandle WaitTopic(const char *name, uint32_t timeout=UINT32_MAX, Domain *domain=nullptr)
等待指定名称的 topic 出现 / Wait until a topic with the given name exists
Definition topic.cpp:206
size_t PayloadAlignment() const
获取该 topic payload 的对齐要求 / Get the payload alignment requirement of this topic
Definition topic.hpp:434
void PublishFromCallback(Data &data, bool in_isr)
在回调或 ISR 路径里发布一条消息,并自动取当前时间戳 / Publish one message from callback or ISR context and stamp it with the...
Definition topic.hpp:473
uint32_t GetKey() const
读取 topic 键值 / Read the key value of this topic
Definition topic.cpp:227
void RegisterCallback(Callback &cb)
注册一个回调订阅者 / Register one callback subscriber
Definition callback.hpp:620
void Publish(Data &data, MicrosecondTimestamp timestamp)
在普通上下文里按指定时间戳发布一条消息 / Publish one message in normal context with an explicit timestamp
Definition topic.hpp:460
static TopicHandle Find(const char *name, Domain *domain=nullptr)
按名称查找一个已存在 topic / Find one existing topic by name
Definition topic.cpp:188
Topic()
构造一个空 topic 视图 / Construct one empty topic view
Definition topic.cpp:114
void PublishBytesFromServer(void *payload_addr, size_t payload_size, MicrosecondTimestamp timestamp)
供 packet/server 路径按字节发布一条消息 / Publish one message from the packet/server path using bytes already arr...
Definition topic.hpp:501
ErrorCode PackRaw(ConstRawData data, RawData packet, MicrosecondTimestamp timestamp)
将一段 raw payload 按当前 topic 元数据和指定时间戳打包成 packet / Pack a raw payload byte view into one packet using th...
Definition topic.hpp:573
ErrorCode PackData(const Data &data, PackedData< Data > &packet)
将一个精确类型消息打包成 packet / Pack one exact-typed message into one packet using the topic's runtime contract...
Definition topic.hpp:533
static ID GetID()
获取类型的唯一标识符 / Get a unique identifier for type T
topic 可承载 payload 的类型约束 / Type constraint for payloads carried by one topic
Definition topic.hpp:27
LibXR 命名空间 / LibXR namespace.
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举 / Defines an enumeration for error codes
@ SIZE_ERR
尺寸错误 / Size error
@ PTR_NULL
空指针 / Null pointer
@ NO_BUFF
缓冲区不足 / Insufficient buffer
@ OK
操作成功 / Operation successful
constexpr void CheckTopicPayload()
在模板上下文里断言 payload 类型满足 topic 契约 / Assert in template context that one payload type satisfies the topi...
Definition topic.hpp:38
异步订阅者自己挂的数据块 / Data block owned by one asynchronous subscriber
Definition async.hpp:26
topic 运行时状态块 / Runtime state block of one topic
Definition topic.hpp:82
std::atomic< LockState > busy
发布路径串行化状态 / Publish-path serialization state.
Definition topic.hpp:84
Mutex * mutex
多发布者主题使用的互斥量 / Mutex used by multi-publisher topics.
Definition topic.hpp:93
TypeID::ID payload_type_id
精确 payload 类型标识 / Exact payload type identifier.
Definition topic.hpp:87
uint32_t payload_alignment
Definition topic.hpp:90
uint32_t payload_size
Definition topic.hpp:88
uint32_t crc32
主题名 CRC32 键 / CRC32 key of the topic name.
Definition topic.hpp:92
LockFreeList subers
已挂接订阅者链表 / List of attached subscribers.
Definition topic.hpp:85
挂在 topic 订阅链表里的回调记录 / Callback record stored in the topic subscriber list
Definition callback.hpp:577
带时间戳和 payload 副本的消息对象 / Message object carrying a timestamp and a payload copy
Definition topic.hpp:168
Data data
payload 对象副本 / Copied payload object.
Definition topic.hpp:171
MicrosecondTimestamp timestamp
消息时间戳 / Message timestamp.
Definition topic.hpp:170
带时间戳和 payload 指针的只读消息视图 / Read-only message view carrying a timestamp and a payload pointer
Definition topic.hpp:135
MicrosecondTimestamp timestamp
消息时间戳 / Message timestamp.
Definition topic.hpp:137
队列订阅者自己挂的数据块 / Data block owned by one queued subscriber
Definition queue.hpp:12
带时间戳和只读原始 payload 视图的消息 / Message carrying a timestamp and a read-only raw payload view
Definition topic.hpp:154
MicrosecondTimestamp timestamp
消息时间戳 / Message timestamp.
Definition topic.hpp:155
所有订阅块共用的公共头 / Common header shared by all subscriber blocks
Definition topic.hpp:237
SuberType type
订阅块的具体种类 / Concrete kind of this subscriber block.
Definition topic.hpp:238
同步订阅者自己挂的数据块 / Data block owned by one synchronous subscriber
Definition sync.hpp:12
队列模块聚合入口 / Aggregate entry of the queue module.