Skip to content

Commit 561078c

Browse files
authored
[hal] Cache sim TCP data to update during HAL_RefreshDSData (#7360)
1 parent d312bcc commit 561078c

File tree

3 files changed

+92
-36
lines changed

3 files changed

+92
-36
lines changed

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

Lines changed: 87 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ struct FRCDriverStation {
5555
~FRCDriverStation() { gShutdown = true; }
5656
wpi::EventVector newDataEvents;
5757
wpi::mutex cacheMutex;
58+
wpi::mutex tcpCacheMutex;
5859
};
5960
} // namespace
6061

@@ -90,6 +91,29 @@ static std::atomic<JoystickDataCache*> currentCache{nullptr};
9091
static JoystickDataCache* lastGiven = &caches[1];
9192
static JoystickDataCache* cacheToUpdate = &caches[2];
9293

94+
namespace {
95+
struct TcpCache {
96+
TcpCache() { std::memset(this, 0, sizeof(*this)); }
97+
void Update();
98+
void CloneTo(TcpCache* other) { std::memcpy(other, this, sizeof(*this)); }
99+
100+
HAL_MatchInfo matchInfo;
101+
HAL_JoystickDescriptor descriptors[HAL_kMaxJoysticks];
102+
};
103+
static_assert(std::is_standard_layout_v<TcpCache>);
104+
} // namespace
105+
106+
static TcpCache tcpCache;
107+
static TcpCache tcpCurrent;
108+
109+
void TcpCache::Update() {
110+
SimDriverStationData->GetMatchInfo(&matchInfo);
111+
112+
for (int i = 0; i < HAL_kMaxJoysticks; i++) {
113+
SimDriverStationData->GetJoystickDescriptor(i, &descriptors[i]);
114+
}
115+
}
116+
93117
static ::FRCDriverStation* driverStation;
94118

95119
namespace hal::init {
@@ -256,32 +280,47 @@ void HAL_GetAllJoystickData(HAL_JoystickAxes* axes, HAL_JoystickPOVs* povs,
256280
int32_t HAL_GetJoystickDescriptor(int32_t joystickNum,
257281
HAL_JoystickDescriptor* desc) {
258282
CHECK_JOYSTICK_NUMBER(joystickNum);
259-
SimDriverStationData->GetJoystickDescriptor(joystickNum, desc);
283+
std::scoped_lock lock{driverStation->tcpCacheMutex};
284+
*desc = tcpCurrent.descriptors[joystickNum];
260285
return 0;
261286
}
262287

263288
HAL_Bool HAL_GetJoystickIsXbox(int32_t joystickNum) {
264-
HAL_JoystickDescriptor desc;
265-
SimDriverStationData->GetJoystickDescriptor(joystickNum, &desc);
266-
return desc.isXbox;
289+
HAL_JoystickDescriptor joystickDesc;
290+
if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
291+
return 0;
292+
} else {
293+
return joystickDesc.isXbox;
294+
}
267295
}
268296

269297
int32_t HAL_GetJoystickType(int32_t joystickNum) {
270-
HAL_JoystickDescriptor desc;
271-
SimDriverStationData->GetJoystickDescriptor(joystickNum, &desc);
272-
return desc.type;
298+
HAL_JoystickDescriptor joystickDesc;
299+
if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
300+
return -1;
301+
} else {
302+
return joystickDesc.type;
303+
}
273304
}
274305

275306
void HAL_GetJoystickName(struct WPI_String* name, int32_t joystickNum) {
276-
HAL_JoystickDescriptor desc;
277-
SimDriverStationData->GetJoystickDescriptor(joystickNum, &desc);
278-
size_t len = std::strlen(desc.name);
307+
HAL_JoystickDescriptor joystickDesc;
308+
const char* cName = joystickDesc.name;
309+
if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
310+
cName = "";
311+
}
312+
auto len = std::strlen(cName);
279313
auto write = WPI_AllocateString(name, len);
280-
std::memcpy(write, desc.name, len);
314+
std::memcpy(write, cName, len);
281315
}
282316

283317
int32_t HAL_GetJoystickAxisType(int32_t joystickNum, int32_t axis) {
284-
return 0;
318+
HAL_JoystickDescriptor joystickDesc;
319+
if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
320+
return -1;
321+
} else {
322+
return joystickDesc.axisTypes[axis];
323+
}
285324
}
286325

