#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/rtc.h>
#include "tag.h"
#include "signal.h"
#include <poll.h>
#include "modbus_tcp_ser.h"
#include "lnxall_list.h"
#include "cmdclient.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h> 
#include <dirent.h>
#include <limits.h>
#include <ctype.h>
#include "common.h"
#include <openssl/md5.h>
#include <curl/curl.h>
#include <openssl/md5.h>
#include <fcntl.h>

int g_proto_log_level = LOG_DEBUG;
int g_ems_log_level = LOG_DEBUG;
const char *show_log[]=
{
    "\033[1;37m[EMERG]\033[0m",    
    "\033[1;30m[ALERT]\033[0m",    
    "\033[1;36m[CRIT]\033[0m",    
    "\033[1;31m[ERR]\033[0m",       
    "\033[1;33m[WARNING]\033[0m",       
    "\033[1;32m[NOTICE]\033[0m",    
    "\033[1;34m[INFO]\033[0m",    
    "\033[1;35m[DEBUG]\033[0m"
} ;

const char *log_level_string[] =
    {
        "[EMERG]",
        "[ALERT]",
        "[CRIT]",
        "[ERR]",
        "[WARNING]",
        "[NOTICE]",
        "[INFO]",
        "[DEBUG]"};
        
const char *protocal_type_name[] =
{
    "none",
    "script",
    "modbus_tcp",
    "modbus_rtu",
    "dlt645_2007",
    "dlt645_1997",
    "ems",
    "ups",
    "bmser_bin",
    "can_raw",
    "opcua",
    "lc",
    "modbus_rtu_rcrc",
    "iec104",
    "custom",
    "ydt1363_2014",
};

const char *get_protocal_string(int type)
{
    if (type < sizeof(protocal_type_name) / sizeof(0))
    {
        return protocal_type_name[type];
    }
    else
    {
        return NULL;
    }
}

const char *property_name[] =
{
    "realdata",
    "system",
    "alarm",
};

PROPERTY_TYPE get_property_by_string(char *tagTypeString)
{
    for (PROPERTY_TYPE i = PROPERTY_REALDATA ; i < PROPERTY_MAX ; i++)
    {
        if(strcmp(tagTypeString,property_name[i])==0)
        return i;
    }
    proto_syslog(LOG_WARNING, "PROPERTY_TYPE_ERROR");
    return PROPERTY_REALDATA;
}


DATA_TAG_TYPE turn_data_type(DATA_TYPE type)
{
    DATA_TAG_TYPE dist_type = TYPE_MAX;
	
    if (type == TYPE_BOOL || type == TYPE_INT || type == TYPE_UINT || type == TYPE_BITS)
        dist_type = TYPE_TAG_INT;
    else if (type == TYPE_FLOAT)
        dist_type = TYPE_TAG_FLOAT;
    
    return dist_type;
}
const char * get_string_property(int type)
{
    return property_name[type];
}

int get_protocal_type(const char *c)
{
    int i = -1;
    EACH_STRING_INDEX(c, protocal_type_name, i);
    return i;
}


int get_localip(const char * eth_name, char *local_ip_addr)
{
	int ret = -1;
    register int fd;
    struct ifreq ifr;
 
	if (local_ip_addr == NULL || eth_name == NULL)
	{
		return ret;
	}
	if ((fd=socket(AF_INET, SOCK_DGRAM, 0)) > 0)
	{
		strcpy(ifr.ifr_name, eth_name);
		if (!(ioctl(fd, SIOCGIFADDR, &ifr)))
		{
			ret = 0;
			strcpy(local_ip_addr, inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr));
		}
	}
	if (fd > 0)
	{
		close(fd);
	}
    return ret;
}


char *tag_type_string[] = {
    "int",
    "float",
};

DATA_TAG_TYPE get_tag_type_by_string(char *tagTypeString)
{
    for (DATA_TAG_TYPE i = TYPE_TAG_INT ; i < TYPE_TAG_MAX ; i++)
    {
        if(strcmp(tagTypeString,tag_type_string[i])==0)
        return i;
    }
    proto_syslog(LOG_WARNING, "TAG_TYPE_ERROR");
    return TYPE_TAG_ERROR;
}

static char *history_type_string[] = {
    "off",
    "change",
    "period",
    "both"};
DATA_HISTORY_TYPE get_history_type_by_string(char *tagTypeString)
{
    for (DATA_HISTORY_TYPE i = HISTORY_TYPE_OFF; i < HISTORY_TYPE_MAX; i++)
    {
        if (strcmp(tagTypeString, history_type_string[i]) == 0)
        return i;
    }
    proto_syslog(LOG_WARNING, "DATA_HISTORY_TYPE ERR");
    return HISTORY_TYPE_MAX;
}

char *type_string[] = {
    "bool",
    "int",
    "uint",
    "float",
    "bits",
    "short",
    "ushort"
};

DATA_TYPE get_type_by_string(char *typeString)
{
    for (DATA_TYPE i = TYPE_BOOL ; i < TYPE_MAX ; i++)
    {
        if(strcmp(typeString,type_string[i])==0)
        return i;
    }
    proto_syslog(LOG_WARNING, "TYPE_ERROR");
    return TYPE_ERROR;
}

char *crc16type_string[] = {
    "CRC16-N",       //None
    "CRC16-X",       //XModem
    "CRC16-C",       //CCITT
    "CRC16-M",       //Modbus
    "CRC16-IBM",     //IBM
    "CRC16-USB",     //USB
    "CRC16-DNP",     //DNP
    "CRC16-DEC"      //DECT
};

CRC16_TYPE get_crc16_by_string(char *crc16typeString)
{
    for (CRC16_TYPE i = CRC16_N ; i < CRC16_MAX ; i++)
    {
        if(strcmp(crc16typeString, crc16type_string[i]) == 0)
        return i;
    }
    proto_syslog(LOG_WARNING, "CRC16_ERROR");
    return CRC16_ERROR;
}

uint16_t crc16_M(uint8_t *buffer, uint16_t buffer_length){
	uint16_t val = 0;
    val = crc16(buffer, buffer_length);
	return val;
}

// 将字节的高4位转换为ASCII码   --YDT1363协议必须是大写字母，
uint8_t toAsciiHigh(uint8_t byte) {
    uint8_t high = (byte >> 4) & 0x0F;
    if (high < 10) {
        return high + '0'; // 0-9 转换为 '0'-'9'
    } else {
        return high - 10 + 'A'; // 10-A 转换为 'A'-'F'
    }
}

// 将字节的低4位转换为ASCII码
uint8_t toAsciiLow(uint8_t byte) {
    uint8_t low = byte & 0x0F;
    if (low < 10) {
        return low + '0'; // 0-9 转换为 '0'-'9'
    } else {
        return low - 10 + 'A'; // 10-A 转换为 'A'-'F'
    }
}

