/********************************* (C) РОТЕК ***********************************
 * @module  ups_monitor
 * @file    ups_monitor.c
 * @version 1.0.0
 * @date    XX.XX.XXXX
 * $brief   Template
 *******************************************************************************
 * @history     Version  Author         Comment
 * XX.XX.XXXX   1.0.0    Telenkov D.A.  First release.
 *******************************************************************************
 */
   
#include "stm32f4xx.h"
#include "ups_monitor.h"
#include "parameters.h"
#include "settings_api.h"
#include "megatec.h"
#include "led.h"
#include "log.h"

#include "FreeRTOS.h"
#include "task.h"

#include "trap_api.h"
#include "snmp_api.h"

#include <stdbool.h>

#define UPS_LOAD                70.0    // Нагрука (граница)
#define UPS_LOAD_HIST           1.0     // Гистерезис нагрузки 

#define UPS_TEMPERATURE         40.0    // Температура (граница)
#define UPS_TEMPERATURE_HIST    1.0     // Гистерезис температуры 


bool flCriticalAlarm = false;
bool flNonCriticalAlarm = false;

/**
  * @brief  Общая структура настроек
  */
extern SETTINGS_t sSettings;

extern bool flUpdateLog;


/**
  * @brief  Задача мониторинга параметров UPS
  */
void UPS_Monitor(void *params)
{
	vTaskDelay(5000);
  for (;;)
  {
	  flCriticalAlarm = false;
	  flNonCriticalAlarm = false;
    // Проверяем флаг подключения UPS
   if (UPS.Present)
    {
      UPS_LineFailMonitor();
      UPS_LowBatMonitor();  
      UPS_PowerMonitor();
      UPS_TemperatureMonitor();
      UPS_BatteryConnectMonitor();
    }
    UPS_ConnectMonitor();
    UPS_DI0Monitor();

    UPS_CriticalAlarmMonitor();
    UPS_NonCriticalAlarmMonitor();
      
    vTaskDelay(1000);
  }
}

/**
  * @brief  Мониторинг бита DI0 state
  */
void UPS_DI0Monitor(void)
{
  static bool isValueRecv = false;
  static uint8_t DI0OldState = 0;
  uint8_t DI0StateCurrent;

  DI0StateCurrent = get_state_din_outs(DIN1);

  	if (!isValueRecv) {
	  isValueRecv = true;
	  DI0OldState = DI0StateCurrent;
	  if (!DI0StateCurrent){
		  log_event_data(LOG_ALARM_DIO, "Авария");
		  SNMP_SendUserTrap(DI0_ALARM);
		  flUpdateLog = true;
	  }
	  return;
	}

	if (!DI0StateCurrent)
	  flCriticalAlarm = true;

	// Значение параметра изменилось
	if (DI0StateCurrent != DI0OldState)
	{
	  if(sSettings.sInOuts.din_type_act[0] == SNMP_TRAP)
	  {
		  if (DI0StateCurrent){
			log_event_data(LOG_ALARM_DIO, "Норма");
			SNMP_SendUserTrap(DI0_NORM);
			flUpdateLog = true;
		  }
		  else{
			log_event_data(LOG_ALARM_DIO, "Авария");
			SNMP_SendUserTrap(DI0_ALARM);
			flUpdateLog = true;
		  }
	  }
	}

	DI0OldState = DI0StateCurrent;
}

/**
  * @brief  Мониторинг бита CriticalAlarm
  */
