/**
 * @file      rs485_comm.h
 * @brief     description about rs485
 * @details  e.g. 485 port config, period polling
 * @author   wangleilei@zjdyiot.com
 * @date     2019.06.20
 * @version  A001
 * @par Copyright (c):
 *       DYIOT
 * @par History:
 *   version: draft\n
 */
#ifndef __RS485_COMMON_H
#define __RS485_COMMON_H

/***************HEADER FILES****************/
#include <string.h>
#include <syslog.h>
#include <termios.h>
#include "dy_utils/dy_modbus_sniffer_session.h"
#include "dy_utils/dy_common.h"
#include "dy_utils/dy_ds.h"
#include "dy_utils/kv_array.h"
#include "dy_utils/dy_ipc.h"
#include "modbus/modbus.h"
#include "ipc_session.h"
#include "lnxall_list.h"
#include "lnxall_ubuslog.h"
/******************************************/

/******************MACRO********************/
#define BAUD_RATE 9600
#define TTYMXC_CNT 128
#define TIMEOUT_CNT_MAX 3
#define RS485_NODE_STATUS_BAK_FILE "/app/485_status.bak"
/******************************************/

/*************ENUM*************************/
typedef enum
{
    RS485_DATA_TYPE_CMD,        //实时命令
    RS485_DATA_TYPE_PERIOD_CMD, //周期命令
    RS485_DATA_TYPE_CMD_CNT,
} rs485_data_type_e;
/******************************************/

/*******************STRUCTURE**************/
/**
 *  rs485 per port config descriptor
 */
typedef struct _rs485_port_cfg_t
{
    gw_port_e port;           ///< 0, 1, 2, 3
    int speed;                ///< 9600, 115200
    int stop;                 ///< 1 ~ 2
    int parity;               ///< 0, 1, 2
    int bits;                 ///< 7, 8
    int protocol;             ///< 3-GW_PROTC_MODBUS, 2-DYWL_RS485_V1
    int sleep_before_read;    /// sleep time before read
    int remote_port;          /// remote RS485/modbus TCP server port-number
    char remote_ipaddr[20];   /// remote RS485/modbus TCP server ipv4-address
    char sub_template_id[16]; ///<下挂设备协议模板
    char bridging_port[16];   ///桥接端口
    bool disable;
    bool sniffer_mode;
    char msg_head[8]; ///<帧头平台下发的字符串格式,我们会将格式转为HEX,例如:帧头是0x31 0x45 开头,需要配置"3145" ,不要加0x 中间不能有空格或其他分隔符
    int msg_head_len;
    char msg_tail[8]; ///<帧尾
    int msg_tail_len;
    int frame_timeout; ///<帧超时时间,如果小于此时间,被认定为一帧,单位ms
    char *frame_header;
    int frame_header_len;
    char *frame_tail;
    char frame_tail_len;
} rs485_port_cfg_t;

static inline int rs485_port_is_remote(const rs485_port_cfg_t * pcfg)
{
    int rport = pcfg->remote_port;
    return (rport > 0) && (rport < 65536) &&
        (pcfg->remote_ipaddr[0] != '\0');
}

/**
 *  rs485 ports config with mi descriptor
 */
typedef struct _rs485_ports_cfg_t
{
    int mi;                                      ///< message cookie
    int port_num;                                ///< number of `rs485_port_cfg
    rs485_port_cfg_t * rs485_port_cfg;           ///< ports config
} rs485_ports_cfg_t;

typedef struct
{
    struct list_head list;
    char sn[SN_MAX_LEN];
    node_action_e action;
} status_list_t;

typedef struct _rs485_var_t
{
    rs485_ports_cfg_t rs485_ports_cfg;
    ipc_session_t *session; //内部broker的MQTT client，用于进程间通信

    nodes_cfg_table_t *nodes_cfg_table; //节点信息
    template_table_t *template_table;   //模板信息
    const char *board_name;
    char sn_str[SN_MAX_LEN];
    char proc_name[PROGRAM_NAME_LEN];
    dev_status_table_t *ds; //下挂节点的状态信息
    pthread_mutex_t status_lock;
    struct list_head status_list;
    time_t nodes_status_sync_date;
    bool cpu_busy;
    int node_status_timer;
    char product_name[PRODUCT_NAME_LEN];
} rs485_var_t;

typedef struct
{
    struct list_head list;
    pp_regulate_signal_t rs;
    long long last_send;
} rs485_data_list_t;

typedef struct _rs485_thread_t
{
    int fd;
    unsigned char start_flag;
    unsigned char period_flag;

    struct list_head data_list[RS485_DATA_TYPE_CMD_CNT];
    rs485_data_list_t *send_data;
    rs485_port_cfg_t *rs485_ports_cfg;
    int rs485_port;
    pp_regulate_signal_t cmd_info;
    gw_protocol_e protocol;
    unsigned char modbus_addr;

    int send_timer;
    int period_timer;

    ipc_session_t *session; //内部broker的MQTT client，用于进程间通信
    kv_array_t identifier_backup;
    bool period_triggered;
    bool period_triggered_cnt;
    bool need_send;

    pthread_mutex_t send_lock;
    pthread_cond_t send_cond;
    int self_check_timer;
    int timeout_cnt;
    unsigned int last_reset;
    bool all_zero;
    modbus_sniffer_session_t modbus_sniffer;
    rs485_var_t *var;
} rs485_thread_t;

typedef struct
{
    rs485_var_t *var;
    rs485_data_list_t *data;
} rs485_var_data_t;

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

/******************FUNCTION****************/
/**
 * This is a rs485 port config handler
 * @param[in] recv_msg msg from commomunication module
 * @param[in] var rs485 descriptor
 * @retval  None
 */
void rs485_load_config(rs485_var_t *var);
void rs485_load_special_config(rs485_var_t *var);
/******************************************/

int rs485_port_remap(int port);
int rs485_port_map(int rs485_port);
const char *get_tty_from_port(gw_port_e port);
int set_gpio_for_port(const char *product_name, gw_port_e port);

extern int rs485_start_thread(rs485_var_t *var, rs485_port_cfg_t *rs485_port_cfg, int rs485_port);

#endif /* __RS485_COMMON_H */