// 将ASCII码转换为字节
uint8_t fromAscii(uint8_t high, uint8_t low) {
    uint8_t highValue, lowValue;

    if (high >= '0' && high <= '9') {
        highValue = high - '0';
    } 
    else if (high >= 'A' && high <= 'F') {
        highValue = high - 'A' + 10;
    } 
    else if (high >= 'a' && high <= 'f') {
        highValue = high - 'a' + 10;
    } 
    else {
        proto_syslog(LOG_ERR, "Invalid high ASCII character: %c ", high);
        return 0xFF; // 返回一个无效值
    }

    if (low >= '0' && low <= '9') {
        lowValue = low - '0';
    } else if (low >= 'A' && low <= 'F') {
        lowValue = low - 'A' + 10;
    } else if (low >= 'a' && low <= 'f') {
        lowValue = low - 'a' + 10;
    } else {
        proto_syslog(LOG_ERR, "Invalid low ASCII character: %c ", low);
        return 0xFF; // 返回一个无效值
    }

    return (highValue << 4) + lowValue;
}

// 函数定义：将字符串转换为十六进制值  "2.5"->0x25
uint8_t stringToHex(const char *str) {
    char tempStr[10];
    size_t tempIndex = 0;

    for (size_t i = 0; i < strlen(str); i++) {    // 遍历原始字符串，去除小数点
        if (str[i] != '.') {
            tempStr[tempIndex++] = str[i];
        }
    }
    tempStr[tempIndex] = '\0';

    uint8_t hexValue = strtol(tempStr, NULL, 16);
    return hexValue;
}

const char* get_type_string(DATA_TYPE type)
{
    if ((TYPE_ERROR < type) && (type < TYPE_MAX))
    {
        return type_string[type];
    }
    else
    {
        return NULL;
    }
}

char *order_string[] = {
    "A",
    "AB",
    "BA",
    "ABCD",
    "DCBA",
    "BADC",
    "CDAB",
    "ABCDEFGH",
    "GHEFCDAB",
    "BADCFEHG",
    "HGFEDCBA"
};

DATA_ORDER get_order_by_string(char *typeString)
{
    for (DATA_ORDER i = ORDER_A; i < ORDER_MAX; i++)
    {
        if(strcmp(typeString,order_string[i])==0)
        return i;
    }
    proto_syslog(LOG_WARNING, "type %s ORDER_ERROR",typeString);
    return ORDER_ERROR;
}

const char* get_order_string(DATA_ORDER order)
{
    if ((ORDER_ERROR < order) && (order < ORDER_MAX))
    {
        return order_string[order];
    }
    else
    {
        return NULL;
    }
}



uint32_t analysis_addr(char *addr) //格式化地址返回整数
{
    uint32_t slave_addr = 0;
    if(strncasecmp(addr,"0x",2) == 0)
    {
        sscanf(addr,"%x",&slave_addr);
    }
    else{
        sscanf(addr,"%u",&slave_addr);
    }
    return slave_addr;
}



uint32_t *analysis_addr_with_p(char *addr)//格式化地址申请空间 返回内存地址
{
    uint32_t* slave_addr = calloc(sizeof(uint32_t),1);
    if(strncasecmp(addr,"0x",2) == 0)
    {
        sscanf(addr,"%x",slave_addr);
    }
    else{
        sscanf(addr,"%u",slave_addr);
    }
    return slave_addr;
}


bool check_float_is_1(double data_f) //判断是不是1
{
    if ((data_f<1.00001)&&(data_f>0.9999))
        return TRUE;
    return FALSE;
}

void cond_signal(pthread_cond_t *t_cond,pthread_mutex_t *t_mutex) //发送条件变量
{
	pthread_mutex_lock(t_mutex);
	pthread_cond_broadcast(t_cond);
	pthread_mutex_unlock(t_mutex);
}
int getCommandReturn(char *command,char *distchar,int size)
{
    FILE *pipe = popen(command, "r"); // 执行命令并打开进程管道

    if (pipe == NULL) {
        printf("Failed to execute command\n");
        return 1;
    }
    while (fgets(distchar, size, pipe) != NULL) {
        printf("%s\n", distchar); // 输出命令的输出字符串
    }
    pclose(pipe); // 关闭进程管道
    return 0;
}

int cond_wait(pthread_cond_t *t_cond,pthread_mutex_t *t_mutex,int timeout_s,int timeout_ms) //等待条件
{	
	int ret;
	pthread_mutex_lock(t_mutex);
	struct timeval now;
	struct timespec outtime;
	gettimeofday(&now,NULL);
	outtime.tv_sec = now.tv_sec+timeout_s;
	outtime.tv_nsec = (now.tv_usec+timeout_ms*1000)*1000;
	ret = pthread_cond_timedwait(t_cond, t_mutex,&outtime);
	pthread_mutex_unlock(t_mutex);
	return ret;
}

void parse_host_port(const char *URL, char **host, uint16_t *port)
{
    if (URL == NULL)
        return;
    char *url_dup = strdup(URL);
    char *p_colon = NULL;          // 主机后第一个冒号的位置
    char *start = (char *)url_dup; // 记录www开始的位置
    p_colon = strchr(start, ':');
    if (p_colon != NULL)
    {
        *port = atoi(p_colon + 1);
        *p_colon = '\0';
    }
    else
    {
        *port = 8080; // 没有的话取默认的8080端口
    }
    *host = strdup(start); // TODO:内存泄漏了!!!!!!
    if (url_dup != NULL)
    {
        free(url_dup);
        url_dup = NULL;
    }
    return;
}