void UPS_CriticalAlarmMonitor(void)
{
  static bool isValueRecv = false;
  static uint8_t CriticalAlarmOldState = 0;
  uint8_t CriticalAlarmCurrent;
  static uint8_t OldRO0type_Sourse = 0;
  static uint8_t OldRO2type_Sourse = 0;
  uint8_t CurrRO2type_Sourse = 0;
  uint8_t CurrRO1type_Sourse = 0;
  char str[30];

  memset(str, 0, 30);

  CurrRO1type_Sourse = sSettings.sInOuts.ro_type_source[0];
  CurrRO2type_Sourse = sSettings.sInOuts.ro_type_source[1];
  CriticalAlarmCurrent = flCriticalAlarm;

  if (!isValueRecv) {
	isValueRecv = true;
	CriticalAlarmOldState = CriticalAlarmCurrent;
	OldRO0type_Sourse = CurrRO1type_Sourse;
	OldRO2type_Sourse = CurrRO2type_Sourse;
	if(OldRO0type_Sourse == CRITICAL){
	  if(CriticalAlarmCurrent){
		  SetROInt(1, 0);
		  SNMP_SendUserTrap(DO0_TOGGLED);
		  log_event_data(LOG_DO0_STATE, "Замкнуто");
	  }
    }
	  if(OldRO2type_Sourse == CRITICAL){
		  if(CriticalAlarmCurrent){
			  SetROInt(1, 1);
			  SNMP_SendUserTrap(DO1_TOGGLED);
			  log_event_data(LOG_DO1_STATE, "Замкнуто");
		  }
	  }
	return;
  }

  if(CriticalAlarmCurrent){
	  if (UPS.Present)
		  LED_Toggle(LED_MAJOR_R);
	  else
		  LED_On(LED_MAJOR_R);
  }
  else{
	  LED_Off(LED_MAJOR_R);
  }

  // Значение параметра изменилось
  if (CriticalAlarmCurrent != CriticalAlarmOldState)
  {
	  if(OldRO0type_Sourse == CRITICAL){
		  memset(str, 0, 30);
		  if(CriticalAlarmCurrent){
			  SetROInt(1, 0);
			  strcat(str, "Замкнуто");
		  }
		  else{
			  SetROInt(0, 0);
			  strcat(str, "Разомкнуто");
		  }
		  SNMP_SendUserTrap(DO0_TOGGLED);
		  log_event_data(LOG_DO0_STATE, str);
	  }
	  if(OldRO2type_Sourse == CRITICAL){
		  memset(str, 0, 30);
		  if(CriticalAlarmCurrent){
			  SetROInt(1, 1);
			  strcat(str, "Замкнуто");
		  }
		  else{
			  SetROInt(0, 1);
			  strcat(str, "Разомкнуто");
		  }
	  	  SNMP_SendUserTrap(DO1_TOGGLED);
	  	log_event_data(LOG_DO1_STATE, str);
	  }
  }
  else
  {
	  if(OldRO0type_Sourse == CRITICAL && OldRO0type_Sourse != OldRO0type_Sourse){
		  memset(str, 0, 30);
		  if(CriticalAlarmCurrent){
			  SetROInt(1, 0);
			  strcat(str, "Замкнуто");
		  }
		  else{
			  SetROInt(0, 0);
			  strcat(str, "Разомкнуто");
		  }
		  SNMP_SendUserTrap(DO0_TOGGLED);
		  log_event_data(LOG_DO0_STATE, str);
	  }
	  if(OldRO2type_Sourse == CRITICAL && OldRO2type_Sourse != OldRO2type_Sourse){
		  memset(str, 0, 30);
		  if(CriticalAlarmCurrent){
			  SetROInt(1, 1);
			  strcat(str, "Замкнуто");
		  }
		  else{
			  SetROInt(0, 1);
			  strcat(str, "Разомкнуто");
		  }
		  SNMP_SendUserTrap(DO1_TOGGLED);
		  log_event_data(LOG_DO1_STATE, str);
	  }
  }

  OldRO0type_Sourse = CurrRO1type_Sourse;
  OldRO2type_Sourse = CurrRO2type_Sourse;
  CriticalAlarmOldState = CriticalAlarmCurrent;
}

/**
  * @brief  Мониторинг бита NonCriticalAlarm
  */
