GCC Code Coverage Report


Directory: Svc/TimeConverter/
File: TimeConverter.cpp
Date: 2026-09-23 21:14:55
Exec Total Coverage
Lines: 113 114 99.1%
Functions: 14 14 100.0%
Branches: 134 138 97.1%

Line Branch Exec Source
1 // ======================================================================
2 // \title TimeConverter.cpp
3 // \author devin
4 // \brief cpp file for TimeConverter component implementation class
5 // ======================================================================
6
7 #include "Svc/TimeConverter/TimeConverter.hpp"
8
9 namespace Svc {
10
11 constexpr I64 TimeConverter::US_PER_SECOND;
12 constexpr I64 TimeConverter::MAX_TIME_US;
13
14 namespace {
15 //! Numeric value of a time base, used to order a pair canonically
16 516 FwTimeBaseStoreType timeBaseValue(const TimeBase& timeBase) {
17
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 516 times.
516 return static_cast<FwTimeBaseStoreType>(timeBase.e);
18 }
19
20 //! A time base denotes a clock, rather than the absence of one or a wildcard
21 238 bool usableTimeBase(const TimeBase& timeBase) {
22
6/8
✗ Branch 3 not taken.
✓ Branch 4 taken 238 times.
✓ Branch 5 taken 227 times.
✓ Branch 6 taken 11 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 227 times.
✓ Branch 12 taken 224 times.
✓ Branch 13 taken 3 times.
238 return (timeBase.e != TimeBase::TB_NONE) && (timeBase.e != TimeBase::TB_DONT_CARE);
23 }
24
25 //! A pair of time bases in canonical order, lesser numeric value first
26 83 Svc::TimeBasePair canonicalPair(const TimeBase& from, const TimeBase& to) {
27 83 const bool ascending = timeBaseValue(from) < timeBaseValue(to);
28
16/16
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 47 times.
✓ Branch 3 taken 36 times.
✓ Branch 4 taken 47 times.
✓ Branch 5 taken 36 times.
✓ Branch 6 taken 47 times.
✓ Branch 9 taken 36 times.
✓ Branch 10 taken 47 times.
✓ Branch 12 taken 36 times.
✓ Branch 13 taken 47 times.
✓ Branch 15 taken 36 times.
✓ Branch 16 taken 47 times.
✓ Branch 17 taken 36 times.
✓ Branch 18 taken 47 times.
✓ Branch 21 taken 36 times.
✓ Branch 22 taken 47 times.
83 return Svc::TimeBasePair(ascending ? from : to, ascending ? to : from);
29 }
30 } // namespace
31
32 // ----------------------------------------------------------------------
33 // Component construction and destruction
34 // ----------------------------------------------------------------------
35
36
1/1
✓ Branch 10 taken 20 times.
20 TimeConverter ::TimeConverter(const char* const compName) : TimeConverterComponentBase(compName) {}
37
38 40 TimeConverter ::~TimeConverter() {}
39
40 // ----------------------------------------------------------------------
41 // Handler implementations for user-defined typed input ports
42 // ----------------------------------------------------------------------
43
44 6 void TimeConverter ::offsetUpdate_handler(FwIndexType portNum, const Svc::TimeOffset& offset) {
45 // storeOffset reports every failure by event, and the port carries no status back
46
3/3
✓ Branch 13 taken 6 times.
✓ Branch 22 taken 6 times.
✓ Branch 26 taken 6 times.
6 (void)this->storeOffset(offset.get_from(), offset.get_to(), offset.get_offset_us());
47 6 }
48
49 32 Svc::ConvertTimeStatus TimeConverter ::convertTime_handler(FwIndexType portNum,
50 const Fw::Time& in_time,
51 Fw::Time& out_time) {
52
1/1
✓ Branch 2 taken 32 times.
32 const TimeBase in_tb = in_time.getTimeBase();
53
1/1
✓ Branch 2 taken 32 times.
32 const TimeBase out_tb = out_time.getTimeBase();
54
55
3/3
✓ Branch 2 taken 32 times.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 31 times.
32 if (this->checkTimeBases(in_tb, out_tb) != Fw::Success::SUCCESS) {
56
1/1
✓ Branch 1 taken 1 times.
1 return Svc::ConvertTimeStatus::UNKNOWN_TIMEBASE;
57 }
58
59 // A time is already in the requested base: no offset is needed
60
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 30 times.
31 if (timeBaseValue(in_tb) == timeBaseValue(out_tb)) {
61
1/1
✓ Branch 4 taken 1 times.
1 out_time = in_time;
62
1/1
✓ Branch 1 taken 1 times.
1 return Svc::ConvertTimeStatus::OK;
63 }
64
65 30 I64 offset_us = 0;
66
3/3
✓ Branch 2 taken 30 times.
✓ Branch 11 taken 12 times.
✓ Branch 12 taken 18 times.
30 if (this->lookupOffset(in_tb, out_tb, offset_us) != Fw::Success::SUCCESS) {
67
1/1
✓ Branch 5 taken 12 times.
12 this->log_WARNING_LO_NoConversionAvailable(in_tb, out_tb);
68
1/1
✓ Branch 1 taken 12 times.
12 return Svc::ConvertTimeStatus::UNKNOWN_TIMEBASE;
69 }
70
71 // Offsets are range-checked when stored, so this addition cannot overflow
72
2/2
✓ Branch 4 taken 18 times.
✓ Branch 10 taken 18 times.
18 const I64 in_time_us = static_cast<I64>(in_time.getSeconds()) * US_PER_SECOND + in_time.getUSeconds();
73 18 const I64 out_time_us = in_time_us + offset_us;
74
4/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 8 times.
18 if ((out_time_us < 0) || (out_time_us > MAX_TIME_US)) {
75
1/1
✓ Branch 5 taken 10 times.
10 this->log_WARNING_LO_InvalidTime(in_tb, out_tb, offset_us, in_time_us);
76
1/1
✓ Branch 1 taken 10 times.
10 return Svc::ConvertTimeStatus::INVALID_TIME;
77 }
78
79
3/3
✓ Branch 6 taken 8 times.
✓ Branch 10 taken 8 times.
✓ Branch 13 taken 8 times.
16 out_time.set(out_tb, in_time.getContext(), static_cast<U32>(out_time_us / US_PER_SECOND),
80 8 static_cast<U32>(out_time_us % US_PER_SECOND));
81
1/1
✓ Branch 1 taken 8 times.
8 return Svc::ConvertTimeStatus::OK;
82 32 }
83
84 // ----------------------------------------------------------------------
85 // Handler implementations for commands
86 // ----------------------------------------------------------------------
87
88 66 void TimeConverter ::SET_OFFSET_cmdHandler(FwOpcodeType opCode,
89 U32 cmdSeq,
90 const TimeBase& from,
91 const TimeBase& to,
92 I64 offset_us) {
93
1/1
✓ Branch 2 taken 66 times.
66 Fw::CmdResponse response = Fw::CmdResponse::OK;
94
4/5
✓ Branch 4 taken 66 times.
✓ Branch 6 taken 28 times.
✓ Branch 7 taken 9 times.
✓ Branch 8 taken 29 times.
✗ Branch 9 not taken.
66 switch (this->storeOffset(from, to, offset_us)) {
95 28 case StoreStatus::OK:
96
1/1
✓ Branch 5 taken 28 times.
28 this->log_ACTIVITY_HI_OffsetSet(from, to, offset_us);
97 28 break;
98 // A full table is a state the operator clears, not a bad argument
99 9 case StoreStatus::TABLE_FULL:
100
1/1
✓ Branch 2 taken 9 times.
9 response = Fw::CmdResponse::EXECUTION_ERROR;
101 9 break;
102 29 case StoreStatus::INVALID:
103
1/1
✓ Branch 2 taken 29 times.
29 response = Fw::CmdResponse::VALIDATION_ERROR;
104 29 break;
105 }
106
2/2
✓ Branch 6 taken 66 times.
✓ Branch 9 taken 66 times.
66 this->cmdResponse_out(opCode, cmdSeq, response);
107 132 }
108
109 8 void TimeConverter ::CLEAR_OFFSETS_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
110 8 const FwSizeType cleared = this->m_offsets.getSize();
111 8 this->m_offsets.clear();
112 // Clearing the table is the operator's recovery action, so re-arm the throttled warnings
113 8 this->log_WARNING_LO_NoConversionAvailable_ThrottleClear();
114 8 this->log_WARNING_LO_InvalidTime_ThrottleClear();
115 8 this->log_WARNING_LO_IdenticalTimeBases_ThrottleClear();
116 8 this->log_WARNING_HI_OffsetOutOfRange_ThrottleClear();
117 8 this->log_WARNING_HI_OffsetTableFull_ThrottleClear();
118 8 this->log_WARNING_HI_UnusableTimeBase_ThrottleClear();
119 8 this->log_ACTIVITY_HI_OffsetsCleared(static_cast<U32>(cleared));
120
2/2
✓ Branch 6 taken 8 times.
✓ Branch 9 taken 8 times.
8 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
121 8 }
122
123 15 void TimeConverter ::GET_OFFSET_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, const TimeBase& from, const TimeBase& to) {
124
3/3
✓ Branch 2 taken 15 times.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 14 times.
15 if (this->checkTimeBases(from, to) != Fw::Success::SUCCESS) {
125
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
126 1 return;
127 }
128
129
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 13 times.
14 if (timeBaseValue(from) == timeBaseValue(to)) {
130
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_LO_IdenticalTimeBases(from);
131
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
132 1 return;
133 }
134
135 13 I64 offset_us = 0;
136
3/3
✓ Branch 2 taken 13 times.
✓ Branch 11 taken 2 times.
✓ Branch 12 taken 11 times.
13 if (this->lookupOffset(from, to, offset_us) != Fw::Success::SUCCESS) {
137
1/1
✓ Branch 5 taken 2 times.
2 this->log_WARNING_LO_NoOffsetStored(from, to);
138
2/2
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
2 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
139 2 return;
140 }
141
142
1/1
✓ Branch 5 taken 11 times.
11 this->log_ACTIVITY_HI_OffsetReport(from, to, offset_us);
143
2/2
✓ Branch 6 taken 11 times.
✓ Branch 9 taken 11 times.
11 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
144 }
145
146 4 void TimeConverter ::DUMP_OFFSETS_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
147
2/2
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 3 times.
4 if (this->m_offsets.getSize() == 0) {
148 1 this->log_ACTIVITY_HI_OffsetTableEmpty();
149 }
150
6/6
✓ Branch 4 taken 4 times.
✓ Branch 7 taken 5 times.
✓ Branch 13 taken 9 times.
✓ Branch 17 taken 9 times.
✓ Branch 21 taken 5 times.
✓ Branch 22 taken 4 times.
14 for (auto it = this->m_offsets.begin(); it != this->m_offsets.end(); ++it) {
151
2/2
✓ Branch 1 taken 5 times.
✓ Branch 10 taken 5 times.
5 const Svc::TimeBasePair& pair = it->getKey();
152
5/5
✓ Branch 5 taken 5 times.
✓ Branch 14 taken 5 times.
✓ Branch 23 taken 5 times.
✓ Branch 32 taken 5 times.
✓ Branch 36 taken 5 times.
5 this->log_ACTIVITY_HI_OffsetReport(pair.get_lower(), pair.get_upper(), it->getValue());
153 4 }
154
2/2
✓ Branch 6 taken 4 times.
✓ Branch 9 taken 4 times.
4 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
155 4 }
156
157 // ----------------------------------------------------------------------
158 // Helper functions
159 // ----------------------------------------------------------------------
160
161 43 Fw::Success TimeConverter ::lookupOffset(const TimeBase& from, const TimeBase& to, I64& offset_us) const {
162 43 I64 stored_us = 0;
163
2/2
✓ Branch 4 taken 43 times.
✓ Branch 9 taken 43 times.
43 const Fw::Success status = this->m_offsets.find(canonicalPair(from, to), stored_us);
164
2/2
✓ Branch 3 taken 29 times.
✓ Branch 4 taken 14 times.
43 if (status == Fw::Success::SUCCESS) {
165 // Offsets are stored in canonical order; the reverse conversion negates them
166
2/2
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 9 times.
29 offset_us = (timeBaseValue(from) < timeBaseValue(to)) ? stored_us : -stored_us;
167 }
168 86 return status;
169 }
170
171 72 TimeConverter::StoreStatus TimeConverter ::storeOffset(const TimeBase& from, const TimeBase& to, I64 offset_us) {
172
3/3
✓ Branch 2 taken 72 times.
✓ Branch 11 taken 11 times.
✓ Branch 12 taken 61 times.
72 if (this->checkTimeBases(from, to) != Fw::Success::SUCCESS) {
173 11 return StoreStatus::INVALID;
174 }
175
2/2
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 51 times.
61 if (timeBaseValue(from) == timeBaseValue(to)) {
176
1/1
✓ Branch 5 taken 10 times.
10 this->log_WARNING_LO_IdenticalTimeBases(from);
177 10 return StoreStatus::INVALID;
178 }
179 // Bound stored offsets so that applying or negating one cannot overflow
180
4/4
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 40 times.
51 if ((offset_us > MAX_TIME_US) || (offset_us < -MAX_TIME_US)) {
181
1/1
✓ Branch 5 taken 11 times.
11 this->log_WARNING_HI_OffsetOutOfRange(from, to, offset_us);
182 11 return StoreStatus::INVALID;
183 }
184
185 40 const bool ascending = timeBaseValue(from) < timeBaseValue(to);
186
2/2
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 14 times.
40 const I64 stored_us = ascending ? offset_us : -offset_us;
187
188 // An insert replaces the entry for a pair already stored, and fails only when the table is full
189
4/4
✓ Branch 5 taken 40 times.
✓ Branch 9 taken 40 times.
✓ Branch 21 taken 10 times.
✓ Branch 22 taken 30 times.
40 if (this->m_offsets.insert(canonicalPair(from, to), stored_us) != Fw::Success::SUCCESS) {
190
1/1
✓ Branch 5 taken 10 times.
10 this->log_WARNING_HI_OffsetTableFull(from, to, static_cast<U32>(Svc::TimeConverterCfg::MAX_OFFSET_ENTRIES));
191 10 return StoreStatus::TABLE_FULL;
192 }
193 30 return StoreStatus::OK;
194 }
195
196 119 Fw::Success TimeConverter ::checkTimeBases(const TimeBase& from, const TimeBase& to) {
197 // TB_NONE and TB_DONT_CARE name no clock, so no offset relates them to one
198 119 Fw::Success status = Fw::Success::SUCCESS;
199
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 107 times.
119 if (!usableTimeBase(from)) {
200
1/1
✓ Branch 5 taken 12 times.
12 this->log_WARNING_HI_UnusableTimeBase(from);
201
1/1
✓ Branch 3 taken 12 times.
12 status = Fw::Success::FAILURE;
202 }
203
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 117 times.
119 if (!usableTimeBase(to)) {
204
1/1
✓ Branch 5 taken 2 times.
2 this->log_WARNING_HI_UnusableTimeBase(to);
205
1/1
✓ Branch 3 taken 2 times.
2 status = Fw::Success::FAILURE;
206 }
207 119 return status;
208 ✗ }
209
210 } // namespace Svc
211