#include "dlt645.h"
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <sys/param.h>
#include "common.h"
#include "../custom_fifo.h"

static char *type_string[] =
    {
        "unsigned",
        "signed"};

dlt645_ext_t dlt645_get_ext_type_by_string(char *typeString)
{
    for (dlt645_ext_t i = DLT645_EXT_UNSIGNED; i < DLT645_EXT_MAX; i++)
    {
        if (strcmp(typeString, type_string[i]) == 0)
            return i;
    }
    proto_syslog(LOG_WARNING, "dlt645 get extract type error, %s!!!", typeString);
    return DLT645_EXT_MAX;
}

static void _sleep_response_timeout(dlt645_hd_t *hd)
{
    struct timespec request, remaining;
    request.tv_sec = hd->read_time_out / 1000;
    request.tv_nsec = (hd->read_time_out % 1000) * 1000000;
    while (nanosleep(&request, &remaining) == -1 && errno == EINTR)
    {
        request = remaining;
    }
}

typedef struct dlt645_data_tcp
{
    char ip[20];
    uint16_t port;
    uint32_t connect_timeout;
} dlt645_data_tcp;


int dlt645_connect_tcp(dlt645_hd_t *hd)
{
    dlt645_data_tcp *ctx_rtu_tcp = (dlt645_data_tcp *)hd->data;
    int re = connect_tcp(ctx_rtu_tcp->ip, ctx_rtu_tcp->port, ctx_rtu_tcp->connect_timeout);
    if (re > -1)
    {
        hd->fd = re;
        return 0;
    }
    hd->fd = -1;
    return -1;
}

int dlt645_dis_connect_tcp(dlt645_hd_t *hd)
{
    int re = close(hd->fd);
    if (re > -1)
    {
        hd->fd = -1;
    }
    return re;
}

int dlt645_free_tcp(dlt645_hd_t *hd)
{
    if (hd == NULL)
    {
        return -1;
    }

    if (hd->fd > -1)
    {
        dlt645_dis_connect_tcp(hd);
    }

    if (hd->data != NULL)
    {
        free(hd->data);
    }

    free(hd);
    return 0;
}

int dlt645_tx_tcp(dlt645_hd_t *hd, void *buff, uint32_t len)
{
    frame_wgap_delay(hd->lastread, hd->frame_gap);
    return send(hd->fd, (const char *)buff, len, MSG_NOSIGNAL);
}

int dlt645_rx_tcp(dlt645_hd_t *hd, void *buff, uint32_t to_read)
{
    uint8_t *dest_buff = (uint8_t *)buff;
    int s_rc;
    int rx_len = 0;
    struct timeval tv;
    tv.tv_sec = hd->read_time_out / 1000;
    tv.tv_usec = (hd->read_time_out % 1000) * 1000;
    fd_set rset;
    FD_ZERO(&rset);
    FD_SET(hd->fd, &rset);

    do
    {
        ssize_t rval;
        while ((s_rc = select(hd->fd + 1, &rset, NULL, NULL, &tv)) == -1)
        {
            if (errno == EINTR)
            {
                proto_syslog(LOG_ERR, "A non blocked signal was caught\n");
                FD_ZERO(&rset);
                FD_SET(hd->fd, &rset);
            }
            else
            {
                return -1;
            }
        }

        if (s_rc == 0)
        {
            errno = ETIMEDOUT;
            break;
        }

        rval = recv(hd->fd, dest_buff + rx_len, to_read - rx_len, 0);
        if (rval > 0) {
            rx_len += (int) rval;
            hd->lastread = sysuptime();
        }
        tv.tv_sec = (hd->interval > 1000) ? hd->interval / 1000 : 0;
        tv.tv_usec = (hd->interval % 1000) * 1000;
    } while (to_read > rx_len);

    return rx_len;
}

int dlt645_flush_tcp(dlt645_hd_t *hd)
{
    int rc;
    char devnull[128];
    fd_set rset;
    struct timeval tv;
    do
    {
        tv.tv_sec = 0;
        tv.tv_usec = 0;
        FD_ZERO(&rset);
        FD_SET(hd->fd, &rset);
        rc = select(hd->fd + 1, &rset, NULL, NULL, &tv);
        if (rc == -1)
        {
            return -1;
        }

        if (rc == 1)
        {
            rc = recv(hd->fd, devnull, sizeof(devnull), 0);
        }
    } while (rc == sizeof(devnull));
    return 0;
}

dlt645_if_t dlt645_tcp_if =
    {
        .dlt645_connect = dlt645_connect_tcp,
        .dlt645_tx = dlt645_tx_tcp,
        .dlt645_rx = dlt645_rx_tcp,
        .dlt645_flush = dlt645_flush_tcp,
        .dlt645_dis_connect = dlt645_dis_connect_tcp,
        .dlt645_free = dlt645_free_tcp,
    };

