/******************************************************************************
 * file    Core/Src/cli.c 
 * author  YuLiang
 * version 1.0.0
 * date    10-Jun-2022
 * brief   This file provides all the cli related operation functions.
 ******************************************************************************
 * Attention
 *
 * <h2><center>&copy; COPYRIGHT(c) 2022 LandPower</center></h2>
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *   1. Redistributions of source code must retain the above copyright notice,
 *      this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright notice,
 *      this list of conditions and the following disclaimer in the documentation
 *      and/or other materials provided with the distribution.
 *   3. Neither the name of WTOE nor the names of its contributors may be used to 
 *      endorse or promote products derived from this software without specific
 *      prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>

#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "cmsis_os.h"
#include "usart.h"
#include "tim.h"
#include "rtc.h"

#include "common.h"
#include "dev_config.h"
#include "FreeRTOS_CLI.h"
#include "cli.h"
#include "flash_if.h"
#include "wireless.h"
#include "RS485_sensor.h"
#include "ADC_collect.h"
#include "RS485_debug.h"
#include "recording_wave.h"
#include "flash_log.h"
#include "position.h"

/* Private define ------------------------------------------------------------*/
/* cli buffer 长度定义. */
#define CLI_OUTPUT_SIZE 4096
#define CLI_CMD_SIZE 256
/* cli 等待超时时间. */
#define CLI_TIMEOUT 300000
/* 可查询任务最大长度. */
#define MAX_TASK_NUM 16
/* 无效 cli 数据. */
#define CLI_DATA_INVALID 0xca

/* Private macro -------------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* cli 状态机 flag 号. */
typedef enum
{
    CLI_FLAG_PASSWORD = 0,  // 表示cli是否输入了密码.
} CLI_FLAG_E;

/* cli 发包缓冲. */
typedef struct
{
    char buf[CLI_OUTPUT_SIZE];
    uint16_t index;
} cli_output_buf_t;

/* cli 全局结构体. */
typedef struct 
{
    uint32_t flag;
    osMutexId_t mutex;
    cli_output_buf_t output_buf;
    char cmd[CLI_CMD_SIZE];
} cli_machine_t;

/* Private variables ---------------------------------------------------------*/
usart_buf_t cli_buf;
static cli_machine_t cli_machine;       // cli 全局结构体.
//static TaskStatus_t cli_tasks_status[MAX_TASK_NUM];     // task 任务监测数组.
static char cli_param_erro[] = "Incorrect command parameter(s).\r\n";
static char cli_rv_ok[] ="ok.\r\n";
/* cli 互斥锁属性结构体. */
static const osMutexAttr_t cli_mutex_attr =
{
    .name = "cli"
};
UART_HandleTypeDef *cli_uart = &huart4;

extern uint8_t yl_test;

/* Private function prototypes -----------------------------------------------*/
static BaseType_t _cli_adc_auto(const char *string);
static BaseType_t _cli_dbg_mode(const char *string);
static BaseType_t _cli_default(const char *string);
static BaseType_t _cli_eny_mode(const char *string);
static BaseType_t _cli_host(const char *string);
static BaseType_t _cli_id(const char *string);
static BaseType_t _cli_password(const char *string);
static BaseType_t _cli_reset(const char *string);
static BaseType_t _cli_save(const char *string);
static BaseType_t _cli_sen_id(const char *string);
static BaseType_t _cli_show(const char *string);
static BaseType_t _cli_test(const char *string);
static BaseType_t _cli_work_mode(const char *string);

#if LD_DEBUG
static BaseType_t _cli_debug(const char *string);
static const char *debug_str[DBG_M_COUNT] =
{
    "adc",
    "4g",
    "sen",
    "dbg",
    "wave",
    "position"
};
static const CLI_Command_Definition_t cmd_debug     = {"debug", _cli_debug, 2};
#endif
static const CLI_Command_Definition_t cmd_adc_auto  = {"adc-auto", _cli_adc_auto, 1};
static const CLI_Command_Definition_t cmd_dbg_mode  = {"dbg-mode", _cli_dbg_mode, 1};
static const CLI_Command_Definition_t cmd_default   = {"default", _cli_default, 1};
static const CLI_Command_Definition_t cmd_eny_mode  = {"eny-mode", _cli_eny_mode, 1};
static const CLI_Command_Definition_t cmd_host      = {"host", _cli_host, 1};
static const CLI_Command_Definition_t cmd_id        = {"id", _cli_id, 1};
static const CLI_Command_Definition_t cmd_pw        = {"passwd", _cli_password, 1};
static const CLI_Command_Definition_t cmd_reset     = {"reset", _cli_reset, 1};
static const CLI_Command_Definition_t cmd_save      = {"save", _cli_save, 1};
static const CLI_Command_Definition_t cmd_sen_id    = {"sen-id", _cli_sen_id, 8};
static const CLI_Command_Definition_t cmd_show      = {"show", _cli_show, 2};
static const CLI_Command_Definition_t cmd_test      = {"test", _cli_test, 1};
static const CLI_Command_Definition_t cmd_work_mode = {"work-mode", _cli_work_mode, 1};

