/************************************************************************** * * Copyright (c) 2021-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 DialysateFlow.c * * @author (last) Bill Bracken * @date (last) 25-Aug-2022 * * @author (original) Hung Nguyen * @date (original) 20-Oct-2021 * ***************************************************************************/ #include #include "FlowSensors.h" #include "FPGA.h" #include "NVDataMgmt.h" #include "PersistentAlarm.h" #include "DialysateFlow.h" #include "SystemCommMessages.h" #include "TaskPriority.h" /** * @addtogroup DialysateFlow * @{ */ // ********** private definitions ********** #define DIALYSATE_FLOW_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Interval (ms/task time) at which the Dialysate flow data is published on the CAN bus. #define FLOW_SENSOR_ZERO_READING 0xFFFF ///< Flow sensor reading indicates zero flow (or flow lower than can be detected by sensor). #define FLOW_SAMPLES_TO_AVERAGE ( 1000 / TASK_PRIORITY_INTERVAL ) ///< Averaging flow data over 1000 ms intervals. #define FLOW_AVERAGE_MULTIPLIER ( 1.0F / (F32)FLOW_SAMPLES_TO_AVERAGE ) ///< Optimization - multiplying is faster than dividing. #define DIALYSATE_FLOW_ADC_TO_LPM_FACTOR 272.72F ///< Conversion factor from pulse period (2us units) to flow rate (L/min) for dialysate flow rate (divide this by pulse period). #define FLOW_OUT_OF_RANGE_PERSISTENT_INTERVAL ( 12 * MS_PER_SECOND ) ///< Flow out of range time out in counts. #define DATA_PUBLISH_COUNTER_START_COUNT 20 ///< Data publish counter start count. // ********** private data ********** static U32 dialysateFlowDataPublicationTimerCounter; ///< Used to schedule Dialysate flow data publication to CAN bus. static OVERRIDE_U32_T dialysateFlowDataPublishInterval = { DIALYSATE_FLOW_DATA_PUB_INTERVAL, DIALYSATE_FLOW_DATA_PUB_INTERVAL, 0, 0 }; ///< Interval (in ms) at which to publish Dialysate flow data to CAN bus. static OVERRIDE_F32_T measuredDialysateFlowRateLPM = { 0.0, 0.0, 0.0, 0 }; ///< Measured Dialysate flow rate (in L/min). static S32 measuredFlowReadingsSum; ///< Raw flow reading sums for averaging. static U32 flowFilterCounter; ///< Flow filtering counter. static DG_FLOW_SENSORS_CAL_RECORD_T flowSensorsCalRecord; ///< Flow sensors calibration record. // ********** private function prototypes ********** static void publishDialysateFlowData( void ); /*********************************************************************//** * @brief * The initDialysateFlowMeter function initializes the Dialysate flow module. * @details Inputs: none * @details Outputs: measuredFlowReadingsSum, flowFilterCounter, * dialysateFlowDataPublicationTimerCounter * @return none *************************************************************************/ void initDialysateFlowMeter( void ) { // Initialize the persistent alarm for flow out of upper and lower range initPersistentAlarm( ALARM_ID_DIALYSATE_FLOW_RATE_OUT_OF_RANGE, FLOW_OUT_OF_RANGE_PERSISTENT_INTERVAL, FLOW_OUT_OF_RANGE_PERSISTENT_INTERVAL ); // Initialize the variables measuredFlowReadingsSum = 0; flowFilterCounter = 0; dialysateFlowDataPublicationTimerCounter = DATA_PUBLISH_COUNTER_START_COUNT; } /*********************************************************************//** * @brief * The execDialysateFlowMeterSelfTest function executes the dialysate flow * sensor's self-test. * @details Inputs: none * @details Outputs: none * @return PressuresSelfTestResult (SELF_TEST_STATUS_T) *************************************************************************/ SELF_TEST_STATUS_T execDialysateFlowMeterSelfTest( void ) { SELF_TEST_STATUS_T result = SELF_TEST_STATUS_IN_PROGRESS; BOOL calStatus = getNVRecord2Driver( GET_CAL_FLOW_SENSORS, (U08*)&flowSensorsCalRecord, sizeof( DG_FLOW_SENSORS_CAL_RECORD_T ), NUM_OF_CAL_DATA_FLOW_SENSORS, ALARM_ID_DG_DIALYSATE_FLOW_SENSOR_INVALID_CAL_RECORD ); result = ( TRUE == calStatus ? SELF_TEST_STATUS_PASSED : SELF_TEST_STATUS_FAILED ); return result; } /*********************************************************************//** * @brief * The execDialysateFlowMeterMonitor function executes the Dialysate flow monitor. * The Dialysate flow sensor is read, filtered, converted to L/min and calibrated. * @details Inputs: measuredFlowReadingsSum, flowFilterCounter, * measuredDialysateFlowRateLPM * @details Outputs: measuredFlowReadingsSum, flowFilterCounter, * measuredDialysateFlowRateLPM * @return none *************************************************************************/ void execDialysateFlowMeterMonitor( void ) { U16 dialysateFlowReading = getFPGADialysateFlowRate(); dialysateFlowReading = ( 0 == dialysateFlowReading ? FLOW_SENSOR_ZERO_READING : dialysateFlowReading ); F32 currentFlow; BOOL isFlowOutOfUpperRange; // Update sum for flow average calculation measuredFlowReadingsSum += (S32)dialysateFlowReading; // Check if a new calibration is available if ( TRUE == isNewCalibrationRecordAvailable() ) { getNVRecord2Driver( GET_CAL_FLOW_SENSORS, (U08*)&flowSensorsCalRecord, sizeof( DG_FLOW_SENSORS_CAL_RECORD_T ), NUM_OF_CAL_DATA_FLOW_SENSORS, ALARM_ID_DG_RO_FLOW_SENSOR_INVALID_CAL_RECORD ); } // Read flow at the control set if ( ++flowFilterCounter >= FLOW_SAMPLES_TO_AVERAGE ) { F32 flow = DIALYSATE_FLOW_ADC_TO_LPM_FACTOR / ( (F32)measuredFlowReadingsSum * FLOW_AVERAGE_MULTIPLIER ); // Convert flow sensor period to L/min // Apply calibration to flow sensor reading measuredDialysateFlowRateLPM.data = pow( flow, 4 ) * flowSensorsCalRecord.flowSensors[ CAL_DATA_DIALYSATE_FLOW_SENSOR ].fourthOrderCoeff + pow( flow, 3 ) * flowSensorsCalRecord.flowSensors[ CAL_DATA_DIALYSATE_FLOW_SENSOR ].thirdOrderCoeff + pow( flow, 2 ) * flowSensorsCalRecord.flowSensors[ CAL_DATA_DIALYSATE_FLOW_SENSOR ].secondOrderCoeff + flow * flowSensorsCalRecord.flowSensors[ CAL_DATA_DIALYSATE_FLOW_SENSOR ].gain + flowSensorsCalRecord.flowSensors[ CAL_DATA_DIALYSATE_FLOW_SENSOR ].offset; // If the flow is less than a certain value, FPGA will return 0xFFFF meaning that the flow is 0. if ( FLOW_SENSOR_ZERO_READING == dialysateFlowReading ) { measuredDialysateFlowRateLPM.data = 0.0; } measuredFlowReadingsSum = 0; flowFilterCounter = 0; } currentFlow = getMeasuredDialysateFlowRate(); isFlowOutOfUpperRange = ( currentFlow > MAX_DIALYSATE_FLOWRATE_LPM ? TRUE : FALSE ); checkPersistentAlarm( ALARM_ID_DIALYSATE_FLOW_RATE_OUT_OF_RANGE, isFlowOutOfUpperRange, currentFlow, MAX_DIALYSATE_FLOWRATE_LPM ); // Publish dialysate flow meter data on the CAN bus according to the specified interval publishDialysateFlowData(); } /*********************************************************************//** * @brief * The publishDialysateFlowData function publishes Dialysate flow data at the set interval. * @details Inputs: dialysateFlowDataPublicationTimerCounter * @details Outputs: dialysateFlowDataPublicationTimerCounter * @return none *************************************************************************/ static void publishDialysateFlowData( void ) { // publish dialysate flow meter data on interval if ( ++dialysateFlowDataPublicationTimerCounter >= getU32OverrideValue( &dialysateFlowDataPublishInterval ) ) { DIALYSATE_FLOW_METER_DATA_T dialysateFlowData; dialysateFlowData.measuredDialysateFlowRate = getMeasuredFlowRateLPM( DIALYSATE_FLOW_SENSOR ); broadcastData( MSG_ID_DG_DIALYSATE_FLOW_METER_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&dialysateFlowData, sizeof( DIALYSATE_FLOW_METER_DATA_T ) ); dialysateFlowDataPublicationTimerCounter = 0; } } /*********************************************************************//** * @brief * The getMeasuredDialysateFlowRate function gets the measured Dialysate flow rate. * @details Inputs: measuredDialysateFlowRateLPM * @details Outputs: measuredDialysateFlowRateLPM * @return the current Dialysate flow rate (in L/min). *************************************************************************/ F32 getMeasuredDialysateFlowRate( void ) { return getF32OverrideValue( &measuredDialysateFlowRateLPM ); } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetDialysateFlowDataPublishIntervalOverride function overrides the * Dialysate flow data publish interval. * @details Inputs: dialysateFlowDataPublishInterval * @details Outputs: dialysateFlowDataPublishInterval * @param: value override Dialysate flow data publish interval with (in ms) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetDialysateFlowDataPublishIntervalOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 intvl = value / TASK_PRIORITY_INTERVAL; dialysateFlowDataPublishInterval.ovData = intvl; dialysateFlowDataPublishInterval.override = OVERRIDE_KEY; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testResetDialysateFlowDataPublishIntervalOverride function resets the * override of the dialysate flow data publish interval. * @details Inputs: dialysateFlowDataPublishInterval * @details Outputs: dialysateFlowDataPublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetDialysateFlowDataPublishIntervalOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { dialysateFlowDataPublishInterval.override = OVERRIDE_RESET; dialysateFlowDataPublishInterval.ovData = dialysateFlowDataPublishInterval.ovInitData; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testSetMeasuredDialysateFlowRateOverride function overrides the measured * Dialysate flow rate. * @details Inputs: measuredDialysateFlowRateLPM * @details Outputs: measuredDialysateFlowRateLPM * @param: value override measured Dialysate flow rate * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetMeasuredDialysateFlowRateOverride( F32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { measuredDialysateFlowRateLPM.ovInitData = measuredDialysateFlowRateLPM.data; measuredDialysateFlowRateLPM.ovData = value; measuredDialysateFlowRateLPM.override = OVERRIDE_KEY; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testResetMeasuredDialysateFlowRateOverride function resets the override * of the measured dialysate flow rate. * @details Inputs: measuredDialysateFlowRateLPM * @details Outputs: measuredDialysateFlowRateLPM * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testResetMeasuredDialysateFlowRateOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { measuredDialysateFlowRateLPM.data = measuredDialysateFlowRateLPM.ovInitData; measuredDialysateFlowRateLPM.override = OVERRIDE_RESET; measuredDialysateFlowRateLPM.ovInitData = 0.0; measuredDialysateFlowRateLPM.ovData = 0.0; result = TRUE; } return result; } /**@}*/