/************************************************************************** * * Copyright (c) 2024-2025 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) Dara Navaei * @date (last) 09-Nov-2025 * * @author (original) Sean Nash * @date (original) 24-Oct-2024 * ***************************************************************************/ #include "AlarmMgmtTD.h" #include "CpldInterface.h" #include "FpgaTD.h" #include "Messaging.h" #include "OperationModes.h" #include "PersistentAlarm.h" #include "TaskGeneral.h" #include "Timers.h" #include "Valves.h" /** * @addtogroup Valves * @{ */ // ********** private definitions ********** #define VALVES_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Valves data publish interval. #define DATA_PUBLISH_COUNTER_START_COUNT 13 ///< Valves data publish start counter. #define VALVE_TRANSITION_MIN_TGT_DELTA 10 ///< Minimum encoder position delta from target position to end transition state. #define VALVE_HOME_MIN_POS_CHG 3 ///< Minimum encoder position change to indicate a home operation is still moving toward edge. #define VALVE_HOME_BACK_OFF_EDGE 3 ///< Encoder counts to back off of detected edge position. #define MAX_HOME_FULL_TRAVEL_DIFF 10U ///< Maximum allowed difference in full travel encoder counts between expected and measured during home operation. #define VALVE_TRANSITION_TIMEOUT_MS ( 3 * MS_PER_SECOND ) ///< Valves transition time out in ms. #define HOMING_EDGE_DETECTION_TIMEOUT_MS ( 0.5 * MS_PER_SECOND ) ///< Valves homing edge detection timeout in milliseconds. #define VALVE_FORCE_HOME TRUE ///< Force valve to home even if already homed. #define ZERO_ENC_DEBOUNCE_THRESHOLD_CNT 5 ///< Valves zero encoder debounce threshold count. #define ZERO_ENC_DEBOUNCE_TIMEOUT_MS ( 0.25 * MS_PER_SECOND ) ///< Valves zero encoder debounce timeout in milliseconds. #define VALVE_OFFEST_FROM_EDG_CNT 32 ///< Valves offset from the edge. #define POS_C_FROM_ZERO_CNT VALVE_OFFEST_FROM_EDG_CNT ///< Position C from zero position in counts. #define POS_D_PARTIAL_CLOSE_FROM_ZERO_CNT ( POS_C_FROM_ZERO_CNT + 32 ) ///< Position D partial close from zero position in counts. /// Valve controller states typedef enum Valve_Control_States { VALVE_STATE_WAIT_FOR_POST = 0, ///< Valve state wait for POST. VALVE_STATE_RESET_VALVE, ///< Valve state reset valve. VALVE_STATE_RESET_ENCODER, ///< Valve state reset encoder. VALVE_STATE_ENABLE_VALVE, ///< Valve state enable valve. VALVE_STATE_HOMING_NOT_STARTED, ///< Valve state homing not started. VALVE_STATE_HOMING_FIND_ENERGIZED_EDGE, ///< Valve state homing find energized edge. VALVE_STATE_IDLE, ///< Valve state idle. VALVE_STATE_IN_TRANSITION, ///< Valve state in transition. NUM_OF_VALVE_STATES, ///< Number of valve exec states. } VALVE_STATE_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. S16 currentEncPosition; ///< Valve current position in encoder counts. S16 priorEncPosition; ///< Valve prior position in encoder counts. BOOL hasTransitionBeenRequested; ///< Valve transition request flag. VALVE_STATE_T controlState; ///< Valve control state. U32 valveOpsStartTime; ///< Valve transition start time. S16 positionsABC[ NUM_OF_VALVE_POSITIONS ]; ///< Valve positions (in encoder counts) for positions A, B, and C. U32 overCurrentCounter; ///< Valve over current counter. U32 positionOutOfRangeCounter; ///< Valve position out of range counter. // Homing variables BOOL hasHomingBeenRequested; ///< Valve homing request flag. BOOL hasValveBeenHomed; ///< Valve homing completed flag. BOOL hasHomingFailed; ///< Valve homing failed flag S16 proposedEnergizedPos; ///< Valve homing proposed energized position. BOOL hasValveBeenReset; ///< Valve homing has valve been reset. } VALVE_STATUS_T; /// Payload record structure for pinch valve set position request typedef struct { U32 valve; ///< which pinch valve to set (0=H1, 1=H19) U32 pos; ///< 1=A, 2=B, 3=C } VALVE_PINCH_SET_CMD_PAYLOAD_T; /// Payload record structure for pinch valve home request typedef struct { U32 valve; ///< which pinch valve to home (0=H1, 1=H19) BOOL force; ///< Flag indicates whether home should be forced to happen even if already homed. BOOL tubing; ///< Flag indicates whether tubing will be present. } VALVE_PINCH_HOME_CMD_PAYLOAD_T; // ********** private data ********** static VALVE_STATUS_T currentValveStates[ NUM_OF_VALVES ]; ///< Current status of each valve. static U32 valvesDataPublicationTimerCounter; ///< Valves data broadcast timer counter. static OVERRIDE_U32_T valvesDataPublishInterval; ///< Valves data broadcast interval (in ms). static SELF_TEST_STATUS_T valvesSelfTestResult; ///< Valves self test result. //static TD_VALVES_CAL_RECORD_T valvesCalibrationRecord; ///< Valves calibration record. // ********** private function prototypes ********** static VALVE_STATE_T handleValvesWait4PostState( VALVE_T valve ); static VALVE_STATE_T handleValvesResetValve( VALVE_T valve ); static VALVE_STATE_T handleValvesResetEncoder( VALVE_T valve ); static VALVE_STATE_T handleValvesEnableValve( VALVE_T valve ); static VALVE_STATE_T handleValvesNotHomedState( VALVE_T valve ); static VALVE_STATE_T handleValvesFindEnergizedEdgeState( VALVE_T valve ); static VALVE_STATE_T handleValvesIdleState( VALVE_T valve ); static VALVE_STATE_T handleValvesTransitionState( VALVE_T valve ); static void checkValveInRange( VALVE_T valve, SW_FAULT_ID_T SWFault ); static void publishValvesData( void ); /*********************************************************************//** * @brief * The initValves function initializes the valves controller unit. * @details \b Inputs: none * @details \b Outputs: Valves unit initialized * @return none *************************************************************************/ void initValves( void ) { // Initialize driver initRotaryValvesDriver(); // Initialize controller variables memset( ¤tValveStates[0], 0, sizeof( currentValveStates ) ); valvesDataPublicationTimerCounter = DATA_PUBLISH_COUNTER_START_COUNT; valvesDataPublishInterval.data = VALVES_DATA_PUB_INTERVAL; valvesDataPublishInterval.ovData = VALVES_DATA_PUB_INTERVAL; valvesDataPublishInterval.ovInitData = 0; valvesDataPublishInterval.override = OVERRIDE_RESET; #ifdef ASN_DEMO currentValveStates[ H1_VALV ].positionsABC[ VALVE_POSITION_C_CLOSE ] = ROTARY_VALVE_MICROSTEP_FRACTION; currentValveStates[ H1_VALV ].positionsABC[ VALVE_POSITION_B_OPEN ] = 1728; currentValveStates[ H1_VALV ].positionsABC[ VALVE_POSITION_A_INSERT_EJECT ] = 880; currentValveStates[ H19_VALV ].positionsABC[ VALVE_POSITION_C_CLOSE ] = ROTARY_VALVE_MICROSTEP_FRACTION; currentValveStates[ H19_VALV ].positionsABC[ VALVE_POSITION_B_OPEN ] = 1728; currentValveStates[ H19_VALV ].positionsABC[ VALVE_POSITION_A_INSERT_EJECT ] = 880; #endif } /*********************************************************************//** * @brief * The homeValve function requests a valve homing operation on a given valve. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if given valve is invalid. * @details \b Inputs: currentValveStates[] * @details \b Outputs: currentValveStates[] * @param valve ID of 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 ) { VALVE_STATE_T currState = currentValveStates[ valve ].controlState; if ( ( VALVE_STATE_HOMING_NOT_STARTED == currState ) || ( VALVE_STATE_IDLE == currState ) ) { // If haven't already homed the valves, home the valves if ( ( currentValveStates[ valve ].hasValveBeenHomed != TRUE ) || ( VALVE_FORCE_HOME == force ) ) { currentValveStates[ 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_TD_SOFTWARE_FAULT, SW_FAULT_ID_TD_VALVES_INVALID_VALVE8, (U32)valve ) } return result; } /*********************************************************************//** * @brief * The setValvePosition function sets the commanded position for a given valve. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if invalid valve or position given. * @details \b Inputs: none * @details \b Outputs: currentValveStates[] * @param valve ID of valve to command position for * @param position ID of position to command valve to * @return TRUE if command accepted, FALSE if not *************************************************************************/ BOOL setValvePosition( VALVE_T valve, VALVE_POSITION_T position ) { BOOL result = FALSE; if ( ( valve < NUM_OF_VALVES ) && ( position < NUM_OF_VALVE_POSITIONS ) ) { currentValveStates[ valve ].pendingCommandedPosition = position; currentValveStates[ valve ].hasTransitionBeenRequested = TRUE; result = TRUE; } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_TD_VALVES_INVALID_PARAM, ( (U32)valve << SHIFT_16_BITS_FOR_WORD_SHIFT ) | (U32)position ) } return result; } /*********************************************************************//** * @brief * The getValvePosition function returns the current position of a given valve. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if given valve is invalid. * @details \b Inputs: currentValveStates[] * @details \b Outputs: none * @param valve ID of valve to get current position of * @return Current position of the given valve *************************************************************************/ VALVE_POSITION_T getValvePosition( VALVE_T valve ) { VALVE_POSITION_T result = VALVE_POSITION_NOT_IN_POSITION; if ( valve < NUM_OF_VALVES ) { result = currentValveStates[ valve ].currentPosition; } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_TD_VALVES_INVALID_VALVE1, (U32)valve ) } return result; } /*********************************************************************//** * @brief * The execValvesController function executes the valves state machine. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if a valve control state is invalid. * @details \b Inputs: currentValveStates[] * @details \b Outputs: currentValveStates[] * @return none *************************************************************************/ void execValvesController( void ) { VALVE_T valve; // get latest valve readings from FPGA readValves(); for ( valve = FIRST_VALVE; valve < NUM_OF_VALVES; valve++ ) { // update valve encoder positions currentValveStates[ valve ].priorEncPosition = currentValveStates[ valve ].currentEncPosition; currentValveStates[ valve ].currentEncPosition = getValveEncoderPosition( valve ); // execute valve state machine switch ( currentValveStates[ valve ].controlState ) { case VALVE_STATE_WAIT_FOR_POST: currentValveStates[ valve ].controlState = handleValvesWait4PostState( valve ); break; case VALVE_STATE_RESET_VALVE: currentValveStates[ valve ].controlState = handleValvesResetValve( valve ); break; case VALVE_STATE_RESET_ENCODER: currentValveStates[ valve ].controlState = handleValvesResetEncoder( valve ); break; case VALVE_STATE_ENABLE_VALVE: currentValveStates[ valve ].controlState = handleValvesEnableValve( valve ); break; case VALVE_STATE_HOMING_NOT_STARTED: currentValveStates[ valve ].controlState = handleValvesNotHomedState( valve ); break; case VALVE_STATE_HOMING_FIND_ENERGIZED_EDGE: currentValveStates[ valve ].controlState = handleValvesFindEnergizedEdgeState( valve ); break; case VALVE_STATE_IDLE: currentValveStates[ valve ].controlState = handleValvesIdleState( valve ); break; case VALVE_STATE_IN_TRANSITION: currentValveStates[ valve ].controlState = handleValvesTransitionState( valve ); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_TD_VALVES_INVALID_STATE, (U32)currentValveStates[ valve ].controlState ) break; } } publishValvesData(); } /*********************************************************************//** * @brief * The handleValvesWait4PostState function handles the Wait for POST state * of the state machine for a given valve. * @details \b Inputs: current operating mode * @details \b Outputs: none * @param valve ID of valve for which to handle the Wait for POST state * @return next state of the state machine for the given valve *************************************************************************/ static VALVE_STATE_T handleValvesWait4PostState( VALVE_T valve ) { VALVE_STATE_T nextState = VALVE_STATE_WAIT_FOR_POST; checkValveInRange( valve, SW_FAULT_ID_TD_VALVES_INVALID_WAIT_FOR_POST_STATE ); if ( getCurrentOperationMode() != MODE_INIT ) { nextState = VALVE_STATE_RESET_VALVE; } return nextState; } /*********************************************************************//** * @brief * The handleValvesResetValve function handles the reset valve of the state * machine for a given valve. * @details \b Inputs: current operating mode * @details \b Outputs: none * @param valve ID of valve for which to handle the Wait for POST state * @return next state of the state machine for the given valve *************************************************************************/ static VALVE_STATE_T handleValvesResetValve( VALVE_T valve ) { VALVE_STATE_T nextState = VALVE_STATE_RESET_ENCODER; checkValveInRange( valve, SW_FAULT_ID_TD_VALVES_INVALID_RESET ); resetValve( valve ); return nextState; } /*********************************************************************//** * @brief * The handleValvesResetEncoder function handles the reset encoder of the state * machine for a given valve. * @details \b Inputs: current operating mode * @details \b Outputs: none * @param valve ID of valve for which to handle the Wait for POST state * @return next state of the state machine for the given valve *************************************************************************/ static VALVE_STATE_T handleValvesResetEncoder( VALVE_T valve ) { VALVE_STATE_T nextState = VALVE_STATE_ENABLE_VALVE; checkValveInRange( valve, SW_FAULT_ID_TD_VALVES_INVALID_RESET_ENCODER ); resetValveEncoder( valve ); // Set the timer for the first debounce read currentValveStates[ valve ].valveOpsStartTime = getMSTimerCount(); return nextState; } /*********************************************************************//** * @brief * The handleValvesEnableValve function handles the enable valve of the state * machine for a given valve. * @details \b Inputs: currentValveStates[] * @details \b Outputs: currentValveStates[] * @param valve ID of valve for which to handle the Wait for POST state * @return next state of the state machine for the given valve *************************************************************************/ static VALVE_STATE_T handleValvesEnableValve( VALVE_T valve ) { VALVE_STATE_T nextState = VALVE_STATE_ENABLE_VALVE; S16 valveEncPosistion = getValveEncoderPosition( valve ); checkValveInRange( valve, SW_FAULT_ID_TD_VALVES_INVALID_ENABLE ); if ( FALSE == currentValveStates[ valve ].hasValveBeenReset ) { enableValve( valve ); currentValveStates[ valve ].hasValveBeenReset = TRUE; } else { setValveZeroEncoderPosition( valve ); } if ( valveEncPosistion >= ZERO_ENC_DEBOUNCE_THRESHOLD_CNT ) { // Keep reseting the debounce timer until the encoder count is less than the encoder count is less than the number currentValveStates[ valve ].valveOpsStartTime = getMSTimerCount(); } else if ( TRUE == didTimeout( currentValveStates[ valve ].valveOpsStartTime, ZERO_ENC_DEBOUNCE_TIMEOUT_MS ) ) { // Getting ready for homing enable the homing bit upon transitioning to homing not started setValveHomingEnableDisable( valve, TRUE ); // Position C is hard coded as count 8. // Position D is hard coded as an offset form 0. currentValveStates[ valve ].positionsABC[ VALVE_POSITION_C_CLOSE ] = POS_C_FROM_ZERO_CNT; currentValveStates[ valve ].positionsABC[ VALVE_POSITION_D_PARTIAL_CLOSE ] = POS_D_PARTIAL_CLOSE_FROM_ZERO_CNT; nextState = VALVE_STATE_HOMING_NOT_STARTED; } return nextState; } /*********************************************************************//** * @brief * The handleValvesNotHomedState function handles the Not Homed state * of the state machine for a given valve. * @details \b Inputs: currentValveStates[] * @details \b Outputs: currentValveStates[] * @param valve ID of valve for which to handle the Not Homed state * @return next state of the state machine for the given valve *************************************************************************/ static VALVE_STATE_T handleValvesNotHomedState( VALVE_T valve ) { VALVE_STATE_T nextState = VALVE_STATE_HOMING_NOT_STARTED; checkValveInRange( valve, SW_FAULT_ID_TD_VALVES_INVALID_NOT_HOMED ); if ( TRUE == currentValveStates[ valve ].hasHomingBeenRequested ) { currentValveStates[ valve ].proposedEnergizedPos = ROTARY_VALVE_INIT_FULL_SWING_COUNTS; // TODO remove this? What is the criteria for the homing failure? if ( ( currentValveStates[ valve ].proposedEnergizedPos <= ROTARY_VALVE_ENERGIZED_EDGE_MIN_COUNTS ) || ( currentValveStates[ valve ].proposedEnergizedPos >= ROTARY_VALVE_ENERGIZED_EDGE_MAX_COUNTS ) ) { currentValveStates[ valve ].hasHomingFailed = TRUE; currentValveStates[ valve ].hasHomingBeenRequested = FALSE; SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_VALVE_HOMING_FAILED, (U32)valve, (U32)currentValveStates[ valve ].proposedEnergizedPos ) } else { // Command valve to move to energized edge (end of travel) currentValveStates[ valve ].valveOpsStartTime = 0; currentValveStates[ valve ].hasValveBeenHomed = FALSE; currentValveStates[ valve ].currentPosition = VALVE_POSITION_NOT_IN_POSITION; nextState = VALVE_STATE_HOMING_FIND_ENERGIZED_EDGE; setValveCmdChangePosition( valve, (U16)currentValveStates[ valve ].proposedEnergizedPos, MOTOR_DIR_FORWARD ); } } return nextState; } /*********************************************************************//** * @brief * The handleValvesFindEnergizedEdgeState function handles the Find Energized * Edge state of the state machine for a given valve. * @details \b Inputs: currentValveStates[] * @details \b Outputs: currentValveStates[] * @param valve ID of valve for which to handle the Find Energized Edge state * @return next state of the state machine for the given valve *************************************************************************/ static VALVE_STATE_T handleValvesFindEnergizedEdgeState( VALVE_T valve ) { VALVE_STATE_T nextState = VALVE_STATE_HOMING_FIND_ENERGIZED_EDGE; S16 currPos = currentValveStates[ valve ].currentEncPosition; BOOL hasEdgeBeenReached = FALSE; checkValveInRange( valve, SW_FAULT_ID_TD_VALVES_INVALID_FIND_ENERGIZED_EDGE ); // have we found forward edge of travel? if ( abs( currPos - currentValveStates[ valve ].priorEncPosition ) < VALVE_HOME_MIN_POS_CHG ) { if ( 0 == currentValveStates[ valve ].valveOpsStartTime ) { currentValveStates[ valve ].valveOpsStartTime = getMSTimerCount(); } else if ( TRUE == didTimeout( currentValveStates[ valve ].valveOpsStartTime, HOMING_EDGE_DETECTION_TIMEOUT_MS ) ) { hasEdgeBeenReached = TRUE; } } else if ( currPos <= 0 ) { // Lost the shaft. Get the maximum value in the special register as the maximum. S16 maxEncPosition = getValveMaximumEncoderPosition( valve ); hasEdgeBeenReached = TRUE; currPos = maxEncPosition - VALVE_OFFEST_FROM_EDG_CNT; } if ( TRUE == hasEdgeBeenReached ) { // Done with homing disable the homing bit setValveHomingEnableDisable( valve, FALSE ); S16 posC = currentValveStates[ valve ].positionsABC[ VALVE_POSITION_C_CLOSE ]; S16 posA = posC + ( ( currPos - posC ) / 2 ); // If position A was calculated to not be a division of 8 it is rounded down to be a division of 8 posA = ( posA % ROTARY_VALVE_MICROSTEP_FRACTION ) < 4 ? ( posA >> 3 ) << 3 : posA; currentValveStates[ valve ].positionsABC[ VALVE_POSITION_B_OPEN ] = currPos; currentValveStates[ valve ].positionsABC[ VALVE_POSITION_A_INSERT_EJECT ] = posA; currentValveStates[ valve ].hasValveBeenHomed = TRUE; currentValveStates[ valve ].currentPosition = VALVE_POSITION_B_OPEN; currentValveStates[ valve ].pendingCommandedPosition = VALVE_POSITION_A_INSERT_EJECT; currentValveStates[ valve ].hasTransitionBeenRequested = TRUE; currentValveStates[ valve ].hasHomingBeenRequested = FALSE; nextState = VALVE_STATE_IDLE; } return nextState; } /*********************************************************************//** * @brief * The handleValvesIdleState function handles the Idle state of the state * machine for a given valve. * @details \b Inputs: currentValveStates[] * @details \b Outputs: currentValveStates[], cmd to FPGA * @param valve ID of valve for which to handle the Idle state * @return next state of the state machine for the given valve *************************************************************************/ static VALVE_STATE_T handleValvesIdleState( VALVE_T valve ) { VALVE_STATE_T nextState = VALVE_STATE_IDLE; checkValveInRange( valve, SW_FAULT_ID_TD_VALVES_INVALID_IDLE ); // handle a home request if ( TRUE == currentValveStates[ valve ].hasHomingBeenRequested ) { S16 tgtPos = currentValveStates[ valve ].positionsABC[ VALVE_POSITION_C_CLOSE ]; S16 mag = currentValveStates[ valve ].currentEncPosition - tgtPos; // calculate magnitude of position change // Set commanded position and transition start time stamp // Transition back to position C to try the next energized position. It has to start from position C currentValveStates[ valve ].commandedPosition = VALVE_POSITION_C_CLOSE; currentValveStates[ valve ].valveOpsStartTime = getMSTimerCount(); currentValveStates[ valve ].hasValveBeenReset = FALSE; nextState = VALVE_STATE_IN_TRANSITION; // Command FPGA to move valve to commanded position setValveCmdChangePosition( valve, (U16)(mag), MOTOR_DIR_REVERSE ); } // handle a position change request else if ( TRUE == currentValveStates[ valve ].hasTransitionBeenRequested ) { currentValveStates[ valve ].hasTransitionBeenRequested = FALSE; if ( currentValveStates[ valve ].currentPosition != currentValveStates[ valve ].pendingCommandedPosition ) { S16 curPos = currentValveStates[ valve ].currentEncPosition; S16 tgtPos = currentValveStates[ valve ].positionsABC[ currentValveStates[ valve ].pendingCommandedPosition ]; S16 mag = tgtPos - curPos; // calculate magnitude of position change MOTOR_DIR_T dir = ( mag < 0 ? MOTOR_DIR_REVERSE : MOTOR_DIR_FORWARD ); // determine direction of position change // Set commanded position and transition start time stamp currentValveStates[ valve ].commandedPosition = currentValveStates[ valve ].pendingCommandedPosition; currentValveStates[ valve ].valveOpsStartTime = getMSTimerCount(); nextState = VALVE_STATE_IN_TRANSITION; // Command FPGA to move valve to commanded position setValveCmdChangePosition( valve, (U16)( abs(mag) ), dir ); } } return nextState; } /*********************************************************************//** * @brief * The handleValvesTransitionState function handles the Transition state * of the state machine for a given valve. * @details \b Inputs: currentValveStates[] * @details \b Outputs: currentValveStates[] * @param valve ID of valve for which to handle the Transition state * @return next state of the state machine for the given valve *************************************************************************/ static VALVE_STATE_T handleValvesTransitionState( VALVE_T valve ) { VALVE_STATE_T nextState = VALVE_STATE_IN_TRANSITION; S16 curPos = currentValveStates[ valve ].currentEncPosition; S16 tgtPos = currentValveStates[ valve ].positionsABC[ currentValveStates[ valve ].commandedPosition ]; S16 delta = tgtPos - curPos; checkValveInRange( valve, SW_FAULT_ID_TD_VALVES_INVALID_IN_TRANSITION ); // have we completed the transition? if ( abs( delta ) < VALVE_TRANSITION_MIN_TGT_DELTA ) { currentValveStates[ valve ].currentPosition = currentValveStates[ valve ].commandedPosition; } // is transition taking too long? else if ( ( TRUE == didTimeout( currentValveStates[ valve ].valveOpsStartTime, 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 currentValveStates[ valve ].currentPosition = VALVE_POSITION_NOT_IN_POSITION; nextState = VALVE_STATE_IDLE; SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_VALVE_TRANSITION_TIMEOUT, (U32)valve, (U32)abs( delta ) ); // If the valve's commanded position is C, activate safety shutdown if ( VALVE_POSITION_C_CLOSE == currentValveStates[ valve ].commandedPosition ) { activateSafetyShutdown(); } } return nextState; } /*********************************************************************//** * @brief * The execValvesSelfTest function executes the valves self-test. Calibration * factors are loaded from non-volatile memory and CRC checked. * @details \b Inputs: Calibration record stored in non-volatile memory. * @details \b Outputs: valvesCalibrationRecord * @return self-test result (pass/fail) *************************************************************************/ SELF_TEST_STATUS_T execValvesSelfTest( void ) { BOOL calStatus = TRUE; /* TODO getNVRecord2Driver( GET_CAL_VALVES, (U08*)&valvesCalibrationRecord, sizeof( HD_VALVES_CAL_RECORD_T ), NUM_OF_CAL_DATA_HD_VALVES, ALARM_ID_NO_ALARM );*/ if ( TRUE == calStatus ) { valvesSelfTestResult = SELF_TEST_STATUS_PASSED; } else { valvesSelfTestResult = SELF_TEST_STATUS_FAILED; } return valvesSelfTestResult; } /*********************************************************************//** * @brief * The checkValveInRange function checks whether the selected valve is in range * and if not it will trigger the provided software fault. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if given valve is invalid. * @details \b Inputs: none * @details \b Outputs: none * @param valve the valve check whether it is in range * @param SWFault the provided software fault to trigger * @return none *************************************************************************/ static void checkValveInRange( VALVE_T valve, SW_FAULT_ID_T SWFault ) { if ( valve >= NUM_OF_VALVES ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, (U32)SWFault, (U32)valve ) } } /*********************************************************************//** * @brief * The publishValvesData function constructs and sends the valves data * broadcast message. * @details \b Message \b Sent: MSG_ID_TD_VALVES_DATA * @details \b Inputs: valvesDataPublicationTimerCounter, valvesDataPublishInterval * @details \b Outputs: valvesDataPublicationTimerCounter * @return none *************************************************************************/ static void publishValvesData( void ) { if ( ++valvesDataPublicationTimerCounter >= getU32OverrideValue( &valvesDataPublishInterval ) ) { VALVE_T valve; for ( valve = FIRST_VALVE; valve < NUM_OF_VALVES; valve++ ) { TD_VALVE_DATA_T data; data.valveID = (U32)valve; data.state = (U32)currentValveStates[ valve ].controlState; data.currentPosID = (U32)currentValveStates[ valve ].currentPosition; data.currentPos = getValveEncoderPosition( valve ); data.cmdPos = currentValveStates[ valve ].commandedPosition; data.posA = currentValveStates[ valve ].positionsABC[ VALVE_POSITION_A_INSERT_EJECT ]; data.posB = currentValveStates[ valve ].positionsABC[ VALVE_POSITION_B_OPEN ]; data.posC = currentValveStates[ valve ].positionsABC[ VALVE_POSITION_C_CLOSE ]; data.posD = currentValveStates[ valve ].positionsABC[ VALVE_POSITION_D_PARTIAL_CLOSE ]; broadcastData( MSG_ID_TD_VALVES_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)&data, sizeof( TD_VALVE_DATA_T ) ); } valvesDataPublicationTimerCounter = 0; } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testValvesDataPublishIntervalOverride function overrides the interval * at which the TD valves data is published. * @details \b Inputs: none * @details \b Outputs: valvesDataPublishInterval * @param message Override message from Dialin which includes the interval * (in ms) to override the valves broadcast interval to. * @return TRUE if override request is successful, FALSE if not *************************************************************************/ BOOL testValvesDataPublishIntervalOverride( MESSAGE_T *message ) { BOOL result = u32BroadcastIntervalOverride( message, &valvesDataPublishInterval, TASK_GENERAL_INTERVAL ); return result; } /*********************************************************************//** * @brief * The testValveSetABCCmdPosition function handles a Dialin command to set * a given valve to a given A/B/C position. * @details \b Inputs: none * @details \b Outputs: currentValveStates[] * @param message Override message from Dialin which includes the ID of the * valve and the position to command it to. * @return TRUE if override request is successful, FALSE if not *************************************************************************/ BOOL testValveSetABCCmdPosition( MESSAGE_T *message ) { BOOL result = FALSE; // Verify tester has logged in with TD if ( TRUE == isTestingActivated() ) { // Verify payload length is valid if ( sizeof( VALVE_PINCH_SET_CMD_PAYLOAD_T ) == message->hdr.payloadLen ) { VALVE_PINCH_SET_CMD_PAYLOAD_T payload; memcpy( &payload, message->payload, sizeof(VALVE_PINCH_SET_CMD_PAYLOAD_T) ); if ( ( (VALVE_T)payload.valve < NUM_OF_VALVES ) && ( (VALVE_POSITION_T)payload.pos < NUM_OF_VALVE_POSITIONS ) ) { result = setValvePosition( (VALVE_T)payload.valve, (VALVE_POSITION_T)payload.pos ); } } } return result; } /*********************************************************************//** * @brief * The testHomeValve function handles a Dialin command to home a given valve. * @details \b Inputs: none * @details \b Outputs: currentValveStates[] * @param message Override message from Dialin which includes the ID of the * valve to home and related flags. * @return TRUE if override request is successful, FALSE if not *************************************************************************/ BOOL testHomeValve( MESSAGE_T *message ) { BOOL result = FALSE; // Verify tester has logged in with TD if ( TRUE == isTestingActivated() ) { // Verify payload length is valid if ( sizeof( VALVE_PINCH_HOME_CMD_PAYLOAD_T ) == message->hdr.payloadLen ) { VALVE_PINCH_HOME_CMD_PAYLOAD_T payload; memcpy( &payload, message->payload, sizeof(VALVE_PINCH_HOME_CMD_PAYLOAD_T) ); if ( (VALVE_T)payload.valve < NUM_OF_VALVES ) { result = homeValve( (VALVE_T)payload.valve, payload.force, payload.tubing ); } } } return result; } /**@}*/