/*********************************************************************************************************************
*
* Description:     DataM module implementation
* FileName:        FBL_DataM.c
* Company:         ETAS GmbH (www.etas.com)
* Generated        28/12/2018 at 11:22
*
**********************************************************************************************************************
*
* COPYRIGHT RESERVED, Copyright ETAS GmbH 2018. All rights reserved, also regarding any disposal,
* exploitation, reproduction, editing, distribution, as well as in the event of applications
* for industrial property rights.
*
**********************************************************************************************************************
* History:
**********************************************************************************************************************
* Author        Date            Comment
**********************************************************************************************************************
BGA1YOK         25/10/2017      First Version    
**********************************************************************************************************************/


/*********************************************************************************************************************
* Includes
*********************************************************************************************************************/
#include "FBL_DataM.h"
#include "FBL_DataMPrv.h"
#include "FBL_DataMPrvCfg.h"
#include "FBL_UserCode.h"
#ifdef __PQRA_QAC__
#pragma PRQA_MESSAGES_OFF
#endif
#include "Os_TimeServices.h"
#ifdef __PQRA_QAC__
#pragma PRQA_MESSAGES_ON
#endif
#ifdef __PQRA_QAC__
#pragma PRQA_MESSAGES_OFF
#endif
#include "NvM.h"
#ifdef __PQRA_QAC__
#pragma PRQA_MESSAGES_ON
#endif
#include "BLSM.h"

/*********************************************************************************************************************
* Local Defines
*********************************************************************************************************************/

/*********************************************************************************************************************
* Local Macros
*********************************************************************************************************************/

/*********************************************************************************************************************
* Local Types
*********************************************************************************************************************/
/* [$Satisfies $RTA_FBL_SDK_DD_DATAM 16] */
typedef enum
{
  FBL_DATAM_CMPSTATE_NOT_INIT,
  FBL_DATAM_CMPSTATE_RUNNING_MIRROR_NOT_LOADED,
  FBL_DATAM_CMPSTATE_RUNNING_MIRROR_LOADED
}Fbl_DataM_CmpStateType;
/*********************************************************************************************************************
* Local Variables
*********************************************************************************************************************/
/* [$Satisfies $RTA_FBL_SDK_DD_DATAM 19] */
static Fbl_DataM_CmpStateType Fbl_DataM_CmpState = FBL_DATAM_CMPSTATE_NOT_INIT;
/* [$Satisfies $RTA_FBL_SDK_DD_DATAM 21] */
static uint32 Fbl_DataM_WriteBlockStartTimestamp;
/* [$Satisfies $RTA_FBL_SDK_DD_DATAM 23] */
static uint16 Fbl_DataM_NextUpdateBlockId;
/*********************************************************************************************************************
* Local Functions Prototypes
*********************************************************************************************************************/
/* [$Satisfies $RTA_FBL_SDK_DD_DATAM 32] */
static void Fbl_DataM_ClearCmpCtx (void);
/* [$Satisfies $RTA_FBL_SDK_DD_DATAM 33] */
static void Fbl_DataM_ManageBlockWriteProcess(void);
/* [$Satisfies $RTA_FBL_SDK_DD_DATAM 34] */
static void Fbl_DataM_MakeBlockWriteRequest(const uint16  blockIndex);
/*********************************************************************************************************************
* Local Functions Implementation
*********************************************************************************************************************/
/* [$Satisfies $RTA_FBL_SDK_DD_DATAM 32] */
static void Fbl_DataM_ClearCmpCtx( void )
{
  uint16 index;
  /* PRQA S 4304 EOF */ /* <Deviation: rule_10.5_4304_boolean */
  Fbl_DataM_RamMirrorLoaded = (boolean)FALSE;

  Fbl_DataM_WriteBlockStartTimestamp = 0;
  Fbl_DataM_NextUpdateBlockId = 0;
  Fbl_DataM_WriteBlockPending = (boolean)FALSE;

  for ( index = 0; index < FBL_DATAM_NUM_NVM_BLOCKS; index++ )
  {
    Fbl_DataM_BlockStatus[ index ] = FBL_DATAM_BLOCK_STATUS_SAVED;
    Fbl_DataM_NvmBlockFault[ index ] = (boolean)FALSE;
  }
}

/* [$Satisfies $RTA_FBL_SDK_DD_DATAM 33] */
static void Fbl_DataM_ManageBlockWriteProcess( void )
{
  uint16 blockIndex;
  boolean timeout; 
  Fbl_DataM_BlockStatusType blockStatus;

  if ( (boolean)FALSE == Fbl_DataM_WriteBlockPending )
  {
    blockIndex = Fbl_DataM_NextUpdateBlockId;
    do
    {
      blockStatus = Fbl_DataM_BlockStatus[ blockIndex ];
      if (FBL_DATAM_BLOCK_STATUS_NOT_SAVED==blockStatus)
      {
        Fbl_DataM_MakeBlockWriteRequest((const uint16) blockIndex );
      }
      blockIndex++;
      blockIndex %= FBL_DATAM_NUM_NVM_BLOCKS ;
    } while ( ( (boolean)FALSE == Fbl_DataM_WriteBlockPending ) &&
                  ( blockIndex != Fbl_DataM_NextUpdateBlockId ) );
  }
  else
  {
    timeout = OS_TimerExt_IsElapsed( Fbl_DataM_WriteBlockStartTimestamp, (uint32)FBL_DATAM_OPERATION_PENDING_TIMEOUT);
    if ( (boolean)TRUE  == timeout )
    {
      Fbl_DataM_WriteBlockPending = (boolean)FALSE;
      OS_Timer32_Stop(&Fbl_DataM_WriteBlockStartTimestamp);
    }
  }
}