int set_net_par(void)
{
    int re = 0;
    char *cfg_f = DEV_PORT_CFG_FILE;
    char *f_data = read_file_data(cfg_f);
    if (f_data == NULL)
    {
        proto_syslog(LOG_ERR, "load device config file error, file:%s", cfg_f);
        return -1;
    }

    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);
        re = -2;
        goto set_net_par_exit;
    }

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

    cJSON *dev_json = cJSON_GetObjectItemCaseSensitive(tmp_json, "lan");
    if (!dev_json)
    {
        proto_syslog(LOG_ERR, "invalid lan");
        re = -4;
        goto set_net_par_exit;
    }

    cJSON *attr_arr_json = cJSON_GetObjectItemCaseSensitive(dev_json, "attr");
    if (!attr_arr_json)
    {
        proto_syslog(LOG_ERR, "invalid attr");
        re = -6;
        goto set_net_par_exit;
    }

    char exec_cmd[256] = {0};
    int len = 0;
    int cnt = cJSON_GetArraySize(attr_arr_json);
    for (int i = 0; i < cnt; i++)
    {
        memset(exec_cmd, 0, sizeof(exec_cmd));
        dev_json = cJSON_GetArrayItem(attr_arr_json, i);
        cJSON *tmp_json = cJSON_GetObjectItemCaseSensitive(dev_json, "name");
        if (tmp_json == NULL)
        {
            proto_syslog(LOG_WARNING, "lan port uses defult name at %d", i);
            len = snprintf(exec_cmd, sizeof(exec_cmd), "set_vlan.sh  set lan ");
        }
        else
        {
            len = snprintf(exec_cmd, sizeof(exec_cmd), "set_vlan.sh  set %s ", tmp_json->valuestring);
        }

        tmp_json = cJSON_GetObjectItemCaseSensitive(dev_json, "ip");
        if (tmp_json == NULL)
        {
            proto_syslog(LOG_ERR, "lan attribute config error at %d, lan port must have a ip", i);
            continue;
        }

        len = strlen(exec_cmd);
        len += snprintf(exec_cmd + len, sizeof(exec_cmd) - len, "--ipaddr %s ", tmp_json->valuestring);

        tmp_json = cJSON_GetObjectItemCaseSensitive(dev_json, "mask");
        if (tmp_json != NULL)
        {
            len += snprintf(exec_cmd + len, sizeof(exec_cmd) - len, "--netmask %s ", tmp_json->valuestring);
        }
        //snprintf(exec_cmd + len, sizeof(exec_cmd) - len, "-S");

        pid_t status = cmd_call(exec_cmd,5,NULL,1);
        if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
        {
            proto_syslog(LOG_INFO, "system run:%s success", exec_cmd);
        }
        else
        {
            proto_syslog(LOG_INFO, "system run:%s err, %d", exec_cmd, status);
        }
        
    }
set_net_par_exit:
    free(f_data);
    if (root != NULL)
    {
        cJSON_Delete(root);
    }
    return re;
}

int file_is_open(const char *file_name)
{
    DIR *proc_dir = NULL;
    DIR *fd_dir = NULL;
    struct dirent *proc_entry = NULL;
    struct dirent *fd_entry = NULL;
    char proc_path[PATH_MAX];
    char fd_path[PATH_MAX];
    char link_path[PATH_MAX];
    int pid = 0;

    proc_dir = opendir("/proc");
    if (!proc_dir)
    {
        perror("opendir /proc");
        return -1;
    }

    while ((proc_entry = readdir(proc_dir)) != NULL)
    {
        // 跳过非数字条目，因为进程ID是数字
        if (proc_entry->d_type != DT_DIR || !isdigit(proc_entry->d_name[0]))
        {
            continue;
        }
        pid = atoi(proc_entry->d_name);
        snprintf(proc_path, sizeof(proc_path), "/proc/%d/fd", pid);
        fd_dir = opendir(proc_path);
        if (!fd_dir)
        {
            continue;
        }

        while ((fd_entry = readdir(fd_dir)) != NULL)
        {
            snprintf(fd_path, sizeof(fd_path), "/proc/%d/fd/%s", pid, fd_entry->d_name);
            ssize_t len = readlink(fd_path, link_path, sizeof(link_path) - 1);
            if (len == -1)
            {
                continue;
            }
            link_path[len] = '\0'; // 终止字符串
            if (strstr(link_path, file_name))
            {
                closedir(fd_dir);
                closedir(proc_dir);
                return 1;
            }
        }
        closedir(fd_dir);
    }
    closedir(proc_dir);

    return 0;
}

int dev_is_open(char *dev)
{
#if 0
    int re = 0;
    char buff[128] = {0};
    snprintf(buff, sizeof(buff), "fuser %s", dev);
    FILE *fh = popen(buff, "r");
    memset(buff, 0, sizeof(buff));
    if ((fread(buff, sizeof(buff), 1, fh) >= 0) && (strlen(buff) > 0))
    {
        re = 1;
    }
    pclose(fh);
    return re;
#else
    return file_is_open(dev);
#endif
}

const static int tag_md_size[ORDER_MAX] = {1, 1, 1, 2, 2, 2, 2};
int get_data_order_size(DATA_ORDER order)
{
    return tag_md_size[order];
}

/********************************************json去注释*************************************************************/

static void remove_comments(char *string, const char *start_token, const char *end_token) {
    int in_string = 0, escaped = 0;
    size_t i;
    char *ptr = NULL, current_char;
    size_t start_token_len = strlen(start_token);
    size_t end_token_len = strlen(end_token);
    if (start_token_len == 0 || end_token_len == 0)
    	return;
    while ((current_char = *string) != '\0') {
        if (current_char == '\\' && !escaped) {
            escaped = 1;
            string++;
            continue;
        } else if (current_char == '\"' && !escaped) {
            in_string = !in_string;
        } else if (!in_string && strncmp(string, start_token, start_token_len) == 0) {
			for(i = 0; i < start_token_len; i++)
                string[i] = ' ';
        	string = string + start_token_len;
            ptr = strstr(string, end_token);
            if (!ptr)
                return;
            for (i = 0; i < (ptr - string) + end_token_len; i++)
            {
                string[i] = ' ';
            }
          	string = ptr + end_token_len - 1;
        }
        escaped = 0;
        string++;
    }
}
cJSON *json_parse_string_with_comments(const char *string) {
    char *string_mutable_copy = NULL;
    string_mutable_copy = strdup(string);
    if (!string_mutable_copy)
        return NULL;
    remove_comments(string_mutable_copy, "/*", "*/");
    remove_comments(string_mutable_copy, "//", "\n");
    cJSON *result = cJSON_Parse(string_mutable_copy);
    free(string_mutable_copy);
    return result;
}

/******************************************************************************************************************/


void GEN_Num2ASIC(unsigned char datas, unsigned char *asic)
{
 	unsigned char i;
	unsigned char fourbits;

    for(i=0;i<2;i++)
    {
        if(i==0)
        {
            fourbits=datas&0x0F;
        }
        else
        {
            fourbits=(datas&0xF0)>>4;
        }

		if (fourbits < 0x0A)
			asic[i] = fourbits + '0';
		else if (fourbits < 0x10)
			asic[i] = fourbits + 'A' - 10;
		else
			asic[i] = '0';
    }
}


void GEN_ARRHEX2ASIC(unsigned char *hexdata, unsigned char *asicdata, unsigned short hexlength,char spilite)
{
	unsigned short i=0;
	unsigned char asic[2];
	if(spilite == 0)
    {
        for(i=0;i<hexlength;i++)
        {
            GEN_Num2ASIC(hexdata[i], asic);
            asicdata[i*2]=asic[1];
            asicdata[i*2+1]=asic[0];
        }
    }
    else
    {
        for(i=0;i<hexlength;i++)
        {
            GEN_Num2ASIC(hexdata[i], asic);
            asicdata[i*3]=asic[1];
            asicdata[i*3+1]=asic[0];
            asicdata[i*3+2]=spilite;
        }
    }
}


