1 // Copyright (C) 2015-2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
2 // This Source Code Form is subject to the terms of the Mozilla Public
3 // License, v. 2.0. If a copy of the MPL was not distributed with this
4 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 
6 #include "../npdu_tests/npdu_test_service.hpp"
7 #include "../npdu_tests/npdu_test_globals.hpp"
8 
9 #include <vsomeip/internal/logger.hpp>
10 #include "../../implementation/configuration/include/configuration.hpp"
11 #include "../../implementation/configuration/include/configuration_impl.hpp"
12 #include "../../implementation/configuration/include/configuration_plugin.hpp"
13 #include "../../implementation/plugin/include/plugin_manager_impl.hpp"
14 
15 
16 
17 // this variable is set during compile time to create 4 service binaries of
18 // which each of them offers a service.
19 // Based on this number the service id, instance id and method ids are
20 // selected from the arrays defined in npdu_test_globals.hpp
21 #ifndef SERVICE_NUMBER
22 #define SERVICE_NUMBER 0
23 #endif
24 
npdu_test_service(vsomeip::service_t _service_id,vsomeip::instance_t _instance_id,std::array<vsomeip::method_t,4> _method_ids,std::array<std::chrono::nanoseconds,4> _debounce_times,std::array<std::chrono::nanoseconds,4> _max_retention_times)25 npdu_test_service::npdu_test_service(vsomeip::service_t _service_id,
26                                                  vsomeip::instance_t _instance_id,
27                                                  std::array<vsomeip::method_t, 4> _method_ids,
28                                                  std::array<std::chrono::nanoseconds, 4> _debounce_times,
29                                                  std::array<std::chrono::nanoseconds, 4> _max_retention_times) :
30                 app_(vsomeip::runtime::get()->create_application()),
31                 is_registered_(false),
32                 method_ids_(_method_ids),
33                 debounce_times_(_debounce_times),
34                 max_retention_times_(_max_retention_times),
35                 service_id_(_service_id),
36                 instance_id_(_instance_id),
37                 blocked_(false),
38                 allowed_to_shutdown_(false),
39                 number_of_received_messages_(0),
40                 offer_thread_(std::bind(&npdu_test_service::run, this)),
41                 shutdown_thread_(std::bind(&npdu_test_service::stop, this))
42 {
43     // init timepoints of last received message to one hour before now.
44     // needed that the first message which arrives isn't registered as undershot
45     // debounce time
46     for(auto &tp : timepoint_last_received_message_) {
47         tp = std::chrono::steady_clock::now() - std::chrono::hours(1);
48     }
49 }
50 
init()51 void npdu_test_service::init()
52 {
53     std::lock_guard<std::mutex> its_lock(mutex_);
54 
55     app_->init();
56 
57     register_message_handler<0>();
58     register_message_handler<1>();
59     register_message_handler<2>();
60     register_message_handler<3>();
61 
62     app_->register_message_handler(service_id_, instance_id_,
63             npdu_test::NPDU_SERVICE_SHUTDOWNMETHOD_ID,
64             std::bind(&npdu_test_service::on_message_shutdown, this,
65                     std::placeholders::_1));
66 
67     app_->register_state_handler(
68             std::bind(&npdu_test_service::on_state, this,
69                     std::placeholders::_1));
70 }
71 
72 template <int method_idx>
register_message_handler()73 void npdu_test_service::register_message_handler() {
74     app_->register_message_handler(service_id_, instance_id_, method_ids_[method_idx],
75             std::bind(&npdu_test_service::on_message<method_idx>, this,
76                     std::placeholders::_1));
77 }
78 
start()79 void npdu_test_service::start()
80 {
81     VSOMEIP_INFO << "Starting...";
82     app_->start();
83 }
84 
stop()85 void npdu_test_service::stop()
86 {
87     std::unique_lock<std::mutex> its_lock(shutdown_mutex_);
88     while (!allowed_to_shutdown_) {
89         shutdown_condition_.wait(its_lock);
90     }
91 
92     VSOMEIP_INFO << "Stopping...";
93     if (!undershot_debounce_times_.empty()) {
94         std::chrono::microseconds sum(0);
95         for (const auto& t : undershot_debounce_times_) {
96             sum += t;
97         }
98         double average = static_cast<double>(sum.count())/static_cast<double>(undershot_debounce_times_.size());
99         VSOMEIP_INFO << "["
100                 << std::setw(4) << std::setfill('0') << std::hex << service_id_ << "."
101                 << std::setw(4) << std::setfill('0') << std::hex << instance_id_ << "]: "
102                 << " Debounce time was undershot " << std::dec << undershot_debounce_times_.size() << "/" << number_of_received_messages_
103                 << "(" << std::setprecision(2) << (static_cast<double>(undershot_debounce_times_.size()) / static_cast<double>(number_of_received_messages_)) * 100.00
104                 << "%) on average: " << std::setprecision(4) << average << "µs";
105     }
106     app_->unregister_message_handler(service_id_, instance_id_, method_ids_[0]);
107     app_->unregister_message_handler(service_id_, instance_id_, method_ids_[1]);
108     app_->unregister_message_handler(service_id_, instance_id_, method_ids_[2]);
109     app_->unregister_message_handler(service_id_, instance_id_, method_ids_[3]);
110     app_->unregister_message_handler(service_id_,
111             instance_id_, npdu_test::NPDU_SERVICE_SHUTDOWNMETHOD_ID);
112     app_->unregister_state_handler();
113     offer_thread_.join();
114     stop_offer();
115     app_->stop();
116 }
117 
offer()118 void npdu_test_service::offer()
119 {
120     app_->offer_service(service_id_, instance_id_);
121 }
122 
stop_offer()123 void npdu_test_service::stop_offer()
124 {
125     app_->stop_offer_service(service_id_, instance_id_);
126 }
127 
join_shutdown_thread()128 void npdu_test_service::join_shutdown_thread() {
129     shutdown_thread_.join();
130 }
131 
on_state(vsomeip::state_type_e _state)132 void npdu_test_service::on_state(vsomeip::state_type_e _state)
133 {
134     VSOMEIP_INFO << "Application " << app_->get_name() << " is "
135             << (_state == vsomeip::state_type_e::ST_REGISTERED ? "registered." :
136                     "deregistered.");
137 
138     if(_state == vsomeip::state_type_e::ST_REGISTERED)
139     {
140         if(!is_registered_)
141         {
142             std::lock_guard<std::mutex> its_lock(mutex_);
143             is_registered_ = true;
144             blocked_ = true;
145             // "start" the run method thread
146             condition_.notify_one();
147         }
148     }
149     else
150     {
151         is_registered_ = false;
152     }
153 }
154 
155 template<int method_idx>
check_times()156 void npdu_test_service::check_times() {
157     std::lock_guard<std::mutex> its_lock(timepoint_mutexes_[method_idx]);
158     // what time is it?
159     std::chrono::steady_clock::time_point now =
160             std::chrono::steady_clock::now();
161     // how long is it since we received the last message?
162     std::chrono::nanoseconds time_since_last_message =
163             std::chrono::duration_cast<std::chrono::nanoseconds>(
164                     now - timepoint_last_received_message_[method_idx]);
165     // store the current time
166     timepoint_last_received_message_[method_idx] = now;
167 
168     // check if the debounce time was undershot
169     if (time_since_last_message < debounce_times_[method_idx]) {
170         const auto time_undershot = std::chrono::duration_cast<
171                 std::chrono::microseconds>(debounce_times_[method_idx] - time_since_last_message);
172         undershot_debounce_times_.push_back(time_undershot);
173     }
174     // check if maximum retention time was exceeded
175     // Disabled as it can't be guaranteed that exact every max retention time a
176     // message leaves the client endpoint.
177 #if 0
178     if(time_since_last_message > max_retention_times_[method_idx]) {
179         VSOMEIP_ERROR << std::setw(4) << std::setfill('0') << std::hex
180                 << service_id_ << ":" << std::setw(4) << std::setfill('0')
181                 << std::hex << instance_id_ << ":" << std::setw(4) << std::setfill('0')
182                 << std::hex << npdu_test::method_ids[SERVICE_NUMBER][method_idx]
183                 << ": max_retention_time exceeded by: " << std::dec
184                 << std::chrono::duration_cast<std::chrono::milliseconds>(
185                         time_since_last_message - max_retention_times_[method_idx]).count()
186                 << "ms";
187         GTEST_FATAL_FAILURE_("Max retention time was exceeded");
188     }
189 #endif
190 }
191 
192 template<int method_idx>
on_message(const std::shared_ptr<vsomeip::message> & _request)193 void npdu_test_service::on_message(const std::shared_ptr<vsomeip::message>& _request)
194 {
195     number_of_received_messages_++;
196     check_times<method_idx>();
197     VSOMEIP_DEBUG << __func__ << " 0x" << std::setw(4) << std::setfill('0') << std::hex
198             << method_ids_[method_idx] << " payload size: "
199             << std::dec << _request->get_payload()->get_length();
200     if(_request->get_message_type() != vsomeip::message_type_e::MT_REQUEST_NO_RETURN) {
201         std::shared_ptr<vsomeip::message> its_response =
202                 vsomeip::runtime::get()->create_response(_request);
203         app_->send(its_response);
204     }
205 }
206 
on_message_shutdown(const std::shared_ptr<vsomeip::message> & _request)207 void npdu_test_service::on_message_shutdown(
208         const std::shared_ptr<vsomeip::message>& _request)
209 {
210     (void)_request;
211     VSOMEIP_DEBUG << "Number of received messages: " << number_of_received_messages_;
212     VSOMEIP_INFO << "Shutdown method was called, going down now.";
213 
214     std::lock_guard<std::mutex> its_lock(shutdown_mutex_);
215     allowed_to_shutdown_ = true;
216     shutdown_condition_.notify_one();
217 }
218 
run()219 void npdu_test_service::run()
220 {
221     std::unique_lock<std::mutex> its_lock(mutex_);
222     while (!blocked_)
223         condition_.wait(its_lock);
224 
225     offer();
226 }
227 
TEST(someip_npdu_test,offer_service_and_check_debounce_times)228 TEST(someip_npdu_test, offer_service_and_check_debounce_times)
229 {
230     // get the configuration
231     std::shared_ptr<vsomeip::configuration> its_configuration;
232     auto its_plugin = vsomeip::plugin_manager::get()->get_plugin(
233             vsomeip::plugin_type_e::CONFIGURATION_PLUGIN, VSOMEIP_CFG_LIBRARY);
234     if (its_plugin) {
235         auto its_config_plugin = std::dynamic_pointer_cast<vsomeip::configuration_plugin>(its_plugin);
236         if (its_config_plugin) {
237             its_configuration = its_config_plugin->get_configuration("");
238         }
239     }
240     if (!its_configuration) {
241         ADD_FAILURE() << "No configuration object. "
242                 "Either memory overflow or loading error detected!";
243         return;
244     }
245 
246     // used to store the debounce times
247     std::array<std::chrono::nanoseconds, 4> debounce_times;
248     std::array<std::chrono::nanoseconds, 4> max_retention_times;
249 
250 
251     // query the debouncetimes from the configuration. We want to know the
252     // debounce times which the _clients_ of this service have to comply with
253     // when they send requests to this service. This is necessary as we want to
254     // check on the service side if they adhere to them.
255     // client one will only query method one, client two will only query method
256     // two and so on.
257     for(int i = 0; i < 4; i++) {
258         std::chrono::nanoseconds debounce(0), retention(0);
259         its_configuration->get_configured_timing_requests(
260                         npdu_test::service_ids[SERVICE_NUMBER],
261                         its_configuration->get_unicast_address().to_string(),
262                         its_configuration->get_unreliable_port(
263                                 npdu_test::service_ids[SERVICE_NUMBER],
264                                 npdu_test::instance_ids[SERVICE_NUMBER]),
265                         npdu_test::method_ids[SERVICE_NUMBER][i],
266                         &debounce_times[i],
267                         &max_retention_times[i]);
268         if (debounce == std::chrono::nanoseconds(VSOMEIP_DEFAULT_NPDU_DEBOUNCING_NANO) &&
269             retention == std::chrono::nanoseconds(VSOMEIP_DEFAULT_NPDU_MAXIMUM_RETENTION_NANO)) {
270             // no timings specified - checks in check_times() should never
271             // report an error in this case.
272             // set debounce time to 0 this can't be undershot
273             debounce_times[i] = std::chrono::nanoseconds(0);
274             // set max retention time its max, this won't be exceeded
275             max_retention_times[i] = std::chrono::nanoseconds::max();
276         }
277     }
278 
279     npdu_test_service test_service(
280             npdu_test::service_ids[SERVICE_NUMBER],
281             npdu_test::instance_ids[SERVICE_NUMBER],
282             npdu_test::method_ids[SERVICE_NUMBER],
283             debounce_times, max_retention_times);
284     test_service.init();
285     test_service.start();
286     test_service.join_shutdown_thread();
287 }
288 
289 #ifndef _WIN32
main(int argc,char ** argv)290 int main(int argc, char** argv)
291 {
292     int i = 1;
293     while (i < argc)
294     {
295         if(std::string("--help") == argv[i])
296         {
297             VSOMEIP_INFO << "Parameters:\n"
298                     << "--help: print this help";
299         }
300         i++;
301     }
302 
303     ::testing::InitGoogleTest(&argc, argv);
304     return RUN_ALL_TESTS();
305 }
306 #endif
307