#ifndef PLC_COMMUNICATOR_HH
#define PLC_COMMUNICATOR_HH

#include <iostream>
#include <string.h>
#include <mutex>
#include <vector>
#include <thread>
#include <unistd.h>
#include <chrono>
#include <cmath>
// #include <fstream>
// #include <stdint.h>
// #include <unistd.h>
// #include <sys/types.h>
// #include <sys/stat.h>
// #include <fcntl.h>

// #include "../error.h"
#include "../task/task_command_manager.h"
#include "../error_code/error_code.h"
#include "plc_task.h"
#include "LibmodbusWrapper.h"
#include "../tool/StdCondition.h"
#include "../tool/MeasureTopicPublisher.h"
#include "plc_module.pb.h"
#include "plc_message.pb.h"

#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/text_format.h>
using google::protobuf::io::FileInputStream;
using google::protobuf::io::FileOutputStream;
using google::protobuf::io::ZeroCopyInputStream;
using google::protobuf::io::CodedInputStream;
using google::protobuf::io::ZeroCopyOutputStream;
using google::protobuf::io::CodedOutputStream;
using google::protobuf::Message;
#include "glog/logging.h"

#include <nnxx/message.h>
#include <nnxx/message_control.h>
#include <nnxx/socket.h>
#include <nnxx/reqrep.h>
//#include <nnxx/unittest.h>
#include <nnxx/timeout.h>
#include <nnxx/error.h>
#include <cstring>
#include <nnxx/message.h>

const int PLC_SIGNAL_BEGIN_OFFSET=2;
const int PLC_REGION_NUM=6;
const int PLC_SIGNAL_NUM_PER_REGION=12;
const int PLC_SLEEP_IN_MILLISECONDS=200;

const int PLC_LASER_START_ADDR = 0;
const int PLC_LASER_STATUS_ADDR = 1;
const int PLC_LASER_X_ADDR = 2;
const int PLC_LASER_Y_ADDR = 3;
const int PLC_LASER_ANGLE_ADDR = 4;
const int PLC_LASER_LENGTH_ADDR = 5;
const int PLC_LASER_WIDTH_ADDR = 6;
const int PLC_LASER_HEIGHT_ADDR = 7;
const int PLC_LASER_CORRECTNESS_ADDR = 8;
const int PLC_LASER_WHEELBASE_ADDR = 9;

typedef Error_manager(*Command_Callback)(int terminal_id, void * p_owner);

// plc通信类，modbus通信
class Plc_Communicator
{
    
public:
    Plc_Communicator(plc_module::plc_connection_params connection_params);
    ~Plc_Communicator();

    // get set 方法
    bool get_initialize_status();
    bool get_connection();
    Error_manager get_error();

    // 设置plc检测到指令后外部回调函数
    Error_manager set_plc_callback(Command_Callback callback, void * p_owner);
    // 执行任务单
    Error_manager execute_task(Task_Base* task);
    // 获取实时数据
    Error_manager get_plc_data(std::vector<uint16_t> &plc_data,int terminal_id=-1);
    // 设置plc状态更新超时时间
    Error_manager set_status_update_timeout(int millisecond);

    struct plc_region_status{
        std::chrono::steady_clock::time_point last_time_point;
        int current_status;
        int cmd;
    };

private:
    // 读写线程函数
    Error_manager ReadProtoParam(std::string path);
    static void plc_update_thread(Plc_Communicator* plc_communicator);
    static void plc_publish_message(Plc_Communicator* plc);
    Error_manager write_result_to_plc(struct measure_result result);
    // 连接函数
    Error_manager connect();
    Error_manager disconnect();

private:
    bool                mb_plc_is_connected;     // 指示plc连接状态
    bool                mb_plc_initialized;      // 指示plc是否初始化
    bool                mb_plc_is_updating;      // 指示plc线程在运行
    void*               mp_plc_owner;            // 回调函数所有者句柄
    Command_Callback    m_plc_callback;         // 回调函数

    std::thread*        m_plc_message_thread;           // plc
    std::thread*        m_plc_thread;           // plc更新线程句柄
    StdCondition        m_plc_cond_exit;        // plc更新线程退出条件控制变量

    std::mutex          m_plc_mutex;            // plc更新互斥锁，锁住与wrapper相关的所有操作
    modbus::CLibmodbusWrapper   m_plc_wrapper;  // plc连接与读写封装实例

    std::string         m_plc_ip_str;               // plc连接ip
    int                 m_plc_port;             // plc连接端口
    int                 m_plc_slave_id;         // plc连接id
    int                 m_plc_status_update_timeout;    // plc状态更新超时时间

    std::vector<uint16_t>   m_plc_data;         // 从plc获取的实时数据
    Error_manager       m_plc_current_error;    // 当前plc出现的错误
    // 当前系统状态，实时更新到plc。状态254-255每1秒互换，状态1从收到指令开始，紧接着改为状态2，
    // 之后根据外部传入的task，决定写入3或4，默认3保留3秒，4持续保留直到新指令
    plc_region_status   m_plc_region_status[PLC_REGION_NUM];   

    // plc_module::plc_connection_params m_connection_params; // 连接参数
};

#endif // !PLC_COMMUNICATOR_HH