int GEN_ARRASIC2HEX(unsigned char *data,unsigned char *dir,unsigned char len) //返回得到的HEX字节数 
{
	short len_t = 0;
	unsigned char data_t[1024] = {0};
	
	for (int i = 0; i < len; i++)
	{
		if((data[i]>=0x30)&&(data[i]<=0x39))
		{
			data_t[len_t]=data[i]-0x30;
		}
		else if((data[i]>=0x41)&&(data[i]<=0x46))
		{
			data_t[len_t]=data[i]-0x37;
		}
		else if((data[i]>=0x61)&&(data[i]<=0x66))
		{
			data_t[len_t]=data[i]-0x57;
		}
		else continue;
		len_t++;
	}
	if (len_t%2 != 0)
	return 0;
	for (int i = 0; i < len_t/2; i++)
	{
		dir[i] = ((data_t[2*i]<<4)|data_t[2*i+1]);
	}
	
	return len_t/2;
}

int is_virtual_device_and_to_dup(const char *info, char *map_ip)
{
    struct uart_to_udp
    {
        const char *uart_dev;
        const char *ip;
        int port;
    };
    struct uart_to_udp uart_to_udp[] =
        {
            {.uart_dev = "RS485_1", .ip = "127.0.0.1", .port = 12000},
            {.uart_dev = "RS485_2", .ip = "127.0.0.1", .port = 12001},
            {.uart_dev = "RS485_3", .ip = "127.0.0.1", .port = 12002},
            {.uart_dev = "RS485_4", .ip = "127.0.0.1", .port = 12003},
            {.uart_dev = "RS485_5", .ip = "127.0.0.1", .port = 12004},
            {.uart_dev = "RS485_10", .ip = "127.0.0.1", .port = 12100}, // 这个是一个内部的DIO控制
        };

    for (int i = 0; i < sizeof(uart_to_udp) / sizeof(uart_to_udp[0]); i++)
    {
        if (strcmp(info, uart_to_udp[i].uart_dev) == 0)
        {
            strcpy(map_ip, uart_to_udp[i].ip);
            return uart_to_udp[i].port;
        }
    }

    return 0;
}

#if 1 // tcp和串口连接
static int tcp_set_ipv4_options(int s)
{
    int rc;
    int option;

    /* Set the TCP no delay flag */
    /* SOL_TCP = IPPROTO_TCP */
    option = 1;
    rc = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const void *)&option, sizeof(int));
    if (rc == -1)
    {
        return -1;
    }

    /* If the OS does not offer SOCK_NONBLOCK, fall back to setting FIONBIO to
     * make sockets non-blocking */
    /* Do not care about the return value, this is optional */
#if !defined(SOCK_NONBLOCK) && defined(FIONBIO)
#ifdef OS_WIN32
    {
        /* Setting FIONBIO expects an unsigned long according to MSDN */
        u_long loption = 1;
        ioctlsocket(s, FIONBIO, &loption);
    }
#else
    option = 1;
    ioctl(s, FIONBIO, &option);
#endif
#endif

#ifndef OS_WIN32
    /**
     * Cygwin defines IPTOS_LOWDELAY but can't handle that flag so it's
     * necessary to workaround that problem.
     **/
    /* Set the IP low delay option */
    option = IPTOS_LOWDELAY;
    rc = setsockopt(s, IPPROTO_IP, IP_TOS, (const void *)&option, sizeof(int));
    if (rc == -1)
    {
        return -1;
    }
#endif

    return 0;
}

static int _connect(int sockfd,
                    const struct sockaddr *addr,
                    socklen_t addrlen,
                    const struct timeval *ro_tv)
{
    int rc = connect(sockfd, addr, addrlen);

#ifdef OS_WIN32
    int wsaError = 0;
    if (rc == -1)
    {
        wsaError = WSAGetLastError();
    }

    if (wsaError == WSAEWOULDBLOCK || wsaError == WSAEINPROGRESS)
    {
#else
    if (rc == -1 && errno == EINPROGRESS)
    {
#endif
        fd_set wset;
        int optval;
        socklen_t optlen = sizeof(optval);
        struct timeval tv = *ro_tv;

        /* Wait to be available in writing */
        FD_ZERO(&wset);
        FD_SET(sockfd, &wset);
        rc = select(sockfd + 1, NULL, &wset, NULL, &tv);
        if (rc <= 0)
        {
            /* Timeout or fail */
            return -1;
        }

        /* The connection is established if SO_ERROR and optval are set to 0 */
        rc = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&optval, &optlen);
        if (rc == 0 && optval == 0)
        {
            return 0;
        }
        else
        {
            errno = ECONNREFUSED;
            return -1;
        }
    }
    return rc;
}

int connect_tcp(const char *ip, int port, uint32_t cnt_timeout)
{
    int rc = 0;
    int fd = 0;
    struct sockaddr_in addr;
    int flags = SOCK_STREAM;

#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

    fd = socket(PF_INET, flags, 0);
    if (fd < 0)
    {
        return -1;
    }

    rc = tcp_set_ipv4_options(fd);
    if (rc == -1)
    {
        close(fd);
        fd = -1;
        return -1;
    }
    proto_syslog(LOG_NOTICE, "Connecting to %s:%d\n", ip, port);

    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    rc = inet_pton(addr.sin_family, ip, &(addr.sin_addr));
    if (rc <= 0)
    {
        proto_syslog(LOG_ERR, "Invalid IP address: %s\n", ip);
        close(fd);
        fd = -1;
        return -1;
    }

    struct timeval response_timeout;
    response_timeout.tv_sec = cnt_timeout;
    response_timeout.tv_usec = 0;
    rc = _connect(fd, (struct sockaddr *)&addr, sizeof(addr), &response_timeout);
    if (rc == -1)
    {
        close(fd);
        fd = -1;
        return -1;
    }

    return fd;
}

