Index: firmware/App/Controllers/Valves.c =================================================================== diff -u -r986abcfcf047822cad1a10c1ee0924a80dd5f512 -r7d4711edd7b40cd3e29f43e766f79a8a09586fe9 --- firmware/App/Controllers/Valves.c (.../Valves.c) (revision 986abcfcf047822cad1a10c1ee0924a80dd5f512) +++ firmware/App/Controllers/Valves.c (.../Valves.c) (revision 7d4711edd7b40cd3e29f43e766f79a8a09586fe9) @@ -1,24 +1,26 @@ /************************************************************************** * -* Copyright (c) 2019-2022 Diality Inc. - All Rights Reserved. +* Copyright (c) 2020-2024 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) 11-Nov-2021 +* @author (last) Vinayakam Mani +* @date (last) 26-Sep-2023 * * @author (original) Sean * @date (original) 06-May-2020 * ***************************************************************************/ +#include // For memcpy #include "FPGA.h" #include "MessageSupport.h" #include "SystemCommMessages.h" -#include "TaskPriority.h" +#include "TaskPriority.h" +#include "Timers.h" #include "Valves.h" /** @@ -28,56 +30,81 @@ // ********** private definitions ********** -#define DEENERGIZED 0 ///< 0 for de-energized valve. -#define ENERGIZED 1 ///< 1 for energized valve. -#define ALL_VALVES_DEENERGIZED 0x0000 ///< 0 in U16 bit field for all valves. +#define DEENERGIZED 0 ///< 0 for de-energized valve. +#define ENERGIZED 1 ///< 1 for energized valve. +#define ALL_VALVES_DEENERGIZED 0x0000 ///< 0 in U16 bit field for all valves. -#define MAX_VALVE_STATE_MISMATCH_COUNT 3 ///< Maximum number of times commanded valves state can fail to match read back valve states in a row. +#define MAX_VALVE_STATE_MISMATCH_TIMER_COUNT (100 / TASK_PRIORITY_INTERVAL ) ///< Maximum time commanded valves state can fail to match read back valve states in a row. -#define VALVES_STATE_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Interval ( ms / task time) at which valves states are published on CAN bus. -#define DATA_PUBLISH_COUNTER_START_COUNT 50 ///< Data publish counter start count. +#define VALVES_STATE_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Interval ( ms / task time) at which valves states are published on CAN bus. +#define DATA_PUBLISH_COUNTER_START_COUNT 50 ///< Data publish counter start count. + +#define VPI_MIN_OPEN_TO_SKIP_DISINFECT_FLUSH_MS ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< VPi minimum required time to be open to skip disinfects flush state in milliseconds. +#define VPI_MAX_OPEN_WINDOW_TO_SKIP_DISINFECT_FLUSH_MS ( 30 * SEC_PER_MIN * MS_PER_SECOND ) ///< VPi maximum allowed time window that VPi could have been open in milliseconds. + +/// VPi open/close time status for disinfects +typedef struct +{ + U32 vpiOpenStartTimeMS; ///< VPi open start time in milliseconds. + U32 vpiOpenDurationMS; ///< VPi duration that is has been open in milliseconds. + U32 vpiClosedStartTimeMS; ///< VPi closed start time in milliseconds. +} VPI_OPEN_CLOSE_TIME_T; // ********** private data ********** static U32 valvesStatesPublicationTimerCounter; ///< Timer counter used to schedule valve state publication to CAN bus. static U16 commandedValvesStates = ALL_VALVES_DEENERGIZED; ///< Initialize commanded valves states bit field. -static U32 valveStateMismatchCounter = 0; ///< Initialize valve state mismatch counter. +static U32 valveStateMismatchTimerCounter; ///< Initialize valve state mismatch timer. static U32 pendingValveStateChanges[ NUM_OF_VALVES ]; ///< Delayed (pending) valve state changes. static U32 pendingValveStateChangeCountDowns[ NUM_OF_VALVES ]; ///< Delayed (pending) valve state change count down timers (in task intervals). - -static OVERRIDE_U32_T valveStates[ NUM_OF_VALVES ]; ///< Currently commanded valves states. + +static OVERRIDE_U32_T valveStates[ NUM_OF_VALVES ]; ///< Currently commanded valves states. +static OVERRIDE_U32_T valveSensedStates[ NUM_OF_VALVES ]; ///< Valve sensed states override. static OVERRIDE_U32_T valvesStatesPublishInterval = { VALVES_STATE_PUB_INTERVAL, VALVES_STATE_PUB_INTERVAL, 0, 0 }; ///< Interval (in ms/task interval) at which to publish valves state to CAN bus. +static VPI_OPEN_CLOSE_TIME_T vpiTime; ///< VPi open/close time structure. // ********** private function prototypes ********** static BOOL checkValveStateName( VALVES_T valve, VALVE_STATE_NAMES_T valveStateName ); -static U32 convertValveStateNameToValveState(VALVE_STATE_NAMES_T valveStateName); +static U32 convertValveStateNameToValveState( VALVE_STATE_NAMES_T valveStateName ); static U16 fromU32ArrayToU16( void ); -static void publishValvesStates( void ); +static void publishValvesStates( void ); +static U32 getValveState( U32 valveID ); +static void checkVPiTimeStatus( void ); /*********************************************************************//** * @brief * The initValves function initializes the Valves module. * @details Inputs: none - * @details Outputs: Valves module initialized + * @details Outputs: valveStates, pendingValveStateChanges, valveSensedStates, + * pendingValveStateChangeCountDowns, valveStateMismatchCounter, + * commandedValvesStates * @return none *************************************************************************/ + void initValves( void ) { U32 i; // initialize commanded valve states for ( i = 0; i < NUM_OF_VALVES; i++ ) { - valveStates[ i ].data = DEENERGIZED; - valveStates[ i ].ovInitData = DEENERGIZED; - valveStates[ i ].ovData = DEENERGIZED; - valveStates[ i ].override = OVERRIDE_RESET; + valveStates[ i ].data = DEENERGIZED; + valveStates[ i ].ovInitData = DEENERGIZED; + valveStates[ i ].ovData = DEENERGIZED; + valveStates[ i ].override = OVERRIDE_RESET; + pendingValveStateChanges[ i ] = DEENERGIZED; + pendingValveStateChangeCountDowns[ i ] = 0; + valveSensedStates[ i ].data = DEENERGIZED; + valveSensedStates[ i ].ovInitData = DEENERGIZED; + valveSensedStates[ i ].ovData = DEENERGIZED; + valveSensedStates[ i ].override = OVERRIDE_RESET; + } - pendingValveStateChanges[ i ] = DEENERGIZED; - pendingValveStateChangeCountDowns[ i ] = 0; - } - commandedValvesStates = fromU32ArrayToU16(); + memset( &vpiTime, 0, sizeof( VPI_OPEN_CLOSE_TIME_T ) ); + + valveStateMismatchTimerCounter = 0; + commandedValvesStates = fromU32ArrayToU16(); setFPGAValveStates( commandedValvesStates ); // initially set all valves to de-energized state via FPGA valvesStatesPublicationTimerCounter = DATA_PUBLISH_COUNTER_START_COUNT; // reset valves states publication timer @@ -98,15 +125,15 @@ // Verify read back FPGA valve states match last commanded valve states if ( readValvesStates != commandedValvesStates ) { - valveStateMismatchCounter++; // increment valve state mismatch counter by 1 - if ( valveStateMismatchCounter > MAX_VALVE_STATE_MISMATCH_COUNT ) + valveStateMismatchTimerCounter++; // increment valve state mismatch counter by 1 + if ( valveStateMismatchTimerCounter > MAX_VALVE_STATE_MISMATCH_TIMER_COUNT ) { - activateAlarmNoData( ALARM_ID_VALVE_CONTROL_FAILURE ); + activateAlarmNoData( ALARM_ID_DG_VALVE_CONTROL_FAILURE ); } } else { - valveStateMismatchCounter = 0; + valveStateMismatchTimerCounter = 0; } // Handle pending delayed valve state changes @@ -125,8 +152,10 @@ // Set valves states (via FPGA) to currently commanded states commandedValvesStates = fromU32ArrayToU16(); - setFPGAValveStates( commandedValvesStates ); - + setFPGAValveStates( commandedValvesStates ); + + checkVPiTimeStatus(); + // Publish valve states on interval publishValvesStates(); } @@ -204,14 +233,14 @@ break; case VALVE_STATE_R1_C_TO_NC: - if ( ( valveID == VRD ) || ( valveID == VRF ) ) + if ( ( valveID == VRF ) ) { result = TRUE; } break; case VALVE_STATE_R2_C_TO_NO: - if ( ( valveID == VRD ) || ( valveID == VRF ) ) + if ( ( valveID == VRF ) ) { result = TRUE; } @@ -246,7 +275,7 @@ U32 i; // flag valves that are currently commanded to be energized - for ( i = 0; i < NUM_OF_VALVES; i++) + for ( i = 0; i < NUM_OF_VALVES; i++ ) { result |= ( getValveState( i ) == ENERGIZED ? 0x0001 << i : 0 ); } @@ -336,7 +365,7 @@ if ( valveID < NUM_OF_VALVES ) { - if ( checkValveStateName( valveID, valveStateName ) ) + if ( TRUE == checkValveStateName( valveID, valveStateName ) ) { valveStates[ valveID ].data = convertValveStateNameToValveState( valveStateName ); result = TRUE; @@ -370,7 +399,7 @@ if ( valveID < NUM_OF_VALVES ) { - if ( checkValveStateName( valveID, valveStateName ) ) + 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 ) @@ -393,26 +422,92 @@ /*********************************************************************//** * @brief - * The getValveState function gets the current valve state for given valve. + * The getValveStateName function gets the current valve state enum for given valve. * @details Inputs: valveStates[] * @details Outputs: none * @param valveID ID of valve to get state for - * @return the current valve state for given valve + * @return the current valve state for given valve in enum *************************************************************************/ -U32 getValveState( U32 valveID ) -{ - U32 valveState = DEENERGIZED; +VALVE_STATE_NAMES_T getValveStateName( VALVES_T valveID ) +{ + // Initialized per CppCheck. + VALVE_STATE_NAMES_T name = NUM_OF_VALVE_STATES; if ( valveID < NUM_OF_VALVES ) { - valveState = getU32OverrideValue( &valveStates[ valveID ] ); + U32 valveState = getU32OverrideValue( &valveStates[ valveID ] ); + + if ( OVERRIDE_KEY == valveSensedStates[ valveID ].override ) + { + valveState = valveSensedStates[ valveID ].ovData; + } + + // Convert the bit status of the valve to the valve name + switch ( valveID ) + { + case VPI: + case VBF: + case VRD1: + case VRD2: + case VSP: + name = ( DEENERGIZED == valveState ? VALVE_STATE_CLOSED : VALVE_STATE_OPEN ); + break; + + case VPD: + name = ( DEENERGIZED == valveState ? VALVE_STATE_DRAIN_C_TO_NO : VALVE_STATE_OPEN_C_TO_NC ); + break; + + case VPO: + name = ( DEENERGIZED == valveState ? VALVE_STATE_NOFILL_C_TO_NO : VALVE_STATE_FILL_C_TO_NC ); + break; + + case VDR: + case VRC: + name = ( DEENERGIZED == valveState ? VALVE_STATE_DRAIN_C_TO_NO : VALVE_STATE_RECIRC_C_TO_NC ); + break; + + case VRI: + case VRO: + name = ( DEENERGIZED == valveState ? VALVE_STATE_R1_C_TO_NO : VALVE_STATE_R2_C_TO_NC ); + break; + + case VRF: + name = ( DEENERGIZED == valveState ? VALVE_STATE_R2_C_TO_NO : VALVE_STATE_R1_C_TO_NC ); + break; + +#ifndef _VECTORCAST_ + default: + name = ( DEENERGIZED == valveState ? VALVE_STATE_CLOSED : VALVE_STATE_OPEN ); + break; +#endif + } } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_VALVES_INVALID_VALVE_ID, valveID ) } - return valveState; + return name; +} + +/*********************************************************************//** + * @brief + * The isFliterFlushRequired function checks whether filter flush is required in + * cleaning modes. + * @details Inputs: vpiTime + * @details Outputs: none + * @return TRUE if filter flush is required otherwise, FALSE + *************************************************************************/ +BOOL isFliterFlushRequired( void ) +{ + BOOL status = TRUE; + + if ( vpiTime.vpiOpenDurationMS >= VPI_MIN_OPEN_TO_SKIP_DISINFECT_FLUSH_MS ) + { + status = ( calcTimeSince( vpiTime.vpiClosedStartTimeMS ) <= VPI_MAX_OPEN_WINDOW_TO_SKIP_DISINFECT_FLUSH_MS ? FALSE : TRUE ); + } + + return status; } /*********************************************************************//** @@ -426,14 +521,70 @@ { // publish valve state on interval if ( ++valvesStatesPublicationTimerCounter >= getU32OverrideValue( &valvesStatesPublishInterval ) ) - { - U16 valvesStatus = getFPGAValveStates(); - broadcastData( MSG_ID_DG_VALVES_STATES, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&valvesStatus, sizeof( U16 ) ); + { + DG_VALVES_DATA_T data; + U32 i; + + data.valvesStatus = getFPGAValveStates(); + + for ( i = 0; i < NUM_OF_VALVES; i++ ) + { + data.valvesSensedState[ i ] = (U08)getValveStateName( (VALVES_T)i ); + } + + broadcastData( MSG_ID_DG_VALVES_STATES_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&data, sizeof( DG_VALVES_DATA_T ) ); valvesStatesPublicationTimerCounter = 0; } -} +} +/*********************************************************************//** + * @brief + * The getValveState function gets the current valve state for given valve. + * @details Inputs: valveStates[] + * @details Outputs: none + * @param valveID ID of valve to get state for + * @return the current valve state for given valve + *************************************************************************/ +static U32 getValveState( U32 valveID ) +{ + U32 valveState = DEENERGIZED; + + if ( valveID < NUM_OF_VALVES ) + { + valveState = getU32OverrideValue( &valveStates[ valveID ] ); + } + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_VALVES_INVALID_VALVE_ID, valveID ) + } + + return valveState; +} + +/*********************************************************************//** + * @brief + * The checkVPiTimeStatus function checks the status of the VPi to see if + * it was open or closed. + * @details Inputs: vpiTime + * @details Outputs: vpiTime + * @return none + *************************************************************************/ +static void checkVPiTimeStatus( void ) +{ + if ( ( VALVE_STATE_OPEN == getValveStateName( VPI ) ) && ( 0 == vpiTime.vpiOpenStartTimeMS ) ) + { + vpiTime.vpiOpenStartTimeMS = getMSTimerCount(); + vpiTime.vpiClosedStartTimeMS = 0; + } + else if ( ( VALVE_STATE_CLOSED == getValveStateName( VPI ) ) && ( 0 == vpiTime.vpiClosedStartTimeMS ) ) + { + vpiTime.vpiOpenDurationMS = calcTimeSince( vpiTime.vpiOpenStartTimeMS ); + vpiTime.vpiOpenStartTimeMS = 0; + vpiTime.vpiClosedStartTimeMS = getMSTimerCount(); + } +} + /************************************************************************* * TEST SUPPORT FUNCTIONS @@ -505,9 +656,9 @@ { if ( TRUE == isTestingActivated() ) { - result = TRUE; - valveStates[ valveID ].ovData = value; - valveStates[ valveID ].override = OVERRIDE_KEY; + valveStates[ valveID ].ovData = value; + valveStates[ valveID ].override = OVERRIDE_KEY; + result = TRUE; } } @@ -537,6 +688,59 @@ } return result; +} + +/*********************************************************************//** + * @brief + * The testSetValveSensedStateOverride function overrides the value of the + * specified sensed state with a given value. + * @details Inputs: none + * @details Outputs: valveSensedStates + * @param valve ID of valve to override for + * @param value override value for the given valve ID + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetValveSensedStateOverride( U32 valve, U32 status ) +{ + BOOL result = FALSE; + + if ( valve < NUM_OF_VALVES ) + { + if ( TRUE == isTestingActivated() ) + { + valveSensedStates[ valve ].ovData = status; + valveSensedStates[ valve ].override = OVERRIDE_KEY; + result = TRUE; + } + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetValveSensedStateOverride function resets the override of + * the specified valve's sensed state. + * @details Inputs: none + * @details Outputs: valveSensedStates + * @param valve ID of valve to reset override state for + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testResetValveSensedStateOverride( U32 valve ) +{ + BOOL result = FALSE; + + if ( valve < NUM_OF_VALVES ) + { + if ( TRUE == isTestingActivated() ) + { + valveSensedStates[ valve ].override = OVERRIDE_RESET; + valveSensedStates[ valve ].ovData = valveSensedStates[ valve ].ovInitData; + result = TRUE; + } + } + + return result; } /**@}*/