RobotCore 1.0
WPILib port to raspberry Pi
Loading...
Searching...
No Matches
TelemetryTable.java
1// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5package org.wpilib.telemetry;
6
7import java.lang.reflect.Array;
8import java.util.Collection;
9import java.util.Objects;
10import java.util.concurrent.ConcurrentHashMap;
11import java.util.concurrent.ConcurrentMap;
12import java.util.concurrent.atomic.AtomicLong;
13import org.wpilib.telemetry.util.PathUtil;
14import org.wpilib.util.protobuf.Protobuf;
15import org.wpilib.util.protobuf.ProtobufSerializable;
16import org.wpilib.util.struct.Struct;
17import org.wpilib.util.struct.StructSerializable;
18
24public final class TelemetryTable {
25 private record StaticFieldLookup(Object value, String warning) {}
26
27 private record CachedEntry(TelemetryEntry entry, long resetGeneration) {}
28
29 private enum EntryMetadataKind {
30 KEEP_DUPLICATES,
31 PROPERTY
32 }
33
34 private static final ClassValue<StaticFieldLookup> s_structLookupCache =
35 new ClassValue<>() {
36 @Override
37 protected StaticFieldLookup computeValue(Class<?> type) {
38 return getStaticField(type, "struct");
39 }
40 };
41
42 private static final ClassValue<StaticFieldLookup> s_protoLookupCache =
43 new ClassValue<>() {
44 @Override
45 protected StaticFieldLookup computeValue(Class<?> type) {
46 return getStaticField(type, "proto");
47 }
48 };
49
50 private final String m_path;
51 private final TelemetryBackend m_backend;
52 private final ConcurrentMap<String, TelemetryTable> m_tableAliasesMap = new ConcurrentHashMap<>();
53 private final ConcurrentMap<String, TelemetryTable> m_tablesMap = new ConcurrentHashMap<>();
54 private final ConcurrentMap<String, CachedEntry> m_entriesMap = new ConcurrentHashMap<>();
55 private String m_type;
56 private Boolean m_hasNonDiscardDescendant;
57 private final AtomicLong m_resetGeneration = new AtomicLong();
58
59 private static StaticFieldLookup getStaticField(Class<?> type, String fieldName) {
60 try {
61 return new StaticFieldLookup(type.getField(fieldName).get(null), null);
62 } catch (NoSuchFieldException e) {
63 return new StaticFieldLookup(
64 null, "could not get " + fieldName + " field for " + type.getName());
65 } catch (IllegalAccessException e) {
66 return new StaticFieldLookup(
67 null, "could not access " + fieldName + " field for " + type.getName());
68 }
69 }
70
76 TelemetryTable(String path) {
77 this(path, null);
78 }
79
87 this("/", Objects.requireNonNull(backend));
88 }
89
90 private TelemetryTable(String path, TelemetryBackend backend) {
91 m_path = path;
92 m_backend = backend;
93 }
94
96 void reset() {
97 synchronized (this) {
98 m_tableAliasesMap.clear();
99 m_tablesMap.clear();
100 m_entriesMap.clear();
101 m_type = null;
102 m_hasNonDiscardDescendant = null;
103 m_resetGeneration.incrementAndGet();
104 }
105 }
106
112 public String getPath() {
113 return m_path;
114 }
115
124 public boolean setType(String typeString) {
125 for (; ; ) {
126 String expectedType = null;
127 long resetGeneration;
128 synchronized (this) {
129 if (m_type != null) {
130 if (m_type.equals(typeString)) {
131 return true;
132 }
133 expectedType = m_type;
134 resetGeneration = -1;
135 } else {
136 m_type = typeString;
137 resetGeneration = m_resetGeneration.get();
138 }
139 }
140 if (expectedType != null) {
141 typeMismatch(expectedType, typeString);
142 return false;
143 }
144
145 TelemetryEntry entry = getEntry(".type");
146 boolean publishType;
147 synchronized (this) {
148 publishType =
149 m_resetGeneration.get() == resetGeneration && Objects.equals(typeString, m_type);
150 }
151 if (publishType) {
152 if (!entry.isDiscard()) {
153 entry.logString(typeString, "string", 0);
154 }
155 return true;
156 }
157 }
158 }
159
160 private void typeMismatch(String expectedType, String typeString) {
162 m_path, "table type mismatch, expected '" + expectedType + "', got '" + typeString + "'");
163 }
164
170 public String getType() {
171 synchronized (this) {
172 return m_type;
173 }
174 }
175
182 public TelemetryTable getTable(String name) {
183 TelemetryTable table = m_tableAliasesMap.get(name);
184 if (table != null) {
185 return table;
186 }
187
188 String path = getTablePath(name);
189 table = m_tablesMap.get(path);
190 if (table != null) {
191 TelemetryTable aliasTable = m_tableAliasesMap.putIfAbsent(name, table);
192 return aliasTable != null ? aliasTable : table;
193 }
194
195 TelemetryTable newTable =
196 m_backend != null ? new TelemetryTable(path, m_backend) : TelemetryRegistry.getTable(path);
197 table = m_tablesMap.putIfAbsent(path, newTable);
198 if (table == null) {
199 table = newTable;
200 }
201
202 TelemetryTable aliasTable = m_tableAliasesMap.putIfAbsent(name, table);
203 return aliasTable != null ? aliasTable : table;
204 }
205
206 private String getEntryPath(String name) {
207 return PathUtil.normalizeName(m_path + name);
208 }
209
210 private String getTablePath(String name) {
211 return PathUtil.normalizeTableName(m_path + name + "/");
212 }
213
220 private TelemetryEntry getEntry(String name) {
221 for (; ; ) {
222 CachedEntry cachedEntry = m_entriesMap.get(name);
223 if (cachedEntry != null) {
224 if (cachedEntry.resetGeneration() == m_resetGeneration.get()) {
225 return cachedEntry.entry();
226 }
227 m_entriesMap.remove(name, cachedEntry);
228 continue;
229 }
230
231 long resetGeneration = m_resetGeneration.get();
232 String path = getEntryPath(name);
233 TelemetryEntry newEntry =
234 m_backend != null ? m_backend.getEntry(path) : TelemetryRegistry.getEntry(path);
235 if (m_backend == null && !newEntry.isDiscard()) {
236 TelemetryRegistry.applyEntryMetadata(path, newEntry);
237 }
238
239 CachedEntry newCachedEntry = new CachedEntry(newEntry, resetGeneration);
240 cachedEntry = m_entriesMap.putIfAbsent(name, newCachedEntry);
241 if (cachedEntry == null) {
242 cachedEntry = newCachedEntry;
243 }
244 if (cachedEntry.resetGeneration() == m_resetGeneration.get()) {
245 return cachedEntry.entry();
246 }
247 m_entriesMap.remove(name, cachedEntry);
248 }
249 }
250
251 private void applyEntryMetadata(
252 String name, String path, EntryMetadataKind kind, String key, String value) {
253 for (; ; ) {
254 CachedEntry cachedEntry = m_entriesMap.get(name);
255 if (cachedEntry != null) {
256 if (cachedEntry.resetGeneration() == m_resetGeneration.get()) {
257 applyEntryMetadata(cachedEntry.entry(), kind, key, value);
258 return;
259 }
260 m_entriesMap.remove(name, cachedEntry);
261 continue;
262 }
263
264 long resetGeneration = m_resetGeneration.get();
265 TelemetryEntry newEntry =
266 m_backend != null ? m_backend.getEntry(path) : TelemetryRegistry.getEntry(path);
267 boolean metadataApplied = false;
268 if (m_backend == null && !newEntry.isDiscard()) {
269 TelemetryRegistry.applyEntryMetadata(path, newEntry);
270 metadataApplied = true;
271 }
272
273 CachedEntry newCachedEntry = new CachedEntry(newEntry, resetGeneration);
274 cachedEntry = m_entriesMap.putIfAbsent(name, newCachedEntry);
275 boolean inserted = cachedEntry == null;
276 if (cachedEntry == null) {
277 cachedEntry = newCachedEntry;
278 }
279 if (cachedEntry.resetGeneration() == m_resetGeneration.get()) {
280 if (!(inserted && metadataApplied)) {
281 applyEntryMetadata(cachedEntry.entry(), kind, key, value);
282 }
283 return;
284 }
285 m_entriesMap.remove(name, cachedEntry);
286 }
287 }
288
289 private void applyEntryMetadata(
290 TelemetryEntry entry, EntryMetadataKind kind, String key, String value) {
291 if (!entry.isDiscard()) {
292 if (kind == EntryMetadataKind.KEEP_DUPLICATES) {
293 entry.keepDuplicates();
294 } else {
295 entry.setProperty(key, value);
296 }
297 }
298 }
299
300 private boolean shouldLogTableValue(String name, TelemetryTable table) {
301 TelemetryEntry entry = getEntry(name);
302 return !entry.isDiscard() || m_backend == null && table.hasNonDiscardDescendant();
303 }
304
305 private boolean hasNonDiscardDescendant() {
306 for (; ; ) {
307 long resetGeneration;
308 synchronized (this) {
309 if (m_hasNonDiscardDescendant != null) {
310 return m_hasNonDiscardDescendant;
311 }
312 resetGeneration = m_resetGeneration.get();
313 }
314
315 boolean hasNonDiscardDescendant = TelemetryRegistry.hasNonDiscardDescendant(m_path);
316 synchronized (this) {
317 if (m_hasNonDiscardDescendant != null) {
318 return m_hasNonDiscardDescendant;
319 }
320 if (resetGeneration != m_resetGeneration.get()) {
321 continue;
322 }
323 m_hasNonDiscardDescendant = hasNonDiscardDescendant;
324 return m_hasNonDiscardDescendant;
325 }
326 }
327 }
328
334 public void keepDuplicates(String name) {
335 if (m_backend != null) {
336 TelemetryEntry entry = getEntry(name);
337 if (!entry.isDiscard()) {
338 entry.keepDuplicates();
339 }
340 return;
341 }
342
343 String path = getEntryPath(name);
344 boolean done = false;
345 while (!done) {
346 long resetGeneration = m_resetGeneration.get();
347 TelemetryRegistry.keepEntryDuplicates(path);
348 applyEntryMetadata(name, path, EntryMetadataKind.KEEP_DUPLICATES, null, null);
349 done = m_resetGeneration.get() == resetGeneration;
350 }
351 }
352
360 public void setProperty(String name, String key, String value) {
361 if (m_backend != null) {
362 TelemetryEntry entry = getEntry(name);
363 if (!entry.isDiscard()) {
364 entry.setProperty(key, value);
365 }
366 return;
367 }
368
369 String path = getEntryPath(name);
370 boolean done = false;
371 while (!done) {
372 long resetGeneration = m_resetGeneration.get();
373 TelemetryRegistry.setEntryProperty(path, key, value);
374 applyEntryMetadata(name, path, EntryMetadataKind.PROPERTY, key, value);
375 done = m_resetGeneration.get() == resetGeneration;
376 }
377 }
378
399 public <T> void log(String name, T value) {
400 switch (value) {
401 case TelemetryLoggable v -> {
402 TelemetryTable table = getTable(name);
403 if (!shouldLogTableValue(name, table)) {
404 return;
405 }
406 String typeString = v.getTelemetryType();
407 if (typeString != null && !table.setType(typeString)) {
408 return;
409 }
410 v.logTo(table);
411 }
412 case StructSerializable v -> {
413 TelemetryEntry entry = getEntry(name);
414 if (entry.isDiscard()) {
415 return;
416 }
417 var lookup = s_structLookupCache.get(v.getClass());
418 if (lookup.warning() != null) {
419 TelemetryRegistry.reportWarning(getEntryPath(name), lookup.warning());
420 return;
421 }
422 switch (lookup.value()) {
423 case Struct<?> s when s.getTypeClass().isAssignableFrom(value.getClass()) -> {
424 @SuppressWarnings("unchecked")
425 Struct<? super T> s2 = (Struct<? super T>) s;
426 entry.logStruct(value, s2, 0);
427 }
428 case Struct<?> s ->
429 TelemetryRegistry.reportWarning(
430 getEntryPath(name),
431 "type mismatch, expected '"
432 + s.getTypeClass().getName()
433 + "', got '"
434 + value.getClass().getName()
435 + "'");
436 default ->
437 TelemetryRegistry.reportWarning(
438 getEntryPath(name),
439 "struct field for " + v.getClass().getName() + " is not of Struct<?> type");
440 }
441 }
442 case ProtobufSerializable v -> {
443 TelemetryEntry entry = getEntry(name);
444 if (entry.isDiscard()) {
445 return;
446 }
447 var lookup = s_protoLookupCache.get(v.getClass());
448 if (lookup.warning() != null) {
449 TelemetryRegistry.reportWarning(getEntryPath(name), lookup.warning());
450 return;
451 }
452 switch (lookup.value()) {
453 case Protobuf<?, ?> s when s.getTypeClass().isAssignableFrom(value.getClass()) -> {
454 @SuppressWarnings("unchecked")
455 Protobuf<? super T, ?> s2 = (Protobuf<? super T, ?>) s;
456 entry.logProtobuf(value, s2, 0);
457 }
458 case Protobuf<?, ?> s ->
459 TelemetryRegistry.reportWarning(
460 getEntryPath(name),
461 "type mismatch, expected '"
462 + s.getTypeClass().getName()
463 + "', got '"
464 + value.getClass().getName()
465 + "'");
466 default ->
467 TelemetryRegistry.reportWarning(
468 getEntryPath(name),
469 "proto field for " + v.getClass().getName() + " is not of Protobuf<?, ?> type");
470 }
471 }
472 case Boolean v -> {
473 TelemetryEntry entry = getEntry(name);
474 if (!entry.isDiscard()) {
475 entry.logBoolean(v.booleanValue(), 0);
476 }
477 }
478 case Float v -> {
479 TelemetryEntry entry = getEntry(name);
480 if (!entry.isDiscard()) {
481 entry.logFloat(v.floatValue(), 0);
482 }
483 }
484 case Double v -> {
485 TelemetryEntry entry = getEntry(name);
486 if (!entry.isDiscard()) {
487 entry.logDouble(v.doubleValue(), 0);
488 }
489 }
490 case Byte v -> {
491 TelemetryEntry entry = getEntry(name);
492 if (!entry.isDiscard()) {
493 entry.logLong(v.longValue(), 0);
494 }
495 }
496 case Short v -> {
497 TelemetryEntry entry = getEntry(name);
498 if (!entry.isDiscard()) {
499 entry.logLong(v.longValue(), 0);
500 }
501 }
502 case Integer v -> {
503 TelemetryEntry entry = getEntry(name);
504 if (!entry.isDiscard()) {
505 entry.logLong(v.longValue(), 0);
506 }
507 }
508 case Long v -> {
509 TelemetryEntry entry = getEntry(name);
510 if (!entry.isDiscard()) {
511 entry.logLong(v.longValue(), 0);
512 }
513 }
514 case Number v -> {
515 TelemetryEntry entry = getEntry(name);
516 if (!entry.isDiscard()) {
517 entry.logDouble(v.doubleValue(), 0);
518 }
519 }
520 case String v -> {
521 TelemetryEntry entry = getEntry(name);
522 if (!entry.isDiscard()) {
523 entry.logString(v, "string", 0);
524 }
525 }
526 case Collection<?> _ -> {
527 TelemetryEntry entry = getEntry(name);
528 if (!entry.isDiscard()) {
529 TelemetryRegistry.reportWarning(
530 getEntryPath(name), "collection element type must be specified");
531 }
532 }
533 default -> {
534 // try other handlers
535 var handler = TelemetryRegistry.getTypeHandler(value);
536 if (handler != null) {
537 TelemetryTable table = getTable(name);
538 if (!shouldLogTableValue(name, table)) {
539 return;
540 }
541 handler.logTo(this, name, value);
542 } else {
543 // fall back to string
544 TelemetryEntry entry = getEntry(name);
545 if (!entry.isDiscard()) {
546 entry.logString(value.toString(), "string", 0);
547 }
548 }
549 }
550 }
551 }
552
561 public <T> void log(String name, T value, Struct<? super T> struct) {
562 TelemetryEntry entry = getEntry(name);
563 if (entry.isDiscard()) {
564 return;
565 }
566 entry.logStruct(value, struct, 0);
567 }
568
577 public <T> void log(String name, T value, Protobuf<? super T, ?> proto) {
578 TelemetryEntry entry = getEntry(name);
579 if (entry.isDiscard()) {
580 return;
581 }
582 entry.logProtobuf(value, proto, 0);
583 }
584
594 public <T> void log(String name, T[] value) {
595 TelemetryEntry entry = getEntry(name);
596 if (entry.isDiscard()) {
597 return;
598 }
599 switch (value) {
600 case StructSerializable[] _ -> {
601 Class<?> componentType = value.getClass().getComponentType();
602 var lookup = s_structLookupCache.get(componentType);
603 if (lookup.warning() != null) {
604 TelemetryRegistry.reportWarning(getEntryPath(name), lookup.warning());
605 return;
606 }
607 switch (lookup.value()) {
608 case Struct<?> s when s.getTypeClass().isAssignableFrom(componentType) -> {
609 @SuppressWarnings("unchecked")
610 Struct<? super T> s2 = (Struct<? super T>) s;
611 entry.logStructArray(value, s2, 0);
612 }
613 case Struct<?> s ->
614 TelemetryRegistry.reportWarning(
615 getEntryPath(name),
616 "type mismatch, expected '"
617 + s.getTypeClass().getName()
618 + "', got '"
619 + value.getClass().getName()
620 + "'");
621 default ->
622 TelemetryRegistry.reportWarning(
623 getEntryPath(name),
624 "struct field for " + componentType.getName() + " is not of Struct<?> type");
625 }
626 }
627 case Boolean[] v -> {
628 boolean[] arr = new boolean[v.length];
629 for (int i = 0; i < v.length; i++) {
630 arr[i] = v[i].booleanValue();
631 }
632 entry.logBooleanArray(arr, 0);
633 }
634 case Byte[] v -> {
635 byte[] arr = new byte[v.length];
636 for (int i = 0; i < v.length; i++) {
637 arr[i] = v[i].byteValue();
638 }
639 entry.logRaw(arr, "raw", 0);
640 }
641 case Float[] v -> {
642 float[] arr = new float[v.length];
643 for (int i = 0; i < v.length; i++) {
644 arr[i] = v[i].floatValue();
645 }
646 entry.logFloatArray(arr, 0);
647 }
648 case Double[] v -> {
649 double[] arr = new double[v.length];
650 for (int i = 0; i < v.length; i++) {
651 arr[i] = v[i].doubleValue();
652 }
653 entry.logDoubleArray(arr, 0);
654 }
655 case Number[] v -> {
656 logNumberArray(entry, v);
657 }
658 default -> {
659 // TODO: use other Object handler?
660 // fall back to string array
661 String[] strs = new String[value.length];
662 for (int i = 0; i < value.length; i++) {
663 strs[i] = String.valueOf(value[i]);
664 }
665 entry.logStringArray(strs, 0);
666 }
667 }
668 }
669
678 public <T> void log(String name, T[] value, Struct<? super T> struct) {
679 TelemetryEntry entry = getEntry(name);
680 if (entry.isDiscard()) {
681 return;
682 }
683 entry.logStructArray(value, struct, 0);
684 }
685
696 public void log(String name, Collection<?> value) throws CollectionElementTypeRequiredException {
698 }
699
715 public void log(String name, Collection<?> value, Class<?> elementType) {
716 Objects.requireNonNull(value, "value");
717 Objects.requireNonNull(elementType, "elementType");
718 TelemetryEntry entry = getEntry(name);
719 if (entry.isDiscard()) {
720 return;
721 }
722
723 if (isBooleanType(elementType)) {
724 logBooleanCollection(name, entry, value);
725 } else if (isByteType(elementType)) {
726 logByteCollection(name, entry, value);
727 } else if (isFloatType(elementType)) {
728 logFloatCollection(name, entry, value);
729 } else if (isDoubleType(elementType)) {
730 logDoubleCollection(name, entry, value);
731 } else if (isIntegralNumberType(elementType)) {
732 logLongCollection(name, entry, value);
733 } else if (isNumberType(elementType)) {
734 logDoubleCollection(name, entry, value);
735 } else if (elementType == String.class) {
736 logStringCollection(name, entry, value);
737 } else if (StructSerializable.class.isAssignableFrom(elementType)) {
738 var lookup = s_structLookupCache.get(elementType);
739 if (lookup.warning() != null) {
740 TelemetryRegistry.reportWarning(getEntryPath(name), lookup.warning());
741 return;
742 }
743 switch (lookup.value()) {
744 case Struct<?> s when s.getTypeClass().isAssignableFrom(elementType) ->
745 logStructCollection(name, entry, value, s);
746 case Struct<?> s ->
748 getEntryPath(name),
749 "type mismatch, expected '"
750 + s.getTypeClass().getName()
751 + "', got '"
752 + elementType.getName()
753 + "'");
754 default ->
756 getEntryPath(name),
757 "struct field for " + elementType.getName() + " is not of Struct<?> type");
758 }
759 } else {
760 logFallbackStringCollection(entry, value);
761 }
762 }
763
772 public <T> void log(String name, Collection<T> value, Struct<? super T> struct) {
773 TelemetryEntry entry = getEntry(name);
774 if (entry.isDiscard()) {
775 return;
776 }
777 entry.logStructArray(toArray(value, struct.getTypeClass()), struct, 0);
778 }
779
786 public void log(String name, boolean value) {
787 TelemetryEntry entry = getEntry(name);
788 if (entry.isDiscard()) {
789 return;
790 }
791 entry.logBoolean(value, 0);
792 }
793
800 public void log(String name, byte value) {
801 TelemetryEntry entry = getEntry(name);
802 if (entry.isDiscard()) {
803 return;
804 }
805 entry.logByte(value, 0);
806 }
807
814 public void log(String name, short value) {
815 TelemetryEntry entry = getEntry(name);
816 if (entry.isDiscard()) {
817 return;
818 }
819 entry.logShort(value, 0);
820 }
821
828 public void log(String name, int value) {
829 TelemetryEntry entry = getEntry(name);
830 if (entry.isDiscard()) {
831 return;
832 }
833 entry.logInt(value, 0);
834 }
835
842 public void log(String name, long value) {
843 TelemetryEntry entry = getEntry(name);
844 if (entry.isDiscard()) {
845 return;
846 }
847 entry.logLong(value, 0);
848 }
849
856 public void log(String name, float value) {
857 TelemetryEntry entry = getEntry(name);
858 if (entry.isDiscard()) {
859 return;
860 }
861 entry.logFloat(value, 0);
862 }
863
870 public void log(String name, double value) {
871 TelemetryEntry entry = getEntry(name);
872 if (entry.isDiscard()) {
873 return;
874 }
875 entry.logDouble(value, 0);
876 }
877
884 public void log(String name, String value) {
885 TelemetryEntry entry = getEntry(name);
886 if (entry.isDiscard()) {
887 return;
888 }
889 entry.logString(value, "string", 0);
890 }
891
899 public void log(String name, String value, String typeString) {
900 TelemetryEntry entry = getEntry(name);
901 if (entry.isDiscard()) {
902 return;
903 }
904 entry.logString(value, typeString, 0);
905 }
906
913 public void log(String name, boolean[] value) {
914 TelemetryEntry entry = getEntry(name);
915 if (entry.isDiscard()) {
916 return;
917 }
918 entry.logBooleanArray(value, 0);
919 }
920
927 public void log(String name, short[] value) {
928 TelemetryEntry entry = getEntry(name);
929 if (entry.isDiscard()) {
930 return;
931 }
932 entry.logShortArray(value, 0);
933 }
934
941 public void log(String name, int[] value) {
942 TelemetryEntry entry = getEntry(name);
943 if (entry.isDiscard()) {
944 return;
945 }
946 entry.logIntArray(value, 0);
947 }
948
955 public void log(String name, long[] value) {
956 TelemetryEntry entry = getEntry(name);
957 if (entry.isDiscard()) {
958 return;
959 }
960 entry.logLongArray(value, 0);
961 }
962
969 public void log(String name, float[] value) {
970 TelemetryEntry entry = getEntry(name);
971 if (entry.isDiscard()) {
972 return;
973 }
974 entry.logFloatArray(value, 0);
975 }
976
983 public void log(String name, double[] value) {
984 TelemetryEntry entry = getEntry(name);
985 if (entry.isDiscard()) {
986 return;
987 }
988 entry.logDoubleArray(value, 0);
989 }
990
997 public void log(String name, String[] value) {
998 TelemetryEntry entry = getEntry(name);
999 if (entry.isDiscard()) {
1000 return;
1001 }
1002 entry.logStringArray(value, 0);
1003 }
1004
1011 public void log(String name, byte[] value) {
1012 TelemetryEntry entry = getEntry(name);
1013 if (entry.isDiscard()) {
1014 return;
1015 }
1016 entry.logRaw(value, "raw", 0);
1017 }
1018
1026 public void log(String name, byte[] value, String typeString) {
1027 TelemetryEntry entry = getEntry(name);
1028 if (entry.isDiscard()) {
1029 return;
1030 }
1031 entry.logRaw(value, typeString, 0);
1032 }
1033
1034 @SuppressWarnings("unchecked")
1035 private static <T> T[] toArray(Collection<?> value, Class<?> componentType) {
1036 return value.toArray((T[]) Array.newInstance(componentType, value.size()));
1037 }
1038
1039 private static boolean isBooleanType(Class<?> type) {
1040 return type == Boolean.class || type == Boolean.TYPE;
1041 }
1042
1043 private static boolean isByteType(Class<?> type) {
1044 return type == Byte.class || type == Byte.TYPE;
1045 }
1046
1047 private static boolean isFloatType(Class<?> type) {
1048 return type == Float.class || type == Float.TYPE;
1049 }
1050
1051 private static boolean isDoubleType(Class<?> type) {
1052 return type == Double.class || type == Double.TYPE;
1053 }
1054
1055 private static boolean isIntegralNumberType(Class<?> type) {
1056 return type == Short.class
1057 || type == Short.TYPE
1058 || type == Integer.class
1059 || type == Integer.TYPE
1060 || type == Long.class
1061 || type == Long.TYPE;
1062 }
1063
1064 private static boolean isNumberType(Class<?> type) {
1065 return Number.class.isAssignableFrom(type);
1066 }
1067
1068 private void logBooleanCollection(String name, TelemetryEntry entry, Collection<?> value) {
1069 boolean[] arr = new boolean[value.size()];
1070 int i = 0;
1071 for (Object element : value) {
1072 if (!(element instanceof Boolean v)) {
1073 collectionElementTypeMismatch(name, Boolean.class, element);
1074 return;
1075 }
1076 arr[i++] = v;
1077 }
1078 entry.logBooleanArray(arr, 0);
1079 }
1080
1081 private void logByteCollection(String name, TelemetryEntry entry, Collection<?> value) {
1082 byte[] arr = new byte[value.size()];
1083 int i = 0;
1084 for (Object element : value) {
1085 if (!(element instanceof Number v)) {
1086 collectionElementTypeMismatch(name, Number.class, element);
1087 return;
1088 }
1089 arr[i++] = v.byteValue();
1090 }
1091 entry.logRaw(arr, "raw", 0);
1092 }
1093
1094 private void logFloatCollection(String name, TelemetryEntry entry, Collection<?> value) {
1095 float[] arr = new float[value.size()];
1096 int i = 0;
1097 for (Object element : value) {
1098 if (!(element instanceof Number v)) {
1099 collectionElementTypeMismatch(name, Number.class, element);
1100 return;
1101 }
1102 arr[i++] = v.floatValue();
1103 }
1104 entry.logFloatArray(arr, 0);
1105 }
1106
1107 private void logDoubleCollection(String name, TelemetryEntry entry, Collection<?> value) {
1108 double[] arr = new double[value.size()];
1109 int i = 0;
1110 for (Object element : value) {
1111 if (!(element instanceof Number v)) {
1112 collectionElementTypeMismatch(name, Number.class, element);
1113 return;
1114 }
1115 arr[i++] = v.doubleValue();
1116 }
1117 entry.logDoubleArray(arr, 0);
1118 }
1119
1120 private void logLongCollection(String name, TelemetryEntry entry, Collection<?> value) {
1121 long[] arr = new long[value.size()];
1122 int i = 0;
1123 for (Object element : value) {
1124 if (!(element instanceof Number v)) {
1125 collectionElementTypeMismatch(name, Number.class, element);
1126 return;
1127 }
1128 arr[i++] = v.longValue();
1129 }
1130 entry.logLongArray(arr, 0);
1131 }
1132
1133 private void logStringCollection(String name, TelemetryEntry entry, Collection<?> value) {
1134 String[] arr = new String[value.size()];
1135 int i = 0;
1136 for (Object element : value) {
1137 if (!(element instanceof String v)) {
1138 collectionElementTypeMismatch(name, String.class, element);
1139 return;
1140 }
1141 arr[i++] = v;
1142 }
1143 entry.logStringArray(arr, 0);
1144 }
1145
1146 private void logStructCollection(
1147 String name, TelemetryEntry entry, Collection<?> value, Struct<?> struct) {
1148 logStructCollectionImpl(name, entry, value, struct);
1149 }
1150
1151 private <T> void logStructCollectionImpl(
1152 String name, TelemetryEntry entry, Collection<?> value, Struct<T> struct) {
1153 try {
1154 entry.logStructArray(toArray(value, struct.getTypeClass()), struct, 0);
1155 } catch (ArrayStoreException e) {
1156 collectionElementTypeMismatch(name, struct.getTypeClass(), null);
1157 }
1158 }
1159
1160 private static void logFallbackStringCollection(TelemetryEntry entry, Collection<?> value) {
1161 String[] strs = new String[value.size()];
1162 int i = 0;
1163 for (Object element : value) {
1164 strs[i++] = String.valueOf(element);
1165 }
1166 entry.logStringArray(strs, 0);
1167 }
1168
1169 private void collectionElementTypeMismatch(String name, Class<?> expectedType, Object element) {
1170 TelemetryRegistry.reportWarning(
1171 getEntryPath(name),
1172 "collection element type mismatch, expected '"
1173 + expectedType.getName()
1174 + "', got '"
1175 + (element == null ? "null" : element.getClass().getName())
1176 + "'");
1177 }
1178
1179 private static void logNumberArray(TelemetryEntry entry, Number[] value) {
1180 if (isIntegralNumberType(value.getClass().getComponentType())) {
1181 long[] arr = new long[value.length];
1182 for (int i = 0; i < value.length; i++) {
1183 arr[i] = value[i].longValue();
1184 }
1185 entry.logLongArray(arr, 0);
1186 } else {
1187 double[] arr = new double[value.length];
1188 for (int i = 0; i < value.length; i++) {
1189 arr[i] = value[i].doubleValue();
1190 }
1191 entry.logDoubleArray(arr, 0);
1192 }
1193 }
1194}
static void reportWarning(String path, String msg)
static TelemetryTable getTable(String path)
void log(String name, int[] value)
TelemetryTable(TelemetryBackend backend)
void log(String name, float[] value)
void log(String name, Collection<?> value, Class<?> elementType)
void log(String name, double[] value)
void log(String name, long[] value)
void log(String name, String[] value)
void log(String name, float value)
void log(String name, String value, String typeString)
void setProperty(String name, String key, String value)
void log(String name, String value)
void log(String name, short[] value)
void log(String name, long value)
void log(String name, short value)
void log(String name, boolean[] value)
TelemetryTable getTable(String name)
void log(String name, double value)
void log(String name, byte value)
void log(String name, byte[] value)
void log(String name, byte[] value, String typeString)
void log(String name, int value)
boolean setType(String typeString)
void log(String name, Collection<?> value)
void log(String name, boolean value)
static String normalizeName(String path)
Definition PathUtil.java:53
void logBoolean(boolean value, long timestamp)
void logRaw(byte[] value, String typeString, long timestamp)
default void logByte(byte value, long timestamp)
void logBooleanArray(boolean[] value, long timestamp)
void logFloatArray(float[] value, long timestamp)
void keepDuplicates()
void logLong(long value, long timestamp)
void logShortArray(short[] value, long timestamp)
void logFloat(float value, long timestamp)
void logStringArray(String[] value, long timestamp)
void logIntArray(int[] value, long timestamp)
default boolean isDiscard()
default void logShort(short value, long timestamp)
< T > void logStructArray(T[] value, Struct<? super T > struct, long timestamp)
void setProperty(String key, String value)
default void logInt(int value, long timestamp)
void logLongArray(long[] value, long timestamp)
void logString(String value, String typeString, long timestamp)
void logDoubleArray(double[] value, long timestamp)
void logDouble(double value, long timestamp)