dlt645_hd_t *new_dlt645_tcp(char *ip_or_dev, uint16_t port, dlt645_protocal_t dlt645_ver)
{
    dlt645_hd_t *hd = calloc(sizeof(dlt645_hd_t), 1);
    if (hd == NULL)
    {
        proto_syslog(LOG_ERR, "calloc error for hd\n");
        return NULL;
    }

    dlt645_data_tcp *tcp_data = calloc(sizeof(dlt645_data_tcp), 1);
    if (tcp_data == NULL)
    {
        free(hd);
        proto_syslog(LOG_ERR, "calloc error for data\n");
        return NULL;
    }
    tcp_data->port = port;
    snprintf(tcp_data->ip, sizeof(tcp_data->ip), "%s", ip_or_dev);
    tcp_data->connect_timeout = 5;
    hd->data = tcp_data;
    hd->if_funs = &dlt645_tcp_if;
    hd->proto_ver = dlt645_ver;
    snprintf(hd->name, sizeof(hd->name), "%s:%d", tcp_data->ip, port);

    hd->frame_gap = -1;
    hd->lastread = sysuptime() - 10000;
    return hd;
}
//////
#if 1
typedef struct dlt645_data_udp
{
    char ip[20];
    uint16_t port;
    uint32_t connect_timeout;
    struct sockaddr_in server_addr;
    uint8_t rx_buff[255];
    custom_fifo fifo;
} dlt645_data_udp;

int dlt645_connect_udp(dlt645_hd_t *hd)
{
    int rc = 0;
    dlt645_data_udp *ctx_rtu_udp = (dlt645_data_udp *)hd->data;
    struct sockaddr_in *addr = &ctx_rtu_udp->server_addr;
    int flags = SOCK_DGRAM;

#ifdef OS_WIN32
    if (_modbus_tcp_init_win32() == -1)
    {
        return -1;
    }
#endif

#ifdef SOCK_CLOEXEC
    flags |= SOCK_CLOEXEC;
#endif

#ifdef SOCK_NONBLOCK
    flags |= SOCK_NONBLOCK;
#endif

    hd->fd = socket(PF_INET, flags, 0);
    if (hd->fd < 0)
    {
        return -1;
    }
    proto_syslog(LOG_NOTICE, "Connecting to %s:%d", ctx_rtu_udp->ip, ctx_rtu_udp->port);

    memset(addr, 0, sizeof(*addr));
    addr->sin_family = PF_INET;
    addr->sin_port = htons(ctx_rtu_udp->port);
    rc = inet_pton(addr->sin_family, ctx_rtu_udp->ip, &(addr->sin_addr));
    if (rc <= 0)
    {
        proto_syslog(LOG_ERR, "Invalid IP address: %s\n", ctx_rtu_udp->ip);
        close(hd->fd);
        hd->fd = -1;
        return -2;
    }

    return 0;
}

int dlt645_dis_connect_udp(dlt645_hd_t *hd)
{
    int re = close(hd->fd);
    if (re > -1)
    {
        hd->fd = -1;
    }
    return re;
}

int dlt645_free_udp(dlt645_hd_t *hd)
{
    if (hd == NULL)
    {
        return -1;
    }

    if (hd->fd > -1)
    {
        dlt645_dis_connect_udp(hd);
    }

    if (hd->data != NULL)
    {
        free(hd->data);
    }

    free(hd);
    return 0;
}

int dlt645_tx_udp(dlt645_hd_t *hd, void *buff, uint32_t len)
{
    struct sockaddr_in *addr = &((dlt645_data_udp *)hd->data)->server_addr;
    frame_wgap_delay(hd->lastread, hd->frame_gap);
    return sendto(hd->fd, buff, len, 0, (struct sockaddr *)addr, sizeof(*addr));
}

int dlt645_rx_udp(dlt645_hd_t *hd, void *buff, uint32_t to_read)
{
    dlt645_data_udp *udp_data = (dlt645_data_udp *)hd->data;
    int s_rc;
    int rx_len = 0;
    struct timeval tv;
    fd_set rset;
    if (fifi_get_counter(&udp_data->fifo) <= 0)
    {
        FD_ZERO(&rset);
        FD_SET(hd->fd, &rset);
        tv.tv_sec = hd->read_time_out / 1000;
        tv.tv_usec = (hd->read_time_out % 1000) * 1000;
        while ((s_rc = select(hd->fd + 1, &rset, NULL, NULL, &tv)) == -1)
        {
            if (errno == EINTR)
            {
                proto_syslog(LOG_ERR, "A non blocked signal was caught\n");
                FD_ZERO(&rset);
                FD_SET(hd->fd, &rset);
            }
            else
            {
                return -1;
            }
        }

        if (s_rc == 0)
        {
            errno = ETIMEDOUT;
            return -2;
        }

        unsigned char tmp_buff[256] = {0};
        rx_len = recvfrom(hd->fd, tmp_buff, sizeof(tmp_buff), 0, NULL, NULL);
        proto_syslog_hex(LOG_DEBUG, tmp_buff, rx_len, "%s:%d rx=%d", udp_data->ip, udp_data->port, rx_len);
        if (rx_len > 0)
        {
            hd->lastread = sysuptime();
            fifo_push_buff(&udp_data->fifo, tmp_buff, rx_len);
        }
    }
    rx_len = fifo_pop_buff(&udp_data->fifo, buff, to_read);

    return rx_len;
}

int dlt645_flush_udp(dlt645_hd_t *hd)
{
    int rc;
    dlt645_data_udp *udp_data = (dlt645_data_udp *)hd->data;
    char devnull[128];
    fd_set rset;
    struct timeval tv;
    do
    {
        tv.tv_sec = 0;
        tv.tv_usec = 0;
        FD_ZERO(&rset);
        FD_SET(hd->fd, &rset);
        rc = select(hd->fd + 1, &rset, NULL, NULL, &tv);
        if (rc == -1)
        {
            return -1;
        }

        if (rc == 1)
        {
            rc = recv(hd->fd, devnull, sizeof(devnull), 0);
        }
    } while (rc == sizeof(devnull));
    fifo_clear(&udp_data->fifo);
    return 0;
}

