/*****************************************************************************
 * file    lib/process/pd_main.c  
 * author  YuLiang
 * version 1.0.0
 * date    07-Feb-2023
 * brief   This file provides all the partial discharge related operation functions.
 ******************************************************************************
 * Attention
 *
 * 
© COPYRIGHT(c) 2021 LandPower
 *
 * 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 LandPower 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 ------------------------------------------------------------------*/
//#ifdef HAVE_CONFIG_H
#include "config.h"
//#endif
#ifdef CFG_DEV_TYPE_LAND_PD
/* 标准C库头文件. */
#include 
#include 
#include 
#include 
#include 
#include 
#include "pd_dau.h"
#include "pd_csg.h"
#include "pd_main.h"
#include "pd_dbg.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define PD_BOARD_PORT 12345
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
pd_data_t pd_data;
pd_config_t pd_config;
pd_state_t pd_state;
cmd_node_t pd_port_node =
{
    PORT_NODE,
    CONFIG_NODE,
    1,
    NULL,
};
/* Private function prototypes -----------------------------------------------*/
extern int32_t _pd_port_str_to_unit_port(const char *port_str, uint8_t *unit, uint8_t *port);
extern void _pd_port_config_save_port(vty_t *vty, uint8_t unit, uint8_t port);
extern int32_t _dau_add(uint8_t unit, uint8_t port_num);
/* Internal functions --------------------------------------------------------*/
/* 进入 DAU 端口模式. */
CMD(pd_port_terminal,
    pd_port_terminal_cmd,
    "interface port WORD",
    "Interface\n"
    "Port\n"
    "Port id: Ex: 1/1\n")
{
    uint8_t unit = 0;
    uint8_t port = 0;
    int32_t vport = 0;
    /* 取出端口号. */
    if (_pd_port_str_to_unit_port(argv[0], &unit, &port) != E_NONE)
    {
        return CMD_ERR_NO_MATCH;
    }
    /* 计算虚拟端口. */
    vport = dau_port_to_vport(unit, port);
    if (vport <= 0)
    {
        return CMD_ERR_NO_MATCH;
    }
    
    pd_port_node.param_num = vport;
    snprintf(pd_port_node.prompt, PD_PORT_PROMPT_LEN, "%%s(interface port %d/%d)# ", unit + 1, port + 1);
    
    vty->node = PORT_NODE;
    
    return CMD_SUCCESS;
}
/* 心跳上传间隔. */
CMD(pd_heartbeat_interval,
    pd_heartbeat_interval_cmd,
    "heartbeat-interval <1-10000>",
    "heartbeat interval\n"
    "Interval time: s\n")
{
    pd_config.config.heartbeat_period = strtol(argv[0], NULL, 10);
    return CMD_SUCCESS;
}
/* 趋势上升周期. */
CMD(pd_trend_interval,
    pd_trend_interval_cmd,
    "trend-interval <1-10000>",
    "Trend interval\n"
    "Interval time: s\n")
{
    pd_config.config.trend_up_period = strtol(argv[0], NULL, 10);
    return CMD_SUCCESS;
}
/* 采样中断传输间隔配置. */
CMD(pd_interrupt_interval,
    pd_interrupt_interval_cmd,
    "interrupt-interval <100-1000000>",
    "Interrupt interval\n"
    "interval: us\n")
{
    dau_t * dau_node = dau[0];
    if(strtol(argv[0], NULL, 10) <= 1000000)
    {
        pd_config.config.interrupt_interval = strtol(argv[0], NULL, 10);
        if(dau_node->is_connect == TRUE)
        {
            dau_node->reg->reg_global.sample_interrupt_intveal_us = pd_config.config.interrupt_interval;
        }
        return CMD_SUCCESS;
    }
    else
    {
        return CMD_ERR_NO_MATCH;
    }
}
/* 触发阈值设置. */
CMD(pd_trig_threshold,
    pd_trig_threshold_cmd,
    "trig threshold <1-32767>",
    "Trigger\n"
    "Threshold\n"
    "mv\n")
{
    dau_t * dau_node = dau[0];
    if(strtol(argv[0], NULL, 10) <= 32767)
    {
        pd_config.config.trig_threshold = strtol(argv[0], NULL, 10);
        if(dau_node->is_connect == TRUE)
        {
            dau_node->reg->reg_global.trig_threshold = pd_config.config.trig_threshold;
        }
        return CMD_SUCCESS;
    }
    else
    {
        return CMD_ERR_NO_MATCH;
    }
}
/* 触发位置设置. */
CMD(pd_sample_numbers,
    pd_sample_numbers_cmd,
    "sample numbers <1000-10240>",
    "Sample\n"
    "Numbers\n"
    "1000~10240\n")
{
    if(strtol(argv[0], NULL, 10) <= 10240)
    {
        pd_config.config.trigger_sample_nums = strtol(argv[0], NULL, 10);
        return CMD_SUCCESS;
    }
    else
    {
        return CMD_ERR_NO_MATCH;
    }
}
/* 触发位置设置. */
CMD(pd_trig_location,
    pd_trig_location_cmd,
    "trig location <0-12>",
    "Trigger\n"
    "Location\n"
    "0~12\n")
{
    dau_t * dau_node = dau[0];
    if(strtol(argv[0], NULL, 10) <= 12)
    {
        pd_config.config.trig_location = strtol(argv[0], NULL, 10);
        if(dau_node->is_connect == TRUE)
        {
            dau_node->reg->reg_global.trig_location = pd_config.config.trig_location;
        }
        return CMD_SUCCESS;
    }
    else
    {
        return CMD_ERR_NO_MATCH;
    }
}
/* 触发间隔. */
CMD(pd_trig_gap,
    pd_trig_gap_cmd,
    "trig gap <1-100000>",
    "Trigger\n"
    "Gap\n"
    "us\n")
{
    dau_t * dau_node = dau[0];
    if(strtol(argv[0], NULL, 10) <= 100000)
    {
        pd_config.config.trig_gap = strtol(argv[0], NULL, 10);
        if(dau_node->is_connect == TRUE)
        {
            dau_node->reg->reg_global.trig_gap = pd_config.config.trig_gap;
        }
        return CMD_SUCCESS;
    }
    else
    {
        return CMD_ERR_NO_MATCH;
    }
}
/* IRIG-B 极性设置. */
CMD(pd_irigB_polarity,
    pd_irigB_polarity_cmd,
    "irigB polarity <0-1>",
    "IrigB\n"
    "Polarity\n"
    "0-nanrui,1-normal\n")
{
    dau_t * dau_node = dau[0];
    if((strtol(argv[0], NULL, 10) <= 1)&&(strtol(argv[0], NULL, 10) >= 0))
    {
        pd_config.config.irigB_polarity = strtol(argv[0], NULL, 10);
        if(dau_node->is_connect == TRUE)
        {
            dau_node->reg->reg_global.irigB_polarity = pd_config.config.irigB_polarity;
        }
        return CMD_SUCCESS;
    }
    else
    {
        return CMD_ERR_NO_MATCH;
    }
}
/* PT和B码同步模式设置. */
CMD(pd_pt_B_sync_mode,
    pd_pt_B_sync_mode_cmd,
    "pt_igirB syncMode <0-3>",
    "Pt_igirB\n"
    "SyncMode\n"
    "0-ptExt&BExt,1-ptInt&BExt,2-ptExt&BInt,3-ptInt&BInt\n")
{
    dau_t * dau_node = dau[0];
    if((strtol(argv[0], NULL, 10) <= 3)&&(strtol(argv[0], NULL, 10) >= 0))
    {
        pd_config.config.pt_B_sync_mode = strtol(argv[0], NULL, 10);
        if(dau_node->is_connect == TRUE)
        {
            dau_node->reg->reg_global.pt_B_sync_mode = pd_config.config.pt_B_sync_mode;
            if (IS_BITMAP_SET(pd_config.config.pt_B_sync_mode, PD_BIT_BCODE))
            {
                dau_node->reg->reg_global.ps_epoch_sec = time(NULL);  
            }
        }
        return CMD_SUCCESS;
    }
    else
    {
        return CMD_ERR_NO_MATCH;
    }
}
/* PT 内同步周期设置. */
CMD(pd_internal_freq,
    pt_internal_freq_cmd,
    "pt intFreq <40.00-300.00>",
    "Pt\n"
    "IntFreq\n"
    "40.00-300.00Hz\n")
{
    dau_t * dau_node = dau[0];
    float freq = atof(argv[0]);
    if (freq >= 40.0 && freq <= 300.0)
    {
        pd_config.config.pt_internal_period = (uint32_t)(1000000000.0 / freq);
        if (dau_node->is_connect == TRUE)
        {
            dau_node->reg->reg_global.pt_selfsync_cycle = pd_config.config.pt_internal_period;
        }
        return CMD_SUCCESS;
    }
    else
    {
        return CMD_ERR_NO_MATCH;
    }
}
/* 显示 DAU 状态. */
CMD(show_pd,
    show_pd_cmd,
    "show pd",
    "Show\n"
    "Partial discharge\n")
{    
    pd_show();
    return CMD_SUCCESS;
}
/* 在终端上显示当前配置信息. */
CMD(show_running_port,
    show_running_port_cmd,
    "show running-config interface port WORD",
    SHOW_STR
    "Running configuration\n"
    "Interface\n"
    "Port\n"
    "Port id: Ex: 1/1\n")
{
    uint8_t unit = 0;
    uint8_t port = 0;
    int32_t vport = 0;
    /* 取出端口号. */
    if (_pd_port_str_to_unit_port(argv[0], &unit, &port) != E_NONE)
    {
        return CMD_ERR_NO_MATCH;
    }
    
    /* 计算虚拟端口. */
    vport = dau_port_to_vport(unit, port);
    if (vport <= 0)
    {
        return CMD_ERR_NO_MATCH;
    }
    vty_out(vty, "Current configuration:%s", VTY_NEWLINE);
    vty_out(vty, "!%s", VTY_NEWLINE);
    _pd_port_config_save_port(vty, unit, port);
    vty_out(vty, "end%s", VTY_NEWLINE);
    return CMD_SUCCESS;
}
uint32_t _pd_get_frequency()
{
    uint32_t frequency;
    
    if (IS_BITMAP_SET(pd_config.config.pt_B_sync_mode, PD_BIT_PT))
    {
        frequency = (uint32_t)(1000000000UL / pd_config.config.pt_internal_period);
    }
    else 
    {
        dau_t * dau_node = dau[0];
        if (dau_node->is_connect != TRUE)
            return E_ERROR;
        frequency = (uint32_t)(1000000000UL / dau_node->reg->reg_global.pt_cycle);;
    }
    return frequency;        
}
int _pd_read_register_century_second()
{
    dau_t * dau_node = dau[0];
   
    if (dau_node->is_connect != TRUE)
    {
        return E_ERROR;
    }
    return dau_node->reg->reg_global.b_time_epoch_sec;
}
/* 配置保存函数. */
int _pd_config_save(vty_t* vty)
{
    int16_t i = 0;
    vty_out(vty, "heartbeat-interval %d%s", pd_config.config.heartbeat_period, VTY_NEWLINE);
    i++;
    vty_out(vty, "interrupt-interval %d%s", pd_config.config.interrupt_interval, VTY_NEWLINE);
    i++;
    vty_out(vty, "trig gap %d%s", pd_config.config.trig_gap, VTY_NEWLINE);
    i++;
    vty_out(vty, "sample numbers %d%s", pd_config.config.trigger_sample_nums, VTY_NEWLINE);
    i++;
    vty_out(vty, "trig location %d%s", pd_config.config.trig_location, VTY_NEWLINE);
    i++;
    vty_out(vty, "trig threshold %d%s", pd_config.config.trig_threshold, VTY_NEWLINE);
    i++;
    vty_out(vty, "trend-interval %d%s", pd_config.config.trend_up_period, VTY_NEWLINE);
    i++;
    vty_out(vty, "pt_igirB syncMode %d%s", pd_config.config.pt_B_sync_mode, VTY_NEWLINE);
    i++;
    vty_out(vty, "pt intFreq %.2f%s", 1000000000.0 / pd_config.config.pt_internal_period, VTY_NEWLINE);
    i++;
    vty_out(vty, "irigB polarity %d%s", pd_config.config.irigB_polarity, VTY_NEWLINE);
    i++;
    return i;
}
/* config模式配置保存函数: vty -- 相应的终端 */
void _pd_port_config_save_port(vty_t *vty, uint8_t unit, uint8_t port)
{
    array_t *configs = pd_port_node.configs;
    pd_port_cmd_save_config_f *func = NULL;
    uint8_t i = 0;
    vty_out(vty, "interface port %d/%d%s", unit + 1, port + 1, VTY_NEWLINE);
    for(i = 0; i < array_active(configs); i++)
    {
        func = array_lookup(configs, i);
        if (!func)
        {
            continue;
        }
        
        func(vty, unit, port);
    }
}
/* config模式配置保存函数: vty -- 相应的终端 */
int32_t _pd_port_config_save(vty_t *vty)
{
    uint8_t unit = 0;
    uint8_t port = 0;
    /* 其他配置保存 */
    for(unit = 0; unit < PD_DAU_SUM; unit++)
    {
        if (!dau_is_valid(dau[unit]))
        {
            continue;
        }
    
        for(port = 0; port < dau[unit]->port_num; port++)
        {
            _pd_port_config_save_port(vty, unit, port);
        }
    }
    return E_NONE;
}
/* 将端口字符串, 转换成 unit port 格式. */
int32_t _pd_port_str_to_unit_port(const char *port_str, uint8_t *unit, uint8_t *port)
{
    char *str = NULL;
    char *p = NULL;
    char temp[8];
    uint8_t len = 0;
    uint8_t i = 0;
    snprintf(temp, 8, "%s", port_str);
    str = strtok_r(temp, "/", &p);
    while(str != NULL)
    {
        /* 检查长度. */
        if (len >= 2)
        {
            return E_BAD_PARAM;
        }
    
        /* 检查字符串 */
        for(i = 0; str[i] && str[i] != '\0'; i++)
        {
            if (!(str[i] >= '0' && str[i] <= '9'))
            {
                return E_BAD_PARAM;
            }
        }
        /* 检查数据长度 */
        if (i != 1)
        {
            return E_BAD_PARAM;
        }
        /* 读取板卡和端口号. */
        if (0 == len)
        {
            *unit = strtol(str, NULL, 10) - 1;
        }
        else
        {
            *port = strtol(str, NULL, 10) - 1;
        }
        len++;
        /* 获取下个数据 */
        str = strtok_r(NULL, "/", &p);
    }
    return E_NONE;
}
/* 广播接收程序 */
void *_pd_broadcast_handle(void *arg)
{
    struct sockaddr_in broad_addr;
    struct sockaddr_in peer_addr;
    socklen_t addr_len = 0;
    char packet[1024];
    int fd = 0;
    int len = 0;
    int rv = -1;
    
    /* 创建报文套接字 */
    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == fd)
    {
        log_err(LOG_PD, "Fail to open broadcast socket.");
        return NULL;
    }
    bzero(&broad_addr, sizeof(broad_addr));
    broad_addr.sin_family = AF_INET;
    broad_addr.sin_port = htons(PD_BOARD_PORT);
    broad_addr.sin_addr.s_addr = INADDR_ANY;
    rv = bind(fd, (struct sockaddr *)&broad_addr, sizeof(broad_addr));
    if (-1 == rv)
    {
        log_err(LOG_PD, "Fail to bind broad socket.");
        return NULL;
    }
    while (1)
    {
        /* 接收广播 */
        len = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr *)&peer_addr, &addr_len);
        if (-1 == len)
        {
            continue;
        }
        
        packet[len] = '\0';
        if (strncmp(packet, "hello", 5) == 0)
        {
            len = snprintf(packet, 1024, "world,DAU5G_V%s,", version_get());
            snprintf(packet + len, 1024 - len, "%s", device_info.hostname);
            sendto(fd, packet, strlen(packet) + 1, 0, (struct sockaddr *)&peer_addr, sizeof(peer_addr));
        }
    }
    /* 关闭套接字 */
    close(fd);
    return NULL;
}
int32_t _pd_broadcast_init(void)
{
    struct sched_param param;
    pthread_attr_t attr;
    pthread_t pid;
    int32_t rv = E_NONE;
    /* 初始化广播报文处理线程. */
    /* 配置线程RR调度, 优先级25 */
    pthread_attr_init(&attr);
    param.sched_priority = 25;
    pthread_attr_setschedpolicy(&attr, SCHED_RR);
    pthread_attr_setschedparam(&attr, ¶m);
    pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
    rv = pthread_create(&pid, &attr, _pd_broadcast_handle, NULL);
    if (rv != 0)
    {
        log_err(LOG_PD, "PD can't create broadcast pthread %d!", rv);
        return E_SYS_CALL;
    }
    else
    {
        thread_m_add("PD_BROADCAST", pid);
    }
    pthread_attr_destroy(&attr);
    return E_NONE;
}
int32_t _pd_main_init(void)
{
    uint8_t i = 0;
    uint8_t j = 0;
    int32_t rv = 0;
    _dau_add(0, PD_DAU_PORT_SUM);
    /* 初始化基本参数. */
    pd_config.config.heartbeat_period = DEFAULT_HEARTBEAT_INTVEAL;
    pd_config.config.ch_en_mask = DEFAULT_PORT_ENBALE;
    pd_config.config.interrupt_interval = DEFAULT_INTVEAL_TIME_US;
    pd_config.config.trigger_sample_nums = DEFAULT_SAMPLE_NUM;
    pd_config.config.sample_frequency = DEFAULT_SAMPLE_FREQ_MHZ;
    pd_config.config.trig_gap = DEFAULT_TRIG_GAP;
    pd_config.config.trig_location = DEFAULT_TRIG_LOCATION;
    pd_config.config.trig_threshold = DEFAULT_TRIG_THRESHOLD;
    pd_config.config.pt_B_sync_mode = DEFAULT_PT_B_SYNC_MODE;
    pd_config.config.trend_up_period = DEFAULT_TREND_PERIOD;  
    pd_config.config.irigB_polarity = DEFAULT_IGIRB_MODE;
    pd_config.config.pt_internal_period = DEFAULT_PT_INTFREQ;
    for(i = 0; i < PD_DAU_SUM; i++)
    {
        for(j = 0; j < PD_DAU_PORT_SUM; j++)
        {
            /* 端口初始化*/
            if(READ_BIT(pd_config.config.ch_en_mask, 0x1<= PD_PORT_CMD_PRI_COUNT || !func)
    {
        return E_BAD_PARAM;
    }
    /* 加入列表 */
    array_set(node->configs, pri, func, MTYPE_DAU);
    return 0;
}
void pd_show(void)
{
    dau_t * dau_node = dau[0];
    printh("Connect: %s\r\n", csg.is_connect ? "yes" : "no");
    //printh("ext sync: %dHz\r\n", _pd_read_pt_ext_sync_freq());
    if(dau_node->is_connect == TRUE)
    {
        printh("pt_B_sync_mode=0x%x\r\n", dau_node->reg->reg_global.pt_B_sync_mode);
        printh("b_time_epoch_sec:%ld\r\n", dau_node->reg->reg_global.b_time_epoch_sec);
    }
}
#else
int32_t PD_main(void)
{
    return 0;
}
#endif
/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****/