/*
 * @Description: cifx RE 50 PNS slave, communicate with PLC through Profinet IO
 * @Author: yct
 * @Date: 2020-04-26 13:39:47
 * @LastEditTime: 2020-04-28 18:01:51
 * @LastEditors: yct
 */
#include "cifx_communicator.h"
using namespace agv;

Cifx_communicator *Cifx_communicator::g_cifx_ins = nullptr;
std::mutex Cifx_communicator::g_cifx_access_mutex;

/*****************************************************************************/
/*! Displays cifX error
*   \param lError     Error code                                             */
/*****************************************************************************/
std::string show_error(int32_t lError)
{
    if (lError != CIFX_NO_ERROR)
    {
        char szError[1024] = {0};
        char buf[1152] = {0};
        xDriverGetErrorDescription(lError, szError, sizeof(szError));
        sprintf(buf, "Error: 0x%X, <%s>\n", (unsigned int)lError, szError);
        return std::string(buf);
    }
    return std::string("No Error\n");
}

// 获取通信卡实例
Cifx_communicator *Cifx_communicator::get_instance()
{
    if (g_cifx_ins == nullptr)
    {
        std::lock_guard<std::mutex> lck(g_cifx_access_mutex);
        if (g_cifx_ins == nullptr)
        {
            g_cifx_ins = new Cifx_communicator();
        }
    }
    return g_cifx_ins;
}

// 释放单例
void Cifx_communicator::release_instance()
{
    std::lock_guard<std::mutex> lck(g_cifx_access_mutex);
    if(g_cifx_ins != nullptr){
        std::cout<<"cifx 释放"<<std::endl;
        delete g_cifx_ins;
        g_cifx_ins = nullptr;
    }
}

// 初始化通信卡
Error_manager Cifx_communicator::cifx_init()
{
    m_cifx_state = CIFX_INSTANCE_CREATED;
    // 清除数据
    while (m_write_cmd_queue.size() != 0)
        m_write_cmd_queue.pop();
    m_cifx_read_data_buffer.clear();
    // 初始化读写数据块
    m_cifx_read_data_buffer.push_back(AREA_DATA_BLOCK(WHEEL_INPUT_AREA));

    // 初始化驱动，linux下特有
    int32_t lRet = cifXDriverInit(&m_init_params);
    std::string error_str = show_error(lRet);
    if (lRet != CIFX_NO_ERROR)
    {
        return Error_manager(CIFX_DRIVER_INIT_ERROR, Error_level::MAJOR_ERROR, error_str.c_str(), error_str.length());
    }
    m_cifx_state = CIFX_DRIVER_INITAILIZED;

    // 开启驱动
    lRet = xDriverOpen(&m_cifx_driver_handle);
    error_str = show_error(lRet);
    if (lRet != CIFX_NO_ERROR)
    {
        return Error_manager(CIFX_DRIVER_OPEN_ERROR, Error_level::MAJOR_ERROR, error_str.c_str(), error_str.length());
    }
    m_cifx_state = CIFX_DRIVER_OPENED;
    usleep(1000 * 40); // 驱动开启后需要约40ms的等待时间

    // 开启通道
    lRet = xChannelOpen(NULL, CIFX_DEV, 0, &m_cifx_driver_handle);
    error_str = show_error(lRet);
    if (lRet != CIFX_NO_ERROR)
    {
        return Error_manager(CIFX_CHANNEL_OPEN_ERROR, Error_level::MAJOR_ERROR, error_str.c_str(), error_str.length());
    }
    m_cifx_state = CIFX_CHANNEL_OPENED;

    // 开启通信总线
    unsigned long ulState;
    lRet = xChannelBusState(m_cifx_driver_handle, CIFX_BUS_STATE_ON, (TLR_UINT32 *)&ulState, 10000);
    error_str = show_error(lRet);
    if (lRet != CIFX_NO_ERROR)
    {
        return Error_manager(CIFX_BUS_OPEN_ERROR, Error_level::MAJOR_ERROR, error_str.c_str(), error_str.length());
    }
    m_cifx_state = CIFX_BUS_IS_ON;

    m_data_exchange_thread = new std::thread(&Cifx_communicator::data_exchange, g_cifx_ins);
    m_data_exchange_thread->detach();
    mb_initialized = true;
    return SUCCESS;
}

// 通过cifx将数据从PLC读入
Error_manager Cifx_communicator::cifx_read(uint32_t area_number, uint32_t offset, uint32_t data_length, char *data)
{
    if (data_length + offset > MAX_DATA_SIZE)
        return PARAMETER_ERROR;
    // std::lock_guard<std::mutex> lck(m_cifx_read_mutex);
    if (m_read_state.state != SUCCESS)
        return CIFX_READ_FAILED;
    if (std::chrono::duration_cast<std::chrono::duration<double, std::micro>>(std::chrono::steady_clock::now() - m_read_state.update_time).count() > 20000)
        return CIFX_READ_TIMEOUT;
    bool read_success = false;
    for (size_t i = 0; i < m_cifx_read_data_buffer.size(); i++)
    {
        if (m_cifx_read_data_buffer[i].area_number == area_number)
        {
            memcpy(data, m_cifx_read_data_buffer[i].data + offset, data_length);
            read_success = true;
            break;
        }
    }
    if (!read_success)
    {
        return PARAMETER_ERROR;
    }
    else
    {
        return SUCCESS;
    }
}

