node_info_retriever.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. /*
  2. * Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
  3. */
  4. #if __GNUC__
  5. // We need auto_ptr for compatibility reasons
  6. # pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  7. # pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
  8. #endif
  9. #include <memory>
  10. #include <gtest/gtest.h>
  11. #include <uavcan/protocol/node_info_retriever.hpp>
  12. #include <uavcan/protocol/node_status_provider.hpp>
  13. #include "helpers.hpp"
  14. static void publishNodeStatus(PairableCanDriver& can, uavcan::NodeID node_id,
  15. uavcan::uint32_t uptime_sec, uavcan::TransferID tid)
  16. {
  17. uavcan::protocol::NodeStatus msg;
  18. msg.health = uavcan::protocol::NodeStatus::HEALTH_OK;
  19. msg.mode = uavcan::protocol::NodeStatus::MODE_OPERATIONAL;
  20. msg.uptime_sec = uptime_sec;
  21. emulateSingleFrameBroadcastTransfer(can, node_id, msg, tid);
  22. }
  23. struct NodeInfoListener : public uavcan::INodeInfoListener
  24. {
  25. std::auto_ptr<uavcan::protocol::GetNodeInfo::Response> last_node_info;
  26. uavcan::NodeID last_node_id;
  27. unsigned status_message_cnt;
  28. unsigned status_change_cnt;
  29. unsigned info_unavailable_cnt;
  30. NodeInfoListener()
  31. : status_message_cnt(0)
  32. , status_change_cnt(0)
  33. , info_unavailable_cnt(0)
  34. { }
  35. virtual void handleNodeInfoRetrieved(uavcan::NodeID node_id,
  36. const uavcan::protocol::GetNodeInfo::Response& node_info)
  37. {
  38. last_node_id = node_id;
  39. std::cout << node_info << std::endl;
  40. last_node_info.reset(new uavcan::protocol::GetNodeInfo::Response(node_info));
  41. }
  42. virtual void handleNodeInfoUnavailable(uavcan::NodeID node_id)
  43. {
  44. std::cout << "NODE INFO FOR " << int(node_id.get()) << " IS UNAVAILABLE" << std::endl;
  45. last_node_id = node_id;
  46. info_unavailable_cnt++;
  47. }
  48. virtual void handleNodeStatusChange(const uavcan::NodeStatusMonitor::NodeStatusChangeEvent& event)
  49. {
  50. std::cout << "NODE " << int(event.node_id.get()) << " STATUS CHANGE: "
  51. << event.old_status.toString() << " --> " << event.status.toString() << std::endl;
  52. status_change_cnt++;
  53. }
  54. virtual void handleNodeStatusMessage(const uavcan::ReceivedDataStructure<uavcan::protocol::NodeStatus>& msg)
  55. {
  56. std::cout << msg << std::endl;
  57. status_message_cnt++;
  58. }
  59. };
  60. TEST(NodeInfoRetriever, Basic)
  61. {
  62. uavcan::GlobalDataTypeRegistry::instance().reset();
  63. uavcan::DefaultDataTypeRegistrator<uavcan::protocol::NodeStatus> _reg1;
  64. uavcan::DefaultDataTypeRegistrator<uavcan::protocol::GetNodeInfo> _reg2;
  65. InterlinkedTestNodesWithSysClock nodes;
  66. uavcan::NodeInfoRetriever retr(nodes.a);
  67. std::cout << "sizeof(uavcan::NodeInfoRetriever): " << sizeof(uavcan::NodeInfoRetriever) << std::endl;
  68. std::cout << "sizeof(uavcan::ServiceClient<uavcan::protocol::GetNodeInfo>): "
  69. << sizeof(uavcan::ServiceClient<uavcan::protocol::GetNodeInfo>) << std::endl;
  70. std::auto_ptr<uavcan::NodeStatusProvider> provider(new uavcan::NodeStatusProvider(nodes.b));
  71. NodeInfoListener listener;
  72. /*
  73. * Initialization
  74. */
  75. ASSERT_LE(0, retr.start());
  76. retr.removeListener(&listener); // Does nothing
  77. retr.addListener(&listener);
  78. retr.addListener(&listener);
  79. retr.addListener(&listener);
  80. ASSERT_EQ(1, retr.getNumListeners());
  81. uavcan::protocol::HardwareVersion hwver;
  82. hwver.unique_id[0] = 123;
  83. hwver.unique_id[4] = 213;
  84. hwver.unique_id[8] = 45;
  85. provider->setName("Ivan");
  86. provider->setHardwareVersion(hwver);
  87. ASSERT_LE(0, provider->startAndPublish());
  88. ASSERT_FALSE(retr.isRetrievingInProgress());
  89. ASSERT_EQ(0, retr.getNumPendingRequests());
  90. EXPECT_EQ(40, retr.getRequestInterval().toMSec()); // Default
  91. /*
  92. * Waiting for discovery
  93. */
  94. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(50));
  95. ASSERT_TRUE(retr.isRetrievingInProgress());
  96. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(1500));
  97. ASSERT_FALSE(retr.isRetrievingInProgress());
  98. ASSERT_EQ(2, listener.status_message_cnt);
  99. ASSERT_EQ(1, listener.status_change_cnt);
  100. ASSERT_EQ(0, listener.info_unavailable_cnt);
  101. ASSERT_TRUE(listener.last_node_info.get());
  102. ASSERT_EQ(uavcan::NodeID(2), listener.last_node_id);
  103. ASSERT_EQ("Ivan", listener.last_node_info->name);
  104. ASSERT_TRUE(hwver == listener.last_node_info->hardware_version);
  105. provider.reset(); // Moving the provider out of the way; its entry will timeout meanwhile
  106. /*
  107. * Declaring a bunch of different nodes that don't support GetNodeInfo
  108. */
  109. ASSERT_FALSE(retr.isRetrievingInProgress());
  110. retr.setNumRequestAttempts(3);
  111. uavcan::TransferID tid;
  112. publishNodeStatus(nodes.can_a, uavcan::NodeID(10), 10, tid);
  113. publishNodeStatus(nodes.can_a, uavcan::NodeID(11), 10, tid);
  114. publishNodeStatus(nodes.can_a, uavcan::NodeID(12), 10, tid);
  115. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(40));
  116. ASSERT_LE(1, retr.getNumPendingRequests());
  117. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(40));
  118. ASSERT_LE(2, retr.getNumPendingRequests());
  119. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(40));
  120. ASSERT_EQ(3, retr.getNumPendingRequests());
  121. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(1000));
  122. ASSERT_TRUE(retr.isRetrievingInProgress());
  123. tid.increment();
  124. publishNodeStatus(nodes.can_a, uavcan::NodeID(10), 11, tid);
  125. publishNodeStatus(nodes.can_a, uavcan::NodeID(11), 11, tid);
  126. publishNodeStatus(nodes.can_a, uavcan::NodeID(12), 11, tid);
  127. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(40));
  128. ASSERT_LE(1, retr.getNumPendingRequests());
  129. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(40));
  130. ASSERT_LE(2, retr.getNumPendingRequests());
  131. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(40));
  132. ASSERT_EQ(3, retr.getNumPendingRequests());
  133. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(1000));
  134. ASSERT_TRUE(retr.isRetrievingInProgress());
  135. tid.increment();
  136. publishNodeStatus(nodes.can_a, uavcan::NodeID(10), 12, tid);
  137. publishNodeStatus(nodes.can_a, uavcan::NodeID(11), 12, tid);
  138. publishNodeStatus(nodes.can_a, uavcan::NodeID(12), 10, tid); // Reset
  139. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(40));
  140. ASSERT_LE(1, retr.getNumPendingRequests());
  141. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(40));
  142. ASSERT_LE(2, retr.getNumPendingRequests());
  143. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(40));
  144. ASSERT_EQ(3, retr.getNumPendingRequests());
  145. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(1000));
  146. ASSERT_TRUE(retr.isRetrievingInProgress());
  147. EXPECT_EQ(11, listener.status_message_cnt);
  148. EXPECT_EQ(5, listener.status_change_cnt); // node 2 online/offline + 3 test nodes above
  149. EXPECT_EQ(2, listener.info_unavailable_cnt);
  150. tid.increment();
  151. publishNodeStatus(nodes.can_a, uavcan::NodeID(12), 11, tid);
  152. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(40));
  153. ASSERT_EQ(1, retr.getNumPendingRequests());
  154. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(40));
  155. ASSERT_EQ(1, retr.getNumPendingRequests()); // Still one because two went offline
  156. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(1200));
  157. ASSERT_TRUE(retr.isRetrievingInProgress());
  158. tid.increment();
  159. publishNodeStatus(nodes.can_a, uavcan::NodeID(12), 12, tid);
  160. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(1200));
  161. ASSERT_FALSE(retr.isRetrievingInProgress()); // Out of attempts, stopping
  162. ASSERT_EQ(0, retr.getNumPendingRequests());
  163. EXPECT_EQ(13, listener.status_message_cnt);
  164. EXPECT_EQ(7, listener.status_change_cnt); // node 2 online/offline + 2 test nodes above online/offline + 1
  165. EXPECT_EQ(3, listener.info_unavailable_cnt);
  166. /*
  167. * Forcing the class to forget everything
  168. */
  169. std::cout << "Invalidation" << std::endl;
  170. retr.invalidateAll();
  171. ASSERT_FALSE(retr.isRetrievingInProgress());
  172. ASSERT_EQ(0, retr.getNumPendingRequests());
  173. tid.increment();
  174. publishNodeStatus(nodes.can_a, uavcan::NodeID(10), 60, tid);
  175. publishNodeStatus(nodes.can_a, uavcan::NodeID(11), 60, tid);
  176. publishNodeStatus(nodes.can_a, uavcan::NodeID(12), 60, tid);
  177. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(200));
  178. ASSERT_TRUE(retr.isRetrievingInProgress());
  179. ASSERT_EQ(3, retr.getNumPendingRequests());
  180. }
  181. TEST(NodeInfoRetriever, MaxConcurrentRequests)
  182. {
  183. uavcan::GlobalDataTypeRegistry::instance().reset();
  184. uavcan::DefaultDataTypeRegistrator<uavcan::protocol::NodeStatus> _reg1;
  185. uavcan::DefaultDataTypeRegistrator<uavcan::protocol::GetNodeInfo> _reg2;
  186. InterlinkedTestNodesWithSysClock nodes;
  187. uavcan::NodeInfoRetriever retr(nodes.a);
  188. std::cout << "sizeof(uavcan::NodeInfoRetriever): " << sizeof(uavcan::NodeInfoRetriever) << std::endl;
  189. std::cout << "sizeof(uavcan::ServiceClient<uavcan::protocol::GetNodeInfo>): "
  190. << sizeof(uavcan::ServiceClient<uavcan::protocol::GetNodeInfo>) << std::endl;
  191. NodeInfoListener listener;
  192. /*
  193. * Initialization
  194. */
  195. ASSERT_LE(0, retr.start());
  196. retr.addListener(&listener);
  197. ASSERT_EQ(1, retr.getNumListeners());
  198. ASSERT_FALSE(retr.isRetrievingInProgress());
  199. ASSERT_EQ(0, retr.getNumPendingRequests());
  200. ASSERT_EQ(40, retr.getRequestInterval().toMSec());
  201. const unsigned MaxPendingRequests = 26; // See class docs
  202. const unsigned MinPendingRequestsAtFullLoad = 12;
  203. /*
  204. * Sending a lot of requests, making sure that the number of concurrent calls does not exceed the specified limit.
  205. */
  206. for (uint8_t node_id = 1U; node_id <= 127U; node_id++)
  207. {
  208. publishNodeStatus(nodes.can_a, node_id, 0, uavcan::TransferID());
  209. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(10));
  210. ASSERT_GE(MaxPendingRequests, retr.getNumPendingRequests());
  211. ASSERT_TRUE(retr.isRetrievingInProgress());
  212. }
  213. ASSERT_GE(MaxPendingRequests, retr.getNumPendingRequests());
  214. ASSERT_LE(MinPendingRequestsAtFullLoad, retr.getNumPendingRequests());
  215. for (int i = 0; i < 8; i++) // Approximate
  216. {
  217. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(35));
  218. std::cout << "!!! SPIN " << i << " COMPLETE" << std::endl;
  219. ASSERT_GE(MaxPendingRequests, retr.getNumPendingRequests());
  220. ASSERT_LE(MinPendingRequestsAtFullLoad, retr.getNumPendingRequests());
  221. ASSERT_TRUE(retr.isRetrievingInProgress());
  222. }
  223. ASSERT_LT(0, retr.getNumPendingRequests());
  224. ASSERT_TRUE(retr.isRetrievingInProgress());
  225. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(5000));
  226. ASSERT_EQ(0, retr.getNumPendingRequests());
  227. ASSERT_FALSE(retr.isRetrievingInProgress());
  228. }