Commit | Line | Data |
---|---|---|
c1ed0a0c | 1 | /******************************************************************************* |
aa67df36 | 2 | * Copyright (c) 2017 Ericsson |
c1ed0a0c PT |
3 | * |
4 | * All rights reserved. This program and the accompanying materials are | |
5 | * made available under the terms of the Eclipse Public License v1.0 which | |
6 | * accompanies this distribution, and is available at | |
7 | * http://www.eclipse.org/legal/epl-v10.html | |
8 | * | |
9 | * Contributors: | |
10 | * Patrick Tasse - Initial API and implementation | |
11 | *******************************************************************************/ | |
12 | ||
13 | package org.eclipse.tracecompass.internal.tmf.ui.markers; | |
14 | ||
15 | import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; | |
16 | ||
17 | import java.util.ArrayList; | |
18 | import java.util.Comparator; | |
19 | import java.util.HashMap; | |
20 | import java.util.LinkedHashSet; | |
21 | import java.util.List; | |
22 | import java.util.Map; | |
23 | import java.util.Set; | |
24 | import java.util.stream.Collectors; | |
25 | ||
aa67df36 | 26 | import org.eclipse.core.runtime.IAdaptable; |
c1ed0a0c PT |
27 | import org.eclipse.core.runtime.IProgressMonitor; |
28 | import org.eclipse.jdt.annotation.NonNull; | |
aa67df36 | 29 | import org.eclipse.jdt.annotation.Nullable; |
c1ed0a0c PT |
30 | import org.eclipse.swt.graphics.RGB; |
31 | import org.eclipse.swt.graphics.RGBA; | |
32 | import org.eclipse.tracecompass.internal.tmf.core.markers.IMarkerConstants; | |
33 | import org.eclipse.tracecompass.internal.tmf.core.markers.Marker; | |
34 | import org.eclipse.tracecompass.internal.tmf.core.markers.Marker.PeriodicMarker; | |
35 | import org.eclipse.tracecompass.internal.tmf.core.markers.MarkerSegment; | |
36 | import org.eclipse.tracecompass.internal.tmf.core.markers.MarkerSet; | |
37 | import org.eclipse.tracecompass.internal.tmf.core.markers.SubMarker; | |
38 | import org.eclipse.tracecompass.internal.tmf.core.markers.SubMarker.SplitMarker; | |
39 | import org.eclipse.tracecompass.internal.tmf.core.markers.SubMarker.WeightedMarker; | |
f16950de PT |
40 | import org.eclipse.tracecompass.tmf.core.signal.TmfMarkerEventSourceUpdatedSignal; |
41 | import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; | |
42 | import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager; | |
43 | import org.eclipse.tracecompass.tmf.core.trace.AbstractTmfTraceAdapterFactory.IDisposableAdapter; | |
980ca0c9 | 44 | import org.eclipse.tracecompass.tmf.core.trace.ICyclesConverter; |
c1ed0a0c | 45 | import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; |
a5c7cc1d | 46 | import org.eclipse.tracecompass.tmf.ui.colors.X11Color; |
aa67df36 | 47 | import org.eclipse.tracecompass.tmf.ui.markers.IMarkerReferenceProvider; |
c1ed0a0c PT |
48 | import org.eclipse.tracecompass.tmf.ui.markers.PeriodicMarkerEventSource; |
49 | import org.eclipse.tracecompass.tmf.ui.markers.PeriodicMarkerEventSource.Reference; | |
50 | import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.IMarkerEvent; | |
51 | import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.IMarkerEventSource; | |
52 | import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.MarkerEvent; | |
53 | ||
54 | import com.google.common.collect.Lists; | |
55 | import com.google.common.collect.RangeSet; | |
56 | ||
57 | /** | |
58 | * Configurable marker event source. | |
59 | */ | |
f16950de | 60 | public class ConfigurableMarkerEventSource implements IMarkerEventSource, IDisposableAdapter { |
c1ed0a0c PT |
61 | |
62 | private static final long NANO_PER_MILLI = 1000000L; | |
63 | private static final long NANO_PER_MICRO = 1000L; | |
64 | private static final int MIN_PERIOD = 5; // in units of resolution intervals | |
65 | private static final int ALPHA = 10; | |
66 | private static final String COLOR_REGEX = "#[A-Fa-f0-9]{6}"; //$NON-NLS-1$ | |
67 | ||
68 | private List<IConfigurableMarkerEventSource> fMarkerEventSources; | |
69 | private Map<Marker, RGBA> fColors = new HashMap<>(); | |
aa67df36 | 70 | private final ITmfTrace fTrace; |
c1ed0a0c PT |
71 | |
72 | /** | |
73 | * Constructor | |
74 | * | |
75 | * @param trace | |
76 | * the trace | |
77 | */ | |
78 | public ConfigurableMarkerEventSource(ITmfTrace trace) { | |
79 | fMarkerEventSources = new ArrayList<>(); | |
aa67df36 | 80 | fTrace = trace; |
f16950de PT |
81 | TmfSignalManager.register(this); |
82 | } | |
83 | ||
84 | @Override | |
85 | public void dispose() { | |
86 | TmfSignalManager.deregister(this); | |
c1ed0a0c PT |
87 | } |
88 | ||
89 | /** | |
90 | * Configure the marker source from the specified marker set | |
91 | * | |
92 | * @param markerSet | |
93 | * the marker set, or null to clear the configuration | |
94 | */ | |
95 | public void configure(MarkerSet markerSet) { | |
96 | fMarkerEventSources.clear(); | |
97 | if (markerSet != null) { | |
98 | for (Marker marker : markerSet.getMarkers()) { | |
99 | configure(marker); | |
100 | } | |
101 | } | |
102 | } | |
103 | ||
104 | private void configure(Marker marker) { | |
105 | if (marker instanceof PeriodicMarker) { | |
106 | PeriodicMarker periodicMarker = (PeriodicMarker) marker; | |
aa67df36 PT |
107 | String referenceId = periodicMarker.getReferenceId(); |
108 | Reference baseReference = null; | |
109 | if (fTrace instanceof IAdaptable && !referenceId.isEmpty()) { | |
110 | @Nullable IMarkerReferenceProvider adapter = ((IAdaptable) fTrace).getAdapter(IMarkerReferenceProvider.class); | |
111 | if (adapter != null) { | |
112 | baseReference = adapter.getReference(referenceId); | |
113 | } | |
114 | } | |
115 | if (baseReference == null) { | |
116 | baseReference = Reference.ZERO; | |
117 | } | |
c1ed0a0c PT |
118 | long rollover = periodicMarker.getRange().hasUpperBound() ? (periodicMarker.getRange().upperEndpoint() - periodicMarker.getRange().lowerEndpoint() + 1) : 0; |
119 | RGBA evenColor = getColor(periodicMarker); | |
120 | RGBA oddColor = getOddColor(evenColor); | |
121 | double period = convertToNanos(periodicMarker.getPeriod(), periodicMarker.getUnit()); | |
aa67df36 | 122 | Reference reference = new Reference(baseReference.getTime() + Math.round(convertToNanos(periodicMarker.getOffset(), periodicMarker.getUnit())), baseReference.getIndex()); |
c1ed0a0c PT |
123 | ConfigurablePeriodicMarkerEventSource markerEventSource = new ConfigurablePeriodicMarkerEventSource(marker, checkNotNull(periodicMarker.getName()), reference, period, rollover, evenColor, oddColor, false, periodicMarker.getRange().lowerEndpoint(), checkNotNull(periodicMarker.getLabel()), periodicMarker.getIndexRange()); |
124 | fMarkerEventSources.add(markerEventSource); | |
125 | } | |
126 | } | |
127 | ||
980ca0c9 | 128 | private double convertToNanos(double number, String unit) { |
c1ed0a0c PT |
129 | if (unit.equalsIgnoreCase(IMarkerConstants.MS)) { |
130 | return number * NANO_PER_MILLI; | |
131 | } else if (unit.equalsIgnoreCase(IMarkerConstants.US)) { | |
132 | return number * NANO_PER_MICRO; | |
133 | } else if (unit.equalsIgnoreCase(IMarkerConstants.NS)) { | |
134 | return number; | |
980ca0c9 PT |
135 | } else if (unit.equalsIgnoreCase(IMarkerConstants.CYCLES) && |
136 | fTrace instanceof IAdaptable) { | |
137 | ICyclesConverter adapter = ((IAdaptable) fTrace).getAdapter(ICyclesConverter.class); | |
138 | if (adapter != null) { | |
139 | return adapter.cyclesToNanos((long) number); | |
140 | } | |
c1ed0a0c PT |
141 | } |
142 | return number; | |
143 | } | |
144 | ||
145 | private @NonNull RGBA getColor(Marker marker) { | |
146 | RGBA color = fColors.get(marker); | |
147 | if (color == null) { | |
148 | color = parseColor(marker.getColor()); | |
149 | fColors.put(marker, color); | |
150 | } | |
151 | return color; | |
152 | } | |
153 | ||
154 | private static @NonNull RGBA getOddColor(RGBA color) { | |
155 | return new RGBA(color.rgb.red, color.rgb.green, color.rgb.blue, 0); | |
156 | } | |
157 | ||
158 | private static @NonNull RGBA parseColor(String color) { | |
159 | RGB rgb = null; | |
160 | if (color.matches(COLOR_REGEX)) { | |
161 | rgb = new RGB(Integer.valueOf(color.substring(1, 3), 16), | |
162 | Integer.valueOf(color.substring(3, 5), 16), | |
163 | Integer.valueOf(color.substring(5, 7), 16)); | |
164 | } else { | |
a5c7cc1d PT |
165 | rgb = X11Color.toRGB(color); |
166 | if (rgb == null) { | |
167 | rgb = new RGB(0, 0, 0); | |
168 | } | |
c1ed0a0c PT |
169 | } |
170 | return new RGBA(rgb.red, rgb.green, rgb.blue, ALPHA); | |
171 | } | |
172 | ||
173 | @Override | |
174 | public List<String> getMarkerCategories() { | |
175 | Set<String> categories = new LinkedHashSet<>(); | |
176 | for (IConfigurableMarkerEventSource source : fMarkerEventSources) { | |
177 | categories.addAll(source.getMarkerCategories()); | |
178 | getSubMarkerCategories(categories, source.getSubMarkers()); | |
179 | } | |
180 | return checkNotNull(Lists.newArrayList(categories)); | |
181 | } | |
182 | ||
183 | private void getSubMarkerCategories(Set<String> categories, List<SubMarker> subMarkers) { | |
184 | for (SubMarker subMarker : subMarkers) { | |
185 | categories.add(subMarker.getName()); | |
186 | getSubMarkerCategories(categories, subMarker.getSubMarkers()); | |
187 | if (subMarker instanceof WeightedMarker) { | |
188 | for (MarkerSegment segment : ((WeightedMarker) subMarker).getSegments()) { | |
189 | getSubMarkerCategories(categories, segment.getSubMarkers()); | |
190 | } | |
191 | } | |
192 | } | |
193 | } | |
194 | ||
195 | @Override | |
196 | public List<IMarkerEvent> getMarkerList(String category, long startTime, long endTime, long resolution, IProgressMonitor monitor) { | |
197 | return getMarkerList(startTime, endTime, resolution, monitor).stream() | |
198 | .filter((marker) -> marker.getCategory().equals(category)) | |
199 | .collect(Collectors.toList()); | |
200 | } | |
201 | ||
202 | @Override | |
203 | public List<IMarkerEvent> getMarkerList(long startTime, long endTime, long resolution, IProgressMonitor monitor) { | |
204 | @NonNull List<@NonNull IMarkerEvent> markerList = new ArrayList<>(); | |
205 | for (IConfigurableMarkerEventSource source : fMarkerEventSources) { | |
206 | long minDuration = resolution * MIN_PERIOD; | |
207 | if (source.getMaxDuration() > minDuration) { | |
208 | @NonNull List<@NonNull IMarkerEvent> list = source.getMarkerList(startTime, endTime, resolution, monitor); | |
209 | for (IMarkerEvent markerEvent : list) { | |
210 | for (SubMarker subMarker : source.getSubMarkers()) { | |
211 | getSubMarkerList(subMarker, markerEvent, markerList, startTime, endTime, minDuration); | |
212 | } | |
213 | markerList.add(markerEvent); | |
214 | } | |
215 | } | |
216 | } | |
217 | markerList.sort(Comparator.comparingLong(marker -> marker.getTime())); | |
218 | return markerList; | |
219 | } | |
220 | ||
221 | private void getSubMarkerList(SubMarker subMarker, IMarkerEvent markerEvent, @NonNull List<@NonNull IMarkerEvent> markerList, long startTime, long endTime, long minDuration) { | |
222 | if (subMarker instanceof SplitMarker) { | |
223 | getSubMarkerList((SplitMarker) subMarker, markerEvent, markerList, startTime, endTime, minDuration); | |
224 | } else if (subMarker instanceof WeightedMarker) { | |
225 | getSubMarkerList((WeightedMarker) subMarker, markerEvent, markerList, startTime, endTime, minDuration); | |
226 | } | |
227 | } | |
228 | ||
229 | private void getSubMarkerList(SplitMarker splitMarker, IMarkerEvent markerEvent, @NonNull List<@NonNull IMarkerEvent> markerList, long startTime, long endTime, long minDuration) { | |
230 | if (markerEvent.getTime() > endTime || markerEvent.getTime() + markerEvent.getDuration() < startTime) { | |
231 | return; | |
232 | } | |
233 | long lower = splitMarker.getRange().lowerEndpoint(); | |
234 | long upper = splitMarker.getRange().upperEndpoint(); | |
235 | long segments = upper - lower + 1; | |
236 | long start = markerEvent.getTime(); | |
237 | for (int i = 0; i < segments; i++) { | |
238 | long end = markerEvent.getTime() + Math.round((double) (i + 1) / segments * markerEvent.getDuration()); | |
239 | long duration = end - start; | |
240 | long labelIndex = lower + i; | |
241 | if (end >= startTime && duration > minDuration && splitMarker.getIndexRange().contains(labelIndex)) { | |
242 | RGBA color = (labelIndex & 1) == 0 ? getColor(splitMarker) : getOddColor(getColor(splitMarker)); | |
243 | IMarkerEvent subMarkerEvent = new MarkerEvent(null, start, end - start, splitMarker.getName(), color, String.format(splitMarker.getLabel(), labelIndex), false); | |
244 | for (SubMarker subMarker : splitMarker.getSubMarkers()) { | |
245 | getSubMarkerList(subMarker, subMarkerEvent, markerList, startTime, endTime, minDuration); | |
246 | } | |
247 | markerList.add(subMarkerEvent); | |
248 | } | |
249 | if (start >= endTime) { | |
250 | break; | |
251 | } | |
252 | start = end; | |
253 | } | |
254 | } | |
255 | ||
256 | private void getSubMarkerList(WeightedMarker weightedMarker, IMarkerEvent markerEvent, @NonNull List<@NonNull IMarkerEvent> markerList, long startTime, long endTime, long minDuration) { | |
257 | if (markerEvent.getTime() > endTime || markerEvent.getTime() + markerEvent.getDuration() < startTime) { | |
258 | return; | |
259 | } | |
260 | long start = markerEvent.getTime(); | |
261 | long length = 0; | |
262 | for (int i = 0; i < weightedMarker.getSegments().size(); i++) { | |
263 | MarkerSegment segment = weightedMarker.getSegments().get(i); | |
264 | length += segment.getLength(); | |
265 | long end = markerEvent.getTime() + Math.round((length / (double) weightedMarker.getTotalLength()) * markerEvent.getDuration()); | |
266 | long duration = end - start; | |
267 | if (end >= startTime && duration > minDuration && !segment.getColor().isEmpty()) { | |
268 | RGBA color = getColor(segment); | |
269 | IMarkerEvent subMarkerEvent = new MarkerEvent(null, start, end - start, weightedMarker.getName(), color, String.format(segment.getLabel(), i), false); | |
270 | for (SubMarker subMarker : segment.getSubMarkers()) { | |
271 | getSubMarkerList(subMarker, subMarkerEvent, markerList, startTime, endTime, minDuration); | |
272 | } | |
273 | for (SubMarker subMarker : weightedMarker.getSubMarkers()) { | |
274 | getSubMarkerList(subMarker, subMarkerEvent, markerList, startTime, endTime, minDuration); | |
275 | } | |
276 | markerList.add(subMarkerEvent); | |
277 | } | |
278 | if (start >= endTime) { | |
279 | break; | |
280 | } | |
281 | start = end; | |
282 | } | |
283 | } | |
284 | ||
285 | private static interface IConfigurableMarkerEventSource extends IMarkerEventSource { | |
286 | double getMaxDuration(); | |
287 | ||
288 | public List<SubMarker> getSubMarkers(); | |
289 | } | |
290 | ||
291 | private class ConfigurablePeriodicMarkerEventSource extends PeriodicMarkerEventSource implements IConfigurableMarkerEventSource { | |
292 | ||
293 | private final Marker fMarker; | |
294 | private final long fStartIndex; | |
295 | private final String fLabel; | |
296 | private final RangeSet<Long> fIndexRange; | |
297 | private final double fMaxDuration; | |
298 | ||
299 | public ConfigurablePeriodicMarkerEventSource(Marker marker, @NonNull String category, @NonNull Reference reference, double period, long rollover, @NonNull RGBA evenColor, @NonNull RGBA oddColor, boolean foreground, long startIndex, @NonNull String label, RangeSet<Long> indexRange) { | |
300 | super(category, reference, period, rollover, evenColor, oddColor, foreground); | |
301 | fMarker = marker; | |
302 | fStartIndex = startIndex; | |
303 | fLabel = label; | |
304 | fIndexRange = indexRange; | |
305 | fMaxDuration = period; | |
306 | } | |
307 | ||
308 | @Override | |
309 | public @NonNull String getMarkerLabel(long index) { | |
310 | return checkNotNull(String.format(fLabel, fStartIndex + index)); | |
311 | } | |
312 | ||
313 | @Override | |
314 | public boolean isApplicable(long index) { | |
315 | if (fIndexRange != null) { | |
316 | return fIndexRange.contains(fStartIndex + index); | |
317 | } | |
318 | return true; | |
319 | } | |
320 | ||
321 | @Override | |
322 | public double getMaxDuration() { | |
323 | return fMaxDuration; | |
324 | } | |
325 | ||
326 | @Override | |
327 | public List<SubMarker> getSubMarkers() { | |
328 | return fMarker.getSubMarkers(); | |
329 | } | |
330 | } | |
f16950de PT |
331 | |
332 | /** | |
333 | * A marker event source has been updated | |
334 | * | |
335 | * @param signal | |
336 | * the signal | |
337 | */ | |
338 | @TmfSignalHandler | |
339 | public void markerEventSourceUpdated(final TmfMarkerEventSourceUpdatedSignal signal) { | |
340 | configure(MarkerUtils.getDefaultMarkerSet()); | |
341 | } | |
c1ed0a0c | 342 | } |