dlt645_if_t dlt645_udp_if =
    {
        .dlt645_connect = dlt645_connect_udp,
        .dlt645_tx = dlt645_tx_udp,
        .dlt645_rx = dlt645_rx_udp,
        .dlt645_flush = dlt645_flush_udp,
        .dlt645_dis_connect = dlt645_dis_connect_udp,
        .dlt645_free = dlt645_free_udp,
    };

dlt645_hd_t *new_dlt645_udp(char *ip_or_dev, uint16_t port, dlt645_protocal_t dlt645_ver)
{
    dlt645_hd_t *hd = calloc(sizeof(dlt645_hd_t), 1);
    if (hd == NULL)
    {
        proto_syslog(LOG_ERR, "calloc error for hd\n");
        return NULL;
    }

    dlt645_data_udp *udp_data = calloc(sizeof(dlt645_data_udp), 1);
    if (udp_data == NULL)
    {
        free(hd);
        proto_syslog(LOG_ERR, "calloc error for data\n");
        return NULL;
    }
    udp_data->port = port;
    snprintf(udp_data->ip, sizeof(udp_data->ip), "%s", ip_or_dev);
    udp_data->connect_timeout = 5;
    hd->data = udp_data;
    hd->if_funs = &dlt645_udp_if;
    hd->proto_ver = dlt645_ver;
    snprintf(hd->name, sizeof(hd->name), "%s:%d", udp_data->ip, port);
    fifo_init(&udp_data->fifo, udp_data->rx_buff, sizeof(udp_data->rx_buff));
    fifo_clear(&udp_data->fifo);

    hd->frame_gap = -1;
    hd->lastread = sysuptime() - 10000;
    return hd;
}
#endif

//////
int dlt645_connect_uart(dlt645_hd_t *hd)
{
    uart_attr *ctx_uart = (uart_attr *)hd->data;
    hd->fd = open_uart(ctx_uart);
    if (hd->fd > -1)
    {
        return 0;
    }
    return -1;
}

int dlt645_tx_uart(dlt645_hd_t *hd, void *buff, uint32_t len)
{
    frame_wgap_delay(hd->lastread, hd->frame_gap);
    return write(hd->fd, buff, len);
}

int dlt645_rx_uart(dlt645_hd_t *hd, void *buff, uint32_t to_read)
{
    uint8_t *dest_buff = (uint8_t *)buff;
    int s_rc;
    int rx_len = 0;
    struct timeval tv;
    tv.tv_sec = hd->read_time_out / 1000;
    tv.tv_usec = (hd->read_time_out % 1000) * 1000;
    fd_set rset;
    FD_ZERO(&rset);
    FD_SET(hd->fd, &rset);

    do
    {
        ssize_t rval;
        while ((s_rc = select(hd->fd + 1, &rset, NULL, NULL, &tv)) == -1)
        {
            if (errno == EINTR)
            {
                proto_syslog(LOG_ERR, "A non blocked signal was caught\n");
                FD_ZERO(&rset);
                FD_SET(hd->fd, &rset);
            }
            else
            {
                return -1;
            }
        }

        if (s_rc == 0)
        {
            errno = ETIMEDOUT;
            break;
        }

        rval = read(hd->fd, dest_buff + rx_len, to_read - rx_len);
        if (rval > 0) {
            hd->lastread = sysuptime();
            rx_len += (int) rval;
        }
        else
        {
            break;
        }
        tv.tv_sec = (hd->interval > 1000) ? hd->interval / 1000 : 0;
        tv.tv_usec = (hd->interval % 1000) * 1000;
    } while (to_read > rx_len);

    return rx_len;
}

int dlt645_dis_connect_uart(dlt645_hd_t *hd)
{
    uart_attr *ctx_uart = hd->data;
    tcsetattr(hd->fd, TCSANOW, &ctx_uart->old_tios);
    int re = close(hd->fd);
    if (re == 0)
    {
        hd->fd = -1;
    }
    return re;
}

int dlt645_free_uart(dlt645_hd_t *hd)
{
    if (hd == NULL)
    {
        return -1;
    }

    if (hd->fd > -1)
    {
        dlt645_dis_connect_uart(hd);
    }

    if (hd->data != NULL)
    {
        free(hd->data);
    }

    free(hd);
    return 0;
}

int dlt645_flush_uart(dlt645_hd_t *hd)
{
    return tcflush(hd->fd, TCIOFLUSH);
}

dlt645_if_t dlt645_uart_if =
    {
        .dlt645_connect = dlt645_connect_uart,
        .dlt645_tx = dlt645_tx_uart,
        .dlt645_rx = dlt645_rx_uart,
        .dlt645_flush = dlt645_flush_uart,
        .dlt645_dis_connect = dlt645_dis_connect_uart,
        .dlt645_free = dlt645_free_uart,
    };

