#include "lip_opcua_server_cfg_defs.h"
#if LIP_OPCUA_SERVER_ENABLE
#include "lip_opcua_server_con.h"
#include "lip_opcua_server.h"
#include "lip_opcua_sc.h"
#include "lip/misc/lip_misc_defs.h"
#include "lip/app/opcua/types/lip_opcua_nodeid.h"
#include "lip/app/opcua/lip_opcua_std_nodeids.h"
#include "lip/app/opcua/binary/lip_opcua_binary_types_procbuf.h"
#include "lip/app/opcua/uacp/lip_opcua_uacp_proto_defs.h"
#include "lip/app/opcua/server/lip_opcua_sc_msgproc.h"
#include "lip/app/opcua/server/lip_opcua_sc_private.h"
#include "lip/app/opcua/server/lip_opcua_server_session.h"
#include "lip/app/opcua/server/req/lip_opcua_server_req.h"
#include "lip/app/opcua/server/req/lip_opcua_server_reqproc.h"
#include "lip/app/opcua/server/lip_opcua_server_con.h"
#include "lip/transport/tcp/lip_tcp.h"
#include "ltimer.h"

typedef struct {
    uint8_t hdr_valid;
    uint8_t data_valid;
    uint32_t msg_type;
    t_lip_proc_buf data;
} t_rx_cur_msg_ctx;


typedef enum {
    LIP_OPCUA_CON_CLOSED = 0,
    LIP_OPCUA_CON_CLOSE_WAIT,
    LIP_OPCUA_CON_LISTEN,
    LIP_OPCUA_CON_WAIT_HELLO,
    LIP_OPCUA_CON_WAIT_SC,
    LIP_OPCUA_CON_SC_WORK
} t_lip_opcua_con_state;


typedef struct st_lip_opcua_con {
    t_lsock_id socket;
    uint8_t state;
    t_ltimer wt_tmr;
    uint16_t port;
    t_rx_cur_msg_ctx rx_msg;
    uint8_t tx_rdy;
    t_lip_proc_buf tx_buf;
    struct st_lip_opcua_sc_ctx *sc;
} t_lip_opcua_con;



static t_lip_opcua_con f_con_list[LIP_OPCUA_SERVER_SOCKET_CNT];

LIP_OPCUA_SERVER_BUF_MEM(static uint8_t f_rx_sock_buf[LIP_OPCUA_SERVER_SOCKET_CNT][LIP_OPCUA_SERVER_SOCKET_RXBUF_SIZE]);
LIP_OPCUA_SERVER_BUF_MEM(static uint8_t f_tx_sock_buf[LIP_OPCUA_SERVER_SOCKET_CNT][LIP_OPCUA_SERVER_SOCKET_TXBUF_SIZE]);
LIP_OPCUA_SERVER_BUF_MEM(static uint8_t f_rx_msg_buf[LIP_OPCUA_SERVER_SOCKET_CNT][LIP_OPCUA_SERVER_RXMSG_MAX_SIZE - LIP_OPCUA_MSG_HDR_SIZE]);
LIP_OPCUA_SERVER_BUF_MEM(static uint8_t f_tx_msg_buf[LIP_OPCUA_SERVER_SOCKET_CNT][LIP_OPCUA_SERVER_TXMSG_MAX_SIZE]);


/* начало прослушивания сокета на прием новых сообщений */
static void f_con_start_listen(t_lip_opcua_con *con) {
    lsock_listen(con->socket, con->port);
    con->rx_msg.hdr_valid = false;
    con->rx_msg.data_valid = false;
    con->tx_rdy = false;
    con->state = LIP_OPCUA_CON_LISTEN;
}




