/**
******************************************************************************
* @file modbus_coils.h
* @brief Работа с коилами Modbus
******************************************************************************
@addtogroup MODBUS_COILS Coils Tools
@ingroup MODBUS_INTERNAL
******************************************************************************
* @details
Модуль предоставляет функции и макросы для работы с битовыми данными:
- Чтение coils (0x01) Упаковка битов в байты
- Запись одиночного coil (0x05) Установка/сброс бита
- Запись множественных coils (0x0F) - распаковка байтов в биты
- Макросы для локального доступа к coils

@section  Организация битовых данных:
Coils упакованы в 16-битные слова для эффективного использования памяти.
Биты нумеруются от младшего к старшему внутри каждого слова.

@section  Адресация:
- Глобальная - абсолютный адрес в пространстве Modbus
- Локальная - относительный адрес внутри массива coils
- Макросы автоматически вычисляют смещения и маски

******************************************************************************/
#ifndef __MODBUS_COILS_H_
#define __MODBUS_COILS_H_
#include "modbus_core.h"

/////////////////////////////////////////////////////////////////////
////////////////////---MODBUS FUNCTION DEFINES---////////////////////



//--------------------------------------------------

/**
  * @addtogroup MODBUS_COILS
  * @{
  */

/** 
  * @brief  Макрос для установки указателя на регистр, содержащий запрашиваемый коил
  * @param  _parr_  - массив коилов.
  * @param  _coil_  - Номер коила от начала массива _arr_.
  * @note   Используется вместе с @ref MB_Set_Coil_Mask
  @verbatim Пояснение выражений
  - (_coil_/16) - индекс регистра, в котором содержится коил по адресу _coil_

Визуальный пример: 30 коил будет в 30/16 = 1 регистре (индексация с 0)
  xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxCx
  |register[0]----| |register[1]----|
  |skip this------| |get this-------|
                    |shift to 14 bit|
  @endverbatim
  */
#define MB_Set_Coil_Reg_Ptr(_parr_, _coil_)           ((uint16_t *)(_parr_)+((_coil_)/16))
/** 
  * @brief  Макрос для установки маски, чтобы выделить запрашиваемый коил из регистра
  * @param  _coil_  - Номер коила от начала массива _arr_.
  * @note   Используется вместе с @ref MB_Set_Coil_Reg_Ptr
  @verbatim Пояснение выражений
  - (16*(_coil_/16) - сколько коилов нужно пропустить. прим. (16*30/16) - первые 16 коилов находятся вне регистра
  - _coil_-(16*(_coil_/16)) - сдвинуть бит на место запрашиваемого коила в регистре
  
  Визуальный пример: 30 коил будет регистре[1], на 14 бите:
    register = 30/16 = 1
    bit = 30 - (16*30/16) = 30 - 16 = 14
    
  xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxCx
  |register[0]----| |register[1]----|
  |skip this------| |get this-------|
                    |shift to 14 bit|
  @endverbatim
  */
#define MB_Set_Coil_Mask(_coil_)                      (1 << ( _coil_ - (16*((_coil_)/16)) ))
  
/** MODBUS_COILS
  * @} 
  */
  

/////////////////////////////////////////////////////////////////////
/////////////////////////---FUNCTIONS---/////////////////////////////
  

/**
  * @addtogroup MODBUS_REQ_COILS_API API for Coils  
  * @ingroup    MODBUS_REQUEST_MSG
  * @brief API для чтения coils из ответа в режиме мастер
  * @details Примеры использования:
  * 
  * @code
  * // Пример: Запросили 10 coils с адреса 20, хотим узнать состояние coil 25
  * int coil_state;
  * if(MB_RespGet_CoilState(&MODBUS_MSG, 25, &coil_state))
  * {
  *   printf("Coil 25 state: %s\n", coil_state ? "ON" : "OFF");
  * }
  * 
  * // Пример: Получить состояние всех запрошенных coils
  * for(int addr = MODBUS_MSG.Addr; addr < MODBUS_MSG.Addr + MODBUS_MSG.Qnt; addr++)
  * {
  *   int state;
  *   if(MB_RespGet_CoilState(&MODBUS_MSG, addr, &state))
  *   {
  *     printf("Coil %d: %s\n", addr, state ? "ON" : "OFF");
  *   }
  * }
  * @endcode
  * @{
  */

