/**
******************************************************************************
* @file upp_errors.c
* @brief Формирование ошибок в ПУИ
******************************************************************************
* @details
Общая логика:
В программе выставляются всякие внутренние флаги ошибок: ERR_PRIVATE
В этом модуле смотрятся какие флаги выставились и переносят эти флаги
в структуру ошибок ПУИ ERR_PUI. 

Исключение: Программные ошибки и ошибки питания плат,
они пишутся напрямую в ERR_PUI.

Также реализована защита от дребезга и в целом задержка на выставление ошибок.
******************************************************************************/
#include "upp_main.h" // УПП
#include "upp_errors.h" // всё остальное по работе с УПП
UPP_Errors_t errors;

static UPP_ErrorType_t UPP_SelectCommonError(void);
__STATIC_FORCEINLINE int setError(int condition, int flag, int *timer, int delay);
void UPP_Errors_Program(void);
void UPP_Errors_Power(void);
void UPP_Errors_Ranges(void);
void UPP_Errors_LossPhase(void);
void UPP_Errors_Other(void);



void UPP_Errors_Handle(void)
{
  /*====== Программные ошибки ======*/
  UPP_Errors_Program();
  /*====== Ошибки питания плат ======*/
#ifndef UPP_DISABLE_PROTECT_BOARDPOWER
  UPP_Errors_Power();
#endif
  /*====== Ошибки выхода за допустимые пределы ======*/
  UPP_Errors_Ranges();
  /*====== Потери фазы ======*/
#ifndef UPP_DISABLE_PROTECT_LOSS_PHASE
  UPP_Errors_LossPhase();
#endif
  /*====== Остальные ======*/
  UPP_Errors_Other();
  
  
  
  errors.common = UPP_SelectCommonError();
}

void UPP_Errors_Program(void)
{
  
}

void UPP_Errors_Power(void)
{
  static int error_latch_ticks = 0;
  int error_latch_timeout = 5000;
  
  /* Считывание неисправностей источников питания */
  int err_24Vdio = GPIO_Read_Switch(&UPP_DIN.err_24Vdio);
  int err_24V = GPIO_Read_Switch(&UPP_DIN.err_24V);
  int err_5Vd = GPIO_Read_Switch(&UPP_DIN.err_5Vd);
  int err_5Vsi = GPIO_Read_Switch(&UPP_DIN.err_5Vsi);
  int err_Va = GPIO_Read_Switch(&UPP_DIN.err_Va);
  
  ERR_PUI.Power_DIO_24V = err_24Vdio;
  ERR_PUI.Power_24V = err_24V;
  ERR_PUI.Power_Digit_5V = err_5Vd;
  ERR_PUI.Power_SCI_5V = err_5Vsi;
  ERR_PUI.Power_Analog_5V = err_Va;
}

void UPP_Errors_Ranges(void)
{
  /* Преобразуем уставки в нормальные тики */
  float ticksTiMax = u2f(PARAM_PUI.TiMax, 1)/PM_SLOW_PERIOD_US;
  /* Счетчики для отсчитывания задержки выставления ошибки */
  static int IMaxCnt = 0;
  static int UMaxCnt = 0;
  static int UMinCnt = 0;
  static int FMaxCnt = 0;
  static int FMinCnt = 0;
  static int TMaxCnt = 0;
  
  /* Напряжения */  
  ERR_PUI.OverVoltage = setError(ERR_PRIVATE.uamp_max,
                                ERR_PUI.OverVoltage,
                                &UMaxCnt,
                                MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_UAMP_ERR)); 
  ERR_PUI.UnderVoltage = setError(ERR_PRIVATE.uamp_min,
                                  ERR_PUI.UnderVoltage,
                                  &UMinCnt,
                                  MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_UAMP_ERR)); 
  
  
  /* Токи */
  int i_max = ( ERR_PRIVATE.iamp_max ||
                ERR_PRIVATE.ia_max ||
                ERR_PRIVATE.ib_max ||
                ERR_PRIVATE.ic_max);
  ERR_PUI.OverCurrent = setError(i_max,
                                 ERR_PUI.OverCurrent,
                                 &IMaxCnt,
                                 ticksTiMax); 
  
  
  /* Частота */
  int f_max = ( ERR_PRIVATE.fac_max ||
                ERR_PRIVATE.fba_max ||
                ERR_PRIVATE.fbc_max);
  int f_min = ( ERR_PRIVATE.fac_min ||
                ERR_PRIVATE.fba_min ||
                ERR_PRIVATE.fbc_min);  
  ERR_PUI.OverFrequency = setError(f_max,
                                   ERR_PUI.OverFrequency,
                                   &FMaxCnt,
                                   MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_F_ERR));
  
  ERR_PUI.UnderFrequency = setError( f_min,
                                     ERR_PUI.UnderFrequency,
                                     &FMinCnt,
                                     MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_F_ERR));
  
  
  /* Температуры */
  ERR_PUI.OverTemperature = setError(ERR_PRIVATE.temp_err,
                                     ERR_PUI.OverTemperature,
                                     &TMaxCnt,
                                     MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_DEFAULT));
}

