/**
*******************************************************************************
* @file modbus_holdregs.c
* @brief Реализация работы с регистрами хранения Modbus
*******************************************************************************
* @details
Модуль для доступа к регистрам внутри программы:
- Функции для доступа к регистрам хранения по глобальным адресам

Модуль обработки команд для holding registers (регистров хранения):
- Чтение множественных регистров (0x03) - копирование данных в буфер ответа
- Запись одиночного регистра (0x06) - прямая запись значения
- Запись множественных регистров (0x10) - пакетная запись из буфера

@section hvalid Валидация данных:
- Проверка соответствия количества байт и регистров
- Валидация адресов через MB_DefineRegistersAddress()
- Обработка исключений при некорректных запросах
******************************************************************************/
#include "modbus_inputregs.h"


#ifdef MODBUS_ENABLE_HOLDINGS



/** 
  * @brief    Записать регистр хранения по глобальному адресу.
  * @param    Addr          Адрес регистра.
  * @param    WriteVal      Число для записи.
  * @return   ExceptionCode Код исключения если регистра по адресу не существует, и ET_NO_ERRORS если все ок.
  *
  * @details  Позволяет обратиться к любому регистру по его глобальному адрессу.
              Вне зависимости от того как регистры размещены в памяти.
  */
MB_ExceptionTypeDef MB_Holding_Write_Global(uint16_t Addr, uint16_t WriteVal)
{
  //---------CHECK FOR ERRORS----------
  MB_ExceptionTypeDef Exception = ET_NO_ERRORS;
  uint16_t *pHoldRegs;
  
  //------------WRITE COIL-------------
  Exception = MB_DefineRegistersAddress(&pHoldRegs, Addr, 1, RegisterType_Holding);
  if(Exception == ET_NO_ERRORS)
  {
    *(pHoldRegs) = WriteVal;
  }
  return Exception;
}


/** 
  * @brief    Считать регистр хранения по глобальному адресу.
  * @param    Addr          Адрес регистра.
  * @param    Exception     Указатель на переменную для кода исключения, в случае неудачи при чтении.
  * @return   uint16_t      Возвращает значение регистра.
  *
  * @details  Позволяет обратиться к любому регистру по его глобальному адрессу.
              Вне зависимости от того как регистры размещены в памяти.
  */
uint16_t MB_Holding_Read_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception)
{
  //---------CHECK FOR ERRORS----------
  MB_ExceptionTypeDef Exception_tmp = 0;
    
  uint16_t *pHoldRegs;
  
  //------------READ COIL--------------
  Exception_tmp = MB_DefineRegistersAddress(&pHoldRegs, Addr, 1, RegisterType_Holding);

  if(Exception)             // if exception is not given to func fill it
    *Exception = Exception_tmp;

  if(Exception_tmp == ET_NO_ERRORS)
  {
    return *(pHoldRegs);
  }
  else
  {
    return 0;
  }
}



/**
  * @brief    Обработать функцию Read Holding Registers (03 - 0x03).
  * @param    modbus_msg        Указатель на структуру собщения modbus.
  * @return   fMessageHandled   Статус о результате обработки комманды.
  * @details  Обработка команды Read Holding Registers.
  */
uint8_t MB_Process_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg)
{
  //---------CHECK FOR ERRORS---------- 
  // get origin address for data
  uint16_t *pHoldRegs;
  modbus_msg->Except_Code = MB_DefineRegistersAddress(&pHoldRegs, modbus_msg->Addr, modbus_msg->Qnt, RegisterType_Holding); // определение адреса регистров
  if(modbus_msg->Except_Code != ET_NO_ERRORS)
    return 0;
  

  //-----------READING REGS------------
  // setup output message data size 
  modbus_msg->ByteCnt = modbus_msg->Qnt*2; // *2 because we transmit 8 bits, not 16 bits
  // read data
  int i;
  for (i = 0; i<modbus_msg->Qnt; i++)
  {
    modbus_msg->MbData[i] = *(pHoldRegs++);
  } 
  return 1;
}

/**
  * @brief    Обработать функцию Write Single Register (06 - 0x06).
  * @param    modbus_msg        Указатель на структуру собщения modbus.
  * @return   fMessageHandled   Статус о результате обработки комманды.
  * @details  Обработка команды Write Single Register.
  */
uint8_t MB_Process_Write_Single_Reg(RS_MsgTypeDef *modbus_msg)
{ 
  // get origin address for data
  uint16_t *pHoldRegs;      
  modbus_msg->Except_Code = MB_DefineRegistersAddress(&pHoldRegs, modbus_msg->Addr, 1, RegisterType_Holding); // определение адреса регистров
  if(modbus_msg->Except_Code != ET_NO_ERRORS)
    return 0;
  
  //-----------WRITTING REG------------ 
  *(pHoldRegs) = modbus_msg->Qnt;
  return 1;
}

/**
  * @brief    Обработать функцию Write Multiple Registers (16 - 0x10).
  * @param    modbus_msg        Указатель на структуру собщения modbus.
  * @return   fMessageHandled   Статус о результате обработки комманды.
  * @details  Обработка команды Write Multiple Registers.
  */
uint8_t MB_Process_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg)
{
  //---------CHECK FOR ERRORS----------
  if (modbus_msg->Qnt*2 != modbus_msg->ByteCnt)
  { // if quantity and bytes count arent match
    modbus_msg->Except_Code = 3;
    return 0;
  } 
  // get origin address for data
  uint16_t *pHoldRegs;      
  modbus_msg->Except_Code = MB_DefineRegistersAddress(&pHoldRegs, modbus_msg->Addr, modbus_msg->Qnt, RegisterType_Holding); // определение адреса регистров
  if(modbus_msg->Except_Code != ET_NO_ERRORS)
    return 0;
  
  //-----------WRITTING REGS----------- 
  for (int i = 0; i<modbus_msg->Qnt; i++)
  {
    *(pHoldRegs++) = modbus_msg->MbData[i];
  }
  return 1;
}

#endif //MODBUS_ENABLE_HOLDINGS
