//
// Created by zx on 2019/12/9.
//

#include "plc_data.h"
#include <string.h>
Plc_data *Plc_data::g_ins = 0;
std::mutex Plc_data::g_lock;

/**
 * 获取单例句柄
 * */
Plc_data *Plc_data::get_instance(std::string ip)
{
    if (g_ins == 0)
    {
        std::lock_guard<std::mutex> lock(g_lock);
        if (g_ins == 0)
        {
            if (ip != "")
            {
                g_ins = new Plc_data(ip);
                LOG(INFO) << "plc data, handle created";
            }
            else
            {
                return 0;
            }
        }
    }
    return g_ins;
}

// void Plc_data::Release()
// {
//     if (g_ins)
//     {
//         g_ins->m_cond_exit.Notify(true);
//         LOG(INFO) << "plc data, try to exit plc send thread";
//         g_lock.lock();
//         if (g_ins->p_send_thread)
//         {
//             LOG(INFO) << "plc data, try to join plc send thread";
//             // LOG(INFO) << g_ins->p_send_thread->joinable();
//             if (g_ins->p_send_thread->joinable())
//                 g_ins->p_send_thread->join();
//             LOG(INFO) << "plc data, try to delete plc send thread";
//             delete g_ins->p_send_thread;
//             g_ins->p_send_thread = 0;
//             LOG(INFO) << "plc data, delete data send thread";
//         }
//         g_lock.unlock();

//         LOG(INFO) << "plc data, start to exit plc handle";
//         // if(g_instance->p_plc){
//         //     LOG(INFO)<<"plc data, try to delete plc handle";
//         //     delete g_instance->p_plc;
//         //     g_instance->p_plc = 0;
//         // }
//         // LOG(INFO)<<"plc data, delete plc handle";
//         LOG(INFO) << "plc data, delete instance";
//         // delete g_ins;
//         // g_ins = 0;
//         LOG(INFO) << "plc data, instance deleted";
//     }
//     else
//     {
//         LOG(WARNING) << "plc data, cannot find the instance";
//     }
// }

/**
 * 更新区域状态
 * */
void Plc_data::update_data(int state_code, int border_status, int id)
{
//     LOG(INFO) << "plc data 更新数据 id: "<<id<<", code: "<<state_code;
    if(id<0 || id>=MAX_REGIONS)
        return ;
    std::lock_guard<std::mutex> lock(g_lock);
    m_data[2 * id] = state_code;
    m_data[2 * id + 1] = border_status;
}

/**
 * plc更新线程，将区域状态写入plc
 * */
void Plc_data::plc_update_thread(Plc_data *p)
{
    if (p == 0)
    {
        LOG(ERROR) << "plc update thread null pointer";
        return;
    }
    while (!p->m_cond_exit.wait_for_millisecond(1))
    {

        // 判断plc状态
        if (p->m_plc.getConnection() && p->mb_is_ready)
        {


            for (int i = 0; i < ELE_FENCE_COUNT; ++i)
            {

                g_lock.lock();
                p->mb_is_ready = p->m_plc.WriteShorts(ELE_FENCE_DB_NUM,
                                                      ELE_FENCE_BOUNDARY_START_ADDR + i * ELE_FENCE_OFFSET,
                                                      2,
                                                      p->m_data + (i * 2));
                memcpy(p->m_last_data + (i * 2), p->m_data + (i * 2), 2 * sizeof(short));

                g_lock.unlock();
                usleep(10 * 1000);
            }

        }
        else
        {
            // 重连plc
            LOG(WARNING) << "find plc connection error, trying to reconnect.";
            g_lock.lock();
            p->m_plc.disconnect();
            // LOG(WARNING) << "find plc connection error, diconnect first.";
            if (p->m_ip_str != "")
            {
                p->mb_is_ready = p->m_plc.connect(p->m_ip_str);
                // LOG(WARNING) << "find plc connection error, trying to connect";
                if (p->mb_is_ready)
                {
                    LOG(INFO) << "successfully reconnect.";
                }
                else
                {
                    LOG(WARNING) << "failed to connect plc.";
                }
            }
            g_lock.unlock();
            usleep(p->m_update_interval_milli * 5 * 1000);
        }
        usleep(p->m_update_interval_milli * 1000);
    }
}

/**
 * 有参构造
 * */
Plc_data::Plc_data(std::string ip) : mp_update_thread(0),
                                     m_ip_str(""),
                                     mb_is_ready(0)
{
    m_ip_str = ip;
    memset(m_data, 0, MAX_REGIONS*2 * sizeof(short));
    memset(m_last_data, 0, MAX_REGIONS*2 * sizeof(short));
    // p_plc = new S7PLC();
    if (m_ip_str != "")
    {
        if (m_plc.connect(m_ip_str))
        {
            mb_is_ready = true;
        }
        else
        {
            LOG(ERROR) << "Plc_data instance, connect failed";
        }
    }
    else
    {
        LOG(ERROR) << "Plc_data instance, empty ip string.";
    }
    m_cond_exit.notify_all(false);
    mp_update_thread = new std::thread(plc_update_thread, this);
    mp_update_thread->detach();
}

/**
 * 获取plc连接状态
 * */
bool Plc_data::get_plc_status()
{
    return mb_is_ready;
}