/******************************************************************************
 * file    Core/Src/RS485_sensor.c 
 * author  YuLiang
 * version 1.0.0
 * date    17-Jun-2022
 * brief   This file provides all the RS485 sensor related operation functions.
 ******************************************************************************
 * Attention
 *
 * <h2><center>&copy; COPYRIGHT(c) 2022 LandPower</center></h2>
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *   1. Redistributions of source code must retain the above copyright notice,
 *      this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright notice,
 *      this list of conditions and the following disclaimer in the documentation
 *      and/or other materials provided with the distribution.
 *   3. Neither the name of WTOE nor the names of its contributors may be used to 
 *      endorse or promote products derived from this software without specific
 *      prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include "stdio.h"
#include "string.h"

#include "usart.h"

#include "common.h"
#include "dev_config.h"
#include "RS485_sensor.h"

/* Private define ------------------------------------------------------------*/
#define SENSOR_TXRX_TIMEOUT 200     // 收发超时时间.
#define SENSOR_ROOMOUT_K_BIT (0x01)
#define SENSOR_PCB_TEMP_BIT (0x02)
#define SENSOR_GRAVITY_X_BIT (0x10)
#define SENSOR_GRAVITY_Y_BIT (0x20)
#define SENSOR_GRAVITY_Z_BIT (0x40)
#define SENSOR_HUNIDITY_BIT (0x80)

#define SENSOR_CMD_READ 0x03

/* Private typedef -----------------------------------------------------------*/
typedef struct
{
    uint8_t id;                 // 传感器 id.
    uint8_t cmd;                // 命令字.
    uint16_t addr;              // 起始地址.
    uint16_t len;               // 读取长度.
    uint16_t crc16;             // crc16 modbus.
} sensor_pkt_tx_t;

typedef struct
{
    int32_t roomout_k;          // 温度: 机外 K 热电偶. 
    int16_t pcb_t1;             // PCB温度 ADT7410.
    int16_t roomshell_t1;       // PCB机壳温度 DS18B20.
    int16_t roomout_t2;         // 外部温度 DS18B20.
    int16_t gravity_x_avg;      // 重力 X 本次通讯期间采集平均值.
    int16_t gravity_y_avg;      // 重力 Y 本次通讯期间采集平均值.
    int16_t gravity_z_avg;      // 重力 Z 本次通讯期间采集平均值
    int16_t gravity_x_max;      // 重力 X: 本次通讯期间采集最大值.
    int16_t gravity_y_max;      // 重力 Y: 本次通讯期间采集最大值.
    int16_t gravity_z_max;      // 重力 Z: 本次通讯期间采集最大值.
    int16_t gravity_x_min;      // 重力 X: 本次通讯期间采集最小值.
    int16_t gravity_y_min;      // 重力 Y: 本次通讯期间采集最小值.
    int16_t gravity_z_min;      // 重力 Z: 本次通讯期间采集最小值.
    uint16_t humidity;          // 湿度.
    uint16_t reserved0;         // 保留.
    uint16_t crc16;             // crc16 modbus.
} sensor_pkt_rx_t;

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static UART_HandleTypeDef *sensor_uart = &huart1;
static sensor_pkt_tx_t sensor_pkt_tx;
static uint8_t sensor_pkt_rx[100];
static uint8_t sensor_err_cnt;
sensor_value_t sensor_value[SENSOR_SUM];
uint8_t sensor_short;

/* Private function prototypes -----------------------------------------------*/
/* Internal functions --------------------------------------------------------*/
static void _sensor_free(uint8_t id)
{
    memset(&sensor_value[id], 0, sizeof(sensor_value_t));

    sensor_value[id].joint_attitude_x = 16000;
    sensor_value[id].joint_attitude_y = 16000;
    sensor_value[id].joint_attitude_z = 16000;
}

static int32_t _sensor_pkt_tx(uint8_t index)
{
    //uint32_t notify_value = 0;
    sensor_pkt_tx_t *pkt = &sensor_pkt_tx;

    pkt->id = dev_config.sensor_id[index];
    pkt->cmd = SENSOR_CMD_READ; // 读取数据.
    pkt->addr = 0;
    pkt->len = iap_htons(0x22);
    pkt->crc16 = crc16((uint8_t*)pkt, sizeof(sensor_pkt_tx_t) - 2);

    HAL_GPIO_WritePin(RS485_A_DE_GPIO_Port, RS485_A_DE_Pin, GPIO_PIN_SET);
    HAL_UART_Transmit_IT(sensor_uart, (uint8_t*)pkt, sizeof(sensor_pkt_tx_t));

    /* 等待收包完成. */
    //notify_value = ulTaskNotifyTake(pdTRUE, SENSOR_TXRX_TIMEOUT);
    //if (0 == notify_value)
    //{
        /* 收包超时. */
    //    return HAL_TIMEOUT;
    //}
    
    DBG(DBG_M_RS485_SEN, "Send:\r\n");
    if (dbg_stat_get(DBG_M_RS485_SEN))
    {
        buf_print((uint8_t*)pkt, sizeof(sensor_pkt_tx_t));
    }

    return HAL_OK;
}