int MB_RespGet_CoilState(RS_MsgTypeDef *modbus_msg, uint16_t coil_addr, int *coil_state);

/** MODBUS_REQ_COILS_API
  * @} 
  */
  

  
  
/**   
  * @addtogroup MODBUS_DATA_ACCESS_FUNCTIONS API for Data Access
  * @ingroup    MODBUS_FUNCTIONS
  * @brief      Функции для доступа к данным модбас
  * @{
  */

/** @brief Structure for coils operation */
typedef enum
{
  SET_COIL,
  RESET_COIL,
  TOOGLE_COIL,
}MB_CoilsOpTypeDef;
  
/** 
  * @brief  Считать коил по локальному адресу.
  * @param  _parr_        - массив коилов.
  * @param  _coil_        - Номер коила от начала массива _arr_.
  * @return uint16_t      Возвращает запрошенный коил на 0м бите.
  *
  * @details  Позволяет обратиться к коилу по адресу относительно _arr_.
  */
#define MB_Coil_Read_Local(_parr_, _coil_)          ((  *MB_Set_Coil_Reg_Ptr(_parr_, _coil_)  &   MB_Set_Coil_Mask(_coil_) ) >> (_coil_))
/** 
  * @brief  Выставить коил по локальному адресу.
  * @param  _parr_        Указатель на массив коилов.
  * @param  _coil_        - Номер коила от начала массива _arr_.
  * 
  * @details  Позволяет обратиться к коилу по адресу относительно _arr_.
  */
#define MB_Coil_Set_Local(_parr_, _coil_)             *MB_Set_Coil_Reg_Ptr(_parr_, _coil_)  |=  MB_Set_Coil_Mask(_coil_)
/** 
  * @brief  Сбросить коил по локальному адресу.
  * @param  _parr_        Указатель на массив коилов.
  * @param  _coil_        - Номер коила от начала массива _arr_.
  *
  * @details  Позволяет обратиться к коилу по адресу относительно _arr_.
  */
#define MB_Coil_Reset_Local(_parr_, _coil_)           *MB_Set_Coil_Reg_Ptr(_parr_, _coil_)  &= ~(MB_Set_Coil_Mask(_coil_))
/** 
  * @brief  Переключить состояние коила по локальному адресу.
  * @param  _parr_        Указатель на массив коилов.
  * @param  _coil_        - Номер коила от начала массива _arr_.
  * 
  * @details  Позволяет обратиться к коилу по адресу относительно _arr_.
  */
#define MB_Coil_Toogle_Local(_parr_, _coil_)          *MB_Set_Coil_Reg_Ptr(_parr_, _coil_)  ^=  MB_Set_Coil_Mask(_coil_)

/* Выставить/сбросить коил по глобальному адресу */
MB_ExceptionTypeDef MB_Coil_Write_Global(uint16_t Addr, MB_CoilsOpTypeDef WriteVal);
/* Считать коил по глобальному адресу */
uint16_t MB_Coil_Read_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception);

/** MODBUS_DATA_ACCESS_FUNCTIONS
  * @} 
  */
  
//---------PROCESS MODBUS COMMAND FUNCTIONS---------
/** 
  * @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS
  @{
  */
/* Обработать функцию Read Coils (01 - 0x01) */
uint8_t MB_Process_Read_Coils(RS_MsgTypeDef *modbus_msg);
/*  Обработать функцию Write Single Coils (05 - 0x05) */
uint8_t MB_Process_Write_Single_Coil(RS_MsgTypeDef *modbus_msg);
/* Обработать функцию Write Multiple Coils (15 - 0x0F) */
uint8_t MB_Process_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg);

/** MODBUS_CMD_PROCESS_FUNCTIONS
  * @} 
  */
/////////////////////////---FUNCTIONS---/////////////////////////////
  
#endif //__MODBUS_COILS_H_

/** MODBUS_COILS
  * @} 
  */