#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 <sys/syslog.h>

#include "../common.h"
#include "../custom_fifo.h"
#include "../protocol.h"
#include "../cmdclient.h"

#include "interact.h"
#include "interface.h"
#include "ota_upgrade.h"

static int _write(int fd, void *buf, size_t len) {
    int ret = write(fd, buf, len);
    if (ret == -1) {
        int err = errno;
        proto_syslog(LOG_ERR, "fd %d, write() failed: %s (errno=%d)", fd, strerror(err), err);
    }
    return ret;
}

static int _send(int fd, void *buf, size_t len) {
    int ret = send(fd, (const char *)buf, len, MSG_NOSIGNAL);
    if (ret == -1) {
        int err = errno;
        proto_syslog(LOG_ERR, "fd %d, send() failed: %s (errno=%d)", fd, strerror(err), err);
    }
    return ret;
}

/* tcp */
typedef struct ota_data_tcp
{
    char ip[20];
    uint16_t port;
    uint32_t connect_timeout;
} ota_data_tcp;

static int ota_connect_tcp(ota_hd_t *hd)
{
    if (hd->same == 0 && hd->effect_fd == 1) return 0;

    ota_data_tcp *ctx_rtu_tcp = (ota_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;
}

static int ota_dis_connect_tcp(ota_hd_t *hd)
{
    if (hd->same == 0 && hd->effect_fd == 1) return 0;

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

static int ota_free_tcp(ota_hd_t *hd)
{
    if (hd == NULL)
    {
        return -1;
    }

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

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

    free(hd);
    return 0;
}

static int ota_tx_tcp(ota_hd_t *hd, void *buff, uint32_t len)
{
    frame_wgap_delay(hd->lastread, hd->frame_gap);
    return _send(hd->fd, buff, len);
}

static int ota_rx_tcp(ota_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;

    do
    {
        FD_ZERO(&rset);
        FD_SET(hd->fd, &rset);

        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;
}

static int ota_flush_tcp(ota_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;
}

static const ota_if_t ota_tcp_if =
    {
        .ota_connect = ota_connect_tcp,
        .ota_tx = ota_tx_tcp,
        .ota_rx = ota_rx_tcp,
        .ota_flush = ota_flush_tcp,
        .ota_dis_connect = ota_dis_connect_tcp,
        .ota_free = ota_free_tcp,
    };

ota_hd_t *new_ota_tcp(channel_t* chan_pr, char *ip_or_dev, uint16_t port)
{
    ota_hd_t *hd = calloc(sizeof(ota_hd_t), 1);
    if (hd == NULL)
    {
        proto_syslog(LOG_ERR, "calloc error for hd\n");
        return NULL;
    }

    ota_data_tcp *tcp_data = calloc(sizeof(ota_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 = &ota_tcp_if;
    hd->type = INTERFACE_TCP;
    hd->same = get_ota_param()->same;
    if (hd->same == 0){
        if (fcntl(chan_pr->s, F_GETFD) == -1){
            if (errno == EBADF) {
                fprintf(stderr, "[ERROR] fd=%d is invalid (EBADF: Bad file descriptor)\n", chan_pr->s);
            } else {
                fprintf(stderr, "[ERROR] fd=%d check failed: %s (errno=%d)\n", 
                        chan_pr->s, strerror(errno), errno);
            }
            hd->effect_fd = 0; 
            proto_syslog(LOG_NOTICE, "tcp channel fd is Invalid(%d), ota_hd will create!!!", hd->effect_fd);
        }
        else{ // 若通道fd可用，前提：必须保持正常的数采功能，才可升级，否则（通道FD超时会被close掉，重新open，大概率会导致升级fd不可用，升级失败）
            hd->effect_fd = 1;
            hd->fd = chan_pr->s; //
            proto_syslog(LOG_NOTICE, "tcp channel fd is Effect(%d), ota_hd inherit :%d", hd->effect_fd, hd->fd);
        }
    }
    snprintf(hd->name, sizeof(hd->name), "%s:%d", tcp_data->ip, port);

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

/* udp */
#if 1
typedef struct ota_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;
} ota_data_udp;

static int ota_connect_udp(ota_hd_t *hd)
{
    if (hd->same == 0 && hd->effect_fd == 1) return 0;

    int rc = 0;
    ota_data_udp *ctx_rtu_udp = (ota_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;
}

static int ota_dis_connect_udp(ota_hd_t *hd)
{
    if (hd->same == 0 && hd->effect_fd == 1) return 0;

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

static int ota_free_udp(ota_hd_t *hd)
{
    if (hd == NULL)
    {
        return -1;
    }

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

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

    free(hd);
    return 0;
}

static int ota_tx_udp(ota_hd_t *hd, void *buff, uint32_t len)
{
    struct sockaddr_in *addr = &((ota_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));
}

static int ota_rx_udp(ota_hd_t *hd, void *buff, uint32_t to_read)
{
    ota_data_udp *udp_data = (ota_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;
}

static int ota_flush_udp(ota_hd_t *hd)
{
    int rc;
    ota_data_udp *udp_data = (ota_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;
}

static const ota_if_t ota_udp_if =
    {
        .ota_connect = ota_connect_udp,
        .ota_tx = ota_tx_udp,
        .ota_rx = ota_rx_udp,
        .ota_flush = ota_flush_udp,
        .ota_dis_connect = ota_dis_connect_udp,
        .ota_free = ota_free_udp,
    };

ota_hd_t *new_ota_udp(channel_t* chan_pr, char *ip_or_dev, uint16_t port)
{
    ota_hd_t *hd = calloc(sizeof(ota_hd_t), 1);
    if (hd == NULL)
    {
        proto_syslog(LOG_ERR, "calloc error for hd\n");
        return NULL;
    }

    ota_data_udp *udp_data = calloc(sizeof(ota_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 = &ota_udp_if;
    hd->type = INTERFACE_UDP;
    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

/* uart */
static int ota_connect_uart(ota_hd_t *hd)
{
    if (hd->same == 0 && hd->effect_fd == 1) return 0;

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

static int ota_dis_connect_uart(ota_hd_t *hd)
{
    if (hd->same == 0 && hd->effect_fd == 1) return 0;

    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;
}

static int ota_free_uart(ota_hd_t *hd)
{
    if (hd == NULL)
    {
        return -1;
    }

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

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

    free(hd);
    return 0;
}

static int ota_tx_uart(ota_hd_t *hd, void *buff, uint32_t len)
{
    frame_wgap_delay(hd->lastread, hd->frame_gap);
    return _write(hd->fd, buff, len);
}

static int ota_rx_uart(ota_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;

    do
    {
        FD_ZERO(&rset);
        FD_SET(hd->fd, &rset);

        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;
}

static int ota_flush_uart(ota_hd_t *hd)
{
    return tcflush(hd->fd, TCIOFLUSH);
}

static const ota_if_t ota_uart_if =
    {
        .ota_connect = ota_connect_uart,
        .ota_tx = ota_tx_uart,
        .ota_rx = ota_rx_uart,
        .ota_flush = ota_flush_uart,
        .ota_dis_connect = ota_dis_connect_uart,
        .ota_free = ota_free_uart,
    };

// dev 设备节点
ota_hd_t *new_ota_uart(channel_t* chan_pr, char *dev, int baud, uint8_t data_bit, uint8_t stop_bit, char parity)
{
    ota_hd_t *hd = calloc(sizeof(ota_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 = &ota_uart_if;
    hd->type = INTERFACE_UART;
    hd->same = get_ota_param()->same;
    if (hd->same == 0){
        if (fcntl(chan_pr->s, F_GETFD) == -1){
            if (errno == EBADF) {
                fprintf(stderr, "[ERROR] fd=%d is invalid (EBADF: Bad file descriptor)\n", chan_pr->s);
            } else {
                fprintf(stderr, "[ERROR] fd=%d check failed: %s (errno=%d)\n", 
                        chan_pr->s, strerror(errno), errno);
            }
            hd->effect_fd = 0; 
            proto_syslog(LOG_NOTICE, "uart channel fd is Invalid(%d), ota_hd will create!!!", hd->effect_fd);
        }
        else{ // 若通道fd可用，前提：必须保持正常的数采功能，才可升级，否则（通道FD超时会被close掉，重新open，大概率会导致升级fd不可用，升级失败）
            hd->effect_fd = 1;
            hd->fd = chan_pr->s; //
            proto_syslog(LOG_NOTICE, "uart channel fd is Effect(%d), ota_hd inherit :%d", hd->effect_fd, hd->fd);
        }
    }
    snprintf(hd->name, sizeof(hd->name), "%s", dev);

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


//can
typedef struct can_attr
{
    int bitrate;
    int filter_len;
    struct can_filter filter[0];
} can_attr;

typedef struct ota_can_attr
{
    char dev_f[64]; // 设备节点/dev/xxx
    can_attr *data;
} ota_can_attr;

static can_attr *get_can_attr(const char *dev)
{
    can_attr *attr = NULL;
    char *cfg_f = DEV_PORT_CFG_FILE;
    char *f_data = read_file_data(cfg_f);
    if (f_data == NULL)
    {
        proto_syslog(LOG_ERR, "load uart config file error, file:%s", cfg_f);
        return attr;
    }

    cJSON *root = json_parse_string_with_comments(f_data);
    if (!root)
    {
        proto_syslog(LOG_ERR, "parse file to json obj error, file:%s", cfg_f);
        goto get_can_attr_exit;
    }

    cJSON *tmp_json = cJSON_GetObjectItemCaseSensitive(root, "dev_cfg");
    if (!tmp_json)
    {
        proto_syslog(LOG_ERR, "invalid dev_cfg");
        goto get_can_attr_exit;
    }

    cJSON *dev_json = cJSON_GetObjectItemCaseSensitive(tmp_json, "can");
    if (!dev_json)
    {
        proto_syslog(LOG_ERR, "invalid can");
        goto get_can_attr_exit;
    }

    cJSON *attr_arr_json = cJSON_GetObjectItemCaseSensitive(dev_json, "attr");
    if (!attr_arr_json)
    {
        proto_syslog(LOG_ERR, "invalid attr");
        goto get_can_attr_exit;
    }
    int cnt = cJSON_GetArraySize(attr_arr_json);
    cJSON *target_json = NULL;
    for (int i = 0; i < cnt; i++)
    {
        tmp_json = cJSON_GetArrayItem(attr_arr_json, i);
        target_json = cJSON_GetObjectItemCaseSensitive(tmp_json, "name");
        if (strcmp(target_json->valuestring, dev) == 0)
        {
            target_json = tmp_json;
            break;
        }
        target_json = NULL;
    }

    if (target_json == NULL)
    {
        proto_syslog(LOG_ERR, "no available config was found, %s", dev);
        goto get_can_attr_exit;
    }

    attr_arr_json = cJSON_GetObjectItemCaseSensitive(target_json, "filter");
    if (attr_arr_json == NULL)
    {
        goto get_can_attr_exit;
    }
    cnt = cJSON_GetArraySize(attr_arr_json);
    attr = calloc(1, sizeof(can_attr) + sizeof(struct can_filter) * cnt);
    attr->filter_len = cnt;

    tmp_json = cJSON_GetObjectItemCaseSensitive(target_json, "baud");
    if (tmp_json == NULL)
    {
        goto get_can_attr_exit;
    }
    attr->bitrate = tmp_json->valueint;

    for (int i = 0; i < cnt; i++)
    {
        tmp_json = cJSON_GetArrayItem(attr_arr_json, i);
        target_json = cJSON_GetObjectItemCaseSensitive(tmp_json, "id");
        dev_json = cJSON_GetObjectItemCaseSensitive(tmp_json, "mask");
        if ((target_json == NULL) || (dev_json == NULL))
        {
            free(attr);
            attr = NULL;
            break;
        }
        attr->filter[i].can_id = analysis_addr(target_json->valuestring);
        if (strlen(target_json->valuestring) > 5)
        {
            attr->filter[i].can_id |= 0x80000000;
        }
        attr->filter[i].can_mask = analysis_addr(dev_json->valuestring) & 0x9fffffff;// 不关心错误和远程帧标志位
    }

    proto_syslog(LOG_NOTICE, "%s bitrate=%d, filter_len=%d", dev, attr->bitrate, attr->filter_len);
    for (int i = 0; i < attr->filter_len; i++)
    {
        proto_syslog(LOG_NOTICE, "%s filter[%d]:{id:0x%08x, mask:0x%08x}", dev, i, attr->filter[i].can_id, attr->filter[i].can_mask);
    }

get_can_attr_exit:
    free(f_data);
    if (root != NULL)
    {
        cJSON_Delete(root);
    }
    return attr;
}

static int set_can_attr(int fd, const can_attr *p_attr)
{
    if (setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FILTER, p_attr->filter, sizeof(struct can_filter) * p_attr->filter_len) != 0)
    {
        return -1;
    }

    return 0;
}

static int set_can_bitrate(const char *dev, int bitrate)
{
    char cmd[256] = {0};
    snprintf(cmd, sizeof(cmd), "ip link set %s down;ip link set %s up type can bitrate %d", dev, dev, bitrate);
    cmd_call(cmd, 5, NULL, 0);
    proto_syslog(LOG_NOTICE, "%s:%s", dev, cmd);
    return 0;
}

static int can_dev_map(char *hw_dev, int max_len)
{
    struct can_map
    {
        const char *sw_dev; // 软件can设备号
        const char *hw_dev; // 硬件丝印
    } can_map[] = {
        {.hw_dev = "can1", .sw_dev = "lnxcan1"},
        {.hw_dev = "can2", .sw_dev = "lnxcan2"},
        {.hw_dev = "can3", .sw_dev = "lnxcan3"},
        {.hw_dev = "can4", .sw_dev = "lnxcan4"},
        {.hw_dev = "can5", .sw_dev = "lnxcan5"},
    };
    for (int i = 0; i < sizeof(can_map) / sizeof(can_map[0]); i++)
    {
        if (strcmp(hw_dev, can_map[i].hw_dev) == 0)
        {
            snprintf(hw_dev, max_len, "%s", can_map[i].sw_dev);
            proto_syslog(LOG_WARNING, "hw_can[%s]->sw_can[%s]", can_map[i].hw_dev, can_map[i].sw_dev);
            return 0;
        }
    }

    proto_syslog(LOG_WARNING, "hw_can[%s]->sw_can[%s]", hw_dev, hw_dev);
    return -1;
}

static int connect_can(const char *dev, can_attr *attr)
{
    int fd = 0;
    int ret = 0;
    struct sockaddr_can addr = {0};
    struct ifreq ifr = {0};

    if (attr == NULL)
    {
        proto_syslog(LOG_ERR, "get can attr error:%s", dev);
        return -99;
    }
    else
    {
        set_can_bitrate(dev, attr->bitrate);
    }
    usleep(1000 * 1000); // cmd_call() 等待cmdserver执行完毕（经验值）

    fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    if (fd < 0)
    {
        proto_syslog(LOG_ERR, "socket can failed:%s", dev);
        return -1;
    }

    strcpy(ifr.ifr_name, dev);
    ret = ioctl(fd, SIOCGIFINDEX, &ifr);
    if (ret < 0)
    {
        proto_syslog(LOG_ERR, "%s ioctl SIOCGIFINDEX failed, ret=%d", dev, ret);
        goto new_can_if_err;
    }

    int retry = 10;
    while (retry--) {
        if (!(ifr.ifr_flags & IFF_UP)) {
            proto_syslog(LOG_WARNING, "%s interface not up (UP: %s, RUNNING: %s), waiting...", 
                        dev,
                        (ifr.ifr_flags & IFF_UP) ? "YES" : "NO", "YES");
                        //(ifr.ifr_flags & IFF_RUNNING) ? "YES" : "NO");
            usleep(100 * 1000);
        }
        else
            break;
    }
    if (retry == 0)
        goto new_can_if_err;

    addr.can_family = PF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
    if (ret < 0)
    {
        proto_syslog(LOG_ERR, "%s bind failed, ret = %d", dev, ret);
        goto new_can_if_err;
    }

    set_can_attr(fd, attr);

    return fd;
new_can_if_err:
    close(fd);
    return ret;
}

static int ota_connect_can(ota_hd_t *hd)
{
    if (hd->same == 0 && hd->effect_fd == 1) return 0;

    ota_can_attr *ctx_can = (ota_can_attr *)hd->data;
    int re = connect_can(ctx_can->dev_f, ctx_can->data);
    if (re > -1)
    {
        hd->fd = re;
        return 0;
    }
    hd->fd = -1;
    return -1;
}

static int ota_dis_connect_can(ota_hd_t *hd)
{
    if (hd->same == 0 && hd->effect_fd == 1) return 0;

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

static int ota_free_can(ota_hd_t *hd)
{
    if (hd == NULL)
    {
        return -1;
    }

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

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

    free(hd);
    return 0;
}

static int ota_tx_can(ota_hd_t *hd, void *buff, uint32_t len)
{
    frame_wgap_delay(hd->lastread, hd->frame_gap);
    return _write(hd->fd, buff, len); // 写数据长度在脚本里限制，固定16字节
}

static int ota_rx_can(ota_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;

    // 为适配can应用层升级协议（多个can帧需组包解析，此处不限制长度）
    // to_read = sizeof(struct can_frame); // CAN数据帧固定16字节
    do
    {
        FD_ZERO(&rset);
        FD_SET(hd->fd, &rset);

        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;
}

static int ota_flush_can(ota_hd_t *hd)
{
    int rc;
    char devnull[256];
    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;
}

static const ota_if_t ota_can_if =
    {
        .ota_connect = ota_connect_can,
        .ota_tx = ota_tx_can,
        .ota_rx = ota_rx_can,
        .ota_flush = ota_flush_can,
        .ota_dis_connect = ota_dis_connect_can,
        .ota_free = ota_free_can,
    };

ota_hd_t *new_ota_can(channel_t* chan_pr, char *dev)
{
    ota_hd_t *hd = calloc(sizeof(ota_hd_t), 1);
    if (hd == NULL)
    {
        proto_syslog(LOG_ERR, "calloc error for hd\n");
        return NULL;
    }

    ota_can_attr *ota_can_data = calloc(sizeof(ota_can_attr), 1);
    if (ota_can_data == NULL)
    {
        free(hd);
        proto_syslog(LOG_ERR, "calloc error for ota_can_data\n");
        return NULL;
    }

    char dev_f[64] = {0};
    snprintf(dev_f, sizeof(dev_f), "%s", dev);
    str_to_lower_case(dev_f);
    can_dev_map(dev_f, sizeof(dev_f));// 根据丝印映射一下
    can_attr *can_data = get_can_attr(dev);
    if (can_data == NULL)
    {
        free(ota_can_data);
        free(hd);
        proto_syslog(LOG_ERR, "calloc error for data\n");
        return NULL;
    }

    snprintf(ota_can_data->dev_f, sizeof(ota_can_data->dev_f), "%s", dev_f);
    ota_can_data->data = can_data;

    hd->data = ota_can_data;
    hd->if_funs = &ota_can_if;
    hd->type = INTERFACE_CAN;
    hd->same = get_ota_param()->same;
    if (hd->same == 0){
        if (fcntl(chan_pr->s, F_GETFD) == -1){
            if (errno == EBADF) {
                fprintf(stderr, "[ERROR] fd=%d is invalid (EBADF: Bad file descriptor)\n", chan_pr->s);
            } else {
                fprintf(stderr, "[ERROR] fd=%d check failed: %s (errno=%d)\n", 
                        chan_pr->s, strerror(errno), errno);
            }
            hd->effect_fd = 0; 
            proto_syslog(LOG_NOTICE, "can channel fd is Invalid(%d), ota_hd will create!!!", hd->effect_fd);
        }
        else{ // 若通道fd可用，前提：必须保持正常的数采功能，才可升级，否则（通道FD超时会被close掉，重新open，大概率会导致升级fd不可用，升级失败）
            hd->effect_fd = 1;
            hd->fd = chan_pr->s; //
            proto_syslog(LOG_NOTICE, "can channel fd is Effect(%d), ota_hd inherit :%d", hd->effect_fd, hd->fd);
        }
    }
    snprintf(hd->name, sizeof(hd->name), "%s", dev);

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

//=============================================================================start===
int ota_set_name(ota_hd_t *hd, char *new_name)
{
    return snprintf(hd->name, sizeof(hd->name), "%s", new_name);
}

int ota_connect(ota_hd_t *hd)
{
    return hd->if_funs->ota_connect(hd);
}

int ota_dis_connect(ota_hd_t *hd)
{
    return hd->if_funs->ota_dis_connect(hd);
}
int ota_free(ota_hd_t *hd)
{
    return hd->if_funs->ota_free(hd);
}

int ota_write(ota_hd_t *hd, void *tx_buff, int tx_len, void *rx_buff, int max_len)
{
    proto_syslog_hex(LOG_DEBUG, tx_buff, tx_len, "[%s]->Tx(%d):", hd->name, tx_len);

    hd->if_funs->ota_flush(hd); // 清空缓冲区
    if (hd->if_funs->ota_tx(hd, tx_buff, tx_len) < 0)
    {
        proto_syslog(LOG_ERR, "channel[%s], ota send data error!", hd->name);
        return -1;
    }

    int rx_len = hd->if_funs->ota_rx(hd, rx_buff, max_len);
    if (rx_len >= 0)
    {
		proto_syslog_hex(LOG_DEBUG, rx_buff, rx_len, "[%s]->Rx(%d):", hd->name, rx_len);
    }

    return rx_len;
}

int ota_set_time(ota_hd_t *hd, uint32_t timeout_ms, uint32_t interval)
{
    hd->read_time_out = timeout_ms;
    hd->interval = interval;
    return 0;
}
int ota_set_single_time(ota_hd_t *hd, uint32_t timeout_ms)
{
    hd->read_time_out = timeout_ms;
    return 0;
}
//============================================================================end====

static int ota_interface_type(channel_t* chan_pr, ota_inter_t *ota_inter) {
    if (chan_pr == NULL || ota_inter == NULL) return -1;

    ota_param_t *ota_param = get_ota_param();
    ota_info_t       *info = (ota_info_t *)ota_param->info;

    if (ota_param->same) // 与通讯口不一致
    {
        keyvalue_t *kv = check_port_inpair(info); // kv sure not null,do not if (== NULL)
        local_strlcpy(ota_inter->channel, kv->value, sizeof(ota_inter->channel) - 1);
        proto_syslog(LOG_DEBUG, "ota new channel info: %s", ota_inter->channel);
    }
    else{
        local_strlcpy(ota_inter->channel, chan_pr->channel, sizeof(ota_inter->channel) - 1);
        proto_syslog(LOG_DEBUG, "ota origin channel info: %s", ota_inter->channel);
    }

    if (strstr(ota_inter->channel, "RS485_") != NULL || strstr(ota_inter->channel, "rs485_") != NULL) {
        ota_inter->type = OTA_RS485;
    } 
    else if (strstr(ota_inter->channel, "CAN") != NULL || strstr(ota_inter->channel, "can") != NULL) {
        ota_inter->type = OTA_CAN;
    }
    else if (strstr(ota_inter->channel, ":") != NULL) {
        ota_inter->type = OTA_SOCKET_TCP;
    } 
    // 暂不支持 OTA_SOCKET_UDP
    else {
        // 默认未知类型
        ota_inter->type = OTA_NONE;
        return -1;
    }

    switch ((type_if_t)ota_inter->type)
    {
        case OTA_RS485:
            ota_inter->dev_or_ip = strdup(ota_inter->channel);
        break;

        case OTA_CAN:
            ota_inter->dev_or_ip = strdup(ota_inter->channel);
        break;

        case OTA_SOCKET_TCP:
            parse_host_port(ota_inter->channel, &ota_inter->dev_or_ip, &ota_inter->port);
        break;

        case OTA_SOCKET_UDP:
        break;
        default:
            break;
    }

    proto_syslog(LOG_INFO, "ota channel info: %s, type: %d(1: RS485 2:CAN 3:SOCKET_TCP 4:SOCKET_UDP)", ota_inter->channel, ota_inter->type);
    return 0;
}

ota_hd_t *register_ota_interface(char* dev_no, channel_t* chan_pr, device_t* dev_pr)
{
    ota_hd_t *hd = NULL;
    uart_par_t p_par = {0};

    ota_inter_t *ota_inter = calloc(sizeof(ota_inter_t), 1);
    if (ota_inter == NULL)
    {
        proto_syslog(LOG_ERR, "ota_inter calloc error!!!");
        goto end; 
    }

    if (ota_interface_type(chan_pr, ota_inter) == -1) { 
        proto_syslog(LOG_ERR, "ota interface type error!!!");
        goto end; 
    }

    switch ((type_if_t)ota_inter->type)
    {
        case OTA_RS485:
        {
            if (0 == extract_uart_par(ota_inter->dev_or_ip, &p_par))
            {
                hd = new_ota_uart(chan_pr, p_par.f_name, p_par.baud, p_par.data_bit, p_par.stop_bit, p_par.parity);
                if (hd == NULL)
                {
                    proto_syslog(LOG_ERR, "new_ota_uart error !!!");
                }
            }
            break;
        }
        case OTA_CAN:
        {
            hd = new_ota_can(chan_pr, ota_inter->dev_or_ip);
            if (hd == NULL)
            {
                proto_syslog(LOG_ERR, "new_ota_can error !!!");
            }
            break;
        }
        case OTA_SOCKET_TCP:
        {
            hd = new_ota_tcp(chan_pr, ota_inter->dev_or_ip, ota_inter->port);
            if (hd == NULL)
            {
                proto_syslog(LOG_ERR, "new_ota_tcp error !!!");
            }
            break;
        }
        case OTA_SOCKET_UDP:
        {
            break;
        }
        default:
            break;
    }

    if (hd != NULL)
    {
        if (0 != ota_connect(hd))
        {
            proto_syslog(LOG_ERR, "ota_connect error !!!");
            ota_free(hd);
            hd = NULL;
            goto end; 
        }

        ota_set_name(hd, ota_inter->channel);  
        ota_set_time(hd, dev_pr->timeout, dev_pr->interval); // 沿用设备user_def参数配置，设置一次即可
    }
end:
    if (ota_inter != NULL){
        if (ota_inter->dev_or_ip != NULL) free(ota_inter->dev_or_ip);
        free(ota_inter);
    }
    return hd;
}

void ota_deinit(ota_hd_t *hd)
{
    if (hd == NULL) return;
    ota_free(hd);
}
