//******************************************** Включаемые файлы *********************************************

#include "tcp.h"
#include "bsp.h"
// #include "ltimer.h"
#include "self_diag.h"
#include "dbg_printf.h"
#include "lta_private.h"
#include "tcp_protocol.h"
// #include "flash_mem.h"
// #include "fpga_cmn.h"



//******************************************* Локальные константы *******************************************

// Размер буферов на прием команд и передачу ответов.
// буфер на прием должен быть не меньше размера в 1.5 команды, т.к. окно TCP
// может овобождаться только по пол буфера. Делаем на 2 команды, чтобы был запас
#define TCP_CMD_RX_SIZE  LIP_TCP_RX_BUF_MIN(TCP_CMD_HDR_SIZE + TCP_CMD_RX_DATA_SIZE_MAX)
#define TCP_CMD_TX_SIZE  (TCP_CMD_RESP_SIZE+ TCP_CMD_TX_DATA_SIZE_MAX + 50U)



//******************************************** Определения типов ********************************************

typedef enum 
{
      CMD_STATE_WT_CMD,         // Ожидание приема команды
      CMD_STATE_RX_DATA,        // Ожидание приема данных
      CMD_STATE_RX_DISCARD,     // Ошибка приема
      CMD_STATE_CLOSING         // Закрытие
} cmd_proc_state_t;

typedef struct 
{
    t_lsock_id sock;
    cmd_proc_state_t state;
    u8 rx_buf[TCP_CMD_RX_SIZE];
    u8 tx_buf[TCP_CMD_TX_SIZE];
    tcp_cmd_hdr_t cmd_hdr;
} cmd_proc_t;

typedef struct
{
    u32 cmd;    
    tcp_rx_cmd_t cb;
} cmd_rx_handler;

typedef struct
{
    u32 cmd;    
    tcp_tx_cmd_t cb;
} cmd_tx_handler;



//************************************ Глобальные (публичные) переменные ************************************

// Структура с параметрами модуля 
extern tcp_info_t tsp_dev_info;


//************************************* Локальные (приватные) переменные ************************************

static cmd_proc_t cmd_process[TCP_CMD_SOCK_CNT];

// Текущий сокет команд
static t_lsock_id current_cmd_socket = 0;

// Фукнции обратного вызова по приему команды
static cmd_rx_handler rx_cmd[TCP_CMD_CALLBACK_QTY];
static cmd_tx_handler tx_cmd[TCP_CMD_CALLBACK_QTY];

static u32 user_var = 0U;


//**************************************** Объявления глобальных функций ************************************

// Инициализация модуля 
fun_res_t tcp_cmd_init(void);
// Деинициализация модуля 
void tcp_cmd_deinit(void);
// Периодический опрос сокетов команд
void tcp_cmd_loop(void);

// Создание и начало прослушивания сокета данных
// num - номер сокета (от 0 до TCP_DATA_SOCK_CNT - 1)
extern fun_res_t tcp_data_create_socket(u8 num);
// Закрытие и удаление сокета данных
extern fun_res_t tcp_data_delete_socket(u8 num);

// Установка запроса на переинициализацию модуля
extern void tcp_restart(void);



//***********************************************************************************************************
//******************************** Определения локальных (приватных) функций ********************************
//***********************************************************************************************************


// Передача ответа на команду
static void f_tcp_send_resp(t_lsock_id sock, tcp_resp_errs_t res, u8* buf, u32 len)
{
    tcp_resp_hdr_t resp;
    resp.sign = TCP_CMD_SIGNATURE;
    resp.res = res;
    if (len > TCP_CMD_TX_DATA_SIZE_MAX)
    {
        len = TCP_CMD_TX_DATA_SIZE_MAX;
    }
    resp.len = len;

    lsock_send(sock, (u8*)&resp, sizeof(resp));
    
    if ((len > 0) && (buf != NULL))
    {
        lsock_send(sock, buf, len);
    }
}



