Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: UAVCAN UAVCAN_Subscriber
firmware_update_trigger.cpp
00001 /* 00002 * Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com> 00003 */ 00004 00005 #include <gtest/gtest.h> 00006 #include <uavcan/protocol/firmware_update_trigger.hpp> 00007 #include <uavcan/protocol/node_status_provider.hpp> 00008 #include "helpers.hpp" 00009 00010 using namespace uavcan::protocol::file; 00011 00012 struct FirmwareVersionChecker : public uavcan::IFirmwareVersionChecker 00013 { 00014 unsigned should_request_cnt; 00015 unsigned should_retry_cnt; 00016 unsigned confirmation_cnt; 00017 00018 std::string firmware_path; 00019 00020 int retry_quota; 00021 std::string expected_node_name_to_update; 00022 00023 BeginFirmwareUpdate::Response last_error_response; 00024 00025 FirmwareVersionChecker() 00026 : should_request_cnt(0) 00027 , should_retry_cnt(0) 00028 , confirmation_cnt(0) 00029 , retry_quota(0) 00030 { } 00031 00032 virtual bool shouldRequestFirmwareUpdate(uavcan::NodeID node_id, 00033 const uavcan::protocol::GetNodeInfo::Response& node_info, 00034 FirmwareFilePath& out_firmware_file_path) 00035 { 00036 should_request_cnt++; 00037 std::cout << "REQUEST? " << int(node_id.get()) << "\n" << node_info << std::endl; 00038 out_firmware_file_path = firmware_path.c_str(); 00039 return node_info.name == expected_node_name_to_update; 00040 } 00041 00042 virtual bool shouldRetryFirmwareUpdate(uavcan::NodeID node_id, 00043 const BeginFirmwareUpdate::Response& error_response, 00044 FirmwareFilePath& out_firmware_file_path) 00045 { 00046 last_error_response = error_response; 00047 std::cout << "RETRY? " << int(node_id.get()) << "\n" << error_response << std::endl; 00048 should_retry_cnt++; 00049 00050 EXPECT_STREQ(firmware_path.c_str(), out_firmware_file_path.c_str()); 00051 00052 if (retry_quota > 0) 00053 { 00054 retry_quota--; 00055 return true; 00056 } 00057 else 00058 { 00059 return false; 00060 } 00061 } 00062 00063 virtual void handleFirmwareUpdateConfirmation(uavcan::NodeID node_id, 00064 const BeginFirmwareUpdate::Response& response) 00065 { 00066 confirmation_cnt++; 00067 std::cout << "CONFIRMED " << int(node_id.get()) << "\n" << response << std::endl; 00068 } 00069 }; 00070 00071 struct BeginFirmwareUpdateServer 00072 { 00073 uint8_t response_error_code; 00074 00075 BeginFirmwareUpdateServer() : response_error_code(0) { } 00076 00077 void handleRequest(const uavcan::ReceivedDataStructure<typename BeginFirmwareUpdate::Request>& req, 00078 uavcan::ServiceResponseDataStructure<typename BeginFirmwareUpdate::Response>& res) const 00079 { 00080 std::cout << "REQUEST\n" << req << std::endl; 00081 res.error = response_error_code; 00082 res.optional_error_message = "foobar"; 00083 } 00084 00085 typedef uavcan::MethodBinder<BeginFirmwareUpdateServer*, 00086 void (BeginFirmwareUpdateServer::*)( 00087 const uavcan::ReceivedDataStructure<typename BeginFirmwareUpdate::Request>&, 00088 uavcan::ServiceResponseDataStructure<typename BeginFirmwareUpdate::Response>&) const> Callback; 00089 00090 Callback makeCallback() { return Callback(this, &BeginFirmwareUpdateServer::handleRequest); } 00091 }; 00092 00093 00094 TEST(FirmwareUpdateTrigger, Basic) 00095 { 00096 uavcan::GlobalDataTypeRegistry::instance().reset(); 00097 uavcan::DefaultDataTypeRegistrator<BeginFirmwareUpdate> _reg1; 00098 uavcan::DefaultDataTypeRegistrator<uavcan::protocol::GetNodeInfo> _reg2; 00099 uavcan::DefaultDataTypeRegistrator<uavcan::protocol::NodeStatus> _reg3; 00100 00101 InterlinkedTestNodesWithSysClock nodes; 00102 00103 FirmwareVersionChecker checker; 00104 00105 uavcan::NodeInfoRetriever node_info_retriever(nodes.a); // On the same node 00106 00107 uavcan::FirmwareUpdateTrigger trigger(nodes.a, checker); 00108 std::cout << "sizeof(uavcan::FirmwareUpdateTrigger): " << sizeof(uavcan::FirmwareUpdateTrigger) << std::endl; 00109 00110 std::auto_ptr<uavcan::NodeStatusProvider> provider(new uavcan::NodeStatusProvider(nodes.b)); // Other node 00111 00112 /* 00113 * Initializing 00114 */ 00115 ASSERT_LE(0, trigger.start(node_info_retriever, "/path_prefix/")); 00116 00117 ASSERT_LE(0, node_info_retriever.start()); 00118 ASSERT_EQ(1, node_info_retriever.getNumListeners()); 00119 00120 uavcan::protocol::HardwareVersion hwver; 00121 hwver.unique_id[0] = 123; 00122 hwver.unique_id[4] = 213; 00123 hwver.unique_id[8] = 45; 00124 00125 provider->setName("Ivan"); 00126 provider->setHardwareVersion(hwver); 00127 00128 ASSERT_LE(0, provider->startAndPublish()); 00129 00130 ASSERT_FALSE(trigger.isTimerRunning()); 00131 ASSERT_EQ(0, trigger.getNumPendingNodes()); 00132 00133 /* 00134 * Updating one node 00135 * The server that can confirm the request is not running yet 00136 */ 00137 checker.firmware_path = "firmware_path"; 00138 checker.expected_node_name_to_update = "Ivan"; 00139 checker.retry_quota = 1000; 00140 00141 nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(2000)); 00142 00143 ASSERT_TRUE(trigger.isTimerRunning()); 00144 ASSERT_EQ(1, trigger.getNumPendingNodes()); 00145 00146 ASSERT_EQ(1, checker.should_request_cnt); 00147 ASSERT_EQ(0, checker.should_retry_cnt); 00148 ASSERT_EQ(0, checker.confirmation_cnt); 00149 00150 nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(2000)); 00151 00152 // Still running! 00153 ASSERT_TRUE(trigger.isTimerRunning()); 00154 ASSERT_EQ(1, trigger.getNumPendingNodes()); 00155 00156 /* 00157 * Starting the firmware update server that returns an error 00158 * The checker will instruct the trigger to repeat 00159 */ 00160 uavcan::ServiceServer<BeginFirmwareUpdate, BeginFirmwareUpdateServer::Callback> srv(nodes.b); 00161 BeginFirmwareUpdateServer srv_impl; 00162 00163 ASSERT_LE(0, srv.start(srv_impl.makeCallback())); 00164 00165 srv_impl.response_error_code = BeginFirmwareUpdate::Response::ERROR_UNKNOWN; 00166 checker.retry_quota = 1000; 00167 00168 nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(1100)); 00169 00170 ASSERT_EQ(1, checker.should_request_cnt); 00171 ASSERT_EQ(1, checker.should_retry_cnt); 00172 ASSERT_EQ(0, checker.confirmation_cnt); 00173 00174 // Still running! 00175 ASSERT_TRUE(trigger.isTimerRunning()); 00176 ASSERT_EQ(1, trigger.getNumPendingNodes()); 00177 00178 /* 00179 * Trying again, this time with ERROR_IN_PROGRESS 00180 */ 00181 srv_impl.response_error_code = BeginFirmwareUpdate::Response::ERROR_IN_PROGRESS; 00182 checker.retry_quota = 0; 00183 00184 nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(2100)); 00185 00186 ASSERT_EQ(1, checker.should_request_cnt); 00187 ASSERT_EQ(1, checker.should_retry_cnt); 00188 ASSERT_EQ(1, checker.confirmation_cnt); 00189 00190 // Stopped! 00191 ASSERT_FALSE(trigger.isTimerRunning()); 00192 ASSERT_EQ(0, trigger.getNumPendingNodes()); 00193 00194 /* 00195 * Restarting the node info provider 00196 * Now it doesn't need an update 00197 */ 00198 provider.reset(new uavcan::NodeStatusProvider(nodes.b)); 00199 00200 provider->setName("Dmitry"); 00201 provider->setHardwareVersion(hwver); 00202 00203 ASSERT_LE(0, provider->startAndPublish()); 00204 00205 nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(2100)); 00206 00207 ASSERT_EQ(2, checker.should_request_cnt); 00208 ASSERT_EQ(1, checker.should_retry_cnt); 00209 ASSERT_EQ(1, checker.confirmation_cnt); 00210 00211 // Stopped! 00212 ASSERT_FALSE(trigger.isTimerRunning()); 00213 ASSERT_EQ(0, trigger.getNumPendingNodes()); 00214 00215 /* 00216 * Final checks 00217 */ 00218 ASSERT_EQ(0, nodes.a.internal_failure_count); 00219 ASSERT_EQ(0, nodes.b.internal_failure_count); 00220 } 00221 00222 00223 TEST(FirmwareUpdateTrigger, MultiNode) 00224 { 00225 uavcan::GlobalDataTypeRegistry::instance().reset(); 00226 uavcan::DefaultDataTypeRegistrator<BeginFirmwareUpdate> _reg1; 00227 uavcan::DefaultDataTypeRegistrator<uavcan::protocol::GetNodeInfo> _reg2; 00228 uavcan::DefaultDataTypeRegistrator<uavcan::protocol::NodeStatus> _reg3; 00229 00230 TestNetwork<5> nodes; 00231 00232 // The trigger node 00233 FirmwareVersionChecker checker; 00234 uavcan::NodeInfoRetriever node_info_retriever(nodes[0]); 00235 uavcan::FirmwareUpdateTrigger trigger(nodes[0], checker); 00236 00237 // The client nodes 00238 std::auto_ptr<uavcan::NodeStatusProvider> provider_a(new uavcan::NodeStatusProvider(nodes[1])); 00239 std::auto_ptr<uavcan::NodeStatusProvider> provider_b(new uavcan::NodeStatusProvider(nodes[2])); 00240 std::auto_ptr<uavcan::NodeStatusProvider> provider_c(new uavcan::NodeStatusProvider(nodes[3])); 00241 std::auto_ptr<uavcan::NodeStatusProvider> provider_d(new uavcan::NodeStatusProvider(nodes[4])); 00242 00243 uavcan::protocol::HardwareVersion hwver; 00244 00245 /* 00246 * Initializing 00247 */ 00248 ASSERT_LE(0, trigger.start(node_info_retriever, "/path_prefix/")); 00249 00250 ASSERT_LE(0, node_info_retriever.start()); 00251 ASSERT_EQ(1, node_info_retriever.getNumListeners()); 00252 00253 hwver.unique_id[0] = 0xAA; 00254 provider_a->setHardwareVersion(hwver); 00255 provider_a->setName("Victor"); 00256 ASSERT_LE(0, provider_a->startAndPublish()); 00257 00258 hwver.unique_id[0] = 0xBB; 00259 provider_b->setHardwareVersion(hwver); 00260 provider_b->setName("Victor"); 00261 ASSERT_LE(0, provider_b->startAndPublish()); 00262 00263 hwver.unique_id[0] = 0xCC; 00264 provider_c->setHardwareVersion(hwver); 00265 provider_c->setName("Alexey"); 00266 ASSERT_LE(0, provider_c->startAndPublish()); 00267 00268 hwver.unique_id[0] = 0xDD; 00269 provider_d->setHardwareVersion(hwver); 00270 provider_d->setName("Victor"); 00271 ASSERT_LE(0, provider_d->startAndPublish()); 00272 00273 checker.expected_node_name_to_update = "Victor"; // Victors will get updated, others will not 00274 checker.firmware_path = "abc"; 00275 00276 /* 00277 * Running - 3 will timout, 1 will be ignored 00278 */ 00279 ASSERT_FALSE(trigger.isTimerRunning()); 00280 ASSERT_EQ(0, trigger.getNumPendingNodes()); 00281 00282 nodes.spinAll(uavcan::MonotonicDuration::fromMSec(4100)); 00283 00284 ASSERT_TRUE(trigger.isTimerRunning()); 00285 ASSERT_EQ(3, trigger.getNumPendingNodes()); 00286 00287 ASSERT_EQ(4, checker.should_request_cnt); 00288 ASSERT_EQ(0, checker.should_retry_cnt); 00289 ASSERT_EQ(0, checker.confirmation_cnt); 00290 00291 /* 00292 * Initializing the BeginFirmwareUpdate servers 00293 */ 00294 uavcan::ServiceServer<BeginFirmwareUpdate, BeginFirmwareUpdateServer::Callback> srv_a(nodes[1]); 00295 uavcan::ServiceServer<BeginFirmwareUpdate, BeginFirmwareUpdateServer::Callback> srv_b(nodes[2]); 00296 uavcan::ServiceServer<BeginFirmwareUpdate, BeginFirmwareUpdateServer::Callback> srv_c(nodes[3]); 00297 uavcan::ServiceServer<BeginFirmwareUpdate, BeginFirmwareUpdateServer::Callback> srv_d(nodes[4]); 00298 00299 BeginFirmwareUpdateServer srv_a_impl; 00300 BeginFirmwareUpdateServer srv_b_impl; 00301 BeginFirmwareUpdateServer srv_c_impl; 00302 BeginFirmwareUpdateServer srv_d_impl; 00303 00304 ASSERT_LE(0, srv_a.start(srv_a_impl.makeCallback())); 00305 ASSERT_LE(0, srv_b.start(srv_b_impl.makeCallback())); 00306 ASSERT_LE(0, srv_c.start(srv_c_impl.makeCallback())); 00307 ASSERT_LE(0, srv_d.start(srv_d_impl.makeCallback())); 00308 00309 srv_a_impl.response_error_code = BeginFirmwareUpdate::Response::ERROR_INVALID_MODE; // retry 00310 srv_b_impl.response_error_code = BeginFirmwareUpdate::Response::ERROR_INVALID_MODE; // retry 00311 srv_c_impl.response_error_code = BeginFirmwareUpdate::Response::ERROR_INVALID_MODE; // ignore (see below) 00312 srv_d_impl.response_error_code = BeginFirmwareUpdate::Response::ERROR_OK; // OK 00313 00314 /* 00315 * Spinning, now we're getting some errors 00316 * This also checks correctness of the round-robin selector 00317 */ 00318 checker.retry_quota = 2; 00319 nodes.spinAll(uavcan::MonotonicDuration::fromMSec(4200)); // Two will retry, one drop, one confirm 00320 00321 ASSERT_TRUE(trigger.isTimerRunning()); 00322 00323 nodes.spinAll(uavcan::MonotonicDuration::fromMSec(1000)); 00324 ASSERT_EQ(0, trigger.getNumPendingNodes()); // All removed now 00325 00326 EXPECT_EQ(4, checker.should_request_cnt); 00327 EXPECT_EQ(4, checker.should_retry_cnt); 00328 EXPECT_EQ(1, checker.confirmation_cnt); 00329 00330 /* 00331 * Waiting for the timer to stop 00332 */ 00333 nodes.spinAll(uavcan::MonotonicDuration::fromMSec(1100)); 00334 00335 ASSERT_FALSE(trigger.isTimerRunning()); 00336 }
Generated on Tue Jul 12 2022 17:17:31 by
