/** 
*******************************************************************************
* @file modbus_oscil.c
* @brief Модуль для реализации осциллографа через MODBUS.
*******************************************************************************
* @details 
* Файл содержит реализацию функций для работы с буфером осциллографа.
*
* @section oscil_functions Функции
* - MB_Oscil_Init()      — Инициализация структуры осциллографа
* - MB_Oscil_Add()       — Добавление данных в буфер
* - MB_Oscil_UpdateTail() — Обновление указателя Tail
******************************************************************************/
#include "modbus_oscil.h"

#ifdef MODBUS_ENABLE_OSCIL



/** 
 * @brief Инициализация структуры осциллографа
 * @param oscil Указатель на структуру осциллографа
 * @param BufferSize Размер буфера в Modbus регистрах (max 125)
 * @param NumbOfChannels Количество каналов (должно быть четным)
 * @param SampleTime Период дискретизации
 * @return HAL Status
 */
HAL_StatusTypeDef MB_Oscil_Init(MB_OscilTypeDef *oscil, uint8_t BufferSize, uint8_t NumbOfChannels, uint16_t SampleTime)
{
  if(!oscil || (BufferSize >= MbData_size) || (NumbOfChannels > 15) || !SampleTime)
    return HAL_ERROR;
  
  if(NumbOfChannels % 2 != 0)
    return HAL_ERROR;
  
  oscil->Config.BufferSize = BufferSize;
  oscil->Config.NumbOfChannels = NumbOfChannels;
  oscil->Config.SampleTime = SampleTime;
  oscil->Config.LastTick = 0;
  oscil->Preamble.Head = 0;
  oscil->Preamble.Tail = 0;
  oscil->Config.Overrun = 0;
  
  return HAL_OK;
}


/** 
 * @brief Инициализация структуры осциллографа
 * @param oscil Указатель на структуру осциллографа
 * @param BufferSize Размер буфера в Modbus регистрах (max 125)
 * @param NumbOfChannels Количество каналов (должно быть четным)
 * @param SampleTime Период дискретизации
 * @return HAL Status
 */
HAL_StatusTypeDef MB_Oscil_Set_SampleTime(MB_OscilTypeDef *oscil, uint16_t SampleTime)
{
  if(!oscil || !SampleTime)
    return HAL_ERROR;
  
  oscil->Config.SampleTime = SampleTime;  
  return HAL_OK;
}


/** 
 * @brief Добавление данных всех каналов в буфер
 * @param oscil Указатель на структуру осциллографа
 * @param Data Массив с данными каналов (длина = NumbOfChannels)
 * @return HAL Status
 */
HAL_StatusTypeDef MB_Oscil_Add(MB_OscilTypeDef *oscil, uint8_t *Data)
{
  if(!oscil || !Data)
    return HAL_ERROR;
  
  if(local_time() - oscil->Config.LastTick < oscil->Config.SampleTime)
    return HAL_BUSY;
  
  oscil->Config.LastTick = local_time();
  
  uint8_t head_max = oscil->Config.BufferSize * 2;
  uint8_t start_pos = oscil->Preamble.Head;
  uint8_t channels = oscil->Config.NumbOfChannels;
  uint8_t tail = oscil->Preamble.Tail;
  
  // Пишем данные
  for(uint8_t i = 0; i < channels; i++) 
  {
    uint8_t write_pos = (start_pos + i) % head_max;
    oscil->Data[write_pos] = Data[i];
  }
  
  // Новое положение Head
  uint8_t new_head = (start_pos + channels) % head_max;
  
  // Проверяем, есть ли непрочитанные данные (Tail != Head)
//  if(tail != start_pos) {
    // Если есть непрочитанные данные, проверяем не перетерли ли мы их
    if(start_pos < new_head) {
      // Линейный случай
      if(tail > start_pos && tail <= new_head) {
        oscil->Preamble.Tail = (new_head + 1) % head_max;
        oscil->Config.Overrun = 1;
      }
    } else if(start_pos > new_head) {
      // Кольцевой случай (было переполнение)
      if(tail > start_pos || tail <= new_head) {
        oscil->Preamble.Tail = (new_head + 1) % head_max;
        oscil->Config.Overrun = 1;
      }
    }
//  }
  oscil->Preamble.Head = new_head;
  
  return HAL_OK;
}

/** 
 * @brief Обновление указателя Tail
 * @param oscil Указатель на структуру осциллографа
 * @return HAL Status
 *
 * @details Вызывается после того, как данные были прочитаны через Modbus.
 *          Tail устанавливается на следующий байт за Head.
 */
HAL_StatusTypeDef MB_Oscil_UpdateTail(MB_OscilTypeDef *oscil)
{
  if(!oscil)
    return HAL_ERROR;
  
  // Сдвигаем Tail на следующий байт за Head
  uint8_t head_max = oscil->Config.BufferSize * 2;
  oscil->Preamble.Tail = oscil->Preamble.Head;
  
  if(oscil->Preamble.Tail >= head_max)
    oscil->Preamble.Tail = 0;
  
  oscil->Config.Overrun = 0;
  
  return HAL_OK;
}


#endif //MODBUS_ENABLE_OSCIL