static int32_t _sensor_pkt_rx(void)
{
    uint32_t tick = 0;
    uint32_t notify_value = 0;

    memset(sensor_pkt_rx, 0, 40);
    HAL_UART_AbortReceive(sensor_uart);
    HAL_UART_Receive_IT(sensor_uart, sensor_pkt_rx, 40);

    /* 等待收包完成. */
    tick = HAL_GetTick();
    while(1)
    {
        /* 等待采样完成. */
        notify_value = ulTaskNotifyTake(pdTRUE, SENSOR_TXRX_TIMEOUT);
        /* 采样超时, 重新开始采样. */
        if (MONITOR_SEN_NOTIFY_OK == notify_value)
        {
            break;
        }

        if (HAL_GetTick() - tick > SENSOR_TXRX_TIMEOUT)
        {
            vty_print("Sensor timeout!\r\n");
            return HAL_TIMEOUT;
        }
    }

    DBG(DBG_M_RS485_SEN, "Recv:\r\n");
    if (dbg_stat_get(DBG_M_RS485_SEN))
    {
        buf_print(sensor_pkt_rx, 40);
    }
    return HAL_OK;
}

static int32_t _sensor_pkt_process(uint8_t index)
{
    uint16_t status = *(uint16_t*)(sensor_pkt_rx +4);   // 传感器的状态: 测量更新标记: 有效置 1, 否则置 0.
    int32_t temp = 0;
    sensor_pkt_rx_t *pkt = (sensor_pkt_rx_t*)(sensor_pkt_rx + 6);

    /* 温度振动. */
    if (status & SENSOR_PCB_TEMP_BIT)
    {
        temp = pkt->pcb_t1;
        sensor_value[index].temp = temp * 10 / 128;
    }

    if (status & SENSOR_GRAVITY_X_BIT)
    {
        sensor_value[index].joint_attitude_x = (uint16_t)(16000 + pkt->gravity_x_avg * 3.90625);
        sensor_value[index].gravity_x_max = (uint16_t)(16000 + pkt->gravity_x_max * 3.90625);
        sensor_value[index].gravity_x_min = (uint16_t)(16000 + pkt->gravity_x_min * 3.90625);
        
    }
    
    if (status & SENSOR_GRAVITY_Y_BIT)
    {
        sensor_value[index].joint_attitude_y = (uint16_t)(16000 + pkt->gravity_y_avg * 3.90625);
        sensor_value[index].gravity_y_max = (uint16_t)(16000 + pkt->gravity_y_max * 3.90625);
        sensor_value[index].gravity_y_min = (uint16_t)(16000 + pkt->gravity_y_min * 3.90625);
        
    }
    
    if (status & SENSOR_GRAVITY_Z_BIT)
    {
        sensor_value[index].joint_attitude_z = (uint16_t)(16000 + pkt->gravity_z_avg * 3.90625);
        sensor_value[index].gravity_z_max = (uint16_t)(16000 + pkt->gravity_z_max * 3.90625);
        sensor_value[index].gravity_z_min = (uint16_t)(16000 + pkt->gravity_z_min * 3.90625);
    }
    
    sensor_value[index].is_valid = TRUE;
    return HAL_OK;
}

/* Interface functions -------------------------------------------------------*/
/* RS485 传感器主任务循环. */
void sensor_start(void)
{
    uint8_t i = 0;

    /* 开启传感器 RS485 电源. */
    HAL_GPIO_WritePin(SENSOR_EN_GPIO_Port, SENSOR_EN_Pin, GPIO_PIN_SET);
    osDelay(700);

    sensor_short = FALSE;
    for (i = 0; i < SENSOR_SUM; i++)
    {
        if (0 == dev_config.sensor_id[i])
        {
            _sensor_free(i);
            continue;
        }

        sensor_err_cnt = 0;
        while(1)
        {
            if (sensor_err_cnt >= 3)
            {
                _sensor_free(i);
                break;
            }

            if (_sensor_pkt_tx(i) != HAL_OK)
            {
                sensor_err_cnt++;
                continue;
            }

            if (_sensor_pkt_rx() != HAL_OK)
            {
                sensor_err_cnt++;
                continue;
            }

            _sensor_pkt_process(i);
            break;
        }
    }

    HAL_GPIO_WritePin(SENSOR_EN_GPIO_Port, SENSOR_EN_Pin, GPIO_PIN_RESET);
}

/* RS485 传感器主任务循环. */
void sensor_start_free(void)
{
    uint8_t i = 0;

    memset(sensor_value, 0, sizeof(sensor_value_t) * SENSOR_SUM);

    for (i = 0; i < SENSOR_SUM; i++)
    {
        sensor_value[i].joint_attitude_x = 16000;
        sensor_value[i].joint_attitude_y = 16000;
        sensor_value[i].joint_attitude_z = 16000;
    }
}

/* 显示命令 */
void sensor_show(void)
{
    uint8_t i = 0;

    vty_print("Short: %d\r\n", sensor_short);
    vty_print("CH ID  VA TE   XG    XA    XI    YG    YA    YI    ZG    ZA    ZI\r\n");
    for(i = 0; i < SENSOR_SUM; i++)
    {
        vty_print("%-2d %-3d %-2d %-4d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d\r\n", i + 1,
            dev_config.sensor_id[i], sensor_value[i].is_valid, sensor_value[i].temp,
            sensor_value[i].joint_attitude_x, sensor_value[i].gravity_x_max, sensor_value[i].gravity_x_min,
            sensor_value[i].joint_attitude_y, sensor_value[i].gravity_y_max, sensor_value[i].gravity_y_min,
            sensor_value[i].joint_attitude_z, sensor_value[i].gravity_z_max, sensor_value[i].gravity_z_min);
    }
    vty_print("\r\n");
}
/******************* (C) COPYRIGHT LandPower ***** END OF FILE ****************/
