RobotCore 1.0
WPILib port to raspberry Pi
Loading...
Searching...
No Matches
RobotBase.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.framework;
6
7import java.util.HashMap;
8import java.util.Map;
9import java.util.concurrent.atomic.AtomicInteger;
10import java.util.concurrent.locks.ReentrantLock;
11import java.util.function.BiConsumer;
12import java.util.function.Consumer;
13import java.util.function.Supplier;
14import org.wpilib.backend.NetworkTablesTelemetryBackend;
15import org.wpilib.backend.NetworkTablesTunableBackend;
16import org.wpilib.driverstation.DriverStationErrors;
17import org.wpilib.driverstation.RobotState;
18import org.wpilib.driverstation.internal.DriverStationBackend;
19import org.wpilib.hardware.hal.HAL;
20import org.wpilib.hardware.hal.HALUtil;
21import org.wpilib.internal.UnitTelemetry;
22import org.wpilib.math.util.MathShared;
23import org.wpilib.math.util.MathSharedStore;
24import org.wpilib.networktables.IntegerPublisher;
25import org.wpilib.networktables.MultiSubscriber;
26import org.wpilib.networktables.NetworkTableEvent;
27import org.wpilib.networktables.NetworkTableInstance;
28import org.wpilib.networktables.PubSubOption;
29import org.wpilib.networktables.StringPublisher;
30import org.wpilib.system.RuntimeType;
31import org.wpilib.system.Timer;
32import org.wpilib.system.WPILibVersion;
33import org.wpilib.telemetry.TelemetryRegistry;
34import org.wpilib.tunable.Tunable;
35import org.wpilib.tunable.TunableConfig;
36import org.wpilib.tunable.TunableDouble;
37import org.wpilib.tunable.TunableRegistry;
38import org.wpilib.units.Measure;
39import org.wpilib.units.Unit;
40import org.wpilib.util.Alert;
41import org.wpilib.util.UsageReporting;
42import org.wpilib.util.WPIUtilJNI;
43
53public abstract class RobotBase implements AutoCloseable {
54 private static final String PROGRAM_START_TIME_TOPIC = "/Robot/ProgramStartTime";
55 private static final String WPILIB_VERSION_TOPIC = "/Robot/WPILibVersion";
56 private static final AtomicInteger s_nextWarningReporterId = new AtomicInteger();
57
59 // This is usually 1, but it is best to make sure
60 private static long m_threadId = -1;
61
62 private final MultiSubscriber m_suball;
63 private final IntegerPublisher m_programStartTimePublisher;
64 private final StringPublisher m_wpilibVersionPublisher;
65 private final Map<String, Alert> m_telemetryWarningAlerts = new HashMap<>();
66 private final Map<String, Alert> m_tunableWarningAlerts = new HashMap<>();
67 private final BiConsumer<String, String> m_telemetryWarningReporter =
68 this::reportTelemetryWarning;
69 private final Consumer<String> m_tunableWarningReporter = this::reportTunableWarning;
70 private final String m_warningAlertIdPrefix =
71 "warning" + s_nextWarningReporterId.getAndIncrement() + "_";
72 private final AtomicInteger m_nextWarningAlertId = new AtomicInteger();
73
74 private final int m_connListenerHandle;
75
76 private static void setupMathShared() {
78 new MathShared() {
79 @Override
80 public void reportError(String error, StackTraceElement[] stackTrace) {
81 DriverStationErrors.reportError(error, stackTrace);
82 }
83
84 @Override
85 public double getTimestamp() {
86 return Timer.getTimestamp();
87 }
88 });
89 }
90
91 private void reportTelemetryWarning(String path, String msg) {
92 reportWarningAlert(
93 m_telemetryWarningAlerts,
94 "Telemetry",
95 path + '\n' + msg,
96 "Telemetry '" + path + "': warning: " + msg);
97 }
98
99 private void reportTunableWarning(String msg) {
100 reportWarningAlert(m_tunableWarningAlerts, "Tunables", msg, "Tunable warning: " + msg);
101 }
102
103 private void reportWarningAlert(
104 Map<String, Alert> alerts, String group, String key, String text) {
105 synchronized (alerts) {
106 Alert alert = alerts.get(key);
107 if (alert == null) {
108 alert =
109 new Alert(
110 group,
111 m_warningAlertIdPrefix + m_nextWarningAlertId.getAndIncrement(),
112 text,
114 alerts.put(key, alert);
115 }
116
117 alert.setText(text);
118 alert.set(true);
119 }
120 }
121
122 private static void closeWarningAlerts(Map<String, Alert> alerts) {
123 synchronized (alerts) {
124 for (Alert alert : alerts.values()) {
125 alert.close();
126 }
127 alerts.clear();
128 }
129 }
130
131 @SuppressWarnings("rawtypes")
132 private static class TunableMeasure extends Tunable<Measure> implements Tunable.CustomTunable {
133 TunableMeasure(Measure initialValue, TunableConfig config) {
134 super(config);
135 m_unit = initialValue.unit();
136 m_baseUnit = m_unit.getBaseUnit();
137 m_value = initialValue;
138 m_magnitudeTunable =
139 new TunableDouble(config, true) {
140 @Override
141 public void set(double value) {
142 m_value = m_unit.ofBaseUnits(value);
143 markChanged();
144 }
145
146 @Override
147 public double get() {
148 return m_value.baseUnitMagnitude();
149 }
150 };
151 }
152
153 @Override
154 public void set(Measure value) {
155 if (!m_baseUnit.equivalent(value.unit().getBaseUnit())) {
156 throw new IllegalArgumentException(
157 "Measure unit " + value.unit() + " is not compatible with " + m_unit);
158 }
159 m_magnitudeTunable.set(value.baseUnitMagnitude());
160 }
161
162 @Override
163 public Measure get() {
164 return m_value;
165 }
166
167 @Override
168 public Measure mutate() {
169 m_magnitudeTunable.set(m_value.baseUnitMagnitude());
170 return m_value;
171 }
172
173 @Override
175 return m_magnitudeTunable;
176 }
177
178 @Override
179 public boolean hasChanged() {
180 return m_magnitudeTunable.hasChanged();
181 }
182
183 @Override
184 public boolean supportsChangeNotification() {
185 return m_magnitudeTunable.supportsChangeNotification();
186 }
187
188 @Override
189 public void resetChanged() {
190 m_magnitudeTunable.resetChanged();
191 }
192
193 @Override
194 public Class<Measure> getTypeClass() {
195 return Measure.class;
196 }
197
198 private final Unit m_unit;
199 private final Unit m_baseUnit;
200 private Measure m_value;
201 private final TunableDouble m_magnitudeTunable;
202 }
203
212 @SuppressWarnings("this-escape")
213 protected RobotBase() {
215 m_threadId = Thread.currentThread().threadId();
216 setupMathShared();
217 // subscribe to "" to force persistent values to propagate to local
218 m_suball = new MultiSubscriber(inst, new String[] {""}, PubSubOption.DISABLE_SIGNAL);
219 if (!isSimulation()) {
220 inst.startServer("/home/systemcore/networktables.json", "", "robot");
221 } else {
222 inst.startServer("networktables.json", "", "robot");
223 }
224
225 // set up telemetry
226 TelemetryRegistry.setReportWarning(m_telemetryWarningReporter);
229 Measure.class,
230 (table, name, value) -> {
231 UnitTelemetry.log(table, name, value);
232 });
233
234 // set up tunables
235 TunableRegistry.setReportWarning(m_tunableWarningReporter);
238 Measure.class,
239 (initialValue, config) -> {
240 if (config == null) {
241 config = new TunableConfig();
242 }
243 return new TunableMeasure(
244 initialValue,
245 config.withProperty(
246 "unit", UnitTelemetry.getUnitMetadata(initialValue.unit().getBaseUnit())));
247 });
248
249 // wait for the NT server to actually start
250 try {
251 int count = 0;
252 while (inst.getNetworkMode().contains(NetworkTableInstance.NetworkMode.STARTING)) {
253 Thread.sleep(10);
254 count++;
255 if (count > 100) {
256 throw new InterruptedException();
257 }
258 }
259 } catch (InterruptedException ex) {
260 System.err.println("timed out while waiting for NT server to start");
261 }
262
263 m_programStartTimePublisher = inst.getIntegerTopic(PROGRAM_START_TIME_TOPIC).publish();
264 m_programStartTimePublisher.set(WPIUtilJNI.getProgramStartTime());
265
266 m_wpilibVersionPublisher = inst.getStringTopic(WPILIB_VERSION_TOPIC).publish();
267 m_wpilibVersionPublisher.set(WPILibVersion.Version);
268
269 m_connListenerHandle =
270 inst.addConnectionListener(
271 false,
272 event -> {
273 if (event.is(NetworkTableEvent.Kind.CONNECTED)) {
274 UsageReporting.reportUsage("NT/" + event.connInfo.remoteId, "");
275 }
276 });
277 }
278
284 public static long getMainThreadId() {
285 return m_threadId;
286 }
287
288 @Override
289 @SuppressWarnings("PMD.CompareObjectsWithEquals")
290 public void close() {
291 if (TelemetryRegistry.getReportWarning() == m_telemetryWarningReporter) {
292 TelemetryRegistry.setReportWarning(null);
293 }
294 if (TunableRegistry.getReportWarning() == m_tunableWarningReporter) {
295 TunableRegistry.setReportWarning(null);
296 }
297 closeWarningAlerts(m_telemetryWarningAlerts);
298 closeWarningAlerts(m_tunableWarningAlerts);
299 m_suball.close();
300 m_programStartTimePublisher.close();
301 m_wpilibVersionPublisher.close();
302 NetworkTableInstance.getDefault().removeListener(m_connListenerHandle);
303 }
304
310 public static RuntimeType getRuntimeType() {
311 return RuntimeType.getValue(HALUtil.getHALRuntimeType());
312 }
313
319 public static boolean isSimulation() {
321 }
322
328 public static boolean isReal() {
329 RuntimeType runtimeType = getRuntimeType();
330 return runtimeType == RuntimeType.SYSTEMCORE;
331 }
332
338 public static boolean isDisabled() {
339 return RobotState.isDisabled();
340 }
341
347 public static boolean isEnabled() {
348 return RobotState.isEnabled();
349 }
350
356 public static boolean isAutonomous() {
357 return RobotState.isAutonomous();
358 }
359
366 public static boolean isAutonomousEnabled() {
368 }
369
375 public static boolean isUtility() {
376 return RobotState.isUtility();
377 }
378
385 public static boolean isUtilityEnabled() {
387 }
388
395 public static boolean isTeleop() {
396 return RobotState.isTeleop();
397 }
398
405 public static boolean isTeleopEnabled() {
407 }
408
416 public static long getOpModeId() {
417 return RobotState.getOpModeId();
418 }
419
427 public static String getOpMode() {
428 return RobotState.getOpMode();
429 }
430
435 public abstract void startCompetition();
436
438 public abstract void endCompetition();
439
440 private static final ReentrantLock m_runMutex = new ReentrantLock();
441 private static RobotBase m_robotCopy;
442 private static boolean m_suppressExitWarning;
443
450 protected static String getRobotName(StackTraceElement[] elements) {
451 // Walk bottom to top to account for multiple layers of subclassing
452 for (int i = elements.length - 1; i >= 0; i--) {
453 StackTraceElement element = elements[i];
454 try {
455 // Skip our own class when walking
456 if (RobotBase.class.equals(Class.forName(element.getClassName()))) {
457 continue;
458 }
459 if (RobotBase.class.isAssignableFrom(Class.forName(element.getClassName()))) {
460 return element.getClassName();
461 }
462 } catch (ClassNotFoundException e) {
463 // Unreachable
464 }
465 }
466 return "Unknown";
467 }
468
470 @SuppressWarnings("PMD.AvoidCatchingGenericException")
471 private static <T extends RobotBase> void runRobot(Supplier<T> robotConstructor) {
472 System.out.println("********** Robot program starting **********");
473
474 T robot;
475 try {
476 robot = robotConstructor.get();
477 } catch (Throwable throwable) {
478 Throwable cause = throwable.getCause();
479 if (cause != null) {
480 throwable = cause;
481 }
482 StackTraceElement[] elements = throwable.getStackTrace();
483 String robotName = getRobotName(elements);
484 DriverStationErrors.reportError(
485 "Unhandled exception instantiating robot " + robotName + " " + throwable, elements);
486 DriverStationErrors.reportError(
487 "The robot program quit unexpectedly."
488 + " This is usually due to a code error.\n"
489 + " The above stacktrace can help determine where the error occurred.\n"
490 + " See https://wpilib.org/stacktrace for more information.\n",
491 false);
492 DriverStationErrors.reportError("Could not instantiate robot " + robotName + "!", false);
493 DriverStationErrors.reportCrash("Could not instantiate robot " + robotName + "!", elements);
494 return;
495 }
496
497 m_runMutex.lock();
498 m_robotCopy = robot;
499 m_runMutex.unlock();
500
501 boolean errorOnExit = false;
502 try {
503 robot.startCompetition();
504 } catch (Throwable throwable) {
505 Throwable cause = throwable.getCause();
506 if (cause != null) {
507 throwable = cause;
508 }
510 "Unhandled exception: " + throwable, throwable.getStackTrace());
512 "Unhandled exception: " + throwable, throwable.getStackTrace());
513 errorOnExit = true;
514 } finally {
515 m_runMutex.lock();
516 boolean suppressExitWarning = m_suppressExitWarning;
517 m_runMutex.unlock();
518 if (!suppressExitWarning) {
519 // startCompetition never returns unless exception occurs....
521 "The robot program quit unexpectedly."
522 + " This is usually due to a code error.\n"
523 + " The above stacktrace can help determine where the error occurred.\n"
524 + " See https://wpilib.org/stacktrace for more information.",
525 false);
526 if (errorOnExit) {
528 "The startCompetition() method (or methods called by it) should have "
529 + "handled the exception above.",
530 false);
531 } else {
533 "Unexpected return from startCompetition() method.", false);
534 }
535 }
536 }
537 }
538
544 public static void suppressExitWarning(boolean value) {
545 m_runMutex.lock();
546 m_suppressExitWarning = value;
547 m_runMutex.unlock();
548 }
549
556 public static <T extends RobotBase> void startRobot(Supplier<T> robotConstructor) {
557 // Check that the MSVC runtime is valid.
558 WPIUtilJNI.checkMsvcRuntime();
559
560 if (!HAL.initialize()) {
561 throw new IllegalStateException("Failed to initialize. Terminating");
562 }
563 UsageReporting.setReportUsageImpl(HAL::reportUsage);
564
565 // Force refresh DS data
567
568 UsageReporting.reportUsage("Language", "Java");
570 HAL.publishWpilibVersion(WPILibVersion.Version + " (Java)");
571
572 if (HAL.hasMain()) {
573 Thread thread =
574 new Thread(
575 () -> {
576 runRobot(robotConstructor);
577 HAL.exitMain();
578 },
579 "robot main");
580 thread.setDaemon(true);
581 thread.start();
582 HAL.runMain();
584 m_runMutex.lock();
585 RobotBase robot = m_robotCopy;
586 m_runMutex.unlock();
587 if (robot != null) {
588 robot.endCompetition();
589 }
590 try {
591 thread.join(1000);
592 } catch (InterruptedException ex) {
593 Thread.currentThread().interrupt();
594 }
595 } else {
596 runRobot(robotConstructor);
597 }
598
599 // On RIO, this will just terminate rather than shutting down cleanly (it's a no-op in sim).
600 // It's not worth the risk of hanging on shutdown when we want the code to restart as quickly
601 // as possible.
602 HAL.terminate();
603
604 HAL.shutdown();
605
606 System.exit(0);
607 }
608}
static void reportCrash(String details, StackTraceElement[] stackTrace)
static void reportWarning(String warning, boolean printTrace)
static void reportError(String error, boolean printTrace)
static boolean isTeleopEnabled()
static< T extends RobotBase > void startRobot(Supplier< T > robotConstructor)
static void suppressExitWarning(boolean value)
static boolean isUtilityEnabled()
abstract void startCompetition()
static RuntimeType getRuntimeType()
static boolean isAutonomousEnabled()
abstract void endCompetition()
static String getRobotName(StackTraceElement[] elements)
static String getUnitMetadata(Unit unit)
static synchronized void setMathShared(MathShared shared)
MultiSubscriber(NetworkTableInstance inst, String[] prefixes, PubSubOption... options)
static synchronized NetworkTableInstance getDefault()
static double getTimestamp()
Definition Timer.java:26
static void registerBackend(String prefix, TelemetryBackend backend)
static< T > void registerTypeHandler(Class< T > cls, TypeHandler< T > handler)
static void setReportWarning(BiConsumer< String, String > func)
static BiConsumer< String, String > getReportWarning()
abstract void set(double value)
TunableDouble(TunableConfig config)
abstract Class< T > getTypeClass()
static void registerBackend(String prefix, TunableBackend backend)
static< T > void registerTypeHandler(Class< T > cls, TypeHandler< T > handler)
static Consumer< String > getReportWarning()
static void setReportWarning(Consumer< String > func)
boolean equivalent(Unit other)
Definition Unit.java:198
abstract Measure<?> ofBaseUnits(double baseUnitMagnitude)
Alert(String id, String text, Level level)
Definition Alert.java:83
void setText(String text)
Definition Alert.java:125
void set(boolean active)
Definition Alert.java:106
static synchronized void setReportUsageImpl(BiConsumer< String, String > func)
static void reportUsage(String resource, String data)
static RuntimeType getValue(int type)