int open_uart(uart_attr *attr)
{
    int fd = 0;
    speed_t speed = 0;
    int flags = 0;
    struct termios tios;
    proto_syslog(LOG_INFO, "Opening %s at %d bauds (%c, %d, %d)\n",
                 attr->dev_f, attr->baud, attr->parity,
                 attr->data_bit, attr->stop_bit);
    flags = O_RDWR | O_NOCTTY | O_NDELAY | O_EXCL;
#ifdef O_CLOEXEC
    flags |= O_CLOEXEC;
#endif
    fd = open(attr->dev_f, flags);
    if (fd == -1)
    {
        proto_syslog(LOG_ERR, "ERROR Can't open the device %s (%s)\n",
                     attr->dev_f, strerror(errno));
        return -1;
    }

    /* Save */
    tcgetattr(fd, &attr->old_tios);

    memset(&tios, 0, sizeof(struct termios));

    switch (attr->baud)
    {
    case 110:
        speed = B110;
        break;
    case 300:
        speed = B300;
        break;
    case 600:
        speed = B600;
        break;
    case 1200:
        speed = B1200;
        break;
    case 2400:
        speed = B2400;
        break;
    case 4800:
        speed = B4800;
        break;
    case 9600:
        speed = B9600;
        break;
    case 19200:
        speed = B19200;
        break;
    case 38400:
        speed = B38400;
        break;
#ifdef B57600
    case 57600:
        speed = B57600;
        break;
#endif
#ifdef B115200
    case 115200:
        speed = B115200;
        break;
#endif
#ifdef B230400
    case 230400:
        speed = B230400;
        break;
#endif
#ifdef B460800
    case 460800:
        speed = B460800;
        break;
#endif
#ifdef B500000
    case 500000:
        speed = B500000;
        break;
#endif
#ifdef B576000
    case 576000:
        speed = B576000;
        break;
#endif
#ifdef B921600
    case 921600:
        speed = B921600;
        break;
#endif
#ifdef B1000000
    case 1000000:
        speed = B1000000;
        break;
#endif
#ifdef B1152000
    case 1152000:
        speed = B1152000;
        break;
#endif
#ifdef B1500000
    case 1500000:
        speed = B1500000;
        break;
#endif
#ifdef B2500000
    case 2500000:
        speed = B2500000;
        break;
#endif
#ifdef B3000000
    case 3000000:
        speed = B3000000;
        break;
#endif
#ifdef B3500000
    case 3500000:
        speed = B3500000;
        break;
#endif
#ifdef B4000000
    case 4000000:
        speed = B4000000;
        break;
#endif
    default:
        speed = B9600;
        proto_syslog(LOG_ERR, "WARNING Unknown baud rate %d for %s (B9600 used)\n",
                     attr->baud, attr->dev_f);
    }
    /* Set the baud rate */
    if ((cfsetispeed(&tios, speed) < 0) ||
        (cfsetospeed(&tios, speed) < 0))
    {
        close(fd);
        fd = -1;
        return -1;
    }

    /* C_CFLAG      Control options
       CLOCAL       Local line - do not change "owner" of port
       CREAD        Enable receiver
    */
    tios.c_cflag |= (CREAD | CLOCAL);
    /* CSIZE, HUPCL, CRTSCTS (hardware flow control) */

    /* Set data bits (5, 6, 7, 8 bits)
       CSIZE        Bit mask for data bits
    */
    tios.c_cflag &= ~CSIZE;
    switch (attr->data_bit)
    {
    case 5:
        tios.c_cflag |= CS5;
        break;
    case 6:
        tios.c_cflag |= CS6;
        break;
    case 7:
        tios.c_cflag |= CS7;
        break;
    case 8:
    default:
        tios.c_cflag |= CS8;
        break;
    }

    /* Stop bit (1 or 2) */
    if (attr->stop_bit == 1)
        tios.c_cflag &= ~CSTOPB;
    else /* 2 */
        tios.c_cflag |= CSTOPB;

    /* PARENB       Enable parity bit
       PARODD       Use odd parity instead of even */
    if (attr->parity == 'N')
    {
        /* None */
        tios.c_cflag &= ~PARENB;
    }
    else if (attr->parity == 'E')
    {
        /* Even */
        tios.c_cflag |= PARENB;
        tios.c_cflag &= ~PARODD;
    }
    else
    {
        /* Odd */
        tios.c_cflag |= PARENB;
        tios.c_cflag |= PARODD;
    }
    tios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    if (attr->parity == 'N')
    {
        /* None */
        tios.c_iflag &= ~INPCK;
    }
    else
    {
        tios.c_iflag |= INPCK;
    }

    tios.c_iflag &= ~(IXON | IXOFF | IXANY);
    tios.c_oflag &= ~OPOST;
    tios.c_cc[VMIN] = 0;
    tios.c_cc[VTIME] = 0;

    if (tcsetattr(fd, TCSANOW, &tios) < 0)
    {
        close(fd);
        fd = -1;
        return -1;
    }

    return fd;
}

int get_file_md5(const char *filename,char *result) {
    if ((filename == NULL)||(result == NULL)) {
        return 1;
    }
    FILE *file = fopen(filename, "rb");
    if (file == NULL) {
        perror("Error opening file");
        return 1;
    }

    MD5_CTX c;
    MD5_Init(&c);

    char buf[1024];
    size_t len;
    while ((len = fread(buf, 1, sizeof(buf), file))) {
        MD5_Update(&c, buf, len);
    }

    unsigned char md[MD5_DIGEST_LENGTH];
    MD5_Final(md, &c);

    fclose(file);
    for (size_t i = 0; i < MD5_DIGEST_LENGTH; i++)
    {
        sprintf(result + i*2,"%02x",md[i]);
    }
    result[MD5_DIGEST_LENGTH*2] = '\0';
    return 0;
}

void copy_file(const char *src_filename, const char *dst_filename) {
    FILE *src_file, *dst_file;
    char buffer[1024];
    size_t bytes_read;
    src_file = fopen(src_filename, "rb");
    if (src_file == NULL) {
        perror("无法打开源文件");
        exit(EXIT_FAILURE);
    }
    dst_file = fopen(dst_filename, "wb");
    if (dst_file == NULL) {
        fclose(src_file); 
        perror("无法创建或打开目标文件");
        exit(EXIT_FAILURE);
    }
    while ((bytes_read = fread(buffer, 1, sizeof(buffer), src_file)) > 0) {
        fwrite(buffer, 1, bytes_read, dst_file);
    }
    fclose(src_file);
    fclose(dst_file);
}

/**
 * 传参的格式样子
 *  const char *ftp_url = "ftp://ftp.example.com/path/to/upload/file.txt";
 *  const char *userpwd = "username:password";
 *  const char *local_file = "local_file.txt";
 * 
 * 
*/

int ftp_upload_file(const char *ftp_url,const char *userpwd,const char *local_file)
{
    int ret = 0;
    CURL *curl;
    CURLcode res;
    unsigned long fsize;
    struct stat file_info;
    if(stat(local_file, &file_info)) {
        printf("Couldn't open '%s': %s\n", local_file, strerror(errno));
        return 1;
    }
    fsize = (unsigned long)file_info.st_size;
    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();
    if(curl) {
        ems_syslog(LOG_INFO, "ftp_url:%s",ftp_url);
        ems_syslog(LOG_INFO, "userpwd:%s",userpwd);
        ems_syslog(LOG_INFO, "local_file:%s",local_file);
        curl_easy_setopt(curl, CURLOPT_URL, ftp_url);
        curl_easy_setopt(curl, CURLOPT_USERPWD, userpwd);
        curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
        curl_easy_setopt(curl, CURLOPT_READDATA, fopen(local_file, "rb"));
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
        curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fsize);
        res = curl_easy_perform(curl);
        if(res != CURLE_OK) {
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
            ret = -1;
        }
        curl_easy_cleanup(curl);
    }
    curl_global_cleanup();
    return ret;
}