/* обработка приема нового сообщения */
static t_lip_opcua_err f_try_rx_msg(t_lip_opcua_con *con) {
    t_lip_opcua_err err_code = 0;
    /* Если не приняли заголовок, сперва пробуем принять его. Заголовок всегда фиксированный и содержит тип сообщения и его размер */
    if (!con->rx_msg.hdr_valid) {
        if (lsock_recv_rdy_size(con->socket) >= (int)LIP_OPCUA_MSG_HDR_SIZE) {
            t_lip_opcua_msg_hdr hdr;
            lsock_recv(con->socket, (uint8_t*)&hdr, LIP_OPCUA_MSG_HDR_SIZE);
            /* проверяем, что сообщение не больше того на которое выделен буфер */
            if (hdr.MessageSize > LIP_OPCUA_SERVER_RXMSG_MAX_SIZE) {
                err_code = LIP_OPCUA_ERR_BAD_TCP_MESSAGE_TOO_LARGE;
            } else if (hdr.MessageSize < LIP_OPCUA_MSG_HDR_SIZE) {
                err_code = LIP_OPCUA_ERR_BAD_DECODING_ERROR;
            } else {
                con->rx_msg.hdr_valid = true;
                con->rx_msg.msg_type = hdr.MessageType;

                /* инициализируем буфер для приема тела сообщения (размер из заголовка за вычетом размера самого заголовка) */
                con->rx_msg.data.cur = con->rx_msg.data.start;
                con->rx_msg.data.end = con->rx_msg.data.start + (hdr.MessageSize - LIP_OPCUA_MSG_HDR_SIZE);
                lip_printf(LIP_LOG_MODULE_OPCUA, LIP_LOG_LVL_DEBUG_LOW, 0, "lip opcua (%d): rx msg type 0x%08X, size %d!\n", hdr.MessageType, hdr.MessageSize, con->socket);
            }
        }
    }

    /* если приняли заголовок, то пробуем принять его данные */
    if (con->rx_msg.hdr_valid) {
        if (con->rx_msg.data.cur != con->rx_msg.data.end) {
            int rdy_size = lsock_recv_rdy_size(con->socket);
            int rem_size = con->rx_msg.data.end - con->rx_msg.data.cur;
            if (rdy_size > rem_size)  {
                rdy_size = rem_size;
            }

            if (rdy_size > 0) {
                lsock_recv(con->socket, con->rx_msg.data.cur, rdy_size);
                con->rx_msg.data.cur += rdy_size;
            }
        }

        /* закончили прием данных, то устанавливаем флаг и возвращаем
         * текущий указатель в начало для использования его в обработке */
        if (con->rx_msg.data.cur == con->rx_msg.data.end) {
            con->rx_msg.data.cur = con->rx_msg.data.start;
            con->rx_msg.data_valid = 1;
        }
    }
    return err_code;
}

/* инициализация буфера для сохранения сообщения на передачу */
static inline void f_tx_buf_init(t_lip_opcua_con *con) {
    con->tx_buf.cur = con->tx_buf.start + LIP_OPCUA_MSG_HDR_SIZE;
    con->tx_buf.end = con->tx_buf.start + LIP_OPCUA_SERVER_TXMSG_MAX_SIZE;
}

/* попытка послать подготовленный буфер, если он уже сформирован */
static void f_tx_buf_try_send(t_lip_opcua_con *con) {
    if (con->tx_rdy) {
        /* проверяем, что в сокете есть место для записи всего пакета */
        if (lsock_send_rdy_size(con->socket) > 0) {
            int pos;
            lsock_send_fifo_get_pos(con->socket, &pos);
            int msg_size = con->tx_buf.end -  con->tx_buf.cur;
            int sent_size = lsock_send(con->socket, con->tx_buf.cur, msg_size);

            if (sent_size > 0) {
                con->tx_buf.cur += sent_size;
                if (con->tx_buf.cur == con->tx_buf.end) {
                    con->tx_rdy = false;
                }
            }
        }
    }
}

/* Завершение формирования сообщения на отправку */
static void f_tx_buf_msg_finish(t_lip_opcua_con *con, uint32_t type) {
    t_lip_opcua_msg_hdr *hdr = (t_lip_opcua_msg_hdr *)con->tx_buf.start;
    hdr->MessageType = type;
    hdr->MessageSize = con->tx_buf.cur - con->tx_buf.start;
    con->tx_buf.end = con->tx_buf.cur;
    con->tx_buf.cur = con->tx_buf.start;
    con->tx_rdy = true;
    f_tx_buf_try_send(con);
}







