diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 17e339885..46ca601bc 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -109,6 +109,7 @@ if (PYTHONLIBS_FOUND) SemanticVersion_TEST SignalStats_TEST Spline_TEST + StopWatch_TEST Temperature_TEST Triangle_TEST Triangle3_TEST diff --git a/src/python/StopWatch.i b/src/python/StopWatch.i new file mode 100644 index 000000000..161bb6105 --- /dev/null +++ b/src/python/StopWatch.i @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * 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. + * +*/ + +%module stopwatch +%{ +#include +#include +#include "ignition/math/Stopwatch.hh" +#include +#include +%} + +%include "typemaps.i" +%typemap(out) (std::chrono::steady_clock::time_point) %{ + $result = SWIG_From_long(std::chrono::duration_cast( + (&result)->time_since_epoch()).count()); +%} + +%typemap(out) (std::chrono::steady_clock::duration) %{ + $result = SWIG_From_long((&result)->count()); +%} + +namespace ignition +{ + namespace math + { + class Stopwatch + { + public: Stopwatch(); + + public: virtual ~Stopwatch(); + + public: bool Start(const bool _reset = false); + + public: std::chrono::steady_clock::time_point StartTime() const; + + public: bool Stop(); + + public: std::chrono::steady_clock::time_point StopTime() const; + + public: bool Running() const; + + public: void Reset(); + + public: std::chrono::steady_clock::duration ElapsedRunTime() const; + + public: std::chrono::steady_clock::duration ElapsedStopTime() const; + + public: bool operator==(const Stopwatch &_watch) const; + + public: bool operator!=(const Stopwatch &_watch) const; + }; + } +} diff --git a/src/python/StopWatch_TEST.py b/src/python/StopWatch_TEST.py new file mode 100644 index 000000000..b0c777e7b --- /dev/null +++ b/src/python/StopWatch_TEST.py @@ -0,0 +1,160 @@ +# Copyright (C) 2021 Open Source Robotics Foundation +# +# 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. + +import time +import unittest +from datetime import datetime, timedelta + +from ignition.math import Stopwatch + + +class TestBox(unittest.TestCase): + # Helper function that runs a few tests + def runTimer(self, _time): + handleSteadyClock = 0 + + # Start the timer + self.assertTrue(_time.start()) + # The timer should be running. + self.assertTrue(_time.running()) + # The start time should be greater than the stop time. + self.assertGreater(_time.start_time(), _time.stop_time()) + # The elapsed stop time should still be zero. + self.assertEqual(0, _time.elapsed_stop_time()) + + # Wait for some time... + time.sleep(1) + # Now the elapsed time should be greater than or equal to the time slept. + self.assertGreaterEqual(_time.elapsed_run_time() + handleSteadyClock, 1000) + + # Stop the timer. + self.assertTrue(_time.stop()) + # The timer should not be running. + self.assertFalse(_time.running()) + # The stop time should be greater than the start time. + self.assertGreater(_time.stop_time(), _time.start_time()) + # The elapsed time should still be greater than the time slept. + self.assertGreaterEqual(_time.elapsed_run_time() + handleSteadyClock, 1000) + + # Save the elapsed time. + elapsedTime = _time.elapsed_run_time() + + # The timer is now stopped, let's sleep some more. + time.sleep(1) + # The elapsed stop time should be greater than or equal to the time + # slept. + self.assertGreaterEqual(_time.elapsed_stop_time() + handleSteadyClock, 1000) + # The elapsed time should be the same. + self.assertEqual(elapsedTime, _time.elapsed_run_time()) + + # Start the timer again. + self.assertTrue(_time.start()) + # Store the elapsed stop time. + elapsedStopTime = _time.elapsed_stop_time() + # The timer should be running. + self.assertTrue(_time.running()) + # Sleep for some time. + time.sleep(1) + # The elapsed stop time should remain the same + self.assertEqual(elapsedStopTime, _time.elapsed_stop_time()) + # The elapsed time should be greater than the previous elapsed time. + self.assertGreater(_time.elapsed_run_time(), elapsedTime) + # The elapsed time should be greater than or equal to the the previous + # two sleep times. + self.assertGreaterEqual(_time.elapsed_run_time() + handleSteadyClock, 2000) + + def test_constructor(self): + watch = Stopwatch() + + self.assertFalse(watch.running()) + self.assertEqual(watch.stop_time(), watch.start_time()) + self.assertEqual(0, watch.elapsed_run_time()) + self.assertEqual(0, watch.elapsed_stop_time()) + + self.runTimer(watch) + + watch2 = watch + self.assertEqual(watch, watch2) + + watch3 = watch2 + self.assertEqual(watch, watch3) + + def test_equal_operator(self): + watch = Stopwatch() + watch2 = Stopwatch() + watch3 = Stopwatch() + self.assertEqual(watch, watch2) + self.assertEqual(watch, watch3) + + self.runTimer(watch) + self.runTimer(watch2) + self.runTimer(watch3) + + self.assertNotEqual(watch, watch2) + self.assertNotEqual(watch, watch3) + + watch2 = watch + self.assertEqual(watch, watch2) + + watch3 = watch2 + self.assertEqual(watch, watch3) + + def test_start_stop_reset(self): + watch = Stopwatch() + + self.runTimer(watch) + + watch.reset() + + self.assertFalse(watch.running()) + self.assertEqual(watch.stop_time(), watch.start_time()) + self.assertEqual(0, watch.elapsed_run_time()) + self.assertEqual(0, watch.elapsed_stop_time()) + + self.runTimer(watch) + + self.assertTrue(watch.running()) + + watch.start(True) + self.assertTrue(watch.running()) + self.assertLess(watch.stop_time(), watch.start_time()) + self.assertNotEqual(0, watch.elapsed_run_time()) + self.assertEqual(0, watch.elapsed_stop_time()) + + def test_fail_start_stop(self): + watch = Stopwatch() + + # Can't stop while not running + self.assertFalse(watch.stop()) + self.assertFalse(watch.running()) + + # Can start while not running + self.assertTrue(watch.start()) + self.assertTrue(watch.running()) + + # Can't start while running + self.assertFalse(watch.start()) + self.assertTrue(watch.running()) + + # Can stop while running + self.assertTrue(watch.stop()) + self.assertFalse(watch.running()) + + # Can start while not running + self.assertTrue(watch.start()) + self.assertTrue(watch.running()) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/python/python.i b/src/python/python.i index 7dc941033..80d6e0572 100644 --- a/src/python/python.i +++ b/src/python/python.i @@ -3,6 +3,7 @@ %include GaussMarkovProcess.i %include Helpers.i %include Rand.i +%include StopWatch.i %include Vector2.i %include Vector3.i %include Vector4.i