int http_post_upload_file(const char* http_url, const char* url_file_name, const char* file, const char* username, const char* userpwd)
{
    CURL*    curl;
    CURLcode res;

    // 初始化libcurl
    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl    = curl_easy_init();
    int ret = -1;
    if (curl)
    {
        curl_easy_setopt(curl, CURLOPT_URL, http_url);
        curl_easy_setopt(curl, CURLOPT_POST, 1L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
        
        // 如果需要用户名和密码认证
        if (username && userpwd)
        {
            char userpass[256];
            snprintf(userpass, sizeof(userpass), "%s:%s", username, userpwd);
            curl_easy_setopt(curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_ANY);
            curl_easy_setopt(curl, CURLOPT_USERPWD, userpass);
        }

        struct curl_httppost* formpost = NULL;
        
        struct curl_httppost* lastptr  = NULL;

        curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "file", CURLFORM_FILE, file, CURLFORM_FILENAME, url_file_name, CURLFORM_END);
        curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);

        res = curl_easy_perform(curl);

        if (res == CURLE_OK)
        {
            ret = 0;
        }
        else
        {
            ems_syslog(LOG_ERR, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        }

        curl_easy_cleanup(curl);
        curl_formfree(formpost);
    }
    curl_global_cleanup();
    return ret;
}

int is_mounted(const char *path) { //返回0 挂载了外置硬盘 
    int ret = -1;
    FILE *fp = fopen("/etc/mtab", "r");  
    if (fp == NULL) {  
        perror("fopen");  
        return -1;  
    }  
    char line[1024];    
    while (fgets(line, sizeof(line), fp)) {  
        if (strstr(line,path)&&strstr(line,"/dev/"))
        {
            ret = 0;
            break;
        }
    }  
    fclose(fp);  
    return ret;
}  

#endif

uint64_t sysuptime(void)
{
    int ret;
    uint64_t ti;
    struct timespec tspec;

    ret = clock_gettime(CLOCK_MONOTONIC, &tspec);
    if (ret < 0) {
        proto_syslog(LOG_ERR, "Error, cannot get monotonic time\n");
        return 0;
    }

    ti = (uint64_t) tspec.tv_sec;
    ti = ti * 1000 + (uint64_t) (tspec.tv_nsec / 1000000);
    return ti;
}

void frame_wgap_delay(uint64_t lastread, int32_t gapms)
{
    uint64_t now;

    if (gapms <= 0)
        return;

    for (;;) {
        struct timespec tspec;

        now = sysuptime();
        if ((now - lastread) >= (uint64_t) gapms)
            break;
        now = lastread + (uint64_t) gapms - now;

        tspec.tv_sec = (time_t) (now / 1000);
        tspec.tv_nsec = (long) ((now % 1000) * 1000000);
        nanosleep(&tspec, NULL);
    }
}


void str_to_lower_case(char *str)
{
    char *p = str;
    while(*p != '\0')
    {
        if (*p >= 'A' && *p <= 'Z')
        {
            *p = *p + 32;
        }
        p++;
    }
}

#if defined(EN_OPCUA) || defined(EN_LC)
UA_ByteString loadFile(const char *const path) {
    UA_ByteString fileContents = UA_BYTESTRING_NULL;

    /* Open the file */
    FILE *fp = fopen(path, "rb");
    if(!fp) {
        errno = 0; /* We read errno also from the tcp layer... */
        return fileContents;
    }

    /* Get the file length, allocate the data and read */
    fseek(fp, 0, SEEK_END);
    fileContents.length = (size_t)ftell(fp);
    fileContents.data = (UA_Byte *)UA_malloc(fileContents.length * sizeof(UA_Byte));
    if(fileContents.data) {
        fseek(fp, 0, SEEK_SET);
        size_t read = fread(fileContents.data, sizeof(UA_Byte), fileContents.length, fp);
        if(read != fileContents.length)
            UA_ByteString_clear(&fileContents);
    } else {
        fileContents.length = 0;
    }
    fclose(fp);

    return fileContents;
}
#endif

bool is_valid_ipv4_str(const char *ip_str)
{
    struct in_addr addr;  // 存储 IPv4 地址的结构体 
    if (ip_str == NULL || ip_str[0] == '\0') {
        return false;
    }
    
    if (inet_pton(AF_INET, ip_str, &addr) == 1) {
        return true;
    } 
    return false;
}

/*
    增加对异常场景的适配处理，始终能获取到一个合法的IP地址，
    当cJOSN中的IP地址无效时，函数会返回0.0.0.0的默认IP地址    
*/
const char *get_cJSON_valid_ip_str(cJSON *ip)
{
    // 建议将默认值定义为 static const，虽然编译器通常会优化，但显式声明更好
    static const char def_ipstr[] = "0.0.0.0";
    
    // 1. 先检查指针有效性
    // 2. 再检查字符串内容有效性 (非空且长度大于0)
    if (ip != NULL && ip->valuestring != NULL && strlen(ip->valuestring) > 0) {
        
        if (is_valid_ipv4_str(ip->valuestring)) {
            return ip->valuestring;
        }
        
        // 安全打印：此时已知 valuestring 不为 NULL
        ems_syslog(LOG_ERR, "the cJSON ip str is invalid: %s", ip->valuestring);
    } else {        
        ems_syslog(LOG_ERR, "the cJSON ip addr is invalid (NULL or empty)");
    }
    
    return def_ipstr;    
}


