/************************************************************************** * * Copyright (c) 2019-2022 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 NVDataMgmt.c * * @author (last) Dara Navaei * @date (last) 04-Jan-2022 * * @author (original) Dara Navaei * @date (original) 12-Feb-2020 * ***************************************************************************/ // Includes #include // For memcpy #include // For ceilf function #include "system.h" // For fapi operations #include "F021.h" // For fapi operations #include "MsgQueues.h" #include "NVDataMgmt.h" #include "RTC.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "Timers.h" #include "Utilities.h" /** * @addtogroup NVDataMgmt * @{ */ // Private defines #define QUEUE_MAX_SIZE 20U ///< Max queue size. #define QUEUE_START_INDEX 0U ///< Queue start index. #define MIN_JOBS_NEEDED_FOR_DATA_LOG 3U ///< Min queue required for data log (3). #define MIN_JOBS_NEEDED_FOR_SECTOR_0 4U ///< Min queue count needed to write all (4) records back in sector 0. // The clock frequency comes from HCLK_FREQ and it has to be rounded up to the nearest number #define ROUNDED_HCLK_FREQ FLOAT_TO_INT_WITH_ROUND(HCLK_FREQ) ///< Rounded HCLK for EERPOM clock. #define BANK7_SECTOR_0_31_ENABLE_BIT_MASK 0x0000000F ///< Bank7 sector 0 t0 31 enable mask. #define BANK7_SECTOR_32_63_ENABLE_BIT_MASK 0x00000000 ///< Bank7 sector 32 to 63 enable mask. #define BANK7_SECTOR0_START_ADDRESS 0xF0200000 ///< Bank7 sector 0 start address. #define BANK7_SECTOR0_END_ADDRESS 0xF0203FFF ///< Bank7 sector 0 end address. #define BANK7_SECTOR1_START_ADDRESS 0xF0204000 ///< Bank7 sector 1 start address. #define BANK7_SECTOR1_END_ADDRESS 0xF0207FFF ///< Bank7 sector 1 end address. #define BANK7_SECTOR2_START_ADDRESS 0xF0208000 ///< Bank7 sector 2 start address. #define BANK7_SECTOR2_END_ADDRESS 0xF020BFFF ///< Bank7 sector 2 end address. #define BANK7_SECTOR3_START_ADDRESS 0xF020C000 ///< Bank7 sector 3 start address. #define BANK7_SECTOR3_END_ADDRESS 0xF020FFFF ///< Bank7 sector 3 end address. #define MAX_EEPROM_WRITE_BUFFER_BYTES 16U ///< Max allowed bytes for an EEPROM write (16 bytes). #define MAX_RTC_RAM_OPS_BUFFER_BYTES 64U ///< Max target RTC RAM operations (read/write) bytes. #define MAX_JOB_DATA_SIZE_BYTES 32U ///< Max bytes per job (32 bytes). #define LOG_DATA_START_INDEX 0U ///< Log data start index. #define MAX_NUM_OF_SECTORS_FOR_LOG_DATA 3U ///< Max number of sector (3 sectors). #define MAX_LOG_DATA_PER_SECTOR (((BANK7_SECTOR1_END_ADDRESS + 1) - \ BANK7_SECTOR1_START_ADDRESS) / MAX_JOB_DATA_SIZE_BYTES) ///< Max log data per sector (512 for now). #define MAX_NUM_OF_EVENTS_IN_SECTOR3 (MAX_NUM_OF_SECTORS_FOR_LOG_DATA * MAX_LOG_DATA_PER_SECTOR) ///< Max number of accumulated logs in sector 3 (1536). #define MAX_NUM_OF_EVENTS_IN_SECTOR2 ((MAX_NUM_OF_SECTORS_FOR_LOG_DATA - 1) * MAX_LOG_DATA_PER_SECTOR) ///< Max number of accumulated logs in sector 2 (1024). #define MAX_NUM_OF_EVENTS_IN_SECTOR1 ((MAX_NUM_OF_SECTORS_FOR_LOG_DATA - 2) * MAX_LOG_DATA_PER_SECTOR) ///< Max number of accumulated logs in sector 1 (512). // Data addresses and length in RTC RAM #define LOG_RECORD_START_ADDRESS 0x00000000 // 0 ///< Log record start address in RTC RAM (0). #define HD_TREATMENT_TIME_ADDRESS 0x00000010 // 16 ///< HD treatment time start address in RTC RAM (16). #define DG_CONSUMED_WATER_ADDRESS 0x00000020 // 32 ///< DG water consumption start address in RTC RAM (32). #define SERVICE_RECORD_START_ADDRESS 0x00000030 // 48 ///< Service date record start address in RTC RAM (HD/DG) (48). #define DG_SCHEDULED_RUNS_START_ADDRESS (SERVICE_RECORD_START_ADDRESS + sizeof(DG_SERVICE_GROUP_T)) ///< DG scheduled runs start address in RTC RAM. #define DG_HEATERS_INFO_START_ADDRESS (DG_SCHEDULED_RUNS_START_ADDRESS + sizeof(DG_HEATERS_INFO_GROUP_T))///< DG heaters info start address in RTC RAM. #define SW_CONFIGS_START_ADDRESS 0x00000100 // 256 ///< Software configurations start address in RTC RAM. #define COMMAND_TIME_OUT 500U ///< Timeout for an EEPROM or RTC command in ms. /// EEPROM functions use the buffer length as the size of U32. So before send the length to any of FAPI functions, it should be divided by 4. #define EEPROM_OPS_SIZE_OF_CONVERTER 4 // ********** Calibration data defines ********** #define NUM_OF_BYTES_PER_CAL_PAYLOAD 150U ///< Number of bytes per calibration payload. #define CAL_DATA_SEND_INTERVAL_COUNT (MS_PER_SECOND / (5 * TASK_GENERAL_INTERVAL)) ///< Calibration data send time interval in counts. #define RECORD_DATA_RECEIVE_TIMEOUT_MS (4 * MS_PER_SECOND) ///< Record data receive all the data packets timeout in ms. #define RECORD_DATA_MAX_MESSAGE_DFFIRENCE 1 ///< Calibration data receive message different from the previous message. #define RECORD_DATA_FIRST_RECEIVING_MSG_NUM 1 ///< Calibration data first receiving message number. #define SYSTEM_RECORD_NV_MEM_START_ADDRESS BANK7_SECTOR0_START_ADDRESS ///< System record storage start address in NV memory. #define CAL_RECORD_NV_MEM_START_ADDRESS BANK7_SECTOR0_START_ADDRESS + 4096 ///< Calibration record storage start address in NV memory. #define RECORD_BYTE_SIZE(r) (sizeof(r) + sizeof(U16)) ///< Record byte size macro. // Padding length calculation: (DG struct size % bytes to write(16) == 0 ? 0 : (DG struct size / bytes to write(16)) + 1) * bytes to write(16) /// DG padding length macro that is calculated with the size of the provided structure. #define RECORD_PADDING_LENGTH(rcrd, buf) (RECORD_BYTE_SIZE(rcrd) % buf == 0 ? 0 : ((U32)(RECORD_BYTE_SIZE(rcrd) / buf) + 1)) * buf #define RECORD_DEFAULT_TIME 0U ///< Record default time (calibration/set). #define RECORD_FOURTH_ORDER_COEFF 0.0 ///< Record fourth order coefficient. #define RECORD_THIRD_ORDER_COEFF 0.0 ///< Record third order coefficient. #define RECORD_SECOND_ORDER_COEFF 0.0 ///< Record second order coefficient. #define RECORD_DEFAULT_GAIN 1.0 ///< Record default gain. #define RECORD_DEFAULT_OFFSET 0.0 ///< Record default offset. #define RECORD_DEFAULT_CONST 0.0 ///< Record default constant. #define RECORD_DEFAULT_RATIO 1.0 ///< Record default ratio. // The service record interval is equivalent to 6 months #define RECORD_DEFAULT_SERVICE_INTERVAL_S 15768000U ///< Record default service interval in seconds. #define RECORD_DEFAULT_CHARACTER ' ' ///< Record default character. // Once a new calibration data is available the driver, sets a signal for the defined time. Once the time is out, it turns the signal off. #define NEW_CAL_AVAILABLE_SIGNAL_TIMEOUT_MS (1 * MS_PER_SECOND) ///< New calibration available signal timeout in milliseconds. // HD specific defines #ifdef _HD_ #define BLOOD_LEAK_DEFAULT_SET_POINT 20 ///< Blood leak default set point. #endif // DG specific defines #ifdef _DG_ #define FLUSH_LINES_DEFAULT_VOLUME_L 0.01 ///< Water volume to flush when starting re-circulate mode in liters. #define ACID_CONC_DEFAULT_MIXING_RATIO ( 2.35618 / FRACTION_TO_PERCENT_FACTOR ) ///< Ratio between RO water and acid concentrate mixing ratio. #define BICARB_CONC_DEFAULT_MIXING_RATIO ( 4.06812 / FRACTION_TO_PERCENT_FACTOR ) ///< Ratio between RO water and bicarbonate concentrate mixing ratio. #define PRES_SENSORS_RESERVED_SPACE_COUNT 6 ///< Pressure sensors reserved space count. #define FLOW_SENSROS_RESERVED_SPACE_COUNT 3 ///< Flow sensors reserved space count. #define TEMP_SENSORS_RESERVED_SPACE_COUNT 5 ///< Temperature sensors reserved space count. #define COND_SENSORS_RESERVED_SPACE_COUNT 2 ///< Conductivity sensors reserved space count. #define GENERIC_VOL_RESERVED_SPACE_COUNT 4 ///< Generic volumes reserved space count. #define RESERVOIR_TEMPERATURE_TAU_C_PER_MIN -0.512 ///< Reservoir temperature time constant C/min. #define ULTRAFILTER_TEMPERATURE_TAU_C_PER_MIN -4.565 ///< Ultrafilter temperature time constant C/min. #define ULTRAFILTER_VOLUME_ML 700 ///< Ultrafilter volume in milliliters. #endif /// NVDataMgmt self-test states enumeration. typedef enum NVDataMgmt_Self_Test_States { NVDATAMGMT_SELF_TEST_STATE_START = 0, ///< Self test start. NVDATAMGMT_SELF_TEST_STATE_READ_CAL_RECORD, ///< Self test read calibration record. NVDATAMGMT_SELF_TEST_STATE_READ_SYS_RECORD, ///< Self test read system record. NVDATAMGMT_SELF_TEST_STATE_READ_SERVICE_RECORD, ///< Self test read service record. NVDATAMGMT_SELF_TEST_STATE_READ_SCHEDULED_RUNS_RECORD, ///< Self test read scheduled runs record (DG only). NVDATAMGMT_SELF_TEST_STATE_READ_LOG_RECORD, ///< Self test read log record. NVDATAMGMT_SELF_TEST_STATE_READ_TREATMENT_TIME, ///< Self test read treatment time. TODO combine with water consumption NVDATAMGMT_SELF_TEST_STATE_READ_WATER_CONSUMPTION, ///< Self test read water consumption. TODO combine with tx time NVDATAMGMT_SELF_TEST_STATE_READ_DG_HEATERS_INFO, ///< Self test read DG heaters information. NVDATAMGMT_SELF_TEST_STATE_READ_SW_CONFIG_RECORD, ///< Self test read software configuration record. NVDATAMGMT_SELF_TEST_STATE_CHECK_CRC, ///< Self test check CRC. NVDATAMGMT_SELF_TEST_STATE_COMPLETE, ///< Self test complete. NUM_OF_NVDATAMGMT_SELF_TEST_STATES ///< Total number of self-test states. } NVDATAMGMT_SELF_TEST_STATE_T; /// NVDataMgmt Exec states enumeration. typedef enum NVDataMgmt_Exec_State { NVDATAMGMT_EXEC_STATE_WAIT_FOR_POST = 0, ///< Exec state wait for POST. NVDATAMGMT_EXEC_STATE_IDLE, ///< Exec state Idle. NVDATAMGMT_EXEC_STATE_WRITE_TO_EEPROM, ///< Exec state write to EEPROM. NVDATAMGMT_EXEC_STATE_VERIFY_EEPROM_WRITE, ///< Exec state verify EEPROM write. NVDATAMGMT_EXEC_STATE_READ_FROM_EEPROM, ///< Exec state read from EEPROM. NVDATAMGMT_EXEC_STATE_ERASE_EEPROM, ///< Exec state erase EEPROM. NVDATAMGMT_EXEC_STATE_WRITE_TO_RTC, ///< Exec state write to RTC. NVDATAMGMT_EXEC_STATE_VERIFY_RTC_WRITE, ///< Exec state verify RTC write. NVDATAMGMT_EXEC_STATE_READ_FROM_RTC, ///< Exec state read from RTC. NUM_OF_NVDATAMGMT_EXEC_STATES ///< Total number of exec states. } NVDATAMGMT_EXEC_STATE_T; /// NVDataMgmt memory operation modes enumeration. typedef enum NVDataMgmt_Operation { NVDATAMGMT_NONE = 0, ///< Default mode to prevent any accidental ops. NVDATAMGMT_WRITE, ///< Operation mode write. NVDATAMGMT_READ, ///< Operation mode read. NVDATAMGMT_ERASE_SECTOR, ///< Operation mode erase a sector (EEPROM). NUM_OF_NVDATAMGMT_OPS_STATES ///< Total number of operation states. } NVDATAMGMT_OPERATION_STATE_T; /// NVDataMgmt memory locations enumeration typedef enum NVDataMgmt_Location { NVDATAMGMT_EEPROM = 0, ///< Location EEPROM. NVDATAMGMT_RTC, ///< Location RTC (RAM). NUM_OF_NVDATAMGMT_LOC_STATES ///< Total number of location states. } NVDATAMGMT_LOCATION_STATE_T; /// NVDataMgmt process records states typedef enum NVDataMgmt_Process_Records_States { NVDATAMGMT_PROCESS_RECORD_STATE_IDLE = 0, ///< NVDataMgmt process record idle state. NVDATAMGMT_PROCESS_RECORD_STATE_SEND_RECORD, ///< NVDataMgmt process record send record state. NUM_OF_NVDATAMGMT_PROCESS_RECORD_STATES ///< Number of NVDataMgmt process records state. } PROCESS_RECORD_STATE_T; /// NVDataMgmt receive records states typedef enum NVDataMgmt_Receive_Records_States { NVDATAMGMT_RECEIVE_RECORD_IDLE = 0, ///< NVDataMgmt receive record idle. NVDATAMGMT_RECEIVE_RECORD_RECEIVE, ///< NVDataMgmt receive record receive. NUM_OF_NVDATAMGMT_RECEIVE_RECORD_STATES ///< Number of NVDataMgmt receive record. } RECEIVE_RECORD_STATE_T; /// NVDataMgmt write record validity check states typedef enum NVDataMgmt_Write_Record_Validity_Check { NVDATAMGMT_RECORD_NOT_CHECKED = 0, ///< NVDataMgmt (written) record not checked. NVDATAMGMT_RECORD_VALID, ///< NVDataMgmt record is valid. NVDATAMGMT_RECORD_NOT_VALID, ///< NVDataMgmt record is not valid. NUM_OF_NVDATAMGMT_RECORD_VALIDITY_CHECK ///< Number of NVDataMgmt validity check states. } RECORD_VALIDITY_CHECK_T; /// NVDataMgmt active queue typedef enum NVDataMgmt_Active_Queue { NVDATAMGMT_QUEUE_NONE = 0, ///< NVDataMgmt queue none. NVDATAMGMT_PROCESS_RECORDS, ///< NVDataMgmt process records. NVDATAMGMT_PROCESS_LOGS, ///< NVDataMgmt process logs. NUM_OF_NVDATAMGMT_QUEUES ///< Number of NVDataMgmt queues. } ACTIVE_QUEUE_T; #pragma pack(push, 1) /// Process records specifications structure typedef struct { U32 startAddress; ///< Jobs spec start address. U32 sizeofJob; ///< Jobs spec size of job. U32 maxWriteBufferSize; ///< Jobs max write allowed processing buffer size. U32 maxReadBufferSize; ///< Jobs max read allowed processing buffer size. U08* structAddressPtr; ///< Jobs structure address pointer. U08* structCRCPtr; ///< Jobs structure CRC pointer. NVDATAMGMT_LOCATION_STATE_T dataLoc; ///< Jobs location (i.e. EEPROM, RTC RAM). } PROCESS_RECORD_SPECS_T; /// Process records job structure typedef struct { NVDATAMGMT_OPERATION_STATE_T memoryOperation; ///< Memory operation. NVDATAMGMT_LOCATION_STATE_T memoryLocation; ///< Memory location. RECORD_JOBS_STATE_T recordJob; ///< Record job (i.e sector 0). } PROCESS_RECORD_JOB_T; /// Memory operations structure. typedef struct { NVDATAMGMT_OPERATION_STATE_T memoryOperation; ///< Memory operation. NVDATAMGMT_LOCATION_STATE_T memoryLocation; ///< Memory location. U32 startAddress; ///< Operation start address. U08 buffer[ MAX_JOB_DATA_SIZE_BYTES ]; ///< Buffer. READ_DATA_T* externalAddress; ///< External address of a buffer. U32 length; ///< Length of a buffer. } MEMORY_LOG_OPS_T; /// Log header structure. typedef struct { U16 recordCount; ///< Record count. U16 nextWriteIndex; ///< Next write index. U16 nextReadIndex; ///< Next read index. BOOL isHdrCorrupted; ///< Log header corruption flag. } LOG_HEADER_T; /// Log record structure. typedef struct { LOG_HEADER_T logHeader; ///< Log header struct. U16 crc; ///< Log header CRC. } LOG_RECORD_T; // ********** HD/DG record structures ********** #ifdef _DG_ /// DG calibration groups structure typedef struct { DG_PRES_SENSORS_CAL_RECORD_T presSensorsCalRecord; ///< DG pressure sensors. DG_FLOW_SENSORS_CAL_RECORD_T flowSensorsCalRecord; ///< DG flow sensors. DG_LOAD_CELLS_CAL_RECORD_T loadCellsCalRecord; ///< DG load cells. DG_TEMP_SENSORS_CAL_RECORD_T tempSensorsCalRecord; ///< DG temperature sensors. DG_COND_SENSORS_CAL_RECORD_T condSensorsCalRecord; ///< DG conductivity sensors. DG_CONC_PUMPS_CAL_RECORD_T concentratePumpsRecord; ///< DG concentrate pumps. DG_DRAIN_PUMP_CAL_RECORD_T drainPumpRecord; ///< DG drain pump. DG_RO_PUMP_CAL_RECORD_T roPumpRecord; ///< DG RO pump. DG_DRAIN_LINE_VOLUME_T drainLineVolumeRecord; ///< DG drain line volume. DG_PRE_RO_PURGE_VOLUME_T preROPurgeVolumeRecord; ///< DG RO purge volume. DG_RESERVOIR_VOLUME_RECORD_T reservoirVolumesRecord; ///< DG reservoir volumes. DG_GENERIC_VOLUME_RECORD_T genericVolumeRecord; ///< DG generic volume (magic number because the value is unknown). DG_ACID_CONCENTRATES_RECORD_T acidConcentratesRecord; ///< DG acid concentrates. DG_BICARB_CONCENTRATES_RECORD_T bicarbConcentratesRecord; ///< DG bicarb concentrates. DG_FILTERS_CAL_RECORD_T filtersRecord; ///< DG filters. DG_FANS_CAL_RECORD_T fansRecord; ///< DG fans. DG_ACCEL_SENSOR_CAL_RECORD_T accelerometerSensorCalRecord; ///< DG accelerometer sensor. DG_HEATING_CAL_RECORD_T heatingCalRecord; ///< DG heating calibration record. } DG_CALIBRATION_GROUPS_T; /// DG calibration records structure typedef struct { DG_CALIBRATION_GROUPS_T dgCalibrationGroups; ///< DG calibration groups. U08 padding[ RECORD_PADDING_LENGTH(DG_CALIBRATION_GROUPS_T, MAX_EEPROM_WRITE_BUFFER_BYTES) - RECORD_BYTE_SIZE(DG_CALIBRATION_GROUPS_T) ]; ///< DG calibration record padding byte array. U16 crc; ///< CRC for the DG calibration record structure. } DG_CALIBRATION_RECORD_T; /// DG system group structure typedef struct { DG_SYSTEM_RECORD_T dgSystemRecord; ///< DG system record. U08 padding[ RECORD_PADDING_LENGTH(DG_SYSTEM_RECORD_T, MAX_EEPROM_WRITE_BUFFER_BYTES) - RECORD_BYTE_SIZE(DG_SYSTEM_RECORD_T) ]; ///< DG system group padding byte array. U16 crc; ///< CRC for the DG system group structure. } DG_SYSTEM_GROUP_T; /// DG service record structure typedef struct { DG_SERVICE_RECORD_T dgServiceRecord; ///< DG service record. U08 padding[ RECORD_PADDING_LENGTH(DG_SERVICE_RECORD_T, MAX_RTC_RAM_OPS_BUFFER_BYTES) - RECORD_BYTE_SIZE(DG_SERVICE_RECORD_T) ]; ///< DG service group padding. U16 crc; ///< CRC for the DG service structure. } DG_SERVICE_GROUP_T; /// DG scheduler record structure typedef struct { DG_SCHEDULED_RUN_RECORD_T dgScheduledRun; ///< DG scheduled runs. U08 padding[ RECORD_PADDING_LENGTH(DG_SCHEDULED_RUN_RECORD_T, MAX_RTC_RAM_OPS_BUFFER_BYTES) - RECORD_BYTE_SIZE(DG_SCHEDULED_RUN_RECORD_T) ]; ///< DG scheduled run group padding. U16 crc; ///< CRC for the DG scheduled runs structure. } DG_SCHEDULED_RUNS_GROUP_T; /// DG usage record structure typedef struct { DG_USAGE_INFO_RECORD_T dgUsageInfo; ///< DG usage info record. U08 padding[ RECORD_PADDING_LENGTH(DG_USAGE_INFO_RECORD_T, MAX_RTC_RAM_OPS_BUFFER_BYTES) - RECORD_BYTE_SIZE(DG_USAGE_INFO_RECORD_T) ]; ///< DG scheduled run group padding. U16 crc; ///< CRC for the DG usage info structure. } DG_USAGE_INFO_GROUP_T; /// DG heaters record typedef struct { DG_HEATERS_RECORD_T dgHeatersInfo; ///< DG heaters info record. U08 padding[ RECORD_PADDING_LENGTH(DG_HEATERS_RECORD_T, MAX_RTC_RAM_OPS_BUFFER_BYTES) - RECORD_BYTE_SIZE(DG_HEATERS_RECORD_T) ]; ///< DG heater info group padding. U16 crc; ///< CRC for the DG heaters info structure. } DG_HEATERS_INFO_GROUP_T; /// DG software configurations group typedef struct { DG_SW_CONFIG_RECORD_T dgSWConfigsRecord; ///< Software configurations record. // Since the software configurations are one byte, Num_of was used for the length of the lists U08 padding[ RECORD_PADDING_LENGTH(DG_SW_CONFIG_RECORD_T, MAX_RTC_RAM_OPS_BUFFER_BYTES) - RECORD_BYTE_SIZE(DG_SW_CONFIG_RECORD_T) ]; ///< Software configurations group padding. U16 crc; ///< Software configurations CRC. } DG_SW_CONFIG_GROUP_T; #endif #ifdef _HD_ /// HD calibration groups structure typedef struct { HD_PUMPS_CAL_RECORD_T pumpsCalRecord; ///< HD pumps. HD_VALVES_CAL_RECORD_T valvesCalRecord; ///< HD valves. HD_OCCLUSION_SENSORS_CAL_RECORD_T occlusionSensorsCalRecord; ///< HD occlusion sensors. HD_FLOW_SENSORS_CAL_RECORD_T flowSensorsCalRecord; ///< HD flow sensors. HD_PRESSURE_SENSORS_CAL_RECORD_T pressureSensorsCalRecord; ///< HD pressure sensors. HD_TEMP_SENSORS_CAL_RECORD_T tempSensorsCalRecord; ///< HD temperature sensors. HD_HEPARIN_FORCE_SENSOR_CAL_RECORD_T heparinForceSensorCalRecord; ///< HD heparin force sensor. HD_ACCELEROMETER_SENSOR_CAL_RECORD_T accelerometerSensorCalRecord; ///< HD accelerometer sensor. HD_BLOOD_LEAK_SENSOR_CAL_RECORD_T bloodLeakSensorCalRecord; ///< HD blood leak sensor. } HD_CALIBRATION_GROUPS; /// HD calibration records structure typedef struct { HD_CALIBRATION_GROUPS hdCalibrationGroups; ///< HD calibration groups. U08 padding[ RECORD_PADDING_LENGTH(HD_CALIBRATION_GROUPS, MAX_EEPROM_WRITE_BUFFER_BYTES) - RECORD_BYTE_SIZE(HD_CALIBRATION_GROUPS) ]; ///< HD calibration record padding byte array. U16 crc; ///< CRC for the DG calibration record structure. } HD_CALIBRATION_RECORD_T; /// HD system group structure typedef struct { HD_SYSTEM_RECORD_T hdsystemRecord; ///< HD system record. U08 padding[ RECORD_PADDING_LENGTH(HD_SYSTEM_RECORD_T, MAX_EEPROM_WRITE_BUFFER_BYTES) - RECORD_BYTE_SIZE(HD_SYSTEM_RECORD_T) ]; ///< HD system group padding. U16 crc; ///< CRC for the HD system group structure. } HD_SYSTEM_GROUP_T; /// HD service record structure typedef struct { HD_SERVICE_RECORD_T hdServiceRecord; ///< HD service record. U08 padding[ RECORD_PADDING_LENGTH(HD_SERVICE_RECORD_T, MAX_RTC_RAM_OPS_BUFFER_BYTES) - RECORD_BYTE_SIZE(HD_SERVICE_RECORD_T) ]; ///< HD service group padding. U16 crc; ///< CRC for the HD service structure. } HD_SERVICE_GROUP_T; /// HD usage info record structure typedef struct { HD_USAGE_INFO_RECORD_T hdUsageInfo; ///< HD usage info record. U08 padding[ RECORD_PADDING_LENGTH(HD_USAGE_INFO_RECORD_T, MAX_RTC_RAM_OPS_BUFFER_BYTES) - RECORD_BYTE_SIZE(HD_USAGE_INFO_RECORD_T) ]; ///< HD scheduled run group padding. U16 crc; ///< CRC for the HD usage info structure. } HD_USAGE_INFO_GROUP_T; #endif #pragma pack(pop) // Calibration variables #ifdef _DG_ static DG_CALIBRATION_RECORD_T dgCalibrationRecord; ///< DG calibration record structure (including padding and final CRC). static DG_SYSTEM_GROUP_T dgSystemGroup; ///< DG system group structure (including padding and final CRC). static DG_SERVICE_GROUP_T dgServiceGroup; ///< DG service group structure (including padding and final CRC). static DG_SCHEDULED_RUNS_GROUP_T dgScheduledRunsGroup; ///< DG scheduled run structure (including padding and final CRC). static DG_USAGE_INFO_GROUP_T dgUsageInfoGroup; ///< DG usage info structure (including padding and final CRC). static DG_HEATERS_INFO_GROUP_T dgHeatersInfoGroup; ///< DG heaters info structure (including padding and final CRC). static DG_SW_CONFIG_GROUP_T dgSWConfigGroup; ///< DG Software configurations structure(including padding and final CRC). // Process records specifications const PROCESS_RECORD_SPECS_T RECORDS_SPECS[ NUM_OF_NVDATMGMT_RECORDS_JOBS ] = { {CAL_RECORD_NV_MEM_START_ADDRESS, sizeof(DG_CALIBRATION_RECORD_T), MAX_EEPROM_WRITE_BUFFER_BYTES, sizeof(DG_CALIBRATION_RECORD_T), (U08*)&dgCalibrationRecord, (U08*)&dgCalibrationRecord.crc, NVDATAMGMT_EEPROM}, // NVDATAMGMT_CALIBRATION_RECORD {SYSTEM_RECORD_NV_MEM_START_ADDRESS, sizeof(DG_SYSTEM_GROUP_T), MAX_EEPROM_WRITE_BUFFER_BYTES, sizeof(DG_SYSTEM_GROUP_T), (U08*)&dgSystemGroup, (U08*)&dgSystemGroup.crc, NVDATAMGMT_EEPROM}, // NVDATAMGMT_SYSTEM_RECORD {SERVICE_RECORD_START_ADDRESS, sizeof(DG_SERVICE_GROUP_T), MAX_RTC_RAM_OPS_BUFFER_BYTES, MAX_RTC_RAM_OPS_BUFFER_BYTES, (U08*)&dgServiceGroup, (U08*)&dgServiceGroup.crc, NVDATAMGMT_RTC}, // NVDATAMGMT_SERVICE_RECORD {SW_CONFIGS_START_ADDRESS, sizeof(DG_SW_CONFIG_GROUP_T), MAX_RTC_RAM_OPS_BUFFER_BYTES, MAX_RTC_RAM_OPS_BUFFER_BYTES, (U08*)&dgSWConfigGroup, (U08*)&dgSWConfigGroup.crc, NVDATAMGMT_RTC}, // NVDATAMGMT_SW_CONFIG_RECORD {DG_SCHEDULED_RUNS_START_ADDRESS, sizeof(DG_SCHEDULED_RUNS_GROUP_T), MAX_RTC_RAM_OPS_BUFFER_BYTES, MAX_RTC_RAM_OPS_BUFFER_BYTES, (U08*)&dgScheduledRunsGroup, (U08*)&dgScheduledRunsGroup.crc, NVDATAMGMT_RTC}, // NVDATAMGMT_SCHEDULER_RECORD {DG_HEATERS_INFO_START_ADDRESS, sizeof(DG_HEATERS_INFO_GROUP_T), MAX_RTC_RAM_OPS_BUFFER_BYTES, MAX_RTC_RAM_OPS_BUFFER_BYTES, (U08*)&dgHeatersInfoGroup, (U08*)&dgHeatersInfoGroup.crc, NVDATAMGMT_RTC} // NVDATAMGMT_HEATERS_INFO_RECORD }; #endif #ifdef _HD_ static HD_CALIBRATION_RECORD_T hdCalibrationRecord; ///< HD calibration record structure. static HD_SYSTEM_GROUP_T hdSystemGroup; ///< HD system group structure (including padding and final CRC). static HD_SERVICE_GROUP_T hdServiceGroup; ///< HD service group structure (including padding and final CRC). static HD_USAGE_INFO_GROUP_T hdUsageInfoGroup; ///< HD usage info group structure (including padding and final CRC). // Process records specifications const PROCESS_RECORD_SPECS_T RECORDS_SPECS [ NUM_OF_NVDATMGMT_RECORDS_JOBS ] = { {CAL_RECORD_NV_MEM_START_ADDRESS, sizeof(HD_CALIBRATION_RECORD_T), MAX_EEPROM_WRITE_BUFFER_BYTES, sizeof(HD_CALIBRATION_RECORD_T), (U08*)&hdCalibrationRecord, (U08*)&hdCalibrationRecord.crc, NVDATAMGMT_EEPROM}, // NVDATAMGMT_CALIBRATION_RECORD {SYSTEM_RECORD_NV_MEM_START_ADDRESS, sizeof(HD_SYSTEM_GROUP_T), MAX_EEPROM_WRITE_BUFFER_BYTES, sizeof(HD_SYSTEM_GROUP_T), (U08*)&hdSystemGroup, (U08*)&hdSystemGroup.crc, NVDATAMGMT_EEPROM}, // NVDATAMGMT_SYSTEM_RECORD {SERVICE_RECORD_START_ADDRESS, sizeof(HD_SERVICE_GROUP_T), MAX_RTC_RAM_OPS_BUFFER_BYTES, MAX_RTC_RAM_OPS_BUFFER_BYTES, (U08*)&hdServiceGroup, (U08*)&hdServiceGroup.crc, NVDATAMGMT_RTC}, // NVDATAMGMT_PROCESS_LAST_SERVICE_RECORD // TODO unocomment{SW_CONFIGS_START_ADDRESS, sizeof(SW_CONFIGS_RECORD_T), MAX_RTC_RAM_OPS_BUFFER_BYTES, MAX_RTC_RAM_OPS_BUFFER_BYTES, (U08*)&swConfigsRecord, (U08*)&swConfigsRecord.crc, NVDATAMGMT_RTC} // NVDATAMGMT_SW_CONFIG_RECORD }; #endif static RECORD_JOBS_STATE_T recordToPublish; ///< Record to publish (i.e. calibration, system) static PROCESS_RECORD_STATE_T nvDataMgmtExecProcessRecordState = NVDATAMGMT_PROCESS_RECORD_STATE_IDLE; ///< NVDataMgmt exec process record state. static RECEIVE_RECORD_STATE_T nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; ///< NVDataMgmt exec receive record state. static BOOL hasPublishRecordBeenRequested = FALSE; ///< Record state machine publish request flag. static U32 calPublishMessageCount = 1; ///< Calibration data publish message counter. static U32 calPublishTotalMessages = 1; ///< Calibration data total number of messages to be sent. static U32 calSendDataIntervalCounter = 0; ///< Calibration data send to CAN bust interval counter. static U32 previousCalMessageNum = 0; ///< Calibration previous message number. static U32 recordUpdateAddress = 0; ///< DG record update address for all the write operations. static U32 recordReceiveStartTime = 0; ///< Time stamp the calibration/service was received. static PROCESS_RECORD_JOB_T recordJobQueue[ QUEUE_MAX_SIZE ]; ///< Record queue jobs. static U08 recordQueueRearIndex; ///< Record queue rear index. static U08 recordQueueFrontIndex; ///< Record queue front index. static U08 recordQueueCount; ///< Record queue count. static PROCESS_RECORD_JOB_T recordCurrentJob; ///< Record queue current job. static U32 recordAddressOffset; ///< Record address offset. static RECORD_VALIDITY_CHECK_T writtenRecordStatus; ///< Record data write validity check. static U08 writtenRecordCheckBuffer[ MAX_EEPROM_WRITE_BUFFER_BYTES ]; ///< Written record validity check buffer. static ACTIVE_QUEUE_T activeQueue; ///< Active queue. // Private variables static MEMORY_LOG_OPS_T jobQueue [ QUEUE_MAX_SIZE ]; ///< Job queue buffer. static MEMORY_LOG_OPS_T currentJob; ///< Current job. static LOG_RECORD_T logRecord; ///< Log record variable. static U08 queueRearIndex = QUEUE_START_INDEX; ///< Queue rear index. static U08 queueFrontIndex = QUEUE_START_INDEX; ///< Queue front index. static U08 queueCount = 0; ///< Queue count. static NVDATAMGMT_SELF_TEST_STATE_T nvDataMgmtSelfTestState = NVDATAMGMT_SELF_TEST_STATE_START; ///< NVDataMgmt self-test state variable. static NVDATAMGMT_EXEC_STATE_T nvDataMgmtExecState = NVDATAMGMT_EXEC_STATE_WAIT_FOR_POST; ///< NVDataMgmt exec state variable. static SELF_TEST_STATUS_T nvDataMgmtSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; ///< NVDataMgmt self-test result. static U32 currentTime = 0; ///< Current time. static volatile BOOL powerOffIsImminent = FALSE; ///< Power off warning has been signaled. Non-volatile memory operations should be completed ASAP and then ceased. static BOOL isNewCalAvailable = FALSE; ///< Signal to indicate whether a new calibration data is available. static U32 newCalStartTimer = 0; ///< New calibration availability start timer. // Private functions static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestStart( void ); static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadCalibrationRecord( void ); static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadSystemRecord( void ); static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadServiceRecord( void ); #ifdef _DG_ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadScheduledRunsRecord( void ); #endif #ifdef _HD_ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadHDTreatmentTime( void ); #endif #ifdef _DG_ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadDGHeatersInfo( void ); static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadDGWaterConsumption( void ); #endif static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadSWConfigRecord( void ); static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadLogRecord( void ); static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestCheckCRC( void ); static NVDATAMGMT_EXEC_STATE_T handleExecWaitForPostState( void ); static NVDATAMGMT_EXEC_STATE_T handleExecIdleState( void ); static NVDATAMGMT_EXEC_STATE_T handleExecEraseState( void ); static NVDATAMGMT_EXEC_STATE_T handleExecWriteToEEPROMState( void ); static NVDATAMGMT_EXEC_STATE_T handleExecVerifyEEPROMWriteState( void ); static NVDATAMGMT_EXEC_STATE_T handleExecReadFromEEPROMState( void ); static NVDATAMGMT_EXEC_STATE_T handleExecWriteToRAMState( void ); static NVDATAMGMT_EXEC_STATE_T handleExecVerifyRTCWriteState( void ); static NVDATAMGMT_EXEC_STATE_T handleExecReadFromRAMState( void ); static PROCESS_RECORD_STATE_T handleExecProcessRecordIdleState( void ); static PROCESS_RECORD_STATE_T handleExecProcessRecordSendRecordState( void ); // Log queue functions static void setMemoryOpsStruct( NVDATAMGMT_OPERATION_STATE_T ops, NVDATAMGMT_LOCATION_STATE_T location, U32 startAddress, U08* data, READ_DATA_T* extAddress, U32 length ); static void enqueue( NVDATAMGMT_OPERATION_STATE_T ops, NVDATAMGMT_LOCATION_STATE_T location, U32 startAddress, U08* data, READ_DATA_T* extAddress, U32 length ); static void dequeue( void ); static U32 prepareWriteLogJobAndGetStartAddress( U08* data ); static U32 prepareReadLogJobAndGetStartAddress( void ); static BOOL isQueueEmpty( void ); static BOOL isQueueFull( void ); static U32 getAvailableQueueCount( void ); // Helper functions static BOOL didCommandTimeout( ALARM_ID_T alarm, U08* state ); static BOOL eraseDataLogSectors( void ); static void monitorNewCalSignal( void ); // Record operations queue functions static void enqueueRecordJob( NVDATAMGMT_OPERATION_STATE_T ops, NVDATAMGMT_LOCATION_STATE_T location, RECORD_JOBS_STATE_T job ); static void dequeueRecordJob( void ); static BOOL isRecordQueueEmpty( void ); static U32 getAvailableRecordQueueCount( void ); static BOOL enqueueSector0Records( void ); // Record check helper functions static BOOL areRecordsValid( void ); static BOOL isPolynomialRecordValid( POLYNOMIAL_CAL_PAYLOAD_T* record ); #ifdef _DG_ static BOOL isDGSystemRecordValid( void ); static BOOL isDGServiceRecordValid( void ); static BOOL isDGCalibrationRecordValid( void ); static BOOL isSWConfigRecordValid( void ); static BOOL isDGConcPumpRecordValid( DG_CONC_PUMPS_CAL_DATA_T* record ); static BOOL isDGDrainPumpRecordValid( DG_DRAIN_PUMP_CAL_RECORD_T* record ); static BOOL isDGROPumpRecordValid( DG_RO_PUMP_CAL_RECORD_T* record ); static BOOL isDGPreROPurgeVolumeRecordValid( DG_PRE_RO_PURGE_VOLUME_T* record ); static BOOL isDGDrainLineVolRecordValid( DG_DRAIN_LINE_VOLUME_T* record ); static BOOL isDGReservoirVolRecordValid( DG_RESERVOIR_VOLUME_DATA_T* record ); static BOOL isDGGenericVolRecordValid( DG_GENERIC_VOLUME_DATA_T* record ); static BOOL isDGAcidConcentrateRecordValid( DG_ACID_CONCENTRATE_T* record ); static BOOL isDGBicarbConcentrateRecordValid( DG_BICARB_CONCENTRATE_T* record ); static BOOL isDGFilterRecordValid( DG_FILTER_CAL_RECORD_T* record ); static BOOL isDGFanRecordValid( DG_FAN_CAL_RECORD_T* record ); static BOOL isDGAccelerometerSensorRecordValid( DG_ACCEL_SENSOR_CAL_RECORD_T* record ); static BOOL isDGHeatingCalRecordValid( DG_HEATING_CAL_RECORD_T* record ); #endif #ifdef _HD_ static BOOL isHDSystemRecordValid( void ); static BOOL isHDServiceRecordValid( void ); static BOOL isHDCalibrationRecordValid( void ); static BOOL isHDValveRecordValid( HD_VALVE_CAL_PAYLOAD_T* record ); static BOOL isHDPumpRecordValid( HD_PUMP_CAL_PAYLOAD_T* record ); static BOOL isHDAccelerometerSensorValid( HD_ACCELEROMETER_SENSOR_CAL_RECORD_T* record ); static BOOL isHDBloodLeakSesnorValid( HD_BLOOD_LEAK_SENSOR_CAL_RECORD_T* record ); #endif /*********************************************************************//** * @brief * The initNVDataMgmt function initializes the module. * @details Inputs: none * @details Outputs: nvDataMgmtSelfTestState, nvDataMgmtExecState, * nvDataMgmtSelfTestResult, nvDataMgmtExecProcessRecordState, * nvDataMgmtExecReceiveRecordState, queueRearIndex, queueFrontIndex, * queueFrontIndex, queueCount, recordUpdateAddress, recordQueueRearIndex, * recordQueueFrontIndex, recordQueueCount, recordAddressOffset, * writtenRecordStatus, hasPublishRecordBeenRequested, isNewCalAvailable, * newCalStartTimer * @return none *************************************************************************/ void initNVDataMgmt( void ) { // Initialize the parameters nvDataMgmtSelfTestState = NVDATAMGMT_SELF_TEST_STATE_START; nvDataMgmtExecState = NVDATAMGMT_EXEC_STATE_WAIT_FOR_POST; nvDataMgmtSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; nvDataMgmtExecProcessRecordState = NVDATAMGMT_PROCESS_RECORD_STATE_IDLE; nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; queueRearIndex = QUEUE_START_INDEX; queueFrontIndex = QUEUE_START_INDEX; queueCount = 0; recordUpdateAddress = 0; recordQueueRearIndex = QUEUE_START_INDEX; recordQueueFrontIndex = QUEUE_START_INDEX; recordQueueCount = 0; recordAddressOffset = 0; writtenRecordStatus = NVDATAMGMT_RECORD_NOT_CHECKED; hasPublishRecordBeenRequested = FALSE; isNewCalAvailable = FALSE; newCalStartTimer = 0; // Initialize and activate flash bank 7 Fapi_initializeFlashBanks( ROUNDED_HCLK_FREQ ); Fapi_setActiveFlashBank( Fapi_FlashBank7 ); Fapi_enableEepromBankSectors( BANK7_SECTOR_0_31_ENABLE_BIT_MASK, BANK7_SECTOR_32_63_ENABLE_BIT_MASK ); } /*********************************************************************//** * @brief * The signalPowerOffWarning signals this module that system power off is * imminent and any non-volatile data writes in progress should be wrapped * up quickly and any pending non-volatile data writes should not be started. * @details Inputs: powerOffIsImminent * @details Outputs: powerOffIsImminent * @return none *************************************************************************/ void signalPowerOffWarning( void ) { powerOffIsImminent = TRUE; } /*********************************************************************//** * @brief * The execNVDataMgmt runs the NVDataMgmt main tasks. * @details Inputs: nvDataMgmtExecState * @details Outputs: nvDataMgmtExecState * @return none *************************************************************************/ void execNVDataMgmt( void ) { switch ( nvDataMgmtExecState ) { case NVDATAMGMT_EXEC_STATE_WAIT_FOR_POST: nvDataMgmtExecState = handleExecWaitForPostState(); break; case NVDATAMGMT_EXEC_STATE_IDLE: nvDataMgmtExecState = handleExecIdleState(); break; case NVDATAMGMT_EXEC_STATE_WRITE_TO_EEPROM: nvDataMgmtExecState = handleExecWriteToEEPROMState(); break; case NVDATAMGMT_EXEC_STATE_VERIFY_EEPROM_WRITE: nvDataMgmtExecState = handleExecVerifyEEPROMWriteState(); break; case NVDATAMGMT_EXEC_STATE_READ_FROM_EEPROM: nvDataMgmtExecState = handleExecReadFromEEPROMState(); break; case NVDATAMGMT_EXEC_STATE_ERASE_EEPROM: nvDataMgmtExecState = handleExecEraseState(); break; case NVDATAMGMT_EXEC_STATE_WRITE_TO_RTC: nvDataMgmtExecState = handleExecWriteToRAMState(); break; case NVDATAMGMT_EXEC_STATE_VERIFY_RTC_WRITE: nvDataMgmtExecState = handleExecVerifyRTCWriteState(); break; case NVDATAMGMT_EXEC_STATE_READ_FROM_RTC: nvDataMgmtExecState = handleExecReadFromRAMState(); break; default: #ifdef _DG_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_NVDATAMGMT_EXEC_INVALID_STATE, nvDataMgmtExecState ); #else SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_NVDATAMGMT_EXEC_INVALID_STATE, nvDataMgmtExecState ); #endif nvDataMgmtExecState = NVDATAMGMT_EXEC_STATE_IDLE; break; } } /*********************************************************************//** * @brief * The execNVDataMgmtSelfTest runs the NVDataMgmt POST during the self-test. * @details Inputs: nvDataMgmtSelfTestState, nvDataMgmtSelfTestResult * @details Outputs: nvDataMgmtSelfTestState * @return nvDataMgmtSelfTestResult the result of self-test *************************************************************************/ SELF_TEST_STATUS_T execNVDataMgmtSelfTest ( void ) { switch ( nvDataMgmtSelfTestState ) { case NVDATAMGMT_SELF_TEST_STATE_START: nvDataMgmtSelfTestState = handleSelfTestStart(); break; case NVDATAMGMT_SELF_TEST_STATE_READ_CAL_RECORD: nvDataMgmtSelfTestState = handleSelfTestReadCalibrationRecord(); break; case NVDATAMGMT_SELF_TEST_STATE_READ_SYS_RECORD: nvDataMgmtSelfTestState = handleSelfTestReadSystemRecord(); break; case NVDATAMGMT_SELF_TEST_STATE_READ_SERVICE_RECORD: nvDataMgmtSelfTestState = handleSelfTestReadServiceRecord(); break; #ifdef _DG_ case NVDATAMGMT_SELF_TEST_STATE_READ_SCHEDULED_RUNS_RECORD: nvDataMgmtSelfTestState = handleSelfTestReadScheduledRunsRecord(); break; #endif case NVDATAMGMT_SELF_TEST_STATE_READ_LOG_RECORD: nvDataMgmtSelfTestState = handleSelfTestReadLogRecord(); break; #ifdef _HD_ case NVDATAMGMT_SELF_TEST_STATE_READ_TREATMENT_TIME: nvDataMgmtSelfTestState = handleSelfTestReadHDTreatmentTime(); break; #endif #ifdef _DG_ case NVDATAMGMT_SELF_TEST_STATE_READ_WATER_CONSUMPTION: nvDataMgmtSelfTestState = handleSelfTestReadDGWaterConsumption(); break; case NVDATAMGMT_SELF_TEST_STATE_READ_DG_HEATERS_INFO: nvDataMgmtSelfTestState = handleSelfTestReadDGHeatersInfo(); break; #endif case NVDATAMGMT_SELF_TEST_STATE_READ_SW_CONFIG_RECORD: nvDataMgmtSelfTestState = handleSelfTestReadSWConfigRecord(); break; case NVDATAMGMT_SELF_TEST_STATE_CHECK_CRC: nvDataMgmtSelfTestState = handleSelfTestCheckCRC(); break; case NVDATAMGMT_SELF_TEST_STATE_COMPLETE: // Done with POST. Do nothing break; default: #ifdef _DG_ SET_ALARM_WITH_2_U32_DATA ( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_NVDATAMGMT_INVALID_SELF_TEST_STATE, nvDataMgmtSelfTestState ); #else SET_ALARM_WITH_2_U32_DATA ( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_NVDATAMGMT_INVALID_SELF_TEST_STATE, nvDataMgmtSelfTestState ); #endif nvDataMgmtSelfTestState = NVDATAMGMT_SELF_TEST_STATE_COMPLETE; nvDataMgmtSelfTestResult = SELF_TEST_STATUS_FAILED; break; } return nvDataMgmtSelfTestResult; } /*********************************************************************//** * @brief * The execNVDataMgmtProcessRecord runs the NVDataMgmt send records related * to tasks. * @details Inputs: nvDataMgmtExecProcessRecordState, * nvDataMgmtExecReceiveRecordState, recordReceiveStartTime * @details Outputs: nvDataMgmtExecProcessRecordState, * nvDataMgmtExecReceiveRecordState * @return none *************************************************************************/ void execNVDataMgmtProcessRecord( void ) { switch ( nvDataMgmtExecProcessRecordState ) { case NVDATAMGMT_PROCESS_RECORD_STATE_IDLE: nvDataMgmtExecProcessRecordState = handleExecProcessRecordIdleState(); break; case NVDATAMGMT_PROCESS_RECORD_STATE_SEND_RECORD: nvDataMgmtExecProcessRecordState = handleExecProcessRecordSendRecordState(); break; default: #ifdef _DG_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_NVDATAMGMT_EXEC_CAL_STATE, nvDataMgmtExecProcessRecordState ); #else SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_NVDATAMGMT_EXEC_CAL_STATE, nvDataMgmtExecProcessRecordState ); #endif nvDataMgmtExecProcessRecordState = NVDATAMGMT_PROCESS_RECORD_STATE_IDLE; break; } // Check if the exec receive records is not idle // This section checks the status of the asynchronous state machine that receives // data from Dialin. if ( nvDataMgmtExecReceiveRecordState != NVDATAMGMT_RECEIVE_RECORD_IDLE ) { // Check if the data receiving process has timed out. The exec receive record // state machine is asynchronous so it is checked in this state machine if ( TRUE == didTimeout( recordReceiveStartTime, RECORD_DATA_RECEIVE_TIMEOUT_MS ) ) { // Exec receive state machine timed out. Schedule a read to update the structure enqueueRecordJob( NVDATAMGMT_READ, NVDATAMGMT_EEPROM, NVDATAMGMT_CALIBRATION_RECORD ); nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; } } // Check the calibration signal monitorNewCalSignal(); } /*********************************************************************//** * @brief * The getDGCalibrationRecord function sets the calibration state machine * to read and publish calibration record. * @details Inputs: hasPublishRecordBeenRequested, recordToPublish * nvDataMgmtExecProcessRecordState * @details Outputs: hasPublishRecordBeenRequested, recordToPublish * @return TRUE if the request was successfully registered *************************************************************************/ BOOL getCalibrationRecord( void ) { BOOL status = FALSE; // Check if the state machine is in idle state and then set the request if ( nvDataMgmtExecProcessRecordState == NVDATAMGMT_PROCESS_RECORD_STATE_IDLE ) { hasPublishRecordBeenRequested = TRUE; recordToPublish = NVDATAMGMT_CALIBRATION_RECORD; status = TRUE; } return status; } /*********************************************************************//** * @brief * The setCalibrationRecord function writes the calibration record that * is received from Dialin into the calibration structure. * @details Inputs: nvDataMgmtExecReceiveRecordState, recordUpdateAddress * recordReceiveStartTime, previousCalMessageNum * @details Outputs: nvDataMgmtExecReceiveRecordState, recordUpdateAddress * recordReceiveStartTime, previousCalMessageNum * @param currentMessage: current message number that is received from * Dialin * @param totalMessages: total number of messages from Dialin * @param length: message length in bytes * @param *addressPtr: address to the beginning of the calibration data from Dialin * @return TRUE if the request was successfully registered *************************************************************************/ BOOL setCalibrationRecord( U32 currentMessage, U32 totalMessages, U32 length, U08 *addressPtr ) { BOOL status = TRUE; // If the calibration message number is the first message number and receive exec state is idle, switch to idle if ( ( RECORD_DATA_FIRST_RECEIVING_MSG_NUM == currentMessage ) && ( NVDATAMGMT_RECEIVE_RECORD_IDLE == nvDataMgmtExecReceiveRecordState ) ) { nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_RECEIVE; recordReceiveStartTime = getMSTimerCount(); previousCalMessageNum = 0; recordUpdateAddress = 0; } // Check if there is still a message left to be received if ( ( NVDATAMGMT_RECEIVE_RECORD_RECEIVE == nvDataMgmtExecReceiveRecordState ) && ( currentMessage <= totalMessages ) ) { // Check if the current message is different from the previous message by 1 if ( RECORD_DATA_MAX_MESSAGE_DFFIRENCE == ( currentMessage - previousCalMessageNum ) ) { // Define a pointer that points to the DG calibration record PROCESS_RECORD_SPECS_T recordSpec = RECORDS_SPECS[ NVDATAMGMT_CALIBRATION_RECORD ]; U08* ptr = recordSpec.structAddressPtr; // Offset the pointer to length that we should start writing from ptr += recordUpdateAddress; // Copy the data into the buffer memcpy(ptr, 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( recordSpec.structAddressPtr, recordSpec.sizeofJob - sizeof(U16) ); U16 recordCRC = *(U16*)recordSpec.structCRCPtr; if ( calcCRC != recordCRC ) { // CRC failed, request a read to read the data back from NV memory and update the structure enqueueRecordJob( NVDATAMGMT_READ, NVDATAMGMT_EEPROM, NVDATAMGMT_CALIBRATION_RECORD ); nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; status = FALSE; } else { // CRC passed, enqueue an erase, a write of calibration data and a write of service record BOOL scheduleStatus = enqueueSector0Records(); // Signal that there is a new calibration record available. newCalStartTimer = getMSTimerCount(); isNewCalAvailable = TRUE; } // Done with receiving data, go back to idle nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; } else { // Update the length as it has successfully been written recordUpdateAddress += length; // Now the current message is the previous message previousCalMessageNum = currentMessage; } } else { status = FALSE; nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; } } return status; } /*********************************************************************//** * @brief * The getSystemRecord function sets the system state machine to read * and publish system record. * @details Inputs: hasPublishRecordBeenRequested, recordToPublish * nvDataMgmtExecProcessRecordState * @details Outputs: hasPublishRecordBeenRequested, recordToPublish * @return TRUE if the request was successfully registered *************************************************************************/ BOOL getSystemRecord( void ) { BOOL status = FALSE; // Check if the state machine is in idle state and then set the request if ( nvDataMgmtExecProcessRecordState == NVDATAMGMT_PROCESS_RECORD_STATE_IDLE ) { hasPublishRecordBeenRequested = TRUE; recordToPublish = NVDATAMGMT_SYSTEM_RECORD; status = TRUE; } return status; } /*********************************************************************//** * @brief * The setSystemRecord function writes the system record that is received * from Dialin into the system structure. * @details Inputs: nvDataMgmtExecReceiveRecordState, recordUpdateAddress * recordReceiveStartTime, previousCalMessageNum * @details Outputs: nvDataMgmtExecReceiveRecordState, recordUpdateAddress * recordReceiveStartTime, previousCalMessageNum * @param currentMessage: current message number that is received from * Dialin * @param totalMessages: total number of messages from Dialin * @param length: message length in bytes * @param *addressPtr: address to the beginning of the calibration data * from Dialin * @return TRUE if the request was successfully registered *************************************************************************/ BOOL setSystemRecord( U32 currentMessage, U32 totalMessages, U32 length, U08 *addressPtr ) { BOOL status = TRUE; // If the calibration message number is the first message number and receive exec state is idle, switch to idle if ( ( RECORD_DATA_FIRST_RECEIVING_MSG_NUM == currentMessage ) && ( NVDATAMGMT_RECEIVE_RECORD_IDLE == nvDataMgmtExecReceiveRecordState ) ) { nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_RECEIVE; recordReceiveStartTime = getMSTimerCount(); previousCalMessageNum = 0; recordUpdateAddress = 0; } // Check if there is still a message left to be received if ( ( NVDATAMGMT_RECEIVE_RECORD_RECEIVE == nvDataMgmtExecReceiveRecordState ) && ( currentMessage <= totalMessages ) ) { // Check if the current message is different from the previous message by 1 if ( RECORD_DATA_MAX_MESSAGE_DFFIRENCE == ( currentMessage - previousCalMessageNum ) ) { // Define a pointer that points to the DG calibration record PROCESS_RECORD_SPECS_T recordSpec = RECORDS_SPECS [ NVDATAMGMT_SYSTEM_RECORD ]; U08* ptr = recordSpec.structAddressPtr; // Offset the pointer to length that we should start writing from ptr += recordUpdateAddress; memcpy(ptr, addressPtr, length); // Check if the current message is total messages // and 0 everything out since we are done writing if ( currentMessage == totalMessages ) { U16 calcCRC = crc16 ( recordSpec.structAddressPtr, recordSpec.sizeofJob - sizeof(U16) ); // Get the CRC of the structure without the last 16 bits which is the CRC as well as the padding values U16 recordCRC = *(U16*)recordSpec.structCRCPtr; if ( calcCRC != recordCRC ) { // CRC failed, request a read to read the data back from NV memory and update the structure enqueueRecordJob( NVDATAMGMT_READ, NVDATAMGMT_EEPROM, NVDATAMGMT_SYSTEM_RECORD ); nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; status = FALSE; } else { // CRC passed, enqueue an erase, a write of calibration data and a write of service record BOOL scheduleStatus = enqueueSector0Records(); // Done with receiving data, go back to idle nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; } } else { // Update the length as it has successfully been written recordUpdateAddress += length; // Now the current message is the previous message previousCalMessageNum = currentMessage; } } } return status; } /*********************************************************************//** * @brief * The getServiceRecord function sets the system state machine to read * and publish service record. * @details Inputs: hasPublishRecordBeenRequested, recordToPublish * hasPublishRecordBeenRequested * @details Outputs: hasPublishRecordBeenRequested, recordToPublish * @return TRUE if the request was successfully registered *************************************************************************/ BOOL getServiceRecord( void ) { BOOL status = FALSE; // Check if the state machine is in idle state and then set the request if ( NVDATAMGMT_PROCESS_RECORD_STATE_IDLE == nvDataMgmtExecProcessRecordState ) { hasPublishRecordBeenRequested = TRUE; recordToPublish = NVDATAMGMT_SERVICE_RECORD; status = TRUE; } return status; } /*********************************************************************//** * @brief * The setServiceRecord function writes the service record that * is received from Dialin into the service structure. * @details Inputs: nvDataMgmtExecReceiveRecordState, recordUpdateAddress * recordReceiveStartTime, previousCalMessageNum * @details Outputs: nvDataMgmtExecReceiveRecordState, recordUpdateAddress * recordReceiveStartTime, previousCalMessageNum * @param currentMessage: current message number that is received from * Dialin * @param totalMessages: total number of messages from Dialin * @param length: message length in bytes * @param *addressPtr: address to the beginning of the calibration data * from Dialin * @return TRUE if the request was successfully registered *************************************************************************/ BOOL setServiceRecord( 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 && nvDataMgmtExecReceiveRecordState == NVDATAMGMT_RECEIVE_RECORD_IDLE ) { nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_RECEIVE; recordReceiveStartTime = getMSTimerCount(); previousCalMessageNum = 0; recordUpdateAddress = 0; } // Check if there is still a message left to be received if ( nvDataMgmtExecReceiveRecordState == NVDATAMGMT_RECEIVE_RECORD_RECEIVE && currentMessage <= totalMessages ) { // Check if the current message is different from the previous message by 1 if ( RECORD_DATA_MAX_MESSAGE_DFFIRENCE == ( currentMessage - previousCalMessageNum ) ) { // Define a pointer that points to the DG calibration record PROCESS_RECORD_SPECS_T recordSpec = RECORDS_SPECS [ NVDATAMGMT_SERVICE_RECORD ]; U08* ptr = recordSpec.structAddressPtr; // Offset the pointer to length that we should start writing from ptr += recordUpdateAddress; memcpy(ptr, addressPtr, length); // Check if the current message is total messages // and 0 everything out since we are done writing if ( currentMessage == totalMessages ) { U16 calcCRC = crc16 ( recordSpec.structAddressPtr, recordSpec.sizeofJob - sizeof(U16) ); // Get the CRC of the structure without the last 16 bits which is the CRC as well as the padding values U16 recordCRC = *(U16*)recordSpec.structCRCPtr; if ( calcCRC != recordCRC ) { // CRC failed, request a read to read the data back from NV memory and update the structure enqueueRecordJob( NVDATAMGMT_READ, NVDATAMGMT_RTC, NVDATAMGMT_SERVICE_RECORD ); nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; status = FALSE; } else { // CRC passed write the last service record to the RTC RAM enqueueRecordJob( NVDATAMGMT_WRITE, NVDATAMGMT_RTC, NVDATAMGMT_SERVICE_RECORD ); // Done with receiving data, go back to idle nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; } } else { // Update the length as it has successfully been written recordUpdateAddress += length; // Now the current message is the previous message previousCalMessageNum = currentMessage; } } } return status; } /*********************************************************************//** * @brief * The sendRecordToDialin function prepares the process record state machine * to send a record to Dialin. * @details Inputs: nvDataMgmtExecProcessRecordState * @details Outputs: hasPublishRecordBeenRequested, recordToPublish * @param job type of job that is requested (i.e. get calibration record, ...) * @return TRUE if the request was successfully registered *************************************************************************/ BOOL sendRecordToDialin( RECORD_JOBS_STATE_T job ) { BOOL status = FALSE; // Check if the state machine is in idle state and then set the request if ( NVDATAMGMT_PROCESS_RECORD_STATE_IDLE == nvDataMgmtExecProcessRecordState ) { hasPublishRecordBeenRequested = TRUE; recordToPublish = job; status = TRUE; } return status; } /*********************************************************************//** * @brief * The receiveRecordFromDialin function receives the record that has been sent * from Dialin and if the CRCs passed, it schedules a write to the NV data. * @details Inputs: currentMessage, nvDataMgmtExecReceiveRecordState, * previousCalMessageNum, totalMessages, previousCalMessageNum, * @details Outputs: nvDataMgmtExecReceiveRecordState, recordReceiveStartTime, * previousCalMessageNum, recordUpdateAddress, recordUpdateAddress * @param job: the job that has to be received and written (i.e. service record) * @param currentMessage: current message number that is received from Dialin * @param totalMessages: total number of messages from Dialin * @param length: message length in bytes * @param *addressPtr: address to the beginning of the calibration data from Dialin * @return TRUE if the request was successfully registered *************************************************************************/ BOOL receiveRecordFromDialin( RECORD_JOBS_STATE_T job, U32 currentMessage, U32 totalMessages, U32 length, U08 *addressPtr ) { BOOL status = TRUE; // If the calibration message number is the first message number and receive exec state is idle, switch to idle if ( ( RECORD_DATA_FIRST_RECEIVING_MSG_NUM == currentMessage ) && ( NVDATAMGMT_RECEIVE_RECORD_IDLE == nvDataMgmtExecReceiveRecordState ) ) { nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_RECEIVE; recordReceiveStartTime = getMSTimerCount(); previousCalMessageNum = 0; recordUpdateAddress = 0; } // Check if there is still a message left to be received if ( ( NVDATAMGMT_RECEIVE_RECORD_RECEIVE == nvDataMgmtExecReceiveRecordState ) && ( currentMessage <= totalMessages ) ) { // Check if the current message is different from the previous message by 1 if ( RECORD_DATA_MAX_MESSAGE_DFFIRENCE == ( currentMessage - previousCalMessageNum ) ) { // Define a pointer that points to the DG calibration record PROCESS_RECORD_SPECS_T recordSpec = RECORDS_SPECS[ job ]; U08* ptr = recordSpec.structAddressPtr; // Offset the pointer to length that we should start writing from ptr += recordUpdateAddress; memcpy(ptr, addressPtr, length); // Check if the current message is total messages // and 0 everything out since we are done writing if ( currentMessage == totalMessages ) { U16 calcCRC = crc16 ( recordSpec.structAddressPtr, recordSpec.sizeofJob - sizeof(U16) ); // Get the CRC of the structure without the last 16 bits which is the CRC as well as the padding values U16 recordCRC = *(U16*)recordSpec.structCRCPtr; if ( calcCRC != recordCRC ) { // CRC failed, request a read to read the data back from NV memory and update the structure enqueueRecordJob( NVDATAMGMT_READ, recordSpec.dataLoc, job ); nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; status = FALSE; } else { if ( NVDATAMGMT_EEPROM == recordSpec.dataLoc ) { // CRC passed, enqueue an erase, a write of calibration data and a write of service record BOOL scheduleStatus = enqueueSector0Records(); } else { // CRC passed write the last service record to the RTC RAM enqueueRecordJob( NVDATAMGMT_WRITE, recordSpec.dataLoc, job ); } // Done with receiving data, go back to idle nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; } } else { // Update the length as it has successfully been written recordUpdateAddress += length; // Now the current message is the previous message previousCalMessageNum = currentMessage; } } } return status; } #ifdef _DG_ /*********************************************************************//** * @brief * The getScheduledRunsRecord function sets the system state machine * to read and publish the scheduled runs record. * @details Inputs: hasPublishRecordBeenRequested, recordToPublish * hasPublishRecordBeenRequested * @details Outputs: hasPublishRecordBeenRequested, recordToPublish * @return TRUE if the request was successfully registered *************************************************************************/ BOOL getScheduledRunsRecord( void ) { BOOL status = FALSE; // Check if the state machine is in idle state and then set the request if ( nvDataMgmtExecProcessRecordState == NVDATAMGMT_PROCESS_RECORD_STATE_IDLE ) { hasPublishRecordBeenRequested = TRUE; recordToPublish = NVDATAMGMT_SCHEDULED_RUNS_RECORD; status = TRUE; } return status; } /*********************************************************************//** * @brief * The setScheduledRunsRecord function writes the scheduled runs record that * is received from Dialin into the scheduled runs structure. * @details Inputs: nvDataMgmtExecReceiveRecordState, recordUpdateAddress * recordReceiveStartTime, previousCalMessageNum * @details Outputs: nvDataMgmtExecReceiveRecordState, recordUpdateAddress * recordReceiveStartTime, previousCalMessageNum * @param currentMessage: current message number that is received from * Dialin * @param totalMessages: total number of messages from Dialin * @param length: message length in bytes * @param *addressPtr: address to the beginning of the calibration data * from Dialin * @return TRUE if the request was successfully registered *************************************************************************/ BOOL setScheduledRunsRecord( 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 && nvDataMgmtExecReceiveRecordState == NVDATAMGMT_RECEIVE_RECORD_IDLE ) { nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_RECEIVE; recordReceiveStartTime = getMSTimerCount(); previousCalMessageNum = 0; recordUpdateAddress = 0; } // Check if there is still a message left to be received if ( nvDataMgmtExecReceiveRecordState == NVDATAMGMT_RECEIVE_RECORD_RECEIVE && currentMessage <= totalMessages ) { // Check if the current message is different from the previous message by 1 if ( RECORD_DATA_MAX_MESSAGE_DFFIRENCE == ( currentMessage - previousCalMessageNum ) ) { // Define a pointer that points to the DG calibration record PROCESS_RECORD_SPECS_T recordSpec = RECORDS_SPECS [ NVDATAMGMT_SCHEDULED_RUNS_RECORD ]; U08* ptr = recordSpec.structAddressPtr; // Offset the pointer to length that we should start writing from ptr += recordUpdateAddress; memcpy(ptr, addressPtr, length); // Check if the current message is total messages // and 0 everything out since we are done writing if ( currentMessage == totalMessages ) { U16 calcCRC = crc16 ( recordSpec.structAddressPtr, recordSpec.sizeofJob - sizeof(U16) ); // Get the CRC of the structure without the last 16 bits which is the CRC as well as the padding values U16 recordCRC = *(U16*)recordSpec.structCRCPtr; if ( calcCRC != recordCRC ) { // CRC failed, request a read to read the data back from NV memory and update the structure enqueueRecordJob( NVDATAMGMT_READ, NVDATAMGMT_RTC, NVDATAMGMT_SCHEDULED_RUNS_RECORD ); nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; status = FALSE; } else { // CRC passed write the last service record to the RTC RAM enqueueRecordJob( NVDATAMGMT_WRITE, NVDATAMGMT_RTC, NVDATAMGMT_SCHEDULED_RUNS_RECORD ); // Done with receiving data, go back to idle nvDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; } } else { // Update the length as it has successfully been written recordUpdateAddress += length; // Now the current message is the previous message previousCalMessageNum = currentMessage; } } } return status; } #endif /*********************************************************************//** * @brief * The isNewCalibrationRecordAvailable function returns the status of a * new calibration availability. * @details Inputs: none * @details Outputs: none * @return isNewCalAvailable which is TRUE if new calibration is available, * otherwise FALSE *************************************************************************/ BOOL isNewCalibrationRecordAvailable( void ) { return isNewCalAvailable; } /*********************************************************************//** * @brief * The getNVRecord2Driver function copies the requested non-volatile * data into the provided buffer by the caller. The function then checks if * the non-volatile data is valid. If the data is not valid, it raises the * provided alarm by the caller. * @details Inputs: hdCalibrationRecord, dgCalibrationRecord * @details Outputs: hdCalibrationRecord, dgCalibrationRecord * @param nvData the non-volatile data to be copied * @param bufferAddress the address of the provided buffer by the caller * @param bufferLength the length of the provided buffer by the caller * @param numOfSnsrs2Check the number of sensors to check in a array of sensors called * @param nvAlarm the corresponding alarm of the non-volatile data to be raised * if the data is not valid * @return TRUE if the non-volatile data is valid otherwise, FALSE *************************************************************************/ BOOL getNVRecord2Driver( NV_DATA_T nvData, U08* bufferAddress, U32 bufferLength, U08 numOfSnsrs2Check, ALARM_ID_T nvAlarm ) { // TODO NOTE: This function is the replacement to the processCalibrationData functions in the drivers // Also, remove the get functions in this driver once this function is used instead of get functions in the NVDataMgmt.h U08 i; U08* nvDataStartPtr = 0; BOOL isNVDataValid = FALSE; U32 nvDataLength = 0; switch ( nvData ) { #ifdef _HD_ case GET_CAL_BLOOD_LEAK_SENSOR: nvDataStartPtr = (U08*)&hdCalibrationRecord.hdCalibrationGroups.bloodLeakSensorCalRecord; isNVDataValid = ( 0 == hdCalibrationRecord.hdCalibrationGroups.bloodLeakSensorCalRecord.calibrationTime ? FALSE : TRUE ); break; case GET_CAL_TEMPERATURE_SESNORS: nvDataStartPtr = (U08*)&hdCalibrationRecord.hdCalibrationGroups.tempSensorsCalRecord; // Loop through each sensor's calibration data and make sure it is valid for ( i = 0; i < NUM_OF_CAL_DATA_HD_TEMP_SENSORS; i++ ) isNVDataValid |= ( 0 == hdCalibrationRecord.hdCalibrationGroups.tempSensorsCalRecord.hdTemperatureSensors[ i ].calibrationTime ? FALSE : TRUE ); break; case GET_CAL_ACCEL_SENSORS: nvDataStartPtr = (U08*)&hdCalibrationRecord.hdCalibrationGroups.accelerometerSensorCalRecord; isNVDataValid = ( 0 == hdCalibrationRecord.hdCalibrationGroups.accelerometerSensorCalRecord.calibrationTime ? FALSE : TRUE ); break; default: // TODO software fault break; #endif #ifdef _DG_ case GET_CAL_PRESSURE_SENOSRS: nvDataStartPtr = (U08*)&dgCalibrationRecord.dgCalibrationGroups.presSensorsCalRecord; nvDataLength = sizeof( dgCalibrationRecord.dgCalibrationGroups.presSensorsCalRecord ); for ( i = 0; i < numOfSnsrs2Check; i++ ) isNVDataValid |= ( 0 == dgCalibrationRecord.dgCalibrationGroups.presSensorsCalRecord.pressureSensors[ i ].calibrationTime ? FALSE : TRUE ); break; case GET_CAL_LOAD_CELL_SENSORS: nvDataStartPtr = (U08*)&dgCalibrationRecord.dgCalibrationGroups.loadCellsCalRecord; nvDataLength = sizeof( dgCalibrationRecord.dgCalibrationGroups.loadCellsCalRecord ); for ( i = 0; i < numOfSnsrs2Check; i++ ) isNVDataValid |= ( 0 == dgCalibrationRecord.dgCalibrationGroups.loadCellsCalRecord.loadCells[ i ].calibrationTime ? FALSE : TRUE ); break; case GET_CAL_FLOW_SENSORS: nvDataStartPtr = (U08*)&dgCalibrationRecord.dgCalibrationGroups.flowSensorsCalRecord; nvDataLength = sizeof( dgCalibrationRecord.dgCalibrationGroups.flowSensorsCalRecord ); for ( i = 0; i < numOfSnsrs2Check; i++ ) isNVDataValid |= ( 0 == dgCalibrationRecord.dgCalibrationGroups.flowSensorsCalRecord.flowSensors[ i ].calibrationTime ? FALSE : TRUE ); break; case GET_CAL_ACID_CONCENTREATES: nvDataStartPtr = (U08*)&dgCalibrationRecord.dgCalibrationGroups.acidConcentratesRecord; nvDataLength = sizeof( dgCalibrationRecord.dgCalibrationGroups.acidConcentratesRecord ); for ( i = 0; i < numOfSnsrs2Check; i++ ) isNVDataValid |= ( 0 == dgCalibrationRecord.dgCalibrationGroups.acidConcentratesRecord.acidConcentrate[ i ].calibrationTime ? FALSE : TRUE ); break; case GET_CAL_BICARB_CONCENTRATES: nvDataStartPtr = (U08*)&dgCalibrationRecord.dgCalibrationGroups.bicarbConcentratesRecord; nvDataLength = sizeof( dgCalibrationRecord.dgCalibrationGroups.bicarbConcentratesRecord ); for ( i = 0; i < numOfSnsrs2Check; i++ ) isNVDataValid |= ( 0 == dgCalibrationRecord.dgCalibrationGroups.bicarbConcentratesRecord.bicarbConcentrate[ i ].calibrationTime ? FALSE : TRUE ); break; case GET_CAL_ACCEL_SENSORS: nvDataStartPtr = (U08*)&dgCalibrationRecord.dgCalibrationGroups.accelerometerSensorCalRecord; isNVDataValid = ( 0 == dgCalibrationRecord.dgCalibrationGroups.accelerometerSensorCalRecord.calibrationTime ? FALSE : TRUE ); break; case GET_CAL_CONDUCTIVITY_SENSORS: nvDataStartPtr = (U08*)&dgCalibrationRecord.dgCalibrationGroups.condSensorsCalRecord; nvDataLength = sizeof( dgCalibrationRecord.dgCalibrationGroups.condSensorsCalRecord ); for ( i = 0; i < numOfSnsrs2Check; i++ ) isNVDataValid |= ( 0 == dgCalibrationRecord.dgCalibrationGroups.condSensorsCalRecord.condSensors[ i ].calibrationTime ? FALSE : TRUE ); break; case GET_CAL_TEMP_SENSORS: nvDataStartPtr = (U08*)&dgCalibrationRecord.dgCalibrationGroups.tempSensorsCalRecord; nvDataLength = sizeof( dgCalibrationRecord.dgCalibrationGroups.tempSensorsCalRecord ); for ( i = 0; i < numOfSnsrs2Check; i++ ) isNVDataValid |= ( 0 == dgCalibrationRecord.dgCalibrationGroups.tempSensorsCalRecord.tempSensors[ i ].calibrationTime ? FALSE : TRUE ); break; case GET_CAL_RSRVRS_VOL_RECORD: nvDataStartPtr = (U08*)&dgCalibrationRecord.dgCalibrationGroups.reservoirVolumesRecord; nvDataLength = sizeof( dgCalibrationRecord.dgCalibrationGroups.reservoirVolumesRecord ); for ( i = 0; i < numOfSnsrs2Check; i++ ) isNVDataValid |= ( 0 == dgCalibrationRecord.dgCalibrationGroups.reservoirVolumesRecord.reservoir[ i ].calibrationTime ? FALSE : TRUE ); break; case GET_CAL_HEATING_RECORD: nvDataStartPtr = (U08*)&dgCalibrationRecord.dgCalibrationGroups.heatingCalRecord; isNVDataValid = ( 0 == dgCalibrationRecord.dgCalibrationGroups.heatingCalRecord.calibrationTime ? FALSE : TRUE ); break; case GET_CAL_DRAIN_LINE_VOLUME_RECORD: nvDataStartPtr = (U08*)&dgCalibrationRecord.dgCalibrationGroups.drainLineVolumeRecord; isNVDataValid = ( 0 == dgCalibrationRecord.dgCalibrationGroups.drainLineVolumeRecord.calibrationTime ? FALSE : TRUE ); break; case GET_INF_HEATERS_RECORD: nvDataStartPtr = (U08*)&dgHeatersInfoGroup.dgHeatersInfo; nvDataLength = sizeof( dgHeatersInfoGroup.dgHeatersInfo ); break; case GET_SYS_RECORD: nvDataStartPtr = (U08*)&dgSystemGroup.dgSystemRecord; nvDataLength = sizeof( dgSystemGroup.dgSystemRecord ); break; case GET_SRV_RECORD: nvDataStartPtr = (U08*)&dgServiceGroup.dgServiceRecord; nvDataLength = sizeof( dgServiceGroup.dgServiceRecord ); break; default: // TODO software fault break; #endif } // 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 ); } #ifndef SKIP_CAL_CHECK // Check if the non-volatile data is valid and if not raise the alarm if ( FALSE == isNVDataValid ) { // If no alarm has been provided to raise, just set the variable as TRUE if ( ALARM_ID_NO_ALARM == nvAlarm ) { isNVDataValid = TRUE; } else { activateAlarmNoData( nvAlarm ); } } #else isNVDataValid = TRUE; #endif return isNVDataValid; } /*********************************************************************//** * @brief * The getSoftwareConfigStatus function returns the status of a software * configuration. * @details Inputs: swConfigsList * @details Outputs: none * @param config which is the configuration of the that its status is requested * @return status of the software configuration (0 or 1) *************************************************************************/ U08 getSoftwareConfigStatus( SOFTWARE_CONFIG_T config ) { U08 value = 0; // If the build is not a release, get the value from the software configurations list // If the build is a release, the configuration not matter what its value is kept in // the NV RAM, it returns a 0 which is the configuration is disabled #ifndef _RELEASE_ #ifdef _DG_ value = dgSWConfigGroup.dgSWConfigsRecord.swConfigs[ config ]; #endif #endif return value; } #ifdef _DG_ /*********************************************************************//** * @brief * The getDGROPumpRecord function returns the DG RO pump calibration record. * @details Inputs: none * @details Outputs: none * @return DG RO pump calibration record *************************************************************************/ DG_RO_PUMP_CAL_RECORD_T getDGROPumpRecord( void ) { return dgCalibrationRecord.dgCalibrationGroups.roPumpRecord; } /*********************************************************************//** * @brief * The getDGConcetnratePumpsRecord function returns the DG concentrate * pumps calibration record. * @details Inputs: none * @details Outputs: none * @return DG concentrate pumps calibration record *************************************************************************/ DG_CONC_PUMPS_CAL_RECORD_T getDGConcetnratePumpsRecord( void ) { return dgCalibrationRecord.dgCalibrationGroups.concentratePumpsRecord; } /*********************************************************************//** * @brief * The getDGDrainPumpRecord function returns the DG drain pump calibration * record. * @details Inputs: none * @details Outputs: none * @return DG drain pump calibration record *************************************************************************/ DG_DRAIN_PUMP_CAL_RECORD_T getDGDrainPumpRecord( void ) { return dgCalibrationRecord.dgCalibrationGroups.drainPumpRecord; } /*********************************************************************//** * @brief * The getDGFiltersRecord function returns the DG filters record. * @details Inputs: none * @details Outputs: none * @return DG filters record *************************************************************************/ DG_FILTERS_CAL_RECORD_T getDGFiltersRecord( void ) { return dgCalibrationRecord.dgCalibrationGroups.filtersRecord; } /*********************************************************************//** * @brief * The getDGFansRecord function returns the DG fans record. * @details Inputs: none * @details Outputs: none * @return DG fans record *************************************************************************/ DG_FANS_CAL_RECORD_T getDGFansRecord( void ) { return dgCalibrationRecord.dgCalibrationGroups.fansRecord; } /*********************************************************************//** * @brief * The getDGScheduledRunsRecord function returns the DG scheduled runs record. * @details Inputs: none * @details Outputs: none * @return DG scheduled runs record *************************************************************************/ DG_SCHEDULED_RUN_RECORD_T getDGScheduledRunsRecord( void ) { return dgScheduledRunsGroup.dgScheduledRun; } #endif #ifdef _HD_ /*********************************************************************//** * @brief * The getHDPumpsCalibrationRecord function returns the HD pumps calibration * record. * @details Inputs: none * @details Outputs: none * @return HD pumps calibration record *************************************************************************/ HD_PUMPS_CAL_RECORD_T getHDPumpsCalibrationRecord( void ) { return hdCalibrationRecord.hdCalibrationGroups.pumpsCalRecord; } /*********************************************************************//** * @brief * The getHDValvesCalibrationRecord function returns the HD valves calibration * record. * @details Inputs: none * @details Outputs: none * @return HD valves calibration record *************************************************************************/ HD_VALVES_CAL_RECORD_T getHDValvesCalibrationRecord( void ) { return hdCalibrationRecord.hdCalibrationGroups.valvesCalRecord; } /*********************************************************************//** * @brief * The getHDOcclusionSensrosCalibrationRecord function returns the HD * occlusion sensors calibration record. * @details Inputs: none * @details Outputs: none * @return HD occlusion sensors calibration record *************************************************************************/ HD_OCCLUSION_SENSORS_CAL_RECORD_T getHDOcclusionSensrosCalibrationRecord( void ) { return hdCalibrationRecord.hdCalibrationGroups.occlusionSensorsCalRecord; } /*********************************************************************//** * @brief * The getHDFlowSensorsCalibrationRecord function returns the HD flow * sensors calibration record. * @details Inputs: none * @details Outputs: none * @return HD flow sensors calibration record *************************************************************************/ HD_FLOW_SENSORS_CAL_RECORD_T getHDFlowSensorsCalibrationRecord( void ) { return hdCalibrationRecord.hdCalibrationGroups.flowSensorsCalRecord; } /*********************************************************************//** * @brief * The getHDPressureSensorsCalibrationRecord function returns the HD pressure * sensors calibration record. * @details Inputs: none * @details Outputs: none * @return HD pressure sensors calibration record *************************************************************************/ HD_PRESSURE_SENSORS_CAL_RECORD_T getHDPressureSensorsCalibrationRecord( void ) { return hdCalibrationRecord.hdCalibrationGroups.pressureSensorsCalRecord; } /*********************************************************************//** * @brief * The getHDTemperatureSensorsCalibrationRecord function returns the HD * temperature sensors calibration record. * @details Inputs: none * @details Outputs: none * @return HD temperature sensors calibration record *************************************************************************/ HD_TEMP_SENSORS_CAL_RECORD_T getHDTemperatureSensorsCalibrationRecord( void ) { return hdCalibrationRecord.hdCalibrationGroups.tempSensorsCalRecord; } /*********************************************************************//** * @brief * The getHDHeparinForceSensorCalibrationRecord function returns the HD * heparin force sensor calibration record. * @details Inputs: none * @details Outputs: none * @return HD heparin force sensor calibration record *************************************************************************/ HD_HEPARIN_FORCE_SENSOR_CAL_RECORD_T getHDHeparinForceSensorCalibrationRecord( void ) { return hdCalibrationRecord.hdCalibrationGroups.heparinForceSensorCalRecord; } /*********************************************************************//** * @brief * The getHDSystemRecord function returns the HD system record. * @details Inputs: none * @details Outputs: none * @return HD system record *************************************************************************/ HD_SYSTEM_RECORD_T getHDSystemRecord( void ) { return hdSystemGroup.hdsystemRecord; } /*********************************************************************//** * @brief * The getHDServiceRecord function returns the HD service record. * @details Inputs: none * @details Outputs: none * @return HD service record *************************************************************************/ HD_SERVICE_RECORD_T getHDServiceRecord( void ) { return hdServiceGroup.hdServiceRecord; } #endif /*********************************************************************//** * @brief * The writeLogData checks if the queue is not full and if it is not, it calls * enqueue to schedule a job for writing the log data. * @details Inputs: logRecord * @details Outputs: none * @param data address of log data buffer * @return TRUE if the queue is not full and if the log header is not corrupted *************************************************************************/ BOOL writeLogData ( LOG_DATA_T* data ) { BOOL status = FALSE; // If the header is corrupted, no more data is allowed to be written if ( !isQueueFull() && logRecord.logHeader.isHdrCorrupted == FALSE ) { enqueue ( NVDATAMGMT_WRITE, NVDATAMGMT_EEPROM, 0, (U08*)&data, 0, sizeof(LOG_DATA_T) ); status = TRUE; } return status; } /*********************************************************************//** * @brief * The readLogData checks if the queue is not full and if it is not, it calls * enqueue to schedule a job for writing the log data. * @details Inputs: none * @details Outputs: none * @param buffer address of read data buffer * @param length length of the data log * @return TRUE if there are enough queues available for the job *************************************************************************/ BOOL readLogData ( READ_DATA_T* buffer, U32 length ) { BOOL status = FALSE; U32 availableQueue = getAvailableQueueCount(); if ( availableQueue >= MIN_JOBS_NEEDED_FOR_DATA_LOG ) { enqueue( NVDATAMGMT_READ, NVDATAMGMT_EEPROM, 0, 0, buffer, length ); status = TRUE; } return status; } #ifdef _HD_ /*********************************************************************//** * @brief * The setTreatmentTime sets a queue job to write the treatment time in * the specified RAM address. * @details Inputs: treatmentTimeRecord * @details Outputs: treatmentTimeRecord * @param hours treatmet time in hours * @return TRUE if the queue was not full so the job was scheduled successfully *************************************************************************/ BOOL setTreatmentTime ( U32 hours ) { BOOL status = FALSE; hdUsageInfoGroup.hdUsageInfo.treatmentTime = hours; hdUsageInfoGroup.crc = crc16 ( (U08*)&hdUsageInfoGroup.hdUsageInfo.treatmentTime, sizeof(U32) ); if ( FALSE == isQueueFull() ) { enqueue( NVDATAMGMT_WRITE, NVDATAMGMT_RTC, HD_TREATMENT_TIME_ADDRESS, (U08*)&hdUsageInfoGroup, 0, sizeof(HD_USAGE_INFO_RECORD_T) ); status = TRUE; } return status; } /*********************************************************************//** * @brief * The getTreatmentTime returns the total number treatment hours of the * HD device. * @details Inputs: none * Output: none * @return treatment time as U32 *************************************************************************/ U32 getTreatmentTime ( void ) { return hdUsageInfoGroup.hdUsageInfo.treatmentTime; } #endif #ifdef _DG_ /*********************************************************************//** * @brief * The setWaterConsumption sets a queue job to write the amount of water * that has been consumed in DG. * @details Inputs: waterConsumptionRecord * @details Outputs: waterConsumptionRecord * @param liters consumed water is liters * @return TRUE if queue is not full *************************************************************************/ BOOL setWaterConsumption ( U32 liters ) { BOOL status = FALSE; dgUsageInfoGroup.dgUsageInfo.waterConsumption = liters; dgUsageInfoGroup.crc = crc16 ( (U08*)&dgUsageInfoGroup.dgUsageInfo.waterConsumption, sizeof(U16) ); if ( !isQueueFull() ) { enqueue( NVDATAMGMT_WRITE, NVDATAMGMT_RTC, DG_CONSUMED_WATER_ADDRESS, (U08*)&dgUsageInfoGroup, 0, sizeof(DG_USAGE_INFO_RECORD_T) ); status = TRUE; //TODO this is not right use enqueueRecordJob function to schedule a write } return status; } /*********************************************************************//** * @brief * The getWaterConsumption returns the amount of consumed water. * @details Inputs: none * @details Outputs: none * @return water consumption as a U32 *************************************************************************/ U32 getWaterConsumption ( void ) { return dgUsageInfoGroup.dgUsageInfo.waterConsumption; } /*********************************************************************//** * @brief * The setHeatersInfoRecord sets a queue job to write the DG heaters info * back to the RTC RAM. * @details Inputs: none * @details Outputs: dgHeatersInfoGroup * @param addressPrt which is an address to the structure that is holding the * heaters information. * @param infoLength which is the lenght of the heaters info to be written to * the NV RAM. * @return TRUE if queue is not full otherwise, FALSE *************************************************************************/ BOOL setHeatersInfoRecord( U08 *addressPtr, U32 infoLength ) { BOOL status = FALSE; // Check if the provided length of the heaters info structure matches the size of the heaters record in NV data. if ( sizeof( DG_HEATERS_RECORD_T ) >= infoLength ) { memcpy( &dgHeatersInfoGroup.dgHeatersInfo, addressPtr, sizeof( DG_HEATERS_RECORD_T ) ); // The crc is the DG heaters info and padding - the CRC U16 variable dgHeatersInfoGroup.crc = crc16 ( (U08*)&dgHeatersInfoGroup.dgHeatersInfo, sizeof( dgHeatersInfoGroup ) - sizeof( U16 ) ); if ( FALSE == isQueueFull() ) { // Queue is not full, schedule a write to RTC RAM the write the heaters information record enqueueRecordJob( NVDATAMGMT_WRITE, NVDATAMGMT_RTC, NVDATAMGMT_HEATERS_INFO_RECORD ); status = TRUE; } } return status; } #endif /*********************************************************************//** * @brief * The handleSelfTestStart enables the EEPROM bank sectors. * @details Inputs: currentTime * @details Outputs: currentTime * @return next state *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestStart ( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_START; if ( FAPI_CHECK_FSM_READY_BUSY == Fapi_Status_FsmReady ) { PROCESS_RECORD_SPECS_T specs = RECORDS_SPECS [ NVDATAMGMT_CALIBRATION_RECORD ]; U32 startAddress = specs.startAddress; U08* bufferAddress = specs.structAddressPtr; U32 maxBufferLength = specs.maxReadBufferSize / EEPROM_OPS_SIZE_OF_CONVERTER; Fapi_doMarginRead( (U32*)startAddress, (U32*)bufferAddress, maxBufferLength, Fapi_NormalRead ); currentTime = getMSTimerCount(); state = NVDATAMGMT_SELF_TEST_STATE_READ_CAL_RECORD; } return state; } /*********************************************************************//** * @brief * The handleSelfTestReadCalibrationRecord reads the calibration record * from EEPROM. * @details Inputs: currentTime * @details Outputs: currentTime * @return next state *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadCalibrationRecord ( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_READ_CAL_RECORD; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_NVDATA_EEPROM_OPS_FAILURE, (U08*)&state ); if ( FAPI_CHECK_FSM_READY_BUSY == Fapi_Status_FsmReady || timeoutStatus == TRUE ) { PROCESS_RECORD_SPECS_T specs = RECORDS_SPECS [ NVDATAMGMT_SYSTEM_RECORD ]; U32 startAddress = specs.startAddress; U08* bufferAddress = specs.structAddressPtr; U32 maxBufferLength = specs.maxReadBufferSize / EEPROM_OPS_SIZE_OF_CONVERTER; Fapi_doMarginRead( (U32*)startAddress, (U32*)bufferAddress, maxBufferLength, Fapi_NormalRead ); currentTime = getMSTimerCount(); state = NVDATAMGMT_SELF_TEST_STATE_READ_SYS_RECORD; } return state; } /*********************************************************************//** * @brief * The handleSelfTestReadSystemRecord reads the system record from EEPROM. * @details Inputs: currentTime, calibrationRecord * @details Outputs: currentTime * @return next state *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadSystemRecord ( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_READ_SYS_RECORD; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_NVDATA_EEPROM_OPS_FAILURE, (U08*)&state ); if ( ( Fapi_Status_FsmReady == FAPI_CHECK_FSM_READY_BUSY ) || ( TRUE == timeoutStatus ) ) { if ( RTC_RAM_STATE_READY == getRTCRAMState() ) { PROCESS_RECORD_SPECS_T specs = RECORDS_SPECS[ NVDATAMGMT_SERVICE_RECORD ]; U32 startAddress = specs.startAddress; U32 maxBufferLength = specs.maxReadBufferSize; currentTime = getMSTimerCount(); readFromRAM( startAddress, maxBufferLength ); state = NVDATAMGMT_SELF_TEST_STATE_READ_SERVICE_RECORD; } } return state; } /*********************************************************************//** * @brief * The handleSelfTestReadServiceRecord reads the service record from RTC * RAM. * @details Inputs: currentTime * @details Outputs: currentTime * @return next state *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadServiceRecord ( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_READ_SERVICE_RECORD; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_RTC_RAM_OPS_ERROR, (U08*)&state ); if ( ( RTC_RAM_STATUS_IDLE == getRTCRAMStatus() ) || ( TRUE == timeoutStatus ) ) { PROCESS_RECORD_SPECS_T specs = RECORDS_SPECS[ NVDATAMGMT_SERVICE_RECORD ]; U08* bufferAddress = specs.structAddressPtr; U32 maxBufferLength = specs.maxReadBufferSize; getDataFromRAM( bufferAddress, maxBufferLength ); #ifdef _DG_ if ( RTC_RAM_STATE_READY == getRTCRAMState() ) { PROCESS_RECORD_SPECS_T specs = RECORDS_SPECS[ NVDATAMGMT_SCHEDULED_RUNS_RECORD ]; U32 startAddress = specs.startAddress; U32 maxBufferLength = specs.maxReadBufferSize; currentTime = getMSTimerCount(); readFromRAM( startAddress, maxBufferLength ); state = NVDATAMGMT_SELF_TEST_STATE_READ_SCHEDULED_RUNS_RECORD; } #endif #ifdef _HD_ state = NVDATAMGMT_SELF_TEST_STATE_READ_SW_CONFIG_RECORD; #endif } return state; } #ifdef _DG_ /*********************************************************************//** * @brief * The handleSelfTestReadScheduledRunsRecord reads the scheduled runs * from RTC RAM (only DG for now). * @details Inputs: currentTime, serviceRecord * @details Outputs: currentTime * @return next state *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadScheduledRunsRecord( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_READ_SCHEDULED_RUNS_RECORD; BOOL timeoutStatus = didCommandTimeout( ALARM_ID_RTC_RAM_OPS_ERROR, (U08*)&state ); if ( ( RTC_RAM_STATUS_IDLE == getRTCRAMStatus() ) || ( TRUE == timeoutStatus ) ) { PROCESS_RECORD_SPECS_T specs = RECORDS_SPECS[ NVDATAMGMT_SCHEDULED_RUNS_RECORD ]; U08* bufferAddress = specs.structAddressPtr; U32 maxBufferLength = specs.maxReadBufferSize; getDataFromRAM( bufferAddress, maxBufferLength ); state = NVDATAMGMT_SELF_TEST_STATE_READ_DG_HEATERS_INFO; } return state; } #endif #ifdef _HD_ /*********************************************************************//** * @brief * The handleSelfTestReadHDTreatmentTime reads the HD treatment time * from RTC RAM. * @details Inputs: currentTime, treatmentTimeRecord * @details Outputs: currentTime * @return next state *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadHDTreatmentTime ( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_READ_TREATMENT_TIME; BOOL timeoutStatus = didCommandTimeout( ALARM_ID_RTC_RAM_OPS_ERROR, (U08*)&state ); // If the RTC RAM is ready, read the results if ( getRTCRAMStatus() == RTC_RAM_STATUS_IDLE || timeoutStatus == TRUE ) { getDataFromRAM( (U08*)&hdUsageInfoGroup, sizeof(HD_USAGE_INFO_RECORD_T) ); // If the RAM is ready, request a read for the log records (RAM) if ( getRTCRAMState() == RTC_RAM_STATE_READY ) { readFromRAM( LOG_RECORD_START_ADDRESS, sizeof(LOG_RECORD_T) ); currentTime = getMSTimerCount(); state = NVDATAMGMT_SELF_TEST_STATE_READ_LOG_RECORD; } } return state; } #endif #ifdef _DG_ /*********************************************************************//** * @brief * The handleSelfTestReadDGHeatersInfo reads the DG heaters information * from RTC RAM. * @details Inputs: none * @details Outputs: dgHeatersInfoGroup * @return next state *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadDGHeatersInfo( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_READ_DG_HEATERS_INFO; BOOL timeoutStatus = didCommandTimeout( ALARM_ID_RTC_RAM_OPS_ERROR, (U08*)&state ); if ( ( RTC_RAM_STATUS_IDLE == getRTCRAMStatus() ) || ( TRUE == timeoutStatus ) ) { PROCESS_RECORD_SPECS_T specs = RECORDS_SPECS[ NVDATAMGMT_HEATERS_INFO_RECORD ]; U08* bufferAddress = specs.structAddressPtr; U32 maxBufferLength = specs.maxReadBufferSize; getDataFromRAM( bufferAddress, maxBufferLength ); // Calculate the CRC of the record U16 calcCRC = crc16 ( specs.structAddressPtr, specs.sizeofJob - sizeof(U16) ); U16 recordCRC = *(U16*)specs.structCRCPtr; // If the CRCs are not equal, set the average fill flow to 0.0 // Otherwise, keep the data so it can be used if ( calcCRC != recordCRC ) { dgHeatersInfoGroup.dgHeatersInfo.averageFillFlow = 0.0; dgHeatersInfoGroup.crc = recordCRC; } else { memcpy( &dgHeatersInfoGroup, bufferAddress, sizeof( DG_HEATERS_INFO_GROUP_T ) ); } if ( RTC_RAM_STATE_READY == getRTCRAMState() ) { PROCESS_RECORD_SPECS_T specs = RECORDS_SPECS[ NVDATAMGMT_SW_CONFIG_RECORD ]; U32 startAddress = specs.startAddress; U32 maxBufferLength = specs.maxReadBufferSize; currentTime = getMSTimerCount(); readFromRAM( startAddress, maxBufferLength ); state = NVDATAMGMT_SELF_TEST_STATE_READ_SW_CONFIG_RECORD; } } return state; } /*********************************************************************//** * @brief * The handleSelfTestReadDGWaterConsumption reads the DG water consumption * from RTC RAM. * @details Inputs: currentTime, waterConsumptionRecord * @details Outputs: currentTime * @return next state *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadDGWaterConsumption ( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_READ_WATER_CONSUMPTION; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_RTC_RAM_OPS_ERROR, (U08*)&state ); // If the RAM is ready, request a read for water consumption if ( ( RTC_RAM_STATUS_IDLE == getRTCRAMStatus() ) || ( TRUE == timeoutStatus ) ) { getDataFromRAM ( (U08*)&dgUsageInfoGroup, sizeof(DG_USAGE_INFO_RECORD_T) ); if ( RTC_RAM_STATE_READY == getRTCRAMState() ) { readFromRAM ( LOG_RECORD_START_ADDRESS, sizeof(LOG_RECORD_T) ); currentTime = getMSTimerCount(); state = NVDATAMGMT_SELF_TEST_STATE_READ_LOG_RECORD; } } return state; } #endif /*********************************************************************//** * @brief * The handleSelfTestReadLogRecord reads the log record from RTC RAM. * @details Inputs: currentTime, logRecord, mfgRecord * @details Outputs: currentTime * @return next state *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadLogRecord ( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_READ_LOG_RECORD; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_RTC_RAM_OPS_ERROR, (U08*)&state ); // If the RAM is in Idle, read the log records if ( getRTCRAMStatus() == RTC_RAM_STATUS_IDLE || timeoutStatus == TRUE ) { // TODO enable the logs once we get back to logging the data in the future phases //U32 len = sizeof(MFG_RECORD_T) / sizeof(U32); //U32 *addr = (U32*)&mfgRecord; getDataFromRAM ( (U08*)&logRecord, sizeof(LOG_RECORD_T) ); currentTime = getMSTimerCount(); // Get ready for reading the manufacturing record // TODO uncomment. currently not working This section is for phase 1B //Fapi_doMarginRead( (U32*)BANK7_SECTOR0_START_ADDRESS, addr, len, Fapi_NormalRead ); state = NVDATAMGMT_SELF_TEST_STATE_READ_SYS_RECORD; } return state; } /*********************************************************************//** * @brief * The handleSelfTestReadSWConfigRecord reads the software configurations * record from RTC RAM. * @details Inputs: none * @details Outputs: none * @return next state *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadSWConfigRecord( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_READ_SW_CONFIG_RECORD; BOOL timeoutStatus = didCommandTimeout( ALARM_ID_RTC_RAM_OPS_ERROR, (U08*)&state ); #ifndef _RELEASE_ if ( ( RTC_RAM_STATUS_IDLE == getRTCRAMStatus() ) || ( TRUE == timeoutStatus ) ) { PROCESS_RECORD_SPECS_T specs = RECORDS_SPECS[ NVDATAMGMT_SW_CONFIG_RECORD ]; U08* bufferAddress = specs.structAddressPtr; U32 maxBufferLength = specs.maxReadBufferSize; getDataFromRAM( bufferAddress, maxBufferLength ); state = NVDATAMGMT_SELF_TEST_STATE_CHECK_CRC; } #else // It is a release so set all the software configurations to 0 to be considered as disable memset( specs.structAddressPtr, 0, specs.sizeofJob ); state = NVDATAMGMT_SELF_TEST_STATE_CHECK_CRC; #endif return state; } /*********************************************************************//** * @brief * The handleSelfTestCheckCRC calculates the CRC of the records and compares * them to the CRC that was read. If they don't match, it will fail POST. * @details Inputs: nvDataMgmtSelfTestResult * @details Outputs: none * @return next state *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestCheckCRC ( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_COMPLETE; BOOL haveCalGroupsPassed = TRUE; BOOL haveRecordsPassed = TRUE; BOOL hasSystemRecordPassed = TRUE; BOOL hasServiceRecordPassed = TRUE; BOOL hasSWConfigRecordPassed = TRUE; // Check the integrity of the records as a whole. Check the upper layer CRC haveRecordsPassed = areRecordsValid(); #ifdef _DG_ // Check all the calibration groups haveCalGroupsPassed = isDGCalibrationRecordValid(); hasSystemRecordPassed = isDGSystemRecordValid(); hasServiceRecordPassed = isDGServiceRecordValid(); hasSWConfigRecordPassed = isSWConfigRecordValid(); #endif #ifdef _HD_ haveCalGroupsPassed = isHDCalibrationRecordValid(); hasSystemRecordPassed = isHDSystemRecordValid(); hasServiceRecordPassed = isHDServiceRecordValid(); #endif // If any of the records did not pass, they should be filled // with benign values. After that, schedule a write to sector 0 // to re-write the records with the benign values if ( FALSE == haveCalGroupsPassed ) { enqueueSector0Records(); } if ( FALSE == hasSWConfigRecordPassed ) { enqueueRecordJob( NVDATAMGMT_WRITE, NVDATAMGMT_RTC, NVDATAMGMT_SW_CONFIG_RECORD ); } // Check if the records' entire CRCs as well as the individual CRCs passed if ( ( TRUE == haveCalGroupsPassed ) && ( TRUE == haveRecordsPassed ) && ( TRUE == hasSystemRecordPassed ) && ( TRUE == hasServiceRecordPassed ) ) { nvDataMgmtSelfTestResult = SELF_TEST_STATUS_PASSED; } else { nvDataMgmtSelfTestResult = SELF_TEST_STATUS_FAILED; } return state; } /*********************************************************************//** * @brief * The handleExecWaitForPostState waits for POST to be completed. * @details Inputs: nvDataMgmtSelfTestState * @details Outputs: none * @return next state *************************************************************************/ static NVDATAMGMT_EXEC_STATE_T handleExecWaitForPostState ( void ) { NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_WAIT_FOR_POST; if ( nvDataMgmtSelfTestState == NVDATAMGMT_SELF_TEST_STATE_COMPLETE ) { state = NVDATAMGMT_EXEC_STATE_IDLE; } return state; } /*********************************************************************//** * @brief * The handleExecIdleState checks if the queue is empty and if it is not * empty, it sets the state of the job. * @details Inputs: powerOffIsImminent * @details Outputs: none * @return next state *************************************************************************/ static NVDATAMGMT_EXEC_STATE_T handleExecIdleState ( void ) { NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_IDLE; NVDATAMGMT_OPERATION_STATE_T ops; NVDATAMGMT_LOCATION_STATE_T location; U32 startAddress; U08* bufferAddress; U32 maxBufferLength; BOOL areQueuesNotEmpty = FALSE; // If power off command has been issued, do not process any new jobs if ( powerOffIsImminent != TRUE ) { // If the record processing queue is not empty, process the queues if ( FALSE == isRecordQueueEmpty() ) { dequeueRecordJob(); RECORD_JOBS_STATE_T job = recordCurrentJob.recordJob; PROCESS_RECORD_SPECS_T jobSpecs = RECORDS_SPECS [ job ]; // Set the record address offset to 0 since a job will just be started recordAddressOffset = 0; areQueuesNotEmpty = TRUE; ops = recordCurrentJob.memoryOperation; location = recordCurrentJob.memoryLocation; currentTime = getMSTimerCount(); startAddress = jobSpecs.startAddress + recordAddressOffset; bufferAddress = jobSpecs.structAddressPtr + recordAddressOffset; activeQueue = NVDATAMGMT_PROCESS_RECORDS; // Choose the right buffer size if ( ops == NVDATAMGMT_WRITE ) { maxBufferLength = jobSpecs.maxWriteBufferSize; } else if ( ops == NVDATAMGMT_READ ) { maxBufferLength = jobSpecs.maxReadBufferSize / EEPROM_OPS_SIZE_OF_CONVERTER; } } // TODO add the log queue later in the future phases. } // Check if a queue job is available if ( TRUE == areQueuesNotEmpty ) { if ( ops == NVDATAMGMT_ERASE_SECTOR ) { Fapi_issueAsyncCommandWithAddress( Fapi_EraseSector, (U32*)startAddress ); state = NVDATAMGMT_EXEC_STATE_ERASE_EEPROM; } else if ( ops == NVDATAMGMT_WRITE && location == NVDATAMGMT_EEPROM ) { Fapi_issueProgrammingCommand( (U32*)startAddress, (U08*)bufferAddress, maxBufferLength, 0x00, 0, Fapi_DataOnly ); state = NVDATAMGMT_EXEC_STATE_WRITE_TO_EEPROM; } else if ( ops == NVDATAMGMT_READ && location == NVDATAMGMT_EEPROM ) { Fapi_doMarginRead( (U32*)startAddress, (U32*)bufferAddress, maxBufferLength / EEPROM_OPS_SIZE_OF_CONVERTER, Fapi_NormalRead ); state = NVDATAMGMT_EXEC_STATE_READ_FROM_EEPROM; } else if ( ops == NVDATAMGMT_WRITE && location == NVDATAMGMT_RTC ) { if ( getRTCRAMState() == RTC_RAM_STATE_READY ) { currentTime = getMSTimerCount(); writeToRAM( startAddress, bufferAddress, maxBufferLength ); state = NVDATAMGMT_EXEC_STATE_WRITE_TO_RTC; } } else if ( ops == NVDATAMGMT_READ && location == NVDATAMGMT_RTC ) { if ( getRTCRAMState() == RTC_RAM_STATE_READY ) { currentTime = getMSTimerCount(); readFromRAM( startAddress, maxBufferLength ); currentJob.externalAddress->status = NVDATAMGMT_READ_IN_PROGRESS; state = NVDATAMGMT_EXEC_STATE_READ_FROM_RTC; } } } return state; } /*********************************************************************//** * @brief * The handleExecWriteToEEPROMState issues a write command to EEPROM and * if the write was successful, it sets the state to Idle. * @details Inputs: recordAddressOffset, writtenRecordCheckBuffer, * writtenRecordStatus * @details Outputs: recordAddressOffset, writtenRecordCheckBuffer, * writtenRecordStatus * @return next state of the state machine *************************************************************************/ static NVDATAMGMT_EXEC_STATE_T handleExecWriteToEEPROMState ( void ) { NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_WRITE_TO_EEPROM; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_NVDATA_EEPROM_OPS_FAILURE, (U08*)&state ); RECORD_JOBS_STATE_T job = recordCurrentJob.recordJob; PROCESS_RECORD_SPECS_T jobSpecs = RECORDS_SPECS [ job ]; // Check if the fapi has finished if ( FAPI_CHECK_FSM_READY_BUSY == Fapi_Status_FsmReady ) { // Check the integrity of data (the 16 bytes that were written to EEPROM should be read and be checked for each byte) if ( writtenRecordStatus == NVDATAMGMT_RECORD_NOT_CHECKED ) { currentTime = getMSTimerCount(); U32 startAddress = jobSpecs.startAddress + recordAddressOffset; U32 maxBufferLength = jobSpecs.maxWriteBufferSize / EEPROM_OPS_SIZE_OF_CONVERTER; // Clear the buffer from the previous content memset( writtenRecordCheckBuffer, 0, sizeof(writtenRecordCheckBuffer)); // Issue a FAPI read command but only the bytes that were written, so use maxWriteBufferSize Fapi_doMarginRead( (U32*)startAddress, (U32*)writtenRecordCheckBuffer, maxBufferLength, Fapi_NormalRead ); state = NVDATAMGMT_EXEC_STATE_VERIFY_EEPROM_WRITE; } else if ( writtenRecordStatus == NVDATAMGMT_RECORD_VALID ) { // If the data is valid, and if it is at the end of the write, change to idle if ( jobSpecs.sizeofJob == recordAddressOffset ) { state = NVDATAMGMT_EXEC_STATE_IDLE; } else { // Update the variables and issue the next write command currentTime = getMSTimerCount(); recordAddressOffset += jobSpecs.maxWriteBufferSize; U32 memoryPtr = jobSpecs.startAddress + recordAddressOffset; U08* structPtr = jobSpecs.structAddressPtr + recordAddressOffset; writtenRecordStatus = NVDATAMGMT_RECORD_NOT_CHECKED; // Issue the write command Fapi_issueProgrammingCommand( (U32*)memoryPtr, structPtr, jobSpecs.maxWriteBufferSize, 0x00, 0, Fapi_DataOnly ); } } } // If timed out, get back to Idle else if ( timeoutStatus == TRUE ) { // TODO schedule another write? recordAddressOffset = 0; state = NVDATAMGMT_EXEC_STATE_IDLE; } return state; } /*********************************************************************//** * @brief * The handleExecVerifyEEPROMWriteState checks all the bytes that were * written to EEPROM to ensure they match the data in the NV memory. * @details Inputs: recordAddressOffset, writtenRecordCheckBuffer, * writtenRecordStatus * @details Outputs: recordAddressOffset, writtenRecordCheckBuffer, * writtenRecordStatus * @return next state of the state machine *************************************************************************/ static NVDATAMGMT_EXEC_STATE_T handleExecVerifyEEPROMWriteState ( void ) { NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_VERIFY_EEPROM_WRITE; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_NVDATA_EEPROM_OPS_FAILURE, (U08*)&state ); RECORD_JOBS_STATE_T job = recordCurrentJob.recordJob; PROCESS_RECORD_SPECS_T jobSpecs = RECORDS_SPECS [ job ]; // Check if the write job is EEPROM or RTC RAM if ( recordCurrentJob.memoryLocation == NVDATAMGMT_EEPROM && FAPI_CHECK_FSM_READY_BUSY == Fapi_Status_FsmReady ) { U32 i; U08* bufferPtr = jobSpecs.structAddressPtr + recordAddressOffset; // Loop through the bytes in the buffer for ( i = 0; i < jobSpecs.maxWriteBufferSize; i++ ) { // Check if data in the buffer is not the same as the data in the structure if ( writtenRecordCheckBuffer[ i ] != *bufferPtr ) { // Data is not valid. Schedule an erase and write of the calibration record since only a part of // data cannot be written to EEPROM. writtenRecordStatus = NVDATAMGMT_RECORD_NOT_VALID; enqueueRecordJob( NVDATAMGMT_ERASE_SECTOR, NVDATAMGMT_EEPROM, NVDATAMGMT_CALIBRATION_RECORD ); enqueueRecordJob( NVDATAMGMT_WRITE, NVDATAMGMT_EEPROM, NVDATAMGMT_CALIBRATION_RECORD ); state = NVDATAMGMT_EXEC_STATE_IDLE; // Exit the loop since there is no point to check the rest of data break; } else { // Everything is good increment the pointer to check the next byte. bufferPtr++; // Record data is valid so far writtenRecordStatus = NVDATAMGMT_RECORD_VALID; // Go back write to EEPROM state to continue writing the record data state = NVDATAMGMT_EXEC_STATE_WRITE_TO_EEPROM; } } } return state; } /*********************************************************************//** * @brief * The handleExecReadFromEEPROMState issues a read command to EEPROM on entry * and if the read was successful, it sets the state to Idle. * @details Inputs: currentJob, activeQueue * @details Outputs: currentJob, activeQueue * @return next state *************************************************************************/ static NVDATAMGMT_EXEC_STATE_T handleExecReadFromEEPROMState ( void ) { NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_READ_FROM_EEPROM; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_NVDATA_EEPROM_OPS_FAILURE, (U08*)&state ); if ( FAPI_CHECK_FSM_READY_BUSY == Fapi_Status_FsmReady || timeoutStatus == TRUE ) { // Signal the external client that the read is complete. // This is only needed for reading the data logs and not the records if ( activeQueue == NVDATAMGMT_PROCESS_LOGS ) { currentJob.externalAddress->status = NVDATAMGMT_READ_COMPLETE; activeQueue = NVDATAMGMT_QUEUE_NONE; } state = NVDATAMGMT_EXEC_STATE_IDLE; } return state; } /*********************************************************************//** * @brief * The handleExecEraseState issues an erase command to EEPROM on entry * and if the erase was successful, it sets the state to Idle. * @details Inputs: none * @details Outputs: none * @return next state *************************************************************************/ static NVDATAMGMT_EXEC_STATE_T handleExecEraseState ( void ) { NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_ERASE_EEPROM; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_NVDATA_EEPROM_OPS_FAILURE, (U08*)&state ); if ( FAPI_CHECK_FSM_READY_BUSY == Fapi_Status_FsmReady || timeoutStatus == TRUE ) { state = NVDATAMGMT_EXEC_STATE_IDLE; } return state; } /*********************************************************************//** * @brief * The handleExecWriteToRAMState issues a write command to RTC RAM if the * RAM was ready, and if the RAM got back to Idle after the write command, * it sets the state to Idle. * @details Inputs: currentTime, writtenRecordStatus, recordAddressOffset * @details Outputs: currentTime, writtenRecordStatus, recordAddressOffset * @return next state *************************************************************************/ static NVDATAMGMT_EXEC_STATE_T handleExecWriteToRAMState ( void ) { NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_WRITE_TO_RTC; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_RTC_RAM_OPS_ERROR, (U08*)&state ); RECORD_JOBS_STATE_T job = recordCurrentJob.recordJob; PROCESS_RECORD_SPECS_T jobSpecs = RECORDS_SPECS [ job ]; // Check if the RTC RAM has finished if ( getRTCRAMStatus() == RTC_RAM_STATUS_IDLE ) { // Check the integrity of data that was written if ( writtenRecordStatus == NVDATAMGMT_RECORD_NOT_CHECKED ) { currentTime = getMSTimerCount(); U32 startAddress = jobSpecs.startAddress + recordAddressOffset; readFromRAM( startAddress, jobSpecs.maxWriteBufferSize ); state = NVDATAMGMT_EXEC_STATE_VERIFY_RTC_WRITE; } // The written record was not valid. rewrite it again. else if ( writtenRecordStatus == NVDATAMGMT_RECORD_NOT_VALID ) { U32 memoryPtr = jobSpecs.startAddress + recordAddressOffset; U08* structPtr = jobSpecs.structAddressPtr + recordAddressOffset; // Set the check status to not checked to check the data again writtenRecordStatus = NVDATAMGMT_RECORD_NOT_CHECKED; // Issue the write command writeToRAM ( memoryPtr, structPtr, jobSpecs.maxWriteBufferSize ); } else if ( writtenRecordStatus == NVDATAMGMT_RECORD_VALID ) { // If the data is valid, and if it is at the end of the write, or the size of job is equal to // max write buffer, go to idle if ( jobSpecs.sizeofJob == recordAddressOffset || jobSpecs.sizeofJob == jobSpecs.maxWriteBufferSize ) { writtenRecordStatus = NVDATAMGMT_RECORD_NOT_CHECKED; state = NVDATAMGMT_EXEC_STATE_IDLE; } else { // Update the variables and issue the next write command currentTime = getMSTimerCount(); recordAddressOffset += jobSpecs.maxWriteBufferSize; U32 memoryPtr = jobSpecs.startAddress + recordAddressOffset; U08* structPtr = jobSpecs.structAddressPtr + recordAddressOffset; writtenRecordStatus = NVDATAMGMT_RECORD_NOT_CHECKED; // Issue the write command writeToRAM ( memoryPtr, structPtr, jobSpecs.maxWriteBufferSize ); } } } // If timed out, get back to Idle else if ( timeoutStatus == TRUE ) { // TODO schedule another write? recordAddressOffset = 0; state = NVDATAMGMT_EXEC_STATE_IDLE; } return state; } /*********************************************************************//** * @brief * The handleExecVerifyRTCWriteState checks all the bytes that were * written to RTC RAM to ensure they match the data in the NV memory. * @details Inputs: recordAddressOffset, writtenRecordStatus * @details Outputs: recordAddressOffset, writtenRecordStatus * @return next state of the state machine *************************************************************************/ static NVDATAMGMT_EXEC_STATE_T handleExecVerifyRTCWriteState( void ) { NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_VERIFY_RTC_WRITE; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_NVDATA_EEPROM_OPS_FAILURE, (U08*)&state ); RECORD_JOBS_STATE_T job = recordCurrentJob.recordJob; PROCESS_RECORD_SPECS_T jobSpecs = RECORDS_SPECS [ job ]; // Check if the write job is EEPROM or RTC RAM if ( getRTCRAMStatus() == RTC_RAM_STATUS_IDLE || timeoutStatus == TRUE ) { U32 i; U08* bufferPtr = jobSpecs.structAddressPtr + recordAddressOffset; // Create a temporary buffer with the size of maximum RTC write U08 tempBufferToCheckData [ MAX_RTC_RAM_OPS_BUFFER_BYTES ]; getDataFromRAM( tempBufferToCheckData, jobSpecs.maxWriteBufferSize ); // Loop through the bytes in the buffer for ( i = 0; i < jobSpecs.maxWriteBufferSize; i++ ) { // Check if data in the buffer is not the same as the data in the structure if ( tempBufferToCheckData[ i ] != *bufferPtr ) { // Data is not valid. Go back to write to RTC state and rewrite the portion that was written // but it is correct. writtenRecordStatus = NVDATAMGMT_RECORD_NOT_VALID; state = NVDATAMGMT_EXEC_STATE_WRITE_TO_RTC; // Exit the loop since there is no point to check the rest of data break; } else { // Everything is good increment the pointer to check the next byte. bufferPtr++; // Record data is valid so far writtenRecordStatus = NVDATAMGMT_RECORD_VALID; // Go back write to RTC state to continue writing the record data state = NVDATAMGMT_EXEC_STATE_WRITE_TO_RTC; } } } return state; } /*********************************************************************//** * @brief * The handleExecReadFromRAMState issues a read command to RTC RAM if the * RAM was ready, and if the RAM got back to Idle after the read command, * it calls another function to read the data back from RTC RAM and * it sets the state to Idle. * @details Inputs: activeQueue, currentJob, recordCurrentJob * @details Outputs: currentJob * @return next state *************************************************************************/ static NVDATAMGMT_EXEC_STATE_T handleExecReadFromRAMState ( void ) { NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_READ_FROM_RTC; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_RTC_RAM_OPS_ERROR, (U08*)&state ); if ( getRTCRAMStatus() == RTC_RAM_STATUS_IDLE || timeoutStatus == TRUE ) { U08* bufferPtr = 0; U32 length = 0; if ( activeQueue == NVDATAMGMT_PROCESS_RECORDS ) { RECORD_JOBS_STATE_T job = recordCurrentJob.recordJob; PROCESS_RECORD_SPECS_T jobSpecs = RECORDS_SPECS [ job ]; bufferPtr = jobSpecs.structAddressPtr; length = jobSpecs.sizeofJob; } else { // TODO add the rest of the items for for logging currentJob.externalAddress->status = NVDATAMGMT_READ_COMPLETE; } getDataFromRAM( bufferPtr, length ); state = NVDATAMGMT_EXEC_STATE_IDLE; } return state; } /*********************************************************************//** * @brief * The handleExecProcessRecordIdleState handles the idle state of the * exec cal state machine. * @details Inputs: hasPublishRecordBeenRequested, calPublishTotalMessages, * calPublishMessageCount, calSendDataIntervalCounter * @details Outputs: hasPublishRecordBeenRequested, calPublishTotalMessages, * calPublishMessageCount, calSendDataIntervalCounter * @return next state of the state machine *************************************************************************/ static PROCESS_RECORD_STATE_T handleExecProcessRecordIdleState( void ) { PROCESS_RECORD_STATE_T state = NVDATAMGMT_PROCESS_RECORD_STATE_IDLE; if ( TRUE == hasPublishRecordBeenRequested ) { // Set the publish flag to FALSE hasPublishRecordBeenRequested = FALSE; // Get the record specifications to find the size of the job PROCESS_RECORD_SPECS_T recordSpec = RECORDS_SPECS [ recordToPublish ]; // Calculate the total number of messages required to be sent using ceilf function. This function rounds up the // value and its result is converted to U32. calPublishTotalMessages = (U32)ceilf( (F32)recordSpec.sizeofJob / (F32)NUM_OF_BYTES_PER_CAL_PAYLOAD ); calPublishMessageCount = 0; // Time interval in between data to be sent. It is set to the interval count so on the first call // of the send calibration record function, the first packet of data is sent calSendDataIntervalCounter = CAL_DATA_SEND_INTERVAL_COUNT; state = NVDATAMGMT_PROCESS_RECORD_STATE_SEND_RECORD; } return state; } /*********************************************************************//** * @brief * The handleExecProcessRecordSendRecordState handles the send calibration * record state of the state machine. * @details Inputs: dgCalibrationRecord, calPublishTotalMessages, * calPublishMessageCount, calSendDataIntervalCounter * @details Outputs: calPublishTotalMessages, calPublishMessageCount, * calSendDataIntervalCounter * @return next state of the state machine *************************************************************************/ static PROCESS_RECORD_STATE_T handleExecProcessRecordSendRecordState( void ) { PROCESS_RECORD_STATE_T state = NVDATAMGMT_PROCESS_RECORD_STATE_SEND_RECORD; // If the current message number is less than the total, keep sending if ( calPublishMessageCount < calPublishTotalMessages ) { // If it is time to send data if ( ++calSendDataIntervalCounter >= CAL_DATA_SEND_INTERVAL_COUNT ) { // Set to default cal data payload length U32 length = NUM_OF_BYTES_PER_CAL_PAYLOAD; PROCESS_RECORD_SPECS_T recordSpec = RECORDS_SPECS [ recordToPublish ]; U08* startPtr = recordSpec.structAddressPtr; // If this is the last calibration data payload, calculate the remainder of the bytes to send if ( ( calPublishMessageCount + 1 ) == calPublishTotalMessages ) { length = recordSpec.sizeofJob - ( calPublishMessageCount * NUM_OF_BYTES_PER_CAL_PAYLOAD ); } // Find the new location of the pointer which is the start of the calibration payload to be sent startPtr += calPublishMessageCount * NUM_OF_BYTES_PER_CAL_PAYLOAD; switch( recordToPublish ) { case NVDATAMGMT_CALIBRATION_RECORD: #ifdef _DG_ // Pass the information to the CAN bus sendDGCalibrationRecord( calPublishMessageCount + 1, calPublishTotalMessages, length, startPtr ); #endif #ifdef _HD_ sendHDCalibrationRecord( calPublishMessageCount + 1, calPublishTotalMessages, length, startPtr ); #endif break; case NVDATAMGMT_SYSTEM_RECORD: #ifdef _DG_ // Pass the information to the CAN bus sendDGSystemRecord( calPublishMessageCount + 1, calPublishTotalMessages, length, startPtr ); #endif #ifdef _HD_ sendHDSystemRecord( calPublishMessageCount + 1, calPublishTotalMessages, length, startPtr ); #endif break; case NVDATAMGMT_SERVICE_RECORD: #ifdef _DG_ // Pass the information to the CAN bus sendDGServiceRecord( calPublishMessageCount + 1, calPublishTotalMessages, length, startPtr ); #endif #ifdef _HD_ sendHDServiceRecord( calPublishMessageCount + 1, calPublishTotalMessages, length, startPtr ); #endif break; #ifdef _DG_ case NVDATAMGMT_SCHEDULED_RUNS_RECORD: // Pass the information to the CAN bus sendDGScheduledRunsRecord( calPublishMessageCount + 1, calPublishTotalMessages, length, startPtr ); // There are no scheduled runs for HD right now. break; #endif case NVDATAMGMT_SW_CONFIG_RECORD: #ifdef _DG_ sendDGSWConfigRecord( calPublishMessageCount + 1, calPublishTotalMessages, length, startPtr ); #endif break; default: //Ignore break; } calPublishMessageCount++; calSendDataIntervalCounter = 0; } } else { state = NVDATAMGMT_PROCESS_RECORD_STATE_IDLE; } return state; } /*********************************************************************//** * @brief * The setMemoryOpsStruct function checks whether it is an EEPROM write, * EEPROM read, or RTC RAM operation. For read or write data logs into EEPROM, * it calls other functions to check for wraps and to find the next address for * read or write. The function then checks for the length of the buffer and if * it is greater than the specified max buffer, it breaks the operation into * multiple jobs. * @details Inputs: jobQueue, queueCount, queueRearIndex * @details Outputs: jobQueue, queueCount, queueRearIndex * @param ops type of operation (i.e. write to EEPROM) * @param location location of operation (i.e. RTC RAM) * @param startAddress start address of operation * @param data address to the data buffer * @param extAddress address of the external buffer. Used for reading the log * data by a client * @param length length of data for operation * @return none *************************************************************************/ static void setMemoryOpsStruct ( NVDATAMGMT_OPERATION_STATE_T ops, NVDATAMGMT_LOCATION_STATE_T location, U32 startAddress, U08* data, READ_DATA_T* extAddress, U32 length ) { U32 myAddIndex; _disable_IRQ(); myAddIndex = queueRearIndex; if ( FALSE == isQueueFull() ) { queueCount++; queueRearIndex = INC_WRAP( queueRearIndex, 0, QUEUE_MAX_SIZE - 1 ); jobQueue [ myAddIndex ].memoryOperation = ops; jobQueue [ myAddIndex ].memoryLocation = location; jobQueue [ myAddIndex ].startAddress = startAddress; jobQueue [ myAddIndex ].length = length; jobQueue [ myAddIndex ].externalAddress = extAddress; if ( extAddress == 0 ) { memcpy ( jobQueue [ myAddIndex ].buffer, data, length ); } } _enable_IRQ(); } /*********************************************************************//** * @brief * The prepareWriteLogJobAndGetStartAddress checks whether the next write * is at edge of the any of the sectors and if yes, it will update the log * header accordingly. If the write is not at the edge, it will prepare a * normal write to EEPROM job. * @details Inputs: logRecord * @details Outputs: logRecord * @param data address of data buffer * @return address of the next write as a U32 *************************************************************************/ static U32 prepareWriteLogJobAndGetStartAddress ( U08* data ) { U32 modulus = 0; U32 opsStartAddress = 0; U16 readIndexChange = 0; U16 writeIndexChange = 0; U16 recordCountChange = 1; // The write address is calculated using the next write index and is and offset from // the start of sector 1. Sectors 1,2, and 3 have been allocated for logging data opsStartAddress = ( logRecord.logHeader.nextWriteIndex * MAX_JOB_DATA_SIZE_BYTES ) + BANK7_SECTOR1_START_ADDRESS; modulus = logRecord.logHeader.nextWriteIndex % MAX_LOG_DATA_PER_SECTOR; // Modulus is 0 so it is at any of the edges if ( modulus == 0 ) { // If full (1536) // 1. set readIndexChange = +512 // 2. set recordCountchange = -512 if ( logRecord.logHeader.recordCount >= MAX_NUM_OF_EVENTS_IN_SECTOR3 - 1 ) { logRecord.logHeader.recordCount = logRecord.logHeader.recordCount - MAX_LOG_DATA_PER_SECTOR; readIndexChange = MAX_LOG_DATA_PER_SECTOR; } setMemoryOpsStruct ( NVDATAMGMT_ERASE_SECTOR, NVDATAMGMT_EEPROM, opsStartAddress, 0, 0, 0 ); } // Check for wrap in 1536 which is at the end of sector 3 writeIndexChange = INC_WRAP( logRecord.logHeader.nextWriteIndex, LOG_DATA_START_INDEX, MAX_NUM_OF_EVENTS_IN_SECTOR3 - 1 ); logRecord.logHeader.nextWriteIndex = writeIndexChange; logRecord.logHeader.recordCount = logRecord.logHeader.recordCount + recordCountChange; logRecord.logHeader.nextReadIndex = logRecord.logHeader.nextReadIndex + readIndexChange; logRecord.logHeader.isHdrCorrupted = FALSE; // Calculate the CRC for the new record logRecord.crc = crc16 ( (U08*)&logRecord.logHeader, sizeof(LOG_HEADER_T) ); // Update the log record setMemoryOpsStruct ( NVDATAMGMT_WRITE, NVDATAMGMT_RTC, LOG_RECORD_START_ADDRESS, (U08*)&logRecord, 0, sizeof(LOG_RECORD_T) ); return opsStartAddress; } /*********************************************************************//** * @brief * The prepareReadLogJobAndGetStartAddress prepares a read from the specified * address of the EEPROM. * @details Inputs: logRecord * @details Outputs: logRecord * @return address of the next read as a U32 *************************************************************************/ static U32 prepareReadLogJobAndGetStartAddress ( void ) { // The read address is calculated using the next read index and is an offset from // the start of sector 1. Sectors 1,2, and 3 have been allocated for logging data U32 opsStartAddress = ( logRecord.logHeader.nextReadIndex * MAX_JOB_DATA_SIZE_BYTES ) + BANK7_SECTOR1_START_ADDRESS; U16 readIndexChange = INC_WRAP( logRecord.logHeader.nextReadIndex, LOG_DATA_START_INDEX, MAX_NUM_OF_EVENTS_IN_SECTOR3 - 1 ); logRecord.logHeader.nextReadIndex = readIndexChange; // Check if the read index has been wrapped to 0 and the flag is for data corruption // is true, the record count will be 0 and the data corruption flag will be false for // normal operations if ( logRecord.logHeader.nextReadIndex == 0 && logRecord.logHeader.isHdrCorrupted == TRUE ) { logRecord.logHeader.recordCount = 0; // If there are enough queues to schedule 3 erases // the flag will be turned off if ( eraseDataLogSectors () ) { logRecord.logHeader.isHdrCorrupted = FALSE; } } logRecord.crc = crc16 ( (U08*)&logRecord.logHeader, sizeof(LOG_HEADER_T) ); // Update the log record setMemoryOpsStruct ( NVDATAMGMT_WRITE, NVDATAMGMT_RTC, LOG_RECORD_START_ADDRESS, (U08*)&logRecord, 0, sizeof(LOG_RECORD_T) ); return opsStartAddress; } /*********************************************************************//** * @brief * The enqueue prepares the jobs to be processed. It checks if the requested * job is read or write, or if it is RTC or EEPROM and sets the job struct. * It checks for the corner cases, for instance, if the next write log to EEPROM * is at the beginning of the next sector, it schedules and erase first and then * it schedules the write jobs. The function breaks the write to EEPROM jobs to * 16 bytes at the time. * @details Inputs: none * @details Outputs: none * @param ops type of operation (i.e. write to EEPROM) * @param location location of operation (i.e. RTC RAM) * @param startAddress start address of operation * @param data address to the data buffer * @param extAddress address of the external buffer. Used for reading the log * data by a client * @param length length of data for operation * @return none *************************************************************************/ static void enqueue ( NVDATAMGMT_OPERATION_STATE_T ops, NVDATAMGMT_LOCATION_STATE_T location, U32 startAddress, U08* data, READ_DATA_T* extAddress, U32 length ) { U32 quotient = 0; U32 modulus = 0; U32 maxBufferLength = length; U32 opsStartAddress = startAddress; U08 i; // Setup EEPROM write log event if ( ops == NVDATAMGMT_WRITE && location == NVDATAMGMT_EEPROM ) { maxBufferLength = MAX_EEPROM_WRITE_BUFFER_BYTES; // If the start address is 0, it means it is a data log and the start // address must be found by referring to the log record header if ( startAddress == 0 ) { opsStartAddress = prepareWriteLogJobAndGetStartAddress ( data ); } } // Setup EEPROM read log event else if ( ops == NVDATAMGMT_READ && location == NVDATAMGMT_EEPROM ) { // If the start address is 0, it means it is a data log and the start // address must be found by referring to the log record header if ( startAddress == 0 ) { opsStartAddress = prepareReadLogJobAndGetStartAddress(); } } // Maximum length for write is 16 bytes // Maximum length for read is 32 bytes // If the length is greater than the specified max lengths, // the operation will be broken into multiple pieces if ( length > maxBufferLength ) { quotient = length / maxBufferLength; modulus = length % maxBufferLength; for ( i = 0; i < quotient; i++ ) { setMemoryOpsStruct ( ops, location, ( opsStartAddress + ( i * maxBufferLength ) ), data + ( i * maxBufferLength ), extAddress, maxBufferLength ); } if ( modulus != 0 ) { setMemoryOpsStruct ( ops, location, ( opsStartAddress + ( quotient * maxBufferLength ) ), data + ( quotient * maxBufferLength ), extAddress, modulus ); } } // The length is less than maximum specified, normal operations else { setMemoryOpsStruct ( ops, location, opsStartAddress, data, extAddress, length ); } } /*********************************************************************//** * @brief * The dequeue increments the front index counter and if it is equal to * rear index, it sets it to -1, meaning that the queue is empty. * @details Inputs: queueFrontIndex, queueCount * @details Outputs: queueFrontIndex, queueCount * @return none *************************************************************************/ static void dequeue ( void ) { U32 tempIndex; _disable_IRQ(); tempIndex = queueFrontIndex; if ( !isQueueEmpty() ) { queueFrontIndex = INC_WRAP( queueFrontIndex, 0, QUEUE_MAX_SIZE - 1 ); currentJob = jobQueue [ tempIndex ]; } if ( queueCount > 0 ) { queueCount--; } _enable_IRQ(); } /*********************************************************************//** * @brief * The isQueueEmpty checks whether the queue is empty and if it is empty, * it will return a false. * @details Inputs: queueCount * @details Outputs: none * @return TRUE if queue is not empty *************************************************************************/ static BOOL isQueueEmpty ( void ) { BOOL isEmpty = TRUE; if ( queueCount > 0 ) { isEmpty = FALSE; } return isEmpty; } /*********************************************************************//** * @brief * The isQueueFull checks whether the queue is full and if it is full, * it will return a true. * @details Inputs: queueCount * @details Outputs: none * @return TRUE is queue is full *************************************************************************/ static BOOL isQueueFull ( void ) { BOOL isFull = FALSE; if ( queueCount >= QUEUE_MAX_SIZE - 1 ) { isFull = TRUE; } return isFull; } /*********************************************************************//** * @brief * The getAvailableQueueCount returns the number of available queues left. * @details Inputs: queueCount * @details Outputs: none * @return available queue counts as a U32 *************************************************************************/ static U32 getAvailableQueueCount ( void ) { return QUEUE_MAX_SIZE - queueCount; } /*********************************************************************//** * @brief * The didCommandTimedout checks whether the a command whether RTC RAM or * EEPROM has timedout. If it has timedout, it sets the alarm and turns * flag to TRUE. * @details Inputs: none * @details Outputs: alarm if command timed out * @param alarm alarm ID * @param state the state that the command timed out * @return TRUE if a command timed out *************************************************************************/ static BOOL didCommandTimeout ( ALARM_ID_T alarm, U08* state ) { /* * NOTE: The state is a pointer so it can cover both the self-test * states and exec states */ BOOL status = FALSE; if ( didTimeout( currentTime, COMMAND_TIME_OUT ) ) { SET_ALARM_WITH_1_U32_DATA( alarm, *state ); status = TRUE; } return status; } /*********************************************************************//** * @brief * The eraseDataLogSectors checks whether there are enough queues available * and if there are, it schedules 3 erases to erase sectors 1,2, and 3. * @details Inputs: none * @details Outputs: non * @return TRUE if there are enough queues available *************************************************************************/ static BOOL eraseDataLogSectors ( void ) { BOOL status = FALSE; U32 availableQueue = getAvailableQueueCount(); if ( availableQueue >= MIN_JOBS_NEEDED_FOR_DATA_LOG ) { enqueue( NVDATAMGMT_ERASE_SECTOR, NVDATAMGMT_EEPROM, BANK7_SECTOR1_START_ADDRESS, 0, 0, 0 ); enqueue( NVDATAMGMT_ERASE_SECTOR, NVDATAMGMT_EEPROM, BANK7_SECTOR2_START_ADDRESS, 0, 0, 0 ); enqueue( NVDATAMGMT_ERASE_SECTOR, NVDATAMGMT_EEPROM, BANK7_SECTOR3_START_ADDRESS, 0, 0, 0 ); status = TRUE; } return status; } /*********************************************************************//** * @brief * The monitorNewCalSignal function monitors the new calibration signal * and if it on and time to on has elapsed, it turns it off. * @details Inputs: isNewCalAvailable * @details Outputs: isNewCalAvailable * @return none *************************************************************************/ static void monitorNewCalSignal( void ) { if ( ( TRUE == didTimeout( newCalStartTimer, NEW_CAL_AVAILABLE_SIGNAL_TIMEOUT_MS ) ) && ( TRUE == isNewCalAvailable ) ) { isNewCalAvailable = FALSE; } } /*********************************************************************//** * @brief * The areRecordsValid function checks whether the records are still valid * by calculating the CRCs and comparing it to the strucutre's CRC. * @details Inputs: none * @details Outputs: none * @return TRUE if the records' data is valid otherwise FALSE *************************************************************************/ static BOOL areRecordsValid( void ) { ALARM_ID_T alarm; BOOL status = TRUE; PROCESS_RECORD_SPECS_T spec = RECORDS_SPECS [ NVDATAMGMT_CALIBRATION_RECORD ]; U16 calcCRC = crc16 ( spec.structAddressPtr, spec.sizeofJob - sizeof(U16) ); U16 recordCRC = *(U16*)spec.structCRCPtr; // If the CRCs do not match, break out of loop since POST will be failed // regardless of the rest of the results if ( calcCRC != recordCRC ) { #ifdef _DG_ alarm = ALARM_ID_DG_NVDATAMGMT_CAL_GROUP_RECORD_CRC_INVALID; dgCalibrationRecord.crc = calcCRC; #endif #ifdef _HD_ alarm = ALARM_ID_HD_NVDATAMGMT_CAL_GROUP_RECORD_CRC_INVALID; hdCalibrationRecord.crc = calcCRC; #endif activateAlarmNoData( alarm ); status = FALSE; } return status; } /*********************************************************************//** * @brief * The isPolynomialRecordValid function checks whether the records are * still valid by calculating the CRCs and comparing it to the strucutre's * CRC. * @details Inputs: none * @details Outputs: none * @param record: pointer to a polynomial payload. The actual calibration * data to be checked * @param isRecordNeeded: TRUE is the calibration record is need in the * firmware right now otherwise, FALSE * @return TRUE if the records' data is valid otherwise FALSE *************************************************************************/ static BOOL isPolynomialRecordValid( POLYNOMIAL_CAL_PAYLOAD_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof(POLYNOMIAL_CAL_PAYLOAD_T) - sizeof(U16) ); U16 recordCRC = record->crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default 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; // Recalculate the CRC with the default values record->crc = crc16 ( (U08*)record, sizeof(POLYNOMIAL_CAL_PAYLOAD_T) - sizeof(U16) ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } #ifdef _DG_ /*********************************************************************//** * @brief * The isDGSystemRecordValid function checks the validity of the DG system * record. * @details Inputs: dgSystemGroup.dgSystemRecord * @details Outputs: none * @return TRUE if the DG system record is valid otherwise FALSE *************************************************************************/ static BOOL isDGSystemRecordValid( void ) { BOOL status = TRUE; U16 calcCRC = crc16( (U08*)&dgSystemGroup.dgSystemRecord, sizeof(DG_SYSTEM_RECORD_T) - sizeof(U16) ); U16 recordCRC = dgSystemGroup.dgSystemRecord.crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default dgSystemGroup.dgSystemRecord.mfgDate = 0; dgSystemGroup.dgSystemRecord.mfgLocation = 0; memset( dgSystemGroup.dgSystemRecord.topLevelPN, RECORD_DEFAULT_CHARACTER, sizeof( dgSystemGroup.dgSystemRecord.topLevelPN ) ); memset( dgSystemGroup.dgSystemRecord.topLevelPN, RECORD_DEFAULT_CHARACTER, sizeof( dgSystemGroup.dgSystemRecord.topLevelPN ) ); // Recalculate the CRC with the default values dgSystemGroup.dgSystemRecord.crc = crc16 ( (U08*)&dgSystemGroup.dgSystemRecord, sizeof(DG_SYSTEM_RECORD_T) - sizeof(U16) ); activateAlarmNoData( ALARM_ID_DG_INVALID_SYSTEM_RECORD_CRC ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } /*********************************************************************//** * @brief * The isDGServiceRecordValid function checks the validity of the DG service * record. * @details Inputs: dgServiceGroup.dgServiceRecord * @details Outputs: none * @return TRUE if the DG service record is valid otherwise FALSE *************************************************************************/ static BOOL isDGServiceRecordValid( void ) { BOOL status = TRUE; U16 calcCRC = crc16( (U08*)&dgServiceGroup.dgServiceRecord, sizeof(DG_SERVICE_RECORD_T) - sizeof(U16) ); U16 recordCRC = dgServiceGroup.dgServiceRecord.crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default dgServiceGroup.dgServiceRecord.lastServiceEpochDate = 0; dgServiceGroup.dgServiceRecord.serviceIntervalSeconds = RECORD_DEFAULT_SERVICE_INTERVAL_S; // Recalculate the CRC with the default values dgServiceGroup.dgServiceRecord.crc = crc16 ( (U08*)&dgServiceGroup.dgServiceRecord, sizeof(DG_SERVICE_RECORD_T) - sizeof(U16) ); #ifndef DISABLE_SERVICE_RECORD // Service record failure is also considered as RTC RAM failure activateAlarmNoData( ALARM_ID_DG_INVALID_SERVICE_RECORD_CRC ); // Set the to FALSE since the record is not valid status = FALSE; #endif } return status; } /*********************************************************************//** * @brief * The isDGCalibrationRecordValid function calls other functions to check * the validity of DG calibration record. * @details Inputs: dgCalibrationRecord * @details Outputs: none * @return TRUE if the DG calibration record is valid otherwise FALSE *************************************************************************/ static BOOL isDGCalibrationRecordValid( void ) { U32 i; POLYNOMIAL_CAL_PAYLOAD_T* record; BOOL isHardwareRecordValid = TRUE; BOOL isCalRecordValid = TRUE; // 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; tempRecord.fourthOrderCoeff = RECORD_FOURTH_ORDER_COEFF; tempRecord.thirdOrderCoeff = RECORD_THIRD_ORDER_COEFF; tempRecord.secondOrderCoeff = RECORD_SECOND_ORDER_COEFF; tempRecord.gain = RECORD_DEFAULT_GAIN; tempRecord.offset = RECORD_DEFAULT_OFFSET; tempRecord.calibrationTime = RECORD_DEFAULT_TIME; // Recalculate the CRC with the default values tempRecord.crc = crc16 ( (U08*)&tempRecord, sizeof(POLYNOMIAL_CAL_PAYLOAD_T) - sizeof(U16) ); // Get the calibration record of the hardware (i.e. pressure sensor) DG_PRES_SENSORS_CAL_RECORD_T* pressure = &dgCalibrationRecord.dgCalibrationGroups.presSensorsCalRecord; // The ones that are an array, are looped through for ( i = 0; i < NUM_OF_CAL_DATA_PRES_SENSORS; i++ ) { // Get calibration payload and assign it to a pointer record = (POLYNOMIAL_CAL_PAYLOAD_T*)&pressure->pressureSensors[ i ]; // Check in the validity of the calibration data // If the variable is already FALSE, let it be FALSE. Even if one record is not // valid, the values should be set to benign values. This variable is used to decide // whether a write should be scheduled or not so it should not be overwritten with a TRUE // once a record set it to FALSE isHardwareRecordValid = isPolynomialRecordValid( record ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; } for ( i = 0; i < PRES_SENSORS_RESERVED_SPACE_COUNT; i++ ) // Benign the pressures reserved spaces memcpy( (POLYNOMIAL_CAL_PAYLOAD_T*)&pressure->reservedSpace[ i ], &tempRecord, sizeof( POLYNOMIAL_CAL_PAYLOAD_T ) ); DG_FLOW_SENSORS_CAL_RECORD_T* flow = &dgCalibrationRecord.dgCalibrationGroups.flowSensorsCalRecord; for ( i = 0; i < NUM_OF_CAL_DATA_FLOW_SENSORS; i++ ) { record = (POLYNOMIAL_CAL_PAYLOAD_T*)&flow->flowSensors[ i ]; isHardwareRecordValid = isPolynomialRecordValid( record ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; } for ( i = 0; i < FLOW_SENSROS_RESERVED_SPACE_COUNT; i++ ) // Benign the the flow sensors reserved spaces memcpy( (POLYNOMIAL_CAL_PAYLOAD_T*)&flow->reservedSpace[ i ], &tempRecord, sizeof( POLYNOMIAL_CAL_PAYLOAD_T ) ); DG_LOAD_CELLS_CAL_RECORD_T* load = &dgCalibrationRecord.dgCalibrationGroups.loadCellsCalRecord; for ( i = 0; i < NUM_OF_CAL_DATA_LOAD_CELLS; i++ ) { record = (POLYNOMIAL_CAL_PAYLOAD_T*)&load->loadCells[ i ]; isHardwareRecordValid = isPolynomialRecordValid( record ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; } DG_TEMP_SENSORS_CAL_RECORD_T* temperature = &dgCalibrationRecord.dgCalibrationGroups.tempSensorsCalRecord; for ( i = 0; i < NUM_OF_CAL_DATA_TEMP_SENSORS; i++ ) { record = (POLYNOMIAL_CAL_PAYLOAD_T*)&temperature->tempSensors[ i ]; isHardwareRecordValid = isPolynomialRecordValid( record ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; } for ( i = 0; i < TEMP_SENSORS_RESERVED_SPACE_COUNT; i++ ) // Benign the temperature sensors reserved spaces memcpy( (POLYNOMIAL_CAL_PAYLOAD_T*)&temperature->reservedSpace[ i ], &tempRecord, sizeof( POLYNOMIAL_CAL_PAYLOAD_T ) ); DG_COND_SENSORS_CAL_RECORD_T* cond = &dgCalibrationRecord.dgCalibrationGroups.condSensorsCalRecord; for ( i = 0; i < NUM_OF_CAL_DATA_COND_SENSORS; i++ ) { record = (POLYNOMIAL_CAL_PAYLOAD_T*)&cond->condSensors[ i ]; isHardwareRecordValid = isPolynomialRecordValid( record ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; } for ( i = 0; i < COND_SENSORS_RESERVED_SPACE_COUNT; i++ ) // Benign the conductivity sensors reserved spaces memcpy( (POLYNOMIAL_CAL_PAYLOAD_T*)&cond->reservedSpace[ i ], &tempRecord, sizeof( POLYNOMIAL_CAL_PAYLOAD_T ) ); DG_CONC_PUMPS_CAL_RECORD_T* concPump = &dgCalibrationRecord.dgCalibrationGroups.concentratePumpsRecord; for ( i = 0; i < NUM_OF_CAL_DATA_DG_CONC_PUMPS; i++ ) { isHardwareRecordValid = isDGConcPumpRecordValid( &concPump->concentratePumps[ i ] ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; } DG_DRAIN_PUMP_CAL_RECORD_T* drainPump = &dgCalibrationRecord.dgCalibrationGroups.drainPumpRecord; isHardwareRecordValid = isDGDrainPumpRecordValid( drainPump ) != TRUE ? FALSE : isCalRecordValid; isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; DG_RO_PUMP_CAL_RECORD_T* roPump = &dgCalibrationRecord.dgCalibrationGroups.roPumpRecord; isHardwareRecordValid = isDGROPumpRecordValid( roPump ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; DG_PRE_RO_PURGE_VOLUME_T* preROPurgeVolume = &dgCalibrationRecord.dgCalibrationGroups.preROPurgeVolumeRecord; isHardwareRecordValid = isDGPreROPurgeVolumeRecordValid( preROPurgeVolume ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; DG_DRAIN_LINE_VOLUME_T* drainLineVol = &dgCalibrationRecord.dgCalibrationGroups.drainLineVolumeRecord; isHardwareRecordValid = isDGDrainLineVolRecordValid( drainLineVol ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; DG_RESERVOIR_VOLUME_RECORD_T* reservoirVol = &dgCalibrationRecord.dgCalibrationGroups.reservoirVolumesRecord; for ( i = 0; i < NUM_OF_CAL_DATA_RSRVRS; i++ ) { isHardwareRecordValid = isDGReservoirVolRecordValid( &reservoirVol->reservoir[ i ] ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; } DG_GENERIC_VOLUME_RECORD_T* genericVol = &dgCalibrationRecord.dgCalibrationGroups.genericVolumeRecord; for ( i = 0; i < GENERIC_VOL_RESERVED_SPACE_COUNT; i++ ) { isHardwareRecordValid = isDGGenericVolRecordValid( &genericVol->genericVolume[ i ] ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; } DG_ACID_CONCENTRATES_RECORD_T* acidConc = &dgCalibrationRecord.dgCalibrationGroups.acidConcentratesRecord; for ( i = 0; i < NUM_OF_CAL_DATA_ACID_CONCENTRATES; i++ ) { isHardwareRecordValid = isDGAcidConcentrateRecordValid( &acidConc->acidConcentrate[ i ] ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; } DG_BICARB_CONCENTRATES_RECORD_T* bicarbConc = &dgCalibrationRecord.dgCalibrationGroups.bicarbConcentratesRecord; for ( i = 0; i < NUM_OF_CAL_DATA_BICARB_CONCENTRATES; i++ ) { isHardwareRecordValid = isDGBicarbConcentrateRecordValid( &bicarbConc->bicarbConcentrate[ i ] ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; } DG_FILTER_CAL_RECORD_T* roFilter = &dgCalibrationRecord.dgCalibrationGroups.filtersRecord.roFilter; isHardwareRecordValid = isDGFilterRecordValid( roFilter ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; DG_FILTER_CAL_RECORD_T* ultraFilter = &dgCalibrationRecord.dgCalibrationGroups.filtersRecord.ultraFilter; isHardwareRecordValid = isDGFilterRecordValid( ultraFilter ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; DG_FILTER_CAL_RECORD_T* sedimentFilter = &dgCalibrationRecord.dgCalibrationGroups.filtersRecord.sedimentFilter; isHardwareRecordValid = isDGFilterRecordValid( sedimentFilter ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; DG_FILTER_CAL_RECORD_T* carbonFilter = &dgCalibrationRecord.dgCalibrationGroups.filtersRecord.carbonFilter; isHardwareRecordValid = isDGFilterRecordValid( carbonFilter ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; DG_FILTER_CAL_RECORD_T* carbonPolishFilter = &dgCalibrationRecord.dgCalibrationGroups.filtersRecord.carbonPolishFilter; isHardwareRecordValid = isDGFilterRecordValid( carbonPolishFilter ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; DG_FANS_CAL_RECORD_T* fan = &dgCalibrationRecord.dgCalibrationGroups.fansRecord; isHardwareRecordValid = isDGFanRecordValid( &fan->fan1 ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; isHardwareRecordValid = isDGFanRecordValid( &fan->fan2 ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; DG_ACCEL_SENSOR_CAL_RECORD_T* accelerometer = &dgCalibrationRecord.dgCalibrationGroups.accelerometerSensorCalRecord; isHardwareRecordValid = isDGAccelerometerSensorRecordValid( accelerometer ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; DG_HEATING_CAL_RECORD_T* heating = &dgCalibrationRecord.dgCalibrationGroups.heatingCalRecord; isHardwareRecordValid = isDGHeatingCalRecordValid( heating ); 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 ) { U16 finalCRC = crc16 ( (U08*)&dgCalibrationRecord, sizeof(DG_CALIBRATION_RECORD_T) - sizeof(U16) ); dgCalibrationRecord.crc = finalCRC; } return isCalRecordValid; } /*********************************************************************//** * @brief * The isSWConfigRecordValid function checks the validity of the software * configuration record. * @details Inputs: dgSystemGroup.dgSystemRecord * @details Outputs: none * @return TRUE if the DG system record is valid otherwise FALSE *************************************************************************/ static BOOL isSWConfigRecordValid( void ) { BOOL status = TRUE; U16 calcCRC = crc16( (U08*)&dgSWConfigGroup, sizeof( DG_SW_CONFIG_GROUP_T ) - sizeof(U16) ); U16 recordCRC = dgSWConfigGroup.crc; if ( calcCRC != recordCRC ) { memset( (U08*)&dgSWConfigGroup, 0, sizeof( DG_SW_CONFIG_GROUP_T ) ); // Recalculate the CRC with the default values dgSWConfigGroup.crc = crc16( (U08*)&dgSWConfigGroup, sizeof( DG_SW_CONFIG_GROUP_T ) - sizeof(U16) ); #ifdef _DG_ //activateAlarmNoData( ALARM_ID_DG_SW_CONFIG_RECORD_INVALID_CRC ); // TODO remove #endif #ifdef _HD_ //activateAlarmNoData( ALARM_ID_HD_SW_CONFIG_RECORD_INVALID_CRC ); // TODO remove #endif // Set the to FALSE since the record is not valid status = FALSE; } return status; } /*********************************************************************//** * @brief * The isDGConcPumpRecordValid function checks whether the calibration * record of concentrate pump is valid or not. * @details Inputs: none * @details Outputs: none * @param record: DG_CONC_PUMPS_CAL_DATA_T pointer * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isDGConcPumpRecordValid( DG_CONC_PUMPS_CAL_DATA_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof(DG_CONC_PUMPS_CAL_DATA_T) - sizeof(U16) ); U16 recordCRC = record->crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default record->stepSpeed2FlowRatio = RECORD_DEFAULT_RATIO; record->calibrationTime = RECORD_DEFAULT_TIME; record->crc = crc16 ( (U08*)record, sizeof(DG_CONC_PUMPS_CAL_DATA_T) - sizeof(U16) ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } /*********************************************************************//** * @brief * The isDGDrainPumpRecordValid function checks whether the calibration * record of drain pump is valid or not. * @details Inputs: none * @details Outputs: none * @param record: DG_DRAIN_PUMP_CAL_RECORD_T pointer * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isDGDrainPumpRecordValid( DG_DRAIN_PUMP_CAL_RECORD_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof(DG_DRAIN_PUMP_CAL_RECORD_T) - sizeof(U16) ); U16 recordCRC = record->crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default record->stepSpeed2FlowRatio = RECORD_DEFAULT_RATIO; record->voltage2SpeedRatio = RECORD_DEFAULT_RATIO; record->calibrationTime = RECORD_DEFAULT_TIME; record->crc = crc16 ( (U08*)record, sizeof(DG_DRAIN_PUMP_CAL_RECORD_T) - sizeof(U16) ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } /*********************************************************************//** * @brief * The isDGROPumpRecordValid function checks whether the calibration * record of RO pump is valid or not. * @details Inputs: none * @details Outputs: none * @param record: DG_RO_PUMP_CAL_RECORD_T pointer * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isDGROPumpRecordValid( DG_RO_PUMP_CAL_RECORD_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof(DG_RO_PUMP_CAL_RECORD_T) - sizeof(U16) ); U16 recordCRC = record->crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default record->gain1Ratio = RECORD_DEFAULT_RATIO; record->gain2Ratio = RECORD_DEFAULT_RATIO; record->gain3Ratio = RECORD_DEFAULT_RATIO; record->calibrationTime = RECORD_DEFAULT_TIME; record->crc = crc16 ( (U08*)record, sizeof(DG_RO_PUMP_CAL_RECORD_T) - sizeof(U16) ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } /*********************************************************************//** * @brief * The isDGPreROPurgeVolumeRecordValid function checks whether the * calibration record of pre RO purge is valid or not. * @details Inputs: none * @details Outputs: none * @param record: DG_PRE_RO_PURGE_VOLUME_T pointer * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isDGPreROPurgeVolumeRecordValid( DG_PRE_RO_PURGE_VOLUME_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof(DG_PRE_RO_PURGE_VOLUME_T) - sizeof(U16) ); U16 recordCRC = record->crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default record->pressure2FlowRatio = RECORD_DEFAULT_RATIO; record->volume = RECORD_DEFAULT_CONST; record->calibrationTime = RECORD_DEFAULT_TIME; record->crc = crc16 ( (U08*)record, sizeof(DG_PRE_RO_PURGE_VOLUME_T) - sizeof(U16) ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } /*********************************************************************//** * @brief * The isDGDrainLineVolRecordValid function checks whether the calibration * record of drain line volume is valid or not. * @details Inputs: none * @details Outputs: none * @param record: DG_DRAIN_LINE_VOLUME_T pointer * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isDGDrainLineVolRecordValid( DG_DRAIN_LINE_VOLUME_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof(DG_DRAIN_LINE_VOLUME_T) - sizeof(U16) ); U16 recordCRC = record->crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default record->volume = FLUSH_LINES_DEFAULT_VOLUME_L; record->calibrationTime = RECORD_DEFAULT_TIME; record->crc = crc16 ( (U08*)record, sizeof(DG_DRAIN_LINE_VOLUME_T) - sizeof(U16) ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } /*********************************************************************//** * @brief * The isDGReservoirVolRecordValid function checks whether the calibration * record of reservoir volume is valid or not. * @details Inputs: none * @details Outputs: none * @param record: DG_RESERVOIR_VOLUME_DATA_T pointer * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isDGReservoirVolRecordValid( DG_RESERVOIR_VOLUME_DATA_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof(DG_RESERVOIR_VOLUME_DATA_T) - sizeof(U16) ); U16 recordCRC = record->crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default record->maxResidualFluid = RECORD_DEFAULT_CONST; record->normalFillVolume = RECORD_DEFAULT_CONST; record->rsrvrUnfilledWeight = RECORD_DEFAULT_CONST; record->rsrvrVolume = RECORD_DEFAULT_CONST; record->calibrationTime = RECORD_DEFAULT_TIME; record->crc = crc16 ( (U08*)record, sizeof(DG_RESERVOIR_VOLUME_DATA_T) - sizeof(U16) ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } /*********************************************************************//** * @brief * The isDGGenericVolRecordValid function checks whether the calibration * record of generic volume is valid or not. * @details Inputs: none * @details Outputs: none * @param record: DG_GENERIC_VOLUME_DATA_T pointer * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isDGGenericVolRecordValid( DG_GENERIC_VOLUME_DATA_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof(DG_GENERIC_VOLUME_DATA_T) - sizeof(U16) ); U16 recordCRC = record->crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default record->volume = RECORD_DEFAULT_CONST; record->calibrationTime = RECORD_DEFAULT_TIME; // Set the to FALSE since the record is not valid status = FALSE; } return status; } /*********************************************************************//** * @brief * The isDGAcidConcentrateRecordValid function checks whether the calibration * record of acid concentrate is valid or not. * @details Inputs: none * @details Outputs: none * @param record: DG_ACID_CONCENTRATE_T pointer * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isDGAcidConcentrateRecordValid( DG_ACID_CONCENTRATE_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof(DG_ACID_CONCENTRATE_T) - sizeof(U16) ); U16 recordCRC = record->crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default record->startVolume = RECORD_DEFAULT_CONST; record->reserverdSpace = RECORD_DEFAULT_CONST; record->acidConcMixRatio = ACID_CONC_DEFAULT_MIXING_RATIO; record->calibrationTime = RECORD_DEFAULT_TIME; record->crc = crc16 ( (U08*)record, sizeof(DG_ACID_CONCENTRATE_T) - sizeof(U16) ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } /*********************************************************************//** * @brief * The isDGBicarbConcentrateRecordValid function checks whether the * calibration record of bicarb concentrate is valid or not. * @details Inputs: none * @details Outputs: none * @param record: DG_BICARB_CONCENTRATE_T pointer * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isDGBicarbConcentrateRecordValid( DG_BICARB_CONCENTRATE_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof(DG_BICARB_CONCENTRATE_T) - sizeof(U16) ); U16 recordCRC = record->crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default record->startVolume = RECORD_DEFAULT_CONST; record->reservedSpace = RECORD_DEFAULT_CONST; record->bicarbConcMixRatio = BICARB_CONC_DEFAULT_MIXING_RATIO; record->calibrationTime = RECORD_DEFAULT_TIME; record->crc = crc16 ( (U08*)record, sizeof(DG_BICARB_CONCENTRATE_T) - sizeof(U16) ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } /*********************************************************************//** * @brief * The isDGFilterRecordValid function checks whether the calibration * record of filter is valid or not. * @details Inputs: none * @details Outputs: none * @param record: DG_FILTER_CAL_RECORD_T pointer * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isDGFilterRecordValid( DG_FILTER_CAL_RECORD_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof(DG_FILTER_CAL_RECORD_T) - sizeof(U16) ); U16 recordCRC = record->crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default record->reservedSpace1 = RECORD_DEFAULT_CONST; record->reservedSpace2 = RECORD_DEFAULT_CONST; record->reservedSpace3 = RECORD_DEFAULT_CONST; record->calibrationTime = RECORD_DEFAULT_TIME; record->crc = crc16 ( (U08*)record, sizeof(DG_FILTER_CAL_RECORD_T) - sizeof(U16) ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } /*********************************************************************//** * @brief * The isDGFanRecordValid function checks whether the calibration record * of fan is valid or not. * @details Inputs: none * @details Outputs: none * @param record: DG_FAN_CAL_RECORD_T pointer * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isDGFanRecordValid( DG_FAN_CAL_RECORD_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof(DG_FAN_CAL_RECORD_T) - sizeof(U16) ); U16 recordCRC = record->crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default record->reservedSpace1 = RECORD_DEFAULT_CONST; record->reservedSpace2 = RECORD_DEFAULT_CONST; record->calibrationTime = RECORD_DEFAULT_TIME; record->crc = crc16 ( (U08*)record, sizeof(DG_FAN_CAL_RECORD_T) - sizeof(U16) ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } /*********************************************************************//** * @brief * The isDGAccelerometerSensorRecordValid function checks whether the * calibration record of accelerometer sensor is valid or not. * @details Inputs: none * @details Outputs: none * @param record: DG_ACCELEROMETER_SENSOR_CAL_RECORD_T pointer * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isDGAccelerometerSensorRecordValid( DG_ACCEL_SENSOR_CAL_RECORD_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof(DG_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(DG_ACCEL_SENSOR_CAL_RECORD_T) - sizeof(U16) ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } /*********************************************************************//** * @brief * The isDGHeatingCalRecordValid function checks whether the calibration record * of heating parameters is valid or not. * @details Inputs: none * @details Outputs: none * @param record: DG_HEATING_CAL_RECORD_T pointer * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isDGHeatingCalRecordValid( DG_HEATING_CAL_RECORD_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof(DG_HEATING_CAL_RECORD_T) - sizeof(U16) ); U16 recordCRC = record->crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default record->reservoirTempTauCPerMin = RESERVOIR_TEMPERATURE_TAU_C_PER_MIN; record->ultrafilterTempTauCPerMin = ULTRAFILTER_TEMPERATURE_TAU_C_PER_MIN; record->ultrafilterVolmL = ULTRAFILTER_VOLUME_ML; record->calibrationTime = RECORD_DEFAULT_TIME; record->crc = crc16 ( (U08*)record, sizeof(DG_HEATING_CAL_RECORD_T) - sizeof(U16) ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } #endif #ifdef _HD_ /*********************************************************************//** * @brief * The isHDSystemRecordValid function checks the validity of the HD system * record. * @details Inputs: hdSystemGroup.hdsystemRecord * @details Outputs: none * @return TRUE if the HD system record is valid otherwise FALSE *************************************************************************/ static BOOL isHDSystemRecordValid( void ) { BOOL status = TRUE; U16 calcCRC = crc16( (U08*)&hdSystemGroup.hdsystemRecord, sizeof( HD_SYSTEM_RECORD_T ) - sizeof( U16 ) ); U16 recordCRC = hdSystemGroup.hdsystemRecord.crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default hdSystemGroup.hdsystemRecord.mfgDate = 0; hdSystemGroup.hdsystemRecord.mfgLocation = 0; memset( hdSystemGroup.hdsystemRecord.topLevelPN, RECORD_DEFAULT_CHARACTER, sizeof( hdSystemGroup.hdsystemRecord.topLevelPN ) ); memset( hdSystemGroup.hdsystemRecord.topLevelPN, RECORD_DEFAULT_CHARACTER, sizeof( hdSystemGroup.hdsystemRecord.topLevelPN ) ); // Recalculate the CRC with the default values hdSystemGroup.hdsystemRecord.crc = crc16 ( (U08*)&hdSystemGroup.hdsystemRecord, sizeof( HD_SYSTEM_RECORD_T ) - sizeof( U16 ) ); activateAlarmNoData( ALARM_ID_HD_INVALID_SYSTEM_RECORD_CRC ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } /*********************************************************************//** * @brief * The isHDServiceRecordValid function checks the validity of the HD service * record. * @details Inputs: hdServiceGroup.hdServiceRecord * @details Outputs: none * @return TRUE if the HD service record is valid otherwise FALSE *************************************************************************/ static BOOL isHDServiceRecordValid( void ) { BOOL status = TRUE; U16 calcCRC = crc16( (U08*)&hdServiceGroup.hdServiceRecord, sizeof( HD_SERVICE_RECORD_T ) - sizeof( U16 ) ); U16 recordCRC = hdServiceGroup.hdServiceRecord.crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default hdServiceGroup.hdServiceRecord.lastServiceEpochDate = 0; hdServiceGroup.hdServiceRecord.serviceIntervalSeconds = RECORD_DEFAULT_SERVICE_INTERVAL_S; // Recalculate the CRC with the default values hdServiceGroup.hdServiceRecord.crc = crc16 ( (U08*)&hdServiceGroup.hdServiceRecord, sizeof( HD_SERVICE_RECORD_T ) - sizeof( U16 ) ); // Service record failure is also considered as RTC RAM failure activateAlarmNoData( ALARM_ID_HD_INVALID_SERVICE_RECORD_CRC ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } /*********************************************************************//** * @brief * The isHDCalibrationRecordValid function checks whether HD calibration * record is valid or not. * @details Inputs: none * @details Outputs: none * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isHDCalibrationRecordValid( void ) { U32 i; POLYNOMIAL_CAL_PAYLOAD_T* record; BOOL isHardwareRecordValid = TRUE; BOOL isCalRecordValid = TRUE; HD_PUMPS_CAL_RECORD_T* pump = &hdCalibrationRecord.hdCalibrationGroups.pumpsCalRecord; for ( i = 0; i < NUM_OF_CAL_DATA_HD_PUMPS; i++ ) { isHardwareRecordValid = isHDPumpRecordValid( &pump->hdPumps[ i ] ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; } HD_VALVES_CAL_RECORD_T* valve = &hdCalibrationRecord.hdCalibrationGroups.valvesCalRecord; for ( i = 0; i < NUM_OF_CAL_DATA_HD_VALVES; i++ ) { isHardwareRecordValid = isHDValveRecordValid( &valve->hdvalves[ i ] ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; } HD_OCCLUSION_SENSORS_CAL_RECORD_T* occlusion = &hdCalibrationRecord.hdCalibrationGroups.occlusionSensorsCalRecord; for ( i = 0; i < NUM_OF_CAL_DATA_OCCLUSION_SENSORS; i++ ) { // Get calibration payload and assign it to a pointer record = (POLYNOMIAL_CAL_PAYLOAD_T*)&occlusion->hdOcclusionSensors[ 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; } HD_FLOW_SENSORS_CAL_RECORD_T* flow = &hdCalibrationRecord.hdCalibrationGroups.flowSensorsCalRecord; for ( i = 0; i < NUM_OF_CAL_DATA_HD_FLOW_SENSORS; i++ ) { record = (POLYNOMIAL_CAL_PAYLOAD_T*)&flow->hdFlowSensors[ i ]; isHardwareRecordValid = isPolynomialRecordValid( record ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; } HD_PRESSURE_SENSORS_CAL_RECORD_T* pressure = &hdCalibrationRecord.hdCalibrationGroups.pressureSensorsCalRecord; for ( i = 0; i < NUM_OF_CAL_DATA_HD_PRESSURE_SESNSORS; i++ ) { record = (POLYNOMIAL_CAL_PAYLOAD_T*)&pressure->hdPressureSensors[ i ]; isHardwareRecordValid = isPolynomialRecordValid( record ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; } HD_TEMP_SENSORS_CAL_RECORD_T* temperature = &hdCalibrationRecord.hdCalibrationGroups.tempSensorsCalRecord; for ( i = 0; i < NUM_OF_CAL_DATA_HD_TEMP_SENSORS; i++ ) { record = (POLYNOMIAL_CAL_PAYLOAD_T*)&temperature->hdTemperatureSensors[ i ]; isHardwareRecordValid = isPolynomialRecordValid( record ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; } HD_HEPARIN_FORCE_SENSOR_CAL_RECORD_T* heparinForce = &hdCalibrationRecord.hdCalibrationGroups.heparinForceSensorCalRecord; record = (POLYNOMIAL_CAL_PAYLOAD_T*)&heparinForce->hdHeparinForceSensor; isHardwareRecordValid = isPolynomialRecordValid( record ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; HD_ACCELEROMETER_SENSOR_CAL_RECORD_T* accelerometer = &hdCalibrationRecord.hdCalibrationGroups.accelerometerSensorCalRecord; isHardwareRecordValid = isHDAccelerometerSensorValid( accelerometer ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; HD_BLOOD_LEAK_SENSOR_CAL_RECORD_T* bloodLeak = &hdCalibrationRecord.hdCalibrationGroups.bloodLeakSensorCalRecord; isHardwareRecordValid = isHDBloodLeakSesnorValid( bloodLeak ); isCalRecordValid = isCalRecordValid == FALSE ? FALSE : isHardwareRecordValid; return isCalRecordValid; } /*********************************************************************//** * @brief * The isHDPumpRecordValid function checks whether the calibration record * of HD pump(s) is valid or not. * @details Inputs: none * @details Outputs: none * @param record: HD_PUMP_CAL_PAYLOAD_T pointer * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isHDPumpRecordValid( HD_PUMP_CAL_PAYLOAD_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof(HD_PUMP_CAL_PAYLOAD_T) - sizeof(U16) ); U16 recordCRC = record->crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default record->maxCurrentLimit = RECORD_DEFAULT_OFFSET; record->minCurrentLimit = RECORD_DEFAULT_OFFSET; record->pwm2Speed = RECORD_DEFAULT_RATIO; record->speed2Flow = RECORD_DEFAULT_RATIO; record->calibrationTime = RECORD_DEFAULT_TIME; record->crc = crc16 ( (U08*)record, sizeof(HD_PUMP_CAL_PAYLOAD_T) - sizeof(U16) ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } /*********************************************************************//** * @brief * The isHDValveRecordValid function checks whether the calibration record * of HD valve is valid or not. * @details Inputs: none * @details Outputs: none * @param record: HD_VALVE_CAL_PAYLOAD_T pointer * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isHDValveRecordValid( HD_VALVE_CAL_PAYLOAD_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof(HD_VALVE_CAL_PAYLOAD_T) - sizeof(U16) ); U16 recordCRC = record->crc; if ( calcCRC != recordCRC ) { // CRC did not pass so set all values to default record->counts2Position = RECORD_DEFAULT_OFFSET; record->calibrationTime = RECORD_DEFAULT_TIME; record->crc = crc16 ( (U08*)record, sizeof(HD_VALVE_CAL_PAYLOAD_T) - sizeof(U16) ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } /*********************************************************************//** * @brief * The isHDAccelerometerSensorValid function checks whether the * calibration record of accelerometer sensor is valid or not. * @details Inputs: none * @details Outputs: none * @param record: HD_ACCELEROMETER_SENSOR_CAL_RECORD_T pointer * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isHDAccelerometerSensorValid( HD_ACCELEROMETER_SENSOR_CAL_RECORD_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof(HD_ACCELEROMETER_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(HD_ACCELEROMETER_SENSOR_CAL_RECORD_T) - sizeof(U16) ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } /*********************************************************************//** * @brief * The isHDBloodLeakSesnorValid function checks whether the calibration record * of HD blood leak sensor is valid or not. * @details Inputs: none * @details Outputs: none * @param record: HD_BLOOD_LEAK_SENSOR_CAL_RECORD_T pointer * @return TRUE if the record is valid otherwise FALSE *************************************************************************/ static BOOL isHDBloodLeakSesnorValid( HD_BLOOD_LEAK_SENSOR_CAL_RECORD_T* record ) { BOOL status = TRUE; U16 calcCRC = crc16 ( (U08*)record, sizeof( HD_BLOOD_LEAK_SENSOR_CAL_RECORD_T ) - sizeof( U16 ) ); U16 recordCRC = record->crc; if ( calcCRC != recordCRC ) { record->setPoint = BLOOD_LEAK_DEFAULT_SET_POINT; record->calibrationTime = RECORD_DEFAULT_TIME; record->crc = crc16 ( (U08*)record, sizeof( HD_BLOOD_LEAK_SENSOR_CAL_RECORD_T ) - sizeof( U16 ) ); // Set the to FALSE since the record is not valid status = FALSE; } return status; } #endif /*********************************************************************//** * @brief * The enqueueRecordJob function enqueues a new record job. * @details Inputs: queueFrontIndex, queueCount, recordJobQueue * @details Outputs: queueFrontIndex, queueCount, recordJobQueue * @param ops: memory operation (i.e write, read) * @param location: memory location which are either EEPROM or RTC RAM * @param job: type of job (i.e write calibration data) * @return none *************************************************************************/ static void enqueueRecordJob( NVDATAMGMT_OPERATION_STATE_T ops, NVDATAMGMT_LOCATION_STATE_T location, RECORD_JOBS_STATE_T job ) { PROCESS_RECORD_JOB_T currentJob; currentJob.memoryLocation = location; currentJob.memoryOperation = ops; currentJob.recordJob = job; recordJobQueue[ recordQueueRearIndex ] = currentJob; recordQueueCount++; recordQueueRearIndex = INC_WRAP( recordQueueRearIndex, 0, QUEUE_MAX_SIZE - 1 ); } /*********************************************************************//** * @brief * The dequeueRecordJob increments the front index counter and if it is * equal to rear index, it sets it to -1, meaning that the queue is empty. * @details Inputs: recordQueueFrontIndex, recordQueueCount, recordCurrentJob, * recordJobQueue * @details Outputs: recordQueueFrontIndex, recordQueueCount, recordCurrentJob * @return none *************************************************************************/ static void dequeueRecordJob( void ) { U32 tempIndex; _disable_IRQ(); tempIndex = recordQueueFrontIndex; if ( !isRecordQueueEmpty() ) { recordQueueFrontIndex = INC_WRAP( recordQueueFrontIndex, 0, QUEUE_MAX_SIZE - 1 ); recordCurrentJob = recordJobQueue[ tempIndex ]; } if ( recordQueueCount > 0 ) { recordQueueCount--; } _enable_IRQ(); } /*********************************************************************//** * @brief * The isRecordQueueEmpty checks whether the queue is empty and if it is * empty, it will return a false. * @details Inputs: recordQueueCount * @details Outputs: none * @return TRUE if queue is not empty *************************************************************************/ static BOOL isRecordQueueEmpty( void ) { BOOL isEmpty = TRUE; if ( recordQueueCount > 0 ) { isEmpty = FALSE; } return isEmpty; } /*********************************************************************//** * @brief * The getAvailableRecordQueueCount returns the number of available record * queues. * @details Inputs: recordQueueCount * @details Outputs: none * @return available record queues *************************************************************************/ static U32 getAvailableRecordQueueCount( void ) { return QUEUE_MAX_SIZE - recordQueueCount; } /*********************************************************************//** * @brief * The enqueueSector0Records checks whether there are enough jobs available * to be able to enqueue to the records. * @details Inputs: none * @details Outputs: none * @return TRUE if the job were successfully enqueued otherwise, FLASE *************************************************************************/ static BOOL enqueueSector0Records( void ) { BOOL status = FALSE; if ( getAvailableRecordQueueCount() >= MIN_JOBS_NEEDED_FOR_SECTOR_0 ) { enqueueRecordJob( NVDATAMGMT_ERASE_SECTOR, NVDATAMGMT_EEPROM, NVDATAMGMT_CALIBRATION_RECORD ); enqueueRecordJob( NVDATAMGMT_WRITE, NVDATAMGMT_EEPROM, NVDATAMGMT_CALIBRATION_RECORD ); enqueueRecordJob( NVDATAMGMT_WRITE, NVDATAMGMT_EEPROM, NVDATAMGMT_SYSTEM_RECORD ); status = TRUE; } return status; } /**@}*/