/* [$Satisfies $RTA_FBL_SDK_DD_DATAM 34] */
static void Fbl_DataM_MakeBlockWriteRequest(const uint16  blockIndex )
{
  static uint8 Fbl_DataM_NvmBlocksTempBuffer[FBL_DATAM_MAX_NVM_BLOCK_BUFFER];   /* [$Satisfies $RTA_FBL_SDK_DD_DATAM 31] */
  Std_ReturnType setRamBlockStatusResult;
  Std_ReturnType writeBlockResult;
  (void) Fbl_DataM_MemCopy( Fbl_DataM_NvmBlocksTempBuffer, Fbl_DataM_NvmBlockInfo[blockIndex].RamMirror, Fbl_DataM_NvmBlockInfo[blockIndex].Size);
  (void) OS_TimerExt_Start( &Fbl_DataM_WriteBlockStartTimestamp);
  setRamBlockStatusResult = NvM_SetRamBlockStatus(Fbl_DataM_NvmBlockInfo[blockIndex].NvMBlockId, (boolean)TRUE);  
  if (E_OK == setRamBlockStatusResult)
  {
    writeBlockResult = NvM_WriteBlock(Fbl_DataM_NvmBlockInfo[blockIndex].NvMBlockId, Fbl_DataM_NvmBlocksTempBuffer);
    if ( E_OK == writeBlockResult )
    {
      Fbl_DataM_BlockStatus[blockIndex] = FBL_DATAM_BLOCK_STATUS_PENDING;
      Fbl_DataM_WriteBlockPending = (boolean)TRUE;
      Fbl_DataM_NextUpdateBlockId = blockIndex + 1U;
      Fbl_DataM_NextUpdateBlockId %= FBL_DATAM_NUM_NVM_BLOCKS;      
    } else {
      /* Nothing we can do - don't change the state */
    }
  }
}
/*********************************************************************************************************************
* Exported Variables Implementation
*********************************************************************************************************************/

/*********************************************************************************************************************
* Exported Functions Implementation
*********************************************************************************************************************/
/* [$Satisfies $RTA_FBL_SDK_DD_DATAM 6] */
void Fbl_DataM_Init (void)
{
  Fbl_DataM_ClearCmpCtx(); /* [$Satisfies $RTA_FBL_SDK_DD_DATAM 9] */
  Fbl_DataM_CmpState = FBL_DATAM_CMPSTATE_RUNNING_MIRROR_NOT_LOADED; /* [$Satisfies $RTA_FBL_SDK_DD_DATAM 10] */
}

/* [$Satisfies $RTA_FBL_SDK_DD_DATAM 7] */
void Fbl_DataM_DeInit (void)
{
  Fbl_DataM_CmpState = FBL_DATAM_CMPSTATE_NOT_INIT; /* [$Satisfies $RTA_FBL_SDK_DD_DATAM 12] */
  Fbl_DataM_ClearCmpCtx(); /* [$Satisfies $RTA_FBL_SDK_DD_DATAM 11] */
}

/* [$Satisfies $RTA_FBL_SDK_DD_DATAM 8] */
void Fbl_DataM_MainFunction (void)
{
  boolean allBlocksFault = (boolean)TRUE;
  uint32 loop;

  switch (Fbl_DataM_CmpState)
  {
    case FBL_DATAM_CMPSTATE_RUNNING_MIRROR_NOT_LOADED:
    {
      if ( (boolean)TRUE == Fbl_DataM_RamMirrorLoaded )
      {
        /* [$Satisfies $RTA_FBL_SDK_DD_DATAM 13] */
        Fbl_DataM_CmpState = FBL_DATAM_CMPSTATE_RUNNING_MIRROR_LOADED;
        /* [$Satisfies $RTA_FBL_SDK_DD_DATAM 14]
        if data flash is blank, call NvM_WriteAll once 
        and wait for first NVM write to finish */       
        for (loop=0; loop < FBL_DATAM_NUM_NVM_BLOCKS; loop++)
        {
          if ((boolean)FALSE == Fbl_DataM_NvmBlockFault[loop]) {allBlocksFault = (boolean)FALSE; break;}
        }
        if ((boolean)TRUE == allBlocksFault)
        {
          Fbl_DataM_WriteAllBlocks();
        }
      }
      else {
        /* Can't do anything until RAM mirror is loaded */
      }
      break;
    }
    case FBL_DATAM_CMPSTATE_NOT_INIT:
    {
      /* Can't do anything until the module is intialized. */
      break;
    }
    case FBL_DATAM_CMPSTATE_RUNNING_MIRROR_LOADED:
    {
      /* [$Satisfies $RTA_FBL_SDK_DD_DATAM 15] */
      /* Module has been initialiased and the RAM mirror loaded
         so process all blocks as necessary */
      Fbl_DataM_ManageBlockWriteProcess();
      break;
    }
    default:
    {
      /* Invalid state - we should never get here */
      break;
    }
  }
}

/* [$Satisfies $RTA_FBL_SDK_DD_DATAM 58] */
void Fbl_DataM_MultiBlockJobEndNotification (uint8 cnServiceIdP, NvM_RequestResultType cnJobResultP)
{
    if (NVM_SERVICE_ID_READ_ALL == cnServiceIdP)
    {
      Fbl_DataM_RamMirrorLoaded = (boolean)TRUE;
    }

    BLSM_NfyDataMJobEnd(cnServiceIdP, cnJobResultP);
}