/* Internal functions --------------------------------------------------------*/
/* cli获取互斥锁. */
static void _cli_mutex_lock(void)
{
    osMutexAcquire(cli_machine.mutex, osWaitForever);
}

/* cli释放互斥锁. */
static void _cli_mutex_unlock(void)
{
    osMutexRelease(cli_machine.mutex);
}

#if LD_DEBUG
static uint8_t _cli_string_for_num(const char *string,BaseType_t param_len)
{
    uint8_t debug_idx = 0xff;
    
    for(uint8_t idx = 0; idx < DBG_M_COUNT; idx++)
    {
        if (0 == memcmp(string, debug_str[idx], param_len))
        {
            debug_idx = idx;
            break;
        }
    }
    
    return debug_idx;
}

static void _cli_debug_process(uint8_t cmd, const char *string)
{
    const char *param = NULL;
    BaseType_t param_len = 0;
    uint8_t data = 0;
    
    param = FreeRTOS_CLIGetParameter(string, 2, &param_len);
    if (NULL == param)
    {
        if (DBG_CMD_OFF == cmd)
            cmd = DBG_CMD_OFF_ALL;
        else
        {
            vty_print(cli_param_erro);
            return;
        }
    }
    else
    {
        data =_cli_string_for_num(param, param_len);
        if (0xff == data)
        {
            vty_print(cli_param_erro);
            return;
        }
    }
    
    if (dbg_cmd_hander(cmd, data) != HAL_OK)
        return;

    vty_print(cli_rv_ok);
}

/* 命令行debug接口. */
static BaseType_t _cli_debug(const char *string)
{
    const char *param = NULL;
    uint8_t param_len = 0;
    uint8_t i = 0;
    
    param = FreeRTOS_CLIGetParameter(string, 1, (BaseType_t *)&param_len);
    if (NULL == param)
    {
        vty_print(cli_param_erro);
        return pdFALSE;
    }

    switch(param[0])
    {
    /* 开debug. */
    case 'e':
        _cli_debug_process(DBG_CMD_ON, string);
        break;
    /* 关debug. */
    case 'd':
        _cli_debug_process(DBG_CMD_OFF, string);
        break;
    /* 帮助信息. */
    case 'h':
        vty_print("debug <cmd> [data]:\r\n <cmd>: e-enable | d-disable\r\n");
        vty_print(" [data]:");
        for(i = 0; i < DBG_M_COUNT; i++)
            vty_print(" %s |", debug_str[i]);
        vty_print("\r\n");
        vty_print("Enable or disable debug module.\r\n");
        return pdFALSE;
    
    default:
        vty_print(cli_param_erro);
        return pdFALSE;
    }

    return pdFALSE;
}
#endif

/* 自动校准. */
static BaseType_t _cli_adc_auto(const char *string)
{
    const char *param = NULL;
    uint8_t param_len = 0;
    uint32_t bitmap = 0;

    param = FreeRTOS_CLIGetParameter(string, 1, (BaseType_t *)&param_len);
    if (NULL == param)
    {
        vty_print(cli_param_erro);
        return pdFALSE;
    }

    if (0 == strncmp(param, "h", param_len))
    {
        vty_print("adc-auto <bitmap>\r\n ADC auto adjust.\r\n");
        return pdFALSE;
    }

    if (sscanf(param, "%x", &bitmap) <= 0)
    {
        vty_print(cli_param_erro);
        return pdFALSE;
    }

    debug_adj_auto(bitmap);

    vty_print(cli_rv_ok);
    return pdFALSE;
}

