/************************************************************************** * * Copyright (c) 2020-2023 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 UVReactors.c * * @author (last) Dara Navaei * @date (last) 12-Oct-2022 * * @author (original) Dara Navaei * @date (original) 24-Nov-2020 * ***************************************************************************/ #include "gio.h" #include "reg_het.h" #include "AlarmMgmt.h" #include "Common.h" #include "FlowSensors.h" #include "MessageSupport.h" #include "PersistentAlarm.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "Timers.h" #include "UVReactors.h" #include "Valves.h" /** * @addtogroup UV Reactors * @{ */ // ********** private definitions ********** #define INLET_UV_REACTOR_ENABLE_PIN 0 ///< Inlet UV reactor GPIO pin number (enable pin). #define OUTLET_UV_REACTOR_ENABLE_PIN 1 ///< Outlet UV reactor GPIO pin number (enable Pin). #define INLET_UV_REACTOR_INDICATION_PIN 0x18 ///< Inlet UV reactor N2HET1 pin number (health check). #define OUTLET_UV_REACTOR_INDICATION_PIN 0x0B ///< Outlet UV reactor N2HET1 pin number (health check). #define UV_REACTORS_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< UV reactors data publication time interval. #define SELF_TEST_DELAY_TIME 1000 ///< Self test wait time after enabling the reactors and before checking for their health in ms. #define MAX_ALLOWED_UNHEALTHY_REACTOR_PERIOD MS_PER_SECOND ///< UV reactors unhealthy state period. #define DATA_PUBLISH_COUNTER_START_COUNT 90 ///< Data publish counter start count. #define UV_REACTORS_ON_NO_FLOW_TIMEOUT_MS ( 10 * MS_PER_SECOND ) ///< UV reactors on with no flow time out in milliseconds. #define MIN_RO_UV_FLOWRATE_LPM 0.2F ///< Minimum target RO UV flow rate in L/min. /// UV reactors self test states typedef enum self_tests { UV_REACTORS_SELF_TEST_OFF = 0, ///< UV reactors self test state off. UV_REACTORS_SELF_TEST_CHECK_HEALTH, ///< UV reactors self test check health. UV_REACTORS_SELF_TEST_COMPLETE, ///< UV reactors self test complete. NUM_OF_UV_REACTORS_SELF_TEST_STATES, ///< Number of UV reactors self test states. } UV_REACTORS_SELF_TEST_STATE_T; /// UV reactors exec states typedef enum exec_states { UV_REACTOR_STATE_OFF = 0, ///< UV reactor state off. UV_REACTOR_STATE_ON, ///< UV reactor state on. NUM_OF_UV_REACTOR_STATES, ///< Number of UV reactor states. } UV_REACTOR_STATE_T; /// UV reactor status typedef struct { UV_REACTOR_STATE_T execState; ///< UV reactor executive state. PIN_SIGNAL_STATE_T pinSignalState; ///< UV reactor pin signal state. UV_REACTOR_STATES_T switchState; ///< UV reactor turn on/turn off state. U32 reactorEnablePin; ///< UV reactor enable pin of GIO port A. U32 reactorHealthStatusPin; ///< UV reactor status pin of N2HET1. OVERRIDE_U32_T healthStatus; ///< UV reactor current health status. } UV_REACTOR_STATUS_T; // ********** private data ********** static UV_REACTOR_STATUS_T reactorsStatus[ NUM_OF_UV_REACTORS ]; ///< UV reactors status array. static UV_REACTORS_SELF_TEST_STATE_T uvReactorsSelfTestStates = UV_REACTORS_SELF_TEST_OFF; ///< UV reactors self test state. static SELF_TEST_STATUS_T uvReactorsSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; ///< Valves self test result. static OVERRIDE_U32_T uvReactorsDataPublishInterval = { UV_REACTORS_DATA_PUB_INTERVAL, UV_REACTORS_DATA_PUB_INTERVAL, 0, 0 }; ///< UV reactors data publish interval. static U32 dataPublishCounter; ///< UV reactors data publish counter. static U32 selfTestElapsedTime; ///< UV reactors self test elapsed time. // Self test functions static UV_REACTORS_SELF_TEST_STATE_T handleUVReactorsSelfTestOff( void ); static UV_REACTORS_SELF_TEST_STATE_T handleUVReactorsSelfTestCheckHealth( void ); // Exec functions static UV_REACTOR_STATE_T handleUVReactorStateOff( UV_REACTORS_T reactor ); static UV_REACTOR_STATE_T handleUVReactorStateOn( UV_REACTORS_T reactor ); // Support functions static U32 getReactorHealth( UV_REACTORS_T reactor ); static void setReactorEnableStatus( UV_REACTORS_T reactor, PIN_SIGNAL_STATE_T state ); static void publishUVReactorsData( void ); /*********************************************************************//** * @brief * The initUVReactors function initializes the UV reactors module. * @details Inputs: uvReactorsSelfTestResult, dataPublishCounter, * reactorsStatus, selfTestStates * @details Outputs: uvReactorsSelfTestResult, dataPublishCounter, * reactorsStatus, selfTestStates, selfTestElapsedTime * @return none *************************************************************************/ void initUVReactors( void ) { UV_REACTORS_T reactor; uvReactorsSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; uvReactorsSelfTestStates = UV_REACTORS_SELF_TEST_OFF; dataPublishCounter = DATA_PUBLISH_COUNTER_START_COUNT; selfTestElapsedTime = 0; // Initialize the UV reactors. These values are specific to the inlet and outlet reactor // so they cannot be in a for loop reactorsStatus[ INLET_UV_REACTOR ].reactorEnablePin = INLET_UV_REACTOR_ENABLE_PIN; reactorsStatus[ INLET_UV_REACTOR ].reactorHealthStatusPin = INLET_UV_REACTOR_INDICATION_PIN; reactorsStatus[ OUTLET_UV_REACTOR ].reactorEnablePin = OUTLET_UV_REACTOR_ENABLE_PIN; reactorsStatus[ OUTLET_UV_REACTOR ].reactorHealthStatusPin = OUTLET_UV_REACTOR_INDICATION_PIN; // Initialize the common values in the UV reactors for( reactor = INLET_UV_REACTOR; reactor < NUM_OF_UV_REACTORS; reactor++ ) { reactorsStatus[ reactor ].pinSignalState = PIN_SIGNAL_LOW; reactorsStatus[ reactor ].execState = UV_REACTOR_STATE_OFF; reactorsStatus[ reactor ].switchState = TURN_OFF; setReactorEnableStatus( reactor, PIN_SIGNAL_LOW ); } initPersistentAlarm( ALARM_ID_UV_REACTOR_NOT_HEALTHY, MAX_ALLOWED_UNHEALTHY_REACTOR_PERIOD, MAX_ALLOWED_UNHEALTHY_REACTOR_PERIOD ); initPersistentAlarm( ALARM_ID_DG_INLET_UV_REACTOR_ON_WITH_NO_FLOW, UV_REACTORS_ON_NO_FLOW_TIMEOUT_MS, UV_REACTORS_ON_NO_FLOW_TIMEOUT_MS ); initPersistentAlarm( ALARM_ID_DG_OUTLET_UV_REACTOR_ON_WITH_NO_FLOW, UV_REACTORS_ON_NO_FLOW_TIMEOUT_MS, UV_REACTORS_ON_NO_FLOW_TIMEOUT_MS ); } /*********************************************************************//** * @brief * The execUVReactorsSelfTest function executes the UV reactors self test. * @details Inputs: FuvReactorsSelfTestStates * @details Outputs: uvReactorsSelfTestStates * @return Status of self test *************************************************************************/ SELF_TEST_STATUS_T execUVReactorsSelfTest( void ) { switch ( uvReactorsSelfTestStates ) { case UV_REACTORS_SELF_TEST_OFF: uvReactorsSelfTestStates = handleUVReactorsSelfTestOff(); break; case UV_REACTORS_SELF_TEST_CHECK_HEALTH: uvReactorsSelfTestStates = handleUVReactorsSelfTestCheckHealth(); break; case UV_REACTORS_SELF_TEST_COMPLETE: // Done with self test, do nothing. break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_UV_REACTORS_INVALID_SELF_TEST_STATE, uvReactorsSelfTestStates ); uvReactorsSelfTestStates = UV_REACTORS_SELF_TEST_COMPLETE; break; } return uvReactorsSelfTestResult; } /*********************************************************************//** * @brief * The execUVReactors function executes the UV reactors exec states. * @details Inputs: reactorsStatus * @details Outputs: reactorsStatus * @return none *************************************************************************/ void execUVReactors( void ) { UV_REACTORS_T reactor; for ( reactor = INLET_UV_REACTOR; reactor < NUM_OF_UV_REACTORS; reactor++ ) { switch ( reactorsStatus[ reactor ].execState ) { case UV_REACTOR_STATE_OFF: reactorsStatus[ reactor ].execState = handleUVReactorStateOff( reactor ); break; case UV_REACTOR_STATE_ON: reactorsStatus[ reactor ].execState = handleUVReactorStateOn( reactor ); break; #ifndef _VECTORCAST_ default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_UV_REACTORS_INVALID_EXEC_STATE, reactorsStatus[ reactor ].execState ); reactorsStatus[ reactor].execState = UV_REACTOR_STATE_OFF; break; #endif } } // Publish all reactors data once publishUVReactorsData(); } /*********************************************************************//** * @brief * The getUVReactorHealth function returns the health status of a UV * reactor. * @details Inputs: reactorsStatus * @details Outputs: none * @param reactor to return its health * @return returns the health of the requested UV reactor as an enum *************************************************************************/ UV_REACTORS_HEALTH_STATUS_T getUVReactorHealth( UV_REACTORS_T reactor ) { UV_REACTORS_HEALTH_STATUS_T health = UV_REACTOR_OFF; // Check if the reactor selected is in range if ( reactor < NUM_OF_UV_REACTORS ) { health = (UV_REACTORS_HEALTH_STATUS_T)getU32OverrideValue( &reactorsStatus[ reactor ].healthStatus ); } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_UV_REACTORS_INVALID_REACTOR_SELECTD, reactor ) } return health; } /*********************************************************************//** * @brief * The turnOnUVReactor function turns on the selected UV reactor per request. * @details Inputs: reactorsStatus * @details Outputs: reactorsStatus * @param reactor to turn on * @return returns TRUE if the reactor was not already on *************************************************************************/ BOOL turnOnUVReactor( UV_REACTORS_T reactor ) { BOOL result = FALSE; // Check if the called reactor is in range. Otherwise, raise an alarm if ( reactor < NUM_OF_UV_REACTORS ) { if ( TURN_OFF == reactorsStatus[ reactor ].switchState ) { reactorsStatus[ reactor ].switchState = TURN_ON; result = TRUE; } } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_UV_REACTORS_INVALID_REACTOR_SELECTD, reactor ) } return result; } /*********************************************************************//** * @brief * The turnOffUVReactor function turns off the selected UV reactor per request. * @details Inputs: reactorsStatus * @details Outputs: reactorsStatus * @param reactor to turn off * @return returns TRUE if the reactor was not already off *************************************************************************/ BOOL turnOffUVReactor( UV_REACTORS_T reactor ) { BOOL result = FALSE; // Check if the called reactor is in range if ( reactor < NUM_OF_UV_REACTORS ) { reactorsStatus[ reactor ].switchState = TURN_OFF; result = TRUE; } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_UV_REACTORS_INVALID_REACTOR_SELECTD, reactor ) } return result; } /*********************************************************************//** * @brief * The handleUVReactorsSelfTestOff function handles the self test off state. * @details Inputs: selfTestElapsedTime * @details Outputs: selfTestElapsedTime * @return returns the next state of the self test state machine *************************************************************************/ static UV_REACTORS_SELF_TEST_STATE_T handleUVReactorsSelfTestOff( void ) { UV_REACTORS_SELF_TEST_STATE_T state = UV_REACTORS_SELF_TEST_CHECK_HEALTH; // Enable both the reactors and set the timer to check the health of the reactors after time elapsed setReactorEnableStatus( INLET_UV_REACTOR, PIN_SIGNAL_HIGH ); setReactorEnableStatus( OUTLET_UV_REACTOR, PIN_SIGNAL_HIGH ); selfTestElapsedTime = getMSTimerCount(); return state; } /*********************************************************************//** * @brief * The handleUVReactorsSelfTestCheckHealth function handles the self test * check health state. * @details Inputs: selfTestElapsedTime, uvReactorsSelfTestResult * @details Outputs: uvReactorsSelfTestResult * @return returns the next state of the self test state machine *************************************************************************/ static UV_REACTORS_SELF_TEST_STATE_T handleUVReactorsSelfTestCheckHealth( void ) { UV_REACTORS_SELF_TEST_STATE_T state = UV_REACTORS_SELF_TEST_CHECK_HEALTH; if ( TRUE == didTimeout( selfTestElapsedTime, SELF_TEST_DELAY_TIME ) ) { // Get the health status of the reactors BOOL isInletHealthy = (BOOL)getReactorHealth( INLET_UV_REACTOR ); BOOL isOutletHealthy = (BOOL)getReactorHealth( OUTLET_UV_REACTOR ); // Check if both of them are healthy and if not, raise an alarm if ( ( TRUE == isInletHealthy ) && ( TRUE == isOutletHealthy ) ) { uvReactorsSelfTestResult = SELF_TEST_STATUS_PASSED; } else { uvReactorsSelfTestResult = SELF_TEST_STATUS_FAILED; // Check which reactor has not been healthy and raise an alarm if ( FALSE == isInletHealthy ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_UV_REACTOR_NOT_HEALTHY, INLET_UV_REACTOR ); } else { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_UV_REACTOR_NOT_HEALTHY, OUTLET_UV_REACTOR ); } } // Turn off the UV reactors once the test is finished setReactorEnableStatus( INLET_UV_REACTOR, PIN_SIGNAL_LOW ); setReactorEnableStatus( OUTLET_UV_REACTOR, PIN_SIGNAL_LOW ); state = UV_REACTORS_SELF_TEST_COMPLETE; } return state; } /*********************************************************************//** * @brief * The handleUVReactorStateOff function handles the off state. * @details Inputs: reactorsStatus * @details Outputs: none * @param reactor to check its state * @return returns the next state of the exec state machine *************************************************************************/ static UV_REACTOR_STATE_T handleUVReactorStateOff( UV_REACTORS_T reactor ) { UV_REACTOR_STATE_T state = UV_REACTOR_STATE_OFF; // Set the health status to be off. When the reactor is off, it does not report its health status reactorsStatus[ reactor ].healthStatus.data = (U32)UV_REACTOR_OFF; // Check if the a reactor is requested to be on and it is off if ( TURN_ON == reactorsStatus[ reactor ].switchState ) { setReactorEnableStatus( reactor, PIN_SIGNAL_HIGH ); state = UV_REACTOR_STATE_ON; } return state; } /*********************************************************************//** * @brief * The handleUVReactorStateOn function handles the on state. * @details Inputs: reactorsStatus * @details Outputs: reactorsStatus * @param reactor to checks it status * @return returns the next state of the exec state machine *************************************************************************/ static UV_REACTOR_STATE_T handleUVReactorStateOn( UV_REACTORS_T reactor ) { UV_REACTOR_STATE_T state = UV_REACTOR_STATE_ON; BOOL isReactorUnhealthy = ( UV_REACTOR_HEALTHY == getUVReactorHealth( reactor ) ? FALSE : TRUE ); // Update the UV reactor's health. It should be either healthy (1) or not healthy (0) reactorsStatus[ reactor ].healthStatus.data = getReactorHealth( reactor ); #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_UV_REACTORS ) != SW_CONFIG_ENABLE_VALUE ) #endif { checkPersistentAlarm( ALARM_ID_UV_REACTOR_NOT_HEALTHY, isReactorUnhealthy, (U32)reactor, MAX_ALLOWED_UNHEALTHY_REACTOR_PERIOD ); if ( TRUE == isAlarmActive( ALARM_ID_UV_REACTOR_NOT_HEALTHY ) ) { // The UV reactor is not healthy turn it off and trigger the alarm reactorsStatus[ reactor ].switchState = TURN_OFF; } } switch( reactor ) { case INLET_UV_REACTOR: { BOOL isVPIClosed = ( VALVE_STATE_CLOSED == getValveStateName( VPI ) ? TRUE : FALSE ); checkPersistentAlarm( ALARM_ID_DG_INLET_UV_REACTOR_ON_WITH_NO_FLOW, isVPIClosed, (F32)VALVE_STATE_CLOSED, (F32)VALVE_STATE_OPEN ); } break; case OUTLET_UV_REACTOR: { F32 flow = getMeasuredFlowRateLPM( RO_FLOW_SENSOR ); BOOL isFlowBelowMin = ( flow < MIN_RO_UV_FLOWRATE_LPM ? TRUE : FALSE ); checkPersistentAlarm( ALARM_ID_DG_OUTLET_UV_REACTOR_ON_WITH_NO_FLOW, isFlowBelowMin, flow, MIN_RO_UV_FLOWRATE_LPM ); } break; default: SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_UV_REACTORS_INVALID_REACTOR_SELECTD ) break; } // Check if it has been requested to turn off a reactor if ( TURN_OFF == reactorsStatus[ reactor ].switchState ) { setReactorEnableStatus( reactor, PIN_SIGNAL_LOW ); state = UV_REACTOR_STATE_OFF; } return state; } /*********************************************************************//** * @brief * The setReactorEnableStatus function sets a reactor to enable or * disable state. * @details Inputs: reactorsStatus * @details Outputs: none * @param reactor to set it enable status * @param state to set the reactor (high or low) * @return none *************************************************************************/ static void setReactorEnableStatus( UV_REACTORS_T reactor, PIN_SIGNAL_STATE_T state ) { // Set the GIO pin to enable or disable gioSetBit( gioPORTA, reactorsStatus[ reactor ].reactorEnablePin, (U32)state ); // Update the pin signal state reactorsStatus[ reactor ].pinSignalState = state; } /*********************************************************************//** * @brief * The getReactorHealth function checks the health status of a reactor. * @details Inputs: reactorsStatus * @details Outputs: none * @param reactor to check its health * @return returns 1 if the reactor is healthy otherwise a 0 *************************************************************************/ static U32 getReactorHealth( UV_REACTORS_T reactor ) { return gioGetBit( hetPORT1, reactorsStatus[ reactor ].reactorHealthStatusPin ); } /*********************************************************************//** * @brief * The publishUVReactorsData function publishes the UV reactors data. * @details Inputs: dataPublishCounter, reactorsStatus * @details Outputs: dataPublishCounter * @return none *************************************************************************/ static void publishUVReactorsData( void ) { if ( ++dataPublishCounter >= getU32OverrideValue( &uvReactorsDataPublishInterval ) ) { UV_REACTORS_DATA_T uvReactorsData; // Publish the reactors health status uvReactorsData.inletUVReactorHealthStatus = (U32)getUVReactorHealth( INLET_UV_REACTOR ); uvReactorsData.outletUVReactorHealthStatus = (U32)getUVReactorHealth( OUTLET_UV_REACTOR ); uvReactorsData.inletUVReactorState = (U32)reactorsStatus[ INLET_UV_REACTOR ].execState; uvReactorsData.outletUVReactorState = (U32)reactorsStatus[ OUTLET_UV_REACTOR ].execState; broadcastData( MSG_ID_DG_UV_REACTORS_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&uvReactorsData, sizeof( UV_REACTORS_DATA_T ) ); dataPublishCounter = 0; } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetReactorsDataPublishInterval function overrides the UV * reactors data publish interval. * @details Inputs: uvReactorsDataPublishInterval * @details Outputs: uvReactorsDataPublishInterval * @param value which is override value for the UV reactors data publish * interval * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetReactorsDataPublishInterval( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 intvl = value / TASK_GENERAL_INTERVAL; result = TRUE; uvReactorsDataPublishInterval.ovData = intvl; uvReactorsDataPublishInterval.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetReactorsDataPublishInterval function resets the override * of the UV reactors publish interval. * @details Inputs: uvReactorsDataPublishInterval * @details Outputs: uvReactorsDataPublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetReactorsDataPublishInterval( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; uvReactorsDataPublishInterval.override = OVERRIDE_RESET; uvReactorsDataPublishInterval.ovData = uvReactorsDataPublishInterval.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetUVReactorHealthOverride function overrides the UV reactors * health * @details Inputs: reactorsStatus * @details Outputs: reactorsStatus * @param reactor that its health will be overridden * @param health which is high for healthy on and low for not healthy * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetUVReactorHealthOverride( U32 reactor, U32 health ) { BOOL result = FALSE; if ( ( TRUE == isTestingActivated() ) && ( (UV_REACTORS_T)reactor < NUM_OF_UV_REACTORS ) ) { reactorsStatus[ (UV_REACTORS_T)reactor ].healthStatus.ovInitData = reactorsStatus[ (UV_REACTORS_T)reactor ].healthStatus.data; reactorsStatus[ (UV_REACTORS_T)reactor ].healthStatus.override = OVERRIDE_KEY; reactorsStatus[ (UV_REACTORS_T)reactor ].healthStatus.ovData = health; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testResetUVReactorHealthOverride function resets the override * of the UV reactors health * @details Inputs: reactorsStatus * @details Outputs: reactorsStatus * @param reactor that its health will be reset * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetUVReactorHealthOverride( U32 reactor ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() && (UV_REACTORS_T)reactor < NUM_OF_UV_REACTORS ) { reactorsStatus[ (UV_REACTORS_T)reactor ].healthStatus.override = OVERRIDE_RESET; reactorsStatus[ (UV_REACTORS_T)reactor ].healthStatus.ovData = reactorsStatus[ (UV_REACTORS_T)reactor ].healthStatus.ovInitData; result = TRUE; } return result; } /**@}*/