2020-12-26 14:12:05 -08:00
|
|
|
// 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.
|
2017-08-18 21:35:53 -07:00
|
|
|
|
2026-01-04 00:41:53 -08:00
|
|
|
#include "wpi/hal/simulation/MockHooks.h"
|
|
|
|
|
|
2020-10-22 20:55:12 -07:00
|
|
|
#include <algorithm>
|
2017-08-18 21:35:53 -07:00
|
|
|
#include <atomic>
|
|
|
|
|
#include <chrono>
|
|
|
|
|
#include <thread>
|
|
|
|
|
|
2026-01-04 00:41:53 -08:00
|
|
|
#include "MockHooksInternal.hpp"
|
|
|
|
|
#include "NotifierInternal.hpp"
|
2025-11-07 19:56:21 -05:00
|
|
|
#include "wpi/hal/simulation/NotifierData.h"
|
2025-11-07 19:57:55 -05:00
|
|
|
#include "wpi/util/print.hpp"
|
2026-01-04 10:22:33 -08:00
|
|
|
#include "wpi/util/timestamp.hpp"
|
2017-08-18 21:35:53 -07:00
|
|
|
|
|
|
|
|
static std::atomic<bool> programStarted{false};
|
2025-12-12 21:25:57 -07:00
|
|
|
static std::atomic<int64_t> programState{0};
|
2017-08-18 21:35:53 -07:00
|
|
|
|
|
|
|
|
static std::atomic<uint64_t> programStartTime{0};
|
2019-11-10 20:54:25 -08:00
|
|
|
static std::atomic<uint64_t> programPauseTime{0};
|
2020-06-29 21:52:23 -07:00
|
|
|
static std::atomic<uint64_t> programStepTime{0};
|
2026-06-06 12:17:14 -07:00
|
|
|
static std::atomic<uint64_t> programStartNotifierAlarmCount{0};
|
2017-08-18 21:35:53 -07:00
|
|
|
|
2025-11-07 20:00:05 -05:00
|
|
|
namespace wpi::hal::init {
|
2020-12-28 12:58:06 -08:00
|
|
|
void InitializeMockHooks() {
|
2026-03-15 15:08:41 -07:00
|
|
|
wpi::util::SetNowImpl(GetMonotonicTime);
|
2020-12-28 12:58:06 -08:00
|
|
|
}
|
2025-11-07 20:00:05 -05:00
|
|
|
} // namespace wpi::hal::init
|
2017-12-10 19:38:53 -08:00
|
|
|
|
2025-11-07 20:00:05 -05:00
|
|
|
namespace wpi::hal {
|
2019-11-10 20:54:25 -08:00
|
|
|
void RestartTiming() {
|
2025-11-07 20:00:05 -05:00
|
|
|
programStartTime = wpi::util::NowDefault();
|
2020-06-29 21:52:23 -07:00
|
|
|
programStepTime = 0;
|
2020-12-28 12:58:06 -08:00
|
|
|
if (programPauseTime != 0) {
|
|
|
|
|
programPauseTime = programStartTime.load();
|
|
|
|
|
}
|
2019-11-10 20:54:25 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PauseTiming() {
|
2020-12-28 12:58:06 -08:00
|
|
|
if (programPauseTime == 0) {
|
2025-11-07 20:00:05 -05:00
|
|
|
programPauseTime = wpi::util::NowDefault();
|
2020-12-28 12:58:06 -08:00
|
|
|
}
|
2019-11-10 20:54:25 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResumeTiming() {
|
|
|
|
|
if (programPauseTime != 0) {
|
2025-11-07 20:00:05 -05:00
|
|
|
programStartTime += wpi::util::NowDefault() - programPauseTime;
|
2019-11-10 20:54:25 -08:00
|
|
|
programPauseTime = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
bool IsTimingPaused() {
|
|
|
|
|
return programPauseTime != 0;
|
|
|
|
|
}
|
2019-11-10 20:54:25 -08:00
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
void StepTiming(uint64_t delta) {
|
|
|
|
|
programStepTime += delta;
|
|
|
|
|
}
|
2017-08-18 21:35:53 -07:00
|
|
|
|
2026-03-15 15:08:41 -07:00
|
|
|
uint64_t GetMonotonicTime() {
|
2019-11-10 20:54:25 -08:00
|
|
|
uint64_t curTime = programPauseTime;
|
2020-12-28 12:58:06 -08:00
|
|
|
if (curTime == 0) {
|
2025-11-07 20:00:05 -05:00
|
|
|
curTime = wpi::util::NowDefault();
|
2020-12-28 12:58:06 -08:00
|
|
|
}
|
2020-06-29 21:52:23 -07:00
|
|
|
return curTime + programStepTime - programStartTime;
|
2017-08-18 21:35:53 -07:00
|
|
|
}
|
|
|
|
|
|
2025-11-29 10:10:01 -08:00
|
|
|
void SetProgramStarted(bool started) {
|
2026-06-06 12:17:14 -07:00
|
|
|
programStartNotifierAlarmCount = GetNotifierAlarmSetCount();
|
2025-11-29 10:10:01 -08:00
|
|
|
programStarted = started;
|
2020-12-28 12:58:06 -08:00
|
|
|
}
|
|
|
|
|
bool GetProgramStarted() {
|
|
|
|
|
return programStarted;
|
|
|
|
|
}
|
2025-11-07 20:00:05 -05:00
|
|
|
} // namespace wpi::hal
|
2017-08-18 21:35:53 -07:00
|
|
|
|
2025-11-07 20:00:05 -05:00
|
|
|
using namespace wpi::hal;
|
2017-08-18 21:35:53 -07:00
|
|
|
|
|
|
|
|
extern "C" {
|
2026-05-29 23:09:25 -07:00
|
|
|
void HALSIM_WaitForProgramStart(HAL_Bool waitForFirstNotifier) {
|
2017-08-18 21:35:53 -07:00
|
|
|
int count = 0;
|
|
|
|
|
while (!programStarted) {
|
|
|
|
|
count++;
|
2025-11-29 10:10:01 -08:00
|
|
|
if (count % 10 == 0) {
|
|
|
|
|
wpi::util::print("Waiting for program start signal: {}\n", count);
|
|
|
|
|
}
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
2017-08-18 21:35:53 -07:00
|
|
|
}
|
2026-05-29 23:09:25 -07:00
|
|
|
|
|
|
|
|
// Frameworks observe program start before arming their first notifier alarm.
|
2026-06-06 12:17:14 -07:00
|
|
|
// Wait for an alarm armed after that program-start edge so a stale notifier
|
|
|
|
|
// cannot satisfy this check.
|
2026-05-29 23:09:25 -07:00
|
|
|
while (waitForFirstNotifier &&
|
2026-06-06 12:17:14 -07:00
|
|
|
(GetNotifierAlarmSetCount() == programStartNotifierAlarmCount ||
|
|
|
|
|
HALSIM_GetNextNotifierTimeout() == UINT64_MAX)) {
|
2026-05-29 23:09:25 -07:00
|
|
|
count++;
|
|
|
|
|
if (count % 10 == 0) {
|
|
|
|
|
wpi::util::print("Waiting for first notifier alarm: {}\n", count);
|
|
|
|
|
}
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
|
|
|
}
|
2017-08-18 21:35:53 -07:00
|
|
|
}
|
|
|
|
|
|
2025-11-29 10:10:01 -08:00
|
|
|
void HALSIM_SetProgramStarted(HAL_Bool started) {
|
|
|
|
|
SetProgramStarted(started);
|
2020-12-28 12:58:06 -08:00
|
|
|
}
|
2017-08-18 21:35:53 -07:00
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
HAL_Bool HALSIM_GetProgramStarted(void) {
|
|
|
|
|
return GetProgramStarted();
|
|
|
|
|
}
|
2018-07-26 01:30:29 -07:00
|
|
|
|
2025-12-12 21:25:57 -07:00
|
|
|
void HALSIM_SetProgramState(HAL_ControlWord controlWord) {
|
|
|
|
|
programState = controlWord.value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HALSIM_GetProgramState(HAL_ControlWord* controlWord) {
|
|
|
|
|
controlWord->value = programState;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
void HALSIM_RestartTiming(void) {
|
|
|
|
|
RestartTiming();
|
|
|
|
|
}
|
2019-11-10 20:54:25 -08:00
|
|
|
|
|
|
|
|
void HALSIM_PauseTiming(void) {
|
|
|
|
|
PauseTiming();
|
|
|
|
|
PauseNotifiers();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HALSIM_ResumeTiming(void) {
|
|
|
|
|
ResumeTiming();
|
|
|
|
|
ResumeNotifiers();
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
HAL_Bool HALSIM_IsTimingPaused(void) {
|
|
|
|
|
return IsTimingPaused();
|
|
|
|
|
}
|
2019-11-10 20:54:25 -08:00
|
|
|
|
|
|
|
|
void HALSIM_StepTiming(uint64_t delta) {
|
2020-10-15 20:18:15 -07:00
|
|
|
WaitNotifiers();
|
2020-10-22 20:55:12 -07:00
|
|
|
|
|
|
|
|
while (delta > 0) {
|
2026-03-15 15:08:41 -07:00
|
|
|
uint64_t curTime = HAL_GetMonotonicTime();
|
2020-10-22 20:55:12 -07:00
|
|
|
uint64_t nextTimeout = HALSIM_GetNextNotifierTimeout();
|
2026-05-29 23:10:02 -07:00
|
|
|
// If a notifier is already due, process it at the current simulated time
|
|
|
|
|
// instead of underflowing nextTimeout - curTime.
|
|
|
|
|
uint64_t step =
|
|
|
|
|
nextTimeout <= curTime ? 0 : (std::min)(delta, nextTimeout - curTime);
|
2020-10-22 20:55:12 -07:00
|
|
|
|
|
|
|
|
StepTiming(step);
|
|
|
|
|
delta -= step;
|
|
|
|
|
|
|
|
|
|
WakeupWaitNotifiers();
|
2026-05-29 23:10:02 -07:00
|
|
|
|
|
|
|
|
// Guard against notifiers that keep rearming at or before the same time.
|
|
|
|
|
if (step == 0 && HALSIM_GetNextNotifierTimeout() <= curTime) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-10-22 20:55:12 -07:00
|
|
|
}
|
2020-09-27 13:27:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HALSIM_StepTimingAsync(uint64_t delta) {
|
2019-11-10 20:54:25 -08:00
|
|
|
StepTiming(delta);
|
|
|
|
|
WakeupNotifiers();
|
|
|
|
|
}
|
2017-10-16 19:56:08 -07:00
|
|
|
} // extern "C"
|