dlt645_hd_t *new_dlt645_uart(char *dev, int baud, uint8_t data_bit, uint8_t stop_bit, char parity, dlt645_protocal_t dlt645_ver)
{
    dlt645_hd_t *hd = calloc(sizeof(dlt645_hd_t), 1);
    if (hd == NULL)
    {
        proto_syslog(LOG_ERR, "calloc error for hd\n");
        return NULL;
    }

    uart_attr *uart_data = calloc(sizeof(uart_attr), 1);
    if (uart_data == NULL)
    {
        free(hd);
        proto_syslog(LOG_ERR, "calloc error for data\n");
        return NULL;
    }
    uart_data->baud = baud;
    uart_data->data_bit = data_bit;
    uart_data->stop_bit = stop_bit;
    uart_data->parity = parity;
    snprintf(uart_data->dev_f, sizeof(uart_data->dev_f), "%s", dev);

    hd->data = uart_data;
    hd->if_funs = &dlt645_uart_if;
    hd->proto_ver = dlt645_ver;
    snprintf(hd->name, sizeof(hd->name), "%s", dev);

    hd->frame_gap = -1;
    hd->lastread = sysuptime() - 10000;
    return hd;
}

int dlt645_set_name(dlt645_hd_t *hd, char *new_name)
{
    return snprintf(hd->name, sizeof(hd->name), "%s", new_name);
}

/////////////////////////////////////////////

static int sum_check(uint8_t *msg, int len)
{
    uint8_t crc = 0;
    while (len--)
    {
        crc += *msg++;
    }
    return crc;
}

int dlt645_common_check(uint8_t *msg, int len, uint8_t *addr)
{
    // 数据帧标志校验
    dlt645_com_head *head = (dlt645_com_head *)msg;
    if ((head->start_code1 != DL645_START_CODE) || (head->start_code2 != DL645_START_CODE) || head->data[head->data_len + 1] != DL645_STOP_CODE)
    {
        proto_syslog(LOG_DEBUG, "dlt645 check code error!");
        return -1;
    }
    // 长度
    if (len != (head->data_len + sizeof(dlt645_com_head) + 2))
    {
        proto_syslog(LOG_DEBUG, "dlt645 check len error!");
        return -2;
    }
    // 校验
    uint8_t sum = sum_check(msg, len - 2);
    if (sum != msg[len - 2])
    {
        proto_syslog(LOG_DEBUG, "check sum error!\n");
        return -3;
    }

    if ((head->ctrl_code & 0x60) != 0)
    {
        proto_syslog(LOG_DEBUG, "Check ctrl_code error! ctrl code = 0x%x", head->ctrl_code);
        return -4;
    }
    // 从站地址校验
    if (memcmp(head->addr, addr, 6) != 0)
    {
        proto_syslog(LOG_DEBUG, "Check code error!");
        return -6;
    }

    return 0;
}

