/************************************************************************** * * Copyright (c) 2024-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 ValvesDD.c * * @author (last) Vinayakam Mani * @date (last) 14-Aug-2024 * * @author (original) Vinayakam Mani * @date (original) 14-Aug-2024 * ***************************************************************************/ #include // For memcpy #include "Valves.h" #include "ValvesDD.h" #include "FPGADD.h" #include "MessageSupport.h" #include "Messaging.h" #include "TaskPriority.h" #include "Timers.h" /** * @addtogroup Valves * @{ */ // ********** private definitions ********** #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 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 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 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 void publishValvesStates( void ); static void checkVPiTimeStatus( void ); /*********************************************************************//** * @brief * The initValves function initializes the Valves module. * @details \b Inputs: none * @details \b Outputs: valveStates, pendingValveStateChanges, valveSensedStates, * pendingValveStateChangeCountDowns, valveStateMismatchCounter, * commandedValvesStates * @return none *************************************************************************/ void initValvesDD( void ) { //generic valves initialization initValves( valveStates, valveSensedStates, pendingValveStateChanges, pendingValveStateChangeCountDowns, (U32)NUM_OF_VALVES ); // initialize specific to DD module 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 } /*********************************************************************//** * @brief * The execValves function executes the valves driver. * @details \b Inputs: valvesStates, valveStateMismatchCounter * @details \b Outputs: valvesStates, valveStateMismatchCounter * @details \b Alarm: ALARM_ID_DD_VALVE_CONTROL_FAILURE when FPGA read back * valve state mismatches with the commanded valve state after defined time (100ms). * @return none *************************************************************************/ void execValves( void ) { U32 i; U16 readValvesStates = getFPGAValveStates(); // get valves states from FPGA // Verify read back FPGA valve states match last commanded valve states if ( readValvesStates != commandedValvesStates ) { valveStateMismatchTimerCounter++; // increment valve state mismatch counter by 1 if ( valveStateMismatchTimerCounter > MAX_VALVE_STATE_MISMATCH_TIMER_COUNT ) { activateAlarmNoData( ALARM_ID_DD_VALVE_CONTROL_FAILURE ); } } else { valveStateMismatchTimerCounter = 0; } // Handle pending delayed valve state changes for ( i = 0; i < NUM_OF_VALVES; i++ ) { if ( pendingValveStateChangeCountDowns[ i ] > 0 ) { pendingValveStateChangeCountDowns[ i ]--; if ( 0 == pendingValveStateChangeCountDowns[ i ] ) { valveStates[ i ].data = pendingValveStateChanges[ i ]; pendingValveStateChanges[ i ] = DEENERGIZED; } } } // Set valves states (via FPGA) to currently commanded states commandedValvesStates = fromU32ArrayToU16(); setFPGAValveStates( commandedValvesStates ); checkVPiTimeStatus(); // Publish valve states on interval publishValvesStates(); } /*********************************************************************//** * @brief * The checkValveStateName function checks the validity of requested valve * state name for given valve. * @details \b Inputs: none * @details \b Outputs: none * @details \b Alarm: ALARM_ID_DD_SOFTWARE_FAULT when invalid valve ID passed. * @param valveID ID of valve to check a valve state name for * @param valveStateName valve state name to check for given valve ID * @return TRUE if given valveStateName is appropriate for given valve ID, FALSE if not. *************************************************************************/ BOOL checkValveStateName( VALVES_T valveID, VALVE_STATE_NAMES_T valveStateName ) { BOOL result = FALSE; // initialize result flag to FALSE switch ( valveStateName ) { case VALVE_STATE_OPEN: if ( ( valveID == VPI ) || ( valveID == VBF ) || ( valveID == VSP ) || ( VRD1 == valveID ) || ( VRD2 == valveID ) ) { result = TRUE; } break; case VALVE_STATE_CLOSED: if ( ( valveID == VPI ) || ( valveID == VBF ) || ( valveID == VSP ) || ( VRD1 == valveID ) || ( VRD2 == valveID ) ) { result = TRUE; } break; // case VALVE_STATE_OPEN_C_TO_NC: // if ( VPD == valveID ) // { // result = TRUE; // } // break; // // case VALVE_STATE_NOFILL_C_TO_NO: // if ( valveID == VPO ) // { // result = TRUE; // } // break; // // case VALVE_STATE_FILL_C_TO_NC: // if ( valveID == VPO ) // { // result = TRUE; // } // break; // // case VALVE_STATE_DRAIN_C_TO_NO: // if ( ( valveID == VDR ) || ( valveID == VRC ) || ( VPD == valveID ) ) // { // result = TRUE; // } // break; // // case VALVE_STATE_RECIRC_C_TO_NC: // if ( ( valveID == VDR ) || ( valveID == VRC ) ) // { // result = TRUE; // } // break; // // case VALVE_STATE_R1_C_TO_NO: // if ( ( valveID == VRO ) || ( valveID == VRI ) ) // { // result = TRUE; // } // break; // // case VALVE_STATE_R1_C_TO_NC: // if ( ( valveID == VRF ) ) // { // result = TRUE; // } // break; // // case VALVE_STATE_R2_C_TO_NO: // if ( ( valveID == VRF ) ) // { // result = TRUE; // } // break; // // case VALVE_STATE_R2_C_TO_NC: // if ( ( valveID == VRO ) || ( valveID == VRI ) ) // { // result = TRUE; // } // break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_VALVES_INVALID_VALVE_STATE_NAME, valveStateName ) break; } 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( VALVES_T valveID ) { // Initialized per CppCheck. VALVE_STATE_NAMES_T name = NUM_OF_VALVE_STATES; if ( valveID < NUM_OF_VALVES ) { 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_DD_SOFTWARE_FAULT, SW_FAULT_ID_VALVES_INVALID_VALVE_ID, valveID ) } return name; } /*********************************************************************//** * @brief * The isFliterFlushRequired function checks whether filter flush is required in * cleaning modes. * @details \b Inputs: vpiTime * @details \b 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; } /*********************************************************************//** * @brief * The publishValvesStates function publishes DG valves states at the set interval. * @details \b Inputs: valvesStatesPublicationTimerCounter * @details \b Outputs: valvesStatesPublicationTimerCounter * @details \b Messages: MSG_ID_DD_VALVES_STATES_DATA to publish FPGA read valve states. * @return none *************************************************************************/ static void publishValvesStates( void ) { // publish valve state on interval if ( ++valvesStatesPublicationTimerCounter >= getU32OverrideValue( &valvesStatesPublishInterval ) ) { DD_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_DD_VALVES_STATES_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&data, sizeof( DD_VALVES_DATA_T ) ); valvesStatesPublicationTimerCounter = 0; } } /*********************************************************************//** * @brief * The checkVPiTimeStatus function checks the status of the VPi to see if * it was open or closed. * @details \b Inputs: vpiTime * @details \b 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 *************************************************************************/ /*********************************************************************//** * @brief * The testSetValvesStatesPublishIntervalOverride function overrides the * valves states publish interval. * @details \b Inputs: none * @details \b Outputs: valvesStatesPublishInterval * @param value override valves states publish interval with (in ms) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetValvesStatesPublishIntervalOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 intvl = value / TASK_PRIORITY_INTERVAL; result = TRUE; valvesStatesPublishInterval.ovData = intvl; valvesStatesPublishInterval.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetValvesStatesPublishIntervalOverride function resets the override * of the valves states publish interval. * @details \b Inputs: none * @details \b Outputs: valvesStatesPublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetValvesStatesPublishIntervalOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; valvesStatesPublishInterval.override = OVERRIDE_RESET; valvesStatesPublishInterval.ovData = valvesStatesPublishInterval.ovInitData; } return result; } /**@}*/