/* посылка сообения об ошибке */
static bool f_send_err(t_lip_opcua_con *con, t_lip_opcua_err err_code) {
    bool done = false;
    const int err_msg_size = LIP_OPCUA_MSG_HDR_SIZE + sizeof(t_lip_opcua_uacp_msg_err_data);

    if (lsock_send_rdy_size(con->socket) >= err_msg_size) {
        struct {
            t_lip_opcua_msg_hdr hdr;
            t_lip_opcua_uacp_msg_err_data data;
        } msg;
        msg.hdr.MessageType = LIP_OPCUA_MSGTYPE_UACP_ERROR;
        msg.hdr.MessageSize = sizeof(msg);
        msg.data.Error = err_code;
        msg.data.Reason.Size = 0;
        done = lsock_send(con->socket, (const uint8_t*)&msg, err_msg_size) == err_msg_size;
    }
    return done;
}

/* Обработка сообщения Hello (согласование размеров буферов для обмена и посылка ACK) */
static t_lip_opcua_err f_proc_hello(t_lip_opcua_con *con) {
    t_lip_opcua_err err_code = lip_opcua_procbuf_get_avsize_check(&con->rx_msg.data, LIP_OPCUA_MSG_UACP_HELLO_MIN_SIZE);
    if (err_code == LIP_OPCUA_ERR_GOOD) {
        const t_lip_opcua_uacp_msg_hello_data *hello = (const t_lip_opcua_uacp_msg_hello_data *)con->rx_msg.data.cur;

        t_lip_opcua_uacp_msg_ack ack;
        ack.hdr.MessageType = LIP_OPCUA_MSGTYPE_UACP_ACK;
        ack.hdr.MessageSize = sizeof(ack);
        ack.data.ProtocolVersion = 0;

        /** @note По спецификации ReceiveBufferSize определяет размер нашего буфера на прием, а SendBufferSize -
                  на передачу. Но судя по всему часть клиентов интерпретируют это как прием и передача со своей стороны,
                  и не принимают сообщения больше, чем возвращенный размер ReceiveBufferSize. Поэтому
                  в данном случае используем макимальный размер между буфером на прием и передачу, чтобы иметь
                  возможность посылать сообщия больше ReceiveBufferSize */

        ack.data.ReceiveBufferSize = MIN(hello->SendBufferSize, MAX(LIP_OPCUA_SERVER_RXMSG_MAX_SIZE, LIP_OPCUA_SERVER_TXMSG_MAX_SIZE));
        ack.data.SendBufferSize    = MIN(hello->ReceiveBufferSize, MAX(LIP_OPCUA_SERVER_RXMSG_MAX_SIZE, LIP_OPCUA_SERVER_TXMSG_MAX_SIZE));

        ack.data.MaxMessageSize    = LIP_OPCUA_SERVER_RXMSG_MAX_SIZE;
        ack.data.MaxChunkCount     = 1; /** @todo message chunk support ! */
        lsock_send(con->socket, (const uint8_t *)&ack, sizeof(ack));
    }

    return err_code;
}


static t_lip_opcua_err f_con_sc_open(t_lip_opcua_con *con, const t_lip_opcua_sc_open_params *params, const t_lip_opcua_req_info *req, uint32_t seqnum,
                                     t_lip_opcua_sc_token **token, t_lip_opcua_string_ptr *server_nonce) {
    t_lip_opcua_err err = LIP_OPCUA_ERR_GOOD;
    if (params->securityMode != LIP_OPCUA_MSG_SECMODE_NONE) {
        err = LIP_OPCUA_ERR_BAD_SECURITY_MODE_REJECTED;
    } else {
        if (params->requestType == LIP_OPCUA_SECTOKEN_REQTYPE_ISSUE) {
            err = lip_opcua_sc_create(params, req->tstmp, seqnum, &con->sc, token, server_nonce);
        } else if (params->requestType == LIP_OPCUA_SECTOKEN_REQTYPE_RENEW) {
            err = lip_opcua_sc_renew_token(con->sc, params, token, server_nonce);
        } else {
            err = LIP_OPCUA_ERR_BAD_REQUEST_TYPE_INVALID;
        }

    }

    return err;
}