static int dlt645_extract_data(dlt645_item *ask_it, void *ack_buff, void *dest_buff, uint32_t max_len)
{
    dlt645_2007_head *head = (dlt645_2007_head *)ack_buff;
    dlt645_1997_head *head_1997 = (dlt645_1997_head *)ack_buff;
    int data_sum = (head->com.data_len - sizeof(head->di)) / ask_it->data_len;
    int act_data_len = head->com.data_len - sizeof(head->di);
    uint8_t *p_data = head->data;
    if (head_1997->com.ctrl_code == 0x81) // 这儿不严谨，后期需优化
    {
        data_sum = (head_1997->com.data_len - sizeof(head_1997->di)) / ask_it->data_len;
        p_data = head_1997->data;
        act_data_len = head_1997->com.data_len - sizeof(head_1997->di);
    }
    int32_t tmp_int = 0;
    uint8_t tmp = 0;
    int32_t *p_int_dest = (int32_t *)dest_buff;
    double *p_float_dest = (double *)dest_buff;
    if (ask_it->pos < 0) // 不支持的类型，以后打算可以用pos负数去支持:字符串和bcd码返回等
    {
        return -1; //
    }

    if (max_len < (sizeof(double) * data_sum)) // 内存不够
    {
        return -2;
    }

    if ((ask_it->data_len > 5) || (ask_it->pos > 5)) // 超出支持范围
    {
        return -3;
    }
    static const double div[6] = {1, 10, 100, 1000, 10000, 100000};

    proto_syslog(LOG_DEBUG, "data_id = %08x, pos = %d, len = %d, data_len = %d", ask_it->data_id, ask_it->pos, ask_it->data_len, act_data_len);

    switch (ask_it->data_len)
    {
    case 1:
        if (ask_it->type == DLT645_EXT_SIGNED) // 带符号项，且为负数
        {
            for (int i = 0; i < data_sum; i++)
            {
                tmp = (*p_data - 0x33) & 0x7f;
                if (*p_data++ & 0x80) // 负数
                {
                    tmp_int = 0 - tmp_int;
                }

                tmp_int = (tmp >> 4) * 10 + (tmp & 0xf);
                if (ask_it->pos > 0)
                {
                    *p_float_dest++ = tmp_int / div[ask_it->pos];
                }
                else
                {
                    *p_int_dest++ = tmp_int;
                }
            }
        }
        else
        {
            for (int i = 0; i < data_sum; i++)
            {
                tmp = *p_data++ - 0x33;
                tmp_int = (tmp >> 4) * 10 + (tmp & 0xf);
                if (ask_it->pos > 0)
                {
                    *p_float_dest++ = tmp_int / div[ask_it->pos];
                }
                else
                {
                    *p_int_dest++ = tmp_int;
                }
            }
        }
        break;

    case 2:
        if (ask_it->type == DLT645_EXT_SIGNED) // 带符号项，且为负数
        {
            for (int i = 0; i < data_sum; i++)
            {
                tmp = *p_data++ - 0x33;
                tmp_int = (tmp >> 4) * 10 + (tmp & 0xf);
                tmp = (*p_data - 0x33) & 0x7f;
                if (*p_data++ & 0x80) // 负数
                {
                    tmp_int = 0 - tmp_int;
                }
                tmp_int += (tmp >> 4) * 1000 + (tmp & 0xf) * 100;
                if (ask_it->pos > 0)
                {
                    *p_float_dest++ = tmp_int / div[ask_it->pos];
                }
                else
                {
                    *p_int_dest++ = tmp_int;
                }
            }
        }
        else
        {
            for (int i = 0; i < data_sum; i++)
            {
                tmp = *p_data++ - 0x33;
                tmp_int = (tmp >> 4) * 10 + (tmp & 0xf);
                tmp = *p_data++ - 0x33;
                tmp_int += (tmp >> 4) * 1000 + (tmp & 0xf) * 100;
                if (ask_it->pos > 0)
                {
                    *p_float_dest++ = tmp_int / div[ask_it->pos];
                }
                else
                {
                    *p_int_dest++ = tmp_int;
                }
            }
        }
        break;

    case 3:
        if (ask_it->type == DLT645_EXT_SIGNED) // 带符号项
        {
            for (int i = 0; i < data_sum; i++)
            {
                tmp = *p_data++ - 0x33;
                tmp_int = (tmp >> 4) * 10 + (tmp & 0xf);
                tmp = *p_data++ - 0x33;
                tmp_int += (tmp >> 4) * 1000 + (tmp & 0xf) * 100;
                tmp = (*p_data - 0x33) & 0x7f;
                tmp_int += (tmp >> 4) * 100000 + (tmp & 0xf) * 10000;
                if (*p_data++ & 0x80) // 负数
                {
                    tmp_int = 0 - tmp_int;
                }
                if (ask_it->pos > 0)
                {
                    *p_float_dest++ = tmp_int / div[ask_it->pos];
                }
                else
                {
                    *p_int_dest++ = tmp_int;
                }
            }
        }
        else
        {
            for (int i = 0; i < data_sum; i++)
            {
                tmp = *p_data++ - 0x33;
                tmp_int = (tmp >> 4) * 10 + (tmp & 0xf);
                tmp = *p_data++ - 0x33;
                tmp_int += (tmp >> 4) * 1000 + (tmp & 0xf) * 100;
                tmp = *p_data++ - 0x33;
                tmp_int += (tmp >> 4) * 100000 + (tmp & 0xf) * 10000;
                if (ask_it->pos > 0)
                {
                    *p_float_dest++ = tmp_int / div[ask_it->pos];
                }
                else
                {
                    *p_int_dest++ = tmp_int;
                }
            }
        }
        break;

    case 4:
        if (ask_it->type == DLT645_EXT_SIGNED) // 带符号项
        {
            for (int i = 0; i < data_sum; i++)
            {
                tmp = *p_data++ - 0x33;
                tmp_int = (tmp >> 4) * 10 + (tmp & 0xf);
                tmp = *p_data++ - 0x33;
                tmp_int += (tmp >> 4) * 1000 + (tmp & 0xf) * 100;
                tmp = *p_data++ - 0x33;
                tmp_int += (tmp >> 4) * 100000 + (tmp & 0xf) * 10000;
                tmp = (*p_data - 0x33) & 0x7f;
                if (*p_data++ & 0x80) // 负数
                {
                    tmp_int = 0 - tmp_int;
                }
                tmp_int += (tmp >> 4) * 10000000 + (tmp & 0xf) * 1000000;
                if (ask_it->pos > 0)
                {
                    *p_float_dest++ = tmp_int / div[ask_it->pos];
                }
                else
                {
                    *p_int_dest++ = tmp_int;
                }
            }
        }
        else
        {
            for (int i = 0; i < data_sum; i++)
            {
                tmp = *p_data++ - 0x33;
                tmp_int = (tmp >> 4) * 10 + (tmp & 0xf);
                tmp = *p_data++ - 0x33;
                tmp_int += (tmp >> 4) * 1000 + (tmp & 0xf) * 100;
                tmp = *p_data++ - 0x33;
                tmp_int += (tmp >> 4) * 100000 + (tmp & 0xf) * 10000;
                tmp = *p_data++ - 0x33;
                tmp_int += (tmp >> 4) * 10000000 + (tmp & 0xf) * 1000000;
                if (ask_it->pos > 0)
                {
                    *p_float_dest++ = tmp_int / div[ask_it->pos];
                }
                else
                {
                    *p_int_dest++ = tmp_int;
                }
            }
        }
        break;

    case 5:
        if (ask_it->type == DLT645_EXT_SIGNED) // 带符号项
        {
            for (int i = 0; i < data_sum; i++)
            {
                tmp = *p_data++ - 0x33;
                tmp_int = (tmp >> 4) * 10 + (tmp & 0xf);
                tmp = *p_data++ - 0x33;
                tmp_int += (tmp >> 4) * 1000 + (tmp & 0xf) * 100;
                tmp = *p_data++ - 0x33;
                tmp_int += (tmp >> 4) * 100000 + (tmp & 0xf) * 10000;
                tmp = *p_data++ - 0x33;
                tmp_int += (tmp >> 4) * 10000000 + (tmp & 0xf) * 1000000;
                tmp = (*p_data - 0x33) & 0x7f;
                if (*p_data++ & 0x80) // 负数
                {
                    tmp_int = 0 - tmp_int;
                }
                tmp_int += (tmp >> 4) * 1000000000 + (tmp & 0xf) * 100000000;
                if (ask_it->pos > 0)
                {
                    *p_float_dest++ = tmp_int / div[ask_it->pos];
                }
                else
                {
                    *p_int_dest++ = tmp_int;
                }
            }
        }
        else
        {
            for (int i = 0; i < data_sum; i++)
            {
                tmp = *p_data++ - 0x33;
                tmp_int = (tmp >> 4) * 10 + (tmp & 0xf);
                tmp = *p_data++ - 0x33;
                tmp_int += (tmp >> 4) * 1000 + (tmp & 0xf) * 100;
                tmp = *p_data++ - 0x33;
                tmp_int += (tmp >> 4) * 100000 + (tmp & 0xf) * 10000;
                tmp = *p_data++ - 0x33;
                tmp_int += (tmp >> 4) * 10000000 + (tmp & 0xf) * 1000000;
                tmp = *p_data++ - 0x33;
                tmp_int += (tmp >> 4) * 1000000000 + (tmp & 0xf) * 100000000;
                if (ask_it->pos > 0)
                {
                    *p_float_dest++ = tmp_int / div[ask_it->pos];
                }
                else
                {
                    *p_int_dest++ = tmp_int;
                }
            }
        }
        break;

    default:
        return -4;
        break;
    }

    return data_sum;
}