void UPS_NonCriticalAlarmMonitor(void)
{
  static bool isValueRecv = false;
  static uint8_t NonCriticalAlarmOldState = 0;
  uint8_t NonCriticalAlarmCurrent;
  static uint8_t OldRO0type_Sourse = 0;
  static uint8_t OldRO2type_Sourse = 0;
  uint8_t CurrRO2type_Sourse = 0;
  uint8_t CurrRO1type_Sourse = 0;
  char str[30];
  memset(str, 0, 30);

  CurrRO1type_Sourse = sSettings.sInOuts.ro_type_source[0];
  CurrRO2type_Sourse = sSettings.sInOuts.ro_type_source[1];
  NonCriticalAlarmCurrent = flNonCriticalAlarm;

  if (!isValueRecv) {
	isValueRecv = true;
	NonCriticalAlarmOldState = NonCriticalAlarmCurrent;
	OldRO0type_Sourse = CurrRO1type_Sourse;
	OldRO2type_Sourse = CurrRO2type_Sourse;
	if(OldRO0type_Sourse == NON_CRITICAL){
	  if(NonCriticalAlarmCurrent){
		  SetROInt(1, 0);
		  SNMP_SendUserTrap(DO0_TOGGLED);
		  log_event_data(LOG_DO0_STATE, "Замкнуто");
	  }
	}
	  if(OldRO2type_Sourse == NON_CRITICAL){
		  if(NonCriticalAlarmCurrent){
			  SetROInt(1, 1);
			  SNMP_SendUserTrap(DO1_TOGGLED);
			 log_event_data(LOG_DO1_STATE, "Замкнуто");
		  }
	  }
	return;
  }

  // Значение параметра изменилось
  if (NonCriticalAlarmCurrent != NonCriticalAlarmOldState)
  {
	  if(OldRO0type_Sourse == NON_CRITICAL){
		  memset(str, 0, 30);
		  if(NonCriticalAlarmCurrent){
			  SetROInt(1, 0);
			  strcat(str, "Замкнуто");
		  }
		  else{
			  SetROInt(0, 0);
			  strcat(str, "Разомкнуто");
		  }
		  SNMP_SendUserTrap(DO0_TOGGLED);
		  log_event_data(LOG_DO0_STATE, str);
	  }
	  if(OldRO2type_Sourse == NON_CRITICAL){
		  memset(str, 0, 30);
		  if(NonCriticalAlarmCurrent){
			  SetROInt(1, 1);
			  strcat(str, "Разомкнуто");
		  }
		  else{
			  SetROInt(0, 1);
			  strcat(str, "Замкнуто");
		  }
	  	  SNMP_SendUserTrap(DO1_TOGGLED);
	  	log_event_data(LOG_DO1_STATE, str);
	  }
  }
  else
  {
	  if(OldRO0type_Sourse == NON_CRITICAL && OldRO0type_Sourse != OldRO0type_Sourse){
		  memset(str, 0, 30);
		  if(NonCriticalAlarmCurrent){
			  SetROInt(1, 0);
			  strcat(str, "Замкнуто");
		  }
		  else{
			  SetROInt(0, 0);
			  strcat(str, "Разомкнуто");
		  }
		  SNMP_SendUserTrap(DO0_TOGGLED);
		  log_event_data(LOG_DO0_STATE, str);
	  }
	  if(OldRO2type_Sourse == NON_CRITICAL && OldRO2type_Sourse != OldRO2type_Sourse){
		  memset(str, 0, 30);
		  if(NonCriticalAlarmCurrent){
			  SetROInt(1, 1);
			  strcat(str, "Замкнуто");
		  }
		  else{
			  SetROInt(0, 1);
			  strcat(str, "Разомкнуто");
		  }
		  SNMP_SendUserTrap(DO1_TOGGLED);
		  log_event_data(LOG_DO1_STATE, str);
	  }
  }

  OldRO0type_Sourse = CurrRO1type_Sourse;
  OldRO2type_Sourse = CurrRO2type_Sourse;
  NonCriticalAlarmOldState = NonCriticalAlarmCurrent;
}

/**
  * @brief  Мониторинг бита LainFail
  */
