Skip to content

Commit 63d9e94

Browse files
authored
[hal] HAL_RefreshDSData: Zero out control word on DS disconnect, use cache in sim (#6380)
1 parent 0ad6b3a commit 63d9e94

File tree

4 files changed

+59
-27
lines changed

4 files changed

+59
-27
lines changed

hal/src/main/native/athena/FRCDriverStation.cpp

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -539,15 +539,25 @@ HAL_Bool HAL_RefreshDSData(void) {
539539
}
540540
// If newest state shows we have a DS attached, just use the
541541
// control word out of the cache, As it will be the one in sync
542-
// with the data. Otherwise use the state that shows disconnected.
543-
if (controlWord.dsAttached) {
544-
newestControlWord = currentRead->controlWord;
545-
} else {
546-
// Zero out the control word. When the DS has never been connected
547-
// this returns garbage. And there is no way we can detect that.
548-
std::memset(&controlWord, 0, sizeof(controlWord));
549-
newestControlWord = controlWord;
542+
// with the data. If no data has been updated, at this point,
543+
// and a DS wasn't attached previously, this will still return
544+
// a zeroed out control word, with is the correct state for
545+
// no new data.
546+
if (!controlWord.dsAttached) {
547+
// If the DS is not attached, we need to zero out the control word.
548+
// This is because HAL_RefreshDSData is called asynchronously from
549+
// the DS data. The dsAttached variable comes directly from netcomm
550+
// and could be updated before the caches are. If that happens,
551+
// we would end up returning the previous cached control word,
552+
// which is out of sync with the current control word and could
553+
// break invariants such as which alliance station is in used.
554+
// Also, when the DS has never been connected the rest of the fields
555+
// in control word are garbage, so we also need to zero out in that
556+
// case too
557+
std::memset(&currentRead->controlWord, 0,
558+
sizeof(currentRead->controlWord));
550559
}
560+
newestControlWord = currentRead->controlWord;
551561
}
552562

553563
uint32_t mask = tcpMask.exchange(0);

hal/src/main/native/sim/DriverStation.cpp

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ struct JoystickDataCache {
4444
HAL_JoystickButtons buttons[kJoystickPorts];
4545
HAL_AllianceStationID allianceStation;
4646
double matchTime;
47+
HAL_ControlWord controlWord;
4748
};
4849
static_assert(std::is_standard_layout_v<JoystickDataCache>);
4950
// static_assert(std::is_trivial_v<JoystickDataCache>);
@@ -65,6 +66,16 @@ void JoystickDataCache::Update() {
6566
}
6667
allianceStation = SimDriverStationData->allianceStationId;
6768
matchTime = SimDriverStationData->matchTime;
69+
70+
HAL_ControlWord tmpControlWord;
71+
std::memset(&tmpControlWord, 0, sizeof(tmpControlWord));
72+
tmpControlWord.enabled = SimDriverStationData->enabled;
73+
tmpControlWord.autonomous = SimDriverStationData->autonomous;
74+
tmpControlWord.test = SimDriverStationData->test;
75+
tmpControlWord.eStop = SimDriverStationData->eStop;
76+
tmpControlWord.fmsAttached = SimDriverStationData->fmsAttached;
77+
tmpControlWord.dsAttached = SimDriverStationData->dsAttached;
78+
this->controlWord = tmpControlWord;
6879
}
6980

7081
#define CHECK_JOYSTICK_NUMBER(stickNum) \
@@ -322,20 +333,32 @@ HAL_Bool HAL_RefreshDSData(void) {
322333
if (gShutdown) {
323334
return false;
324335
}
325-
HAL_ControlWord controlWord;
326-
std::memset(&controlWord, 0, sizeof(controlWord));
327-
controlWord.enabled = SimDriverStationData->enabled;
328-
controlWord.autonomous = SimDriverStationData->autonomous;
329-
controlWord.test = SimDriverStationData->test;
330-
controlWord.eStop = SimDriverStationData->eStop;
331-
controlWord.fmsAttached = SimDriverStationData->fmsAttached;
332-
controlWord.dsAttached = SimDriverStationData->dsAttached;
336+
bool dsAttached = SimDriverStationData->dsAttached;
333337
std::scoped_lock lock{driverStation->cacheMutex};
334338
JoystickDataCache* prev = currentCache.exchange(nullptr);
335339
if (prev != nullptr) {
336340
currentRead = prev;
337341
}
338-
newestControlWord = controlWord;
342+
// If newest state shows we have a DS attached, just use the
343+
// control word out of the cache, As it will be the one in sync
344+
// with the data. If no data has been updated, at this point,
345+
// and a DS wasn't attached previously, this will still return
346+
// a zeroed out control word, with is the correct state for
347+
// no new data.
348+
if (!dsAttached) {
349+
// If the DS is not attached, we need to zero out the control word.
350+
// This is because HAL_RefreshDSData is called asynchronously from
351+
// the DS data. The dsAttached variable comes directly from netcomm
352+
// and could be updated before the caches are. If that happens,
353+
// we would end up returning the previous cached control word,
354+
// which is out of sync with the current control word and could
355+
// break invariants such as which alliance station is in used.
356+
// Also, when the DS has never been connected the rest of the fields
357+
// in control word are garbage, so we also need to zero out in that
358+
// case too
359+
std::memset(&currentRead->controlWord, 0, sizeof(currentRead->controlWord));
360+
}
361+
newestControlWord = currentRead->controlWord;
339362
return prev != nullptr;
340363
}
341364