// Обработка команд без передачи данных
static tcp_resp_errs_t f_tcp_cmd_proc_rx(tcp_cmd_hdr_t* header, 
                                         u8* buf, 
                                         u32 length, 
                                         u32 param) 
{
    tcp_resp_errs_t err = TCP_ERR_UNKNOWN_CMD;
    u32 cmd = header->cmd;

    // Поиск команды и выполнение колбека
    for (u16 j = 0; j < TCP_CMD_CALLBACK_QTY; j++)
    {
        if (cmd == rx_cmd[j].cmd)
        {
            err = rx_cmd[j].cb(param, buf, length);

            if (err != TCP_ERR_OK)
            {
                DBG_LOGE("CMD err= %d, cmd= %d\n", err, cmd);
            }
            else
            {
                DBG_LOGD("CMD %d is done! par= %d, len= %d\n", cmd, param, length);
            }

            break;
        }
    }

    return err;
}



// Обработка команд c передачей данных
static tcp_resp_errs_t f_tcp_cmd_proc_tx(tcp_cmd_hdr_t *header, 
                                         u8** buf, 
                                         u32* length, 
                                         u32 resp_len,
                                         u32 param) 
{
    tcp_resp_errs_t err = TCP_ERR_OK;
    u32 cmd = header->cmd;    

    // Поиск команды и выполнение колбека
    for (u16 j = 0; j < TCP_CMD_CALLBACK_QTY; j++)
    {
        if (cmd == tx_cmd[j].cmd)
        {
            err = tx_cmd[j].cb(param, resp_len, buf, length);

            if (err != TCP_ERR_OK)
            {
                DBG_LOGE("CMD err= %d, cmd= %d\n", err, cmd);
            }
            else
            {
                DBG_LOGD("CMD %d is done! par= %d, resp_len= %d\n", cmd, param, resp_len);
            }

            break;
        }
    }

    return err;
}



// Команда отключения всех сокетов (кроме текущего)
static tcp_resp_errs_t f_cmd_drop_data_con(u32 param, const u8* buf, u32 length)
{
    tcp_resp_errs_t err = TCP_ERR_OK;
    L_UNUSED(param);
    L_UNUSED(buf);
    L_UNUSED(length);

    // Закрытие всех сокетов команд, кроме текущего
    for (u8 i = 0; i < TCP_CMD_SOCK_CNT; i++)
    {
        if (i != current_cmd_socket)
        {
            lsock_close(cmd_process[i].sock, TCP_SOCKETS_CLOSE_TIMEOUT);
        }
    }

    // Закрытие всех сокетов данных
    for (u8 i = 0; i < TCP_DATA_SOCK_CNT; i++)
    {
        tcp_data_delete_socket(i);
    }

    return err;
}



// Команда сброса модуля
static tcp_resp_errs_t f_cmd_reset_module(u32 param, const u8* buf, u32 length)
{
    tcp_resp_errs_t err = TCP_ERR_OK;
    L_UNUSED(param);
    L_UNUSED(buf);
    L_UNUSED(length);

    lip_close_request(TCP_LIP_CLOSE_TIMEOUT);

    // Установить запрос на перезагрузку (по таймауту)
    bsp_set_reset_req(2*TCP_LIP_CLOSE_TIMEOUT);    

    return err;
}



// Команда получения информации об устройстве
static tcp_resp_errs_t f_cmd_get_dev_info(u32 param, u32 resp_len, u8** buf,  u32* length)
{
    tcp_resp_errs_t err = TCP_ERR_OK;
    L_UNUSED(param);
    L_UNUSED(resp_len);

    // Запрос актуального статуса самодиагностики
    tsp_dev_info.self_diag_err = sdiag_get_last_error();

    *buf = (u8*)&tsp_dev_info;
    *length = sizeof(tsp_dev_info);

    return err;
}



// Команда получения пользовательской переменной
static tcp_resp_errs_t f_cmd_set_user_var(u32 param, const u8* buf, u32 length)
{
    tcp_resp_errs_t err = TCP_ERR_OK;
    L_UNUSED(buf);
    L_UNUSED(length);

    user_var = param;

    return err;
}



// Команда передачи пользовательской переменной
static tcp_resp_errs_t f_cmd_get_user_var(u32 param, u32 resp_len, u8** buf,  u32* length)
{
    tcp_resp_errs_t err = TCP_ERR_OK;
    L_UNUSED(param);
    L_UNUSED(resp_len);

    *buf = (u8*)&user_var;
    *length = sizeof(u32);

    return err;
}

    

//***********************************************************************************************************
//******************************* Определения глобальных (публичных) функций ********************************
//***********************************************************************************************************