void UPP_Errors_LossPhase(void)
{
  /* Счетчики для отсчитывания задержки выставления ошибки */
  static int LossPhaseAllCnt = 0;
  static int LossPhaseACnt = 0;
  static int LossPhaseBCnt = 0;
  static int LossPhaseCCnt = 0;
  
  int loss_phases_all = ( ERR_PRIVATE.ia_min &&
                          ERR_PRIVATE.ib_min &&
                          ERR_PRIVATE.ic_min );
  
  ERR_PUI.LossPhaseAll = setError( loss_phases_all,
                                   ERR_PUI.LossPhaseAll,
                                   &LossPhaseAllCnt,
                                   MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_DEFAULT));
  
  /* Если хотя бы одна фаза есть проверяем фазы отдельно */
  if(!ERR_PUI.LossPhaseAll)
  {
    ERR_PUI.LossPhaseA = setError( ERR_PRIVATE.ia_min,
                                   ERR_PUI.LossPhaseA,
                                   &LossPhaseACnt,
                                   MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_DEFAULT));
    
    ERR_PUI.LossPhaseB = setError( ERR_PRIVATE.ib_min,
                                   ERR_PUI.LossPhaseB,
                                   &LossPhaseBCnt,
                                   MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_DEFAULT));
    
    ERR_PUI.LossPhaseC = setError( ERR_PRIVATE.ic_min,
                                   ERR_PUI.LossPhaseC,
                                   &LossPhaseCCnt,
                                   MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_DEFAULT));
  }
  /* Если всех фаз нет, то отдельные не смотрим */
  else
  {
    ERR_PUI.LossPhaseA = 0;
    ERR_PUI.LossPhaseB = 0;
    ERR_PUI.LossPhaseC = 0;
  }
}

void UPP_Errors_Other(void)
{
  static int InterlaceCnt = 0;
  
  
  if(ERR_PRIVATE.longstart)
    ERR_PUI.LongStart = 1;
  else
    ERR_PUI.LongStart = 0;
  
  ERR_PUI.Interlace = setError(ERR_PRIVATE.interlance,
                               ERR_PUI.Interlace,
                               &InterlaceCnt,
                               MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_DEFAULT));
  //Interlance
}

static UPP_ErrorType_t UPP_SelectCommonError(void)
{
  // Пока нет ошибки
  UPP_ErrorType_t best = Err_None;
  // Приоритет отсутствия ошибок
  uint8_t best_prio = UPP_ErrorPriority[Err_None];

  // Перебираем все возможные ошибки по enum
  for (int e = Err_None + 1; e <= Err_UnderFrequency; e++)
  {
    // Проверяем: установлен ли соответствующий бит в pui.all
    // e-1, потому что ошибка №1 = бит 0, ошибка №2 = бит 1 и т.д.
    if (errors.pui.all & (1u << (e - 1)))
    {
      // Получаем приоритет этой ошибки
      uint8_t pr = UPP_ErrorPriority[e];

      // Если её приоритет лучше (меньше число) — запоминаем
      if (pr < best_prio)
      {
        best_prio = pr;
        best = (UPP_ErrorType_t)e;
      }
    }
  }

  // Возвращаем самую важную (наивысшего приоритета) ошибку
  return best;
}


__STATIC_FORCEINLINE int setError(int condition, int flag, int *timer, int delay)
{
  if (condition) {
    if (*timer < delay)
      (*timer)++;
    else
      flag = 1;
  } else {
    if (*timer > 0)
      (*timer)--;
    else
      flag = 0;
  }

  return flag;
}