@@ -369,6 +392,7 @@ void NewDriverStationData() {
369392
if (gShutdown) {
370393
return;
371394
}
395+
SimDriverStationData->dsAttached = true;
372396
cacheToUpdate->Update();
373397

374398
JoystickDataCache* given = cacheToUpdate;
@@ -382,7 +406,6 @@ void NewDriverStationData() {
382406
}
383407
lastGiven = given;
384408

385-
SimDriverStationData->dsAttached = true;
386409
driverStation->newDataEvents.Wakeup();
387410
SimDriverStationData->CallNewDataCallbacks();
388411
}

wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/button/RobotModeTriggersTest.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
import static org.junit.jupiter.api.Assertions.assertTrue;
88

9-
import edu.wpi.first.wpilibj.DriverStation;
109
import edu.wpi.first.wpilibj.simulation.DriverStationSim;
1110
import edu.wpi.first.wpilibj2.command.CommandTestBase;
1211
import org.junit.jupiter.api.Test;
@@ -18,7 +17,7 @@ void autonomousTest() {
1817
DriverStationSim.setAutonomous(true);
1918
DriverStationSim.setTest(false);
2019
DriverStationSim.setEnabled(true);
21-
DriverStation.refreshData();
20+
DriverStationSim.notifyNewData();
2221
Trigger auto = RobotModeTriggers.autonomous();
2322
assertTrue(auto.getAsBoolean());
2423
}
@@ -29,7 +28,7 @@ void teleopTest() {
2928
DriverStationSim.setAutonomous(false);
3029
DriverStationSim.setTest(false);
3130
DriverStationSim.setEnabled(true);
32-
DriverStation.refreshData();
31+
DriverStationSim.notifyNewData();
3332
Trigger teleop = RobotModeTriggers.teleop();
3433
assertTrue(teleop.getAsBoolean());
3534
}
@@ -40,7 +39,7 @@ void testModeTest() {
4039
DriverStationSim.setAutonomous(false);
4140
DriverStationSim.setTest(true);
4241
DriverStationSim.setEnabled(true);
43-
DriverStation.refreshData();
42+
DriverStationSim.notifyNewData();
4443
Trigger test = RobotModeTriggers.test();
4544
assertTrue(test.getAsBoolean());
4645
}
@@ -51,7 +50,7 @@ void disabledTest() {
5150
DriverStationSim.setAutonomous(false);
5251
DriverStationSim.setTest(false);
5352
DriverStationSim.setEnabled(false);
54-
DriverStation.refreshData();
53+
DriverStationSim.notifyNewData();
5554
Trigger disabled = RobotModeTriggers.disabled();
5655
assertTrue(disabled.getAsBoolean());
5756
}

wpilibNewCommands/src/test/native/cpp/frc2/command/button/RobotModeTriggersTest.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ TEST(RobotModeTriggersTest, Autonomous) {
1818
DriverStationSim::SetAutonomous(true);
1919
DriverStationSim::SetTest(false);
2020
DriverStationSim::SetEnabled(true);
21-
frc::DriverStation::RefreshData();
21+
DriverStationSim::NotifyNewData();
2222
Trigger autonomous = RobotModeTriggers::Autonomous();
2323
EXPECT_TRUE(autonomous.Get());
2424
}
@@ -28,7 +28,7 @@ TEST(RobotModeTriggersTest, Teleop) {
2828
DriverStationSim::SetAutonomous(false);
2929
DriverStationSim::SetTest(false);
3030
DriverStationSim::SetEnabled(true);
31-
frc::DriverStation::RefreshData();
31+
DriverStationSim::NotifyNewData();
3232
Trigger teleop = RobotModeTriggers::Teleop();
3333
EXPECT_TRUE(teleop.Get());
3434
}
@@ -38,7 +38,7 @@ TEST(RobotModeTriggersTest, Disabled) {
3838
DriverStationSim::SetAutonomous(false);
3939
DriverStationSim::SetTest(false);
4040
DriverStationSim::SetEnabled(false);
41-
frc::DriverStation::RefreshData();
41+
DriverStationSim::NotifyNewData();
4242
Trigger disabled = RobotModeTriggers::Disabled();
4343
EXPECT_TRUE(disabled.Get());
4444
}
@@ -48,7 +48,7 @@ TEST(RobotModeTriggersTest, TestMode) {
4848
DriverStationSim::SetAutonomous(false);
4949
DriverStationSim::SetTest(true);
5050
DriverStationSim::SetEnabled(true);
51-
frc::DriverStation::RefreshData();
51+
DriverStationSim::NotifyNewData();
5252
Trigger test = RobotModeTriggers::Test();
5353
EXPECT_TRUE(test.Get());
5454
}

0 commit comments

Comments
 (0)