/* 配置 debug 模式. */
static BaseType_t _cli_dbg_mode(const char *string)
{
    const char *param = NULL;
    uint32_t reset_delay = 2;
    uint8_t param_len = 0;
    
    param = FreeRTOS_CLIGetParameter(string, 1, (BaseType_t *)&param_len);
    if (NULL == param)
    {
        vty_print(cli_param_erro);
        return pdFALSE;
    }

    switch(param[0])
    {
    /* CLI 模式. */
    case 'c':
        dev_config_flag_set(DEV_FLAG_CLI);
        break;
    /* 可视化模式. */
    case 'p':
        dev_config_flag_unset(DEV_FLAG_CLI);
        break;
    /* 帮助信息. */
    case 'h':
        vty_print("dbg-mode <mode>\r\n <mode>: cli | pc\r\n Entry debug mode.\r\n");
        return pdFALSE;
    
    default:
        vty_print(cli_param_erro);
        return pdFALSE;
    }

    flash_log_write(FLASH_LOG_TYPE_INFO, "Change debug mode system reset!\r\n");
    common_sys_set(COM_SYS_SAVE_CONFIG, 0);
    common_sys_set(COM_SYS_RESET, (void*)&reset_delay);
    
    vty_print(cli_rv_ok);
    return pdFALSE;
}

/* 恢复出厂设置.. */
static BaseType_t _cli_default(const char *string)
{
    const char *param = NULL;
    uint8_t param_len = 0;
    
    param = FreeRTOS_CLIGetParameter(string, 1, (BaseType_t *)&param_len);
    if (param != NULL)
    {
        if (param[0] != 'h')
        {
            vty_print(cli_param_erro);
            return pdFALSE;
        }
        else
        {
            vty_print("default\r\n Set config to default.\r\n");
            return pdFALSE;
        }
    }

    dev_config_defaults_set();
    
    vty_print(cli_rv_ok);
    return pdFALSE;
}

/* 配置电源工作模式. */
static BaseType_t _cli_eny_mode(const char *string)
{
    const char *param = NULL;
    uint8_t param_len = 0;
    
    param = FreeRTOS_CLIGetParameter(string, 1, (BaseType_t *)&param_len);
    if (NULL == param)
    {
        vty_print(cli_param_erro);
        return pdFALSE;
    }

    switch(param[0])
    {
    /* 智能模式. */
    case 'a':
        dev_config.energy_mode = ADC_ENERGY_AUTO;
        break;
    /* 正常模式. */
    case 'n':
        dev_config.energy_mode = ADC_ENERGY_NORMAL;
        break;
    /* 节能模式. */
    case 's':
        dev_config.energy_mode = ADC_ENERGY_SLEEP;
        break;
    /* 帮助信息. */
    case 'h':
        vty_print("eny-mode <mode>\r\n <mode>: auto | normal | sleep\r\n Entry energy mode.\r\n");
        return pdFALSE;
    
    default:
        vty_print(cli_param_erro);
        return pdFALSE;
    }

    common_sys_set(COM_SYS_SAVE_CONFIG, 0);
    
    vty_print(cli_rv_ok);
    return pdFALSE;
}

/* 设置主机名. */
static BaseType_t _cli_host(const char *string)
{
    const char *param = NULL;
    uint8_t param_len = 0;

    param = FreeRTOS_CLIGetParameter(string, 1, (BaseType_t *)&param_len);
    if (NULL == param)
    {
        vty_print(cli_param_erro);
        return pdFALSE;
    }

    if (0 == strncmp(param, "h", param_len))
    {
        vty_print("host <name>\r\n Set host name.\r\n");
        return pdFALSE;
    }

    dev_config_host_name_set((uint8_t*)param);

    vty_print(cli_rv_ok);
    return pdFALSE;
}

/* 设置设备 id. */
static BaseType_t _cli_id(const char *string)
{
    const char *param = NULL;
    uint8_t param_len = 0;
    uint32_t id = 0;

    param = FreeRTOS_CLIGetParameter(string, 1, (BaseType_t *)&param_len);
    if (NULL == param)
    {
        vty_print(cli_param_erro);
        return pdFALSE;
    }

    if (0 == strncmp(param, "h", param_len))
    {
        vty_print("id <ID>\r\n Set device id.\r\n");
        return pdFALSE;
    }

    if (sscanf(param, "%x", &id) <= 0)
    {
        vty_print(cli_param_erro);
        return pdFALSE;
    }

    dev_info_id_set(id);

    vty_print(cli_rv_ok);
    return pdFALSE;
}