static int dlt645_get_ack(dlt645_hd_t *hd, void *buff, uint32_t max_len, int *valid_start)
{
    uint8_t *tmp_buff = (uint8_t *)buff;
    int rx_len = hd->if_funs->dlt645_rx(hd, tmp_buff, sizeof(dlt645_com_head));
    int re = 0;
    if (rx_len < sizeof(dlt645_com_head))
    {
        goto dlt645_get_ack_exit;
    }
    // 检查前导数据
    int pre_sum = 0;
    if (*tmp_buff == 0xfe)
    {
        while (pre_sum < 5)
        {
            if (*(tmp_buff + pre_sum) == 0xfe)
            {
                pre_sum++;
                if (*(tmp_buff + pre_sum) == 0x68)
                {
                    break;
                }
            }
            else
            {
                goto dlt645_get_ack_exit;
            }
        }
    }
    if (pre_sum > 0) // 有前导码.再读取头剩下的部分
    {
        re = hd->if_funs->dlt645_rx(hd, tmp_buff + rx_len, pre_sum);
        if (re > 0)
        {
            rx_len += re;
        }
        else
        {
            goto dlt645_get_ack_exit;
        }
    }

    dlt645_com_head *head = (dlt645_com_head *)(tmp_buff + pre_sum);
    re = head->data_len + 2;
    if ((re + rx_len) > max_len) // 如果剩余的数据装不下
    {
        goto dlt645_get_ack_exit;
    }
    re = hd->if_funs->dlt645_rx(hd, head->data, re); // 读余下的数据
    if (re > 0)
    {
        *valid_start = pre_sum;
        rx_len += re;
    }

dlt645_get_ack_exit:
    if (rx_len < 0)
    {
        rx_len = 0;
    }
    return rx_len;
}

int dlt645_recv_check(uint8_t *msg, int len, uint8_t *addr, uint32_t code)
{

    if (dlt645_common_check(msg, len, addr) < 0)
    {
        return -1;
    }

    dlt645_com_head *head = (dlt645_com_head *)msg;
    switch (head->ctrl_code)
    {
    case 0x81: // 97协议
    {
        uint8_t *code_buf = (uint8_t *)&code;
        for (uint8_t i = 0; i < 2; i++)
        {
            code_buf[i] += 0x33;
        }

        if (*((uint16_t *)(msg + DL645_DATA_POS)) != code)
        {
            proto_syslog(LOG_DEBUG, "dlt645 data id error!\n");
            return -2;
        }
    }
    break;

    case 0x91: // 07协议
    {
        uint8_t *code_buf = (uint8_t *)&code;
        for (uint8_t i = 0; i < 4; i++)
        {
            code_buf[i] += 0x33;
        }

        if (*((uint32_t *)(msg + DL645_DATA_POS)) != code)
        {
            proto_syslog(LOG_DEBUG, "dlt645 data id error!\n");
            return -2;
        }
    }
    break;
    }

    return 0;
}

/************************************* 2007 ************************************/

int dlt645_2007_pack_read_cmd(uint8_t *bcd_addr, uint32_t data_id, void *buff, int max_len)
{
    if (max_len < sizeof(dlt645_2007_ask))
    {
        return -1;
    }

    dlt645_2007_ask *cmd = (dlt645_2007_ask *)buff;
    cmd->head.com.start_code1 = DL645_START_CODE;
    memcpy(cmd->head.com.addr, bcd_addr, DL645_ADDR_LEN);
    cmd->head.com.start_code2 = DL645_START_CODE;
    cmd->head.com.ctrl_code = C_2007_CODE_RD;
    cmd->head.com.data_len = 4;
    cmd->head.di[0] = data_id + 0x33;
    cmd->head.di[1] = (data_id >> 8) + 0x33;
    cmd->head.di[2] = (data_id >> 16) + 0x33;
    cmd->head.di[3] = (data_id >> 24) + 0x33;
    cmd->sum = sum_check((uint8_t *)cmd, sizeof(dlt645_2007_ask) - 2);
    cmd->end_code = DL645_STOP_CODE;
    return sizeof(dlt645_2007_ask);
}