287326
int32_t HAL_SetJoystickOutputs(int32_t joystickNum, int64_t outputs,
@@ -300,7 +339,8 @@ double HAL_GetMatchTime(int32_t* status) {
300339
}
301340

302341
int32_t HAL_GetMatchInfo(HAL_MatchInfo* info) {
303-
SimDriverStationData->GetMatchInfo(info);
342+
std::scoped_lock lock{driverStation->tcpCacheMutex};
343+
*info = tcpCurrent.matchInfo;
304344
return 0;
305345
}
306346

@@ -329,31 +369,42 @@ HAL_Bool HAL_RefreshDSData(void) {
329369
return false;
330370
}
331371
bool dsAttached = SimDriverStationData->dsAttached;
332-
std::scoped_lock lock{driverStation->cacheMutex};
333-
JoystickDataCache* prev = currentCache.exchange(nullptr);
334-
if (prev != nullptr) {
335-
currentRead = prev;
372+
JoystickDataCache* prev;
373+
{
374+
std::scoped_lock lock{driverStation->cacheMutex};
375+
prev = currentCache.exchange(nullptr);
376+
if (prev != nullptr) {
377+
currentRead = prev;
378+
}
379+
// If newest state shows we have a DS attached, just use the
380+
// control word out of the cache, As it will be the one in sync
381+
// with the data. If no data has been updated, at this point,
382+
// and a DS wasn't attached previously, this will still return
383+
// a zeroed out control word, with is the correct state for
384+
// no new data.
385+
if (!dsAttached) {
386+
// If the DS is not attached, we need to zero out the control word.
387+
// This is because HAL_RefreshDSData is called asynchronously from
388+
// the DS data. The dsAttached variable comes directly from netcomm
389+
// and could be updated before the caches are. If that happens,
390+
// we would end up returning the previous cached control word,
391+
// which is out of sync with the current control word and could
392+
// break invariants such as which alliance station is in used.
393+
// Also, when the DS has never been connected the rest of the fields
394+
// in control word are garbage, so we also need to zero out in that
395+
// case too
396+
std::memset(&currentRead->controlWord, 0,
397+
sizeof(currentRead->controlWord));
398+
}
399+
newestControlWord = currentRead->controlWord;
336400
}
337-
// If newest state shows we have a DS attached, just use the
338-
// control word out of the cache, As it will be the one in sync
339-
// with the data. If no data has been updated, at this point,
340-
// and a DS wasn't attached previously, this will still return
341-
// a zeroed out control word, with is the correct state for
342-
// no new data.
343-
if (!dsAttached) {
344-
// If the DS is not attached, we need to zero out the control word.
345-
// This is because HAL_RefreshDSData is called asynchronously from
346-
// the DS data. The dsAttached variable comes directly from netcomm
347-
// and could be updated before the caches are. If that happens,
348-
// we would end up returning the previous cached control word,
349-
// which is out of sync with the current control word and could
350-
// break invariants such as which alliance station is in used.
351-
// Also, when the DS has never been connected the rest of the fields
352-
// in control word are garbage, so we also need to zero out in that
353-
// case too
354-
std::memset(&currentRead->controlWord, 0, sizeof(currentRead->controlWord));
401+
402+
{
403+
tcpCache.Update();
404+
std::scoped_lock tcpLock(driverStation->tcpCacheMutex);
405+
tcpCache.CloneTo(&tcpCurrent);
355406
}
356-
newestControlWord = currentRead->controlWord;
407+
357408
return prev != nullptr;
358409
}
359410

hal/src/test/native/cpp/mockdata/DriverStationDataTest.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ TEST(DriverStationTest, EventInfo) {
131131
info.replayNumber = 42;
132132
HALSIM_SetMatchInfo(&info);
133133

134+
HAL_RefreshDSData();
135+
134136
HAL_MatchInfo dataBack;
135137
HAL_GetMatchInfo(&dataBack);
136138

wpilibj/src/test/java/edu/wpi/first/wpilibj/hal/MatchInfoDataTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import edu.wpi.first.hal.MatchInfoData;
1212
import edu.wpi.first.hal.simulation.DriverStationDataJNI;
1313
import edu.wpi.first.wpilibj.DriverStation.MatchType;
14+
import edu.wpi.first.wpilibj.simulation.DriverStationSim;
1415
import org.junit.jupiter.api.Test;
1516

1617
class MatchInfoDataTest {
@@ -19,6 +20,8 @@ void testSetMatchInfo() {
1920
MatchType matchType = MatchType.Qualification;
2021
DriverStationDataJNI.setMatchInfo("Event Name", "Game Message", 174, 191, matchType.ordinal());
2122

23+
DriverStationSim.notifyNewData();
24+
2225
MatchInfoData outMatchInfo = new MatchInfoData();
2326
DriverStationJNI.getMatchInfo(outMatchInfo);
2427

0 commit comments

Comments
 (0)