1919import java .util .Iterator ;
2020import java .util .List ;
2121import java .util .Map ;
22+ import java .util .Objects ;
2223import java .util .function .Predicate ;
2324
2425import org .apache .commons .lang3 .StringUtils ;
3738import org .eclipse .tracecompass .incubator .internal .ros2 .core .model .messages .Ros2SubCallbackInstance ;
3839import org .eclipse .tracecompass .incubator .internal .ros2 .core .model .messages .Ros2TakeInstance ;
3940import org .eclipse .tracecompass .incubator .internal .ros2 .core .model .messages .Ros2TimerCallbackInstance ;
41+ import org .eclipse .tracecompass .incubator .internal .ros2 .core .model .objects .Ros2ClientObject ;
4042import org .eclipse .tracecompass .incubator .internal .ros2 .core .model .objects .Ros2NodeObject ;
4143import org .eclipse .tracecompass .incubator .internal .ros2 .core .model .objects .Ros2ObjectHandle ;
4244import org .eclipse .tracecompass .incubator .internal .ros2 .core .model .objects .Ros2PublisherObject ;
45+ import org .eclipse .tracecompass .incubator .internal .ros2 .core .model .objects .Ros2ServiceObject ;
4346import org .eclipse .tracecompass .incubator .internal .ros2 .core .model .objects .Ros2SubscriptionObject ;
4447import org .eclipse .tracecompass .incubator .internal .ros2 .core .model .objects .Ros2TimerObject ;
4548import org .eclipse .tracecompass .internal .tmf .core .model .filters .FetchParametersUtils ;
@@ -108,7 +111,7 @@ public Ros2MessagesDataProvider(@NonNull ITmfTrace trace, @NonNull Ros2MessagesA
108111 * @author Christophe Bedard
109112 */
110113 public enum ArrowType {
111- /** Transport link (pub->sub over network) */
114+ /** Transport link (pub->sub or request/response over network) */
112115 TRANSPORT (1 ),
113116 /** Callback-publication link */
114117 CALLBACK_PUB (2 ),
@@ -206,7 +209,7 @@ public int getId() {
206209 if (monitor != null && monitor .isCanceled ()) {
207210 return new TimeGraphModel (Collections .emptyList ());
208211 }
209- addRows (rows , entry , intervals , predicates , monitor );
212+ addRows (ss , rows , entry , intervals , predicates , monitor );
210213 }
211214 return new TimeGraphModel (rows );
212215 }
@@ -236,12 +239,39 @@ private static void queryIntervals(@NonNull ITmfStateSystem ss, TreeMultimap<Int
236239 return predicates ;
237240 }
238241
239- private void addRows (List <@ NonNull ITimeGraphRowModel > rows , Map .Entry <@ NonNull Long , @ NonNull Integer > entry , TreeMultimap <Integer , ITmfStateInterval > intervals ,
240- @ NonNull Map <@ NonNull Integer , @ NonNull Predicate <@ NonNull Multimap <@ NonNull String , @ NonNull Object >>> predicates , @ Nullable IProgressMonitor monitor ) {
242+ private void addRows (@ NonNull ITmfStateSystem ss , List <@ NonNull ITimeGraphRowModel > rows , Map .Entry <@ NonNull Long , @ NonNull Integer > entry , TreeMultimap <Integer , ITmfStateInterval > intervals ,
243+ @ NonNull Map <@ NonNull Integer , @ NonNull Predicate <@ NonNull Multimap <@ NonNull String , @ NonNull Object >>> predicates , @ Nullable IProgressMonitor monitor ) throws StateSystemDisposedException {
241244 List <@ NonNull ITimeGraphState > eventList = new ArrayList <>();
242245 for (ITmfStateInterval interval : intervals .get (entry .getValue ())) {
243246 addRow (entry , predicates , monitor , eventList , interval );
244247 }
248+
249+ /**
250+ * State system intervals for clients & services are stored under two
251+ * attributes (send & take). However, an entry only corresponds to one
252+ * attribute. Since we create entry models for clients & services using
253+ * the "take" attribute, we need to do a simple workaround here to also
254+ * create time graph states for state system intervals under the "send"
255+ * attribute in the same client/service entry model.
256+ */
257+ int quark = Objects .requireNonNull (entry .getValue ());
258+ String name = ss .getAttributeName (quark );
259+ int parentQuark = ss .getParentAttributeQuark (quark );
260+ int grandParentQuark = ss .getParentAttributeQuark (parentQuark );
261+ String grandParentName = grandParentQuark != ITmfStateSystem .ROOT_ATTRIBUTE ? ss .getAttributeName (grandParentQuark ) : StringUtils .EMPTY ;
262+ // If this is an entry for a "take" attribute
263+ if (name .equals (Ros2MessagesUtil .ClientServiceInstanceType .TAKE .toString ()) &&
264+ (grandParentName .equals (Ros2MessagesUtil .LIST_CLIENTS ) || grandParentName .equals (Ros2MessagesUtil .LIST_SERVICES ))) {
265+ // Find quark for "send" attribute
266+ int clientSendQuark = ss .optQuarkRelative (parentQuark , Ros2MessagesUtil .ClientServiceInstanceType .SEND .toString ());
267+ if (ITmfStateSystem .INVALID_ATTRIBUTE != clientSendQuark ) {
268+ // Create time graph states under the same ("take") entry model
269+ for (ITmfStateInterval interval : ss .query2D (Collections .singleton (clientSendQuark ), ss .getStartTime (), ss .getCurrentEndTime ())) {
270+ addRow (entry , predicates , monitor , eventList , interval );
271+ }
272+ }
273+ }
274+
245275 rows .add (new TimeGraphRowModel (entry .getKey (), eventList ));
246276 }
247277
@@ -264,7 +294,7 @@ private void addRow(Map.Entry<@NonNull Long, @NonNull Integer> entry, @NonNull M
264294
265295 fHandleToIdMap .put (pubInstance .getPublisherHandle (), entry .getKey ());
266296 } else if (valObject instanceof Ros2SubCallbackInstance ) {
267- // Subscription callback
297+ // Subscription callback or service request callback
268298 Ros2SubCallbackInstance subCallbackInstance = (Ros2SubCallbackInstance ) valObject ;
269299
270300 Ros2TakeInstance takeInstance = subCallbackInstance .getTakeInstance ();
@@ -275,6 +305,13 @@ private void addRow(Map.Entry<@NonNull Long, @NonNull Integer> entry, @NonNull M
275305 Ros2CallbackTimeGraphState callbackState = new Ros2CallbackTimeGraphState (callbackInstance );
276306 applyFilterAndAddState (eventList , callbackState , entry .getKey (), predicates , monitor );
277307
308+ fHandleToIdMap .put (takeInstance .getSubscriptionHandle (), entry .getKey ());
309+ } else if (valObject instanceof Ros2TakeInstance ) {
310+ // Client response take
311+ Ros2TakeInstance takeInstance = (Ros2TakeInstance ) valObject ;
312+ Ros2TakeTimeGraphState takeState = new Ros2TakeTimeGraphState (takeInstance );
313+ applyFilterAndAddState (eventList , takeState , entry .getKey (), predicates , monitor );
314+
278315 fHandleToIdMap .put (takeInstance .getSubscriptionHandle (), entry .getKey ());
279316 } else if (valObject instanceof Ros2TimerCallbackInstance ) {
280317 // Timer callback
@@ -311,6 +348,8 @@ private void addChildrenEntryModel(ITmfStateSystem ss, Builder<@NonNull TimeGrap
311348 long childId = getId (child );
312349 String name = ss .getAttributeName (child );
313350 String parentName = quark != ITmfStateSystem .ROOT_ATTRIBUTE ? ss .getAttributeName (quark ) : StringUtils .EMPTY ;
351+ int grandParentQuark = ss .getParentAttributeQuark (quark );
352+ String grandParentName = grandParentQuark != ITmfStateSystem .ROOT_ATTRIBUTE ? ss .getAttributeName (grandParentQuark ) : StringUtils .EMPTY ;
314353 if (ITmfStateSystem .ROOT_ATTRIBUTE == quark ) {
315354 if (addEntryModel (ss , builder , childId , parentId , child , Ros2ObjectTimeGraphEntryModelType .TRACE )) {
316355 addChildren (ss , builder , child , childId );
@@ -323,9 +362,19 @@ private void addChildrenEntryModel(ITmfStateSystem ss, Builder<@NonNull TimeGrap
323362 addEntryModel (ss , builder , childId , parentId , child , Ros2ObjectTimeGraphEntryModelType .PUBLISHER );
324363 } else if (parentName .equals (Ros2MessagesUtil .LIST_SUBSCRIPTIONS )) {
325364 addEntryModel (ss , builder , childId , parentId , child , Ros2ObjectTimeGraphEntryModelType .SUBSCRIPTION );
365+ } else if (name .equals (Ros2MessagesUtil .ClientServiceInstanceType .TAKE .toString ())) {
366+ // Only use the "take" attribute as the entry model
367+ if (grandParentName .equals (Ros2MessagesUtil .LIST_CLIENTS )) {
368+ addEntryModel (ss , builder , childId , parentId , child , Ros2ObjectTimeGraphEntryModelType .CLIENT );
369+ } else if (grandParentName .equals (Ros2MessagesUtil .LIST_SERVICES )) {
370+ addEntryModel (ss , builder , childId , parentId , child , Ros2ObjectTimeGraphEntryModelType .SERVICE );
371+ }
326372 } else if (parentName .equals (Ros2MessagesUtil .LIST_TIMERS )) {
327373 addEntryModel (ss , builder , childId , parentId , child , Ros2ObjectTimeGraphEntryModelType .TIMER );
328- } else if (name .equals (Ros2MessagesUtil .LIST_NODES ) || name .equals (Ros2MessagesUtil .LIST_PUBLISHERS ) || name .equals (Ros2MessagesUtil .LIST_SUBSCRIPTIONS ) || name .equals (Ros2MessagesUtil .LIST_TIMERS )) {
374+ } else if (name .equals (Ros2MessagesUtil .LIST_NODES ) || name .equals (Ros2MessagesUtil .LIST_PUBLISHERS ) || name .equals (Ros2MessagesUtil .LIST_SUBSCRIPTIONS ) ||
375+ name .equals (Ros2MessagesUtil .LIST_CLIENTS ) || parentName .equals (Ros2MessagesUtil .LIST_CLIENTS ) ||
376+ name .equals (Ros2MessagesUtil .LIST_SERVICES ) || parentName .equals (Ros2MessagesUtil .LIST_SERVICES ) ||
377+ name .equals (Ros2MessagesUtil .ClientServiceInstanceType .SEND .toString ()) || name .equals (Ros2MessagesUtil .LIST_TIMERS )) {
329378 /**
330379 * Skip this attribute: don't add an entry model, but do proceed
331380 * with children, effectively skipping a layer in the state system
@@ -372,6 +421,22 @@ private boolean addEntryModel(ITmfStateSystem ss, Builder<@NonNull TimeGraphEntr
372421 return true ;
373422 }
374423 break ;
424+ case CLIENT :
425+ @ Nullable
426+ Ros2ClientObject clientObject = getClientObject (ss , quark );
427+ if (null != clientObject ) {
428+ builder .add (new Ros2ObjectTimeGraphEntryModel (id , parentId , ss .getStartTime (), ss .getCurrentEndTime (), Ros2ObjectTimeGraphEntryModelType .CLIENT , clientObject ));
429+ return true ;
430+ }
431+ break ;
432+ case SERVICE :
433+ @ Nullable
434+ Ros2ServiceObject serviceObject = getServiceObject (ss , quark );
435+ if (null != serviceObject ) {
436+ builder .add (new Ros2ObjectTimeGraphEntryModel (id , parentId , ss .getStartTime (), ss .getCurrentEndTime (), Ros2ObjectTimeGraphEntryModelType .SERVICE , serviceObject ));
437+ return true ;
438+ }
439+ break ;
375440 case TIMER :
376441 @ Nullable
377442 Ros2TimerObject timerObject = getTimerObject (ss , quark );
@@ -459,6 +524,46 @@ private boolean addEntryModel(ITmfStateSystem ss, Builder<@NonNull TimeGraphEntr
459524 return null ;
460525 }
461526
527+ private @ Nullable Ros2ClientObject getClientObject (ITmfStateSystem ss , int quark ) {
528+ try {
529+ // Get client handle from a time graph state
530+ Iterable <@ NonNull ITmfStateInterval > query2d = ss .query2D (Collections .singleton (quark ), ss .getStartTime (), ss .getCurrentEndTime ());
531+ for (ITmfStateInterval iTmfStateInterval : query2d ) {
532+ if (iTmfStateInterval .getValue () instanceof Ros2TakeInstance ) {
533+ @ Nullable
534+ Ros2TakeInstance responseTakeInstance = (Ros2TakeInstance ) iTmfStateInterval .getValue ();
535+ if (null != responseTakeInstance ) {
536+ return Ros2ObjectsUtil .getClientObjectFromHandle (fObjectsSs , ss .getCurrentEndTime (), responseTakeInstance .getSubscriptionHandle ());
537+ }
538+ }
539+ }
540+ } catch (IndexOutOfBoundsException | TimeRangeException | StateSystemDisposedException e ) {
541+ // Do nothing
542+ }
543+ Activator .getInstance ().logError ("could not get client object for entry model" ); //$NON-NLS-1$
544+ return null ;
545+ }
546+
547+ private @ Nullable Ros2ServiceObject getServiceObject (ITmfStateSystem ss , int quark ) {
548+ try {
549+ // Get service handle from a time graph state
550+ Iterable <@ NonNull ITmfStateInterval > query2d = ss .query2D (Collections .singleton (quark ), ss .getStartTime (), ss .getCurrentEndTime ());
551+ for (ITmfStateInterval iTmfStateInterval : query2d ) {
552+ if (iTmfStateInterval .getValue () instanceof Ros2SubCallbackInstance ) {
553+ @ Nullable
554+ Ros2SubCallbackInstance subCallbackInstance = (Ros2SubCallbackInstance ) iTmfStateInterval .getValue ();
555+ if (null != subCallbackInstance ) {
556+ return Ros2ObjectsUtil .getServiceObjectFromHandle (fObjectsSs , ss .getCurrentEndTime (), subCallbackInstance .getTakeInstance ().getSubscriptionHandle ());
557+ }
558+ }
559+ }
560+ } catch (IndexOutOfBoundsException | TimeRangeException | StateSystemDisposedException e ) {
561+ // Do nothing
562+ }
563+ Activator .getInstance ().logError ("could not get service object for entry model" ); //$NON-NLS-1$
564+ return null ;
565+ }
566+
462567 private @ Nullable Ros2TimerObject getTimerObject (ITmfStateSystem ss , int quark ) {
463568 try {
464569 // Get timer handle from a time graph state
0 commit comments