static t_lip_opcua_err f_proc_sc_open_msg(t_lip_opcua_con *con) {
    t_lip_opcua_sc_seq_hdr hdr;
    t_lip_opcua_sc_open_params sc_open_params;
    t_lip_opcua_err err =  lip_opcua_open_hdr_parse(&con->rx_msg.data, &hdr, &sc_open_params);
    if ((err == LIP_OPCUA_ERR_GOOD) && con->sc) {
        err = lip_opcua_sc_check_rx_seqnum(con->sc, sc_open_params.SecureChannelId, hdr.SequenceNumber);
    }

    if (err == LIP_OPCUA_ERR_GOOD) {
        t_lip_opcua_req_info req_info;
        err = lip_opcua_req_parse_header(&con->rx_msg.data, &req_info);
        if (err == LIP_OPCUA_ERR_GOOD) {
            err = lip_opcua_check_user_auth_token(lip_opcua_con_session(con), &req_info.auth_token);
        }

        /* проверяем, что в запросе передан SecureChannelRequest (поддерживаем только binary encoding) */
        if (err == LIP_OPCUA_ERR_GOOD) {
            if (!(lip_opcua_nodeid_check_is_stdns_int(&req_info.node) && (req_info.node.intval == LIP_OPCUA_NODEID_STD_OPEN_SECURE_CHANNEL_REQUEST_BIN))) {
                err = LIP_OPCUA_ERR_BAD_SERVICE_UNSUPPORTED;
            }
        }

        if (err == LIP_OPCUA_ERR_GOOD) {
            uint32_t proto_ver;
            err = lip_opcua_sc_open_req_parse(&con->rx_msg.data, &sc_open_params, &proto_ver);
            if (err == LIP_OPCUA_ERR_GOOD) {
                if (proto_ver != LIP_OPCUA_UACP_PROTOCOL_VERSION) {
                    /* Версия должна быть таже, что и в Hello. На текущий момент, существует только версия 0 */
                    err = LIP_OPCUA_ERR_BAD_PROTOCOL_VERSION_UNSUPPORTED;
                } else {
                    t_lip_opcua_string_ptr server_nonce;
                    t_lip_opcua_sc_token *token;
                    err = f_con_sc_open(con, &sc_open_params, &req_info, hdr.SequenceNumber,&token, &server_nonce);
                    if (err == LIP_OPCUA_ERR_GOOD) {
                        lip_opcua_next_rx_seqnum(con->sc);

                        f_tx_buf_init(con);
                        hdr.SequenceNumber = con->sc->tx_seq_num;
                        err = lip_opcua_open_hdr_put(&con->tx_buf, &hdr, &sc_open_params, token);
                        if (err == LIP_OPCUA_ERR_GOOD) {
                            static const t_lip_opcua_nodeid f_resp_node_id = LIP_OPCUA_NODEID_STDNS_INT(LIP_OPCUA_NODEID_STD_OPEN_SECURE_CHANNEL_RESPONSE_BIN);
                            err = lip_opcua_req_put_resp_header(&con->tx_buf, hdr.RequestId, &f_resp_node_id, err);
                        }
                        if (err == LIP_OPCUA_ERR_GOOD) {
                            err = lip_opcua_sc_open_resp_put(&con->tx_buf, proto_ver, token, &server_nonce);
                        }
                        if (err == LIP_OPCUA_ERR_GOOD) {
                            f_tx_buf_msg_finish(con, LIP_OPCUA_MSGTYPE_UASC_OPEN);
                            lip_opcua_sc_next_msg(con->sc);
                        }
                    }
                }
            }
        }
    }
    return err;
}