/* 设置 cli 密码. */
static BaseType_t _cli_password(const char *string)
{
    const char *param = NULL;
    uint8_t param_len = 0;
    
    param = FreeRTOS_CLIGetParameter(string, 1, (BaseType_t *)&param_len);
    if (NULL == param)
    {
        vty_print(cli_param_erro);
        return pdFALSE;
    }

    if (0 == strncmp(param, "h", param_len))
    {
        vty_print("passwd <password>\r\n Set cli password.\r\n");
        return pdFALSE;
    }

    dev_config_password_set((uint8_t*)param);

    vty_print(cli_rv_ok);
    return pdFALSE;
}

/* 设备软复位. */
static BaseType_t _cli_reset(const char *string)
{
    const char *param = NULL;
    uint8_t param_len = 0;
    uint32_t reset_delay = 0;
    
    param = FreeRTOS_CLIGetParameter(string, 1, (BaseType_t *)&param_len);
    if ('h' == param[0])
    {
        vty_print("reset [iap]\r\n Reset system.\r\n");
        return pdFALSE;
    }
    else if('i' == param[0])
    {
        dev_record_reset_type_set(RESET_IAP);
        common_sys_set(COM_SYS_SAVE_RECORD, 0);
    }

    flash_log_write(FLASH_LOG_TYPE_INFO, "Cli system reset!\r\n");
    common_sys_set(COM_SYS_RESET, (void*)&reset_delay);
    
    vty_print(cli_rv_ok);
    return pdFALSE;
}

/* 配置保存命令. */
BaseType_t _cli_save(const char *string)
{
    const char *param = NULL;
    uint8_t param_len = 0;
    
    param = FreeRTOS_CLIGetParameter(string, 1, (BaseType_t *)&param_len);
    if (NULL == param)
    {
        vty_print(cli_param_erro);
        return pdFALSE;
    }

    if (0 == strncmp(param, "h", param_len))
        vty_print("save <config | info | record>\r\n Save info or config.\r\n");
    else if (0 == strncmp(param, "info", param_len))
        common_sys_set(COM_SYS_SAVE_INFO, 0);
    else if (0 == strncmp(param, "config", param_len))
        common_sys_set(COM_SYS_SAVE_CONFIG, 0);
    else if (0 == strncmp(param, "record", param_len))
        common_sys_set(COM_SYS_SAVE_RECORD, 0);

    vty_print(cli_rv_ok);
    return pdFALSE;
}

/* 设置传感器 id. */
static BaseType_t _cli_sen_id(const char *string)
{
    const char *param = NULL;
    uint8_t param_len = 0;
    uint8_t i = 0;
    uint32_t id = 0;

    for(i = 0; i < SENSOR_SUM; i++)
    {
        param = FreeRTOS_CLIGetParameter(string, i + 1, (BaseType_t *)&param_len);
        if (NULL == param)
        {
            vty_print(cli_param_erro);
            return pdFALSE;
        }

        if (0 == strncmp(param, "h", param_len))
        {
            vty_print("sen-id <id1> <id2> <id3> <id4> <id5> <id6> <id7> <id8>\r\n");
            vty_print(" Set sensor id.\r\n");
            return pdFALSE;
        }
        else
        {
            if (sscanf(param, "%d", &id) <= 0)
            {
                vty_print(cli_param_erro);
                return pdFALSE;
            }

            dev_config.sensor_id[i] = id;
        }
    }
    
    vty_print(cli_rv_ok);
    return pdFALSE;
}

