DPsim
Loading...
Searching...
No Matches
Timer.cpp
1/* Copyright 2017-2021 Institute for Automation of Complex Power Systems,
2 * EONERC, RWTH Aachen University
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 *********************************************************************************/
8
9#include <assert.h>
10#include <thread>
11
12#include <dpsim-models/Definitions.h>
13#include <dpsim/Timer.h>
14
15#ifdef HAVE_TIMERFD
16#include <sys/timerfd.h>
17#include <unistd.h>
18#endif // HAVE_TIMERFD
19
20using namespace DPsim;
22
23Timer::Timer(int flags, CPS::Logger::Level logLevel)
24 : mState(stopped), mOverruns(0), mTicks(0), mFlags(flags),
25 mLogLevel(logLevel) {
26 mSLog = CPS::Logger::get("Timer");
27#ifdef HAVE_TIMERFD
28 mTimerFd = timerfd_create(CLOCK_MONOTONIC, 0);
29 if (mTimerFd < 0) {
30 SPDLOG_LOGGER_ERROR(mSLog, "Failed to create timerfd");
31 throw SystemError("Failed to create timerfd");
32 }
33#else
34 SPDLOG_LOGGER_WARN(mSLog,
35 "No high resolution timer available. Clock might drift!");
36#endif
37}
38
39Timer::~Timer() {
40 if (mState == State::running)
41 try {
42 stop();
43 } catch (SystemError &e) {
44 SPDLOG_LOGGER_ERROR(mSLog,
45 "The timer was not stopped properly. The simulation "
46 "behaviour is not guaranteed anymore.");
47 }
48
49#ifdef HAVE_TIMERFD
50 close(mTimerFd);
51#endif
52}
53
55 uint64_t ticks = 0, overruns;
56
57#ifdef HAVE_TIMERFD
58 ssize_t bytes;
59
60 bytes = read(mTimerFd, &ticks, sizeof(ticks));
61 if (bytes < 0) {
62 SPDLOG_LOGGER_ERROR(mSLog, "Read from timerfd failed");
63 throw SystemError("Read from timerfd failed");
64 }
65#else
66 std::this_thread::sleep_until(mNextTick);
67
68 auto now = IntervalClock::now();
69
70 while (mNextTick < now) {
71 mNextTick += mTickInterval;
72 ticks++;
73 }
74
75#endif
76 overruns = ticks - 1;
77
78 mOverruns += overruns;
79 mTicks += ticks;
80
81 if (overruns > 0) {
82 SPDLOG_LOGGER_WARN(mSLog, "Timer overrun of {} timesteps at {}", overruns,
83 mTicks);
84 if (mFlags & Flags::fail_on_overrun) {
85 SPDLOG_LOGGER_ERROR(mSLog, "The overrun has made the simulation to fail "
86 "because the flag is active");
87 throw OverrunException{overruns};
88 }
89 }
90}
91
93 assert(mState == stopped);
94
95 mTicks = 0;
96 mOverruns = 0;
97
98 // Determine offset between clocks.
99 auto rt = StartClock::now();
100 auto steady = IntervalClock::now();
101
102 /* This handles the offset between
103 * - IntervalClock (CLOCK_MONOTONIC aka std::chrono::steady_clock) and
104 * - StartClock (CLOCK_REALTIME aka std::chrono::system_clock)
105 */
106 auto start = mStartAt > StartTimePoint()
107 ? mStartAt.time_since_epoch() - rt.time_since_epoch() +
108 steady.time_since_epoch()
109 : steady.time_since_epoch();
110
111#ifdef HAVE_TIMERFD
112 int ret;
113 struct itimerspec ts = {.it_interval = to_timespec(mTickInterval),
114 .it_value = to_timespec(start)};
115
116 ret = timerfd_settime(mTimerFd, TFD_TIMER_ABSTIME, &ts, 0);
117 if (ret < 0) {
118 throw SystemError("Failed to arm timerfd");
119 }
120#endif
121 mNextTick = IntervalTimePoint(start) + mTickInterval;
122 mState = State::running;
123}
124
126 assert(mState == State::running);
127
128#ifdef HAVE_TIMERFD
129 int ret;
130 struct itimerspec ts = {.it_interval = {0, 0}, .it_value = {0, 0}};
131
132 ret = timerfd_settime(mTimerFd, 0, &ts, 0);
133 if (ret < 0) {
134 throw SystemError("Failed to arm timerfd");
135 }
136#endif
137
138 mState = State::stopped;
139}
void start()
Start real-time timer.
Definition Timer.cpp:92
void stop()
Stop real-time timer.
Definition Timer.cpp:125
CPS::Logger::Log mSLog
Logger.
Definition Timer.h:49
void sleep()
Suspend thread execution until next tick.
Definition Timer.cpp:54