void tcp_cmd_pre_init(void)
{
    // Обнуление колбеков
    for (u16 i = 0; i < TCP_CMD_CALLBACK_QTY; i++) 
    {
        rx_cmd[i].cmd = 0;
        rx_cmd[i].cb = NULL;

        tx_cmd[i].cmd = 0;
        tx_cmd[i].cb = NULL;
    }
}



// Инициализация модуля 
fun_res_t tcp_cmd_init(void)
{
    fun_res_t err = ERR_OK;
    
    //открываем сокеты для прослушивания команд
    for (u8 i = 0; i < TCP_CMD_SOCK_CNT; i++) 
    {
        int val = 1;
        cmd_process[i].sock = lsock_create();

        lsock_set_recv_fifo(cmd_process[i].sock, cmd_process[i].rx_buf, TCP_CMD_RX_SIZE);
        lsock_set_send_fifo(cmd_process[i].sock, cmd_process[i].tx_buf, TCP_CMD_TX_SIZE);

        lsock_listen(cmd_process[i].sock, tsp_dev_info.cur_settings.tcp_cmd_port);

        lsock_set_opt(cmd_process[i].sock, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val));
        lsock_set_opt(cmd_process[i].sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));

        cmd_process[i].state = CMD_STATE_WT_CMD;
    }

    // Инициализация команд TCP для текущего модуля

    // Команда отключения всех сокетов (кроме текущего)
    tcp_cmd_set_rx_callback(TCP_CMD_DROP_DATA_CON, f_cmd_drop_data_con);
    // Команда получения пользовательской переменной
    tcp_cmd_set_rx_callback(TCP_CMD_SET_USER_VAR, f_cmd_set_user_var);
    // Команда сброса модуля
    tcp_cmd_set_rx_callback(TCP_CMD_RESET_MODULE, f_cmd_reset_module);

    // Команда получения информации об устройстве
    tcp_cmd_set_tx_callback(TCP_CMD_GET_DEV_INFO, f_cmd_get_dev_info);
    // Команда передачи пользовательской переменной
    tcp_cmd_set_tx_callback(TCP_CMD_GET_USER_VAR, f_cmd_get_user_var);

    return err;
}



// Деинициализация модуля 
void tcp_cmd_deinit(void)
{
    for (u8 i = 0; i < TCP_CMD_SOCK_CNT; i++) 
    {
        lsock_close(cmd_process[i].sock, TCP_SOCKETS_CLOSE_TIMEOUT);
    }
}