/* 打印版本信息. */
static BaseType_t _cli_show(const char *string)
{
    const char *param = NULL;
    uint8_t param_len = 0;
    uint32_t temp = 0;
    
    param = FreeRTOS_CLIGetParameter(string, 1, (BaseType_t *)&param_len);
    if (NULL == param)
    {
        vty_print(cli_param_erro);
        return pdFALSE;
    }

    if (0 == strncmp(param, "h", param_len))
    {
        vty_print("show <cmd> [param]:\r\n");
        vty_print(" <cmd>: info | sen | ADC | ADCi | ADCv | ADCj | mode | wave | wl | position\r\n");
        vty_print(" Show info.\r\n");
    }
    else if(0 == strncmp(param, "ADC", param_len))
    {
        ADC_show();
    }
    else if(0 == strncmp(param, "ADCi", param_len))
    {
        ADCi_show();
    }
    else if(0 == strncmp(param, "ADCv", param_len))
    {
        param = FreeRTOS_CLIGetParameter(string, 2, (BaseType_t *)&param_len);
        if (NULL == param)
        {
            vty_print(cli_param_erro);
            return pdFALSE;
        }

        if (sscanf(param, "%d", &temp) != 1)
        {
            vty_print(cli_param_erro);
            return pdFALSE;
        }
        
        ADC_value_show(temp);
    }
    else if(0 == strncmp(param, "ADCj", param_len))
    {
        param = FreeRTOS_CLIGetParameter(string, 2, (BaseType_t *)&param_len);
        if (NULL == param)
        {
            ADC_adj_show();
            return pdFALSE;
        }

        if (sscanf(param, "%d", &temp) != 1)
        {
            vty_print(cli_param_erro);
            return pdFALSE;
        }
        
        ADC_adj_show_ch(temp);
    }
    else if(0 == strncmp(param, "csg", param_len))
    {
        dev_csg_print();
    }
    else if(0 == strncmp(param, "data", param_len))
    {
        fd_show(20);
    }
    else if(0 == strncmp(param, "info", param_len))
    {
        dev_info_print();
    }
    else if(0 == strncmp(param, "log", param_len))
    {
        flash_log_show(20);
    }
    else if(0 == strncmp(param, "mode", param_len))
    {
        vty_print("Debug mode: %s\r\n", IS_MONITOR_BIT_SET(dev_config.flag, DEV_FLAG_CLI) ? "CLI" : "PC");
        vty_print("Work mode: %s\r\n", IS_MONITOR_BIT_SET(dev_config.flag, DEV_FLAG_ADJ) ? "ADJ" : "NORMAL");
        vty_print("Work mode: %s\r\n", IS_MONITOR_BIT_SET(dev_config.flag, DEV_FLAG_FACTORY) ? "FACTORY" : "NORMAL");
        vty_print("Energy mode: %d\r\n", ADC_ctrl.energy_mode);
        vty_print("Normal sleep: %d\r\n", dev_config.normal_sleep);
        vty_print("Force sleep up: %d\r\n", st_data.force_sleep_up);
        vty_print("BAT charge: %d\r\n", HAL_GPIO_ReadPin(BAT_CHECK_GPIO_Port, BAT_CHECK_Pin));
        vty_print("Collect interval: %d min\r\n", dev_config.collect_interval);
        vty_print("Collect threshold: %d A\r\n", dev_config.threshold);
        vty_print("Wave interval: %d hour\r\n", dev_config.wave_interval);
        vty_print("Wave threshold: %d mV\r\n", dev_config.wave_threshold);
        vty_print("Mian cable: %d\r\n", dev_config.main_cable);
        vty_print("Voltage col: %d\r\n", dev_config.is_voltage_col);
        vty_print("Temp col: %d\r\n", dev_config.is_temp_col);
        vty_print("Wave col: %d\r\n", dev_config.is_wave_col);
    }
    else if(0 == strncmp(param, "sen", param_len))
    {
        sensor_show();
    }
    else if(0 == strncmp(param, "state", param_len))
    {
        st_show(20);
    }
    else if(0 == strncmp(param, "wave", param_len))
    {
        wave_show();
    }
    else if(0 == strncmp(param, "wl", param_len))
    {
        wl_show();
    }
    else if(0 == strncmp(param, "position", param_len))
    {
        position_show(20);
    }
    else
    {
        vty_print(cli_param_erro);
    }
    
    return pdFALSE;
}

