helpers.hpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. /*
  2. * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
  3. */
  4. #pragma once
  5. #include <memory>
  6. #include <string>
  7. #include <vector>
  8. #include <chrono>
  9. #include <iostream>
  10. #include <sstream>
  11. #include <uavcan/uavcan.hpp>
  12. #include <uavcan/node/sub_node.hpp>
  13. namespace uavcan_linux
  14. {
  15. /**
  16. * Default log sink will dump everything into stderr.
  17. * It is installed by default.
  18. */
  19. class DefaultLogSink : public uavcan::ILogSink
  20. {
  21. void log(const uavcan::protocol::debug::LogMessage& message) override
  22. {
  23. const auto tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
  24. const auto tstr = std::ctime(&tt);
  25. std::cerr << "### UAVCAN " << tstr << message << std::endl;
  26. }
  27. };
  28. /**
  29. * Wrapper over uavcan::ServiceClient<> for blocking calls.
  30. * Blocks on uavcan::Node::spin() internally until the call is complete.
  31. */
  32. template <typename DataType>
  33. class BlockingServiceClient : public uavcan::ServiceClient<DataType>
  34. {
  35. typedef uavcan::ServiceClient<DataType> Super;
  36. typename DataType::Response response_;
  37. bool call_was_successful_;
  38. void callback(const uavcan::ServiceCallResult<DataType>& res)
  39. {
  40. response_ = res.getResponse();
  41. call_was_successful_ = res.isSuccessful();
  42. }
  43. void setup()
  44. {
  45. Super::setCallback(std::bind(&BlockingServiceClient::callback, this, std::placeholders::_1));
  46. call_was_successful_ = false;
  47. response_ = typename DataType::Response();
  48. }
  49. public:
  50. BlockingServiceClient(uavcan::INode& node)
  51. : uavcan::ServiceClient<DataType>(node)
  52. , call_was_successful_(false)
  53. {
  54. setup();
  55. }
  56. /**
  57. * Performs a blocking service call using default timeout (see the specs).
  58. * Use @ref getResponse() to get the actual response.
  59. * Returns negative error code.
  60. */
  61. int blockingCall(uavcan::NodeID server_node_id, const typename DataType::Request& request)
  62. {
  63. return blockingCall(server_node_id, request, Super::getDefaultRequestTimeout());
  64. }
  65. /**
  66. * Performs a blocking service call using the specified timeout. Please consider using default timeout instead.
  67. * Use @ref getResponse() to get the actual response.
  68. * Returns negative error code.
  69. */
  70. int blockingCall(uavcan::NodeID server_node_id, const typename DataType::Request& request,
  71. uavcan::MonotonicDuration timeout)
  72. {
  73. const auto SpinDuration = uavcan::MonotonicDuration::fromMSec(2);
  74. setup();
  75. Super::setRequestTimeout(timeout);
  76. const int call_res = Super::call(server_node_id, request);
  77. if (call_res >= 0)
  78. {
  79. while (Super::hasPendingCalls())
  80. {
  81. const int spin_res = Super::getNode().spin(SpinDuration);
  82. if (spin_res < 0)
  83. {
  84. return spin_res;
  85. }
  86. }
  87. }
  88. return call_res;
  89. }
  90. /**
  91. * Whether the last blocking call was successful.
  92. */
  93. bool wasSuccessful() const { return call_was_successful_; }
  94. /**
  95. * Use this to retrieve the response of the last blocking service call.
  96. * This method returns default constructed response object if the last service call was unsuccessful.
  97. */
  98. const typename DataType::Response& getResponse() const { return response_; }
  99. };
  100. /**
  101. * Contains all drivers needed for uavcan::Node.
  102. */
  103. struct DriverPack
  104. {
  105. SystemClock clock;
  106. std::shared_ptr<uavcan::ICanDriver> can;
  107. explicit DriverPack(ClockAdjustmentMode clock_adjustment_mode,
  108. const std::shared_ptr<uavcan::ICanDriver>& can_driver)
  109. : clock(clock_adjustment_mode)
  110. , can(can_driver)
  111. { }
  112. explicit DriverPack(ClockAdjustmentMode clock_adjustment_mode,
  113. const std::vector<std::string>& iface_names)
  114. : clock(clock_adjustment_mode)
  115. {
  116. std::shared_ptr<SocketCanDriver> socketcan(new SocketCanDriver(clock));
  117. can = socketcan;
  118. for (auto ifn : iface_names)
  119. {
  120. if (socketcan->addIface(ifn) < 0)
  121. {
  122. throw Exception("Failed to add iface " + ifn);
  123. }
  124. }
  125. }
  126. };
  127. typedef std::shared_ptr<DriverPack> DriverPackPtr;
  128. typedef std::shared_ptr<uavcan::INode> INodePtr;
  129. typedef std::shared_ptr<uavcan::Timer> TimerPtr;
  130. template <typename T>
  131. using SubscriberPtr = std::shared_ptr<uavcan::Subscriber<T>>;
  132. template <typename T>
  133. using PublisherPtr = std::shared_ptr<uavcan::Publisher<T>>;
  134. template <typename T>
  135. using ServiceServerPtr = std::shared_ptr<uavcan::ServiceServer<T>>;
  136. template <typename T>
  137. using ServiceClientPtr = std::shared_ptr<uavcan::ServiceClient<T>>;
  138. template <typename T>
  139. using BlockingServiceClientPtr = std::shared_ptr<BlockingServiceClient<T>>;
  140. static constexpr std::size_t NodeMemPoolSize = 1024 * 512; ///< This shall be enough for any possible use case
  141. /**
  142. * Generic wrapper for node objects with some additional convenience functions.
  143. */
  144. template <typename NodeType>
  145. class NodeBase : public NodeType
  146. {
  147. protected:
  148. DriverPackPtr driver_pack_;
  149. static void enforce(int error, const std::string& msg)
  150. {
  151. if (error < 0)
  152. {
  153. std::ostringstream os;
  154. os << msg << " [" << error << "]";
  155. throw Exception(os.str());
  156. }
  157. }
  158. template <typename DataType>
  159. static std::string getDataTypeName()
  160. {
  161. return DataType::getDataTypeFullName();
  162. }
  163. public:
  164. /**
  165. * Simple forwarding constructor, compatible with uavcan::Node.
  166. */
  167. NodeBase(uavcan::ICanDriver& can_driver, uavcan::ISystemClock& clock) :
  168. NodeType(can_driver, clock)
  169. { }
  170. /**
  171. * Takes ownership of the driver container via the shared pointer.
  172. */
  173. explicit NodeBase(DriverPackPtr driver_pack)
  174. : NodeType(*driver_pack->can, driver_pack->clock)
  175. , driver_pack_(driver_pack)
  176. { }
  177. /**
  178. * Allocates @ref uavcan::Subscriber in the heap using shared pointer.
  179. * The subscriber will be started immediately.
  180. * @throws uavcan_linux::Exception.
  181. */
  182. template <typename DataType>
  183. SubscriberPtr<DataType> makeSubscriber(const typename uavcan::Subscriber<DataType>::Callback& cb)
  184. {
  185. SubscriberPtr<DataType> p(new uavcan::Subscriber<DataType>(*this));
  186. enforce(p->start(cb), "Subscriber start failure " + getDataTypeName<DataType>());
  187. return p;
  188. }
  189. /**
  190. * Allocates @ref uavcan::Publisher in the heap using shared pointer.
  191. * The publisher will be initialized immediately.
  192. * @throws uavcan_linux::Exception.
  193. */
  194. template <typename DataType>
  195. PublisherPtr<DataType> makePublisher(uavcan::MonotonicDuration tx_timeout =
  196. uavcan::Publisher<DataType>::getDefaultTxTimeout())
  197. {
  198. PublisherPtr<DataType> p(new uavcan::Publisher<DataType>(*this));
  199. enforce(p->init(), "Publisher init failure " + getDataTypeName<DataType>());
  200. p->setTxTimeout(tx_timeout);
  201. return p;
  202. }
  203. /**
  204. * Allocates @ref uavcan::ServiceServer in the heap using shared pointer.
  205. * The server will be started immediately.
  206. * @throws uavcan_linux::Exception.
  207. */
  208. template <typename DataType>
  209. ServiceServerPtr<DataType> makeServiceServer(const typename uavcan::ServiceServer<DataType>::Callback& cb)
  210. {
  211. ServiceServerPtr<DataType> p(new uavcan::ServiceServer<DataType>(*this));
  212. enforce(p->start(cb), "ServiceServer start failure " + getDataTypeName<DataType>());
  213. return p;
  214. }
  215. /**
  216. * Allocates @ref uavcan::ServiceClient in the heap using shared pointer.
  217. * The service client will be initialized immediately.
  218. * @throws uavcan_linux::Exception.
  219. */
  220. template <typename DataType>
  221. ServiceClientPtr<DataType> makeServiceClient(const typename uavcan::ServiceClient<DataType>::Callback& cb)
  222. {
  223. ServiceClientPtr<DataType> p(new uavcan::ServiceClient<DataType>(*this));
  224. enforce(p->init(), "ServiceClient init failure " + getDataTypeName<DataType>());
  225. p->setCallback(cb);
  226. return p;
  227. }
  228. /**
  229. * Allocates @ref uavcan_linux::BlockingServiceClient in the heap using shared pointer.
  230. * The service client will be initialized immediately.
  231. * @throws uavcan_linux::Exception.
  232. */
  233. template <typename DataType>
  234. BlockingServiceClientPtr<DataType> makeBlockingServiceClient()
  235. {
  236. BlockingServiceClientPtr<DataType> p(new BlockingServiceClient<DataType>(*this));
  237. enforce(p->init(), "BlockingServiceClient init failure " + getDataTypeName<DataType>());
  238. return p;
  239. }
  240. /**
  241. * Allocates @ref uavcan::Timer in the heap using shared pointer.
  242. * The timer will be started immediately in one-shot mode.
  243. */
  244. TimerPtr makeTimer(uavcan::MonotonicTime deadline, const typename uavcan::Timer::Callback& cb)
  245. {
  246. TimerPtr p(new uavcan::Timer(*this));
  247. p->setCallback(cb);
  248. p->startOneShotWithDeadline(deadline);
  249. return p;
  250. }
  251. /**
  252. * Allocates @ref uavcan::Timer in the heap using shared pointer.
  253. * The timer will be started immediately in periodic mode.
  254. */
  255. TimerPtr makeTimer(uavcan::MonotonicDuration period, const typename uavcan::Timer::Callback& cb)
  256. {
  257. TimerPtr p(new uavcan::Timer(*this));
  258. p->setCallback(cb);
  259. p->startPeriodic(period);
  260. return p;
  261. }
  262. const DriverPackPtr& getDriverPack() const { return driver_pack_; }
  263. DriverPackPtr& getDriverPack() { return driver_pack_; }
  264. };
  265. /**
  266. * Wrapper for uavcan::Node with some additional convenience functions.
  267. * Note that this wrapper adds stderr log sink to @ref uavcan::Logger, which can be removed if needed.
  268. * Do not instantiate this class directly; instead use the factory functions defined below.
  269. */
  270. class Node : public NodeBase<uavcan::Node<NodeMemPoolSize>>
  271. {
  272. typedef NodeBase<uavcan::Node<NodeMemPoolSize>> Base;
  273. DefaultLogSink log_sink_;
  274. public:
  275. /**
  276. * Simple forwarding constructor, compatible with uavcan::Node.
  277. */
  278. Node(uavcan::ICanDriver& can_driver, uavcan::ISystemClock& clock) :
  279. Base(can_driver, clock)
  280. {
  281. getLogger().setExternalSink(&log_sink_);
  282. }
  283. /**
  284. * Takes ownership of the driver container via the shared pointer.
  285. */
  286. explicit Node(DriverPackPtr driver_pack) :
  287. Base(driver_pack)
  288. {
  289. getLogger().setExternalSink(&log_sink_);
  290. }
  291. };
  292. /**
  293. * Wrapper for uavcan::SubNode with some additional convenience functions.
  294. * Do not instantiate this class directly; instead use the factory functions defined below.
  295. */
  296. class SubNode : public NodeBase<uavcan::SubNode<NodeMemPoolSize>>
  297. {
  298. typedef NodeBase<uavcan::SubNode<NodeMemPoolSize>> Base;
  299. public:
  300. /**
  301. * Simple forwarding constructor, compatible with uavcan::Node.
  302. */
  303. SubNode(uavcan::ICanDriver& can_driver, uavcan::ISystemClock& clock) : Base(can_driver, clock) { }
  304. /**
  305. * Takes ownership of the driver container via the shared pointer.
  306. */
  307. explicit SubNode(DriverPackPtr driver_pack) : Base(driver_pack) { }
  308. };
  309. typedef std::shared_ptr<Node> NodePtr;
  310. typedef std::shared_ptr<SubNode> SubNodePtr;
  311. /**
  312. * Use this function to create a node instance with default SocketCAN driver.
  313. * It accepts the list of interface names to use for the new node, e.g. "can1", "vcan2", "slcan0".
  314. * Clock adjustment mode will be detected automatically unless provided explicitly.
  315. * @throws uavcan_linux::Exception.
  316. */
  317. static inline NodePtr makeNode(const std::vector<std::string>& iface_names,
  318. ClockAdjustmentMode clock_adjustment_mode =
  319. SystemClock::detectPreferredClockAdjustmentMode())
  320. {
  321. DriverPackPtr dp(new DriverPack(clock_adjustment_mode, iface_names));
  322. return NodePtr(new Node(dp));
  323. }
  324. /**
  325. * Use this function to create a node instance with a custom driver.
  326. * Clock adjustment mode will be detected automatically unless provided explicitly.
  327. * @throws uavcan_linux::Exception.
  328. */
  329. static inline NodePtr makeNode(const std::shared_ptr<uavcan::ICanDriver>& can_driver,
  330. ClockAdjustmentMode clock_adjustment_mode =
  331. SystemClock::detectPreferredClockAdjustmentMode())
  332. {
  333. DriverPackPtr dp(new DriverPack(clock_adjustment_mode, can_driver));
  334. return NodePtr(new Node(dp));
  335. }
  336. /**
  337. * This function extends the other two overloads in such a way that it instantiates and initializes
  338. * the node immediately; if initialization fails, it throws.
  339. *
  340. * If NodeID is not provided, it will not be initialized, and therefore the node will be started in
  341. * listen-only (i.e. silent) mode. The node can be switched to normal (i.e. non-silent) mode at any
  342. * later time by calling setNodeID() explicitly. Read the Node class docs for more info.
  343. *
  344. * Clock adjustment mode will be detected automatically unless provided explicitly.
  345. *
  346. * @throws uavcan_linux::Exception, uavcan_linux::LibuavcanErrorException.
  347. */
  348. template <typename DriverType>
  349. static inline NodePtr makeNode(const DriverType& driver,
  350. const uavcan::NodeStatusProvider::NodeName& name,
  351. const uavcan::protocol::SoftwareVersion& software_version,
  352. const uavcan::protocol::HardwareVersion& hardware_version,
  353. const uavcan::NodeID node_id = uavcan::NodeID(),
  354. const uavcan::TransferPriority node_status_transfer_priority =
  355. uavcan::TransferPriority::Default,
  356. ClockAdjustmentMode clock_adjustment_mode =
  357. SystemClock::detectPreferredClockAdjustmentMode())
  358. {
  359. NodePtr node = makeNode(driver, clock_adjustment_mode);
  360. node->setName(name);
  361. node->setSoftwareVersion(software_version);
  362. node->setHardwareVersion(hardware_version);
  363. if (node_id.isValid())
  364. {
  365. node->setNodeID(node_id);
  366. }
  367. const auto res = node->start(node_status_transfer_priority);
  368. if (res < 0)
  369. {
  370. throw LibuavcanErrorException(res);
  371. }
  372. return node;
  373. }
  374. /**
  375. * Use this function to create a sub-node instance with default SocketCAN driver.
  376. * It accepts the list of interface names to use for the new node, e.g. "can1", "vcan2", "slcan0".
  377. * Clock adjustment mode will be detected automatically unless provided explicitly.
  378. * @throws uavcan_linux::Exception.
  379. */
  380. static inline SubNodePtr makeSubNode(const std::vector<std::string>& iface_names,
  381. ClockAdjustmentMode clock_adjustment_mode =
  382. SystemClock::detectPreferredClockAdjustmentMode())
  383. {
  384. DriverPackPtr dp(new DriverPack(clock_adjustment_mode, iface_names));
  385. return SubNodePtr(new SubNode(dp));
  386. }
  387. /**
  388. * Use this function to create a sub-node instance with a custom driver.
  389. * Clock adjustment mode will be detected automatically unless provided explicitly.
  390. * @throws uavcan_linux::Exception.
  391. */
  392. static inline SubNodePtr makeSubNode(const std::shared_ptr<uavcan::ICanDriver>& can_driver,
  393. ClockAdjustmentMode clock_adjustment_mode =
  394. SystemClock::detectPreferredClockAdjustmentMode())
  395. {
  396. DriverPackPtr dp(new DriverPack(clock_adjustment_mode, can_driver));
  397. return SubNodePtr(new SubNode(dp));
  398. }
  399. /**
  400. * This function extends the other two overloads in such a way that it instantiates the node
  401. * and sets its Node ID immediately.
  402. *
  403. * Clock adjustment mode will be detected automatically unless provided explicitly.
  404. *
  405. * @throws uavcan_linux::Exception, uavcan_linux::LibuavcanErrorException.
  406. */
  407. template <typename DriverType>
  408. static inline SubNodePtr makeSubNode(const DriverType& driver,
  409. const uavcan::NodeID node_id,
  410. ClockAdjustmentMode clock_adjustment_mode =
  411. SystemClock::detectPreferredClockAdjustmentMode())
  412. {
  413. SubNodePtr sub_node = makeSubNode(driver, clock_adjustment_mode);
  414. sub_node->setNodeID(node_id);
  415. return sub_node;
  416. }
  417. }