void UPS_LineFailMonitor(void)
{
  static bool isValueRecv = false;
  static uint8_t lineFailOldState = 0;
  uint8_t lineFailCurrent;
  
  lineFailCurrent = (UPS.Status >> 7) & 0x01;
  
  if (!isValueRecv) {
    isValueRecv = true;
    lineFailOldState = lineFailCurrent;
    if (lineFailCurrent){
    	log_event_data(LOG_ALARM_LINE, "Авария");
          SNMP_SendUserTrap(LINE_ALARM);
          flUpdateLog = true;
    }
    return;
  }

  if (lineFailCurrent)
	  flCriticalAlarm = true;
    
  // Значение параметра изменилось
  if (lineFailCurrent != lineFailOldState)
  {
    if (lineFailCurrent){
      log_event_data(LOG_ALARM_LINE, "Авария");
      SNMP_SendUserTrap(LINE_ALARM);
      flUpdateLog = true;
    }
    else{
      log_event_data(LOG_ALARM_LINE, "Норма");
      SNMP_SendUserTrap(LINE_NORM);
      flUpdateLog = true;
    }
  }
  
  lineFailOldState = lineFailCurrent;
}

/**
  * @brief  Мониторинг бита LowBat  
  */
void UPS_LowBatMonitor(void)
{
  static bool isValueRecv = false;
  static uint8_t lowBatOldState = 0;
  uint8_t lowBatCurrent;
  if((UPS.Status >> 7) & 0x01)
	  lowBatCurrent = (UPS.Status >> 6) & 0x01;
  else
	  lowBatCurrent = 0;
  
  if (!isValueRecv) {
    isValueRecv = true;
    lowBatOldState = lowBatCurrent;
    if (lowBatCurrent){
    	log_event_data(LOG_ALARM_LOW_BAT, "Авария");
          SNMP_SendUserTrap(LOW_BAT_ALARM);
          flUpdateLog = true;
    }
    return;
  }
  if (lowBatCurrent)
	  flNonCriticalAlarm = true;
  // Значение параметра изменилось
  if (lowBatCurrent != lowBatOldState)
  {
    if (lowBatCurrent){
      SNMP_SendUserTrap(LOW_BAT_ALARM);
      log_event_data(LOG_ALARM_LOW_BAT, "Авария");
      flUpdateLog = true;
    }
    else{
      SNMP_SendUserTrap(LOW_BAT_NORM);
      log_event_data(LOG_ALARM_LOW_BAT, "Норма");
      flUpdateLog = true;
    }
  }
  
  lowBatOldState = lowBatCurrent;
}

/**
  * @brief  Мониторинг нагрузки 
  */
void UPS_PowerMonitor(void)
{
  float load;
  static uint8_t stateCurrent = HYST_IDLE;
    
  load = UPS.Load;

  /* Отслеживается переход через верхнюю границу */
  if (load > UPS_LOAD)
  {
	flCriticalAlarm = true;
	UPS.Alarm = (UPS.Alarm & 0x0e) | (1 << 0);
    if (stateCurrent == HYST_IDLE)
	{
      LED_On(LED_MINOR_R);
      LED_On(LED_MINOR_G);
	  stateCurrent = HYST_UP;
      
	  log_event_data(LOG_ALARM_POWER, "Авария");
	  // Отправка трапа о завышении 
	  SNMP_SendUserTrap(POWER_ALARM);
	  flUpdateLog = true;
	}
  }
  /* Отслеживается нормализация */
  else if (load < (UPS_LOAD - UPS_LOAD_HIST))
  {
	UPS.Alarm = (UPS.Alarm & 0x0e);
    if (stateCurrent == HYST_UP)
	{
    	LED_Off(LED_MINOR_R);
    	LED_Off(LED_MINOR_G);
	  stateCurrent = HYST_IDLE;
      
	  log_event_data(LOG_ALARM_POWER, "Норма");
	  // Отправка трапа о нормализации 
	  SNMP_SendUserTrap(POWER_NORM);
	  flUpdateLog = true;
	}
  }
}

/**
  * @brief  Мониторинг температуры 
  */
