#include "stdio.h"
#include "PLCDevice.h"
#include <iostream>
#include <common.h>
#include <QDebug>

extern SysConf g_sys_conf;

bool PLCDevice::init_plc(PLCDevice* PLCptr)
{
    // modbus_connect() 建立连接，成功返回0，错误返回-1
    // modbus_new_rtu 生成RTU的连接，建立成功则返回指向modbus_t结构的指针，否则将返回NULL/0

    const char* comport = g_sys_conf.ComPort.data();
    qDebug() << "COM:" << comport;
    //连接PLC
    PLCptr->g_modbus = modbus_new_rtu(comport, 9600, 'N', 8, 1);
    modbus_set_debug(PLCptr->g_modbus, 0);	// 用flag设置debug调试标志位，flag=1时显示modbus消息的字节
    modbus_set_response_timeout(PLCptr->g_modbus, 1, 0);	// 设置响应超时
    modbus_connect(PLCptr->g_modbus);
    modbus_set_slave(PLCptr->g_modbus, 1);	// 设置从站id

    uint8_t data;
    int ret = modbus_read_bits(PLCptr->g_modbus, 30000, 1, &data);
    if (PLCptr->g_modbus && ret == -1) {
        PLCptr->g_plc_ok = false;
    }
    else {

        PLCptr->g_plc_ok = true;
    }
    return true;
}

bool PLCDevice::init_plc_tcp(PLCDevice* PLCptr)
{
#ifdef __ModebusServer
    PLCptr->g_modbus = modbus_new_tcp("192.168.1.180", 2000);
    modbus_set_debug(PLCptr->g_modbus, 0);	// 用flag设置debug调试标志位，flag=1时显示modbus消息的字节
    modbus_set_response_timeout(PLCptr->g_modbus, 1, 0);	// 设置响应超时
    modbus_connect(PLCptr->g_modbus);
    modbus_set_slave(PLCptr->g_modbus, 1);	// 设置从站id

    uint8_t data;
    int ret = modbus_read_bits(PLCptr->g_modbus, 0, 1, &data);
    // 连接失败
    if (-1 == ret)
    {
        PLCptr->g_plc_ok = false;
        qDebug("TCP connect failed:%s\n", modbus_strerror(errno));
    }
    else
    {
        PLCptr->g_plc_ok = true;      
        qDebug("TCP connect successful ");
    }
#endif
#ifdef __ModebusClient
    const char* ip = g_sys_conf.TcpIP.data();
    qDebug() << "Client ip:" << ip;
    qDebug() << "Client port:" << g_sys_conf.TcpPort;
    PLCptr->g_modbus = modbus_new_tcp(ip, g_sys_conf.TcpPort);
    modbus_set_debug(PLCptr->g_modbus, 0);	// 用flag设置debug调试标志位，flag=1时显示modbus消息的字节
    modbus_set_response_timeout(PLCptr->g_modbus, 1, 0);	// 设置响应超时
    // 侦听主站连接
    PLCptr->m_modbusSocket = modbus_tcp_listen(PLCptr->g_modbus, 1);  // 最多支持的主机数量   1
    /*设置线圈, 离散输入, 输入寄存器, 保持寄存器个数(数组元素个数))*/
    PLCptr->mapping = modbus_mapping_new(PLCptr->m_numBits, PLCptr->m_numInputBits, PLCptr->m_numInputRegisters, PLCptr->m_numRegisters);
    if (PLCptr->mapping == NULL)
    {
        modbus_free(PLCptr->g_modbus);
        PLCptr->m_initialized = false;
        return false;
    }
    PLCptr->m_initialized = true;

    PLCptr->modSerThread = new std::thread(&PLCDevice::recieveMessages, PLCptr);
    PLCptr->modSerThread->detach();
    std::cout << "Running ModbusTcpSlave" << std::endl;
#endif
    return true;
}

#ifdef __ModebusClient
/***************************************************************
 * @file       PLCDevice.cpp
 * @author     seer-txj
 * @brief      支持多个master同时连接
 * @version    v1
 * @return     null
 * @date       2021/10/6
 **************************************************************/