static t_lip_opcua_err f_proc_sc_close_msg(t_lip_opcua_con *con) {
    t_lip_opcua_sc_msg_info msg;
    t_lip_opcua_err err_code = lip_opcua_sc_msg_hdr_parse(&con->rx_msg.data, &msg);
    if (err_code == LIP_OPCUA_ERR_GOOD) {
        err_code = lip_opcua_sc_check_msg_params(con->sc, &msg);
    }
    if (err_code == LIP_OPCUA_ERR_GOOD) {
        t_lip_opcua_req_info req;
        err_code = lip_opcua_req_parse_header(&con->rx_msg.data, &req);
        if (err_code == LIP_OPCUA_ERR_GOOD) {
            if (!(lip_opcua_nodeid_check_is_stdns_int(&req.node) &&
                  (req.node.intval == LIP_OPCUA_NODEID_STD_CLOSE_SECURE_CHANNEL_REQUEST_BIN))) {
                err_code = LIP_OPCUA_ERR_BAD_SERVICE_UNSUPPORTED;
            } else {
                lip_opcua_con_sc_close(con);
            }
        }
    }


    return err_code;
}

static t_lip_opcua_err f_proc_req_msg(t_lip_opcua_con *con) {
    t_lip_opcua_sc_msg_info msg;
    t_lip_opcua_err err_code = lip_opcua_sc_msg_hdr_parse(&con->rx_msg.data, &msg);
    if (err_code == LIP_OPCUA_ERR_GOOD) {
        err_code = lip_opcua_sc_check_msg_params(con->sc, &msg);
    }
    if (err_code == LIP_OPCUA_ERR_GOOD) {
        t_lip_opcua_req_info req;
        err_code = lip_opcua_req_parse_header(&con->rx_msg.data, &req);

        if (err_code == LIP_OPCUA_ERR_GOOD) {
            req.resp.req_id = msg.RequestId;
            /* для открытой сессии проверяем корректность auth_token во всех запросах */
            t_lip_opcua_err proc_err = lip_opcua_check_user_auth_token(lip_opcua_con_session(con), &req.auth_token);
            if (proc_err == LIP_OPCUA_ERR_GOOD) {
                proc_err = lip_opcua_server_req_process(con, &req, &con->rx_msg.data);
            }
            if (proc_err != LIP_OPCUA_ERR_GOOD) {
                lip_opcua_server_resp_fault(con, &req.resp, proc_err);
            }
        }
    }

    return err_code;
}


void lip_opcua_con_sc_close(struct st_lip_opcua_con *con) {
    if (con->sc) {
        lip_opcua_sc_close(con->sc);
        con->sc = NULL;

    }
    if (con->state == LIP_OPCUA_CON_SC_WORK)  {
        con->state = LIP_OPCUA_CON_WAIT_SC;
    }
}









t_lip_proc_buf *lip_opcua_con_resp_prepare(struct st_lip_opcua_con *con, const t_lip_opcua_req_resp_info *req, const t_lip_opcua_nodeid *nodeid, t_lip_opcua_err status) {
    t_lip_opcua_err err =  LIP_OPCUA_ERR_GOOD;
    if (con->tx_rdy) {
        err = LIP_OPCUA_ERR_BAD_TCP_SERVER_TOO_BUSY;
    } else {
        f_tx_buf_init(con);
        err = lip_opcua_sc_msg_hdr_put(&con->tx_buf, con->sc, req->req_id);
        if (err == LIP_OPCUA_ERR_GOOD) {
            err = lip_opcua_req_put_resp_header(&con->tx_buf, req->req_handle, nodeid, status);
        }
    }
    return (err == LIP_OPCUA_ERR_GOOD) ? &con->tx_buf : NULL;
}

