-
Notifications
You must be signed in to change notification settings - Fork 321
PlayerClock initial implementation - Player functionally unchanged #689
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
59b6bff
3d3c9c8
8b3a48a
67fe510
6f7466d
bb59ff7
20f37f3
3173b87
bffc11e
daadf5b
b668a92
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| // Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| #ifndef ROSBAG2_CPP__CLOCKS__PLAYER_CLOCK_HPP_ | ||
| #define ROSBAG2_CPP__CLOCKS__PLAYER_CLOCK_HPP_ | ||
|
|
||
| #include <chrono> | ||
| #include <functional> | ||
| #include <memory> | ||
|
|
||
| #include "rcutils/time.h" | ||
| #include "rosbag2_cpp/visibility_control.hpp" | ||
|
|
||
| namespace rosbag2_cpp | ||
| { | ||
|
|
||
| /** | ||
| * Virtual interface used to drive the timing of bag playback. | ||
| * This clock should be used to query times and sleep between message playing, | ||
| * so that the complexity involved around time control and time sources | ||
| * is encapsulated in this one place. | ||
| */ | ||
| class PlayerClock | ||
| { | ||
| public: | ||
| // Type representing the current time as according to the playback | ||
| typedef rcutils_time_point_value_t ROSTimePoint; | ||
|
Karsten1987 marked this conversation as resolved.
Outdated
|
||
| /** | ||
| * Type representing an arbitrary steady time, used to measure real-time durations | ||
| * This type is never exposed by the PlayerClock - it is only used as input to the PlayerClock. | ||
| */ | ||
| typedef std::chrono::time_point<std::chrono::steady_clock> SteadyTimePoint; | ||
| typedef std::function<SteadyTimePoint()> NowFunction; | ||
|
|
||
| ROSBAG2_CPP_PUBLIC | ||
| virtual ~PlayerClock() = default; | ||
|
|
||
| /** | ||
| * Calculate and return current ROSTimePoint based on starting time, playback rate, pause state. | ||
| */ | ||
| ROSBAG2_CPP_PUBLIC | ||
| virtual ROSTimePoint now() const = 0; | ||
|
|
||
| /** | ||
| * Try to sleep (non-busy) the current thread until the provided time is reached - according to this Clock | ||
| * | ||
| * Return true if the time has been reached, false if it was not successfully reached after sleeping | ||
| * for the appropriate duration. | ||
| * The user should not take action based on this sleep until it returns true. | ||
| */ | ||
| ROSBAG2_CPP_PUBLIC | ||
| virtual bool sleep_until(ROSTimePoint until) = 0; | ||
|
|
||
| /** | ||
| * Return the current playback rate. | ||
| */ | ||
| ROSBAG2_CPP_PUBLIC | ||
| virtual double get_rate() const = 0; | ||
| }; | ||
|
|
||
| } // namespace rosbag2_cpp | ||
|
|
||
| #endif // ROSBAG2_CPP__CLOCKS__PLAYER_CLOCK_HPP_ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| // Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| #ifndef ROSBAG2_CPP__CLOCKS__TIME_CONTROLLER_CLOCK_HPP_ | ||
| #define ROSBAG2_CPP__CLOCKS__TIME_CONTROLLER_CLOCK_HPP_ | ||
|
|
||
| #include <chrono> | ||
| #include <memory> | ||
|
|
||
| #include "rosbag2_cpp/clocks/player_clock.hpp" | ||
|
|
||
| namespace rosbag2_cpp | ||
| { | ||
|
|
||
| /** | ||
| * Version of the PlayerClock interface that has control over time. | ||
| * It does not listen to any external ROS Time Source and can optionally publish /clock | ||
| */ | ||
| class TimeControllerClockImpl; | ||
| class TimeControllerClock : public PlayerClock | ||
| { | ||
| public: | ||
| /** | ||
| * Constructor. | ||
| * | ||
| * \param starting_time: provides an initial offset for managing time | ||
| * This will likely be the timestamp of the first message in the bag | ||
| * \param rate: Rate of playback, a unit-free ratio. 1.0 is real-time | ||
| * \param now_fn: Function used to get the current steady time | ||
| * defaults to std::chrono::steady_clock::now | ||
| * Used to control for unit testing, or for specialized needs | ||
| * \throws std::runtime_error if rate is <= 0 | ||
| */ | ||
| ROSBAG2_CPP_PUBLIC | ||
| TimeControllerClock( | ||
| ROSTimePoint starting_time, | ||
| double rate = 1.0, | ||
| NowFunction now_fn = std::chrono::steady_clock::now); | ||
|
|
||
| ROSBAG2_CPP_PUBLIC | ||
| virtual ~TimeControllerClock(); | ||
|
|
||
| /** | ||
| * Calculate and return current ROSTimePoint based on starting time, playback rate, pause state. | ||
| */ | ||
| ROSBAG2_CPP_PUBLIC | ||
| ROSTimePoint now() const override; | ||
|
|
||
| /** | ||
| * Try to sleep (non-busy) the current thread until the provided time is reached - according to this Clock | ||
| * | ||
| * Return true if the time has been reached, false if it was not successfully reached after sleeping | ||
| * for the appropriate duration. | ||
| * The user should not take action based on this sleep until it returns true. | ||
| */ | ||
| ROSBAG2_CPP_PUBLIC | ||
| bool sleep_until(ROSTimePoint until) override; | ||
|
|
||
| /** | ||
| * Return the current playback rate. | ||
| */ | ||
| ROSBAG2_CPP_PUBLIC | ||
| double get_rate() const override; | ||
|
|
||
| private: | ||
| std::unique_ptr<TimeControllerClockImpl> impl_; | ||
| }; | ||
|
|
||
|
|
||
| } // namespace rosbag2_cpp | ||
|
|
||
| #endif // ROSBAG2_CPP__CLOCKS__TIME_CONTROLLER_CLOCK_HPP_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| // Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| #include <memory> | ||
| #include <mutex> | ||
| #include <thread> | ||
|
|
||
| #include "rcpputils/thread_safety_annotations.hpp" | ||
| #include "rosbag2_cpp/clocks/time_controller_clock.hpp" | ||
| #include "rosbag2_cpp/types.hpp" | ||
|
|
||
| namespace rosbag2_cpp | ||
| { | ||
|
|
||
| class TimeControllerClockImpl | ||
| { | ||
| public: | ||
| /** | ||
| * Stores an exact time match between a system steady clock and the playback ROS clock. | ||
| * This snapshot is taken whenever a factor changes such that a new reference is needed, | ||
| * such as pause, resume, rate change, or jump | ||
| */ | ||
| struct TimeReference | ||
| { | ||
| PlayerClock::ROSTimePoint ros; | ||
| PlayerClock::SteadyTimePoint steady; | ||
| }; | ||
|
|
||
| TimeControllerClockImpl() = default; | ||
| virtual ~TimeControllerClockImpl() = default; | ||
|
|
||
| template<typename T> | ||
| rcutils_duration_value_t duration_nanos(const T & duration) | ||
| { | ||
| return std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count(); | ||
| } | ||
|
|
||
| PlayerClock::ROSTimePoint steady_to_ros(PlayerClock::SteadyTimePoint steady_time) | ||
| { | ||
| return reference.ros + (rate * duration_nanos(steady_time - reference.steady)); | ||
| } | ||
|
|
||
| PlayerClock::SteadyTimePoint ros_to_steady(PlayerClock::ROSTimePoint ros_time) | ||
| { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar. It would be nice to add |
||
| const rcutils_duration_value_t diff_nanos = (ros_time - reference.ros) / rate; | ||
| return reference.steady + std::chrono::nanoseconds(diff_nanos); | ||
| } | ||
|
|
||
| double rate = 1.0; | ||
| PlayerClock::NowFunction now_fn; | ||
| std::mutex mutex; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is bad practice to use names reserved in C++. It would be more safe if rename variable |
||
| TimeReference reference RCPPUTILS_TSA_GUARDED_BY(mutex); | ||
| }; | ||
|
|
||
| TimeControllerClock::TimeControllerClock( | ||
| ROSTimePoint starting_time, | ||
| double rate, | ||
| NowFunction now_fn) | ||
| : impl_(std::make_unique<TimeControllerClockImpl>()) | ||
| { | ||
| std::lock_guard<std::mutex> lock(impl_->mutex); | ||
| impl_->now_fn = now_fn; | ||
| impl_->reference.ros = starting_time; | ||
| impl_->reference.steady = impl_->now_fn(); | ||
| impl_->rate = rate; | ||
| } | ||
|
|
||
| TimeControllerClock::~TimeControllerClock() | ||
| {} | ||
|
|
||
| PlayerClock::ROSTimePoint TimeControllerClock::now() const | ||
| { | ||
| std::lock_guard<std::mutex> lock(impl_->mutex); | ||
| return impl_->steady_to_ros(impl_->now_fn()); | ||
| } | ||
|
|
||
| bool TimeControllerClock::sleep_until(ROSTimePoint until) | ||
| { | ||
| SteadyTimePoint steady_until; | ||
| { | ||
| std::lock_guard<std::mutex> lock(impl_->mutex); | ||
| steady_until = impl_->ros_to_steady(until); | ||
| } | ||
| // TODO(emersonknapp) - when we have methods that can change timeflow during a sleep, | ||
| // it will probably be better to use a condition_variable::wait_until | ||
|
Karsten1987 marked this conversation as resolved.
Outdated
|
||
| std::this_thread::sleep_until(steady_until); | ||
| return now() >= until; | ||
|
Karsten1987 marked this conversation as resolved.
|
||
| } | ||
|
|
||
| double TimeControllerClock::get_rate() const | ||
| { | ||
| std::lock_guard<std::mutex> lock(impl_->mutex); | ||
| return impl_->rate; | ||
| } | ||
|
|
||
| } // namespace rosbag2_cpp | ||
Uh oh!
There was an error while loading. Please reload this page.