/* * Copyright (C) 2014 Pavel Kirienko */ #include #include #include #include #include #include #include #include #include "../clock.hpp" #include "../transport/can/can.hpp" #include "test_node.hpp" template struct ServiceCallResultHandler { typedef typename uavcan::ServiceCallResult::Status StatusType; StatusType last_status; uavcan::NodeID last_server_node_id; typename DataType::Response last_response; void handleResponse(const uavcan::ServiceCallResult& result) { std::cout << result << std::endl; last_status = result.status; last_response = result.response; last_server_node_id = result.server_node_id; } bool match(StatusType status, uavcan::NodeID server_node_id, const typename DataType::Response& response) const { return status == last_status && server_node_id == last_server_node_id && response == last_response; } typedef uavcan::MethodBinder&)> Binder; Binder bind() { return Binder(this, &ServiceCallResultHandler::handleResponse); } }; static void stringServiceServerCallback(const uavcan::ReceivedDataStructure& req, root_ns_a::StringService::Response& rsp) { rsp.string_response = "Request string: "; rsp.string_response += req.string_request; } struct PairableCanDriver : public uavcan::ICanDriver, public uavcan::ICanIface { uavcan::ISystemClock& clock; PairableCanDriver* other; std::queue read_queue; PairableCanDriver(uavcan::ISystemClock& clock) : clock(clock) , other(NULL) { } void linkTogether(PairableCanDriver* with) { this->other = with; with->other = this; } uavcan::ICanIface* getIface(int iface_index) { if (iface_index == 0) return this; return NULL; } int getNumIfaces() const { return 1; } int select(int& inout_write_iface_mask, int& inout_read_iface_mask, uavcan::MonotonicTime blocking_deadline) { assert(other); if (inout_read_iface_mask == 1) inout_read_iface_mask = read_queue.size() ? 1 : 0; if (inout_read_iface_mask || inout_write_iface_mask) return 1; while (clock.getMonotonic() < blocking_deadline) usleep(1000); return 0; } int send(const uavcan::CanFrame& frame, uavcan::MonotonicTime) { assert(other); other->read_queue.push(frame); return 1; } int receive(uavcan::CanFrame& out_frame, uavcan::MonotonicTime& out_ts_monotonic, uavcan::UtcTime& out_ts_utc) { assert(other); assert(read_queue.size()); out_frame = read_queue.front(); read_queue.pop(); out_ts_monotonic = clock.getMonotonic(); out_ts_utc = clock.getUtc(); return 1; } int configureFilters(const uavcan::CanFilterConfig*, int) { return -1; } int getNumFilters() const { return 0; } uint64_t getNumErrors() const { return 0; } }; TEST(ServiceClient, Basic) { SystemClockDriver clock; PairableCanDriver can_a(clock), can_b(clock); can_a.linkTogether(&can_b); TestNode node_a(can_a, clock, 1), node_b(can_b, clock, 2); // Type registration uavcan::GlobalDataTypeRegistry::instance().reset(); uavcan::DefaultDataTypeRegistrator _registrator; // Server uavcan::ServiceServer server(node_a); ASSERT_EQ(1, server.start(stringServiceServerCallback)); { // Caller typedef uavcan::ServiceCallResult ResultType; typedef uavcan::ServiceClient::Binder > ClientType; ServiceCallResultHandler handler; ClientType client1(node_b); ClientType client2(node_b); ClientType client3(node_b); client1.setCallback(handler.bind()); client2.setCallback(client1.getCallback()); client3.setCallback(client1.getCallback()); client3.setRequestTimeout(uavcan::MonotonicDuration::fromMSec(100)); ASSERT_EQ(1, node_a.getDispatcher().getNumServiceRequestListeners()); ASSERT_EQ(0, node_b.getDispatcher().getNumServiceResponseListeners()); // NOT listening! root_ns_a::StringService::Request request; request.string_request = "Hello world"; ASSERT_LT(0, client1.call(1, request)); // OK ASSERT_LT(0, client2.call(1, request)); // OK ASSERT_LT(0, client3.call(99, request)); // Will timeout! ASSERT_EQ(3, node_b.getDispatcher().getNumServiceResponseListeners()); // Listening now! ASSERT_TRUE(client1.isPending()); ASSERT_TRUE(client2.isPending()); ASSERT_TRUE(client3.isPending()); node_a.spin(uavcan::MonotonicDuration::fromMSec(10)); node_b.spin(uavcan::MonotonicDuration::fromMSec(10)); ASSERT_EQ(1, node_b.getDispatcher().getNumServiceResponseListeners()); // Third is still listening! ASSERT_FALSE(client1.isPending()); ASSERT_FALSE(client2.isPending()); ASSERT_TRUE(client3.isPending()); // 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)); node_a.spin(uavcan::MonotonicDuration::fromMSec(100)); node_b.spin(uavcan::MonotonicDuration::fromMSec(100)); ASSERT_FALSE(client1.isPending()); ASSERT_FALSE(client2.isPending()); ASSERT_FALSE(client3.isPending()); ASSERT_EQ(0, node_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.isPending()); ASSERT_EQ(1, node_b.getDispatcher().getNumServiceResponseListeners()); } // All destroyed - nobody listening ASSERT_EQ(0, node_b.getDispatcher().getNumServiceResponseListeners()); } TEST(ServiceClient, Sizes) { using namespace uavcan; std::cout << "ComputeAggregateTypeSignature server: " << sizeof(ServiceServer) << std::endl; std::cout << "ComputeAggregateTypeSignature client: " << sizeof(ServiceClient) << std::endl; std::cout << "ComputeAggregateTypeSignature request data struct: " << sizeof(protocol::ComputeAggregateTypeSignature::Request) << std::endl; std::cout << "GetDataTypeInfo server: " << sizeof(ServiceServer) << std::endl; std::cout << "GetDataTypeInfo client: " << sizeof(ServiceClient) << std::endl; }