123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454 |
- /*
- * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
- */
- #include <gtest/gtest.h>
- #include <uavcan/node/service_client.hpp>
- #include <uavcan/node/service_server.hpp>
- #include <uavcan/util/method_binder.hpp>
- #include <uavcan/protocol/RestartNode.hpp>
- #include <uavcan/protocol/GetDataTypeInfo.hpp>
- #include <root_ns_a/StringService.hpp>
- #include <root_ns_a/EmptyService.hpp>
- #include <queue>
- #include <sstream>
- #include "test_node.hpp"
- template <typename DataType>
- struct ServiceCallResultHandler
- {
- typedef typename uavcan::ServiceCallResult<DataType>::Status StatusType;
- StatusType last_status;
- uavcan::NodeID last_server_node_id;
- typename DataType::Response last_response;
- std::queue<typename DataType::Response> responses;
- void handleResponse(const uavcan::ServiceCallResult<DataType>& result)
- {
- std::cout << result << std::endl;
- last_status = result.getStatus();
- last_response = result.getResponse();
- last_server_node_id = result.getCallID().server_node_id;
- responses.push(result.getResponse());
- }
- bool match(StatusType status, uavcan::NodeID server_node_id, const typename DataType::Response& response) const
- {
- if (status == last_status &&
- server_node_id == last_server_node_id &&
- response == last_response)
- {
- return true;
- }
- else
- {
- std::cout << "MISMATCH: status=" << int(last_status) << ", last_server_node_id="
- << int(last_server_node_id.get()) << ", last response:\n" << last_response << std::endl;
- return false;
- }
- }
- typedef uavcan::MethodBinder<ServiceCallResultHandler*,
- void (ServiceCallResultHandler::*)(const uavcan::ServiceCallResult<DataType>&)> Binder;
- Binder bind() { return Binder(this, &ServiceCallResultHandler::handleResponse); }
- };
- static void stringServiceServerCallback(const uavcan::ReceivedDataStructure<root_ns_a::StringService::Request>& req,
- uavcan::ServiceResponseDataStructure<root_ns_a::StringService::Response>& rsp)
- {
- rsp.string_response = "Request string: ";
- rsp.string_response += req.string_request;
- }
- static void rejectingStringServiceServerCallback(
- const uavcan::ReceivedDataStructure<root_ns_a::StringService::Request>& req,
- uavcan::ServiceResponseDataStructure<root_ns_a::StringService::Response>& rsp)
- {
- rsp.string_response = "Request string: ";
- rsp.string_response += req.string_request;
- ASSERT_TRUE(rsp.isResponseEnabled());
- rsp.setResponseEnabled(false);
- ASSERT_FALSE(rsp.isResponseEnabled());
- }
- static void emptyServiceServerCallback(const uavcan::ReceivedDataStructure<root_ns_a::EmptyService::Request>&,
- uavcan::ServiceResponseDataStructure<root_ns_a::EmptyService::Response>&)
- {
- // Nothing to do - the service is empty
- }
- TEST(ServiceClient, Basic)
- {
- InterlinkedTestNodesWithSysClock nodes;
- // Type registration
- uavcan::GlobalDataTypeRegistry::instance().reset();
- uavcan::DefaultDataTypeRegistrator<root_ns_a::StringService> _registrator;
- // Server
- uavcan::ServiceServer<root_ns_a::StringService> server(nodes.a);
- ASSERT_EQ(0, server.start(stringServiceServerCallback));
- {
- // Caller
- typedef uavcan::ServiceCallResult<root_ns_a::StringService> ResultType;
- typedef uavcan::ServiceClient<root_ns_a::StringService,
- typename ServiceCallResultHandler<root_ns_a::StringService>::Binder > ClientType;
- ServiceCallResultHandler<root_ns_a::StringService> handler;
- ClientType client1(nodes.b);
- ClientType client2(nodes.b);
- ClientType client3(nodes.b);
- ASSERT_EQ(0, client1.getNumPendingCalls());
- ASSERT_EQ(0, client2.getNumPendingCalls());
- ASSERT_EQ(0, client3.getNumPendingCalls());
- ASSERT_FALSE(client1.hasPendingCallToServer(1));
- client1.setCallback(handler.bind());
- client2.setCallback(client1.getCallback());
- client3.setCallback(client1.getCallback());
- client3.setRequestTimeout(uavcan::MonotonicDuration::fromMSec(100));
- ASSERT_EQ(1, nodes.a.getDispatcher().getNumServiceRequestListeners());
- ASSERT_EQ(0, nodes.b.getDispatcher().getNumServiceResponseListeners()); // NOT listening!
- root_ns_a::StringService::Request request;
- request.string_request = "Hello world";
- std::cout << "!!! Calling!" << std::endl;
- ASSERT_LT(0, client1.call(1, request)); // OK
- ASSERT_LT(0, client1.call(1, request)); // OK - second request
- ASSERT_LT(0, client2.call(1, request)); // OK
- ASSERT_LT(0, client3.call(99, request)); // Will timeout!
- ASSERT_LT(0, client3.call(1, request)); // OK - second request
- ASSERT_TRUE(client1.hasPendingCallToServer(1));
- ASSERT_TRUE(client2.hasPendingCallToServer(1));
- ASSERT_TRUE(client3.hasPendingCallToServer(99));
- ASSERT_TRUE(client3.hasPendingCallToServer(1));
- std::cout << "!!! Spinning!" << std::endl;
- ASSERT_EQ(3, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Listening now!
- ASSERT_TRUE(client1.hasPendingCalls());
- ASSERT_TRUE(client2.hasPendingCalls());
- ASSERT_TRUE(client3.hasPendingCalls());
- ASSERT_EQ(2, client1.getNumPendingCalls());
- ASSERT_EQ(1, client2.getNumPendingCalls());
- ASSERT_EQ(2, client3.getNumPendingCalls());
- ASSERT_EQ(uavcan::NodeID(1), client2.getCallIDByIndex(0).server_node_id);
- ASSERT_EQ(uavcan::NodeID(), client2.getCallIDByIndex(1).server_node_id);
- nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(20));
- std::cout << "!!! Spin finished!" << std::endl;
- ASSERT_EQ(1, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Third is still listening!
- ASSERT_FALSE(client1.hasPendingCalls());
- ASSERT_FALSE(client2.hasPendingCalls());
- ASSERT_TRUE(client3.hasPendingCalls());
- ASSERT_EQ(0, client1.getNumPendingCalls());
- ASSERT_EQ(0, client2.getNumPendingCalls());
- ASSERT_EQ(1, client3.getNumPendingCalls()); // The one addressed to 99 is still waiting
- // Validating
- root_ns_a::StringService::Response expected_response;
- expected_response.string_response = "Request string: Hello world";
- ASSERT_TRUE(handler.match(ResultType::Success, 1, expected_response));
- nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(200));
- ASSERT_FALSE(client1.hasPendingCalls());
- ASSERT_FALSE(client2.hasPendingCalls());
- ASSERT_FALSE(client3.hasPendingCalls());
- ASSERT_EQ(0, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Third has timed out :(
- // Validating
- ASSERT_TRUE(handler.match(ResultType::ErrorTimeout, 99, root_ns_a::StringService::Response()));
- // Stray request
- ASSERT_LT(0, client3.call(99, request)); // Will timeout!
- ASSERT_TRUE(client3.hasPendingCalls());
- ASSERT_EQ(1, nodes.b.getDispatcher().getNumServiceResponseListeners());
- }
- // All destroyed - nobody listening
- ASSERT_EQ(0, nodes.b.getDispatcher().getNumServiceResponseListeners());
- }
- TEST(ServiceClient, Rejection)
- {
- InterlinkedTestNodesWithSysClock nodes;
- // Type registration
- uavcan::GlobalDataTypeRegistry::instance().reset();
- uavcan::DefaultDataTypeRegistrator<root_ns_a::StringService> _registrator;
- // Server
- uavcan::ServiceServer<root_ns_a::StringService> server(nodes.a);
- ASSERT_EQ(0, server.start(rejectingStringServiceServerCallback));
- // Caller
- typedef uavcan::ServiceCallResult<root_ns_a::StringService> ResultType;
- typedef uavcan::ServiceClient<root_ns_a::StringService,
- typename ServiceCallResultHandler<root_ns_a::StringService>::Binder > ClientType;
- ServiceCallResultHandler<root_ns_a::StringService> handler;
- ClientType client1(nodes.b);
- client1.setRequestTimeout(uavcan::MonotonicDuration::fromMSec(100));
- client1.setCallback(handler.bind());
- root_ns_a::StringService::Request request;
- request.string_request = "Hello world";
- ASSERT_LT(0, client1.call(1, request));
- ASSERT_EQ(uavcan::NodeID(1), client1.getCallIDByIndex(0).server_node_id);
- ASSERT_EQ(uavcan::NodeID(), client1.getCallIDByIndex(1).server_node_id);
- ASSERT_EQ(1, nodes.b.getDispatcher().getNumServiceResponseListeners());
- ASSERT_TRUE(client1.hasPendingCalls());
- ASSERT_TRUE(client1.hasPendingCallToServer(1));
- nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(200));
- ASSERT_FALSE(client1.hasPendingCalls());
- ASSERT_FALSE(client1.hasPendingCallToServer(1));
- ASSERT_EQ(0, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Timed out
- ASSERT_TRUE(handler.match(ResultType::ErrorTimeout, 1, root_ns_a::StringService::Response()));
- }
- TEST(ServiceClient, ConcurrentCalls)
- {
- InterlinkedTestNodesWithSysClock nodes;
- // Type registration
- uavcan::GlobalDataTypeRegistry::instance().reset();
- uavcan::DefaultDataTypeRegistrator<root_ns_a::StringService> _registrator;
- // Server
- uavcan::ServiceServer<root_ns_a::StringService> server(nodes.a);
- ASSERT_EQ(0, server.start(stringServiceServerCallback));
- // Caller
- typedef uavcan::ServiceCallResult<root_ns_a::StringService> ResultType;
- typedef uavcan::ServiceClient<root_ns_a::StringService,
- typename ServiceCallResultHandler<root_ns_a::StringService>::Binder > ClientType;
- ServiceCallResultHandler<root_ns_a::StringService> handler;
- /*
- * Initializing
- */
- ClientType client(nodes.b);
- ASSERT_EQ(0, client.getNumPendingCalls());
- client.setCallback(handler.bind());
- ASSERT_EQ(1, nodes.a.getDispatcher().getNumServiceRequestListeners());
- ASSERT_EQ(0, nodes.b.getDispatcher().getNumServiceResponseListeners()); // NOT listening!
- ASSERT_FALSE(client.hasPendingCalls());
- ASSERT_EQ(0, client.getNumPendingCalls());
- /*
- * Calling ten requests, the last one will be cancelled
- * Note that there will be non-unique transfer ID values; the client must handle that correctly
- */
- uavcan::ServiceCallID last_call_id;
- for (int i = 0; i < 10; i++)
- {
- std::ostringstream os;
- os << i;
- root_ns_a::StringService::Request request;
- request.string_request = os.str().c_str();
- ASSERT_LT(0, client.call(1, request, last_call_id));
- }
- ASSERT_LT(0, client.call(99, root_ns_a::StringService::Request())); // Will timeout in 1000 ms
- client.setRequestTimeout(uavcan::MonotonicDuration::fromMSec(100));
- ASSERT_LT(0, client.call(88, root_ns_a::StringService::Request())); // Will timeout in 100 ms
- ASSERT_TRUE(client.hasPendingCalls());
- ASSERT_EQ(12, client.getNumPendingCalls());
- ASSERT_EQ(1, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Listening
- /*
- * Cancelling one
- */
- client.cancelCall(last_call_id);
- ASSERT_TRUE(client.hasPendingCalls());
- ASSERT_EQ(11, client.getNumPendingCalls());
- ASSERT_EQ(1, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Still listening
- /*
- * Spinning
- */
- nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(20));
- ASSERT_TRUE(client.hasPendingCalls());
- ASSERT_EQ(2, client.getNumPendingCalls()); // Two still pending
- ASSERT_EQ(1, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Still listening
- /*
- * Validating the ones that didn't timeout
- */
- root_ns_a::StringService::Response last_response;
- for (int i = 0; i < 9; i++)
- {
- std::ostringstream os;
- os << "Request string: " << i;
- last_response.string_response = os.str().c_str();
- ASSERT_FALSE(handler.responses.empty());
- ASSERT_STREQ(last_response.string_response.c_str(), handler.responses.front().string_response.c_str());
- handler.responses.pop();
- }
- ASSERT_TRUE(handler.responses.empty());
- /*
- * Validating the 100 ms timeout
- */
- nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(100));
- ASSERT_TRUE(client.hasPendingCalls());
- ASSERT_EQ(1, client.getNumPendingCalls()); // One dropped
- ASSERT_EQ(1, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Still listening
- ASSERT_TRUE(handler.match(ResultType::ErrorTimeout, 88, root_ns_a::StringService::Response()));
- /*
- * Validating the 1000 ms timeout
- */
- nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(1000));
- ASSERT_FALSE(client.hasPendingCalls());
- ASSERT_EQ(0, client.getNumPendingCalls()); // All finished
- ASSERT_EQ(0, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Not listening
- ASSERT_TRUE(handler.match(ResultType::ErrorTimeout, 99, root_ns_a::StringService::Response()));
- }
- TEST(ServiceClient, Empty)
- {
- InterlinkedTestNodesWithSysClock nodes;
- // Type registration
- uavcan::GlobalDataTypeRegistry::instance().reset();
- uavcan::DefaultDataTypeRegistrator<root_ns_a::EmptyService> _registrator;
- // Server
- uavcan::ServiceServer<root_ns_a::EmptyService> server(nodes.a);
- ASSERT_EQ(0, server.start(emptyServiceServerCallback));
- {
- // Caller
- typedef uavcan::ServiceClient<root_ns_a::EmptyService,
- typename ServiceCallResultHandler<root_ns_a::EmptyService>::Binder > ClientType;
- ServiceCallResultHandler<root_ns_a::EmptyService> handler;
- ClientType client(nodes.b);
- client.setCallback(handler.bind());
- root_ns_a::EmptyService::Request request;
- ASSERT_LT(0, client.call(1, request)); // OK
- }
- // All destroyed - nobody listening
- ASSERT_EQ(0, nodes.b.getDispatcher().getNumServiceResponseListeners());
- }
- TEST(ServiceClient, Priority)
- {
- InterlinkedTestNodesWithSysClock nodes;
- // Type registration
- uavcan::GlobalDataTypeRegistry::instance().reset();
- uavcan::DefaultDataTypeRegistrator<root_ns_a::EmptyService> _registrator;
- // Initializing
- typedef uavcan::ServiceClient<root_ns_a::EmptyService,
- typename ServiceCallResultHandler<root_ns_a::EmptyService>::Binder > ClientType;
- ServiceCallResultHandler<root_ns_a::EmptyService> handler;
- ClientType client(nodes.b);
- client.setCallback(handler.bind());
- // Calling
- root_ns_a::EmptyService::Request request;
- client.setPriority(0);
- ASSERT_LT(0, client.call(1, request)); // OK
- client.setPriority(10);
- ASSERT_LT(0, client.call(1, request)); // OK
- client.setPriority(20);
- ASSERT_LT(0, client.call(1, request)); // OK
- client.setPriority(31);
- ASSERT_LT(0, client.call(1, request)); // OK
- // Validating
- ASSERT_EQ(4, nodes.can_a.read_queue.size());
- uavcan::Frame frame;
- ASSERT_TRUE(frame.parse(nodes.can_a.read_queue.front()));
- nodes.can_a.read_queue.pop();
- ASSERT_EQ(0, frame.getPriority().get());
- ASSERT_TRUE(frame.parse(nodes.can_a.read_queue.front()));
- nodes.can_a.read_queue.pop();
- ASSERT_EQ(10, frame.getPriority().get());
- ASSERT_TRUE(frame.parse(nodes.can_a.read_queue.front()));
- nodes.can_a.read_queue.pop();
- ASSERT_EQ(20, frame.getPriority().get());
- ASSERT_TRUE(frame.parse(nodes.can_a.read_queue.front()));
- nodes.can_a.read_queue.pop();
- ASSERT_EQ(31, frame.getPriority().get());
- }
- TEST(ServiceClient, Sizes)
- {
- using namespace uavcan;
- std::cout << "GetDataTypeInfo server: " <<
- sizeof(ServiceServer<protocol::GetDataTypeInfo>) << std::endl;
- std::cout << "RestartNode server: " <<
- sizeof(ServiceServer<protocol::RestartNode>) << std::endl;
- std::cout << "GetDataTypeInfo client: " <<
- sizeof(ServiceClient<protocol::GetDataTypeInfo>) << std::endl;
- }
|