// Периодический опрос сокетов команд
void tcp_cmd_loop(void)
{
    static u8 tmp_cmd_buf[TCP_CMD_TX_DATA_SIZE_MAX];
    u32 rcv_size;
    tcp_resp_errs_t res;

    for (u8 i=0; i < TCP_CMD_SOCK_CNT; i++) 
    {
        // Запоминаем текущий сокет
        current_cmd_socket = i;

        t_lip_tcp_sock_state cstate = lsock_state(cmd_process[i].sock);

        if (cstate == LIP_TCP_SOCK_STATE_CLOSED) 
        {
            // Закрытое соединение. Сокет не соответствует
            // ни одному соединению и может быть использован
            // для установки нового

            // если завершили закрытие предыдущего соединения начинаем снова прослушивать порт
            lsock_listen(cmd_process[i].sock, tsp_dev_info.cur_settings.tcp_cmd_port);
            cmd_process[i].state = CMD_STATE_WT_CMD;
            DBG_LOGI("cmd sock %d: restart listen\n", i);
        } 
        else if (cstate == LIP_TCP_SOCK_STATE_CLOSE_WAIT) 
        {
            // Был принят запрос на закрытие от другой
            // стороны, но еще не была вызвана функция
            // закрытия соединения со стороны программы
            // на локальном хосте
            lsock_close(cmd_process[i].sock, TCP_SOCKETS_CLOSE_TIMEOUT);
            cmd_process[i].state = CMD_STATE_CLOSING;
            DBG_LOGI("cmd sock %d: closed\n", i);
        }
        else if (cstate == LIP_TCP_SOCK_STATE_ESTABLISHED)
        {
            // Установленное соединение для обмена данными
            // как на прием, так и на передачу

            //  смотрим, сколько данных пришло
            rcv_size = lsock_recv_rdy_size(cmd_process[i].sock);

            if (cmd_process[i].state == CMD_STATE_WT_CMD)
            {
                /* если пришел заголовок команды */
                if (rcv_size >= TCP_CMD_HDR_SIZE) 
                {
                    /* принимаем заголовок команды */
                    lsock_recv(cmd_process[i].sock, (u8*)&cmd_process[i].cmd_hdr, TCP_CMD_HDR_SIZE);
                    
                    /* проверяем сигнатуру команды */
                    if (cmd_process[i].cmd_hdr.sign != TCP_CMD_SIGNATURE) 
                    {
                        f_tcp_send_resp(cmd_process[i].sock, TCP_ERR_CMD_SIGNATURE, 0,0);
                        DBG_LOGE("Invalid CMD signature!\n");
                    } 
                    else 
                    {
                        /* если сигнатура верна - разбор команды */
                        if ((cmd_process[i].cmd_hdr.data_len == 0) &&
                                (cmd_process[i].cmd_hdr.resp_len == 0)) 
                        {
                            // команда без данных -> обрабатываем
                            res = f_tcp_cmd_proc_rx(&cmd_process[i].cmd_hdr, 
                                            0, 
                                            0,
                                            cmd_process[i].cmd_hdr.par);
                            f_tcp_send_resp(cmd_process[i].sock, res, 0, 0);
                        } 
                        else if ((cmd_process[i].cmd_hdr.data_len > 0) && 
                                (cmd_process[i].cmd_hdr.resp_len == 0)) 
                        {
                            /* если данных больше размера чем буфер - посылаем ошибку и принимаем и выбрасываем данные */
                            if (cmd_process[i].cmd_hdr.data_len > TCP_CMD_RX_DATA_SIZE_MAX) 
                            {
                                    f_tcp_send_resp(cmd_process[i].sock, TCP_ERR_INVALID_CMD_DATA_SIZE, 0,0);
                                    cmd_process[i].state = CMD_STATE_RX_DISCARD;
                                    DBG_LOGE("Invalid CMD data_len!\n");
                            } 
                            else 
                            {
                                /* если есть данные на прием - переходим в состояние
                                    * ожидания данных */
                                cmd_process[i].state = CMD_STATE_RX_DATA;
                            }
                        } 
                        else if ((cmd_process[i].cmd_hdr.data_len == 0) &&
                                (cmd_process[i].cmd_hdr.resp_len > 0)) 
                        {
                            //команда с данными на передачу
                            u32 len = 0;
                            u8* snd_buf = NULL;
                            
                            res = f_tcp_cmd_proc_tx(&cmd_process[i].cmd_hdr, 
                                            &snd_buf, 
                                            &len,
                                            cmd_process[i].cmd_hdr.resp_len,
                                            cmd_process[i].cmd_hdr.par);
                            
                            //возвращаем данных не больше, чем запросили
                            if (len > cmd_process[i].cmd_hdr.resp_len)
                            {
                                len = cmd_process[i].cmd_hdr.resp_len;
                            }
                            
                            f_tcp_send_resp(cmd_process[i].sock, res, snd_buf, len);
                        }
                    }
                } //if (rcv_size >= LPRT_CMD_HDR_SIZE)
            }
            else if (cmd_process[i].state == CMD_STATE_RX_DATA) 
            {
                /* если приняли данные от команды - выполнение */
                if (rcv_size >= cmd_process[i].cmd_hdr.data_len) 
                {
                    lsock_recv(cmd_process[i].sock, tmp_cmd_buf, cmd_process[i].cmd_hdr.data_len);
                    res = f_tcp_cmd_proc_rx(&cmd_process[i].cmd_hdr, 
                                        tmp_cmd_buf, 
                                        cmd_process[i].cmd_hdr.data_len,
                                        cmd_process[i].cmd_hdr.par);
                    f_tcp_send_resp(cmd_process[i].sock, res, 0, 0);
                    cmd_process[i].state = CMD_STATE_WT_CMD;
                }
            } 
            else if (cmd_process[i].state == CMD_STATE_RX_DISCARD)    
            {
                u32 rcv_size_tmp = rcv_size;

                // Очистить весь фифо
                while(rcv_size_tmp > 0)
                {
                    if (rcv_size_tmp > sizeof(tmp_cmd_buf))
                    {
                        rcv_size_tmp = sizeof(tmp_cmd_buf);
                    }
                    lsock_recv(cmd_process[i].sock, tmp_cmd_buf, rcv_size_tmp);

                    rcv_size_tmp = lsock_recv_rdy_size(cmd_process[i].sock);
                }

                cmd_process[i].state = CMD_STATE_WT_CMD;
            }
        } // else if()
    } // for()
}



