Index: firmware/App/Controllers/Valves.c =================================================================== diff -u -r59c806b6d87e3cb3baf3ef2d94c2e341247f9a2a -rfd21f03d28eec499fb5c01bf595c57893db94406 --- firmware/App/Controllers/Valves.c (.../Valves.c) (revision 59c806b6d87e3cb3baf3ef2d94c2e341247f9a2a) +++ firmware/App/Controllers/Valves.c (.../Valves.c) (revision fd21f03d28eec499fb5c01bf595c57893db94406) @@ -7,8 +7,8 @@ * * @file Valves.c * -* @author (last) Varshini Nagabooshanam -* @date (last) 23-Jan-2026 +* @author (last) Michael Garthwaite +* @date (last) 15-Apr-2026 * * @author (original) Vinayakam Mani * @date (original) 26-Aug-2024 @@ -35,7 +35,7 @@ #define ENERGIZED 1 ///< 1 for energized valve. #define ALL_VALVES_DEENERGIZED 0x0000 ///< 0 in U16 bit field for all valves. -#define VALVE_PWM_PERIOD 50 ///< Valve PWM Period in 0.1 us res ( 20kHz ) +#define VALVE_PWM_PERIOD 500 ///< Valve PWM Period in 0.1 us res ( 20kHz ) #define VALVE_PWM_LOW ( VALVE_PWM_PERIOD * 0.2 ) ///< Valve low PWM percentage #define VALVE_PWM_PULL_IN 100 ///< Valve 'strike' period at 100% PWM in ms @@ -67,6 +67,7 @@ static OVERRIDE_U32_T valveSensedStates[ DD_NUM_OF_VALVES ]; ///< Valve sensed states override. static OVERRIDE_U32_T valvesStatesPublishInterval; ///< Interval (in ms/task interval) at which to publish valves state to CAN bus. static OVERRIDE_U32_T fpValveStatesPublishInterval; ///< Interval (in ms/task interval) at which to publish valves state to CAN bus. +static RECOVERY_STATE_T recoveryState; // ********** private function prototypes ********** @@ -97,6 +98,7 @@ fpValveStatesPublishInterval.ovData = VALVES_STATE_PUB_INTERVAL; fpValveStatesPublishInterval.ovInitData = 0; fpValveStatesPublishInterval.override = OVERRIDE_RESET; + recoveryState = MAX_RECOVERY; // Initialize commanded valve states for ( i = 0; i < DD_NUM_OF_VALVES; i++ ) @@ -126,7 +128,7 @@ // TODO set FPGA PWM after finalizing the decision on valve strike and hold behavior // initially set valve PWM configuration and enable -// setFPGAPWMConfigurations(); + //setFPGAPWMConfigurations(); // initially set all valves to de-energized state via FPGA setFPGADDValveStates( commandedValvesStates ); @@ -213,6 +215,37 @@ U08 readValveIOStates = getFPGAIOValveStates(); U08 readValveFPStates = getFPGAFPValveStates(); + // Update sensed valve states from FPGA readback (unless overridden via override mechanism) + for ( i = FIRST_HYD_VALVE; i <= LAST_HYD_VALVE; i++ ) + { + U16 mask = (U16)( 0x01u << i ); + valveSensedStates[ i ].data = ( ( readValvesStates & mask ) != 0u ? ENERGIZED : DEENERGIZED ); + } + + for ( i = FIRST_BC_VALVE; i <= LAST_BC_VALVE; i++ ) + { + U08 mask = (U08)( 0x01u << ( i - FIRST_BC_VALVE ) ); + valveSensedStates[ i ].data = ( ( readValveBCStates & mask ) != 0u ? ENERGIZED : DEENERGIZED ); + } + + for ( i = FIRST_SP_VALVE; i <= LAST_SP_VALVE; i++ ) + { + U08 mask = (U08)( 0x01u << ( i - FIRST_SP_VALVE ) ); + valveSensedStates[ i ].data = ( ( readValveSpStates & mask ) != 0u ? ENERGIZED : DEENERGIZED ); + } + + for ( i = FIRST_IO_VALVE; i <= LAST_IO_VALVE; i++ ) + { + U08 mask = (U08)( 0x01u << ( i - FIRST_IO_VALVE ) ); + valveSensedStates[ i ].data = ( ( readValveIOStates & mask ) != 0u ? ENERGIZED : DEENERGIZED ); + } + + for ( i = FIRST_FP_VALVE; i <= LAST_FP_VALVE; i++ ) + { + U08 mask = (U08)( 0x01u << ( i - FIRST_FP_VALVE ) ); + valveSensedStates[ i ].data = ( ( readValveFPStates & mask ) != 0u ? ENERGIZED : DEENERGIZED ); + } + // Verify read back FPGA valve states match last commanded valve states if ( ( readValvesStates != commandedValvesStates ) || ( readValveBCStates != commandedBCValveStates ) || @@ -259,6 +292,126 @@ /*********************************************************************//** * @brief + * The setValveState function sets the valve state for given valve. + * @details \b Inputs: none + * @details \b Outputs: valveStates[], pendingValveStateChanges[], pendingValveStateChangeCountDowns[] + * @details \b Alarm: ALARM_ID_DD_SOFTWARE_FAULT when invalid valve ID passed + * @param valveID ID of valve to set state for + * @param valveStateName name of valve state to set given valve to + * @return TRUE if new valve state is set for given valve ID, FALSE if not. + *************************************************************************/ +BOOL setValveState( DD_VALVES_T valveID, VALVE_STATE_NAMES_T valveStateName ) +{ + BOOL result = FALSE; // initialize result flag to FALSE + + if ( valveID < DD_NUM_OF_VALVES ) + { + if ( TRUE == checkValveStateName( valveID, valveStateName ) ) + { + valveStates[ valveID ].data = convertValveStateNameToValveState( valveStateName ); + result = TRUE; + // If a delayed state change is pending for this valve, cancel it + pendingValveStateChanges[ valveID ] = DEENERGIZED; + pendingValveStateChangeCountDowns[ valveID ] = 0; + } + } + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_VALVES_INVALID_VALVE_ID, valveID ) + } + + return result; +} + +/*********************************************************************//** + * @brief + * The setValveStateDelayed function sets the valve state for given valve + * after a given delay. + * @details \b Inputs: pendingValveStateChangeCountDowns[] + * @details \b Outputs: pendingValveStateChangeCountDowns[], pendingValveStateChangeCountDowns[] + * @details \b Alarm: ALARM_ID_DD_SOFTWARE_FAULT when invalid valve ID passed + * @param valveID ID of valve to set state for + * @param valveStateName name of valve state to set given valve to + * @param delayMs delay duration (in ms) before actuation + * @return TRUE if new valve state is set for given valve ID, FALSE if not. + *************************************************************************/ +BOOL setValveStateDelayed( DD_VALVES_T valveID, VALVE_STATE_NAMES_T valveStateName, U32 delayMs ) +{ + BOOL result = FALSE; // initialize result flag to FALSE + + if ( valveID < DD_NUM_OF_VALVES ) + { + if ( TRUE == checkValveStateName( valveID, valveStateName ) ) + { + // If a delayed state change is already pending for this valve, execute it now before setting a new delayed state change + if ( pendingValveStateChangeCountDowns[ valveID ] > 0 ) + { + valveStates[ valveID ].data = pendingValveStateChanges[ valveID ]; + } + // Set delayed valve state change + pendingValveStateChanges[ valveID ] = convertValveStateNameToValveState( valveStateName ); + pendingValveStateChangeCountDowns[ valveID ] = delayMs / TASK_PRIORITY_INTERVAL; + result = TRUE; + } + } + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_VALVES_INVALID_VALVE_ID, valveID ) + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getValveStateName function gets the current valve state enum for given valve. + * @details \b Inputs: valveStates[] + * @details \b Outputs: none + * @details \b Alarm: ALARM_ID_DD_SOFTWARE_FAULT when invalid valve ID passed. + * @param valveID ID of valve to get state for + * @return the current valve state for given valve in enum + *************************************************************************/ +VALVE_STATE_NAMES_T getValveStateName( DD_VALVES_T valveID ) +{ + // Initialized per CppCheck. + VALVE_STATE_NAMES_T name = NUM_OF_VALVE_STATES; + + if ( valveID < DD_NUM_OF_VALVES ) + { + U32 valveState = getU32OverrideValue( &valveSensedStates[ valveID ] ); + + name = ( DEENERGIZED == valveState ? VALVE_STATE_CLOSED : VALVE_STATE_OPEN ); + } + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_VALVES_INVALID_VALVE_ID, valveID ) + } + + return name; +} + + +/*********************************************************************//** + * @brief + * The setHydValvesStatetoClosedState function sets all Hydraulics / DD + * valves to close state except balancing chamber, UF and Inlet water control. + * @details \b Inputs: none + * @details \b Outputs: valve states + * @return none. + *************************************************************************/ +void setHydValvesStatetoClosedState( void ) +{ + DD_VALVES_T valve; + + // Set all Hydraulics valves to closed state + for ( valve = FIRST_HYD_VALVE; valve <= LAST_HYD_VALVE; valve++ ) + { + setValveState( valve, VALVE_STATE_CLOSED ); + } +} + +/*********************************************************************//** + * @brief * The readCommandedValveStates function gets the hydraulics, Balancing chamber * and Ultrafiltration valvesStates that can be passed to FPGA for setting valves. * @details \b Inputs: valveStates[] @@ -330,7 +483,7 @@ ( D65_VALV == valveID ) || ( D64_VALV == valveID ) || ( D31_VALV == valveID ) || ( D34_VALV == valveID ) || ( D35_VALV == valveID ) || ( D40_VALV == valveID ) || ( D47_VALV == valveID ) || ( D3_VALV == valveID ) || ( M4_VALV == valveID ) || ( D23_VALV == valveID ) || ( D19_VALV == valveID ) || ( D25_VALV == valveID ) || ( D21_VALV == valveID ) || ( D24_VALV == valveID ) || ( D20_VALV == valveID ) || - ( D26_VALV == valveID ) || ( D22_VALV == valveID ) || ( D80_VALV == valveID ) || ( D81_VALV == valveID ) || ( D79_PMP_VALV == valveID ) || + ( D26_VALV == valveID ) || ( D22_VALV == valveID ) || ( D80_VALV == valveID ) || ( D81_VALV == valveID ) || ( D88_79_VALV == valveID ) || ( P11_VALV == valveID ) || ( P33_VALV == valveID ) || ( P34_VALV == valveID ) || ( P37_VALV == valveID ) || ( P6_VALV == valveID ) || ( M12_VALV == valveID ) || ( P39_VALV == valveID ) || ( D83_VALV == valveID ) || ( D91_VALV == valveID ) || ( D100_VALV == valveID ) || ( D85_VALV == valveID ) ) { @@ -380,145 +533,92 @@ /*********************************************************************//** * @brief - * The setValveState function sets the valve state for given valve. - * @details \b Inputs: none - * @details \b Outputs: valveStates[], pendingValveStateChanges[], pendingValveStateChangeCountDowns[] - * @details \b Alarm: ALARM_ID_DD_SOFTWARE_FAULT when invalid valve ID passed - * @param valveID ID of valve to set state for - * @param valveStateName name of valve state to set given valve to - * @return TRUE if new valve state is set for given valve ID, FALSE if not. + * The getValveState function gets the current valve state for given valve. + * @details \b Inputs: valveStates[] + * @details \b Outputs: none + * @details \b Alarm : ALARM_ID_DD_SOFTWARE_FAULT when invalid valve ID passed + * @param valveID ID of valve to get state for + * @return the current valve state for given valve *************************************************************************/ -BOOL setValveState( DD_VALVES_T valveID, VALVE_STATE_NAMES_T valveStateName ) +static U32 getValveState( U32 valveID ) { - BOOL result = FALSE; // initialize result flag to FALSE + U32 valveState = DEENERGIZED; if ( valveID < DD_NUM_OF_VALVES ) { - if ( TRUE == checkValveStateName( valveID, valveStateName ) ) - { - valveStates[ valveID ].data = convertValveStateNameToValveState( valveStateName ); - result = TRUE; - // If a delayed state change is pending for this valve, cancel it - pendingValveStateChanges[ valveID ] = DEENERGIZED; - pendingValveStateChangeCountDowns[ valveID ] = 0; - } + valveState = getU32OverrideValue( &valveStates[ valveID ] ); } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_VALVES_INVALID_VALVE_ID, valveID ) } - return result; + return valveState; } /*********************************************************************//** * @brief - * The setValveStateDelayed function sets the valve state for given valve - * after a given delay. - * @details \b Inputs: pendingValveStateChangeCountDowns[] - * @details \b Outputs: pendingValveStateChangeCountDowns[], pendingValveStateChangeCountDowns[] - * @details \b Alarm: ALARM_ID_DD_SOFTWARE_FAULT when invalid valve ID passed - * @param valveID ID of valve to set state for - * @param valveStateName name of valve state to set given valve to - * @param delayMs delay duration (in ms) before actuation - * @return TRUE if new valve state is set for given valve ID, FALSE if not. + * The getRecoveryValvesConfig function gets + * recovery configuration from institutional record. + * @details \b Inputs: recoveryState + * @details \b Outputs: none + * @return recoveryState. *************************************************************************/ -BOOL setValveStateDelayed( DD_VALVES_T valveID, VALVE_STATE_NAMES_T valveStateName, U32 delayMs ) +RECOVERY_STATE_T getRecoveryStatesConfig( void ) { - BOOL result = FALSE; // initialize result flag to FALSE - - if ( valveID < DD_NUM_OF_VALVES ) - { - if ( TRUE == checkValveStateName( valveID, valveStateName ) ) - { - // If a delayed state change is already pending for this valve, execute it now before setting a new delayed state change - if ( pendingValveStateChangeCountDowns[ valveID ] > 0 ) - { - valveStates[ valveID ].data = pendingValveStateChanges[ valveID ]; - } - // Set delayed valve state change - pendingValveStateChanges[ valveID ] = convertValveStateNameToValveState( valveStateName ); - pendingValveStateChangeCountDowns[ valveID ] = delayMs / TASK_PRIORITY_INTERVAL; - result = TRUE; - } - } - else - { - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_VALVES_INVALID_VALVE_ID, valveID ) - } - - return result; + return recoveryState; } /*********************************************************************//** * @brief - * The getValveStateName function gets the current valve state enum for given valve. - * @details \b Inputs: valveStates[] - * @details \b Outputs: none - * @details \b Alarm: ALARM_ID_DD_SOFTWARE_FAULT when invalid valve ID passed. - * @param valveID ID of valve to get state for - * @return the current valve state for given valve in enum + * The setRecoveryStatesConfig function gets + * recovery configuration from institutional record. + * @details \b Inputs: none + * @details \b Outputs: recoveryState + * @param recovery state to set valve config. + * @return none. *************************************************************************/ -VALVE_STATE_NAMES_T getValveStateName( DD_VALVES_T valveID ) +void setRecoveryStatesConfig( RECOVERY_STATE_T recovery ) { - // Initialized per CppCheck. - VALVE_STATE_NAMES_T name = NUM_OF_VALVE_STATES; - - if ( valveID < DD_NUM_OF_VALVES ) - { - U32 valveState = getU32OverrideValue( &valveSensedStates[ valveID ] ); - - name = ( DEENERGIZED == valveState ? VALVE_STATE_CLOSED : VALVE_STATE_OPEN ); - } - else - { - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_VALVES_INVALID_VALVE_ID, valveID ) - } - - return name; + recoveryState = recovery; } - /*********************************************************************//** * @brief - * The getValveState function gets the current valve state for given valve. - * @details \b Inputs: valveStates[] - * @details \b Outputs: none - * @details \b Alarm : ALARM_ID_DD_SOFTWARE_FAULT when invalid valve ID passed - * @param valveID ID of valve to get state for - * @return the current valve state for given valve + * The setRecoveryValvesConfig function sets all recovery valves per + * recovery configuration from institutional record. + * @details \b Inputs: recoveryState + * @details \b Outputs: P33, P34 and P37 recovery valve states + * @return none. *************************************************************************/ -static U32 getValveState( U32 valveID ) +void setRecoveryValvesConfig( void ) { - U32 valveState = DEENERGIZED; + // TODO define hdInstitutionalRecord and get the NVData when it's ready +// RECOVERY_STATE_T recoveryState = (RECOVERY_STATE_T)hdInstitutionalRecord.recoveryConfig; - if ( valveID < DD_NUM_OF_VALVES ) + // Set all P33, P34 and P37 valves per recovery configuration + switch( recoveryState ) { - valveState = getU32OverrideValue( &valveStates[ valveID ] ); - } - else - { - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_VALVES_INVALID_VALVE_ID, valveID ) - } + case NO_RECOVERY: + setValveState( P33_VALV, VALVE_STATE_CLOSED ); + setValveState( P34_VALV, VALVE_STATE_CLOSED ); + setValveState( P37_VALV, VALVE_STATE_CLOSED ); + break; - return valveState; -} + case MEDIUM_RECOVERY: + setValveState( P33_VALV, VALVE_STATE_OPEN ); + setValveState( P34_VALV, VALVE_STATE_CLOSED ); + setValveState( P37_VALV, VALVE_STATE_OPEN ); + break; -/*********************************************************************//** - * @brief - * The setHydValvesStatetoClosedState function sets all Hydraulics / DD - * valves to close state except balancing chamber, UF and Inlet water control. - * @details \b Inputs: none - * @details \b Outputs: valve states - * @return none. - *************************************************************************/ -void setHydValvesStatetoClosedState( void ) -{ - DD_VALVES_T valve; + case MAX_RECOVERY: + setValveState( P33_VALV, VALVE_STATE_OPEN ); + setValveState( P34_VALV, VALVE_STATE_CLOSED ); + setValveState( P37_VALV, VALVE_STATE_CLOSED ); + break; - // Set all Hydraulics valves to closed state - for ( valve = FIRST_HYD_VALVE; valve <= LAST_HYD_VALVE; valve++ ) - { - setValveState( valve, VALVE_STATE_CLOSED ); + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_FP_SOFTWARE_FAULT, FP_FAULT_ID_FP_INVALID_RECOVERY_VALVE_STATE, recoveryState ) + break; } } @@ -633,15 +733,35 @@ { DD_BC_VALVE_OVERRIDE_PAYLOAD_T payload; DD_VALVES_T valve; - BOOL result = FALSE; + U32 mask; + VALVE_STATE_NAMES_T pos; + OVERRIDE_TYPE_T ovType = OVERRIDE_INVALID; + BOOL result = FALSE; memcpy( (U08*)(&payload), message->payload, sizeof( DD_BC_VALVE_OVERRIDE_PAYLOAD_T ) ); - for ( valve = FIRST_BC_VALVE; valve <= LAST_BC_VALVE; valve++ ) + ovType = ( TRUE == payload.reset ? OVERRIDE_RESET_OVERRIDE : OVERRIDE_OVERRIDE ); + + // Verify tester has logged in with f/w and override type is valid + if ( ( TRUE == isTestingActivated() ) && ( ovType != OVERRIDE_INVALID ) && ( ovType < NUM_OF_OVERRIDE_TYPES ) ) { - U32 mask = 0x1 << (valve - FIRST_BC_VALVE); - VALVE_STATE_NAMES_T pos = ( ( ( payload.valveStatesBits & mask ) != 0 ) ? VALVE_STATE_OPEN : VALVE_STATE_CLOSED ); + for ( valve = FIRST_BC_VALVE; valve <= LAST_BC_VALVE; valve++ ) + { + mask = ( 0x1 << ( valve - FIRST_BC_VALVE ) ); + pos = ( ( ( payload.valveStatesBits & mask ) != 0 ) ? VALVE_STATE_OPEN : VALVE_STATE_CLOSED); - result = setValveState( valve, pos ); + if ( OVERRIDE_OVERRIDE == ovType ) + { + result = TRUE; + valveStates[ valve ].ovData = convertValveStateNameToValveState( pos ); + valveStates[ valve ].override = OVERRIDE_KEY; + } + else + { + result = TRUE; + valveStates[ valve ].override = OVERRIDE_RESET; + valveStates[ valve ].ovData = valveStates[ valve ].ovInitData; + } + } } return result; @@ -772,4 +892,39 @@ return result; } +/*********************************************************************//** + * @brief + * The testIOFPValveSensedStateOverride function overrides the value of the + * specified sensed state of an IOFP valve with a given value. + * @details \b Inputs: none + * @details \b Outputs: valveSensedStates[] + * @param message Override message from Dialin which includes an ID of + * the valve to override and the state to override the valve to. + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testIOFPSetValveRecoveryConfig( MESSAGE_T *message ) +{ + BOOL result = FALSE; + + // Verify tester has logged in with DD + if ( TRUE == isTestingActivated() ) + { + // Verify payload length is valid + if ( sizeof( U32 ) == message->hdr.payloadLen ) + { + U32 payload; + + memcpy( &payload, message->payload, sizeof(U32) ); + + if (payload < NUM_OF_RECOVERY_STATES ) + { + setRecoveryStatesConfig((RECOVERY_STATE_T)payload); + result = TRUE; + } + } + } + + return result; +} + /**@}*/