Line data Source code
1 : /********************************************************************************
2 : * Copyright (c) 2026 Accenture
3 : *
4 : * This program and the accompanying materials are made available under the
5 : * terms of the Apache License Version 2.0 which is available at
6 : * https://www.apache.org/licenses/LICENSE-2.0
7 : *
8 : * SPDX-License-Identifier: Apache-2.0
9 : ********************************************************************************/
10 :
11 : #include "someip/ServiceTracker.h"
12 :
13 : #include "bsp/timer/SystemTimer.h"
14 : #include "someip/IServiceListener.h"
15 : #include "someip/IServiceTrackerListener.h"
16 : #include "someip/ServiceQuery.h"
17 : #include "someip/SomeIpConstants.h"
18 : #include "someip/logger.h"
19 :
20 : #include <etl/algorithm.h>
21 :
22 : // Logger API uses printf-style varargs for fixed diagnostic messages in this module.
23 : // NOLINTBEGIN(cppcoreguidelines-pro-type-vararg)
24 :
25 : namespace someip
26 : {
27 : using ::ip::IPAddress;
28 : using ::util::logger::SOMEIP;
29 :
30 : // protected
31 54 : ServiceTracker::ServiceTracker(ServiceList& serviceList)
32 54 : : _services(serviceList), _pListener(nullptr), _reliabilityConfig{0, 0}
33 54 : {}
34 :
35 1 : ServiceTracker::ServiceTracker(
36 1 : ServiceList& serviceList, ServiceReliabilityConfig const& reliabilityConfig)
37 1 : : _services(serviceList), _pListener(nullptr), _reliabilityConfig(reliabilityConfig)
38 1 : {}
39 :
40 35 : void ServiceTracker::init(IServiceTrackerListener& listener)
41 : {
42 35 : _services.clear();
43 35 : _pListener = &listener;
44 35 : }
45 :
46 46 : bool ServiceTracker::addService(ServiceDescription const& service)
47 : {
48 46 : ttl::type const ttl = service.ttl;
49 46 : if ((ttl == 0U) || (ttl == ttl::INVALID))
50 : {
51 5 : WARN_LOG(SOMEIP, "ServiceTracker:addService() invalid ttl");
52 5 : return false;
53 : }
54 :
55 41 : internal::FindServiceCondition const condition(service);
56 41 : if (!condition.isValid())
57 : {
58 0 : WARN_LOG(SOMEIP, "ServiceTracker:addService() invalid condition");
59 0 : return false;
60 : }
61 :
62 : ServiceList::const_iterator const itr
63 41 : = ::etl::find_if(_services.begin(), _services.end(), condition);
64 :
65 41 : if (itr != _services.end()) // already contained
66 : {
67 9 : DEBUG_LOG(
68 : SOMEIP,
69 : "ServiceTracker::updateService(service: %d, version: %d, instance: %d, ttl: %d)",
70 : service.serviceId,
71 : service.majorVersion,
72 : service.instanceId,
73 : service.ttl);
74 :
75 9 : internal::TrackedService& entry = const_cast<internal::TrackedService&>(*itr);
76 9 : entry.serviceDescription.ttl = service.ttl;
77 :
78 9 : if ((entry.serviceDescription.ipAddress != service.ipAddress)
79 7 : || (entry.serviceDescription.port != service.port)
80 16 : || (entry.serviceDescription.proto != service.proto))
81 : {
82 4 : entry.serviceDescription = service;
83 4 : entry.lastOfferTimestamp = getSystemTimeMs32Bit();
84 4 : entry.sequentialOffersReceived = 1U;
85 :
86 4 : if (_pListener != nullptr)
87 : {
88 4 : _pListener->serviceTrackerChanged(
89 : service, IServiceTrackerListener::ServiceTrackerStatus::SERVICE_CHANGED);
90 : }
91 : }
92 : else
93 : {
94 5 : if ((_reliabilityConfig.expectedOfferPeriodMs > 0)
95 2 : && (_reliabilityConfig.offerCountThreshold > 0))
96 : {
97 2 : uint64_t const now = getSystemTimeMs32Bit();
98 2 : uint64_t const timeDiff = static_cast<uint64_t>(now - entry.lastOfferTimestamp);
99 2 : uint64_t const timeAccuracyMs = 999U;
100 2 : if (timeDiff <= (_reliabilityConfig.expectedOfferPeriodMs + timeAccuracyMs))
101 : {
102 2 : if (entry.sequentialOffersReceived < _reliabilityConfig.offerCountThreshold)
103 : {
104 2 : ++(entry.sequentialOffersReceived);
105 2 : if (entry.sequentialOffersReceived
106 2 : == _reliabilityConfig.offerCountThreshold)
107 : {
108 1 : if (_pListener != nullptr)
109 : {
110 1 : _pListener->serviceTrackerChanged(
111 : service,
112 : IServiceTrackerListener::ServiceTrackerStatus::
113 : SERVICE_RELIABLE);
114 : }
115 : else
116 : {
117 : // no listener
118 : }
119 : }
120 : else
121 : {
122 : // keep counting
123 : }
124 : }
125 : else
126 : {
127 : // already reliable
128 : }
129 : }
130 : else
131 : {
132 : // if once reliable don't go back to unreliable, that transition is done when
133 : // ttl is exceeded.
134 0 : if (entry.sequentialOffersReceived < _reliabilityConfig.offerCountThreshold)
135 : {
136 0 : entry.sequentialOffersReceived = 1U;
137 : }
138 : else
139 : {
140 : // already reliable
141 : }
142 : }
143 2 : entry.lastOfferTimestamp = now;
144 : }
145 : else
146 : {
147 : // reliable service feature is disabled
148 : }
149 : }
150 : }
151 32 : else if (_services.full()) // list full
152 : {
153 1 : WARN_LOG(SOMEIP, "ServiceTracker:addService() list full");
154 1 : return false;
155 : }
156 : else // add as new
157 : {
158 31 : INFO_LOG(
159 : SOMEIP,
160 : "ServiceTracker::addService(service: %d, version: %d, instance: %d, ttl: %d)",
161 : service.serviceId,
162 : service.majorVersion,
163 : service.instanceId,
164 : service.ttl);
165 :
166 31 : (void)_services.insert({service, 1U, getSystemTimeMs32Bit()});
167 :
168 31 : if (_pListener != nullptr)
169 : {
170 30 : _pListener->serviceTrackerChanged(
171 : service, IServiceTrackerListener::ServiceTrackerStatus::SERVICE_ADDED);
172 : }
173 : }
174 :
175 40 : return true;
176 : }
177 :
178 13 : void ServiceTracker::removeService(ServiceDescription const& service)
179 : {
180 13 : internal::FindServiceCondition const condition(service);
181 13 : if (!condition.isValid())
182 : {
183 0 : WARN_LOG(SOMEIP, "ServiceTracker:removeService() invalid condition");
184 0 : return;
185 : }
186 :
187 13 : ServiceList::size_type const before = _services.size();
188 :
189 13 : auto it = _services.begin();
190 30 : while (it != _services.end())
191 : {
192 17 : if (condition(*it))
193 : {
194 9 : it = _services.erase(it);
195 : }
196 : else
197 : {
198 8 : ++it;
199 : }
200 : }
201 :
202 13 : if (_services.size() < before)
203 : {
204 9 : INFO_LOG(
205 : SOMEIP,
206 : "ServiceTracker::removeService(service: %d, version: %d, instance: %d)",
207 : service.serviceId,
208 : service.majorVersion,
209 : service.instanceId);
210 :
211 9 : if (_pListener != nullptr)
212 : {
213 8 : _pListener->serviceTrackerChanged(
214 : service, IServiceTrackerListener::ServiceTrackerStatus::SERVICE_REMOVED);
215 : }
216 : }
217 : }
218 :
219 1 : void ServiceTracker::stop() { _services.clear(); }
220 :
221 10 : bool ServiceTracker::getService(ServiceDescription& service) const
222 : {
223 10 : internal::FindServiceCondition const condition(service);
224 10 : if (!condition.isValid())
225 : {
226 0 : WARN_LOG(SOMEIP, "ServiceTracker:getService() invalid condition");
227 0 : return false;
228 : }
229 :
230 : ServiceList::const_iterator const itr
231 10 : = ::etl::find_if(_services.begin(), _services.end(), condition);
232 :
233 10 : if (itr != _services.end())
234 : {
235 6 : service = itr->serviceDescription;
236 6 : return true;
237 : }
238 :
239 4 : return false;
240 : }
241 :
242 10 : instance_id::type ServiceTracker::getInstanceId(ServiceDescription const& service) const
243 : {
244 10 : internal::FindInstanceCondition const condition(service);
245 10 : if (!condition.isValid())
246 : {
247 0 : WARN_LOG(SOMEIP, "ServiceTracker:getInstanceId() invalid condition");
248 0 : return instance_id::ANY;
249 : }
250 :
251 : ServiceList::const_iterator const itr
252 10 : = ::etl::find_if(_services.begin(), _services.end(), condition);
253 :
254 10 : if (itr == _services.end())
255 : {
256 5 : return instance_id::ANY;
257 : }
258 :
259 5 : return itr->serviceDescription.instanceId;
260 : }
261 :
262 3 : void ServiceTracker::rebootDetected(IPAddress const& ipAddr)
263 : {
264 3 : internal::RemoveIpCondition const condition(ipAddr, _pListener);
265 3 : auto it = _services.begin();
266 10 : while (it != _services.end())
267 : {
268 7 : if (condition(*it))
269 : {
270 5 : it = _services.erase(it);
271 : }
272 : else
273 : {
274 2 : ++it;
275 : }
276 : }
277 3 : }
278 :
279 4 : void ServiceTracker::updateTTLs(uint32_t const ticks)
280 : {
281 4 : internal::UpdateTTLCondition const condition(ticks, _pListener);
282 4 : auto it = _services.begin();
283 15 : while (it != _services.end())
284 : {
285 11 : if (condition(*it))
286 : {
287 3 : it = _services.erase(it);
288 : }
289 : else
290 : {
291 8 : ++it;
292 : }
293 : }
294 4 : }
295 :
296 10 : void ServiceTracker::notifyServices(ServiceQuery& query) const
297 : {
298 10 : if (query.listener == nullptr)
299 : {
300 1 : return;
301 : }
302 :
303 : internal::FindServiceCondition const condition(
304 9 : query.description, (query.description.instanceId == instance_id::ANY));
305 9 : if (!condition.isValid())
306 : {
307 0 : WARN_LOG(SOMEIP, "ServiceTracker:notifyServices() invalid condition");
308 0 : return;
309 : }
310 :
311 9 : ServiceList::const_iterator itr = ::etl::find_if(_services.begin(), _services.end(), condition);
312 :
313 13 : while ((itr != _services.end()) && (condition(*itr)))
314 : {
315 8 : query.listener->serviceStatusChanged(
316 4 : itr->serviceDescription, IServiceListener::ServiceStatus::SERVICE_AVAILABLE);
317 4 : ++itr;
318 : }
319 : }
320 :
321 5 : uint16_t ServiceTracker::getCurrentNumberOfServices() const
322 : {
323 5 : return static_cast<uint16_t>(_services.size());
324 : }
325 :
326 2 : uint16_t ServiceTracker::getMaximumNumberOfServices() const
327 : {
328 2 : return static_cast<uint16_t>(_services.max_size());
329 : }
330 :
331 : namespace internal
332 : {
333 24 : bool LessThanComparator::operator()(TrackedService const& lhs, TrackedService const& rhs) const
334 : {
335 : return (
336 24 : (lhs.serviceDescription.serviceId < rhs.serviceDescription.serviceId)
337 18 : || (lhs.serviceDescription.majorVersion < rhs.serviceDescription.majorVersion)
338 42 : || (lhs.serviceDescription.instanceId < rhs.serviceDescription.instanceId));
339 : }
340 :
341 73 : FindServiceCondition::FindServiceCondition(
342 73 : ServiceDescription const& service, bool const instanceAny)
343 73 : : _service(service), _instanceAny(instanceAny)
344 73 : {}
345 :
346 73 : bool FindServiceCondition::isValid() const
347 : {
348 : return (
349 73 : (_service.serviceId != service_id::INVALID)
350 73 : && (_service.majorVersion != major_version::INVALID)
351 146 : && (_instanceAny || (_service.instanceId != instance_id::ANY)));
352 : }
353 :
354 82 : bool FindServiceCondition::operator()(TrackedService const& service) const
355 : {
356 : return (
357 82 : (_service.serviceId == service.serviceDescription.serviceId)
358 47 : && (_service.majorVersion == service.serviceDescription.majorVersion)
359 129 : && (_instanceAny || (_service.instanceId == service.serviceDescription.instanceId)));
360 : }
361 :
362 10 : FindInstanceCondition::FindInstanceCondition(ServiceDescription const& service) : _service(service)
363 10 : {}
364 :
365 10 : bool FindInstanceCondition::isValid() const
366 : {
367 : return (
368 10 : (_service.serviceId != service_id::INVALID)
369 10 : && (_service.majorVersion != major_version::INVALID)
370 20 : && (!::ip::isUnspecified(_service.ipAddress)) && (_service.port != port::INVALID));
371 : }
372 :
373 12 : bool FindInstanceCondition::operator()(TrackedService const& service) const
374 : {
375 : return (
376 12 : (_service.serviceId == service.serviceDescription.serviceId)
377 12 : && (_service.majorVersion == service.serviceDescription.majorVersion)
378 24 : && (_service.ipAddress == service.serviceDescription.ipAddress));
379 : }
380 :
381 3 : RemoveIpCondition::RemoveIpCondition(
382 3 : IPAddress const& ipAddr, IServiceTrackerListener* const listener)
383 3 : : _ip(ipAddr), _pListener(listener)
384 3 : {}
385 :
386 7 : bool RemoveIpCondition::operator()(TrackedService const& service) const
387 : {
388 7 : if (service.serviceDescription.ipAddress != _ip)
389 : {
390 2 : return false;
391 : }
392 :
393 5 : if (_pListener != nullptr)
394 : {
395 5 : ServiceDescription temp(service.serviceDescription);
396 5 : temp.ttl = 0U;
397 :
398 5 : _pListener->serviceTrackerChanged(
399 : temp, IServiceTrackerListener::ServiceTrackerStatus::SERVICE_REMOVED);
400 : }
401 :
402 5 : return true;
403 : }
404 :
405 4 : UpdateTTLCondition::UpdateTTLCondition(
406 4 : uint32_t const ticks, IServiceTrackerListener* const listener)
407 4 : : _ticks(ticks), _pListener(listener)
408 4 : {}
409 :
410 11 : bool UpdateTTLCondition::operator()(TrackedService const& service) const
411 : {
412 11 : uint32_t const ticks = service.serviceDescription.ttl;
413 11 : if (ticks >= _ticks)
414 : {
415 8 : const_cast<ServiceDescription&>(service.serviceDescription).ttl = ticks - _ticks;
416 8 : return false;
417 : }
418 :
419 3 : INFO_LOG(
420 : SOMEIP,
421 : "ServiceTracker::serviceExpired(service: %d, version: %d, instance: %d)",
422 : service.serviceDescription.serviceId,
423 : service.serviceDescription.majorVersion,
424 : service.serviceDescription.instanceId);
425 :
426 3 : ServiceDescription temp(service.serviceDescription);
427 3 : temp.ttl = 0U;
428 :
429 3 : if (_pListener != nullptr)
430 : {
431 3 : _pListener->serviceTrackerChanged(
432 : temp, IServiceTrackerListener::ServiceTrackerStatus::SERVICE_REMOVED);
433 : }
434 :
435 3 : return true;
436 : }
437 :
438 : } // namespace internal
439 : } // namespace someip
440 :
441 : // NOLINTEND(cppcoreguidelines-pro-type-vararg)
|