// 通过cifx将数据写入PLC
Error_manager Cifx_communicator::cifx_write(uint32_t area_number, uint32_t offset, uint32_t data_length, char *data)
{
    // std::lock_guard<std::mutex> lck(m_cifx_write_mutex);
    if (m_write_state.state != SUCCESS)
        return CIFX_WRITE_FAILED;
    // if (std::chrono::duration_cast<std::chrono::duration<double, std::micro>>(std::chrono::steady_clock::now() - m_write_state.update_time).count() > 20000)
    //     return CIFX_WRITE_TIMEOUT;
    m_write_cmd_queue.push(WRITE_CMD_DATA_BLOCK(area_number, offset, data_length, data));
    // std::cout<<"write queue size: "<<m_write_cmd_queue.size()<<std::endl;
    return SUCCESS;
}

// 获取初始化状态
bool Cifx_communicator::is_initialized()
{
    return mb_initialized;
}

// 无参构造函数
Cifx_communicator::Cifx_communicator()
{
    m_read_callback = nullptr;
    mp_callback_owner = nullptr;
    m_cifx_state = CIFX_UNINITIALIZED;
    mb_exit = false;
    mb_thread_exit = false;
    mb_initialized = false;
    m_data_exchange_thread = nullptr;
    m_cifx_driver_handle = nullptr;
    m_init_params.init_options = CIFX_DRIVER_INIT_AUTOSCAN;
    m_init_params.iCardNumber = 0;
    m_init_params.fEnableCardLocking = 0;
    m_init_params.base_dir = NULL;
    m_init_params.poll_interval = 0;
    m_init_params.poll_StackSize = 0; /* set to 0 to use default */
    m_init_params.trace_level = 255;
    m_init_params.user_card_cnt = 0;
    m_init_params.user_cards = NULL;
    m_init_params.poll_priority = 0;
}

// 析构函数
Cifx_communicator::~Cifx_communicator()
{
    mb_exit = true;
    unsigned long ulState;
    while(m_data_exchange_thread != nullptr && !mb_thread_exit){
        // std::cout << "等待线程退出" << std::endl;
        usleep(1);
    }
    std::cout << "析构线程" << std::endl;
    if (m_data_exchange_thread != nullptr)
    {
        if (m_data_exchange_thread->joinable())
        {
            m_data_exchange_thread->join();
        }
        delete m_data_exchange_thread;
        m_data_exchange_thread = nullptr;
    }
    std::cout << "释放SDK" << std::endl;
    // 顺序释放cifx通信卡SDK
    if (m_cifx_state == CIFX_BUS_IS_ON)
        xChannelBusState(m_cifx_driver_handle, CIFX_BUS_STATE_OFF, (TLR_UINT32 *)&ulState, 10000);
    if (m_cifx_state == CIFX_CHANNEL_OPENED)
        xChannelClose(m_cifx_driver_handle);
    if (m_cifx_state == CIFX_DRIVER_OPENED)
        xDriverClose(m_cifx_driver_handle);
    if (m_cifx_state == CIFX_DRIVER_INITAILIZED)
        cifXDriverDeinit();
}

// 设置读数据回调，获取到不同数据时激活
Error_manager Cifx_communicator::set_read_data_callback(CIFX_READ_CALLBACK callback, void* pOwner)
{
    m_read_callback = callback;
    mp_callback_owner = pOwner;
    return SUCCESS;
}