void PLCDevice::recieveMessages()
{
    uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];

    int fd_num = 0, fd_max = 0, ret = 0, i = 0, clnt_sock = -1;
    fd_set reads, cpy_reads;
    FD_ZERO(&reads);
    FD_SET(m_modbusSocket, &reads);
    fd_max = m_modbusSocket;
    while (m_initialized)
    {
        cpy_reads = reads;
        if ((fd_num = select(fd_max + 1, &cpy_reads, 0, 0, 0)) == -1)
            break;
        if (fd_num == 0)
            continue;
        for (i = 0; i < fd_max + 1; i++)
        {
            if (FD_ISSET(i, &cpy_reads))
            {
                if (i == m_modbusSocket)
                {
                    clnt_sock = modbus_tcp_accept(g_modbus, &m_modbusSocket);
                    if ((m_modbusSocket == -1) || (clnt_sock == -1))
                    {
                        std::cerr << modbus_strerror(errno) << std::endl;
                        continue;
                    }
                    FD_SET(clnt_sock, &reads);
                    if (fd_max < clnt_sock)
                        fd_max = clnt_sock;
                }
                else
                {
                    ret = modbus_receive(g_modbus, query);
                    if (ret == 0)
                    {
                        m_errCount = 0;
                        continue;
                    }
                    else if (ret > 0)
                    {
                        m_errCount = 0;
// 填写输出内容，用于测试，实际输出在threadSendTCP::sendData中实现，这边只要reply
  /*                      mapping->tab_registers[0] = 0;
                        mapping->tab_registers[1] = 11;
                        mapping->tab_registers[2] = 22;

                        mapping->tab_input_registers[0] = 0;
                        mapping->tab_input_registers[1] = 33;
                        mapping->tab_input_registers[2] = 44;

                        mapping->tab_bits[0] = 0;
                        mapping->tab_bits[1] = 1;
                        mapping->tab_bits[2] = 0;

                        mapping->tab_input_bits[0] = 1;
                        mapping->tab_input_bits[1] = 0;
                        mapping->tab_input_bits[2] = 1;*/

                        modbus_reply(g_modbus, query, sizeof(query), mapping);
                    }
                    else
                    {
                        modbus_set_error_recovery(g_modbus, MODBUS_ERROR_RECOVERY_NONE);
                        modbus_set_error_recovery(g_modbus, MODBUS_ERROR_RECOVERY_LINK);
                        modbus_close(g_modbus);
                        FD_CLR(i, &reads);
#ifdef _WIN32
                        closesocket(i);
#else
                        close(i);
#endif // _WIN32

                        m_errCount++;
                    }
                    if(m_errCount > 5)
                    {
                        m_initialized = false;
                        break;
                    }
                }
            }
        }
    }
    m_initialized = false;
}
#endif

bool PLCDevice::disconnect_plc(void)
{
    if (g_modbus)
    {
        modbus_close(g_modbus);
        modbus_free(g_modbus);
    }
    return true;
}

int PLCDevice::write_bit_2_plc(int addr, int value)
{
    if (g_plc_ok)
    {
        g_mutex.lock();
        int ret = modbus_write_bit(g_modbus, addr, value);
        g_mutex.unlock();
        //printf("modbus_write_bit:addr=%d,value=%d,ret=%d\n", addr, value, ret);
        return ret;
    }
    else {
        //std::cout << "PLC no ok" << std::endl;
        return false;
    }
}

int PLCDevice::read_bits_from_plc(int addr, int nb, uint8_t* value)
{
    if (g_plc_ok)
    {
        g_mutex.lock();
        int ret = modbus_read_bits(g_modbus, addr, nb, value);
        g_mutex.unlock();
        //printf("modbus_read_bits:addr=%d,nb=%d,value=%d,ret=%d\n", addr, nb, *value, ret);
        return ret;
    }
    else {
        //std::cout << "PLC no ok" << std::endl;
        return false;
    }
}

int PLCDevice::write_short_2_plc(int addr, int nb, uint16_t* value)
{
    if (g_plc_ok)
    {
        g_mutex.lock();
        int ret = modbus_write_registers(g_modbus, addr, nb, value);
        g_mutex.unlock();
        //printf("modbus_write_registers:addr=%d,nb=%d,value=%d,ret=%d\n", addr, nb, *value, ret);
        return ret;
    }
    else {
        //std::cout << "PLC no ok" << std::endl;
        return false;
    }
}

int PLCDevice::read_short_from_plc(int addr, int nb, uint16_t* value)
{
    if (g_plc_ok)
    {
        g_mutex.lock();
        int ret = modbus_read_registers(g_modbus, addr, nb, value);
        g_mutex.unlock();
        //printf("modbus_read_registers:addr=%d,nb=%d,value=%d,ret=%d\n", addr, nb, *value ,ret);
        return ret;
    }
    else {
        //std::cout << "PLC no ok" << std::endl;
        return false;
    }
}

int PLCDevice::write_2_plc(int addr, int nb, uint16_t* value)
{
    if (g_plc_ok)
    {
        //g_mutex.lock();
        int ret = modbus_write_registers(g_modbus, addr, nb, value);
        //g_mutex.unlock();
        //printf("modbus_write_registers:addr=%d,nb=%d,value=%d,ret=%d\n", addr, nb, *value, ret);
        return ret;
    }
    else {
        //std::cout << "PLC no ok" << std::endl;
        return false;
    }
}