/************************************************************************** * * Copyright (c) 2020-2023 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 Valves.c * * @author (last) Sean Nash * @date (last) 10-Aug-2023 * * @author (original) Dara Navaei * @date (original) 07-Aug-2020 * ***************************************************************************/ // Includes #include "reg_het.h" #include "gio.h" #include "CPLD.h" #include "FPGA.h" #include "MessageSupport.h" #include "NVDataMgmt.h" #include "SafetyShutdown.h" #include "SystemCommMessages.h" #include "Switches.h" #include "TaskPriority.h" #include "Timers.h" #include "Valves.h" /** * @addtogroup Valves * @{ */ // ********** private definitions ********** // Valve dialyzer inlet defines #define VDI_SET_PID_BIT_MASK 0x0010 ///< VDI enable PID bit mask #define VDI_SET_BYPASS_BIT_MASK 0x0020 ///< VDI enable bypass bit mask #define VDI_RESET_CONTROL_BIT_MASK 0x0FCF ///< VDI reset control bit mask #define VDI_INIT_STATUS_BIT_MASK 0x0040 ///< VDI init status bit mask #define VDI_ENABLE_PID_STATUS_BIT_MASK 0x0080 ///< VDI PID status bit mask #define VDI_ENABLE_BYPASS_STATUS_BIT_MASK 0x0100 ///< VDI bypass status bit mask // Valve dialyzer outlet defines #define VDO_SET_PID_BIT_MASK 0x0040 ///< VDO enable PID interface #define VDO_SET_BYPASS_BIT_MASK 0x0080 ///< VDO enable bypass bit mask #define VDO_RESET_CONTROL_BIT_MASK 0x09FF ///< VDO reset control bit mask #define VDO_INIT_STATUS_BIT_MASK 0x0200 ///< VDO init status bit mask #define VDO_ENABLE_PID_STATUS_BIT_MASK 0x0400 ///< VDO enable PID status bit mask #define VDO_ENABLE_BYPASS_STATUS_BIT_MASK 0x0800 ///< VDO enable bypass status bit mask // Valve blood arterial defines #define VBA_SET_PID_BIT_MASK 0x0001 ///< VBA enable PID interface #define VBA_SET_BYPASS_BIT_MASK 0x0002 ///< VBA enable bypass interface #define VBA_RESET_CONTROL_BIT_MASK 0x0FFC ///< VBA reset control bit mask #define VBA_INIT_STATUS_BIT_MASK 0x0001 ///< VBA init status bit mask #define VBA_ENABLE_PID_STATUS_BIT_MASK 0x0002 ///< VBA PID status bit mask #define VBA_ENABLE_BYPASS_STATUS_BIT_MASK 0x0004 ///< VBA bypass status bit mask // Valve blood venous defines #define VBV_SET_PID_BIT_MASK 0x0004 ///< VBV enable PID interface bit mask #define VBV_SET_BYPASS_BIT_MASK 0x0008 ///< VBV enable bypass interface bit mask #define VBV_RESET_CONTROL_BIT_MASK 0x0FF3 ///< VBV reset control bit mask #define VBV_INIT_STATUS_BIT_MASK 0x0008 ///< VBV init status bit mask #define VBV_ENABLE_PID_STATUS_BIT_MASK 0x0010 ///< VBV PID status bit mask #define VBV_ENABLE_BYPASS_STATUS_BIT_MASK 0x0020 ///< VBV bypass status bit mask #define ENABLE_VALVE_CURRENT_RELAXATION 0x8000 ///< Enable valves current relaxation #define DISABLE_VALVE_CURRENT_RELAXATION 0x7FFF ///< Disable valves current relaxation #define ADC_TO_CURRENT_CONVERSION_CONSTANT 2048.0F ///< Valves ADC count to current conversion #define INITIAL_EDGE_OFFSET_READ_COUNT 100U ///< Offset in counts from energized and de-energized edges #define INITIAL_ENERGIZED_EDGE_UPPER_RANGE 13250U ///< Energized edge maximum count #define INITIAL_ENERGIZED_EDGE_LOWER_RANGE 10750U ///< Energized edge minimum count #define INITIAL_V3_ENERGIZED_EDGE_UPPER_RANGE 14000U ///< Energized edge maximum count #define INITIAL_V3_ENERGIZED_EDGE_LOWER_RANGE 10750U ///< Energized edge minimum count #define AIR_TRAP_VALVE_GPIO_PIN 0x12 ///< Air trap valve GPIO Pin 18 of HET Port 1 #define HOMING_STEP_CHANGE_IN_COUNTS 1000 ///< Step change in counts during homing #define TRANSITION_STEP_CHANGE_IN_COUNTS 15000 ///< Step change in counts during normal operations #define MAX_DEVIATION_FROM_TARGET_IN_COUNTS 1000 ///< Maximum deviation from target in counts #define MAX_DEVIATION_FROM_TGT_IN_TRAVEL_CNTS 150 ///< Maximum deviation from target in counts #define MAX_DEVIATION_FROM_TRAGET_IN_POS_B 1000 ///< Maximum allowed deviation from position B in counts #define MAX_ALLOWED_FAILED_HOMINGS 3U ///< Maximum allowed failed homings /// The time that the valve must be at the edge to be considered for edge detection static const U32 HOMING_EDGE_DETECTION_TIME_INTERVAL = ( MS_PER_SECOND / ( 2 * TASK_PRIORITY_INTERVAL ) ); #define VALVES_CURRENT_THRESHOLD_AMPS 1.0F ///< Valves current threshold /// Valves over current time interval counter static const U32 MAX_OVER_CURRENT_TIME_INTERVAL_COUNTER = ( MS_PER_SECOND / ( TASK_PRIORITY_INTERVAL ) ); /// Valves out of range time interval counter static const U32 MAX_POS_DEVIATION_TIME_INTERVAL_COUNTER = ( MS_PER_SECOND / ( TASK_PRIORITY_INTERVAL ) ); #define VALVE_TRANSITION_TIMEOUT_MS 3000U ///< Valves transition time out in ms /// Valve data publication time interval #define VALVES_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) #define VALVE_MAX_ALLOWED_PWM_PERCENT 100U ///< Valve maximum allowed PWM in percent #define VALVE_CW_PWM_TO_CNT_CONVERSION( pwm ) ( ( 20 * pwm ) + 2500 ) ///< Valve clockwise PWM to count conversion #define VALVE_CW_CNT_TO_PWM_CONVERSION( cnt ) ( ( 0.05F * cnt ) - 125 ) ///< Valve clockwise count to PWM conversion #define VALVE_CCW_PWM_TO_CNT_CONVERSION( pwm ) ( ( -20 * pwm ) + 2500 ) ///< Valve counter clockwise PWM to count conversion #define VALVE_CCW_CNT_TO_PWM_CONVERSION( cnt ) ( ( -0.05F * cnt ) + 125 ) ///< Valve counter clockwise count to PWM conversion #define DATA_PUBLISH_COUNTER_START_COUNT 80 ///< Data publish counter start count. /// Exec valve self test states typedef enum valves_Self_Test_States { VALVE_SELF_TEST_ENABLE_VALVES = 0, ///< Valve self test enable valves VALVE_SELF_TEST_CONFIRM_ENABLE, ///< Valve self test confirm enable VALVE_SELF_TEST_COMPLETE, ///< Valve self test complete NUM_OF_VALVE_SELF_TEST_STATES ///< Number of valve self test states } VALVE_SELF_TEST_STATE_T; /// Exec valve states typedef enum valve_Exec_States { VALVE_STATE_WAIT_FOR_POST = 0, ///< Valve state wait for POST VALVE_STATE_HOMING_NOT_STARTED, ///< Valve state homing not started VALVE_STATE_HOMING_FIND_ENERGIZED_EDGE, ///< Valve state homing find energized edge VALVE_STATE_HOMING_FIND_DEENERGIZED_EDGE, ///< Valve state homing find de-energized edge VALVE_STATE_IDLE, ///< Valve state idle VALVE_STATE_IN_TRANSITION, ///< Valve state in transition VALVE_STATE_IN_BYPASS_MODE, ///< Valve state in bypass mode (Fixed PWM control) NUM_OF_VALVE_STATES, ///< Number of valve exec states } VALVE_STATE_T; /// Valve modes typedef enum valve_modes { VALVE_CONTROL_MODE_ENABLE_PID = 0, ///< Valve control mode enable PID VALVE_CONTROL_MODE_ENABLE_BYPASS, ///< Valve control mode enable bypass VALVE_CONTROL_MODE_DISABLE_ALL, ///< Valve control mode disable all modes NUM_OF_VALVE_CONTROL_MODES, ///< Number of valve control modes } VALVE_MODE_T; /// Valve control status enum valve_control_status { VALVE_CONTROL_STATUS_INITIALIZED = 0, ///< Valve control status initialized (PID control is initialized) VALVE_CONTROL_STATUS_PID_ENABLED, ///< Valve control status PID enabled VALVE_CONTROL_STATUS_BYPASS_ENABLED, ///< Valve control status bypass enabled VALVE_CONTROL_STATUS_DISABLED, ///< Valve control status all disabled NUM_OF_VALVE_CONTROL_STATUS ///< Number of valve control status }; /// Valves bypass mode direction typedef enum valve_bypass_mode_direction { VAVLE_DIR_CLOCKWISE = 0, ///< Clockwise direction in bypass mode to moves the valves towards energized edge VALVE_DIR_COUNTERCLOCKWISE, ///< Counter clockwise direction in bypass mode to move the valves towards de-energized edge NUM_OF_VALVE_DIRECTION ///< Number of valve directions } VALVE_DIRECTION_T; /// Valve bypass (fixed PWM) structure typedef struct { BOOL hasBypassModeBeenRequeseted; ///< Valve bypass mode request flag BOOL hasChangeBeenRequested; ///< Valve bypass mode change of direction or PWM flag U16 currentPWMInPercent; ///< Valve current PWM in percent U16 commandedPWMInPercent; ///< Valve commanded PWM in percent VALVE_DIRECTION_T direction; ///< Valve move direction (CW or CCW) } VALVE_BYPASS_T; /// Valve status structure typedef struct { VALVE_POSITION_T commandedPosition; ///< Valve commanded position enum VALVE_POSITION_T currentPosition; ///< Valve current position enum VALVE_POSITION_T pendingCommandedPosition; ///< Valve pending position enum OVERRIDE_S32_T currentPositionInCounts; ///< Valve current position in counts S16 targetPositionInCounts; ///< Valve target position in counts BOOL hasTransitionBeenRequested; ///< Valve transition request flag VALVE_STATE_T execState; ///< Valve execution state U32 transitionStartTime; ///< Valve transition start time S16 positions[ NUM_OF_VALVE_POSITIONS ]; ///< Valve positions array (Pos A, Pos B, Pos C) VALVE_MODE_T controlMode; ///< Valve control mode (PID, bypass, disable) OVERRIDE_F32_T current; ///< Valve current U32 overCurrentCounter; ///< Valve over current counter U32 positionOutOfRangeCounter; ///< Valve position out of range counter U32 dataPublishCounter; ///< Valve data publish counter // Homing variables U32 numberOfFailedHomings; ///< Valve number of failed homing U32 homingEdgeDetectionCounter; ///< Valve homing counter when there is not much travel BOOL hasHomingBeenRequested; ///< Valve homing request flag BOOL hasValveBeenHomed; ///< Valve homing completed flag BOOL hasHomingFailed; ///< Valve homing failed flag // Bypass (fixed PWM) mode VALVE_BYPASS_T bypassModeStatus; ///< Valve bypass (fixed PWM) mode } VALVE_STATUS_T; // ********** private data ********** static VALVE_SELF_TEST_STATE_T valveSelfTestState; ///< Valves self test state static SELF_TEST_STATUS_T valvesSelfTestResult; ///< Valves self test result static VALVE_STATUS_T valvesStatus[ NUM_OF_VALVES ]; ///< Array of type VALVE_STATUS_T structure that holds the status of each valve static OVERRIDE_U32_T valvesDataPublishInterval = { VALVES_DATA_PUB_INTERVAL, VALVES_DATA_PUB_INTERVAL, 0, 0 }; ///< Valves data publish interval static OVERRIDE_U32_T valvesPositionOverride[ NUM_OF_VALVES ] = { VALVE_POSITION_C_CLOSE, VALVE_POSITION_C_CLOSE, 0, 0 }; ///< Valves position override static const U16 VALVE_CONTROL_MODES_SET_BITS[ NUM_OF_VALVES ][ NUM_OF_VALVE_CONTROL_MODES ] = { { VDI_SET_PID_BIT_MASK, VDI_SET_BYPASS_BIT_MASK, VDI_RESET_CONTROL_BIT_MASK }, { VDO_SET_PID_BIT_MASK, VDO_SET_BYPASS_BIT_MASK, VDO_RESET_CONTROL_BIT_MASK }, { VBA_SET_PID_BIT_MASK, VBA_SET_BYPASS_BIT_MASK, VBA_RESET_CONTROL_BIT_MASK }, { VBV_SET_PID_BIT_MASK, VBV_SET_BYPASS_BIT_MASK, VBV_RESET_CONTROL_BIT_MASK } }; ///< Valves control mode set bits static const U16 VALVE_CONTROL_MODES_RESET_BITS[ NUM_OF_VALVES ] = { VDI_RESET_CONTROL_BIT_MASK, VDO_RESET_CONTROL_BIT_MASK, VBA_RESET_CONTROL_BIT_MASK, VBV_RESET_CONTROL_BIT_MASK }; ///< Valves control modes rest bits /// Valve check entries enum valve_check_entries { VALVE_CHECK_EXPECTED_CONTROL = 0, ///< Valve check expected control values VALVE_CHECK_BIT_MASK, ///< Valve check bit mask NUM_OF_VALVE_CHECK_ENTRIES, ///< Number of valve check entries }; #define VALVE_INIT_STATUS_BIT 0x0001 ///< valve init #define VALVE_ENABLE_PID_STATUS_BIT 0x0002 ///< valve PID enabled #define VALVE_ENABLE_BYPASS_STATUS_BIT 0x0004 ///< valve bypass #define VALVE_PID_CHECK ( VALVE_INIT_STATUS_BIT | VALVE_ENABLE_PID_STATUS_BIT ) #define VALVE_CHECK_MASK ( VALVE_INIT_STATUS_BIT | VALVE_ENABLE_PID_STATUS_BIT | VALVE_ENABLE_BYPASS_STATUS_BIT ) #define VALVE_CHECK_BITS_PER_VALVE ( 3 ) #define VDI_POS ( 2 ) #define VDO_POS ( 3 ) #define VBA_POS ( 0 ) #define VBV_POS ( 1 ) #define VDI_VALVE_CHECK ( VALVE_PID_CHECK << ( VDI_POS * VALVE_CHECK_BITS_PER_VALVE ) ) #define VDO_VALVE_CHECK ( VALVE_PID_CHECK << ( VDO_POS * VALVE_CHECK_BITS_PER_VALVE ) ) #define VBA_VALVE_CHECK ( VALVE_PID_CHECK << ( VBA_POS * VALVE_CHECK_BITS_PER_VALVE ) ) #define VBV_VALVE_CHECK ( VALVE_PID_CHECK << ( VBV_POS * VALVE_CHECK_BITS_PER_VALVE ) ) #define VDI_VALVE_CHECK_MASK ( ( VALVE_CHECK_MASK << ( VDI_POS * VALVE_CHECK_BITS_PER_VALVE ) ) ) #define VDO_VALVE_CHECK_MASK ( ( VALVE_CHECK_MASK << ( VDO_POS * VALVE_CHECK_BITS_PER_VALVE ) ) ) #define VBA_VALVE_CHECK_MASK ( ( VALVE_CHECK_MASK << ( VBA_POS * VALVE_CHECK_BITS_PER_VALVE ) ) ) #define VBV_VALVE_CHECK_MASK ( ( VALVE_CHECK_MASK << ( VBV_POS * VALVE_CHECK_BITS_PER_VALVE ) ) ) static const U16 VALVE_CHECK[ NUM_OF_VALVES ][ NUM_OF_VALVE_CHECK_ENTRIES ] = { { VDI_VALVE_CHECK, VDI_VALVE_CHECK_MASK }, { VDO_VALVE_CHECK, VDO_VALVE_CHECK_MASK }, { VBA_VALVE_CHECK, VBA_VALVE_CHECK_MASK }, { VBV_VALVE_CHECK, VBV_VALVE_CHECK_MASK } }; ///< Valve check entries static U16 valvesControlSetBits; ///< Valves control set bit static OPN_CLS_STATE_T valveAirTrapStatus; ///< Air trap valve status (open/close) static HD_VALVES_CAL_RECORD_T valvesCalibrationRecord; ///< Valves calibration record. // Self test function prototypes static VALVE_SELF_TEST_STATE_T handleValveSelfTestEnableValves( void ); static VALVE_SELF_TEST_STATE_T handleValveSelfTestConfirmEnable( void ); // Exec function prototypes static VALVE_STATE_T handleValveStateWaitForPost( VALVE_T valve ); static VALVE_STATE_T handleValveStateHomingNotStarted( VALVE_T valve ); static VALVE_STATE_T handleValveStateHomingFindEnergizedEdge( VALVE_T valve ); static VALVE_STATE_T handleValveStateHomingFindDeenergizedEdge( VALVE_T valve ); static VALVE_STATE_T handleValveStateIdle( VALVE_T valve ); static VALVE_STATE_T handleValveStateInTransition( VALVE_T valve ); #ifdef DEBUG_ENABLED static VALVE_STATE_T handleValveStateInBypassMode( VALVE_T valve ); #endif // Private function prototypes static void setValveControlMode( VALVE_T valve, VALVE_MODE_T mode ); static void execMonitorValves( void ); static BOOL areValvesFunctional( void ); static void setFPGAValveSetPoint( VALVE_T valve, S16 position, BOOL enableCurrentRelaxation ); static void convertAndMonitorValvesCurrent( void ); static void getAndMonitorValvesCurrentFPGAPosition( void ); static void publishValvesData( VALVE_T valve ); static void setValveNextStep( VALVE_T valve, U32 stepChange ); static S16 getValvePositionCounts( VALVE_T valve ); static BOOL isValveCloseToCommandedPosition( VALVE_T valve, VALVE_POSITION_T positionToCheck ); // These functions will be used in debug only mode #ifdef DEBUG_ENABLED static void setFPGAValvePWM( VALVE_T valve ); static void getValvesCurrentPWM( void ); #endif /*********************************************************************//** * @brief * The initValves function initializes the valves driver. * @details Inputs: none * @details Outputs: valveSelfTestState, valvesSelfTestResult, valvesStatus * @return none *************************************************************************/ void initValves( void ) { VALVE_T valve; valveSelfTestState = VALVE_SELF_TEST_ENABLE_VALVES; valvesSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; valveAirTrapStatus = STATE_CLOSED; // Set the valves bit control to 0 valvesControlSetBits = 0x0000; setFPGAValvesControlMode( valvesControlSetBits ); // Set the air trap valve to be in closed state setValveAirTrap( STATE_CLOSED ); // Initialize some of the variables for ( valve = VDI; valve < NUM_OF_VALVES; valve++ ) { valvesStatus[ valve ].execState = VALVE_STATE_WAIT_FOR_POST; valvesStatus[ valve ].current.data = 0.0F; valvesStatus[ valve ].current.ovData = 0.0F; valvesStatus[ valve ].current.ovInitData = 0.0F; valvesStatus[ valve ].current.override = 0; valvesStatus[ valve ].dataPublishCounter = DATA_PUBLISH_COUNTER_START_COUNT; } // Close air trap valve setValveAirTrap( STATE_CLOSED ); } /*********************************************************************//** * @brief * The homeValve function processes the homing of a valve * @details Inputs: valvesStatus * @details Outputs: valvesStatus * @param valve that is set to be homed * @param force flag indicating whether homing should be forced even if already homed * @param cartridge flag indicating whether a cartridge may be present requiring a more lax homing * @return TRUE if the homing command accepted otherwise, FALSE *************************************************************************/ BOOL homeValve( VALVE_T valve, BOOL force, BOOL cartridge ) { BOOL result = FALSE; if ( valve < NUM_OF_VALVES ) { // If haven't already homed the valves, home the valves if ( ( valvesStatus[ valve ].hasValveBeenHomed != TRUE ) || ( VALVE_FORCE_HOME == force ) ) { valvesStatus[ valve ].hasHomingBeenRequested = TRUE; } // Otherwise, go to position A (home position) else { setValvePosition( valve, VALVE_POSITION_A_INSERT_EJECT ); } result = TRUE; } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_VALVES_INVALID_VALVE_SELECTED, (U32)valve ); } return result; } /*********************************************************************//** * @brief * The setValvePosition function sets the requested position of a valve. * @details Inputs: none * @details Outputs: valvesStatus * @param valve that its position is set * @param position that the valve is set * @return returns TRUE if the requested position was legal *************************************************************************/ BOOL setValvePosition( VALVE_T valve, VALVE_POSITION_T position ) { BOOL result = FALSE; // Valve not in position cannot be requested // All the other positions are valid if ( ( position != VALVE_POSITION_NOT_IN_POSITION ) && ( valve < NUM_OF_VALVES ) ) { valvesStatus[ valve ].pendingCommandedPosition = position; valvesStatus[ valve ].hasTransitionBeenRequested = TRUE; result = TRUE; } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_VALVES_INVALID_VALVE_SELECTED, (U32)valve ); } return result; } /*********************************************************************//** * @brief * The getValvePosition function returns the current position of a valve. * @details Inputs: none * @details Outputs: valvesStatus * @param valve that the position is requested * @return returns the current position of the valve in enum *************************************************************************/ VALVE_POSITION_T getValvePosition( VALVE_T valve ) { VALVE_POSITION_T position = VALVE_POSITION_NOT_IN_POSITION; if ( valve < NUM_OF_VALVES ) { position = valvesStatus[ valve ].currentPosition; } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_VALVES_INVALID_VALVE_SELECTED, (U32)valve ); } return position; } /*********************************************************************//** * @brief * The getValveCurrent function returns the current of a valve. * @details Inputs: valvesStatus * @details Outputs: valvesStatus * @param valve that the current is requested * @return returns the current of the valve in float *************************************************************************/ F32 getValveCurrent( VALVE_T valve ) { F32 current = valvesStatus[ valve ].current.data; if ( OVERRIDE_KEY == valvesStatus[ valve ].current.override ) { current = valvesStatus[ valve ].current.ovData; } return current; } /*********************************************************************//** * @brief * The setValveBloodTrap function set the blood trap valve to open or close * position. * @details Inputs: none * @details Outputs: valveAirTrapStatus * @param state enumeration of open or close for valve * @return: none *************************************************************************/ void setValveAirTrap( OPN_CLS_STATE_T state ) { BOOL energized = FALSE; // Check if the proper air trap state was requested if ( state < NUM_OF_OPN_CLS_STATES ) { if ( STATE_OPEN == state ) { energized = TRUE; } } // Of course if invalid air trap state was requested, keep the valve de-energized (close) else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_VALVES_INVALID_AIR_TRAP_REQUEST, state ); } valveAirTrapStatus = state; gioSetBit( hetPORT1, AIR_TRAP_VALVE_GPIO_PIN, energized ); } /*********************************************************************//** * @brief * The getValveAirTrapStatus function returns the status of air trap valve * which is either open or close * @details Inputs: valveAirTrapStatus * @details Outputs: none * @return: valveAirTrapStatus the status of air trap valve (open or close) *************************************************************************/ OPN_CLS_STATE_T getValveAirTrapStatus( void ) { return valveAirTrapStatus; } /*********************************************************************//** * @brief * The execValvesSelfTest function executes the valves self test. * @details Inputs: valveSelfTestState * @details Outputs: valveSelfTestState * @return valvesSelfTestResult The status of the self test *************************************************************************/ SELF_TEST_STATUS_T execValvesSelfTest( void ) { switch( valveSelfTestState ) { case VALVE_SELF_TEST_ENABLE_VALVES: valveSelfTestState = handleValveSelfTestEnableValves(); break; case VALVE_SELF_TEST_CONFIRM_ENABLE: valveSelfTestState = handleValveSelfTestConfirmEnable(); break; case VALVE_SELF_TEST_COMPLETE: // Do nothing POST completed break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_VALVES_INVALID_SELF_TEST_STATE, valveSelfTestState ); valveSelfTestState = VALVE_SELF_TEST_COMPLETE; } return valvesSelfTestResult; } /*********************************************************************//** * @brief * The resetValvesPOSTState function resets the valves POST state; * @details Inputs: none * @details Outputs: valveSelfTestState, valvesSelfTestResult * @return none *************************************************************************/ void resetValvesPOSTState( void ) { valveSelfTestState = VALVE_SELF_TEST_ENABLE_VALVES; valvesSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; } /*********************************************************************//** * @brief * The execValves function executes the valves exec state machine. * @details Inputs: valvesStatus * @details Outputs: valvesStatus * @return none *************************************************************************/ void execValves( void ) { VALVE_T valve; // Monitoring should be done once POST is complete and the valves are // Set correctly if ( valveSelfTestState == VALVE_SELF_TEST_COMPLETE ) { execMonitorValves(); } // Loop through all of the valves for ( valve = VDI; valve < NUM_OF_VALVES; valve++ ) { switch ( valvesStatus[ valve ].execState ) { case VALVE_STATE_WAIT_FOR_POST: valvesStatus[ valve ].execState = handleValveStateWaitForPost( valve ); break; case VALVE_STATE_HOMING_NOT_STARTED: valvesStatus[ valve ].execState = handleValveStateHomingNotStarted( valve ); break; case VALVE_STATE_HOMING_FIND_ENERGIZED_EDGE: valvesStatus[ valve ].execState = handleValveStateHomingFindEnergizedEdge( valve ); break; case VALVE_STATE_HOMING_FIND_DEENERGIZED_EDGE: valvesStatus[ valve ].execState = handleValveStateHomingFindDeenergizedEdge( valve ); break; case VALVE_STATE_IDLE: valvesStatus[ valve ].execState = handleValveStateIdle( valve ); break; case VALVE_STATE_IN_TRANSITION: valvesStatus[ valve ].execState = handleValveStateInTransition( valve ); break; // PWM mode only used in debug mode #ifdef DEBUG_ENABLED case VALVE_STATE_IN_BYPASS_MODE: valvesStatus[ valve ].execState = handleValveStateInBypassMode( valve ); break; #endif #ifndef _VECTORCAST_ // The default cannot be reached in VectorCAST since the cases are run in a for loop default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_VALVES_INVALID_EXEC_STATE, valvesStatus[ valve ].execState ); valvesStatus[ valve ].execState = VALVE_STATE_IDLE; break; #endif } publishValvesData( valve ); } } /*********************************************************************//** * @brief * The handleValveSelfTestEnableValves function starts the valves self test. * @details Inputs: none * @details Outputs: valvesStatus, valvesSelfTestResult * @return next state of the self test state machine *************************************************************************/ static VALVE_SELF_TEST_STATE_T handleValveSelfTestEnableValves( void ) { VALVE_T valve; VALVE_SELF_TEST_STATE_T state = VALVE_SELF_TEST_CONFIRM_ENABLE; valvesSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; // Set all the valves to be in PID control mode for ( valve = VDI; valve < NUM_OF_VALVES; valve++ ) { setValveControlMode( valve, VALVE_CONTROL_MODE_ENABLE_PID ); // Set the control mode of each valve to be PID mode valvesStatus[ valve ].controlMode = VALVE_CONTROL_MODE_ENABLE_PID; } return state; } /*********************************************************************//** * @brief * The handleValveSelfTestConfirmEnable function checks to make sure the * valves are enabled properly. * @details Inputs: none * @details Outputs: valvesSelfTestResult * @return next state of the self test state machine *************************************************************************/ static VALVE_SELF_TEST_STATE_T handleValveSelfTestConfirmEnable( void ) { VALVE_SELF_TEST_STATE_T state = VALVE_SELF_TEST_COMPLETE; BOOL calStatus = getNVRecord2Driver( GET_CAL_VALVES, (U08*)&valvesCalibrationRecord, sizeof( HD_VALVES_CAL_RECORD_T ), NUM_OF_CAL_DATA_HD_VALVES, ALARM_ID_NO_ALARM ); if ( ( TRUE == areValvesFunctional() ) && ( TRUE == calStatus ) ) { valvesSelfTestResult = SELF_TEST_STATUS_PASSED; } else { valvesSelfTestResult = SELF_TEST_STATUS_FAILED; } return state; } /*********************************************************************//** * @brief * The handleValveStateWaitForPost function handles the wait for POST state. * @details Inputs: valveSelfTestState * @details Outputs: none * @param valve that is waiting for POST to complete * @return next state of exec state *************************************************************************/ static VALVE_STATE_T handleValveStateWaitForPost( VALVE_T valve ) { VALVE_STATE_T state = VALVE_STATE_WAIT_FOR_POST; if ( valveSelfTestState == VALVE_SELF_TEST_COMPLETE ) { state = VALVE_STATE_HOMING_NOT_STARTED; } return state; } /*********************************************************************//** * @brief * The handleValveStateHomingNotStarted function handles homing not started * state of the state machine. * @details Inputs: valvesStatus, valveSelfTestState, isDoorClosed * @details Outputs: valvesStatus * @param valve that is set to be homed * @return next state of the exec state machine *************************************************************************/ static VALVE_STATE_T handleValveStateHomingNotStarted( VALVE_T valve ) { VALVE_STATE_T state = VALVE_STATE_HOMING_NOT_STARTED; OPN_CLS_STATE_T frontDoor = getSwitchStatus( FRONT_DOOR ); // If homing has been requested or POST is completed and the door has been close for the specified // Period of time, start the homing process if ( ( VALVE_SELF_TEST_COMPLETE == valveSelfTestState ) && ( ( TRUE == valvesStatus[ valve ].hasHomingBeenRequested ) || ( STATE_CLOSED == frontDoor ) ) && ( FALSE == valvesStatus[ valve ].hasHomingFailed ) ) { // Get ready for the energized state valvesStatus[ valve ].homingEdgeDetectionCounter = 0; valvesStatus[ valve ].targetPositionInCounts = getValvePositionCounts( valve ) + HOMING_STEP_CHANGE_IN_COUNTS; valvesStatus[ valve ].hasValveBeenHomed = FALSE; valvesStatus[ valve ].currentPosition = VALVE_POSITION_NOT_IN_POSITION; setFPGAValveSetPoint( valve, valvesStatus[ valve ].targetPositionInCounts, FALSE ); state = VALVE_STATE_HOMING_FIND_ENERGIZED_EDGE; } return state; } /*********************************************************************//** * @brief * The handleValveStateHomingFindEnergizedEdge function handles find * energized state of the state machine. * @details Inputs: valvesStatus * @details Outputs: valvesStatus * @param valve that its energized edge is found * @return next state of the exec state machine *************************************************************************/ static VALVE_STATE_T handleValveStateHomingFindEnergizedEdge( VALVE_T valve ) { VALVE_STATE_T state = VALVE_STATE_HOMING_FIND_ENERGIZED_EDGE; S16 currentPosition = getValvePositionCounts( valve ); S16 targetPosition = valvesStatus[ valve ].targetPositionInCounts; S16 deltaPosition = targetPosition - currentPosition; // If there has not been any major travel for a certain period of time if ( deltaPosition > MAX_DEVIATION_FROM_TGT_IN_TRAVEL_CNTS ) { if ( valvesStatus[ valve ].homingEdgeDetectionCounter >= HOMING_EDGE_DETECTION_TIME_INTERVAL ) { // Current position (positive or negative) will be stored in the Position B of the current valve valvesStatus[ valve ].positions[ VALVE_POSITION_B_OPEN ] = currentPosition; valvesStatus[ valve ].homingEdgeDetectionCounter = 0; valvesStatus[ valve ].targetPositionInCounts = currentPosition - HOMING_STEP_CHANGE_IN_COUNTS; setFPGAValveSetPoint( valve, valvesStatus[ valve ].targetPositionInCounts, FALSE ); state = VALVE_STATE_HOMING_FIND_DEENERGIZED_EDGE; } else { valvesStatus[ valve ].homingEdgeDetectionCounter++; } } // Reached to target, go for the next target else { valvesStatus[ valve ].homingEdgeDetectionCounter = 0; valvesStatus[ valve ].targetPositionInCounts = currentPosition + HOMING_STEP_CHANGE_IN_COUNTS; setFPGAValveSetPoint( valve, valvesStatus[ valve ].targetPositionInCounts, FALSE ); } return state; } /*********************************************************************//** * @brief * The handleValveStateHomingFindDeenergizedEdge function handles find * de-energized state of the state machine. * @details Inputs: valvesStatus * @details Outputs: valvesStatus * @param valve that its de-energized edge is found * @return next state of the exec state machine *************************************************************************/ static VALVE_STATE_T handleValveStateHomingFindDeenergizedEdge( VALVE_T valve ) { VALVE_STATE_T state = VALVE_STATE_HOMING_FIND_DEENERGIZED_EDGE; S16 currentPosition = getValvePositionCounts( valve ); S16 targetPosition = valvesStatus[ valve ].targetPositionInCounts; S16 deltaPosition = currentPosition - targetPosition; U32 energizedUpperEdge = INITIAL_ENERGIZED_EDGE_UPPER_RANGE; U32 deenergizedLowerEdge = INITIAL_ENERGIZED_EDGE_LOWER_RANGE; #ifndef _RELEASE_ if ( HW_CONFIG_BETA == getHardwareConfigStatus() ) { energizedUpperEdge = INITIAL_V3_ENERGIZED_EDGE_UPPER_RANGE; deenergizedLowerEdge = INITIAL_V3_ENERGIZED_EDGE_LOWER_RANGE; } #endif // Still too far from intermediate target position? Either still more travel to go or stuck at end of travel if ( deltaPosition > MAX_DEVIATION_FROM_TGT_IN_TRAVEL_CNTS ) { // Are we stuck at end of travel? if ( valvesStatus[ valve ].homingEdgeDetectionCounter >= HOMING_EDGE_DETECTION_TIME_INTERVAL ) { S16 energizedEdge = valvesStatus[ valve ].positions[ VALVE_POSITION_B_OPEN ]; S16 deltaEdges = energizedEdge - currentPosition; // The range from energized to de-energized should be in the specified range if ( ( deltaEdges <= energizedUpperEdge ) && ( deltaEdges >= deenergizedLowerEdge ) ) { S16 positionC = currentPosition + INITIAL_EDGE_OFFSET_READ_COUNT; S16 positionB = valvesStatus[ valve ].positions[ VALVE_POSITION_B_OPEN ]; // Positions B and C will have an offset from the edge to make sure each time the valve will not hit the edge valvesStatus[ valve ].positions[ VALVE_POSITION_B_OPEN ] = positionB; valvesStatus[ valve ].positions[ VALVE_POSITION_C_CLOSE ] = positionC; // Position A is the average of the Position B that was read last time and position C that was the target of this state valvesStatus[ valve ].positions[ VALVE_POSITION_A_INSERT_EJECT ] = positionC + ( ( positionB - positionC ) / 2 ); #ifndef _RELEASE_ if ( ( getSoftwareConfigStatus( SW_CONFIG_ENABLE_VBA_SPECIAL_POSITION_C ) == SW_CONFIG_ENABLE_VALUE ) && ( VBA == valve ) ) { valvesStatus[ valve ].positions[ VALVE_POSITION_C_CLOSE ] = currentPosition + 3000; } #endif // Set the current position to Position C and the commanded position to Position A valvesStatus[ valve ].hasValveBeenHomed = TRUE; valvesStatus[ valve ].currentPosition = VALVE_POSITION_C_CLOSE; valvesStatus[ valve ].pendingCommandedPosition = VALVE_POSITION_A_INSERT_EJECT; valvesStatus[ valve ].hasTransitionBeenRequested = TRUE; valvesStatus[ valve ].hasHomingBeenRequested = FALSE; valvesStatus[ valve ].numberOfFailedHomings = 0; // Idle will initiate a transition state = VALVE_STATE_IDLE; } // Max number of failed homing. Fault and go back to homing not started else if ( ++valvesStatus[ valve ].numberOfFailedHomings >= MAX_ALLOWED_FAILED_HOMINGS ) { valvesStatus[ valve ].hasValveBeenHomed = FALSE; valvesStatus[ valve ].hasHomingFailed = TRUE; state = VALVE_STATE_HOMING_NOT_STARTED; SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_VALVE_HOMING_FAILED, (U32)valve ); } else { state = VALVE_STATE_HOMING_NOT_STARTED; } } // The time has not elapsed, increment it else { valvesStatus[ valve ].homingEdgeDetectionCounter++; } } // Reached to target, go for the next target else { valvesStatus[ valve ].homingEdgeDetectionCounter = 0; valvesStatus[ valve ].targetPositionInCounts = currentPosition - HOMING_STEP_CHANGE_IN_COUNTS; setFPGAValveSetPoint( valve, valvesStatus[ valve ].targetPositionInCounts, FALSE ); } return state; } /*********************************************************************//** * @brief * The handleValveStateIdle function handles the idle state of the valves * state machine. * @details Inputs: valvesStatus * @details Outputs: valvesStatus * @param valve that is in idle state * @return next state of the exec state machine *************************************************************************/ static VALVE_STATE_T handleValveStateIdle( VALVE_T valve ) { VALVE_STATE_T state = VALVE_STATE_IDLE; if ( TRUE == valvesStatus[ valve ].hasHomingBeenRequested ) { state = VALVE_STATE_HOMING_NOT_STARTED; } else if ( ( TRUE == valvesStatus[ valve ].hasValveBeenHomed ) && ( TRUE == valvesStatus[ valve ].hasTransitionBeenRequested ) ) { if ( ( valvesStatus[ valve ].currentPosition != valvesStatus[ valve ].pendingCommandedPosition ) || ( FALSE == isValveCloseToCommandedPosition( valve, valvesStatus[ valve ].pendingCommandedPosition ) ) ) { // Just set the valves to transition so it will not be in a known position for a while valvesStatus[ valve ].commandedPosition = valvesStatus[ valve ].pendingCommandedPosition; valvesStatus[ valve ].transitionStartTime = getMSTimerCount(); setValveNextStep( valve, TRANSITION_STEP_CHANGE_IN_COUNTS ); state = VALVE_STATE_IN_TRANSITION; } valvesStatus[ valve ].hasTransitionBeenRequested = FALSE; } // This option is only available in a debug build #ifdef DEBUG_ENABLED // Check if the valves have been homed and a bypass mode has been requested else if ( ( TRUE == valvesStatus[ valve ].hasValveBeenHomed ) && ( TRUE == valvesStatus[ valve ].bypassModeStatus.hasBypassModeBeenRequeseted ) ) { // Set the valve control mode to bypass and set the PWM setValveControlMode( valve, VALVE_CONTROL_MODE_ENABLE_BYPASS ); valvesStatus[ valve ].controlMode = VALVE_CONTROL_MODE_ENABLE_BYPASS; valvesStatus[ valve ].bypassModeStatus.hasChangeBeenRequested = FALSE; setFPGAValvePWM( valve ); state = VALVE_STATE_IN_BYPASS_MODE; } #endif return state; } /*********************************************************************//** * @brief * The handleValveStateInTransition function handles the in transition * state of the state machine. * @details Inputs: valvesStatus * @details Outputs: valvesStatus * @param valve that is in position transition * @return next state of the exec state machine *************************************************************************/ static VALVE_STATE_T handleValveStateInTransition( VALVE_T valve ) { VALVE_STATE_T state = VALVE_STATE_IN_TRANSITION; VALVE_POSITION_T commandedPositionEnum = valvesStatus[ valve ].commandedPosition; // Get the corresponding counts of the positions S16 commandedPosition = valvesStatus[ valve ].positions[ commandedPositionEnum ]; S16 currentPosition = getValvePositionCounts( valve ); S16 targetPosition = valvesStatus[ valve ].targetPositionInCounts; // Check if the valve is within range of the final commanded position. // NOTE: This is checked to the final position and not the temporary target en route to the final destination if ( abs( currentPosition - commandedPosition ) < MAX_DEVIATION_FROM_TARGET_IN_COUNTS ) { // The valve is in range. Set the current position to the commanded position valvesStatus[ valve ].currentPosition = commandedPositionEnum; // If we are in Pos B, enable current relaxation if ( commandedPositionEnum == VALVE_POSITION_B_OPEN ) { // Enable current relaxation setFPGAValveSetPoint( valve, targetPosition, TRUE ); } // Go back to Idle state state = VALVE_STATE_IDLE; } // Check if the valve's transition time has timed out else if ( ( TRUE == didTimeout( valvesStatus[ valve ].transitionStartTime, VALVE_TRANSITION_TIMEOUT_MS ) ) && ( getCPLDACPowerLossDetected() != TRUE ) ) { // Go back to Idle state and set the valve position to not in position because it was not able to get to the target position valvesStatus[ valve ].currentPosition = VALVE_POSITION_NOT_IN_POSITION; state = VALVE_STATE_IDLE; SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_VALVE_TRANSITION_TIMEOUT, (U32)valve ); // If the valve transition timeout is active and the valve's commanded positions is position C, activate safety shutdown if ( ( TRUE == isAlarmActive( ALARM_ID_HD_VALVE_TRANSITION_TIMEOUT ) ) && ( VALVE_POSITION_C_CLOSE == valvesStatus[ valve ].commandedPosition ) ) { activateSafetyShutdown(); } } // Check if the valve is close to the temporary target position and if it is, assign the next target position else if ( abs( currentPosition - targetPosition ) < MAX_DEVIATION_FROM_TGT_IN_TRAVEL_CNTS ) { setValveNextStep( valve, TRANSITION_STEP_CHANGE_IN_COUNTS ); } return state; } #ifdef DEBUG_ENABLED /*********************************************************************//** * @brief * The handleValveStateInBypassMode function handles the bypass mode * state of the state machine. * @details Inputs: valvesStatus * @details Outputs: valvesStatus * @param valve that is in position transition * @return next state of the exec state machine *************************************************************************/ static VALVE_STATE_T handleValveStateInBypassMode( VALVE_T valve ) { VALVE_STATE_T state = VALVE_STATE_IN_BYPASS_MODE; // If bypass mode has been turned off if ( valvesStatus[ valve ].bypassModeStatus.hasBypassModeBeenRequeseted == FALSE ) { // Change the control mode to PID setValveControlMode( valve, VALVE_CONTROL_MODE_ENABLE_PID ); valvesStatus[ valve ].controlMode = VALVE_CONTROL_MODE_ENABLE_PID; // Request a change of position to get back to the last known position in PID mode // Go back to Pos A valvesStatus[ valve ].commandedPosition = VALVE_POSITION_A_INSERT_EJECT; valvesStatus[ valve ].hasTransitionBeenRequested = TRUE; state = VALVE_STATE_IDLE; } // If a valve change either a change in CW or CCW or PWM has changed, set the new valve PWM else if ( valvesStatus[ valve ].bypassModeStatus.hasChangeBeenRequested ) { setFPGAValvePWM( valve ); } return state; } #endif /*********************************************************************//** * @brief * The setValveControlMode function sets the valves control mode. * @details Inputs: none * @details Outputs: valvesControlSetBits * @param valve that its control mode is set * @param mode the control mode that the valve will be set to * @return none *************************************************************************/ static void setValveControlMode( VALVE_T valve, VALVE_MODE_T mode ) { // Get the set and reset bits for the particular valve U16 control = VALVE_CONTROL_MODES_SET_BITS[ valve ][ mode ]; U16 reset = VALVE_CONTROL_MODES_RESET_BITS[ valve ]; // Reset the control bits of the particular valve valvesControlSetBits &= reset; // Set the new control mode of the valve to the variable valvesControlSetBits |= control; setFPGAValvesControlMode( valvesControlSetBits ); } /*********************************************************************//** * @brief * The execMonitorValves function handles monitoring of the valves. The * function calls other functions to monitor the values. * @details Inputs: none * @details Outputs: none * @return none *************************************************************************/ static void execMonitorValves( void ) { // Check if the valves are still enabled areValvesFunctional(); // Get the current position of the valves in counts and store them getAndMonitorValvesCurrentFPGAPosition(); // Get the current in ADC and convert them to amps // Check whether any of the valves are over current convertAndMonitorValvesCurrent(); // Get the current PWM values back. // This function is only available if the valves are in bypass mode #ifdef DEBUG_ENABLED getValvesCurrentPWM(); #endif } /*********************************************************************//** * @brief * The areValvesEnabled function checks the current valves status from FPGA * to the enable bit mask of each valve. If any of the valves is not enabled * the function raises an alarm. * @details Inputs: valvesStatus, VALVE_CHECK * @details Outputs: none * @return Returns TRUE if all the valves status bits are correct *************************************************************************/ static BOOL areValvesFunctional( void ) { BOOL result = TRUE; VALVE_T valve; // Get the status of the valves from FPGA U16 status = getFPGAValvesStatus(); for ( valve = VDI; valve < NUM_OF_VALVES; valve++ ) { U16 statusCheck = status & VALVE_CHECK[ valve ][ VALVE_CHECK_BIT_MASK ]; if ( VALVE_CHECK[ valve ][ VALVE_CHECK_EXPECTED_CONTROL ] != statusCheck ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_VALVE_NOT_FUNCTIONAL, (U32)valve, (U32)status); result = FALSE; } } return result; } /*********************************************************************//** * @brief * The setFPGAValveSetPoint function sets the position of a valve to FPGA. * @details Inputs: none * @details Outputs: none * @param valve that its FPGA set point is updated * @param position that is set in counts * @param enableCurrentRelaxation flag. Indicates whether current relaxation * should be enabled for the valve * @return none *************************************************************************/ static void setFPGAValveSetPoint( VALVE_T valve, S16 position, BOOL enableCurrentRelaxation ) { // Always set the MSB of the S16 to 0 position &= DISABLE_VALVE_CURRENT_RELAXATION; // If current relaxation is needed, set the MSB to 1 if ( enableCurrentRelaxation ) { position |= ENABLE_VALVE_CURRENT_RELAXATION; } switch ( valve ) { case VDI: setFPGAValveDialyzerInletPosition( position ); break; case VDO: setFPGAValveDialyzerOutletPosition( position ); break; case VBA: setFPGAValveBloodArterialPosition( position ); break; case VBV: setFPGAValveBloodVenousPosition( position ); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_VALVES_INVALID_VALVE_SELECTED, (U32)valve ); break; } } /*********************************************************************//** * @brief * The convertAndMonitorValvesCurrent function gets the current values * in counts from FPGA and converts them to current. If any of the values * is above the threshold, the function raises an alarm. * @details Inputs: valvesStatus * @details Outputs: valvesStatus * @return none *************************************************************************/ static void convertAndMonitorValvesCurrent( void ) { VALVE_T valve; U16 currentInADC = 0; currentInADC = getFPGAValveDialyzerInletCurrentCounts(); valvesStatus[ VDI ].current.data = ( (F32)currentInADC / ADC_TO_CURRENT_CONVERSION_CONSTANT ) - 1; currentInADC = getFPGAValveDialyzerOutletCurrentCounts(); valvesStatus[ VDO ].current.data = ( (F32)currentInADC / ADC_TO_CURRENT_CONVERSION_CONSTANT ) - 1; currentInADC = getFPGAValveBloodArterialCurrentCounts(); valvesStatus[ VBA ].current.data = ( (F32)currentInADC / ADC_TO_CURRENT_CONVERSION_CONSTANT ) - 1; currentInADC = getFPGAValveBloodVenousCurrentCounts(); valvesStatus[ VBV ].current.data = ( (F32)currentInADC / ADC_TO_CURRENT_CONVERSION_CONSTANT ) - 1; // Check the current of all the valves for ( valve = VDI; valve < NUM_OF_VALVES; valve++) { // Absolute value is used to cover both positive and negative values F32 current = fabs( getValveCurrent( valve ) ); if ( current > VALVES_CURRENT_THRESHOLD_AMPS ) { valvesStatus[ valve ].overCurrentCounter++; } // Check if the current is over the threshold for the defined amount of time if ( valvesStatus[ valve ].overCurrentCounter > MAX_OVER_CURRENT_TIME_INTERVAL_COUNTER ) { SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_VALVE_CURRENT_OUT_OF_RANGE, (F32)valve, current ); } // If the current is below the threshold again and the counter for the time is greater than else if ( ( current < VALVES_CURRENT_THRESHOLD_AMPS ) && ( valvesStatus[ valve ].overCurrentCounter > 0 ) ) { valvesStatus[ valve ].overCurrentCounter = 0; } } } /*********************************************************************//** * @brief * The getAndMonitorValvesCurrentFPGAPosition function gets the current * position of the valves from FPGA and stores them into the structure of * each valve.The function checks whether any of the valves have deviated * from their current position if they are in idle state. If any of the * valves have deviated, the function raises an alarm. * @details Inputs: valvesStatus * @details Outputs: valvesStatus * @return none *************************************************************************/ static void getAndMonitorValvesCurrentFPGAPosition( void ) { VALVE_T valve; VALVE_POSITION_T commandedPositionEnum; S16 currentPosition = 0; S16 commandedPosition = 0; // Get the position of the valves and update the structure of each valve valvesStatus[ VDI ].currentPositionInCounts.data = (S32)getFPGAValveDialyzerInletPosition(); valvesStatus[ VDO ].currentPositionInCounts.data = (S32)getFPGAValveDialyzerOutletPosition(); valvesStatus[ VBA ].currentPositionInCounts.data = (S32)getFPGAValveBloodArterialPosition(); valvesStatus[ VBV ].currentPositionInCounts.data = (S32)getFPGAValveBloodVenousPosition(); // Check the position of each valve for ( valve = VDI; valve < NUM_OF_VALVES; valve++ ) { // Check the valve is in Idle state, otherwise the position should not be checked if ( ( valvesStatus[ valve ].execState == VALVE_STATE_IDLE ) && ( getCPLDACPowerLossDetected() != TRUE ) ) { U32 maxDeviation = MAX_DEVIATION_FROM_TARGET_IN_COUNTS; currentPosition = getValvePositionCounts( valve ); commandedPositionEnum = valvesStatus[ valve ].commandedPosition; commandedPosition = valvesStatus[ valve ].positions[ commandedPositionEnum ]; // If the current position is B, current relaxation requires looser range check if ( VALVE_POSITION_B_OPEN == commandedPositionEnum ) { maxDeviation = MAX_DEVIATION_FROM_TRAGET_IN_POS_B; } // Check if the current position has deviated from the position it is supposed to be in // For more than a certain amount of time. If it has, raise an alarm // Absolute value is used for comparison to cover +/- from the commanded position if ( abs( currentPosition - commandedPosition ) > maxDeviation ) { valvesStatus[ valve ].positionOutOfRangeCounter++; } if ( valvesStatus[ valve ].positionOutOfRangeCounter > MAX_POS_DEVIATION_TIME_INTERVAL_COUNTER ) { // If the valve's deviation from target was more than the counts for the define period of time trigger the alarm. // Also, set the state to Idle so in the fault mode, the valve can transition to Pos C. The exec state is directly set here // because this is a monitor function that is called in the controller function. valvesStatus[ valve ].execState = VALVE_STATE_IDLE; SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_VALVE_POSITION_OUT_OF_RANGE, (U32)valve, currentPosition ); if ( VALVE_POSITION_C_CLOSE == valvesStatus[ valve ].commandedPosition ) { activateSafetyShutdown(); } } else if ( ( TRUE == isValveCloseToCommandedPosition( valve, commandedPositionEnum ) ) && ( valvesStatus[ valve ].positionOutOfRangeCounter > 0 ) ) { valvesStatus[ valve ].positionOutOfRangeCounter = 0; } } else { valvesStatus[ valve ].positionOutOfRangeCounter = 0; } } } /*********************************************************************//** * @brief * The publishValvesData function publishes the data of the valves at the * specified time interval. * @details Inputs: valvesStatus * @details Outputs: valvesStatus * @param valve that its data is published * @return none *************************************************************************/ static void publishValvesData( VALVE_T valve ) { // Check the counter if ( ++valvesStatus[ valve ].dataPublishCounter >= getU32OverrideValue( &valvesDataPublishInterval ) ) { HD_VALVE_DATA_T valveData; valveData.valveID = (U32)valve; valveData.state = (U32)valvesStatus[ valve ].execState; valveData.currentPosID = (U32)getValvePosition( valve ); valveData.currentPos = getValvePositionCounts( valve ); valveData.nextPos = valvesStatus[ valve ].targetPositionInCounts; valveData.current = getValveCurrent( valve ); valveData.posC = valvesStatus[ valve ].positions[ VALVE_POSITION_C_CLOSE ]; valveData.posA = valvesStatus[ valve ].positions[ VALVE_POSITION_A_INSERT_EJECT ]; valveData.posB = valvesStatus[ valve ].positions[ VALVE_POSITION_B_OPEN ]; valveData.pwm = valvesStatus[ valve ].bypassModeStatus.currentPWMInPercent; valveData.airTrapValve = (U32)getValveAirTrapStatus(); broadcastData( MSG_ID_HD_VALVES_DATA, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&valveData, sizeof( HD_VALVE_DATA_T ) ); valvesStatus[ valve ].dataPublishCounter = 0; } } /*********************************************************************//** * @brief * The setValveNextStep function calculates the next step of a valve and * calls another function to set the next step. * @details Inputs: valvesStatus * @details Outputs: valvesStatus * @param valve that its next step is calculated and set * @return none *************************************************************************/ static void setValveNextStep( VALVE_T valve, U32 stepChange ) { S16 nextStep = 0; VALVE_POSITION_T currentPositionEnum = valvesStatus[ valve ].currentPosition; VALVE_POSITION_T commandedPositionEnum = valvesStatus[ valve ].commandedPosition; S16 currentPosition = getValvePositionCounts( valve ); S16 commandedPosition = valvesStatus[ valve ].positions[ commandedPositionEnum ]; // If the next step is less than the specified step change, set that if ( abs( currentPosition - commandedPosition ) <= stepChange ) { nextStep = abs( currentPosition - commandedPosition ); } else { nextStep = stepChange; } switch ( currentPositionEnum ) { case VALVE_POSITION_NOT_IN_POSITION: // If the valve is in an unknown position, it means the transition timed out and the software faulted. In the fault mode, // the valves are commanded to transition to position C so from wherever the valve is stuck it is subtracted to go to position C. // Position C is de-energized position and it is the negative of 0 position. valvesStatus[ valve ].targetPositionInCounts = currentPosition - nextStep; break; case VALVE_POSITION_A_INSERT_EJECT: // If the valve is currently in position A and its commanded position is position B, // Add the defined number of steps for the next transition // If the valve is currently in position A and its commanded position is position C, // Subtract the defined number of steps for the next transition if ( commandedPositionEnum == VALVE_POSITION_B_OPEN ) { valvesStatus[ valve ].targetPositionInCounts = currentPosition + nextStep; } if ( commandedPositionEnum == VALVE_POSITION_C_CLOSE ) { valvesStatus[ valve ].targetPositionInCounts = currentPosition - nextStep; } break; case VALVE_POSITION_B_OPEN: // If the valve is currently in position B, subtract the defined number of steps for the next transition valvesStatus[ valve ].targetPositionInCounts = currentPosition - nextStep; break; case VALVE_POSITION_C_CLOSE: // If the valve is currently in position C, add the defined number of steps for the next transition valvesStatus[ valve ].targetPositionInCounts = currentPosition + nextStep; break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_VAVLES_INVALID_POSITION_SELECTED, (U32)valve ); break; } // Call the function to send the set point to FPGA. Current relaxation is not // Enabled here, so it is always false setFPGAValveSetPoint( valve, valvesStatus[ valve ].targetPositionInCounts, FALSE ); } /*********************************************************************//** * @brief * The getValvePositionCounts function returns a valve's position in * counts. * @details Inputs: valvesStatus * @details Outputs: none * @param valve that its position in counts is requested * @return current position of the valve in counts (S16) *************************************************************************/ static S16 getValvePositionCounts( VALVE_T valve ) { S16 position = (S16)valvesStatus[ valve ].currentPositionInCounts.data; if ( OVERRIDE_KEY == valvesStatus[ valve ].currentPositionInCounts.override ) { position = (S16)valvesStatus[ valve ].currentPositionInCounts.ovData; } return position; } /*********************************************************************//** * @brief * The isValveCloseToCommandedPosition function checks whether a valve is close * to its commanded position or not * @details Inputs: valvesStatus * @details Outputs: none * @param valve that its position in counts is requested * @param positionToCheck the position enum (can be commanded to pending commanded enums * to check) * @return TRUE if the valve is within accepted deviation from commanded otherwise, * FALSE *************************************************************************/ static BOOL isValveCloseToCommandedPosition( VALVE_T valve, VALVE_POSITION_T positionToCheck ) { S16 currentPosition = getValvePositionCounts( valve ); S16 commandedPosition = valvesStatus[ valve ].positions[ positionToCheck ]; BOOL isInRange = ( abs( currentPosition - commandedPosition ) < MAX_DEVIATION_FROM_TARGET_IN_COUNTS ? TRUE : FALSE ); return isInRange; } #ifdef DEBUG_ENABLED /*********************************************************************//** * @brief * The setFPGAValvePWM function checks whether the valve should be moving * CW or CCW and converts the PWM in percent to PWM in counts accordingly. * The converted PWM in counts is set via the FPGA functions. * @details Inputs: valvesStatus * @details Outputs: none * @param valve which its PWM is being set * @return none *************************************************************************/ static void setFPGAValvePWM( VALVE_T valve ) { U16 pwmInCounts = 0; U16 commandedPWMPct = valvesStatus[ valve ].bypassModeStatus.commandedPWMInPercent; // Check the requested direction and convert PWM in percent to counts if ( valvesStatus[ valve ].bypassModeStatus.direction == VAVLE_DIR_CLOCKWISE ) { pwmInCounts = VALVE_CW_PWM_TO_CNT_CONVERSION( commandedPWMPct ); } else { pwmInCounts = VALVE_CCW_PWM_TO_CNT_CONVERSION( commandedPWMPct ); } switch ( valve ) { case VDI: setFPGAValveDialyzerInletPWM( pwmInCounts ); break; case VDO: setFPGAValveDialyzerOutletPWM( pwmInCounts ); break; case VBA: setFPGAValveBloodArterialPWM( pwmInCounts ); break; case VBV: setFPGAValveBloodVenousPWM( pwmInCounts ); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_VALVES_INVALID_VALVE_SELECTED, (U32)valve ); break; } } /*********************************************************************//** * @brief * The getValvesCurrentPWM function gets the PWM counts from the FPGA module * and converts them in to duty cycle in percent. * @details Inputs: none * @details Outputs: valvesStatus * @return none *************************************************************************/ static void getValvesCurrentPWM( void ) { VALVE_T valve; U16 currentPWM = 0; // Get all the PWMs in count U16 pwmInCounts[ NUM_OF_VALVES ] = { getFPGAValveDialyzerInletPWM(), getFPGAValveDialyzerOutletPWM(), getFPGAValveBloodArterialPWM(), getFPGAValveBloodVenousPWM() }; for ( valve = VDI; valve < NUM_OF_VALVES; valve++ ) { // Get the count currentPWM = pwmInCounts[ valve ]; // Check if the direction is clockwise or not to use the right conversion equation // TODO check the PWM conversion macros. These macros are not needed anywhere and they // are only used for Bypass mode so they are not critical. /*if ( valvesStatus[ valve ].bypassModeStatus.direction == VAVLE_DIR_CLOCKWISE ) { currentPWM = VALVE_CW_CNT_TO_PWM_CONVERSION( currentPWM ); } else { currentPWM = VALVE_CCW_CNT_TO_PWM_CONVERSION( currentPWM ); }*/ valvesStatus[ valve ].bypassModeStatus.currentPWMInPercent = currentPWM; } } #endif /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetValvesDataPublishInterval function overrides the valves data * publish interval. * @details Inputs: valvesDataPublishInterval * @details Outputs: valvesDataPublishInterval * @param value which is override value for the valve data publish interval * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetValvesDataPublishInterval( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 intvl = value / TASK_PRIORITY_INTERVAL; result = TRUE; valvesDataPublishInterval.ovData = intvl; valvesDataPublishInterval.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetValvesDataPublishInterval function resets the override * of the valves publish interval. * @details Inputs: none * @details Outputs: valvesDataPublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetValvesDataPublishInterval( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; valvesDataPublishInterval.override = OVERRIDE_RESET; valvesDataPublishInterval.ovData = valvesDataPublishInterval.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetValvesPositionOverride function overrides the valves position. * @details Inputs: none * @details Outputs: valvesPositionOverride * @param valve to override its position * @param position of the valve that will be overridden * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetValvesPositionOverride( U32 valve, U32 position ) { BOOL result = FALSE; if ( ( TRUE == isTestingActivated() ) && ( valve < NUM_OF_VALVES ) ) { // Get the current position of the valve as the initial position valvesPositionOverride[ valve ].ovInitData = (U32)valvesStatus[ valve ].currentPosition; valvesPositionOverride[ valve ].ovData = position; valvesPositionOverride[ valve ].override = OVERRIDE_KEY; result = setValvePosition( (VALVE_T)valve, (VALVE_POSITION_T)position ); } return result; } /*********************************************************************//** * @brief * The testResetValvesDataPublishInterval function resets the override * of the valves publish interval. * @details Inputs: valvesDataPublishInterval * @details Outputs: valvesDataPublishInterval * @param valve that its position override will be reset * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetValvesPositionOverride( U32 valve ) { BOOL result = FALSE; if ( ( TRUE == isTestingActivated() ) && ( valve < NUM_OF_VALVES ) ) { valvesPositionOverride[ valve ].override = OVERRIDE_RESET; valvesPositionOverride[ valve ].ovData = valvesPositionOverride[ valve ].ovInitData; result = setValvePosition( (VALVE_T)valve, (VALVE_POSITION_T)valvesPositionOverride[ valve ].ovInitData ); } return result; } /*********************************************************************//** * @brief * The testSetValvePWMOverride function overrides the valves PWM in bypass * control mode. * @details Inputs: valvesStatus * @details Outputs: valvesStatus * @param valve to override its PWM * @param pwm in percent * @param direction of the movement of the valve in clockwise or counter * clockwise * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetValvePWMOverride( U32 valve, U32 pwm, U32 direction ) { BOOL result = FALSE; if ( ( TRUE == isTestingActivated() ) && ( valve < NUM_OF_VALVES ) ) { BOOL homingStatus = valvesStatus[ valve ].hasValveBeenHomed; // The PWM should be in range and also the valve direction should be legal and the valve must have been homed // Prior to running this command if ( ( pwm <= VALVE_MAX_ALLOWED_PWM_PERCENT ) && ( direction < NUM_OF_VALVE_DIRECTION ) && ( TRUE == homingStatus ) ) { valvesStatus[ (VALVE_T)valve ].bypassModeStatus.commandedPWMInPercent = (U16)pwm; valvesStatus[ (VALVE_T)valve ].bypassModeStatus.direction = (VALVE_DIRECTION_T)direction; // This flag sets the valves to bypass mode in the idle state. After that the valve will be sitting in the // Bypass mode until this flag is set to FALSE in the reset function. valvesStatus[ (VALVE_T)valve ].bypassModeStatus.hasBypassModeBeenRequeseted = TRUE; // This flag is used to check if a change in PWM has been requested so the new PWM is processed and set valvesStatus[ (VALVE_T)valve ].bypassModeStatus.hasChangeBeenRequested = TRUE; result = TRUE; } } return result; } /*********************************************************************//** * @brief * The testResetValvePWMOverride function resets the override PWM in bypass * control mode. * @details Inputs: none * @details Outputs: valvesStatus * @param valve that its PWM override will be reset * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetValvePWMOverride( U32 valve ) { BOOL result = FALSE; // Check if the valve is in the range of the valves available if ( ( TRUE == isTestingActivated() ) && ( valve < NUM_OF_VALVES ) ) { // Turn off the bypass mode request and the change request valvesStatus[ (VALVE_T)valve ].bypassModeStatus.hasBypassModeBeenRequeseted = FALSE; valvesStatus[ (VALVE_T)valve ].bypassModeStatus.hasChangeBeenRequested = FALSE; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testSetValvesCurrentOverride function overrides the valves' current. * @details Inputs: valvesStatus * @details Outputs: valvesStatus * @param valve to override its current * @param current of the valve that will be overridden * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetValvesCurrentOverride( U32 valve, F32 current ) { BOOL result = FALSE; // Check if the valve is in the range of the valves available if ( ( TRUE == isTestingActivated() ) && ( valve < NUM_OF_VALVES ) ) { valvesStatus[ (VALVE_T)valve ].current.override = OVERRIDE_KEY; valvesStatus[ (VALVE_T)valve ].current.ovData = current; valvesStatus[ (VALVE_T)valve ].current.ovInitData = valvesStatus[ (VALVE_T)valve ].current.data; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testResetValvesCurrentOverride function resets the override * of the valves' current. * @details Inputs: valvesStatus * @details Outputs: valvesStatus * @param valve that its current override will be reset * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetValvesCurrentOverride( U32 valve ) { BOOL result = FALSE; if ( ( TRUE == isTestingActivated() ) && ( valve < NUM_OF_VALVES ) ) { valvesStatus[ (VALVE_T)valve ].current.override = OVERRIDE_RESET; valvesStatus[ (VALVE_T)valve ].current.ovData = valvesStatus[ (VALVE_T)valve ].current.ovInitData; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testSetValvesPositionCountOverride function overrides the valves * position in counts. * @details Inputs: valvesStatus * @details Outputs: none * @param valve to override its position * @param position of the valve that will be overridden * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetValvesPositionCountOverride( U32 valve, U32 count ) { BOOL result = FALSE; // Check if the valve is in the range of the valves available if ( ( TRUE == isTestingActivated() ) && ( valve < NUM_OF_VALVES ) ) { valvesStatus[ (VALVE_T)valve ].currentPositionInCounts.override = OVERRIDE_KEY; valvesStatus[ (VALVE_T)valve ].currentPositionInCounts.ovData = (S32)count; valvesStatus[ (VALVE_T)valve ].currentPositionInCounts.ovInitData = valvesStatus[ (VALVE_T)valve ].currentPositionInCounts.data; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testResetValvesPositionCountOverride function resets the override * of the valves' position in counts. * @details Inputs: none * @details Outputs: valvesStatus * @param valve that its position count override will be reset * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetValvesPositionCountOverride( U32 valve ) { BOOL result = FALSE; if ( ( TRUE == isTestingActivated() ) && ( valve < NUM_OF_VALVES ) ) { valvesStatus[ (VALVE_T)valve ].currentPositionInCounts.override = OVERRIDE_RESET; valvesStatus[ (VALVE_T)valve ].currentPositionInCounts.ovData = valvesStatus[ (VALVE_T)valve ].currentPositionInCounts.ovInitData; result = TRUE; } return result; } /**@}*/