// Задать колбек для обработки команды с приемом данных
void tcp_cmd_set_rx_callback(u32 cmd, tcp_rx_cmd_t fun)
{
    if (fun != NULL)
    {
        for (u16 i = 0; i < TCP_CMD_CALLBACK_QTY; i++) 
        {
            if (rx_cmd[i].cb == NULL)
            {
                rx_cmd[i].cmd = cmd;
                rx_cmd[i].cb = fun;
                break;
            }
        }
    }
}



// Задать колбек для обработки команды с передачей данных
void tcp_cmd_set_tx_callback(u32 cmd, tcp_tx_cmd_t fun)
{
    if (fun != NULL)
    {
        for (u16 i = 0; i < TCP_CMD_CALLBACK_QTY; i++) 
        {
            if (tx_cmd[i].cb == NULL)
            {
                tx_cmd[i].cmd = cmd;
                tx_cmd[i].cb = fun;
                break;
            }
        }
    }
}




//-----------------------------------------------------------------------------------------------------------
bool tcp_cmd_socket_is_est(void)
{
    bool res = false;

    // Если хотя бы 1 сокет команд подключен
    for (u8 i=0; i < TCP_CMD_SOCK_CNT; i++) 
    {
        int cstate = lsock_state(cmd_process[i].sock);

        if (cstate == LIP_TCP_SOCK_STATE_ESTABLISHED)
        {
            res = true;
            break;
        }
    }

    return res;
}



//*********************************************** КОНЕЦ ФАЙЛА ***********************************************

//! Старые реализации команд

// // Отключение соединения всех сокетов данных и неиспользуемых сокетов команд
// static void f_cmd_drop_data_con(void) 
// {
//     // Закрытие всех сокетов команд, кроме текущего
//     for (u8 i = 0; i < TCP_CMD_SOCK_CNT; i++)
//     {
//         if (i != current_cmd_socket)
//         {
//             lsock_close(cmd_process[i].sock, TCP_SOCKETS_CLOSE_TIMEOUT);
//         }
//     }

//     // Закрытие всех сокетов данных
//     for (u8 i = 0; i < TCP_DATA_SOCK_CNT; i++)
//     {
//         tcp_data_delete_socket(i);
//     }
// }



// // Команда записи сетевых настроек
// static i32 f_cmd_set_eth_config(u32 param, const u8* buf, u32 length)
// {
//     i32 err = ERR_OK;
//     fun_res_t fun_res = ERR_OK;

//     // Нельзя делать при запущенном сборе!
//     err = (b_allow_srv_cmd == false) ? (TCP_ERR_NOT_ALLOWED) : (TCP_ERR_OK);
//     L_CHECK_ERR(err);

//     // Проверка размера пакета
//     err = (length > sizeof(tcp_eth_cfg_hdr_t)) ? (TCP_ERR_CMD_WASNT_EXECUTED) : (TCP_ERR_OK);
//     L_CHECK_ERR(err);

//     tcp_eth_cfg_hdr_t data_hdr;
//     memcpy(&data_hdr, buf, length);

//     // Формат команды
//     switch(param)
//     {
//         case(LTA_FM_FORMAT_WRITE_SETTINGS):
//         {
//             // Проверяем текущий пароль, устанавливаем новый
//             fun_res = fm_write_user_data(&data_hdr.settings, FM_US_FORMAT_WRITE_ALL, data_hdr.cur_passwd, data_hdr.new_passwd);
//         }
//         break;

//         case(LTA_FM_FORMAT_WRITE_ONLY_FLAGS):
//         {
//             // Не проверяем текущий пароль, не устанавливаем новый
//             fun_res = fm_write_user_data(&data_hdr.settings, FM_US_FORMAT_WRITE_ONLY_FLAGS, data_hdr.cur_passwd, data_hdr.new_passwd);
//         }
//         break;
        
//         case(LTA_FM_FORMAT_ERASE):
//         {
//             // Проверяем текущий пароль
//             fun_res = fm_erase_user_data(data_hdr.cur_passwd);
//         }
//         break;

