1 #include <cpp-pcp-client/connector/timings.hpp>
2
3 #include <leatherman/locale/locale.hpp>
4
5 #include <stdint.h>
6
7 namespace PCPClient {
8
9 namespace lth_loc = leatherman::locale;
10
11 // Helper
12
normalizeTimeInterval(uint32_t duration_min)13 static std::string normalizeTimeInterval(uint32_t duration_min)
14 {
15 auto hours = duration_min / 60;
16 auto minutes = duration_min % 60;
17
18 if (hours > 0)
19 return lth_loc::format("{1} hrs {2} min", hours, minutes);
20
21 return lth_loc::format("{1} min", minutes);
22 }
23
24 //
25 // ConnectionTimings
26 //
27
isOpen() const28 bool ConnectionTimings::isOpen() const { return _open; }
isClosingStarted() const29 bool ConnectionTimings::isClosingStarted() const { return _closing_started; }
isFailed() const30 bool ConnectionTimings::isFailed() const { return _failed; }
isClosed() const31 bool ConnectionTimings::isClosed() const { return _closed; }
32
reset()33 void ConnectionTimings::reset()
34 {
35 start = boost::chrono::high_resolution_clock::now();
36 tcp_pre_init = boost::chrono::high_resolution_clock::time_point();
37 tcp_post_init = boost::chrono::high_resolution_clock::time_point();
38 open = boost::chrono::high_resolution_clock::time_point();
39 closing_handshake = boost::chrono::high_resolution_clock::time_point();
40 close = boost::chrono::high_resolution_clock::time_point();
41
42 _open = false;
43 _closing_started = false;
44 _failed = false;
45 _closed = false;
46 }
47
setOpen()48 void ConnectionTimings::setOpen()
49 {
50 open = boost::chrono::high_resolution_clock::now();
51 _open = true;
52 }
53
setClosing()54 void ConnectionTimings::setClosing()
55 {
56 closing_handshake = boost::chrono::high_resolution_clock::now();
57 _closing_started = true;
58 }
59
setClosed(bool onFail_event)60 void ConnectionTimings::setClosed(bool onFail_event)
61 {
62 close = boost::chrono::high_resolution_clock::now();
63 _closed = true;
64 _failed = onFail_event;
65 }
66
getTCPInterval() const67 ConnectionTimings::Duration_us ConnectionTimings::getTCPInterval() const
68 {
69 return boost::chrono::duration_cast<ConnectionTimings::Duration_us>(
70 tcp_pre_init - start);
71 }
72
73 ConnectionTimings::Duration_us
getOpeningHandshakeInterval() const74 ConnectionTimings::getOpeningHandshakeInterval() const
75 {
76 if (!_open)
77 return Duration_us::zero();
78
79 return boost::chrono::duration_cast<ConnectionTimings::Duration_us>(
80 tcp_post_init - tcp_pre_init);
81 }
82
getWebSocketInterval() const83 ConnectionTimings::Duration_us ConnectionTimings::getWebSocketInterval() const
84 {
85 if (!_open)
86 return Duration_us::zero();
87
88 return boost::chrono::duration_cast<ConnectionTimings::Duration_us>(
89 open - start);
90 }
91
92 ConnectionTimings::Duration_us
getClosingHandshakeInterval() const93 ConnectionTimings::getClosingHandshakeInterval() const
94 {
95 if (!_open || !_closing_started || !_closed)
96 return Duration_us::zero();
97
98 return boost::chrono::duration_cast<ConnectionTimings::Duration_us>(
99 close - closing_handshake);
100 }
101
102 ConnectionTimings::Duration_min
getOverallConnectionInterval_min() const103 ConnectionTimings::getOverallConnectionInterval_min() const
104 {
105 if (!_open)
106 return Duration_min::zero();
107
108 if (!_closed)
109 return boost::chrono::duration_cast<Duration_min>(
110 boost::chrono::high_resolution_clock::now() - start);
111
112 return boost::chrono::duration_cast<Duration_min>(close - start);
113 }
114
115 ConnectionTimings::Duration_us
getOverallConnectionInterval_us() const116 ConnectionTimings::getOverallConnectionInterval_us() const
117 {
118 if (!_open)
119 return Duration_us::zero();
120
121 if (_closed)
122 return boost::chrono::duration_cast<Duration_us>(close - start);
123
124 return boost::chrono::duration_cast<Duration_us>(
125 boost::chrono::high_resolution_clock::now() - start);
126 }
127
toString() const128 std::string ConnectionTimings::toString() const
129 {
130 if (_open)
131 return lth_loc::format(
132 "connection timings: TCP {1} us, WS handshake {2} us, overall {3} us",
133 getTCPInterval().count(),
134 getOpeningHandshakeInterval().count(),
135 getWebSocketInterval().count());
136
137 if (_failed)
138 return lth_loc::format("time to failure {1}", getOverallDurationTxt());
139
140 return lth_loc::translate("the endpoint has not been connected yet");
141 }
142
143 // Private helper
getOverallDurationTxt() const144 std::string ConnectionTimings::getOverallDurationTxt() const
145 {
146 auto duration_min = getOverallConnectionInterval_min().count();
147
148 if (duration_min)
149 return normalizeTimeInterval(duration_min);
150
151 return lth_loc::format("{1} us", getOverallConnectionInterval_us().count());
152 }
153
154 //
155 // AssociationTimings
156 //
157
reset()158 void AssociationTimings::reset()
159 {
160 start = boost::chrono::high_resolution_clock::now();
161 association = boost::chrono::high_resolution_clock::time_point();
162 close = boost::chrono::high_resolution_clock::time_point();
163 completed = false;
164 success = false;
165 closed = false;
166 }
167
setCompleted(bool _success)168 void AssociationTimings::setCompleted(bool _success)
169 {
170 if (closed) {
171 // The WebSocket connection was previously closed; use its timestamp
172 association = close;
173 } else {
174 association = boost::chrono::high_resolution_clock::now();
175 }
176
177 completed = true;
178 success = _success;
179 }
180
setClosed()181 void AssociationTimings::setClosed()
182 {
183 close = boost::chrono::high_resolution_clock::now();
184 closed = true;
185 }
186
getAssociationInterval() const187 AssociationTimings::Duration_ms AssociationTimings::getAssociationInterval() const
188 {
189 if (!completed)
190 return Duration_ms::zero();
191
192 return boost::chrono::duration_cast<AssociationTimings::Duration_ms>(
193 association - start);
194 }
195
getOverallSessionInterval_min() const196 AssociationTimings::Duration_min AssociationTimings::getOverallSessionInterval_min() const
197 {
198 if (!completed)
199 return Duration_min::zero();
200
201 if (closed)
202 return boost::chrono::duration_cast<AssociationTimings::Duration_min>(
203 close - association);
204
205 return boost::chrono::duration_cast<AssociationTimings::Duration_min>(
206 boost::chrono::high_resolution_clock::now() - association);
207 }
208
getOverallSessionInterval_ms() const209 AssociationTimings::Duration_ms AssociationTimings::getOverallSessionInterval_ms() const
210 {
211 if (!completed)
212 return Duration_ms::zero();
213
214 if (closed)
215 return boost::chrono::duration_cast<AssociationTimings::Duration_ms>(
216 close - association);
217
218 return boost::chrono::duration_cast<AssociationTimings::Duration_ms>(
219 boost::chrono::high_resolution_clock::now() - association);
220 }
221
toString(bool include_completion) const222 std::string AssociationTimings::toString(bool include_completion) const
223 {
224 if (!completed)
225 return lth_loc::translate("the endpoint has not been associated yet");
226
227 if (success) {
228 if (closed) {
229 return lth_loc::format(
230 "PCP Session Association successfully completed in {1} ms, "
231 "then closed after {2}",
232 getAssociationInterval().count(),
233 normalizeTimeInterval(getOverallSessionInterval_min().count()));
234 } else if (include_completion) {
235 return lth_loc::format(
236 "PCP Session Association successfully completed in {1} ms; "
237 "the current session has been associated for {2}",
238 getAssociationInterval().count(),
239 normalizeTimeInterval(getOverallSessionInterval_min().count()));
240 } else {
241 return lth_loc::format(
242 "PCP Session Association successfully completed in {1} ms",
243 getAssociationInterval().count());
244 }
245 } else {
246 return lth_loc::format("PCP Session Association failed after {1} ms",
247 getAssociationInterval().count());
248 }
249 }
250
251 } // namespace PCPClient
252