/************************************************************************** * * Copyright (c) 2021-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 Switches.c * * @author (last) Dara Navaei * @date (last) 05-Mar-2024 * * @author (original) Dara Navaei * @date (original) 25-Jul-2021 * ***************************************************************************/ #include "FPGA.h" #include "NVDataMgmt.h" #include "Switches.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "Timers.h" /** * @addtogroup Switches * @{ */ // ********** private definitions ********** #define SWITCHES_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the switches data is published on the CAN bus. #define SWITCHES_DEBOUNCE_TIME_MS ( MS_PER_SECOND / 4 ) ///< Switches FPGA status check interval. #define DATA_PUBLISH_COUNTER_START_COUNT 90 ///< Data publish counter start count. #define FRONT_DOOR_SWITCH_MASK 0x0010 ///< Front door switch bit mask. Bit 4 of the FPGA GPIO register. #define PUMP_TRACK_SWITCH_MASK 0x0020 ///< Pump track switch bit mask. Bit 5 of the FPGA GPIO register. /// Switch status structure typedef struct { OVERRIDE_U32_T status; ///< Switch status. U32 debounceStartTime; ///< Debounce start time. } SWITCH_STATUS_T; // ********** private data ********** static U32 switchesDataPublicationCounter; ///< Switches data publication counter. static OVERRIDE_U32_T switchesDataPublishInterval = { SWITCHES_DATA_PUB_INTERVAL, SWITCHES_DATA_PUB_INTERVAL, 0, 0 }; ///< Interval (in ms) at which to publish switches data to CAN bus. static SWITCH_STATUS_T switchesStatus[ NUM_OF_DOORS_AND_SWITCHES ]; ///< Switches status array. static BOOL requireDoorClosed; ///< Flag indicates whether door is required to be closed in current state. static BOOL requirePumpTrackLocked; ///< Flag indicates whether pump track is required to be locked in current state. // ********** private function prototypes ********** static void publishSwitchesData( void ); static void handleSwitchAlarms( void ); static U32 getPublishSwitchesDataInterval( void ); /*********************************************************************//** * @brief * The initSwitches function initializes the switches module. * @details Inputs: none * @details Outputs: switchesDataPublicationCounter, switchStatus * @return none *************************************************************************/ void initSwitches( void ) { U08 i; switchesDataPublicationCounter = DATA_PUBLISH_COUNTER_START_COUNT; requireDoorClosed = FALSE; requirePumpTrackLocked = FALSE; // Initialize all the switches for ( i = 0; i < NUM_OF_DOORS_AND_SWITCHES; i++ ) { switchesStatus[ i ].status.data = (U32)STATE_CLOSED; switchesStatus[ i ].status.ovData = (U32)STATE_CLOSED; switchesStatus[ i ].status.ovInitData = (U32)STATE_CLOSED; switchesStatus[ i ].status.override = OVERRIDE_RESET; switchesStatus[ i ].debounceStartTime = 0; } } /*********************************************************************//** * @brief * The execSwitches function executes the switches executive. * @details Inputs: switchesStatus * @details Outputs: switchStatus * @return none *************************************************************************/ void execSwitches( void ) { U08 i; U16 currentSwitchStatus; for ( i = 0; i < NUM_OF_DOORS_AND_SWITCHES; i++ ) { // Get the current switch status switch ( i ) { case FRONT_DOOR: currentSwitchStatus = ( FRONT_DOOR_SWITCH_MASK == getFPGAFrontDoorStatus() ? STATE_OPEN : STATE_CLOSED ); break; case PUMP_TRACK_SWITCH: currentSwitchStatus = ( PUMP_TRACK_SWITCH_MASK == getFPGAPumpTrackSwitchStatus() ? STATE_OPEN : STATE_CLOSED ); break; #ifndef _VECTORCAST_ // Since this is a for loop the default cannot be reached in VectorCAST for 100% coverage default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_INVALID_SWITCH_ID, i ) break; #endif } #ifndef _RELEASE_ if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_DISABLE_SWITCHES_MONITOR ) ) { switchesStatus[ i ].status.data = STATE_CLOSED; } else #endif { // Check if the current switch status is not the same as the recorded data if ( currentSwitchStatus != switchesStatus[ i ].status.data ) { // If the debounce time is 0, start the timer if ( 0 == switchesStatus[ i ].debounceStartTime ) { switchesStatus[ i ].debounceStartTime = getMSTimerCount(); } // If the debounce time has been elapsed, update the switch status to the new status else if ( TRUE == didTimeout( switchesStatus[ i ].debounceStartTime, SWITCHES_DEBOUNCE_TIME_MS ) ) { // If the bit is 0, the door switch is open, because it is normally open switch switchesStatus[ i ].debounceStartTime = 0; switchesStatus[ i ].status.data = currentSwitchStatus; } } else { switchesStatus[ i ].debounceStartTime = 0; } } } handleSwitchAlarms(); publishSwitchesData(); } /*********************************************************************//** * @brief * The getSwitchStatus function returns the status of the called switch. * @details Inputs: switchStatus * @details Outputs: switchStatus, requireDoorClosed, requirePumpTrackLocked * @param switchId which is the switch that its status is requested * @return switch status *************************************************************************/ static void handleSwitchAlarms( void ) { #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SWITCHES_MONITOR ) != SW_CONFIG_ENABLE_VALUE ) #endif { // Check for door closed alarm if ( TRUE == requireDoorClosed ) { if ( getSwitchStatus( FRONT_DOOR ) != STATE_CLOSED ) { activateAlarmNoData( ALARM_ID_HD_CARTRIDGE_DOOR_OPENED ); } } // Check for pump track unlocked alarm if ( TRUE == requirePumpTrackLocked ) { if ( getSwitchStatus( PUMP_TRACK_SWITCH ) != STATE_CLOSED ) { clearAlarmCondition( ALARM_ID_HD_PRE_TREATMENT_WET_PRIME_TEST_FAILURE ); // Spurious bubble alarms occur when the cartridge is disturbed (unlatched) activateAlarmNoData( ALARM_ID_HD_PUMP_TRACK_LATCH_OPENED ); } } // Handle clearing alarm conditions if ( STATE_CLOSED == getSwitchStatus( FRONT_DOOR ) ) { clearAlarmCondition( ALARM_ID_HD_CARTRIDGE_DOOR_OPENED ); } if ( STATE_CLOSED == getSwitchStatus( PUMP_TRACK_SWITCH ) ) { clearAlarmCondition( ALARM_ID_HD_PUMP_TRACK_LATCH_OPENED ); } } } /*********************************************************************//** * @brief * The getSwitchStatus function returns the status of the called switch. * @details Inputs: switchStatus * @details Outputs: switchStatus * @param switchId which is the switch that its status is requested * @return switch status *************************************************************************/ OPN_CLS_STATE_T getSwitchStatus( SWITCH_T switchId ) { U32 status = 0; if ( switchId < NUM_OF_DOORS_AND_SWITCHES ) { status = switchesStatus[ switchId ].status.data; if ( OVERRIDE_KEY == switchesStatus[ switchId ].status.override ) { status = switchesStatus[ switchId ].status.ovData; } } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_INVALID_SWITCH_ID, (U32)switchId ) } return (OPN_CLS_STATE_T)status; } /*********************************************************************//** * @brief * The doorClosedRequired function sets flags that determine whether door * and pump tracks should be closed/locked in current state. Associated * alarms will be triggered if switches not in required state. * @details Inputs: none * @details Outputs: requireDoorClosed, requirePumpTrackLocked * @param door is door required to be closed in current state? * @param pumpTrack is pump track required to be locked in current state? * @return none *************************************************************************/ void doorClosedRequired( BOOL door, BOOL pumpTrack ) { requireDoorClosed = door; requirePumpTrackLocked = pumpTrack; } // ********** private functions ********** /*********************************************************************//** * @brief * The publishSwitchesData function broadcasts the switches data at the * publication interval. * @details Inputs: switchesDataPublicationCounter * @details Outputs: switchesDataPublicationCounter * @return none *************************************************************************/ static void publishSwitchesData( void ) { if ( ++switchesDataPublicationCounter >= getPublishSwitchesDataInterval() ) { SWITCHES_DATA_T data; data.frontDoor = (U32)getSwitchStatus( FRONT_DOOR ); data.pumpTrackSwitch = (U32)getSwitchStatus( PUMP_TRACK_SWITCH ); switchesDataPublicationCounter = 0; broadcastData( MSG_ID_HD_SWITCHES_DATA, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&data, sizeof( SWITCHES_DATA_T ) ); } } /*********************************************************************//** * @brief * The getPublishSwitchesDataInterval function returns the data * publication interval either from the data or from the override. * @details Inputs: switchesDataPublishInterval * @details Outputs: none * @return data publish interval *************************************************************************/ static U32 getPublishSwitchesDataInterval( void ) { U32 result = switchesDataPublishInterval.data; if ( OVERRIDE_KEY == switchesDataPublishInterval.override ) { result = switchesDataPublishInterval.ovData; } return result; } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetSwitchesDataPublishIntervalOverride function overrides * the switches publish data interval. * @details Inputs: switchesDataPublishInterval * @details Outputs: switchesDataPublishInterval * @param value switches data broadcast interval (in ms) to override * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetSwitchesDataPublishIntervalOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 interval = value / TASK_GENERAL_INTERVAL; result = TRUE; switchesDataPublishInterval.ovData = interval; switchesDataPublishInterval.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetSwitchesDataPublishIntervalOverrid function resets * the override value of switches publish data interval. * @details Inputs: switchesDataPublishInterval * @details Outputs: switchesDataPublishInterval * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testResetSwitchesDataPublishIntervalOverrid( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; switchesDataPublishInterval.override = OVERRIDE_RESET; switchesDataPublishInterval.ovData = switchesDataPublishInterval.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetSwitchesStatusOverride function sets the override status * for a specific switch. * @details Inputs: none * @details Outputs: switchStatus * @param switchId switch index * @param status switch status to override if testing is activated * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetSwitchesStatusOverride( U32 switchId, U32 status ) { BOOL result = FALSE; if ( switchId < NUM_OF_DOORS_AND_SWITCHES ) { if ( TRUE == isTestingActivated() ) { result = TRUE; switchesStatus[ switchId ].status.ovData = status; switchesStatus[ switchId ].status.override = OVERRIDE_KEY; } } return result; } /*********************************************************************//** * @brief * The testResetSwitchesStatusOverride function resets the override * status of a specified switch. * @details Inputs: none * @details Outputs: switchStatus * @param switchId switch index * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testResetSwitchesStatusOverride( U32 switchId ) { BOOL result = FALSE; if ( switchId < NUM_OF_DOORS_AND_SWITCHES ) { if ( TRUE == isTestingActivated() ) { result = TRUE; switchesStatus[ switchId ].status.override = OVERRIDE_RESET; switchesStatus[ switchId ].status.ovData = switchesStatus[ switchId ].status.ovInitData; } } return result; } /**@}*/