//         default:
//             err = TCP_ERR_INVALID_CMD_PARAMS;
//         break;
//     }

//     // Обработка ошибок
//     if (fun_res == ERR_ASSERT_ERROR)
//     {
//         err = TCP_ERR_CMD_WASNT_EXECUTED;
//     }
//     else if(fun_res == ERR_RUNTIME_ERROR)
//     {
//         // Память пуста, а пишут только флаги - это недопустимо
//         err = TCP_ERR_INVALID_CMD_PARAMS;
//     }
//     else if(fun_res == ERR_INVALID_ARGUMENT)
//     {
//         err = TCP_ERR_INVALID_PASSWORD;
//     }

//     L_EXIT;
//     return err;
// }



// // Команда записи заводских параметров
// static i32 f_cmd_set_factory_mem(u32 param, const u8* buf, u32 length)
// {
//     i32 err = ERR_OK;
//     fun_res_t fun_res = ERR_OK;

//     // Нельзя делать при запущенном сборе!
//     err = (b_allow_srv_cmd == false) ? (TCP_ERR_NOT_ALLOWED) : (TCP_ERR_OK);
//     L_CHECK_ERR(err);

//     // Проверка размера пакета
//     err = (length > sizeof(tcp_factory_mem_data_hdr_t)) ? (TCP_ERR_CMD_WASNT_EXECUTED) : (TCP_ERR_OK);
//     L_CHECK_ERR(err);

//     tcp_factory_mem_data_hdr_t data_hdr;
//     memcpy(&data_hdr, buf, length);

//     // Формат команды
//     switch(param)
//     {
//         case(LTA_FM_FORMAT_WRITE_SETTINGS):
//         case(LTA_FM_FORMAT_WRITE_ONLY_FLAGS):
//         {
//             // Проверяем текущий пароль, устанавливаем новый
//             fun_res = fm_write_factory_data(&data_hdr.settings, data_hdr.cur_passwd, data_hdr.new_passwd);
//         }
//         break;
        
//         case(LTA_FM_FORMAT_ERASE):
//         {
//             // Проверяем текущий пароль
//             fun_res = fm_erase_factory_data(data_hdr.cur_passwd);
//         }
//         break;

//         default:
//             err = TCP_ERR_INVALID_CMD_PARAMS;
//         break;
//     }

//     // Обработка ошибок
//     if (fun_res == ERR_ASSERT_ERROR)
//     {
//         err = TCP_ERR_CMD_WASNT_EXECUTED;
//     }
//     else if(fun_res == ERR_RUNTIME_ERROR)
//     {
//         // Память пуста, а пишут только флаги - это недопустимо
//         err = TCP_ERR_INVALID_CMD_PARAMS;
//     }
//     else if(fun_res == ERR_INVALID_ARGUMENT)
//     {
//         err = TCP_ERR_INVALID_PASSWORD;
//     }

//     L_EXIT;
//     return err;
// }




//! Может пригодиться реализация команд
# if 0


// Функция для обработки команды с приемом данных
// typedef int32_t (*tcp_rx_cmd_t)(u32 cmd,
//                                 const u8* buf, 
//                                 int length);

// // Функция для обработки команды с передачей данных
// typedef int32_t (*tcp_tx_cmd_t)(u32 cmd,
//                                 u8** buf, 
//                                 int* length);




