Index: firmware/App/Controllers/DialInFlow.c =================================================================== diff -u -rd3596da054513ebf85d4114221eddbe875d1f1a1 -r8e7158d8231435496fcf1d5649e51babf859ccc7 --- firmware/App/Controllers/DialInFlow.c (.../DialInFlow.c) (revision d3596da054513ebf85d4114221eddbe875d1f1a1) +++ firmware/App/Controllers/DialInFlow.c (.../DialInFlow.c) (revision 8e7158d8231435496fcf1d5649e51babf859ccc7) @@ -1,36 +1,38 @@ -/************************************************************************** - * - * Copyright (c) 2019-2020 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 DialInFlow.c - * - * @date 16-Dec-2019 - * @author S. Nash - * - * @brief Monitor/Controller for dialysate inlet pump and flow sensor. - * - **************************************************************************/ +/************************************************************************** +* +* Copyright (c) 2019-2021 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 DialInFlow.c +* +* @author (last) Sean Nash +* @date (last) 01-Dec-2020 +* +* @author (original) Sean +* @date (original) 16-Dec-2019 +* +***************************************************************************/ -#ifndef _VECTORCAST_ - #include -#endif +#include #include "etpwm.h" #include "gio.h" #include "mibspi.h" +#include "DialInFlow.h" #include "FPGA.h" #include "InternalADC.h" +#include "NVDataMgmt.h" #include "OperationModes.h" +#include "PersistentAlarm.h" #include "PIControllers.h" +#include "SafetyShutdown.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "TaskPriority.h" #include "Timers.h" -#include "DialInFlow.h" /** * @addtogroup DialysateInletFlow @@ -39,53 +41,66 @@ // ********** private definitions ********** -#define DIAL_IN_FLOW_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< interval (ms/task time) at which the dialIn flow data is published on the CAN bus +/// interval (ms/task time) at which the dialIn flow data is published on the CAN bus. +#define DIAL_IN_FLOW_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) #define MAX_DIAL_IN_PUMP_PWM_STEP_UP_CHANGE 0.0133 ///< max duty cycle change when ramping up ~ 200 mL/min/s. #define MAX_DIAL_IN_PUMP_PWM_STEP_DN_CHANGE 0.02 ///< max duty cycle change when ramping down ~ 300 mL/min/s. -#define MAX_DIAL_IN_PUMP_PWM_DUTY_CYCLE 0.88 ///< controller will error if PWM duty cycle > 90%, so set max to 88% -#define MIN_DIAL_IN_PUMP_PWM_DUTY_CYCLE 0.12 ///< controller will error if PWM duty cycle < 10%, so set min to 12% +#define MAX_DIAL_IN_PUMP_PWM_DUTY_CYCLE 0.88 ///< controller will error if PWM duty cycle > 90%, so set max to 88%. +#define MIN_DIAL_IN_PUMP_PWM_DUTY_CYCLE 0.12 ///< controller will error if PWM duty cycle < 10%, so set min to 12%. #define DIP_CONTROL_INTERVAL ( 10000 / TASK_GENERAL_INTERVAL ) ///< interval (ms/task time) at which the dialIn pump is controlled -#define DIP_P_COEFFICIENT 0.00005 ///< P term for dialIn pump control -#define DIP_I_COEFFICIENT 0.00015 ///< I term for dialIn pump control +#define DIP_P_COEFFICIENT 0.00035 ///< P term for dialIn pump control. +#define DIP_I_COEFFICIENT 0.00035 ///< I term for dialIn pump control. #define DIP_HOME_RATE 100 ///< target pump speed (in estimate mL/min) for homing. #define DIP_HOME_TIMEOUT_MS 10000 ///< maximum time allowed for homing to complete (in ms). -#define DIP_SPEED_CALC_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< interval (ms/task time) at which the dialysate inlet pump speed is calculated. +/// interval (ms/task time) at which the blood pump speed is calculated (every 40 ms). +#define DIP_SPEED_CALC_INTERVAL ( 40 / TASK_PRIORITY_INTERVAL ) +/// number of hall sensor counts kept in buffer to hold last 1 second of count data. +#define DIP_SPEED_CALC_BUFFER_LEN ( 1000 / DIP_SPEED_CALC_INTERVAL / TASK_PRIORITY_INTERVAL ) #define DIP_HALL_EDGE_COUNTS_PER_REV 48 ///< number of hall sensor edge counts per motor revolution. -#define DIP_MAX_FLOW_VS_SPEED_DIFF_ML_MIN 50.0 ///< maximum difference between measured flow and flow implied by measured motor speed. +#define DIP_MAX_FLOW_VS_SPEED_DIFF_RPM 200.0 ///< maximum difference between measured motor speed and speed implied by measured flow. #define DIP_MAX_MOTOR_SPEED_WHILE_OFF_RPM 100.0 ///< maximum motor speed (RPM) while motor is commanded off. #define DIP_MAX_ROTOR_VS_MOTOR_DIFF_RPM 5.0 ///< maximum difference in speed between motor and rotor (in rotor RPM). #define DIP_MAX_MOTOR_SPEED_ERROR_RPM 300.0 ///< maximum difference in speed between measured and commanded RPM. -#define DIP_FLOW_VS_SPEED_PERSIST ((5 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL) ///< persist time (task intervals) for flow vs. motor speed error condition. -#define DIP_OFF_ERROR_PERSIST ((5 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL) ///< persist time (task intervals) for motor off error condition. -#define DIP_MOTOR_SPEED_ERROR_PERSIST ((5 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL) ///< persist time (task intervals) motor speed error condition. -#define DIP_ROTOR_SPEED_ERROR_PERSIST ((12 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL) ///< persist time (task intervals) rotor speed error condition. +#define DIP_FLOW_VS_SPEED_PERSIST ((5 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL) ///< persist time (task intervals) for flow vs. motor speed error condition. +#define DIP_OFF_ERROR_PERSIST ((5 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL) ///< persist time (task intervals) for motor off error condition. +#define DIP_MOTOR_SPEED_ERROR_PERSIST ((5 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL) ///< persist time (task intervals) motor speed error condition. +#define DIP_ROTOR_SPEED_ERROR_PERSIST ((12 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL) ///< persist time (task intervals) rotor speed error condition. +#define DIP_DIRECTION_ERROR_PERSIST (250 / TASK_PRIORITY_INTERVAL) ///< persist time (task intervals) pump direction error condition. -#define DIP_MAX_CURR_WHEN_STOPPED_MA 150.0 ///< motor controller current should not exceed this when pump should be stopped -#define DIP_MIN_CURR_WHEN_RUNNING_MA 150.0 ///< motor controller current should always exceed this when pump should be running -#define DIP_MAX_CURR_WHEN_RUNNING_MA 1000.0 ///< motor controller current should not exceed this when pump should be running -#define DIP_MAX_CURR_ERROR_DURATION_MS 2000 ///< motor controller current errors persisting beyond this duration will trigger an alarm +#define DIP_MAX_CURR_WHEN_STOPPED_MA 150.0 ///< motor controller current should not exceed this when pump should be stopped. +#define DIP_MIN_CURR_WHEN_RUNNING_MA 150.0 ///< motor controller current should always exceed this when pump should be running. +#define DIP_MAX_CURR_WHEN_RUNNING_MA 2000.0 ///< motor controller current should not exceed this when pump should be running. +#define DIP_MAX_CURR_ERROR_DURATION_MS 2000 ///< motor controller current errors persisting beyond this duration will trigger an alarm. -#define DIP_SPEED_ADC_TO_RPM_FACTOR 1.280938 ///< conversion factor from ADC counts to RPM for dialIn pump motor -#define DIP_CURRENT_ADC_TO_MA_FACTOR 3.002 ///< conversion factor from ADC counts to mA for dialIn pump motor +#define DIP_SPEED_ADC_TO_RPM_FACTOR 1.280938 ///< conversion factor from ADC counts to RPM for dialIn pump motor. +#define DIP_CURRENT_ADC_TO_MA_FACTOR 3.002 ///< conversion factor from ADC counts to mA for dialIn pump motor. -#define DIP_REV_PER_LITER 150.24 ///< rotor revolutions per liter -#define DIP_ML_PER_MIN_TO_PUMP_RPM_FACTOR ( DIP_REV_PER_LITER / ML_PER_LITER ) ///< -#define DIP_GEAR_RATIO 32.0 ///< dialIn pump motor to dialIn pump gear ratio -#define DIP_MOTOR_RPM_TO_PWM_DC_FACTOR 0.00035 ///< ~28 BP motor RPM = 1% PWM duty cycle -#define DIP_PWM_ZERO_OFFSET 0.1 ///< 10% PWM duty cycle = zero speed -#define DIP_PWM_FROM_ML_PER_MIN(rate) ( (rate) * DIP_ML_PER_MIN_TO_PUMP_RPM_FACTOR * DIP_GEAR_RATIO * DIP_MOTOR_RPM_TO_PWM_DC_FACTOR + DIP_PWM_ZERO_OFFSET ) ///< +#define DIP_REV_PER_LITER 150.0 ///< rotor revolutions per liter. +/// Macro converts flow rate to motor RPM. +#define DIP_ML_PER_MIN_TO_PUMP_RPM_FACTOR ( DIP_REV_PER_LITER / ML_PER_LITER ) +#define DIP_GEAR_RATIO 32.0 ///< dialIn pump motor to dialIn pump gear ratio. +#define DIP_MOTOR_RPM_TO_PWM_DC_FACTOR 0.00028 ///< ~28 BP motor RPM = 1% PWM duty cycle. +#define DIP_PWM_ZERO_OFFSET 0.1 ///< 10% PWM duty cycle = zero speed. +/// Macro converts flow rate to estimate PWM needed to achieve it. +#define DIP_PWM_FROM_ML_PER_MIN(rate) ( (rate) * DIP_ML_PER_MIN_TO_PUMP_RPM_FACTOR * DIP_GEAR_RATIO * DIP_MOTOR_RPM_TO_PWM_DC_FACTOR + DIP_PWM_ZERO_OFFSET ) -#define DIAL_IN_PUMP_ADC_FULL_SCALE_V 3.0 ///< BP analog signals are 0-3V (while int. ADC ref V may be different) +#define DIAL_IN_PUMP_ADC_FULL_SCALE_V 3.0 ///< BP analog signals are 0-3V (while int. ADC ref V may be different). #define DIAL_IN_PUMP_ADC_ZERO 1998 ///< Mid-point (zero) for ADC readings. -#define SIGN_FROM_12_BIT_VALUE(v) ( (S16)(v) - (S16)DIAL_IN_PUMP_ADC_ZERO ) ///< Macro converts a 12-bit ADC reading to a signed 16-bit value. - -#define SIZE_OF_ROLLING_AVG ( ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) * 10 ) ///< measured dialIn flow is filtered w/ moving average - +///< Macro converts a 12-bit ADC reading to a signed 16-bit value. +#define SIGN_FROM_12_BIT_VALUE(v) ( (S16)(v) - (S16)DIAL_IN_PUMP_ADC_ZERO ) + +/// measured dialIn flow is filtered w/ moving average. +#define SIZE_OF_ROLLING_AVG ( ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) * 10 ) + +/// dialysate flow sensor signal strength low alarm persistence. +#define FLOW_SIG_STRGTH_ALARM_PERSIST ( 5 * MS_PER_SECOND ) +#define MIN_FLOW_SIG_STRENGTH 0.9 ///< Minimum flow sensor signal strength (90%). + /// Enumeration of dialysate inlet pump states. typedef enum DialInPump_States { @@ -96,13 +111,13 @@ NUM_OF_DIAL_IN_PUMP_STATES ///< Number of dialysate inlet pump states. } DIAL_IN_PUMP_STATE_T; -/// Enumeration of dialysate inlet self test states. +/// Enumeration of dialysate inlet self-test states. typedef enum DialInFlow_Self_Test_States { - DIAL_IN_FLOW_SELF_TEST_STATE_START = 0, ///< Start state for the dialysate inlet pump self test. - DIAL_IN_FLOW_TEST_STATE_IN_PROGRESS, ///< Test in progress state for the dialysate inlet pump self test. - DIAL_IN_FLOW_TEST_STATE_COMPLETE, ///< Test completed state for the dialysate inlet pump self test. - NUM_OF_DIAL_IN_FLOW_SELF_TEST_STATES ///< Number of dialysate inlet pump self test states. + DIAL_IN_FLOW_SELF_TEST_STATE_START = 0, ///< Start state for the dialysate inlet pump self-test. + DIAL_IN_FLOW_TEST_STATE_IN_PROGRESS, ///< Test in progress state for the dialysate inlet pump self-test. + DIAL_IN_FLOW_TEST_STATE_COMPLETE, ///< Test completed state for the dialysate inlet pump self-test. + NUM_OF_DIAL_IN_FLOW_SELF_TEST_STATES ///< Number of dialysate inlet pump self-test states. } DIAL_IN_FLOW_SELF_TEST_STATE_T; // pin assignments for pump stop and direction outputs @@ -126,33 +141,38 @@ static BOOL isDialInPumpOn = FALSE; ///< dialIn pump is currently running static F32 dialInPumpPWMDutyCyclePct = 0.0; ///< initial dialIn pump PWM duty cycle static F32 dialInPumpPWMDutyCyclePctSet = 0.0; ///< currently set dialIn pump PWM duty cycle -static MOTOR_DIR_T dialInPumpDirection = MOTOR_DIR_FORWARD; ///< requested dialIn flow direction -static MOTOR_DIR_T dialInPumpDirectionSet = MOTOR_DIR_FORWARD; ///< currently set dialIn flow direction +static MOTOR_DIR_T dialInPumpDirection = MOTOR_DIR_FORWARD; ///< requested dialysate flow direction +static MOTOR_DIR_T dialInPumpDirectionSet = MOTOR_DIR_FORWARD; ///< currently set dialysate flow direction static PUMP_CONTROL_MODE_T dialInPumpControlMode = PUMP_CONTROL_MODE_CLOSED_LOOP; ///< requested dialIn pump control mode. static PUMP_CONTROL_MODE_T dialInPumpControlModeSet = PUMP_CONTROL_MODE_CLOSED_LOOP;///< currently set dialIn pump control mode. +static F32 dialInFlowCalGain = 1.0; ///< dialysate flow calibration gain. +static F32 dialInFlowCalOffset = 0.0; ///< dialysate flow calibration offset. -static OVERRIDE_U32_T dialInFlowDataPublishInterval = { DIAL_IN_FLOW_DATA_PUB_INTERVAL, DIAL_IN_FLOW_DATA_PUB_INTERVAL, DIAL_IN_FLOW_DATA_PUB_INTERVAL, 0 }; ///< interval (in ms) at which to publish dialIn flow data to CAN bus -static OVERRIDE_S32_T targetDialInFlowRate = { 0, 0, 0, 0 }; ///< requested dialIn flow rate +/// interval (in ms) at which to publish dialIn flow data to CAN bus +static OVERRIDE_U32_T dialInFlowDataPublishInterval = { DIAL_IN_FLOW_DATA_PUB_INTERVAL, DIAL_IN_FLOW_DATA_PUB_INTERVAL, DIAL_IN_FLOW_DATA_PUB_INTERVAL, 0 }; +static S32 targetDialInFlowRate = 0; ///< requested dialIn flow rate static OVERRIDE_F32_T measuredDialInFlowRate = { 0.0, 0.0, 0.0, 0 }; ///< measured dialysate inlet flow rate static OVERRIDE_F32_T dialInPumpRotorSpeedRPM = { 0.0, 0.0, 0.0, 0 }; ///< measured dialysate inlet pump rotor speed static OVERRIDE_F32_T dialInPumpSpeedRPM = { 0.0, 0.0, 0.0, 0 }; ///< measured dialysate inlet pump motor speed static OVERRIDE_F32_T adcDialInPumpMCSpeedRPM = { 0.0, 0.0, 0.0, 0 }; ///< measured dialysate inlet pump motor controller speed static OVERRIDE_F32_T adcDialInPumpMCCurrentmA = { 0.0, 0.0, 0.0, 0 }; ///< measured dialysate inlet pump motor controller current +static OVERRIDE_F32_T dialInFlowSignalStrength = { 0.0, 0.0, 0.0, 0 }; ///< measured dialysate flow signal strength (%) static U32 dipControlTimerCounter = 0; ///< determines when to perform control on dialIn flow static U32 dipRotorRevStartTime = 0; ///< dialysate inlet pump rotor rotation start time (in ms) static BOOL dipStopAtHomePosition = FALSE; ///< stop dialysate inlet pump at next home position static U32 dipHomeStartTime = 0; ///< when did dialysate inlet pump home command begin? (in ms) -static U16 dipLastMotorHallSensorCount = 0; ///< last hall sensor count for the dialysate inlet pump motor -static MOTOR_DIR_T dipMotorDirectionFromHallSensors = MOTOR_DIR_FORWARD; ///< pump direction according to hall sensor count +static U16 dipLastMotorHallSensorCounts[ DIP_SPEED_CALC_BUFFER_LEN ]; ///< last hall sensor count for the dialysate inlet pump motor +static U32 dipMotorSpeedCalcIdx = 0; ///< index into 1 second buffer of motor speed hall sensor counts static U32 dipMotorSpeedCalcTimerCtr = 0; ///< counter determines interval for calculating dialysate inlet pump motor speed from hall sensor count. static U32 errorDialInFlowVsMotorSpeedPersistTimerCtr = 0; ///< persistence timer counter for flow vs. motor speed error condition. static U32 errorDialInMotorOffPersistTimerCtr = 0; ///< persistence timer counter for motor off check error condition. static U32 errorDialInMotorSpeedPersistTimerCtr = 0; ///< persistence timer counter for motor speed error condition. static U32 errorDialInRotorSpeedPersistTimerCtr = 0; ///< persistence timer counter for rotor speed error condition. +static U32 errorDialInPumpDirectionPersistTimerCtr = 0; ///< persistence timer counter for pump direction error condition. static F32 flowReadings[ SIZE_OF_ROLLING_AVG ]; ///< holds flow samples for a rolling average static U32 flowReadingsIdx = 0; ///< index for next sample in rolling average array @@ -161,8 +181,8 @@ static U32 dipCurrErrorDurationCtr = 0; ///< used for tracking persistence of dip current errors -static DIAL_IN_FLOW_SELF_TEST_STATE_T dialInPumpSelfTestState = DIAL_IN_FLOW_SELF_TEST_STATE_START; ///< current dialIn pump self test state -static U32 dialInPumpSelfTestTimerCount = 0; ///< timer counter for dialIn pump self test +static DIAL_IN_FLOW_SELF_TEST_STATE_T dialInPumpSelfTestState = DIAL_IN_FLOW_SELF_TEST_STATE_START; ///< current dialIn pump self-test state +static U32 dialInPumpSelfTestTimerCount = 0; ///< timer counter for dialIn pump self-test // ********** private function prototypes ********** @@ -183,42 +203,53 @@ static void checkDialInPumpSpeeds( void ); static void checkDialInPumpFlowAgainstSpeed( void ); static void checkDialInPumpMCCurrent( void ); +static void checkDialInFlowSensorSignalStrength( void ); static DATA_GET_PROTOTYPE( U32, getPublishDialInFlowDataInterval ); /*********************************************************************//** * @brief * The initDialInFlow function initializes the DialInFlow module. - * @details - * Inputs : none - * Outputs : DialInFlow module initialized. + * @details Inputs: none + * @details Outputs: DialInFlow module initialized. * @return none *************************************************************************/ void initDialInFlow( void ) -{ - dipLastMotorHallSensorCount = getFPGADialInPumpHallSensorCount(); +{ + U32 i; stopDialInPump(); setDialInPumpDirection( MOTOR_DIR_FORWARD ); // zero rolling flow average buffer resetDialInFlowMovingAverage(); + // zero motor hall sensors counts buffer + dipMotorSpeedCalcIdx = 0; + for ( i = 0; i < DIP_SPEED_CALC_BUFFER_LEN; i++ ) + { + dipLastMotorHallSensorCounts[ i ] = 0; + } + // initialize dialysate inlet flow PI controller initializePIController( PI_CONTROLLER_ID_DIALYSATE_FLOW, MIN_DIAL_IN_PUMP_PWM_DUTY_CYCLE, DIP_P_COEFFICIENT, DIP_I_COEFFICIENT, MIN_DIAL_IN_PUMP_PWM_DUTY_CYCLE, MAX_DIAL_IN_PUMP_PWM_DUTY_CYCLE ); + + // initialize persistent alarm for flow sensor signal strength too low + initPersistentAlarm( PERSISTENT_ALARM_DIALYSATE_FLOW_SIGNAL_STRENGTH, + ALARM_ID_DIALYSATE_FLOW_SIGNAL_STRENGTH_TOO_LOW, + FALSE, FLOW_SIG_STRGTH_ALARM_PERSIST, FLOW_SIG_STRGTH_ALARM_PERSIST ); } /*********************************************************************//** * @brief * The setDialInPumpTargetFlowRate function sets a new target flow rate and * pump direction. - * @details - * Inputs : isDialInPumpOn, dialInPumpDirectionSet - * Outputs : targetDialInFlowRate, dialInPumpdirection, dialInPumpPWMDutyCyclePct - * @param flowRate : new target dialIn flow rate - * @param dir : new dialIn flow direction - * @param mode : new control mode + * @details Inputs: isDialInPumpOn, dialInPumpDirectionSet + * @details Outputs: targetDialInFlowRate, dialInPumpdirection, dialInPumpPWMDutyCyclePct + * @param flowRate new target dialIn flow rate + * @param dir new dialIn flow direction + * @param mode new control mode * @return TRUE if new flow rate & dir are set, FALSE if not *************************************************************************/ BOOL setDialInPumpTargetFlowRate( U32 flowRate, MOTOR_DIR_T dir, PUMP_CONTROL_MODE_T mode ) @@ -232,7 +263,7 @@ if ( flowRate <= MAX_DIAL_IN_FLOW_RATE ) { resetDialInFlowMovingAverage(); - targetDialInFlowRate.data = ( dir == MOTOR_DIR_FORWARD ? (S32)flowRate : (S32)flowRate * -1 ); + targetDialInFlowRate = ( dir == MOTOR_DIR_FORWARD ? (S32)flowRate : (S32)flowRate * -1 ); dialInPumpDirection = dir; dialInPumpControlMode = mode; // set PWM duty cycle target to an estimated initial target to ramp to based on target flow rate - then we'll control to flow when ramp completed @@ -270,7 +301,7 @@ } else // requested flow rate too high { - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_SOFTWARE_FAULT, SW_FAULT_ID_DIAL_IN_FLOW_SET_TOO_HIGH, flowRate ) + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_DIAL_IN_FLOW_SET_TOO_HIGH, flowRate ) } } @@ -280,14 +311,13 @@ /*********************************************************************//** * @brief * The signalDialInPumpHardStop function stops the dialIn pump immediately. - * @details - * Inputs : none - * Outputs : DialIn pump stopped, set point reset, state changed to off + * @details Inputs: none + * @details Outputs: DialIn pump stopped, set point reset, state changed to off * @return none *************************************************************************/ void signalDialInPumpHardStop( void ) { - targetDialInFlowRate.data = 0; + targetDialInFlowRate = 0; stopDialInPump(); dialInPumpState = DIAL_IN_PUMP_OFF_STATE; dialInPumpPWMDutyCyclePct = 0.0; @@ -297,12 +327,11 @@ /*********************************************************************//** * @brief - * The signalDialInPumpRotorHallSensor function handles the dialysate inlet pump rotor \n - * hall sensor detection. Calculates rotor speed (in RPM). Stops pump if \n + * The signalDialInPumpRotorHallSensor function handles the dialysate inlet pump rotor + * hall sensor detection. Calculates rotor speed (in RPM). Stops pump if * there is a pending request to home the pump. - * @details - * Inputs : dipRotorRevStartTime, dipStopAtHomePosition - * Outputs : dipRotorRevStartTime, dialInPumpRotorSpeedRPM + * @details Inputs: dipRotorRevStartTime, dipStopAtHomePosition + * @details Outputs: dipRotorRevStartTime, dialInPumpRotorSpeedRPM * @return none *************************************************************************/ void signalDialInPumpRotorHallSensor( void ) @@ -325,9 +354,8 @@ /*********************************************************************//** * @brief * The homeDialInPump function initiates a dialysate inlet pump home operation. - * @details - * Inputs : dialInPumpState - * Outputs : dipStopAtHomePosition, dipHomeStartTime, dialysate inlet pump started (slow) + * @details Inputs: dialInPumpState + * @details Outputs: dipStopAtHomePosition, dipHomeStartTime, dialysate inlet pump started (slow) * @return none *************************************************************************/ BOOL homeDialInPump( void ) @@ -344,30 +372,44 @@ return result; } +/*********************************************************************//** + * @brief + * The isDialInPumpRunning function returns whether the dialysate inlet pump + * is currently running or not. + * @details Inputs: isDialInPumpOn + * @details Outputs: none + * @return isDialInPumpOn + *************************************************************************/ +BOOL isDialInPumpRunning( void ) +{ + return isDialInPumpOn; +} + /*********************************************************************//** * @brief * The execDialInFlowMonitor function executes the dialIn flow monitor. - * @details - * Inputs : none - * Outputs : measuredDialInFlowRate, adcDialInPumpMCSpeedRPM, adcDialInPumpMCCurrentmA + * @details Inputs: none + * @details Outputs: measuredDialInFlowRate, adcDialInPumpMCSpeedRPM, adcDialInPumpMCCurrentmA * @return none *************************************************************************/ void execDialInFlowMonitor( void ) { + HD_OP_MODE_T opMode = getCurrentOperationMode(); U16 dipRPM = getIntADCReading( INT_ADC_DIAL_IN_PUMP_SPEED ); U16 dipmA = getIntADCReading( INT_ADC_DIAL_IN_PUMP_MOTOR_CURRENT ); - F32 dipFlow = getFPGADialysateFlow(); + F32 dipFlow = ( getFPGADialysateFlow() * dialInFlowCalGain ) + dialInFlowCalOffset; adcDialInPumpMCSpeedRPM.data = (F32)(SIGN_FROM_12_BIT_VALUE(dipRPM)) * DIP_SPEED_ADC_TO_RPM_FACTOR; adcDialInPumpMCCurrentmA.data = (F32)(SIGN_FROM_12_BIT_VALUE(dipmA)) * DIP_CURRENT_ADC_TO_MA_FACTOR; + dialInFlowSignalStrength.data = getFPGADialysateFlowSignalStrength(); filterDialInFlowReadings( dipFlow ); // calculate dialysate inlet pump motor speed/direction from hall sensor count updateDialInPumpSpeedAndDirectionFromHallSensors(); // don't start enforcing checks until out of init/POST mode - if ( getCurrentOperationMode() != MODE_INIT ) + if ( opMode != MODE_INIT ) { // check pump direction checkDialInPumpDirection(); @@ -377,7 +419,9 @@ checkDialInPumpSpeeds(); checkDialInPumpFlowAgainstSpeed(); // check for home position, zero/low speed - checkDialInPumpRotor(); + checkDialInPumpRotor(); + // check flow sensor signal strength + checkDialInFlowSensorSignalStrength(); } // publish dialIn flow data on interval @@ -387,9 +431,8 @@ /*********************************************************************//** * @brief * The execDialInFlowController function executes the dialIn flow controller. - * @details - * Inputs : dialInPumpState - * Outputs : dialInPumpState + * @details Inputs: dialInPumpState + * @details Outputs: dialInPumpState * @return none *************************************************************************/ void execDialInFlowController( void ) @@ -413,26 +456,25 @@ break; default: - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_SOFTWARE_FAULT, SW_FAULT_ID_DIAL_IN_FLOW_INVALID_DIAL_IN_PUMP_STATE, dialInPumpState ) + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_DIAL_IN_FLOW_INVALID_DIAL_IN_PUMP_STATE, dialInPumpState ) break; } } /*********************************************************************//** * @brief - * The handleDialInPumpOffState function handles the dialIn pump off state \n + * The handleDialInPumpOffState function handles the dialIn pump off state * of the dialIn pump controller state machine. - * @details - * Inputs : targetDialInFlowRate, dialInPumpDirection - * Outputs : dialInPumpPWMDutyCyclePctSet, dialInPumpDirectionSet, isDialInPumpOn + * @details Inputs: targetDialInFlowRate, dialInPumpDirection + * @details Outputs: dialInPumpPWMDutyCyclePctSet, dialInPumpDirectionSet, isDialInPumpOn * @return next state *************************************************************************/ static DIAL_IN_PUMP_STATE_T handleDialInPumpOffState( void ) { DIAL_IN_PUMP_STATE_T result = DIAL_IN_PUMP_OFF_STATE; // if we've been given a flow rate, setup ramp up and transition to ramp up state - if ( getTargetDialInFlowRate() != 0 ) + if ( targetDialInFlowRate != 0 ) { // set initial PWM duty cycle dialInPumpPWMDutyCyclePctSet = DIP_PWM_ZERO_OFFSET + MAX_DIAL_IN_PUMP_PWM_STEP_UP_CHANGE; @@ -449,19 +491,18 @@ /*********************************************************************//** * @brief - * The handleDialInPumpRampingUpState function handles the ramp up state \n + * The handleDialInPumpRampingUpState function handles the ramp up state * of the dialIn pump controller state machine. - * @details - * Inputs : dialInPumpPWMDutyCyclePctSet - * Outputs : dialInPumpPWMDutyCyclePctSet + * @details Inputs: dialInPumpPWMDutyCyclePctSet + * @details Outputs: dialInPumpPWMDutyCyclePctSet * @return next state *************************************************************************/ static DIAL_IN_PUMP_STATE_T handleDialInPumpRampingUpState( void ) { DIAL_IN_PUMP_STATE_T result = DIAL_IN_PUMP_RAMPING_UP_STATE; // have we been asked to stop the dialIn pump? - if ( 0 == getTargetDialInFlowRate() ) + if ( 0 == targetDialInFlowRate ) { // start ramp down to stop dialInPumpPWMDutyCyclePctSet -= MAX_DIAL_IN_PUMP_PWM_STEP_DN_CHANGE; @@ -494,19 +535,18 @@ /*********************************************************************//** * @brief - * The handleDialInPumpRampingDownState function handles the ramp down state \n + * The handleDialInPumpRampingDownState function handles the ramp down state * of the dialIn pump controller state machine. - * @details - * Inputs : dialInPumpPWMDutyCyclePctSet - * Outputs : dialInPumpPWMDutyCyclePctSet + * @details Inputs: dialInPumpPWMDutyCyclePctSet + * @details Outputs: dialInPumpPWMDutyCyclePctSet * @return next state *************************************************************************/ static DIAL_IN_PUMP_STATE_T handleDialInPumpRampingDownState( void ) { DIAL_IN_PUMP_STATE_T result = DIAL_IN_PUMP_RAMPING_DOWN_STATE; // have we essentially reached zero speed - if ( dialInPumpPWMDutyCyclePctSet < (MAX_DIAL_IN_PUMP_PWM_STEP_UP_CHANGE + DIP_PWM_ZERO_OFFSET) ) + if ( dialInPumpPWMDutyCyclePctSet < (MAX_DIAL_IN_PUMP_PWM_STEP_DN_CHANGE + DIP_PWM_ZERO_OFFSET) ) { stopDialInPump(); result = DIAL_IN_PUMP_OFF_STATE; @@ -537,11 +577,10 @@ /*********************************************************************//** * @brief - * The handleDialInPumpControlToTargetState function handles the "control to \n + * The handleDialInPumpControlToTargetState function handles the "control to * target" state of the dialIn pump controller state machine. - * @details - * Inputs : none - * Outputs : dialInPumpState + * @details Inputs: none + * @details Outputs: dialInPumpState * @return next state *************************************************************************/ static DIAL_IN_PUMP_STATE_T handleDialInPumpControlToTargetState( void ) @@ -553,7 +592,7 @@ { if ( dialInPumpControlModeSet == PUMP_CONTROL_MODE_CLOSED_LOOP ) { - F32 tgtFlow = (F32)getTargetDialInFlowRate(); + F32 tgtFlow = (F32)targetDialInFlowRate; F32 actFlow = getMeasuredDialInFlowRate(); F32 newPWM; @@ -569,12 +608,11 @@ /*********************************************************************//** * @brief - * The setDialInPumpControlSignalPWM function sets the PWM duty cycle for \n + * The setDialInPumpControlSignalPWM function sets the PWM duty cycle for * the dialysate inlet pump to a given %. - * @details - * Inputs : none - * Outputs : dialIn pump stop signal activated, PWM duty cycle zeroed - * @param newPWM : new duty cycle % to apply to PWM + * @details Inputs: none + * @details Outputs: dialIn pump stop signal activated, PWM duty cycle zeroed + * @param newPWM new duty cycle % to apply to PWM * @return none *************************************************************************/ static void setDialInPumpControlSignalPWM( F32 newPWM ) @@ -585,9 +623,8 @@ /*********************************************************************//** * @brief * The stopDialInPump function sets the dialIn pump stop signal. - * @details - * Inputs : none - * Outputs : dialIn pump stop signal activated, PWM duty cycle zeroed + * @details Inputs: none + * @details Outputs: dialIn pump stop signal activated, PWM duty cycle zeroed * @return none *************************************************************************/ static void stopDialInPump( void ) @@ -601,9 +638,8 @@ /*********************************************************************//** * @brief * The releaseDialInPumpStop function clears the dialIn pump stop signal. - * @details - * Inputs : none - * Outputs : dialIn pump stop signal + * @details Inputs: none + * @details Outputs: dialIn pump stop signal * @return none *************************************************************************/ static void releaseDialInPumpStop( void ) @@ -613,12 +649,11 @@ /*********************************************************************//** * @brief - * The setDialInPumpDirection function sets the set dialIn pump direction to \n + * The setDialInPumpDirection function sets the set dialIn pump direction to * the given direction. - * @details - * Inputs : dialInPumpState - * Outputs : dialInPumpState - * @param dir : dialIn pump direction to set + * @details Inputs: dialInPumpState + * @details Outputs: dialInPumpState + * @param dir dialIn pump direction to set * @return none *************************************************************************/ static void setDialInPumpDirection( MOTOR_DIR_T dir ) @@ -636,122 +671,200 @@ break; default: - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_SOFTWARE_FAULT, SW_FAULT_ID_DIAL_IN_FLOW_INVALID_DIAL_IN_PUMP_DIRECTION, dir ) + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_DIAL_IN_FLOW_INVALID_DIAL_IN_PUMP_DIRECTION, dir ) break; } } /*********************************************************************//** * @brief - * The getPublishDialInFlowDataInterval function gets the dialIn flow data \n + * The getPublishDialInFlowDataInterval function gets the dialIn flow data * publication interval. - * @details - * Inputs : dialInFlowDataPublishInterval - * Outputs : none - * @return the current dialIn flow data publication interval (in ms). + * @details Inputs: dialInFlowDataPublishInterval + * @details Outputs: none + * @return the current dialIn flow data publication interval (in task intervals). *************************************************************************/ -DATA_GET( U32, getPublishDialInFlowDataInterval, dialInFlowDataPublishInterval ) +U32 getPublishDialInFlowDataInterval( void ) +{ + U32 result = dialInFlowDataPublishInterval.data; + + if ( OVERRIDE_KEY == dialInFlowDataPublishInterval.override ) + { + result = dialInFlowDataPublishInterval.ovData; + } + + return result; +} /*********************************************************************//** * @brief - * The getTargetDialInFlowRate function gets the current target dialIn flow \n + * The getTargetDialInFlowRate function gets the current target dialIn flow * rate. - * @details - * Inputs : targetDialInFlowRate - * Outputs : none + * @details Inputs: targetDialInFlowRate + * @details Outputs: none * @return the current target dialIn flow rate (in mL/min). *************************************************************************/ -DATA_GET( S32, getTargetDialInFlowRate, targetDialInFlowRate ) +S32 getTargetDialInFlowRate( void ) +{ + return targetDialInFlowRate; +} /*********************************************************************//** * @brief - * The getMeasuredDialInFlowRate function gets the measured dialIn flow \n + * The getMeasuredDialInFlowRate function gets the measured dialIn flow * rate. - * @details - * Inputs : measuredDialInFlowRate - * Outputs : none -/ * @return the current dialIn flow rate (in mL/min). + * @details Inputs: measuredDialInFlowRate + * @details Outputs: none + * @return the current dialIn flow rate (in mL/min). *************************************************************************/ -DATA_GET( F32, getMeasuredDialInFlowRate, measuredDialInFlowRate ) +F32 getMeasuredDialInFlowRate( void ) +{ + F32 result = measuredDialInFlowRate.data; + + if ( OVERRIDE_KEY == measuredDialInFlowRate.override ) + { + result = measuredDialInFlowRate.ovData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getMeasuredDialInFlowSignalStrength function gets the measured dialIn flow + * signal strength. + * @details Inputs: dialInFlowSignalStrength + * @details Outputs: none + * @return the current dialIn flow signal strength (in %). + *************************************************************************/ +F32 getMeasuredDialInFlowSignalStrength( void ) +{ + F32 result = dialInFlowSignalStrength.data; + + if ( OVERRIDE_KEY == dialInFlowSignalStrength.override ) + { + result = dialInFlowSignalStrength.ovData; + } + + return result; +} /*********************************************************************//** * @brief - * The getMeasuredDialInPumpRotorSpeed function gets the measured dialIn flow \n + * The getMeasuredDialInPumpRotorSpeed function gets the measured dialIn flow * rate. - * @details - * Inputs : dialInPumpRotorSpeedRPM - * Outputs : none + * @details Inputs: dialInPumpRotorSpeedRPM + * @details Outputs: none * @return the current dialIn flow rate (in mL/min). *************************************************************************/ -DATA_GET( F32, getMeasuredDialInPumpRotorSpeed, dialInPumpRotorSpeedRPM ) +F32 getMeasuredDialInPumpRotorSpeed( void ) +{ + F32 result = dialInPumpRotorSpeedRPM.data; + + if ( OVERRIDE_KEY == dialInPumpRotorSpeedRPM.override ) + { + result = dialInPumpRotorSpeedRPM.ovData; + } + + return result; +} /*********************************************************************//** * @brief - * The getMeasuredDialInPumpSpeed function gets the measured dialIn flow \n + * The getMeasuredDialInPumpSpeed function gets the measured dialIn flow * rate. - * @details - * Inputs : dialInPumpSpeedRPM - * Outputs : none + * @details Inputs: dialInPumpSpeedRPM + * @details Outputs: none * @return the current dialIn flow rate (in mL/min). *************************************************************************/ -DATA_GET( F32, getMeasuredDialInPumpSpeed, dialInPumpSpeedRPM ) +F32 getMeasuredDialInPumpSpeed( void ) +{ + F32 result = dialInPumpSpeedRPM.data; + + if ( OVERRIDE_KEY == dialInPumpSpeedRPM.override ) + { + result = dialInPumpSpeedRPM.ovData; + } + + return result; +} /*********************************************************************//** * @brief - * The getMeasuredDialInPumpMCSpeed function gets the measured dialIn pump \n + * The getMeasuredDialInPumpMCSpeed function gets the measured dialIn pump * speed. - * @details - * Inputs : adcDialInPumpMCSpeedRPM - * Outputs : none + * @details Inputs: adcDialInPumpMCSpeedRPM + * @details Outputs: none * @return the current dialIn pump speed (in RPM). *************************************************************************/ -DATA_GET( F32, getMeasuredDialInPumpMCSpeed, adcDialInPumpMCSpeedRPM ) +F32 getMeasuredDialInPumpMCSpeed( void ) +{ + F32 result = adcDialInPumpMCSpeedRPM.data; + + if ( OVERRIDE_KEY == adcDialInPumpMCSpeedRPM.override ) + { + result = adcDialInPumpMCSpeedRPM.ovData; + } + + return result; +} /*********************************************************************//** * @brief - * The getMeasuredDialInPumpMCCurrent function gets the measured dialIn pump \n + * The getMeasuredDialInPumpMCCurrent function gets the measured dialIn pump * current. - * @details - * Inputs : adcDialInPumpMCCurrentmA - * Outputs : none -/ * @return the current dialIn pump current (in mA). + * @details Inputs: adcDialInPumpMCCurrentmA + * @details Outputs: none + * @return the current dialIn pump current (in mA). *************************************************************************/ -DATA_GET( F32, getMeasuredDialInPumpMCCurrent, adcDialInPumpMCCurrentmA ) +F32 getMeasuredDialInPumpMCCurrent( void ) +{ + F32 result = adcDialInPumpMCCurrentmA.data; + + if ( OVERRIDE_KEY == adcDialInPumpMCCurrentmA.override ) + { + result = adcDialInPumpMCCurrentmA.ovData; + } + + return result; +} /*********************************************************************//** * @brief - * The publishDialInFlowData function publishes dialIn flow data at the set \n + * The publishDialInFlowData function publishes dialIn flow data at the set * interval. - * @details - * Inputs : target flow rate, measured flow rate, measured MC speed, \n + * @details Inputs: target flow rate, measured flow rate, measured MC speed, * measured MC current - * Outputs : DialIn flow data is published to CAN bus. + * @details Outputs: DialIn flow data is published to CAN bus. * @return none *************************************************************************/ static void publishDialInFlowData( void ) { // publish dialIn flow data on interval if ( ++dialInFlowDataPublicationTimerCounter >= getPublishDialInFlowDataInterval() ) { - S32 flowStPt = (S32)getTargetDialInFlowRate(); - F32 measFlow = getMeasuredDialInFlowRate(); - F32 measRotSpd = getMeasuredDialInPumpRotorSpeed(); - F32 measSpd = getMeasuredDialInPumpSpeed(); - F32 measMCSpd = getMeasuredDialInPumpMCSpeed(); - F32 measMCCurr = getMeasuredDialInPumpMCCurrent(); - F32 pumpPWMPctDutyCycle = dialInPumpPWMDutyCyclePctSet * FRACTION_TO_PERCENT_FACTOR; - broadcastDialInFlowData( flowStPt, measFlow, measRotSpd, measSpd, measMCSpd, measMCCurr, pumpPWMPctDutyCycle ); + DIALIN_PUMP_STATUS_PAYLOAD_T payload; + + payload.setPoint = targetDialInFlowRate; + payload.measFlow = getMeasuredDialInFlowRate(); + payload.measRotorSpd = getMeasuredDialInPumpRotorSpeed(); + payload.measPumpSpd = getMeasuredDialInPumpSpeed(); + payload.measMCSpd = getMeasuredDialInPumpMCSpeed(); + payload.measMCCurr = getMeasuredDialInPumpMCCurrent(); + payload.pwmDC = dialInPumpPWMDutyCyclePctSet * FRACTION_TO_PERCENT_FACTOR; + payload.flowSigStrength = getMeasuredDialInFlowSignalStrength() * FRACTION_TO_PERCENT_FACTOR; + broadcastDialInFlowData( &payload ); dialInFlowDataPublicationTimerCounter = 0; } } /*********************************************************************//** * @brief - * The resetDialInFlowMovingAverage function resets the properties of the \n + * The resetDialInFlowMovingAverage function resets the properties of the * dialIn flow moving average sample buffer. - * @details - * Inputs : none - * Outputs : flowReadingsTotal, flowReadingsIdx, flowReadingsCount all set to zero. + * @details Inputs: none + * @details Outputs: flowReadingsTotal, flowReadingsIdx, flowReadingsCount all set to zero. + * @return none *************************************************************************/ static void resetDialInFlowMovingAverage( void ) { @@ -763,11 +876,9 @@ /*********************************************************************//** * @brief - * The filterDialInFlowReadings function adds a new flow sample to the filter \n - * if decimation rate for current set point calls for it. - * @details - * Inputs : none - * Outputs : flowReadings[], flowReadingsIdx, flowReadingsCount + * The filterDialInFlowReadings function adds a new flow sample to the filter. + * @details Inputs: none + * @details Outputs: flowReadings[], flowReadingsIdx, flowReadingsCount, flowReadingsTotal * @return none *************************************************************************/ static void filterDialInFlowReadings( F32 flow ) @@ -789,51 +900,51 @@ /*********************************************************************//** * @brief - * The updateDialInPumpSpeedAndDirectionFromHallSensors function calculates \n - * the dialysate inlet pump motor speed and direction from hall sensor counter on \n + * The updateDialInPumpSpeedAndDirectionFromHallSensors function calculates + * the dialysate inlet pump motor speed and direction from hall sensor counter on * a 1 second interval. - * @details - * Inputs : dipLastMotorHallSensorCount, dipMotorSpeedCalcTimerCtr, current count from FPGA - * Outputs : dipMotorDirectionFromHallSensors, dialInPumpSpeedRPM + * @details Inputs: dipLastMotorHallSensorCount, dipMotorSpeedCalcTimerCtr, current count from FPGA + * @details Outputs: dipMotorDirectionFromHallSensors, dialInPumpSpeedRPM * @return none *************************************************************************/ static void updateDialInPumpSpeedAndDirectionFromHallSensors( void ) { if ( ++dipMotorSpeedCalcTimerCtr >= DIP_SPEED_CALC_INTERVAL ) { U16 dipMotorHallSensorCount = getFPGADialInPumpHallSensorCount(); - U16 incDelta = ( dipMotorHallSensorCount >= dipLastMotorHallSensorCount ? dipMotorHallSensorCount - dipLastMotorHallSensorCount : ( HEX_64_K - dipLastMotorHallSensorCount ) + dipMotorHallSensorCount ); + U32 nextIdx = INC_WRAP( dipMotorSpeedCalcIdx, 0, DIP_SPEED_CALC_BUFFER_LEN - 1 ); + U16 incDelta = ( dipMotorHallSensorCount >= dipLastMotorHallSensorCounts[ nextIdx ] ? \ + dipMotorHallSensorCount - dipLastMotorHallSensorCounts[ nextIdx ] : \ + ( HEX_64_K - dipLastMotorHallSensorCounts[ nextIdx ] ) + dipMotorHallSensorCount ); U16 decDelta = HEX_64_K - incDelta; U16 delta; // determine dialysate inlet pump speed/direction from delta hall sensor count since last interval if ( incDelta < decDelta ) { - dipMotorDirectionFromHallSensors = MOTOR_DIR_FORWARD; delta = incDelta; dialInPumpSpeedRPM.data = ( (F32)delta / (F32)DIP_HALL_EDGE_COUNTS_PER_REV ) * (F32)SEC_PER_MIN; } else { - dipMotorDirectionFromHallSensors = MOTOR_DIR_REVERSE; delta = decDelta; dialInPumpSpeedRPM.data = ( (F32)delta / (F32)DIP_HALL_EDGE_COUNTS_PER_REV ) * (F32)SEC_PER_MIN * -1.0; } // update last count for next time - dipLastMotorHallSensorCount = dipMotorHallSensorCount; + dipLastMotorHallSensorCounts[ nextIdx ] = dipMotorHallSensorCount; + dipMotorSpeedCalcIdx = nextIdx; dipMotorSpeedCalcTimerCtr = 0; } } /*********************************************************************//** * @brief - * The checkDialInPumpRotor function checks the rotor for the dialysate inlet \n - * pump. If homing, this function will stop when hall sensor detected. If pump \n + * The checkDialInPumpRotor function checks the rotor for the dialysate inlet + * pump. If homing, this function will stop when hall sensor detected. If pump * is off or running very slowly, rotor speed will be set to zero. - * @details - * Inputs : dipStopAtHomePosition, dipHomeStartTime, dipRotorRevStartTime - * Outputs : pump may be stopped if homing, dialInPumpRotorSpeedRPM may be set to zero. + * @details Inputs: dipStopAtHomePosition, dipHomeStartTime, dipRotorRevStartTime + * @details Outputs: pump may be stopped if homing, dialInPumpRotorSpeedRPM may be set to zero. * @return none *************************************************************************/ static void checkDialInPumpRotor( void ) @@ -855,47 +966,68 @@ /*********************************************************************//** * @brief - * The checkDialInPumpDirection function checks the set direction vs. \n + * The checkDialInPumpDirection function checks the set direction vs. * the direction implied by the sign of the measured MC speed. - * @details - * Inputs : adcDialInPumpMCSpeedRPM, dialInPumpDirectionSet, dialInPumpState - * Outputs : none + * @details Inputs: adcDialInPumpMCSpeedRPM, dialInPumpDirectionSet, dialInPumpState + * @details Outputs: none * @return none *************************************************************************/ static void checkDialInPumpDirection( void ) { - MOTOR_DIR_T dipMCDir; - if ( DIAL_IN_PUMP_CONTROL_TO_TARGET_STATE == dialInPumpState ) { - // check set direction vs. direction from hall sensors or sign of motor controller speed - dipMCDir = ( getMeasuredDialInPumpMCSpeed() >= 0.0 ? MOTOR_DIR_FORWARD : MOTOR_DIR_REVERSE ); - if ( ( dialInPumpDirectionSet != dipMCDir ) || ( dialInPumpDirectionSet != dipMotorDirectionFromHallSensors ) ) + MOTOR_DIR_T dipMCDir, dipDir; + + dipMCDir = ( getMeasuredDialInPumpMCSpeed() >= 0.0 ? MOTOR_DIR_FORWARD : MOTOR_DIR_REVERSE ); + dipDir = ( getMeasuredDialInPumpSpeed() >= 0.0 ? MOTOR_DIR_FORWARD : MOTOR_DIR_REVERSE ); + + // check set direction vs. direction from hall sensors + if ( dialInPumpDirectionSet != dipDir ) { + if ( ++errorDialInPumpDirectionPersistTimerCtr >= DIP_DIRECTION_ERROR_PERSIST ) + { #ifndef DISABLE_PUMP_DIRECTION_CHECKS - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DIAL_IN_PUMP_MC_DIRECTION_CHECK, (U32)dialInPumpDirectionSet, (U32)dipMotorDirectionFromHallSensors ) -#endif + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DIAL_IN_PUMP_MC_DIRECTION_CHECK, (U32)dialInPumpDirectionSet, (U32)dipDir ) +#endif + } } + // check set direction vs. direction from sign of motor controller speed + else if ( dialInPumpDirectionSet != dipMCDir ) + { + if ( ++errorDialInPumpDirectionPersistTimerCtr >= DIP_DIRECTION_ERROR_PERSIST ) + { +#ifndef DISABLE_PUMP_DIRECTION_CHECKS + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DIAL_IN_PUMP_MC_DIRECTION_CHECK, (U32)dialInPumpDirectionSet, (U32)dipMCDir ) +#endif + } + } + else + { + errorDialInPumpDirectionPersistTimerCtr = 0; + } + } + else + { + errorDialInPumpDirectionPersistTimerCtr = 0; } } /*********************************************************************//** * @brief - * The checkDialInPumpSpeeds function checks several aspects of the dialysate \n - * inlet pump speed. \n - * 1. while pump is commanded off, measured motor speed should be < limit. \n - * 2. while pump is controlling, measured motor speed should be within allowed range of commanded speed. \n - * 3. measured motor speed should be within allowed range of measured rotor speed. \n + * The checkDialInPumpSpeeds function checks several aspects of the dialysate + * inlet pump speed. + * 1. while pump is commanded off, measured motor speed should be < limit. + * 2. while pump is controlling, measured motor speed should be within allowed range of commanded speed. + * 3. measured motor speed should be within allowed range of measured rotor speed. * All 3 checks have a persistence time that must be met before an alarm is triggered. - * @details - * Inputs : targetDialInFlowRate, dialInPumpSpeedRPM, dialInPumpRotorSpeedRPM - * Outputs : alarm(s) may be triggered + * @details Inputs: targetDialInFlowRate, dialInPumpSpeedRPM, dialInPumpRotorSpeedRPM + * @details Outputs: alarm(s) may be triggered * @return none *************************************************************************/ static void checkDialInPumpSpeeds( void ) { F32 measMotorSpeed = getMeasuredDialInPumpSpeed(); - S32 cmdRate = getTargetDialInFlowRate(); + S32 cmdRate = targetDialInFlowRate; // check for pump running while commanded off if ( 0 == cmdRate ) @@ -906,6 +1038,7 @@ { #ifndef DISABLE_PUMP_SPEED_CHECKS SET_ALARM_WITH_1_F32_DATA( ALARM_ID_DIAL_IN_PUMP_OFF_CHECK, measMotorSpeed ); + activateSafetyShutdown(); #endif } } @@ -922,10 +1055,10 @@ if ( DIAL_IN_PUMP_CONTROL_TO_TARGET_STATE == dialInPumpState ) { F32 cmdMotorSpeed = ( (F32)cmdRate / (F32)ML_PER_LITER ) * DIP_REV_PER_LITER * DIP_GEAR_RATIO; - F32 deltaMotorSpeed = FABS( measMotorSpeed - cmdMotorSpeed ); + F32 deltaMotorSpeed = fabs( measMotorSpeed - cmdMotorSpeed ); F32 measRotorSpeed = getMeasuredDialInPumpRotorSpeed(); F32 measMotorSpeedInRotorRPM = measMotorSpeed / DIP_GEAR_RATIO; - F32 deltaRotorSpeed = FABS( measRotorSpeed - measMotorSpeedInRotorRPM ); + F32 deltaRotorSpeed = fabs( measRotorSpeed - measMotorSpeedInRotorRPM ); // check measured motor speed vs. commanded motor speed while controlling to target if ( deltaMotorSpeed > DIP_MAX_MOTOR_SPEED_ERROR_RPM ) @@ -967,13 +1100,12 @@ /*********************************************************************//** * @brief - * The checkDialInPumpFlowAgainstSpeed function checks the measured dialysate flow \n - * against the implied flow of the measured pump speed when in treatment mode \n - * and controlling to target flow. If a sufficient difference persists, a \n + * The checkDialInPumpFlowAgainstSpeed function checks the measured dialysate flow + * against the implied flow of the measured pump speed when in treatment mode + * and controlling to target flow. If a sufficient difference persists, a * flow vs. motor speed check error is triggered. - * @details - * Inputs : measuredDialInFlowRate, dialInPumpSpeedRPM, errorDialInFlowVsMotorSpeedPersistTimerCtr - * Outputs : alarm may be triggered + * @details Inputs: measuredDialInFlowRate, dialInPumpSpeedRPM, errorDialInFlowVsMotorSpeedPersistTimerCtr + * @details Outputs: alarm may be triggered * @return none *************************************************************************/ static void checkDialInPumpFlowAgainstSpeed( void ) @@ -983,15 +1115,15 @@ { F32 flow = getMeasuredDialInFlowRate(); F32 speed = getMeasuredDialInPumpSpeed(); - F32 impliedFlow = ( ( speed / DIP_GEAR_RATIO ) / DIP_REV_PER_LITER ) * (F32)ML_PER_LITER; - F32 delta = FABS( flow - impliedFlow ); + F32 impliedSpeed = ( flow / (F32)ML_PER_LITER ) * DIP_REV_PER_LITER * DIP_GEAR_RATIO; + F32 delta = fabs( speed - impliedSpeed ); - if ( delta > DIP_MAX_FLOW_VS_SPEED_DIFF_ML_MIN ) + if ( delta > DIP_MAX_FLOW_VS_SPEED_DIFF_RPM ) { if ( ++errorDialInFlowVsMotorSpeedPersistTimerCtr >= DIP_FLOW_VS_SPEED_PERSIST ) { #ifndef DISABLE_PUMP_FLOW_CHECKS - SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DIAL_IN_PUMP_FLOW_VS_MOTOR_SPEED_CHECK, flow, impliedFlow ); + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DIAL_IN_PUMP_FLOW_VS_MOTOR_SPEED_CHECK, flow, speed ); #endif } } @@ -1008,11 +1140,10 @@ /*********************************************************************//** * @brief - * The checkDialInPumpMCCurrent function checks the measured MC current vs. \n + * The checkDialInPumpMCCurrent function checks the measured MC current vs. * the set state of the dialIn pump (stopped or running). - * @details - * Inputs : dialInPumpState, dipCurrErrorDurationCtr, adcDialInPumpMCCurrentmA - * Outputs : none + * @details Inputs: dialInPumpState, dipCurrErrorDurationCtr, adcDialInPumpMCCurrentmA + * @details Outputs: none * @return none *************************************************************************/ static void checkDialInPumpMCCurrent( void ) @@ -1057,23 +1188,69 @@ dipCurrErrorDurationCtr = 0; } } +} + +/*********************************************************************//** + * @brief + * The checkDialInFlowSensorSignalStrength function checks the measured + * dialysate flow sensor signal strength is sufficient for accurate flow sensing. + * @details Inputs: + * @details Outputs: + * @return none + *************************************************************************/ +static void checkDialInFlowSensorSignalStrength( void ) +{ +#ifndef DISABLE_PUMP_FLOW_CHECKS + HD_OP_MODE_T opMode = getCurrentOperationMode(); + + // check flow sensor signal strength when appropriate TODO - in pre-treatment, must be far enough along for fluid to be in tubing + if ( MODE_TREA == opMode || ( MODE_PRET == opMode && FALSE ) ) + { + F32 sigStrength = getMeasuredDialInFlowSignalStrength(); + BOOL outOfRange = ( sigStrength < MIN_FLOW_SIG_STRENGTH ? TRUE : FALSE ); + + checkPersistentAlarm( PERSISTENT_ALARM_DIALYSATE_FLOW_SIGNAL_STRENGTH, outOfRange, sigStrength, MIN_FLOW_SIG_STRENGTH ); + } +#endif } /*********************************************************************//** * @brief - * The execDialInFlowTest function executes the state machine for the \n - * DialInFlow self test. - * @details - * Inputs : none - * Outputs : none - * @return the current state of the DialInFlow self test. + * The execDialInFlowTest function executes the state machine for the + * DialInFlow self-test. + * @details Inputs: none + * @details Outputs: none + * @return the current state of the DialInFlow self-test. *************************************************************************/ SELF_TEST_STATUS_T execDialInFlowTest( void ) { SELF_TEST_STATUS_T result = SELF_TEST_STATUS_FAILED; - - // TODO - implement self test(s) - + CALIBRATION_DATA_T cal; + + switch ( dialInPumpSelfTestState ) + { + case DIAL_IN_FLOW_SELF_TEST_STATE_START: + // retrieve blood flow sensor calibration data + if ( TRUE == getCalibrationData( &cal ) ) + { + dialInFlowCalGain = cal.dialysateFlowGain; + dialInFlowCalOffset = cal.dialysateFlowOffset_mL_min; + dialInPumpSelfTestState = DIAL_IN_FLOW_TEST_STATE_COMPLETE; // TODO - implement rest of self-test(s) + result = SELF_TEST_STATUS_PASSED; + } + break; + + case DIAL_IN_FLOW_TEST_STATE_IN_PROGRESS: + break; + + case DIAL_IN_FLOW_TEST_STATE_COMPLETE: + break; + + default: + // TODO - s/w fault + break; + } + return result; } @@ -1083,14 +1260,63 @@ *************************************************************************/ +/*********************************************************************//** + * @brief + * The setDialInFlowCalibration function sets the dialysate flow calibration + * factors and has them stored in non-volatile memory. + * @details Inputs: none + * @details Outputs: dialInFlowCalGain, dialInFlowCalOffset + * @param gain gain calibration factor for dialysate flow sensor + * @param offset offset calibration factor for dialysate flow sensor + * @return TRUE if calibration factors successfully set/stored, FALSE if not + *************************************************************************/ +BOOL setDialInFlowCalibration( F32 gain, F32 offset ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + CALIBRATION_DATA_T cal; + + getCalibrationData( &cal ); + // keep locally and apply immediately + dialInFlowCalGain = gain; + dialInFlowCalOffset = offset; + // also update calibration record in non-volatile memory + cal.dialysateFlowGain = gain; + cal.dialysateFlowOffset_mL_min = offset; + if ( TRUE == setCalibrationData( cal ) ) + { + result = TRUE; + } + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getDialInFlowCalibration function retrieves the current dialysate flow + * calibration factors. + * @details Inputs: dialInFlowCalGain, dialInFlowCalOffset + * @details Outputs: none + * @param gain value to populate with gain calibration factor for dialysate flow sensor + * @param offset value to populate with offset calibration factor for dialysate flow sensor + * @return none + *************************************************************************/ +void getDialInFlowCalibration( F32 *gain, F32 *offset ) +{ + *gain = dialInFlowCalGain; + *offset = dialInFlowCalOffset; +} + /*********************************************************************//** * @brief - * The testSetDialInFlowDataPublishIntervalOverride function overrides the \n + * The testSetDialInFlowDataPublishIntervalOverride function overrides the * dialIn flow data publish interval. - * @details - * Inputs : none - * Outputs : dialInFlowDataPublishInterval - * @param value : override dialIn flow data publish interval with (in ms) + * @details Inputs: none + * @details Outputs: dialInFlowDataPublishInterval + * @param value override dialIn flow data publish interval with (in ms) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetDialInFlowDataPublishIntervalOverride( U32 value ) @@ -1111,11 +1337,10 @@ /*********************************************************************//** * @brief - * The testResetDialInFlowDataPublishIntervalOverride function resets the override \n + * The testResetDialInFlowDataPublishIntervalOverride function resets the override * of the dialIn flow data publish interval. - * @details - * Inputs : none - * Outputs : dialInFlowDataPublishInterval + * @details Inputs: none + * @details Outputs: dialInFlowDataPublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetDialInFlowDataPublishIntervalOverride( void ) @@ -1134,15 +1359,15 @@ /*********************************************************************//** * @brief - * The testSetTargetDialInFlowRateOverride function overrides the target \n - * dialysate inlet flow rate. \n - * @details - * Inputs : none - * Outputs : targetDialInFlowRate - * @param value : override target dialysate inlet flow rate (in mL/min) + * The testSetTargetDialInFlowRateOverride function overrides the target + * dialysate inlet flow rate.n + * @details Inputs: none + * @details Outputs: targetDialInFlowRate + * @param value override target dialysate inlet flow rate (in mL/min) + * @param ctrlMode override pump control mode to this mode (0 = closed loop, 1 = open loop) * @return TRUE if override successful, FALSE if not *************************************************************************/ -BOOL testSetTargetDialInFlowRateOverride( S32 value ) +BOOL testSetTargetDialInFlowRateOverride( S32 value, U32 ctrlMode ) { BOOL result = FALSE; @@ -1158,48 +1383,23 @@ { dir = MOTOR_DIR_FORWARD; } - targetDialInFlowRate.ovInitData = targetDialInFlowRate.data; // backup current target flow rate - targetDialInFlowRate.ovData = value; - targetDialInFlowRate.override = OVERRIDE_KEY; - result = setDialInPumpTargetFlowRate( ABS(value), dir, dialInPumpControlMode ); + if ( ctrlMode < NUM_OF_PUMP_CONTROL_MODES ) + { + targetDialInFlowRate = value; + result = setDialInPumpTargetFlowRate( abs(value), dir, (PUMP_CONTROL_MODE_T)ctrlMode ); + } } return result; } /*********************************************************************//** * @brief - * The testResetTargetDialInFlowRateOverride function resets the override of the \n - * target dialysate inlet flow rate. - * @details - * Inputs : none - * Outputs : targetDialInFlowRate - * @return TRUE if override reset successful, FALSE if not - *************************************************************************/ -BOOL testResetTargetDialInFlowRateOverride( void ) -{ - BOOL result = FALSE; - - if ( TRUE == isTestingActivated() ) - { - targetDialInFlowRate.data = targetDialInFlowRate.ovInitData; // restore pre-override target flow rate - targetDialInFlowRate.override = OVERRIDE_RESET; - targetDialInFlowRate.ovInitData = 0; - targetDialInFlowRate.ovData = 0; - result = setDialInPumpTargetFlowRate( targetDialInFlowRate.data, dialInPumpDirection, dialInPumpControlMode ); - } - - return result; -} - -/*********************************************************************//** - * @brief - * The testResetMeasuredDialInFlowRateOverride function overrides the measured \n + * The testResetMeasuredDialInFlowRateOverride function overrides the measured * dialIn flow rate. - * @details - * Inputs : none - * Outputs : measuredDialInFlowRate - * @param value : override measured dialIn flow rate (in mL/min) + * @details Inputs: none + * @details Outputs: measuredDialInFlowRate + * @param value override measured dialIn flow rate (in mL/min) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetMeasuredDialInFlowRateOverride( F32 value ) @@ -1218,11 +1418,10 @@ /*********************************************************************//** * @brief - * The testResetMeasuredDialInFlowRateOverride function resets the override of the \n + * The testResetMeasuredDialInFlowRateOverride function resets the override of the * measured dialIn flow rate. - * @details - * Inputs : none - * Outputs : measuredDialInFlowRate + * @details Inputs: none + * @details Outputs: measuredDialInFlowRate * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetMeasuredDialInFlowRateOverride( void ) @@ -1241,12 +1440,11 @@ /*********************************************************************//** * @brief - * The testSetMeasuredDialInPumpRotorSpeedOverride function overrides the measured \n + * The testSetMeasuredDialInPumpRotorSpeedOverride function overrides the measured * dialIn pump rotor speed. - * @details - * Inputs : none - * Outputs : dialInPumpRotorSpeedRPM - * @param value : override measured dialIn pump rotor speed (in RPM) + * @details Inputs: none + * @details Outputs: dialInPumpRotorSpeedRPM + * @param value override measured dialIn pump rotor speed (in RPM) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetMeasuredDialInPumpRotorSpeedOverride( F32 value ) @@ -1265,11 +1463,10 @@ /*********************************************************************//** * @brief - * The testResetMeasuredDialInPumpRotorSpeedOverride function resets the override of the \n + * The testResetMeasuredDialInPumpRotorSpeedOverride function resets the override of the * measured dialIn pump rotor speed. - * @details - * Inputs : none - * Outputs : dialInPumpRotorSpeedRPM + * @details Inputs: none + * @details Outputs: dialInPumpRotorSpeedRPM * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetMeasuredDialInPumpRotorSpeedOverride( void ) @@ -1288,12 +1485,11 @@ /*********************************************************************//** * @brief - * The testSetMeasuredDialInPumpSpeedOverride function overrides the measured \n + * The testSetMeasuredDialInPumpSpeedOverride function overrides the measured * dialIn pump motor speed. - * @details - * Inputs : none - * Outputs : dialInPumpSpeedRPM - * @param value : override measured dialIn pump motor speed (in RPM) + * @details Inputs: none + * @details Outputs: dialInPumpSpeedRPM + * @param value override measured dialIn pump motor speed (in RPM) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetMeasuredDialInPumpSpeedOverride( F32 value ) @@ -1312,11 +1508,10 @@ /*********************************************************************//** * @brief - * The testResetMeasuredDialInPumpSpeedOverride function resets the override of the \n + * The testResetMeasuredDialInPumpSpeedOverride function resets the override of the * measured dialIn pump motor speed. - * @details - * Inputs : none - * Outputs : dialInPumpSpeedRPM + * @details Inputs: none + * @details Outputs: dialInPumpSpeedRPM * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetMeasuredDialInPumpSpeedOverride( void ) @@ -1335,12 +1530,11 @@ /*********************************************************************//** * @brief - * The testSetMeasuredDialInPumpMCSpeedOverride function overrides the measured \n + * The testSetMeasuredDialInPumpMCSpeedOverride function overrides the measured * dialIn pump motor speed. - * @details - * Inputs : none - * Outputs : adcDialInPumpMCSpeedRPM - * @param value : override measured dialIn pump speed (in RPM) + * @details Inputs: none + * @details Outputs: adcDialInPumpMCSpeedRPM + * @param value override measured dialIn pump speed (in RPM) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetMeasuredDialInPumpMCSpeedOverride( F32 value ) @@ -1359,11 +1553,10 @@ /*********************************************************************//** * @brief - * The testResetMeasuredDialInPumpMCSpeedOverride function resets the override of the \n + * The testResetMeasuredDialInPumpMCSpeedOverride function resets the override of the * measured dialIn pump motor speed. - * @details - * Inputs : none - * Outputs : adcDialInPumpMCSpeedRPM + * @details Inputs: none + * @details Outputs: adcDialInPumpMCSpeedRPM * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetMeasuredDialInPumpMCSpeedOverride( void ) @@ -1382,12 +1575,11 @@ /*********************************************************************//** * @brief - * The testSetMeasuredDialInPumpMCCurrentOverride function overrides the measured \n + * The testSetMeasuredDialInPumpMCCurrentOverride function overrides the measured * dialIn pump motor current. - * @details - * Inputs : none - * Outputs : adcDialInPumpMCCurrentmA - * @param value : override measured dialIn pump current (in mA) + * @details Inputs: none + * @details Outputs: adcDialInPumpMCCurrentmA + * @param value override measured dialIn pump current (in mA) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetMeasuredDialInPumpMCCurrentOverride( F32 value ) @@ -1406,11 +1598,10 @@ /*********************************************************************//** * @brief - * The testResetMeasuredDialInPumpMCCurrentOverride function resets the override of the \n + * The testResetMeasuredDialInPumpMCCurrentOverride function resets the override of the * measured dialIn pump motor current. - * @details - * Inputs : none - * Outputs : adcDialInPumpMCCurrentmA + * @details Inputs: none + * @details Outputs: adcDialInPumpMCCurrentmA * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetMeasuredDialInPumpMCCurrentOverride( void ) @@ -1426,5 +1617,50 @@ return result; } + +/*********************************************************************//** + * @brief + * The testSetMeasuredDialInFlowSignalStrengthOverride function overrides the measured + * dialysate flow signal strength. + * @details Inputs: none + * @details Outputs: dialInFlowSignalStrength + * @param value override measured dialysate flow signal strength (in %) + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetMeasuredDialInFlowSignalStrengthOverride( F32 value ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + dialInFlowSignalStrength.ovData = value / 100.0; + dialInFlowSignalStrength.override = OVERRIDE_KEY; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetMeasuredDialInFlowSignalStrengthOverride function resets the override + * of the measured dialysate flow signal strength. + * @details Inputs: none + * @details Outputs: dialInFlowSignalStrength + * @return TRUE if reset successful, FALSE if not + *************************************************************************/ +BOOL testResetMeasuredDialInFlowSignalStrengthOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + dialInFlowSignalStrength.override = OVERRIDE_RESET; + dialInFlowSignalStrength.ovData = dialInFlowSignalStrength.ovInitData; + } + + return result; +} /**@}*/