void UPS_TemperatureMonitor(void)
{
  float temperature;
  static uint8_t stateCurrent = HYST_IDLE;
    
  temperature = UPS.Temp;

  /* Отслеживается переход через верхнюю границу */
  if (temperature > UPS_TEMPERATURE)
  {
	flCriticalAlarm = true;
	UPS.Alarm = (UPS.Alarm & 0x0d) | (1 << 1);
    if (stateCurrent == HYST_IDLE)
	{
	  stateCurrent = HYST_UP;
      

	  log_event_data(LOG_ALARM_TEMP, "Авария");
	  // Отправка трапа о завышении 
	  SNMP_SendUserTrap(BATTERY_TEMPERATURE_ALARM);
	  flUpdateLog = true;
	}
  }
  /* Отслеживается нормализация */
  else if (temperature < (UPS_TEMPERATURE - UPS_TEMPERATURE_HIST))
  {
	  UPS.Alarm = (UPS.Alarm & 0x0d);
    if (stateCurrent == HYST_UP)
	{
	  stateCurrent = HYST_IDLE;
      
	  log_event_data(LOG_ALARM_TEMP, "Норма");
	  // Отправка трапа о нормализации 
	  SNMP_SendUserTrap(BATTERY_TEMPERATURE_NORM);
	  flUpdateLog = true;
	}
  }
}

/**
  * @brief  Мониторинг параметра upsParams.connect
  */
void UPS_ConnectMonitor(void)
{
  static bool isValueRecv = false;
  static uint8_t connectOldState = 0;
  uint8_t connectCurrent;
  
  connectCurrent = UPS.Present;
  
  UPS.Alarm = (UPS.Alarm & 0x0b) | ((connectCurrent^1) << 2);

  if (!isValueRecv) {
    isValueRecv = true;
    connectOldState = connectCurrent;
    if (!connectCurrent){
    	log_event_data(LOG_ALARM_UPS, "Авария");
    	SNMP_SendUserTrap(CONNECT_MONITOR_ALARM);
    	flUpdateLog = true;
    }
    return;
  }
  if (!connectCurrent)
  	  flCriticalAlarm = true;
  // Значение параметра изменилось
  if (connectCurrent != connectOldState)
  {
    if (connectCurrent){
      log_event_data(LOG_ALARM_UPS, "Норма");
      SNMP_SendUserTrap(CONNECT_MONITOR_NORM);
      flUpdateLog = true;
    }
    else{
      log_event_data(LOG_ALARM_UPS, "Авария");
      SNMP_SendUserTrap(CONNECT_MONITOR_ALARM);
      flUpdateLog = true;
    }
  }
  
  connectOldState = connectCurrent;
}

/**
  * @brief  Мониторинг параметра upsParams.connect
  */
void UPS_BatteryConnectMonitor(void)
{
  static bool isValueRecv = false;
  static uint8_t AKBconnectOldState = 0;
  uint8_t AKBconnectCurrent;

  if(((UPS.Status >> 7) & 0x01) == 0)
	  AKBconnectCurrent = (UPS.Status >> 6) & 0x01;
  else{
	  AKBconnectCurrent = 0;
  }

  UPS.Alarm = (UPS.Alarm & 0x07) | (AKBconnectCurrent << 3);

  if (!isValueRecv) {
    isValueRecv = true;
    AKBconnectOldState = AKBconnectCurrent;
    if (AKBconnectCurrent){
      log_event_data(LOG_ALARM_AKB, "Авария");
	  SNMP_SendUserTrap(BATTERY_CONNECT_ALARM);
	  flUpdateLog = true;
    }
    return;
  }
  if (AKBconnectCurrent)
  	  flCriticalAlarm = true;
  // Значение параметра изменилось
  if (AKBconnectCurrent != AKBconnectOldState)
  {
    if (!AKBconnectCurrent){
      log_event_data(LOG_ALARM_AKB, "Норма");
      SNMP_SendUserTrap(BATTERY_CONNECT_NORM);
      flUpdateLog = true;
    }
    else{
      log_event_data(LOG_ALARM_AKB, "Авария");
      SNMP_SendUserTrap(BATTERY_CONNECT_ALARM);
      flUpdateLog = true;
    }
  }

  AKBconnectOldState = AKBconnectCurrent;
}


/********************************* (C) РОТЕК **********************************/
