/************************************************************************** * * 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 Level.c * * @author (last) Vinayakam Mani * @date (last) 11-Oct-2024 * * @author (original) Vinayakam Mani * @date (original) 11-Oct-2024 * ***************************************************************************/ #include "FpgaDD.h" #include "Level.h" #include "MessageSupport.h" #include "Messaging.h" #include "TaskPriority.h" #include "Timers.h" /** * @addtogroup Level * @{ */ // ********** private definitions ********** #define FPGA_LEVEL_LOW 5 ///< Floater low level status #define FPGA_LEVEL_MEDIUM 4 ///< Floater medium level status #define FPGA_LEVEL_HIGH 6 ///< Floater high level status #define LEVEL_COUNT_LOW 0xFFFF ///< Level sensor count when fluid level is low or non submerged #define LEVEL_COUNT_HIGH_START 0x2000 ///< Start range of level sensor count when fluid level is high or submerged #define LEVEL_COUNT_HIGH_END 0x2FFF ///< End range of level sensor count when fluid level is high or submerged #define LOW_LEVEL_COUNT_TOLERANCE ( ( LEVEL_COUNT_LOW / 100 ) * 1 ) ///< Level sensor count 1% tolerance for low level #define HIGH_LEVEL_COUNT_TOLERANCE ( ( LEVEL_COUNT_HIGH / 100 ) * 1 ) ///< Level sensor count 1% tolerance for high level #define LEVEL_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Interval (ms/task time) at which the level data is published on the CAN bus. #define LEVEL_DEBOUNCE_TIME_MS ( MS_PER_SECOND / 10 ) ///< Level debounce time in milliseconds. #define DATA_PUBLISH_COUNTER_START_COUNT 7 ///< Data publish counter start count. /// Level status structure typedef struct { U32 priorRawLevel; ///< Prior level state (not debounced). U32 debounceStartTime; ///< Debounce start time. U32 debounceTime; ///< Debounce time } LEVEL_STATUS_T; // ********** private data ********** static U32 levelsDataPublicationCounter; ///< Level data publication counter. static U32 fplevelsDataPublicationCounter; ///< fp Level data publication counter. static OVERRIDE_U32_T levelsDataPublishInterval; ///< Interval (in ms) at which to publish Level data to CAN bus. static OVERRIDE_U32_T fplevelsDataPublishInterval; ///< Interval (in ms/task interval) for FP levels at which to publish valves state to CAN bus. static LEVEL_STATUS_T levelsStatus[ NUM_OF_LEVELS ]; ///< Level status array. static OVERRIDE_U32_T status[ NUM_OF_LEVELS ]; ///< Level status. // ********** private function prototypes ********** static void publishLevelsData( void ); static BOOL processLevelCount( U16 count ); static LEVEL_STATE_T readFloaterLevelstatus( LEVEL_T levelId ); /*********************************************************************//** * @brief * The initLevels function initializes the level module. * @details \b Inputs: none * @details \b Outputs: Level unit variables initialized. * @return none *************************************************************************/ void initLevels( void ) { U32 i; levelsDataPublicationCounter = DATA_PUBLISH_COUNTER_START_COUNT; fplevelsDataPublishInterval.data = LEVEL_DATA_PUB_INTERVAL; fplevelsDataPublishInterval.ovData = LEVEL_DATA_PUB_INTERVAL; fplevelsDataPublishInterval.ovInitData = 0; fplevelsDataPublishInterval.override = OVERRIDE_RESET; levelsDataPublishInterval.data = LEVEL_DATA_PUB_INTERVAL; levelsDataPublishInterval.ovData = LEVEL_DATA_PUB_INTERVAL; levelsDataPublishInterval.ovInitData = 0; levelsDataPublishInterval.override = OVERRIDE_RESET; // Initialize all the Level for ( i = 0; i < NUM_OF_LEVELS; i++ ) { status[ i ].data = (U32)LEVEL_STATE_HIGH; status[ i ].ovData = (U32)LEVEL_STATE_HIGH; status[ i ].ovInitData = (U32)LEVEL_STATE_HIGH; status[ i ].override = OVERRIDE_RESET; levelsStatus[ i ].debounceStartTime = 0; levelsStatus[ i ].debounceTime = LEVEL_DEBOUNCE_TIME_MS; levelsStatus[ i ].priorRawLevel = (U32)LEVEL_STATE_HIGH; } } /*********************************************************************//** * @brief * The execLevels function executes the floater and level sensor states. * @details \b Inputs: level sensor and floater reading from FPGA * @details \b Outputs: levelStatus * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT when invalid level sensor * executed. * @return none *************************************************************************/ void execLevels( void ) { U32 i; U32 currentLevelStatus = 0; for ( i = 0; i < NUM_OF_LEVELS; i++ ) { // Get the current level status switch ( i ) { // Process the status of the Level case D6_LEVL: currentLevelStatus = readFloaterLevelstatus( D6_LEVL ); break; case D63_LEVL: currentLevelStatus = ( processLevelCount( getFPGAD63LevelSensor() ) == 0 ? LEVEL_STATE_LOW : LEVEL_STATE_HIGH ); break; case D46_LEVL: currentLevelStatus = ( processLevelCount( getFPGAD46LevelSensor() ) == 0 ? LEVEL_STATE_LOW : LEVEL_STATE_HIGH ); break; case P25_LEVL: currentLevelStatus = readFloaterLevelstatus( P25_LEVL ); break; #ifndef _VECTORCAST_ default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_LEVEL_SELECTED, i ); break; #endif } // Check if the current level status is not the same as the recorded data if ( currentLevelStatus != status[ i ].data ) { // If the debounce time is 0, start the timer if ( ( 0 == levelsStatus[ i ].debounceStartTime ) || ( currentLevelStatus != levelsStatus[i].priorRawLevel ) ) { levelsStatus[ i ].debounceStartTime = getMSTimerCount(); } // If the debounce time has been elapsed, update the level sensor status to the new status else if ( TRUE == didTimeout( levelsStatus[ i ].debounceStartTime, levelsStatus[ i ].debounceTime ) ) { switch ( i ) { case D6_LEVL: SEND_EVENT_WITH_2_U32_DATA( DD_EVENT_D6_LEVL_CHANGE, (U32)status[ i ].data, (U32)currentLevelStatus ); break; case D63_LEVL: SEND_EVENT_WITH_2_U32_DATA( DD_EVENT_D63_LEVL_CHANGE, (U32)status[ i ].data, (U32)currentLevelStatus ); break; case D46_LEVL: SEND_EVENT_WITH_2_U32_DATA( DD_EVENT_D46_LEVL_CHANGE, (U32)status[ i ].data, (U32)currentLevelStatus ); break; #ifndef _VECTORCAST_ default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_LEVEL_SELECTED, i ); break; #endif } levelsStatus[ i ].debounceStartTime = 0; status[ i ].data = currentLevelStatus; } } else { levelsStatus[ i ].debounceStartTime = 0; } levelsStatus[i].priorRawLevel = currentLevelStatus; } publishLevelsData(); } /*********************************************************************//** * @brief * The getLevelStatus function returns the status of the called level sensor. * @details \b Inputs: levelStatus * @details \b Outputs: levelStatus * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT when invalid level sensor * passed. * @param levelId which is the sensor that its status is requested * @return level status *************************************************************************/ LEVEL_STATE_T getLevelStatus( LEVEL_T levelId ) { U32 stat = 0; if ( levelId < NUM_OF_LEVELS ) { // Assume there is no override stat = status[ levelId ].data; if ( OVERRIDE_KEY == status[ levelId ].override ) { stat = status[ levelId ].ovData; } } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_DD_INVALID_LEVEL_ID, (U32)levelId ) } return (LEVEL_STATE_T)stat; } /*********************************************************************//** * @brief * The processLevelCount function checks the range of count reported by FPGA * and determine level based on the count. * @details \b Inputs: none * @details \b Outputs: level * @param count The level count reported by FPGA * @return level status *************************************************************************/ static BOOL processLevelCount( U16 count ) { BOOL level = FALSE; // Check the level count with in the high level range if ( ( count >= LEVEL_COUNT_HIGH_START ) && ( count <= LEVEL_COUNT_HIGH_END ) ) { level = TRUE; } else { level = FALSE; } return level; } /*********************************************************************//** * @brief * The getFloaterLevelstatus function gets the floater level reported by FPGA * @details \b Inputs: FPGA level sensor data * @details \b Outputs: level * @return level status *************************************************************************/ static LEVEL_STATE_T readFloaterLevelstatus( LEVEL_T levelId ) { LEVEL_STATE_T currentLevelStatus = LEVEL_STATE_HIGH; U32 levelStatus = 0; if ( D6_LEVL == levelId ) { levelStatus = getFPGAD6LevelStatus(); if ( FPGA_LEVEL_LOW == levelStatus ) { currentLevelStatus = LEVEL_STATE_LOW ; } else if ( FPGA_LEVEL_MEDIUM == levelStatus ) { currentLevelStatus = LEVEL_STATE_MEDIUM ; } else if ( FPGA_LEVEL_HIGH == levelStatus ) { currentLevelStatus = LEVEL_STATE_HIGH ; } else { // TODO - Handle invalid level alarm currentLevelStatus = LEVEL_STATE_ILLEGAL ; } } else if ( P25_LEVL == levelId ) { levelStatus = getFPGAP25FloaterState(); if ( FPGA_LEVEL_LOW == levelStatus ) { currentLevelStatus = LEVEL_STATE_LOW ; } else if ( FPGA_LEVEL_MEDIUM == levelStatus ) { currentLevelStatus = LEVEL_STATE_MEDIUM ; } else if ( FPGA_LEVEL_HIGH == levelStatus ) { currentLevelStatus = LEVEL_STATE_HIGH ; } else { // TODO - Handle invalid level alarm currentLevelStatus = LEVEL_STATE_ILLEGAL ; } } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_DD_INVALID_LEVEL_ID, (U32)levelId ) } return currentLevelStatus; } /*********************************************************************//** * @brief * The publishLevelsData function broadcasts the level data at the * publication interval. * @details \b Inputs: levelsDataPublicationCounter * @details \b Outputs: levelsDataPublicationCounter * @details \b Message \b Sent: MSG_ID_DD_LEVEL_DATA to publish level * sensors data. * @return none *************************************************************************/ static void publishLevelsData( void ) { if ( ++levelsDataPublicationCounter >= getU32OverrideValue( &levelsDataPublishInterval ) ) { DD_LEVEL_DATA_T data; data.d6Level = (U32)getLevelStatus( D6_LEVL ); data.d63Level = (U32)getLevelStatus( D63_LEVL ); data.d46Level = (U32)getLevelStatus( D46_LEVL ); levelsDataPublicationCounter = 0; broadcastData( MSG_ID_DD_LEVEL_DATA, COMM_BUFFER_OUT_CAN_DD_BROADCAST, (U08*)&data, sizeof( DD_LEVEL_DATA_T ) ); } // publish IOFP float states on interval if ( ++fplevelsDataPublicationCounter >= getU32OverrideValue( &fplevelsDataPublishInterval ) ) { FP_LEVEL_DATA_T data; data.p25Level = (U32)getLevelStatus( P25_LEVL ); fplevelsDataPublicationCounter = 0; broadcastData( MSG_ID_FP_LEVEL_DATA, COMM_BUFFER_OUT_CAN_FP_BROADCAST, (U08*)&data, sizeof( FP_LEVEL_DATA_T ) ); } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testLevelsDataPublishIntervalOverride function overrides * the Level publish data interval. * @details \b Inputs: levelsDataPublishInterval * @details \b Outputs: levelsDataPublishInterval * @param Override message from Dialin which includes the interval * (in ms) to override the level data broadcast interval to. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testLevelsDataPublishIntervalOverride( MESSAGE_T *message ) { BOOL result = u32BroadcastIntervalOverride( message, &levelsDataPublishInterval, TASK_PRIORITY_INTERVAL ); return result; } /*********************************************************************//** * @brief * The testLevelStatusOverride function sets the override status * for a specific level sensor. * @details \b Inputs: none * @details \b Outputs: levelStatus * @param message Override message from Dialin which includes an ID of * the level sensor to override and the state to override the level sensor to. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testLevelStatusOverride( MESSAGE_T *message ) { BOOL result = u32ArrayOverride( message, &status[0], NUM_OF_LEVELS - 1, 0, NUM_OF_LEVELS_STATES -1 ); return result; } /*********************************************************************//** * @brief * The testFPLevelsDataPublishIntervalOverride function overrides the Level * data publish interval. * @details \b Inputs: levelsDataPublishInterval * @details \b Outputs: levelsDataPublishInterval * @param Override message from Dialin which includes the interval * (in ms) to override the level data broadcast interval to. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testFPLevelsDataPublishIntervalOverride( MESSAGE_T *message ) { BOOL result = u32BroadcastIntervalOverride( message, &fplevelsDataPublishInterval, TASK_PRIORITY_INTERVAL ); return result; } /*********************************************************************//** * @brief * The testFPLevelStateOverride function sets the override state for the floater * level sensor. * @details \b Inputs: none * @details \b Outputs: levelState * @param message Override message from Dialin which includes the state to * override the floater level sensor to. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testFPLevelStateOverride( MESSAGE_T *message ) { BOOL result = FALSE; TEST_OVERRIDE_ARRAY_PAYLOAD_T payload; OVERRIDE_TYPE_T ovType = getOverrideArrayPayloadFromMessage( message, &payload ); if ( ( payload.index >= FIRST_FP_LEVL ) && ( payload.index <= LAST_FP_LEVL ) ) { BOOL result = u32Override( message, &status[0], 0, NUM_OF_LEVELS_STATES -1 ); } return result; } /**@}*/