/************************************************************************** * * 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 "DDDefs.h" #include "NVJobQ.h" #include "NVMessagingDD.h" #include "NVRecordsDD.h" #include "Timers.h" #include "Utilities.h" // For crc calculation /** * @addtogroup NVRecordsDD * @{ */ // ********** private definitions ********** #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_D12_PUMP_TARGET_SPEED 0 ///< Default D12 dailysate pump target speed. #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_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_BLOOD_LEAK_SET_POINT 20 ///< Blood leak default set point. // Default Values for Institutional Records #define DEFAULT_MIN_DIALYSATE_FLOW_MLPM 50 ///< Default value of min dialysate flow in mL/min #define DEFAULT_MAX_DIALYSATE_FLOW_MLPM 600 ///< Default value of max dialysate flow in mL/min #define DEFAULT_MIN_DIALYSATE_TEMP_C 35.0F ///< Default value of min dialysate temp in °C #define DEFAULT_MAX_DIALYSATE_TEMP_C 38.0F ///< Default value of max dialysate temp in °C #define DEFAULT_MIN_ACID_CONCENTRATE 0 ///< Default value of min acid concentrate #define DEFAULT_MAX_ACID_CONCENTRATE 2 ///< Default value of max acid concentrate #define DEFAULT_MIN_BICARB_CARTRIDGE_SIZE_G 0 ///< Default value of min bicarb cartridge size in grams #define DEFAULT_MAX_BICARB_CARTRIDGE_SIZE_G 5 ///< Default value of max bicarb cartridge size in grams #define DEFAULT_MIN_SODIUM_MEQL 130 ///< Default value of min sodium in mEq/L #define DEFAULT_MAX_SODIUM_MEQL 155 ///< Default value of max sodium in mEq/L #define DEFAULT_MIN_BICARBONATE_MEQL 20 ///< Default value of min bicarbonate in mEq/L #define DEFAULT_MAX_BICARBONATE_MEQL 40 ///< Default value of max bicarbonate in mEq/L #define DEFAULT_MAX_RO_REJECTION_RATIO_PCT 90 ///< Max RO rejection ratio in percent. #define DEFAULT_NUM_OF_DISINFECTION_FREQUENCY 3 ///< Number of Days between each disinfection cycle #define DEFAULT_NUM_OF_DISINFECTION_CYCLE_TIME 30 ///< Time period of which disinfection is done #define DEFAULT_MIN_INLET_WATER_COND_ALARM_US_P_CM 200.0F ///< Min inlet water conductivity alarm limit in uS/cm. #define DEFAULT_MAX_INLET_WATER_COND_ALARM_US_P_CM 50.0F ///< Max inlet water conductivity alarm limit in uS/cm. #define DEFAULT_ACID_CONCENTRATE_JUG_SIZE 3.43F ///< Acid Concentrate jug size in litres #define DEFAULT_MIN_ACID_ALARM_US_P_CM 0 ///< Min acid alarm limit in percent. #define DEFAULT_MIN_BICARB_ALARM_US_P_CM 0 ///< Min bicarb alarm limit in percent. #define DEFAULT_POST_TREATMENT_DRAIN_OPTION 0 ///< Default Post Treatment Drain Option #define DEFAULT_POST_TREATMENT_DRY_BICARB_OPTION 0 ///< Default Post Treatment Dry Bicarb Option #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: (DD struct size % bytes to write(16) == 0 ? 0 : ((DD struct size / bytes to write(16)) + 1) * bytes to write(16)) - DD struct size. // NOTE: assuming the macro is calculating the padding length for a non-volatile memory. For NV, 16 bytes is used for buffer. // 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 ) ) ///< DD padding length macro. #define SYSTEM_RECORD_PADDING_LENGTH ( RECORD_PADDING_LENGTH( DD_SYSTEM_RECORD_T, MAX_EEPROM_WRITE_BUFFER_BYTES ) ) #define SERVICE_RECORD_PADDING_LENGTH ( RECORD_PADDING_LENGTH( DD_SERVICE_RECORD_T, MAX_EEPROM_WRITE_BUFFER_BYTES ) ) #define CALIBRATION_RECORD_PADDING_LENGTH ( RECORD_PADDING_LENGTH( DD_CALIBRATION_RECORD_T, MAX_EEPROM_WRITE_BUFFER_BYTES ) ) #define INSTITUTIONAL_RECORD_PADDING_LENGTH ( RECORD_PADDING_LENGTH( DD_INSTITUTIONAL_RECORD_T, MAX_EEPROM_WRITE_BUFFER_BYTES ) ) #define USAGE_INFO_RECORD_PADDING_LENGTH ( RECORD_PADDING_LENGTH( DD_USAGE_INFO_RECORD_T, MAX_EEPROM_WRITE_BUFFER_BYTES ) ) #pragma pack(push, 1) // ********** DD record structures ********** /// DD calibration groups structure typedef struct { DD_PRES_SENSORS_CAL_RECORD_T presSensorsCalRecord; ///< DD pressure sensors calibration record DD_TEMP_SENSORS_CAL_RECORD_T tempSensorsCalRecord; ///< DD temperature sensors calibration record DD_CONC_PUMPS_CAL_RECORD_T concentratePumpsRecord; ///< DD concentrate pumps calibration record DD_D12_DIALYSATE_PUMP_RECORD_T d12DialysatePumpRecord; ///< DD dialysate pump d12 calibration data. DD_D48_DIALYSATE_PUMP_RECORD_T d48DialysatePumpRecord; ///< DD dialysate pumps calibration record DD_ACID_CONCENTRATES_RECORD_T acidConcentratesRecord; ///< DD acid concentrates calibration record DD_BICARB_CONCENTRATES_RECORD_T bicarbConcentratesRecord; ///< DD bicarb concentrates calibration record DD_ACCEL_SENSOR_CAL_RECORD_T accelerometerSensorCalRecord; ///< DD accelerometer sensor. DD_BLOOD_LEAK_SENSOR_CAL_RECORD_T bloodLeakSensorCalRecord; ///< DD blood leak detector calibration record } DD_CALIBRATION_RECORD_T; /// DD calibration records structure typedef struct { DD_CALIBRATION_RECORD_T ddCalibrationRecord; ///< DD calibration groups. U08 padding[ CALIBRATION_RECORD_PADDING_LENGTH ]; ///< DD calibration record padding byte array. U16 crc; ///< CRC for the DG calibration record structure. } DD_CALIBRATION_GROUP_T; /// DD system group structure typedef struct { DD_SYSTEM_RECORD_T ddSystemRecord; ///< DD system record. U08 padding[ SYSTEM_RECORD_PADDING_LENGTH ]; ///< DD system group padding byte array. U16 crc; ///< CRC for the DG system group structure. } DD_SYSTEM_GROUP_T; /// DD service record structure typedef struct { DD_SERVICE_RECORD_T ddServiceRecord; ///< DD service record. U08 padding[ SERVICE_RECORD_PADDING_LENGTH ]; ///< DD service group padding. U16 crc; ///< CRC for the DG service structure. } DD_SERVICE_GROUP_T; /// DD institutional record structure typedef struct { DD_INSTITUTIONAL_RECORD_T ddInstitutionalRecord; ///< DD institutional record. U08 padding[ INSTITUTIONAL_RECORD_PADDING_LENGTH ]; ///< DD institutional group padding. U16 crc; ///< CRC for the HD institutional structure. } DD_INSTITUTIONAL_GROUP_T; /// DD usage record structure typedef struct { DD_USAGE_INFO_RECORD_T ddUsageInfoRecord; ///< DD usage info record. U08 padding[ USAGE_INFO_RECORD_PADDING_LENGTH ]; ///< DD scheduled run group padding. U16 crc; ///< CRC for the DG usage info structure. } DD_USAGE_INFO_GROUP_T; #pragma pack(pop) // ********** private data ********** /// Main NVM Record variables static DD_SYSTEM_GROUP_T ddSystemGroup; ///< DD system group structure (including padding and final CRC). static DD_SERVICE_GROUP_T ddServiceGroup; ///< DD service group structure (including padding and final CRC). static DD_CALIBRATION_GROUP_T ddCalibrationGroup; ///< DD calibration record structure (including padding and final CRC). static DD_INSTITUTIONAL_GROUP_T ddInstitutionalGroup; ///< DD institutional group structure (including padding and final CRC). static DD_USAGE_INFO_GROUP_T ddUsageInfoGroup; ///< DD usage info structure (including padding and final CRC). /// Main Process records specifications const PROCESS_RECORD_SPECS_T RECORDS_SPECS[ NUM_OF_NVM_RECORD_TYPES ] = { // Start address Size of the job Record structure pointer Record CRC pointer Event calibration record update {SYSTEM_RECORD_NV_MEM_START_ADDRESS, sizeof(DD_SYSTEM_GROUP_T), (U08*)&ddSystemGroup, (U08*)&ddSystemGroup.crc, DD_EVENT_SYSTEM_RECORD_UPDATE }, // NVM_SYSTEM_RECORD {SERVICE_RECORD_NV_MEM_START_ADDRESS, sizeof(DD_SERVICE_GROUP_T), (U08*)&ddServiceGroup, (U08*)&ddServiceGroup.crc, DD_EVENT_SERVICE_UPDATE }, // NVM_SERVICE_RECORD {CAL_RECORD_NV_MEM_START_ADDRESS, sizeof(DD_CALIBRATION_GROUP_T), (U08*)&ddCalibrationGroup, (U08*)&ddCalibrationGroup.crc, DD_EVENT_CAL_RECORD_UPDATE }, // NVM_CALIBRATION_RECORD {INSTIT_RECORD_NV_MEM_START_ADDRESS, sizeof(DD_INSTITUTIONAL_GROUP_T), (U08*)&ddInstitutionalGroup, (U08*)&ddInstitutionalGroup.crc, DD_EVENT_INSTIT_RECORD_UPDATE }, // NVM_INSTITUTIONAL_RECORD {USAGE_INFO_START_ADDRESS, sizeof(DD_USAGE_INFO_GROUP_T), (U08*)&ddUsageInfoGroup, (U08*)&ddUsageInfoGroup.crc, DD_EVENT_USAGE_INFO_UPDATE }, // NVM_USAGE_INFO_RECORD }; static NVM_SELF_TEST_STATE_T nvmSelfTestState; ///< NVM self-test state variable. static SELF_TEST_STATUS_T nvmSelfTestResult; ///< NVM self-test result. static U32 usageWriteTries; ///< Usage write tries. static BOOL isSelfTestReadRecordsDone; ///< Set to true when all the records are read // ********** private function prototypes ********** // Self test functions static NVM_SELF_TEST_STATE_T handleSelfTestReadRecords( void ); static NVM_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 isDDInstitutionalRecordValid( void ); static BOOL isDDUsageRecordValid( void ); static BOOL isDDCalibrationRecordValid( void ); static BOOL isDDPumpD12RecordValid( DD_D12_DIALYSATE_PUMP_RECORD_T* record ); static BOOL isDDAcidConcentrateRecordValid( DD_ACID_CONCENTRATE_T* record ); static BOOL isDDBicarbConcentrateRecordValid( DD_BICARB_CONCENTRATE_T* record ); static BOOL isDDAccelerometerSensorRecordValid( DD_ACCEL_SENSOR_CAL_RECORD_T* record ); static BOOL isDDBloodLeakSesnorValid( DD_BLOOD_LEAK_SENSOR_CAL_RECORD_T* record ); static void initDDSystemRecord( void ); static void initDDServiceRecord( void ); static void initDDInstitutionalRecord( void ); static void initDDUsageRecord( void ); static void initDDCalibrationRecord( void ); static void updateRecordPadding( NVM_RECORD_TYPE_T recType ); static void updateRecordCRC( NVM_RECORD_TYPE_T recType ); /*********************************************************************//** * @brief * The initNVRecordsDD function initializes the NV records data management * state and resets related control variables. It sets the self-test * process to start reading records. * @details \b Inputs: none * @details \b Outputs: nvmSelfTestState, * nvmSelfTestResult, usageWriteTries * @return none *************************************************************************/ void initNVRecordsDD( void ) { // Initialize the parameters nvmSelfTestState = NVM_SELF_TEST_STATE_READ_RECORDS; nvmSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; usageWriteTries = 0; initDDSystemRecord(); initDDServiceRecord(); initDDInstitutionalRecord(); initDDCalibrationRecord(); initDDUsageRecord(); int i; for( i = 0; i < NUM_OF_NVM_RECORD_TYPES; i++ ) { updateRecordPadding(i); updateRecordCRC(i); } } /*********************************************************************//** * @brief * The execNVMRecordsSelfTest function runs the NV data management * self-test state machine. It processes states and updates the result * based on self-test execution. * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT * @details \b Inputs: nvmSelfTestState, * nvmSelfTestResult * @details \b Outputs: nvmSelfTestState, * nvmSelfTestResult * @return nvmSelfTestResult the result of self-test *************************************************************************/ SELF_TEST_STATUS_T execNVMRecordsSelfTest ( void ) { switch ( nvmSelfTestState ) { case NVM_SELF_TEST_STATE_READ_RECORDS: nvmSelfTestState = handleSelfTestReadRecords(); break; case NVM_SELF_TEST_STATE_CHECK_CRC: nvmSelfTestState = handleSelfTestCheckCRC(); break; case NVM_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_NVM_INVALID_SELF_TEST_STATE, ( U32 )nvmSelfTestState ); nvmSelfTestState = NVM_SELF_TEST_STATE_COMPLETE; nvmSelfTestResult = SELF_TEST_STATUS_FAILED; break; } return nvmSelfTestResult; } /*********************************************************************//** * @brief * The getProcessRecord function gets the process record * specification for the given job. * @details \b Inputs: RECORDS_SPECS * @details \b Outputs: none * @param job Record job identifier used to select the process record * @return RECORDS_SPECS[job] corresponding process record specification *************************************************************************/ PROCESS_RECORD_SPECS_T getProcessRecord( NVM_RECORD_TYPE_T job ) { return RECORDS_SPECS[ job ]; } /*********************************************************************//** * @brief * The handleSelfTestReadRecords function waits for the records to be * read and updates the state when reading is complete. * @details \b Inputs: isSelfTestReadRecordsDone * @details \b Outputs: none * @return state next self-test state *************************************************************************/ static NVM_SELF_TEST_STATE_T handleSelfTestReadRecords( void ) { NVM_SELF_TEST_STATE_T state = NVM_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 == isSelfTestReadRecordsDone ) { updateRecordReadStatus( NVM_RECORDS_READ ); state = NVM_SELF_TEST_STATE_CHECK_CRC; } return state; } /*********************************************************************//** * @brief * The handleSelfTestCheckCRC function checks the CRC of records and * compares them with stored CRC values. If they don't match, it will fail POST. * It updates the self-test result * and schedules rewrite for invalid records. * @details \b Inputs: nvmSelfTestResult * @details \b Outputs: nvmSelfTestResult * @return state next self-test state *************************************************************************/ static NVM_SELF_TEST_STATE_T handleSelfTestCheckCRC ( void ) { NVM_SELF_TEST_STATE_T state = NVM_SELF_TEST_STATE_COMPLETE; BOOL hasSystemRecordPassed = TRUE; BOOL hasServiceRecordPassed = TRUE; BOOL haveCalGroupsPassed = TRUE; BOOL hasUsageRecordPassed = TRUE; updateRecordReadStatus( NVM_RECORDS_CRC_CHECKED ); // Check all the calibration groups haveCalGroupsPassed = isDDCalibrationRecordValid(); hasServiceRecordPassed = isDDServiceRecordValid(); hasUsageRecordPassed = isDDUsageRecordValid(); // If any of the records did not pass, they should be filled // with benign values. After that, schedule a write to sector // to re-write the records with the benign values // Validate system record hasSystemRecordPassed = isDDSystemRecordValid(); if ( FALSE == hasSystemRecordPassed ) { enqueueEraseAndWriteSector ( NVM_SYSTEM_RECORD ); SEND_EVENT_WITH_2_U32_DATA( RECORDS_SPECS[ NVM_SYSTEM_RECORD ].nvEvent, 0, 0 ); } // Validate service and calibration records hasServiceRecordPassed = isDDServiceRecordValid(); haveCalGroupsPassed = isDDCalibrationRecordValid(); if ( ( FALSE == hasServiceRecordPassed ) || ( FALSE == haveCalGroupsPassed ) ) { enqueueEraseAndWriteSector ( NVM_SERVICE_RECORD ); SEND_EVENT_WITH_2_U32_DATA( RECORDS_SPECS[ NVM_SERVICE_RECORD ].nvEvent, 0, 0 ); SEND_EVENT_WITH_2_U32_DATA( RECORDS_SPECS[ NVM_CALIBRATION_RECORD ].nvEvent, 0, 0 ); } // Validate usage info record if ( FALSE == hasUsageRecordPassed ) { enqueueEraseAndWriteSector ( NVM_USAGE_INFO_RECORD ); SEND_EVENT_WITH_2_U32_DATA( RECORDS_SPECS[ NVM_USAGE_INFO_RECORD ].nvEvent, 0, 0 ); } // Check if the records' entire CRCs as well as the individual CRCs passed if ( ( TRUE == hasSystemRecordPassed ) && ( TRUE == hasServiceRecordPassed ) && ( TRUE == haveCalGroupsPassed ) && ( TRUE == hasUsageRecordPassed ) ) { startNewCalRecordAvailableTimer(); setNewCalibrationRecordAvailable( TRUE ); nvmSelfTestResult = SELF_TEST_STATUS_PASSED; } else { nvmSelfTestResult = SELF_TEST_STATUS_FAILED; } return state; } /*********************************************************************//** * @brief * The benignPolynomialCalRecord function fills the provided polynomial * calibration record with benign default values and updates its CRC. * @details \b Inputs: none * @details \b Outputs: none * @param record Pointer to the polynomial calibration record to update * @return 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 if the polynomial record * is valid by comparing calculated CRC with stored CRC. If invalid, * it updates the record with benign values. * @details \b Inputs: none * @details \b Outputs: none * @param record Pointer to the polynomial calibration record to check * @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; } /*********************************************************************//** * @brief * The isDDSystemRecordValid function checks the validity of the DD * system record. It verifies CRC and updates the record with default * values if invalid. * @details \b Alarms: ALARM_ID_DD_NVM_INVALID_SYSTEM_RECORD_CRC if CRC * check fails, ALARM_ID_DD_NVM_INVALID_SERIAL_NUMBER if serial number * is not initialized * @details \b Inputs: ddSystemGroup.ddSystemRecord * @details \b Outputs: ddSystemGroup.ddSystemRecord, ddSystemGroup.crc * @return TRUE if the DD 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 initDDSystemRecord(); // Recalculate the CRC with the default values updateRecordCRC( NVM_SYSTEM_RECORD ); status = FALSE; activateAlarmNoData( ALARM_ID_DD_NVM_INVALID_SYSTEM_RECORD_CRC ); } else { // Verify the serial number from the system record if ( RECORD_DEFAULT_CHARACTER == ddSystemGroup.ddSystemRecord.topLevelSN[ 0 ] ) { activateAlarmNoData( ALARM_ID_DD_NVM_INVALID_SERIAL_NUMBER ); } } return status; } /*********************************************************************//** * @brief * The isDDServiceRecordValid function checks the validity of the DD * service record. It verifies CRC and updates the record with default * values if invalid. * @details \b Alarms: ALARM_ID_DD_NVM_INVALID_SERVICE_RECORD_CRC if CRC * check fails * @details \b Inputs: ddServiceGroup.ddServiceRecord * @details \b Outputs: ddServiceGroup.ddServiceRecord, * ddServiceGroup.crc * @return TRUE if the DD 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 initDDServiceRecord(); // Recalculate the CRC with the default values updateRecordCRC( NVM_SERVICE_RECORD ); status = FALSE; // Service record failure activateAlarmNoData( ALARM_ID_DD_NVM_INVALID_SERVICE_RECORD_CRC ); } return status; } /*********************************************************************//** * @brief * The isDDInstitutionalRecordValid function checks the validity of the DD * institutional record. It verifies CRC and updates the record * with default values if invalid. * @details \b Alarms: ALARM_ID_DD_NVM_INVALID_INSTITUTIONAL_RECORD_CRC if CRC * check fails * @details \b Inputs: ddInstitutionalGroup * @details \b Outputs: ddInstitutionalGroup * @return TRUE if the DD Institutional record is valid otherwise FALSE *************************************************************************/ static BOOL isDDInstitutionalRecordValid( void ) { BOOL status = TRUE; U16 calcCRC = crc16( (U08*)&ddInstitutionalGroup.ddInstitutionalRecord, sizeof( DD_INSTITUTIONAL_RECORD_T ) - sizeof( U16 ) ); U16 recordCRC = ddInstitutionalGroup.ddInstitutionalRecord.crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default initDDInstitutionalRecord(); // Recalculate the CRC with the default values updateRecordCRC( NVM_INSTITUTIONAL_RECORD ); status = FALSE; // Institutional record failure is also considered as RTC RAM failure activateAlarmNoData( ALARM_ID_DD_NVM_INVALID_INSTITUTIONAL_RECORD_CRC ); } return status; } /*********************************************************************//** * @brief * The isDDUsageRecordValid function checks the validity of the DD * usage information record. It verifies CRC and updates the record * with default values if invalid. * @details \b Alarms: ALARM_ID_DD_NVM_INVALID_USAGE_RECORD_CRC if CRC * check fails * @details \b Inputs: ddUsageInfoGroup * @details \b Outputs: ddUsageInfoGroup * @return TRUE if the DD usage record is valid otherwise FALSE *************************************************************************/ static BOOL isDDUsageRecordValid( void ) { BOOL status = TRUE; U16 calcCRC = crc16( (U08*)&ddUsageInfoGroup.ddUsageInfoRecord, sizeof( DD_USAGE_INFO_RECORD_T ) - sizeof( U16 ) ); U16 recordCRC = ddUsageInfoGroup.ddUsageInfoRecord.crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default initDDUsageRecord(); // Recalculate the CRC with the default values updateRecordCRC( NVM_USAGE_INFO_RECORD ); status = FALSE; activateAlarmNoData( ALARM_ID_DD_NVM_INVALID_USAGE_RECORD_CRC ); } return status; } /*********************************************************************//** * @brief * The isDDCalibrationRecordValid function checks the validity of the DD * calibration record. It verifies all calibration sub-records and * updates them with benign values if invalid. * @details \b Alarms: ALARM_ID_DD_NVM_INVALID_CALIBRATION_RECORD_CRC if * CRC check fails or any sub-record is invalid * @details \b Inputs: ddCalibrationGroup * @details \b Outputs: ddCalibrationGroup * @return TRUE if the DD 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*)&ddCalibrationGroup, sizeof( DD_CALIBRATION_GROUP_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 = &ddCalibrationGroup.ddCalibrationRecord.presSensorsCalRecord; // The ones that are an array, are looped through for ( i = 0; i < NUM_OF_PRESSURE_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; } DD_TEMP_SENSORS_CAL_RECORD_T* temperature = &ddCalibrationGroup.ddCalibrationRecord.tempSensorsCalRecord; for ( i = 0; i < NUM_OF_TEMPERATURE_SENSORS; i++ ) { record = (POLYNOMIAL_CAL_PAYLOAD_T*)&temperature->tempSensors[ i ]; isHardwareRecordValid = isPolynomialRecordValid( record ); isCalRecordValid = ( isCalRecordValid == FALSE ) ? FALSE : isHardwareRecordValid; } DD_CONC_PUMPS_CAL_RECORD_T* concPump = &ddCalibrationGroup.ddCalibrationRecord.concentratePumpsRecord; for ( i = 0; i < NUM_OF_CONCENTRATE_PUMPS; i++ ) { record = (POLYNOMIAL_CAL_PAYLOAD_T*)&concPump->concentratePumps[ i ]; isHardwareRecordValid = isPolynomialRecordValid( record ); isCalRecordValid = ( isCalRecordValid == FALSE ) ? FALSE : isHardwareRecordValid; } DD_D12_DIALYSATE_PUMP_RECORD_T* d12Pump = &ddCalibrationGroup.ddCalibrationRecord.d12DialysatePumpRecord; isHardwareRecordValid = isDDPumpD12RecordValid( d12Pump ); isCalRecordValid = ( isCalRecordValid == FALSE ) ? FALSE : isHardwareRecordValid; DD_D48_DIALYSATE_PUMP_RECORD_T* d48Pump = &ddCalibrationGroup.ddCalibrationRecord.d48DialysatePumpRecord; record = (POLYNOMIAL_CAL_PAYLOAD_T*)&d48Pump->d48DialysatePump; isHardwareRecordValid = isPolynomialRecordValid( record ); isCalRecordValid = ( isCalRecordValid == FALSE ) ? FALSE : isHardwareRecordValid; DD_ACID_CONCENTRATES_RECORD_T* acidConc = &ddCalibrationGroup.ddCalibrationRecord.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 = &ddCalibrationGroup.ddCalibrationRecord.bicarbConcentratesRecord; for ( i = 0; i < NUM_OF_CAL_DATA_BICARB_CONCENTRATES; i++ ) { isHardwareRecordValid = isDDBicarbConcentrateRecordValid( &bicarbConc->bicarbConcentrate[ i ] ); isCalRecordValid = ( isCalRecordValid == FALSE ) ? FALSE : isHardwareRecordValid; } DD_ACCEL_SENSOR_CAL_RECORD_T* accelerometer = &ddCalibrationGroup.ddCalibrationRecord.accelerometerSensorCalRecord; isHardwareRecordValid = isDDAccelerometerSensorRecordValid( accelerometer ); isCalRecordValid = ( isCalRecordValid == FALSE ) ? FALSE : isHardwareRecordValid; DD_BLOOD_LEAK_SENSOR_CAL_RECORD_T* bloodLeak = &ddCalibrationGroup.ddCalibrationRecord.bloodLeakSensorCalRecord; isHardwareRecordValid = isDDBloodLeakSesnorValid( bloodLeak ); 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 != ddCalibrationGroup.crc ) ) { isCalRecordValid = FALSE; ddCalibrationGroup.crc = crc16 ( (U08*)&ddCalibrationGroup, sizeof( DD_CALIBRATION_GROUP_T ) - sizeof( U16 ) ); activateAlarmNoData( ALARM_ID_DD_NVM_INVALID_CALIBRATION_RECORD_CRC ); } return isCalRecordValid; } /*********************************************************************//** * @brief * The isDDPumpD12RecordValid function checks if the D12 Dialysate Pump * record is valid. It verifies CRC and updates * the record with default values if invalid. * @details \b Inputs: none * @details \b Outputs: none * @param record Pointer to DD D12 Dialysate record to check * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isDDPumpD12RecordValid( DD_D12_DIALYSATE_PUMP_RECORD_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof( DD_D12_DIALYSATE_PUMP_RECORD_T ) - sizeof( U16 ) ); U16 recordCRC = record->crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default record->targetPumpSpeed = DEFAULT_D12_PUMP_TARGET_SPEED; record->calibrationTime = RECORD_DEFAULT_TIME; record->crc = crc16 ( (U08*)record, sizeof( DD_D12_DIALYSATE_PUMP_RECORD_T ) - sizeof( U16 ) ); status = FALSE; } return status; } /*********************************************************************//** * @brief * The isDDAcidConcentrateRecordValid function checks if the acid * concentrate calibration record is valid. It verifies CRC and updates * the record with default values if invalid. * @details \b Inputs: none * @details \b Outputs: none * @param record Pointer to DD acid concentrate record to check * @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->acidConcMixRatio = DEFAULT_ACID_CONC_MIXING_RATIO; record->acidFullBottleVolumeML = DEFAULT_ACID_BOTTLE_VOL_ML; record->acidConductivityUSPerCM = DEFAULT_ACID_COND_US_PER_CM; record->acidBottleTemperature = DEFAULT_ACID_BOTTLE_TEMP_C; 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 if the bicarb * concentrate calibration record is valid. It verifies CRC and updates * the record with default values if invalid. * @details \b Inputs: none * @details \b Outputs: none * @param record Pointer to DD bicarb concentrate record to check * @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->bicarbConcMixRatio = DEFAULT_BICARB_CONC_MIXING_RATIO; record->bicarbStartVolumeML = DEFAULT_BICARB_BOTTLE_VOL_ML; record->bicarbConductivityUSPerCM = DEFAULT_BICARB_COND_US_PER_CM; record->bicarbBottleTemperature = DEFAULT_BICARB_BOTTLE_TEMP_C; record->calibrationTime = RECORD_DEFAULT_TIME; record->crc = crc16 ( (U08*)record, sizeof( DD_BICARB_CONCENTRATE_T ) - sizeof( U16 ) ); status = FALSE; } return status; } /*********************************************************************//** * @brief * The isDDAccelerometerSensorRecordValid function checks if the * accelerometer sensor calibration record is valid. It verifies CRC * and updates the record with default values if invalid. * @details \b Inputs: none * @details \b Outputs: none * @param record Pointer to accelerometer sensor calibration record to check * @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 isDDBloodLeakSesnorValid function checks if the blood leak sensor * calibration record is valid. It verifies CRC and updates the record * with default values if invalid. * @details \b Inputs: none * @details \b Outputs: none * @param record Pointer to blood leak sensor calibration record to check * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isDDBloodLeakSesnorValid( DD_BLOOD_LEAK_SENSOR_CAL_RECORD_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof( DD_BLOOD_LEAK_SENSOR_CAL_RECORD_T ) - sizeof( U16 ) ); U16 recordCRC = record->crc; if ( calcCRC != recordCRC ) { record->setPoint = DEFAULT_BLOOD_LEAK_SET_POINT; record->calibrationTime = RECORD_DEFAULT_TIME; record->crc = crc16 ( (U08*)record, sizeof( DD_BLOOD_LEAK_SENSOR_CAL_RECORD_T ) - sizeof( U16 ) ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } static void initDDSystemRecord( void ) { ddSystemGroup.ddSystemRecord.isROFeatured = FALSE; ddSystemGroup.ddSystemRecord.isROFeaturedBoostPump = FALSE; memset( ddSystemGroup.ddSystemRecord.topLevelPN, RECORD_DEFAULT_CHARACTER, sizeof( ddSystemGroup.ddSystemRecord.topLevelPN ) ); memset( ddSystemGroup.ddSystemRecord.topLevelSN, RECORD_DEFAULT_CHARACTER, sizeof( ddSystemGroup.ddSystemRecord.topLevelSN ) ); ddSystemGroup.ddSystemRecord.mfgLocation = 0; ddSystemGroup.ddSystemRecord.mfgDate = 0; } static void initDDServiceRecord( void ) { ddServiceGroup.ddServiceRecord.isHDFOnlineFluid = FALSE; ddServiceGroup.ddServiceRecord.waterRecovery = 0; ddServiceGroup.ddServiceRecord.serviceLoc = 0; ddServiceGroup.ddServiceRecord.lastServiceEpochDate = 0; ddServiceGroup.ddServiceRecord.serviceIntervalSeconds = RECORD_DEFAULT_SERVICE_INTERVAL_S; // ddServiceGroup.ddServiceRecord.lastResetTimeEpoch = getRTCTimestamp(); } static void initDDInstitutionalRecord( void ) { ddInstitutionalGroup.ddInstitutionalRecord.minDialysateFlowMLPM = DEFAULT_MIN_DIALYSATE_FLOW_MLPM; ddInstitutionalGroup.ddInstitutionalRecord.maxDialysateFlowMLPM = DEFAULT_MAX_DIALYSATE_FLOW_MLPM; ddInstitutionalGroup.ddInstitutionalRecord.minDialysateTempC = DEFAULT_MIN_DIALYSATE_TEMP_C; ddInstitutionalGroup.ddInstitutionalRecord.maxDialysateTempC = DEFAULT_MAX_DIALYSATE_TEMP_C; ddInstitutionalGroup.ddInstitutionalRecord.minAcidConcentrate = DEFAULT_MIN_ACID_CONCENTRATE; ddInstitutionalGroup.ddInstitutionalRecord.maxAcidConcentrate = DEFAULT_MAX_ACID_CONCENTRATE; ddInstitutionalGroup.ddInstitutionalRecord.minBicarbCartridgeSizeG = DEFAULT_MIN_BICARB_CARTRIDGE_SIZE_G; ddInstitutionalGroup.ddInstitutionalRecord.maxBicarbCartridgeSizeG = DEFAULT_MAX_BICARB_CARTRIDGE_SIZE_G; ddInstitutionalGroup.ddInstitutionalRecord.minSodiumMEQL = DEFAULT_MIN_SODIUM_MEQL; ddInstitutionalGroup.ddInstitutionalRecord.maxSodiumMEQL = DEFAULT_MAX_SODIUM_MEQL; ddInstitutionalGroup.ddInstitutionalRecord.minBicarbonateMEQL = DEFAULT_MIN_BICARBONATE_MEQL; ddInstitutionalGroup.ddInstitutionalRecord.maxBicarbonateMEQL = DEFAULT_MAX_BICARBONATE_MEQL; ddInstitutionalGroup.ddInstitutionalRecord.minRORejectionRatioPCT = DEFAULT_MAX_RO_REJECTION_RATIO_PCT; ddInstitutionalGroup.ddInstitutionalRecord.disinfectionFrequency = DEFAULT_NUM_OF_DISINFECTION_FREQUENCY; ddInstitutionalGroup.ddInstitutionalRecord.disinfectionCycleTime = DEFAULT_NUM_OF_DISINFECTION_CYCLE_TIME; ddInstitutionalGroup.ddInstitutionalRecord.minInletWaterCondAlarmLimitUSPCM = DEFAULT_MIN_INLET_WATER_COND_ALARM_US_P_CM; ddInstitutionalGroup.ddInstitutionalRecord.maxInletWaterCondAlarmLimitUSPCM = DEFAULT_MAX_INLET_WATER_COND_ALARM_US_P_CM; ddInstitutionalGroup.ddInstitutionalRecord.acidConcentrateJugSizeL = DEFAULT_ACID_CONCENTRATE_JUG_SIZE; ddInstitutionalGroup.ddInstitutionalRecord.minAcidAlarmLimitPCT = DEFAULT_MIN_ACID_ALARM_US_P_CM; ddInstitutionalGroup.ddInstitutionalRecord.minBicarbAlarmLimitPCT = DEFAULT_MIN_BICARB_ALARM_US_P_CM; ddInstitutionalGroup.ddInstitutionalRecord.postTreatDrainOption = DEFAULT_POST_TREATMENT_DRAIN_OPTION; ddInstitutionalGroup.ddInstitutionalRecord.postTreatDryBicarbOption = DEFAULT_POST_TREATMENT_DRY_BICARB_OPTION; // ddInstitutionalGroup.ddInstitutionalRecord.calibrationTime = getRTCTimestamp(); } static void initDDUsageRecord( void ) { ddUsageInfoGroup.ddUsageInfoRecord.roWaterGenTotalL = 0.0F; ddUsageInfoGroup.ddUsageInfoRecord.roWaterGenSinceLastServiceL = 0.0F; ddUsageInfoGroup.ddUsageInfoRecord.lastBasicFlushCompleteDateEpoch = 0; ddUsageInfoGroup.ddUsageInfoRecord.lastHeatDisCompleteDateEpoch = 0; ddUsageInfoGroup.ddUsageInfoRecord.lastHeatActiveCoolCompleteDateEpoch = 0; ddUsageInfoGroup.ddUsageInfoRecord.lastFilterFlushCompleteDateEpoch = 0; // ddUsageInfoGroup.ddUsageInfoRecord.lastResetTimeEpoch = getRTCTimestamp(); } static void initDDCalibrationRecord( void ) { U32 i; POLYNOMIAL_CAL_PAYLOAD_T* record; DD_PRES_SENSORS_CAL_RECORD_T* pressure = &ddCalibrationGroup.ddCalibrationRecord.presSensorsCalRecord; for ( i = 0; i < NUM_OF_PRESSURE_SENSORS; i++ ) { record = (POLYNOMIAL_CAL_PAYLOAD_T*)&pressure->pressureSensors[ i ]; benignPolynomialCalRecord( record ); } DD_TEMP_SENSORS_CAL_RECORD_T* temperature = &ddCalibrationGroup.ddCalibrationRecord.tempSensorsCalRecord; for ( i = 0; i < NUM_OF_TEMPERATURE_SENSORS; i++ ) { record = (POLYNOMIAL_CAL_PAYLOAD_T*)&temperature->tempSensors[ i ]; benignPolynomialCalRecord( record ); } DD_CONC_PUMPS_CAL_RECORD_T* concPump = &ddCalibrationGroup.ddCalibrationRecord.concentratePumpsRecord; for ( i = 0; i < NUM_OF_CONCENTRATE_PUMPS; i++ ) { record = (POLYNOMIAL_CAL_PAYLOAD_T*)&concPump->concentratePumps[ i ]; benignPolynomialCalRecord( record ); } ddCalibrationGroup.ddCalibrationRecord.d12DialysatePumpRecord.targetPumpSpeed = DEFAULT_D12_PUMP_TARGET_SPEED; ddCalibrationGroup.ddCalibrationRecord.d12DialysatePumpRecord.calibrationTime = RECORD_DEFAULT_TIME; ddCalibrationGroup.ddCalibrationRecord.d12DialysatePumpRecord.crc = crc16 ( (U08*)record, sizeof( DD_D12_DIALYSATE_PUMP_RECORD_T ) - sizeof( U16 ) ); DD_D48_DIALYSATE_PUMP_RECORD_T* d48Pump = &ddCalibrationGroup.ddCalibrationRecord.d48DialysatePumpRecord; record = (POLYNOMIAL_CAL_PAYLOAD_T*)&d48Pump->d48DialysatePump; benignPolynomialCalRecord( record ); DD_ACID_CONCENTRATES_RECORD_T* acidConc = &ddCalibrationGroup.ddCalibrationRecord.acidConcentratesRecord; for ( i = 0; i < NUM_OF_CAL_DATA_ACID_CONCENTRATES; i++ ) { acidConc->acidConcentrate[ i ].acidConcMixRatio = DEFAULT_ACID_CONC_MIXING_RATIO; acidConc->acidConcentrate[ i ].acidFullBottleVolumeML = DEFAULT_ACID_BOTTLE_VOL_ML; acidConc->acidConcentrate[ i ].acidConductivityUSPerCM = DEFAULT_ACID_COND_US_PER_CM; acidConc->acidConcentrate[ i ].acidBottleTemperature = DEFAULT_ACID_BOTTLE_TEMP_C; acidConc->acidConcentrate[ i ].calibrationTime = RECORD_DEFAULT_TIME; acidConc->acidConcentrate[ i ].crc = crc16 ( (U08*)record, sizeof( DD_ACID_CONCENTRATE_T ) - sizeof( U16 ) ); } DD_BICARB_CONCENTRATES_RECORD_T* bicarbConc = &ddCalibrationGroup.ddCalibrationRecord.bicarbConcentratesRecord; for ( i = 0; i < NUM_OF_CAL_DATA_BICARB_CONCENTRATES; i++ ) { bicarbConc->bicarbConcentrate[ i ].bicarbConcMixRatio = DEFAULT_BICARB_CONC_MIXING_RATIO; bicarbConc->bicarbConcentrate[ i ].bicarbStartVolumeML = DEFAULT_BICARB_BOTTLE_VOL_ML; bicarbConc->bicarbConcentrate[ i ].bicarbConductivityUSPerCM = DEFAULT_BICARB_COND_US_PER_CM; bicarbConc->bicarbConcentrate[ i ].bicarbBottleTemperature = DEFAULT_BICARB_BOTTLE_TEMP_C; bicarbConc->bicarbConcentrate[ i ].calibrationTime = RECORD_DEFAULT_TIME; bicarbConc->bicarbConcentrate[ i ].crc = crc16 ( (U08*)record, sizeof( DD_BICARB_CONCENTRATE_T ) - sizeof( U16 ) ); } ddCalibrationGroup.ddCalibrationRecord.accelerometerSensorCalRecord.accelXOffset = RECORD_DEFAULT_OFFSET; ddCalibrationGroup.ddCalibrationRecord.accelerometerSensorCalRecord.accelYOffset = RECORD_DEFAULT_OFFSET; ddCalibrationGroup.ddCalibrationRecord.accelerometerSensorCalRecord.accelZOffset = RECORD_DEFAULT_OFFSET; ddCalibrationGroup.ddCalibrationRecord.accelerometerSensorCalRecord.calibrationTime = RECORD_DEFAULT_TIME; ddCalibrationGroup.ddCalibrationRecord.accelerometerSensorCalRecord.crc = crc16 ( (U08*)record, sizeof(DD_ACCEL_SENSOR_CAL_RECORD_T) - sizeof(U16) ); ddCalibrationGroup.ddCalibrationRecord.bloodLeakSensorCalRecord.setPoint = DEFAULT_BLOOD_LEAK_SET_POINT; ddCalibrationGroup.ddCalibrationRecord.bloodLeakSensorCalRecord.calibrationTime = RECORD_DEFAULT_TIME; ddCalibrationGroup.ddCalibrationRecord.bloodLeakSensorCalRecord.crc = crc16 ( (U08*)record, sizeof( DD_BLOOD_LEAK_SENSOR_CAL_RECORD_T ) - sizeof( U16 ) ); } static void updateRecordPadding( NVM_RECORD_TYPE_T recType ) { U32 i = 0; switch( recType ) { case NVM_SYSTEM_RECORD: { for ( i = 0; i < SYSTEM_RECORD_PADDING_LENGTH; i++ ) { ddSystemGroup.padding[ i ] = 0; } } break; case NVM_SERVICE_RECORD: { for ( i = 0; i < SERVICE_RECORD_PADDING_LENGTH; i++ ) { ddServiceGroup.padding[ i ] = 0; } } break; case NVM_CALIBRATION_RECORD: { for ( i = 0; i < CALIBRATION_RECORD_PADDING_LENGTH; i++ ) { ddCalibrationGroup.padding[ i ] = 0; } } break; case NVM_INSTITUTIONAL_RECORD: { for ( i = 0; i < INSTITUTIONAL_RECORD_PADDING_LENGTH; i++ ) { ddInstitutionalGroup.padding[ i ] = 0; } } break; case NVM_USAGE_INFO_RECORD: { for ( i = 0; i < USAGE_INFO_RECORD_PADDING_LENGTH; i++ ) { ddUsageInfoGroup.padding[ i ] = 0; } } break; default: break; } } static void updateRecordCRC( NVM_RECORD_TYPE_T recType ) { U32 i = 0; switch( recType ) { case NVM_SYSTEM_RECORD: { 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 ) ); } break; case NVM_SERVICE_RECORD: { 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 ) ); } break; case NVM_CALIBRATION_RECORD: { ddCalibrationGroup.crc = crc16 ( (U08*)&ddCalibrationGroup, sizeof( DD_CALIBRATION_GROUP_T ) - sizeof( U16 ) ); } break; case NVM_INSTITUTIONAL_RECORD: { ddInstitutionalGroup.ddInstitutionalRecord.crc = crc16 ( (U08*)&ddInstitutionalGroup.ddInstitutionalRecord, sizeof( DD_INSTITUTIONAL_RECORD_T ) - sizeof( U16 ) ); ddInstitutionalGroup.crc = crc16 ( (U08*)&ddInstitutionalGroup, sizeof( DD_INSTITUTIONAL_GROUP_T ) - sizeof( U16 ) ); } break; case NVM_USAGE_INFO_RECORD: { ddUsageInfoGroup.ddUsageInfoRecord.crc = crc16 ( (U08*)&ddUsageInfoGroup.ddUsageInfoRecord, sizeof( DD_SERVICE_RECORD_T ) - sizeof( U16 ) ); ddUsageInfoGroup.crc = crc16 ( (U08*)&ddUsageInfoGroup, sizeof( DD_SERVICE_GROUP_T ) - sizeof( U16 ) ); } break; default: break; } } /*********************************************************************//** * @brief * The setLastDisinfectDate function updates the last disinfect time in * usage info and schedules a write to memory if queue is available. * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT if invalid disinfect * type is selected or write retries exceed limit * @details \b Inputs: ddUsageInfoGroup, usageWriteTries * @details \b Outputs: ddUsageInfoGroup, usageWriteTries * @param disinfect Type of disinfect operation * @param epochTime Last disinfect time in epoch * @return TRUE if queue is not full otherwise FALSE *************************************************************************/ BOOL setLastDisinfectDate( DD_USAGE_INFO_ITEMS_T disinfect, U32 epochTime ) { BOOL status = FALSE; if ( FALSE == isRecordQueueFull() ) { switch ( disinfect ) { case USAGE_INFO_BASIC_FLUSH: ddUsageInfoGroup.ddUsageInfoRecord.lastBasicFlushCompleteDateEpoch = epochTime; break; case USAGE_INFO_HEAT_DIS: ddUsageInfoGroup.ddUsageInfoRecord.lastHeatDisCompleteDateEpoch = epochTime; break; case USAGE_INFO_FILTER_FLUSH: ddUsageInfoGroup.ddUsageInfoRecord.lastFilterFlushCompleteDateEpoch = epochTime; break; case USAGE_INFO_HEAT_DIS_ACTIVE_COOL: ddUsageInfoGroup.ddUsageInfoRecord.lastHeatActiveCoolCompleteDateEpoch = epochTime; break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_NVM_INVALID_USAGE_INFO_SELECTED, ( U32 )disinfect ); break; } ddUsageInfoGroup.ddUsageInfoRecord.crc = crc16( (U08*)&ddUsageInfoGroup.ddUsageInfoRecord, sizeof( DD_USAGE_INFO_RECORD_T ) - sizeof( U16 ) ); ddUsageInfoGroup.crc = crc16( (U08*)&ddUsageInfoGroup, sizeof( DD_USAGE_INFO_GROUP_T ) - sizeof( U16 ) ); usageWriteTries = 0; status = TRUE; enqueueEraseAndWriteSector( NVM_USAGE_INFO_RECORD ); SEND_EVENT_WITH_2_U32_DATA( RECORDS_SPECS[ NVM_USAGE_INFO_RECORD ].nvEvent, 0, 0 ); } else if ( ++usageWriteTries > MAX_NUM_OF_WRITE_TRIES ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_NVM_WRITE_USAGE_INFO_FAILURE, ( U32 )disinfect ); } return status; } /*********************************************************************//** * @brief * The setServiceTime function updates the latest service time and * resets related usage information. It schedules writing updated * records if sufficient queue space is available. * @details \b Inputs: ddServiceGroup, ddUsageInfoGroup * @details \b Outputs: ddServiceGroup, ddUsageInfoGroup * @return TRUE if the queue has sufficient space to enqueue all the * changed records otherwise FALSE *************************************************************************/ BOOL setServiceTime( void ) { BOOL status = FALSE; if ( getAvailableRecordQueueCount() >= ( MIN_JOBS_NEEDED_TO_WRITE_ALL_RECORDS + 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.ddUsageInfoRecord.roWaterGenSinceLastServiceL = 0; ddUsageInfoGroup.ddUsageInfoRecord.crc = crc16( (U08*)&ddUsageInfoGroup.ddUsageInfoRecord, 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 enqueueEraseAndWriteSector( NVM_SERVICE_RECORD ); enqueueEraseAndWriteSector( NVM_USAGE_INFO_RECORD ); // Both the usage and service records have been updated SEND_EVENT_WITH_2_U32_DATA( RECORDS_SPECS[ NVM_USAGE_INFO_RECORD ].nvEvent, 0, 0 ); SEND_EVENT_WITH_2_U32_DATA( RECORDS_SPECS[ NVM_SERVICE_RECORD ].nvEvent, 0, 0 ); status = TRUE; } return status; } /*********************************************************************//** * @brief * The getMinRORejectionRatioInInstitRecordPCT function gets the minimum * RO rejection ratio value from the institutional record in percent. * @details \b Inputs: ddInstitutionalGroup * @details \b Outputs: none * @return the min RO rejection ratio in institutional record in percent *************************************************************************/ U32 getMinRORejectionRatioInInstitRecordPCT( void ) { return ddInstitutionalGroup.ddInstitutionalRecord.minRORejectionRatioPCT; } /*********************************************************************//** * @brief * The getMinInletWaterConductivityLimitInstitRecordUSPCM function gets * the inlet water conductivity alarm limit value from the institutional * record in uS/cm. * @details \b Inputs: ddInstitutionalGroup * @details \b Outputs: none * @return the inlet water conductivity alarm limit in uS/cm in * institutional record *************************************************************************/ F32 getMinInletWaterConductivityLimitInstitRecordUSPCM( void ) { return ddInstitutionalGroup.ddInstitutionalRecord.minInletWaterCondAlarmLimitUSPCM; } /*********************************************************************//** * @brief * The getNVMRecord function copies the requested non-volatile * data into the provided buffer and checks if the data is valid. It * raises the specified alarm if invalid data is detected. * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT if invalid record is * selected, nvAlarm if non-volatile data is invalid * @details \b Inputs: ddCalibrationGroup * @details \b Outputs: none * @param nvData The non-volatile data to be copied * @param bufferAddress Address of the provided buffer * @param bufferLength Length of the provided buffer * @param numOfSnsrs2Check Number of sensors to check * @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 getNVMRecord( 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*)&ddCalibrationGroup.ddCalibrationRecord.presSensorsCalRecord; nvDataLength = sizeof( ddCalibrationGroup.ddCalibrationRecord.presSensorsCalRecord ); for ( i = 0; i < numOfSnsrs2Check; i++ ) isNVDataInvalid |= ( 0 == ddCalibrationGroup.ddCalibrationRecord.presSensorsCalRecord.pressureSensors[ i ].calibrationTime ? TRUE : FALSE ); break; case GET_CAL_TEMP_SENSORS: nvDataStartPtr = (U08*)&ddCalibrationGroup.ddCalibrationRecord.tempSensorsCalRecord; nvDataLength = sizeof( ddCalibrationGroup.ddCalibrationRecord.tempSensorsCalRecord ); for ( i = 0; i < numOfSnsrs2Check; i++ ) isNVDataInvalid |= ( 0 == ddCalibrationGroup.ddCalibrationRecord.tempSensorsCalRecord.tempSensors[ i ].calibrationTime ? TRUE : FALSE ); break; case GET_CAL_CONCENTRATE_PUMPS_RECORD: nvDataStartPtr = (U08*)&ddCalibrationGroup.ddCalibrationRecord.concentratePumpsRecord; nvDataLength = sizeof( ddCalibrationGroup.ddCalibrationRecord.concentratePumpsRecord ); for ( i = 0; i < numOfSnsrs2Check; i++ ) isNVDataInvalid |= ( 0 == ddCalibrationGroup.ddCalibrationRecord.concentratePumpsRecord.concentratePumps[ i ].calibrationTime ? TRUE : FALSE ); break; case GET_CAL_D12_PUMP_RECORD: nvDataStartPtr = (U08*)&ddCalibrationGroup.ddCalibrationRecord.d12DialysatePumpRecord; nvDataLength = sizeof( ddCalibrationGroup.ddCalibrationRecord.d12DialysatePumpRecord ); isNVDataInvalid |= ( 0 == ddCalibrationGroup.ddCalibrationRecord.d12DialysatePumpRecord.calibrationTime ? TRUE : FALSE ); break; case GET_CAL_D48_PUMP_RECORD: nvDataStartPtr = (U08*)&ddCalibrationGroup.ddCalibrationRecord.d48DialysatePumpRecord; nvDataLength = sizeof( ddCalibrationGroup.ddCalibrationRecord.d48DialysatePumpRecord ); isNVDataInvalid |= ( 0 == ddCalibrationGroup.ddCalibrationRecord.d48DialysatePumpRecord.d48DialysatePump.calibrationTime ? TRUE : FALSE ); break; case GET_CAL_ACID_CONCENTREATES: nvDataStartPtr = (U08*)&ddCalibrationGroup.ddCalibrationRecord.acidConcentratesRecord; nvDataLength = sizeof( ddCalibrationGroup.ddCalibrationRecord.acidConcentratesRecord ); for ( i = 0; i < numOfSnsrs2Check; i++ ) isNVDataInvalid |= ( 0 == ddCalibrationGroup.ddCalibrationRecord.acidConcentratesRecord.acidConcentrate[ i ].calibrationTime ? TRUE : FALSE ); break; case GET_CAL_BICARB_CONCENTRATES: nvDataStartPtr = (U08*)&ddCalibrationGroup.ddCalibrationRecord.bicarbConcentratesRecord; nvDataLength = sizeof( ddCalibrationGroup.ddCalibrationRecord.bicarbConcentratesRecord ); for ( i = 0; i < numOfSnsrs2Check; i++ ) isNVDataInvalid |= ( 0 == ddCalibrationGroup.ddCalibrationRecord.bicarbConcentratesRecord.bicarbConcentrate[ i ].calibrationTime ? TRUE : FALSE ); break; case GET_CAL_ACCEL_SENSORS: nvDataStartPtr = (U08*)&ddCalibrationGroup.ddCalibrationRecord.accelerometerSensorCalRecord; isNVDataInvalid = ( 0 == ddCalibrationGroup.ddCalibrationRecord.accelerometerSensorCalRecord.calibrationTime ? TRUE : FALSE ); break; case GET_CAL_BLOOD_LEAK_SENSOR: nvDataStartPtr = (U08*)&ddCalibrationGroup.ddCalibrationRecord.bloodLeakSensorCalRecord; nvDataLength = sizeof( ddCalibrationGroup.ddCalibrationRecord.bloodLeakSensorCalRecord ); isNVDataInvalid = ( 0 == ddCalibrationGroup.ddCalibrationRecord.bloodLeakSensorCalRecord.calibrationTime ? TRUE : FALSE ); break; case GET_SYSTEM_RECORD: nvDataStartPtr = (U08*)&ddSystemGroup.ddSystemRecord; nvDataLength = sizeof( ddSystemGroup.ddSystemRecord ); break; case GET_SERVICE_RECORD: nvDataStartPtr = (U08*)&ddServiceGroup.ddServiceRecord; nvDataLength = sizeof( ddServiceGroup.ddServiceRecord ); break; case GET_INSTITUTIONAL_RECORD: nvDataStartPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord; nvDataLength = sizeof( ddInstitutionalGroup.ddInstitutionalRecord ); break; case GET_USAGE_RECORD: nvDataStartPtr = (U08*)&ddUsageInfoGroup.ddUsageInfoRecord; nvDataLength = sizeof( ddUsageInfoGroup.ddUsageInfoRecord ); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_NVM_INVALID_RECORD_SELECTED, ( U32 )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 { // activateAlarmNoData( nvAlarm ); } } // Reverse the polarity to signal the outside users that the calibration has passed. return ( FALSE == isNVDataInvalid ? TRUE : FALSE ); } BOOL setNVMRecord( NVM_RECORD_TYPE_T recType, U08* bufferAddress ) { BOOL result = FALSE; U08* destPtr = NULL; U16 dataSize = 0; switch( recType ) { case NVM_SYSTEM_RECORD: destPtr = (U08*)&ddSystemGroup.ddSystemRecord; dataSize = sizeof( ddSystemGroup.ddSystemRecord ); break; case NVM_SERVICE_RECORD: destPtr = (U08*)&ddServiceGroup.ddServiceRecord; dataSize = sizeof( ddServiceGroup.ddServiceRecord ); break; case NVM_USAGE_INFO_RECORD: destPtr = (U08*)&ddUsageInfoGroup.ddUsageInfoRecord; dataSize = sizeof( ddUsageInfoGroup.ddUsageInfoRecord ); break; default: break; } if ( (destPtr != NULL ) && ( bufferAddress != NULL ) && ( dataSize > 0 ) ) { memcpy( destPtr, bufferAddress, dataSize ); result = TRUE; } return result; } BOOL setNVMCalRecord( DD_CAL_REC_TYPE calRecType, U08* bufferAddress, U08 idx ) { BOOL result = FALSE; U08* destPtr = NULL; U16 dataSize = 0; switch (calRecType) { case DD_CAL_RECORD_PRESSURE_SENSOR: destPtr = (U08*)&ddCalibrationGroup.ddCalibrationRecord.presSensorsCalRecord.pressureSensors[ idx ]; dataSize = sizeof( ddCalibrationGroup.ddCalibrationRecord.presSensorsCalRecord.pressureSensors[ 0 ] ); break; case DD_CAL_RECORD_TEMPERATURE_SENSOR: destPtr = (U08*)&ddCalibrationGroup.ddCalibrationRecord.tempSensorsCalRecord.tempSensors[ idx ]; dataSize = sizeof( ddCalibrationGroup.ddCalibrationRecord.tempSensorsCalRecord.tempSensors[ 0 ] ); break; case DD_CAL_RECORD_CONCENTRATE_PUMP: destPtr = (U08*)&ddCalibrationGroup.ddCalibrationRecord.concentratePumpsRecord.concentratePumps[ idx ]; dataSize = sizeof( ddCalibrationGroup.ddCalibrationRecord.concentratePumpsRecord.concentratePumps[ 0 ] ); break; case DD_CAL_RECORD_D12_PUMP: destPtr = (U08*)&ddCalibrationGroup.ddCalibrationRecord.d12DialysatePumpRecord; dataSize = sizeof( ddCalibrationGroup.ddCalibrationRecord.d12DialysatePumpRecord ); break; case DD_CAL_RECORD_D48_PUMP: destPtr = (U08*)&ddCalibrationGroup.ddCalibrationRecord.d48DialysatePumpRecord; dataSize = sizeof( ddCalibrationGroup.ddCalibrationRecord.d48DialysatePumpRecord ); break; case DD_CAL_RECORD_ACID_CONCENTRATE: destPtr = (U08*)&ddCalibrationGroup.ddCalibrationRecord.acidConcentratesRecord.acidConcentrate[ idx ]; dataSize = sizeof( ddCalibrationGroup.ddCalibrationRecord.acidConcentratesRecord.acidConcentrate[ 0 ] ); break; case DD_CAL_RECORD_BICARB_CONCENTRATE: destPtr = (U08*)&ddCalibrationGroup.ddCalibrationRecord.bicarbConcentratesRecord.bicarbConcentrate[ idx ]; dataSize = sizeof( ddCalibrationGroup.ddCalibrationRecord.bicarbConcentratesRecord.bicarbConcentrate[ 0 ] ); break; case DD_CAL_RECORD_ACCELEROMETER_SENSOR: destPtr = (U08*)&ddCalibrationGroup.ddCalibrationRecord.accelerometerSensorCalRecord; dataSize = sizeof( ddCalibrationGroup.ddCalibrationRecord.accelerometerSensorCalRecord ); break; case DD_CAL_RECORD_BLLOD_LEAK_SENSOR: destPtr = (U08*)&ddCalibrationGroup.ddCalibrationRecord.bloodLeakSensorCalRecord; dataSize = sizeof( ddCalibrationGroup.ddCalibrationRecord.bloodLeakSensorCalRecord ); break; default: break; } if ( (destPtr != NULL ) && ( bufferAddress != NULL ) && ( dataSize > 0 ) ) { memcpy( destPtr, bufferAddress, dataSize ); result = TRUE; } return result; } BOOL setNVMInstitRecord( DD_INSTIT_REC_TYPE institRecType, U08 *valuePtr ) { BOOL result = FALSE; U08 *destPtr = NULL; U16 dataSize = 0; switch (institRecType) { case DD_INSTIT_MIN_DIALYSATE_FLOW_MLPM: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.minDialysateFlowMLPM; dataSize = sizeof(U32); break; case DD_INSTIT_MAX_DIALYSATE_FLOW_MLPM: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.maxDialysateFlowMLPM; dataSize = sizeof(U32); break; case DD_INSTIT_MIN_DIALYSATE_TEMP_C: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.minDialysateTempC; dataSize = sizeof(F32); break; case DD_INSTIT_MAX_DIALYSATE_TEMP_C: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.maxDialysateTempC; dataSize = sizeof(F32); break; case DD_INSTIT_MIN_ACID_CONCENTRATE: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.minAcidConcentrate; dataSize = sizeof(U32); break; case DD_INSTIT_MAX_ACID_CONCENTRATE: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.maxAcidConcentrate; dataSize = sizeof(U32); break; case DD_INSTIT_MIN_BICARB_CARTRIDGE_SIZE_G: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.minBicarbCartridgeSizeG; dataSize = sizeof(U32); break; case DD_INSTIT_MAX_BICARB_CARTRIDGE_SIZE_G: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.maxBicarbCartridgeSizeG; dataSize = sizeof(U32); break; case DD_INSTIT_MIN_SODIUM_MEQL: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.minSodiumMEQL; dataSize = sizeof(U32); break; case DD_INSTIT_MAX_SODIUM_MEQL: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.maxSodiumMEQL; dataSize = sizeof(U32); break; case DD_INSTIT_MIN_BICARBONATE_MEQL: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.minBicarbonateMEQL; dataSize = sizeof(U32); break; case DD_INSTIT_MAX_BICARBONATE_MEQL: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.maxBicarbonateMEQL; dataSize = sizeof(U32); break; case DD_INSTIT_MIN_RO_REJECTION_RATIO_PCT: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.minRORejectionRatioPCT; dataSize = sizeof(U32); break; case DD_INSTIT_DISINFECTION_FREQUENCY: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.disinfectionFrequency; dataSize = sizeof(U32); break; case DD_INSTIT_DISINFECTION_CYCLE_TIME: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.disinfectionCycleTime; dataSize = sizeof(F32); break; case DD_INSTIT_MIN_INLET_WATER_COND_ALARM_LIMIT_USPCM: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.minInletWaterCondAlarmLimitUSPCM; dataSize = sizeof(F32); break; case DD_INSTIT_MAX_INLET_WATER_COND_ALARM_LIMIT_USPCM: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.maxInletWaterCondAlarmLimitUSPCM; dataSize = sizeof(F32); break; case DD_INSTIT_ACID_CONCENTRATE_JUG_SIZE_L: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.acidConcentrateJugSizeL; dataSize = sizeof(F32); break; case DD_INSTIT_MIN_ACID_ALARM_LIMIT_PCT: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.minAcidAlarmLimitPCT; dataSize = sizeof(F32); break; case DD_INSTIT_MIN_BICARB_ALARM_LIMIT_PCT: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.minBicarbAlarmLimitPCT; dataSize = sizeof(F32); break; case DD_INSTIT_POST_TREAT_DRAIN_OPTION: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.postTreatDrainOption; dataSize = sizeof(U32); break; case DD_INSTIT_POST_TREAT_DRY_BICARB_OPTION: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.postTreatDryBicarbOption; dataSize = sizeof(U32); break; case DD_INSTIT_CALIBRATION_TIME: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.calibrationTime; dataSize = sizeof(U32); break; case DD_INSTIT_CRC: destPtr = (U08*)&ddInstitutionalGroup.ddInstitutionalRecord.crc; dataSize = sizeof(U32); break; default: break; } if ( ( NULL != destPtr ) && ( NULL != valuePtr ) && ( dataSize > 0 ) ) { memcpy( destPtr, valuePtr, dataSize ); // Update CRC after every change ddInstitutionalGroup.crc = crc16( (U08*)&ddInstitutionalGroup.ddInstitutionalRecord, sizeof( DD_INSTITUTIONAL_RECORD_T ) - sizeof(U16) ); result = TRUE; } return result; } void sendNVEvent( NVM_RECORD_TYPE_T recType, U32 d1, U32 d2 ) { SEND_EVENT_WITH_2_U32_DATA( RECORDS_SPECS[recType].nvEvent, d1, d2 ); } /*********************************************************************//** * @brief * The updateNVSelfTestResult function updates the NV data management * self-test result with the provided value. * @details \b Inputs: none * @details \b Outputs: nvmSelfTestResult * @param result Self-test result to be updated * @return none *************************************************************************/ void updateNVSelfTestResult( SELF_TEST_STATUS_T result ) { nvmSelfTestResult = result; } /*********************************************************************//** * @brief * The updateNVSelfTestState function updates the NV data management * self-test state with the provided value. * @details \b Inputs: none * @details \b Outputs: nvmSelfTestState * @param state Self-test state to be updated * @return none *************************************************************************/ void updateNVSelfTestState( NVM_SELF_TEST_STATE_T state ) { nvmSelfTestState = state; } /*********************************************************************//** * @brief * The updateSelfTestReadRecordsFlag function updates the flag that * indicates whether self-test record reading is complete. * @details \b Inputs: none * @details \b Outputs: isSelfTestReadRecordsDone * @param value Flag value to be updated * @return none *************************************************************************/ void updateSelfTestReadRecordsFlag( BOOL value ) { isSelfTestReadRecordsDone = value; } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The updateNVRecordCRC function overrides the CRC value of * the selected non-volatile record and schedules it for writing. * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT if invalid job is * selected * @details \b Inputs: none * @details \b Outputs: ddCalibrationGroup, ddSystemGroup, * ddServiceGroup, ddInstitutionalGroup, ddUsageInfoGroup * @param job The job whose CRC needs to be overridden * @param crc The CRC value to be set * @return TRUE if the job was scheduled successfully otherwise FALSE *************************************************************************/ BOOL updateNVRecordCRC( U32 job, U16 crc ) { BOOL status = FALSE; NVM_RECORD_TYPE_T nvJob = (NVM_RECORD_TYPE_T)job; switch( nvJob ) { case NVM_CALIBRATION_RECORD: ddCalibrationGroup.crc = crc; break; case NVM_SYSTEM_RECORD: ddSystemGroup.ddSystemRecord.crc = crc; break; case NVM_SERVICE_RECORD: ddServiceGroup.ddServiceRecord.crc = crc; break; case NVM_INSTITUTIONAL_RECORD: ddInstitutionalGroup.ddInstitutionalRecord.crc = crc; break; case NVM_USAGE_INFO_RECORD: ddUsageInfoGroup.ddUsageInfoRecord.crc = crc; break; default: // Software fault SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, ( U32 )nvJob ); break; } status = enqueueEraseAndWriteSector( nvJob ); return status; } /**@}*/