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