firmware_update_trigger.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. /*
  2. * Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
  3. */
  4. #include <gtest/gtest.h>
  5. #include <uavcan/protocol/firmware_update_trigger.hpp>
  6. #include <uavcan/protocol/node_status_provider.hpp>
  7. #include "helpers.hpp"
  8. using namespace uavcan::protocol::file;
  9. struct FirmwareVersionChecker : public uavcan::IFirmwareVersionChecker
  10. {
  11. unsigned should_request_cnt;
  12. unsigned should_retry_cnt;
  13. unsigned confirmation_cnt;
  14. std::string firmware_path;
  15. int retry_quota;
  16. std::string expected_node_name_to_update;
  17. BeginFirmwareUpdate::Response last_error_response;
  18. FirmwareVersionChecker()
  19. : should_request_cnt(0)
  20. , should_retry_cnt(0)
  21. , confirmation_cnt(0)
  22. , retry_quota(0)
  23. { }
  24. virtual bool shouldRequestFirmwareUpdate(uavcan::NodeID node_id,
  25. const uavcan::protocol::GetNodeInfo::Response& node_info,
  26. FirmwareFilePath& out_firmware_file_path)
  27. {
  28. should_request_cnt++;
  29. std::cout << "REQUEST? " << int(node_id.get()) << "\n" << node_info << std::endl;
  30. out_firmware_file_path = firmware_path.c_str();
  31. return node_info.name == expected_node_name_to_update;
  32. }
  33. virtual bool shouldRetryFirmwareUpdate(uavcan::NodeID node_id,
  34. const BeginFirmwareUpdate::Response& error_response,
  35. FirmwareFilePath& out_firmware_file_path)
  36. {
  37. last_error_response = error_response;
  38. std::cout << "RETRY? " << int(node_id.get()) << "\n" << error_response << std::endl;
  39. should_retry_cnt++;
  40. EXPECT_STREQ(firmware_path.c_str(), out_firmware_file_path.c_str());
  41. if (retry_quota > 0)
  42. {
  43. retry_quota--;
  44. return true;
  45. }
  46. else
  47. {
  48. return false;
  49. }
  50. }
  51. virtual void handleFirmwareUpdateConfirmation(uavcan::NodeID node_id,
  52. const BeginFirmwareUpdate::Response& response)
  53. {
  54. confirmation_cnt++;
  55. std::cout << "CONFIRMED " << int(node_id.get()) << "\n" << response << std::endl;
  56. }
  57. };
  58. struct BeginFirmwareUpdateServer
  59. {
  60. uint8_t response_error_code;
  61. BeginFirmwareUpdateServer() : response_error_code(0) { }
  62. void handleRequest(const uavcan::ReceivedDataStructure<typename BeginFirmwareUpdate::Request>& req,
  63. uavcan::ServiceResponseDataStructure<typename BeginFirmwareUpdate::Response>& res) const
  64. {
  65. std::cout << "REQUEST\n" << req << std::endl;
  66. res.error = response_error_code;
  67. res.optional_error_message = "foobar";
  68. }
  69. typedef uavcan::MethodBinder<BeginFirmwareUpdateServer*,
  70. void (BeginFirmwareUpdateServer::*)(
  71. const uavcan::ReceivedDataStructure<typename BeginFirmwareUpdate::Request>&,
  72. uavcan::ServiceResponseDataStructure<typename BeginFirmwareUpdate::Response>&) const> Callback;
  73. Callback makeCallback() { return Callback(this, &BeginFirmwareUpdateServer::handleRequest); }
  74. };
  75. TEST(FirmwareUpdateTrigger, Basic)
  76. {
  77. uavcan::GlobalDataTypeRegistry::instance().reset();
  78. uavcan::DefaultDataTypeRegistrator<BeginFirmwareUpdate> _reg1;
  79. uavcan::DefaultDataTypeRegistrator<uavcan::protocol::GetNodeInfo> _reg2;
  80. uavcan::DefaultDataTypeRegistrator<uavcan::protocol::NodeStatus> _reg3;
  81. InterlinkedTestNodesWithSysClock nodes;
  82. FirmwareVersionChecker checker;
  83. uavcan::NodeInfoRetriever node_info_retriever(nodes.a); // On the same node
  84. uavcan::FirmwareUpdateTrigger trigger(nodes.a, checker);
  85. std::cout << "sizeof(uavcan::FirmwareUpdateTrigger): " << sizeof(uavcan::FirmwareUpdateTrigger) << std::endl;
  86. std::auto_ptr<uavcan::NodeStatusProvider> provider(new uavcan::NodeStatusProvider(nodes.b)); // Other node
  87. /*
  88. * Initializing
  89. */
  90. ASSERT_LE(0, trigger.start(node_info_retriever, "/path_prefix/"));
  91. ASSERT_LE(0, node_info_retriever.start());
  92. ASSERT_EQ(1, node_info_retriever.getNumListeners());
  93. uavcan::protocol::HardwareVersion hwver;
  94. hwver.unique_id[0] = 123;
  95. hwver.unique_id[4] = 213;
  96. hwver.unique_id[8] = 45;
  97. provider->setName("Ivan");
  98. provider->setHardwareVersion(hwver);
  99. ASSERT_LE(0, provider->startAndPublish());
  100. ASSERT_FALSE(trigger.isTimerRunning());
  101. ASSERT_EQ(0, trigger.getNumPendingNodes());
  102. /*
  103. * Updating one node
  104. * The server that can confirm the request is not running yet
  105. */
  106. checker.firmware_path = "firmware_path";
  107. checker.expected_node_name_to_update = "Ivan";
  108. checker.retry_quota = 1000;
  109. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(2000));
  110. ASSERT_TRUE(trigger.isTimerRunning());
  111. ASSERT_EQ(1, trigger.getNumPendingNodes());
  112. ASSERT_EQ(1, checker.should_request_cnt);
  113. ASSERT_EQ(0, checker.should_retry_cnt);
  114. ASSERT_EQ(0, checker.confirmation_cnt);
  115. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(2000));
  116. // Still running!
  117. ASSERT_TRUE(trigger.isTimerRunning());
  118. ASSERT_EQ(1, trigger.getNumPendingNodes());
  119. /*
  120. * Starting the firmware update server that returns an error
  121. * The checker will instruct the trigger to repeat
  122. */
  123. uavcan::ServiceServer<BeginFirmwareUpdate, BeginFirmwareUpdateServer::Callback> srv(nodes.b);
  124. BeginFirmwareUpdateServer srv_impl;
  125. ASSERT_LE(0, srv.start(srv_impl.makeCallback()));
  126. srv_impl.response_error_code = BeginFirmwareUpdate::Response::ERROR_UNKNOWN;
  127. checker.retry_quota = 1000;
  128. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(1100));
  129. ASSERT_EQ(1, checker.should_request_cnt);
  130. ASSERT_EQ(1, checker.should_retry_cnt);
  131. ASSERT_EQ(0, checker.confirmation_cnt);
  132. // Still running!
  133. ASSERT_TRUE(trigger.isTimerRunning());
  134. ASSERT_EQ(1, trigger.getNumPendingNodes());
  135. /*
  136. * Trying again, this time with ERROR_IN_PROGRESS
  137. */
  138. srv_impl.response_error_code = BeginFirmwareUpdate::Response::ERROR_IN_PROGRESS;
  139. checker.retry_quota = 0;
  140. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(2100));
  141. ASSERT_EQ(1, checker.should_request_cnt);
  142. ASSERT_EQ(1, checker.should_retry_cnt);
  143. ASSERT_EQ(1, checker.confirmation_cnt);
  144. // Stopped!
  145. ASSERT_FALSE(trigger.isTimerRunning());
  146. ASSERT_EQ(0, trigger.getNumPendingNodes());
  147. /*
  148. * Restarting the node info provider
  149. * Now it doesn't need an update
  150. */
  151. provider.reset(new uavcan::NodeStatusProvider(nodes.b));
  152. provider->setName("Dmitry");
  153. provider->setHardwareVersion(hwver);
  154. ASSERT_LE(0, provider->startAndPublish());
  155. nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(2100));
  156. ASSERT_EQ(2, checker.should_request_cnt);
  157. ASSERT_EQ(1, checker.should_retry_cnt);
  158. ASSERT_EQ(1, checker.confirmation_cnt);
  159. // Stopped!
  160. ASSERT_FALSE(trigger.isTimerRunning());
  161. ASSERT_EQ(0, trigger.getNumPendingNodes());
  162. /*
  163. * Final checks
  164. */
  165. ASSERT_EQ(0, nodes.a.internal_failure_count);
  166. ASSERT_EQ(0, nodes.b.internal_failure_count);
  167. }
  168. TEST(FirmwareUpdateTrigger, MultiNode)
  169. {
  170. uavcan::GlobalDataTypeRegistry::instance().reset();
  171. uavcan::DefaultDataTypeRegistrator<BeginFirmwareUpdate> _reg1;
  172. uavcan::DefaultDataTypeRegistrator<uavcan::protocol::GetNodeInfo> _reg2;
  173. uavcan::DefaultDataTypeRegistrator<uavcan::protocol::NodeStatus> _reg3;
  174. TestNetwork<5> nodes;
  175. // The trigger node
  176. FirmwareVersionChecker checker;
  177. uavcan::NodeInfoRetriever node_info_retriever(nodes[0]);
  178. uavcan::FirmwareUpdateTrigger trigger(nodes[0], checker);
  179. // The client nodes
  180. std::auto_ptr<uavcan::NodeStatusProvider> provider_a(new uavcan::NodeStatusProvider(nodes[1]));
  181. std::auto_ptr<uavcan::NodeStatusProvider> provider_b(new uavcan::NodeStatusProvider(nodes[2]));
  182. std::auto_ptr<uavcan::NodeStatusProvider> provider_c(new uavcan::NodeStatusProvider(nodes[3]));
  183. std::auto_ptr<uavcan::NodeStatusProvider> provider_d(new uavcan::NodeStatusProvider(nodes[4]));
  184. uavcan::protocol::HardwareVersion hwver;
  185. /*
  186. * Initializing
  187. */
  188. ASSERT_LE(0, trigger.start(node_info_retriever, "/path_prefix/"));
  189. ASSERT_LE(0, node_info_retriever.start());
  190. ASSERT_EQ(1, node_info_retriever.getNumListeners());
  191. hwver.unique_id[0] = 0xAA;
  192. provider_a->setHardwareVersion(hwver);
  193. provider_a->setName("Victor");
  194. ASSERT_LE(0, provider_a->startAndPublish());
  195. hwver.unique_id[0] = 0xBB;
  196. provider_b->setHardwareVersion(hwver);
  197. provider_b->setName("Victor");
  198. ASSERT_LE(0, provider_b->startAndPublish());
  199. hwver.unique_id[0] = 0xCC;
  200. provider_c->setHardwareVersion(hwver);
  201. provider_c->setName("Alexey");
  202. ASSERT_LE(0, provider_c->startAndPublish());
  203. hwver.unique_id[0] = 0xDD;
  204. provider_d->setHardwareVersion(hwver);
  205. provider_d->setName("Victor");
  206. ASSERT_LE(0, provider_d->startAndPublish());
  207. checker.expected_node_name_to_update = "Victor"; // Victors will get updated, others will not
  208. checker.firmware_path = "abc";
  209. /*
  210. * Running - 3 will timout, 1 will be ignored
  211. */
  212. ASSERT_FALSE(trigger.isTimerRunning());
  213. ASSERT_EQ(0, trigger.getNumPendingNodes());
  214. nodes.spinAll(uavcan::MonotonicDuration::fromMSec(4100));
  215. ASSERT_TRUE(trigger.isTimerRunning());
  216. ASSERT_EQ(3, trigger.getNumPendingNodes());
  217. ASSERT_EQ(4, checker.should_request_cnt);
  218. ASSERT_EQ(0, checker.should_retry_cnt);
  219. ASSERT_EQ(0, checker.confirmation_cnt);
  220. /*
  221. * Initializing the BeginFirmwareUpdate servers
  222. */
  223. uavcan::ServiceServer<BeginFirmwareUpdate, BeginFirmwareUpdateServer::Callback> srv_a(nodes[1]);
  224. uavcan::ServiceServer<BeginFirmwareUpdate, BeginFirmwareUpdateServer::Callback> srv_b(nodes[2]);
  225. uavcan::ServiceServer<BeginFirmwareUpdate, BeginFirmwareUpdateServer::Callback> srv_c(nodes[3]);
  226. uavcan::ServiceServer<BeginFirmwareUpdate, BeginFirmwareUpdateServer::Callback> srv_d(nodes[4]);
  227. BeginFirmwareUpdateServer srv_a_impl;
  228. BeginFirmwareUpdateServer srv_b_impl;
  229. BeginFirmwareUpdateServer srv_c_impl;
  230. BeginFirmwareUpdateServer srv_d_impl;
  231. ASSERT_LE(0, srv_a.start(srv_a_impl.makeCallback()));
  232. ASSERT_LE(0, srv_b.start(srv_b_impl.makeCallback()));
  233. ASSERT_LE(0, srv_c.start(srv_c_impl.makeCallback()));
  234. ASSERT_LE(0, srv_d.start(srv_d_impl.makeCallback()));
  235. srv_a_impl.response_error_code = BeginFirmwareUpdate::Response::ERROR_INVALID_MODE; // retry
  236. srv_b_impl.response_error_code = BeginFirmwareUpdate::Response::ERROR_INVALID_MODE; // retry
  237. srv_c_impl.response_error_code = BeginFirmwareUpdate::Response::ERROR_INVALID_MODE; // ignore (see below)
  238. srv_d_impl.response_error_code = BeginFirmwareUpdate::Response::ERROR_OK; // OK
  239. /*
  240. * Spinning, now we're getting some errors
  241. * This also checks correctness of the round-robin selector
  242. */
  243. checker.retry_quota = 2;
  244. nodes.spinAll(uavcan::MonotonicDuration::fromMSec(4200)); // Two will retry, one drop, one confirm
  245. ASSERT_TRUE(trigger.isTimerRunning());
  246. nodes.spinAll(uavcan::MonotonicDuration::fromMSec(1000));
  247. ASSERT_EQ(0, trigger.getNumPendingNodes()); // All removed now
  248. EXPECT_EQ(4, checker.should_request_cnt);
  249. EXPECT_EQ(4, checker.should_retry_cnt);
  250. EXPECT_EQ(1, checker.confirmation_cnt);
  251. /*
  252. * Waiting for the timer to stop
  253. */
  254. nodes.spinAll(uavcan::MonotonicDuration::fromMSec(1100));
  255. ASSERT_FALSE(trigger.isTimerRunning());
  256. }