void lip_opcua_con_resp_finish(struct st_lip_opcua_con *con, t_lip_proc_buf *buf) {
    if (buf == &con->tx_buf) {
        f_tx_buf_msg_finish(con, LIP_OPCUA_MSGTYPE_UASC_MSG);
        lip_opcua_sc_next_msg(con->sc);
    }
}

struct st_lip_opcua_sc_ctx *lip_opcua_con_sc(struct st_lip_opcua_con *con) {
    return con->sc;
}

struct st_lip_opcua_session_ctx *lip_opcua_con_session(struct st_lip_opcua_con *con) {
    return lip_opcua_sc_session(con->sc);
}

struct st_lip_opcua_con *lip_opcua_sc_get_con(struct st_lip_opcua_sc_ctx *sc) {
    struct st_lip_opcua_con *ret = NULL;
    for (int con_idx = 0; (con_idx < LIP_OPCUA_SERVER_SOCKET_CNT) && !ret; ++con_idx) {
        t_lip_opcua_con *con = &f_con_list[con_idx];
        if ((con->state == LIP_OPCUA_CON_SC_WORK) && (con->sc == sc)) {
            ret = con;
        }
    }
    return ret;
}



void lip_opcua_server_con_init(void) {
    for (int con_idx = 0; con_idx < LIP_OPCUA_SERVER_SOCKET_CNT; ++con_idx) {
        t_lip_opcua_con *con = &f_con_list[con_idx];
        con->socket = lsock_create();
        con->port = lip_opcua_server_port();
        con->rx_msg.data.start = &f_rx_msg_buf[con_idx][0];
        con->tx_buf.start = &f_tx_msg_buf[con_idx][0];
        if (con->socket != LIP_TCP_INVALID_SOCKET_ID) {
            lsock_set_send_fifo(con->socket, &f_tx_sock_buf[con_idx][0], LIP_OPCUA_SERVER_SOCKET_TXBUF_SIZE);
            lsock_set_recv_fifo(con->socket, &f_rx_sock_buf[con_idx][0], LIP_OPCUA_SERVER_SOCKET_RXBUF_SIZE);
            f_con_start_listen(con);
        }
    }
}

