add WallTime và WallTimer
This commit is contained in:
parent
35b77e9fa2
commit
750dc94c61
|
|
@ -52,6 +52,7 @@ add_library(${PROJECT_NAME} SHARED
|
||||||
src/rate.cpp
|
src/rate.cpp
|
||||||
src/time.cpp
|
src/time.cpp
|
||||||
src/timer.cpp
|
src/timer.cpp
|
||||||
|
src/wall_timer.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(BUILDING_WITH_CATKIN)
|
if(BUILDING_WITH_CATKIN)
|
||||||
|
|
@ -151,3 +152,12 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/test/duration.cpp)
|
||||||
GTest::Main
|
GTest::Main
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Standalone WallTime test (header-only)
|
||||||
|
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/test/walltime_standalone_test.cpp)
|
||||||
|
add_executable(${PROJECT_NAME}_walltime_standalone_test test/walltime_standalone_test.cpp)
|
||||||
|
target_include_directories(${PROJECT_NAME}_walltime_standalone_test PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||||
|
)
|
||||||
|
target_compile_features(${PROJECT_NAME}_walltime_standalone_test PUBLIC cxx_std_17)
|
||||||
|
endif()
|
||||||
|
|
|
||||||
66
README.md
66
README.md
|
|
@ -98,7 +98,70 @@ Rate tự động tính toán thời gian còn lại trong mỗi chu kỳ và sl
|
||||||
- Có thể quản lý nhiều timer đồng thời
|
- Có thể quản lý nhiều timer đồng thời
|
||||||
- Cung cấp thông tin chi tiết về timing để debug và monitoring
|
- Cung cấp thông tin chi tiết về timing để debug và monitoring
|
||||||
|
|
||||||
### 5. WallRate - Rate dùng thời gian thực
|
### 5. WallTime - Thời gian thực (Wall-clock Time)
|
||||||
|
|
||||||
|
`WallTime` đại diện cho thời gian thực từ hệ thống, luôn sử dụng wall-clock time và không bị ảnh hưởng bởi simulated time.
|
||||||
|
|
||||||
|
**Đặc điểm:**
|
||||||
|
- Luôn sử dụng thời gian thực từ system clock
|
||||||
|
- Không cần khởi tạo (`Time::init()`)
|
||||||
|
- Không bao giờ throw exception
|
||||||
|
- Không bị ảnh hưởng bởi simulated time
|
||||||
|
- Thread-safe và cross-platform
|
||||||
|
|
||||||
|
**Khác biệt với Time:**
|
||||||
|
- `Time` có thể sử dụng simulated time (hữu ích cho testing)
|
||||||
|
- `WallTime` luôn dùng thời gian thực, không phụ thuộc vào simulated time
|
||||||
|
- `Time` cần khởi tạo, `WallTime` không cần
|
||||||
|
|
||||||
|
**Ứng dụng:**
|
||||||
|
- Đo thời gian thực thi (profiling/benchmarking)
|
||||||
|
- Timeout và thời gian chờ thực tế
|
||||||
|
- Logging với timestamp thực
|
||||||
|
- Giao tiếp với hardware cần timing chính xác
|
||||||
|
- Performance monitoring
|
||||||
|
|
||||||
|
### 6. WallTimer - Hẹn giờ định kỳ với thời gian thực
|
||||||
|
|
||||||
|
`WallTimer` tương tự như `Timer` nhưng sử dụng wall-clock time thay vì simulated time. Đây là công cụ mạnh mẽ cho các tác vụ cần timing chính xác trong thời gian thực.
|
||||||
|
|
||||||
|
**Đặc điểm:**
|
||||||
|
- Chạy trong thread riêng, không chặn thread chính
|
||||||
|
- Luôn sử dụng wall-clock time (không bị ảnh hưởng bởi simulated time)
|
||||||
|
- Có thể là one-shot (chỉ chạy một lần) hoặc repeating (lặp lại)
|
||||||
|
- Callback nhận thông tin chi tiết về timing (WallTimerEvent)
|
||||||
|
- Có thể start/stop và điều chỉnh period động
|
||||||
|
- Tự động cleanup khi hủy
|
||||||
|
|
||||||
|
**Khác biệt với Timer:**
|
||||||
|
- `Timer` sử dụng `Time`/`Duration` (có thể bị ảnh hưởng bởi simulated time)
|
||||||
|
- `WallTimer` sử dụng `WallTime`/`WallDuration` (luôn dùng thời gian thực)
|
||||||
|
- `Timer` phù hợp cho ROS messages và simulation
|
||||||
|
- `WallTimer` phù hợp cho performance monitoring, hardware interfaces, và real-time operations
|
||||||
|
|
||||||
|
**WallTimerEvent chứa thông tin:**
|
||||||
|
- Thời gian thực tế khi callback được gọi (wall-clock time)
|
||||||
|
- Thời gian mong đợi khi callback được gọi (wall-clock time)
|
||||||
|
- Thời gian của callback trước đó (wall-clock time)
|
||||||
|
- Khoảng thời gian giữa các callback (để phát hiện drift)
|
||||||
|
|
||||||
|
**Ứng dụng:**
|
||||||
|
- Performance monitoring và profiling
|
||||||
|
- Real-time deadlines và timeouts
|
||||||
|
- Hardware interfaces cần timing chính xác
|
||||||
|
- Benchmarking và measurement
|
||||||
|
- Background tasks cần chạy theo thời gian thực
|
||||||
|
|
||||||
|
**Ví dụ:**
|
||||||
|
```cpp
|
||||||
|
// Đo thời gian thực thi
|
||||||
|
robot::WallTime start = robot::WallTime::now();
|
||||||
|
doSomething();
|
||||||
|
robot::WallDuration elapsed = robot::WallTime::now() - start;
|
||||||
|
std::cout << "Took " << elapsed.toSec() << " seconds" << std::endl;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. WallRate - Rate dùng thời gian thực
|
||||||
|
|
||||||
`WallRate` tương tự như `Rate` nhưng luôn sử dụng thời gian thực (wall-clock time) thay vì simulated time.
|
`WallRate` tương tự như `Rate` nhưng luôn sử dụng thời gian thực (wall-clock time) thay vì simulated time.
|
||||||
|
|
||||||
|
|
@ -218,6 +281,7 @@ Sử dụng simulated time để chạy các test cases với timing cụ thể,
|
||||||
|
|
||||||
## Tài liệu tham khảo
|
## Tài liệu tham khảo
|
||||||
|
|
||||||
|
- `WALLTIME_USAGE.md` - Hướng dẫn chi tiết về WallTime với các ví dụ và use cases
|
||||||
- `TIMER_USAGE.md` - Hướng dẫn chi tiết về cách sử dụng Timer với các ví dụ
|
- `TIMER_USAGE.md` - Hướng dẫn chi tiết về cách sử dụng Timer với các ví dụ
|
||||||
- `TIMER_EVENT_EXPLANATION.md` - Giải thích chi tiết về TimerEvent structure và các trường dữ liệu
|
- `TIMER_EVENT_EXPLANATION.md` - Giải thích chi tiết về TimerEvent structure và các trường dữ liệu
|
||||||
- `CHANGELOG.rst` - Lịch sử thay đổi và version history
|
- `CHANGELOG.rst` - Lịch sử thay đổi và version history
|
||||||
|
|
|
||||||
256
WALLTIMER_USAGE.md
Normal file
256
WALLTIMER_USAGE.md
Normal file
|
|
@ -0,0 +1,256 @@
|
||||||
|
# robot::WallTimer Usage Guide
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
`robot::WallTimer` is a class similar to `ros::WallTimer` that allows you to call a callback function at a specified rate using **wall-clock time**. It creates a separate thread that periodically invokes your callback function.
|
||||||
|
|
||||||
|
**Key Difference from Timer:**
|
||||||
|
- `Timer` uses `Time` and `Duration` (can be affected by simulated time)
|
||||||
|
- `WallTimer` uses `WallTime` and `WallDuration` (always uses real wall-clock time)
|
||||||
|
|
||||||
|
This makes `WallTimer` ideal for:
|
||||||
|
- Performance monitoring and profiling
|
||||||
|
- Real-time deadlines and timeouts
|
||||||
|
- Hardware interfaces requiring precise timing
|
||||||
|
- Benchmarking and measurement
|
||||||
|
- Any task that needs to run at real-world intervals
|
||||||
|
|
||||||
|
## Basic Usage
|
||||||
|
|
||||||
|
### Simple WallTimer Example
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <robot/wall_timer.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
void myCallback(const robot::WallTimerEvent& event)
|
||||||
|
{
|
||||||
|
std::cout << "WallTimer fired! Current wall time: "
|
||||||
|
<< event.current_real.toSec() << std::endl;
|
||||||
|
std::cout << "Time since last callback: "
|
||||||
|
<< event.last_duration.toSec() << " seconds" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// Create a timer that fires every 1 second (wall-clock time)
|
||||||
|
robot::WallTimer timer(
|
||||||
|
robot::WallDuration(1.0), // Period: 1 second
|
||||||
|
myCallback, // Callback function
|
||||||
|
false, // Not one-shot (repeats)
|
||||||
|
true // Auto-start
|
||||||
|
);
|
||||||
|
|
||||||
|
// Timer is now running...
|
||||||
|
// Do other work here
|
||||||
|
|
||||||
|
// Sleep for 5 seconds to see timer fire multiple times
|
||||||
|
robot::WallDuration(5.0).sleep();
|
||||||
|
|
||||||
|
// Stop the timer
|
||||||
|
timer.stop();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using with Class Methods
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <robot/wall_timer.h>
|
||||||
|
|
||||||
|
class PerformanceMonitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void startMonitoring()
|
||||||
|
{
|
||||||
|
// Create timer with member function callback
|
||||||
|
timer_ = std::make_unique<robot::WallTimer>(
|
||||||
|
robot::WallDuration(0.5), // 2 Hz (every 0.5 seconds)
|
||||||
|
[this](const robot::WallTimerEvent& event) {
|
||||||
|
this->monitorCallback(event);
|
||||||
|
},
|
||||||
|
false, // Repeating
|
||||||
|
true // Auto-start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void monitorCallback(const robot::WallTimerEvent& event)
|
||||||
|
{
|
||||||
|
// Do periodic performance monitoring here
|
||||||
|
// This always uses real wall-clock time, not simulated time
|
||||||
|
std::cout << "Performance check at real time: "
|
||||||
|
<< event.current_real.toSec() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopMonitoring()
|
||||||
|
{
|
||||||
|
if (timer_)
|
||||||
|
{
|
||||||
|
timer_->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<robot::WallTimer> timer_;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### One-Shot WallTimer
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <robot/wall_timer.h>
|
||||||
|
|
||||||
|
void delayedAction(const robot::WallTimerEvent& event)
|
||||||
|
{
|
||||||
|
std::cout << "Delayed action executed after 5 seconds (real time)" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// Create a one-shot timer that fires once after 5 seconds (wall-clock)
|
||||||
|
robot::WallTimer timer(
|
||||||
|
robot::WallDuration(5.0), // Wait 5 seconds
|
||||||
|
delayedAction, // Callback
|
||||||
|
true, // One-shot
|
||||||
|
true // Auto-start
|
||||||
|
);
|
||||||
|
|
||||||
|
// Wait for timer to fire
|
||||||
|
robot::WallDuration(6.0).sleep();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## WallTimerEvent Structure
|
||||||
|
|
||||||
|
The `WallTimerEvent` structure passed to your callback contains:
|
||||||
|
|
||||||
|
- `current_real`: The actual wall-clock time when the callback was called
|
||||||
|
- `current_expected`: The expected wall-clock time when the callback should have been called
|
||||||
|
- `last_real`: The actual wall-clock time of the previous callback
|
||||||
|
- `last_expected`: The expected wall-clock time of the previous callback
|
||||||
|
- `last_duration`: The wall-clock duration between the last two callbacks
|
||||||
|
|
||||||
|
All times are in wall-clock time (real time), not simulated time.
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
### Constructor
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
WallTimer(const WallDuration& period,
|
||||||
|
const Callback& callback,
|
||||||
|
bool oneshot = false,
|
||||||
|
bool autostart = true);
|
||||||
|
```
|
||||||
|
|
||||||
|
- `period`: Time between callbacks (wall-clock duration)
|
||||||
|
- `callback`: Function to call (signature: `void(const WallTimerEvent&)`)
|
||||||
|
- `oneshot`: If true, timer fires only once
|
||||||
|
- `autostart`: If true, timer starts automatically
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
|
||||||
|
- `void start()`: Start the timer. Does nothing if already started.
|
||||||
|
- `void stop()`: Stop the timer. Once this returns, no more callbacks will be called.
|
||||||
|
- `void setPeriod(const WallDuration& period, bool reset = true)`: Set the timer period. If `reset` is true, timer ignores elapsed time and next callback occurs at now()+period.
|
||||||
|
- `bool hasStarted()`: Check if timer is running
|
||||||
|
- `bool isValid()`: Check if timer has a valid callback
|
||||||
|
- `bool hasPending()`: Check if timer has any pending events to call
|
||||||
|
- `bool isOneShot()`: Check if timer is one-shot
|
||||||
|
- `void setOneShot(bool oneshot)`: Set one-shot mode
|
||||||
|
- `WallDuration getPeriod()`: Get the timer period
|
||||||
|
|
||||||
|
### Operators
|
||||||
|
|
||||||
|
- `operator void*()`: Conversion to bool (for checking validity)
|
||||||
|
- `operator==(const WallTimer&)`: Equality comparison
|
||||||
|
- `operator!=(const WallTimer&)`: Inequality comparison
|
||||||
|
- `operator<(const WallTimer&)`: Less-than comparison (for ordering in containers)
|
||||||
|
|
||||||
|
## Comparison: Timer vs WallTimer
|
||||||
|
|
||||||
|
| Feature | `Timer` | `WallTimer` |
|
||||||
|
|---------|---------|-------------|
|
||||||
|
| Time Type | `Time` / `Duration` | `WallTime` / `WallDuration` |
|
||||||
|
| Simulated Time | Can be affected | Never affected |
|
||||||
|
| Use Case | ROS message timestamps, simulation | Performance, real-time, hardware |
|
||||||
|
| Initialization | Requires `Time::init()` | No initialization needed |
|
||||||
|
| Exception | Can throw if time not initialized | Never throws |
|
||||||
|
|
||||||
|
## Example: Performance Profiling
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <robot/wall_timer.h>
|
||||||
|
|
||||||
|
class Profiler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void startProfiling()
|
||||||
|
{
|
||||||
|
profile_timer_ = std::make_unique<robot::WallTimer>(
|
||||||
|
robot::WallDuration(1.0), // Profile every 1 second
|
||||||
|
[this](const robot::WallTimerEvent& event) {
|
||||||
|
this->profileCallback(event);
|
||||||
|
},
|
||||||
|
false, // Repeating
|
||||||
|
true // Auto-start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void profileCallback(const robot::WallTimerEvent& event)
|
||||||
|
{
|
||||||
|
// Measure actual wall-clock time between callbacks
|
||||||
|
double actual_interval = event.last_duration.toSec();
|
||||||
|
double expected_interval = 1.0;
|
||||||
|
|
||||||
|
if (actual_interval > expected_interval * 1.1) // 10% tolerance
|
||||||
|
{
|
||||||
|
std::cout << "WARNING: Timer drift detected! "
|
||||||
|
<< "Expected: " << expected_interval
|
||||||
|
<< "s, Actual: " << actual_interval << "s" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<robot::WallTimer> profile_timer_;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example: Dynamic Period Adjustment
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <robot/wall_timer.h>
|
||||||
|
|
||||||
|
robot::WallTimer timer(robot::WallDuration(1.0), myCallback);
|
||||||
|
|
||||||
|
// Later, change the period
|
||||||
|
timer.setPeriod(robot::WallDuration(0.5), true); // Reset to 0.5s, reset timer
|
||||||
|
|
||||||
|
// Or change without reset
|
||||||
|
timer.setPeriod(robot::WallDuration(2.0), false); // Change to 2s, don't reset
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Use WallTimer for real-time operations**: When you need precise wall-clock timing
|
||||||
|
2. **Use Timer for ROS messages**: When working with ROS messages that use simulated time
|
||||||
|
3. **Always stop timers**: Make sure to call `stop()` before destroying the timer
|
||||||
|
4. **Handle exceptions in callbacks**: Exceptions in callbacks are caught to prevent timer thread crashes
|
||||||
|
5. **Check validity**: Use `isValid()` or `operator void*()` to check if timer has a callback
|
||||||
|
|
||||||
|
## Thread Safety
|
||||||
|
|
||||||
|
`WallTimer` is thread-safe:
|
||||||
|
- Multiple threads can safely call `start()`, `stop()`, `setPeriod()`, etc.
|
||||||
|
- The callback is executed in a separate thread
|
||||||
|
- All internal state is protected by mutexes
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [ROS WallTimer Documentation](http://docs.ros.org/en/indigo/api/roscpp/html/classros_1_1WallTimer.html)
|
||||||
|
- `TIMER_USAGE.md` - Guide for `robot::Timer` (uses simulated time)
|
||||||
|
- `WALLTIME_USAGE.md` - Guide for `WallTime` and `WallDuration`
|
||||||
|
|
||||||
362
WALLTIME_LIBRARY.md
Normal file
362
WALLTIME_LIBRARY.md
Normal file
|
|
@ -0,0 +1,362 @@
|
||||||
|
# WallTime Standalone Library
|
||||||
|
|
||||||
|
## Tổng quan
|
||||||
|
|
||||||
|
`walltime.h` là một **header-only library** độc lập cung cấp các class `WallTime` và `WallDuration` để làm việc với thời gian thực (wall-clock time). Thư viện này có thể được sử dụng độc lập hoặc như một phần của `robot_time` library.
|
||||||
|
|
||||||
|
## Đặc điểm
|
||||||
|
|
||||||
|
- ✅ **Header-only**: Chỉ cần include một file header
|
||||||
|
- ✅ **Không cần khởi tạo**: Có thể sử dụng ngay
|
||||||
|
- ✅ **Thread-safe**: An toàn khi sử dụng trong multi-threaded applications
|
||||||
|
- ✅ **Cross-platform**: Hoạt động trên Linux, Windows, macOS
|
||||||
|
- ✅ **Nanosecond precision**: Độ chính xác nanosecond
|
||||||
|
- ✅ **Không phụ thuộc**: Chỉ sử dụng C++ standard library
|
||||||
|
|
||||||
|
## Cài đặt
|
||||||
|
|
||||||
|
### Cách 1: Sử dụng trực tiếp (Header-only)
|
||||||
|
|
||||||
|
Chỉ cần copy file `walltime.h` vào project của bạn:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include "walltime.h" // hoặc đường dẫn tương đối
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cách 2: Sử dụng với robot_time library
|
||||||
|
|
||||||
|
Nếu bạn đã có `robot_time` library:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <robot/walltime.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
### WallTime
|
||||||
|
|
||||||
|
#### Constructors
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
robot::WallTime t1; // Default: (0, 0)
|
||||||
|
robot::WallTime t2(1234567890, 123456789); // (sec, nsec)
|
||||||
|
robot::WallTime t3(1234567890.123456789); // From seconds (double)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Static Methods
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Get current wall-clock time
|
||||||
|
robot::WallTime now = robot::WallTime::now();
|
||||||
|
|
||||||
|
// Sleep until a specific time
|
||||||
|
robot::WallTime target = robot::WallTime::now() + robot::WallDuration(5.0);
|
||||||
|
bool success = robot::WallTime::sleepUntil(target);
|
||||||
|
|
||||||
|
// Check if using system time (always true)
|
||||||
|
bool is_system = robot::WallTime::isSystemTime(); // Always returns true
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Instance Methods
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
robot::WallTime t = robot::WallTime::now();
|
||||||
|
|
||||||
|
// Convert to seconds
|
||||||
|
double seconds = t.toSec();
|
||||||
|
|
||||||
|
// Convert to nanoseconds
|
||||||
|
uint64_t nanoseconds = t.toNSec();
|
||||||
|
|
||||||
|
// Initialize from seconds
|
||||||
|
t.fromSec(1234567890.123456789);
|
||||||
|
|
||||||
|
// Initialize from nanoseconds
|
||||||
|
t.fromNSec(1234567890123456789ULL);
|
||||||
|
|
||||||
|
// Check if zero
|
||||||
|
bool is_zero = t.isZero();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Arithmetic Operations
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
robot::WallTime t = robot::WallTime::now();
|
||||||
|
robot::WallDuration d(5.0);
|
||||||
|
|
||||||
|
// Addition
|
||||||
|
robot::WallTime future = t + d;
|
||||||
|
|
||||||
|
// Subtraction
|
||||||
|
robot::WallTime past = t - d;
|
||||||
|
|
||||||
|
// Duration subtraction
|
||||||
|
robot::WallDuration elapsed = future - t;
|
||||||
|
|
||||||
|
// Compound assignment
|
||||||
|
t += d;
|
||||||
|
t -= d;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Comparison Operators
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
robot::WallTime t1, t2;
|
||||||
|
|
||||||
|
bool eq = (t1 == t2);
|
||||||
|
bool ne = (t1 != t2);
|
||||||
|
bool lt = (t1 < t2);
|
||||||
|
bool gt = (t1 > t2);
|
||||||
|
bool le = (t1 <= t2);
|
||||||
|
bool ge = (t1 >= t2);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Constants
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
robot::WallTime::ZERO // (0, 0)
|
||||||
|
robot::WallTime::MAX // Maximum representable time
|
||||||
|
robot::WallTime::MIN // Minimum representable time
|
||||||
|
```
|
||||||
|
|
||||||
|
### WallDuration
|
||||||
|
|
||||||
|
#### Constructors
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
robot::WallDuration d1; // Default: (0, 0)
|
||||||
|
robot::WallDuration d2(5, 123456789); // (sec, nsec)
|
||||||
|
robot::WallDuration d3(5.123456789); // From seconds (double)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Instance Methods
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
robot::WallDuration d(5.0);
|
||||||
|
|
||||||
|
// Convert to seconds
|
||||||
|
double seconds = d.toSec();
|
||||||
|
|
||||||
|
// Convert to nanoseconds
|
||||||
|
int64_t nanoseconds = d.toNSec();
|
||||||
|
|
||||||
|
// Initialize from seconds
|
||||||
|
d.fromSec(5.123456789);
|
||||||
|
|
||||||
|
// Initialize from nanoseconds
|
||||||
|
d.fromNSec(5123456789LL);
|
||||||
|
|
||||||
|
// Sleep for this duration
|
||||||
|
bool success = d.sleep();
|
||||||
|
|
||||||
|
// Check if zero
|
||||||
|
bool is_zero = d.isZero();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Arithmetic Operations
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
robot::WallDuration d1(5.0);
|
||||||
|
robot::WallDuration d2(3.0);
|
||||||
|
|
||||||
|
// Addition
|
||||||
|
robot::WallDuration sum = d1 + d2;
|
||||||
|
|
||||||
|
// Subtraction
|
||||||
|
robot::WallDuration diff = d1 - d2;
|
||||||
|
|
||||||
|
// Negation
|
||||||
|
robot::WallDuration neg = -d1;
|
||||||
|
|
||||||
|
// Multiplication
|
||||||
|
robot::WallDuration scaled = d1 * 2.0;
|
||||||
|
|
||||||
|
// Compound assignment
|
||||||
|
d1 += d2;
|
||||||
|
d1 -= d2;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Comparison Operators
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
robot::WallDuration d1, d2;
|
||||||
|
|
||||||
|
bool eq = (d1 == d2);
|
||||||
|
bool ne = (d1 != d2);
|
||||||
|
bool lt = (d1 < d2);
|
||||||
|
bool gt = (d1 > d2);
|
||||||
|
bool le = (d1 <= d2);
|
||||||
|
bool ge = (d1 >= d2);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Constants
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
robot::WallDuration::ZERO // (0, 0)
|
||||||
|
robot::WallDuration::MAX // Maximum representable duration
|
||||||
|
robot::WallDuration::MIN // Minimum representable duration (negative)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ví dụ sử dụng
|
||||||
|
|
||||||
|
### Ví dụ 1: Đo thời gian thực thi
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <robot/walltime.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
void measureExecutionTime()
|
||||||
|
{
|
||||||
|
robot::WallTime start = robot::WallTime::now();
|
||||||
|
|
||||||
|
// Do some work
|
||||||
|
for (int i = 0; i < 1000000; ++i)
|
||||||
|
{
|
||||||
|
// ... your code ...
|
||||||
|
}
|
||||||
|
|
||||||
|
robot::WallTime end = robot::WallTime::now();
|
||||||
|
robot::WallDuration elapsed = end - start;
|
||||||
|
|
||||||
|
std::cout << "Execution time: " << elapsed.toSec() << " seconds" << std::endl;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ví dụ 2: Timeout
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <robot/walltime.h>
|
||||||
|
|
||||||
|
bool doWorkWithTimeout(double timeout_seconds)
|
||||||
|
{
|
||||||
|
robot::WallTime start = robot::WallTime::now();
|
||||||
|
robot::WallDuration timeout(timeout_seconds);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (robot::WallTime::now() - start > timeout)
|
||||||
|
{
|
||||||
|
std::cout << "Timeout!" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do work
|
||||||
|
if (workComplete())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
robot::WallDuration(0.1).sleep(); // Sleep 100ms
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ví dụ 3: Sleep until
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <robot/walltime.h>
|
||||||
|
|
||||||
|
void scheduleTask()
|
||||||
|
{
|
||||||
|
// Schedule task 5 seconds from now
|
||||||
|
robot::WallTime target = robot::WallTime::now() + robot::WallDuration(5.0);
|
||||||
|
|
||||||
|
// Do other work...
|
||||||
|
|
||||||
|
// Sleep until target time
|
||||||
|
robot::WallTime::sleepUntil(target);
|
||||||
|
|
||||||
|
// Execute task
|
||||||
|
executeTask();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ví dụ 4: Rate limiting
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <robot/walltime.h>
|
||||||
|
|
||||||
|
class RateLimiter
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
robot::WallTime last_time_;
|
||||||
|
robot::WallDuration min_interval_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RateLimiter(double min_rate_hz)
|
||||||
|
: min_interval_(1.0 / min_rate_hz)
|
||||||
|
, last_time_(robot::WallTime::now())
|
||||||
|
{}
|
||||||
|
|
||||||
|
void wait()
|
||||||
|
{
|
||||||
|
robot::WallTime now = robot::WallTime::now();
|
||||||
|
robot::WallDuration elapsed = now - last_time_;
|
||||||
|
|
||||||
|
if (elapsed < min_interval_)
|
||||||
|
{
|
||||||
|
robot::WallDuration remaining = min_interval_ - elapsed;
|
||||||
|
remaining.sleep();
|
||||||
|
}
|
||||||
|
|
||||||
|
last_time_ = robot::WallTime::now();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
RateLimiter limiter(10.0); // 10 Hz max
|
||||||
|
while (running)
|
||||||
|
{
|
||||||
|
limiter.wait();
|
||||||
|
doWork();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## So sánh với robot_time::WallTime
|
||||||
|
|
||||||
|
Thư viện standalone này tương thích với `robot::WallTime` trong `robot_time` library:
|
||||||
|
|
||||||
|
| Đặc điểm | Standalone `walltime.h` | `robot_time::WallTime` |
|
||||||
|
|----------|------------------------|------------------------|
|
||||||
|
| Header-only | ✅ Có | ❌ Cần link library |
|
||||||
|
| Dependencies | Chỉ C++ stdlib | Cần robot_time |
|
||||||
|
| API | Giống nhau | Giống nhau |
|
||||||
|
| Performance | Tương đương | Tương đương |
|
||||||
|
| Kích thước | Nhỏ hơn | Lớn hơn |
|
||||||
|
|
||||||
|
## Build và Test
|
||||||
|
|
||||||
|
### Compile test file
|
||||||
|
|
||||||
|
```bash
|
||||||
|
g++ -std=c++17 -I./include test/walltime_standalone_test.cpp -o walltime_test
|
||||||
|
./walltime_test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sử dụng trong CMake
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
# Option 1: Header-only (không cần link)
|
||||||
|
target_include_directories(your_target PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
|
|
||||||
|
# Option 2: Với robot_time library
|
||||||
|
find_package(robot_time REQUIRED)
|
||||||
|
target_link_libraries(your_target PRIVATE robot_time)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- **C++17** hoặc cao hơn
|
||||||
|
- **Compiler**: GCC 7+, Clang 5+, MSVC 2017+
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
BSD License - Tương tự như robot_time library
|
||||||
|
|
||||||
|
## Tài liệu tham khảo
|
||||||
|
|
||||||
|
- `WALLTIME_USAGE.md` - Hướng dẫn chi tiết về WallTime
|
||||||
|
- `examples/walltime_example.cpp` - Các ví dụ sử dụng
|
||||||
|
- `test/walltime_standalone_test.cpp` - Unit tests
|
||||||
|
|
||||||
87
WALLTIME_QUICKSTART.md
Normal file
87
WALLTIME_QUICKSTART.md
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
# WallTime - Quick Start Guide
|
||||||
|
|
||||||
|
## Sử dụng nhanh
|
||||||
|
|
||||||
|
### 1. Include header
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <robot/walltime.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Đo thời gian thực thi
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
robot::WallTime start = robot::WallTime::now();
|
||||||
|
// ... your code ...
|
||||||
|
robot::WallDuration elapsed = robot::WallTime::now() - start;
|
||||||
|
std::cout << "Took " << elapsed.toSec() << " seconds" << std::endl;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Timeout
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
robot::WallTime deadline = robot::WallTime::now() + robot::WallDuration(5.0);
|
||||||
|
while (robot::WallTime::now() < deadline) {
|
||||||
|
// Do work
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Sleep
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Sleep for 1 second
|
||||||
|
robot::WallDuration(1.0).sleep();
|
||||||
|
|
||||||
|
// Sleep until specific time
|
||||||
|
robot::WallTime target = robot::WallTime::now() + robot::WallDuration(5.0);
|
||||||
|
robot::WallTime::sleepUntil(target);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Đặc điểm
|
||||||
|
|
||||||
|
- ✅ **Header-only**: Chỉ cần include một file
|
||||||
|
- ✅ **Không cần khởi tạo**: Dùng ngay
|
||||||
|
- ✅ **Thread-safe**: An toàn với multi-threading
|
||||||
|
- ✅ **Cross-platform**: Linux, Windows, macOS
|
||||||
|
|
||||||
|
## Tài liệu đầy đủ
|
||||||
|
|
||||||
|
- `WALLTIME_LIBRARY.md` - API reference đầy đủ
|
||||||
|
- `WALLTIME_USAGE.md` - Hướng dẫn chi tiết và ví dụ
|
||||||
|
- `examples/walltime_example.cpp` - Nhiều ví dụ thực tế
|
||||||
|
|
||||||
|
## So sánh với Time
|
||||||
|
|
||||||
|
| | `Time` | `WallTime` |
|
||||||
|
|---|---|---|
|
||||||
|
| Cần init | ✅ Có | ❌ Không |
|
||||||
|
| Simulated time | ✅ Có | ❌ Không |
|
||||||
|
| Dùng cho ROS messages | ✅ Nên | ❌ Không nên |
|
||||||
|
| Đo thời gian thực | ❌ Không phù hợp | ✅ Phù hợp |
|
||||||
|
|
||||||
|
## Ví dụ hoàn chỉnh
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <robot/walltime.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// Đo thời gian
|
||||||
|
robot::WallTime start = robot::WallTime::now();
|
||||||
|
|
||||||
|
// Simulate work
|
||||||
|
robot::WallDuration(0.5).sleep();
|
||||||
|
|
||||||
|
robot::WallDuration elapsed = robot::WallTime::now() - start;
|
||||||
|
std::cout << "Elapsed: " << elapsed.toSec() << "s" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Compile:
|
||||||
|
```bash
|
||||||
|
g++ -std=c++17 -I./include your_file.cpp -o your_program
|
||||||
|
```
|
||||||
|
|
||||||
466
WALLTIME_USAGE.md
Normal file
466
WALLTIME_USAGE.md
Normal file
|
|
@ -0,0 +1,466 @@
|
||||||
|
# WallTime - Hướng dẫn sử dụng
|
||||||
|
|
||||||
|
## Tổng quan
|
||||||
|
|
||||||
|
`WallTime` là class đại diện cho **thời gian thực (wall-clock time)** trong thư viện `robot_time`. Khác với `Time`, `WallTime` luôn sử dụng thời gian thực từ hệ thống và **không bị ảnh hưởng bởi simulated time**.
|
||||||
|
|
||||||
|
## Đặc điểm chính
|
||||||
|
|
||||||
|
### 1. Luôn sử dụng thời gian thực
|
||||||
|
- `WallTime` luôn lấy thời gian từ system clock
|
||||||
|
- Không phụ thuộc vào simulated time
|
||||||
|
- Không cần khởi tạo (`Time::init()`)
|
||||||
|
- Không bao giờ throw exception
|
||||||
|
|
||||||
|
### 2. Không cần khởi tạo
|
||||||
|
```cpp
|
||||||
|
// Time - CẦN khởi tạo
|
||||||
|
robot::Time::init(); // Phải gọi trước
|
||||||
|
robot::Time t = robot::Time::now(); // Có thể throw exception
|
||||||
|
|
||||||
|
// WallTime - KHÔNG cần khởi tạo
|
||||||
|
robot::WallTime t = robot::WallTime::now(); // Luôn hoạt động
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Không bị ảnh hưởng bởi simulated time
|
||||||
|
```cpp
|
||||||
|
// Trong simulation với /use_sim_time = true
|
||||||
|
robot::Time t1 = robot::Time::now(); // Trả về simulated time
|
||||||
|
robot::WallTime t2 = robot::WallTime::now(); // Vẫn trả về thời gian thực
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
### Constructors
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Default constructor - khởi tạo với giá trị 0
|
||||||
|
robot::WallTime t1;
|
||||||
|
|
||||||
|
// Constructor với giây và nanosecond
|
||||||
|
robot::WallTime t2(1234567890, 123456789);
|
||||||
|
|
||||||
|
// Constructor từ số thực (giây)
|
||||||
|
robot::WallTime t3(1234567890.123456789);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Static Methods
|
||||||
|
|
||||||
|
#### `static WallTime now()`
|
||||||
|
Trả về thời gian hiện tại (wall-clock time).
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
robot::WallTime current_time = robot::WallTime::now();
|
||||||
|
```
|
||||||
|
|
||||||
|
**Đặc điểm:**
|
||||||
|
- Luôn trả về thời gian thực
|
||||||
|
- Không cần khởi tạo trước
|
||||||
|
- Không throw exception
|
||||||
|
- Thread-safe
|
||||||
|
|
||||||
|
#### `static bool sleepUntil(const WallTime& end)`
|
||||||
|
Ngủ cho đến khi đạt đến thời điểm `end`.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
robot::WallTime end_time = robot::WallTime::now() + robot::WallDuration(5.0);
|
||||||
|
bool success = robot::WallTime::sleepUntil(end_time);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Return value:**
|
||||||
|
- `true`: Đã ngủ đến thời điểm mong muốn
|
||||||
|
- `false`: Bị gián đoạn hoặc có lỗi
|
||||||
|
|
||||||
|
#### `static bool isSystemTime()`
|
||||||
|
Luôn trả về `true` vì `WallTime` luôn sử dụng system time.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
bool is_system = robot::WallTime::isSystemTime(); // Luôn là true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Methods kế thừa từ `TimeBase`
|
||||||
|
|
||||||
|
#### Chuyển đổi đơn vị
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
robot::WallTime t(1234567890, 123456789);
|
||||||
|
|
||||||
|
// Chuyển sang giây (double)
|
||||||
|
double seconds = t.toSec(); // 1234567890.123456789
|
||||||
|
|
||||||
|
// Chuyển sang nanosecond
|
||||||
|
uint64_t nanoseconds = t.toNSec();
|
||||||
|
|
||||||
|
// Khởi tạo từ giây
|
||||||
|
t.fromSec(1234567890.123456789);
|
||||||
|
|
||||||
|
// Khởi tạo từ nanosecond
|
||||||
|
t.fromNSec(1234567890123456789ULL);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### So sánh
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
robot::WallTime t1 = robot::WallTime::now();
|
||||||
|
robot::WallDuration d(1.0);
|
||||||
|
robot::WallTime t2 = t1 + d;
|
||||||
|
|
||||||
|
// So sánh
|
||||||
|
bool is_equal = (t1 == t2); // false
|
||||||
|
bool is_less = (t1 < t2); // true
|
||||||
|
bool is_greater = (t1 > t2); // false
|
||||||
|
bool is_less_equal = (t1 <= t2); // true
|
||||||
|
bool is_greater_equal = (t1 >= t2); // false
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Phép toán với WallDuration
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
robot::WallTime t = robot::WallTime::now();
|
||||||
|
robot::WallDuration d(5.0);
|
||||||
|
|
||||||
|
// Cộng duration
|
||||||
|
robot::WallTime future = t + d;
|
||||||
|
|
||||||
|
// Trừ duration
|
||||||
|
robot::WallTime past = t - d;
|
||||||
|
|
||||||
|
// Trừ hai WallTime để được WallDuration
|
||||||
|
robot::WallDuration elapsed = future - t;
|
||||||
|
|
||||||
|
// Compound assignment
|
||||||
|
t += d; // t = t + d
|
||||||
|
t -= d; // t = t - d
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Kiểm tra giá trị
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
robot::WallTime t;
|
||||||
|
|
||||||
|
// Kiểm tra có phải zero không
|
||||||
|
bool is_zero = t.isZero(); // true
|
||||||
|
|
||||||
|
// Kiểm tra các giá trị đặc biệt
|
||||||
|
bool is_max = (t == robot::WallTime::MAX);
|
||||||
|
bool is_min = (t == robot::WallTime::MIN);
|
||||||
|
bool is_zero_const = (t == robot::WallTime::ZERO);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Constants
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
robot::WallTime::MAX // Giá trị lớn nhất
|
||||||
|
robot::WallTime::MIN // Giá trị nhỏ nhất
|
||||||
|
robot::WallTime::ZERO // Zero (0, 0)
|
||||||
|
robot::WallTime::UNINITIALIZED // Uninitialized (0, 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
## So sánh với `Time`
|
||||||
|
|
||||||
|
| Đặc điểm | `Time` | `WallTime` |
|
||||||
|
|----------|--------|------------|
|
||||||
|
| Nguồn thời gian | Simulated hoặc wall-clock | Luôn wall-clock |
|
||||||
|
| Cần khởi tạo | Có (`Time::init()`) | Không |
|
||||||
|
| Có thể throw exception | Có (nếu chưa init) | Không |
|
||||||
|
| Ảnh hưởng bởi sim time | Có | Không |
|
||||||
|
| Dùng trong ROS messages | Nên dùng | Không nên |
|
||||||
|
| Đo thời gian thực thi | Không phù hợp | Phù hợp |
|
||||||
|
| Timeout thực tế | Không phù hợp | Phù hợp |
|
||||||
|
| Logging với timestamp | Có thể | Phù hợp |
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
|
||||||
|
### 1. Đo thời gian thực thi (Profiling/Benchmarking)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Đo thời gian thực thi của một hàm
|
||||||
|
robot::WallTime start = robot::WallTime::now();
|
||||||
|
|
||||||
|
// Thực hiện công việc
|
||||||
|
doSomething();
|
||||||
|
|
||||||
|
robot::WallTime end = robot::WallTime::now();
|
||||||
|
robot::WallDuration elapsed = end - start;
|
||||||
|
|
||||||
|
std::cout << "Thời gian thực thi: " << elapsed.toSec() << " giây" << std::endl;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ví dụ thực tế từ codebase:**
|
||||||
|
```cpp
|
||||||
|
// Trong clear_costmap_recovery.cpp
|
||||||
|
robot::WallTime t0 = robot::WallTime::now();
|
||||||
|
clear(global_costmap_);
|
||||||
|
ROS_DEBUG("Global costmap cleared in %fs",
|
||||||
|
(robot::WallTime::now() - t0).toSec());
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Timeout và thời gian chờ
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Đặt timeout cho một thao tác
|
||||||
|
robot::WallTime start = robot::WallTime::now();
|
||||||
|
robot::WallDuration timeout(5.0); // 5 giây
|
||||||
|
|
||||||
|
while (!operation_complete)
|
||||||
|
{
|
||||||
|
if (robot::WallTime::now() - start > timeout)
|
||||||
|
{
|
||||||
|
std::cout << "Timeout!" << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thực hiện công việc
|
||||||
|
doWork();
|
||||||
|
|
||||||
|
// Sleep một chút
|
||||||
|
robot::WallDuration(0.1).sleep();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Logging với timestamp thực
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void logMessage(const std::string& msg)
|
||||||
|
{
|
||||||
|
robot::WallTime now = robot::WallTime::now();
|
||||||
|
std::cout << "[" << now.toSec() << "] " << msg << std::endl;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Giao tiếp với hardware
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Giao tiếp với hardware cần timing chính xác
|
||||||
|
robot::WallTime last_update = robot::WallTime::now();
|
||||||
|
robot::WallDuration update_interval(0.02); // 50Hz
|
||||||
|
|
||||||
|
while (running)
|
||||||
|
{
|
||||||
|
// Cập nhật hardware
|
||||||
|
updateHardware();
|
||||||
|
|
||||||
|
// Đợi đến lần cập nhật tiếp theo
|
||||||
|
robot::WallTime next_update = last_update + update_interval;
|
||||||
|
robot::WallTime::sleepUntil(next_update);
|
||||||
|
last_update = robot::WallTime::now();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Performance monitoring
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class PerformanceMonitor
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
robot::WallTime start_time_;
|
||||||
|
std::vector<robot::WallDuration> measurements_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void start()
|
||||||
|
{
|
||||||
|
start_time_ = robot::WallTime::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
void record()
|
||||||
|
{
|
||||||
|
robot::WallTime now = robot::WallTime::now();
|
||||||
|
robot::WallDuration elapsed = now - start_time_;
|
||||||
|
measurements_.push_back(elapsed);
|
||||||
|
start_time_ = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getAverageTime()
|
||||||
|
{
|
||||||
|
if (measurements_.empty()) return 0.0;
|
||||||
|
|
||||||
|
double total = 0.0;
|
||||||
|
for (const auto& d : measurements_)
|
||||||
|
{
|
||||||
|
total += d.toSec();
|
||||||
|
}
|
||||||
|
return total / measurements_.size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### 1. Sử dụng WallTime cho đo đạc thời gian thực
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// ✅ ĐÚNG - Dùng WallTime để đo thời gian thực thi
|
||||||
|
robot::WallTime start = robot::WallTime::now();
|
||||||
|
processData();
|
||||||
|
robot::WallDuration elapsed = robot::WallTime::now() - start;
|
||||||
|
|
||||||
|
// ❌ SAI - Dùng Time có thể không chính xác trong simulation
|
||||||
|
robot::Time start = robot::Time::now();
|
||||||
|
processData();
|
||||||
|
robot::Duration elapsed = robot::Time::now() - start;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Sử dụng Time cho ROS messages
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// ✅ ĐÚNG - Dùng Time cho ROS messages
|
||||||
|
geometry_msgs::PoseStamped msg;
|
||||||
|
msg.header.stamp = robot::Time::now();
|
||||||
|
|
||||||
|
// ❌ SAI - Không nên dùng WallTime cho ROS messages
|
||||||
|
msg.header.stamp = robot::WallTime::now(); // Không tương thích
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Kết hợp Time và WallTime
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Sử dụng Time cho ROS operations
|
||||||
|
robot::Time ros_time = robot::Time::now();
|
||||||
|
publishMessage(msg);
|
||||||
|
|
||||||
|
// Sử dụng WallTime cho performance monitoring
|
||||||
|
robot::WallTime wall_start = robot::WallTime::now();
|
||||||
|
expensiveOperation();
|
||||||
|
robot::WallDuration wall_elapsed = robot::WallTime::now() - wall_start;
|
||||||
|
std::cout << "Operation took " << wall_elapsed.toSec() << " seconds" << std::endl;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Timeout với WallTime
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// ✅ ĐÚNG - Dùng WallTime cho timeout thực tế
|
||||||
|
robot::WallTime deadline = robot::WallTime::now() + robot::WallDuration(10.0);
|
||||||
|
while (robot::WallTime::now() < deadline)
|
||||||
|
{
|
||||||
|
if (tryOperation())
|
||||||
|
break;
|
||||||
|
robot::WallDuration(0.1).sleep();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ví dụ hoàn chỉnh
|
||||||
|
|
||||||
|
### Ví dụ 1: Đo thời gian thực thi của nhiều operations
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <robot/time.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
void benchmarkOperations()
|
||||||
|
{
|
||||||
|
std::vector<std::string> operations = {"operation1", "operation2", "operation3"};
|
||||||
|
|
||||||
|
for (const auto& op_name : operations)
|
||||||
|
{
|
||||||
|
robot::WallTime start = robot::WallTime::now();
|
||||||
|
|
||||||
|
// Thực hiện operation (giả lập)
|
||||||
|
performOperation(op_name);
|
||||||
|
|
||||||
|
robot::WallDuration elapsed = robot::WallTime::now() - start;
|
||||||
|
std::cout << op_name << " took " << elapsed.toSec() << " seconds" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ví dụ 2: Rate limiting với WallTime
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class RateLimiter
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
robot::WallTime last_time_;
|
||||||
|
robot::WallDuration min_interval_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RateLimiter(double min_rate_hz)
|
||||||
|
: min_interval_(1.0 / min_rate_hz)
|
||||||
|
, last_time_(robot::WallTime::now())
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool canProceed()
|
||||||
|
{
|
||||||
|
robot::WallTime now = robot::WallTime::now();
|
||||||
|
robot::WallDuration elapsed = now - last_time_;
|
||||||
|
|
||||||
|
if (elapsed >= min_interval_)
|
||||||
|
{
|
||||||
|
last_time_ = now;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ví dụ 3: Timeout handler
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class TimeoutHandler
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
robot::WallTime start_time_;
|
||||||
|
robot::WallDuration timeout_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TimeoutHandler(double timeout_seconds)
|
||||||
|
: timeout_(timeout_seconds)
|
||||||
|
, start_time_(robot::WallTime::now())
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool isExpired() const
|
||||||
|
{
|
||||||
|
robot::WallTime now = robot::WallTime::now();
|
||||||
|
return (now - start_time_) > timeout_;
|
||||||
|
}
|
||||||
|
|
||||||
|
robot::WallDuration remaining() const
|
||||||
|
{
|
||||||
|
robot::WallTime now = robot::WallTime::now();
|
||||||
|
robot::WallDuration elapsed = now - start_time_;
|
||||||
|
robot::WallDuration remaining = timeout_ - elapsed;
|
||||||
|
return (remaining > robot::WallDuration(0)) ? remaining : robot::WallDuration(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
start_time_ = robot::WallTime::now();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tương thích với ROS
|
||||||
|
|
||||||
|
`robot::WallTime` được thiết kế để tương thích với `ros::WallTime` trong ROS. Các API và behavior đều giống nhau:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// ROS
|
||||||
|
ros::WallTime t1 = ros::WallTime::now();
|
||||||
|
ros::WallDuration d = ros::WallDuration(1.0);
|
||||||
|
ros::WallTime t2 = t1 + d;
|
||||||
|
|
||||||
|
// robot_time (tương đương)
|
||||||
|
robot::WallTime t1 = robot::WallTime::now();
|
||||||
|
robot::WallDuration d = robot::WallDuration(1.0);
|
||||||
|
robot::WallTime t2 = t1 + d;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lưu ý quan trọng
|
||||||
|
|
||||||
|
1. **Không dùng WallTime cho ROS messages**: ROS messages yêu cầu `Time`, không phải `WallTime`
|
||||||
|
|
||||||
|
2. **Precision**: `WallTime` có độ chính xác nanosecond, nhưng độ chính xác thực tế phụ thuộc vào hệ thống
|
||||||
|
|
||||||
|
3. **Thread safety**: Tất cả operations của `WallTime` đều thread-safe
|
||||||
|
|
||||||
|
4. **Performance**: `WallTime::now()` rất nhanh, có thể gọi thường xuyên mà không lo về performance
|
||||||
|
|
||||||
|
5. **Cross-platform**: Hoạt động trên cả Linux và Windows
|
||||||
|
|
||||||
|
## Tài liệu tham khảo
|
||||||
|
|
||||||
|
- `README.md` - Tổng quan về thư viện robot_time
|
||||||
|
- `time.h` - Header file với API đầy đủ
|
||||||
|
- ROS Documentation: http://docs.ros.org/en/diamondback/api/rostime/html/classros_1_1WallTime.html
|
||||||
|
|
||||||
331
examples/walltime_example.cpp
Normal file
331
examples/walltime_example.cpp
Normal file
|
|
@ -0,0 +1,331 @@
|
||||||
|
/*********************************************************************
|
||||||
|
* WallTime Usage Examples
|
||||||
|
*
|
||||||
|
* File này minh họa cách sử dụng WallTime trong các tình huống thực tế
|
||||||
|
*********************************************************************/
|
||||||
|
|
||||||
|
#include <robot/time.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
// Ví dụ 1: Đo thời gian thực thi của một hàm
|
||||||
|
void example1_measureExecutionTime()
|
||||||
|
{
|
||||||
|
std::cout << "\n=== Ví dụ 1: Đo thời gian thực thi ===" << std::endl;
|
||||||
|
|
||||||
|
robot::WallTime start = robot::WallTime::now();
|
||||||
|
|
||||||
|
// Giả lập một công việc tốn thời gian
|
||||||
|
double sum = 0.0;
|
||||||
|
for (int i = 0; i < 1000000; ++i)
|
||||||
|
{
|
||||||
|
sum += std::sin(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
robot::WallTime end = robot::WallTime::now();
|
||||||
|
robot::WallDuration elapsed = end - start;
|
||||||
|
|
||||||
|
std::cout << "Thời gian thực thi: " << elapsed.toSec() << " giây" << std::endl;
|
||||||
|
std::cout << "Thời gian thực thi: " << elapsed.toNSec() << " nanoseconds" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ví dụ 2: Timeout với WallTime
|
||||||
|
void example2_timeout()
|
||||||
|
{
|
||||||
|
std::cout << "\n=== Ví dụ 2: Timeout ===" << std::endl;
|
||||||
|
|
||||||
|
robot::WallTime start = robot::WallTime::now();
|
||||||
|
robot::WallDuration timeout(2.0); // 2 giây timeout
|
||||||
|
|
||||||
|
int iterations = 0;
|
||||||
|
while (robot::WallTime::now() - start < timeout)
|
||||||
|
{
|
||||||
|
iterations++;
|
||||||
|
// Giả lập công việc
|
||||||
|
robot::WallDuration(0.1).sleep();
|
||||||
|
}
|
||||||
|
|
||||||
|
robot::WallDuration actual_elapsed = robot::WallTime::now() - start;
|
||||||
|
std::cout << "Số lần lặp: " << iterations << std::endl;
|
||||||
|
std::cout << "Thời gian thực tế: " << actual_elapsed.toSec() << " giây" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ví dụ 3: So sánh Time và WallTime
|
||||||
|
void example3_compareTimeAndWallTime()
|
||||||
|
{
|
||||||
|
std::cout << "\n=== Ví dụ 3: So sánh Time và WallTime ===" << std::endl;
|
||||||
|
|
||||||
|
// Khởi tạo Time (cần thiết)
|
||||||
|
robot::Time::init();
|
||||||
|
|
||||||
|
robot::Time ros_time = robot::Time::now();
|
||||||
|
robot::WallTime wall_time = robot::WallTime::now();
|
||||||
|
|
||||||
|
std::cout << "ROS Time: " << ros_time.toSec() << std::endl;
|
||||||
|
std::cout << "Wall Time: " << wall_time.toSec() << std::endl;
|
||||||
|
|
||||||
|
// Trong trường hợp không có simulated time, chúng sẽ giống nhau
|
||||||
|
// Nhưng WallTime không cần init()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ví dụ 4: Sleep until một thời điểm cụ thể
|
||||||
|
void example4_sleepUntil()
|
||||||
|
{
|
||||||
|
std::cout << "\n=== Ví dụ 4: Sleep until ===" << std::endl;
|
||||||
|
|
||||||
|
robot::WallTime start = robot::WallTime::now();
|
||||||
|
robot::WallTime target = start + robot::WallDuration(1.5); // 1.5 giây sau
|
||||||
|
|
||||||
|
std::cout << "Bắt đầu: " << start.toSec() << std::endl;
|
||||||
|
std::cout << "Mục tiêu: " << target.toSec() << std::endl;
|
||||||
|
|
||||||
|
bool success = robot::WallTime::sleepUntil(target);
|
||||||
|
|
||||||
|
robot::WallTime end = robot::WallTime::now();
|
||||||
|
robot::WallDuration actual = end - start;
|
||||||
|
|
||||||
|
std::cout << "Kết thúc: " << end.toSec() << std::endl;
|
||||||
|
std::cout << "Thời gian thực tế: " << actual.toSec() << " giây" << std::endl;
|
||||||
|
std::cout << "Thành công: " << (success ? "Có" : "Không") << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ví dụ 5: Benchmark nhiều operations
|
||||||
|
void example5_benchmark()
|
||||||
|
{
|
||||||
|
std::cout << "\n=== Ví dụ 5: Benchmark ===" << std::endl;
|
||||||
|
|
||||||
|
std::vector<std::string> operations = {
|
||||||
|
"Operation A",
|
||||||
|
"Operation B",
|
||||||
|
"Operation C"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& op_name : operations)
|
||||||
|
{
|
||||||
|
robot::WallTime start = robot::WallTime::now();
|
||||||
|
|
||||||
|
// Giả lập công việc với thời gian khác nhau
|
||||||
|
if (op_name == "Operation A")
|
||||||
|
{
|
||||||
|
robot::WallDuration(0.1).sleep();
|
||||||
|
}
|
||||||
|
else if (op_name == "Operation B")
|
||||||
|
{
|
||||||
|
robot::WallDuration(0.2).sleep();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
robot::WallDuration(0.15).sleep();
|
||||||
|
}
|
||||||
|
|
||||||
|
robot::WallDuration elapsed = robot::WallTime::now() - start;
|
||||||
|
std::cout << op_name << ": " << elapsed.toSec() << " giây" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ví dụ 6: Rate limiting với WallTime
|
||||||
|
class RateLimiter
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
robot::WallTime last_time_;
|
||||||
|
robot::WallDuration min_interval_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RateLimiter(double min_rate_hz)
|
||||||
|
: min_interval_(1.0 / min_rate_hz)
|
||||||
|
, last_time_(robot::WallTime::now())
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool canProceed()
|
||||||
|
{
|
||||||
|
robot::WallTime now = robot::WallTime::now();
|
||||||
|
robot::WallDuration elapsed = now - last_time_;
|
||||||
|
|
||||||
|
if (elapsed >= min_interval_)
|
||||||
|
{
|
||||||
|
last_time_ = now;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait()
|
||||||
|
{
|
||||||
|
robot::WallTime now = robot::WallTime::now();
|
||||||
|
robot::WallDuration elapsed = now - last_time_;
|
||||||
|
|
||||||
|
if (elapsed < min_interval_)
|
||||||
|
{
|
||||||
|
robot::WallDuration remaining = min_interval_ - elapsed;
|
||||||
|
remaining.sleep();
|
||||||
|
last_time_ = robot::WallTime::now();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
last_time_ = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void example6_rateLimiter()
|
||||||
|
{
|
||||||
|
std::cout << "\n=== Ví dụ 6: Rate Limiter ===" << std::endl;
|
||||||
|
|
||||||
|
RateLimiter limiter(2.0); // Tối đa 2 lần/giây
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; ++i)
|
||||||
|
{
|
||||||
|
robot::WallTime start = robot::WallTime::now();
|
||||||
|
limiter.wait();
|
||||||
|
robot::WallTime end = robot::WallTime::now();
|
||||||
|
|
||||||
|
robot::WallDuration elapsed = end - start;
|
||||||
|
std::cout << "Lần " << (i+1) << ": " << elapsed.toSec() << " giây" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ví dụ 7: Timeout handler
|
||||||
|
class TimeoutHandler
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
robot::WallTime start_time_;
|
||||||
|
robot::WallDuration timeout_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TimeoutHandler(double timeout_seconds)
|
||||||
|
: timeout_(timeout_seconds)
|
||||||
|
, start_time_(robot::WallTime::now())
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool isExpired() const
|
||||||
|
{
|
||||||
|
robot::WallTime now = robot::WallTime::now();
|
||||||
|
return (now - start_time_) > timeout_;
|
||||||
|
}
|
||||||
|
|
||||||
|
robot::WallDuration remaining() const
|
||||||
|
{
|
||||||
|
robot::WallTime now = robot::WallTime::now();
|
||||||
|
robot::WallDuration elapsed = now - start_time_;
|
||||||
|
robot::WallDuration remaining = timeout_ - elapsed;
|
||||||
|
return (remaining > robot::WallDuration(0)) ? remaining : robot::WallDuration(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
start_time_ = robot::WallTime::now();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void example7_timeoutHandler()
|
||||||
|
{
|
||||||
|
std::cout << "\n=== Ví dụ 7: Timeout Handler ===" << std::endl;
|
||||||
|
|
||||||
|
TimeoutHandler handler(3.0); // 3 giây timeout
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
while (!handler.isExpired() && count < 10)
|
||||||
|
{
|
||||||
|
robot::WallDuration remaining = handler.remaining();
|
||||||
|
std::cout << "Còn lại: " << remaining.toSec() << " giây" << std::endl;
|
||||||
|
|
||||||
|
robot::WallDuration(0.5).sleep();
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handler.isExpired())
|
||||||
|
{
|
||||||
|
std::cout << "Timeout!" << std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << "Hoàn thành trước khi timeout" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ví dụ 8: Phép toán với WallTime và WallDuration
|
||||||
|
void example8_arithmetic()
|
||||||
|
{
|
||||||
|
std::cout << "\n=== Ví dụ 8: Phép toán ===" << std::endl;
|
||||||
|
|
||||||
|
robot::WallTime t1 = robot::WallTime::now();
|
||||||
|
robot::WallDuration d(5.0); // 5 giây
|
||||||
|
|
||||||
|
// Cộng duration
|
||||||
|
robot::WallTime t2 = t1 + d;
|
||||||
|
std::cout << "t1: " << t1.toSec() << std::endl;
|
||||||
|
std::cout << "t2 = t1 + 5s: " << t2.toSec() << std::endl;
|
||||||
|
|
||||||
|
// Trừ duration
|
||||||
|
robot::WallTime t3 = t2 - robot::WallDuration(2.0);
|
||||||
|
std::cout << "t3 = t2 - 2s: " << t3.toSec() << std::endl;
|
||||||
|
|
||||||
|
// Trừ hai WallTime để được WallDuration
|
||||||
|
robot::WallDuration elapsed = t2 - t1;
|
||||||
|
std::cout << "t2 - t1 = " << elapsed.toSec() << " giây" << std::endl;
|
||||||
|
|
||||||
|
// So sánh
|
||||||
|
std::cout << "t1 < t2: " << (t1 < t2) << std::endl;
|
||||||
|
std::cout << "t1 == t3: " << (t1 == t3) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ví dụ 9: Chuyển đổi đơn vị
|
||||||
|
void example9_conversion()
|
||||||
|
{
|
||||||
|
std::cout << "\n=== Ví dụ 9: Chuyển đổi đơn vị ===" << std::endl;
|
||||||
|
|
||||||
|
// Tạo WallTime từ giây
|
||||||
|
robot::WallTime t1(1234567890.123456789);
|
||||||
|
std::cout << "Từ giây: " << t1.toSec() << std::endl;
|
||||||
|
std::cout << "Sang nanosecond: " << t1.toNSec() << std::endl;
|
||||||
|
|
||||||
|
// Tạo từ giây và nanosecond
|
||||||
|
robot::WallTime t2(1234567890, 123456789);
|
||||||
|
std::cout << "Từ sec/nsec: " << t2.toSec() << std::endl;
|
||||||
|
|
||||||
|
// Chuyển đổi
|
||||||
|
double seconds = t2.toSec();
|
||||||
|
uint64_t nanoseconds = t2.toNSec();
|
||||||
|
std::cout << "Giây: " << seconds << std::endl;
|
||||||
|
std::cout << "Nanosecond: " << nanoseconds << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ví dụ 10: Constants
|
||||||
|
void example10_constants()
|
||||||
|
{
|
||||||
|
std::cout << "\n=== Ví dụ 10: Constants ===" << std::endl;
|
||||||
|
|
||||||
|
std::cout << "MAX: " << robot::WallTime::MAX.toSec() << std::endl;
|
||||||
|
std::cout << "MIN: " << robot::WallTime::MIN.toSec() << std::endl;
|
||||||
|
std::cout << "ZERO: " << robot::WallTime::ZERO.toSec() << std::endl;
|
||||||
|
std::cout << "UNINITIALIZED: " << robot::WallTime::UNINITIALIZED.toSec() << std::endl;
|
||||||
|
|
||||||
|
robot::WallTime t;
|
||||||
|
std::cout << "Default WallTime is zero: " << t.isZero() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
std::cout << "========================================" << std::endl;
|
||||||
|
std::cout << "WallTime Usage Examples" << std::endl;
|
||||||
|
std::cout << "========================================" << std::endl;
|
||||||
|
|
||||||
|
example1_measureExecutionTime();
|
||||||
|
example2_timeout();
|
||||||
|
example3_compareTimeAndWallTime();
|
||||||
|
example4_sleepUntil();
|
||||||
|
example5_benchmark();
|
||||||
|
example6_rateLimiter();
|
||||||
|
example7_timeoutHandler();
|
||||||
|
example8_arithmetic();
|
||||||
|
example9_conversion();
|
||||||
|
example10_constants();
|
||||||
|
|
||||||
|
std::cout << "\n========================================" << std::endl;
|
||||||
|
std::cout << "Tất cả ví dụ đã hoàn thành!" << std::endl;
|
||||||
|
std::cout << "========================================" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
229
examples/walltimer_example.cpp
Normal file
229
examples/walltimer_example.cpp
Normal file
|
|
@ -0,0 +1,229 @@
|
||||||
|
/*********************************************************************
|
||||||
|
* WallTimer Example
|
||||||
|
*
|
||||||
|
* Demonstrates various uses of robot::WallTimer
|
||||||
|
*********************************************************************/
|
||||||
|
|
||||||
|
#include <robot/wall_timer.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
// Example 1: Simple periodic callback
|
||||||
|
void simpleCallback(const robot::WallTimerEvent& event)
|
||||||
|
{
|
||||||
|
std::cout << "[Simple] Timer fired at wall time: "
|
||||||
|
<< event.current_real.toSec() << std::endl;
|
||||||
|
std::cout << "[Simple] Time since last callback: "
|
||||||
|
<< event.last_duration.toSec() << " seconds" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example 2: Performance monitoring
|
||||||
|
class PerformanceMonitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void start()
|
||||||
|
{
|
||||||
|
timer_ = std::make_unique<robot::WallTimer>(
|
||||||
|
robot::WallDuration(1.0), // Check every 1 second
|
||||||
|
[this](const robot::WallTimerEvent& event) {
|
||||||
|
this->monitor(event);
|
||||||
|
},
|
||||||
|
false, // Repeating
|
||||||
|
true // Auto-start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void monitor(const robot::WallTimerEvent& event)
|
||||||
|
{
|
||||||
|
static int count = 0;
|
||||||
|
count++;
|
||||||
|
|
||||||
|
double actual_interval = event.last_duration.toSec();
|
||||||
|
double expected_interval = 1.0;
|
||||||
|
|
||||||
|
std::cout << "[Monitor] Check #" << count
|
||||||
|
<< " - Expected: " << expected_interval
|
||||||
|
<< "s, Actual: " << actual_interval << "s";
|
||||||
|
|
||||||
|
if (actual_interval > expected_interval * 1.1)
|
||||||
|
{
|
||||||
|
std::cout << " (DRIFT DETECTED!)";
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop()
|
||||||
|
{
|
||||||
|
if (timer_)
|
||||||
|
{
|
||||||
|
timer_->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<robot::WallTimer> timer_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Example 3: One-shot delayed action
|
||||||
|
void delayedAction(const robot::WallTimerEvent& event)
|
||||||
|
{
|
||||||
|
std::cout << "[Delayed] Action executed after delay!" << std::endl;
|
||||||
|
std::cout << "[Delayed] Executed at wall time: "
|
||||||
|
<< event.current_real.toSec() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example 4: Dynamic period adjustment
|
||||||
|
class DynamicTimer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void start()
|
||||||
|
{
|
||||||
|
timer_ = std::make_unique<robot::WallTimer>(
|
||||||
|
robot::WallDuration(1.0), // Start with 1 second
|
||||||
|
[this](const robot::WallTimerEvent& event) {
|
||||||
|
this->callback(event);
|
||||||
|
},
|
||||||
|
false, // Repeating
|
||||||
|
true // Auto-start
|
||||||
|
);
|
||||||
|
|
||||||
|
// After 3 seconds, change period to 0.5 seconds
|
||||||
|
adjust_thread_ = std::thread([this]() {
|
||||||
|
robot::WallDuration(3.0).sleep();
|
||||||
|
std::cout << "[Dynamic] Changing period to 0.5 seconds..." << std::endl;
|
||||||
|
timer_->setPeriod(robot::WallDuration(0.5), true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void callback(const robot::WallTimerEvent& event)
|
||||||
|
{
|
||||||
|
static int count = 0;
|
||||||
|
count++;
|
||||||
|
std::cout << "[Dynamic] Callback #" << count
|
||||||
|
<< " at " << event.current_real.toSec() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop()
|
||||||
|
{
|
||||||
|
if (timer_)
|
||||||
|
{
|
||||||
|
timer_->stop();
|
||||||
|
}
|
||||||
|
if (adjust_thread_.joinable())
|
||||||
|
{
|
||||||
|
adjust_thread_.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<robot::WallTimer> timer_;
|
||||||
|
std::thread adjust_thread_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Example 5: Timer with member function
|
||||||
|
class TimerClass
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void startTimer()
|
||||||
|
{
|
||||||
|
timer_ = std::make_unique<robot::WallTimer>(
|
||||||
|
robot::WallDuration(0.5), // 2 Hz
|
||||||
|
&TimerClass::timerCallback,
|
||||||
|
this,
|
||||||
|
false, // Repeating
|
||||||
|
true // Auto-start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void timerCallback(const robot::WallTimerEvent& event)
|
||||||
|
{
|
||||||
|
static int count = 0;
|
||||||
|
count++;
|
||||||
|
std::cout << "[Class] Member function callback #" << count
|
||||||
|
<< " at " << event.current_real.toSec() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopTimer()
|
||||||
|
{
|
||||||
|
if (timer_)
|
||||||
|
{
|
||||||
|
timer_->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<robot::WallTimer> timer_;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
std::cout << "=== WallTimer Examples ===" << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
// Example 1: Simple timer
|
||||||
|
std::cout << "Example 1: Simple periodic callback" << std::endl;
|
||||||
|
{
|
||||||
|
robot::WallTimer timer(
|
||||||
|
robot::WallDuration(1.0),
|
||||||
|
simpleCallback,
|
||||||
|
false, // Repeating
|
||||||
|
true // Auto-start
|
||||||
|
);
|
||||||
|
|
||||||
|
robot::WallDuration(3.0).sleep(); // Run for 3 seconds
|
||||||
|
timer.stop();
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
// Example 2: Performance monitoring
|
||||||
|
std::cout << "Example 2: Performance monitoring" << std::endl;
|
||||||
|
{
|
||||||
|
PerformanceMonitor monitor;
|
||||||
|
monitor.start();
|
||||||
|
|
||||||
|
robot::WallDuration(5.0).sleep(); // Run for 5 seconds
|
||||||
|
monitor.stop();
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
// Example 3: One-shot timer
|
||||||
|
std::cout << "Example 3: One-shot delayed action" << std::endl;
|
||||||
|
{
|
||||||
|
robot::WallTimer timer(
|
||||||
|
robot::WallDuration(2.0),
|
||||||
|
delayedAction,
|
||||||
|
true, // One-shot
|
||||||
|
true // Auto-start
|
||||||
|
);
|
||||||
|
|
||||||
|
robot::WallDuration(3.0).sleep(); // Wait for it to fire
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
// Example 4: Dynamic period adjustment
|
||||||
|
std::cout << "Example 4: Dynamic period adjustment" << std::endl;
|
||||||
|
{
|
||||||
|
DynamicTimer dynamic;
|
||||||
|
dynamic.start();
|
||||||
|
|
||||||
|
robot::WallDuration(8.0).sleep(); // Run for 8 seconds
|
||||||
|
dynamic.stop();
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
// Example 5: Member function callback
|
||||||
|
std::cout << "Example 5: Member function callback" << std::endl;
|
||||||
|
{
|
||||||
|
TimerClass timer_obj;
|
||||||
|
timer_obj.startTimer();
|
||||||
|
|
||||||
|
robot::WallDuration(3.0).sleep(); // Run for 3 seconds
|
||||||
|
timer_obj.stopTimer();
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
std::cout << "All examples completed!" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
289
include/robot/wall_timer.h
Normal file
289
include/robot/wall_timer.h
Normal file
|
|
@ -0,0 +1,289 @@
|
||||||
|
/*********************************************************************
|
||||||
|
* Software License Agreement (BSD License)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*********************************************************************/
|
||||||
|
|
||||||
|
#ifndef ROBOT_WALL_TIMER_H
|
||||||
|
#define ROBOT_WALL_TIMER_H
|
||||||
|
|
||||||
|
#include <robot/walltime.h>
|
||||||
|
#include "robot_time_decl.h"
|
||||||
|
#include <functional>
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace robot
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Structure containing information about a wall timer event
|
||||||
|
*
|
||||||
|
* Similar to ros::WallTimerEvent, this structure is passed to wall timer callbacks
|
||||||
|
* and contains timing information about the current and previous timer events.
|
||||||
|
* Uses wall-clock time (not affected by simulated time).
|
||||||
|
*/
|
||||||
|
struct ROBOT_TIME_DECL WallTimerEvent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief The time when the current callback was actually called (wall-clock time)
|
||||||
|
*/
|
||||||
|
WallTime current_real;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The time when the current callback was expected to be called (wall-clock time)
|
||||||
|
*/
|
||||||
|
WallTime current_expected;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The time when the previous callback was actually called (wall-clock time)
|
||||||
|
*/
|
||||||
|
WallTime last_real;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The time when the previous callback was expected to be called (wall-clock time)
|
||||||
|
*/
|
||||||
|
WallTime last_expected;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The time between the last two callbacks (wall-clock duration)
|
||||||
|
*/
|
||||||
|
WallDuration last_duration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default constructor
|
||||||
|
*/
|
||||||
|
WallTimerEvent()
|
||||||
|
: current_real(WallTime::now())
|
||||||
|
, current_expected(WallTime::now())
|
||||||
|
, last_real(WallTime())
|
||||||
|
, last_expected(WallTime())
|
||||||
|
, last_duration(WallDuration())
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class WallTimer
|
||||||
|
* @brief A class to call a callback function at a specified rate using wall-clock time
|
||||||
|
*
|
||||||
|
* This class is similar to ros::WallTimer. It creates a separate thread that
|
||||||
|
* calls a callback function at a specified period. The callback receives
|
||||||
|
* a WallTimerEvent structure containing timing information.
|
||||||
|
*
|
||||||
|
* Unlike Timer, WallTimer always uses wall-clock time and is not affected
|
||||||
|
* by simulated time. This makes it ideal for:
|
||||||
|
* - Performance monitoring
|
||||||
|
* - Real-time deadlines
|
||||||
|
* - Hardware interfaces
|
||||||
|
* - Profiling and benchmarking
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* @code
|
||||||
|
* void myCallback(const robot::WallTimerEvent& event) {
|
||||||
|
* // Do something periodically
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* robot::WallTimer timer(robot::WallDuration(1.0), myCallback);
|
||||||
|
* timer.start();
|
||||||
|
* // ... later ...
|
||||||
|
* timer.stop();
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
class ROBOT_TIME_DECL WallTimer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Callback function type for wall timer events
|
||||||
|
*/
|
||||||
|
using Callback = std::function<void(const WallTimerEvent&)>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default constructor - creates an uninitialized timer
|
||||||
|
* Timer must be assigned or constructed with parameters before use
|
||||||
|
*/
|
||||||
|
WallTimer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructor
|
||||||
|
* @param period The period between timer callbacks (wall-clock duration)
|
||||||
|
* @param callback The callback function to call
|
||||||
|
* @param oneshot If true, the timer will only fire once. If false, it will fire repeatedly.
|
||||||
|
* @param autostart If true, the timer will start automatically. If false, you must call start().
|
||||||
|
*/
|
||||||
|
WallTimer(const WallDuration& period,
|
||||||
|
const Callback& callback,
|
||||||
|
bool oneshot = false,
|
||||||
|
bool autostart = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructor with member function pointer
|
||||||
|
* @param period The period between timer callbacks (wall-clock duration)
|
||||||
|
* @param callback Member function pointer to call
|
||||||
|
* @param obj Object instance to call the member function on
|
||||||
|
* @param oneshot If true, the timer will only fire once. If false, it will fire repeatedly.
|
||||||
|
* @param autostart If true, the timer will start automatically. If false, you must call start().
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* @code
|
||||||
|
* class MyClass {
|
||||||
|
* void callback(const robot::WallTimerEvent& event) { }
|
||||||
|
* };
|
||||||
|
* MyClass obj;
|
||||||
|
* robot::WallTimer timer(robot::WallDuration(1.0), &MyClass::callback, &obj);
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
WallTimer(const WallDuration& period,
|
||||||
|
void (T::*callback)(const WallTimerEvent&),
|
||||||
|
T* obj,
|
||||||
|
bool oneshot = false,
|
||||||
|
bool autostart = true)
|
||||||
|
: WallTimer(period,
|
||||||
|
[obj, callback](const WallTimerEvent& event) { (obj->*callback)(event); },
|
||||||
|
oneshot,
|
||||||
|
autostart)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy constructor
|
||||||
|
*/
|
||||||
|
WallTimer(const WallTimer& rhs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destructor - stops the timer and joins the thread
|
||||||
|
*/
|
||||||
|
~WallTimer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start the timer. Does nothing if the timer is already started.
|
||||||
|
*/
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Stop the timer. Once this call returns, no more callbacks will be called.
|
||||||
|
* Does nothing if the timer is already stopped.
|
||||||
|
*/
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the period of this timer
|
||||||
|
* @param period The new period between timer callbacks
|
||||||
|
* @param reset Whether to reset the timer. If true, timer ignores elapsed time and next callback occurs at now()+period
|
||||||
|
*/
|
||||||
|
void setPeriod(const WallDuration& period, bool reset = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the timer has been started
|
||||||
|
* @return True if the timer is running, false otherwise
|
||||||
|
*/
|
||||||
|
bool hasStarted() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the timer is valid (has a callback)
|
||||||
|
* @return True if the timer is valid, false otherwise
|
||||||
|
*/
|
||||||
|
bool isValid() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the timer has any pending events to call
|
||||||
|
* @return True if there are pending events, false otherwise
|
||||||
|
*/
|
||||||
|
bool hasPending() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the timer is one-shot
|
||||||
|
* @return True if the timer is one-shot, false if it repeats
|
||||||
|
*/
|
||||||
|
bool isOneShot() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set whether the timer is one-shot
|
||||||
|
* @param oneshot If true, the timer will only fire once
|
||||||
|
*/
|
||||||
|
void setOneShot(bool oneshot);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the timer period
|
||||||
|
* @return The period between timer callbacks
|
||||||
|
*/
|
||||||
|
WallDuration getPeriod() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Conversion to bool (for checking validity)
|
||||||
|
*/
|
||||||
|
operator void*() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Equality operator
|
||||||
|
*/
|
||||||
|
bool operator==(const WallTimer& rhs) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Inequality operator
|
||||||
|
*/
|
||||||
|
bool operator!=(const WallTimer& rhs) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Less-than operator (for ordering in containers)
|
||||||
|
*/
|
||||||
|
bool operator<(const WallTimer& rhs) const;
|
||||||
|
|
||||||
|
// Non-copyable assignment
|
||||||
|
WallTimer& operator=(const WallTimer&) = delete;
|
||||||
|
|
||||||
|
// Movable
|
||||||
|
WallTimer(WallTimer&& other) noexcept;
|
||||||
|
WallTimer& operator=(WallTimer&& other) noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief The timer thread function
|
||||||
|
*/
|
||||||
|
void timerThread();
|
||||||
|
|
||||||
|
WallDuration period_; ///< Period between callbacks
|
||||||
|
Callback callback_; ///< Callback function
|
||||||
|
bool oneshot_; ///< Whether timer is one-shot
|
||||||
|
std::atomic<bool> running_; ///< Whether timer is running
|
||||||
|
std::atomic<bool> should_stop_; ///< Flag to stop the timer thread
|
||||||
|
|
||||||
|
std::thread thread_; ///< Timer thread
|
||||||
|
mutable std::mutex mutex_; ///< Mutex for thread safety
|
||||||
|
std::condition_variable cv_; ///< Condition variable for timing
|
||||||
|
|
||||||
|
WallTime last_real_; ///< Last actual callback time
|
||||||
|
WallTime last_expected_; ///< Last expected callback time
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace robot
|
||||||
|
|
||||||
|
#endif // ROBOT_WALL_TIMER_H
|
||||||
|
|
||||||
475
include/robot/walltime.h
Normal file
475
include/robot/walltime.h
Normal file
|
|
@ -0,0 +1,475 @@
|
||||||
|
/*********************************************************************
|
||||||
|
* WallTime - Standalone Wall-clock Time Library
|
||||||
|
*
|
||||||
|
* Header-only library for wall-clock time operations.
|
||||||
|
* This is a simplified, standalone version of WallTime that can be
|
||||||
|
* used independently or as a convenience wrapper.
|
||||||
|
*
|
||||||
|
* Features:
|
||||||
|
* - Always uses real wall-clock time (not affected by simulated time)
|
||||||
|
* - No initialization required
|
||||||
|
* - Thread-safe
|
||||||
|
* - Cross-platform (Linux, Windows, macOS)
|
||||||
|
* - Nanosecond precision
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* #include <robot/walltime.h>
|
||||||
|
*
|
||||||
|
* robot::WallTime now = robot::WallTime::now();
|
||||||
|
* robot::WallDuration elapsed = robot::WallTime::now() - now;
|
||||||
|
*********************************************************************/
|
||||||
|
|
||||||
|
#ifndef ROBOT_WALLTIME_H
|
||||||
|
#define ROBOT_WALLTIME_H
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <limits>
|
||||||
|
#include <cmath>
|
||||||
|
#include <thread>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace robot
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief WallDuration - Represents a time interval using wall-clock time
|
||||||
|
*/
|
||||||
|
class WallDuration
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int32_t sec; // Seconds (can be negative)
|
||||||
|
int32_t nsec; // Nanoseconds (0-999999999)
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
WallDuration() : sec(0), nsec(0) {}
|
||||||
|
|
||||||
|
WallDuration(int32_t _sec, int32_t _nsec) : sec(_sec), nsec(_nsec)
|
||||||
|
{
|
||||||
|
normalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit WallDuration(double t)
|
||||||
|
{
|
||||||
|
fromSec(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize nanoseconds to [0, 999999999] range
|
||||||
|
void normalize()
|
||||||
|
{
|
||||||
|
int64_t sec64 = sec;
|
||||||
|
int64_t nsec64 = nsec;
|
||||||
|
|
||||||
|
// Handle negative nanoseconds
|
||||||
|
while (nsec64 < 0)
|
||||||
|
{
|
||||||
|
nsec64 += 1000000000LL;
|
||||||
|
sec64 -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle overflow nanoseconds
|
||||||
|
while (nsec64 >= 1000000000LL)
|
||||||
|
{
|
||||||
|
nsec64 -= 1000000000LL;
|
||||||
|
sec64 += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check bounds
|
||||||
|
if (sec64 > std::numeric_limits<int32_t>::max() ||
|
||||||
|
sec64 < std::numeric_limits<int32_t>::min())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("WallDuration out of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
sec = static_cast<int32_t>(sec64);
|
||||||
|
nsec = static_cast<int32_t>(nsec64);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to seconds (double)
|
||||||
|
double toSec() const
|
||||||
|
{
|
||||||
|
return static_cast<double>(sec) + 1e-9 * static_cast<double>(nsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to nanoseconds
|
||||||
|
int64_t toNSec() const
|
||||||
|
{
|
||||||
|
return static_cast<int64_t>(sec) * 1000000000LL + static_cast<int64_t>(nsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize from seconds
|
||||||
|
WallDuration& fromSec(double t)
|
||||||
|
{
|
||||||
|
sec = static_cast<int32_t>(std::floor(t));
|
||||||
|
nsec = static_cast<int32_t>((t - sec) * 1e9);
|
||||||
|
normalize();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize from nanoseconds
|
||||||
|
WallDuration& fromNSec(int64_t t)
|
||||||
|
{
|
||||||
|
sec = static_cast<int32_t>(t / 1000000000LL);
|
||||||
|
nsec = static_cast<int32_t>(t % 1000000000LL);
|
||||||
|
normalize();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arithmetic operations
|
||||||
|
WallDuration operator+(const WallDuration& rhs) const
|
||||||
|
{
|
||||||
|
return WallDuration(sec + rhs.sec, nsec + rhs.nsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
WallDuration operator-(const WallDuration& rhs) const
|
||||||
|
{
|
||||||
|
return WallDuration(sec - rhs.sec, nsec - rhs.nsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
WallDuration operator-() const
|
||||||
|
{
|
||||||
|
return WallDuration(-sec, -nsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
WallDuration operator*(double scale) const
|
||||||
|
{
|
||||||
|
return WallDuration(toSec() * scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
WallDuration& operator+=(const WallDuration& rhs)
|
||||||
|
{
|
||||||
|
sec += rhs.sec;
|
||||||
|
nsec += rhs.nsec;
|
||||||
|
normalize();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
WallDuration& operator-=(const WallDuration& rhs)
|
||||||
|
{
|
||||||
|
sec -= rhs.sec;
|
||||||
|
nsec -= rhs.nsec;
|
||||||
|
normalize();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparison operators
|
||||||
|
bool operator==(const WallDuration& rhs) const
|
||||||
|
{
|
||||||
|
return sec == rhs.sec && nsec == rhs.nsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const WallDuration& rhs) const
|
||||||
|
{
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const WallDuration& rhs) const
|
||||||
|
{
|
||||||
|
if (sec < rhs.sec) return true;
|
||||||
|
if (sec > rhs.sec) return false;
|
||||||
|
return nsec < rhs.nsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>(const WallDuration& rhs) const
|
||||||
|
{
|
||||||
|
return rhs < *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<=(const WallDuration& rhs) const
|
||||||
|
{
|
||||||
|
return !(rhs < *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>=(const WallDuration& rhs) const
|
||||||
|
{
|
||||||
|
return !(*this < rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sleep for this duration
|
||||||
|
bool sleep() const
|
||||||
|
{
|
||||||
|
if (sec < 0) return false;
|
||||||
|
|
||||||
|
using namespace std::chrono;
|
||||||
|
std::chrono::nanoseconds ns(toNSec());
|
||||||
|
std::this_thread::sleep_for(ns);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if zero
|
||||||
|
bool isZero() const
|
||||||
|
{
|
||||||
|
return sec == 0 && nsec == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
static const WallDuration ZERO;
|
||||||
|
static const WallDuration MAX;
|
||||||
|
static const WallDuration MIN;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief WallTime - Represents a point in time using wall-clock time
|
||||||
|
*/
|
||||||
|
class WallTime
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint32_t sec; // Seconds since epoch
|
||||||
|
uint32_t nsec; // Nanoseconds (0-999999999)
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
WallTime() : sec(0), nsec(0) {}
|
||||||
|
|
||||||
|
WallTime(uint32_t _sec, uint32_t _nsec) : sec(_sec), nsec(_nsec)
|
||||||
|
{
|
||||||
|
normalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit WallTime(double t)
|
||||||
|
{
|
||||||
|
fromSec(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize nanoseconds to [0, 999999999] range
|
||||||
|
void normalize()
|
||||||
|
{
|
||||||
|
uint64_t sec64 = sec;
|
||||||
|
uint64_t nsec64 = nsec;
|
||||||
|
|
||||||
|
// Handle overflow nanoseconds
|
||||||
|
uint64_t sec_part = nsec64 / 1000000000ULL;
|
||||||
|
nsec64 = nsec64 % 1000000000ULL;
|
||||||
|
sec64 += sec_part;
|
||||||
|
|
||||||
|
// Check bounds
|
||||||
|
if (sec64 > std::numeric_limits<uint32_t>::max())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("WallTime out of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
sec = static_cast<uint32_t>(sec64);
|
||||||
|
nsec = static_cast<uint32_t>(nsec64);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current wall-clock time
|
||||||
|
static WallTime now()
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
auto now_time = system_clock::now();
|
||||||
|
auto duration = now_time.time_since_epoch();
|
||||||
|
auto nanoseconds_count = duration_cast<std::chrono::nanoseconds>(duration).count();
|
||||||
|
|
||||||
|
WallTime t;
|
||||||
|
uint64_t sec64 = nanoseconds_count / 1000000000ULL;
|
||||||
|
uint64_t nsec64 = nanoseconds_count % 1000000000ULL;
|
||||||
|
|
||||||
|
if (sec64 > std::numeric_limits<uint32_t>::max())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("WallTime::now() - time out of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
t.sec = static_cast<uint32_t>(sec64);
|
||||||
|
t.nsec = static_cast<uint32_t>(nsec64);
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to seconds (double)
|
||||||
|
double toSec() const
|
||||||
|
{
|
||||||
|
return static_cast<double>(sec) + 1e-9 * static_cast<double>(nsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to nanoseconds
|
||||||
|
uint64_t toNSec() const
|
||||||
|
{
|
||||||
|
return static_cast<uint64_t>(sec) * 1000000000ULL + static_cast<uint64_t>(nsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize from seconds
|
||||||
|
WallTime& fromSec(double t)
|
||||||
|
{
|
||||||
|
sec = static_cast<uint32_t>(std::floor(t));
|
||||||
|
nsec = static_cast<uint32_t>((t - sec) * 1e9);
|
||||||
|
normalize();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize from nanoseconds
|
||||||
|
WallTime& fromNSec(uint64_t t)
|
||||||
|
{
|
||||||
|
sec = static_cast<uint32_t>(t / 1000000000ULL);
|
||||||
|
nsec = static_cast<uint32_t>(t % 1000000000ULL);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arithmetic operations with WallDuration
|
||||||
|
WallTime operator+(const WallDuration& d) const
|
||||||
|
{
|
||||||
|
int64_t total_sec = static_cast<int64_t>(sec) + d.sec;
|
||||||
|
int64_t total_nsec = static_cast<int64_t>(nsec) + d.nsec;
|
||||||
|
|
||||||
|
if (total_sec < 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("WallTime::operator+ - result would be negative");
|
||||||
|
}
|
||||||
|
|
||||||
|
WallTime result;
|
||||||
|
result.sec = static_cast<uint32_t>(total_sec);
|
||||||
|
result.nsec = static_cast<uint32_t>(total_nsec);
|
||||||
|
result.normalize();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
WallTime operator-(const WallDuration& d) const
|
||||||
|
{
|
||||||
|
int64_t total_sec = static_cast<int64_t>(sec) - d.sec;
|
||||||
|
int64_t total_nsec = static_cast<int64_t>(nsec) - d.nsec;
|
||||||
|
|
||||||
|
if (total_sec < 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("WallTime::operator- - result would be negative");
|
||||||
|
}
|
||||||
|
|
||||||
|
WallTime result;
|
||||||
|
result.sec = static_cast<uint32_t>(total_sec);
|
||||||
|
result.nsec = static_cast<uint32_t>(total_nsec);
|
||||||
|
result.normalize();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
WallDuration operator-(const WallTime& rhs) const
|
||||||
|
{
|
||||||
|
int64_t sec_diff = static_cast<int64_t>(sec) - static_cast<int64_t>(rhs.sec);
|
||||||
|
int64_t nsec_diff = static_cast<int64_t>(nsec) - static_cast<int64_t>(rhs.nsec);
|
||||||
|
|
||||||
|
return WallDuration(static_cast<int32_t>(sec_diff), static_cast<int32_t>(nsec_diff));
|
||||||
|
}
|
||||||
|
|
||||||
|
WallTime& operator+=(const WallDuration& d)
|
||||||
|
{
|
||||||
|
*this = *this + d;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
WallTime& operator-=(const WallDuration& d)
|
||||||
|
{
|
||||||
|
*this = *this - d;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparison operators
|
||||||
|
bool operator==(const WallTime& rhs) const
|
||||||
|
{
|
||||||
|
return sec == rhs.sec && nsec == rhs.nsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const WallTime& rhs) const
|
||||||
|
{
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const WallTime& rhs) const
|
||||||
|
{
|
||||||
|
if (sec < rhs.sec) return true;
|
||||||
|
if (sec > rhs.sec) return false;
|
||||||
|
return nsec < rhs.nsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>(const WallTime& rhs) const
|
||||||
|
{
|
||||||
|
return rhs < *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<=(const WallTime& rhs) const
|
||||||
|
{
|
||||||
|
return !(rhs < *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>=(const WallTime& rhs) const
|
||||||
|
{
|
||||||
|
return !(*this < rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sleep until this time
|
||||||
|
static bool sleepUntil(const WallTime& end)
|
||||||
|
{
|
||||||
|
WallTime now_time = now();
|
||||||
|
if (end <= now_time)
|
||||||
|
{
|
||||||
|
return true; // Already past the target time
|
||||||
|
}
|
||||||
|
|
||||||
|
WallDuration remaining = end - now_time;
|
||||||
|
return remaining.sleep();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if zero
|
||||||
|
bool isZero() const
|
||||||
|
{
|
||||||
|
return sec == 0 && nsec == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is system time (always true for WallTime)
|
||||||
|
static bool isSystemTime()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
static const WallTime ZERO;
|
||||||
|
static const WallTime MAX;
|
||||||
|
static const WallTime MIN;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Stream operators
|
||||||
|
inline std::ostream& operator<<(std::ostream& os, const WallTime& rhs)
|
||||||
|
{
|
||||||
|
auto flags = os.flags();
|
||||||
|
auto fillc = os.fill();
|
||||||
|
auto width = os.width();
|
||||||
|
os << rhs.sec << "." << std::setw(9) << std::setfill('0') << rhs.nsec;
|
||||||
|
os.flags(flags);
|
||||||
|
os.fill(fillc);
|
||||||
|
os.width(width);
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::ostream& operator<<(std::ostream& os, const WallDuration& rhs)
|
||||||
|
{
|
||||||
|
auto flags = os.flags();
|
||||||
|
auto fillc = os.fill();
|
||||||
|
auto width = os.width();
|
||||||
|
if (rhs.sec >= 0 || rhs.nsec == 0)
|
||||||
|
{
|
||||||
|
os << rhs.sec << "." << std::setw(9) << std::setfill('0') << rhs.nsec;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
os << (rhs.sec == -1 ? "-" : "") << (rhs.sec + 1) << "."
|
||||||
|
<< std::setw(9) << std::setfill('0') << (1000000000 - rhs.nsec);
|
||||||
|
}
|
||||||
|
os.flags(flags);
|
||||||
|
os.fill(fillc);
|
||||||
|
os.width(width);
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constants definitions (inline to avoid multiple definition)
|
||||||
|
inline const WallDuration WallDuration::ZERO(0, 0);
|
||||||
|
inline const WallDuration WallDuration::MAX(
|
||||||
|
std::numeric_limits<int32_t>::max(), 999999999);
|
||||||
|
inline const WallDuration WallDuration::MIN(
|
||||||
|
std::numeric_limits<int32_t>::min(), 0);
|
||||||
|
|
||||||
|
inline const WallTime WallTime::ZERO(0, 0);
|
||||||
|
inline const WallTime WallTime::MAX(
|
||||||
|
std::numeric_limits<uint32_t>::max(), 999999999);
|
||||||
|
inline const WallTime WallTime::MIN(0, 1);
|
||||||
|
|
||||||
|
} // namespace robot
|
||||||
|
|
||||||
|
#endif // ROBOT_WALLTIME_H
|
||||||
|
|
||||||
375
src/wall_timer.cpp
Normal file
375
src/wall_timer.cpp
Normal file
|
|
@ -0,0 +1,375 @@
|
||||||
|
/*********************************************************************
|
||||||
|
* Software License Agreement (BSD License)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*********************************************************************/
|
||||||
|
|
||||||
|
#include <robot/wall_timer.h>
|
||||||
|
#include <chrono>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace robot
|
||||||
|
{
|
||||||
|
|
||||||
|
WallTimer::WallTimer()
|
||||||
|
: period_(WallDuration(1.0))
|
||||||
|
, callback_(nullptr)
|
||||||
|
, oneshot_(false)
|
||||||
|
, running_(false)
|
||||||
|
, should_stop_(false)
|
||||||
|
, last_real_(WallTime())
|
||||||
|
, last_expected_(WallTime())
|
||||||
|
{
|
||||||
|
// Default constructor - timer is not started
|
||||||
|
}
|
||||||
|
|
||||||
|
WallTimer::WallTimer(const WallDuration& period,
|
||||||
|
const Callback& callback,
|
||||||
|
bool oneshot,
|
||||||
|
bool autostart)
|
||||||
|
: period_(period)
|
||||||
|
, callback_(callback)
|
||||||
|
, oneshot_(oneshot)
|
||||||
|
, running_(false)
|
||||||
|
, should_stop_(false)
|
||||||
|
, last_real_(WallTime())
|
||||||
|
, last_expected_(WallTime())
|
||||||
|
{
|
||||||
|
if (autostart)
|
||||||
|
{
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WallTimer::WallTimer(const WallTimer& rhs)
|
||||||
|
: period_(rhs.period_)
|
||||||
|
, callback_(rhs.callback_)
|
||||||
|
, oneshot_(rhs.oneshot_)
|
||||||
|
, running_(false)
|
||||||
|
, should_stop_(false)
|
||||||
|
, last_real_(rhs.last_real_)
|
||||||
|
, last_expected_(rhs.last_expected_)
|
||||||
|
{
|
||||||
|
// Copy constructor - timer is not started
|
||||||
|
// If rhs was running, we don't copy the running state
|
||||||
|
}
|
||||||
|
|
||||||
|
WallTimer::~WallTimer()
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
WallTimer::WallTimer(WallTimer&& other) noexcept
|
||||||
|
: period_(other.period_)
|
||||||
|
, callback_(std::move(other.callback_))
|
||||||
|
, oneshot_(other.oneshot_)
|
||||||
|
, running_(false)
|
||||||
|
, should_stop_(false)
|
||||||
|
, last_real_(other.last_real_)
|
||||||
|
, last_expected_(other.last_expected_)
|
||||||
|
{
|
||||||
|
// Note: We don't move a running thread because it references 'this' of the old object.
|
||||||
|
// If other was running, it will be stopped by its destructor.
|
||||||
|
// We only move the configuration, not the running state.
|
||||||
|
|
||||||
|
// Reset other to safe state
|
||||||
|
other.running_ = false;
|
||||||
|
other.should_stop_ = false;
|
||||||
|
other.oneshot_ = false;
|
||||||
|
other.period_ = WallDuration(1.0);
|
||||||
|
other.last_real_ = WallTime();
|
||||||
|
other.last_expected_ = WallTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
WallTimer& WallTimer::operator=(WallTimer&& other) noexcept
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
// Stop current timer if running
|
||||||
|
stop();
|
||||||
|
|
||||||
|
// Stop other timer if running (before moving, to ensure thread is properly stopped)
|
||||||
|
bool was_running = other.running_.load();
|
||||||
|
if (was_running)
|
||||||
|
{
|
||||||
|
other.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move configuration from other
|
||||||
|
period_ = other.period_;
|
||||||
|
callback_ = std::move(other.callback_);
|
||||||
|
oneshot_ = other.oneshot_;
|
||||||
|
last_real_ = other.last_real_;
|
||||||
|
last_expected_ = other.last_expected_;
|
||||||
|
|
||||||
|
// Reset running state - we'll start fresh if needed
|
||||||
|
running_ = false;
|
||||||
|
should_stop_ = false;
|
||||||
|
|
||||||
|
// If other was running (and had autostart), we need to start this timer
|
||||||
|
// (This handles the case: timer = robot::WallTimer(..., autostart=true))
|
||||||
|
if (was_running)
|
||||||
|
{
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset other to safe state
|
||||||
|
other.running_ = false;
|
||||||
|
other.should_stop_ = false;
|
||||||
|
other.oneshot_ = false;
|
||||||
|
other.period_ = WallDuration(1.0);
|
||||||
|
other.last_real_ = WallTime();
|
||||||
|
other.last_expected_ = WallTime();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WallTimer::start()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
|
||||||
|
if (running_)
|
||||||
|
{
|
||||||
|
return; // Already running
|
||||||
|
}
|
||||||
|
|
||||||
|
should_stop_ = false;
|
||||||
|
running_ = true;
|
||||||
|
last_real_ = WallTime();
|
||||||
|
last_expected_ = WallTime();
|
||||||
|
|
||||||
|
// Start the timer thread
|
||||||
|
thread_ = std::thread(&WallTimer::timerThread, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WallTimer::stop()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
|
||||||
|
if (!running_)
|
||||||
|
{
|
||||||
|
return; // Not running
|
||||||
|
}
|
||||||
|
|
||||||
|
should_stop_ = true;
|
||||||
|
running_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify the thread to wake up and check should_stop_
|
||||||
|
cv_.notify_all();
|
||||||
|
|
||||||
|
// Wait for thread to finish
|
||||||
|
if (thread_.joinable())
|
||||||
|
{
|
||||||
|
thread_.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WallTimer::setPeriod(const WallDuration& period, bool reset)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
period_ = period;
|
||||||
|
|
||||||
|
if (reset && running_)
|
||||||
|
{
|
||||||
|
// Reset timing - next callback will occur at now() + period
|
||||||
|
last_real_ = WallTime();
|
||||||
|
last_expected_ = WallTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wake up the thread so it can use the new period
|
||||||
|
cv_.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WallTimer::hasStarted() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
return running_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WallTimer::isValid() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
return callback_ != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WallTimer::hasPending() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
// For simplicity, we consider a timer to have pending events if it's running
|
||||||
|
// In a more sophisticated implementation, we could track pending callbacks
|
||||||
|
return running_ && !should_stop_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WallTimer::isOneShot() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
return oneshot_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WallTimer::setOneShot(bool oneshot)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
oneshot_ = oneshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
WallDuration WallTimer::getPeriod() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
return period_;
|
||||||
|
}
|
||||||
|
|
||||||
|
WallTimer::operator void*() const
|
||||||
|
{
|
||||||
|
return isValid() ? const_cast<WallTimer*>(this) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WallTimer::operator==(const WallTimer& rhs) const
|
||||||
|
{
|
||||||
|
// Compare by pointer address (same as ROS implementation)
|
||||||
|
return this == &rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WallTimer::operator!=(const WallTimer& rhs) const
|
||||||
|
{
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WallTimer::operator<(const WallTimer& rhs) const
|
||||||
|
{
|
||||||
|
// Compare by pointer address (same as ROS implementation)
|
||||||
|
return this < &rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WallTimer::timerThread()
|
||||||
|
{
|
||||||
|
WallTime next_expected = WallTime::now() + period_;
|
||||||
|
last_expected_ = next_expected - period_;
|
||||||
|
|
||||||
|
while (!should_stop_)
|
||||||
|
{
|
||||||
|
WallTime current_real = WallTime::now();
|
||||||
|
WallTime current_expected = next_expected;
|
||||||
|
|
||||||
|
// Calculate how long to sleep
|
||||||
|
WallDuration sleep_duration = current_expected - current_real;
|
||||||
|
|
||||||
|
// If we're already past the expected time, don't sleep
|
||||||
|
if (sleep_duration <= WallDuration())
|
||||||
|
{
|
||||||
|
// We're late, adjust next_expected
|
||||||
|
next_expected = current_real + period_;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Sleep until the expected time
|
||||||
|
// Use a polling approach with small sleep intervals to allow for stop signal
|
||||||
|
WallTime sleep_until = current_expected;
|
||||||
|
WallDuration remaining = sleep_duration;
|
||||||
|
|
||||||
|
while (remaining > WallDuration() && !should_stop_)
|
||||||
|
{
|
||||||
|
// Sleep in small chunks to allow checking should_stop_
|
||||||
|
WallDuration sleep_chunk = remaining;
|
||||||
|
if (sleep_chunk.toSec() > 0.1) // Max 100ms chunks
|
||||||
|
{
|
||||||
|
sleep_chunk = WallDuration(0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep_chunk.sleep();
|
||||||
|
|
||||||
|
// Check if we should stop
|
||||||
|
if (should_stop_)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update remaining time
|
||||||
|
WallTime now = WallTime::now();
|
||||||
|
remaining = sleep_until - now;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we were woken up to stop
|
||||||
|
if (should_stop_)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update times after sleep
|
||||||
|
current_real = WallTime::now();
|
||||||
|
current_expected = next_expected;
|
||||||
|
next_expected = current_expected + period_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create timer event
|
||||||
|
WallTimerEvent event;
|
||||||
|
event.current_real = current_real;
|
||||||
|
event.current_expected = current_expected;
|
||||||
|
event.last_real = last_real_;
|
||||||
|
event.last_expected = last_expected_;
|
||||||
|
|
||||||
|
if (!last_real_.isZero())
|
||||||
|
{
|
||||||
|
event.last_duration = current_real - last_real_;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
event.last_duration = WallDuration();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update last times
|
||||||
|
last_real_ = current_real;
|
||||||
|
last_expected_ = current_expected;
|
||||||
|
|
||||||
|
// Call the callback
|
||||||
|
if (callback_)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
callback_(event);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
// Swallow exceptions to prevent timer thread from crashing
|
||||||
|
// In production, you might want to log this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If one-shot, stop after first callback
|
||||||
|
if (oneshot_)
|
||||||
|
{
|
||||||
|
should_stop_ = true;
|
||||||
|
running_ = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace robot
|
||||||
|
|
||||||
280
test/walltime_standalone_test.cpp
Normal file
280
test/walltime_standalone_test.cpp
Normal file
|
|
@ -0,0 +1,280 @@
|
||||||
|
/*********************************************************************
|
||||||
|
* Standalone WallTime Library Test
|
||||||
|
*
|
||||||
|
* Test file for the header-only WallTime library
|
||||||
|
*********************************************************************/
|
||||||
|
|
||||||
|
#include <robot/walltime.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
void test_walltime_basic()
|
||||||
|
{
|
||||||
|
std::cout << "Testing basic WallTime operations..." << std::endl;
|
||||||
|
|
||||||
|
// Test default constructor
|
||||||
|
robot::WallTime t1;
|
||||||
|
assert(t1.sec == 0);
|
||||||
|
assert(t1.nsec == 0);
|
||||||
|
assert(t1.isZero());
|
||||||
|
|
||||||
|
// Test constructor with values
|
||||||
|
robot::WallTime t2(1234567890, 123456789);
|
||||||
|
assert(t2.sec == 1234567890);
|
||||||
|
assert(t2.nsec == 123456789);
|
||||||
|
|
||||||
|
// Test constructor from double
|
||||||
|
robot::WallTime t3(1234567890.123456789);
|
||||||
|
assert(std::abs(t3.toSec() - 1234567890.123456789) < 1e-6);
|
||||||
|
|
||||||
|
std::cout << " ✓ Basic operations passed" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_walltime_now()
|
||||||
|
{
|
||||||
|
std::cout << "Testing WallTime::now()..." << std::endl;
|
||||||
|
|
||||||
|
robot::WallTime t1 = robot::WallTime::now();
|
||||||
|
robot::WallTime t2 = robot::WallTime::now();
|
||||||
|
|
||||||
|
// t2 should be >= t1
|
||||||
|
assert(t2 >= t1);
|
||||||
|
|
||||||
|
// Should be system time
|
||||||
|
assert(robot::WallTime::isSystemTime());
|
||||||
|
|
||||||
|
std::cout << " ✓ WallTime::now() passed" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_wallduration_basic()
|
||||||
|
{
|
||||||
|
std::cout << "Testing basic WallDuration operations..." << std::endl;
|
||||||
|
|
||||||
|
// Test default constructor
|
||||||
|
robot::WallDuration d1;
|
||||||
|
assert(d1.sec == 0);
|
||||||
|
assert(d1.nsec == 0);
|
||||||
|
assert(d1.isZero());
|
||||||
|
|
||||||
|
// Test constructor with values
|
||||||
|
robot::WallDuration d2(5, 123456789);
|
||||||
|
assert(d2.sec == 5);
|
||||||
|
assert(d2.nsec == 123456789);
|
||||||
|
|
||||||
|
// Test constructor from double
|
||||||
|
robot::WallDuration d3(5.123456789);
|
||||||
|
assert(std::abs(d3.toSec() - 5.123456789) < 1e-6);
|
||||||
|
|
||||||
|
std::cout << " ✓ Basic operations passed" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_wallduration_normalize()
|
||||||
|
{
|
||||||
|
std::cout << "Testing WallDuration normalization..." << std::endl;
|
||||||
|
|
||||||
|
// Test overflow nanoseconds
|
||||||
|
robot::WallDuration d1(0, 2000000000);
|
||||||
|
assert(d1.sec == 2);
|
||||||
|
assert(d1.nsec == 0);
|
||||||
|
|
||||||
|
// Test negative nanoseconds
|
||||||
|
robot::WallDuration d2(5, -500000000);
|
||||||
|
assert(d2.sec == 4);
|
||||||
|
assert(d2.nsec == 500000000);
|
||||||
|
|
||||||
|
std::cout << " ✓ Normalization passed" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_arithmetic()
|
||||||
|
{
|
||||||
|
std::cout << "Testing arithmetic operations..." << std::endl;
|
||||||
|
|
||||||
|
robot::WallTime t1(1000, 0);
|
||||||
|
robot::WallDuration d1(5, 0);
|
||||||
|
|
||||||
|
// Test addition
|
||||||
|
robot::WallTime t2 = t1 + d1;
|
||||||
|
assert(t2.sec == 1005);
|
||||||
|
assert(t2.nsec == 0);
|
||||||
|
|
||||||
|
// Test subtraction
|
||||||
|
robot::WallTime t3 = t2 - d1;
|
||||||
|
assert(t3.sec == 1000);
|
||||||
|
assert(t3.nsec == 0);
|
||||||
|
|
||||||
|
// Test duration subtraction
|
||||||
|
robot::WallDuration d2 = t2 - t1;
|
||||||
|
assert(d2.sec == 5);
|
||||||
|
assert(d2.nsec == 0);
|
||||||
|
|
||||||
|
// Test compound assignment
|
||||||
|
robot::WallTime t4 = t1;
|
||||||
|
t4 += d1;
|
||||||
|
assert(t4.sec == 1005);
|
||||||
|
|
||||||
|
t4 -= d1;
|
||||||
|
assert(t4.sec == 1000);
|
||||||
|
|
||||||
|
std::cout << " ✓ Arithmetic operations passed" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_comparison()
|
||||||
|
{
|
||||||
|
std::cout << "Testing comparison operations..." << std::endl;
|
||||||
|
|
||||||
|
robot::WallTime t1(1000, 0);
|
||||||
|
robot::WallTime t2(1000, 500000000);
|
||||||
|
robot::WallTime t3(2000, 0);
|
||||||
|
|
||||||
|
// Test equality
|
||||||
|
assert(t1 == t1);
|
||||||
|
assert(t1 != t2);
|
||||||
|
|
||||||
|
// Test less than
|
||||||
|
assert(t1 < t2);
|
||||||
|
assert(t1 < t3);
|
||||||
|
|
||||||
|
// Test greater than
|
||||||
|
assert(t2 > t1);
|
||||||
|
assert(t3 > t1);
|
||||||
|
|
||||||
|
// Test less than or equal
|
||||||
|
assert(t1 <= t1);
|
||||||
|
assert(t1 <= t2);
|
||||||
|
|
||||||
|
// Test greater than or equal
|
||||||
|
assert(t2 >= t1);
|
||||||
|
assert(t2 >= t2);
|
||||||
|
|
||||||
|
std::cout << " ✓ Comparison operations passed" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_conversion()
|
||||||
|
{
|
||||||
|
std::cout << "Testing conversion operations..." << std::endl;
|
||||||
|
|
||||||
|
robot::WallTime t(1234567890, 123456789);
|
||||||
|
|
||||||
|
// Test toSec
|
||||||
|
double seconds = t.toSec();
|
||||||
|
assert(std::abs(seconds - 1234567890.123456789) < 1e-6);
|
||||||
|
|
||||||
|
// Test toNSec
|
||||||
|
uint64_t nanoseconds = t.toNSec();
|
||||||
|
assert(nanoseconds == 1234567890123456789ULL);
|
||||||
|
|
||||||
|
// Test fromSec
|
||||||
|
robot::WallTime t2;
|
||||||
|
t2.fromSec(1234567890.123456789);
|
||||||
|
assert(std::abs(t2.toSec() - 1234567890.123456789) < 1e-6);
|
||||||
|
|
||||||
|
// Test fromNSec
|
||||||
|
robot::WallTime t3;
|
||||||
|
t3.fromNSec(1234567890123456789ULL);
|
||||||
|
assert(t3.sec == 1234567890);
|
||||||
|
assert(t3.nsec == 123456789);
|
||||||
|
|
||||||
|
std::cout << " ✓ Conversion operations passed" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_sleep()
|
||||||
|
{
|
||||||
|
std::cout << "Testing sleep operations..." << std::endl;
|
||||||
|
|
||||||
|
robot::WallTime start = robot::WallTime::now();
|
||||||
|
|
||||||
|
// Test WallDuration::sleep()
|
||||||
|
robot::WallDuration sleep_duration(0.1); // 100ms
|
||||||
|
bool sleep_result = sleep_duration.sleep();
|
||||||
|
assert(sleep_result);
|
||||||
|
|
||||||
|
robot::WallTime end = robot::WallTime::now();
|
||||||
|
robot::WallDuration elapsed = end - start;
|
||||||
|
|
||||||
|
// Should have slept at least 100ms
|
||||||
|
assert(elapsed.toSec() >= 0.09); // Allow some tolerance
|
||||||
|
|
||||||
|
// Test WallTime::sleepUntil()
|
||||||
|
robot::WallTime target = robot::WallTime::now() + robot::WallDuration(0.1);
|
||||||
|
bool sleep_until_result = robot::WallTime::sleepUntil(target);
|
||||||
|
assert(sleep_until_result);
|
||||||
|
|
||||||
|
robot::WallTime after = robot::WallTime::now();
|
||||||
|
assert(after >= target);
|
||||||
|
|
||||||
|
std::cout << " ✓ Sleep operations passed" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_measurement()
|
||||||
|
{
|
||||||
|
std::cout << "Testing time measurement..." << std::endl;
|
||||||
|
|
||||||
|
robot::WallTime start = robot::WallTime::now();
|
||||||
|
|
||||||
|
// Simulate some work
|
||||||
|
double sum = 0.0;
|
||||||
|
for (int i = 0; i < 100000; ++i)
|
||||||
|
{
|
||||||
|
sum += i;
|
||||||
|
}
|
||||||
|
|
||||||
|
robot::WallTime end = robot::WallTime::now();
|
||||||
|
robot::WallDuration elapsed = end - start;
|
||||||
|
|
||||||
|
// Should have taken some time
|
||||||
|
assert(elapsed.toSec() >= 0.0);
|
||||||
|
assert(elapsed.toSec() < 1.0); // Should be fast
|
||||||
|
|
||||||
|
std::cout << " ✓ Time measurement passed (elapsed: " << elapsed.toSec() << "s)" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_constants()
|
||||||
|
{
|
||||||
|
std::cout << "Testing constants..." << std::endl;
|
||||||
|
|
||||||
|
// Test WallTime constants
|
||||||
|
assert(robot::WallTime::ZERO.isZero());
|
||||||
|
assert(robot::WallTime::MAX > robot::WallTime::ZERO);
|
||||||
|
assert(robot::WallTime::MIN < robot::WallTime::MAX);
|
||||||
|
|
||||||
|
// Test WallDuration constants
|
||||||
|
assert(robot::WallDuration::ZERO.isZero());
|
||||||
|
assert(robot::WallDuration::MAX > robot::WallDuration::ZERO);
|
||||||
|
assert(robot::WallDuration::MIN < robot::WallDuration::ZERO);
|
||||||
|
|
||||||
|
std::cout << " ✓ Constants passed" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
std::cout << "========================================" << std::endl;
|
||||||
|
std::cout << "WallTime Standalone Library Tests" << std::endl;
|
||||||
|
std::cout << "========================================" << std::endl;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
test_walltime_basic();
|
||||||
|
test_walltime_now();
|
||||||
|
test_wallduration_basic();
|
||||||
|
test_wallduration_normalize();
|
||||||
|
test_arithmetic();
|
||||||
|
test_comparison();
|
||||||
|
test_conversion();
|
||||||
|
test_sleep();
|
||||||
|
test_measurement();
|
||||||
|
test_constants();
|
||||||
|
|
||||||
|
std::cout << "\n========================================" << std::endl;
|
||||||
|
std::cout << "All tests passed!" << std::endl;
|
||||||
|
std::cout << "========================================" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << "Test failed with exception: " << e.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user