/* 测试函数. */
static BaseType_t _cli_test(const char *string)
{
    const char *param = NULL;
    uint8_t param_len = 0;
    
    param = FreeRTOS_CLIGetParameter(string, 1, (BaseType_t *)&param_len);
    if (param != NULL)
    {
        if (param[0] != 'h')
        {
            vty_print(cli_param_erro);
            return pdFALSE;
        }
        else
        {
            vty_print("test\r\n Test.\r\n");
            return pdFALSE;
        }
    }

    //flash_log_write(FLASH_LOG_TYPE_ERROR, "test%d\r\n", 1);
    //flash_log_write(FLASH_LOG_TYPE_WARNING, "test%d\r\n", 2);
    //flash_log_write(FLASH_LOG_TYPE_NOTIFY, "test%d\r\n", 3);
    //flash_log_write(FLASH_LOG_TYPE_INFO, "test%d\r\n", 4);

#if 1
    uint8_t i = 0;
    st_data.wave_time = 0;
    st_data.wave_start = TRUE;
    st_data.wave_up_start = TRUE;
    st_data.wave_index = 0;
    for(i = 0; i < WAVE_SUM; i++)
    {
        st_data.wave_max[i] = 0;
    }
    st_write(&st_data);
    uint32_t reset_delay = 2;
    common_sys_set(COM_SYS_RESET, (void*)&reset_delay);
#endif
    
    vty_print(cli_rv_ok);
    return pdFALSE;
}

/* 工作模式配置命令. */
static BaseType_t _cli_work_mode(const char *string)
{
    const char *param = NULL;
    uint32_t reset_delay = 2;
    uint8_t param_len = 0;
    
    param = FreeRTOS_CLIGetParameter(string, 1, (BaseType_t *)&param_len);
    if (NULL == param)
    {
        vty_print(cli_param_erro);
        return pdFALSE;
    }

    switch(param[0])
    {
    case 'w':
        dev_config_flag_unset(DEV_FLAG_ADJ);
        dev_config_flag_unset(DEV_FLAG_FACTORY);
        break;
    case 'a':
        dev_config_flag_set(DEV_FLAG_ADJ);
        dev_config_flag_unset(DEV_FLAG_FACTORY);
        break;
    case 'f':
        dev_config_flag_unset(DEV_FLAG_ADJ);
        dev_config_flag_set(DEV_FLAG_FACTORY);
        break;
    /* 帮助信息. */
    case 'h':
        vty_print("work-mode <mode>\r\n <mode>: w-work | a-adj | f-factory\r\n Select work mode.\r\n");
        return pdFALSE;
    
    default:
        vty_print(cli_param_erro);
        return pdFALSE;
    }

    flash_log_write(FLASH_LOG_TYPE_INFO, "Change work mode system reset!\r\n");
    common_sys_set(COM_SYS_SAVE_CONFIG, 0);
    common_sys_set(COM_SYS_RESET, (void*)&reset_delay);
    
    vty_print(cli_rv_ok);
    return pdFALSE;
}

/* 命令行等待一个字符串. */
static HAL_StatusTypeDef _cli_wait_string(char *string, uint16_t size, uint32_t timeout)
{
    uint32_t tickstart = HAL_GetTick();
    uint32_t notify_value = 0xff;
    uint16_t index = 0;
    uint8_t key = 0;
    uint8_t cmd_index = 0;

    while(1)
    {
        /* 判断超时. */
        if (timeout != portMAX_DELAY
            && (HAL_GetTick() - tickstart) > timeout)
        {
            string[0] = '\0';
            HAL_UART_AbortReceive(cli_uart);
            return HAL_TIMEOUT;
        }
    
        /* 启动串口中断. */
        if (HAL_OK == HAL_UARTEx_ReceiveToIdle_DMA(cli_uart, cli_buf.buf, USART_BUF_SIZE))
        {
            cli_buf.end = 0;
            cli_buf.is_half = FALSE;
        }

        common_watchdog_set(COM_WDG_CLI);

        /* 等待收包完成. */
        notify_value = ulTaskNotifyTake(pdTRUE, 10000);
        if (0 == notify_value)
        {
            continue;
        }

        /* 命令过长. */
        if (cli_buf.end >= USART_BUF_SIZE)
        {
            cmd_index = 0;
            string[0] = '\0';
            continue;
        }

        /* 收到数据,刷新tick. */
        tickstart = HAL_GetTick();

        for(index = 0; index < cli_buf.end; index++)
        {
            key = cli_buf.buf[index];
            if ('\r' == key || '\n' == key)
            {
                /* 字符串结束. */
                HAL_UART_AbortReceive(cli_uart);
                string[cmd_index] = 0;
                vty_print("\r\n");
                return HAL_OK;
            }
            else if((KEY_BACK == key) || (KEY_DEL == key))
            {
                /* Backspace was pressed. Erase the last character in the string. */
                if (cmd_index > 0)
                {
                    cmd_index--;
                    string[cmd_index] = '\0';
                    vty_print("%c%c%c", KEY_BACK, ' ', KEY_BACK);
                }
            }
            else if((key >= ' ') && (key <= '~'))
            {
                /* A character was entered. Add it to the string entered so far. */
                if (cmd_index < size - 1)
                {
                    string[cmd_index] = key;
                    if (!IS_MONITOR_BIT_SET(cli_machine.flag, CLI_FLAG_PASSWORD))
                        vty_print("*");
                    else
                        vty_print("%c", key);
                    cmd_index++;
                }
            }
        }
    }
}