void lip_opcua_server_con_pull(void) {
    for (int con_idx = 0; con_idx < LIP_OPCUA_SERVER_SOCKET_CNT; ++con_idx) {
        t_lip_opcua_con *con = &f_con_list[con_idx];
        if (con->state != LIP_OPCUA_CON_CLOSED) {
            t_lip_tcp_sock_state soc_state = lsock_state(con->socket);
            if (soc_state == LIP_TCP_SOCK_STATE_ESTABLISHED) {
                if (con->state == LIP_OPCUA_CON_LISTEN) {
                    lip_printf(LIP_LOG_MODULE_OPCUA, LIP_LOG_LVL_DEBUG_MEDIUM, 0, "lip opcua (%d): new tcp connection open!\n", con->socket);
                    /* после установки соединения ждем сообщение hello с ограничением по таймауту */
                    con->state = LIP_OPCUA_CON_WAIT_HELLO;
                    ltimer_set_ms(&con->wt_tmr, LIP_OPCUA_SERVER_WAIT_HELLO_TOUT);
                } else {
                    t_lip_opcua_err err_code = LIP_OPCUA_ERR_GOOD;
                    /* если есть не переданное сообщение, то пытаемся его передать */
                    if (con->tx_rdy) {
                        f_tx_buf_try_send(con);
                    }

                    if (!con->rx_msg.data_valid) {
                        /* если нет принятого, но еще не обработанного сообщения - пытаемя принять */
                        err_code = f_try_rx_msg(con);
                    }

                    /* обрабатываем принятое сообщение, только если нет непосланного ответа,
                      * т.к. у нас только один буфер на подготовку сообщения для передачи */
                    if (!con->tx_rdy &&  (err_code == LIP_OPCUA_ERR_GOOD)) {
                        if (con->rx_msg.data_valid) {
                            if (con->state == LIP_OPCUA_CON_WAIT_HELLO) {
                                if (con->rx_msg.msg_type == LIP_OPCUA_MSGTYPE_UACP_HELLO) {
                                    err_code = f_proc_hello(con);
                                    if (err_code == 0) {
                                        con->state = LIP_OPCUA_CON_WAIT_SC;
                                    }
                                } else {
                                    err_code = LIP_OPCUA_ERR_BAD_TCP_MESSAGE_TYPE_INVALID;
                                }
                            } else if (con->state == LIP_OPCUA_CON_WAIT_SC) {
                                if (con->rx_msg.msg_type == LIP_OPCUA_MSGTYPE_UASC_OPEN) {
                                    err_code = f_proc_sc_open_msg(con);
                                    if (err_code == LIP_OPCUA_ERR_GOOD) {
                                        con->state = LIP_OPCUA_CON_SC_WORK;
                                    }
                                } else {
                                    err_code = LIP_OPCUA_ERR_BAD_TCP_MESSAGE_TYPE_INVALID;
                                }
                            } else if (con->state == LIP_OPCUA_CON_SC_WORK) {
                                if (con->rx_msg.msg_type == LIP_OPCUA_MSGTYPE_UASC_MSG) {
                                    err_code = f_proc_req_msg(con);
                                } else if (con->rx_msg.msg_type == LIP_OPCUA_MSGTYPE_UASC_CLO) {
                                    err_code = f_proc_sc_close_msg(con);
                                } else if (con->rx_msg.msg_type == LIP_OPCUA_MSGTYPE_UASC_OPEN) {
                                    err_code = f_proc_sc_open_msg(con);
                                } else {
                                    err_code = LIP_OPCUA_ERR_BAD_TCP_MESSAGE_TYPE_INVALID;
                                }
                            }
                            con->rx_msg.hdr_valid = con->rx_msg.data_valid = 0;
                        } else {
                            if (con->state == LIP_OPCUA_CON_WAIT_HELLO) {
                                if (ltimer_expired(&con->wt_tmr)) {
                                    lip_printf(LIP_LOG_MODULE_OPCUA, LIP_LOG_LVL_WARNING_LOW, 0, "lip opcua (%d): hellow wait tout!\n", con->socket);
                                    con->state = LIP_OPCUA_CON_CLOSE_WAIT;
                                    lsock_close(con->socket, LIP_TCP_DEFAULT_CLOSE_TOUT);
                                }
                            }
                        }
                    }


                    if (err_code != LIP_OPCUA_ERR_GOOD) {
                        f_send_err(con, err_code);
                        con->state = LIP_OPCUA_CON_CLOSE_WAIT;
                        lip_opcua_con_sc_close(con);
                        lsock_close(con->socket, LIP_TCP_DEFAULT_CLOSE_TOUT);
                    }
                }
            } else if (soc_state == LIP_TCP_SOCK_STATE_CLOSE_WAIT) {
                lip_opcua_con_sc_close(con);
                lsock_close(con->socket, LIP_TCP_DEFAULT_CLOSE_TOUT);
            } else if (soc_state == LIP_TCP_SOCK_STATE_CLOSED) {
                lip_opcua_con_sc_close(con);
                lip_printf(LIP_LOG_MODULE_OPCUA, LIP_LOG_LVL_DEBUG_MEDIUM, 0, "lip opcua (%d): tcp connection closed!\n", con->socket);
                if (con->state != LIP_OPCUA_CON_CLOSED) {
                    f_con_start_listen(con);
                }
            }
        }
    }
}

void lip_opcua_server_con_close(void) {
    for (int con_idx = 0; con_idx < LIP_OPCUA_SERVER_SOCKET_CNT; ++con_idx) {
        t_lip_opcua_con *con = &f_con_list[con_idx];
        if (con->state != LIP_OPCUA_CON_CLOSED) {
            lip_opcua_con_sc_close(con);
            lsock_close(con->socket, LIP_TCP_DEFAULT_CLOSE_TOUT);
            con->state = LIP_OPCUA_CON_CLOSED;
        }
    }
}
#endif