/************************************* 1997 ************************************/
int dlt645_1997_pack_read_cmd(uint8_t *bcd_addr, uint32_t data_id, void *buff, int max_len)
{
    if (max_len < sizeof(dlt645_1997_ask))
    {
        return -1;
    }

    dlt645_1997_ask *cmd = (dlt645_1997_ask *)buff;
    cmd->head.com.start_code1 = DL645_START_CODE;
    memcpy(cmd->head.com.addr, bcd_addr, DL645_ADDR_LEN);
    cmd->head.com.start_code2 = DL645_START_CODE;
    cmd->head.com.ctrl_code = C_1997_CODE_RD;
    cmd->head.com.data_len = 2;
    cmd->head.di[0] = data_id + 0x33;
    cmd->head.di[1] = (data_id >> 8) + 0x33;
    cmd->sum = sum_check((uint8_t *)cmd, sizeof(dlt645_1997_ask) - 2);
    cmd->end_code = DL645_STOP_CODE;
    return sizeof(dlt645_1997_ask);
}

/*************************************************************************/
int dlt645_connect(dlt645_hd_t *hd)
{
    return hd->if_funs->dlt645_connect(hd);
}

int dlt645_dis_connect(dlt645_hd_t *hd)
{
    return hd->if_funs->dlt645_dis_connect(hd);
}
int dlt645_free(dlt645_hd_t *hd)
{
    return hd->if_funs->dlt645_free(hd);
}

int dlt645_analysis(dlt645_item *it, void *ack_buf, int len, uint8_t *bcd_addr, void *dist_buff, unsigned int max_len)
{
    int re = -1;
    if (0 == dlt645_recv_check(ack_buf, len, bcd_addr, it->data_id))
    {
        re = dlt645_extract_data(it, ack_buf, dist_buff, max_len);
        if (re <= 0)
        {
            proto_syslog(LOG_ERR, "dlt645 extract data");
            re = -7;
        }
    }
    return re;
}

void dlt645_set_raw_send_callback(dlt645_hd_t *hd, void (*cb)(dlt645_hd_t *hd, const uint8_t *data, int data_length, void *channel), void *channel)
{
    if (hd == NULL) {
        return;
    }
    
    hd->raw_send_cb = cb;
    hd->raw_send_channel = channel;
}

/* Set raw receive callback function */
void dlt645_set_raw_receive_callback(dlt645_hd_t *hd, void (*cb)(dlt645_hd_t *hd, const uint8_t *data, int data_length, void *channel), void *channel)
{
    if (hd == NULL) {
        return;
    }
    
    hd->raw_receive_cb = cb;
    hd->raw_receive_channel = channel;
}

int dlt645_read_data(dlt645_hd_t *hd, uint8_t *bcd_addr, int (*get_raw_data)(char *data, int len, void *contex), void *contex, dlt645_item *it, void *buff, uint32_t max_len)
{
    uint8_t tx_buff[32] = {0};
    char tmp[64] = {0};
    uint8_t tmp_buff[DLT745_RX_BUFF_MAX] = {0};
    int pack_len = 0;
    if (hd->proto_ver == DLT645_1997)
    {
        pack_len = dlt645_1997_pack_read_cmd(bcd_addr, it->data_id, tx_buff, sizeof(tx_buff));
    }
    else
    {
        pack_len = dlt645_2007_pack_read_cmd(bcd_addr, it->data_id, tx_buff, sizeof(tx_buff));
    }
    hd->if_funs->dlt645_flush(hd);

    snprintf(tmp, sizeof(tmp), "[%s]->Tx(%d):", hd->name, pack_len);
    proto_syslog_hex(LOG_DEBUG, tx_buff, pack_len, "%s", tmp);

    /* Call raw data callback for request data */
    if (hd->raw_send_cb != NULL) {
        hd->raw_send_cb(hd, tx_buff, pack_len, hd->raw_send_channel);
    }

    if (hd->if_funs->dlt645_tx(hd, tx_buff, pack_len) < 0)
    {
        proto_syslog(LOG_ERR, "send data error!");
        return -1;
    }

    int re = -99;
    int pre_sum = 0;
    int rx_len = dlt645_get_ack(hd, tmp_buff, DLT745_RX_BUFF_MAX, &pre_sum);

    memset(tmp, 0, sizeof(tmp));
    snprintf(tmp, sizeof(tmp), "[%s]->Rx(%d):", hd->name, rx_len);
    proto_syslog_hex(LOG_DEBUG, tmp_buff, rx_len, "%s", tmp);
    if (rx_len > 0 && hd->raw_receive_cb != NULL) {
        hd->raw_receive_cb(hd, tmp_buff, rx_len, hd->raw_receive_channel);
    }
    if (rx_len > sizeof(dlt645_com_head))
    {
        if (0 == dlt645_recv_check(tmp_buff + pre_sum, rx_len - pre_sum, bcd_addr, it->data_id))
        {
            if (get_raw_data != NULL)
                get_raw_data((char *)(tmp_buff + pre_sum), rx_len - pre_sum, contex);
            re = dlt645_extract_data(it, tmp_buff + pre_sum, buff, max_len);
            if (re <= 0)
            {
                proto_syslog(LOG_ERR, "dlt645 extract data error");
                re = (errno == ETIMEDOUT) ? 0 : -7;
            }
        }
        else
        {
            proto_syslog(LOG_ERR, "dlt645 recv check error");
            re = (errno == ETIMEDOUT) ? 0 : -8;
            //无论什么校验错误都等一个超时，并且清除一下接收缓存，防止错包状态无法解除
            _sleep_response_timeout(hd);
            hd->if_funs->dlt645_flush(hd);

        }
    }
    else
    {
        proto_syslog(LOG_ERR, "dlt645 rx data error");
        re = (errno == ETIMEDOUT) ? 0 : -3;
    }

    return re;
}