// 读写线程函数
void Cifx_communicator::data_exchange(Cifx_communicator *cifx_handle)
{
    std::cout << "线程启动" << std::endl;
    if (cifx_handle == nullptr)
    {
        std::cout << "空指针" << std::endl;
        return;
    }
    if (!cifx_handle->is_initialized())
    {
        std::cout << "未初始化" << std::endl;
        return;
    }
    while (!cifx_handle->mb_exit)
    {
        // 连接状态正常
        if (cifx_handle->m_cifx_state == CIFX_BUS_IS_ON)
        {
            // std::cout << " 000000 " << std::endl;
            // 读取PLC数据
            // cifx_handle->m_cifx_read_mutex.lock();
            for (size_t i = 0; i < cifx_handle->m_cifx_read_data_buffer.size(); i++)
            {
                if (cifx_handle->m_cifx_read_data_buffer[i].area_number == WHEEL_INPUT_AREA)
                {
                    // std::cout << " 000111 " << std::endl;
                    char t_read_data[WHEEL_INPUT_AREA_SIZE]={0};
                    int32_t lRet = xChannelIORead(cifx_handle->m_cifx_driver_handle,
                                                  WHEEL_INPUT_AREA, 0, WHEEL_INPUT_AREA_SIZE, t_read_data, 10);
                    // std::cout << " 000222 " << std::endl;
                    if (lRet != CIFX_NO_ERROR)
                    {
                        if(cifx_handle->m_read_state.state != CIFX_READ_FAILED)
                            std::cout << "cifx read failed" << std::endl;
                        cifx_handle->m_read_state.state = CIFX_READ_FAILED;
                        cifx_handle->m_read_state.update_time = std::chrono::steady_clock::now();
                    }
                    else
                    {
                        // 比较差异（暂时不比较全部传输），激活回调
                        int cmp = memcmp(t_read_data, cifx_handle->m_cifx_read_data_buffer[i].data, WHEEL_INPUT_AREA_SIZE);
                        if(/*cmp != 0 &&*/ cifx_handle->m_read_callback != nullptr && cifx_handle->mp_callback_owner != nullptr){
                            memcpy(cifx_handle->m_cifx_read_data_buffer[i].data, t_read_data, WHEEL_INPUT_AREA_SIZE);
                            AREA_DATA_BLOCK t_read_block = cifx_handle->m_cifx_read_data_buffer[i];
                            cifx_handle->m_read_callback(t_read_block, cifx_handle->mp_callback_owner);
                        }
                        cifx_handle->m_read_state.state = SUCCESS;
                        cifx_handle->m_read_state.update_time = std::chrono::steady_clock::now();
                        // std::cout << "cifx read success" << std::endl;
                    }
                }
            }
            // cifx_handle->m_cifx_read_mutex.unlock();

            // 写入数据到PLC
            // cifx_handle->m_cifx_write_mutex.lock();
            // std::cout<<"write2 queue size: "<<cifx_handle->m_write_cmd_queue.size()<<std::endl;
            while (cifx_handle->m_write_cmd_queue.size()!=0)
            {
                // std::cout << " 123 " << std::endl;
                WRITE_CMD_DATA_BLOCK t_write_block = cifx_handle->m_write_cmd_queue.front();
                int32_t lRet = xChannelIOWrite(cifx_handle->m_cifx_driver_handle,
                                               t_write_block.area_number, t_write_block.offset, t_write_block.data_length, t_write_block.data, 10);
                // std::cout << " 1234 " << std::endl;
                if (lRet != CIFX_NO_ERROR)
                {
                    if(cifx_handle->m_write_state.state != CIFX_WRITE_FAILED)
                        std::cout << "cifx write failed" << std::endl;
                    cifx_handle->m_write_state.state = CIFX_WRITE_FAILED;
                    cifx_handle->m_write_state.update_time = std::chrono::steady_clock::now();
                }
                else
                {
                    cifx_handle->m_write_state.state = SUCCESS;
                    cifx_handle->m_write_state.update_time = std::chrono::steady_clock::now();
                    // std::cout << "cifx write success" << std::endl;
                }
                cifx_handle->m_write_cmd_queue.pop();
            }
            // cifx_handle->m_cifx_write_mutex.unlock();
        }
        // 连接状态异常，尝试重连
        else
        {
        }
        //std::this_thread::yield();
        usleep(1000*4);
    }
    std::cout << "线程退出" << std::endl;
    cifx_handle->mb_thread_exit = true;
}

void data_debug(char *data, int length)
{
    char buf[255] = {0};
    sprintf(buf, "current data: ");
    for (size_t i = 0; i < length; i++)
    {
        sprintf(buf + strlen(buf), "\t%u", uint8_t(*(data + i)));
    }
    // sprintf(buf + strlen(buf), "\n");
    std::cout << buf << std::endl;
}

// int main()
// {
//     Cifx_communicator *cc = Cifx_communicator::get_instance();
//     Error_manager ec = cc->cifx_init();
//     std::cout << "init state: " << cc->is_initailized() << std::endl;

//     usleep(1000 * 50);
//     if (ec == SUCCESS)
//     {
//         char read_buffer[8] = {0};
//         char write_buffer[8] = {2};
//         for (size_t i = 0; i < 1000000; i++)
//         {
//             ec = cc->cifx_read(0, 0, 8, read_buffer);
//             if (ec == SUCCESS)
//             {
//                 data_debug(read_buffer, sizeof(read_buffer));
//             }
//             else
//             {
//                 std::cout << ec.to_string() << std::endl;
//             }

//             ec = cc->cifx_write(0, 0, 8, read_buffer);
//             // if (ec == SUCCESS)
//             // {
//             //     data_debug(read_buffer, sizeof(read_buffer));
//             // }
//             // else
//             // {
//             //     std::cout << ec.to_string() << std::endl;
//             // }
//             usleep(1000 * 2);
//         }
//         usleep(1000*2000);
//         std::cout << "---------------------end------------------" << std::endl;
//     }
//     Cifx_communicator::release_instance();
// }
