[hal, wpilib] New DS thread model and implementation (#3787)

The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.

This change completely rethinks how threading works in the driver station model.

First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.

After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.

Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.

The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.

IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.

All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.

As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
This commit is contained in:
Thad House
2022-10-21 22:01:55 -07:00
committed by GitHub
parent c195b4fc46
commit 4a401b89d7
53 changed files with 1649 additions and 1384 deletions

View File

@@ -0,0 +1,137 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.hal;
import java.nio.ByteBuffer;
public class DriverStationJNI extends JNIWrapper {
public static native void observeUserProgramStarting();
public static native void observeUserProgramDisabled();
public static native void observeUserProgramAutonomous();
public static native void observeUserProgramTeleop();
public static native void observeUserProgramTest();
public static void report(int resource, int instanceNumber) {
report(resource, instanceNumber, 0, "");
}
public static void report(int resource, int instanceNumber, int context) {
report(resource, instanceNumber, context, "");
}
/**
* Report the usage of a resource of interest.
*
* <p>Original signature: <code>uint32_t report(tResourceType, uint8_t, uint8_t, const
* char*)</code>
*
* @param resource one of the values in the tResourceType above (max value 51).
* @param instanceNumber an index that identifies the resource instance.
* @param context an optional additional context number for some cases (such as module number).
* Set to 0 to omit.
* @param feature a string to be included describing features in use on a specific resource.
* Setting the same resource more than once allows you to change the feature string.
* @return TODO
*/
public static native int report(int resource, int instanceNumber, int context, String feature);
public static native int nativeGetControlWord();
@SuppressWarnings("MissingJavadocMethod")
public static void getControlWord(ControlWord controlWord) {
int word = nativeGetControlWord();
controlWord.update(
(word & 1) != 0,
((word >> 1) & 1) != 0,
((word >> 2) & 1) != 0,
((word >> 3) & 1) != 0,
((word >> 4) & 1) != 0,
((word >> 5) & 1) != 0);
}
private static native int nativeGetAllianceStation();
public static final int kRed1AllianceStation = 0;
public static final int kRed2AllianceStation = 1;
public static final int kRed3AllianceStation = 2;
public static final int kBlue1AllianceStation = 3;
public static final int kBlue2AllianceStation = 4;
public static final int kBlue3AllianceStation = 5;
@SuppressWarnings("MissingJavadocMethod")
public static AllianceStationID getAllianceStation() {
switch (nativeGetAllianceStation()) {
case kRed1AllianceStation:
return AllianceStationID.Red1;
case kRed2AllianceStation:
return AllianceStationID.Red2;
case kRed3AllianceStation:
return AllianceStationID.Red3;
case kBlue1AllianceStation:
return AllianceStationID.Blue1;
case kBlue2AllianceStation:
return AllianceStationID.Blue2;
case kBlue3AllianceStation:
return AllianceStationID.Blue3;
default:
return null;
}
}
public static final int kMaxJoystickAxes = 12;
public static final int kMaxJoystickPOVs = 12;
public static final int kMaxJoysticks = 6;
public static native int getJoystickAxes(byte joystickNum, float[] axesArray);
public static native int getJoystickAxesRaw(byte joystickNum, int[] rawAxesArray);
public static native int getJoystickPOVs(byte joystickNum, short[] povsArray);
public static native int getJoystickButtons(byte joystickNum, ByteBuffer count);
public static native void getAllJoystickData(
float[] axesArray, byte[] rawAxesArray, short[] povsArray, long[] buttonsAndMetadata);
public static native int setJoystickOutputs(
byte joystickNum, int outputs, short leftRumble, short rightRumble);
public static native int getJoystickIsXbox(byte joystickNum);
public static native int getJoystickType(byte joystickNum);
public static native String getJoystickName(byte joystickNum);
public static native int getJoystickAxisType(byte joystickNum, byte axis);
public static native double getMatchTime();
public static native int getMatchInfo(MatchInfoData info);
public static native int sendError(
boolean isError,
int errorCode,
boolean isLVCode,
String details,
String location,
String callStack,
boolean printMsg);
public static native int sendConsoleLine(String line);
public static native void refreshDSData();
public static native void provideNewDataEventHandle(int handle);
public static native void removeNewDataEventHandle(int handle);
public static native boolean getOutputsActive();
private DriverStationJNI() {}
}

View File

@@ -4,7 +4,6 @@
package edu.wpi.first.hal;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
@@ -13,8 +12,6 @@ import java.util.List;
* .
*/
public final class HAL extends JNIWrapper {
public static native void waitForDSData();
public static native boolean initialize(int timeout, int mode);
public static native void shutdown();
@@ -116,15 +113,13 @@ public final class HAL extends JNIWrapper {
}
}
public static native void observeUserProgramStarting();
public static native boolean getBrownedOut();
public static native void observeUserProgramDisabled();
public static native boolean getSystemActive();
public static native void observeUserProgramAutonomous();
public static native int getPortWithModule(byte module, byte channel);
public static native void observeUserProgramTeleop();
public static native void observeUserProgramTest();
public static native int getPort(byte channel);
public static void report(int resource, int instanceNumber) {
report(resource, instanceNumber, 0, "");
@@ -134,114 +129,9 @@ public final class HAL extends JNIWrapper {
report(resource, instanceNumber, context, "");
}
/**
* Report the usage of a resource of interest.
*
* <p>Original signature: <code>uint32_t report(tResourceType, uint8_t, uint8_t, const
* char*)</code>
*
* @param resource one of the values in the tResourceType above (max value 51).
* @param instanceNumber an index that identifies the resource instance.
* @param context an optional additional context number for some cases (such as module number).
* Set to 0 to omit.
* @param feature a string to be included describing features in use on a specific resource.
* Setting the same resource more than once allows you to change the feature string.
* @return TODO
*/
public static native int report(int resource, int instanceNumber, int context, String feature);
public static native int nativeGetControlWord();
/**
* Get the current DriverStation control word.
*
* @param controlWord Storage for control word.
*/
public static void getControlWord(ControlWord controlWord) {
int word = nativeGetControlWord();
controlWord.update(
(word & 1) != 0,
((word >> 1) & 1) != 0,
((word >> 2) & 1) != 0,
((word >> 3) & 1) != 0,
((word >> 4) & 1) != 0,
((word >> 5) & 1) != 0);
public static int report(int resource, int instanceNumber, int context, String feature) {
return DriverStationJNI.report(resource, instanceNumber, context, feature);
}
private static native int nativeGetAllianceStation();
/**
* Get the alliance station.
*
* @return The alliance station.
*/
public static AllianceStationID getAllianceStation() {
switch (nativeGetAllianceStation()) {
case 0:
return AllianceStationID.Red1;
case 1:
return AllianceStationID.Red2;
case 2:
return AllianceStationID.Red3;
case 3:
return AllianceStationID.Blue1;
case 4:
return AllianceStationID.Blue2;
case 5:
return AllianceStationID.Blue3;
default:
return null;
}
}
public static native boolean isNewControlData();
public static native void releaseDSMutex();
public static native boolean waitForDSDataTimeout(double timeout);
public static final int kMaxJoystickAxes = 12;
public static final int kMaxJoystickPOVs = 12;
public static native short getJoystickAxes(byte joystickNum, float[] axesArray);
public static native short getJoystickPOVs(byte joystickNum, short[] povsArray);
public static native int getJoystickButtons(byte joystickNum, ByteBuffer count);
public static native int setJoystickOutputs(
byte joystickNum, int outputs, short leftRumble, short rightRumble);
public static native int getJoystickIsXbox(byte joystickNum);
public static native int getJoystickType(byte joystickNum);
public static native String getJoystickName(byte joystickNum);
public static native int getJoystickAxisType(byte joystickNum, byte axis);
public static native double getMatchTime();
public static native boolean getSystemActive();
public static native boolean getBrownedOut();
public static native int getMatchInfo(MatchInfoData info);
public static native int sendError(
boolean isError,
int errorCode,
boolean isLVCode,
String details,
String location,
String callStack,
boolean printMsg);
public static native int sendConsoleLine(String line);
public static native int getPortWithModule(byte module, byte channel);
public static native int getPort(byte channel);
private HAL() {}
}