技术标签: uavcan
讲一下libuavcan如何通过发布订阅模式实现数据收发。
发布者对应uavcan::Publisher类,这是一个模板类,模板参数指定要发布的数据类型,发布者类构造时需要一个参数,就是我们前面介绍的节点Node,同时其模板参数指定要发布的数据类型。在这个例子里消息类型是uavcan.protocol.debug.KeyValue。
订阅者同样是个模板类,模板参数指定订阅的数据类型。
uavcan::Publisher<msg类型> kv_pub(node);//创建发布者对象
kv_pub.broadcast(msg);//发布者广播消息
uavcan::Subscriber<msg类型> kv_sub(node);//创建订阅者对象
kv_sub.start(注册的回调函数);
回调函数接收的参数类型可以是
uavcan::ReceivedDataStructure实际上对接收的消息T包装了一层,它会从传输层接收数据之外的其他信息,如Source Node ID, timestamps, Transfer ID, index of the redundant interface this transfer was picked up from等。当我们需要获取消息的源节点ID时就可以用uavcan::ReceivedDataStructure。
template <typename DataType_>
class UAVCAN_EXPORT ReceivedDataStructure : public DataType_, Noncopyable
{
const IncomingTransfer* const _transfer_; ///< Such weird name is necessary to avoid clashing with DataType fields
template <typename Ret, Ret(IncomingTransfer::*Fun) () const>
Ret safeget() const
{
if (_transfer_ == UAVCAN_NULLPTR)
{
return Ret();
}
return (_transfer_->*Fun)();
}
protected:
ReceivedDataStructure()
: _transfer_(UAVCAN_NULLPTR)
{
}
ReceivedDataStructure(const IncomingTransfer* arg_transfer)
: _transfer_(arg_transfer)
{
UAVCAN_ASSERT(arg_transfer != UAVCAN_NULLPTR);
}
public:
typedef DataType_ DataType;
MonotonicTime getMonotonicTimestamp() const
{
return safeget<MonotonicTime, &IncomingTransfer::getMonotonicTimestamp>();
}
UtcTime getUtcTimestamp() const {
return safeget<UtcTime, &IncomingTransfer::getUtcTimestamp>(); }
TransferPriority getPriority() const {
return safeget<TransferPriority, &IncomingTransfer::getPriority>(); }
TransferType getTransferType() const {
return safeget<TransferType, &IncomingTransfer::getTransferType>(); }
TransferID getTransferID() const {
return safeget<TransferID, &IncomingTransfer::getTransferID>(); }
NodeID getSrcNodeID() const {
return safeget<NodeID, &IncomingTransfer::getSrcNodeID>(); }
uint8_t getIfaceIndex() const {
return safeget<uint8_t, &IncomingTransfer::getIfaceIndex>(); }
bool isAnonymousTransfer() const {
return safeget<bool, &IncomingTransfer::isAnonymousTransfer>(); }
};
可以看到,我们不仅可以获取源nodeid,还可以获取时间戳,来消息来自哪个硬件接口,消息传输id,传输类型等。
需要注意的是,Publisher和Subscriber都禁止拷贝构造。auto pub_copy = kv_pub是非法操作。
发布者进程:
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <uavcan/uavcan.hpp>
/*
* We're going to use messages of type uavcan.protocol.debug.KeyValue, so the appropriate header must be included.
* Given a data type named X, the header file name would be:
* X.replace('.', '/') + ".hpp"
*/
#include <uavcan/protocol/debug/KeyValue.hpp> // uavcan.protocol.debug.KeyValue
extern uavcan::ICanDriver& getCanDriver();
extern uavcan::ISystemClock& getSystemClock();
constexpr unsigned NodeMemoryPoolSize = 16384;
typedef uavcan::Node<NodeMemoryPoolSize> Node;
static Node& getNode()
{
static Node node(getCanDriver(), getSystemClock());
return node;
}
int main(int argc, const char** argv)
{
if (argc < 2)
{
std::cerr << "Usage: " << argv[0] << " <node-id>" << std::endl;
return 1;
}
const int self_node_id = std::stoi(argv[1]);
auto& node = getNode();
node.setNodeID(self_node_id);
node.setName("org.uavcan.tutorial.publisher");
/*
* Dependent objects (e.g. publishers, subscribers, servers, callers, timers, ...) can be initialized only
* if the node is running. Note that all dependent objects always keep a reference to the node object.
*/
const int node_start_res = node.start();
if (node_start_res < 0)
{
throw std::runtime_error("Failed to start the node; error: " + std::to_string(node_start_res));
}
/*
* Create the publisher object to broadcast standard key-value messages of type uavcan.protocol.debug.KeyValue.
* Keep in mind that most classes defined in the library are not copyable; attempt to copy objects of
* such classes will result in compilation failure.
* A publishing node won't see its own messages.
*/
uavcan::Publisher<uavcan::protocol::debug::KeyValue> kv_pub(node);
const int kv_pub_init_res = kv_pub.init();
if (kv_pub_init_res < 0)
{
throw std::runtime_error("Failed to start the publisher; error: " + std::to_string(kv_pub_init_res));
}
/*
* This would fail because most of the objects - including publishers - are noncopyable.
* The error message may look like this:
* "error: ‘uavcan::Noncopyable::Noncopyable(const uavcan::Noncopyable&)’ is private"
*/
// auto pub_copy = kv_pub; // Don't try this at home.
/*
* TX timeout can be overridden if needed.
* Default value should be OK for most use cases.
*/
kv_pub.setTxTimeout(uavcan::MonotonicDuration::fromMSec(1000));
/*
* Priority of outgoing tranfers can be changed as follows.
* Default priority is 16 (medium).
*/
kv_pub.setPriority(uavcan::TransferPriority::MiddleLower);
/*
* Running the node.
*/
node.setModeOperational();
while (true)
{
/*
* Spinning for 1 second.
* The method spin() may return earlier if an error occurs (e.g. driver failure).
* All error codes are listed in the header uavcan/error.hpp.
*/
const int spin_res = node.spin(uavcan::MonotonicDuration::fromMSec(1000));
if (spin_res < 0)
{
std::cerr << "Transient failure: " << spin_res << std::endl;
}
/*
* Publishing a random value using the publisher created above.
* All message types have zero-initializing default constructors.
* Relevant usage info for every data type is provided in its DSDL definition.
*/
uavcan::protocol::debug::KeyValue kv_msg; // Always zero initialized
kv_msg.value = std::rand() / float(RAND_MAX);
/*
* Arrays in DSDL types are quite extensive in the sense that they can be static,
* or dynamic (no heap needed - all memory is pre-allocated), or they can emulate std::string.
* The last one is called string-like arrays.
* ASCII strings can be directly assigned or appended to string-like arrays.
* For more info, please read the documentation for the class uavcan::Array<>.
*/
kv_msg.key = "a"; // "a"
kv_msg.key += "b"; // "ab"
kv_msg.key += "c"; // "abc"
/*
* Publishing the message.
*/
const int pub_res = kv_pub.broadcast(kv_msg);
if (pub_res < 0)
{
std::cerr << "KV publication failure: " << pub_res << std::endl;
}
}
}
订阅者进程:
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <uavcan/uavcan.hpp>
#include <uavcan/protocol/debug/KeyValue.hpp>
#include <uavcan/protocol/debug/LogMessage.hpp>
extern uavcan::ICanDriver& getCanDriver();
extern uavcan::ISystemClock& getSystemClock();
constexpr unsigned NodeMemoryPoolSize = 16384;
typedef uavcan::Node<NodeMemoryPoolSize> Node;
static Node& getNode()
{
static Node node(getCanDriver(), getSystemClock());
return node;
}
int main(int argc, const char** argv)
{
if (argc < 2)
{
std::cerr << "Usage: " << argv[0] << " <node-id>" << std::endl;
return 1;
}
const int self_node_id = std::stoi(argv[1]);
auto& node = getNode();
node.setNodeID(self_node_id);
node.setName("org.uavcan.tutorial.subscriber");
/*
* Dependent objects (e.g. publishers, subscribers, servers, callers, timers, ...) can be initialized only
* if the node is running. Note that all dependent objects always keep a reference to the node object.
*/
const int node_start_res = node.start();
if (node_start_res < 0)
{
throw std::runtime_error("Failed to start the node; error: " + std::to_string(node_start_res));
}
/*
* Subscribing to standard log messages of type uavcan.protocol.debug.LogMessage.
*
* Received messages will be passed to the application via a callback, the type of which can be set via the second
* template argument.
* In C++11 mode, callback type defaults to std::function<>.
* In C++03 mode, callback type defaults to a plain function pointer; use a binder object to call member
* functions as callbacks (refer to uavcan::MethodBinder<>).
*
* N.B.: Some libuavcan users report that C++ lambda functions when used with GCC may actually break the code
* on some embedded targets, particularly ARM Cortex M0. These reports still remain unconfirmed though;
* please refer to the UAVCAN mailing list to learn more.
*
* The type of the argument of the callback can be either of these two:
* - T&
* - uavcan::ReceivedDataStructure<T>&
* For the first option, ReceivedDataStructure<T>& will be cast into a T& implicitly.
*
* The class uavcan::ReceivedDataStructure extends the received data structure with extra information obtained from
* the transport layer, such as Source Node ID, timestamps, Transfer ID, index of the redundant interface this
* transfer was picked up from, etc.
*/
uavcan::Subscriber<uavcan::protocol::debug::LogMessage> log_sub(node);
const int log_sub_start_res = log_sub.start(
[&](const uavcan::ReceivedDataStructure<uavcan::protocol::debug::LogMessage>& msg)
{
/*
* The message will be streamed in YAML format.
*/
std::cout << msg << std::endl;
/*
* If the standard iostreams are not available (they rarely available in embedded environments),
* use the helper class uavcan::OStream defined in the header file <uavcan/helpers/ostream.hpp>.
*/
// uavcan::OStream::instance() << msg << uavcan::OStream::endl;
});
/*
* C++03 WARNING
* The code above will not compile in C++03, because it uses a lambda function.
* In order to compile the code in C++03, move the code from the lambda to a standalone static function.
* Use uavcan::MethodBinder<> to invoke member functions.
*/
if (log_sub_start_res < 0)
{
throw std::runtime_error("Failed to start the log subscriber; error: " + std::to_string(log_sub_start_res));
}
/*
* Subscribing to messages of type uavcan.protocol.debug.KeyValue.
* This time we don't want to receive extra information about the received message, so the callback's argument type
* would be just T& instead of uavcan::ReceivedDataStructure<T>&.
* The callback will print the message in YAML format via std::cout (also refer to uavcan::OStream).
*/
uavcan::Subscriber<uavcan::protocol::debug::KeyValue> kv_sub(node);
const int kv_sub_start_res =
kv_sub.start([&](const uavcan::protocol::debug::KeyValue& msg) {
std::cout << msg << std::endl; });
if (kv_sub_start_res < 0)
{
throw std::runtime_error("Failed to start the key/value subscriber; error: " + std::to_string(kv_sub_start_res));
}
/*
* Running the node.
*/
node.setModeOperational();
while (true)
{
/*
* The method spin() may return earlier if an error occurs (e.g. driver failure).
* All error codes are listed in the header uavcan/error.hpp.
*/
const int res = node.spin(uavcan::MonotonicDuration::getInfinite());
if (res < 0)
{
std::cerr << "Transient failure: " << res << std::endl;
}
}
}
订阅者订阅特定类型的消息,收到消息之后回调函数被自动调用。
上面的例子使用C++11的lamba表达式方式来注册回调函数,如果不支持C++11,如C++03,则可以用uavcan::MethodBinder<>,这个模板类,它接受两个模板参数,将对象和对象的成员函数实现绑定,生成一个可用于注册的回调函数,其作用类似于C++11的std::bind。
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <uavcan/uavcan.hpp>
#include <uavcan/protocol/debug/KeyValue.hpp>
#include <uavcan/protocol/debug/LogMessage.hpp>
extern uavcan::ICanDriver& getCanDriver();
extern uavcan::ISystemClock& getSystemClock();
/**
* This class demonstrates how to use uavcan::MethodBinder with subscriber objects in C++03.
* In C++11 and newer standards it is recommended to use lambdas and std::function<> instead, as this approach
* would be much easier to implement and to understand.
*/
class Node
{
static const unsigned NodeMemoryPoolSize = 16384;
uavcan::Node<NodeMemoryPoolSize> node_;
/*
* Instantiations of uavcan::MethodBinder<>
*/
typedef uavcan::MethodBinder<Node*, void (Node::*)(const uavcan::protocol::debug::LogMessage&) const>
LogMessageCallbackBinder;
typedef uavcan::MethodBinder<Node*,
void (Node::*)(const uavcan::ReceivedDataStructure<uavcan::protocol::debug::KeyValue>&) const>
KeyValueCallbackBinder;
uavcan::Subscriber<uavcan::protocol::debug::LogMessage, LogMessageCallbackBinder> log_sub_;
uavcan::Subscriber<uavcan::protocol::debug::KeyValue, KeyValueCallbackBinder> kv_sub_;
void logMessageCallback(const uavcan::protocol::debug::LogMessage& msg) const
{
std::cout << "Log message:\n" << msg << std::endl;
}
void keyValueCallback(const uavcan::ReceivedDataStructure<uavcan::protocol::debug::KeyValue>& msg) const
{
std::cout << "KV message:\n" << msg << std::endl;
}
public:
Node(uavcan::NodeID self_node_id, const std::string& self_node_name) :
node_(getCanDriver(), getSystemClock()),
log_sub_(node_),
kv_sub_(node_)
{
node_.setNodeID(self_node_id);
node_.setName(self_node_name.c_str());
}
void run()
{
const int start_res = node_.start();
if (start_res < 0)
{
throw std::runtime_error("Failed to start the node: " + std::to_string(start_res));
}
const int log_sub_start_res = log_sub_.start(LogMessageCallbackBinder(this, &Node::logMessageCallback));
if (log_sub_start_res < 0)
{
throw std::runtime_error("Failed to start the log subscriber; error: " + std::to_string(log_sub_start_res));
}
const int kv_sub_start_res = kv_sub_.start(KeyValueCallbackBinder(this, &Node::keyValueCallback));
if (kv_sub_start_res < 0)
{
throw std::runtime_error("Failed to start the KV subscriber; error: " + std::to_string(kv_sub_start_res));
}
node_.setModeOperational();
while (true)
{
const int res = node_.spin(uavcan::MonotonicDuration::getInfinite());
if (res < 0)
{
std::cerr << "Transient failure: " << res << std::endl;
}
}
}
};
int main(int argc, const char** argv)
{
if (argc < 2)
{
std::cerr << "Usage: " << argv[0] << " <node-id>" << std::endl;
return 1;
}
const int self_node_id = std::stoi(argv[1]);
Node node(self_node_id, "org.uavcan.tutorial.subscriber_cpp03");
node.run();
}
cmake文件:
cmake_minimum_required(VERSION 2.8)
project(tutorial_project)
find_library(UAVCAN_LIB uavcan REQUIRED)
set(CMAKE_CXX_FLAGS "-Wall -Wextra -pedantic -std=c++11")
add_executable(publisher publisher.cpp ${CMAKE_SOURCE_DIR}/../2._Node_initialization_and_startup/platform_linux.cpp)
target_link_libraries(publisher ${UAVCAN_LIB} rt)
add_executable(subscriber subscriber.cpp ${CMAKE_SOURCE_DIR}/../2._Node_initialization_and_startup/platform_linux.cpp)
target_link_libraries(subscriber ${UAVCAN_LIB} rt)
add_executable(subscriber_cpp03 subscriber_cpp03.cpp ${CMAKE_SOURCE_DIR}/../2._Node_initialization_and_startup/platform_linux.cpp)
target_link_libraries(subscriber_cpp03 ${UAVCAN_LIB} rt)
二分法的具体用法:1.二分查找 2.快速幂
小程序网络请求wx.request({})是需要在header中添加参数content-typeheader: { 'content-type': 'application/json' // 默认值},有时参数为application/json的请求后台接收参数为空,改成application/x-www-form-urlencoded就好了,所以查了一下相关资料常见的表单数据...
作为一名程序员,要不断的学习新的技术。不断汲取新的知识。CoordinatorLayout 坐标局AppBarLayout 用于替换早起的actionbar CollapsingToolbarLayout 可折叠的toolbar Toolbar 标题栏通过以上组件 写一个简单的可折叠的标题栏 先看三张图片通过这个三张图可以看出来,标题栏的大小会随着recyclerview 的滑动伸
2018/3/15 1.Call to undefined function curl_init : 【解决办法】:意思是没有定义curl_init这个方法。找到php.ini,修改extension=php_curl.dll 把前面的分号去掉 2.获取,access_token,返NULL问题,而且写了curl_errno($ch)还不报错。 出现问题的代码段: public f...
排序算法快速排序归并排序堆排序基数排序几个排序算法的比较查找算法二分查找缓存算法LRU数据结构单链表二叉树二叉搜索树栈哈希表红黑树动态规划贪心算法回溯NP最短路算法
FatFs文件系统简单读写
你是否在dreamweaver里编辑网页的时候看到&#x3A3;这样的东西,你曾使用过&nbsp;这样的玩意吧,或者你在调试webservice的时候看到返回xml字符串中现&#x3B3;这样的怪物呢?你看不懂他们可能用浏览器或者DW预览一下都是可以看到庐山真面目的,它是谁? &#x3A3;这个是 Numeric Character Refere...
1.文件预览原本的elmType属性新增一个filepreview,并在attribute属性中添加值为@thumbnail.Size.Demo:{ "$schema": "https://developer.microsoft.com/json-schemas/sp/column-formatting.schema.json", "elmType": "filepreview", "attributes": { "src": "@thumbnail.medium" },
本文将详细解析STM32微控制器的时钟树和STM32CubeMX Clock Configuration的配置关系。
引用参考--UML百度百科[url]https://baike.baidu.com/item/%E7%BB%9F%E4%B8%80%E5%BB%BA%E6%A8%A1%E8%AF%AD%E8%A8%80/3160571?fr=aladdin&fromid=446747&fromtitle=UML[/url]--UML之时序图详解[url]https://blog.csdn.ne...
[译] NSCollectionView 入门教程原文地址:https://segmentfault.com/a/1190000011336477#articleHeader21本文翻译自 raywenderlich.com 的 NSCollectionView Tutorial,已咨询对方网站,可至多翻译 10 篇文章。希望各位有英语阅读能力的话,还是 先打赏 然后去阅读英文原吧,毕...
com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: connect at com.sun.jersey.client.apache4.ApacheHttpClient4Handler.handle(ApacheHttpClient4Handler.jav...