#ifndef __TOOL_H
#define __TOOL_H
#define CRITICAL_T	pthread_mutex_t
#define CRITICAL_INIT(x)	do { \
								pthread_mutexattr_t attr; \
								pthread_mutexattr_init(&attr); \
								pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
								pthread_mutex_init(x, &attr); \
							} while (0)
#define CRITICAL_LOCK(x)	pthread_mutex_lock(x)
#define CRITICAL_UNLOCK(x)	pthread_mutex_unlock(x)
#define CRITICAL_DESTORY(x) pthread_mutex_destroy(x)

#define enum_string(x) #x

void printf_mem(char* mem, int len);

void mq_resource();

int check_timeout(struct timespec *prev, int timeout_ms);
int check_timeout_second(time_t now, time_t *prev, int timeout_s);

int calculate_crc(uint8_t *req, int nb);
uint16_t crc16(uint8_t *buffer, uint16_t buffer_length);
int modbus_data_check_crc(uint8_t *msg,        int msg_length);

char* GetFileName(char* path);
/* 正常情况下不应去使用，因为会存在溢出情况 */
long long clock_get_ms();

unsigned int clock_get_second();
int clock_diff_ms(struct timespec* now, struct timespec* prev);
void busy_wait(unsigned int us);

/*
 * 初始化 pthread 线程库提供的条件变量。
 * 当使用 pthread_cond_timedwait 等待条件变量，传入的时间
 * 应当为系统启动时间，即通过 clock_gettime(CLOCK_MONOTONIC, ...) 获取的时间，
 * 该时间不会因实时时间的修改而产生跳变
 * 注意，函数的参数实际类型为 pthread_cond_t *
 */
int lnxall_condvar_init(void *);

/*
 * 等待条件变量，inval为等待的超时时间，单位为千分之一秒。
 * 注意，condp应使用 lnxall_condvar_init 初始化。
 * 该函数的返回值为 phtread_cond_timedwait的返回值。
 */
int lnxall_condvar_timedwait(void * condp, void * mutexp, unsigned int inval);

/*
 * 睡眠指定的时间，单位为千分之一秒。
 * 当`noint为非零时，即使睡眠被信号中断，也会继续睡眠。
 * 返回值：返回睡眠被中断的次数
 */
int lnxall_msleep(unsigned int msec, int noint);

int my_timer_create();
int my_timer_delete(int timer);
/*
start_sec秒后首次触发，间隔为interval_ms毫秒，如果interval_ms == -1 则只触发一次
*/
int my_timer_set(int timer, int start_sec, int interval_ms);
/*
start_msec秒后首次触发，间隔为interval_ms毫秒，如果interval_ms == -1 则只触发一次
*/
int my_timer_set_ms(int timer, int start_msec, int interval_ms);

/*
参考系统时钟，start_sec秒后触发，且只触发一次
*/
int my_timer_set2(int timer, int start_sec);

int object_datatype_char2enum(char *c);
/*
定时器触发后的处理
*/
int my_timer_read(int timer);

int file_read_map(char* file_path, char** addr, int* len);

void file_read_unmap(int fd, char* addr, int len);

int file_set_hole(int fd, int len);

void alog(char *format, ...);

int tcp_connect_host(int fd, unsigned char server_ip[4], unsigned short server_port);
int tcp_create_fd();

//基本等同于write，但当发生EINTR或EAGAIN时，等待10毫秒后重试，重试最多10次
int nwrite(int fd, void* data, int len);

//基本等同于read，但当发生EINTR或EAGAIN时，等待10毫秒后重试，重试最多10次
int nread(int fd, void* data, int len);

int net_set_mac(char *ifname, unsigned char mac[6]);
int net_set_ipv4_addr(char *ifname, unsigned char ip_addr[4] ,unsigned char mask_addr[4]);
int net_get_ipv4_addr(char *ifname, unsigned int* ip);
int get_netlink_status(char *if_name);

int get_exec_info(char* command, char *res, int buf_len);

void check_make_dir(char * dir);

void get_ip(unsigned char ip[4], const char* ip_string);

//创建bitmask，包含元素数量为index_cnt个，index_cnt为8的倍数
void* bm_open(int index_cnt);
void bm_close(void* bm_handle);
int bm_set(void* bm_handle, int index);
int bm_clear(void* bm_handle, int index);
int bm_read(void* bm_handle, int* index);

/**
 *
 * 执行system并将执行结果返回到rsp_command
 * @param[command]   需要执行的shell命令
 * @param[rsp_command]  返回执行命令的指针，如果return = 0,rsp_command 必须由调用方释放
 * @retval  0  成功
 * @retval  -1   错误
 * @par 标识符
 *      保留
 * @par 其它
 *      无
 * @par 修改日志
 *      张国宏于2019-06-12创建
 */
int system_reply(char* command, char **rsp_command);

/**
 * 转化协议port项中字符类型为内部枚举，
 *
 * @param[c]   input char string.
 * @retval  该字符匹配成功返回：RS485_1/RS485_2/RS485_3/RS485_4/LORA_1;否则返回0
 * @par 标识符
 *      保留
 * @par 其它
 *      全词匹配，不区分大小写
 * @par 修改日志
 *      张国宏于2019-06-20创建
 */
int port_char2enum(char *c);

