Index: firmware/App/Drivers/NVDriver.c =================================================================== diff -u --- firmware/App/Drivers/NVDriver.c (revision 0) +++ firmware/App/Drivers/NVDriver.c (revision 1e809a81cb8831f5aa5938bd333cc374920d5b87) @@ -0,0 +1,65 @@ +/************************************************************************** +* +* Copyright (c) 2026-2027 Diality Inc. - All Rights Reserved. +* +* THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +* WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +* +* @file NVDriver.c +* +* @author (original) Arpita Srivastava +* @date (original) 31-Mar-2026 +* +***************************************************************************/ +#include "system.h" // For fapi operations +#include "F021.h" // For fapi operations +#include "FapiFunctions.h" +#include "NVDriver.h" + +// The clock frequency comes from HCLK_FREQ and it has to be rounded up to the nearest number +#define ROUNDED_HCLK_FREQ FLOAT_TO_INT_WITH_ROUND(HCLK_FREQ) ///< Rounded HCLK for EERPOM clock. +#define BANK7_SECTOR_0_31_ENABLE_BIT_MASK 0x0000000F ///< Bank7 sector 0 t0 31 enable mask. +#define BANK7_SECTOR_32_63_ENABLE_BIT_MASK 0x00000000 ///< Bank7 sector 32 to 63 enable mask. + +#define BANK7_SECTOR0_START_ADDRESS 0xF0200000 ///< Bank7 sector 0 start address. +#define BANK7_SECTOR0_END_ADDRESS 0xF0203FFF ///< Bank7 sector 0 end address. + +#define BANK7_SECTOR1_START_ADDRESS 0xF0204000 ///< Bank7 sector 1 start address. +#define BANK7_SECTOR1_END_ADDRESS 0xF0207FFF ///< Bank7 sector 1 end address. + +#define BANK7_SECTOR2_START_ADDRESS 0xF0208000 ///< Bank7 sector 2 start address. +#define BANK7_SECTOR2_END_ADDRESS 0xF020BFFF ///< Bank7 sector 2 end address. + +#define BANK7_SECTOR3_START_ADDRESS 0xF020C000 ///< Bank7 sector 3 start address. +#define BANK7_SECTOR3_END_ADDRESS 0xF020FFFF ///< Bank7 sector 3 end address. + +/// EEPROM functions use the buffer length as the size of U32. So before send the length to any of FAPI functions, it should be divided by 4. +#define EEPROM_OPS_SIZE_OF_CONVERTER 4 + +void initNVDriver( void ) +{ + // Initialize and activate flash bank 7 + Fapi_initializeFlashBanks( ROUNDED_HCLK_FREQ ); + Fapi_setActiveFlashBank( Fapi_FlashBank7 ); + Fapi_enableEepromBankSectors( BANK7_SECTOR_0_31_ENABLE_BIT_MASK, BANK7_SECTOR_32_63_ENABLE_BIT_MASK ); +} + +void eraseSector( U32* startAddress ) +{ + Fapi_issueAsyncCommandWithAddress( Fapi_EraseSector, startAddress ); +} + +void writeSector( U32* startAddress, U08* bufferAddress, U32 bufferSize ) +{ + Fapi_issueProgrammingCommand( startAddress, bufferAddress, bufferSize, 0x00, 0, Fapi_DataOnly ); +} + +void readSector( U32* startAddress, U32* bufferAddress, U32 bufferSize ) +{ + Fapi_doMarginRead( startAddress, bufferAddress, bufferSize, Fapi_NormalRead ); +} + +BOOL isFlashReady( void ) +{ + return (Fapi_Status_FsmReady == FAPI_CHECK_FSM_READY_BUSY); +} Index: firmware/App/Drivers/NVDriver.h =================================================================== diff -u --- firmware/App/Drivers/NVDriver.h (revision 0) +++ firmware/App/Drivers/NVDriver.h (revision 1e809a81cb8831f5aa5938bd333cc374920d5b87) @@ -0,0 +1,26 @@ +/************************************************************************** +* +* Copyright (c) 2026-2027 Diality Inc. - All Rights Reserved. +* +* THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +* WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +* +* @file NVDriver.h +* +* @author (original) Arpita Srivastava +* @date (original) 31-Mar-2026 +* +***************************************************************************/ + +#ifndef _NV_DRIVER_H +#define _NV_DRIVER_H + +#include "Common.h" + +void initNVDriver( void ); +void eraseSector( U32* startAddress ); +void writeSector( U32* startAddress, U08* bufferAddress, U32 bufferSize ); +void readSector( U32* startAddress, U32* bufferAddress, U32 bufferSize ); +BOOL isFlashReady( void ); + +#endif /* _NV_DRIVER_H */ Index: firmware/App/Services/NVMgmtDD.c =================================================================== diff -u --- firmware/App/Services/NVMgmtDD.c (revision 0) +++ firmware/App/Services/NVMgmtDD.c (revision 1e809a81cb8831f5aa5938bd333cc374920d5b87) @@ -0,0 +1,677 @@ +/************************************************************************** +* +* Copyright (c) 2026-2027 Diality Inc. - All Rights Reserved. +* +* THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +* WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +* +* @file NVMgmtDD.c +* +* @author (original) Arpita Srivastava +* @date (original) 31-Mar-2026 +* +***************************************************************************/ +#include // For memcpy +#include "NVDriver.h" +#include "NVMgmtDD.h" +#include "NVMsgQ.h" +#include "NVRecordsDD.h" + +/// NVDataMgmt Exec states enumeration. +typedef enum NVDataMgmt_Exec_State +{ + NVDATAMGMT_EXEC_STATE_IDLE = 0, ///< Exec state Idle. + NVDATAMGMT_EXEC_STATE_WRITE_TO_EEPROM, ///< Exec state write to EEPROM. + NVDATAMGMT_EXEC_STATE_VERIFY_EEPROM_WRITE, ///< Exec state verify EEPROM write. + NVDATAMGMT_EXEC_STATE_READ_FROM_EEPROM, ///< Exec state read from EEPROM. + NVDATAMGMT_EXEC_STATE_ERASE_EEPROM, ///< Exec state erase EEPROM. + NUM_OF_NVDATAMGMT_EXEC_STATES ///< Total number of exec states. +} NVDATAMGMT_EXEC_STATE_T; + +/// NVDataMgmt write record validity check states +typedef enum NVDataMgmt_Write_Record_Validity_Check +{ + NVDATAMGMT_RECORD_NOT_CHECKED = 0, ///< NVDataMgmt (written) record not checked. + NVDATAMGMT_RECORD_VALID, ///< NVDataMgmt record is valid. + NVDATAMGMT_RECORD_NOT_VALID, ///< NVDataMgmt record is not valid. + NUM_OF_NVDATAMGMT_RECORD_VALIDITY_CHECK ///< Number of NVDataMgmt validity check states. +} RECORD_VALIDITY_CHECK_T; + +#define COMMAND_TIME_OUT (1 * MS_PER_SECOND) ///< Timeout for an EEPROM or RTC command in ms. + + +static NVDATAMGMT_EXEC_STATE_T nvDataMgmtExecState; ///< NVDataMgmt exec state variable. +static volatile BOOL powerOffIsImminent; ///< Power off warning has been signaled. Non-volatile memory operations should be completed ASAP and then ceased. +static RECORD_VALIDITY_CHECK_T writtenRecordStatus; ///< Record data write validity check. +static U08 writtenRecordCheckBuffer[ MAX_EEPROM_WRITE_BUFFER_BYTES ]; ///< Written record validity check buffer. +static U32 currentTime; ///< Current time. + +// Exec functions +static NVDATAMGMT_EXEC_STATE_T handleExecIdleState( void ); +static NVDATAMGMT_EXEC_STATE_T handleExecEraseState( void ); +static NVDATAMGMT_EXEC_STATE_T handleExecWriteToEEPROMState( void ); +static NVDATAMGMT_EXEC_STATE_T handleExecReadFromEEPROMState( void ); +static NVDATAMGMT_EXEC_STATE_T handleExecVerifyEEPROMWriteState( void ); + +// Helper functions +static BOOL didCommandTimeout( NVDATAMGMT_EXEC_STATE_T state ); + +void initNVMgmtDD( void ) +{ + nvDataMgmtExecState = NVDATAMGMT_EXEC_STATE_IDLE; + powerOffIsImminent = FALSE; + + currentTime = 0; + writtenRecordStatus = NVDATAMGMT_RECORD_NOT_CHECKED; + + initNVDriver(); + initNVMsgQ(); + enqueuePOSTReadRecords(); +} + +/*********************************************************************//** + * @brief + * The signalPowerOffWarning signals this module that system power off is + * imminent and any non-volatile data writes in progress should be wrapped + * up quickly and any pending non-volatile data writes should not be started. + * @details Inputs: powerOffIsImminent + * @details Outputs: powerOffIsImminent + * @return none + *************************************************************************/ +void signalPowerOffWarning( void ) +{ + powerOffIsImminent = TRUE; +} + +/*********************************************************************//** + * @brief + * The execNVDataMgmt runs the NVDataMgmt main tasks. + * @details Inputs: nvDataMgmtExecState + * @details Outputs: nvDataMgmtExecState + * @return none + *************************************************************************/ +void execNVDataMgmt( void ) +{ + switch ( nvDataMgmtExecState ) + { + case NVDATAMGMT_EXEC_STATE_IDLE: + nvDataMgmtExecState = handleExecIdleState(); + break; + + case NVDATAMGMT_EXEC_STATE_ERASE_EEPROM: + nvDataMgmtExecState = handleExecEraseState(); + break; + + case NVDATAMGMT_EXEC_STATE_WRITE_TO_EEPROM: + nvDataMgmtExecState = handleExecWriteToEEPROMState(); + break; + + case NVDATAMGMT_EXEC_STATE_READ_FROM_EEPROM: + nvDataMgmtExecState = handleExecReadFromEEPROMState(); + break; + + case NVDATAMGMT_EXEC_STATE_VERIFY_EEPROM_WRITE: + nvDataMgmtExecState = handleExecVerifyEEPROMWriteState(); + break; + + default: +// SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_NVDATAMGMT_EXEC_INVALID_STATE, nvDataMgmtExecState ); + nvDataMgmtExecState = NVDATAMGMT_EXEC_STATE_IDLE; + break; + } +} + +/*********************************************************************//** + * @brief + * The handleExecIdleState checks if the queue is empty and if it is not + * empty, it sets the state of the job. + * @details Inputs: powerOffIsImminent + * @details Outputs: none + * @return next state + *************************************************************************/ +static NVDATAMGMT_EXEC_STATE_T handleExecIdleState ( void ) +{ + NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_IDLE; + BOOL areQueuesNotEmpty = FALSE; + NVDATAMGMT_OPERATION_STATE_T ops; + U32 startAddress; + U08* bufferAddress; + + // If power off command has been issued, do not process any new jobs + if ( powerOffIsImminent != TRUE ) + { + // If the record processing queue is not empty, process the queues + if ( ( FALSE == isRecordQueueEmpty() ) && ( TRUE == areResourcesAvailableForNextJob() ) ) + { + dequeueRecordJob(); + + RECORD_JOBS_STATE_T job = recordCurrentJob.recordJob; + PROCESS_RECORD_SPECS_T jobSpecs = RECORDS_SPECS[ job ]; + // Set the record address offset to 0 since a job will just be started + recordAddressOffset = 0; + areQueuesNotEmpty = TRUE; + ops = recordCurrentJob.memoryOperation; + currentTime = getMSTimerCount(); + startAddress = jobSpecs.startAddress + recordAddressOffset; + bufferAddress = jobSpecs.structAddressPtr + recordAddressOffset; + } + } + + // Check if a queue job is available + if ( TRUE == areQueuesNotEmpty ) + { + switch ( ops ) + { + case NVDATAMGMT_ERASE_SECTOR: + eraseSector( (U32*)startAddress ); + state = NVDATAMGMT_EXEC_STATE_ERASE_EEPROM; + break; + + case NVDATAMGMT_WRITE: + writeSector( (U32*)startAddress, (U08*)bufferAddress, jobSpecs.maxWriteBufferSize ); + currentTime = getMSTimerCount(); + state = NVDATAMGMT_EXEC_STATE_WRITE_TO_EEPROM; + break; + + case NVDATAMGMT_READ: + readSector( (U32*)startAddress, (U32*)bufferAddress, ( jobSpecs.maxReadBufferSize / EEPROM_OPS_SIZE_OF_CONVERTER ) ); + currentTime = getMSTimerCount(); + state = NVDATAMGMT_EXEC_STATE_READ_FROM_EEPROM; + break; + + default: + break; + } + + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleExecEraseState issues an erase command to EEPROM on entry + * and if the erase was successful, it sets the state to Idle. + * @details Inputs: none + * @details Outputs: none + * @return next state + *************************************************************************/ +static NVDATAMGMT_EXEC_STATE_T handleExecEraseState ( void ) +{ + NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_ERASE_EEPROM; + BOOL timeoutStatus = didCommandTimeout( state ); + + if ( TRUE == isFlashReady() || timeoutStatus == TRUE ) + { + state = NVDATAMGMT_EXEC_STATE_IDLE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleExecWriteToEEPROMState issues a write command to EEPROM and + * if the write was successful, it sets the state to Idle. + * @details Inputs: recordAddressOffset, writtenRecordCheckBuffer, + * writtenRecordStatus + * @details Outputs: recordAddressOffset, writtenRecordCheckBuffer, + * writtenRecordStatus + * @return next state of the state machine + *************************************************************************/ +static NVDATAMGMT_EXEC_STATE_T handleExecWriteToEEPROMState ( void ) +{ + NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_WRITE_TO_EEPROM; + BOOL timeoutStatus = didCommandTimeout( state ); + RECORD_JOBS_STATE_T job = recordCurrentJob.recordJob; + PROCESS_RECORD_SPECS_T jobSpecs = RECORDS_SPECS [ job ]; + + // Check if the fapi has finished + if ( TRUE == isFlashReady() ) + { + // Check the integrity of data (the 16 bytes that were written to EEPROM should be read and be checked for each byte) + if ( writtenRecordStatus == NVDATAMGMT_RECORD_NOT_CHECKED ) + { + currentTime = getMSTimerCount(); + U32 startAddress = jobSpecs.startAddress + recordAddressOffset; + U32 maxBufferLength = jobSpecs.maxWriteBufferSize / EEPROM_OPS_SIZE_OF_CONVERTER; + + // Clear the buffer from the previous content + memset( writtenRecordCheckBuffer, 0, sizeof( writtenRecordCheckBuffer ) ); + + // Issue a FAPI read command but only the bytes that were written, so use maxWriteBufferSize + readSector( (U32*)startAddress, (U32*)writtenRecordCheckBuffer, maxBufferLength ); + + state = NVDATAMGMT_EXEC_STATE_VERIFY_EEPROM_WRITE; + } + else if ( writtenRecordStatus == NVDATAMGMT_RECORD_VALID ) + { + // If the data is valid, and if it is at the end of the write, change to idle + if ( jobSpecs.sizeofJob == recordAddressOffset ) + { + state = NVDATAMGMT_EXEC_STATE_IDLE; + } + else + { + // Update the variables and issue the next write command + currentTime = getMSTimerCount(); + recordAddressOffset += jobSpecs.maxWriteBufferSize; + U32 memoryPtr = jobSpecs.startAddress + recordAddressOffset; + U08* structPtr = jobSpecs.structAddressPtr + recordAddressOffset; + + writtenRecordStatus = NVDATAMGMT_RECORD_NOT_CHECKED; + + // Issue the write command + writeSector( (U32*)memoryPtr, structPtr, jobSpecs.maxWriteBufferSize ); + } + } + } + // If timed out, get back to Idle + else if ( timeoutStatus == TRUE ) + { + recordAddressOffset = 0; + state = NVDATAMGMT_EXEC_STATE_IDLE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleExecReadFromEEPROMState issues a read command to EEPROM on entry + * and if the read was successful, it sets the state to Idle. + * @details Inputs: None + * @details Outputs: None + * @return next state + *************************************************************************/ +static NVDATAMGMT_EXEC_STATE_T handleExecReadFromEEPROMState ( void ) +{ + NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_READ_FROM_EEPROM; + BOOL timeoutStatus = didCommandTimeout( state ); + + if ( ( TRUE == isFlashReady() ) || ( TRUE == timeoutStatus ) ) + { + state = NVDATAMGMT_EXEC_STATE_IDLE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleExecVerifyEEPROMWriteState checks all the bytes that were + * written to EEPROM to ensure they match the data in the NV memory. + * @details Inputs: recordAddressOffset, writtenRecordCheckBuffer, + * writtenRecordStatus + * @details Outputs: recordAddressOffset, writtenRecordCheckBuffer, + * writtenRecordStatus + * @return next state of the state machine + *************************************************************************/ +static NVDATAMGMT_EXEC_STATE_T handleExecVerifyEEPROMWriteState ( void ) +{ + NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_VERIFY_EEPROM_WRITE; + BOOL timeoutStatus = didCommandTimeout( state ); + RECORD_JOBS_STATE_T job = recordCurrentJob.recordJob; + PROCESS_RECORD_SPECS_T jobSpecs = RECORDS_SPECS [ job ]; + + // Check if the write job is EEPROM or RTC RAM + if ( TRUE == isFlashReady() ) + { + U32 i; + + U08* bufferPtr = jobSpecs.structAddressPtr + recordAddressOffset; + + // Loop through the bytes in the buffer + for ( i = 0; i < jobSpecs.maxWriteBufferSize; i++ ) + { + // Check if data in the buffer is not the same as the data in the structure + if ( writtenRecordCheckBuffer[ i ] != *bufferPtr ) + { + // Data is not valid. Schedule an erase and write of the calibration record since only a part of + // data cannot be written to EEPROM. + writtenRecordStatus = NVDATAMGMT_RECORD_NOT_VALID; + enqueueRecordJob( NVDATAMGMT_ERASE_SECTOR, NVDATAMGMT_CALIBRATION_RECORD ); + enqueueRecordJob( NVDATAMGMT_WRITE, NVDATAMGMT_CALIBRATION_RECORD ); + state = NVDATAMGMT_EXEC_STATE_IDLE; + // Exit the loop since there is no point to check the rest of data + break; + } + else + { + // Everything is good increment the pointer to check the next byte. + bufferPtr++; + // Record data is valid so far + writtenRecordStatus = NVDATAMGMT_RECORD_VALID; + // Go back write to EEPROM state to continue writing the record data + state = NVDATAMGMT_EXEC_STATE_WRITE_TO_EEPROM; + } + } + } + + return state; +} + +/*********************************************************************//** + * @brief + * The resetNVDataMgmtPOSTState function resets the NV data management POST + * state and enqueues all the NV records to be read again from the NV memory. + * @details Inputs: none + * @details Outputs: nvDataMgmtSelfTestState, nvDataMgmtSelfTestResult + * @return none + *************************************************************************/ +void resetNVDataMgmtPOSTState( void ) +{ + nvDataMgmtSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; + nvDataMgmtSelfTestState = NVDATAMGMT_SELF_TEST_STATE_READ_RECORDS; +} + +/*********************************************************************//** + * @brief + * The getNVRecord2Driver function copies the requested non-volatile + * data into the provided buffer by the caller. The function then checks if + * the non-volatile data is valid. If the data is not valid, it raises the + * provided alarm by the caller. + * @details Inputs: ddCalibrationRecord + * @details Outputs: ddCalibrationRecord + * @param nvData the non-volatile data to be copied + * @param bufferAddress the address of the provided buffer by the caller + * @param bufferLength the length of the provided buffer by the caller + * @param numOfSnsrs2Check the number of sensors to check in a array of sensors called + * @param nvAlarm the corresponding alarm of the non-volatile data to be raised + * if the data is not valid + * @return TRUE if the non-volatile data is valid otherwise, FALSE + *************************************************************************/ +BOOL getNVRecord2Driver( NV_DATA_T nvData, U08* bufferAddress, U32 bufferLength, U08 numOfSnsrs2Check, ALARM_ID_T nvAlarm ) +{ + U08 i; + U08* nvDataStartPtr = 0; + BOOL isNVDataInvalid = FALSE; + U32 nvDataLength = 0; + + switch ( nvData ) + { + + case GET_CAL_PRESSURE_SENOSRS: + nvDataStartPtr = (U08*)&ddCalibrationRecord.ddCalibrationGroups.presSensorsCalRecord; + nvDataLength = sizeof( ddCalibrationRecord.ddCalibrationGroups.presSensorsCalRecord ); + for ( i = 0; i < numOfSnsrs2Check; i++ ) + isNVDataInvalid |= ( 0 == ddCalibrationRecord.ddCalibrationGroups.presSensorsCalRecord.pressureSensors[ i ].calibrationTime ? TRUE : FALSE ); + break; + + case GET_CAL_LOAD_CELL_SENSORS: + nvDataStartPtr = (U08*)&ddCalibrationRecord.ddCalibrationGroups.loadCellsCalRecord; + nvDataLength = sizeof( ddCalibrationRecord.ddCalibrationGroups.loadCellsCalRecord ); + for ( i = 0; i < numOfSnsrs2Check; i++ ) + isNVDataInvalid |= ( 0 == ddCalibrationRecord.ddCalibrationGroups.loadCellsCalRecord.loadCells[ i ].calibrationTime ? TRUE : FALSE ); + break; + + case GET_CAL_FLOW_SENSORS: + nvDataStartPtr = (U08*)&ddCalibrationRecord.ddCalibrationGroups.flowSensorsCalRecord; + nvDataLength = sizeof( ddCalibrationRecord.ddCalibrationGroups.flowSensorsCalRecord ); + for ( i = 0; i < numOfSnsrs2Check; i++ ) + isNVDataInvalid |= ( 0 == ddCalibrationRecord.ddCalibrationGroups.flowSensorsCalRecord.flowSensors[ i ].calibrationTime ? TRUE : FALSE ); + break; + + case GET_CAL_ACID_CONCENTREATES: + nvDataStartPtr = (U08*)&ddCalibrationRecord.ddCalibrationGroups.acidConcentratesRecord; + nvDataLength = sizeof( ddCalibrationRecord.ddCalibrationGroups.acidConcentratesRecord ); + for ( i = 0; i < numOfSnsrs2Check; i++ ) + isNVDataInvalid |= ( 0 == ddCalibrationRecord.ddCalibrationGroups.acidConcentratesRecord.acidConcentrate[ i ].calibrationTime ? TRUE : FALSE ); + break; + + case GET_CAL_BICARB_CONCENTRATES: + nvDataStartPtr = (U08*)&ddCalibrationRecord.ddCalibrationGroups.bicarbConcentratesRecord; + nvDataLength = sizeof( ddCalibrationRecord.ddCalibrationGroups.bicarbConcentratesRecord ); + for ( i = 0; i < numOfSnsrs2Check; i++ ) + isNVDataInvalid |= ( 0 == ddCalibrationRecord.ddCalibrationGroups.bicarbConcentratesRecord.bicarbConcentrate[ i ].calibrationTime ? TRUE : FALSE ); + break; + + case GET_CAL_ACCEL_SENSORS: + nvDataStartPtr = (U08*)&ddCalibrationRecord.ddCalibrationGroups.accelerometerSensorCalRecord; + isNVDataInvalid = ( 0 == ddCalibrationRecord.ddCalibrationGroups.accelerometerSensorCalRecord.calibrationTime ? TRUE : FALSE ); + break; + + case GET_CAL_TEMP_SENSORS: + nvDataStartPtr = (U08*)&ddCalibrationRecord.ddCalibrationGroups.tempSensorsCalRecord; + nvDataLength = sizeof( ddCalibrationRecord.ddCalibrationGroups.tempSensorsCalRecord ); + for ( i = 0; i < numOfSnsrs2Check; i++ ) + isNVDataInvalid |= ( 0 == ddCalibrationRecord.ddCalibrationGroups.tempSensorsCalRecord.tempSensors[ i ].calibrationTime ? TRUE : FALSE ); + break; + + case GET_CAL_RSRVRS_VOL_RECORD: + nvDataStartPtr = (U08*)&ddCalibrationRecord.ddCalibrationGroups.reservoirVolumesRecord; + nvDataLength = sizeof( ddCalibrationRecord.ddCalibrationGroups.reservoirVolumesRecord ); + for ( i = 0; i < numOfSnsrs2Check; i++ ) + isNVDataInvalid |= ( 0 == ddCalibrationRecord.ddCalibrationGroups.reservoirVolumesRecord.reservoir[ i ].calibrationTime ? TRUE : FALSE ); + break; + + case GET_CAL_HEATING_RECORD: + nvDataStartPtr = (U08*)&ddCalibrationRecord.ddCalibrationGroups.heatingCalRecord; + nvDataLength = sizeof( ddCalibrationRecord.ddCalibrationGroups.heatingCalRecord ); + isNVDataInvalid = ( 0 == ddCalibrationRecord.ddCalibrationGroups.heatingCalRecord.calibrationTime ? TRUE : FALSE ); + break; + + case GET_CAL_DRAIN_LINE_VOLUME_RECORD: + nvDataStartPtr = (U08*)&ddCalibrationRecord.ddCalibrationGroups.drainLineVolumeRecord; + nvDataLength = sizeof( ddCalibrationRecord.ddCalibrationGroups.drainLineVolumeRecord ); + isNVDataInvalid = ( 0 == ddCalibrationRecord.ddCalibrationGroups.drainLineVolumeRecord.calibrationTime ? TRUE : FALSE ); + break; + + case GET_CAL_RO_PUMP_RECORD: + nvDataStartPtr = (U08*)&ddCalibrationRecord.ddCalibrationGroups.roPumpRecord; + nvDataLength = sizeof( ddCalibrationRecord.ddCalibrationGroups.roPumpRecord ); + isNVDataInvalid = ( 0 == ddCalibrationRecord.ddCalibrationGroups.roPumpRecord.calibrationTime ? TRUE : FALSE ); + break; + + case GET_CAL_CONCENTRATE_PUMPS_RECORD: + nvDataStartPtr = (U08*)&ddCalibrationRecord.ddCalibrationGroups.concentratePumpsRecord; + nvDataLength = sizeof( ddCalibrationRecord.ddCalibrationGroups.concentratePumpsRecord ); + for ( i = 0; i < numOfSnsrs2Check; i++ ) + isNVDataInvalid |= ( 0 == ddCalibrationRecord.ddCalibrationGroups.concentratePumpsRecord.concentratePumps[ i ].calibrationTime ? TRUE : FALSE ); + break; + + case GET_CAL_DRAIN_PUMP_RECORD: + nvDataStartPtr = (U08*)&ddCalibrationRecord.ddCalibrationGroups.drainPumpRecord; + nvDataLength = sizeof( ddCalibrationRecord.ddCalibrationGroups.drainPumpRecord ); + isNVDataInvalid = ( 0 == ddCalibrationRecord.ddCalibrationGroups.drainPumpRecord.calibrationTime ? TRUE : FALSE ); + break; + + case GET_CAL_FANS_RECORD: + nvDataStartPtr = (U08*)&ddCalibrationRecord.ddCalibrationGroups.fansRecord; + nvDataLength = sizeof( ddCalibrationRecord.ddCalibrationGroups.fansRecord ); + for ( i = 0; i < numOfSnsrs2Check; i++ ) + isNVDataInvalid |= ( 0 == ddCalibrationRecord.ddCalibrationGroups.fansRecord.fans[ i ].calibrationTime ? TRUE : FALSE ); + break; + + case GET_CAL_PRE_RO_PURGE_VOLUME_RECORD: + nvDataStartPtr = (U08*)&ddCalibrationRecord.ddCalibrationGroups.preROPurgeVolumeRecord; + nvDataLength = sizeof( ddCalibrationRecord.ddCalibrationGroups.preROPurgeVolumeRecord ); + isNVDataInvalid = ( 0 == ddCalibrationRecord.ddCalibrationGroups.preROPurgeVolumeRecord.calibrationTime ? TRUE : FALSE ); + break; + + case GET_CAL_FILTERS_RECORD: + nvDataStartPtr = (U08*)&ddCalibrationRecord.ddCalibrationGroups.filtersRecord; + nvDataLength = sizeof( ddCalibrationRecord.ddCalibrationGroups.filtersRecord ); + isNVDataInvalid |= ( 0 == ddCalibrationRecord.ddCalibrationGroups.filtersRecord.carbonFilter.calibrationTime ? TRUE : FALSE ); + isNVDataInvalid |= ( 0 == ddCalibrationRecord.ddCalibrationGroups.filtersRecord.carbonPolishFilter.calibrationTime ? TRUE : FALSE ); + isNVDataInvalid |= ( 0 == ddCalibrationRecord.ddCalibrationGroups.filtersRecord.roFilter.calibrationTime ? TRUE : FALSE ); + isNVDataInvalid |= ( 0 == ddCalibrationRecord.ddCalibrationGroups.filtersRecord.sedimentFilter.calibrationTime ? TRUE : FALSE ); + isNVDataInvalid |= ( 0 == ddCalibrationRecord.ddCalibrationGroups.filtersRecord.ultraFilter.calibrationTime ? TRUE : FALSE ); + break; + + case GET_CAL_FILL_CONDUCTIVITIES_RECORD: + { + DG_FILL_COND_OPS_T fillOps; + + nvDataStartPtr = (U08*)&ddCalibrationRecord.ddCalibrationGroups.fillCondCalRecord.fillCondValues; + nvDataLength = sizeof( ddCalibrationRecord.ddCalibrationGroups.fillCondCalRecord.fillCondValues ); + + for ( i = 0; i < numOfSnsrs2Check; i++ ) + { + for ( fillOps = FILL_COND_NORMAL_OP; fillOps < NUM_OF_FILL_COND_TEST; fillOps++ ) + { + isNVDataInvalid |= ( 0 == ddCalibrationRecord.ddCalibrationGroups.fillCondCalRecord.fillCondValues[ i ][ fillOps ].calibrationTime ? TRUE : FALSE ); + } + } + } + break; + + case GET_INF_HEATERS_RECORD: + nvDataStartPtr = (U08*)&ddHeatersInfoGroup.ddHeatersInfo; + nvDataLength = sizeof( ddHeatersInfoGroup.ddHeatersInfo ); + break; + + case GET_SYS_RECORD: + nvDataStartPtr = (U08*)&ddSystemGroup.ddSystemRecord; + nvDataLength = sizeof( ddSystemGroup.ddSystemRecord ); + break; + + case GET_SRV_RECORD: + nvDataStartPtr = (U08*)&ddServiceGroup.ddServiceRecord; + nvDataLength = sizeof( ddServiceGroup.ddServiceRecord ); + break; + + case GET_SRR_RECORD: + nvDataStartPtr = (U08*)&ddScheduledRunsGroup.ddScheduledRun; + nvDataLength = sizeof( ddScheduledRunsGroup.ddScheduledRun ); + break; + + case GET_USAGE_RECORD: + nvDataStartPtr = (U08*)&ddUsageInfoGroup.ddUsageInfo; + nvDataLength = sizeof( ddUsageInfoGroup.ddUsageInfo ); + break; + + default: +// SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_NV_RECORD_SELECTED, nvData ); + break; + } + + // Make sure the provided buffer length is >= NV Data Length in the NV data management so the memory of the other variables is not + // overridden. + if ( ( bufferLength >= nvDataLength ) && ( nvDataStartPtr != 0 ) ) + { + // Copy the data into the provided buffer + memcpy( bufferAddress, nvDataStartPtr, bufferLength ); + } + + // Check if the non-volatile data is valid and if not raise the alarm + if ( TRUE == isNVDataInvalid ) + { + // If no alarm has been provided to raise, just set the variable as TRUE + if ( ALARM_ID_NO_ALARM == nvAlarm ) + { + isNVDataInvalid = FALSE; + } + else + { +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_CAL_CHECK ) != SW_CONFIG_ENABLE_VALUE ) +#endif + { + activateAlarmNoData( nvAlarm ); + } +#ifndef _RELEASE_ + else + { + isNVDataInvalid = FALSE; + } +#endif + } + } + + // Reverse the polarity to signal the outside users that the calibration has passed. + return ( FALSE == isNVDataInvalid ? TRUE : FALSE ); +} + +#ifndef _RELEASE_ +/*********************************************************************//** + * @brief + * The getSoftwareConfigStatus function returns the status of a software + * configuration. + * @details Inputs: swConfigsList + * @details Outputs: none + * @param config which is the configuration of the that its status is requested + * @return status of the software configuration (0 or 1) + *************************************************************************/ +U08 getSoftwareConfigStatus( SOFTWARE_CONFIG_T config ) +{ + U08 value = 0; + + // If the build is not a release, get the value from the software configurations list + // If the build is a release, the configuration not matter what its value is kept in + // the NV RAM, it returns a 0 which is the configuration is disabled + value = ddSWConfigGroup.ddSWConfigsRecord.swConfigs[ config ]; + + return value; +} +#endif + +/*********************************************************************//** + * @brief + * The didCommandTimedout checks whether the a command whether RTC RAM or + * EEPROM has timedout. If it has timedout, it sets the alarm and turns + * flag to TRUE. + * @details Inputs: none + * @details Outputs: alarm if command timed out + * @param state the state that the command timed out + * @return TRUE if a command timed out + *************************************************************************/ +static BOOL didCommandTimeout ( NVDATAMGMT_EXEC_STATE_T state ) +{ + BOOL status = FALSE; + + if ( TRUE == didTimeout( currentTime, COMMAND_TIME_OUT ) ) + { +// SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_NVDATA_MANAGEMENT_OPS_TIMEOUT, state ) + status = TRUE; + } + + return status; +} + + +/*********************************************************************//** + * @brief + * The testSetNVRecordCRCOverride overrides the non-volatile record CRC override. + * @details Inputs: none + * @details Outputs: none + * @param job the job to override its CRC (i.e. calibration record) + * @param crc the value its CRC to be overridden + * @return TRUE if the job was scheduled successfully otherwise, FALSE + *************************************************************************/ +BOOL testSetNVRecordCRCOverride( U32 job, U16 crc ) +{ + BOOL status = FALSE; + RECORD_JOBS_STATE_T nvJob = (RECORD_JOBS_STATE_T)job; + + switch( nvJob ) + { + case NVDATAMGMT_CALIBRATION_RECORD: + ddCalibrationRecord.crc = crc; + status = enqueueSector0Records(); + break; + + case NVDATAMGMT_SYSTEM_RECORD: + ddSystemGroup.ddSystemRecord.crc = crc; + status = enqueueSector0Records(); + break; + + case NVDATAMGMT_SERVICE_RECORD: + ddServiceGroup.ddServiceRecord.crc = crc; + status = enqueueSector0Records(); + break; + + case NVDATAMGMT_USAGE_INFO_RECORD: + ddUsageInfoGroup.ddUsageInfo.crc = crc; + if ( getAvailableRecordQueueCount() > 0 ) + { + enqueueRecordJob( NVDATAMGMT_WRITE, NVDATAMGMT_USAGE_INFO_RECORD ); + status = TRUE; + } + break; + + } + + return status; +} + Index: firmware/App/Services/NVMgmtDD.h =================================================================== diff -u --- firmware/App/Services/NVMgmtDD.h (revision 0) +++ firmware/App/Services/NVMgmtDD.h (revision 1e809a81cb8831f5aa5938bd333cc374920d5b87) @@ -0,0 +1,39 @@ +/************************************************************************** +* +* Copyright (c) 2026-2027 Diality Inc. - All Rights Reserved. +* +* THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +* WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +* +* @file NVMgmtDD.h +* +* @author (original) Arpita Srivastava +* @date (original) 31-Mar-2026 +* +***************************************************************************/ + +#ifndef _NV_MGMT_DD_H_ +#define _NV_MGMT_DD_H_ + +#include "AlarmDefs.h" +#include "NVRecordsDD.h" + +#ifndef _RELEASE_ +#define SW_CONFIG_ENABLE_VALUE 1 ///< Software configuration enable. +#define SW_CONFIG_DISABLE_VALUE 0 ///< Software configuration disable. +#endif + +void initNVMgmtDD( void ); +void signalPowerOffWarning( void ); +void execNVDataMgmt( void ); +void resetNVDataMgmtPOSTState( void ); +BOOL getNVRecord2Driver( NV_DATA_T nvData, U08* bufferAddress, U32 bufferLength, U08 numOfSnsrs2Check, ALARM_ID_T nvAlarm ); + + +#ifndef _RELEASE_ +U08 getSoftwareConfigStatus( SOFTWARE_CONFIG_T config ); +#endif + +BOOL testSetNVRecordCRCOverride( U32 job, U16 crc ); + +#endif /* _NV_MGMT_DD_H_ */ Index: firmware/App/Services/NVMsgQ.c =================================================================== diff -u --- firmware/App/Services/NVMsgQ.c (revision 0) +++ firmware/App/Services/NVMsgQ.c (revision 1e809a81cb8831f5aa5938bd333cc374920d5b87) @@ -0,0 +1,605 @@ +/************************************************************************** +* +* Copyright (c) 2026-2027 Diality Inc. - All Rights Reserved. +* +* THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +* WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +* +* @file NVMsgQ.c +* +* @author (original) Arpita Srivastava +* @date (original) 31-Mar-2026 +* +***************************************************************************/ + +#include // For memcpy +#include // For ceilf function + +#include "NVMsgQ.h" + +#define QUEUE_MAX_SIZE 20U ///< Max queue size. +#define QUEUE_START_INDEX 0U ///< Queue start index. +#define MAX_JOB_DATA_SIZE_BYTES 32U ///< Max bytes per job (32 bytes). +#define NUM_OF_BYTES_PER_CAL_PAYLOAD 150U ///< Number of bytes per calibration payload. +#define CAL_DATA_SEND_INTERVAL_COUNT (MS_PER_SECOND / (5 * TASK_GENERAL_INTERVAL)) ///< Calibration data send time interval in counts. +#define RECORD_DATA_RECEIVE_TIMEOUT_MS (4 * MS_PER_SECOND) ///< Record data receive all the data packets timeout in ms. +#define RECORD_DATA_MAX_MESSAGE_DFFIRENCE 1 ///< Calibration data receive message different from the previous message. +#define RECORD_DATA_FIRST_RECEIVING_MSG_NUM 1 ///< Calibration data first receiving message number. + +// Once a new calibration data is available the driver, sets a signal for the defined time. Once the time is out, it turns the signal off. +#define NEW_CAL_AVAILABLE_SIGNAL_TIMEOUT_MS (1 * MS_PER_SECOND) ///< New calibration available signal timeout in milliseconds. +/// Memory operations structure. +typedef struct +{ + NVDATAMGMT_OPERATION_STATE_T memoryOperation; ///< Memory operation. + U32 startAddress; ///< Operation start address. + U08 buffer[ MAX_JOB_DATA_SIZE_BYTES ]; ///< Buffer. + U32 length; ///< Length of a buffer. +} MEMORY_OPS_T; + +/// Process records job structure +typedef struct +{ + NVDATAMGMT_OPERATION_STATE_T memoryOperation; ///< Memory operation. + RECORD_JOBS_STATE_T recordJob; ///< Record job (i.e sector 0). +} PROCESS_RECORD_JOB_T; + +/// NVDataMgmt process records states +typedef enum NVDataMgmt_Process_Records_States +{ + NVDATAMGMT_PROCESS_RECORD_STATE_IDLE = 0, ///< NVDataMgmt process record idle state. + NVDATAMGMT_PROCESS_RECORD_STATE_SEND_RECORD, ///< NVDataMgmt process record send record state. + NUM_OF_NVDATAMGMT_PROCESS_RECORD_STATES ///< Number of NVDataMgmt process records state. +} PROCESS_RECORD_STATE_T; + +/// NVDataMgmt receive records states +typedef enum NVDataMgmt_Receive_Records_States +{ + NVDATAMGMT_RECEIVE_RECORD_IDLE = 0, ///< NVDataMgmt receive record idle. + NVDATAMGMT_RECEIVE_RECORD_RECEIVE, ///< NVDataMgmt receive record receive. + NUM_OF_NVDATAMGMT_RECEIVE_RECORD_STATES ///< Number of NVDataMgmt receive record. +} RECEIVE_RECORD_STATE_T; + +static PROCESS_RECORD_JOB_T recordJobQueue[ QUEUE_MAX_SIZE ]; ///< Record queue jobs. +static U08 recordQueueRearIndex; ///< Record queue rear index. +static U08 recordQueueFrontIndex; ///< Record queue front index. +static U08 recordQueueCount; ///< Record queue count. +static PROCESS_RECORD_JOB_T recordCurrentJob; ///< Record queue current job. + +static U32 recordAddressOffset; ///< Record address offset. +static MEMORY_OPS_T currentJob; ///< Current job. +static NVDATAMGMT_RECORDS_READ_STATUS_T recordsReadStatus; ///< NVDataMgmt records read status. +static BOOL isNewCalAvailable; ///< Signal to indicate whether a new calibration data is available. + +static RECORD_JOBS_STATE_T recordToPublish; ///< Record to publish (i.e. calibration, system) +static PROCESS_RECORD_STATE_T nvDataMgmtExecProcessRecordState; ///< NVDataMgmt exec process record state. +static RECEIVE_RECORD_STATE_T nvDataMgmtExecReceiveRecordState; ///< NVDataMgmt exec receive record state. +static BOOL hasPublishRecordBeenRequested; ///< Record state machine publish request flag. +static U32 calPublishMessageCount; ///< Calibration data publish message counter. +static U32 calPublishTotalMessages; ///< Calibration data total number of messages to be sent. +static U32 calSendDataIntervalCounter; ///< Calibration data send to CAN bust interval counter. +static U32 previousCalMessageNum; ///< Calibration previous message number. +static U32 recordUpdateAddress; ///< DG record update address for all the write operations. +static U32 recordReceiveStartTime; ///< Time stamp the calibration/service was received. +static U32 newCalStartTimer; ///< New calibration availability start timer. + +// Process record functions +static PROCESS_RECORD_STATE_T handleExecProcessRecordIdleState( void ); +static PROCESS_RECORD_STATE_T handleExecProcessRecordSendRecordState( void ); + +static void monitorNewCalSignal( void ); + +void initNVMsgQ ( void ) +{ + recordUpdateAddress = 0; + recordQueueRearIndex = QUEUE_START_INDEX; + recordQueueFrontIndex = QUEUE_START_INDEX; + recordQueueCount = 0; + recordAddressOffset = 0; + + recordsReadStatus = NVDATAMGMT_RECORDS_NOT_STARTED; + isNewCalAvailable = FALSE; + nvDataMgmtExecProcessRecordState = NVDATAMGMT_PROCESS_RECORD_STATE_IDLE; + nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; + hasPublishRecordBeenRequested = FALSE; + + newCalStartTimer = 0; + + calPublishMessageCount = 1; + calPublishTotalMessages = 1; + calSendDataIntervalCounter = 0; + previousCalMessageNum = 0; + recordReceiveStartTime = 0; +} + +/*********************************************************************//** + * @brief + * The execNVDataMgmtProcessRecord runs the NVDataMgmt send records related + * to tasks. + * @details Inputs: nvDataMgmtExecProcessRecordState, + * nvDataMgmtExecReceiveRecordState, recordReceiveStartTime + * @details Outputs: nvDataMgmtExecProcessRecordState, + * nvDataMgmtExecReceiveRecordState + * @return none + *************************************************************************/ +void execNVDataMgmtProcessRecord( void ) +{ + switch ( nvDataMgmtExecProcessRecordState ) + { + case NVDATAMGMT_PROCESS_RECORD_STATE_IDLE: + nvDataMgmtExecProcessRecordState = handleExecProcessRecordIdleState(); + break; + + case NVDATAMGMT_PROCESS_RECORD_STATE_SEND_RECORD: + nvDataMgmtExecProcessRecordState = handleExecProcessRecordSendRecordState(); + break; + + default: +// SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_NVDATAMGMT_EXEC_CAL_STATE, nvDataMgmtExecProcessRecordState ); + + nvDataMgmtExecProcessRecordState = NVDATAMGMT_PROCESS_RECORD_STATE_IDLE; + break; + } + + // Check if the exec receive records is not idle + // This section checks the status of the asynchronous state machine that receives + // data from Dialin. + if ( nvDataMgmtExecReceiveRecordState != NVDATAMGMT_RECEIVE_RECORD_IDLE ) + { + // Check if the data receiving process has timed out. The exec receive record + // state machine is asynchronous so it is checked in this state machine + if ( TRUE == didTimeout( recordReceiveStartTime, RECORD_DATA_RECEIVE_TIMEOUT_MS ) ) + { + // Exec receive state machine timed out. Schedule a read to update the structure + enqueueRecordJob( NVDATAMGMT_READ, NVDATAMGMT_CALIBRATION_RECORD ); + nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; + } + } + + // Check the calibration signal + monitorNewCalSignal(); +} + +/*********************************************************************//** + * @brief + * The handleExecProcessRecordIdleState handles the idle state of the + * exec cal state machine. + * @details Inputs: hasPublishRecordBeenRequested, calPublishTotalMessages, + * calPublishMessageCount, calSendDataIntervalCounter + * @details Outputs: hasPublishRecordBeenRequested, calPublishTotalMessages, + * calPublishMessageCount, calSendDataIntervalCounter + * @return next state of the state machine + *************************************************************************/ +static PROCESS_RECORD_STATE_T handleExecProcessRecordIdleState( void ) +{ + PROCESS_RECORD_STATE_T state = NVDATAMGMT_PROCESS_RECORD_STATE_IDLE; + + if ( TRUE == hasPublishRecordBeenRequested ) + { + // Set the publish flag to FALSE + hasPublishRecordBeenRequested = FALSE; + + // Get the record specifications to find the size of the job + PROCESS_RECORD_SPECS_T recordSpec = RECORDS_SPECS [ recordToPublish ]; + + // Calculate the total number of messages required to be sent using ceilf function. This function rounds up the + // value and its result is converted to U32. + calPublishTotalMessages = (U32)ceilf( (F32)recordSpec.sizeofJob / (F32)NUM_OF_BYTES_PER_CAL_PAYLOAD ); + calPublishMessageCount = 0; + + // Time interval in between data to be sent. It is set to the interval count so on the first call + // of the send calibration record function, the first packet of data is sent + calSendDataIntervalCounter = CAL_DATA_SEND_INTERVAL_COUNT; + + state = NVDATAMGMT_PROCESS_RECORD_STATE_SEND_RECORD; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleExecProcessRecordSendRecordState handles the send calibration + * record state of the state machine. + * @details Inputs: dgCalibrationRecord, calPublishTotalMessages, + * calPublishMessageCount, calSendDataIntervalCounter + * @details Outputs: calPublishTotalMessages, calPublishMessageCount, + * calSendDataIntervalCounter + * @return next state of the state machine + *************************************************************************/ +static PROCESS_RECORD_STATE_T handleExecProcessRecordSendRecordState( void ) +{ + PROCESS_RECORD_STATE_T state = NVDATAMGMT_PROCESS_RECORD_STATE_SEND_RECORD; + + // If the current message number is less than the total, keep sending + if ( calPublishMessageCount < calPublishTotalMessages ) + { + // If it is time to send data + if ( ++calSendDataIntervalCounter >= CAL_DATA_SEND_INTERVAL_COUNT ) + { + // Set to default cal data payload length + U32 length = NUM_OF_BYTES_PER_CAL_PAYLOAD; + + PROCESS_RECORD_SPECS_T recordSpec = RECORDS_SPECS [ recordToPublish ]; + U08* startPtr = recordSpec.structAddressPtr; + + // If this is the last calibration data payload, calculate the remainder of the bytes to send + if ( ( calPublishMessageCount + 1 ) == calPublishTotalMessages ) + { + length = recordSpec.sizeofJob - ( calPublishMessageCount * NUM_OF_BYTES_PER_CAL_PAYLOAD ); + } + + // Find the new location of the pointer which is the start of the calibration payload to be sent + startPtr += calPublishMessageCount * NUM_OF_BYTES_PER_CAL_PAYLOAD; + + switch( recordToPublish ) + { + case NVDATAMGMT_CALIBRATION_RECORD: + // Pass the information to the CAN bus + sendDGCalibrationRecord( calPublishMessageCount + 1, calPublishTotalMessages, length, startPtr ); + break; + + case NVDATAMGMT_SYSTEM_RECORD: + // Pass the information to the CAN bus + sendDGSystemRecord( calPublishMessageCount + 1, calPublishTotalMessages, length, startPtr ); + break; + + case NVDATAMGMT_SERVICE_RECORD: + // Pass the information to the CAN bus + sendDGServiceRecord( calPublishMessageCount + 1, calPublishTotalMessages, length, startPtr ); + break; + + +#ifndef _RELEASE_ + case NVDATAMGMT_SW_CONFIG_RECORD: + sendDGSWConfigRecord( calPublishMessageCount + 1, calPublishTotalMessages, length, startPtr ); + break; +#endif + + case NVDATAMGMT_USAGE_INFO_RECORD: + sendDGUsageInfoRecord( calPublishMessageCount + 1, calPublishTotalMessages, length, startPtr ); + break; + + default: + //Ignore + break; + } + + calPublishMessageCount++; + calSendDataIntervalCounter = 0; + } + } + else + { + state = NVDATAMGMT_PROCESS_RECORD_STATE_IDLE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The enqueueRecordJob function enqueues a new record job. + * @details Inputs: recordQueueRearIndex, queueCount, recordJobQueue + * @details Outputs: recordQueueRearIndex, queueCount, recordJobQueue + * @param ops: memory operation (i.e write, read) + * @param job: type of job (i.e write calibration data) + * @return none + *************************************************************************/ +void enqueueRecordJob( NVDATAMGMT_OPERATION_STATE_T ops, RECORD_JOBS_STATE_T job ) +{ + PROCESS_RECORD_JOB_T currentJob; + + currentJob.memoryOperation = ops; + currentJob.recordJob = job; + recordJobQueue[ recordQueueRearIndex ] = currentJob; + + recordQueueCount++; + recordQueueRearIndex = INC_WRAP( recordQueueRearIndex, 0, QUEUE_MAX_SIZE - 1 ); +} + +/*********************************************************************//** + * @brief + * The dequeueRecordJob increments the front index counter and if it is + * equal to rear index, it sets it to -1, meaning that the queue is empty. + * @details Inputs: recordQueueFrontIndex, recordQueueCount, recordCurrentJob, + * recordJobQueue + * @details Outputs: recordQueueFrontIndex, recordQueueCount, recordCurrentJob + * @return none + *************************************************************************/ +void dequeueRecordJob( void ) +{ + U32 tempIndex; + + _disable_IRQ(); + tempIndex = recordQueueFrontIndex; + + if ( FALSE == isRecordQueueEmpty() ) + { + recordQueueFrontIndex = INC_WRAP( recordQueueFrontIndex, 0, QUEUE_MAX_SIZE - 1 ); + recordCurrentJob = recordJobQueue[ tempIndex ]; + } + if ( recordQueueCount > 0 ) + { + recordQueueCount--; + } + _enable_IRQ(); +} + +/*********************************************************************//** + * @brief + * The areResourcesAvailableForNextJob checks whether the resources such as + * semaphore for RTC is available prior to dequeuing the job to be processed. + * @details Inputs: recordQueueFrontIndex + * @details Outputs: none + * @return TRUE if the resources are available otherwise, FALSE + *************************************************************************/ +BOOL areResourcesAvailableForNextJob( void ) +{ + U32 tempIndex; + PROCESS_RECORD_JOB_T tempJob; + BOOL status = FALSE; + + // Initialize the structure per CppCheck run + tempJob.memoryOperation = NVDATAMGMT_NONE; + tempJob.recordJob = NVDATAMGMT_CALIBRATION_RECORD; + + _disable_IRQ(); + tempIndex = recordQueueFrontIndex; + + if ( FALSE == isRecordQueueEmpty() ) + { + tempJob = recordJobQueue[ tempIndex ]; + } + _enable_IRQ(); + + if ( TRUE == isFlashReady() ) + { + status = TRUE; + } + + + return status; +} + +/*********************************************************************//** + * @brief + * The isRecordQueueEmpty checks whether the queue is empty and if it is + * empty, it will return a false. + * @details Inputs: recordQueueCount + * @details Outputs: none + * @return TRUE if queue is not empty + *************************************************************************/ +BOOL isRecordQueueEmpty( void ) +{ + BOOL isEmpty = TRUE; + + if ( recordQueueCount > 0 ) + { + isEmpty = FALSE; + } + + return isEmpty; +} + +/*********************************************************************//** + * @brief + * The getAvailableRecordQueueCount returns the number of available record + * queues. + * @details Inputs: recordQueueCount + * @details Outputs: none + * @return available record queues + *************************************************************************/ +U32 getAvailableRecordQueueCount( void ) +{ + return QUEUE_MAX_SIZE - recordQueueCount; +} + +/*********************************************************************//** + * @brief + * The enqueueSector0Records checks whether there are enough jobs available + * to be able to enqueue to the records. + * @details Inputs: none + * @details Outputs: none + * @return TRUE if the job were successfully enqueued otherwise, FLASE + *************************************************************************/ +BOOL enqueueSector0Records( void ) +{ + BOOL status = FALSE; + + if ( getAvailableRecordQueueCount() >= MIN_JOBS_NEEDED_FOR_SECTOR_0 ) + { + RECORD_JOBS_STATE_T record; + + // It is sector 0 of bank 7 so the sector must be erased first + enqueueRecordJob( NVDATAMGMT_ERASE_SECTOR, NVDATAMGMT_CALIBRATION_RECORD ); + + for ( record = NVDATAMGMT_CALIBRATION_RECORD; record < NUM_OF_NVDATMGMT_RECORDS_JOBS; record++ ) + { + enqueueRecordJob( NVDATAMGMT_WRITE, record ); + } + + status = TRUE; + } + + return status; +} + +/*********************************************************************//** + * @brief + * The enqueuePOSTReadRecords enqueues the NV records to be read during POST. + * @details Inputs: none + * @details Outputs: recordsReadStatus + * @return none + *************************************************************************/ +void enqueuePOSTReadRecords( void ) +{ + RECORD_JOBS_STATE_T record; + + for ( record = NVDATAMGMT_CALIBRATION_RECORD; record < NUM_OF_NVDATMGMT_RECORDS_JOBS; record++ ) + { + enqueueRecordJob( NVDATAMGMT_READ, record ); + } + + // Set the status to records were queued successfully + recordsReadStatus = NVDATAMGMT_RECORDS_QUEUED; +} + +/*********************************************************************//** + * @brief + * The getNVRecordsReadStatus function returns the status of the NV POST. + * @details Inputs: none + * @details Outputs: none + * @return recordsReadStatus + *************************************************************************/ +NVDATAMGMT_RECORDS_READ_STATUS_T getNVRecordsReadStatus( void ) +{ + return recordsReadStatus; +} + +/*********************************************************************//** + * @brief + * The sendRecordToDialin function prepares the process record state machine + * to send a record to Dialin. + * @details Inputs: nvDataMgmtExecProcessRecordState + * @details Outputs: hasPublishRecordBeenRequested, recordToPublish + * @param job type of job that is requested (i.e. get calibration record, ...) + * @return TRUE if the request was successfully registered + *************************************************************************/ +BOOL sendRecordToDialin( RECORD_JOBS_STATE_T job ) +{ + BOOL status = FALSE; + + // Check if the state machine is in idle state and then set the request + if ( NVDATAMGMT_PROCESS_RECORD_STATE_IDLE == nvDataMgmtExecProcessRecordState ) + { + hasPublishRecordBeenRequested = TRUE; + recordToPublish = job; + status = TRUE; + } + + return status; +} + +/*********************************************************************//** + * @brief + * The receiveRecordFromDialin function receives the record that has been sent + * from Dialin and if the CRCs passed, it schedules a write to the NV data. + * @details Inputs: currentMessage, nvDataMgmtExecReceiveRecordState, + * previousCalMessageNum, totalMessages, previousCalMessageNum, + * @details Outputs: nvDataMgmtExecReceiveRecordState, recordReceiveStartTime, + * previousCalMessageNum, recordUpdateAddress, recordUpdateAddress + * @param job: the job that has to be received and written (i.e. service record) + * @param currentMessage: current message number that is received from Dialin + * @param totalMessages: total number of messages from Dialin + * @param length: message length in bytes + * @param *addressPtr: address to the beginning of the calibration data from Dialin + * @return TRUE if the request was successfully registered + *************************************************************************/ +BOOL receiveRecordFromDialin( RECORD_JOBS_STATE_T job, U32 currentMessage, U32 totalMessages, U32 length, U08 *addressPtr ) +{ + BOOL status = TRUE; + + // If the calibration message number is the first message number and receive exec state is idle, switch to idle + if ( ( RECORD_DATA_FIRST_RECEIVING_MSG_NUM == currentMessage ) && ( NVDATAMGMT_RECEIVE_RECORD_IDLE == nvDataMgmtExecReceiveRecordState ) ) + { + nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_RECEIVE; + recordReceiveStartTime = getMSTimerCount(); + previousCalMessageNum = 0; + recordUpdateAddress = 0; + } + + // Check if there is still a message left to be received + if ( ( NVDATAMGMT_RECEIVE_RECORD_RECEIVE == nvDataMgmtExecReceiveRecordState ) && ( currentMessage <= totalMessages ) ) + { + // Check if the current message is different from the previous message by 1 + if ( RECORD_DATA_MAX_MESSAGE_DFFIRENCE == ( currentMessage - previousCalMessageNum ) ) + { + // Define a pointer that points to the DG calibration record + PROCESS_RECORD_SPECS_T recordSpec = RECORDS_SPECS[ job ]; + U08* ptr = recordSpec.structAddressPtr; + + // Offset the pointer to length that we should start writing from + ptr += recordUpdateAddress; + + memcpy( ptr, addressPtr, length ); + + // Check if the current message is total messages + // and 0 everything out since we are done writing + if ( currentMessage == totalMessages ) + { + // Assume the institutional record is fine unless the record is really the institutional record + BOOL isInstRcrdValid = TRUE; + U16 calcCRC = crc16 ( recordSpec.structAddressPtr, recordSpec.sizeofJob - sizeof(U16) ); + // Get the CRC of the structure without the last 16 bits which is the CRC as well as the padding values + U16 recordCRC = *(U16*)recordSpec.structCRCPtr; + + if ( ( calcCRC != recordCRC ) || ( FALSE == isInstRcrdValid ) ) + { + // Institutional record has failed so do not write it into the NV memory and read back what was in the NV memory + // CRC failed, request a read to read the data back from NV memory and update the structure + enqueueRecordJob( NVDATAMGMT_READ, job ); + + nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; + status = FALSE; + } + else + { + // CRC passed, enqueue an erase, a write of calibration data and a write of service record + BOOL scheduleStatus = enqueueSector0Records(); + + // Signal that there is a new calibration record available. + // NOTE: as of now, this signal will be sent even after the system record is sent + newCalStartTimer = getMSTimerCount(); + isNewCalAvailable = TRUE; + + // Update the event of the received record that has been accepted + SEND_EVENT_WITH_2_U32_DATA( recordSpec.nvEvent, 0, 0 ) + + // Done with receiving data, go back to idle + nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; + } + } + else + { + // Update the length as it has successfully been written + recordUpdateAddress += length; + + // Now the current message is the previous message + previousCalMessageNum = currentMessage; + } + } + } + + return status; +} + +/*********************************************************************//** + * @brief + * The monitorNewCalSignal function monitors the new calibration signal + * and if it on and time to on has elapsed, it turns it off. + * @details Inputs: isNewCalAvailable + * @details Outputs: isNewCalAvailable + * @return none + *************************************************************************/ +static void monitorNewCalSignal( void ) +{ + if ( ( TRUE == didTimeout( newCalStartTimer, NEW_CAL_AVAILABLE_SIGNAL_TIMEOUT_MS ) ) && ( TRUE == isNewCalAvailable ) ) + { + isNewCalAvailable = FALSE; + } +} + +/*********************************************************************//** + * @brief + * The isNewCalibrationRecordAvailable function returns the status of a + * new calibration availability. + * @details Inputs: none + * @details Outputs: none + * @return isNewCalAvailable which is TRUE if new calibration is available, + * otherwise FALSE + *************************************************************************/ +BOOL isNewCalibrationRecordAvailable( void ) +{ + return isNewCalAvailable; +} Index: firmware/App/Services/NVMsgQ.h =================================================================== diff -u --- firmware/App/Services/NVMsgQ.h (revision 0) +++ firmware/App/Services/NVMsgQ.h (revision 1e809a81cb8831f5aa5938bd333cc374920d5b87) @@ -0,0 +1,109 @@ +/************************************************************************** +* +* Copyright (c) 2026-2027 Diality Inc. - All Rights Reserved. +* +* THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +* WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +* +* @file NVMsgQ.h +* +* @author (original) Arpita Srivastava +* @date (original) 31-Mar-2026 +* +***************************************************************************/ + +#ifndef _NV_MSG_Q_H_ +#define _NV_MSG_Q_H_ + +#include "Common.h" +#indluce "DDDefs.h" + +#define MIN_JOBS_NEEDED_FOR_SECTOR_0 5U ///< Min queue count needed to erase and write all (5) records back in sector 0. +#define DD_USAGE_INFO_START_ADDRESS 0x00000030 /* 48 */ ///< DG usage info start address in RTC RAM (48). +#define DD_SCHEDULED_RUNS_START_ADDRESS (DD_USAGE_INFO_START_ADDRESS + sizeof(DD_SERVICE_GROUP_T)) ///< DG scheduled runs start address in RTC RAM. +#define DD_HEATERS_INFO_START_ADDRESS (DD_SCHEDULED_RUNS_START_ADDRESS + sizeof(DD_HEATERS_INFO_GROUP_T))///< DG heaters info start address in RTC RAM. +#define SW_CONFIGS_START_ADDRESS 0x00000100 /* 256 */ ///< Software configurations start address in RTC RAM. + +#define SYSTEM_RECORD_NV_MEM_START_ADDRESS (BANK7_SECTOR0_START_ADDRESS) ///< System record storage start address in NV memory. +#define SERVICE_RECORD_NV_MEM_START_ADDRESS (BANK7_SECTOR0_START_ADDRESS + BITS_12_FULL_SCALE) ///< Service record storage start address in NV memory. +#define CAL_RECORD_NV_MEM_START_ADDRESS (SERVICE_RECORD_NV_MEM_START_ADDRESS + BITS_12_FULL_SCALE) ///< Calibration record storage start address in NV memory. + +/// NVDataMgmt memory operation modes enumeration. +typedef enum NVDataMgmt_Operation +{ + NVDATAMGMT_NONE = 0, ///< Default mode to prevent any accidental ops. + NVDATAMGMT_WRITE, ///< Operation mode write. + NVDATAMGMT_READ, ///< Operation mode read. + NVDATAMGMT_ERASE_SECTOR, ///< Operation mode erase a sector (EEPROM). + NUM_OF_NVDATAMGMT_OPS_STATES ///< Total number of operation states. +} NVDATAMGMT_OPERATION_STATE_T; + +/// NVDataMgmt records' jobs states +typedef enum NVDataMgmt_Records_Jobs +{ + NVDATAMGMT_CALIBRATION_RECORD = 0, ///< NVDataMgmt process write calibration record. + NVDATAMGMT_SYSTEM_RECORD, ///< NVDataMgmt process write system record. + NVDATAMGMT_SERVICE_RECORD, ///< NVDataMgmt process service record. + NVDATAMGMT_SCHEDULED_RUNS_RECORD, ///< NVDataMgmt process scheduled runs record. + NVDATAMGMT_HEATERS_INFO_RECORD, ///< NVDataMgmt process heaters info record. + NVDATAMGMT_USAGE_INFO_RECORD, ///< NVDataMgmt process usage info record. +#ifndef _RELEASE_ + NVDATAMGMT_SW_CONFIG_RECORD, ///< NVDataMgmt process software record. +#endif + NUM_OF_NVDATMGMT_RECORDS_JOBS ///< Number of NVDataMgmt records jobs. +} RECORD_JOBS_STATE_T; + +/// NVDataMgmt records read status +typedef enum NVDataMgmt_NV_Records_Read_Status +{ + NVDATAMGMT_RECORDS_NOT_STARTED = 0, ///< NVDataMgmt records not started. + NVDATAMGMT_RECORDS_QUEUED, ///< NVDataMgmt records queued. + NVDATAMGMT_RECORDS_READ, ///< NVDataMgmt records read. + NVDATAMGMT_RECORDS_CRC_CHECKED, ///< NVDataMgmt records CRC checked. + NUM_OF_NVDATAMGMT_READ_RECORDS_STATES, ///< Number of NVDataMgmt read records states. +} NVDATAMGMT_RECORDS_READ_STATUS_T; + +/// Process records specifications structure +typedef struct +{ + U32 startAddress; ///< Jobs spec start address. + U32 sizeofJob; ///< Jobs spec size of job. + U32 maxWriteBufferSize; ///< Jobs max write allowed processing buffer size. + U32 maxReadBufferSize; ///< Jobs max read allowed processing buffer size. + U08* structAddressPtr; ///< Jobs structure address pointer. + U08* structCRCPtr; ///< Jobs structure CRC pointer. + DD_EVENT_ID_T nvEvent; ///< Jobs non-volatile DG event (i.e calibration, system). + +} PROCESS_RECORD_SPECS_T; + +// Process records specifications +const PROCESS_RECORD_SPECS_T RECORDS_SPECS[ NUM_OF_NVDATMGMT_RECORDS_JOBS ] = { + // Start address Size of the job Max write bytes per job Max read bytes per job Record structure pointer Record CRC pointer Event calibration record update + {CAL_RECORD_NV_MEM_START_ADDRESS, sizeof(DD_CALIBRATION_RECORD_T), MAX_EEPROM_WRITE_BUFFER_BYTES, sizeof(DD_CALIBRATION_RECORD_T), (U08*)&ddCalibrationRecord, (U08*)&ddCalibrationRecord.crc, DD_EVENT_CAL_RECORD_UPDATE }, // NVDATAMGMT_CALIBRATION_RECORD + {SYSTEM_RECORD_NV_MEM_START_ADDRESS, sizeof(DD_SYSTEM_GROUP_T), MAX_EEPROM_WRITE_BUFFER_BYTES, sizeof(DD_SYSTEM_GROUP_T), (U08*)&ddSystemGroup, (U08*)&ddSystemGroup.crc, DD_EVENT_SYSTEM_RECORD_UPDATE }, // NVDATAMGMT_SYSTEM_RECORD + {SERVICE_RECORD_NV_MEM_START_ADDRESS, sizeof(DD_SERVICE_GROUP_T), MAX_EEPROM_WRITE_BUFFER_BYTES, sizeof(DD_SERVICE_GROUP_T), (U08*)&ddServiceGroup, (U08*)&ddServiceGroup.crc, DD_EVENT_SERVICE_UPDATE }, // NVDATAMGMT_SERVICE_RECORD + {DD_SCHEDULED_RUNS_START_ADDRESS, sizeof(DD_SCHEDULED_RUNS_GROUP_T), MAX_EEPROM_WRITE_BUFFER_BYTES, sizeof(DD_SCHEDULED_RUNS_GROUP_T),(U08*)&ddScheduledRunsGroup, (U08*)&ddScheduledRunsGroup.crc, DD_EVENT_SCHEDULED_RUNS_UPDATE }, // NVDATAMGMT_SCHEDULER_RECORD + {DD_HEATERS_INFO_START_ADDRESS, sizeof(DD_HEATERS_INFO_GROUP_T), MAX_EEPROM_WRITE_BUFFER_BYTES, sizeof(DD_HEATERS_INFO_GROUP_T), (U08*)&ddHeatersInfoGroup, (U08*)&ddHeatersInfoGroup.crc, DD_EVENT_HEATERS_INFO_UPDATE }, // NVDATAMGMT_HEATERS_INFO_RECORD + {DD_USAGE_INFO_START_ADDRESS, sizeof(DD_USAGE_INFO_GROUP_T), MAX_EEPROM_WRITE_BUFFER_BYTES, sizeof(DD_USAGE_INFO_GROUP_T), (U08*)&ddUsageInfoGroup, (U08*)&ddUsageInfoGroup.crc, DD_EVENT_USAGE_INFO_UPDATE }, // NVDATAMGMT_USAGE_INFO_RECORD +#ifndef _RELEASE_ + {SW_CONFIGS_START_ADDRESS, sizeof(DD_SW_CONFIG_GROUP_T), MAX_EEPROM_WRITE_BUFFER_BYTES, sizeof(DD_SW_CONFIG_GROUP_T), (U08*)&ddSWConfigGroup, (U08*)&ddSWConfigGroup.crc, DD_EVENT_SW_CONFIG_UPDATE } // NVDATAMGMT_SW_CONFIG_RECORD +#endif +}; + +void initNVMsgQ ( void ); +void execNVDataMgmtProcessRecord( void ); + +// Record operations queue functions +void enqueueRecordJob( NVDATAMGMT_OPERATION_STATE_T ops, RECORD_JOBS_STATE_T job ); +void dequeueRecordJob( void ); +BOOL areResourcesAvailableForNextJob( void ); +BOOL isRecordQueueEmpty( void ); +U32 getAvailableRecordQueueCount( void ); +BOOL enqueueSector0Records( void ); +void enqueuePOSTReadRecords( void ); + +BOOL sendRecordToDialin( RECORD_JOBS_STATE_T job ); +BOOL receiveRecordFromDialin( RECORD_JOBS_STATE_T job, U32 currentMessage, U32 totalMessages, U32 length, U08 *addressPtr ); +BOOL isNewCalibrationRecordAvailable( void ); + +#endif /* _NV_MSG_Q_H_ */ Index: firmware/App/Services/NVRecordsDD.c =================================================================== diff -u --- firmware/App/Services/NVRecordsDD.c (revision 0) +++ firmware/App/Services/NVRecordsDD.c (revision 1e809a81cb8831f5aa5938bd333cc374920d5b87) @@ -0,0 +1,1193 @@ +/************************************************************************** +* +* Copyright (c) 2026-2027 Diality Inc. - All Rights Reserved. +* +* THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +* WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +* +* @file NVRecordsDD.c +* +* @author (original) Arpita Srivastava +* @date (original) 31-Mar-2026 +* +***************************************************************************/ + +#include // For memcpy +#include "NVMsgQ.h" +#include "NVRecordsDD.h" +#include "Utilities.h" // For crc calculation + +// Data addresses and length in RTC RAM + + +#define RECORD_DEFAULT_TIME 0U ///< Record default time (calibration/set). +#define RECORD_FOURTH_ORDER_COEFF 0.0F ///< Record fourth order coefficient. +#define RECORD_THIRD_ORDER_COEFF 0.0F ///< Record third order coefficient. +#define RECORD_SECOND_ORDER_COEFF 0.0F ///< Record second order coefficient. +#define RECORD_DEFAULT_GAIN 1.0F ///< Record default gain. +#define RECORD_DEFAULT_OFFSET 0.0F ///< Record default offset. +#define RECORD_DEFAULT_CONST 0.0F ///< Record default constant. +#define RECORD_DEFAULT_RATIO 1.0F ///< Record default ratio. +#define RECORD_DEFAULT_SERVICE_INTERVAL_S 15768000U ///< Record default service interval in seconds (6 months). +#define RECORD_DEFAULT_CHARACTER ' ' ///< Record default character. + +// DD specific defines +#define DEFAULT_FLUSH_LINES_VOLUME_L 0.01F ///< Water volume to flush when starting re-circulate mode in liters. +#define DEFAULT_BICARB_CONC_MIXING_RATIO ( 4.06812F / FRACTION_TO_PERCENT_FACTOR ) ///< Ratio between RO water and bicarbonate concentrate mixing ratio. +#define DEFAULT_BICARB_BOTTLE_VOL_ML 3780.0F ///< Record default bicarb bottle volume in milliliters. +#define DEFAULT_BICARB_COND_US_PER_CM 13734.88F ///< Record default acid conductivity in uS/cm. +#define DEFAULT_BICARB_BOTTLE_TEMP_C 23.5F ///< Record default acid bottle temperature in C. +#define DEFAULT_ACID_CONC_MIXING_RATIO ( 2.35618F / FRACTION_TO_PERCENT_FACTOR ) ///< Ratio between RO water and acid concentrate mixing ratio. +#define DEFAULT_ACID_BOTTLE_VOL_ML 3430.0F ///< Record default acid bottle volume in milliliters. +#define DEFAULT_ACID_COND_US_PER_CM 11645.05F ///< Record default acid conductivity in uS/cm. +#define DEFAULT_ACID_BOTTLE_TEMP_C 23.5F ///< Record default acid bottle temperature in C. +#define DEFAULT_RSRVR_TEMP_TAU_C_PER_MIN -0.25F ///< Reservoir temperature time constant C/min. +#define DEFAULT_UF_TEMP_TAU_C_PER_MIN -0.6F ///< Ultrafilter temperature time constant C/min. +#define DEFAULT_UF_VOLUME_ML 700 ///< Ultrafilter volume in milliliters. + +#define DEFAULT_FILL_1251_1_ACID_SNSR_US_PER_CM 0.0F ///< Fill acid 1251_1 acid sensor conductivity uS/cm. +#define DEFAULT_FILL_1251_1_BIC_SNSR_US_PER_CM 13616.23F ///< Fill acid 1251_1 bicarb sensor conductivity uS/cm. +#define DEFAULT_FILL_2251_0_ACID_SNSR_US_PER_CM 0.0F ///< Fill acid 2251_0 acid sensor conductivity uS/cm. +#define DEFAULT_FILL_2251_0_BIC_SNSR_US_PER_CM 13734.88F ///< Fill acid 2251_0 bicarb sensor conductivity uS/cm. +#define DEFAULT_FILL_3251_9_ACID_SNSR_US_PER_CM 0.0F ///< Fill acid 3251_9 acid sensor conductivity uS/cm. +#define DEFAULT_FILL_3251_9_BIC_SNSR_US_PER_CM 13854.49F ///< Fill acid 3251_9 bicarb sensor conductivity uS/cm. + +#define DEFAULT_ACID_TEST_1251_1_US_PER_CM 13768.28F ///< Acid test acid 1251_1 acid conductivity uS/cm. +#define DEFAULT_ACID_TEST_2251_0_US_PER_CM 13919.05F ///< Acid test acid 2251_0 acid conductivity uS/cm. +#define DEFAULT_ACID_TEST_3251_9_US_PER_CM 14071.04F ///< Acid acid 3251_9 acid conductivity uS/cm. + +#define DEFAULT_BIC_TEST_ACID_SNSR_US_PER_CM 0.0F ///< Bicarb test acid sensor conductivity uS/cm. +#define DEFAULT_BIC_TEST_BIC_SNSR_US_PER_CM 3890.0F ///< Bicarb test bicarb sensor conductivity uS/cm. + +#define MAX_NUM_OF_WRITE_TRIES 3U ///< Max number of write tries. + +#define RECORD_BYTE_SIZE(r) (sizeof(r) + sizeof(U16)) ///< Record byte size macro. +// Padding length calculation: (DG struct size % bytes to write(16) == 0 ? 0 : ((DG struct size / bytes to write(16)) + 1) * bytes to write(16)) - DG struct size. +// NOTE: assuming the macro is calculating the padding length for a non-volatile memory. For NV, 16 bytes is used and buffer and for the RTC RAM 64 bytes is used. +// If the size of the structure + a 2-byte crc mod 16 is 0, then the size of the padding is 0 +// Otherwise, the (((structure size + crc) / 16) + 1) * 16. In the calculations, a + 1 is added since the division has a decimal so + 1 is used +// to round up. The result is then multiplied by 16 bytes to get the number of bytes needed and is subtracted from the size of the structure and CRC. +#define RECORD_PADDING_LENGTH(rcrd, buf) (RECORD_BYTE_SIZE(rcrd) % buf == 0 ? 0 : \ + ((((RECORD_BYTE_SIZE(rcrd) / buf) + 1)) * buf) \ + - RECORD_BYTE_SIZE(rcrd)) ///< DG padding length macro. + + + + +/// NVDataMgmt self-test states enumeration. +typedef enum NVDataMgmt_Self_Test_States +{ + NVDATAMGMT_SELF_TEST_STATE_READ_RECORDS = 0, ///< Self test read records. + NVDATAMGMT_SELF_TEST_STATE_CHECK_CRC, ///< Self test check CRC. + NVDATAMGMT_SELF_TEST_STATE_COMPLETE, ///< Self test complete. + NUM_OF_NVDATAMGMT_SELF_TEST_STATES ///< Total number of self-test states. +} NVDATAMGMT_SELF_TEST_STATE_T; + +#pragma pack(push, 1) + +// ********** DD record structures ********** + +/// DD calibration groups structure +typedef struct +{ + DD_PRES_SENSORS_CAL_RECORD_T presSensorsCalRecord; ///< DG pressure sensors. + DD_FLOW_SENSORS_CAL_RECORD_T flowSensorsCalRecord; ///< DG flow sensors. + DD_LOAD_CELLS_CAL_RECORD_T loadCellsCalRecord; ///< DG load cells. + DD_TEMP_SENSORS_CAL_RECORD_T tempSensorsCalRecord; ///< DG temperature sensors. + DD_CONC_PUMPS_CAL_RECORD_T concentratePumpsRecord; ///< DG concentrate pumps. + DD_DRAIN_PUMP_CAL_RECORD_T drainPumpRecord; ///< DG drain pump. + DD_RO_PUMP_CAL_RECORD_T roPumpRecord; ///< DG RO pump. + DD_DRAIN_LINE_VOLUME_T drainLineVolumeRecord; ///< DG drain line volume. + DD_PRE_RO_PURGE_VOLUME_T preROPurgeVolumeRecord; ///< DG RO purge volume. + DD_RESERVOIR_VOLUME_RECORD_T reservoirVolumesRecord; ///< DG reservoir volumes. + DD_GENERIC_VOLUME_RECORD_T genericVolumeRecord; ///< DG generic volume (magic number because the value is unknown). + DD_ACID_CONCENTRATES_RECORD_T acidConcentratesRecord; ///< DG acid concentrates. + DD_BICARB_CONCENTRATES_RECORD_T bicarbConcentratesRecord; ///< DG bicarb concentrates. + DD_FILTERS_CAL_RECORD_T filtersRecord; ///< DG filters. + DD_FANS_CAL_RECORD_T fansRecord; ///< DG fans. + DD_ACCEL_SENSOR_CAL_RECORD_T accelerometerSensorCalRecord; ///< DG accelerometer sensor. + DD_HEATING_CAL_RECORD_T heatingCalRecord; ///< DG heating calibration record. + DD_CHEMICALS_FILL_COND_CAL_RECORD_T fillCondCalRecord; ///< DG fill acid/bicarb target conductivities calibration record. +} DD_CALIBRATION_GROUPS_T; + +/// DG calibration records structure +typedef struct +{ + DD_CALIBRATION_GROUPS_T ddCalibrationGroups; ///< DG calibration groups. + U08 padding[ RECORD_PADDING_LENGTH(DD_CALIBRATION_GROUPS_T, MAX_EEPROM_WRITE_BUFFER_BYTES) ]; ///< DG calibration record padding byte array. + U16 crc; ///< CRC for the DG calibration record structure. +} DD_CALIBRATION_RECORD_T; + +/// DG system group structure +typedef struct +{ + DD_SYSTEM_RECORD_T ddSystemRecord; ///< DG system record. + U08 padding[ RECORD_PADDING_LENGTH(DD_SYSTEM_RECORD_T, MAX_EEPROM_WRITE_BUFFER_BYTES) ]; ///< DG system group padding byte array. + U16 crc; ///< CRC for the DG system group structure. +} DD_SYSTEM_GROUP_T; + +/// DG service record structure +typedef struct +{ + DD_SERVICE_RECORD_T ddServiceRecord; ///< DG service record. + U08 padding[ RECORD_PADDING_LENGTH(DD_SERVICE_RECORD_T, MAX_RTC_RAM_OPS_BUFFER_BYTES) ]; ///< DG service group padding. + U16 crc; ///< CRC for the DG service structure. +} DD_SERVICE_GROUP_T; + +/// DG scheduler record structure +typedef struct +{ + DD_SCHEDULED_RUN_RECORD_T ddScheduledRun; ///< DG scheduled runs. + U08 padding[ RECORD_PADDING_LENGTH(DD_SCHEDULED_RUN_RECORD_T, MAX_RTC_RAM_OPS_BUFFER_BYTES) ]; ///< DG scheduled run group padding. + U16 crc; ///< CRC for the DG scheduled runs structure. +} DD_SCHEDULED_RUNS_GROUP_T; + +/// DG usage record structure +typedef struct +{ + DD_USAGE_INFO_RECORD_T ddUsageInfo; ///< DG usage info record. + U08 padding[ RECORD_PADDING_LENGTH(DD_USAGE_INFO_RECORD_T, MAX_RTC_RAM_OPS_BUFFER_BYTES) ]; ///< DG scheduled run group padding. + U16 crc; ///< CRC for the DG usage info structure. +} DD_USAGE_INFO_GROUP_T; + +/// DG heaters record +typedef struct +{ + DD_HEATERS_RECORD_T ddHeatersInfo; ///< DG heaters info record. + U08 padding[ RECORD_PADDING_LENGTH(DD_HEATERS_RECORD_T, MAX_RTC_RAM_OPS_BUFFER_BYTES) ]; ///< DG heater info group padding. + U16 crc; ///< CRC for the DG heaters info structure. +} DD_HEATERS_INFO_GROUP_T; + +#ifndef _RELEASE_ +/// DG software configurations group +typedef struct +{ + DD_SW_CONFIG_RECORD_T ddSWConfigsRecord; ///< Software configurations record. + + // Since the software configurations are one byte, Num_of was used for the length of the lists + U08 padding[ RECORD_PADDING_LENGTH(DD_SW_CONFIG_RECORD_T, MAX_RTC_RAM_OPS_BUFFER_BYTES) ]; ///< Software configurations group padding. + U16 crc; ///< Software configurations CRC. +} DD_SW_CONFIG_GROUP_T; +#endif + +#pragma pack(pop) + +// Calibration variables +static DD_CALIBRATION_RECORD_T ddCalibrationRecord; ///< DG calibration record structure (including padding and final CRC). +static DD_SYSTEM_GROUP_T ddSystemGroup; ///< DG system group structure (including padding and final CRC). +static DD_SERVICE_GROUP_T ddServiceGroup; ///< DG service group structure (including padding and final CRC). +static DD_SCHEDULED_RUNS_GROUP_T ddScheduledRunsGroup; ///< DG scheduled run structure (including padding and final CRC). +static DD_USAGE_INFO_GROUP_T ddUsageInfoGroup; ///< DG usage info structure (including padding and final CRC). +static DD_HEATERS_INFO_GROUP_T ddHeatersInfoGroup; ///< DG heaters info structure (including padding and final CRC). +#ifndef _RELEASE_ +static DD_SW_CONFIG_GROUP_T ddSWConfigGroup; ///< DG Software configurations structure(including padding and final CRC). +#endif + +static NVDATAMGMT_SELF_TEST_STATE_T nvDataMgmtSelfTestState; ///< NVDataMgmt self-test state variable. +static SELF_TEST_STATUS_T nvDataMgmtSelfTestResult; ///< NVDataMgmt self-test result. +static U32 usageWriteTries; ///< Usage write tries. + +// Self test functions +static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadRecords( void ); +static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestCheckCRC( void ); + +// Record check helper functions +static BOOL isPolynomialRecordValid( POLYNOMIAL_CAL_PAYLOAD_T* record ); +static BOOL isDDSystemRecordValid( void ); +static BOOL isDDServiceRecordValid( void ); +static BOOL isDDUsageRecordValid( void ); +static BOOL isDDCalibrationRecordValid( void ); +static BOOL isDDDrainPumpRecordValid( DD_DRAIN_PUMP_CAL_RECORD_T* record ); +static BOOL isDDROPumpRecordValid( DD_RO_PUMP_CAL_RECORD_T* record ); +static BOOL isDDPreROPurgeVolumeRecordValid( DD_PRE_RO_PURGE_VOLUME_T* record ); +static BOOL isDDDrainLineVolRecordValid( DD_DRAIN_LINE_VOLUME_T* record ); +static BOOL isDDReservoirVolRecordValid( DD_RESERVOIR_VOLUME_DATA_T* record ); +static BOOL isDDGenericVolRecordValid( DD_GENERIC_VOLUME_DATA_T* record ); +static BOOL isDDAcidConcentrateRecordValid( DD_ACID_CONCENTRATE_T* record ); +static BOOL isDDBicarbConcentrateRecordValid( DD_BICARB_CONCENTRATE_T* record ); +static BOOL isDDFilterRecordValid( DD_FILTER_CAL_RECORD_T* record ); +static BOOL isDDFanRecordValid( DD_FAN_CAL_RECORD_T* record ); +static BOOL isDDAccelerometerSensorRecordValid( DD_ACCEL_SENSOR_CAL_RECORD_T* record ); +static BOOL isDDHeatingCalRecordValid( DD_HEATING_CAL_RECORD_T* record ); +#ifndef _RELEASE_ +static BOOL isSWConfigRecordValid( void ); +#endif + +void initNVRecordsDD( void ) +{ + // Initialize the parameters + nvDataMgmtSelfTestState = NVDATAMGMT_SELF_TEST_STATE_READ_RECORDS; + nvDataMgmtSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; + usageWriteTries = 0; +} + +/*********************************************************************//** + * @brief + * The execNVDataMgmtSelfTest runs the NVDataMgmt POST during the self-test. + * @details Inputs: nvDataMgmtSelfTestState, nvDataMgmtSelfTestResult + * @details Outputs: nvDataMgmtSelfTestState + * @return nvDataMgmtSelfTestResult the result of self-test + *************************************************************************/ +SELF_TEST_STATUS_T execNVDataMgmtSelfTest ( void ) +{ + switch ( nvDataMgmtSelfTestState ) + { + case NVDATAMGMT_SELF_TEST_STATE_READ_RECORDS: + nvDataMgmtSelfTestState = handleSelfTestReadRecords(); + break; + + case NVDATAMGMT_SELF_TEST_STATE_CHECK_CRC: + nvDataMgmtSelfTestState = handleSelfTestCheckCRC(); + break; + + case NVDATAMGMT_SELF_TEST_STATE_COMPLETE: + // Done with POST. Do nothing + break; + + default: +// SET_ALARM_WITH_2_U32_DATA ( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_NVDATAMGMT_INVALID_SELF_TEST_STATE, nvDataMgmtSelfTestState ); + nvDataMgmtSelfTestState = NVDATAMGMT_SELF_TEST_STATE_COMPLETE; + nvDataMgmtSelfTestResult = SELF_TEST_STATUS_FAILED; + break; + } + + return nvDataMgmtSelfTestResult; +} + +/*********************************************************************//** + * @brief + * The handleSelfTestReadRecords waits for the records to be read + * @details Inputs: none + * @details Outputs: none + * @return next state + *************************************************************************/ +static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadRecords( void ) +{ + NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_READ_RECORDS; + + // Check if the queues are empty and the exec state machine is in Idle meaning all the records have been read and the state machine + // is back at Idle so even the last job in the queue has been processed + if ( ( TRUE == isRecordQueueEmpty() ) && ( NVDATAMGMT_EXEC_STATE_IDLE == nvDataMgmtExecState ) ) + { + recordsReadStatus = NVDATAMGMT_RECORDS_READ; + state = NVDATAMGMT_SELF_TEST_STATE_CHECK_CRC; + } + + return state; +} + + +/*********************************************************************//** + * @brief + * The handleSelfTestCheckCRC calculates the CRC of the records and compares + * them to the CRC that was read. If they don't match, it will fail POST. + * @details Inputs: nvDataMgmtSelfTestResult + * @details Outputs: none + * @return next state + *************************************************************************/ +static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestCheckCRC ( void ) +{ + NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_COMPLETE; + BOOL haveCalGroupsPassed = TRUE; + BOOL hasSystemRecordPassed = TRUE; + BOOL hasServiceRecordPassed = TRUE; + BOOL hasInstitutionalRecordPassed = TRUE; + BOOL hasSWConfigRecordPassed = TRUE; + BOOL hasUsageRecordPassed = TRUE; + + recordsReadStatus = NVDATAMGMT_RECORDS_CRC_CHECKED; + + // Check all the calibration groups + haveCalGroupsPassed = isDDCalibrationRecordValid(); + hasSystemRecordPassed = isDDSystemRecordValid(); + hasServiceRecordPassed = isDDServiceRecordValid(); + hasUsageRecordPassed = isDDUsageRecordValid(); +#ifndef _RELEASE_ + hasSWConfigRecordPassed = isSWConfigRecordValid(); +#endif + + // If any of the records did not pass, they should be filled + // with benign values. After that, schedule a write to sector 0 + // to re-write the records with the benign values + if ( ( FALSE == hasServiceRecordPassed ) || ( FALSE == haveCalGroupsPassed ) || ( FALSE == hasSystemRecordPassed ) || ( FALSE == hasInstitutionalRecordPassed ) ) + { + enqueueSector0Records(); + SEND_EVENT_WITH_2_U32_DATA( RECORDS_SPECS[ NVDATAMGMT_CALIBRATION_RECORD ].nvEvent, 0, 0 ) + SEND_EVENT_WITH_2_U32_DATA( RECORDS_SPECS[ NVDATAMGMT_SYSTEM_RECORD ].nvEvent, 0, 0 ) + SEND_EVENT_WITH_2_U32_DATA( RECORDS_SPECS[ NVDATAMGMT_SERVICE_RECORD ].nvEvent, 0, 0 ) + } + +#ifndef _RELEASE_ + if ( FALSE == hasSWConfigRecordPassed ) + { + enqueueRecordJob( NVDATAMGMT_WRITE, NVDATAMGMT_SW_CONFIG_RECORD ); + SEND_EVENT_WITH_2_U32_DATA( RECORDS_SPECS[ NVDATAMGMT_SW_CONFIG_RECORD ].nvEvent, 0, 0 ) + } +#endif + + if ( FALSE == hasUsageRecordPassed ) + { + enqueueRecordJob( NVDATAMGMT_WRITE, NVDATAMGMT_USAGE_INFO_RECORD ); + SEND_EVENT_WITH_2_U32_DATA( RECORDS_SPECS[ NVDATAMGMT_USAGE_INFO_RECORD ].nvEvent, 0, 0 ) + } + + // Check if the records' entire CRCs as well as the individual CRCs passed + if ( ( TRUE == haveCalGroupsPassed ) && ( TRUE == hasSystemRecordPassed ) && ( TRUE == hasServiceRecordPassed ) && ( TRUE == hasInstitutionalRecordPassed ) ) + { + newCalStartTimer = getMSTimerCount(); + isNewCalAvailable = TRUE; + nvDataMgmtSelfTestResult = SELF_TEST_STATUS_PASSED; + + } + else + { + nvDataMgmtSelfTestResult = SELF_TEST_STATUS_FAILED; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The benignPolynomialCalRecord function benigns the provided polynomial + * calibration record. + * @details Inputs: none + * @details Outputs: none + * @return record which is a pointer to a polynomial calibration record + * otherwise none + *************************************************************************/ +void benignPolynomialCalRecord( POLYNOMIAL_CAL_PAYLOAD_T* record ) +{ + record->fourthOrderCoeff = RECORD_FOURTH_ORDER_COEFF; + record->thirdOrderCoeff = RECORD_THIRD_ORDER_COEFF; + record->secondOrderCoeff = RECORD_SECOND_ORDER_COEFF; + record->gain = RECORD_DEFAULT_GAIN; + record->offset = RECORD_DEFAULT_OFFSET; + record->calibrationTime = RECORD_DEFAULT_TIME; + record->crc = crc16 ( (U08*)record, sizeof( POLYNOMIAL_CAL_PAYLOAD_T ) - sizeof( U16 ) ); +} + +/*********************************************************************//** + * @brief + * The isPolynomialRecordValid function checks whether the records are + * still valid by calculating the CRCs and comparing it to the strucutre's + * CRC. + * @details Inputs: none + * @details Outputs: none + * @param record: pointer to a polynomial payload. The actual calibration + * data to be checked + * @param isRecordNeeded: TRUE is the calibration record is need in the + * firmware right now otherwise, FALSE + * @return TRUE if the records' data is valid otherwise FALSE + *************************************************************************/ +static BOOL isPolynomialRecordValid( POLYNOMIAL_CAL_PAYLOAD_T* record ) +{ + BOOL status = TRUE; + U16 calcCRC = crc16 ( (U08*)record, sizeof( POLYNOMIAL_CAL_PAYLOAD_T ) - sizeof( U16 ) ); + U16 recordCRC = record->crc; + + if ( calcCRC != recordCRC ) + { + benignPolynomialCalRecord( record ); + + // Set status to FALSE since the record is not valid + status = FALSE; + } + + return status; +} + +#ifndef _RELEASE_ +/*********************************************************************//** + * @brief + * The isSWConfigRecordValid function checks the validity of the software + * configuration record. + * @details Inputs: ddSWConfigGroup, hdSWConfigGroup + * @details Outputs: ddSWConfigGroup, hdSWConfigGroup + * @return TRUE if the DG system record is valid otherwise FALSE + *************************************************************************/ +static BOOL isSWConfigRecordValid( void ) +{ + BOOL status = TRUE; + + U16 calcCRC = crc16( (U08*)&ddSWConfigGroup, sizeof( DD_SW_CONFIG_GROUP_T ) - sizeof( U16 ) ); + U16 recordCRC = ddSWConfigGroup.crc; + + if ( calcCRC != recordCRC ) + { + memset( (U08*)&ddSWConfigGroup, 0, sizeof( DD_SW_CONFIG_GROUP_T ) ); + + // Recalculate the CRC with the default values + ddSWConfigGroup.crc = crc16( (U08*)&ddSWConfigGroup, sizeof( DD_SW_CONFIG_GROUP_T ) - sizeof( U16 ) ); + + // Set the to FALSE since the record is not valid + status = FALSE; + } + + return status; +} +#endif + +/*********************************************************************//** + * @brief + * The isDDSystemRecordValid function checks the validity of the DG system + * record. + * @details Inputs: ddSystemGroup.ddSystemRecord + * @details Outputs: none + * @return TRUE if the DG system record is valid otherwise FALSE + *************************************************************************/ +static BOOL isDDSystemRecordValid( void ) +{ + BOOL status = TRUE; + U16 calcCRC = crc16( (U08*)&ddSystemGroup.ddSystemRecord, sizeof( DD_SYSTEM_RECORD_T ) - sizeof( U16 ) ); + U16 recordCRC = ddSystemGroup.ddSystemRecord.crc; + + if ( calcCRC != recordCRC ) + { + // CRC did not pass so set all values to default + ddSystemGroup.ddSystemRecord.mfgDate = 0; + ddSystemGroup.ddSystemRecord.mfgLocation = 0; + memset( ddSystemGroup.ddSystemRecord.topLevelPN, RECORD_DEFAULT_CHARACTER, sizeof( ddSystemGroup.ddSystemRecord.topLevelPN ) ); + memset( ddSystemGroup.ddSystemRecord.topLevelSN, RECORD_DEFAULT_CHARACTER, sizeof( ddSystemGroup.ddSystemRecord.topLevelSN ) ); + + // Recalculate the CRC with the default values + ddSystemGroup.ddSystemRecord.crc = crc16 ( (U08*)&ddSystemGroup.ddSystemRecord, sizeof( DD_SYSTEM_RECORD_T ) - sizeof( U16 ) ); + ddSystemGroup.crc = crc16 ( (U08*)&ddSystemGroup, sizeof( DD_SYSTEM_GROUP_T ) - sizeof( U16 ) ); + status = FALSE; + +// activateAlarmNoData( ALARM_ID_DD_INVALID_SYSTEM_RECORD_CRC ); + } + else + { + if ( RECORD_DEFAULT_CHARACTER == ddSystemGroup.ddSystemRecord.topLevelSN[ 0 ] ) + { +// activateAlarmNoData( ALARM_ID_DD_INVALID_SERIAL_NUMBER ); + } + } + + return status; +} + +/*********************************************************************//** + * @brief + * The isDDServiceRecordValid function checks the validity of the DG service + * record. + * @details Inputs: ddServiceGroup.ddServiceRecord + * @details Outputs: none + * @return TRUE if the DG service record is valid otherwise FALSE + *************************************************************************/ +static BOOL isDDServiceRecordValid( void ) +{ + BOOL status = TRUE; + U16 calcCRC = crc16( (U08*)&ddServiceGroup.ddServiceRecord, sizeof( DD_SERVICE_RECORD_T ) - sizeof( U16 ) ); + U16 recordCRC = ddServiceGroup.ddServiceRecord.crc; + + if ( calcCRC != recordCRC ) + { + // CRC did not pass so set all values to default + ddServiceGroup.ddServiceRecord.lastServiceEpochDate = 0; + ddServiceGroup.ddServiceRecord.serviceIntervalSeconds = RECORD_DEFAULT_SERVICE_INTERVAL_S; + ddServiceGroup.ddServiceRecord.lastResetTimeEpoch = getRTCTimestamp(); + ddServiceGroup.ddServiceRecord.crc = crc16 ( (U08*)&ddServiceGroup.ddServiceRecord, sizeof( DD_SERVICE_RECORD_T ) - sizeof( U16 ) ); + ddServiceGroup.crc = crc16 ( (U08*)&ddServiceGroup, sizeof( DD_SERVICE_GROUP_T ) - sizeof( U16 ) ); + status = FALSE; + + // Service record failure is also considered as RTC RAM failure +// activateAlarmNoData( ALARM_ID_DD_INVALID_SERVICE_RECORD_CRC ); + } + + return status; +} + +/*********************************************************************//** + * @brief + * The isDDUsageRecordValid function checks whether the DG usage information + * is valid or not. + * @details Inputs: ddUsageInfoGroup + * @details Outputs: ddUsageInfoGroup + * @return TRUE if the DG usage record is valid otherwise FALSE + *************************************************************************/ +static BOOL isDDUsageRecordValid( void ) +{ + BOOL status = TRUE; + U16 calcCRC = crc16( (U08*)&ddUsageInfoGroup.ddUsageInfo, sizeof( DD_USAGE_INFO_RECORD_T ) - sizeof( U16 ) ); + U16 recordCRC = ddUsageInfoGroup.ddUsageInfo.crc; + + if ( calcCRC != recordCRC ) + { + ddUsageInfoGroup.ddUsageInfo.lastChemDisCompleteDateEpoch = 0; + ddUsageInfoGroup.ddUsageInfo.lastHeatDisCompleteDateEpoch = 0; + ddUsageInfoGroup.ddUsageInfo.roWaterGenSinceLastServiceL = 0.0F; + ddUsageInfoGroup.ddUsageInfo.roWaterGenTotalL = 0.0F; + ddUsageInfoGroup.ddUsageInfo.lastResetTimeEpoch = getRTCTimestamp(); + ddUsageInfoGroup.ddUsageInfo.crc = crc16( (U08*)&ddUsageInfoGroup.ddUsageInfo, sizeof( DD_USAGE_INFO_RECORD_T ) - sizeof( U16 ) ); + ddUsageInfoGroup.crc = crc16( (U08*)&ddUsageInfoGroup, sizeof( DD_USAGE_INFO_GROUP_T ) - sizeof( U16 ) ); + status = FALSE; + +// activateAlarmNoData( ALARM_ID_DD_INVALID_USAGE_RECORD_CRC ); + } + + return status; +} + +/*********************************************************************//** + * @brief + * The isDDCalibrationRecordValid function calls other functions to check + * the validity of DG calibration record. + * @details Inputs: ddCalibrationRecord + * @details Outputs: none + * @return TRUE if the DG calibration record is valid otherwise FALSE + *************************************************************************/ +static BOOL isDDCalibrationRecordValid( void ) +{ + U32 i; + POLYNOMIAL_CAL_PAYLOAD_T* record; + BOOL isHardwareRecordValid = TRUE; + BOOL isCalRecordValid = TRUE; + U16 recordCRC = crc16 ( (U08*)&ddCalibrationRecord, sizeof( DD_CALIBRATION_RECORD_T ) - sizeof( U16 ) ); + + // Create a benign polynomial calibration record. This record is used to + // clear the reserved calibration record. The reserved spaces are not used + // but this will prevent those records to be nan or a random number. + POLYNOMIAL_CAL_PAYLOAD_T tempRecord; + + benignPolynomialCalRecord( &tempRecord ); + + // Get the calibration record of the hardware (i.e. pressure sensor) + DD_PRES_SENSORS_CAL_RECORD_T* pressure = &ddCalibrationRecord.ddCalibrationGroups.presSensorsCalRecord; + // The ones that are an array, are looped through + for ( i = 0; i < NUM_OF_CAL_DATA_PRES_SENSORS; i++ ) + { + // Get calibration payload and assign it to a pointer + record = (POLYNOMIAL_CAL_PAYLOAD_T*)&pressure->pressureSensors[ i ]; + // Check in the validity of the calibration data + // If the variable is already FALSE, let it be FALSE. Even if one record is not + // valid, the values should be set to benign values. This variable is used to decide + // whether a write should be scheduled or not so it should not be overwritten with a TRUE + // once a record set it to FALSE + isHardwareRecordValid = isPolynomialRecordValid( record ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + } + + for ( i = 0; i < PRES_SENSORS_RESERVED_SPACE_COUNT; i++ ) + { + // Benign the pressures reserved spaces + memcpy( (POLYNOMIAL_CAL_PAYLOAD_T*)&pressure->reservedSpace[ i ], &tempRecord, sizeof( POLYNOMIAL_CAL_PAYLOAD_T ) ); + } + + DD_FLOW_SENSORS_CAL_RECORD_T* flow = &ddCalibrationRecord.ddCalibrationGroups.flowSensorsCalRecord; + for ( i = 0; i < NUM_OF_CAL_DATA_FLOW_SENSORS; i++ ) + { + record = (POLYNOMIAL_CAL_PAYLOAD_T*)&flow->flowSensors[ i ]; + isHardwareRecordValid = isPolynomialRecordValid( record ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + } + + for ( i = 0; i < FLOW_SENSROS_RESERVED_SPACE_COUNT; i++ ) + { + // Benign the the flow sensors reserved spaces + memcpy( (POLYNOMIAL_CAL_PAYLOAD_T*)&flow->reservedSpace[ i ], &tempRecord, sizeof( POLYNOMIAL_CAL_PAYLOAD_T ) ); + } + + DD_LOAD_CELLS_CAL_RECORD_T* load = &ddCalibrationRecord.ddCalibrationGroups.loadCellsCalRecord; + for ( i = 0; i < NUM_OF_CAL_DATA_LOAD_CELLS; i++ ) + { + record = (POLYNOMIAL_CAL_PAYLOAD_T*)&load->loadCells[ i ]; + isHardwareRecordValid = isPolynomialRecordValid( record ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + } + + DD_TEMP_SENSORS_CAL_RECORD_T* temperature = &ddCalibrationRecord.ddCalibrationGroups.tempSensorsCalRecord; + for ( i = 0; i < NUM_OF_CAL_DATA_TEMP_SENSORS; i++ ) + { + record = (POLYNOMIAL_CAL_PAYLOAD_T*)&temperature->tempSensors[ i ]; + isHardwareRecordValid = isPolynomialRecordValid( record ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + } + + for ( i = 0; i < TEMP_SENSORS_RESERVED_SPACE_COUNT; i++ ) + { + // Benign the temperature sensors reserved spaces + memcpy( (POLYNOMIAL_CAL_PAYLOAD_T*)&temperature->reservedSpace[ i ], &tempRecord, sizeof( POLYNOMIAL_CAL_PAYLOAD_T ) ); + } + + DD_CONC_PUMPS_CAL_RECORD_T* concPump = &ddCalibrationRecord.ddCalibrationGroups.concentratePumpsRecord; + for ( i = 0; i < NUM_OF_CAL_DATA_DD_CONC_PUMPS; i++ ) + { + record = &concPump->concentratePumps[ i ]; + isHardwareRecordValid = isPolynomialRecordValid( record ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + } + + DD_DRAIN_PUMP_CAL_RECORD_T* drainPump = &ddCalibrationRecord.ddCalibrationGroups.drainPumpRecord; + isHardwareRecordValid = isDDDrainPumpRecordValid( drainPump ) != TRUE ? FALSE : isCalRecordValid; + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + + DD_RO_PUMP_CAL_RECORD_T* roPump = &ddCalibrationRecord.ddCalibrationGroups.roPumpRecord; + isHardwareRecordValid = isDDROPumpRecordValid( roPump ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + + DD_PRE_RO_PURGE_VOLUME_T* preROPurgeVolume = &ddCalibrationRecord.ddCalibrationGroups.preROPurgeVolumeRecord; + isHardwareRecordValid = isDDPreROPurgeVolumeRecordValid( preROPurgeVolume ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + + DD_DRAIN_LINE_VOLUME_T* drainLineVol = &ddCalibrationRecord.ddCalibrationGroups.drainLineVolumeRecord; + isHardwareRecordValid = isDDDrainLineVolRecordValid( drainLineVol ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + + DD_RESERVOIR_VOLUME_RECORD_T* reservoirVol = &ddCalibrationRecord.ddCalibrationGroups.reservoirVolumesRecord; + for ( i = 0; i < NUM_OF_CAL_DATA_RSRVRS; i++ ) + { + isHardwareRecordValid = isDDReservoirVolRecordValid( &reservoirVol->reservoir[ i ] ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + } + + DD_GENERIC_VOLUME_RECORD_T* genericVol = &ddCalibrationRecord.ddCalibrationGroups.genericVolumeRecord; + for ( i = 0; i < GENERIC_VOL_RESERVED_SPACE_COUNT; i++ ) + { + isHardwareRecordValid = isDDGenericVolRecordValid( &genericVol->genericVolume[ i ] ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + } + + DD_ACID_CONCENTRATES_RECORD_T* acidConc = &ddCalibrationRecord.ddCalibrationGroups.acidConcentratesRecord; + for ( i = 0; i < NUM_OF_CAL_DATA_ACID_CONCENTRATES; i++ ) + { + isHardwareRecordValid = isDDAcidConcentrateRecordValid( &acidConc->acidConcentrate[ i ] ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + } + + DD_BICARB_CONCENTRATES_RECORD_T* bicarbConc = &ddCalibrationRecord.ddCalibrationGroups.bicarbConcentratesRecord; + for ( i = 0; i < NUM_OF_CAL_DATA_BICARB_CONCENTRATES; i++ ) + { + isHardwareRecordValid = isDDBicarbConcentrateRecordValid( &bicarbConc->bicarbConcentrate[ i ] ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + } + + DD_FILTER_CAL_RECORD_T* roFilter = &ddCalibrationRecord.ddCalibrationGroups.filtersRecord.roFilter; + isHardwareRecordValid = isDDFilterRecordValid( roFilter ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + + DD_FILTER_CAL_RECORD_T* ultraFilter = &ddCalibrationRecord.ddCalibrationGroups.filtersRecord.ultraFilter; + isHardwareRecordValid = isDDFilterRecordValid( ultraFilter ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + + DD_FILTER_CAL_RECORD_T* sedimentFilter = &ddCalibrationRecord.ddCalibrationGroups.filtersRecord.sedimentFilter; + isHardwareRecordValid = isDDFilterRecordValid( sedimentFilter ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + + DD_FILTER_CAL_RECORD_T* carbonFilter = &ddCalibrationRecord.ddCalibrationGroups.filtersRecord.carbonFilter; + isHardwareRecordValid = isDDFilterRecordValid( carbonFilter ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + + DD_FILTER_CAL_RECORD_T* carbonPolishFilter = &ddCalibrationRecord.ddCalibrationGroups.filtersRecord.carbonPolishFilter; + isHardwareRecordValid = isDDFilterRecordValid( carbonPolishFilter ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + + DD_FANS_CAL_RECORD_T* fan = &ddCalibrationRecord.ddCalibrationGroups.fansRecord; + for ( i = 0; i < NUM_OF_CAL_DATA_FANS; i++ ) + { + isHardwareRecordValid = isDDFanRecordValid( &fan->fans[ i ] ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + } + + DD_ACCEL_SENSOR_CAL_RECORD_T* accelerometer = &ddCalibrationRecord.ddCalibrationGroups.accelerometerSensorCalRecord; + isHardwareRecordValid = isDDAccelerometerSensorRecordValid( accelerometer ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + + DD_HEATING_CAL_RECORD_T* heating = &ddCalibrationRecord.ddCalibrationGroups.heatingCalRecord; + isHardwareRecordValid = isDDHeatingCalRecordValid( heating ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + + DD_FILL_COND_OPS_T j; + DD_CHEMICALS_FILL_COND_CAL_RECORD_T* acidBicarbTempRecord = &ddCalibrationRecord.ddCalibrationGroups.fillCondCalRecord; + for ( i = 0; i < NUM_OF_ACID_TYPE; i++ ) + { + for ( j = FILL_COND_NORMAL_OP; j < NUM_OF_FILL_COND_TEST; j++ ) + { + isHardwareRecordValid = isDDFillConductiviesRecordValid( &acidBicarbTempRecord->fillCondValues[ i ][ j ], (DD_ACID_TYPES_T)i, (DD_FILL_COND_OPS_T)j ); + isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; + } + } + + // If the sub groups failed, they are all updated to their benign values + // so the main CRC of the calibration group is calculated again + if ( ( FALSE == isCalRecordValid ) || ( recordCRC != ddCalibrationRecord.crc ) ) + { + isCalRecordValid = FALSE; + ddCalibrationRecord.crc = crc16 ( (U08*)&ddCalibrationRecord, sizeof( DD_CALIBRATION_RECORD_T ) - sizeof( U16 ) ); + +// activateAlarmNoData( ALARM_ID_DD_NVDATAMGMT_CAL_GROUP_RECORD_CRC_INVALID ); + } + + return isCalRecordValid; +} + +/*********************************************************************//** + * @brief + * The isDDDrainPumpRecordValid function checks whether the calibration + * record of drain pump is valid or not. + * @details Inputs: none + * @details Outputs: none + * @param record: DD_DRAIN_PUMP_CAL_RECORD_T pointer + * @return TRUE if the record is valid otherwise FALSE + *************************************************************************/ +static BOOL isDDDrainPumpRecordValid( DD_DRAIN_PUMP_CAL_RECORD_T* record ) +{ + BOOL status = TRUE; + U16 calcCRC = crc16 ( (U08*)record, sizeof(DD_DRAIN_PUMP_CAL_RECORD_T) - sizeof(U16) ); + U16 recordCRC = record->crc; + + if ( calcCRC != recordCRC ) + { + // CRC did not pass so set all values to default + record->stepSpeed2FlowRatio = RECORD_DEFAULT_RATIO; + record->voltage2SpeedRatio = RECORD_DEFAULT_RATIO; + record->calibrationTime = RECORD_DEFAULT_TIME; + record->crc = crc16 ( (U08*)record, sizeof(DD_DRAIN_PUMP_CAL_RECORD_T) - sizeof(U16) ); + + // Set the to FALSE since the record is not valid + status = FALSE; + } + + return status; +} + +/*********************************************************************//** + * @brief + * The isDDROPumpRecordValid function checks whether the calibration + * record of RO pump is valid or not. + * @details Inputs: none + * @details Outputs: none + * @param record: DD_RO_PUMP_CAL_RECORD_T pointer + * @return TRUE if the record is valid otherwise FALSE + *************************************************************************/ +static BOOL isDDROPumpRecordValid( DD_RO_PUMP_CAL_RECORD_T* record ) +{ + BOOL status = TRUE; + U16 calcCRC = crc16 ( (U08*)record, sizeof(DD_RO_PUMP_CAL_RECORD_T) - sizeof(U16) ); + U16 recordCRC = record->crc; + + if ( calcCRC != recordCRC ) + { + // CRC did not pass so set all values to default + record->gain1Ratio = RECORD_DEFAULT_RATIO; + record->gain2Ratio = RECORD_DEFAULT_RATIO; + record->gain3Ratio = RECORD_DEFAULT_RATIO; + record->calibrationTime = RECORD_DEFAULT_TIME; + record->crc = crc16 ( (U08*)record, sizeof(DD_RO_PUMP_CAL_RECORD_T) - sizeof(U16) ); + + // Set the to FALSE since the record is not valid + status = FALSE; + } + + return status; +} + +/*********************************************************************//** + * @brief + * The isDDPreROPurgeVolumeRecordValid function checks whether the + * calibration record of pre RO purge is valid or not. + * @details Inputs: none + * @details Outputs: none + * @param record: DD_PRE_RO_PURGE_VOLUME_T pointer + * @return TRUE if the record is valid otherwise FALSE + *************************************************************************/ +static BOOL isDDPreROPurgeVolumeRecordValid( DD_PRE_RO_PURGE_VOLUME_T* record ) +{ + BOOL status = TRUE; + U16 calcCRC = crc16 ( (U08*)record, sizeof(DD_PRE_RO_PURGE_VOLUME_T) - sizeof(U16) ); + U16 recordCRC = record->crc; + + if ( calcCRC != recordCRC ) + { + // CRC did not pass so set all values to default + record->pressure2FlowRatio = RECORD_DEFAULT_RATIO; + record->volume = RECORD_DEFAULT_CONST; + record->calibrationTime = RECORD_DEFAULT_TIME; + record->crc = crc16 ( (U08*)record, sizeof(DD_PRE_RO_PURGE_VOLUME_T) - sizeof(U16) ); + + // Set the to FALSE since the record is not valid + status = FALSE; + } + + return status; +} + +/*********************************************************************//** + * @brief + * The isDDDrainLineVolRecordValid function checks whether the calibration + * record of drain line volume is valid or not. + * @details Inputs: none + * @details Outputs: none + * @param record: DD_DRAIN_LINE_VOLUME_T pointer + * @return TRUE if the record is valid otherwise FALSE + *************************************************************************/ +static BOOL isDDDrainLineVolRecordValid( DD_DRAIN_LINE_VOLUME_T* record ) +{ + BOOL status = TRUE; + U16 calcCRC = crc16 ( (U08*)record, sizeof(DD_DRAIN_LINE_VOLUME_T) - sizeof(U16) ); + U16 recordCRC = record->crc; + + if ( calcCRC != recordCRC ) + { + // CRC did not pass so set all values to default + record->volume = DEFAULT_FLUSH_LINES_VOLUME_L; + record->calibrationTime = RECORD_DEFAULT_TIME; + record->crc = crc16 ( (U08*)record, sizeof(DD_DRAIN_LINE_VOLUME_T) - sizeof(U16) ); + + // Set the to FALSE since the record is not valid + status = FALSE; + } + + return status; +} + +/*********************************************************************//** + * @brief + * The isDDReservoirVolRecordValid function checks whether the calibration + * record of reservoir volume is valid or not. + * @details Inputs: none + * @details Outputs: none + * @param record: DD_RESERVOIR_VOLUME_DATA_T pointer + * @return TRUE if the record is valid otherwise FALSE + *************************************************************************/ +static BOOL isDDReservoirVolRecordValid( DD_RESERVOIR_VOLUME_DATA_T* record ) +{ + BOOL status = TRUE; + U16 calcCRC = crc16 ( (U08*)record, sizeof(DD_RESERVOIR_VOLUME_DATA_T) - sizeof(U16) ); + U16 recordCRC = record->crc; + + if ( calcCRC != recordCRC ) + { + // CRC did not pass so set all values to default + record->maxResidualFluid = RECORD_DEFAULT_CONST; + record->normalFillVolume = RECORD_DEFAULT_CONST; + record->rsrvrUnfilledWeight = EMPTY_RESERVOIR_WEIGHT_GRAMS; + record->rsrvrVolume = RECORD_DEFAULT_CONST; + record->calibrationTime = RECORD_DEFAULT_TIME; + record->crc = crc16 ( (U08*)record, sizeof(DD_RESERVOIR_VOLUME_DATA_T) - sizeof(U16) ); + + // Set the to FALSE since the record is not valid + status = FALSE; + } + + return status; +} + +/*********************************************************************//** + * @brief + * The isDDGenericVolRecordValid function checks whether the calibration + * record of generic volume is valid or not. + * @details Inputs: none + * @details Outputs: none + * @param record: DD_GENERIC_VOLUME_DATA_T pointer + * @return TRUE if the record is valid otherwise FALSE + *************************************************************************/ +static BOOL isDDGenericVolRecordValid( DD_GENERIC_VOLUME_DATA_T* record ) +{ + BOOL status = TRUE; + U16 calcCRC = crc16 ( (U08*)record, sizeof( DD_GENERIC_VOLUME_DATA_T ) - sizeof( U16 ) ); + U16 recordCRC = record->crc; + + if ( calcCRC != recordCRC ) + { + // CRC did not pass so set all values to default + record->volume = RECORD_DEFAULT_CONST; + record->calibrationTime = RECORD_DEFAULT_TIME; + record->crc = crc16 ( (U08*)record, sizeof( DD_GENERIC_VOLUME_DATA_T ) - sizeof( U16 ) ); + status = FALSE; + } + + return status; +} + +/*********************************************************************//** + * @brief + * The isDDAcidConcentrateRecordValid function checks whether the calibration + * record of acid concentrate is valid or not. + * @details Inputs: none + * @details Outputs: none + * @param record: DD_ACID_CONCENTRATE_T pointer + * @return TRUE if the record is valid otherwise FALSE + *************************************************************************/ +static BOOL isDDAcidConcentrateRecordValid( DD_ACID_CONCENTRATE_T* record ) +{ + BOOL status = TRUE; + U16 calcCRC = crc16 ( (U08*)record, sizeof( DD_ACID_CONCENTRATE_T ) - sizeof( U16 ) ); + U16 recordCRC = record->crc; + + if ( calcCRC != recordCRC ) + { + // CRC did not pass so set all values to default + record->acidFullBottleVolumeML = DEFAULT_ACID_BOTTLE_VOL_ML; + record->acidConductivityUSPerCM = DEFAULT_ACID_COND_US_PER_CM; + record->acidBottleTemperature = DEFAULT_ACID_BOTTLE_TEMP_C; + record->acidConcMixRatio = DEFAULT_ACID_CONC_MIXING_RATIO; + record->calibrationTime = RECORD_DEFAULT_TIME; + record->crc = crc16 ( (U08*)record, sizeof( DD_ACID_CONCENTRATE_T ) - sizeof( U16 ) ); + status = FALSE; + } + + return status; +} + +/*********************************************************************//** + * @brief + * The isDDBicarbConcentrateRecordValid function checks whether the + * calibration record of bicarb concentrate is valid or not. + * @details Inputs: none + * @details Outputs: none + * @param record: DD_BICARB_CONCENTRATE_T pointer + * @return TRUE if the record is valid otherwise FALSE + *************************************************************************/ +static BOOL isDDBicarbConcentrateRecordValid( DD_BICARB_CONCENTRATE_T* record ) +{ + BOOL status = TRUE; + U16 calcCRC = crc16 ( (U08*)record, sizeof( DD_BICARB_CONCENTRATE_T ) - sizeof( U16 ) ); + U16 recordCRC = record->crc; + + if ( calcCRC != recordCRC ) + { + // CRC did not pass so set all values to default + record->bicarbStartVolumeML = DEFAULT_BICARB_BOTTLE_VOL_ML; + record->bicarbConductivityUSPerCM = DEFAULT_BICARB_COND_US_PER_CM; + record->bicarbBottleTemperature = DEFAULT_BICARB_BOTTLE_TEMP_C; + record->bicarbConcMixRatio = DEFAULT_BICARB_CONC_MIXING_RATIO; + record->calibrationTime = RECORD_DEFAULT_TIME; + record->crc = crc16 ( (U08*)record, sizeof( DD_BICARB_CONCENTRATE_T ) - sizeof( U16 ) ); + status = FALSE; + } + + return status; +} + +/*********************************************************************//** + * @brief + * The isDDFilterRecordValid function checks whether the calibration + * record of filter is valid or not. + * @details Inputs: none + * @details Outputs: none + * @param record: DD_FILTER_CAL_RECORD_T pointer + * @return TRUE if the record is valid otherwise FALSE + *************************************************************************/ +static BOOL isDDFilterRecordValid( DD_FILTER_CAL_RECORD_T* record ) +{ + BOOL status = TRUE; + U16 calcCRC = crc16 ( (U08*)record, sizeof(DD_FILTER_CAL_RECORD_T) - sizeof(U16) ); + U16 recordCRC = record->crc; + + if ( calcCRC != recordCRC ) + { + // CRC did not pass so set all values to default + record->reservedSpace1 = RECORD_DEFAULT_CONST; + record->reservedSpace2 = RECORD_DEFAULT_CONST; + record->reservedSpace3 = RECORD_DEFAULT_CONST; + record->calibrationTime = RECORD_DEFAULT_TIME; + record->crc = crc16 ( (U08*)record, sizeof(DD_FILTER_CAL_RECORD_T) - sizeof(U16) ); + + // Set the to FALSE since the record is not valid + status = FALSE; + } + + return status; +} + +/*********************************************************************//** + * @brief + * The isDDFanRecordValid function checks whether the calibration record + * of fan is valid or not. + * @details Inputs: none + * @details Outputs: none + * @param record: DD_FAN_CAL_RECORD_T pointer + * @return TRUE if the record is valid otherwise FALSE + *************************************************************************/ +static BOOL isDDFanRecordValid( DD_FAN_CAL_RECORD_T* record ) +{ + BOOL status = TRUE; + U16 calcCRC = crc16 ( (U08*)record, sizeof(DD_FAN_CAL_RECORD_T) - sizeof(U16) ); + U16 recordCRC = record->crc; + + if ( calcCRC != recordCRC ) + { + // CRC did not pass so set all values to default + record->reservedSpace1 = RECORD_DEFAULT_CONST; + record->reservedSpace2 = RECORD_DEFAULT_CONST; + record->calibrationTime = RECORD_DEFAULT_TIME; + record->crc = crc16 ( (U08*)record, sizeof(DD_FAN_CAL_RECORD_T) - sizeof(U16) ); + + // Set the to FALSE since the record is not valid + status = FALSE; + } + + return status; +} + +/*********************************************************************//** + * @brief + * The isDDAccelerometerSensorRecordValid function checks whether the + * calibration record of accelerometer sensor is valid or not. + * @details Inputs: none + * @details Outputs: none + * @param record: DD_ACCELEROMETER_SENSOR_CAL_RECORD_T pointer + * @return TRUE if the record is valid otherwise FALSE + *************************************************************************/ +static BOOL isDDAccelerometerSensorRecordValid( DD_ACCEL_SENSOR_CAL_RECORD_T* record ) +{ + BOOL status = TRUE; + U16 calcCRC = crc16 ( (U08*)record, sizeof(DD_ACCEL_SENSOR_CAL_RECORD_T) - sizeof(U16) ); + U16 recordCRC = record->crc; + + if ( calcCRC != recordCRC ) + { + // CRC did not pass so set all values to default + record->accelXOffset = RECORD_DEFAULT_OFFSET; + record->accelYOffset = RECORD_DEFAULT_OFFSET; + record->accelZOffset = RECORD_DEFAULT_OFFSET; + record->calibrationTime = RECORD_DEFAULT_TIME; + record->crc = crc16 ( (U08*)record, sizeof(DD_ACCEL_SENSOR_CAL_RECORD_T) - sizeof(U16) ); + + // Set the to FALSE since the record is not valid + status = FALSE; + } + + return status; +} + +/*********************************************************************//** + * @brief + * The isDDHeatingCalRecordValid function checks whether the calibration record + * of heating parameters is valid or not. + * @details Inputs: none + * @details Outputs: none + * @param record: DD_HEATING_CAL_RECORD_T pointer + * @return TRUE if the record is valid otherwise FALSE + *************************************************************************/ +static BOOL isDDHeatingCalRecordValid( DD_HEATING_CAL_RECORD_T* record ) +{ + BOOL status = TRUE; + U16 calcCRC = crc16 ( (U08*)record, sizeof(DD_HEATING_CAL_RECORD_T) - sizeof(U16) ); + U16 recordCRC = record->crc; + + if ( calcCRC != recordCRC ) + { + // CRC did not pass so set all values to default + record->reservoirTempTauCPerMin = DEFAULT_RSRVR_TEMP_TAU_C_PER_MIN; + record->ultrafilterTempTauCPerMin = DEFAULT_UF_TEMP_TAU_C_PER_MIN; + record->ultrafilterVolmL = DEFAULT_UF_VOLUME_ML; + record->calibrationTime = RECORD_DEFAULT_TIME; + record->crc = crc16 ( (U08*)record, sizeof(DD_HEATING_CAL_RECORD_T) - sizeof(U16) ); + + // Set the to FALSE since the record is not valid + status = FALSE; + } + + return status; +} + +/*********************************************************************//** + * @brief + * The setLastDisinfectDate sets a queue job to write the last time that DG + * was disinfected. + * @details Inputs: dgUsageInfoGroup + * @details Outputs: dgUsageInfoGroup + * @param disinfect type (i.e chemical) + * @param epochTime last disinfect time in epoch + * @return TRUE if queue is not full + *************************************************************************/ +BOOL setLastDisinfectDate( DG_USAGE_INFO_ITEMS_T disinfect, U32 epochTime ) +{ + BOOL status = FALSE; + + if ( FALSE == isQueueFull() ) + { + switch ( disinfect ) + { + case USAGE_INFO_BASIC_FLUSH: + dgUsageInfoGroup.dgUsageInfo.lastBasicFlushCompleteDateEpoch = epochTime; + break; + + case USAGE_INFO_CHEM_DIS_START: + dgUsageInfoGroup.dgUsageInfo.lastChemDisStartDateEpoch = epochTime; + break; + + case USAGE_INFO_CHEM_DIS: + dgUsageInfoGroup.dgUsageInfo.lastChemDisCompleteDateEpoch = epochTime; + break; + + case USAGE_INFO_CHEM_FLUSH: + dgUsageInfoGroup.dgUsageInfo.lastChemDisFlushCompleteDateEpoch = epochTime; + break; + + case USAGE_INFO_HEAT_DIS: + dgUsageInfoGroup.dgUsageInfo.lastHeatDisCompleteDateEpoch = epochTime; + break; + + case USAGE_INFO_FILTER_FLUSH: + dgUsageInfoGroup.dgUsageInfo.lastFilterFlushCompleteDateEpoch = epochTime; + break; + + case USAGE_INFO_HEAT_DIS_ACTIVE_COOL: + dgUsageInfoGroup.dgUsageInfo.lastHeatActiveCoolCompleteDateEpoch = epochTime; + break; + + default: +// SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_USAGE_INFO_SELECTED, disinfect ); + break; + } + + dgUsageInfoGroup.dgUsageInfo.crc = crc16( (U08*)&dgUsageInfoGroup.dgUsageInfo, sizeof( DG_USAGE_INFO_RECORD_T ) - sizeof( U16 ) ); + dgUsageInfoGroup.crc = crc16( (U08*)&dgUsageInfoGroup, sizeof( DG_USAGE_INFO_GROUP_T ) - sizeof( U16 ) ); + usageWriteTries = 0; + status = TRUE; + + enqueueRecordJob( NVDATAMGMT_WRITE, NVDATAMGMT_USAGE_INFO_RECORD ); + SEND_EVENT_WITH_2_U32_DATA( RECORDS_SPECS[ NVDATAMGMT_USAGE_INFO_RECORD ].nvEvent, 0, 0 ) + } + else if ( ++usageWriteTries > MAX_NUM_OF_WRITE_TRIES ) + { +// SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_WRITE_USAGE_INFO_TO_NV_FAILURE, disinfect ); + } + + return status; +} + +/*********************************************************************//** + * @brief + * The setServiceTime function sets the latest service time in epoch and updates + * the usage information after the service. + * @details Inputs: none + * @details Outputs: hdServiceGroup, hdUsageInfoGroup + * @return TRUE if the queue has sufficient space to enqueue all the changed + * records + *************************************************************************/ +BOOL setServiceTime( void ) +{ + BOOL status = FALSE; + + if ( getAvailableRecordQueueCount() >= ( MIN_JOBS_NEEDED_FOR_SECTOR_0 + 1 ) ) + { + // When the service record is changed, all the sector 0 must be re-written plus the stack's usage information must be updated. + // Therefore, at least 4 queues are needed to be able to update all the records. The usage records are changed: + // In HD the treatment time since last service is reset to 0 + // In DG the RO water generation since the last service in reset to 0 + + ddServiceGroup.ddServiceRecord.lastServiceEpochDate = getRTCTimestamp(); + ddServiceGroup.ddServiceRecord.crc = crc16( (U08*)&ddServiceGroup.ddServiceRecord, sizeof( DD_SERVICE_RECORD_T ) - sizeof( U16 ) ); + ddServiceGroup.crc = crc16( (U08*)&ddServiceGroup, sizeof( DD_SERVICE_GROUP_T ) - sizeof( U16 ) ); + // Update the DG usage info + ddUsageInfoGroup.ddUsageInfo.roWaterGenSinceLastServiceL = 0; + ddUsageInfoGroup.ddUsageInfo.crc = crc16( (U08*)&ddUsageInfoGroup.ddUsageInfo, sizeof( DD_USAGE_INFO_RECORD_T ) - sizeof( U16 ) ); + ddUsageInfoGroup.crc = crc16( (U08*)&ddUsageInfoGroup, sizeof( DD_USAGE_INFO_GROUP_T ) - sizeof( U16 ) ); + + // Service record has been touched so the entire sector 0 of the EEPROM must be rewritten + enqueueSector0Records(); + enqueueRecordJob( NVDATAMGMT_WRITE, NVDATAMGMT_USAGE_INFO_RECORD ); + // Both the usage and service records have been updated + SEND_EVENT_WITH_2_U32_DATA( RECORDS_SPECS[ NVDATAMGMT_USAGE_INFO_RECORD ].nvEvent, 0, 0 ) + SEND_EVENT_WITH_2_U32_DATA( RECORDS_SPECS[ NVDATAMGMT_SERVICE_RECORD ].nvEvent, 0, 0 ) + + status = TRUE; + } + + return status; +} Index: firmware/App/Services/NVRecordsDD.h =================================================================== diff -u --- firmware/App/Services/NVRecordsDD.h (revision 0) +++ firmware/App/Services/NVRecordsDD.h (revision 1e809a81cb8831f5aa5938bd333cc374920d5b87) @@ -0,0 +1,502 @@ +/************************************************************************** +* +* Copyright (c) 2026-2027 Diality Inc. - All Rights Reserved. +* +* THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +* WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +* +* @file NVRecordsDD.h +* +* @author (original) Arpita Srivastava +* @date (original) 31-Mar-2026 +* +***************************************************************************/ + +#ifndef _NV_RECORDS_DD_H_ +#define _NV_RECORDS_DD_H_ + +#include "Common.h" +#include "DDDefs.h" + +// ********** public definitions ********** + +#define MAX_TOP_LEVEL_PN_CHARS 10U ///< Max number of characters for top level part number. +#define MAX_TOP_LEVEL_SN_CHARS 20U ///< Max number of characters for top level serial number. +#define PRES_SENSORS_RESERVED_SPACE_COUNT 5 ///< Pressure sensors reserved space count. +#define FLOW_SENSROS_RESERVED_SPACE_COUNT 2 ///< Flow sensors reserved space count. +#define TEMP_SENSORS_RESERVED_SPACE_COUNT 2 ///< Temperature sensors reserved space count. +#define GENERIC_VOL_RESERVED_SPACE_COUNT 4 ///< Generic volumes reserved space count. + + +#define MAX_EEPROM_WRITE_BUFFER_BYTES 16U ///< Max allowed bytes for an EEPROM write (16 bytes). + +#ifndef _RELEASE_ +/// Software configuration enums +typedef enum software_configurations +{ + SW_CONFIG_DISABLE_TRIMMER_HEATER = 0, ///< Software configuration disable trimmer heater. + SW_CONFIG_DISABLE_ACCELS, ///< Software configuration disable accelerometers. + SW_CONFIG_DISABLE_CAL_CHECK, ///< Software configuration disable calibration check. + SW_CONFIG_DISABLE_HEATERS_MONITOR, ///< Software configuration disable heaters monitor. + SW_CONFIG_DISABLE_DRAIN_PUMP_MONITOR, ///< Software configuration disable drain pump monitor. + SW_CONFIG_DISABLE_RO_PUMP_MONITOR, ///< Software configuration disable RO pump monitor. + SW_CONFIG_DISABLE_RO_RATIO_CHECK, // TODO remove ///< Software configuration disable RO ratio check + SW_CONFIG_DISABLE_COND_SENSOR_CHECK, ///< Software configuration disable conductivity sensor check. + SW_CONFIG_DISABLE_WATER_QUALITY_CHECK, ///< Software configuration disable water quality check. + SW_CONFIG_DISABLE_FLOW_VS_LOAD_CELL_CHECK_IN_FILL, ///< Software configuration disable flow vs. load cell check in fill. + SW_CONFIG_DISABLE_HEATERS_EFFICIENCY, ///< Software configuration disable heaters efficiency. + SW_CONFIG_DISABLE_DISINFECT_CONDUCTIVITY_CHECK, ///< Software configuration disable disinfect conductivity check. + SW_CONFIG_DISABLE_CONC_PUMPS, ///< Software configuration disable concentrate pumps. + SW_CONFIG_DISABLE_CAPS_MONITOR, ///< Software configuration disable cap monitor. + SW_CONFIG_DISABLE_UV_REACTORS, ///< Software configuration disable UV reactors. + SW_CONFIG_DISABLE_VOLTAGES_MONITOR, ///< Software configuration disable voltages monitor. + SW_CONFIG_DISABLE_BICARB_ALARMS, ///< Software configuration disable bicarb alarms. + SW_CONFIG_DISABLE_EMPTY_BOTTLES_ALARM, ///< Software configuration disable empty bottle alarms. + SW_CONFIG_DISABLE_BICARB_CONDUCTIVITY_TEST, // TODO remove ///< Software configuration disable bicarb conductivity test. + SW_CONFIG_DISABLE_ACID_CONDUCTIVITY_TEST, // TODO remove ///< Software configuration disable acid conductivity test. + SW_CONFIG_DISABLE_CONC_PUMPS_SPEED_ALARM, ///< Software configuration disable concentrate pumps speed alarm. + SW_CONFIG_DISABLE_MIXING_IN_FILL, // TODO remove ///< Software configuration disable mixing in fill. + SW_CONFIG_DISABLE_TEMPERATURE_SENSORS_ALARM, ///< Software configuration disable temperature sensors alarm. + SW_CONFIG_DISABLE_CONCENTRATE_PUMPS_PARK, ///< Software configuration disable concentrate pumps park feature. + SW_CONFIG_ENABLE_USING_TPO_FOR_PRIMARY_HEATER_CONTROL, ///< Software configuration enable TPo for primary heater control. + NUM_OF_SW_CONFIGS ///< Number of software configurations. +} SOFTWARE_CONFIG_T; +#endif + +/// DG available NV data to get +typedef enum DD_nv_commands +{ + GET_CAL_PRESSURE_SENOSRS = 0, ///< Get pressure sensors calibration data. + GET_CAL_LOAD_CELL_SENSORS, ///< Get load cell sensors calibration data. + GET_CAL_FLOW_SENSORS, ///< Get flow sensors calibration data. + GET_CAL_ACID_CONCENTREATES, ///< Get acid concentrates calibration data. + GET_CAL_BICARB_CONCENTRATES, ///< Get bicarb concentrates calibration data. + GET_CAL_ACCEL_SENSORS, ///< Get accelerometers calibration data. + GET_CAL_CONDUCTIVITY_SENSORS, ///< Get conductivity sensors calibration data. + GET_CAL_CONDUCTIVITY_SENSORS_TEMP_COMP, ///< Get conductivity sensors temperature compensation calibration data. + GET_CAL_TEMP_SENSORS, ///< Get temperature sensors calibration data. + GET_CAL_RSRVRS_VOL_RECORD, ///< Get reservoirs volume record. + GET_CAL_HEATING_RECORD, ///< Get heating calibration record. + GET_CAL_DRAIN_LINE_VOLUME_RECORD, ///< Get drain line volume record. + GET_CAL_RO_PUMP_RECORD, ///< Get RO pump calibration record. + GET_CAL_CONCENTRATE_PUMPS_RECORD, ///< Get concentrate pumps calibration record. + GET_CAL_DRAIN_PUMP_RECORD, ///< Get drain pump calibration record. + GET_CAL_FANS_RECORD, ///< Get fans calibration record. + GET_CAL_PRE_RO_PURGE_VOLUME_RECORD, ///< Get pre RO purge volume record. + GET_CAL_FILTERS_RECORD, ///< Get filters record. + GET_CAL_FILL_CONDUCTIVITIES_RECORD, ///< Get fill conductivities record. + GET_INF_HEATERS_RECORD, ///< Get heaters information form the the last run. + GET_SYS_RECORD, ///< Get system record. + GET_SRV_RECORD, ///< Get service record. + GET_SRR_RECORD, ///< Get scheduled runs record. + GET_USAGE_RECORD, ///< Get usage record. + NUM_OF_NV_DD_DATA ///< Number of non-volatile data. +} NV_DATA_T; + +/// DG pressure sensors enumeration. +typedef enum DD_pressure_sensors +{ + CAL_DATA_RO_PUMP_INLET_PRES_SENSOR = 0, ///< DG calibration data RO pump inlet pressure sensor. + CAL_DATA_RO_PUMP_OUTLET_PRES_SENSOR, ///< DG calibration data RO pump outlet pressure sensor. + CAL_DATA_DRAIN_PUMP_INLET_PRES_SENSOR, ///< DG calibration data drain pump inlet pressure sensor. + CAL_DATA_DRAIN_PUMP_OUTLET_PRES_SENSOR, ///< DG calibration data drain pump outlet pressure sensor. + CAL_DATA_BAROMETRIC_PRES_SENSOR, ///< DG calibration data barometric pressure sensor. + NUM_OF_CAL_DATA_PRES_SENSORS, ///< Number of calibration data pressure sensors. +} CAL_DATA_DD_PRES_SENSORS_T; + +/// DG flow sensors enumeration. +typedef enum DD_flow_sensors +{ + CAL_DATA_RO_PUMP_FLOW_SENSOR = 0, ///< RO pump flow sensor. + CAL_DATA_DIALYSATE_FLOW_SENSOR, ///< RO pump dialysate flow sensor. + NUM_OF_CAL_DATA_FLOW_SENSORS, ///< Number of flow sensors. +} CAL_DATA_DD_FLOW_SENSORS_T; + +/// DG load cells enumeration. +typedef enum DD_load_cells +{ + CAL_DATA_LOAD_CELL_A1 = 0, ///< Load cell A1. + CAL_DATA_LOAD_CELL_A2, ///< Load cell A2. + CAL_DATA_LOAD_CELL_B1, ///< Load cell B1. + CAL_DATA_LOAD_CELL_B2, ///< Load cell B2. + NUM_OF_CAL_DATA_LOAD_CELLS, ///< Number of load cells. +} CAL_DATA_DD_LOAD_CELLS_T; + +/// DG temperature sensors enumeration. +typedef enum DD_temperature_sensors +{ + CAL_DATA_FPGA_BOARD_TEMP = 0, ///< FPGA board temperature sensor. + CAL_DATA_LOAD_CELL_A1_B1_TEMP, ///< Load cell A1/B1 temperature sensor. + CAL_DATA_LOAD_CELL_A2_B2_TEMP, ///< Load cell A2/B2 temperature sensor. + CAL_DATA_INTERNAL_THDO_RTD_TEMP, ///< Internal THDO RTC temperature sensor. + CAL_DATA_INTERNAL_TDI_RTD_TEMP, ///< Internal TDI RTD temperature sensor. + CAL_DATA_INTERNAL_COND_SENSOR_TEMP, ///< Internal conductivity sensor temperature sensor. + CAL_DATA_THERMISTOR_DD_ONBOARD_NTC_TEMP, ///< DG onboard NTC thermistor. + CAL_DATA_THERMISTOR_POWER_SUPPLY_1_TEMP, ///< Power supply 1 thermistor. + CAL_DATA_THERMISTOR_POWER_SUPPLY_2_TEMP, ///< Power supply 2 thermistor. + CAL_DATA_OUTLET_REDUNDANT_TEMP, ///< Outlet redundant temperature sensor. + CAL_DATA_INLET_DIALYSATE_TEMP, ///< Inlet dialysate temperature sensor. + CAL_DATA_INLET_PRIMARY_HEATER_TEMP, ///< Inlet primary heater temperature sensor. + CAL_DATA_OUTLET_PRIMARY_HEATER_TEMP, ///< Outlet primary heater temperature sensor. + CAL_DATA_COND_SENSOR_1_TEMP, ///< Conductivity sensor 1 temperature sensor. + CAL_DATA_COND_SENSOR_2_TEMP, ///< Conductivity sensor 2 temperature sensor. + CAL_DATA_HEAT_DISINFECT_TEMP, ///< Heat disinfect temperature sensor. + CAL_DATA_INTERNAL_THD_TEMP, ///< Heat disinfect internal ADC temperature sensor. + CAL_DATA_BARMOTERIC_TEMP, ///< Barometric temperature sensor. + NUM_OF_CAL_DATA_TEMP_SENSORS, ///< Number of temperature sensors. +} CAL_DATA_DD_TEMP_SENSORS_T; + +/// DG pumps enumeration. +typedef enum DD_pumps +{ + CAL_DATA_CONC_PUMP_CP1_ACID = 0, ///< Concentrate pump 1. + CAL_DATA_CONC_PUMP_CP2_BICARB, ///< Concentrate pump 2. + NUM_OF_CAL_DATA_DD_CONC_PUMPS ///< Number of DG concentrate pumps. +} CAL_DATA_DD_PUMPS_T; + +/// DG reservoirs enumeration. +typedef enum DD_reservoirs +{ + CAL_DATA_RSRVR_1 = 0, ///< Reservoir 1. + CAL_DATA_RSRVR_2, ///< Reservoir 2. + NUM_OF_CAL_DATA_RSRVRS ///< Number of reservoirs. +} CAL_DATA_DD_RESERVOIRS_T; + +/// DG acid concentrate enumeration. +typedef enum DD_acid_concentrate +{ + CAL_DATA_ACID_CONCENTRATE_1 = 0, ///< Acid concentrate 1. + CAL_DATA_ACID_CONCENTRATE_2, ///< Acid concentrate 2. + CAL_DATA_ACID_CONCENTRATE_3, ///< Acid concentrate 3. + CAL_DATA_ACID_CONCENTRATE_4, ///< Acid concentrate 4. + CAL_DATA_ACID_CONCENTRATE_5, ///< Acid concentrate 5. + NUM_OF_CAL_DATA_ACID_CONCENTRATES ///< Number of acid concentrates. +} CAL_DATA_DD_ACID_CONCENTRATES_T; + +/// DG bicarb concentrate enumeration. +typedef enum DD_bicarb_concentrate +{ + CAL_DATA_BICARB_CONCENTRATE_1 = 0, ///< Bicarb concentrate 1. + CAL_DATA_BICARB_CONCENTRATE_2, ///< Bicarb concentrate 2. + CAL_DATA_BICARB_CONCENTRATE_3, ///< Bicarb concentrate 3. + CAL_DATA_BICARB_CONCENTRATE_4, ///< Bicarb concentrate 4. + CAL_DATA_BICARB_CONCENTRATE_5, ///< Bicarb concentrate 5. + NUM_OF_CAL_DATA_BICARB_CONCENTRATES ///< Number of bicarb concentrates. +} CAL_DATA_DD_BICARB_CONCENTRATES_T; + +/// DG fans enumeration. +typedef enum DD_fans +{ + CAL_DATA_INLET_FAN_1 = 0, ///< Inlet fan 1. + CAL_DATA_INLET_FAN_2, ///< Inlet fan 2. + CAL_DATA_INLET_FAN_3, ///< Inlet fan 3. + CAL_DATA_OUTLET_FAN_1, ///< Outlet fan 1. + CAL_DATA_OUTLET_FAN_2, ///< Outlet fan 2. + CAL_DATA_OUTLET_FAN_3, ///< Outlet fan 3. + NUM_OF_CAL_DATA_FANS ///< Number of fans. +} CAL_DATA_DD_FANS_T; + +/// DG scheduled runs enumeration. +typedef enum DD_scheduled_runs +{ + RUN_FLUSH = 0, ///< Flush run. + RUN_HEAT_DISINFECT, ///< Heat disinfect run. + RUN_CHEMICAL_DISINFECT, ///< Chemical disinfect run. + NUM_OF_DD_SCHEDULED_RUNS ///< Number of DG scheduled runs. +} SCHEDULED_DD_RUNS_T; + +/// DG usage info items +typedef enum DD_usage_items +{ + USAGE_INFO_RO_GEN_WATER = 0, ///< Usage info RO generated water. + USAGE_INFO_BASIC_FLUSH, ///< Usage info basic flush. + USAGE_INFO_CHEM_DIS_START, ///< Usage info chemcial disinfect start. + USAGE_INFO_CHEM_DIS, ///< Usage info chemical disinfect complete. + USAGE_INFO_CHEM_FLUSH, ///< Usage info chemical flush complete. + USAGE_INFO_HEAT_DIS, ///< Usage info heat disinfect complete. + USAGE_INFO_FILTER_FLUSH, ///< Usage info filter flush complete. + USAGE_INFO_HEAT_DIS_ACTIVE_COOL, ///< Usage info heat disinfect active cool complete. + NUM_OF_USAGE_INFO_ITEMS ///< Number of usage info items. +} DD_USAGE_INFO_ITEMS_T; + +/// DG fill conductivity tests +typedef enum DD_conductivity_ops +{ + FILL_COND_NORMAL_OP = 0, ///< Fill conductivity normal operation. + FILL_COND_ACID_TEST, ///< Fill conductivity acid test. + FILL_COND_BICARB_TEST, ///< Fill conductivity bicarb test. + NUM_OF_FILL_COND_TEST ///< Number of fill conductivity test. +} DD_FILL_COND_OPS_T; + +#pragma pack(push, 1) +/// Polynomial calibration structure +typedef struct +{ + F32 fourthOrderCoeff; ///< Fourth order coefficient. + F32 thirdOrderCoeff; ///< Third order coefficient. + F32 secondOrderCoeff; ///< Second order coefficient. + F32 gain; ///< Gain. + F32 offset; ///< Offset. + U32 calibrationTime; ///< Calibration time. + U16 crc; ///< CRC for the polynomial calibration payload. +} POLYNOMIAL_CAL_PAYLOAD_T; + +/// DG calibration sensor temperature compensation calibration structure +typedef struct +{ + F32 gain; ///< Gain. + F32 coefficient; ///< Coefficient. + F32 offset; ///< Offset. + U32 calibrationTime; ///< Calibration time. + U16 crc; ///< CRC for the conductivity sensors temperature compensation cal payload. +} DD_COND_SENSOR_TEMP_COMP_CAL_PAYLOAD_T; + +/// DG concentrate pumps calibration record +typedef struct +{ + POLYNOMIAL_CAL_PAYLOAD_T concentratePumps[ NUM_OF_CAL_DATA_DD_CONC_PUMPS ]; ///< DG concentrate pumps calibration data. +} DD_CONC_PUMPS_CAL_RECORD_T; + +/// DG drain pump two-gain payload +typedef struct +{ + F32 voltage2SpeedRatio; ///< Voltage to speed ratio. + F32 stepSpeed2FlowRatio; ///< Step speed to flow ratio. + U32 calibrationTime; ///< Calibration time. + U16 crc; ///< CRC for the drain pump structure. +} DD_DRAIN_PUMP_CAL_RECORD_T; + +/// DG RO pump calibration structure. +typedef struct +{ + F32 gain1Ratio; ///< RO pump gain1 ratio. + F32 gain2Ratio; ///< RO pump gain2 ratio. + F32 gain3Ratio; ///< RO pump gain3 ratio. + U32 calibrationTime; ///< Calibration time. + U16 crc; ///< CRC for the DG RO pump calibration structure. +} DD_RO_PUMP_CAL_RECORD_T; + +/// Pressure sensors calibration structure +typedef struct +{ + POLYNOMIAL_CAL_PAYLOAD_T pressureSensors[ NUM_OF_CAL_DATA_PRES_SENSORS ]; ///< Pressure sensors to calibrate. + POLYNOMIAL_CAL_PAYLOAD_T reservedSpace[ PRES_SENSORS_RESERVED_SPACE_COUNT ]; ///< Reserved space for future pressure sensors. +} DD_PRES_SENSORS_CAL_RECORD_T; + +/// Flow sensors calibration structure +typedef struct +{ + POLYNOMIAL_CAL_PAYLOAD_T flowSensors[ NUM_OF_CAL_DATA_FLOW_SENSORS ]; ///< Flow sensors to calibrate. + POLYNOMIAL_CAL_PAYLOAD_T reservedSpace[ FLOW_SENSROS_RESERVED_SPACE_COUNT ]; ///< Reserved space for future flow sensors. +} DD_FLOW_SENSORS_CAL_RECORD_T; + +/// Load cells calibration structure +typedef struct +{ + POLYNOMIAL_CAL_PAYLOAD_T loadCells[ NUM_OF_CAL_DATA_LOAD_CELLS ]; ///< Load cells to calibrate. +} DD_LOAD_CELLS_CAL_RECORD_T; + +/// Temperature sensors calibration structure +typedef struct +{ + POLYNOMIAL_CAL_PAYLOAD_T tempSensors[ NUM_OF_CAL_DATA_TEMP_SENSORS ]; ///< Temperature sensors to calibrate. + POLYNOMIAL_CAL_PAYLOAD_T reservedSpace[ TEMP_SENSORS_RESERVED_SPACE_COUNT ]; ///< Reserved space for future temperature sensors. +} DD_TEMP_SENSORS_CAL_RECORD_T; + +/// Drain line volume calibration structure +typedef struct +{ + F32 volume; ///< DG Drain line volume. + U32 calibrationTime; ///< Calibration time. + U16 crc; ///< CRC. +} DD_DRAIN_LINE_VOLUME_T; + +/// DG pre RO purge volume calibration structure +typedef struct +{ + F32 pressure2FlowRatio; ///< Pressure to flow ratio. + F32 volume; ///< Volume. + U32 calibrationTime; ///< Calibration time. + U16 crc; ///< CRC. +} DD_PRE_RO_PURGE_VOLUME_T; + +/// DG reservoir volume calibration structure +typedef struct +{ + F32 rsrvrVolume; ///< Reservoir volume. + F32 normalFillVolume; ///< Normal fill volume. + F32 maxResidualFluid; ///< Max residual fluid. + F32 rsrvrUnfilledWeight; ///< Reservoir unfilled weight. + U32 calibrationTime; ///< Calibration time. + U16 crc; ///< CRC. +} DD_RESERVOIR_VOLUME_DATA_T; + +/// DG reservoir volume record +typedef struct +{ + DD_RESERVOIR_VOLUME_DATA_T reservoir[ NUM_OF_CAL_DATA_RSRVRS ]; ///< DG reservoir volume data. +} DD_RESERVOIR_VOLUME_RECORD_T; + +/// DG generic volumes (reserved space) +typedef struct +{ + F32 volume; ///< Volume. + U32 calibrationTime; ///< Calibration time. + U16 crc; ///< CRC. +} DD_GENERIC_VOLUME_DATA_T; + +/// DG generic volume record +typedef struct +{ + DD_GENERIC_VOLUME_DATA_T genericVolume[ GENERIC_VOL_RESERVED_SPACE_COUNT ]; ///< DG generic volume record. +}DD_GENERIC_VOLUME_RECORD_T; + +/// DG acid concentrate +typedef struct +{ + F32 acidConcMixRatio; ///< Acid concentrate mix ratio. + F32 acidFullBottleVolumeML; ///< Acid full bottle volume in milliliters. + F32 acidConductivityUSPerCM; ///< Acid conductivity in uS/cm. + F32 acidBottleTemperature; ///< Acid bottle temperature in C. + U32 calibrationTime; ///< Calibration time. + U16 crc; ///< CRC. +} DD_ACID_CONCENTRATE_T; + +/// DG bicarb concentrate +typedef struct +{ + F32 bicarbConcMixRatio; ///< Bicarb concentrate mix ratio. + F32 bicarbStartVolumeML; ///< Bicarb start volume. + F32 bicarbConductivityUSPerCM; ///< Bicarb conductivity in uS/cm. + F32 bicarbBottleTemperature; ///< Bicarb bottle temperature in C. + U32 calibrationTime; ///< Calibration time. + U16 crc; ///< CRC. +} DD_BICARB_CONCENTRATE_T; + +/// DG acid concentrates +typedef struct +{ + DD_ACID_CONCENTRATE_T acidConcentrate[ NUM_OF_CAL_DATA_ACID_CONCENTRATES ]; ///< DG acid concentrates. +} DD_ACID_CONCENTRATES_RECORD_T; + +/// DG bicarb concentrates +typedef struct +{ + DD_BICARB_CONCENTRATE_T bicarbConcentrate[ NUM_OF_CAL_DATA_BICARB_CONCENTRATES ]; ///< DG bicarb concentrates. +} DD_BICARB_CONCENTRATES_RECORD_T; + +/// DG filter calibration record +typedef struct +{ + F32 reservedSpace1; ///< DG filter reserved space 1. + F32 reservedSpace2; ///< DG filter reserved space 2. + F32 reservedSpace3; ///< DG filter reserved space 3 + U32 calibrationTime; ///< Calibration time. + U16 crc; ///< CRC. +} DD_FILTER_CAL_RECORD_T; + +/// DG filters calibration data structure +typedef struct +{ + DD_FILTER_CAL_RECORD_T roFilter; ///< RO filter. + DD_FILTER_CAL_RECORD_T ultraFilter; ///< Ultra filter. + DD_FILTER_CAL_RECORD_T sedimentFilter; ///< Sediment filter. + DD_FILTER_CAL_RECORD_T carbonFilter; ///< Carbon filter. + DD_FILTER_CAL_RECORD_T carbonPolishFilter; ///< Carbon polish filter. +} DD_FILTERS_CAL_RECORD_T; + +/// DG fan calibration data structure +typedef struct +{ + F32 reservedSpace1; ///< Reserved space 1. + F32 reservedSpace2; ///< Reserved space 2. + U32 calibrationTime; ///< Calibration time. + U16 crc; ///< CRC. +} DD_FAN_CAL_RECORD_T; + +/// DG fans calibration data structure +typedef struct +{ + DD_FAN_CAL_RECORD_T fans[ NUM_OF_CAL_DATA_FANS ]; ///< Fans calibration record. +} DD_FANS_CAL_RECORD_T; + +/// HD accelerometer sensor calibration record +typedef struct +{ + F32 accelXOffset; ///< DG accelerometer X axis offset. + F32 accelYOffset; ///< DG accelerometer Y axis offset. + F32 accelZOffset; ///< DG accelerometer Z axis offset. + U32 calibrationTime; ///< Calibration time. + U16 crc; ///< CRC for the DG accelerometer sensor. +} DD_ACCEL_SENSOR_CAL_RECORD_T; + +/// DG heating calibration record +typedef struct +{ + F32 reservoirTempTauCPerMin; ///< Reservoir temperature Tau C/min. + F32 ultrafilterTempTauCPerMin; ///< Ultrafilter temperature Tau C/min. + U32 ultrafilterVolmL; ///< Ultrafilter volume in mL. + U32 calibrationTime; ///< Calibration time. + U16 crc; ///< CRC for the reservoirs and UF record. +} DD_HEATING_CAL_RECORD_T; + +/// DG acid and bicarb fill conductivity values +typedef struct +{ + F32 acidConduSPerCM; ///< Acid conductivity uS/cm. + F32 bicarbConduSPerCM; ///< Bicarb conductivity uS/cm. + U32 calibrationTime; ///< Calibration time. + U16 crc; ///< CRC for the acid/bicarb fill conductivity values. +} DD_ACID_BICARB_FILL_COND_VALUES_T; + +/// DG acid and bicarb fill conductivity record +typedef struct +{ + DD_ACID_BICARB_FILL_COND_VALUES_T fillCondValues[ NUM_OF_ACID_TYPE ][ NUM_OF_FILL_COND_TEST ]; ///< Acid and bicarb fill conductivity values. +} DD_CHEMICALS_FILL_COND_CAL_RECORD_T; + +/// DG systems record structure +typedef struct +{ + char topLevelPN[ MAX_TOP_LEVEL_PN_CHARS ]; ///< DG top level part number. + char topLevelSN[ MAX_TOP_LEVEL_SN_CHARS ]; ///< DG top level serial number. + U08 mfgLocation; ///< DG manufacturing location. + U32 mfgDate; ///< DG manufacturing date. + U16 crc; ///< CRC for the DG system record structure. +} DD_SYSTEM_RECORD_T; + +/// DG scheduled runs record structure +typedef struct +{ + U32 lastRunTime; ///< DG scheduled run last run time. + U16 crc; ///< CRC for the DG scheduled run record structure. +} DD_SCHEDULED_RUN_T; + +/// DG scheduled run record structure +typedef struct +{ + DD_SCHEDULED_RUN_T ddScheduledRun[ NUM_OF_DD_SCHEDULED_RUNS ]; ///< DG scheduled run. +} DD_SCHEDULED_RUN_RECORD_T; + +/// DG heaters information structure +typedef struct +{ + F32 averageFillFlow; ///< Average fill flow rate. +} DD_HEATERS_RECORD_T; + +#ifndef _RELEASE_ +/// DG software configurations +typedef struct +{ + U08 swConfigs[ NUM_OF_SW_CONFIGS ]; ///< Software configurations. +} DD_SW_CONFIG_RECORD_T; +#endif +#pragma pack(pop) + +void initNVRecordsDD( void ); +SELF_TEST_STATUS_T execNVDataMgmtSelfTest ( void ); +void benignPolynomialCalRecord( POLYNOMIAL_CAL_PAYLOAD_T* record ); +BOOL setLastDisinfectDate( DD_USAGE_INFO_ITEMS_T disinfect, U32 epochTime ); +BOOL setServiceTime( void ); + +#endif /* _NV_RECORDS_DD_H_ */