/* cli处理函数. */
static void _cli_process(void)
{
    BaseType_t rv = 0;

    for(;;)
    {
        /* 获取命令字符串. */
        if (_cli_wait_string(cli_machine.cmd, CLI_CMD_SIZE, CLI_TIMEOUT) != HAL_OK)
        {
            vty_print("\r\nCli is timeout.\r\n");
            return;
        }

        /* 处理命令. */
        do
        {        
            rv = FreeRTOS_CLIProcessCommand(cli_machine.cmd);
        } while(rv != pdFALSE);
        FreeRTOS_CLIPrintPrefix((char*)dev_config.host);
    }
}

/* 命令行系统相关初始化. */
static void _cli_start(void)
{
    /* 命令注册. */
#if LD_DEBUG
    FreeRTOS_CLIRegisterCommand(&cmd_debug, CLI_NODE_COMMON);
#endif
    FreeRTOS_CLIRegisterCommand(&cmd_adc_auto, CLI_NODE_CONFIG);
    FreeRTOS_CLIRegisterCommand(&cmd_dbg_mode, CLI_NODE_CONFIG);
    FreeRTOS_CLIRegisterCommand(&cmd_default, CLI_NODE_CONFIG);
    FreeRTOS_CLIRegisterCommand(&cmd_eny_mode, CLI_NODE_CONFIG);
    FreeRTOS_CLIRegisterCommand(&cmd_host, CLI_NODE_CONFIG);
    FreeRTOS_CLIRegisterCommand(&cmd_id, CLI_NODE_CONFIG);
    FreeRTOS_CLIRegisterCommand(&cmd_pw, CLI_NODE_CONFIG);
    FreeRTOS_CLIRegisterCommand(&cmd_reset, CLI_NODE_CONFIG);
    FreeRTOS_CLIRegisterCommand(&cmd_save, CLI_NODE_CONFIG);
    FreeRTOS_CLIRegisterCommand(&cmd_sen_id, CLI_NODE_CONFIG);
    FreeRTOS_CLIRegisterCommand(&cmd_show, CLI_NODE_COMMON);
    FreeRTOS_CLIRegisterCommand(&cmd_test, CLI_NODE_COMMON);
    FreeRTOS_CLIRegisterCommand(&cmd_work_mode, CLI_NODE_CONFIG);

    for(;;)
    {
        /* 提示输入密码. */
        vty_print("Password: ");
    
        /* 等待输入密码. */
        _cli_wait_string(cli_machine.cmd, CLI_CMD_SIZE, portMAX_DELAY);
        if (0 == strncmp((char*)dev_config.password, cli_machine.cmd, DEV_PASSWORD_LEN - 1))
        {
            FreeRTOS_CLIPrintPrefix((char*)dev_config.host);
            MONITOR_BITMAP_SET(cli_machine.flag, CLI_FLAG_PASSWORD);
            _cli_process();
            MONITOR_BITMAP_RESET(cli_machine.flag, CLI_FLAG_PASSWORD);
        }
        else
            vty_print("Password not match!!!\r\n");
    }
}

/* 命令行系统相关初始化. */
void _cli_init_os(void)
{
    /* 初始化互斥锁. */
    cli_machine.mutex = osMutexNew(&cli_mutex_attr);
    /* 致命错误,此处不应该被运行,记录错误log,打印错误信息. */
    if (NULL == cli_machine.mutex)
        ERROR_PRINT(-1);
}