int dlt645_set_time(dlt645_hd_t *hd, uint32_t timeout_ms, uint32_t interval)
{
    hd->read_time_out = timeout_ms;
    hd->interval = interval;
    return 0;
}

int dlt645_sync_date_time(dlt645_hd_t *hd, time_t time_stamp)
{
    char tx_buff[32] = {0};
    char tmp[64] = {0};
    // time_stamp = time(NULL);
    struct tm target_time = {};  
    localtime_r(&time_stamp, &target_time);

    struct tm *lt = &target_time;
    
    int tmp_int = lt->tm_mon + 1;

    dlt645_com_head *head = (dlt645_com_head *)tx_buff;
    head->start_code1 = DL645_START_CODE;
    memset(head->addr, 0x99, 6);
    head->start_code2 = DL645_START_CODE;
    head->ctrl_code = C_2007_CODE_BRC;
    head->data_len = 6;
    //
    head->data[0] = 0x33 + ((lt->tm_sec / 10) << 4 | (lt->tm_sec % 10));
    head->data[1] = 0x33 + ((lt->tm_min / 10) << 4 | (lt->tm_min % 10));
    head->data[2] = 0x33 + ((lt->tm_hour / 10) << 4 | (lt->tm_hour % 10));
    head->data[3] = 0x33 + ((lt->tm_mday / 10) << 4 | (lt->tm_mday % 10));
    head->data[4] = 0x33 + ((tmp_int / 10) << 4 | (tmp_int % 10));
    tmp_int = (lt->tm_year + 1900) % 100;
    head->data[5] = 0x33 + ((tmp_int / 10) << 4 | (tmp_int % 10));
    //
    head->data[6] = sum_check((uint8_t *)head, sizeof(dlt645_com_head) + head->data_len);
    head->data[7] = DL645_STOP_CODE;
    //
    int len = sizeof(dlt645_com_head) + head->data_len + 2;
    snprintf(tmp, sizeof(tmp), "[%s]->Tx(%d):", hd->name, len);
    proto_syslog_hex(LOG_DEBUG, tx_buff, len, "%s", tmp);

    if (hd->if_funs->dlt645_tx(hd, tx_buff, len) < 0)
    {
        proto_syslog(LOG_ERR, "send data error!");
        return -1;
    }
    return 0;
}

#if 0 ////本打算实现dlt645写功能，因为拿不到表的密码所以没有实现了
static int data_dlt64_data(void *s_dat, dlt645_item *it, uint8_t *dest_buff)
{
    if (it->data_len > 5)
    {
        return -1;
    }

    if (it->pos == 0)//整数
    {
        int data = *((int*)s_dat);
        int div[] = {1, 100, 10000, 1000000, 100000000};
        int tmp = 0;
        for (int i = 0; i < 5; i++)
        {
            tmp = (data / div[i]) % 100;
            dest_buff[i] = (((tmp / 10) << 4) | (tmp % 10)) + 0x33;
        }
    }
    return it->data_len;
}

int dlt645_write_data(dlt645_hd_t *hd, uint8_t *bcd_addr, dlt645_item *it, void *buff)
{
    char tx_buff[128] = {0};
    char tmp[128] = {0};
    uint32_t data_id = it->data_id;
    dlt645_2007_head *head = (dlt645_2007_head *)tx_buff;
    head->com.start_code1 = DL645_START_CODE;
    memcpy(head->com.addr, bcd_addr, DL645_ADDR_LEN);
    head->com.start_code2 = DL645_START_CODE;
    head->com.ctrl_code = C_2007_CODE_WR;
    head->di[0] = data_id + 0x33;
    head->di[1] = (data_id >> 8) + 0x33;
    head->di[2] = (data_id >> 16) + 0x33;
    head->di[3] = (data_id >> 24) + 0x33;
    //密码
    head->di[4] = 0x04 + 0x33;
    head->di[5] = 0x33;
    head->di[6] = 0x33;
    head->di[7] = 0x33;
    //操作者代码
    head->di[8] = 0x33 + 0x11;
    head->di[9] = 0x33 + 0x22;
    head->di[10] = 0x33 + 0x33;
    head->di[11] = 0x33 + 0x44;
    //
    int len = data_dlt64_data(buff, it, &head->di[12]);
    if (len > 0)
    {
        head->com.data_len = 12 + len;
    }
    head->di[12 + len] = sum_check((uint8_t *)head, len + sizeof(dlt645_2007_head) + 8);
    head->di[13 + len] = DL645_STOP_CODE;

    len = sizeof(dlt645_com_head) + head->com.data_len + 2;
    snprintf(tmp, sizeof(tmp), "[%s]->Tx(%d):", hd->name, len);
    proto_syslog_hex(LOG_DEBUG, tx_buff, len, "==================================== %s", tmp);

    if (hd->if_funs->dlt645_tx(hd, tx_buff, len) < 0)
    {
        proto_syslog(LOG_ERR, "send data error!");
        return -1;
    }

    len = hd->if_funs->dlt645_rx(hd, tx_buff, sizeof(tx_buff));
    return 0;
}
#endif