int port_config_update_wan(const char * ip, const char * gwip, const char * netmask)
{
    int ret;
    char * pcfg, * newcfg;
    cJSON * root, * wancfg;
    const char * config = DEV_PORT_CFG_FILE;

    ret = 0;
    root = NULL;
    newcfg = NULL;
    wancfg = NULL;
    pcfg = read_file_data(config);
    if (pcfg == NULL) {
        ems_syslog(LOG_ERR, "failed to read config: %s", config);
        return -1;
    }

    root = cJSON_Parse(pcfg);
    if (root == NULL ||!cJSON_IsObject(root)) {
        ret = -1;
        ems_syslog(LOG_ERR, "failed to parse config: %s", config);
        goto err0;
    }

    wancfg = cJSON_GetObjectItem(root, "dev_cfg");
    if (wancfg == NULL || !cJSON_IsObject(wancfg)) {
        ret = -1;
        ems_syslog(LOG_ERR, "failed to find dev_cfg");
        goto err0;
    }

    wancfg = cJSON_GetObjectItem(wancfg, "wan");
    if (wancfg == NULL || !cJSON_IsObject(wancfg)) {
        ret = -1;
        ems_syslog(LOG_ERR, "failed to find wan");
        goto err0;
    }

    wancfg = cJSON_GetObjectItem(wancfg, "attr");
    if (wancfg == NULL || !cJSON_IsArray(wancfg)) {
        ret = -1;
        ems_syslog(LOG_ERR, "failed to find attr");
        goto err0;
    }

    wancfg = cJSON_GetArrayItem(wancfg, 0);
    if (wancfg == NULL) {
        ret = -1;
        ems_syslog(LOG_ERR, "failed to find first WAN config");
        goto err0;
    }

    for (ret = 0; ret < 2; ++ret) {
        cJSON_DeleteItemFromObject(wancfg, "type");
        cJSON_DeleteItemFromObject(wancfg, "ip");
        cJSON_DeleteItemFromObject(wancfg, "mask");
        cJSON_DeleteItemFromObject(wancfg, "gateway");
    }

    ret = 0;
    
    if (is_valid_ipv4_str(ip) && is_valid_ipv4_str(gwip)) {
        const char *mask = "255.255.255.0";
        if (is_valid_ipv4_str(netmask))
            mask = netmask;
        cJSON_AddStringToObject(wancfg, "ip", ip);
        cJSON_AddStringToObject(wancfg, "gateway", gwip);
        cJSON_AddStringToObject(wancfg, "mask", mask);
        cJSON_AddStringToObject(wancfg, "type", "static");
    } else {
        cJSON_AddStringToObject(wancfg, "type", "dhcp");
    }

    newcfg = cJSON_Print(root);
    if (newcfg && strcmp(newcfg, pcfg)) {
        if (ip && gwip) {
            ems_syslog(LOG_ERR, "Setting WAN static ipaddr: %s, gateway: %s", ip, gwip);
        } else {
            ems_syslog(LOG_ERR, "Setting WAN DHCP");
        }
        write_file_data(config, newcfg, strlen(newcfg));
        ret = 1; // new WAN ip-address updated
    }

err0:
    if (newcfg != NULL)
        free(newcfg);
    if (pcfg != NULL)
        free(pcfg);
    if (root != NULL)
        cJSON_Delete(root);
    return ret;
}


void check_load_template_config(char * pfile,
    unsigned int maxlen, const char * userdef)
{
    int docmd;
    char * pdot;
    char * newfile;
    MD5_CTX ctx;
    struct timespec ts;
    unsigned char md5[MD5_DIGEST_LENGTH];

    docmd = 3;
    newfile = NULL;
    if (pfile == NULL || pfile[0] == '\0')
        return;

    pdot = strrchr(pfile, '.');
    if (pdot == NULL || strcmp(pdot, ".lua") != 0)
        return;

    MD5_Init(&ctx);
    if (userdef != NULL) {
        size_t len = strlen(userdef);
        if (len > 0)
            MD5_Update(&ctx, userdef, len);
    }

    MD5_Final(md5, &ctx);
    newfile = (char *) malloc(512);
    if (newfile == NULL) {
        proto_syslog(LOG_ERR, "Error, system out of memory!");
        return;
    }

    *pdot = '\0';
    snprintf(newfile, 512, "%s-LUAGEN-%02x%02x%02x%02x%02x%02x.json",
        pfile, (unsigned int) md5[0], (unsigned int) md5[1],
        (unsigned int) md5[2], (unsigned int) md5[3],
        (unsigned int) md5[4], (unsigned int) md5[5]);
    *pdot = '.'; // restore original file name

retry:
    if (access(newfile, F_OK) == 0) {
        strncpy(pfile, newfile, (size_t) maxlen);
        free(newfile);
        return;
    }

    if (docmd <= 0) {
        proto_syslog(LOG_ERR, "Error, failed to generate configure for %s", pfile);
        free(newfile);
        return;
    }

    docmd--;
    if (docmd == 2) {
        char * cmdstr;
        cmdstr = (char *) malloc(1024);
        if (cmdstr == NULL) {
            free(newfile);
            proto_syslog(LOG_ERR, "Error, system out of memory!");
            return;
        }

        if (userdef != NULL && userdef[0] != '\0')
            snprintf(cmdstr, 1024, "lua %s '%s'", pfile, userdef);
        else
            snprintf(cmdstr, 1024, "lua %s", pfile);

        proto_syslog(LOG_INFO, "CMDSVR: execute script %s...", pfile);
        cmd_call(cmdstr, -1, NULL, 0);
        free(cmdstr);
    }

    ts.tv_sec = 1;
    ts.tv_nsec = 0;
	nanosleep(&ts, NULL);
	goto retry;
}

// 创建缓冲区
StringBuffer *StringBuffer_Create(int size)
{
    StringBuffer *tmp = (StringBuffer *)calloc(1, sizeof(StringBuffer));
    if (tmp == NULL) {
        ems_syslog(LOG_ERR, "StringBuffer_Create:calloc StringBuffer error!!");
        return NULL;
    }

    tmp->buffer_str = calloc(1, size);
    if (NULL == tmp->buffer_str)
    {
        free(tmp);
        ems_syslog(LOG_ERR, "StringBuffer_Create:calloc buffer_str error!!");
        return NULL;
    }
    tmp->size = size;
    return tmp;
}
// 释放缓冲区
void StringBuffer_Free(StringBuffer *buffer)
{
    free(buffer->buffer_str);
    free(buffer);
}

/*
 * 缓冲区拼入字符,若缓冲区不足,将指数增加空间
 * 可变参数 用于适配 vsnprintf
 * 调用规则 StringBuffer_Append(buffer, "{\"%s\":%d}", "test", 123); or StringBuffer_Append(buffer, "abcdef 114514\n");
 *
 */

int StringBuffer_Append(StringBuffer *buffer, const char *format, ...)
{
    va_list args;
    va_start(args, format);

    // 首先尝试格式化字符串来计算所需空间大小
    int required_length = vsnprintf(NULL, 0, format, args);
    va_end(args); // 重置va_list以便再次使用

    // 检查当前缓冲区是否足够大
    while (strlen(buffer->buffer_str) + required_length >= buffer->size)
    {
        char *new_buffer = realloc(buffer->buffer_str, buffer->size * 2);
        if (!new_buffer)
        {
            ems_syslog(LOG_ERR, "StringBuffer_Append:calloc StringBuffer error!!");
            return -1;
        }
        buffer->buffer_str = new_buffer;
        buffer->size *= 2;

        // 新空间清 0 放置字符串访问出界
        memset(buffer->buffer_str + strlen(buffer->buffer_str), 0, buffer->size - strlen(buffer->buffer_str)); // 清空新增加的部分
    }

    // 重新初始化va_list并格式化字符串到输出缓冲
    va_start(args, format); // vsnprintf写入数据
    return vsnprintf(buffer->buffer_str + strlen(buffer->buffer_str), buffer->size - strlen(buffer->buffer_str), format, args);
}

// 初始化IO队列结构
// 应该在执行其他操作之前被调用
//
// item_size表示元素项的大小(以字节为单位，sizeof()
// 存储在队列中
IoQueueResult io_queue_init(IoQueue *io_queue, size_t item_size)
{
    atomic_init(&io_queue->head, (uintptr_t)NULL);
    atomic_init(&io_queue->tail, (uintptr_t)NULL);
    io_queue->item_size = item_size;
    return IO_QUEUE_RESULT_SUCCESS;
}

