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