/**
 * 转化SOCKET协议类型项中字符类型为内部枚举，
 *
 * @param[c]   input char string.
 * @retval  该字符匹配成功返回：TCP/UDP;否则返回0
 * @par 标识符
 *      保留
 * @par 其它
 *      全词匹配，不区分大小写
 * @par 修改日志
 *      龙文件于2020-04-01创建
 */
int socket_type_char2enum(char *c);

/**
 * 转化协议protocol项中字符类型为内部枚举，
 *
 * @param[c]   input char string.
 * @retval  该字符匹配成功返回：GW_PROTC_NULL/GW_PROTC_DYWL_LORA_V1/GW_PROTC_DYWL_RS485_V1/GW_PROTC_MODBUS;否则返回0
 * @par 标识符
 *      保留
 * @par 其它
 *      全词匹配,不区分大小写
 * @par 修改日志
 *      张国宏于2019-06-20创建
 */
int protocol_char2enum(char *c);

const char* port_enum2char(int i);
/**
 * 转化协议date_type项中字符类型为内部枚举，
 *
 * @param[c]   input char string.
 * @retval  该字符匹配成功返回：TAG_TYPE_NULL/TAG_TYPE_INT16/TAG_TYPE_UINT16/......;否则返回0
 * @par 标识符
 *      保留
 * @par 其它
 *      全词匹配,不区分大小写
 * @par 修改日志
 *      张国宏于2019-06-20创建
 */
int datatype_char2enum(char *c);

int device_type_char2enum(char *c);
/**
 * 下载远程http文件
 *
 * @param[path]   下载到本地的路径
 * @param[remote_file] 远程文件资源的url
 * @param[timeout] 指定超时时间
 * @retval  0：成功;其他：失败
 * @par 标识符
 *      保留
 * @par 其它
 *      保留
 * @par 修改日志
 *      张国宏于2019-06-28创建
 */
int download_file(char* path,char* url,int timeout);

/*
 * Download to `path
 */
int download_file1(const char *path, char *url, int timeout);

/*
 * get current executable file name
 */
int lnxall_exename(char * out, int outlen);

/**
 * 校验文件的MD5
 *
 * @param[file]   文件的本地的路径
 * @param[r_md5sum] 用来比较的MD5
 * @retval  0：匹配成功;其他：失败
 * @par 标识符
 *      保留
 * @par 其它
 *      保留
 * @par 修改日志
 *      张国宏于2019-06-28创建
 */
int check_md5sum_file(char* file,char* r_md5sum);

/**
 * 读取json文件中mi字段作为消息流水号
 *
 * @param[str] json string
 * @retval  0：没有读取到mi;-1：读取错误
 * @par 标识符
 *      保留
 * @par 其它
 *      保留
 * @par 修改日志
 *      张国宏于2019-07-03创建
 */
int json_pkt_json_mi(const char *str);

/**
 * 读取json文件中version字段
 *
 * @param[str] json string
 * @param[version] version buffer size
 * @param[size] version buffer size
 * @retval  0：没有读取到version;-1：读取错误
 * @par 标识符
 *      保留
 * @par 其它
 *      保留
 * @par 修改日志
 *      张国宏于2019-07-03创建
 */
int json_pkt_json_version(const char *str,char *version,int size);

/**
 * 从json串里读取version字段，并通知变更
 *
 * @param[ver_key] 版本名称的枚举
 * @param[str] json string
 * @retval
 * @par 标识符
 *      保留
 * @par 其它
 *      保留
 * @par 修改日志
 *      张国宏于2019-07-03创建
 */
void version_noify(int  ver_key,char *str);

/**
 * 读取json文件中URL字段
 *
 * @param[str] json string
 * @param[url_v] version buffer size
 * @retval  0：没有读取到version;-1：读取错误
 * @par 标识符
 *      保留
 * @par 其它
 *      保留
 * @par 修改日志
 *      张国宏于2019-08-09创建
 */
int json_pkt_json_url(const char *str,char *url_v);


/**
 * 为任意json字段添加mi,若字段中有mi，则会被覆盖
 *
 * @param[in] json string
 * @param[mi] json string
 * @retval  NULL：str指针内容非json结构;其他返回json字符指针，需要调用方free
 * @par 标识符
 *      保留
 * @par 其它
 *      保留
 * @par 修改日志
 *      张国宏于2019-09-03创建
 */
char* json_pkt_add_mi2str(char *in,int mi);

char * cJSON_GetItemString(const void * const object, const char * const string);

/**
 * bytes to string
 *
 * @param[uint] bytes input point
 * @param[str] string output
 * @param[size] bytes input size
 * @retval  void
 * @par 标识符
 *      保留
 * @par 其它
 *      保留
 * @par 修改日志
 *      张国宏于2019-09-30创建
 */
void i82hexstr(unsigned char* uint, char* str, int size) ;

/**
 * bytes to string
 *
 * @param[dest] bytes output point
 * @param[src] string input
 * @param[len] bytes converlenght
 * @retval  void
 * @par 标识符
 *      保留
 * @par 其它
 *      保留
 * @par 修改日志
 *      张国宏于2019-09-30创建
 */
void str2hex(unsigned char* dest, char* src, int len);

int check_timeout_msecond(long long now, long long *prev, int timeout_ms);

#endif /* __TOOL_H */