// if there is not
// 检查队列中是否有数据
// 这是一个consumer操作
//
// 如果有front，则返回IO_QUEUE_RESULT_TRUE
// 如果没有 IO_QUEUE_RESULT_TRUE
IoQueueResult io_queue_has_front(IoQueue *io_queue)
{
    if (atomic_load(&io_queue->head) == 0)
    {
        return IO_QUEUE_RESULT_FALSE;
    }
    return IO_QUEUE_RESULT_TRUE;
}

// 获取队列最前面的值
// 这是一个consumer操作
// 存储的值将被复制到data参数所指向的值
IoQueueResult io_queue_front(IoQueue *io_queue, void *data)
{
    IoQueueNode *head = (IoQueueNode *)atomic_load(&io_queue->head);
    memcpy(data, (void *)(head + 1), io_queue->item_size);
    return IO_QUEUE_RESULT_SUCCESS;
}

// 移除队列最前面的元素
// 这是一个consumer操作
IoQueueResult io_queue_pop(IoQueue *io_queue)
{
    assert(io_queue);
    assert(io_queue_has_front(io_queue) == IO_QUEUE_RESULT_TRUE);
    // get the head
    IoQueueNode *popped = (IoQueueNode *)atomic_load(&io_queue->head);
    IoQueueNode *compare = popped;
    // set the tail and head to nothing if they are the same
    if (atomic_compare_exchange_strong(&io_queue->tail, &compare, 0))
    {
        compare = popped;
        // its possible for another thread to have pushed after we swap out the
        // tail, in this case the head will be different then what was popped,
        // so we just do a blind exchange, not caring about the result
        atomic_compare_exchange_strong(&io_queue->head, &compare, 0);
    }
    else
    // tail is different from head, set the head to the next value
    {
        IoQueueNode *new_head = 0;
        while (!new_head)
        {
            // its possible that the next node hasn't been assigned yet, so just
            // spin until the pushing thread stores the value
            new_head = (IoQueueNode *)atomic_load(&popped->next);
        }
        atomic_store(&io_queue->head, (uintptr_t)new_head);
    }
    atomic_fetch_sub(&io_queue->size, 1);
    // delete the popped node
    free(popped);
    return IO_QUEUE_RESULT_SUCCESS;
}

// 将一个元素添加到队列的尾部
// 这是一个生产者操作
// 存储的值将从数据指向的值复制参数
// 如果堆耗尽，可能会以IO_QUEUE_RESULT_OUT_OF_MEMORY失败
IoQueueResult io_queue_push(IoQueue *io_queue, void *data)
{
    // create the new tail
    IoQueueNode *new_tail = malloc(
        sizeof(IoQueueNode) + io_queue->item_size);
    if (!new_tail)
    {
        return IO_QUEUE_RESULT_OUT_OF_MEMORY;
    }
    atomic_init(&new_tail->next, 0);
    memcpy(new_tail + 1, data, io_queue->item_size);
    // swap the new tail with the old
    IoQueueNode *old_tail = (IoQueueNode *)atomic_exchange(
        &io_queue->tail,
        (uintptr_t)new_tail);
    // link the old tail to the new
    if (old_tail)
    {
        atomic_store(&old_tail->next, (uintptr_t)new_tail);
    }
    else
    {
        atomic_store(&io_queue->head, (uintptr_t)new_tail);
    }
    atomic_fetch_add(&io_queue->size, 1);
    return IO_QUEUE_RESULT_SUCCESS;
}

// 清空整个队列
// 这是一个consumer操作
// 把它看作是类似于队列的析构函数，尽管是队列
// 清除后仍然可用，但你应该在清除之前清除它
// 删除IoQueue结构本身
IoQueueResult io_queue_clear(IoQueue *io_queue)
{
    // pop everything
    while (io_queue_has_front(io_queue) == IO_QUEUE_RESULT_TRUE)
    {
        io_queue_pop(io_queue);
    }
    return IO_QUEUE_RESULT_SUCCESS;
}

//没有strlcpy函数，作为strlcpy函数的替代
size_t local_strlcpy(char *dest, const char *src, size_t dest_size)
{
    if ((dest_size == 0) || (src == NULL) || (dest == NULL))
    {
        return 0;
    }

    size_t len = strlen(src);
    if (len < dest_size)
    {
        memcpy(dest, src, len + 1);
    }
    else
    {
        memcpy(dest, src, dest_size - 1);
        dest[dest_size - 1] = '\0';
    }

    return len;
}

int write_file_data_safe(const char* file_path, const char* data, int data_len)
{
    int fd = open(file_path, O_WRONLY | O_CREAT, 0666);
    int ret = -1;
    if (fd < 0)
    {
        char tmp[256];
        snprintf(tmp, sizeof(tmp), "%s", file_path);
        for (char* p = strchr(tmp + 1, '/'); p; p = strchr(p + 1, '/'))
        {
            *p = '\0';
            mkdir(tmp, 0777);
            *p = '/';
        }
        fd = open(file_path, O_WRONLY | O_CREAT, 0666);
    }
    if (fd > 0)
    {
        flock(fd, LOCK_EX); // 获取排他锁
        ret = write(fd, data, data_len);
        flock(fd, LOCK_UN); // 释放锁
        close(fd);
    }
    return ret;
}

int http_post_download_file(const char* url, const char* save_path, const char* file_name, download_callback_t callback)
{
    CURL*    curl = NULL;
    FILE*    fp   = NULL;
    CURLcode res;
    int      ret = -1;
    char     filepath[256];
    snprintf(filepath, sizeof(filepath), "%s/%s", save_path, file_name);

    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();
    if (!curl)
    {
        ems_syslog(LOG_ERR, "curl_easy_init failed");
        goto END;
    }

    fp = fopen(filepath, "wb");
    if (!fp)
    {
        ems_syslog(LOG_ERR, "fopen file failed:%s\n", filepath);
        goto END;
    }
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
    // 打开调试信息
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

    if (callback)
    {
        // 不直接调用，为了使用旧方法不告警
        CURLcode (*to_no_warning)(CURL *, CURLoption, ...) = curl_easy_setopt;
        to_no_warning(curl, CURLOPT_PROGRESSFUNCTION, callback);
    }

    res = curl_easy_perform(curl);

    if(res == CURLE_OK)
    {
        long http_code = -1;
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
        ret = (http_code == 200) ? 0 : http_code;
    }
    else
    {
        ret = res;
        ems_syslog(LOG_ERR, "download file failed:%s, %d", curl_easy_strerror(res), res);
    }
    if (ret != 0)
    {
        ems_syslog(LOG_ERR, "download file failed:%s", curl_easy_strerror(res));
    }

END:
    fflush(fp);
    if (fp) fclose(fp);
    if (curl) curl_easy_cleanup(curl);
    curl_global_cleanup();
    return ret;
}