/* Interface functions -------------------------------------------------------*/
/* 命令行线程开始函数. */
void cli_start(void const * argument)
{
    /******** START INIT ********/   
    /* 以下代码是freeRTOS启动后第一个运行的任务,因为优先级最高,且最早创建.所以需要在freeRTOS启动后再初始化
     * 的地方统一放在下面这段地方. */
    /* crc32 初始化. */
    crc32_table_init();
    /* 命令行系统相关初始化. */
    _cli_init_os();
    /* spi flash 初始化. */
    spi_flash_init_os();
    FLASH_If_Init();
    /* 配置系统初始化. */
    dev_config_init_os();
    dev_config_init();
    flash_log_init();
    st_init();
    /* 公共监控任务初始化. */
    common_sys_init_os();
    /* 默认开启的 debug. */
    dbg_cmd_hander(DBG_CMD_ON, DBG_M_4G);
    //dbg_cmd_hander(DBG_CMD_ON, DBG_M_RS485_SEN);
    //dbg_cmd_hander(DBG_CMD_ON, DBG_M_WAVE);
    /* 初始化 ADC 采样任务. */
    ADC_init_os();
    /* 初始化高频录波任务. */    
    wave_init_os();
    if (!IS_MONITOR_BIT_SET(dev_config.flag, DEV_FLAG_ADJ)
        && !IS_MONITOR_BIT_SET(dev_config.flag, DEV_FLAG_FACTORY))
    {
        /* 4G 通讯任务.*/
        wl_init_os();
    }

    /* 在这个标志置位前vty_print是原地循环等待,置位后变为任务调度. */
    MONITOR_BITMAP_SET(system_init_flag, SYS_INIT_OS);
    flash_log_write(FLASH_LOG_TYPE_INFO, "System start!\r\n");
    /********* END INIT *********/
    
    if (IS_MONITOR_BIT_SET(dev_config.flag, DEV_FLAG_CLI))
    {
        /* 使用命令行调试. */
        _cli_start();
    }
    else
    {
        /* 使用 PC 调试软件调试. */
        debug_start();
    }
}

/* description: 将cli需要发送的数据存入缓存.
 * param: 无.
 * return: 无. */
void cli_usart_data_buf_send(char *format, va_list ap)
{
    cli_output_buf_t *buf = &cli_machine.output_buf;
    uint16_t index = 0;
    int16_t num = 0;

    /* 串口为中断发送,在没有发送完前不能写数据. */
    while(HAL_UART_STATE_BUSY_TX == cli_uart->gState)   
        osThreadYield();

    _cli_mutex_lock();
    
    index = buf->index;
    if ((num = vsnprintf(&buf->buf[index], CLI_OUTPUT_SIZE - index, format, ap)) < 0)
    {
        _cli_mutex_unlock();
        return;
    }

    if ((index + num) >= CLI_OUTPUT_SIZE)
        buf->index = CLI_OUTPUT_SIZE - 1;
    else
        buf->index = index + num;
    
    _cli_mutex_unlock();
}

/* description: 在操作系统没有启动的情况下发送cli数据.
 * param: 无.
 * return: 无. */
void cli_usart_data_buf_send_without_os(char *format, va_list ap)
{
    cli_output_buf_t *buf = &cli_machine.output_buf;

    while(HAL_UART_STATE_BUSY_TX == cli_uart->gState)
    {
        continue;
    }
    
    if (vsnprintf(buf->buf, CLI_OUTPUT_SIZE, format, ap) > 0)
    {
        HAL_GPIO_WritePin(RS485_DBG_EN_GPIO_Port, RS485_DBG_EN_Pin, GPIO_PIN_SET);
        HAL_UART_Transmit_DMA(cli_uart, (uint8_t*)buf->buf, strlen(buf->buf));
    }
}

/* description: 将cli缓存数据通过串口发送出去.
 * param: 无.
 * return: 无. */
void cli_data_buf_send_hw(void)
{
    cli_output_buf_t *buf = &cli_machine.output_buf;

    if (HAL_UART_STATE_BUSY_TX == cli_uart->gState)   
        return;

    _cli_mutex_lock();
    if (buf->index > 0)
    {
        HAL_GPIO_WritePin(RS485_DBG_EN_GPIO_Port, RS485_DBG_EN_Pin, GPIO_PIN_SET);
        HAL_UART_Transmit_DMA(cli_uart, (uint8_t*)buf->buf, buf->index);
        buf->index = 0;
    }
    _cli_mutex_unlock();
}
/******************* (C) COPYRIGHT LandPower ***** END OF FILE ****************/
