GCC Code Coverage Report


Directory: ./
File: Svc/PrmDb/PrmDbImpl.cpp
Date: 2026-09-03 22:12:29
Exec Total Coverage
Lines: 47 355 13.2%
Functions: 9 20 45.0%
Branches: 28 384 7.3%

Line Branch Exec Source
1 /*
2 * PrmDbImpl.cpp
3 *
4 * Created on: March 9, 2015
5 * Author: Timothy Canham
6 */
7
8 #include <Fw/Types/Assert.hpp>
9 #include <Svc/PrmDb/PrmDbImpl.hpp>
10
11 #include <Os/File.hpp>
12 #include <Os/SandboxedFile.hpp>
13 #include <Utils/Hash/Hash.hpp>
14
15 #include <cstdio>
16 #include <cstring>
17
18 static_assert(std::numeric_limits<FwSizeType>::max() >= PRMDB_NUM_DB_ENTRIES,
19 "PRMDB_NUM_DB_ENTRIES must fit within range of FwSizeType");
20
21 namespace Svc {
22
23 // anonymous namespace for buffer declaration
24 namespace {
25 class WorkingBuffer : public Fw::LinearBufferBase {
26 public:
27 WorkingBuffer() : Fw::LinearBufferBase(m_buff, sizeof(m_buff)) {}
28
29 private:
30 // Set to max of parameter buffer + id
31 U8 m_buff[FW_PARAM_BUFFER_MAX_SIZE + sizeof(FwPrmIdType)];
32 static_assert(sizeof(m_buff) >= sizeof(U32), "Size of parameter buffer storage must be >= sizeof(U32)");
33 };
34 } // namespace
35
36 //! ----------------------------------------------------------------------
37 //! Construction, initialization, and destruction
38 //! ----------------------------------------------------------------------
39
5/5
✓ Branch 2 taken 1 times.
✓ Branch 5 taken 1 times.
✓ Branch 8 taken 1 times.
✓ Branch 11 taken 1 times.
✓ Branch 14 taken 1 times.
1 PrmDbImpl::PrmDbImpl(const char* name) : PrmDbComponentBase(name), m_state(PrmDbFileLoadState::IDLE) {
40 1 this->m_activeDb = &this->m_dbStore1;
41 1 this->m_stagingDb = &this->m_dbStore2;
42
43
2/2
✓ Branch 1 taken 1 times.
✓ Branch 4 taken 1 times.
1 this->clearDb(PrmDbType::DB_ACTIVE);
44
2/2
✓ Branch 1 taken 1 times.
✓ Branch 4 taken 1 times.
1 this->clearDb(PrmDbType::DB_STAGING);
45 1 }
46
47 2 PrmDbImpl::~PrmDbImpl() {}
48
49 1 void PrmDbImpl::configure(const char* file) {
50 1 FW_ASSERT(file != nullptr);
51 1 this->m_fileName = file;
52 1 }
53
54 void PrmDbImpl::configureLoadSandbox(const char* directory) {
55 FW_ASSERT(directory != nullptr);
56 this->m_sandboxDir = directory;
57 }
58
59 void PrmDbImpl::readParamFile() {
60 // Assumed to run at initialization time
61 // State should be IDLE upon entry
62 FW_ASSERT(static_cast<FwAssertArgType>(m_state == PrmDbFileLoadState::IDLE));
63
64 // Clear databases
65 this->clearDb(PrmDbType::DB_ACTIVE);
66 this->clearDb(PrmDbType::DB_STAGING);
67
68 // Read parameter file to active database
69 (void)readParamFileImpl(this->m_fileName, PrmDbType::DB_ACTIVE);
70 }
71
72 //! ----------------------------------------------------------------------
73 //! Port & Command Handlers
74 //! ----------------------------------------------------------------------
75
76 // If ports are no longer guarded, these accesses need to be protected from each other
77 // If there are a lot of accesses, perhaps an interrupt lock could be used instead of guarded ports
78
79 9 Fw::ParamValid PrmDbImpl::getPrm_handler(FwIndexType portNum, FwPrmIdType id, Fw::ParamBuffer& val) {
80 // search for entry
81
1/1
✓ Branch 1 taken 9 times.
9 auto success = this->m_activeDb->find(id, val);
82
83
1/3
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
9 switch (success.e) {
84 9 case Fw::Success::FAILURE:
85 // if unable to find parameter, send error message
86
1/1
✓ Branch 1 taken 9 times.
9 this->log_WARNING_LO_PrmIdNotFound(id);
87
1/1
✓ Branch 1 taken 9 times.
9 return Fw::ParamValid::INVALID;
88 case Fw::Success::SUCCESS:
89 return Fw::ParamValid::VALID;
90 default:
91 FW_ASSERT(false, success.e);
92 return Fw::ParamValid::INVALID;
93 }
94 9 }
95
96 4 void PrmDbImpl::setPrm_handler(FwIndexType portNum, FwPrmIdType id, Fw::ParamBuffer& val) {
97 // Reject parameter updates during non-idle file load states
98
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (m_state != PrmDbFileLoadState::IDLE) {
99 this->log_WARNING_LO_PrmDbFileLoadInvalidAction(m_state, PrmDb_PrmLoadAction::SET_PARAMETER);
100 return;
101 }
102
103 // Update the parameter in the active database
104
2/2
✓ Branch 1 taken 4 times.
✓ Branch 4 taken 4 times.
4 PrmUpdateType update_status = updateAddPrmImpl(id, val, PrmDbType::DB_ACTIVE);
105
106 // Issue relevant EVR
107
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (update_status == PARAM_UPDATED) {
108 this->log_ACTIVITY_HI_PrmIdUpdated(id);
109
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 } else if (update_status == NO_SLOTS) {
110 this->log_WARNING_HI_PrmDbFull(id);
111 } else {
112 4 this->log_ACTIVITY_HI_PrmIdAdded(id);
113 }
114 }
115
116 59 void PrmDbImpl::pingIn_handler(FwIndexType portNum, U32 key) {
117 // respond to ping
118 59 this->pingOut_out(0, key);
119 59 }
120
121 void PrmDbImpl::PRM_SAVE_FILE_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
122 // Reject PRM_SAVE_FILE command during non-idle file load states
123 if (m_state != PrmDbFileLoadState::IDLE) {
124 this->log_WARNING_LO_PrmDbFileLoadInvalidAction(m_state, PrmDb_PrmLoadAction::SAVE_FILE_COMMAND);
125 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::BUSY);
126 return;
127 }
128
129 FW_ASSERT(this->m_fileName.length() > 0);
130
131 Os::File paramFile;
132 WorkingBuffer buff;
133
134 Os::File::Status stat = paramFile.open(this->m_fileName.toChar(), Os::File::OPEN_WRITE, Os::File::OVERWRITE);
135 if (stat != Os::File::OP_OK) {
136 this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::OPEN, 0, stat);
137 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
138 return;
139 }
140
141 // write placeholder for the CRC
142 Utils::Hash crc;
143 U32 crcInitial = 0xFFFFFFFF;
144 buff.resetSer();
145 Fw::SerializeStatus serStat = buff.serializeFrom(crcInitial);
146 FW_ASSERT(Fw::FW_SERIALIZE_OK == serStat, static_cast<FwAssertArgType>(serStat));
147 FwSizeType writeSize = static_cast<FwSizeType>(buff.getSize());
148 stat = paramFile.write(buff.getBuffAddr(), writeSize, Os::File::WaitType::WAIT);
149
150 if (stat != Os::File::OP_OK) {
151 this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::CRC_PLACE, 0, stat);
152 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
153 return;
154 }
155
156 this->lock();
157 auto db = getDbPtr(PrmDbType::DB_ACTIVE);
158 FW_ASSERT(db != nullptr);
159
160 // Traverse the parameter list, saving each entry
161
162 U32 numRecords = 0;
163
164 for (const auto& entry : *db) {
165 // write delimiter
166 static const U8 delim = PRMDB_ENTRY_DELIMITER;
167 writeSize = static_cast<FwSizeType>(sizeof(delim));
168 stat = paramFile.write(&delim, writeSize, Os::File::WaitType::WAIT);
169 if (stat != Os::File::OP_OK) {
170 this->unLock();
171 this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::DELIMITER, static_cast<I32>(numRecords), stat);
172 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
173 return;
174 }
175 if (writeSize != sizeof(delim)) {
176 this->unLock();
177 this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::DELIMITER_SIZE, static_cast<I32>(numRecords),
178 static_cast<I32>(writeSize));
179 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
180 return;
181 }
182
183 // add delimiter to CRC
184 crc.update(&delim, sizeof(delim));
185
186 // serialize record size = id field + data
187 U32 recordSize = static_cast<U32>(sizeof(FwPrmIdType) + entry.getValue().getSize());
188
189 // reset buffer
190 buff.resetSer();
191 serStat = buff.serializeFrom(recordSize);
192 // should always work
193 FW_ASSERT(Fw::FW_SERIALIZE_OK == serStat, static_cast<FwAssertArgType>(serStat));
194
195 // write record size
196 writeSize = static_cast<FwSizeType>(buff.getSize());
197 stat = paramFile.write(buff.getBuffAddr(), writeSize, Os::File::WaitType::WAIT);
198 if (stat != Os::File::OP_OK) {
199 this->unLock();
200 this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::RECORD_SIZE, static_cast<I32>(numRecords), stat);
201 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
202 return;
203 }
204 if (writeSize != sizeof(recordSize)) {
205 this->unLock();
206 this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::RECORD_SIZE_SIZE, static_cast<I32>(numRecords),
207 static_cast<I32>(writeSize));
208 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
209 return;
210 }
211
212 // add recordSize to CRC
213 if (buff.getBuffAddr() != nullptr && writeSize != 0) {
214 crc.update(buff.getBuffAddr(), writeSize);
215 }
216
217 // reset buffer
218 buff.resetSer();
219
220 // serialize parameter id
221
222 serStat = buff.serializeFrom(entry.getKey());
223 // should always work
224 FW_ASSERT(Fw::FW_SERIALIZE_OK == serStat, static_cast<FwAssertArgType>(serStat));
225
226 // write parameter ID
227 writeSize = static_cast<FwSizeType>(buff.getSize());
228 stat = paramFile.write(buff.getBuffAddr(), writeSize, Os::File::WaitType::WAIT);
229 if (stat != Os::File::OP_OK) {
230 this->unLock();
231 this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::PARAMETER_ID, static_cast<I32>(numRecords), stat);
232 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
233 return;
234 }
235 if (writeSize != static_cast<FwSizeType>(buff.getSize())) {
236 this->unLock();
237 this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::PARAMETER_ID_SIZE, static_cast<I32>(numRecords),
238 static_cast<I32>(writeSize));
239 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
240 return;
241 }
242
243 // add parameter ID to CRC
244 if (buff.getBuffAddr() != nullptr && writeSize != 0) {
245 crc.update(buff.getBuffAddr(), writeSize);
246 }
247
248 // write serialized parameter value
249
250 writeSize = static_cast<FwSizeType>(entry.getValue().getSize());
251 stat = paramFile.write(entry.getValue().getBuffAddr(), writeSize, Os::File::WaitType::WAIT);
252 if (stat != Os::File::OP_OK) {
253 this->unLock();
254 this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::PARAMETER_VALUE, static_cast<I32>(numRecords), stat);
255 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
256 return;
257 }
258 if (writeSize != static_cast<FwSizeType>(entry.getValue().getSize())) {
259 this->unLock();
260 this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::PARAMETER_VALUE_SIZE, static_cast<I32>(numRecords),
261 static_cast<I32>(writeSize));
262 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
263 return;
264 }
265
266 // add serialized parameter value to crc
267 if (entry.getValue().getBuffAddr() != nullptr && writeSize != 0) {
268 crc.update(entry.getValue().getBuffAddr(), writeSize);
269 }
270
271 numRecords++;
272 }
273
274 this->unLock();
275
276 // save current location of pointer in paramFile
277 FwSizeType currPosInParamFile;
278
279 stat = paramFile.position(currPosInParamFile);
280 if (stat != Os::File::OP_OK) {
281 this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::CURR_POSITION, 0, stat);
282 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
283 return;
284 }
285
286 // seek to beginning and write CRC value
287 stat = paramFile.seek(0, Os::File::SeekType::ABSOLUTE);
288 if (stat != Os::File::OP_OK) {
289 this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::SEEK_ZERO, 0, stat);
290 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
291 return;
292 }
293 buff.resetSer();
294 U32 crcFinal = 0;
295 crc.finalize(crcFinal);
296 crcFinal = ~crcFinal;
297 serStat = buff.serializeFrom(crcFinal);
298
299 FW_ASSERT(Fw::FW_SERIALIZE_OK == serStat, static_cast<FwAssertArgType>(serStat));
300 writeSize = static_cast<FwSizeType>(buff.getSize());
301
302 stat = paramFile.write(buff.getBuffAddr(), writeSize, Os::File::WaitType::WAIT);
303 if (stat != Os::File::OP_OK) {
304 this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::CRC_REAL, 0, stat);
305 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
306 return;
307 }
308
309 // Restore pointer to previously saved location
310 stat = paramFile.seek(static_cast<FwSignedSizeType>(currPosInParamFile), Os::File::SeekType::ABSOLUTE);
311 if (stat != Os::File::OP_OK) {
312 this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::SEEK_POSITION, 0, stat);
313 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
314 return;
315 }
316
317 this->log_ACTIVITY_HI_PrmFileSaveComplete(numRecords);
318 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
319 }
320
321 void PrmDbImpl::PRM_LOAD_FILE_cmdHandler(FwOpcodeType opCode,
322 U32 cmdSeq,
323 const Fw::CmdStringArg& fileName,
324 const PrmDb_Merge& merge) {
325 // Reject PRM_LOAD_FILE command during non-idle file load states
326 if (m_state != PrmDbFileLoadState::IDLE) {
327 this->log_WARNING_LO_PrmDbFileLoadInvalidAction(m_state, PrmDb_PrmLoadAction::LOAD_FILE_COMMAND);
328 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::BUSY);
329 return;
330 }
331
332 // Set state to loading
333 m_state = PrmDbFileLoadState::LOADING_FILE_UPDATES;
334
335 // If reset is true, clear the staging database first
336 if (merge == PrmDb_Merge::MERGE) {
337 // Copy active to staging for merging
338 dbCopy(PrmDbType::DB_STAGING, PrmDbType::DB_ACTIVE);
339 } else {
340 // reset staging db, all file contents will be loaded but no old parameters will be retained
341 this->clearDb(PrmDbType::DB_STAGING);
342 }
343
344 // Load the file into staging database
345 // The readParamFileImpl will emit the relevant EVR if the file load fails
346 // and also if it succeeds will emit EVRs with the number of records
347 PrmDbImpl::PrmLoadStatus success = PrmDbImpl::readParamFileImpl(fileName, PrmDbType::DB_STAGING);
348
349 if (success == PrmLoadStatus::SUCCESS) {
350 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
351 m_state = PrmDbFileLoadState::FILE_UPDATES_STAGED;
352 } else {
353 this->log_WARNING_HI_PrmDbFileLoadFailed();
354 // clear the staging DB and reset to an IDLE state in case of issues
355 this->clearDb(PrmDbType::DB_STAGING);
356 m_state = PrmDbFileLoadState::IDLE;
357 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
358 }
359 }
360
361 void PrmDbImpl::PRM_COMMIT_STAGED_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
362 // Verify we are in the correct state
363 if (m_state != PrmDbFileLoadState::FILE_UPDATES_STAGED) {
364 this->log_WARNING_LO_PrmDbFileLoadInvalidAction(m_state, PrmDb_PrmLoadAction::COMMIT_STAGED_COMMAND);
365 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
366 return;
367 }
368
369 // Swap active and staging databases, safely w.r.t. prmGet
370 this->lock();
371 PrmDbStore* temp = this->m_activeDb;
372 this->m_activeDb = this->m_stagingDb;
373 this->unLock();
374 this->m_stagingDb = temp;
375
376 // Clear the new staging database
377 this->clearDb(PrmDbType::DB_STAGING);
378
379 // Set file load state to idle
380 m_state = PrmDbFileLoadState::IDLE;
381
382 this->log_ACTIVITY_HI_PrmDbCommitComplete();
383 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
384 }
385
386 //! ----------------------------------------------------------------------
387 //! Helpers for construction/destruction, init, & handlers
388 //! ----------------------------------------------------------------------
389
390 PrmDbImpl::PrmLoadStatus PrmDbImpl::readParamFileImpl(const Fw::StringBase& fileName, PrmDbType dbType) {
391 FW_ASSERT(dbType == PrmDbType::DB_ACTIVE or dbType == PrmDbType::DB_STAGING);
392 FW_ASSERT(fileName.length() > 0);
393
394 // Commanded loads (staging) may carry a ground-supplied path; restrict them
395 // to the configured sandbox directory to prevent path traversal
396 if ((dbType == PrmDbType::DB_STAGING) && (this->m_sandboxDir.length() > 0)) {
397 Os::SandboxedFile paramFile;
398 paramFile.configure(this->m_sandboxDir.toChar());
399 return this->readParamFileWork(paramFile, fileName, dbType);
400 }
401 Os::File paramFile;
402 return this->readParamFileWork(paramFile, fileName, dbType);
403 }
404
405 template <typename FileType>
406 PrmDbImpl::PrmLoadStatus PrmDbImpl::readParamFileWork(FileType& paramFile,
407 const Fw::StringBase& fileName,
408 PrmDbType dbType) {
409 Fw::String dbString = getDbString(dbType);
410
411 Os::File::Status stat = paramFile.open(fileName.toChar(), Os::File::OPEN_READ);
412 if (stat != Os::File::OP_OK) {
413 this->log_WARNING_HI_PrmFileReadError(PrmReadError::OPEN, 0, stat);
414 return PrmLoadStatus::ERROR;
415 }
416 //===========================================================================
417 // read CRC from beginning of file
418 WorkingBuffer buff;
419 U32 fileCrc;
420 FwSizeType readSize = static_cast<FwSizeType>(sizeof(fileCrc));
421
422 // Read raw CRC bytes from file
423 stat = paramFile.read(buff.getBuffAddr(), readSize);
424 if (stat != Os::File::OP_OK) {
425 this->log_WARNING_HI_PrmFileReadError(PrmReadError::CRC, static_cast<I32>(0), stat);
426 return PrmLoadStatus::ERROR;
427 }
428
429 if (readSize != sizeof(fileCrc)) {
430 this->log_WARNING_HI_PrmFileReadError(PrmReadError::CRC_SIZE, static_cast<I32>(0), static_cast<I32>(readSize));
431 return PrmLoadStatus::ERROR;
432 }
433
434 // Deserialize the CRC in a portable way
435 Fw::SerializeStatus serStat = buff.setBuffLen(readSize);
436 FW_ASSERT(Fw::FW_SERIALIZE_OK == serStat, static_cast<FwAssertArgType>(serStat));
437 buff.resetDeser();
438 serStat = buff.deserializeTo(fileCrc);
439 FW_ASSERT(Fw::FW_SERIALIZE_OK == serStat, static_cast<FwAssertArgType>(serStat));
440
441 U32 crc = 0xFFFFFFFF;
442 // read into CRC buffer for checking
443
444 Os::File::Status status = paramFile.calculateCrc(crc);
445 if (status != Os::File::OP_OK) {
446 this->log_WARNING_HI_PrmFileReadError(PrmReadError::CRC_BUFFER, static_cast<I32>(0), status);
447 return PrmLoadStatus::ERROR;
448 }
449
450 if (fileCrc != crc) {
451 this->log_WARNING_HI_PrmFileBadCrc(fileCrc, crc);
452 return PrmLoadStatus::ERROR;
453 }
454
455 // seek back to just after CRC
456 stat = paramFile.seek(sizeof(fileCrc), Os::File::SeekType::ABSOLUTE);
457 if (stat != Os::File::OP_OK) {
458 this->log_WARNING_HI_PrmFileReadError(PrmReadError::SEEK_ZERO, 0, stat);
459 return PrmLoadStatus::ERROR;
460 }
461 //===========================================================================
462
463 U32 recordNumTotal = 0;
464 U32 recordNumAdded = 0;
465 U32 recordNumUpdated = 0;
466 U32 recordNumDropped = 0;
467
468 for (FwSizeType entry = 0; entry < PRMDB_NUM_DB_ENTRIES; entry++) {
469 U8 delimiter;
470 readSize = static_cast<FwSizeType>(sizeof(delimiter));
471
472 // read delimiter
473 Os::File::Status fStat = paramFile.read(&delimiter, readSize, Os::File::WaitType::WAIT);
474
475 // check for end of file (successful read of size 0); a failed read can
476 // also yield size 0 and must not be mistaken for a clean EOF
477 if ((Os::File::OP_OK == fStat) && (0 == readSize)) {
478 break;
479 }
480
481 if (fStat != Os::File::OP_OK) {
482 this->log_WARNING_HI_PrmFileReadError(PrmReadError::DELIMITER, static_cast<I32>(recordNumTotal), fStat);
483 return PrmLoadStatus::ERROR;
484 }
485
486 if (sizeof(delimiter) != readSize) {
487 this->log_WARNING_HI_PrmFileReadError(PrmReadError::DELIMITER_SIZE, static_cast<I32>(recordNumTotal),
488 static_cast<I32>(readSize));
489 return PrmLoadStatus::ERROR;
490 }
491
492 if (PRMDB_ENTRY_DELIMITER != delimiter) {
493 this->log_WARNING_HI_PrmFileReadError(PrmReadError::DELIMITER_VALUE, static_cast<I32>(recordNumTotal),
494 delimiter);
495 return PrmLoadStatus::ERROR;
496 }
497
498 U32 recordSize = 0;
499
500 // read record size
501 readSize = sizeof(recordSize);
502
503 fStat = paramFile.read(buff.getBuffAddr(), readSize, Os::File::WaitType::WAIT);
504 if (fStat != Os::File::OP_OK) {
505 this->log_WARNING_HI_PrmFileReadError(PrmReadError::RECORD_SIZE, static_cast<I32>(recordNumTotal), fStat);
506 return PrmLoadStatus::ERROR;
507 }
508 if (sizeof(recordSize) != readSize) {
509 this->log_WARNING_HI_PrmFileReadError(PrmReadError::RECORD_SIZE_SIZE, static_cast<I32>(recordNumTotal),
510 static_cast<I32>(readSize));
511 return PrmLoadStatus::ERROR;
512 }
513 // set serialized size to read size
514 Fw::SerializeStatus desStat = buff.setBuffLen(static_cast<Fw::Serializable::SizeType>(readSize));
515 // should never fail
516 FW_ASSERT(Fw::FW_SERIALIZE_OK == desStat, static_cast<FwAssertArgType>(desStat));
517 // reset deserialization
518 buff.resetDeser();
519 // deserialize, since record size is serialized in file
520 desStat = buff.deserializeTo(recordSize);
521 FW_ASSERT(Fw::FW_SERIALIZE_OK == desStat);
522
523 // sanity check value. It can't be larger than the maximum parameter buffer size + id
524 // or smaller than the record id
525 if ((recordSize > FW_PARAM_BUFFER_MAX_SIZE + sizeof(FwPrmIdType)) or (recordSize < sizeof(FwPrmIdType))) {
526 this->log_WARNING_HI_PrmFileReadError(PrmReadError::RECORD_SIZE_VALUE, static_cast<I32>(recordNumTotal),
527 static_cast<I32>(recordSize));
528 return PrmLoadStatus::ERROR;
529 }
530
531 // read the parameter ID
532 FwPrmIdType parameterId = 0;
533 readSize = static_cast<FwSizeType>(sizeof(FwPrmIdType));
534
535 fStat = paramFile.read(buff.getBuffAddr(), readSize, Os::File::WaitType::WAIT);
536 if (fStat != Os::File::OP_OK) {
537 this->log_WARNING_HI_PrmFileReadError(PrmReadError::PARAMETER_ID, static_cast<I32>(recordNumTotal), fStat);
538 return PrmLoadStatus::ERROR;
539 }
540 if (sizeof(parameterId) != static_cast<FwSizeType>(readSize)) {
541 this->log_WARNING_HI_PrmFileReadError(PrmReadError::PARAMETER_ID_SIZE, static_cast<I32>(recordNumTotal),
542 static_cast<I32>(readSize));
543 return PrmLoadStatus::ERROR;
544 }
545
546 // set serialized size to read parameter ID
547 desStat = buff.setBuffLen(static_cast<Fw::Serializable::SizeType>(readSize));
548 // should never fail
549 FW_ASSERT(Fw::FW_SERIALIZE_OK == desStat, static_cast<FwAssertArgType>(desStat));
550 // reset deserialization
551 buff.resetDeser();
552 // deserialize, since parameter ID is serialized in file
553 desStat = buff.deserializeTo(parameterId);
554 FW_ASSERT(Fw::FW_SERIALIZE_OK == desStat);
555
556 // copy parameter value from file into a temporary buffer
557 Fw::ParamBuffer tmpParamBuffer; // temporary param buffer to read parameter value from file
558 readSize = recordSize - sizeof(parameterId);
559 desStat = tmpParamBuffer.setBuffLen(static_cast<Fw::Serializable::SizeType>(readSize));
560 FW_ASSERT(Fw::FW_SERIALIZE_OK == desStat, static_cast<FwAssertArgType>(desStat)); // should never fail
561 fStat = paramFile.read(tmpParamBuffer.getBuffAddr(), readSize);
562
563 if (fStat != Os::File::OP_OK) {
564 this->log_WARNING_HI_PrmFileReadError(PrmReadError::PARAMETER_VALUE, static_cast<I32>(recordNumTotal),
565 fStat);
566 return PrmLoadStatus::ERROR;
567 }
568 if (static_cast<U32>(readSize) != recordSize - sizeof(parameterId)) {
569 this->log_WARNING_HI_PrmFileReadError(PrmReadError::PARAMETER_VALUE_SIZE, static_cast<I32>(recordNumTotal),
570 static_cast<I32>(readSize));
571 return PrmLoadStatus::ERROR;
572 }
573
574 // Actually update or add parameter
575 PrmUpdateType updateStatus = updateAddPrmImpl(parameterId, tmpParamBuffer, dbType);
576 if (updateStatus == PARAM_ADDED) {
577 recordNumAdded++;
578 } else if (updateStatus == PARAM_UPDATED) {
579 recordNumUpdated++;
580 }
581
582 if (updateStatus == NO_SLOTS) {
583 this->log_WARNING_HI_PrmDbFull(parameterId);
584 recordNumDropped++;
585 }
586 recordNumTotal++;
587 }
588
589 // Dropped records mean the database does not reflect the file: report an error
590 if (recordNumDropped > 0) {
591 return PrmLoadStatus::ERROR;
592 }
593
594 this->log_ACTIVITY_HI_PrmFileLoadComplete(dbString, recordNumTotal, recordNumAdded, recordNumUpdated);
595 return PrmLoadStatus::SUCCESS;
596 }
597
598 4 PrmDbImpl::PrmUpdateType PrmDbImpl::updateAddPrmImpl(FwPrmIdType id, Fw::ParamBuffer& val, PrmDbType prmDbType) {
599
2/2
✓ Branch 1 taken 4 times.
✓ Branch 4 taken 4 times.
4 auto* db = getDbPtr(prmDbType);
600
601 4 PrmUpdateType updateStatus = NO_SLOTS;
602
603 4 this->lock();
604
605 4 auto prevSize = db->getSize();
606
2/4
✓ Branch 1 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
4 switch (db->insert(id, val)) {
607 case Fw::Success::FAILURE:
608 updateStatus = NO_SLOTS;
609 break;
610 4 case Fw::Success::SUCCESS:
611
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 if (prevSize < db->getSize()) {
612 4 updateStatus = PARAM_ADDED;
613 } else {
614 FW_ASSERT(prevSize == db->getSize(), static_cast<FwAssertArgType>(prevSize),
615 static_cast<FwAssertArgType>(db->getSize()));
616 updateStatus = PARAM_UPDATED;
617 }
618 4 break;
619 default:
620 FW_ASSERT(false);
621 }
622
623 4 this->unLock();
624 4 return updateStatus;
625 }
626
627 //! ----------------------------------------------------------------------
628 //! Helpers for database management
629 //! ----------------------------------------------------------------------
630
631 2 void PrmDbImpl::clearDb(PrmDbType prmDbType) {
632
3/3
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
2 getDbPtr(prmDbType)->clear();
633 2 }
634
635 void PrmDbImpl::dbCopy(PrmDbType dest, PrmDbType src) {
636 *getDbPtr(dest) = *getDbPtr(src);
637 this->log_ACTIVITY_HI_PrmDbCopyAllComplete(getDbString(src), getDbString(dest));
638 }
639
640 6 PrmDbImpl::PrmDbStore* PrmDbImpl::getDbPtr(PrmDbType dbType) {
641 6 FW_ASSERT(dbType == PrmDbType::DB_ACTIVE or dbType == PrmDbType::DB_STAGING);
642
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 1 times.
6 if (dbType == PrmDbType::DB_ACTIVE) {
643 5 return m_activeDb;
644 }
645 1 return m_stagingDb;
646 }
647
648 Fw::String PrmDbImpl::getDbString(PrmDbType dbType) {
649 FW_ASSERT(dbType == PrmDbType::DB_ACTIVE or dbType == PrmDbType::DB_STAGING);
650 if (dbType == PrmDbType::DB_ACTIVE) {
651 return Fw::String("ACTIVE");
652 }
653 return Fw::String("STAGING");
654 }
655
656 } // namespace Svc
657