#include "Valves.h" #include "FPGA.h" #include "Timers.h" /** * @addtogroup Valves * @{ */ // ********** private definitions ********** // Valve dialyzer inlet defines #define VDI_ENABLE_PID_INTERFACE 0x0010 #define VDI_ENABLE_BYPASS_INTERFACE 0x00EF #define VDI_INITIALIZATION_STATUS_BIT_MASK 0x0040 #define VDI_ENABLE_PID_STATUS_BIT_MASK 0x0080 #define VDI_ENABLE_BYPASS_STATUS_BIT_MASK 0x00100 // Valve dialyzer outlet defines #define VDO_ENABLE_PID_INTERFACE 0x0040 #define VDO_ENABLE_BYPASS_INTERFACE 0x00BF #define VDO_INITIALIZATION_STATUS_BIT_MASK 0x0200 #define VDO_ENABLE_PID_STATUS_BIT_MASK 0x0400 #define VDO_ENABLE_BYPASS_STATUS_BIT_MASK 0x0800 // Valve blood arterial defines #define VBA_ENABLE_PID_INTERFACE 0x0001 #define VBA_ENABLE_BYPASS_INTERFACE 0x00FE #define VBA_INITIALIZATION_STATUS_BIT_MASK 0x0001 #define VBA_ENABLE_PID_STATUS_BIT_MASK 0x0002 #define VBA_ENABLE_BYPASS_STATUS_BIT_MASK 0x0004 // Valve blood venous defines #define VBV_ENABLE_PID_INTERFACE 0x0004 #define VBV_ENABLE_BYPASS_INTERFACE 0x00FB #define VBV_INITIALIZATION_STATUS_BIT_MASK 0x0008 #define VBV_ENABLE_PID_STATUS_BIT_MASK 0x0010 #define VBV_ENABLE_BYPASS_STATUS_BIT_MASK 0x0020 #define INITIAL_EDGE_OFFSET_READ_COUNT 100 #define INITIAL_DEENERGIZED_POSITION ( 0 - INITIAL_EDGE_OFFSET_READ_COUNT ) #define INITIAL_ENERGIZED_POSITION (12000 + INITIAL_EDGE_OFFSET_READ_COUNT ) #define CONTINUOUS_CLOSED_DOOR_TIME_MS 1000U #define STEP_CHANGE_IN_COUNTS 100U #define MAX_POS_DEVIATION_FROM_TARGET_COUNTS 1000U //TODO test this tolerance #define VALVE_TRANSITION_TIMEOUT_MS 50U //TODO test the timeout #define MAX_POSITION_DEVIATION_FROM_EDGES_COUNTS 50U #define MAX_ALLOWED_FAILED_HOMINGS 3U // ********** private data ********** /// Exec valve self test states typedef enum valves_Self_Test_States { VALVE_SELF_TEST_ENABLE_VALVES = 0, VALVE_SELF_TEST_CONFIRM_ENABLE, VALVE_SELF_TEST_COMPLETE, NUM_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_IDLE, VALVE_STATE_IN_TRANSITION, VALVE_STATE_HOMING_NOT_STARTED, VALVE_STATE_HOMING_FIND_ENERGIZED_EDGE, VALVE_STATE_HOMING_FIND_DEENERGIZED_EDGE, VALVE_STATE_HOMING_TRANISTION_TO_POS_A, NUM_OF_VALVE_STATES, } VALVE_STATE_T; typedef enum homingStatus { HOMING_NOT_STARTED = 0, HOMING_IN_PROGRESS, HOMING_COMPLETE, NUM_OF_HOMING_STATES } HOMING_STATUS_T; //TODO remove typedef struct { VALVE_POSITION_T commandedPosition; VALVE_POSITION_T currentPosition; S16 currentPositionInCounts; S16 previousPositionInCounts; BOOL hasTransitionBeenRequested; HOMING_STATUS_T homingStatus; //TODO remove? BOOL hasHomingBeenRequested; S16 positions[ NUM_OF_VALVE_POSITIONS ]; U32 numberOfFailedHoming; U32 transitionStartTime; } VALVE_STATUS_T; static VALVE_STATE_T dialyzerInletValveState = VALVE_STATE_WAIT_FOR_POST; //TODO Remove these variables? static VALVE_STATE_T dialyzerOutletValveState = VALVE_STATE_WAIT_FOR_POST; static VALVE_STATE_T bloodArterialValveState = VALVE_STATE_WAIT_FOR_POST; static VALVE_STATE_T bloodVenousValveState = VALVE_STATE_WAIT_FOR_POST; static VALVE_SELF_TEST_STATE_T valveSelfTestState = VALVE_SELF_TEST_ENABLE_VALVES; static SELF_TEST_STATUS_T valvesSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; static VALVE_STATUS_T valvesStatus[ NUM_OF_VALVE_POSITIONS ]; static BOOL isDoorBeingChecked = FALSE; static U32 generalTimer = 0; static void execMonitorValves( void ); static BOOL areValvesEnabled( void ); static BOOL isDoorClosed( void ); // 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 handleValveStateIdle( VALVE_T valve ); static VALVE_STATE_T handleValveStateInTransition( 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 handleValveStateHomingTransitionToPosA( VALVE_T valve ); static VALVE_STATE_T processHomingEnergizedState( VALVE_T valve ); static VALVE_STATE_T processHomingDeenergizedState( VALVE_T valve ); static VALVE_STATE_T processTransitionToPositions( VALVE_T valve ); // Use this to the last state of homing and transition states void initValves( void ) { U08 i; dialyzerInletValveState = VALVE_STATE_WAIT_FOR_POST; dialyzerOutletValveState = VALVE_STATE_WAIT_FOR_POST; bloodArterialValveState = VALVE_STATE_WAIT_FOR_POST; bloodVenousValveState = VALVE_STATE_WAIT_FOR_POST; valveSelfTestState = VALVE_SELF_TEST_ENABLE_VALVES; valvesSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; // Initialize some of the variables for ( i = 0; i < NUM_OF_VALVE_POSITIONS; i++ ) { valvesStatus[ i ].homingStatus = HOMING_NOT_STARTED; } } 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: //TODO alarm valveSelfTestState = VALVE_SELF_TEST_COMPLETE; } return valvesSelfTestResult; } void execValves( void ) { execMonitorValves(); // VDi state machine switch ( dialyzerInletValveState ) { case VALVE_STATE_WAIT_FOR_POST: dialyzerInletValveState = handleValveStateWaitForPost( VDI ); break; case VALVE_STATE_IDLE: dialyzerInletValveState = handleValveStateIdle( VDI ); break; case VALVE_STATE_IN_TRANSITION: dialyzerInletValveState = handleValveStateInTransition( VDI ); break; case VALVE_STATE_HOMING_NOT_STARTED: dialyzerInletValveState = handleValveStateHomingNotStarted( VDI ); break; case VALVE_STATE_HOMING_FIND_ENERGIZED_EDGE: dialyzerInletValveState = handleValveStateHomingFindEnergizedEdge( VDI ); break; case VALVE_STATE_HOMING_FIND_DEENERGIZED_EDGE: dialyzerInletValveState = handleValveStateHomingFindDeenergizedEdge( VDI ); break; default: break; } } BOOL homeValve( VALVE_T valve ) { BOOL result = FALSE; valvesStatus[ valve ].hasHomingBeenRequested = TRUE; return result; } BOOL setValvePosition( VALVE_T valve, VALVE_POSITION_T position ) { valvesStatus[ valve ].commandedPosition = position; return TRUE; } S16 getValvePosition( VALVE_T valve) { } BOOL setValveBloodTrap( OPN_CLS_STATE_T state ) { } static void execMonitorValves( void ) { // TODO check current sense // TODO check enable status // Get the current position of the valves in counts and store them valvesStatus [ VDI ].currentPositionInCounts = getFPGAValveDialyzerInletPosition(); valvesStatus [ VDO ].currentPositionInCounts = getFPGAValveDialyzerOutletPosition(); valvesStatus [ VBA ].currentPositionInCounts = getFPGAValveBloodArterialPosition(); valvesStatus [ VBV ].currentPositionInCounts = getFPGAValveBloodVenousPosition(); } static VALVE_STATE_T handleValveStateWaitForPost( VALVE_T valve ) { VALVE_STATE_T state = VALVE_STATE_WAIT_FOR_POST; switch ( valve ) { case VDI: if ( valveSelfTestState == VALVE_SELF_TEST_COMPLETE ) { state = VALVE_STATE_HOMING_NOT_STARTED; } break; case VDO: break; case VBA: break; case VBV: break; default: break; } return state; } static VALVE_SELF_TEST_STATE_T handleValveSelfTestEnableValves( void ) { VALVE_SELF_TEST_STATE_T state = VALVE_SELF_TEST_CONFIRM_ENABLE; valvesSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; // Enable the PID controller of all of the valves enableFPGAValvesPIDControl( VDI_ENABLE_PID_INTERFACE ); enableFPGAValvesPIDControl( VDO_ENABLE_PID_INTERFACE ); enableFPGAValvesPIDControl( VBA_ENABLE_PID_INTERFACE ); enableFPGAValvesPIDControl( VBV_ENABLE_PID_INTERFACE ); return state; } static VALVE_SELF_TEST_STATE_T handleValveSelfTestConfirmEnable( void ) { VALVE_SELF_TEST_STATE_T state = VALVE_SELF_TEST_CONFIRM_ENABLE; // TODO do we need to wait for 50ms? if ( areValvesEnabled() ) { valvesSelfTestResult = SELF_TEST_STATUS_PASSED; state = VALVE_SELF_TEST_COMPLETE; } return state; } static VALVE_STATE_T handleValveStateIdle( VALVE_T valve ) { VALVE_STATE_T state = VALVE_STATE_IDLE; if ( valvesStatus[ valve ].hasHomingBeenRequested ) { state = VALVE_STATE_HOMING_NOT_STARTED; } else if ( valvesStatus[ valve ].homingStatus == HOMING_COMPLETE && valvesStatus[ valve ].hasTransitionBeenRequested ) { if ( valvesStatus[ valve ].currentPosition != valvesStatus[ valve ].commandedPosition ) { //processHomingEnergizedState( valve, valvesStatus[ valve ].commandedPosition ); } } return state; } static VALVE_STATE_T handleValveStateInTransition( VALVE_T valve ) { } static VALVE_STATE_T handleValveStateHomingNotStarted( VALVE_T valve ) { VALVE_STATE_T state = VALVE_STATE_HOMING_NOT_STARTED; switch ( valve ) { case VDI: // If homing has been requested or POST is completed and the door has been close for the specified // period of time, start the homing if ( ( valvesStatus[ valve ].hasHomingBeenRequested || valveSelfTestState == VALVE_SELF_TEST_COMPLETE ) && isDoorClosed() ) { setFPGAValveDialyzerInletPosition( STEP_CHANGE_IN_COUNTS ); valvesStatus[ valve ].transitionStartTime = getMSTimerCount(); state = VALVE_STATE_HOMING_FIND_ENERGIZED_EDGE; } break; case VDO: break; case VBA: break; case VBV: break; default: break; } return state; } static VALVE_STATE_T handleValveStateHomingFindEnergizedEdge( VALVE_T valve ) { VALVE_STATE_T state = VALVE_STATE_HOMING_FIND_ENERGIZED_EDGE; switch ( valve ) { case VDI: state = processHomingEnergizedState( valve ); break; case VDO: break; case VBA: break; case VBV: break; default: break; } return state; } static VALVE_STATE_T handleValveStateHomingFindDeenergizedEdge( VALVE_T valve ) { VALVE_STATE_T state = VALVE_STATE_HOMING_FIND_DEENERGIZED_EDGE; switch ( valve ) { case VDI: state = processHomingDeenergizedState( valve ); break; case VDO: break; case VBA: break; case VBV: break; default: break; } return state; } static VALVE_STATE_T processHomingEnergizedState( VALVE_T valve ) { VALVE_STATE_T state = VALVE_STATE_HOMING_FIND_ENERGIZED_EDGE; S16 currentPosition = 0; S16 previousPosition = 0; U32 elapsedTime = 0; BOOL hasTimeElapsed = FALSE; // Read the energized position back currentPosition = fabs( valvesStatus[ valve ].currentPositionInCounts ); previousPosition = fabs( valvesStatus[ valve ].previousPositionInCounts ); elapsedTime = valvesStatus[ valve ].transitionStartTime; //TODO this is wrong hasTimeElapsed = didTimeout( elapsedTime, VALVE_TRANSITION_TIMEOUT_MS ); //TODO use the right timeout //TODO get the timing right (50ms should be started correctly) if ( fabs( currentPosition - previousPosition ) <= MAX_POSITION_DEVIATION_FROM_EDGES_COUNTS ) { if( fabs( currentPosition - INITIAL_ENERGIZED_POSITION ) <= MAX_POS_DEVIATION_FROM_TARGET_COUNTS ) { // Current position (positive or negative) will be stored in the Position B of the current valve // Do not use the above variables because they absolute valves using fabs valvesStatus[ valve ].positions[ VALVE_POSITION_B_OPEN ] = valvesStatus[ valve ].currentPositionInCounts; // Reset the current time and transition to the next state valvesStatus[ valve ].transitionStartTime = getMSTimerCount(); state = VALVE_STATE_HOMING_FIND_DEENERGIZED_EDGE; } } else if ( didTimeout( valvesStatus[ valve ].transitionStartTime, VALVE_TRANSITION_TIMEOUT_MS ) ) { U32 failedHomingCount = valvesStatus[ valve ].numberOfFailedHoming; if ( failedHomingCount >= MAX_ALLOWED_FAILED_HOMINGS ) { //TODO fault alarm } state = VALVE_STATE_HOMING_NOT_STARTED; } else { S16 nextStep = valvesStatus[ valve ].currentPositionInCounts + STEP_CHANGE_IN_COUNTS; setFPGAValveDialyzerInletPosition ( nextStep ); } return state; } static VALVE_STATE_T processHomingDeenergizedState( VALVE_T valve ) { VALVE_STATE_T state = VALVE_STATE_HOMING_FIND_DEENERGIZED_EDGE; S16 currentPosition = 0; S16 previousPosition = 0; U32 elapsedTime = 0; BOOL hasTimeElapsed = FALSE; // Read the energized position back currentPosition = fabs( valvesStatus[ valve ].currentPositionInCounts ); previousPosition = fabs( valvesStatus[ valve ].previousPositionInCounts ); elapsedTime = valvesStatus[ valve ].transitionStartTime; //TODO this is wrong hasTimeElapsed = didTimeout( elapsedTime, VALVE_TRANSITION_TIMEOUT_MS ); //TODO use the right timeout //TODO get the timing right (50ms should be started correctly) if ( fabs( currentPosition - previousPosition ) <= MAX_POSITION_DEVIATION_FROM_EDGES_COUNTS ) { if( fabs( currentPosition - INITIAL_DEENERGIZED_POSITION ) <= MAX_POS_DEVIATION_FROM_TARGET_COUNTS ) { S16 positionB = valvesStatus[ valve ].positions[ VALVE_POSITION_B_OPEN ]; // Position A is the average of the Position B that was read last time and position C that was // the target of this state S16 positionA = ( valvesStatus[ valve ].currentPositionInCounts + positionB ) / 2; valvesStatus[ valve ].positions[ VALVE_POSITION_A_INSERT_EJECT ] = positionA; // 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 - INITIAL_EDGE_OFFSET_READ_COUNT; valvesStatus[ valve ].positions[ VALVE_POSITION_C_CLOSE ] = valvesStatus[ valve ].currentPositionInCounts - INITIAL_EDGE_OFFSET_READ_COUNT; // Reset the number of failed homing for the next homing sequence valvesStatus[ valve ].numberOfFailedHoming = 0; // Reset the current time and transition to the next state valvesStatus[ valve ].transitionStartTime = getMSTimerCount(); state = VALVE_STATE_HOMING_TRANISTION_TO_POS_A; } } else if ( didTimeout( valvesStatus[ valve ].transitionStartTime, VALVE_TRANSITION_TIMEOUT_MS ) ) { U32 failedHomingCount = valvesStatus[ valve ].numberOfFailedHoming; if ( failedHomingCount >= MAX_ALLOWED_FAILED_HOMINGS ) { //TODO fault alarm } state = VALVE_STATE_HOMING_NOT_STARTED; } else { S16 nextStep = valvesStatus[ valve ].currentPositionInCounts - STEP_CHANGE_IN_COUNTS; setFPGAValveDialyzerInletPosition ( nextStep ); } return state; } static BOOL areValvesEnabled( void ) { //TODO alarm for the valve status BOOL result = TRUE; U16 status = getFPGAValvesStatus(); // VDi status if ( ! status & VDI_INITIALIZATION_STATUS_BIT_MASK ) { result = FALSE; } if ( ! status & VDI_ENABLE_PID_STATUS_BIT_MASK ) { result = FALSE; } if ( status & VDI_ENABLE_BYPASS_STATUS_BIT_MASK ) { result = FALSE; } // VDo status if ( ! status & VDO_INITIALIZATION_STATUS_BIT_MASK ) { result = FALSE; } if ( ! status & VDO_ENABLE_PID_STATUS_BIT_MASK ) { result = FALSE; } if ( status & VDO_ENABLE_BYPASS_STATUS_BIT_MASK ) { result = FALSE; } // VBA status if ( ! status & VBA_INITIALIZATION_STATUS_BIT_MASK ) { result = FALSE; } if ( ! status & VBA_ENABLE_PID_STATUS_BIT_MASK ) { result = FALSE; } if ( status & VBA_ENABLE_BYPASS_STATUS_BIT_MASK ) { result = FALSE; } // VBV status if ( ! status & VBV_INITIALIZATION_STATUS_BIT_MASK ) { result = FALSE; } if ( ! status & VBV_ENABLE_PID_STATUS_BIT_MASK ) { result = FALSE; } if ( status & VBV_ENABLE_BYPASS_STATUS_BIT_MASK ) { result = FALSE; } return result; } static BOOL isDoorClosed( void ) { BOOL result = FALSE; // TODO change this real door status // Check the door status BOOL doorStatus = TRUE; // Checking the doors status for the first time if ( isDoorBeingChecked == FALSE ) { generalTimer = getMSTimerCount(); isDoorBeingChecked = TRUE; } else { // If the door status is true and the time has elapsed if ( doorStatus && didTimeout( generalTimer, CONTINUOUS_CLOSED_DOOR_TIME_MS ) ) { isDoorBeingChecked = FALSE; result = TRUE; } // If door status is not true, then we should exit and reset else if ( doorStatus == FALSE ) { isDoorBeingChecked = FALSE; } } return result; }