/************************************************************************** * * 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 NVMessagingDD.c * * @author (original) Arpita Srivastava * @date (original) 31-Mar-2026 * ***************************************************************************/ #include // For ceilf function #include "NVJobQ.h" #include "NVMessagingDD.h" #include "NVRecordsDD.h" #include "OperationModes.h" #include "TaskGeneral.h" #include "Timers.h" #include "Utilities.h" // For crc calculation /** * @addtogroup NVMessagingDD * @{ */ // ********** private definitions ********** #define NEW_CAL_AVAILABLE_SIGNAL_TIMEOUT_MS (1 * MS_PER_SECOND) ///< New calibration available signal timeout in milliseconds. #define NUM_OF_BYTES_PER_RECORD_PAYLOAD ( MAX_MSG_PAYLOAD_SIZE - ( sizeof( U32 ) ) ) ///< Number of bytes per calibration payload. #define RECORD_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. /// NVM send records states typedef enum NVM_Send_Records_States { NVM_SEND_RECORD_STATE_IDLE = 0, ///< NVM process record idle state. NVM_SEND_RECORD_STATE_SEND, ///< NVM process record send record state. NUM_OF_NVM_SEND_RECORD_STATES ///< Number of NVM process records state. } SEND_RECORD_STATE_T; /// NVM receive records states typedef enum NVM_Receive_Records_States { NVM_RECEIVE_RECORD_STATE_IDLE = 0, ///< NVM receive record idle. NVM_RECEIVE_RECORD_STATE_RECEIVE, ///< NVM receive record receive. NUM_OF_NVM_RECEIVE_RECORD_STATES ///< Number of NVM receive record. } RECEIVE_RECORD_STATE_T; /// DD NVM Record Payload typedef struct { U32 idx; ///< Index of sensor / pump / concentrates U08 data[ NUM_OF_BYTES_PER_RECORD_PAYLOAD ]; ///< Data to be sent } DD_NVM_SEND_RECORD_PAYLOAD_T; // ********** private data ********** static SEND_RECORD_STATE_T nvMExecSendRecordState; ///< NVM exec process record state. static RECEIVE_RECORD_STATE_T nvmExecreceiveRecordState; ///< NVM exec receive record state. static U32 newRecordStartTimer; ///< New record availability start timer. static BOOL isNewCalRecordAvailable; ///< Signal to indicate whether a new NVM data is available. static NVM_RECORD_TYPE_T recordToPublish; ///< Record type which is being processed currently for sending static NVM_RECORD_TYPE_T nvPublishRecordType; ///< Used to index over isPublishRecordRequested in the idle state static NVM_RECORD_TYPE_T currentRxRecordType; ///< Record type which is being processed currently for receiving static U32 recordSendDataIntervalCounter; ///< Record data send to CAN bust interval counter. static U32 previousRecordMessageNum; ///< Record previous message number. static U32 recordUpdateAddress; ///< DD record update address for all the write operations. static U32 recordReceiveStartTime; ///< Time stamp the calibration/service was received. static BOOL isPublishRecordRequested[ NUM_OF_NVM_RECORD_TYPES ]; ///< Record state machine publish request flag. // ********** private function prototypes ********** // Process record functions static SEND_RECORD_STATE_T handleExecSendRecordIdleState( void ); static SEND_RECORD_STATE_T handleExecSendRecordSendState( void ); static BOOL sendDDSystemRecord( void ); static BOOL sendDDServiceRecord( void ); static BOOL sendDDCalibrationRecord( void ); static BOOL sendDDInstitutionalRecord( void ); static BOOL sendDDUsageInfoRecord( void ); static BOOL sendDDRecord( MSG_ID_T msgId, U32 idx, U32 length, U08* recordAddress ); static BOOL receiveDDRecord( MESSAGE_T *message ); static BOOL verifyAndSaveReceivedRecord( NVM_RECORD_TYPE_T job, U32 currentMessage, U32 totalMessages, U32 length, U08 *addressPtr ); static NVM_RECORD_TYPE_T getNVMRecordJobState( MSG_ID_T msgID ); static MSG_ID_T getNVMRecordResponseMsgId (NVM_RECORD_TYPE_T job ); static void monitorNewCalSignal( void ); /*********************************************************************//** * @brief * The initNVMessagingDD function initializes NV messaging related * states and variables. It resets send and receive states, flags, * counters, and timing values. * @details \b Inputs: none * @details \b Outputs: nvMExecSendRecordState, * nvmExecreceiveRecordState, newRecordStartTimer, * isNewCalRecordAvailable, nvPublishRecordType, * isPublishRecordRequested, recordPublishMsgCount, * recordPublishTotalMsgs, recordSendDataIntervalCounter, * previousRecordMessageNum, recordReceiveStartTime * @return none *************************************************************************/ void initNVMessagingDD( void ) { nvMExecSendRecordState = NVM_SEND_RECORD_STATE_IDLE; nvmExecreceiveRecordState = NVM_RECEIVE_RECORD_STATE_IDLE; newRecordStartTimer = 0; isNewCalRecordAvailable = FALSE; nvPublishRecordType = NVM_SYSTEM_RECORD; isPublishRecordRequested[ NVM_SYSTEM_RECORD ] = FALSE; isPublishRecordRequested[ NVM_SERVICE_RECORD ] = FALSE; isPublishRecordRequested[ NVM_CALIBRATION_RECORD ] = FALSE; isPublishRecordRequested[ NVM_INSTITUTIONAL_RECORD ] = FALSE; isPublishRecordRequested[ NVM_USAGE_INFO_RECORD ] = FALSE; recordSendDataIntervalCounter = 0; previousRecordMessageNum = 0; recordReceiveStartTime = 0; } /*********************************************************************//** * @brief * The execNVMPSendreceiveDDRecord function executes the NV record processing * state machines. It handles send and receive operations and monitors * for timeouts and calibration signals. * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT if invalid send state * @details \b Inputs: nvMExecSendRecordState, * nvmExecreceiveRecordState, recordReceiveStartTime * @details \b Outputs: nvMExecSendRecordState, * nvmExecreceiveRecordState * @return none *************************************************************************/ void execNVMPSendReceiveRecord( void ) { switch ( nvMExecSendRecordState ) { case NVM_SEND_RECORD_STATE_IDLE: nvMExecSendRecordState = handleExecSendRecordIdleState(); break; case NVM_SEND_RECORD_STATE_SEND: nvMExecSendRecordState = handleExecSendRecordSendState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_NVM_INVALID_EXEC_SEND_STATE, ( U32 )nvMExecSendRecordState ); nvMExecSendRecordState = NVM_SEND_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 ( nvmExecreceiveRecordState != NVM_RECEIVE_RECORD_STATE_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( NVM_OPERATION_READ, currentRxRecordType ); nvmExecreceiveRecordState = NVM_RECEIVE_RECORD_STATE_IDLE; } } // Check the calibration signal monitorNewCalSignal(); } /*********************************************************************//** * @brief * The setNewCalibrationRecordAvailable function updates the flag * indicating new NV record availability. * @details \b Inputs: none * @details \b Outputs: isNewCalRecordAvailable * @param value Flag value to be updated * @return none *************************************************************************/ void setNewCalibrationRecordAvailable( BOOL isAvailable) { isNewCalRecordAvailable = isAvailable; } /*********************************************************************//** * @brief * The getNewCalRecordAvalability function gets the status of new * calibration record availability. * @details \b Inputs: isNewCalRecordAvailable * @details \b Outputs: none * @return TRUE if new calibration record is available otherwise FALSE *************************************************************************/ BOOL getNewCalRecordAvalability( void ) { return isNewCalRecordAvailable; } /*********************************************************************//** * @brief * The startNewCalRecordAvailableTimer function starts the time for * new calibration record availability. * @details \b Inputs: none * @details \b Outputs: newRecordStartTimer * @return none *************************************************************************/ void startNewCalRecordAvailableTimer( void ) { newRecordStartTimer = getMSTimerCount(); } /*********************************************************************//** * @brief * The handleExecSendRecordIdleState function handles the idle state of * the exec send record state machine. It prepares record data for * publishing when requested. * @details \b Inputs: isPublishRecordRequested, * nvPublishRecordType * @details \b Outputs: isPublishRecordRequested, * recordPublishTotalMsgs, recordPublishMsgCount, * recordSendDataIntervalCounter, nvPublishRecordType, * recordToPublish * @return state next state of the state machine *************************************************************************/ static SEND_RECORD_STATE_T handleExecSendRecordIdleState( void ) { SEND_RECORD_STATE_T state = NVM_SEND_RECORD_STATE_IDLE; if ( TRUE == isPublishRecordRequested[ nvPublishRecordType ] ) { recordToPublish = ( NVM_RECORD_TYPE_T )nvPublishRecordType; // Set the publish flag to FALSE isPublishRecordRequested[ recordToPublish ] = FALSE; // 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 recordSendDataIntervalCounter = RECORD_DATA_SEND_INTERVAL_COUNT; state = NVM_SEND_RECORD_STATE_SEND; } nvPublishRecordType = ( NVM_RECORD_TYPE_T )( nvPublishRecordType + 1 ); if( nvPublishRecordType >= NUM_OF_NVM_RECORD_TYPES ) { nvPublishRecordType = NVM_SYSTEM_RECORD; } return state; } /*********************************************************************//** * @brief * The handleExecSendRecordSendState function handles the send state of * the exec send record state machine. It sends record data in chunks * based on timing and message count. * @details \b Inputs: recordPublishTotalMsgs, * recordPublishMsgCount, recordSendDataIntervalCounter, * recordToPublish * @details \b Outputs: recordPublishMsgCount, * recordSendDataIntervalCounter * @return state next state of the state machine *************************************************************************/ static SEND_RECORD_STATE_T handleExecSendRecordSendState( void ) { SEND_RECORD_STATE_T state = NVM_SEND_RECORD_STATE_SEND; // If it is time to send data if ( ++recordSendDataIntervalCounter >= RECORD_DATA_SEND_INTERVAL_COUNT ) { switch(recordToPublish) { case NVM_SYSTEM_RECORD: sendDDSystemRecord(); break; case NVM_SERVICE_RECORD: sendDDServiceRecord(); break; case NVM_CALIBRATION_RECORD: sendDDCalibrationRecord(); break; case NVM_INSTITUTIONAL_RECORD: sendDDInstitutionalRecord(); break; case NVM_USAGE_INFO_RECORD: sendDDUsageInfoRecord(); break; default: break; } recordSendDataIntervalCounter = 0; state = NVM_SEND_RECORD_STATE_IDLE; } return state; } static BOOL sendDDSystemRecord( void ) { BOOL sysStatus = FALSE; DD_SYSTEM_RECORD_T* systemRecord; sysStatus = getNVMRecord( GET_SYSTEM_RECORD, (U08*)&systemRecord, sizeof( DD_SYSTEM_RECORD_T ), 0, ALARM_ID_DD_NVM_INVALID_SYSTEM_RECORD_CRC ); sysStatus = sendDDRecord( MSG_ID_DD_NVM_SYSTEM_RECORD_RESPONSE, 0, sizeof(DD_SYSTEM_RECORD_T), (U08*)&systemRecord ); return sysStatus; } static BOOL sendDDServiceRecord( void ) { BOOL serStatus = FALSE; DD_SERVICE_RECORD_T* serviceRecord; serStatus = getNVMRecord( GET_SERVICE_RECORD, (U08*)&serviceRecord, sizeof( DD_SERVICE_RECORD_T ), 0, ALARM_ID_DD_NVM_INVALID_SYSTEM_RECORD_CRC ); serStatus = sendDDRecord( MSG_ID_DD_NVM_SERVICE_RECORD_RESPONSE, 0, sizeof(DD_SERVICE_RECORD_T), (U08*)&serviceRecord ); return serStatus; } static BOOL sendDDCalibrationRecord( void ) { BOOL calStatus = FALSE; int i; DD_PRES_SENSORS_CAL_RECORD_T* pressure; calStatus = getNVMRecord( GET_CAL_PRESSURE_SENOSRS, (U08*)&pressure, sizeof( DD_PRES_SENSORS_CAL_RECORD_T ), NUM_OF_PRESSURE_SENSORS, ALARM_ID_DD_NVM_INVALID_CAL_PRESSURE_SENSOR ); for ( i = 0; i < NUM_OF_PRESSURE_SENSORS; i++ ) { calStatus = sendDDRecord( MSG_ID_DD_NVM_CAL_PRESSURE_SENSOR_RESPONSE, i, sizeof(DD_PRES_SENSORS_CAL_RECORD_T), (U08*)&pressure->pressureSensors[ i ] ); } DD_TEMP_SENSORS_CAL_RECORD_T* temperature; calStatus = getNVMRecord( GET_CAL_TEMP_SENSORS, (U08*)&temperature, sizeof( DD_TEMP_SENSORS_CAL_RECORD_T ), NUM_OF_TEMPERATURE_SENSORS, ALARM_ID_DD_NVM_INVALID_CAL_TEMP_SENSOR ); for ( i = 0; i < NUM_OF_TEMPERATURE_SENSORS; i++ ) { calStatus = sendDDRecord( MSG_ID_DD_NVM_CAL_TEMP_SENSOR_RESPONSE, i, sizeof(DD_PRES_SENSORS_CAL_RECORD_T), (U08*)&temperature->tempSensors[ i ] ); } DD_CONC_PUMPS_CAL_RECORD_T* concPump; calStatus = getNVMRecord( GET_CAL_CONCENTRATE_PUMPS_RECORD, (U08*)&concPump, sizeof( DD_CONC_PUMPS_CAL_RECORD_T ), NUM_OF_CONCENTRATE_PUMPS, ALARM_ID_DD_NVM_INVALID_CAL_CONC_PUMP ); for ( i = 0; i < NUM_OF_CONCENTRATE_PUMPS; i++ ) { calStatus = sendDDRecord( MSG_ID_DD_NVM_CAL_CONC_PUMP_RESPONSE, i, sizeof(DD_CONC_PUMPS_CAL_RECORD_T), (U08*)&concPump->concentratePumps[ i ] ); } DD_D12_DIALYSATE_PUMP_RECORD_T* d12Pump; calStatus = getNVMRecord( GET_CAL_D12_PUMP_RECORD, (U08*)&d12Pump, sizeof( DD_D12_DIALYSATE_PUMP_RECORD_T ), 0, ALARM_ID_DD_NVM_INVALID_CAL_D12_PUMP ); calStatus = sendDDRecord( MSG_ID_DD_NVM_CAL_D12_PUMP_RESPONSE, 0, sizeof(DD_D12_DIALYSATE_PUMP_RECORD_T), (U08*)&d12Pump ); DD_D48_DIALYSATE_PUMP_RECORD_T* d48Pump; calStatus = getNVMRecord( GET_CAL_D48_PUMP_RECORD, (U08*)&d48Pump, sizeof( DD_D48_DIALYSATE_PUMP_RECORD_T ), 0, ALARM_ID_DD_NVM_INVALID_CAL_D48_PUMP ); calStatus = sendDDRecord( MSG_ID_DD_NVM_CAL_D48_PUMP_RESPONSE, 0, sizeof(DD_D48_DIALYSATE_PUMP_RECORD_T), (U08*)&d48Pump ); DD_ACID_CONCENTRATES_RECORD_T* acidConc; calStatus = getNVMRecord( GET_CAL_ACID_CONCENTREATES, (U08*)&acidConc, sizeof( DD_ACID_CONCENTRATES_RECORD_T ), NUM_OF_CAL_DATA_ACID_CONCENTRATES, ALARM_ID_DD_NVM_INVALID_CAL_ACID_CONCENTRATE ); for ( i = 0; i < NUM_OF_CAL_DATA_ACID_CONCENTRATES; i++ ) { calStatus = sendDDRecord( MSG_ID_DD_NVM_CAL_ACID_CONCENTRATE_RESPONSE, i, sizeof(DD_ACID_CONCENTRATES_RECORD_T), (U08*)&acidConc->acidConcentrate[ i ] ); } DD_BICARB_CONCENTRATES_RECORD_T* bicarbConc; calStatus |= getNVMRecord( GET_CAL_BICARB_CONCENTRATES, (U08*)&bicarbConc, sizeof( DD_BICARB_CONCENTRATES_RECORD_T ), NUM_OF_CAL_DATA_BICARB_CONCENTRATES, ALARM_ID_DD_NVM_INVALID_CAL_BICARB_CONCENTRATE ); for ( i = 0; i < NUM_OF_CAL_DATA_BICARB_CONCENTRATES; i++ ) { calStatus = sendDDRecord( MSG_ID_DD_NVM_CAL_BICARB_CONCENTRATE_RESPONSE, i, sizeof(DD_BICARB_CONCENTRATES_RECORD_T), (U08*)&bicarbConc->bicarbConcentrate[ i ] ); } DD_ACCEL_SENSOR_CAL_RECORD_T* accelerometer; calStatus = getNVMRecord( GET_CAL_ACCEL_SENSORS, (U08*)&accelerometer, sizeof( DD_ACCEL_SENSOR_CAL_RECORD_T ), 0, ALARM_ID_DD_NVM_INVALID_CAL_ACCEL_SENSOR ); calStatus = sendDDRecord( MSG_ID_DD_NVM_CAL_ACCEL_SENSOR_RESPONSE, 0, sizeof(DD_ACCEL_SENSOR_CAL_RECORD_T), (U08*)&accelerometer ); DD_BLOOD_LEAK_SENSOR_CAL_RECORD_T* bloodLeak; calStatus = getNVMRecord( GET_CAL_BLOOD_LEAK_SENSOR, (U08*)&bloodLeak, sizeof( DD_BLOOD_LEAK_SENSOR_CAL_RECORD_T ), 0, ALARM_ID_DD_NVM_INVALID_CAL_BLOOD_LEAK_SENSOR ); calStatus = sendDDRecord( MSG_ID_DD_NVM_CAL_BLOOD_LEAK_SENSOR_RESPONSE, 0, sizeof(DD_BLOOD_LEAK_SENSOR_CAL_RECORD_T), (U08*)&bloodLeak ); return calStatus; } static BOOL sendDDInstitutionalRecord( void ) { BOOL institStatus = FALSE; DD_INSTITUTIONAL_RECORD_T* institRecord; institStatus = getNVMRecord( GET_INSTITUTIONAL_RECORD, (U08*)&institRecord, sizeof( DD_INSTITUTIONAL_RECORD_T ), 0, ALARM_ID_DD_NVM_INVALID_INSTITUTIONAL_RECORD_CRC ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_MIN_DIALYSATE_FLOW_MLPM, sizeof(U32), (U08*)&institRecord->minDialysateFlowMLPM ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_MAX_DIALYSATE_FLOW_MLPM, sizeof(U32), (U08*)&institRecord->maxDialysateFlowMLPM ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_MIN_DIALYSATE_TEMP_C, sizeof(F32), (U08*)&institRecord->minDialysateTempC ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_MAX_DIALYSATE_TEMP_C, sizeof(F32), (U08*)&institRecord->maxDialysateTempC ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_MIN_ACID_CONCENTRATE, sizeof(U32), (U08*)&institRecord->minAcidConcentrate ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_MAX_ACID_CONCENTRATE, sizeof(U32), (U08*)&institRecord->maxAcidConcentrate ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_MIN_BICARB_CARTRIDGE_SIZE_G, sizeof(U32), (U08*)&institRecord->minBicarbCartridgeSizeG ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_MAX_BICARB_CARTRIDGE_SIZE_G, sizeof(U32), (U08*)&institRecord->maxBicarbCartridgeSizeG ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_MIN_SODIUM_MEQL, sizeof(U32), (U08*)&institRecord->minSodiumMEQL ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_MAX_SODIUM_MEQL, sizeof(U32), (U08*)&institRecord->maxSodiumMEQL ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_MIN_BICARBONATE_MEQL, sizeof(U32), (U08*)&institRecord->minBicarbonateMEQL ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_MAX_BICARBONATE_MEQL, sizeof(U32), (U08*)&institRecord->maxBicarbonateMEQL ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_MIN_RO_REJECTION_RATIO_PCT, sizeof(U32), (U08*)&institRecord->minRORejectionRatioPCT ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_DISINFECTION_FREQUENCY, sizeof(F32), (U08*)&institRecord->disinfectionFrequency ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_DISINFECTION_CYCLE_TIME, sizeof(F32), (U08*)&institRecord->disinfectionCycleTime ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_MIN_INLET_WATER_COND_ALARM_LIMIT_USPCM, sizeof(F32), (U08*)&institRecord->minInletWaterCondAlarmLimitUSPCM ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_MAX_INLET_WATER_COND_ALARM_LIMIT_USPCM, sizeof(F32), (U08*)&institRecord->maxInletWaterCondAlarmLimitUSPCM ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_ACID_CONCENTRATE_JUG_SIZE_L, sizeof(F32), (U08*)&institRecord->acidConcentrateJugSizeL ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_MIN_ACID_ALARM_LIMIT_PCT, sizeof(F32), (U08*)&institRecord->minAcidAlarmLimitPCT ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_MIN_BICARB_ALARM_LIMIT_PCT, sizeof(F32), (U08*)&institRecord->minBicarbAlarmLimitPCT ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_POST_TREAT_DRAIN_OPTION, sizeof(U32), (U08*)&institRecord->postTreatDrainOption ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_POST_TREAT_DRY_BICARB_OPTION, sizeof(U32), (U08*)&institRecord->postTreatDryBicarbOption ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_CALIBRATION_TIME, sizeof(U32), (U08*)&institRecord->calibrationTime ); institStatus = sendDDRecord( MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE, DD_INSTIT_CRC, sizeof(U16), (U08*)&institRecord->crc ); return institStatus; } static BOOL sendDDUsageInfoRecord( void ) { BOOL usageStatus = FALSE; DD_USAGE_INFO_RECORD_T* usageInfoRecord; usageStatus = getNVMRecord( GET_USAGE_RECORD, (U08*)&usageInfoRecord, sizeof( DD_USAGE_INFO_RECORD_T ), 0, ALARM_ID_DD_NVM_INVALID_USAGE_RECORD_CRC ); usageStatus = sendDDRecord( MSG_ID_DD_NVM_USAGE_INFO_RECORD_RESPONSE, 0, sizeof(DD_USAGE_INFO_RECORD_T), (U08*)&usageInfoRecord ); return usageStatus; } /*********************************************************************//** * @brief * The sendDDRecord function sends a DD record payload over communication. * It prepares the payload and queues it for transmission. * @details \b Inputs: none * @details \b Outputs: none * @param msgId Message Id for response record * @param payloadCurrNum Current payload number * @param payloadTotalNum Total number of payloads * @param length Buffer length to be written * @param recordAddress Start address of the record data * @return TRUE if msg successfully queued for transmit, FALSE if not *************************************************************************/ static BOOL sendDDRecord( MSG_ID_T msgId, U32 idx, U32 length, U08* recordAddress ) { BOOL result = FALSE; DD_NVM_SEND_RECORD_PAYLOAD_T payload; payload.idx = idx; memcpy( payload.data, recordAddress, length ); result = sendMessage( msgId, COMM_BUFFER_OUT_DD_CAN_PC, (U08 *)&payload, sizeof( DD_NVM_SEND_RECORD_PAYLOAD_T ) ); return result; } /*********************************************************************//** * @brief * The receiveDDRecord function processes a received DD record message. * It extracts payload information and verifies the received data before * saving it. * @details \b Inputs: none * @details \b Outputs: none * @param message Pointer to received message structure * @return TRUE if the record is successfully verified and saved, * otherwise FALSE *************************************************************************/ static BOOL receiveDDRecord( MESSAGE_T *message ) { BOOL status = FALSE; U32 currentMessage; U32 totalMessages; U32 payloadLength; U08* payloadPtr = message->payload; U08 minPayloadLen = ( sizeof(currentMessage) + sizeof(totalMessages) + sizeof(payloadLength) ); MSG_ID_T msgID = (MSG_ID_T)message->hdr.msgID; NVM_RECORD_TYPE_T job = getNVMRecordJobState( msgID ); // To proceed, the payload length should be valid. And, if it is a service job, then DD mode should also be in service. if ( message->hdr.payloadLen >= minPayloadLen ) { memcpy(¤tMessage, payloadPtr, sizeof(U32)); payloadPtr += sizeof(U32); memcpy(&totalMessages, payloadPtr, sizeof(U32)); payloadPtr += sizeof(U32); memcpy(&payloadLength, payloadPtr, sizeof(U32)); payloadPtr += sizeof(U32); status = verifyAndSaveReceivedRecord( job, currentMessage, totalMessages, payloadLength, payloadPtr ); } return status; } /*********************************************************************//** * @brief * The verifyAndSaveReceivedRecord function receives the record sent * from Dialin, assembles it, validates it using CRC, and schedules a * write to NV memory if valid. * @details \b Inputs: nvmExecreceiveRecordState, * @details \b Outputs: nvmExecreceiveRecordState, * recordReceiveStartTime, previousRecordMessageNum, * recordUpdateAddress, currentRxRecordType, * @param job The job that has to be received and written * @param currentMessage Current message number received from Dialin * @param totalMessages Total number of messages from Dialin * @param length Message length in bytes * @param addressPtr Address to the beginning of received data from Dialin * @return TRUE if the request was successfully registered *************************************************************************/ static BOOL verifyAndSaveReceivedRecord( NVM_RECORD_TYPE_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 ) && ( NVM_RECEIVE_RECORD_STATE_IDLE == nvmExecreceiveRecordState ) ) { nvmExecreceiveRecordState = NVM_RECEIVE_RECORD_STATE_RECEIVE; currentRxRecordType = job; recordReceiveStartTime = getMSTimerCount(); previousRecordMessageNum = 0; recordUpdateAddress = 0; } // Check if there is still a message left to be received if ( ( NVM_RECEIVE_RECORD_STATE_RECEIVE == nvmExecreceiveRecordState ) && ( currentMessage <= totalMessages ) ) { // Check if the current message is different from the previous message by 1 if ( RECORD_DATA_MAX_MESSAGE_DFFIRENCE == ( currentMessage - previousRecordMessageNum ) ) { // Define a pointer that points to the temporary receive record PROCESS_RECORD_SPECS_T tempRxSpec = getTemporaryRxRecord( job ); U08* tempRxPtr = tempRxSpec.structAddressPtr; // Get the DD main nvm record PROCESS_RECORD_SPECS_T recordSpec = getProcessRecord( job ); // Offset the pointer to length that we should start writing from tempRxPtr += recordUpdateAddress; memcpy( tempRxPtr, addressPtr, length ); // Check if the current message is total messages // and 0 everything out since we are done writing if ( currentMessage == totalMessages ) { U16 calcCRC = crc16 ( tempRxSpec.structAddressPtr, tempRxSpec.sizeofRecord - 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*)tempRxSpec.structCRCPtr; // Check if calculated CRC matches the Stored CRC if ( ( calcCRC == recordCRC ) ) { _disable_IRQ(); // Copy the valid temporary record into the main record spec memcpy(recordSpec.structAddressPtr, tempRxSpec.structAddressPtr, tempRxSpec.sizeofRecord); _enable_IRQ(); // Enqueue an erase and write of the nvm record status = enqueueEraseAndWriteSector( job ); if( NVM_CALIBRATION_RECORD == job ) { // 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 startNewCalRecordAvailableTimer(); setNewCalibrationRecordAvailable( 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 nvmExecreceiveRecordState = NVM_RECEIVE_RECORD_STATE_IDLE; } else { // CRC match failed, go to idle nvmExecreceiveRecordState = NVM_RECEIVE_RECORD_STATE_IDLE; status = FALSE; } } else { // Update the length as it has successfully been written recordUpdateAddress += length; // Now the current message is the previous message previousRecordMessageNum = currentMessage; } } } return status; } /*********************************************************************//** * @brief * The getNVMRecordJobState function maps the received message ID to * the corresponding NV record job type. * @details \b Inputs: none * @details \b Outputs: none * @param msgID Message ID received * @return job corresponding NV record type *************************************************************************/ static NVM_RECORD_TYPE_T getNVMRecordJobState( MSG_ID_T msgID ) { NVM_RECORD_TYPE_T job; switch ( msgID ) { // case MSG_ID_DD_NVM_RECV_CAL_PRESSURE_SENSOR: // case MSG_ID_DD_NVM_RECV_CAL_TEMP_SENSOR: // case MSG_ID_DD_NVM_RECV_CAL_CONC_PUMP: // case MSG_ID_DD_NVM_RECV_CAL_D12_PUMP: // case MSG_ID_DD_NVM_RECV_CAL_D48_PUMP: // case MSG_ID_DD_NVM_RECV_CAL_ACID_CONCENTRATE: // case MSG_ID_DD_NVM_RECV_CAL_BICARB_CONCENTRATE: // case MSG_ID_DD_NVM_RECV_CAL_ACCEL_SENSOR: // case MSG_ID_DD_NVM_RECV_CAL_BLOOD_LEAK_SENSOR: // job = NVM_CALIBRATION_RECORD; // break; case MSG_ID_UI_DD_NVM_SET_SYSTEM_RECORD: job = NVM_SYSTEM_RECORD; break; case MSG_ID_UI_DD_NVM_SET_SERVICE_RECORD: job = NVM_SERVICE_RECORD; break; case MSG_ID_UI_DD_NVM_SET_INSTITUTIONAL_RECORD: job = NVM_INSTITUTIONAL_RECORD; break; case MSG_ID_UI_DD_NVM_SET_USAGE_INFO_RECORD: job = NVM_USAGE_INFO_RECORD; break; default: // Do nothing for Invalid Input from Dialin break; } return job; } /*********************************************************************//** * @brief * The getNVMRecordResponseMsgId function maps the NV record type to * the corresponding response message ID. * provided * @details \b Inputs: none * @details \b Outputs: none * @param job NV record type * @return msgID corresponding response message ID *************************************************************************/ static MSG_ID_T getNVMRecordResponseMsgId( NVM_RECORD_TYPE_T job ) { MSG_ID_T msgID; switch( job ) { case NVM_CALIBRATION_RECORD: // msgID = MSG_ID_DD_NVM_SEND_CALIBRATION_RECORD; break; case NVM_SYSTEM_RECORD: msgID = MSG_ID_DD_NVM_SYSTEM_RECORD_RESPONSE; break; case NVM_SERVICE_RECORD: msgID = MSG_ID_DD_NVM_SERVICE_RECORD_RESPONSE; break; case NVM_INSTITUTIONAL_RECORD: msgID = MSG_ID_DD_NVM_INSTITUTIONAL_RECORD_RESPONSE; break; case NVM_USAGE_INFO_RECORD: msgID = MSG_ID_DD_NVM_USAGE_INFO_RECORD_RESPONSE; break; default: // Do nothing for Invalid Input from Dialin break; } return msgID; } /*********************************************************************//** * @brief * The monitorNewCalSignal function monitors the new calibration signal * and clears it when the timeout has elapsed. * @details \b Inputs: isNewCalRecordAvailable, * newRecordStartTimer * @details \b Outputs: isNewCalRecordAvailable * @return none *************************************************************************/ static void monitorNewCalSignal( void ) { // Check if new calibration record is available and // the timer has elapsed if ( ( TRUE == didTimeout( newRecordStartTimer, NEW_CAL_AVAILABLE_SIGNAL_TIMEOUT_MS ) ) && ( TRUE == getNewCalRecordAvalability() ) ) { // Clear the new calibration record available flag setNewCalibrationRecordAvailable( FALSE ); } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testDDGetNVRecord function processes a request to publish an NV * record. It validates the payload and sets the publish request flag * for the selected record. * @details \b Inputs: nvMExecSendRecordState * @details \b Outputs: isPublishRecordRequested, * recordToPublish * @param message Pointer to the received message * @return TRUE if the request is accepted otherwise FALSE *************************************************************************/ BOOL testDDGetNVRecord( MESSAGE_T *message ) { BOOL result = FALSE; NVM_RECORD_TYPE_T job; // verify payload length if ( 1 == message->hdr.payloadLen ) { job = ( NVM_RECORD_TYPE_T )message->payload[ 0 ]; // Accept the request only if the send exec is in the idle state if ( ( job < NUM_OF_NVM_RECORD_TYPES ) && ( NVM_SEND_RECORD_STATE_IDLE == nvMExecSendRecordState ) ) { isPublishRecordRequested[ job ] = TRUE; recordToPublish = job; result = TRUE; } } return result; } /*********************************************************************//** * @brief * The testDDSetNVSystemRecord function processes a request to update * the system record. It allows updates only when the system is in * service mode. * @details \b Inputs: none * @details \b Outputs: none * @param message Pointer to the received message * @return TRUE if the record is successfully processed otherwise FALSE *************************************************************************/ BOOL testDDSetNVSystemRecord( MESSAGE_T *message ) { BOOL result = FALSE; // System record can be updated only in service mode if ( DD_MODE_SERV == getCurrentOperationMode() ) { result = receiveDDRecord( message ); } return result; } /*********************************************************************//** * @brief * The testDDSetNVServiceRecord function processes a request to update * the service record. It allows updates only when the system is in * service mode. * @details \b Inputs: none * @details \b Outputs: none * @param message Pointer to the received message * @return TRUE if the record is successfully processed otherwise FALSE *************************************************************************/ BOOL testDDSetNVServiceRecord( MESSAGE_T *message ) { BOOL result = FALSE; // Service record can be updated only in service mode if ( DD_MODE_SERV == getCurrentOperationMode() ) { result = receiveDDRecord( message ); } return result; } /*********************************************************************//** * @brief * The testDDSetNVCalibrationRecord function processes a request to * update the calibration record. It allows updates only when the * system is in service mode. * @details \b Inputs: none * @details \b Outputs: none * @param message Pointer to the received message * @return TRUE if the record is successfully processed otherwise FALSE *************************************************************************/ BOOL testDDSetNVCalibrationRecord( MESSAGE_T *message ) { BOOL result = FALSE; // Calibration record can be updated only in service mode if ( DD_MODE_SERV == getCurrentOperationMode() ) { result = receiveDDRecord( message ); } return result; } /*********************************************************************//** * @brief * The testDDSetNVInstitutionalRecord function processes a request to * update the institutional record. * @details \b Inputs: none * @details \b Outputs: none * @param message Pointer to the received message * @return TRUE if the record is successfully processed otherwise FALSE *************************************************************************/ BOOL testDDSetNVInstitutionalRecord( MESSAGE_T *message ) { BOOL result = FALSE; result = receiveDDRecord( message ); return result; } /*********************************************************************//** * @brief * The testDDSetNVUsageInfoRecord function processes a request to * update the usage information record. * @details \b Inputs: none * @details \b Outputs: none * @param message Pointer to the received message * @return TRUE if the record is successfully processed otherwise FALSE *************************************************************************/ BOOL testDDSetNVUsageInfoRecord( MESSAGE_T *message ) { BOOL result = FALSE; result = receiveDDRecord( message ); return result; } /*********************************************************************//** * @brief * The testSetNVRecordCRCOverride function overrides the CRC value of * the selected non-volatile record and schedules it for writing. * @details \b Inputs: none * @details \b Outputs: none * @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 testSetNVRecordCRCOverride( U32 job, U16 crc ) { BOOL result = FALSE; result = updateNVRecordCRC( job, crc ); return result; } /**@}*/