RobotCore 1.0
WPILib port to raspberry Pi
Loading...
Searching...
No Matches
Tunable.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.tunable;
6
7import java.util.Objects;
8import java.util.function.Consumer;
9import java.util.function.Supplier;
10import org.wpilib.util.protobuf.Protobuf;
11import org.wpilib.util.protobuf.ProtobufSerializable;
12import org.wpilib.util.struct.Struct;
13import org.wpilib.util.struct.StructSerializable;
14
20public abstract class Tunable<T> extends TunableBase implements Supplier<T>, Consumer<T> {
21 private static final String UNSUPPORTED_TUNABLE_TYPE = "Unsupported tunable type: ";
22
28 public static class TunableStruct<T> extends Tunable<T> {
29 TunableStruct(T initialValue, Struct<T> struct, TunableConfig config) {
30 super(config, true);
31 m_struct = struct;
32 m_value = initialValue;
33 }
34
35 @Override
36 public void set(T value) {
37 m_value = value;
39 }
40
41 @Override
42 public T get() {
43 return m_value;
44 }
45
46 @Override
47 public Class<T> getTypeClass() {
48 return m_struct.getTypeClass();
49 }
50
56 public Struct<T> getStruct() {
57 return m_struct;
58 }
59
60 private final Struct<T> m_struct;
61 private T m_value;
62 }
63
69 public static class TunableStructArray<T> extends Tunable<T[]> {
70 TunableStructArray(T[] initialValue, Struct<T> struct, Class<T[]> cls, TunableConfig config) {
71 super(config, true);
72 m_struct = struct;
73 m_cls = cls;
74 m_value = initialValue == null ? null : initialValue.clone();
75 }
76
77 @Override
78 public void set(T[] value) {
79 m_value = value == null ? null : value.clone();
81 }
82
83 @Override
84 public T[] get() {
85 return m_value == null ? null : m_value.clone();
86 }
87
88 @Override
89 @SuppressWarnings("PMD.MethodReturnsInternalArray")
90 public T[] mutate() {
92 return m_value;
93 }
94
95 @Override
96 public Class<T[]> getTypeClass() {
97 return m_cls;
98 }
99
105 public Struct<T> getStruct() {
106 return m_struct;
107 }
108
109 private final Struct<T> m_struct;
110 private final Class<T[]> m_cls;
111 private T[] m_value;
112 }
113
119 public static class TunableProtobuf<T> extends Tunable<T> {
120 TunableProtobuf(T initialValue, Protobuf<T, ?> proto, TunableConfig config) {
121 super(config, true);
122 m_proto = proto;
123 m_value = initialValue;
124 }
125
126 @Override
127 public void set(T value) {
128 m_value = value;
129 markChanged();
130 }
131
132 @Override
133 public T get() {
134 return m_value;
135 }
136
137 @Override
138 public Class<T> getTypeClass() {
139 return m_proto.getTypeClass();
140 }
141
147 public Protobuf<T, ?> getProtobuf() {
148 return m_proto;
149 }
150
151 private final Protobuf<T, ?> m_proto;
152 private T m_value;
153 }
154
155 private abstract static class BasicTunable<T> extends Tunable<T> {
156 BasicTunable(Class<T> cls, TunableConfig config) {
157 super(config, true);
158 m_cls = cls;
159 }
160
161 @Override
162 public Class<T> getTypeClass() {
163 return m_cls;
164 }
165
166 private final Class<T> m_cls;
167 }
168
170 @FunctionalInterface
171 public interface CustomTunable {
178 }
179
180 @SuppressWarnings("unchecked")
181 private static <T> Class<T> getValueClass(T value) {
182 return (Class<T>) value.getClass();
183 }
184
185 private static boolean isSupportedBasicType(Class<?> cls) {
186 return cls == Boolean.class
187 || cls == Integer.class
188 || cls == Long.class
189 || cls == Float.class
190 || cls == Double.class
191 || cls == String.class
192 || cls == byte[].class
193 || cls == boolean[].class
194 || cls == int[].class
195 || cls == long[].class
196 || cls == float[].class
197 || cls == double[].class
198 || cls == String[].class;
199 }
200
201 private static String getTypeName(Class<?> cls) {
202 return cls == null ? "<null>" : cls.getName();
203 }
204
205 private static void warnIfUnsupportedBasicType(Class<?> cls) {
206 if (!isSupportedBasicType(cls)) {
207 TunableRegistry.reportWarning(
208 UNSUPPORTED_TUNABLE_TYPE + getTypeName(cls) + "; it may not be published by backends");
209 }
210 }
211
212 private static void warnIfUnpublishableTunable(TunableBase tunable) {
213 if (tunable == null) {
214 return;
215 }
216 if (tunable instanceof CustomTunable custom) {
217 warnIfUnpublishableTunable(custom.getInnerTunable());
218 } else if (tunable instanceof TunableBoolean
219 || tunable instanceof TunableInt
220 || tunable instanceof TunableLong
221 || tunable instanceof TunableFloat
222 || tunable instanceof TunableDouble
223 || tunable instanceof TunableStruct<?>
224 || tunable instanceof TunableStructArray<?>
225 || tunable instanceof TunableProtobuf<?>) {
226 return;
227 } else if (tunable instanceof Tunable<?> generic) {
228 warnIfUnsupportedBasicType(generic.getTypeClass());
229 } else {
230 TunableRegistry.reportWarning(UNSUPPORTED_TUNABLE_TYPE + tunable.getClass().getName());
231 }
232 }
233
234 private static <T> Tunable<T> createBasic(T initialValue, TunableConfig config) {
235 return createBasic(initialValue, getValueClass(initialValue), config);
236 }
237
238 private static <T> Tunable<T> createBasic(T initialValue, Class<T> cls, TunableConfig config) {
239 warnIfUnsupportedBasicType(cls);
240 return new BasicTunable<T>(cls, config) {
241 @Override
242 public void set(T value) {
243 m_value = value;
244 markChanged();
245 }
246
247 @Override
248 public T get() {
249 return m_value;
250 }
251
252 private T m_value = initialValue;
253 };
254 }
255
256 private static <T> Struct<T> getStruct(Class<T> cls) {
257 Struct<?> struct = getStructObject(cls);
258 if (struct == null) {
259 return null;
260 }
261 @SuppressWarnings("unchecked")
262 Struct<T> typedStruct = (Struct<T>) struct;
263 return typedStruct;
264 }
265
266 private static Struct<?> getStructObject(Class<?> cls) {
267 // use introspection to get "struct" static variable
268 Object obj;
269 try {
270 obj = cls.getField("struct").get(null);
271 } catch (NoSuchFieldException e) {
272 TunableRegistry.reportWarning("could not get struct field for " + cls.getName());
273 return null;
274 } catch (IllegalAccessException e) {
275 TunableRegistry.reportWarning("could not access struct field for " + cls.getName());
276 return null;
277 }
278 switch (obj) {
279 case Struct<?> s when s.getTypeClass().equals(cls) -> {
280 return s;
281 }
282 case Struct<?> s ->
283 TunableRegistry.reportWarning(
284 "type mismatch, expected '"
285 + s.getTypeClass().getName()
286 + "', got '"
287 + cls.getName()
288 + "'");
289 default ->
290 TunableRegistry.reportWarning(
291 "struct field for " + cls.getName() + " is not of Struct<?> type");
292 }
293 return null;
294 }
295
296 @SuppressWarnings("unchecked")
297 private static <T> Tunable<T> createStructArrayTunable(
298 T initialValue, Class<T> cls, Struct<?> struct, TunableConfig config) {
299 return (Tunable<T>)
300 new TunableStructArray<>(
301 (Object[]) initialValue, (Struct<Object>) struct, (Class<Object[]>) cls, config);
302 }
303
304 @SuppressWarnings("unchecked")
305 private static <T> Tunable<T> createGetterSetterStructArrayTunable(
306 Supplier<T> getter,
307 Consumer<T> setter,
308 Class<T> cls,
309 Struct<?> struct,
310 TunableConfig config) {
311 return (Tunable<T>)
312 new TunableStructArray<>(null, (Struct<Object>) struct, (Class<Object[]>) cls, config) {
313 @Override
314 public void set(Object[] value) {
315 if (setter != null) {
316 setter.accept((T) value);
317 }
318 markChanged();
319 }
320
321 @Override
322 public Object[] get() {
323 return (Object[]) getter.get();
324 }
325
326 @Override
327 public Object[] mutate() {
328 markChanged();
329 return get();
330 }
331 };
332 }
333
334 private static <T> Protobuf<T, ?> getProtobuf(Class<T> cls) {
335 // use introspection to get "proto" static variable
336 Object obj;
337 try {
338 obj = cls.getField("proto").get(null);
339 } catch (NoSuchFieldException e) {
340 TunableRegistry.reportWarning("could not get proto field for " + cls.getName());
341 return null;
342 } catch (IllegalAccessException e) {
343 TunableRegistry.reportWarning("could not access proto field for " + cls.getName());
344 return null;
345 }
346 switch (obj) {
347 case Protobuf<?, ?> s when s.getTypeClass().equals(cls) -> {
348 @SuppressWarnings("unchecked")
349 Protobuf<T, ?> proto = (Protobuf<T, ?>) s;
350 return proto;
351 }
352 case Protobuf<?, ?> s ->
353 TunableRegistry.reportWarning(
354 "type mismatch, expected '"
355 + s.getTypeClass().getName()
356 + "', got '"
357 + cls.getName()
358 + "'");
359 default ->
360 TunableRegistry.reportWarning(
361 "proto field for " + cls.getName() + " is not of Protobuf<?, ?> type");
362 }
363 return null;
364 }
365
366 private static <T> Tunable<T> createGetterSetterTunable(
367 Supplier<T> getter, Consumer<T> setter, Struct<T> struct, TunableConfig config) {
368 return new TunableStruct<T>(null, struct, config) {
369 @Override
370 public void set(T value) {
371 if (setter != null) {
372 setter.accept(value);
373 }
374 markChanged();
375 }
376
377 @Override
378 public T get() {
379 return getter.get();
380 }
381 };
382 }
383
384 private static <T> Tunable<T> createGetterSetterTunable(
385 Supplier<T> getter, Consumer<T> setter, Protobuf<T, ?> proto, TunableConfig config) {
386 return new TunableProtobuf<T>(null, proto, config) {
387 @Override
388 public void set(T value) {
389 if (setter != null) {
390 setter.accept(value);
391 }
392 markChanged();
393 }
394
395 @Override
396 public T get() {
397 return getter.get();
398 }
399 };
400 }
401
409 public static <T> Tunable<T> createNull(Class<T> cls) {
410 return createNullConfig(cls, null);
411 }
412
421 public static <T> Tunable<T> createNullConfig(Class<T> cls, TunableConfig config) {
422 return createBasic(null, cls, config);
423 }
424
432 public static <T> Tunable<T> create(T initialValue) {
433 return createConfig(initialValue, null);
434 }
435
444 public static <T> Tunable<T> create(T initialValue, Struct<T> struct) {
445 Objects.requireNonNull(struct);
446 return createConfig(initialValue, struct, null);
447 }
448
457 public static <T> Tunable<T> create(T initialValue, Protobuf<T, ?> proto) {
458 Objects.requireNonNull(proto);
459 return createConfig(initialValue, proto, null);
460 }
461
473 public static <T> Tunable<T> create(Supplier<T> getter, Consumer<T> setter, Class<T> cls) {
474 return createConfig(getter, setter, cls, null);
475 }
476
485 public static <T> Tunable<T> createConfig(T initialValue, TunableConfig config) {
486 Objects.requireNonNull(initialValue);
487 Class<T> cls = getValueClass(initialValue);
488 if (initialValue instanceof StructSerializable) {
489 Struct<T> struct = getStruct(cls);
490 if (struct != null) {
491 return createConfig(initialValue, struct, config);
492 }
493 } else if (cls.isArray() && StructSerializable.class.isAssignableFrom(cls.getComponentType())) {
494 Struct<?> struct = getStructObject(cls.getComponentType());
495 if (struct != null) {
496 return createStructArrayTunable(initialValue, cls, struct, config);
497 }
498 } else if (initialValue instanceof ProtobufSerializable) {
499 Protobuf<T, ?> proto = getProtobuf(cls);
500 if (proto != null) {
501 return createConfig(initialValue, proto, config);
502 }
503 } else {
504 // try other handlers
505 var handler = TunableRegistry.getTypeHandler(initialValue);
506 if (handler != null) {
507 Tunable<T> tunable = handler.createTunable(initialValue, config);
508 if (tunable != null) {
509 warnIfUnpublishableTunable(tunable);
510 return tunable;
511 }
512 }
513 }
514 return createBasic(initialValue, config);
515 }
516
526 public static <T> Tunable<T> createConfig(
527 T initialValue, Struct<T> struct, TunableConfig config) {
528 return new TunableStruct<T>(initialValue, struct, config);
529 }
530
540 public static <T> Tunable<T> createConfig(
541 T initialValue, Protobuf<T, ?> proto, TunableConfig config) {
542 return new TunableProtobuf<T>(initialValue, proto, config);
543 }
544
557 public static <T> Tunable<T> createConfig(
558 Supplier<T> getter, Consumer<T> setter, Class<T> cls, TunableConfig config) {
559 if (config == null) {
561 } else if (config.getPolling() == TunableConfig.Polling.DEFAULT) {
563 }
564 if (cls != null && StructSerializable.class.isAssignableFrom(cls)) {
565 Struct<T> struct = getStruct(cls);
566 if (struct != null) {
567 return createGetterSetterTunable(getter, setter, struct, config);
568 }
569 } else if (cls != null
570 && cls.isArray()
571 && StructSerializable.class.isAssignableFrom(cls.getComponentType())) {
572 Struct<?> struct = getStructObject(cls.getComponentType());
573 if (struct != null) {
574 return createGetterSetterStructArrayTunable(getter, setter, cls, struct, config);
575 }
576 } else if (cls != null && ProtobufSerializable.class.isAssignableFrom(cls)) {
577 Protobuf<T, ?> proto = getProtobuf(cls);
578 if (proto != null) {
579 return createGetterSetterTunable(getter, setter, proto, config);
580 }
581 }
582 warnIfUnsupportedBasicType(cls);
583 return new BasicTunable<T>(cls, config) {
584 @Override
585 public void set(T value) {
586 if (setter != null) {
587 setter.accept(value);
588 }
589 markChanged();
590 }
591
592 @Override
593 public T get() {
594 return getter.get();
595 }
596 };
597 }
598
604 protected Tunable(TunableConfig config) {
605 super(config);
606 }
607
615 super(config, supportsChangeNotification);
616 }
617
625 public abstract void set(T value);
626
634 @Override
635 public abstract T get();
636
647 public T mutate() {
648 markChanged();
649 return get();
650 }
651
652 @Override
653 public void accept(T value) {
654 set(value);
655 }
656
662 public abstract Class<T> getTypeClass();
663}
TunableBase(TunableConfig config)
TunableConfig withPolling(Polling polling)
static TunableConfig of(TunableOption... options)
static< T > Tunable< T > createConfig(T initialValue, Struct< T > struct, TunableConfig config)
Definition Tunable.java:526
static< T > Tunable< T > createConfig(T initialValue, TunableConfig config)
Definition Tunable.java:485
static< T > Tunable< T > create(T initialValue, Protobuf< T, ?> proto)
Definition Tunable.java:457
static< T > Tunable< T > createNull(Class< T > cls)
Definition Tunable.java:409
Tunable(TunableConfig config, boolean supportsChangeNotification)
Definition Tunable.java:614
static< T > Tunable< T > createNullConfig(Class< T > cls, TunableConfig config)
Definition Tunable.java:421
static< T > Tunable< T > create(T initialValue)
Definition Tunable.java:432
Tunable(TunableConfig config)
Definition Tunable.java:604
static< T > Tunable< T > create(Supplier< T > getter, Consumer< T > setter, Class< T > cls)
Definition Tunable.java:473
abstract Class< T > getTypeClass()
static< T > Tunable< T > createConfig(T initialValue, Protobuf< T, ?> proto, TunableConfig config)
Definition Tunable.java:540
static< T > Tunable< T > create(T initialValue, Struct< T > struct)
Definition Tunable.java:444
static< T > Tunable< T > createConfig(Supplier< T > getter, Consumer< T > setter, Class< T > cls, TunableConfig config)
Definition Tunable.java:557
static< T > TypeHandler< T > getTypeHandler(T value)