// Обработка команд без передачи данных
static i32 f_tcp_cmd_proc_rx(tcp_cmd_hdr_t* header, 
                         u8* buf, 
                         u32 length, 
                         u32 param) 
{
    i32 err = TCP_ERR_UNKNOWN_CMD;
    fun_res_t fun_res = ERR_OK;
    u32 cmd = header->cmd;
    int32_t res; 

    // Поиск команды и выполнение колбека
    for (u16 j = 0; j < TCP_CMD_CALLBACK_QTY; j++)
    {
        if (cmd == rx_cmd[j].cmd)
        {
            err = rx_cmd[i].cb(param, buf, length);
            break;
        }
    }

    if (err != TCP_ERR_OK)
    {
        DBG_LOGE("err= %d, cmd= %d\n", err, cmd);
    }
   
    

    // todo - сделать поиск из массива зарегестрированных команд и вызвать соответствующую функцию.
    // если не найдено - ошибка

//     switch (cmd) 
//     {
//         case(TCP_CMD_ETH_CFG_SET):
//             err = f_cmd_set_eth_config(param, buf, length);
//         break;

//         case(TCP_CMD_FACTORY_DATA_SET):
//             err = f_cmd_set_factory_mem(param, buf, length);
//         break;

//         // Калибровки
//         case(TCP_CMD_CBR_DATA_WR):
//             fun_res = fm_write_cbr_data(buf, param, length);
//         break;
//         case(TCP_CMD_CBR_DATA_ER):
//             if (length > sizeof(u32))
//             {
//                 err = TCP_ERR_INVALID_CMD_PARAMS;
//             }
//             else
//             {
//                 u16 erase_len = buf[0] | (buf[1] << 8);
//                 fun_res = fm_erase_cbr_data(param, erase_len);
//             }
//         break;

//         // Общие данные
//         case(TCP_CMD_CMN_DATA_WR):
//             fun_res = fm_write_cmn_data(buf, param, length);
//         break;
//         case(TCP_CMD_CMN_DATA_ER):
//             if (length > sizeof(u32))
//             {
//                 err = TCP_ERR_INVALID_CMD_PARAMS;
//             }
//             else
//             {
//                 u16 erase_len = buf[0] | (buf[1] << 8);
//                 fun_res = fm_erase_cmn_data(param, erase_len);
//             }
//         break;



//         case(TCP_CMD_CREATE_DATA_SOCKET):
//             fun_res = tcp_data_create_socket(param);
//             if (fun_res != ERR_OK) {
//                 err = TCP_ERR_INVALID_CMD_PARAMS;
//             }
//         break;

//         case(TCP_CMD_DELETE_DATA_SOCKET):
//             fun_res = tcp_data_delete_socket(param);
//             if (fun_res != ERR_OK) 
//             {
//                 err = TCP_ERR_INVALID_CMD_PARAMS;
//             }
//         break;

//         // todo - добавить стримы в отдельную группу
//         case(TCP_CMD_STREAMS_ENABLE):
//             // fun_res = STREAMS_Enable(param);
//             // if (fun_res == RES_NOT_OK) {
//             //     err = TCP_ERR_CMD_WASNT_EXECUTED;
//             // }
//         break;

//         case(TCP_CMD_STREAMS_DISABLE):
//             // fun_res = STREAMS_Disable(param);
//             // if (fun_res == RES_NOT_OK) {
//             //     err = TCP_ERR_CMD_WASNT_EXECUTED;
//             // }
//         break;

//         case(TCP_CMD_STREAMS_START):
//             // fun_res = STREAMS_Start(true);
//             // if (fun_res == RES_NOT_OK) {
//             //     err = TCP_ERR_CMD_WASNT_EXECUTED;
//             // }
//         break;

//         case(TCP_CMD_STREAMS_STOP):
//             // fun_res = STREAMS_Start(false);
//             // if (fun_res == RES_NOT_OK) {
//             //     err = TCP_ERR_CMD_WASNT_EXECUTED;
//             // }
//         break;

//         // case(TCP_CMD_SYNC_CFG_SET):
//         // break;

//         case(TCP_CMD_DROP_DATA_CON):
//             f_cmd_drop_data_con();
//         break;

//         case (TCP_CMD_FPGA_REG_WRITE):
//             if (length >= sizeof(u32)) 
//             {   
//                 u32 val = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
//                 fun_res = fpga_write_reg((u16)(param & 0xFFFF), val);
//             } else 
//             {
//                 err = TCP_ERR_INVALID_CMD_PARAMS;
//             }
//         break;

//         case (TCP_CMD_RELOAD_FPGA):
//         break;

//         case (TCP_CMD_GO_TO_BOOT):
//         //             void lboot_request_bootenter(int mode);
// //             lboot_request_bootenter(LBOOT_BOOTMODE_TFTP_SERVER);
// //             lprintf("lboot_request_bootenter\n");
//         break;

//         default:
//         {
//             // #define FUN_IS_NOT_FOUND     (-100)
//             // res = FUN_IS_NOT_FOUND;

//             // for (U8 i = 0; i < TCP_CMD_CALLBACK_QTY; i++) {
//             //     if (f_user_rx_cmd_callback[i] != NULL) {
//             //         res = f_user_rx_cmd_callback[i](cmd, buf, length);
//             //     }
                
//             //     if (res != FUN_IS_NOT_FOUND) {
//             //         break;
//             //     }
//             // }

//             // if (res == FUN_IS_NOT_FOUND) {
//             //     // Функция обратного вызова не найдена
//             //     lprintf("rx cmd %x unknown!\n", cmd);
//             //     err = TCP_ERR_UNKNOWN_CMD;
//             // }

//             // #undef FUN_IS_NOT_FOUND
//         }
//         break;
//     }

    // if (fun_res != ERR_OK)
    // {
    //     err = TCP_ERR_CMD_WASNT_EXECUTED;
    // }

    return err;
}





