Руслан Урядинский / libuavcan

Dependents:   UAVCAN UAVCAN_Subscriber

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers firmware_update_trigger.cpp Source File

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 }