// Обработка команд c передачей данных
static i32 f_tcp_cmd_proc_tx(tcp_cmd_hdr_t *header, 
                        u8** buf, 
                        u32* length, 
                        u32 resp_len,
                        u32 param) 
{
    i32 err = TCP_ERR_OK;
    fun_res_t fun_res = ERR_OK;
    u8* wr_buf = *buf;
    u32 cmd = header->cmd;
    int32_t res;

    switch (cmd)
    {
        // Получить информацию об устройстве
        case (TCP_CMD_GET_DEV_INFO):
            wr_buf = (u8*)&tsp_dev_info;
            *length = sizeof(tsp_dev_info);
        break;
        
        // Получить записанные в память пользовательские настройки сети
        case (TCP_CMD_ETH_CFG_GET):
            if (fm_user_settings_data != NULL)
            {
                wr_buf = (u8*)fm_user_settings_data;
                *length = sizeof(fm_user_settings_data_t);
            }
            else
            {
                err = TCP_ERR_DATA_IS_EMPTY;
            }
        break;

        // Получить записанные заводские параметры
        case(TCP_CMD_FACTORY_DATA_GET):
            
            if (fm_factory_mem_data != NULL)
            {
                wr_buf = (u8*)fm_factory_mem_data;
                *length = sizeof(fm_factory_mem_data_t);
            }
            else
            {
                err = TCP_ERR_DATA_IS_EMPTY;
            }
        break;

        // Получить записанные калибровки
        case(TCP_CMD_CBR_DATA_RD):
            if (fm_cbr_data != NULL)
            {
                wr_buf = (u8*)&fm_cbr_data[param];
                *length = resp_len;
            }
            else
            {
                err = TCP_ERR_DATA_IS_EMPTY;
            }
        break;

        // Получить записанные общие данные
        case(TCP_CMD_CMN_DATA_RD):
            if (fm_cmn_data != NULL)
            {
                wr_buf = (u8*)&fm_cmn_data[param];
                *length = resp_len;
            }
            else
            {
                err = TCP_ERR_DATA_IS_EMPTY;
            }
        break;


        // Прочитать значение регистра ПЛИС
        case (TCP_CMD_FPGA_REG_READ):
            fun_res = fpga_read_reg((u16)(param & 0xFFFF), &fpga_reg_val_tmp);
            wr_buf = (u8*)&fpga_reg_val_tmp;
            *length = sizeof(u32);
        break;

        // case (TCP_CMD_SYNC_CFG_GET):
        // break;
        // case (TCP_CMD_GET_ERRORS):
        // break;

        default:
        {
            // #define FUN_IS_NOT_FOUND     (-100)
            // res = FUN_IS_NOT_FOUND;

            // for (U8 i = 0; i < TCP_CMD_CALLBACK_QTY; i++) {
            //     if (f_user_tx_cmd_callback[i] != NULL) {
            //         res = f_user_tx_cmd_callback[i](cmd, &wr_buf, length);
            //     }
                
            //     if (res != FUN_IS_NOT_FOUND) {   
            //         break;
            //     }
            // }

            // if (res == FUN_IS_NOT_FOUND) {
            //     // Функция обратного вызова не найдена
            //     lprintf("tx cmd %x unknown!\n", cmd);
            //     err = TCP_ERR_UNKNOWN_CMD;
            // }
        
            // #undef FUN_IS_NOT_FOUND
        }
        break;
    }
 
    if (fun_res != ERR_OK)
    {
        err = TCP_ERR_CMD_WASNT_EXECUTED;
    }

    *buf = wr_buf;
    return err;
}


#endif