Browse Source

添加已有代码

LiuZe 10 months ago
parent
commit
9f4d195e43

+ 2 - 0
.gitignore

@@ -67,6 +67,8 @@ CMakeCache.txt
 CMakeFiles
 CMakeScripts
 Makefile
+
+*.pb.*
 cmake_install.cmake
 install_manifest.txt
 

+ 14 - 0
message/CMakeLists.txt

@@ -0,0 +1,14 @@
+set(LIBRARY_NAME zxmessage)
+
+# 获取当前目录下的所有源文件
+aux_source_directory(${CMAKE_CURRENT_LIST_DIR} GLOB_RECURSE)
+
+set(LIBRARY_SOURCE_LIST
+        ${GLOB_RECURSE}
+)
+
+set(LIBRARY_DEPEND_LIST)
+
+add_library(${LIBRARY_NAME} ${LIBRARY_SOURCE_LIST})
+target_link_libraries(${LIBRARY_NAME} PUBLIC ${LIBRARY_DEPEND_LIST})
+

+ 277 - 0
message/measure.proto

@@ -0,0 +1,277 @@
+syntax = "proto3";
+
+enum Range_status {
+    Range_correct = 0x0000;                 // 未超界
+    Range_front = 0x0001;                   //前超界
+    Range_back = 0x0002;                    //后超界
+    Range_left = 0x0004;                    // 左超界
+    Range_right = 0x0008;                   // 右超界
+    Range_bottom = 0x0010;                  //底盘超界
+    Range_top = 0x0020;                     // 车顶超界
+    Range_car_width = 0x0040;               // 车宽超界
+    Range_car_wheelbase = 0x0080;           // 轴距超界
+    Range_angle_anti_clock = 0x0100;        // 左(逆时针)旋转超界
+    Range_angle_clock = 0x0200;             // 右(顺时针)旋转超界
+    Range_steering_wheel_nozero = 0x0400;   // 方向盘未回正
+    Range_car_moving = 0x0800;              // 车辆移动为1,静止为0
+}
+
+enum MeasureStatu {
+    Measure_OK = 0;                 // 测量完成
+    Measure_Empty_Clouds = 1;       // 测量结果:测量区域数据为空
+    Measure_Failture = 2;           // 测量结果:测量区域数据无法检测出车辆
+    Measure_Border = 3;             // 测量结果:对于PLC, 车辆存在超界,具体超界状态对比Range_status
+    Measure_Terminal_Border = 4;    // 测量结果:对于终端, 车辆存在超界,具体超界状态对比Range_status
+    Lidar_Disconnect = 5;           // 测量雷达:失去连接
+
+    Measure_Statu_Max = 6;
+}
+
+/*测量信息*/
+message measure_info {
+    float cx=1;                                 // 车辆中心坐标x
+    float cy=2;                                 // 车辆中心坐标y
+    float theta=3;                              // 车身偏转角度(相对于y轴,左正右负)
+    float length=4;                             // 车身长度(厦门四个雷达,含有该值,楚天两个雷达,该值为0)
+    float width=5;                              // 车身宽度(左右两侧轮子最大宽度)
+    float height=6;                             // 车身高度
+    float wheelbase=7;                          // 车辆前后轴距
+    float front_theta=8;                        // 车辆前轮偏转角度
+    int32 border_statu=9;                       // 超界状态, 位运算
+    MeasureStatu ground_status=10;              // 测量状态,0=正常, 1=空, 2=测量失败, 3=超界, 4=终端超界, 5=雷达断连
+    int32 is_stop=11;                           // <是否可停> 1为可停,0为不可停
+    int32 motion_statu=12;                      // 运动状态,0=运动, 1=静止(只有三秒内都是静止才会写1,只要瞬间触发运动就会写0)
+    float move_distance=13;                     // 前进距离
+}
+
+/*分配的车位信息*/
+message parkspace_info{
+    int32 id=1;
+    int32 serial_id=2;    //排序id
+    int32 table_id=3;     //标签id
+    int32 unit_id=4;      //单元号
+    int32 floor=5;        //楼层号
+    int32 room_id=6;      //同层编号
+    float height=7;       //车高档位
+}
+
+//任务表单状态
+enum STATU{
+    eNormal=0;        // 正常
+    eWarning=1;       // 警告
+    eError=2;         // 错误
+    eCritical=3;      // 严重错误
+}
+
+//表单流程模式
+enum Table_process_mode
+{
+    PROCESS_NORMAL = 0;             // 0:正常模式, 检查节点会向收费系统发送请求,收费系统的答复 通过后,再向调度发送请求
+    PROCESS_ONLY_TO_DISPATCH = 1;   // 1:强制存取车,检查节点会向收费系统发送请求,忽略收费系统的答复,直接向调度发送请求。
+    PROCESS_ONLY_TO_PAY = 2;        // 2:虚拟存取车, 检查节点会向收费系统发送请求,忽略收费系统的答复。
+}
+
+/*
+表单执行状态
+ */
+message table_statu{
+    STATU execute_statu=1;                    // 执行状态
+    string statu_description=2;               // 状态描述
+    Table_process_mode table_process_mod=3;   // 表单流程模式
+}
+/*
+号牌信息
+ */
+message plate_number_info
+{
+    string plate_number = 1;        // 号牌
+    string plate_color = 2;         // 号牌颜色
+    string plate_type = 3;          // 号牌类型, 车辆类型
+    int32  plate_confidence = 4;    // 号牌可信度, 1-100 值越高可信度越高
+    string recognition_time = 5;    // 识别时间点, yyyyMMddHHmmss
+    string plate_full_image = 6;    // 号牌全景图, base64编码
+    string plate_clip_image = 7;    // 号牌特写图, base64编码
+}
+/*
+停车表单
+ */
+message park_table{
+    table_statu statu=1;                      //表单状态
+    int32 queue_id=2;                         //指令排队编号
+
+    string car_number=3;
+    int32 unit_id=4;
+    int32 terminal_id=5;
+    string primary_key=6;
+
+    measure_buffer entrance_measure_info=7;     // 入口测量信息
+    parkspace_info allocated_space_info=8;    // 分配的车位信息
+    measure_buffer actually_measure_info=9;     // 实际测量信息或者叫二次测量信息
+    parkspace_info actually_space_info=10;    // 实际停放的车位
+    int32 import_id =11;                      // 入口id, 1~2
+
+    plate_number_info car_number_info = 12;   // 车牌号信息
+}
+
+/*
+取车表单
+ */
+message pick_table{
+    table_statu statu=1;      // 表单状态
+    int32 queue_id=2;         // 指令排队编号
+
+    string car_number=3;
+    int32 unit_id=4;
+    int32 terminal_id=5;
+    string primary_key=6;
+
+    parkspace_info actually_space_info=7;     // 实际停放的车位信息
+
+    measure_buffer actually_measure_info=8;     // 存车时的实际测量信息(轴距)
+
+    int32 export_id=9;                        // 出口id, 3~4
+    bool  is_leaved=10;                       // 是否离开
+    plate_number_info car_number_info = 11;   // 车牌号信息
+}
+
+/*
+以下是状态消息
+ */
+
+/*
+单片机节点状态
+ */
+message out_mcpu_statu{     //数值+1后
+    int32 door_statu=1;       //外门状态       0无效, 1无效, 2开到位, 3 关到位, 4开关中, 5 故障
+    int32 outside_safety=2;    //是否有车      0无效, 1无车, 2有车
+}
+
+message in_mcpu_statu{      //数值+1后
+    int32 door_statu=1;       //外门状态       0无效, 1无效, 2开到位, 3 关到位, 4开关中, 5 故障
+    int32 back_io=2;          //后超界       0无效, 1后超界, 2正常
+    int32 is_occupy=3;        //是否有车      0无效, 1无车, 2有车
+    int32 heighth=4;          //车高状态      0无效, 1无效, 2小车, 3中车, 4大车, 5故障, 6故障
+}
+/*
+测量节点状态
+ */
+message measure_buffer{
+    measure_info measure_info_to_plc_forward=1;     //雷达数据给plc,正向
+    measure_info measure_info_to_plc_reverse=2;     //雷达数据给plc,反向
+    measure_info measure_info_to_terminal=3;        //雷达数据给终端,边界较小
+}
+
+//搬运器状态枚举
+enum CarrierStatu{
+    eIdle=0;
+    eBusy=1;
+    eFault=2;
+}
+
+
+
+
+/*
+//楚天调度入口汽车范围的修正信息
+message dispatch_region_info
+{
+    int32 terminal_id=1;                //入口终端编号, 1~6
+    float turnplate_angle_min=2;        //转盘角度最小值, 负值, 例如 -5度
+    float turnplate_angle_max=3;        //转盘角度最大值, 正值, 例如 +5度
+}
+
+//楚天搬运器状态消息
+message dispatch_node_statu{
+    CarrierStatu statu=1;
+    int32 idle_stop_floor=2;  //空闲时停留位置
+    park_table  running_pack_info=3;  //正在执行的停车表单
+    pick_table  running_pick_info=4;  //正在执行的取车表单
+
+    int32                                   unit_id = 5;                            //单元号, 1~3
+    int32                                   plc_heartbeat = 6;                      //plc心跳信息
+    int32                                   plc_status_info = 7;                    //plc状态的集合
+                                                                                            //0 bit, 手动模式
+                                                                                            //1 bit, 自动模式
+                                                                                            //2 bit, 自动运行中
+                                                                                            //3 bit, 复位
+                                                                                            //4 bit, 1号口可以进车
+                                                                                            //5 bit, 2号口可以进车
+                                                                                            //6 bit, 预留
+                                                                                            //7 bit, 预留
+    repeated dispatch_region_info           dispatch_region_info_vector = 8;        //调度入口汽车范围的修正信息
+}
+ */
+
+
+
+//plc出入口状态结构体
+message dispatch_plc_passway_status
+{
+    int32			car_height = 1;			            //车高 0=无车 1=小车 2=中车 3=大车 4=超高车
+    int32			outside_door_status = 2;	        //外门状态 0=无状态 1=开到位 2=关到位
+    int32			inside_door_status = 3;	            //内门状态 0=无状态 1=开到位 2=关到位
+    int32			comb_body_status = 4;	            //梳体状态 0=无状态 1=上到位 2=下到位, AB特有
+    float			turnplate_angle_min = 5;	        //转盘角度最小值, C特有, 负值, 例如 -5度
+    float			turnplate_angle_max = 6;	        //转盘角度最大值, C特有, 正值, 例如 +5度
+    int32			sensor_1 = 7;				        //传感器状态的集合1
+                                                        //0 bit, 地感 0=无车 1=有车
+                                                        //1 bit, 移动检测 0=运动 1=静止
+                                                        //2 bit, 动态超限 0=遮挡 1=正常
+                                                        //3 bit, 后超界 0=遮挡 1=正常
+                                                        //4 bit, 前超界 0=遮挡 1=正常
+                                                        //5 bit, 左超界 0=遮挡 1=正常
+                                                        //6 bit, 右超界 0=遮挡 1=正常
+                                                        //7 bit, 车高小车, 0=遮挡 1=正常,AB单元为1480, C单元为1780,
+    int32			sensor_2 = 8;				        //传感器状态的集合1
+                                                        //0 bit, 车高中车, 0=遮挡 1=正常,AB单元为1500, C单元为1800,
+                                                        //1 bit, 车高大车, 0=遮挡 1=正常,AB单元为2050, C单元为2050,
+                                                        //2 bit, 有车检测 0=无车 1=有车
+                                                        //3 bit, 车轮1检测 0=无车 1=有车, AB特有
+                                                        //4 bit, 车轮2检测 0=无车 1=有车, AB特有
+                                                        //5 bit, 预留
+                                                        //6 bit, 预留
+                                                        //7 bit, 预留
+    int32           plc_passway_enable=9;               //出入口 0=不可进车, 1=可进车, 2=维护不可进车, 3=维护可进车
+}
+
+//厦门搬运器状态消息
+message dispatch_node_statu{
+    CarrierStatu                            statu=1;
+    int32                                   idle_stop_floor=2;          //空闲时停留位置
+    park_table                              running_pack_info=3;        //正在执行的停车表单
+    pick_table                              running_pick_info=4;        //正在执行的取车表单
+    int32                                   unit_id = 5;                //单元号, 1~3
+
+    int32                                   plc_heartbeat = 6;                      //plc心跳信息
+    int32                                   plc_mode_status = 7;                    //plc状态的集合
+                                                                                    //0 bit, 手动模式, 维修模式
+                                                                                    //1 bit, 自动模式
+                                                                                    //2 bit, 自动运行中
+                                                                                    //3 bit, 复位
+                                                                                    //4 bit, 预留
+                                                                                    //5 bit, 预留
+                                                                                    //6 bit, 预留
+                                                                                    //7 bit, 预留
+    int32                                   plc_passway_status = 8;                 //plc 出入口状态
+                                                                                    //0 bit, 入口1 可进车
+                                                                                    //1 bit, 入口1 维护
+                                                                                    //2 bit, 入口2 可进车
+                                                                                    //3 bit, 入口2 维护
+                                                                                    //4 bit, 出口1 可出车
+                                                                                    //5 bit, 出口1 维护
+                                                                                    //6 bit, 出口2 可出车
+                                                                                    //7 bit, 出口2 维护
+    int32                                   plc_carrier_status = 9;                 //搬运器状态 0=故障 1=存车 2=取车 3=空闲 4=维护
+    int32                                   plc_inlet_1_status = 10;                //入口1 0=不可进车, 1=可进车, 2=维护不可进车, 3=维护可进车
+    int32                                   plc_inlet_2_status = 11;                //入口2 0=不可进车, 1=可进车, 2=维护不可进车, 3=维护可进车
+    int32                                   plc_outlet_3_status = 12;               //出口3 0=不可进车, 1=可进车, 2=维护不可进车, 3=维护可进车
+    int32                                   plc_outlet_4_status = 13;               //出口4 0=不可进车, 1=可进车, 2=维护不可进车, 3=维护可进车
+
+    repeated dispatch_plc_passway_status    dispatch_plc_passway_status_vector = 14; //plc出入口状态结构体,  数组下标0~1是入口, 数组下标2~3是出口
+
+}
+message terminal_node_statu{
+    int32 terminal_id = 1;
+    int32 import_id = 2;
+    string car_number = 3;
+}

+ 277 - 0
message/message.proto

@@ -0,0 +1,277 @@
+syntax = "proto3";
+
+enum Range_status {
+    Range_correct = 0x0000;                 // 未超界
+    Range_front = 0x0001;                   //前超界
+    Range_back = 0x0002;                    //后超界
+    Range_left = 0x0004;                    // 左超界
+    Range_right = 0x0008;                   // 右超界
+    Range_bottom = 0x0010;                  //底盘超界
+    Range_top = 0x0020;                     // 车顶超界
+    Range_car_width = 0x0040;               // 车宽超界
+    Range_car_wheelbase = 0x0080;           // 轴距超界
+    Range_angle_anti_clock = 0x0100;        // 左(逆时针)旋转超界
+    Range_angle_clock = 0x0200;             // 右(顺时针)旋转超界
+    Range_steering_wheel_nozero = 0x0400;   // 方向盘未回正
+    Range_car_moving = 0x0800;              // 车辆移动为1,静止为0
+}
+
+enum MeasureStatu {
+    Measure_OK = 0;                 // 测量完成
+    Measure_Empty_Clouds = 1;       // 测量结果:测量区域数据为空
+    Measure_Failture = 2;           // 测量结果:测量区域数据无法检测出车辆
+    Measure_Border = 3;             // 测量结果:对于PLC, 车辆存在超界,具体超界状态对比Range_status
+    Measure_Terminal_Border = 4;    // 测量结果:对于终端, 车辆存在超界,具体超界状态对比Range_status
+    Lidar_Disconnect = 5;           // 测量雷达:失去连接
+
+    Measure_Statu_Max = 6;
+}
+
+/*测量信息*/
+message measure_info {
+    float cx=1;                                 // 车辆中心坐标x
+    float cy=2;                                 // 车辆中心坐标y
+    float theta=3;                              // 车身偏转角度(相对于y轴,左正右负)
+    float length=4;                             // 车身长度(厦门四个雷达,含有该值,楚天两个雷达,该值为0)
+    float width=5;                              // 车身宽度(左右两侧轮子最大宽度)
+    float height=6;                             // 车身高度
+    float wheelbase=7;                          // 车辆前后轴距
+    float front_theta=8;                        // 车辆前轮偏转角度
+    int32 border_statu=9;                       // 超界状态, 位运算
+    MeasureStatu ground_status=10;              // 测量状态,0=正常, 1=空, 2=测量失败, 3=超界, 4=终端超界, 5=雷达断连
+    int32 is_stop=11;                           // <是否可停> 1为可停,0为不可停
+    int32 motion_statu=12;                      // 运动状态,0=运动, 1=静止(只有三秒内都是静止才会写1,只要瞬间触发运动就会写0)
+    float move_distance=13;                     // 前进距离
+}
+
+/*分配的车位信息*/
+message parkspace_info{
+    int32 id=1;
+    int32 serial_id=2;    //排序id
+    int32 table_id=3;     //标签id
+    int32 unit_id=4;      //单元号
+    int32 floor=5;        //楼层号
+    int32 room_id=6;      //同层编号
+    float height=7;       //车高档位
+}
+
+//任务表单状态
+enum STATU{
+    eNormal=0;        // 正常
+    eWarning=1;       // 警告
+    eError=2;         // 错误
+    eCritical=3;      // 严重错误
+}
+
+//表单流程模式
+enum Table_process_mode
+{
+    PROCESS_NORMAL = 0;             // 0:正常模式, 检查节点会向收费系统发送请求,收费系统的答复 通过后,再向调度发送请求
+    PROCESS_ONLY_TO_DISPATCH = 1;   // 1:强制存取车,检查节点会向收费系统发送请求,忽略收费系统的答复,直接向调度发送请求。
+    PROCESS_ONLY_TO_PAY = 2;        // 2:虚拟存取车, 检查节点会向收费系统发送请求,忽略收费系统的答复。
+}
+
+/*
+表单执行状态
+ */
+message table_statu{
+    STATU execute_statu=1;                    // 执行状态
+    string statu_description=2;               // 状态描述
+    Table_process_mode table_process_mod=3;   // 表单流程模式
+}
+/*
+号牌信息
+ */
+message plate_number_info
+{
+    string plate_number = 1;        // 号牌
+    string plate_color = 2;         // 号牌颜色
+    string plate_type = 3;          // 号牌类型, 车辆类型
+    int32  plate_confidence = 4;    // 号牌可信度, 1-100 值越高可信度越高
+    string recognition_time = 5;    // 识别时间点, yyyyMMddHHmmss
+    string plate_full_image = 6;    // 号牌全景图, base64编码
+    string plate_clip_image = 7;    // 号牌特写图, base64编码
+}
+/*
+停车表单
+ */
+message park_table{
+    table_statu statu=1;                      //表单状态
+    int32 queue_id=2;                         //指令排队编号
+
+    string car_number=3;
+    int32 unit_id=4;
+    int32 terminal_id=5;
+    string primary_key=6;
+
+    measure_buffer entrance_measure_info=7;     // 入口测量信息
+    parkspace_info allocated_space_info=8;    // 分配的车位信息
+    measure_buffer actually_measure_info=9;     // 实际测量信息或者叫二次测量信息
+    parkspace_info actually_space_info=10;    // 实际停放的车位
+    int32 import_id =11;                      // 入口id, 1~2
+
+    plate_number_info car_number_info = 12;   // 车牌号信息
+}
+
+/*
+取车表单
+ */
+message pick_table{
+    table_statu statu=1;      // 表单状态
+    int32 queue_id=2;         // 指令排队编号
+
+    string car_number=3;
+    int32 unit_id=4;
+    int32 terminal_id=5;
+    string primary_key=6;
+
+    parkspace_info actually_space_info=7;     // 实际停放的车位信息
+
+    measure_buffer actually_measure_info=8;     // 存车时的实际测量信息(轴距)
+
+    int32 export_id=9;                        // 出口id, 3~4
+    bool  is_leaved=10;                       // 是否离开
+    plate_number_info car_number_info = 11;   // 车牌号信息
+}
+
+/*
+以下是状态消息
+ */
+
+/*
+单片机节点状态
+ */
+message out_mcpu_statu{     //数值+1后
+    int32 door_statu=1;       //外门状态       0无效, 1无效, 2开到位, 3 关到位, 4开关中, 5 故障
+    int32 outside_safety=2;    //是否有车      0无效, 1无车, 2有车
+}
+
+message in_mcpu_statu{      //数值+1后
+    int32 door_statu=1;       //外门状态       0无效, 1无效, 2开到位, 3 关到位, 4开关中, 5 故障
+    int32 back_io=2;          //后超界       0无效, 1后超界, 2正常
+    int32 is_occupy=3;        //是否有车      0无效, 1无车, 2有车
+    int32 heighth=4;          //车高状态      0无效, 1无效, 2小车, 3中车, 4大车, 5故障, 6故障
+}
+/*
+测量节点状态
+ */
+message measure_buffer{
+    measure_info measure_info_to_plc_forward=1;     //雷达数据给plc,正向
+    measure_info measure_info_to_plc_reverse=2;     //雷达数据给plc,反向
+    measure_info measure_info_to_terminal=3;        //雷达数据给终端,边界较小
+}
+
+//搬运器状态枚举
+enum CarrierStatu{
+    eIdle=0;
+    eBusy=1;
+    eFault=2;
+}
+
+
+
+
+/*
+//楚天调度入口汽车范围的修正信息
+message dispatch_region_info
+{
+    int32 terminal_id=1;                //入口终端编号, 1~6
+    float turnplate_angle_min=2;        //转盘角度最小值, 负值, 例如 -5度
+    float turnplate_angle_max=3;        //转盘角度最大值, 正值, 例如 +5度
+}
+
+//楚天搬运器状态消息
+message dispatch_node_statu{
+    CarrierStatu statu=1;
+    int32 idle_stop_floor=2;  //空闲时停留位置
+    park_table  running_pack_info=3;  //正在执行的停车表单
+    pick_table  running_pick_info=4;  //正在执行的取车表单
+
+    int32                                   unit_id = 5;                            //单元号, 1~3
+    int32                                   plc_heartbeat = 6;                      //plc心跳信息
+    int32                                   plc_status_info = 7;                    //plc状态的集合
+                                                                                            //0 bit, 手动模式
+                                                                                            //1 bit, 自动模式
+                                                                                            //2 bit, 自动运行中
+                                                                                            //3 bit, 复位
+                                                                                            //4 bit, 1号口可以进车
+                                                                                            //5 bit, 2号口可以进车
+                                                                                            //6 bit, 预留
+                                                                                            //7 bit, 预留
+    repeated dispatch_region_info           dispatch_region_info_vector = 8;        //调度入口汽车范围的修正信息
+}
+ */
+
+
+
+//plc出入口状态结构体
+message dispatch_plc_passway_status
+{
+    int32			car_height = 1;			            //车高 0=无车 1=小车 2=中车 3=大车 4=超高车
+    int32			outside_door_status = 2;	        //外门状态 0=无状态 1=开到位 2=关到位
+    int32			inside_door_status = 3;	            //内门状态 0=无状态 1=开到位 2=关到位
+    int32			comb_body_status = 4;	            //梳体状态 0=无状态 1=上到位 2=下到位, AB特有
+    float			turnplate_angle_min = 5;	        //转盘角度最小值, C特有, 负值, 例如 -5度
+    float			turnplate_angle_max = 6;	        //转盘角度最大值, C特有, 正值, 例如 +5度
+    int32			sensor_1 = 7;				        //传感器状态的集合1
+                                                        //0 bit, 地感 0=无车 1=有车
+                                                        //1 bit, 移动检测 0=运动 1=静止
+                                                        //2 bit, 动态超限 0=遮挡 1=正常
+                                                        //3 bit, 后超界 0=遮挡 1=正常
+                                                        //4 bit, 前超界 0=遮挡 1=正常
+                                                        //5 bit, 左超界 0=遮挡 1=正常
+                                                        //6 bit, 右超界 0=遮挡 1=正常
+                                                        //7 bit, 车高小车, 0=遮挡 1=正常,AB单元为1480, C单元为1780,
+    int32			sensor_2 = 8;				        //传感器状态的集合1
+                                                        //0 bit, 车高中车, 0=遮挡 1=正常,AB单元为1500, C单元为1800,
+                                                        //1 bit, 车高大车, 0=遮挡 1=正常,AB单元为2050, C单元为2050,
+                                                        //2 bit, 有车检测 0=无车 1=有车
+                                                        //3 bit, 车轮1检测 0=无车 1=有车, AB特有
+                                                        //4 bit, 车轮2检测 0=无车 1=有车, AB特有
+                                                        //5 bit, 预留
+                                                        //6 bit, 预留
+                                                        //7 bit, 预留
+    int32           plc_passway_enable=9;               //出入口 0=不可进车, 1=可进车, 2=维护不可进车, 3=维护可进车
+}
+
+//厦门搬运器状态消息
+message dispatch_node_statu{
+    CarrierStatu                            statu=1;
+    int32                                   idle_stop_floor=2;          //空闲时停留位置
+    park_table                              running_pack_info=3;        //正在执行的停车表单
+    pick_table                              running_pick_info=4;        //正在执行的取车表单
+    int32                                   unit_id = 5;                //单元号, 1~3
+
+    int32                                   plc_heartbeat = 6;                      //plc心跳信息
+    int32                                   plc_mode_status = 7;                    //plc状态的集合
+                                                                                    //0 bit, 手动模式, 维修模式
+                                                                                    //1 bit, 自动模式
+                                                                                    //2 bit, 自动运行中
+                                                                                    //3 bit, 复位
+                                                                                    //4 bit, 预留
+                                                                                    //5 bit, 预留
+                                                                                    //6 bit, 预留
+                                                                                    //7 bit, 预留
+    int32                                   plc_passway_status = 8;                 //plc 出入口状态
+                                                                                    //0 bit, 入口1 可进车
+                                                                                    //1 bit, 入口1 维护
+                                                                                    //2 bit, 入口2 可进车
+                                                                                    //3 bit, 入口2 维护
+                                                                                    //4 bit, 出口1 可出车
+                                                                                    //5 bit, 出口1 维护
+                                                                                    //6 bit, 出口2 可出车
+                                                                                    //7 bit, 出口2 维护
+    int32                                   plc_carrier_status = 9;                 //搬运器状态 0=故障 1=存车 2=取车 3=空闲 4=维护
+    int32                                   plc_inlet_1_status = 10;                //入口1 0=不可进车, 1=可进车, 2=维护不可进车, 3=维护可进车
+    int32                                   plc_inlet_2_status = 11;                //入口2 0=不可进车, 1=可进车, 2=维护不可进车, 3=维护可进车
+    int32                                   plc_outlet_3_status = 12;               //出口3 0=不可进车, 1=可进车, 2=维护不可进车, 3=维护可进车
+    int32                                   plc_outlet_4_status = 13;               //出口4 0=不可进车, 1=可进车, 2=维护不可进车, 3=维护可进车
+
+    repeated dispatch_plc_passway_status    dispatch_plc_passway_status_vector = 14; //plc出入口状态结构体,  数组下标0~1是入口, 数组下标2~3是出口
+
+}
+message terminal_node_statu{
+    int32 terminal_id = 1;
+    int32 import_id = 2;
+    string car_number = 3;
+}

+ 309 - 0
message/message_base.proto

@@ -0,0 +1,309 @@
+syntax = "proto2";
+package message;
+
+//消息类型定义;每个在网络上传输的消息必须含有这个属性
+enum Message_type
+{
+    eBase_msg=0x00;
+    eCommand_msg=0x01;                      //指令消息
+
+
+    eLocate_status_msg=0x11;                //定位模块状态消息
+    eLocate_request_msg=0x12;               //定位请求消息
+    eLocate_response_msg=0x13;              //定位反馈消息
+
+    eLocate_sift_request_msg = 0x14;            //预测算法请求消息
+    eLocate_sift_response_msg = 0x15;           //预测算法反馈消息
+
+    eDispatch_status_msg=0x21;                //调度模块硬件状态消息
+    eDispatch_request_msg=0x22;              //请求调度消息
+    eDispatch_response_msg=0x23;             //调度结果反馈消息
+
+    eParkspace_allocation_status_msg=0x31;  //车位分配模块状态消息,包括车位信息
+    eParkspace_allocation_request_msg=0x32; //请求分配车位消息
+    eParkspace_allocation_response_msg=0x33;//分配车位结果反馈消息
+    eParkspace_search_request_msg = 0x34;		//查询车位请求消息
+    eParkspace_search_response_msg = 0x35;		//查询车位反馈消息
+    eParkspace_release_request_msg = 0x36;		//释放车位请求消息
+    eParkspace_release_response_msg = 0x37;		//释放车位反馈消息
+    eParkspace_force_update_request_msg = 0x38;	//手动修改车位消息
+    eParkspace_force_update_response_msg = 0x39;//手动修改车位反馈消息
+    eParkspace_confirm_alloc_request_msg = 0x3A;//确认分配车位请求消息
+    eParkspace_confirm_alloc_response_msg = 0x3B;//确认分配车位反馈消息
+    eParkspace_allocation_data_msg = 0x3C;     //车位分配模块车位数据消息
+    eParkspace_allocation_data_response_msg =0x3D;//车位数据反馈消息
+    eParkspace_manual_search_request_msg = 0x3E;	//手动查询车位请求消息
+    eParkspace_manual_search_response_msg = 0x3F;//手动查询车位反馈消息
+
+    eStore_command_request_msg=0x41;        //终端停车请求消息
+    eStore_command_response_msg=0x42;       //停车请求反馈消息
+    ePickup_command_request_msg=0x43;       //取车请求消息
+    ePickup_command_response_msg=0x44;       //取车请求反馈消息
+
+    eTerminal_status_msg = 0x50;	 //终端状态消息
+
+    eStoring_process_statu_msg=0x90;        //停车指令进度条消息
+    ePicking_process_statu_msg=0x91;        //取车指令进度消息
+
+
+    eCentral_controller_statu_msg=0xa0;     //中控系统状态消息
+
+
+    eEntrance_manual_operation_msg=0xb0;            //针对出入口状态操作的手动消息
+    eProcess_manual_operation_msg=0xb1;             //针对流程的手动消息
+
+    eNotify_request_msg=0xc0;               //取车等候区通知请求
+    eNotify_response_msg=0xc1;              //等候区反馈
+    eNotify_status_msg=0xc2;                //等候区通知节点状态
+
+    eUnNormalized_module_statu_msg = 0xd0; //非标节点状态
+
+    eDispatch_plan_request_msg          = 0xe0;     //调度总规划的请求(用于启动整个调度算法)(调度管理->调度算法)
+    eDispatch_plan_response_msg         = 0xe1;     //调度总规划的答复(调度算法->调度管理)
+    eDispatch_control_request_msg       = 0xe2;     //调度控制的任务请求(调度算法->调度管理)
+    eDispatch_control_response_msg      = 0xe3;     //调度控制的任务答复(调度管理->调度算法)
+    eDispatch_manager_status_msg        = 0xea;     //调度管理的设备状态消息(调度底下所有硬件设备状态的汇总)
+    eDispatch_manager_data_msg          = 0xeb;     //调度管理的设备详细的数据信息
+
+
+    eGround_detect_request_msg=0xf0;        //地面雷达测量请求消息
+    eGround_detect_response_msg=0xf1;       //地面雷达测量反馈消息
+    eGround_status_msg=0xf2;                //地面雷达状态消息
+
+}
+
+//通讯单元
+enum Communicator
+{
+    eEmpty=0x0000;
+    eMain=0x0001;    //主流程
+
+    eTerminor=0x0100;
+    //车位表
+    eParkspace=0x0200;
+    //测量单元
+    eMeasurer=0x0300;
+    //测量单元的服务器
+    eMeasurer_sift_server=0x0301;
+    //调度机构
+    eDispatch_manager=0x0400;
+    //调度机构
+    eDispatch_control=0x0401;
+    //...
+  //取车等候区通知节点
+    eNotify=0x0501;
+
+    //地面测量单元
+	eGround_measurer=0x0f00;
+}
+////base message 用于解析未知类型的消息
+message Base_info
+{
+    required Message_type               msg_type=1[default = eBase_msg];
+    optional int32                      timeout_ms=2[default = 0];
+    required Communicator               sender=3[default = eEmpty];                       //发送者
+    required Communicator               receiver=4[default = eEmpty];                     //接收者
+}
+
+// 事件,停车或者取车
+enum Process_type
+{
+    eStoring=1;
+    ePicking=2;
+}
+
+
+message Base_msg
+{
+    required Base_info                  base_info=1;
+}
+
+//错误等级,用来做故障处理
+enum Error_level
+{
+    NORMAL                = 0;      //    正常,没有错误,默认值0
+
+    NEGLIGIBLE_ERROR      = 1;      //    轻微故障;可忽略的故障,NEGLIGIBLE_ERROR
+
+    MINOR_ERROR           = 2;      //    一般故障,MINOR_ERROR
+
+    MAJOR_ERROR           = 3;      //    严重故障,MAJOR_ERROR
+
+    CRITICAL_ERROR        = 4;      //    致命故障,CRITICAL_ERROR
+
+}
+
+message Error_manager
+{
+    required int32                      error_code = 1[default = 0];
+    optional Error_level                error_level = 2[default = NORMAL];
+    optional string                     error_description = 3[default = ""];
+}
+
+//测量结果结构体
+message Locate_information
+{
+    optional float locate_x = 1[default = 0];				//整车的中心点x值; 四轮的中心
+    optional float locate_y = 2[default = 0];				//整车的中心点y值; 四轮的中心
+    optional float locate_angle = 3[default = 0];			//整车的旋转角; 四轮的旋转角
+    optional float locate_length = 4[default = 0];		    //整车的长度; 用于规避碰撞
+    optional float locate_width = 5[default = 0];			//整车的宽度; 用于规避碰撞
+    optional float locate_height = 6[default = 0];		    //整车的高度; 用于规避碰撞
+    optional float locate_wheel_base = 7[default = 0];	    //整车的轮距; 前后轮的距离; 用于机器人或agv的抓车
+    optional float locate_wheel_width = 8[default = 0];	    //整车的轮距; 左右轮的距离; 用于机器人或agv的抓车
+    optional bool locate_correct = 9[default = false];		    //整车的校准标记位
+
+    optional float locate_front_theta = 10[default = 0];	    //整车的前轮的旋转角
+    
+    optional float uniformed_car_x = 11;        //转角复位后,车辆中心点x
+    optional float uniformed_car_y = 12;        //转角复位后,车辆中心点y
+}
+
+//车辆基本信息
+message Car_info
+{
+    optional float                      car_length=1[default = 0];           //车长
+    optional float                      car_width=2[default = 0];            //车宽
+    optional float                      car_height=3[default = 0];           //车高
+    optional string                     license=4[default = ""];             //车辆凭证号(车牌号+唯一码)
+    optional string                     car_numberPlate = 5[default = ""];   //车牌号
+    optional float                      car_wheel_base = 6[default = 0];	 //整车的轮距; 前后轮的距离; 用于机器人或agv的抓车
+    optional float                      car_wheel_width = 7[default = 0];	 //整车的轮距; 左右轮的距离; 用于机器人或agv的抓车
+}
+
+//车位状态枚举
+enum Parkspace_status
+{
+    eParkspace_status_unknow    = 0;
+    eParkspace_empty            = 1;         //空闲,可分配
+    eParkspace_occupied         = 2;         //被占用,不可分配
+    eParkspace_reserved         = 3;         //被预约,预约车辆可分配
+    eParkspace_locked           = 4;         //临时锁定,不可分配
+    eParkspace_error            = 5;         //车位机械结构或硬件故障
+}
+
+//车位朝向, 小号朝前朝南, 大号朝后朝北
+enum Direction
+{
+    eDirection_unknow = 0;
+    eForward = 1;                   //小号朝前朝南
+    eBackward = 2;                  //大号朝后朝北
+}
+
+//车位分配路线(根据中跑车的路线来定)
+enum Parkspace_path
+{
+	UNKNOW_PATH = 0;
+    OPTIMAL_PATH = 1;
+    LEFT_PATH = 2;
+    RIGHT_PATH = 3;
+    TEMPORARY_CACHE_PATH = 4;
+}
+
+//车位类型
+enum Parkspace_type
+{
+    UNKNOW_PARKSPACE_TYPE = 0;
+    MIN_PARKINGSPACE = 1;//小车位
+    MID_PARKINGSPACE = 2;//中车位
+    BIG_PARKINGSPACE = 3;//大车位
+}
+
+//汽车类型
+enum Car_type
+{
+    UNKNOW_CAR_TYPE = 0;
+    MIN_CAR = 1;//小车
+    MID_CAR = 2;//中车
+    BIG_CAR = 3;//大车
+}
+
+//单个车位基本信息与状态信息,车位信息以及车位上的车辆信息
+message Parkspace_info
+{
+    optional int32              parkingspace_index_id = 1;            //车位ID
+    optional Parkspace_type     parkingspace_type = 2;                //车位类型
+    optional int32              parkingspace_unit_id = 3;            //车位单元号
+    optional int32              parkingspace_label_id = 4;            //车位单元内部ID
+    optional int32              parkingspace_room_id = 5;             //同层编号
+    optional Direction          parkingspace_direction = 6;           //前后
+    optional int32              parkingspace_floor_id = 7;            //楼层
+    optional float              parkingspace_width = 8;               //车位宽
+    optional float              parkingspace_height = 9;              //车位高
+    optional Parkspace_status   parkingspace_status = 10;              //车位当前状态
+    optional Car_info           car_info = 11;                        //车辆信息
+    optional string             entry_time = 12;                      //入场时间
+    optional string             leave_time = 13;                      //离场时间
+
+
+    optional Parkspace_path     parkspace_path = 14;            // 车位分配路线
+    optional float              path_estimate_time = 15;        //车位分配路线 time(s)
+    optional Parkspace_status   parkspace_status_target = 16;     //车位目标状态
+
+    optional Car_type           car_type = 17;                        //车辆类型
+
+
+}
+
+/*
+*流程中的步骤类型, 例如:停车流程包含5个步骤 , 分配车位-测量-检验结果-搬运-更新车位表
+*/
+enum Step_type
+{
+    eAlloc_step=0;
+    eMeasure_step=1;
+    eCompare_step=2;
+    eDispatch_step=3;
+    eConfirm_step=4;
+
+    eSearch_step=5;        //查询数据库
+    eWait_step=6;             //等待车辆离开
+    eRelease_step=7;          //释放车位
+
+    eComplete=8;              //完成
+
+    eBackConfirm_step=9;
+    eBack_compare_step=10;
+    eBackMeasure_step=11;
+    eBackAlloc_step=12;
+
+    eBackWait_step=13;
+    eBackDispatch_step=14;
+    eBackSearch_step=15;
+
+    eBackComplete=16;
+}
+//步骤状态,每个步骤有四中可能状态 ,等待中-执行中-完成或者错误  四个状态
+enum Step_statu
+{
+    eWaiting=0;               //完成/空闲
+    eWorking=1;
+    eError=2;
+    eFinished=3;
+}
+
+//调度设备的类型
+enum Dispatch_device_type
+{
+    ROBOT_1                                 = 101;      //一号出口的专用机器手(只能负责1号出口的取车)(目前没有安装,暂时不考虑)
+    ROBOT_2                                 = 102;      //中间的大型机器手   (可以负责1~6号出入口的停车和取车)
+
+    CARRIER_1                               = 200;      //左侧0号电梯井的搬运器(升降电梯 中跑车 小跑车 三合一为搬运器)
+    CARRIER_2                               = 207;      //右侧7号电梯井的搬运器(升降电梯 中跑车 小跑车 三合一为搬运器)
+    CARRIER_3                               = 203;      //中间3楼的搬运器(中跑车 小跑车 二合一为搬运器)(没有电梯, 只能在3楼活动)
+
+    PASSAGEWAY_0                            = 300;      //0号出口(在左侧电梯井, 只能取车)(暂时不存在)
+    PASSAGEWAY_1                            = 301;      //1号出入口
+    PASSAGEWAY_2                            = 302;      //2号出入口
+    PASSAGEWAY_3                            = 303;      //3号出入口
+    PASSAGEWAY_4                            = 304;      //4号出入口
+    PASSAGEWAY_5                            = 305;      //5号出入口
+    PASSAGEWAY_6                            = 306;      //6号出入口
+    PASSAGEWAY_7                            = 307;      //7号出口(在右侧电梯井, 只能取车)
+}
+
+message Id_struct
+{
+    optional int32 terminal_id=1;   //终端ID
+    optional int32 unit_id=2;       //单元号
+}

+ 16 - 0
pahoc/CMakeLists.txt

@@ -0,0 +1,16 @@
+set(LIBRARY_NAME zxpahoc)
+
+# 获取当前目录下的所有源文件
+aux_source_directory(${CMAKE_CURRENT_LIST_DIR} GLOB_RECURSE)
+
+set(LIBRARY_SOURCE_LIST
+        ${GLOB_RECURSE}
+)
+
+set(LIBRARY_DEPEND_LIST paho-mqtt3a
+        -lgflags
+        glog::glog)
+
+add_library(${LIBRARY_NAME} ${LIBRARY_SOURCE_LIST})
+target_link_libraries(${LIBRARY_NAME} PUBLIC ${LIBRARY_DEPEND_LIST})
+

+ 349 - 0
pahoc/mqtt_async.cpp

@@ -0,0 +1,349 @@
+#include "mqtt_async.h"
+
+MqttAsyncClient::MqttAsyncClient(/* args */) {
+    m_etc_ = new MqttAsyncConfig();
+    m_client_ = nullptr;
+    m_message_arrived_callback_ = nullptr;
+    m_connect_lost_callback_ = nullptr;
+    m_connect_success_ = nullptr;
+    m_disconnect_success_ = nullptr;
+    m_subscribe_success_ = nullptr;
+    m_unsubscribe_success_ = nullptr;
+    m_send_success_ = nullptr;
+    m_connect_failure_ = nullptr;
+    m_disconnect_failure_ = nullptr;
+    m_subscribe_failure_ = nullptr;
+    m_unsubscribe_failure_ = nullptr;
+    m_send_failure_ = nullptr;
+}
+
+MqttAsyncClient::~MqttAsyncClient() {
+    Disconnect();
+    MQTTAsync_destroy(&m_client_);
+}
+
+bool MqttAsyncClient::init_from_proto(const std::string &path) {
+    auto ret = loadProtobufFile(path, *m_etc_);
+    LOG(INFO) << m_etc_->DebugString();
+    if (ret != SUCCESS) {
+        LOG(ERROR) << "init config proto error from " << path;
+        return false;
+    }
+
+    m_create_opts.maxBufferedMessages = m_etc_->create_opts().maxbufferedmessages();
+
+    m_connect_ops.cleansession = m_etc_->connect_ops().cleansession();
+    m_connect_ops.keepAliveInterval = m_etc_->connect_ops().keepaliveinterval();
+    m_connect_ops.automaticReconnect = m_etc_->connect_ops().automaticreconnect();
+    m_connect_ops.minRetryInterval = m_etc_->connect_ops().minretryinterval();
+    m_connect_ops.maxRetryInterval = m_etc_->connect_ops().maxretryinterval();
+
+    if (m_etc_->client_id() == "Auto") {
+        m_etc_->set_client_id(std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
+    }
+    LOG(INFO) << m_etc_->client_id();
+    Connect(m_etc_->address(), m_etc_->client_id());
+    return true;
+}
+
+bool MqttAsyncClient::Connect(const std::string &address, const std::string &client_id) {
+    int ret = MQTTASYNC_FAILURE;
+    if ((ret = MQTTAsync_createWithOptions(&m_client_, address.c_str(), client_id.c_str(), MQTTCLIENT_PERSISTENCE_NONE,
+                                           nullptr, &m_create_opts)
+               != MQTTASYNC_SUCCESS)) {
+        LOG(ERROR) << "Failed to create client object, return code " << ret;
+        return false;
+    } else {
+        DLOG(INFO) << "Success to create client object: " << address << ", client id: " << client_id << ".";
+    }
+
+    if ((ret = MQTTAsync_setCallbacks(m_client_, this, onMessageLost, onMessageArrived, nullptr)) !=
+        MQTTASYNC_SUCCESS) {
+        LOG(ERROR) << "Failed to set callback, return code " << ret;
+        return false;
+    }
+
+    if ((ret = MQTTAsync_setConnected(m_client_, this, onConnected)) != MQTTASYNC_SUCCESS) {
+        printf("Failed to set connected callback, return code %d\n", ret);
+        return false;
+    }
+
+    if ((ret = MQTTAsync_connect(m_client_, &m_connect_ops)) != MQTTASYNC_SUCCESS) {
+        printf("Failed to start connect, return code %d\n", ret);
+        return false;
+    }
+
+    return true;
+}
+
+bool MqttAsyncClient::Disconnect() {
+    DLOG(INFO) << __func__;
+    int ret = MQTTASYNC_FAILURE;
+
+    MQTTAsync_disconnectOptions opts = MQTTAsync_disconnectOptions_initializer;
+
+    opts.timeout = 30;
+    opts.context = this;
+    opts.onSuccess = onDisconnectSuccess;
+    opts.onFailure = onDisconnectFailure;
+
+    if ((ret = MQTTAsync_disconnect(m_client_, &opts)) != MQTTASYNC_SUCCESS) {
+        printf("Failed to disconnect, return code %d\n", ret);
+        return false;
+    }
+    return true;
+}
+
+bool MqttAsyncClient::isConnected() {
+    return MQTTAsync_isConnected(m_client_);
+}
+
+bool MqttAsyncClient::SendMessage(const std::string& topic, void *message, int length, int qos) {
+    if (message == nullptr) {
+        return false;
+    }
+
+    int ret = MQTTASYNC_FAILURE;
+
+    MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
+    MQTTAsync_message pubmsg = MQTTAsync_message_initializer;
+
+    opts.context = this;
+    opts.onSuccess = onSendMessageSuccess;
+    opts.onFailure = onSendMessageFailure;
+
+    pubmsg.payload = message;
+    pubmsg.payloadlen = length;
+    pubmsg.retained = 0;
+    m_etc_->sendmessage().size();
+//    if (m_etc.findsendMessageEtc(topic) != nullptr) {
+//        pubmsg.qos = m_etc.findsendMessageEtc(topic)->qos;
+//    } else {
+    pubmsg.qos = qos;
+//    }
+
+    if ((ret = MQTTAsync_sendMessage(m_client_, topic.c_str(), &pubmsg, &opts)) != MQTTASYNC_SUCCESS) {
+        printf("Failed to start sendMessage, return code %d, connect statu %d\n", ret, MQTTAsync_isConnected(m_client_));
+        return false;
+    }
+    return true;
+}
+
+void MqttAsyncClient::setCallback(MessageArrived callback) {
+    DLOG(INFO) << __func__;
+    m_message_arrived_callback_ = callback;
+}
+
+void MqttAsyncClient::setCallback(ConnectionLost callback) {
+    DLOG(INFO) << __func__;
+    m_connect_lost_callback_ = callback;
+}
+
+bool MqttAsyncClient::setCallback(OnSuccess callback, const std::string &function) {
+    DLOG(INFO) << __func__;
+    bool ret = true;
+    if (function == "subscribe") {
+        m_subscribe_success_ = callback;
+    } else if (function == "unsubscribe") {
+        m_unsubscribe_success_ = callback;
+    } else if (function == "send") {
+        m_send_success_ = callback;
+    } else if (function == "connect") {
+        m_connect_success_ = callback;
+    } else if (function == "disconnect") {
+        m_disconnect_success_ = callback;
+    } else {
+        ret = false;
+    }
+
+    return ret;
+}
+
+bool MqttAsyncClient::setCallback(OnFailure callback, const std::string &function) {
+    DLOG(INFO) << __func__ << function;
+    bool ret = true;
+    if (function == "subscribe") {
+        m_subscribe_failure_ = callback;
+    } else if (function == "unsubscribe") {
+        m_unsubscribe_failure_ = callback;
+    } else if (function == "send") {
+        m_send_failure_ = callback;
+    } else if (function == "connect") {
+        m_connect_failure_ = callback;
+    } else if (function == "disconnect") {
+        m_disconnect_failure_ = callback;
+    } else {
+        ret = false;
+    }
+
+    return ret;
+}
+
+google::protobuf::RepeatedPtrField<SubscribeEtc> MqttAsyncClient::getSubscribeEtc() {
+    return m_etc_->subscribe();
+}
+
+bool MqttAsyncClient::Subscribe(const std::string &topic, int qos) {
+    int ret = MQTTASYNC_FAILURE;
+
+    MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
+
+    opts.context = this;
+    opts.onSuccess = onSubscribeSuccess;
+    opts.onFailure = onSubscribeFailure;
+
+    if ((ret = MQTTAsync_subscribe(m_client_, topic.c_str(), qos, &opts)) != MQTTASYNC_SUCCESS) {
+        DLOG(INFO) << "Failed to subscribe " << topic << ", return code " << ret;
+    } else {
+        DLOG(INFO) << "Success to subscribe " << topic;
+    }
+
+    return true;
+}
+
+bool MqttAsyncClient::unSubscribe(const std::string &topic) {
+    DLOG(INFO) << __func__;
+    int ret = MQTTASYNC_FAILURE;
+
+    MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
+
+    opts.context = this;
+    opts.onSuccess = onUnSubscribeSuccess;
+    opts.onFailure = onUnSubscribeFailure;
+
+    if ((ret = MQTTAsync_unsubscribe(m_client_, topic.c_str(), &opts)) != MQTTASYNC_SUCCESS) {
+        printf("Failed to subscribe %s, return code %d\n", topic.c_str(), ret);
+        return false;
+    }
+
+    return true;
+}
+
+int MqttAsyncClient::onMessageArrived(void *context, char *topicName, int topicLen, MQTTAsync_message *message) {
+    bool ret = true;
+    auto *client = (MqttAsyncClient *) context;
+
+    if (client->m_message_arrived_callback_ != nullptr) {
+        ret = client->m_message_arrived_callback_(client, topicName, topicLen, message);
+    } else {
+    }
+
+    MQTTAsync_freeMessage(&message);
+    MQTTAsync_free(topicName);
+
+    return ret;
+}
+
+void MqttAsyncClient::onMessageLost(void *context, char *cause) {
+    if (context == nullptr || cause == nullptr) {
+        DLOG(INFO) << __func__;
+    }
+    auto *client = (MqttAsyncClient *) context;
+    if (client->m_connect_lost_callback_ != nullptr) {
+        client->m_connect_lost_callback_(client, cause);
+    } else {
+    }
+}
+
+void MqttAsyncClient::onConnectSuccess(void *context, MQTTAsync_successData *response) {
+    DLOG(INFO) << __func__;
+    auto *client = (MqttAsyncClient *) context;
+    if (client->m_connect_success_ != nullptr) {
+        DLOG(INFO) << "run m_connect_success_.";
+        client->m_connect_success_(client, response);
+    }
+    DLOG(INFO) << "m_connect_success_ is nullptr.";
+}
+
+void MqttAsyncClient::onConnectFailure(void *context, MQTTAsync_failureData *response) {
+    DLOG(INFO) << __func__;
+    auto *client = (MqttAsyncClient *) context;
+    if (client->m_connect_failure_ != nullptr) {
+        client->m_connect_failure_(client, response);
+    } else {
+    }
+}
+
+void MqttAsyncClient::onDisconnectSuccess(void *context, MQTTAsync_successData *response) {
+    DLOG(INFO) << __func__;
+    auto *client = (MqttAsyncClient *) context;
+    if (client->m_disconnect_success_ != nullptr) {
+        client->m_disconnect_success_(client, response);
+    } else {
+    }
+}
+
+void MqttAsyncClient::onDisconnectFailure(void *context, MQTTAsync_failureData *response) {
+    DLOG(INFO) << __func__;
+    auto *client = (MqttAsyncClient *) context;
+    if (client->m_disconnect_failure_ != nullptr) {
+        client->m_disconnect_failure_(client, response);
+    } else {
+    }
+}
+
+void MqttAsyncClient::onSubscribeSuccess(void *context, MQTTAsync_successData *response) {
+    DLOG(INFO) << __func__;
+    auto *client = (MqttAsyncClient *) context;
+    if (client->m_subscribe_success_ != nullptr) {
+        client->m_subscribe_success_(client, response);
+    } else {
+    }
+}
+
+void MqttAsyncClient::onSubscribeFailure(void *context, MQTTAsync_failureData *response) {
+    DLOG(INFO) << __func__;
+    auto *client = (MqttAsyncClient *) context;
+    if (client->m_subscribe_failure_ != nullptr) {
+        client->m_subscribe_failure_(client, response);
+    } else {
+    }
+}
+
+void MqttAsyncClient::onUnSubscribeSuccess(void *context, MQTTAsync_successData *response) {
+    DLOG(INFO) << __func__;
+    auto *client = (MqttAsyncClient *) context;
+    if (client->m_unsubscribe_success_ != nullptr) {
+        client->m_unsubscribe_success_(client, response);
+    } else {
+    }
+}
+
+void MqttAsyncClient::onUnSubscribeFailure(void *context, MQTTAsync_failureData *response) {
+    DLOG(INFO) << __func__;
+    auto *client = (MqttAsyncClient *) context;
+    if (client->m_unsubscribe_failure_ != nullptr) {
+        client->m_unsubscribe_failure_(client, response);
+    } else {
+    }
+}
+
+void MqttAsyncClient::onSendMessageSuccess(void *context, MQTTAsync_successData *response) {
+    DLOG(INFO) << __func__;
+    auto *client = (MqttAsyncClient *) context;
+    if (client->m_send_success_ != nullptr) {
+        client->m_send_success_(client, response);
+    } else {
+    }
+}
+
+void MqttAsyncClient::onSendMessageFailure(void *context, MQTTAsync_failureData *response) {
+    DLOG(INFO) << __func__;
+    auto *client = (MqttAsyncClient *) context;
+    if (client->m_send_failure_ != nullptr) {
+        client->m_send_failure_(client, response);
+    } else {
+    }
+}
+
+void MqttAsyncClient::onConnected(void *context, char *cause) {
+    DLOG(INFO) << __func__;
+    auto *client = (MqttAsyncClient *) context;
+    if (client == nullptr || client->m_etc_->subscribe_size() == 0) {
+        return;
+    }
+
+    for (auto &sub: client->m_etc_->subscribe()) {
+        client->Subscribe(sub.topic(), sub.qos());
+    }
+}

+ 155 - 0
pahoc/mqtt_async.h

@@ -0,0 +1,155 @@
+#ifndef MQTT_ASYNC_CLIENT_H_
+#define MQTT_ASYNC_CLIENT_H_
+
+#include <MQTTAsync.h>
+#include <mutex>
+#include "tool/load_protobuf.hpp"
+#include "tool/log.hpp"
+#include "mqtt_async.pb.h"
+
+class MqttAsyncClient;
+
+typedef int (*MessageArrived)(void *client, char *topicName, int topicLen, MQTTAsync_message *message);
+
+typedef void (*ConnectionLost)(MqttAsyncClient *client, char *cause);
+
+typedef void (*OnSuccess)(MqttAsyncClient *client, MQTTAsync_successData *response);
+
+typedef void (*OnFailure)(MqttAsyncClient *client, MQTTAsync_failureData *response);
+
+class MqttAsyncClient {
+protected:
+    MqttAsyncClient();
+
+    virtual ~MqttAsyncClient();
+
+public:
+    /**
+      * @brief   通过json文件初始化配置,请保证配置文件路径及内容正确.
+      * @param   path json配置文件路径
+      * @return  如果获取到配置文件就会解析配置,配置文件没有则按照默认配置,找不到配置文件则会返回false.
+      */
+    bool init_from_proto(const std::string &path);
+
+    /**
+     * @brief 连接指定地址和id的EMQX服务器
+     * @param address EMQX服务器地址,不要带端口
+     * @param client_id 注册的客户端id,不同客户端id不要重复
+    **/
+    bool Connect(const std::string &address, const std::string &client_id);
+
+    /**
+     * @brief 断开当前已连接的EMQX服务器
+    **/
+    bool Disconnect();
+
+    /**
+     * @brief 当前mqtt连接状态
+    **/
+    bool isConnected();
+
+    /**
+     * @brief 添加对EMQX服务器指定topic的监听
+     * @param topic EMQX服务器topic
+     * @param qos 通信模式,默认为1
+    **/
+    bool Subscribe(const std::string &topic, int qos = 1);
+
+    /**
+     * @brief 取消对EMQX服务器指定topic的监听
+     * @param topic EMQX服务器topic
+     * @param qos 通信模式,默认为1
+    **/
+    bool unSubscribe(const std::string &topic);
+
+    /**
+     * @brief 向EMQX服务器指定的topic发布消息
+     * @param topic EMQX服务器topic
+     * @param message 发布消息的具体内容
+     * @param length 发布消息的长度
+     * @param qos 通信模式,默认为1
+    **/
+    bool SendMessage(const std::string& topic, void *message, int length, int qos = 1);
+
+    /**
+     * @brief 设置接收消息的回调函数,当本客户端收到消息时会执行该回调函数,如果未设置则按照默认回调函数执行
+     * @param callback 回调函数
+    **/
+    void setCallback(MessageArrived callback);
+
+    /**
+     * @brief 设置连接丢失的回调函数,当本客户端与EMQX服务器连接丢失时会执行该回调函数,如果未设置则按照默认回调函数执行
+     * @param callback 回调函数
+    **/
+    void setCallback(ConnectionLost callback);
+
+    /**
+     * @brief 设置指定功能执行成功的回调函数,当本客户端执行某功能成功时会执行该回调函数,如果未设置则按照默认回调函数执行
+     * @param callback 回调函数
+     * @param function 指定功能,目前有subscribe、send、connect、disconnect
+    **/
+    bool setCallback(OnSuccess callback, const std::string &function);
+
+    /**
+     * @brief 设置指定功能执行失败的回调函数,当本客户端执行某功能失败时会执行该回调函数,如果未设置则按照默认回调函数执行
+     * @param callback 回调函数
+     * @param function 指定功能,目前有subscribe、send、connect、disconnect
+    **/
+    bool setCallback(OnFailure callback, const std::string &function);
+
+    /**
+     * @brief 获取监听话题的配置
+    **/
+    google::protobuf::RepeatedPtrField<SubscribeEtc> getSubscribeEtc();
+
+private:
+    static int onMessageArrived(void *context, char *topicName, int topicLen, MQTTAsync_message *message);
+
+    static void onMessageLost(void *context, char *cause);
+
+    static void onConnected(void *context, char *cause);
+
+    static void onConnectSuccess(void *context, MQTTAsync_successData *response);
+
+    static void onConnectFailure(void *context, MQTTAsync_failureData *response);
+
+    static void onDisconnectSuccess(void *context, MQTTAsync_successData *response);
+
+    static void onDisconnectFailure(void *context, MQTTAsync_failureData *response);
+
+    static void onSubscribeSuccess(void *context, MQTTAsync_successData *response);
+
+    static void onSubscribeFailure(void *context, MQTTAsync_failureData *response);
+
+    static void onUnSubscribeSuccess(void *context, MQTTAsync_successData *response);
+
+    static void onUnSubscribeFailure(void *context, MQTTAsync_failureData *response);
+
+    static void onSendMessageSuccess(void *context, MQTTAsync_successData *response);
+
+    static void onSendMessageFailure(void *context, MQTTAsync_failureData *response);
+
+public:
+
+protected:
+    MqttAsyncConfig *m_etc_;
+    MQTTAsync m_client_;
+    MQTTAsync_createOptions m_create_opts = MQTTAsync_createOptions_initializer;
+    MQTTAsync_connectOptions m_connect_ops = MQTTAsync_connectOptions_initializer;
+
+    MessageArrived m_message_arrived_callback_;
+    ConnectionLost m_connect_lost_callback_;
+    OnSuccess m_connect_success_;
+    OnSuccess m_disconnect_success_;
+    OnSuccess m_subscribe_success_;
+    OnSuccess m_unsubscribe_success_;
+    OnSuccess m_send_success_;
+    OnFailure m_connect_failure_;
+    OnFailure m_disconnect_failure_;
+    OnFailure m_subscribe_failure_;
+    OnFailure m_unsubscribe_failure_;
+    OnFailure m_send_failure_;
+
+};
+
+#endif

+ 33 - 0
pahoc/mqtt_async.proto

@@ -0,0 +1,33 @@
+syntax = "proto2";
+
+message CreateOpts {
+  optional int32 maxBufferedMessages = 3 [default = 1000000];
+}
+
+message ConnectOps {
+  optional bool cleansession = 1 [default = true];
+  optional int32 keepAliveInterval = 2 [default = 30];
+  optional bool automaticReconnect = 3 [default = true];
+  optional int32 minRetryInterval = 4 [default = 3];
+  optional int32 maxRetryInterval = 5 [default = 10];
+}
+
+message SendMessageEtc {
+  required string topic = 1;
+  optional int32 qos = 2 [default = 1];
+}
+
+message SubscribeEtc {
+  required string topic = 1;
+  optional int32 qos = 2 [default = 1];
+}
+
+message MqttAsyncConfig {
+  optional string address = 1 [default = "mqtt://127.0.0.1:1883"];
+  optional string client_id = 2 [default = "Auto"];
+  optional CreateOpts create_opts = 3;
+  optional ConnectOps connect_ops = 4;
+  repeated SendMessageEtc sendMessage = 5;
+  repeated SubscribeEtc subscribe = 6;
+}
+

+ 31 - 0
rabbitmq/CMakeLists.txt

@@ -0,0 +1,31 @@
+set(LIBRARY_NAME zxrabbitmq)
+
+set(LIBRARY_SOURCE_LIST
+    ${CMAKE_CURRENT_LIST_DIR}/rabbitmq_base.h
+    ${CMAKE_CURRENT_LIST_DIR}/rabbitmq_base.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/rabbitmq_message.h
+    ${CMAKE_CURRENT_LIST_DIR}/rabbitmq_message.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/rabbitmq.pb.cc
+    ${CMAKE_CURRENT_LIST_DIR}/rabbitmq.pb.h
+)
+
+if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
+    set(Rabbitmq
+            /usr/lib/aarch64-linux-gnu/librabbitmq.a
+            )
+else()
+    set(Rabbitmq
+            /usr/local/lib/librabbitmq.a
+            )
+endif()
+
+set(LIBRARY_DEPEND_LIST
+    zxthread
+    zxmessage
+    ${Rabbitmq}
+    ${PROTOBUF_LIBRARIES}
+)
+
+add_library(${LIBRARY_NAME} ${LIBRARY_SOURCE_LIST})
+target_link_libraries(${LIBRARY_NAME} PUBLIC ${LIBRARY_DEPEND_LIST})
+

+ 27 - 0
rabbitmq/executor_mq.hpp

@@ -0,0 +1,27 @@
+/*
+ * @brief 该模块用于接收命令并执行命令, 然后将执行结果发送回发送着.
+*/
+
+#pragma once
+#include "executor.hpp"
+#include "defines.hpp"
+
+class ExecutorRabbitmq: public Executor<ExecutorRabbitmq> {
+    friend class SingletonIter<ExecutorRabbitmq>;
+public:
+    Error_manager connect() {
+        return Error_code::SUCCESS;
+    }
+
+    ~ExecutorRabbitmq() override = default;
+private:
+
+protected:
+    ExecutorRabbitmq() = default;
+
+};
+
+
+
+
+

+ 67 - 0
rabbitmq/rabbitmq.proto

@@ -0,0 +1,67 @@
+syntax = "proto2";
+package Rabbitmq_proto;
+
+//Rabbitmq 配置的通道,队列和消费者,
+//注:目前规定
+//接受请求消息:同一子节点所有的请求消息写同一个channel通道编号,queue_durable = 1, queue_auto_delete = 0, consume_no_ack = 0,
+//接受状态消息:同一子节点所有的状态消息写同一个channel通道编号,queue_durable = 0, queue_auto_delete = 1, consume_no_ack = 1,
+message Rabbitmq_channel_queue_consume
+{
+    optional int32 channel = 1;                             //连接通道,必写, 可以相同, 不同的消息队列可以共用同一个连接通道,
+                                                            //配合 amqp_basic_qos 和 amqp_basic_ack , 来阻塞这个通道的接受消息
+                                                            //请求消息和状态消息必须分别写不同的通道,例如所有的请求都可以写12, 所有的状态都可以写34
+    optional string exchange_name = 2;                      //交换机名称,必写, 可以相同, 不同的消息队列可以共用同一个交换机,
+                                                            //配合 routingkey 和 bindingkey , 来分配消息到合适的队列
+
+    //发送队列专属
+    optional string routing_key = 3;                        //发送端的路由键, 交换机分配消息的判断条件
+    optional int32 timeout_ms = 4;                          //发送超时时间, 单位ms, 服务器会自动删除超时的消息, 如果写0,那么就没有超时删除
+
+    //接受队列专属
+    optional string binding_key = 5;                        //接受端的绑定键, 交换机分配消息的判断条件
+    optional string queue_name = 6;                         //队列名称,必写, 不能相同
+    optional int32 queue_passive = 7[default = 0];          //是否被动,默认0
+    optional int32 queue_durable = 8;                       //是否持久,必写, 节点代码可以创建临时队列(所有权归节点), 服务器手动创建永久队列(所有权归服务器)
+                                              		        //		1表示永久队列,当节点死掉,队列在服务器保留,仍然可以接受数据,节点上线后,可以接受掉线期间的所有数据
+                                              		        //		0表示临时队列,当节点死掉,队列消失,不再接受数据,直到下次恢复正常
+    optional int32 queue_exclusive = 9[default = 0];        //是否独立,默认0
+    optional int32 queue_auto_delete = 10[default = 0];      //是否自动删除, 固定写0,
+                                                            //1表示消息被消费者接受后,就自动删除消息, 当接收端断连后,队列也会删除,
+                                                            //0表示消息被消费者接受后,不会自动删除消息, 需要手动ack才会删除消息, 队列不会删除
+                                                            //一般情况下设为0,然后让接受者手动删除.
+     optional int32 queue_meassage_ttl = 11[default = 0];   //队列配置的扩展参数, x-message-ttl 队列接受消息 的超时时间 (单位毫秒)
+                                                            //默认写0, 不配置超时, 一般在状态消息的临时队列写入1000ms
+
+
+    optional string consume_name = 12;                       //消费者名称,必写, 不能相同
+    optional int32 consume_no_local = 13[default = 0];      //是否非本地, 默认0,表示本地
+    optional int32 consume_no_ack = 14[default = 0];        //是否确认应答,默认0,表示接收后需要应答
+                                                            //请求消息必须写0, 必须应答之后才能接受下一条
+                                                            //状态消息必须写1, 可以无限循环接受,收到的瞬间,服务器就会删除这一条消息
+    optional int32 consume_exclusive = 15;                  //是否独立,默认0
+
+}
+
+
+
+//Rabbitmq 配置参数
+message Rabbitmq_parameter
+{
+    optional string ip = 1;             //服务器ip地址, 不带端口
+    optional int32 port = 2;            //端口,默认5672
+    optional string user = 3;           //用户名, 默认guest
+    optional string password = 4;       //密码, 默认guest
+
+    repeated Rabbitmq_channel_queue_consume rabbitmq_reciever_vector= 5;                //Rabbitmq 接受的通道,队列和消费者, 多个
+    repeated Rabbitmq_channel_queue_consume rabbitmq_sender_request_vector= 6;         //Rabbitmq 发送请求的通道
+    repeated Rabbitmq_channel_queue_consume rabbitmq_sender_status_vector= 7;          //Rabbitmq 发送状态的通道
+    //注:rabbitmq的接受是以队列为目标的, 可以同时接受多个队列的消息.
+    //注:rabbitmq的发送是以交换机为目标的,我们发送到交换机后,由交换机按照规则,去分配到下面的队列里面
+
+}
+
+//Rabbitmq 配置参数 总配置
+message Rabbitmq_parameter_all
+{
+    optional Rabbitmq_parameter        rabbitmq_parameters=1;
+}

+ 985 - 0
rabbitmq/rabbitmq_base.cpp

@@ -0,0 +1,985 @@
+
+#include "rabbitmq_base.h"
+
+Rabbitmq_base::Rabbitmq_base() {
+    m_rabbitmq_status = RABBITMQ_STATUS_UNKNOW;
+
+    mp_connect = NULL;
+    mp_socket = NULL;
+    m_port = 0;
+
+    mp_receive_analysis_thread = NULL;
+    mp_send_thread = NULL;
+    mp_encapsulate_status_thread = NULL;
+    m_encapsulate_status_cycle_time = 100;//默认1000ms,就自动封装一次状态信息
+
+    check_msg_callback = NULL;
+    check_executer_callback = NULL;
+    execute_msg_callback = NULL;
+    encapsulate_status_callback = NULL;
+
+}
+
+Rabbitmq_base::~Rabbitmq_base() {
+    rabbitmq_uninit();
+}
+
+//初始化 通信 模块。如下三选一
+Error_manager Rabbitmq_base::rabbitmq_init() {
+    return rabbitmq_init_from_protobuf(RABBITMQ_PARAMETER_PATH);
+}
+
+//初始化 通信 模块。从文件读取
+Error_manager Rabbitmq_base::rabbitmq_init_from_protobuf(std::string prototxt_path) {
+    Rabbitmq_proto::Rabbitmq_parameter_all t_rabbitmq_parameter_all;
+    if (loadProtobufFile(prototxt_path, t_rabbitmq_parameter_all) != SUCCESS) {
+        return Error_manager(RABBITMQ_READ_PROTOBUF_ERROR, MINOR_ERROR,
+                             "rabbitmq_init_from_file: %s read_proto_param  failed", prototxt_path.c_str());
+    }
+    return rabbitmq_init_from_protobuf(t_rabbitmq_parameter_all);
+}
+
+//初始化 通信 模块。从protobuf读取
+Error_manager
+Rabbitmq_base::rabbitmq_init_from_protobuf(Rabbitmq_proto::Rabbitmq_parameter_all &rabbitmq_parameter_all) {
+    LOG(INFO) << " ---Rabbitmq_base::rabbitmq_init_from_protobuf() run--- " << this;
+
+    int t_status = 0;        //状态
+    amqp_rpc_reply_t t_reply;    //reply答复结果
+    Error_manager t_error;
+    m_rabbitmq_parameter_all = rabbitmq_parameter_all;
+
+    for (auto &queue:rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_reciever_vector()) {
+        mp_rabbitmq_reciever.insert(std::pair<const std::string, Rabbitmq_proto::Rabbitmq_channel_queue_consume>(queue.routing_key(), queue));
+    }
+    for (auto &queue:rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_request_vector()) {
+        mp_rabbitmq_reciever.insert(std::pair<const std::string, Rabbitmq_proto::Rabbitmq_channel_queue_consume>(queue.routing_key(), queue));
+    }
+    for (auto &queue:rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_status_vector()) {
+        mp_rabbitmq_reciever.insert(std::pair<const std::string, Rabbitmq_proto::Rabbitmq_channel_queue_consume>(queue.routing_key(), queue));
+    }
+
+    //amqp_new_connection 新建amqp的连接配置,里面只有连接状态参数
+    // 返回amqp_connection_state_t_ *, 函数内部分配内存, amqp_destroy_connection()可以释放内存, 内存不为空则成功
+    mp_connect = amqp_new_connection();
+    if (mp_connect == nullptr) {
+        return Error_manager(Error_code::RABBITMQ_AMQP_NEW_CONNECTION_ERROR, Error_level::MINOR_ERROR,
+                             "amqp_new_connection fun error ");
+    }
+
+    //amqp_tcp_socket_new 新建tcp_socket连接
+    // 返回amqp_socket_t *, 函数内部分配内存, amqp_connection_close()可以释放内存, 内存不为空则成功
+    mp_socket = amqp_tcp_socket_new(mp_connect);
+    if (mp_socket == nullptr) {
+        return Error_manager(Error_code::RABBITMQ_AMQP_TCP_SOCKET_NEW_ERROR, Error_level::MINOR_ERROR,
+                             "amqp_tcp_socket_new fun error ");
+    }
+
+    //载入外部参数
+    if (rabbitmq_parameter_all.rabbitmq_parameters().has_ip() &&
+        rabbitmq_parameter_all.rabbitmq_parameters().has_port() &&
+        rabbitmq_parameter_all.rabbitmq_parameters().has_user() &&
+        rabbitmq_parameter_all.rabbitmq_parameters().has_password()) {
+        m_ip = rabbitmq_parameter_all.rabbitmq_parameters().ip();
+        m_port = rabbitmq_parameter_all.rabbitmq_parameters().port();
+        m_user = rabbitmq_parameter_all.rabbitmq_parameters().user();
+        m_password = rabbitmq_parameter_all.rabbitmq_parameters().password();
+    } else {
+        return Error_manager(Error_code::RABBITMQ_PROTOBUF_LOSS_ERROR, Error_level::MINOR_ERROR,
+                             " rabbitmq_parameter_all.rabbitmq_parameters() The data is not complete   ");
+    }
+
+    //amqp_socket_open 打开socket连接, 输入ip和port,
+    // 成功返回AMQP_STATUS_OK = 0x0, 失败返回错误状态码, 详见 enum amqp_status_enum_
+    //只需要设置配置服务器的ip和port, 不需要配置子节点客户端的ip和port, 在后面配置channel通道时,进行设置.
+    t_status = amqp_socket_open(mp_socket, m_ip.c_str(), m_port);
+    if (t_status != AMQP_STATUS_OK) {
+        return Error_manager(Error_code::RABBITMQ_AMQP_SOCKET_OPEN_ERROR, Error_level::MINOR_ERROR,
+                             amqp_error_to_string(t_status, "amqp_socket_open"));
+    }
+
+    //amqp_login() 登录代理服务器,
+    //输入 连接参数结构体 amqp_connection_state_t,
+    //输入 连接地址, 前面 amqp_socket_open() 已经输入了,这里默认写"/"
+    //输入 连接通道最大值, 默认值0表示没有限制
+    //输入 连接帧率最大值, 默认值是131072 (128KB)
+    //输入 心跳帧之间的秒数, 默认值0禁用心跳
+    //输入 身份验证模式, 	AMQP_SASL_METHOD_PLAIN, 追加用户名和密码
+    //					AMQP_SASL_METHOD_EXTERNAL, 追加身份证
+    //返回 结果的结构体 amqp_rpc_reply_t
+    //	amqp_response_type_enum reply_type 登录成功是 AMQP_RESPONSE_NORMAL
+    //		失败:如果是 reply_type == AMQP_RESPONSE_SERVER_EXCEPTION, 服务器连接错误, 错误信息在 amqp_method_t reply
+    //		失败:如果是 reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION, 库函数错误, 错误信息在 int library_error
+    t_reply = amqp_login(mp_connect, "/", 0, 131072, 0,
+                         AMQP_SASL_METHOD_PLAIN, m_user.c_str(), m_password.c_str());
+    if (t_reply.reply_type != AMQP_RESPONSE_NORMAL) {
+        return Error_manager(Error_code::RABBITMQ_AMQP_LOGIN_ERROR, Error_level::MINOR_ERROR,
+                             amqp_error_to_string(t_reply, "amqp_login"));
+    }
+
+    //清除channel_map, 通道的缓存,防止重复开启, (channel允许重复使用, 但是不能重复初始化)
+    m_channel_map.clear();
+
+    //创建通道队列消费者, (交换机和永久队列不在代码里创建,请在服务器上手动创建)
+    t_error = rabbitmq_new_channel_queue_consume(rabbitmq_parameter_all);
+    if (t_error != Error_code::SUCCESS) {
+        return t_error;
+    }
+
+    //启动通信, 开启线程, run thread
+    t_error = rabbitmq_run();
+    if (t_error != Error_code::SUCCESS) {
+        return t_error;
+    }
+    return Error_code::SUCCESS;
+}
+
+//创建通道队列消费者, (交换机和永久队列不在代码里创建,请在服务器上手动创建)
+Error_manager
+Rabbitmq_base::rabbitmq_new_channel_queue_consume(Rabbitmq_proto::Rabbitmq_parameter_all &rabbitmq_parameter_all) {
+    int t_status = 0;        //状态
+    amqp_rpc_reply_t t_reply;    //reply答复结果
+    Error_manager t_error;
+
+    ///Rabbitmq 接受的通道,队列和消费者, 多个
+    for (int i = 0; i < rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_reciever_vector_size(); ++i) {
+        //Rabbitmq 配置的通道,队列和消费者,
+        Rabbitmq_proto::Rabbitmq_channel_queue_consume t_inf =
+                rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_reciever_vector(i);
+
+        //通道查重,防止重复开启(channel允许重复使用, 但是不能重复初始化)
+        if (m_channel_map.find(t_inf.channel()) == m_channel_map.end()) {
+            //amqp_channel_open() 打开连接通道, 同一台电脑可以多个进程和线程进行连接服务器, 每个连接需要自己独特的通道.
+            amqp_channel_open(mp_connect, t_inf.channel());
+            //amqp_get_rpc_reply() 获取当前网络连接的状态结果.
+            t_reply = amqp_get_rpc_reply(mp_connect);
+            if (t_reply.reply_type != AMQP_RESPONSE_NORMAL) {
+                return Error_manager(Error_code::RABBITMQ_AMQP_CHANNEL_OPEN_ERROR, Error_level::MINOR_ERROR,
+                                     amqp_error_to_string(t_reply, "amqp_channel_open"));
+            }
+            if (t_inf.consume_no_ack() == 0) {
+                //amqp_basic_qos设置通道每次只能接受一条消息, 直到该消息被ack,才能接受下一条.状态消息可以继续接受
+                //uint16_t prefetch_count 同时接受消息的个数, 这里固定写1,
+                //配合 amqp_basic_qos 和 amqp_basic_ack , 来阻塞这个通道的接受消息
+                //注:请求消息no_ack==0, 当接受一条指令后,该通道被阻塞,其他通道仍然正常接受, 等到任务被执行完,手动调用amqp_basic_ack函数, 则可以继续接受请求消息.
+                //注:状态消息no_ack==1, 当接受一条指令后,该状态消息立刻被删除,然后可以继续接受下一条状态消息.
+                amqp_basic_qos(mp_connect, t_inf.channel(), 0, PREFETCH_COUNT, 0);
+            }
+            m_channel_map[t_inf.channel()] = true;
+        }
+
+        //临时队列需要代码创建, 永久队列需要在服务器上提前手动创建
+        if (t_inf.queue_durable() == 0) {
+            //目前只填充超时时间,  x-message-ttl 队列接受消息 的超时时间 (单位毫秒)
+            if (t_inf.queue_meassage_ttl() != 0) {
+                amqp_table_t t_arguments;        //队列的扩展属性 num_entries 是map长度, amqp_table_entry_t_ 是map指针
+                //目前只填充超时时间,  x-message-ttl 队列接受消息 的超时时间 (单位毫秒)
+                t_arguments.num_entries = 1;
+                amqp_table_entry_t_ t_map_arg;
+                t_map_arg.key = amqp_cstring_bytes("x-message-ttl");        //需要配置的参数
+                t_map_arg.value.kind = AMQP_FIELD_KIND_U16;                                //需要配置的数据类型, 如果是字符串, 写 AMQP_FIELD_KIND_UTF8
+                t_map_arg.value.value.u16 = t_inf.queue_meassage_ttl();                    //需要配置的数值
+                t_arguments.entries = &t_map_arg;
+
+                //amqp_queue_declare() 队列声明, 就是创建新的队列.
+                //输入 amqp_connection_state_t state 连接状态参数的结构体
+                //输入 amqp_channel_t channel 连接通道的编号
+                //输入 amqp_bytes_t queue 队列名称,可以手动命名,如果写空,系统就会自动分配, 手动写amqp_cstring_bytes("abcdefg"), 默认空 amqp_empty_bytes
+                //输入 amqp_boolean_t passive 是否被动,默认0
+                //输入 amqp_boolean_t durable 是否持久,默认0, 节点代码可以创建临时队列(所有权归节点), 服务器手动创建永久队列(所有权归服务器)
+                //		1表示永久队列,当节点死掉,队列在服务器保留,仍然可以接受数据,节点上线后,可以接受掉线期间的所有数据
+                //		0表示临时队列,当节点死掉,队列消失,不再接受数据,直到下次恢复正常
+                //输入 amqp_boolean_t exclusive 是否独立,默认0
+                //输入 amqp_boolean_t auto_delete 是否自动删除,默认0, 1表示消息被消费者接受后,就自动删除消息, 当接收端断连后,队列也会才删除,
+                // 													一般情况下设为0,然后让接受者手动删除.
+                //输入 amqp_table_t arguments 预留参数,默认空 amqp_empty_table
+                //返回 amqp_queue_declare_ok_t *	返回结果
+                amqp_queue_declare(mp_connect, t_inf.channel(), amqp_cstring_bytes(t_inf.queue_name().c_str()),
+                                   t_inf.queue_passive(), t_inf.queue_durable(), t_inf.queue_exclusive(),
+                                   t_inf.queue_auto_delete(), t_arguments);
+            } else {
+                //amqp_queue_declare() 队列声明, 就是创建新的队列.
+                //输入 amqp_connection_state_t state 连接状态参数的结构体
+                //输入 amqp_channel_t channel 连接通道的编号
+                //输入 amqp_bytes_t queue 队列名称,可以手动命名,如果写空,系统就会自动分配, 手动写amqp_cstring_bytes("abcdefg"), 默认空 amqp_empty_bytes
+                //输入 amqp_boolean_t passive 是否被动,默认0
+                //输入 amqp_boolean_t durable 是否持久,默认0, 节点代码可以创建临时队列(所有权归节点), 服务器手动创建永久队列(所有权归服务器)
+                //		1表示永久队列,当节点死掉,队列在服务器保留,仍然可以接受数据,节点上线后,可以接受掉线期间的所有数据
+                //		0表示临时队列,当节点死掉,队列消失,不再接受数据,直到下次恢复正常
+                //输入 amqp_boolean_t exclusive 是否独立,默认0
+                //输入 amqp_boolean_t auto_delete 是否自动删除,默认0, 1表示消息被消费者接受后,就自动删除消息, 当接收端断连后,队列也会才删除,
+                // 													一般情况下设为0,然后让接受者手动删除.
+                //输入 amqp_table_t arguments 预留参数,默认空 amqp_empty_table
+                //返回 amqp_queue_declare_ok_t *	返回结果
+                amqp_queue_declare(mp_connect, t_inf.channel(), amqp_cstring_bytes(t_inf.queue_name().c_str()),
+                                   t_inf.queue_passive(), t_inf.queue_durable(), t_inf.queue_exclusive(),
+                                   t_inf.queue_auto_delete(), amqp_empty_table);
+            }
+
+
+            //amqp_queue_bind 队列绑定, 将队列加载到服务器的交换机下面, 交换机收到消息后,就会检查key,然后放到指定的队列.
+            //输入 amqp_connection_state_t state 连接状态参数的结构体
+            //输入 amqp_channel_t channel 连接通道的编号
+            //输入 amqp_bytes_t queue 队列名称,
+            //输入 amqp_bytes_t exchange 交换机模式字符串
+            //输入 amqp_bytes_t bindingkey 绑定密钥字符串, 交换机的判断规则. 发送端的 routingkey 和 接收端的 bindingkey 需要保持一致
+            //输入 amqp_table_t arguments 预留参数,默认空 amqp_empty_table
+            //返回 amqp_queue_bind_ok_t *	返回结果
+            //注注注注注意了, 队列绑定交换机时,必须保证交换机是有效的.否则报错
+            amqp_queue_bind(mp_connect, t_inf.channel(), amqp_cstring_bytes(t_inf.queue_name().c_str()),
+                            amqp_cstring_bytes(t_inf.exchange_name().c_str()),
+                            amqp_cstring_bytes(t_inf.binding_key().c_str()), amqp_empty_table);
+
+            amqp_rpc_reply_t t_reply = amqp_get_rpc_reply(mp_connect);
+            if (t_reply.reply_type != AMQP_RESPONSE_NORMAL) {
+                return Error_manager(Error_code::RABBITMQ_AMQP_QUEUE_BIND_ERROR, Error_level::MINOR_ERROR,
+                                     amqp_error_to_string(t_reply, "amqp_queue_bind"));
+            }
+        }
+
+        //amqp_basic_consume 创建基本类型的消费者,就是接收端, 消费者绑定队列,只能接受一个队列里面的消息
+        //输入 amqp_connection_state_t state 连接状态参数的结构体
+        //输入 amqp_channel_t channel 连接通道的编号
+        //输入 amqp_bytes_t queue 队列名称,
+        //输入 amqp_bytes_t consumer_tag 消费者名称
+        //输入 amqp_boolean_t no_local 是否非本地, 默认0,表示本地
+        //输入 amqp_boolean_t no_ack, 是否确认应答,默认0,表示接收后需要应答
+        //输入 amqp_boolean_t exclusive 是否独立,默认0
+        //输入 amqp_table_t arguments 预留参数,默认空 amqp_empty_table
+        //返回 amqp_basic_consume_ok_t *	返回结果
+        //注注注注注意了, 接受端绑定队列时,必须保证队列是有效的,否则报错,
+        amqp_basic_consume(mp_connect, t_inf.channel(), amqp_cstring_bytes(t_inf.queue_name().c_str()),
+                           amqp_cstring_bytes(t_inf.consume_name().c_str()), t_inf.consume_no_local(),
+                           t_inf.consume_no_ack(), t_inf.consume_exclusive(), amqp_empty_table);
+        amqp_rpc_reply_t t_reply = amqp_get_rpc_reply(mp_connect);
+        if (t_reply.reply_type != AMQP_RESPONSE_NORMAL) {
+            return Error_manager(Error_code::RABBITMQ_AMQP_NEW_CONSUME_ERROR, Error_level::MINOR_ERROR,
+                                 amqp_error_to_string(t_reply, "amqp_basic_consume"));
+        }
+    }
+
+    //Rabbitmq 发送请求的通道
+    for (int i = 0; i < rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_request_vector_size(); ++i) {
+        //Rabbitmq 配置发送通道
+        Rabbitmq_proto::Rabbitmq_channel_queue_consume t_inf1 =
+                rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_request_vector(i);
+
+        //通道查重,防止重复开启(channel允许重复使用, 但是不能重复初始化)
+        if (m_channel_map.find(t_inf1.channel()) == m_channel_map.end()) {
+            //amqp_channel_open() 打开连接通道, 同一台电脑可以多个进程和线程进行连接服务器, 每个连接需要自己独特的通道.
+            amqp_channel_open(mp_connect, t_inf1.channel());
+            //amqp_get_rpc_reply() 获取当前网络连接的状态结果.
+            t_reply = amqp_get_rpc_reply(mp_connect);
+            if (t_reply.reply_type != AMQP_RESPONSE_NORMAL) {
+                return Error_manager(Error_code::RABBITMQ_AMQP_CHANNEL_OPEN_ERROR, Error_level::MINOR_ERROR,
+                                     amqp_error_to_string(t_reply, "amqp_channel_open"));
+            }
+            m_channel_map[t_inf1.channel()] = true;
+        }
+    }
+
+    //Rabbitmq 发送状态的通道
+    for (int i = 0; i < rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_status_vector_size(); ++i) {
+        //Rabbitmq 配置发送通道
+        Rabbitmq_proto::Rabbitmq_channel_queue_consume t_inf2 =
+                rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_status_vector(i);
+
+        //通道查重,防止重复开启(channel允许重复使用, 但是不能重复初始化)
+        if (m_channel_map.find(t_inf2.channel()) == m_channel_map.end()) {
+            //amqp_channel_open() 打开连接通道, 同一台电脑可以多个进程和线程进行连接服务器, 每个连接需要自己独特的通道.
+            amqp_channel_open(mp_connect, t_inf2.channel());
+            //amqp_get_rpc_reply() 获取当前网络连接的状态结果.
+            t_reply = amqp_get_rpc_reply(mp_connect);
+            if (t_reply.reply_type != AMQP_RESPONSE_NORMAL) {
+                return Error_manager(Error_code::RABBITMQ_AMQP_CHANNEL_OPEN_ERROR, Error_level::MINOR_ERROR,
+                                     amqp_error_to_string(t_reply, "amqp_channel_open"));
+            }
+            m_channel_map[t_inf2.channel()] = true;
+        }
+    }
+
+    return Error_code::SUCCESS;
+}
+
+//启动通信, 开启线程, run thread
+Error_manager Rabbitmq_base::rabbitmq_run() {
+    //启动 线程。
+    //接受线程默认循环, 内部的 amqp_consume_message 进行等待, 超时1ms
+    m_receive_analysis_condition.reset(false, true, false);
+    mp_receive_analysis_thread = new std::thread(&Rabbitmq_base::receive_analysis_thread, this);
+    //发送线程默认循环, 内部的wait_and_pop进行等待,
+    m_send_condition.reset(false, true, false);
+    mp_send_thread = new std::thread(&Rabbitmq_base::send_thread, this);
+    //封装线程默认等待, ...., 超时1秒, 超时后主动 封装心跳和状态信息,
+    m_encapsulate_status_condition.reset(false, false, false);
+    mp_encapsulate_status_thread = new std::thread(&Rabbitmq_base::encapsulate_status_thread, this);
+
+    m_rabbitmq_status = RABBITMQ_STATUS_READY;
+    return Error_code::SUCCESS;
+}
+
+//反初始化 通信 模块。
+Error_manager Rabbitmq_base::rabbitmq_uninit() {
+    LOG(INFO) << " ---Rabbitmq_base::rabbitmq_uninit() run--- " << this;
+
+    //终止list,防止 wait_and_pop 阻塞线程。
+    m_send_list.termination_list();
+
+    //杀死线程,强制退出
+    if (mp_receive_analysis_thread) {
+        m_receive_analysis_condition.kill_all();
+    }
+    if (mp_send_thread) {
+        m_send_condition.kill_all();
+    }
+    if (mp_encapsulate_status_thread) {
+        m_encapsulate_status_condition.kill_all();
+    }
+
+
+    //回收线程的资源
+    if (mp_receive_analysis_thread) {
+        mp_receive_analysis_thread->join();
+        delete mp_receive_analysis_thread;
+        mp_receive_analysis_thread = NULL;
+    }
+    if (mp_send_thread) {
+        mp_send_thread->join();
+        delete mp_send_thread;
+        mp_send_thread = NULL;
+    }
+    if (mp_encapsulate_status_thread) {
+        mp_encapsulate_status_thread->join();
+        delete mp_encapsulate_status_thread;
+        mp_encapsulate_status_thread = NULL;
+    }
+
+    //清空list
+    m_send_list.clear_and_delete();
+
+    if (m_rabbitmq_status == RABBITMQ_STATUS_READY) {
+        for (auto iter = m_channel_map.begin(); iter != m_channel_map.end(); ++iter) {
+            amqp_channel_close(mp_connect, iter->first, AMQP_REPLY_SUCCESS);
+        }
+        amqp_connection_close(mp_connect, AMQP_REPLY_SUCCESS);
+        amqp_destroy_connection(mp_connect);
+    }
+
+    m_rabbitmq_status = RABBITMQ_STATUS_UNKNOW;
+    return Error_code::SUCCESS;
+}
+
+//重连, 快速uninit, init
+Error_manager Rabbitmq_base::rabbitmq_reconnnect() {
+    //重连全程加锁,防止其他线程运行.
+    std::unique_lock<std::mutex> lk(m_mutex);
+    m_rabbitmq_status = RABBITMQ_STATUS_RECONNNECT;
+
+    //断开连接
+    for (auto iter = m_channel_map.begin(); iter != m_channel_map.end(); ++iter) {
+        amqp_channel_close(mp_connect, iter->first, AMQP_REPLY_SUCCESS);
+    }
+    amqp_connection_close(mp_connect, AMQP_REPLY_SUCCESS);
+    amqp_destroy_connection(mp_connect);
+
+    //重新连接,线程不需要重启
+    LOG(INFO) << " ---Rabbitmq_base::rabbitmq_reconnnect() run--- " << this;
+
+    int t_status = 0;        //状态
+    amqp_rpc_reply_t t_reply;    //reply答复结果
+    Error_manager t_error;
+
+    //amqp_new_connection 新建amqp的连接配置,里面只有连接状态参数
+    // 返回amqp_connection_state_t_ *, 函数内部分配内存, amqp_destroy_connection()可以释放内存, 内存不为空则成功
+    mp_connect = amqp_new_connection();
+    if (mp_connect == NULL) {
+        return Error_manager(Error_code::RABBITMQ_AMQP_NEW_CONNECTION_ERROR, Error_level::MINOR_ERROR,
+                             "amqp_new_connection fun error ");
+    }
+
+    //amqp_tcp_socket_new 新建tcp_socket连接
+    // 返回amqp_socket_t *, 函数内部分配内存, amqp_connection_close()可以释放内存, 内存不为空则成功
+    mp_socket = amqp_tcp_socket_new(mp_connect);
+    if (mp_socket == NULL) {
+        return Error_manager(Error_code::RABBITMQ_AMQP_TCP_SOCKET_NEW_ERROR, Error_level::MINOR_ERROR,
+                             "amqp_tcp_socket_new fun error ");
+    }
+
+    //载入外部参数
+    if (m_rabbitmq_parameter_all.rabbitmq_parameters().has_ip() &&
+        m_rabbitmq_parameter_all.rabbitmq_parameters().has_port() &&
+        m_rabbitmq_parameter_all.rabbitmq_parameters().has_user() &&
+        m_rabbitmq_parameter_all.rabbitmq_parameters().has_password()) {
+        m_ip = m_rabbitmq_parameter_all.rabbitmq_parameters().ip();
+        m_port = m_rabbitmq_parameter_all.rabbitmq_parameters().port();
+        m_user = m_rabbitmq_parameter_all.rabbitmq_parameters().user();
+        m_password = m_rabbitmq_parameter_all.rabbitmq_parameters().password();
+    } else {
+        return Error_manager(Error_code::RABBITMQ_PROTOBUF_LOSS_ERROR, Error_level::MINOR_ERROR,
+                             " rabbitmq_parameter_all.rabbitmq_parameters() The data is not complete   ");
+    }
+
+    //amqp_socket_open 打开socket连接, 输入ip和port,
+    // 成功返回AMQP_STATUS_OK = 0x0, 失败返回错误状态码, 详见 enum amqp_status_enum_
+    //只需要设置配置服务器的ip和port, 不需要配置子节点客户端的ip和port, 在后面配置channel通道时,进行设置.
+    t_status = amqp_socket_open(mp_socket, m_ip.c_str(), m_port);
+    if (t_status != AMQP_STATUS_OK) {
+        return Error_manager(Error_code::RABBITMQ_AMQP_SOCKET_OPEN_ERROR, Error_level::MINOR_ERROR,
+                             amqp_error_to_string(t_status, "amqp_socket_open"));
+    }
+
+    //amqp_login() 登录代理服务器,
+    //输入 连接参数结构体 amqp_connection_state_t,
+    //输入 连接地址, 前面 amqp_socket_open() 已经输入了,这里默认写"/"
+    //输入 连接通道最大值, 默认值0表示没有限制
+    //输入 连接帧率最大值, 默认值是131072 (128KB)
+    //输入 心跳帧之间的秒数, 默认值0禁用心跳
+    //输入 身份验证模式, 	AMQP_SASL_METHOD_PLAIN, 追加用户名和密码
+    //					AMQP_SASL_METHOD_EXTERNAL, 追加身份证
+    //返回 结果的结构体 amqp_rpc_reply_t
+    //	amqp_response_type_enum reply_type 登录成功是 AMQP_RESPONSE_NORMAL
+    //		失败:如果是 reply_type == AMQP_RESPONSE_SERVER_EXCEPTION, 服务器连接错误, 错误信息在 amqp_method_t reply
+    //		失败:如果是 reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION, 库函数错误, 错误信息在 int library_error
+    t_reply = amqp_login(mp_connect, "/", 0, 131072, 0,
+                         AMQP_SASL_METHOD_PLAIN, m_user.c_str(), m_password.c_str());
+    if (t_reply.reply_type != AMQP_RESPONSE_NORMAL) {
+        return Error_manager(Error_code::RABBITMQ_AMQP_LOGIN_ERROR, Error_level::MINOR_ERROR,
+                             amqp_error_to_string(t_reply, "amqp_login"));
+    }
+
+    //清除channel_map, 通道的缓存,防止重复开启, (channel允许重复使用, 但是不能重复初始化)
+    m_channel_map.clear();
+
+    //创建通道队列消费者, (交换机和永久队列不在代码里创建,请在服务器上手动创建)
+    t_error = rabbitmq_new_channel_queue_consume(m_rabbitmq_parameter_all);
+    if (t_error != Error_code::SUCCESS) {
+        return t_error;
+    }
+
+    //不用重启线程
+    return Error_code::SUCCESS;
+
+}
+
+//设置 自动封装状态的时间周期
+void Rabbitmq_base::set_encapsulate_status_cycle_time(unsigned int encapsulate_status_cycle_time) {
+    m_encapsulate_status_cycle_time = encapsulate_status_cycle_time;
+}
+
+//设置回调函数check_msg_callback
+void Rabbitmq_base::set_check_msg_callback(Error_manager (*callback)(Rabbitmq_message *p_msg)) {
+    check_msg_callback = callback;
+}
+
+//设置回调函数check_executer_callback
+void Rabbitmq_base::set_check_executer_callback(Error_manager (*callback)(Rabbitmq_message *p_msg)) {
+    check_executer_callback = callback;
+}
+
+//设置回调函数execute_msg_callback
+void Rabbitmq_base::set_execute_msg_callback(Error_manager (*callback)(Rabbitmq_message *p_msg)) {
+    execute_msg_callback = callback;
+}
+
+//设置回调函数encapsulate_status_callback
+void Rabbitmq_base::set_encapsulate_status_callback(Error_manager (*callback)()) {
+    encapsulate_status_callback = callback;
+}
+
+//mp_receive_analysis_thread 接受解析 执行函数,
+void Rabbitmq_base::receive_analysis_thread() {
+    LOG(INFO) << " Rabbitmq_base::receive_analysis_thread start " << this;
+
+    //通信接受线程, 负责接受socket消息, 并存入 m_receive_data_list
+    while (m_receive_analysis_condition.is_alive()) {
+        //这里就不需要超时等待了, rabbitmq的接受函数可以配置等待超时....
+//		m_receive_analysis_condition.wait_for_ex(std::chrono::microseconds(1));
+        m_receive_analysis_condition.wait();
+        if (m_receive_analysis_condition.is_alive()) {
+            std::this_thread::sleep_for(std::chrono::microseconds(100));
+            std::this_thread::yield();
+
+            amqp_rpc_reply_t t_reply;        //运行结果
+            amqp_envelope_t t_envelope;        //数据包, 含有一些包裹属性和数据内容
+            //接受消息等待超时,默认1000us, 当收到消息后,立刻通过阻塞,否则等待超时后通过阻塞
+            struct timeval t_timeout;        //超时时间, 默认1ms
+            t_timeout.tv_sec = 0;
+            t_timeout.tv_usec = 1000;
+
+            {//这个大括号表示只对 recv 和 send 加锁, 不要因为后面的复杂逻辑影响通信效率
+                std::unique_lock<std::mutex> lk(m_mutex);
+                //允许释放连接参数状态的内存,
+                // 因为这个连接是底层分配的内存,是全局的. 为了开启多个连接,就要重复使用
+                //这里释放之后,其他代码就开启多线程开启新的连接了.
+                amqp_maybe_release_buffers(mp_connect);
+                //amqp_consume_message 接受消息, 阻塞函数,可以设置超时.
+                //输入 amqp_connection_state_t state 连接状态参数的结构体
+                //输入 amqp_envelope_t *envelope 接受数据包的指针, 成功接收到数据后,数据包会覆盖
+                //输入 const struct timeval *timeout 超时时间, 防止阻塞. 传入NULL就是完全阻塞.
+                //输入 int flags 未使用, 默认0
+                //输入 amqp_connection_state_t state 连接状态参数的结构体
+                //返回 状态结果的结构体 amqp_rpc_reply_t
+                //	amqp_response_type_enum reply_type 成功是 AMQP_RESPONSE_NORMAL
+                //		失败:如果是 reply_type == AMQP_RESPONSE_SERVER_EXCEPTION, 服务器连接错误, 错误信息在 amqp_method_t reply
+                //		失败:如果是 reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION, 库函数错误, 错误信息在 int library_error
+                t_reply = amqp_consume_message(mp_connect, &t_envelope, &t_timeout, 0);
+            }
+
+            if (AMQP_RESPONSE_NORMAL == t_reply.reply_type)//正常接受到消息
+            {
+
+
+                m_rabbitmq_status = RABBITMQ_STATUS_READY;
+                //从t_envelope数据包里面提取信息
+                std::string t_receive_string = std::string((char *) t_envelope.message.body.bytes,
+                                                           t_envelope.message.body.len);
+                int t_channel = t_envelope.channel;
+                int t_delivery_tag = t_envelope.delivery_tag;
+                std::string t_exchange_name = std::string((char *) t_envelope.exchange.bytes, t_envelope.exchange.len);
+                std::string t_routing_key = std::string((char *) t_envelope.routing_key.bytes,
+                                                        t_envelope.routing_key.len);
+                //如果这里接受到了消息, 在这提前解析消息最前面的Base_msg (消息公共内容), 用于后续的check
+                message::Base_msg t_base_msg;
+//				if( t_base_msg.ParseFromString(t_receive_string) )
+
+                //删除 message::Base_msg 里面的 message::Base_info的机制,完全依赖服务器来分发消息
+                if (true) {
+                    //第一次解析之后转化为, Communication_message, 自定义的通信消息格式
+                    Rabbitmq_message t_rabbitmq_message;
+                    t_rabbitmq_message.reset(t_base_msg.base_info(), t_receive_string, t_channel, t_delivery_tag,
+                                             t_exchange_name, t_routing_key);
+                    //检查消息是否有效, 主要检查消息类型和接受者, 判断这条消息是不是给我的.
+                    if (check_msg(&t_rabbitmq_message) == SUCCESS) {
+                        //这里直接就用当前线程进行处理,
+                        //检查消息是否可以被处理
+                        if (check_executer(&t_rabbitmq_message) == SUCCESS) {
+                            //处理消息
+                            if (execute_msg(&t_rabbitmq_message) == SUCCESS) {
+
+                            }
+                            //else不做处理
+                        }
+                        //else不做处理
+                    }
+                    //else不做处理
+                }
+                    //else解析失败, 就当做什么也没发生, 认为接收消息无效,
+                else {
+                    std::cout << " huli test :::: " << " t_receive_string = " << t_receive_string << std::endl;
+                    if (t_channel == 401) {
+                        amqp_basic_ack(mp_connect, t_channel, t_delivery_tag, 0);
+                    }
+                }
+
+                //amqp_destroy_envelope 销毁数据包, 只有接受成功, t_envelope才有内存
+                amqp_destroy_envelope(&t_envelope);
+            } else//没有接受到消息
+            {
+                //超时报错,不做处理, continue
+                //注注注注注意了, 没有收到消息会超时报错, res.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION, res.library_error = -13, (-0x000D request timed out)
+                if (t_reply.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION && t_reply.library_error == -13) {
+                    m_rabbitmq_status = RABBITMQ_STATUS_READY;
+                    continue;
+                } else//其他报错,特殊处理
+                {
+                    //need
+
+                    std::string error_description = amqp_error_to_string(t_reply, "amqp_consume_message");
+                    LOG(WARNING) << " huli test 123123123:::: " << " error_description = " << error_description
+                              << std::endl;
+//					return Error_manager(Error_code::RABBITMQ_AMQP_CONSUME_MESSAGE_ERROR, Error_level::MINOR_ERROR,
+//											 amqp_error_to_string(t_reply, "amqp_consume_message") );
+                    //重启
+                    rabbitmq_reconnnect();
+
+                }
+            }
+        }
+    }
+
+    LOG(INFO) << " Rabbitmq_base::receive_analysis_thread end " << this;
+    return;
+}
+
+//检查消息是否有效, 主要检查消息类型和接受者, 判断这条消息是不是给我的., 需要子类重载
+Error_manager Rabbitmq_base::check_msg(Rabbitmq_message *p_msg) {
+    if (check_msg_callback != NULL) {
+        return check_msg_callback(p_msg);
+    }
+    return Error_code::SUCCESS;
+}
+
+//检查执行者的状态, 判断能否处理这条消息, 需要子类重载
+Error_manager Rabbitmq_base::check_executer(Rabbitmq_message *p_msg) {
+    if (check_executer_callback != NULL) {
+        return check_executer_callback(p_msg);
+    }
+    return Error_code::SUCCESS;
+}
+
+//处理消息, 需要子类重载
+Error_manager Rabbitmq_base::execute_msg(Rabbitmq_message *p_msg) {
+    if (execute_msg_callback != NULL) {
+        return execute_msg_callback(p_msg);
+    } else {
+        //需要子类重载
+        std::cout << " huli test :::: " << " execute_msg Rabbitmq_message = " << p_msg->get_message_buf() << std::endl;
+
+        //如果是请求消息,那么在子节点继承的时候一定要记得调用
+        //配置rabbitmq.proto时,  如果consume_no_ack == 0 , 一定要手动调用 amqp_basic_ack
+        int consume_no_ack = 1;
+        if (consume_no_ack == 0 || p_msg->m_channel == 401) {
+            //amqp_basic_ack 确认消息, 通知服务器队列手动删除消息.
+            //输入 amqp_connection_state_t state 连接状态参数的结构体
+            //输入 amqp_channel_t channel 连接通道的编号
+            //输入 uint64_t delivery_tag  消息传递编号,
+            //输入 amqp_boolean_t multiple 多个标记位, 默认0,   1表示删除1~delivery_tag的所有消息, 不删除大于delivery_tag的, 0表示只删除这一条
+            int ack_result = amqp_basic_ack(mp_connect, p_msg->m_channel, p_msg->m_delivery_tag, 0);
+        }
+    }
+    return Error_code::SUCCESS;
+}
+
+//ack_msg 处理完消息后, 手动确认消息, 通知服务器队列删除消息.
+//执行者在execute_msg里面可以调用这个函数, 或者回调也行.
+Error_manager Rabbitmq_base::ack_msg(Rabbitmq_message *p_msg) {
+    //amqp_basic_ack 确认消息, 通知服务器队列手动删除消息.
+    //输入 amqp_connection_state_t state 连接状态参数的结构体
+    //输入 amqp_channel_t channel 连接通道的编号
+    //输入 uint64_t delivery_tag  消息传递编号,
+    //输入 amqp_boolean_t multiple 多个标记位, 默认0,   1表示删除1~delivery_tag的所有消息, 不删除大于delivery_tag的, 0表示只删除这一条
+    int ack_result = amqp_basic_ack(mp_connect, p_msg->m_channel, p_msg->m_delivery_tag, 0);
+    if (ack_result != 0) {
+        return Error_manager(Error_code::RABBITMQ_AMQP_BASIC_ACK_ERROR, Error_level::MINOR_ERROR,
+                             amqp_error_to_string(ack_result, "amqp_basic_ack"));
+    }
+    return Error_code::SUCCESS;
+}
+
+//mp_send_thread 发送线程执行函数,
+void Rabbitmq_base::send_thread() {
+    LOG(INFO) << " Rabbitmq_base::send_thread start " << this;
+
+    //通信发送线程, 负责巡检m_send_list, 并发送消息
+    while (m_send_condition.is_alive()) {
+        m_send_condition.wait();
+        if (m_send_condition.is_alive()) {
+            std::this_thread::yield();
+
+            Rabbitmq_message *tp_msg = NULL;
+            int t_result = 0;
+            //这里 wait_and_pop 会使用链表内部的 m_data_cond 条件变量来控制等待,
+            //封装线程使用push的时候, 会唤醒线程并通过等待, 此时 m_send_data_condition 是一直通过的.
+            //如果需要退出, 那么就要 m_send_data_list.termination_list();	和 m_send_data_condition.kill_all();
+            bool is_pop = m_send_list.wait_and_pop(tp_msg);
+            if (is_pop) {
+                if (tp_msg != NULL) {
+                    //amqp_basic_properties_t 消息数据的基本属性,里面有15个成员.
+                    amqp_basic_properties_t props;
+
+                    //判断是否要设置发送消息的超时时间, 如果配置10秒,超时后,服务器会自动删除消息
+                    if (tp_msg->m_timeout_ms == std::chrono::milliseconds(0)) {
+                        //amqp_flags_t _flags	一个uint32_t, 按位 表示这15个属性的修改开关.
+                        //例如:     _flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG = 0b 1001 0000 0000 0000;
+                        //就表示   content-type  和  delivery-mode  是有效属性. 接下来的设置就会生效.
+                        props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG;
+                        //amqp_bytes_t content_type 消息数据的类型 "text/plain"是 普通文本格式
+                        //注意了,需要使用 amqp_cstring_bytes() 将char*转为amqp_bytes_t(自定义的字符串, 类似于std::string)
+                        props.content_type = amqp_cstring_bytes("text/plain");
+                        //uint8_t delivery_mode 配送模式  2表示持续发送模式
+                        props.delivery_mode = AMQP_DELIVERY_PERSISTENT;
+                    } else {
+                        //amqp_flags_t _flags	一个uint32_t, 按位 表示这15个属性的修改开关.
+                        //例如:     _flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG = 0b 1001 0000 0000 0000;
+                        //就表示   content-type  和  delivery-mode  是有效属性. 接下来的设置就会生效.
+                        props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG |
+                                       AMQP_BASIC_EXPIRATION_FLAG;
+                        //amqp_bytes_t content_type 消息数据的类型 "text/plain"是 普通文本格式
+                        //注意了,需要使用 amqp_cstring_bytes() 将char*转为amqp_bytes_t(自定义的字符串, 类似于std::string)
+                        props.content_type = amqp_cstring_bytes("text/plain");
+                        //uint8_t delivery_mode 配送模式  2表示持续发送模式
+                        props.delivery_mode = AMQP_DELIVERY_PERSISTENT;
+                        char buf[256] = {0};
+                        sprintf(buf, "%d", (int) tp_msg->m_timeout_ms.count());
+                        props.expiration = amqp_cstring_bytes(buf);//超时, 单位ms;
+                    }
+
+                    {//这个大括号表示只对 recv 和 send 加锁, 不要因为后面的复杂逻辑影响通信效率
+                        std::unique_lock<std::mutex> lk(m_mutex);
+//						std::cout << " huli test :::: " << " tp_msg->m_message_buf = " << tp_msg->m_message_buf << std::endl;
+
+                        //amqp_basic_publish() 发布消息给代理服务器, 在交换器上发布一个带有路由密钥的消息。交换机会根据路由密钥匹配,放到对应的队列里面
+                        //输入 amqp_connection_state_t state 连接状态参数的结构体
+                        //输入 amqp_channel_t channel 连接通道的编号
+                        //输入 amqp_bytes_t exchange 交换机模式字符串
+                        //输入 amqp_bytes_t routing_key 路由密钥字符串, 交换机的判断规则. 发送端的 routingkey 和 接收端的 bindingkey 需要保持一致
+                        //输入 amqp_boolean_t mandatory 强制服务器必须通过路由密钥才能存到队列, 默认为0
+                        //输入 amqp_boolean_t immediate 表示服务器必须立刻转发消息给接受者, 默认为0
+                        //输入 struct amqp_basic_properties_t_ const *properties 消息数据的基本属性
+                        //输入 amqp_bytes_t body 消息数据内容
+                        //返回错误码 成功返回AMQP_STATUS_OK = 0x0, 失败返回错误状态码, 详见 enum amqp_status_enum_
+                        //注注注注注意了::amqp_basic_publish()是异步通信,
+                        // return AMQP_STATUS_OK 也只是表示消息成功发送到服务器. 无法确认 接收端是否正常接受消息
+                        t_result = amqp_basic_publish(mp_connect, tp_msg->m_channel,
+                                                      amqp_cstring_bytes(tp_msg->m_exchange_name.c_str()),
+                                                      amqp_cstring_bytes(tp_msg->m_routing_key.c_str()), 0, 0,
+                                                      &props, amqp_cstring_bytes(tp_msg->m_message_buf.c_str()));
+                    }
+
+
+                    if (t_result == AMQP_STATUS_OK) {
+                        m_rabbitmq_status = RABBITMQ_STATUS_READY;
+                        delete (tp_msg);
+                        tp_msg = NULL;
+//							std::string re = amqp_error_to_string(t_result, "amqp_basic_publish");
+//							std::cout << " huli test :::: " << " re = " << re << std::endl;
+//							return Error_manager(Error_code::RABBITMQ_AMQP_BASIC_PUBLISH_ERROR, Error_level::MINOR_ERROR,
+//												 amqp_error_to_string(t_result, "amqp_basic_publish") );
+                    } else {
+                        std::string re = amqp_error_to_string(t_result, "amqp_basic_publish");
+                        std::cout << " huli test :::: " << " re = " << re << std::endl;
+
+                        //重启
+                        m_rabbitmq_status = RABBITMQ_STATUS_RECONNNECT;
+                        m_send_list.push(tp_msg);    //重新加入队列,下一次再发
+                        tp_msg = NULL;
+                        rabbitmq_reconnnect();
+                    }
+
+
+                }
+            } else {
+                //没有取出, 那么应该就是 m_termination_flag 结束了
+//				return Error_manager(Error_code::CONTAINER_IS_TERMINATE, Error_level::MINOR_ERROR,
+//									 " Communication_socket_base::send_data_thread() error ");
+            }
+        }
+    }
+
+    LOG(INFO) << " Rabbitmq_base::send_thread end " << this;
+    return;
+}
+
+//手动封装消息,需要手动写入参数channel,exchange_name,routing_key
+Error_manager
+Rabbitmq_base::encapsulate_msg(std::string message, int channel, std::string exchange_name, std::string routing_key,
+                               int timeout_ms = 0) {
+    if (m_rabbitmq_status != RABBITMQ_STATUS_READY) {
+        LOG(ERROR) << " m_rabbitmq_status error ";
+        return Error_manager(Error_code::ERROR, Error_level::MINOR_ERROR,
+                             " m_rabbitmq_status error ");
+    }
+//    LOG(INFO) << exchange_name << " " << routing_key;
+    Rabbitmq_message *tp_msg = new Rabbitmq_message(message, channel, exchange_name, routing_key, timeout_ms);
+    bool is_push = m_send_list.push(tp_msg);
+    if (is_push == false) {
+        delete (tp_msg);
+        tp_msg = NULL;
+        return Error_manager(Error_code::CONTAINER_IS_TERMINATE, Error_level::MINOR_ERROR,
+                             " Communication_socket_base::encapsulate_msg error ");
+    }
+    return Error_code::SUCCESS;
+}
+
+//手动封装消息,需要手动写入参数channel,exchange_name,routing_key
+Error_manager Rabbitmq_base::encapsulate_msg(Rabbitmq_message *p_msg) {
+    Rabbitmq_message *tp_msg = new Rabbitmq_message(*p_msg);
+    bool is_push = m_send_list.push(tp_msg);
+    if (is_push == false) {
+        delete (tp_msg);
+        tp_msg = NULL;
+        return Error_manager(Error_code::CONTAINER_IS_TERMINATE, Error_level::MINOR_ERROR,
+                             " Communication_socket_base::encapsulate_msg error ");
+    }
+    return Error_code::SUCCESS;
+}
+
+//手动封装任务消息(请求和答复), 系统会使用rabbitmq.proto的配置参数,
+Error_manager Rabbitmq_base::encapsulate_task_msg(std::string message, int vector_index) {
+    int channel = m_rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_request_vector(vector_index).channel();
+    std::string exchange_name = m_rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_request_vector(
+            vector_index).exchange_name();
+    std::string routing_key = m_rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_request_vector(
+            vector_index).routing_key();
+    int timeout_ms = m_rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_request_vector(
+            vector_index).timeout_ms();
+    Rabbitmq_message *tp_msg = new Rabbitmq_message(message, channel, exchange_name, routing_key, timeout_ms);
+    bool is_push = m_send_list.push(tp_msg);
+    if (is_push == false) {
+        delete (tp_msg);
+        tp_msg = NULL;
+        return Error_manager(Error_code::CONTAINER_IS_TERMINATE, Error_level::MINOR_ERROR,
+                             " Communication_socket_base::encapsulate_msg error ");
+    }
+    return Error_code::SUCCESS;
+}
+
+//手动封装状态消息, 系统会使用rabbitmq.proto的配置参数,
+Error_manager Rabbitmq_base::encapsulate_status_msg(std::string message, int vector_index) {
+    if (vector_index >= m_rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_status_vector().size()) {
+        LOG(WARNING) << "vector index error.";
+        return {FAILED, NORMAL};
+    }
+    int channel = m_rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_status_vector(vector_index).channel();
+    std::string exchange_name = m_rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_status_vector(
+            vector_index).exchange_name();
+    std::string routing_key = m_rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_status_vector(
+            vector_index).routing_key();
+    int timeout_ms = m_rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_status_vector(
+            vector_index).timeout_ms();
+    Rabbitmq_message *tp_msg = new Rabbitmq_message(message, channel, exchange_name, routing_key, timeout_ms);
+    bool is_push = m_send_list.push(tp_msg);
+    if (is_push == false) {
+        delete (tp_msg);
+        tp_msg = NULL;
+        return Error_manager(Error_code::CONTAINER_IS_TERMINATE, Error_level::MINOR_ERROR,
+                             " Communication_socket_base::encapsulate_msg error ");
+    }
+    return Error_code::SUCCESS;
+}
+
+//mp_encapsulate_stauts_thread 自动封装线程执行函数,
+void Rabbitmq_base::encapsulate_status_thread() {
+    LOG(INFO) << " Rabbitmq_base::encapsulate_status_thread start " << this;
+
+    //通信封装线程, 负责定时封装消息, 并存入 m_send_data_list
+    while (m_encapsulate_status_condition.is_alive()) {
+        bool t_pass_flag = m_encapsulate_status_condition.wait_for_millisecond(m_encapsulate_status_cycle_time);
+
+        if (m_encapsulate_status_condition.is_alive()) {
+            std::this_thread::yield();
+            //如果封装线程被主动唤醒, 那么就表示 需要主动发送消息,
+            if (t_pass_flag) {
+                //主动发送消息,
+            }
+                //如果封装线程超时通过, 那么就定时封装心跳和状态信息
+            else {
+                //只有通信正常的时候,才封装发送状态消息
+                if (m_rabbitmq_status == RABBITMQ_STATUS_READY) {
+                    auto_encapsulate_status();
+                }
+            }
+        }
+    }
+
+    LOG(INFO) << " Rabbitmq_base::encapsulate_status_thread end " << this;
+    return;
+}
+
+//定时封装发送消息, 一般为心跳和状态信息, 需要子类重载
+Error_manager Rabbitmq_base::auto_encapsulate_status() {
+    if (encapsulate_status_callback != NULL) {
+        return encapsulate_status_callback();
+    }
+    return Error_code::SUCCESS;
+}
+
+
+//把rabbitmq的错误信息转化为string, amqp_status就是enum amqp_status_enum_, amqp_error_string2()函数可以把他翻译为string
+std::string Rabbitmq_base::amqp_error_to_string(int amqp_status) {
+    char buf[256] = {0};
+    sprintf(buf, "amqp_status = 0x%x, %s", amqp_status, amqp_error_string2(amqp_status));
+    return buf;
+}
+
+//把rabbitmq的错误信息转化为string, amqp_status就是enum amqp_status_enum_, amqp_error_string2()函数可以把他翻译为string
+std::string Rabbitmq_base::amqp_error_to_string(int amqp_status, std::string amqp_fun_name) {
+    char buf[256] = {0};
+    sprintf(buf, "amqp_fun_name = %s, amqp_status = 0x%x, %s", amqp_fun_name.c_str(), amqp_status,
+            amqp_error_string2(amqp_status));
+    return buf;
+}
+
+//把rabbitmq的错误信息转化为string, amqp_rpc_reply_t就是amqp函数运行的结果
+std::string Rabbitmq_base::amqp_error_to_string(amqp_rpc_reply_t amqp_rpc_reply) {
+    char buf[256] = {0};
+    //	amqp_response_type_enum reply_type 登录成功是 AMQP_RESPONSE_NORMAL
+    //		失败:如果是 reply_type == AMQP_RESPONSE_SERVER_EXCEPTION, 服务器连接错误, 错误信息在 amqp_method_t reply
+    //		失败:如果是 reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION, 库函数错误, 错误信息在 int library_error
+    switch (amqp_rpc_reply.reply_type) {
+        case AMQP_RESPONSE_NORMAL: {
+            sprintf(buf, "SUCCESS");
+            break;
+        }
+        case AMQP_RESPONSE_NONE: {
+            sprintf(buf, " reply_type = AMQP_RESPONSE_NONE ");
+            break;
+        }
+        case AMQP_RESPONSE_LIBRARY_EXCEPTION: {
+            sprintf(buf, " reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION, library_error = %s, ",
+                    amqp_error_string2(amqp_rpc_reply.library_error));
+            break;
+        }
+        case AMQP_RESPONSE_SERVER_EXCEPTION: {
+            if (amqp_rpc_reply.reply.id == AMQP_CONNECTION_CLOSE_METHOD) {
+                amqp_connection_close_t *p_decoded = (amqp_connection_close_t *) amqp_rpc_reply.reply.decoded;
+                sprintf(buf, " reply.id = AMQP_CONNECTION_CLOSE_METHOD, reply = %u, %.*s ",
+                        p_decoded->reply_code, (int) p_decoded->reply_text.len, (char *) p_decoded->reply_text.bytes);
+            } else if (amqp_rpc_reply.reply.id == AMQP_CHANNEL_CLOSE_METHOD) {
+                amqp_channel_close_t *p_decoded = (amqp_channel_close_t *) amqp_rpc_reply.reply.decoded;
+                sprintf(buf, " reply.id = AMQP_CHANNEL_CLOSE_METHOD, reply = %u, %.*s ",
+                        p_decoded->reply_code, (int) p_decoded->reply_text.len, (char *) p_decoded->reply_text.bytes);
+            } else {
+                sprintf(buf, " reply_type = AMQP_RESPONSE_SERVER_EXCEPTION ");
+            }
+            break;
+        }
+        default: {
+            sprintf(buf, " reply_type = unknown, reply.id = 0x%08X, ",
+                    amqp_rpc_reply.reply.id);
+            break;
+        }
+    }
+
+    return buf;
+}
+
+//把rabbitmq的错误信息转化为string, amqp_rpc_reply_t就是amqp函数运行的结果
+std::string Rabbitmq_base::amqp_error_to_string(amqp_rpc_reply_t amqp_rpc_reply, std::string amqp_fun_name) {
+    char buf[256] = {0};
+    //	amqp_response_type_enum reply_type 登录成功是 AMQP_RESPONSE_NORMAL
+    //		失败:如果是 reply_type == AMQP_RESPONSE_SERVER_EXCEPTION, 服务器连接错误, 错误信息在 amqp_method_t reply
+    //		失败:如果是 reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION, 库函数错误, 错误信息在 int library_error
+    switch (amqp_rpc_reply.reply_type) {
+        case AMQP_RESPONSE_NORMAL: {
+            sprintf(buf, "SUCCESS");
+            break;
+        }
+        case AMQP_RESPONSE_NONE: {
+            sprintf(buf, "amqp_fun_name = %s, reply_type = AMQP_RESPONSE_NONE ", amqp_fun_name.c_str());
+            break;
+        }
+        case AMQP_RESPONSE_LIBRARY_EXCEPTION: {
+            // huli test 123123123::::  error_description = amqp_fun_name = amqp_consume_message, reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION, library_error = unexpected protocol state,
+            sprintf(buf, "amqp_fun_name = %s, reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION, library_error = %s, ",
+                    amqp_fun_name.c_str(), amqp_error_string2(amqp_rpc_reply.library_error));
+            break;
+        }
+        case AMQP_RESPONSE_SERVER_EXCEPTION: {
+            if (amqp_rpc_reply.reply.id == AMQP_CONNECTION_CLOSE_METHOD) {
+                amqp_connection_close_t *p_decoded = (amqp_connection_close_t *) amqp_rpc_reply.reply.decoded;
+                sprintf(buf, "amqp_fun_name = %s, reply.id = AMQP_CONNECTION_CLOSE_METHOD, reply = %u, %.*s ",
+                        amqp_fun_name.c_str(), p_decoded->reply_code, (int) p_decoded->reply_text.len,
+                        (char *) p_decoded->reply_text.bytes);
+            } else if (amqp_rpc_reply.reply.id == AMQP_CHANNEL_CLOSE_METHOD) {
+                amqp_channel_close_t *p_decoded = (amqp_channel_close_t *) amqp_rpc_reply.reply.decoded;
+                sprintf(buf, "amqp_fun_name = %s, reply.id = AMQP_CHANNEL_CLOSE_METHOD, reply = %u, %.*s ",
+                        amqp_fun_name.c_str(), p_decoded->reply_code, (int) p_decoded->reply_text.len,
+                        (char *) p_decoded->reply_text.bytes);
+            } else {
+                sprintf(buf, "amqp_fun_name = %s, reply_type = AMQP_RESPONSE_SERVER_EXCEPTION ", amqp_fun_name.c_str());
+            }
+            break;
+        }
+        default: {
+            sprintf(buf, "amqp_fun_name = %s, reply_type = unknown, reply.id = 0x%08X, ",
+                    amqp_fun_name.c_str(), amqp_rpc_reply.reply.id);
+            break;
+        }
+    }
+
+    return buf;
+}
+
+
+

+ 207 - 0
rabbitmq/rabbitmq_base.h

@@ -0,0 +1,207 @@
+/*
+ * rabbitmq_base 通信模块的基类,
+ * 用户从这个基类继承, 初始化之后, 便可以自动进行通信
+ * 重载解析消息和封装消息,
+
+ nanosmg 必须实时接受消息, 如果不收可能就丢包, 所以本地开启了接受线程和解析线程.
+ 	接受线程实时接受,然后缓存到本地list, 然后解析线程再慢慢处理.
+ rabbitmq 的服务器可以缓存消息, 如果子节点不接受, 数据仍然留在服务器上, 在需要的时候再去接受,不会丢包.
+ 	因此删除了接受线程和缓存list, 直接使用解析线程,一边接受一边处理,处理完后再接受下一条.
+
+ * */
+
+#ifndef __RIBBITMQ_BASE__HH__
+#define __RIBBITMQ_BASE__HH__
+
+#include <mutex>
+#include <thread>
+
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <map>
+#include "tool/load_protobuf.hpp"
+
+#include <rabbitmq-c/amqp.h>
+#include <rabbitmq-c/tcp_socket.h>
+#include <cassert>
+
+#include <glog/logging.h>
+#include "tool/error_code.hpp"
+#include "thread/binary_buf.h"
+#include "tool/thread_safe_list.hpp"
+#include "thread/thread_condition.h"
+
+#include "rabbitmq/rabbitmq.pb.h"
+#include "rabbitmq/rabbitmq_message.h"
+
+//rabbitmq初始化配置参数的默认路径
+#define RABBITMQ_PARAMETER_PATH "../etc/rabbitmq.prototxt"
+//amqp_basic_qos设置通道每次只能接受PREFETCH_COUNT条消息, 默认每次只能同时接受1条消息
+#define PREFETCH_COUNT 1
+
+class Rabbitmq_base {
+    //通信状态
+    enum Rabbitmq_status {
+        RABBITMQ_STATUS_UNKNOW = 0,                //通信状态 未知
+        RABBITMQ_STATUS_READY = 1,                //通信状态 正常
+        RABBITMQ_STATUS_DISCONNECT = 11,                //通信状态 断连(可能会在断连和重连之间反复跳动)
+        RABBITMQ_STATUS_RECONNNECT = 12,                //通信状态 重连(可能会在断连和重连之间反复跳动)
+
+
+        RABBITMQ_STATUS_FAULT = 100,            //通信状态 错误
+    };
+public:
+    struct QueueKey {
+        std::string ex;
+        std::string queue;
+    };
+
+public:
+    Rabbitmq_base();
+
+    Rabbitmq_base(const Rabbitmq_base &other) = delete;
+
+    Rabbitmq_base &operator=(const Rabbitmq_base &other) = delete;
+
+    ~Rabbitmq_base();
+
+public://API functions
+    //初始化 通信 模块。如下三选一
+    virtual Error_manager rabbitmq_init();
+
+    //初始化 通信 模块。从文件读取
+    virtual Error_manager rabbitmq_init_from_protobuf(std::string prototxt_path);
+
+    //初始化 通信 模块。从protobuf读取
+    Error_manager rabbitmq_init_from_protobuf(Rabbitmq_proto::Rabbitmq_parameter_all &rabbitmq_parameter_all);
+
+    //反初始化 通信 模块。
+    Error_manager rabbitmq_uninit();
+
+    //重连, 快速uninit, init
+    Error_manager rabbitmq_reconnnect();
+
+    //手动封装消息, 如下四选一
+    //手动封装消息,需要手动写入参数channel,exchange_name,routing_key
+    Error_manager encapsulate_msg(std::string message, int channel, std::string exchange_name, std::string routing_key,
+                                  int timeout_ms);
+
+    //手动封装消息,需要手动写入参数channel,exchange_name,routing_key
+    Error_manager encapsulate_msg(Rabbitmq_message *p_msg);
+
+    //手动封装任务消息(请求和答复), 系统会使用rabbitmq.proto的配置参数,
+    Error_manager encapsulate_task_msg(std::string message, int vector_index = 0);
+
+    //手动封装状态消息, 系统会使用rabbitmq.proto的配置参数,
+    Error_manager encapsulate_status_msg(std::string message, int vector_index = 0);
+
+    //ack_msg 处理完消息后, 手动确认消息, 通知服务器队列删除消息.
+    //执行者在execute_msg里面可以调用这个函数, 或者回调也行.
+    Error_manager ack_msg(Rabbitmq_message *p_msg);
+
+    //设置 自动封装状态的时间周期, 可选(默认1000ms)
+    void set_encapsulate_status_cycle_time(unsigned int encapsulate_status_cycle_time);
+
+    //设置回调函数check_msg_callback
+    void set_check_msg_callback(Error_manager (*callback)(Rabbitmq_message *p_msg));
+
+    //设置回调函数check_executer_callback
+    void set_check_executer_callback(Error_manager (*callback)(Rabbitmq_message *p_msg));
+
+    //设置回调函数execute_msg_callback
+    void set_execute_msg_callback(Error_manager (*callback)(Rabbitmq_message *p_msg));
+
+    //设置回调函数encapsulate_status_callback
+    void set_encapsulate_status_callback(Error_manager (*callback)());
+
+protected:
+    //创建通道队列消费者, (交换机和永久队列不在代码里创建,请在服务器上手动创建)
+    Error_manager rabbitmq_new_channel_queue_consume(Rabbitmq_proto::Rabbitmq_parameter_all &rabbitmq_parameter_all);
+
+    //启动通信, 开启线程, run thread
+    Error_manager rabbitmq_run();
+
+    //mp_receive_analysis_thread 接受解析 执行函数,
+    void receive_analysis_thread();
+
+    //检查消息是否有效, 主要检查消息类型和接受者, 判断这条消息是不是给我的., 需要子类重载
+    virtual Error_manager check_msg(Rabbitmq_message *p_msg);
+
+    //检查执行者的状态, 判断能否处理这条消息, 需要子类重载
+    virtual Error_manager check_executer(Rabbitmq_message *p_msg);
+
+    //处理消息, 需要子类重载
+    virtual Error_manager execute_msg(Rabbitmq_message *p_msg);
+
+    //mp_send_thread 发送线程执行函数,
+    void send_thread();
+
+    //mp_encapsulate_stauts_thread 自动封装线程执行函数,
+    void encapsulate_status_thread();
+
+    //定时封装发送消息, 一般为心跳和状态信息, 需要子类重载
+    virtual Error_manager auto_encapsulate_status();
+
+protected:
+    //把rabbitmq的错误信息转化为string, amqp_status就是enum amqp_status_enum_, amqp_error_string2()函数可以把他翻译为string
+    std::string amqp_error_to_string(int amqp_status);
+
+    //把rabbitmq的错误信息转化为string, amqp_status就是enum amqp_status_enum_, amqp_error_string2()函数可以把他翻译为string
+    std::string amqp_error_to_string(int amqp_status, std::string amqp_fun_name);
+
+    //把rabbitmq的错误信息转化为string, amqp_rpc_reply_t就是amqp函数运行的结果
+    std::string amqp_error_to_string(amqp_rpc_reply_t amqp_rpc_reply);
+
+    //把rabbitmq的错误信息转化为string, amqp_rpc_reply_t就是amqp函数运行的结果
+    std::string amqp_error_to_string(amqp_rpc_reply_t amqp_rpc_reply, std::string amqp_fun_name);
+
+protected://member variable
+    Rabbitmq_status m_rabbitmq_status;        //通信状态
+
+    //rabbitmq网络通信 连接配置信息
+    Rabbitmq_proto::Rabbitmq_parameter_all m_rabbitmq_parameter_all;
+    amqp_connection_state_t_ *mp_connect;        // 连接参数的结构体, 内存系统自动分配,自动释放
+    amqp_socket_t *mp_socket;        // 网口通信socket, 内存系统自动分配,自动释放
+    std::string m_ip;            //服务器ip地址, 不带端口
+    int m_port;            //端口,默认5672
+    std::string m_user;            //用户名, 默认guest
+    std::string m_password;        //密码, 默认guest
+    std::mutex m_mutex;        // socket的锁, 发送和接受的通信锁
+    std::map<int, bool> m_channel_map;    // 通道的缓存,防止重复开启
+
+    // rabbitmq发送队列map
+    std::map<const std::string, Rabbitmq_proto::Rabbitmq_channel_queue_consume> mp_rabbitmq_reciever;
+    std::map<const std::string, Rabbitmq_proto::Rabbitmq_channel_queue_consume> mp_rabbitmq_sender_request;
+    std::map<const std::string, Rabbitmq_proto::Rabbitmq_channel_queue_consume> mp_rabbitmq_sender_status;
+
+    //接受模块,
+    //rabbitmq 的服务器可以缓存消息, 如果子节点不接受, 数据仍然留在服务器上, 在需要的时候再去接受,不会丢包.
+    //因此删除了接受线程和缓存list, 直接使用解析线程,一边接受一边处理,处理完后再接受下一条.
+    std::thread *mp_receive_analysis_thread;                //接受解析的线程指针
+    Thread_condition m_receive_analysis_condition;            //接受解析的条件变量
+
+    //发送模块,
+    Thread_safe_list<Rabbitmq_message *> m_send_list;                //发送的list容器
+    std::thread *mp_send_thread;                //发送的线程指针
+    Thread_condition m_send_condition;            //发送的条件变量
+    //自动发送状态的
+    std::thread *mp_encapsulate_status_thread;            //自动封装状态的线程指针
+    Thread_condition m_encapsulate_status_condition;            //自动封装状态的条件变量
+    unsigned int m_encapsulate_status_cycle_time;        //自动封装状态的时间周期
+
+    //回调函数,
+    // //可以选择设置回调函数,或者子类继承重载,二选一.
+    Error_manager (*check_msg_callback)(Rabbitmq_message *p_msg);
+
+    Error_manager (*check_executer_callback)(Rabbitmq_message *p_msg);
+
+    Error_manager (*execute_msg_callback)(Rabbitmq_message *p_msg);
+
+    Error_manager (*encapsulate_status_callback)();
+};
+
+
+#endif //__RIBBITMQ_BASE__HH__

+ 99 - 0
rabbitmq/rabbitmq_message.cpp

@@ -0,0 +1,99 @@
+#include "rabbitmq_message.h"
+
+
+Rabbitmq_message::Rabbitmq_message() {
+    m_message_type = message::Message_type::eBase_msg;
+    m_receive_time = std::chrono::system_clock::now();
+    m_timeout_ms = std::chrono::milliseconds(0);        //超时默认0秒
+    m_sender = message::Communicator::eEmpty;
+    m_receiver = message::Communicator::eEmpty;
+//	m_message_buf = "";
+}
+
+Rabbitmq_message::Rabbitmq_message(const message::Base_info &base_info, std::string receive_string, int channel,
+                                   int delivery_tag, std::string exchange_name, std::string routing_key) {
+    m_message_type = (message::Message_type) (base_info.msg_type());
+
+    m_receive_time = std::chrono::system_clock::now();
+    m_timeout_ms = std::chrono::milliseconds(base_info.timeout_ms());
+    m_sender = (message::Communicator) (base_info.sender());
+    m_receiver = (message::Communicator) (base_info.receiver());
+    m_message_buf = receive_string;
+
+    m_channel = channel;
+    m_delivery_tag = delivery_tag;
+    m_exchange_name = exchange_name;
+    m_routing_key = routing_key;
+}
+
+Rabbitmq_message::Rabbitmq_message(std::string receive_string, int channel, std::string exchange_name,
+                                   std::string routing_key, int timeout_ms = 0) {
+    m_timeout_ms = std::chrono::milliseconds(timeout_ms);
+    m_message_buf = receive_string;
+    m_channel = channel;
+    m_exchange_name = exchange_name;
+    m_routing_key = routing_key;
+}
+
+
+Rabbitmq_message::~Rabbitmq_message() {
+
+}
+
+bool Rabbitmq_message::is_over_time() {
+    if (std::chrono::system_clock::now() - m_receive_time > m_timeout_ms) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+
+void
+Rabbitmq_message::reset(const message::Base_info &base_info, std::string receive_string, int channel, int delivery_tag,
+                        std::string exchange_name, std::string routing_key) {
+    m_message_type = (message::Message_type) (base_info.msg_type());
+
+    m_receive_time = std::chrono::system_clock::now();
+    m_timeout_ms = std::chrono::milliseconds(base_info.timeout_ms());
+    m_sender = (message::Communicator) (base_info.sender());
+    m_receiver = (message::Communicator) (base_info.receiver());
+    m_message_buf = receive_string;
+    m_channel = channel;
+    m_delivery_tag = delivery_tag;
+    m_exchange_name = exchange_name;
+    m_routing_key = routing_key;
+}
+
+void
+Rabbitmq_message::reset(std::string receive_string, int channel, std::string exchange_name, std::string routing_key,
+                        int timeout_ms = 0) {
+    m_timeout_ms = std::chrono::milliseconds(timeout_ms);
+    m_message_buf = receive_string;
+    m_channel = channel;
+    m_exchange_name = exchange_name;
+    m_routing_key = routing_key;
+}
+
+message::Message_type Rabbitmq_message::get_message_type() {
+    return m_message_type;
+}
+
+message::Communicator Rabbitmq_message::get_sender() {
+    return m_sender;
+}
+
+message::Communicator Rabbitmq_message::get_receiver() {
+    return m_receiver;
+}
+
+std::string Rabbitmq_message::get_message_buf() {
+    return m_message_buf;
+}
+
+std::chrono::system_clock::time_point Rabbitmq_message::get_receive_time() {
+    return m_receive_time;
+}
+
+
+

+ 76 - 0
rabbitmq/rabbitmq_message.h

@@ -0,0 +1,76 @@
+//
+// Created by huli on 2020/6/29.
+//
+
+#ifndef __RABBITMQ_MESSAGE_H
+#define __RABBITMQ_MESSAGE_H
+
+#include "tool/error_code.hpp"
+
+#include <time.h>
+#include <sys/time.h>
+#include <chrono>
+//#include <iosfwd>
+
+#include <string>
+#include "message/message_base.pb.h"
+
+class Rabbitmq_message {
+public:
+    Rabbitmq_message();
+
+    Rabbitmq_message(const message::Base_info &base_info, std::string receive_string, int channel, int delivery_tag,
+                     std::string exchange_name, std::string routing_key);
+
+    Rabbitmq_message(std::string receive_string, int channel, std::string exchange_name, std::string routing_key,
+                     int timeout_ms);
+
+    Rabbitmq_message(const Rabbitmq_message &other) = default;
+
+    Rabbitmq_message &operator=(const Rabbitmq_message &other) = default;
+
+    ~Rabbitmq_message();
+
+public://API functions
+    bool is_over_time();
+
+public://get or set member variable
+    void reset(const message::Base_info &base_info, std::string receive_string, int channel, int delivery_tag,
+               std::string exchange_name, std::string routing_key);
+
+    void
+    reset(std::string receive_string, int channel, std::string exchange_name, std::string routing_key, int timeout_ms);
+
+    message::Message_type get_message_type();
+
+    message::Communicator get_sender();
+
+    message::Communicator get_receiver();
+
+    std::string get_message_buf();
+
+    std::chrono::system_clock::time_point get_receive_time();
+
+public://member variable
+    message::Message_type m_message_type;                //消息类型
+    std::chrono::system_clock::time_point m_receive_time;                //接收消息的时间点
+    std::chrono::milliseconds m_timeout_ms;                //超时时间, 整个软件都统一为毫秒
+    message::Communicator m_sender;                    //发送者
+    message::Communicator m_receiver;                    //接受者
+
+    std::string m_message_buf;                //消息数据
+
+    //rabbitmq 接受特有, 保存channel和delivery_tag, 用来ack
+    //rabbitmq 通用, 填写m_channel m_exchange_name m_routing_key 用来区别消息
+    int m_channel;                    //接受消息来源的通道
+    int m_delivery_tag;                //接受消息的传递编号
+    std::string m_exchange_name;            //交换机名称
+    std::string m_routing_key;                //key,识别码
+
+
+private:
+
+};
+
+
+#endif //__RABBITMQ_MESSAGE_H

+ 79 - 0
rabbitmq/胡力的rabbitmq-c说明文档.md

@@ -0,0 +1,79 @@
+
+
+安装 rabbitMQ-c 的注意事项:(这里只介绍C语言版本的客户端)
+
+1.代码下载
+	rabbitMQ官网 		https://www.rabbitmq.com/
+	各种语言支持			https://www.rabbitmq.com/devtools.html
+		里面有C/C++的支持
+	github上源码			https://github.com/alanxz/rabbitmq-c
+
+2.安装方式
+	终端指令:注意了,要用sudo使用管理员权限
+	git clone https://github.com/alanxz/rabbitmq-c  
+	cd rabbitmq-c
+	mkdir build
+	cd build
+	sudo cmake ..
+	sudo cmake --build .
+	sudo make 
+	sudo make install
+
+	详情参考		https://blog.csdn.net/caicaiatnbu/article/details/98099779?ops_request_misc=&request_id=&biz_id=102&utm_term=RabbitMQ-C&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-3-98099779.nonecase&spm=1018.2226.3001.4187
+
+	如果提示openssl有报错,卸载并重装openssl
+	详情参考		https://blog.csdn.net/Cai181191/article/details/120648055?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%8D%B8%E8%BD%BDopenssl&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-120648055.142^v41^pc_rank_34,185^v2^control&spm=1018.2226.3001.4187
+
+3.examples代码编译
+	方案一:不建议
+	打开		./rabbitmq-c/CMakeLists.txt
+		找到		option(BUILD_EXAMPLES "Build Examples" OFF)
+		修改为	option(BUILD_EXAMPLES "Build Examples" ON)
+	然后重新编译 ./rabbitmq-c/CMakeLists.txt  就是把上面第二步再做一遍
+		cd ./rabbitmq-c/build
+		sudo cmake ..
+		sudo cmake --build .
+		sudo make 
+	然后就可以看到	./rabbitmq-c/build/examples 路径下面出现了样例的执行文件
+
+	方案二:强烈推荐
+	进入		./rabbitmq-c/examples 文件夹	,后面的操作都在这个文件夹里面
+	打开		./rabbitmq-c/examples/CMakeLists.txt
+	全局替换		rabbitmq::rabbitmq   改为	rabbitmq
+	就是删除 rabbitmq::
+	然后 编译 ./rabbitmq-c/examples/CMakeLists.txt
+		cd ./rabbitmq-c/examples/
+		mkdir build
+		cd build
+		cmake ..
+		make 
+	然后就可以看到	./rabbitmq-c/examples/build 路径下面出现了样例的执行文件
+
+4.自己开发rabbitMQ-c代码
+	在自己的  CMakeLists.txt  里面 增加系统头文件和库文件的目录.
+	例如:
+		include_directories(
+        /usr/local/include
+		)
+		link_directories("/usr/local/lib")
+
+	在自己的  CMakeLists.txt  里面 target_link_libraries 追加 rabbitmq
+	例如:	
+		target_link_libraries(xxx工程名		rabbitmq )
+
+	具体的函数使用,参考examples里面的amqp_listen.c和amqp_sendstring.c
+
+5.服务器网站配置
+	登录服务器   http://127.0.0.1:15672/     或者     http://192.168.2.39:15672/
+		默认账号密码   guest   guest    (注, guest只能本机访问,其他电脑需要新建账户)
+	
+	终端指令: 使用sudo
+		创建用户 rabbitmqctl add_user admin admin
+		设置管理员 rabbitmqctl set_user_tags admin administrator
+		设置权限  rabbitmqctl set_permissions -p/admin ".*"".*"".*"
+		查看用户  rabbitmqctl list_users
+		详见  https://blog.csdn.net/z446981439/article/details/103634524?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166141324016782388032414%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=166141324016782388032414&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-103634524-null-null.142^v42^pc_rank_34,185^v2^control&utm_term=rabbitmq%20%E7%94%A8%E6%88%B7&spm=1018.2226.3001.4187
+
+		个人建议:只在终端上创建账户,然后使用guest在网站上面配置权限.
+	
+	

+ 31 - 0
thread/CMakeLists.txt

@@ -0,0 +1,31 @@
+set(LIBRARY_NAME zxthread)
+
+unset(OPTION_ENABLE_TEST_CODE CACHE)
+option(OPTION_ENABLE_TEST_CODE "Whether enable test code." OFF)
+message("<=${LIBRARY_NAME}=> OPTION_ENABLE_TEST_CODE: " ${OPTION_ENABLE_TEST_CODE})
+
+set(LIBRARY_SOURCE_LIST
+        ${CMAKE_CURRENT_LIST_DIR}/thread_condition.h
+        ${CMAKE_CURRENT_LIST_DIR}/thread_condition.cpp
+        ${CMAKE_CURRENT_LIST_DIR}/time_tool.h
+        ${CMAKE_CURRENT_LIST_DIR}/time_tool.cpp
+        ${CMAKE_CURRENT_LIST_DIR}/binary_buf.h
+        ${CMAKE_CURRENT_LIST_DIR}/binary_buf.cpp
+)
+
+set(LIBRARY_DEPEND_LIST
+        -lpthread
+)
+
+add_library(${LIBRARY_NAME} ${LIBRARY_SOURCE_LIST})
+target_link_libraries(${LIBRARY_NAME} PUBLIC ${LIBRARY_DEPEND_LIST})
+
+if (OPTION_ENABLE_TEST_CODE)
+    set(LIBRARY_TEST_NAME "${LIBRARY_NAME}_test")
+    set(LIBRARY_TEST_SOURCE_LIST
+            ${CMAKE_CURRENT_LIST_DIR}/test.h
+            ${CMAKE_CURRENT_LIST_DIR}/test.cpp
+    )
+    add_executable(${LIBRARY_TEST_NAME} ${LIBRARY_TEST_SOURCE_LIST})
+    target_link_libraries(${LIBRARY_TEST_NAME} ${LIBRARY_NAME} )
+endif ()

+ 264 - 0
thread/binary_buf.cpp

@@ -0,0 +1,264 @@
+
+/*
+ * binary_buf是二进制缓存
+ * 这里用字符串,来存储雷达的通信消息的原始数据
+ * Binary_buf 的内容格式:消息类型 + 消息数据
+ *
+ * 例如思科的雷达的消息类型
+ * ready->ready->start->data->data->data->stop->ready->ready
+ *
+ * 提供了 is_equal 系列的函数,来进行判断前面的消息类型
+ * 
+ * 注意了:m_buf是中间可以允许有‘\0’的,不是单纯的字符串格式
+ * 			末尾也不一定是‘\0’
+ */
+
+#include "binary_buf.h"
+
+#include <string>
+#include <string.h>
+
+Binary_buf::Binary_buf() {
+    mp_buf = NULL;
+    m_length = 0;
+}
+
+Binary_buf::Binary_buf(const Binary_buf &other) {
+    mp_buf = NULL;
+    m_length = 0;
+
+    if (other.m_length > 0 && other.mp_buf != NULL) {
+        mp_buf = (char *) malloc(other.m_length);
+        memcpy(mp_buf, other.mp_buf, other.m_length);
+        m_length = other.m_length;
+    }
+}
+
+Binary_buf::~Binary_buf() {
+    if (mp_buf) {
+        free(mp_buf);
+        mp_buf = NULL;
+    }
+    m_length = 0;
+
+//	std::cout << "Binary_buf::~Binary_buf()" << std::endl;
+}
+
+
+//使用参数构造,深拷贝,len为0时,使用strlen(buf),不存储结束符'\0'
+Binary_buf::Binary_buf(const char *p_buf, int len) {
+    mp_buf = NULL;
+    m_length = 0;
+
+    if (p_buf != NULL) {
+        if (len <= 0) {
+            len = strlen(p_buf);
+        }
+
+        mp_buf = (char *) malloc(len);
+        memcpy(mp_buf, p_buf, len);
+        m_length = len;
+    }
+}
+
+
+//使用参数构造,深拷贝,len为0时,使用strlen(buf),不存储结束符'\0'
+Binary_buf::Binary_buf(char *p_buf, int len) {
+    mp_buf = NULL;
+    m_length = 0;
+
+    if (p_buf != NULL) {
+        if (len <= 0) {
+            len = strlen(p_buf);
+        }
+
+        mp_buf = (char *) malloc(len);
+        memcpy(mp_buf, p_buf, len);
+        m_length = len;
+    }
+}
+
+//重载=,深拷贝,
+Binary_buf &Binary_buf::operator=(const Binary_buf &other) {
+    clear();
+
+    if (other.m_length > 0 && other.mp_buf != NULL) {
+        mp_buf = (char *) malloc(other.m_length);
+        memcpy(mp_buf, other.mp_buf, other.m_length);
+        m_length = other.m_length;
+    }
+    return *this;
+}
+
+//重载=,深拷贝,使用strlen(buf),不存储结束符'\0'
+Binary_buf &Binary_buf::operator=(const char *p_buf) {
+    clear();
+
+    if (p_buf != NULL) {
+        int len = strlen(p_buf);
+        mp_buf = (char *) malloc(len);
+        memcpy(mp_buf, p_buf, len);
+        m_length = len;
+    }
+    return *this;
+}
+
+//重载+,other追加在this的后面,
+Binary_buf &Binary_buf::operator+(Binary_buf &other) {
+    if (other.mp_buf != NULL && other.m_length > 0) {
+        int t_length_total = m_length + other.m_length;
+        char *tp_buf_total = (char *) malloc(t_length_total);
+        memcpy(tp_buf_total, mp_buf, m_length);
+        memcpy(tp_buf_total + m_length, other.mp_buf, other.m_length);
+        free(mp_buf);
+        mp_buf = tp_buf_total;
+        m_length = t_length_total;
+    }
+    return *this;
+}
+
+//重载+,追加在this的后面,使用strlen(buf),不存储结束符'\0'
+Binary_buf &Binary_buf::operator+(const char *p_buf) {
+    if (p_buf != NULL) {
+        int t_length_back = strlen(p_buf);
+        int t_length_total = m_length + t_length_back;
+        char *tp_buf_total = (char *) malloc(t_length_total);
+        memcpy(tp_buf_total, mp_buf, m_length);
+        memcpy(tp_buf_total + m_length, p_buf, t_length_back);
+        free(mp_buf);
+        mp_buf = tp_buf_total;
+        m_length = t_length_total;
+    }
+    return *this;
+}
+
+//重载[],允许直接使用数组的形式,直接访问buf的内存。注意,n值必须在0~m_length之间,
+char &Binary_buf::operator[](int n) {
+    if (n >= 0 && n < m_length) {
+        return mp_buf[n];
+    } else {
+        throw (n);
+    }
+}
+
+
+//判空
+bool Binary_buf::is_empty() {
+    if (mp_buf != NULL && m_length > 0) {
+        return false;
+    } else {
+        return true;
+    }
+}
+
+//清空
+void Binary_buf::clear() {
+    if (mp_buf) {
+        free(mp_buf);
+        mp_buf = NULL;
+    }
+    m_length = 0;
+}
+
+
+//比较前面部分的buf是否相等,使用 other.m_length 为标准
+bool Binary_buf::is_equal_front(const Binary_buf &other) {
+    if (other.mp_buf == NULL || other.m_length <= 0) {
+        if (mp_buf == NULL || m_length <= 0) {
+            return true;
+        } else {
+            return false;
+        }
+    } else {
+        if (mp_buf != NULL && m_length > 0) {
+            if (other.m_length > m_length) {
+                return false;
+            }
+            return (strncmp((const char *) mp_buf, other.mp_buf, other.m_length) == 0);
+        } else {
+            return false;
+        }
+
+    }
+}
+
+//比较前面部分的buf是否相等,len为0时,使用strlen(buf)为标准,不比较结束符'\0'
+bool Binary_buf::is_equal_front(const char *p_buf, int len) {
+    if (p_buf == NULL) {
+        if (mp_buf == NULL || m_length <= 0) {
+            return true;
+        } else {
+            return false;
+        }
+    } else {
+        if (mp_buf != NULL && m_length > 0) {
+            if (len == 0) {
+                len = strlen(p_buf);
+            }
+            if (len > m_length) {
+                return false;
+            }
+            return (strncmp((const char *) mp_buf, p_buf, len) == 0);
+        } else {
+            return false;
+        }
+
+    }
+}
+
+//比较的buf是否全部相等,
+bool Binary_buf::is_equal_all(const Binary_buf &other) {
+    if (other.mp_buf == NULL || other.m_length <= 0) {
+        if (mp_buf == NULL || m_length <= 0) {
+            return true;
+        } else {
+            return false;
+        }
+    } else {
+        if (mp_buf != NULL && m_length > 0) {
+            if (other.m_length != m_length) {
+                return false;
+            }
+            return (strncmp((const char *) mp_buf, other.mp_buf, other.m_length) == 0);
+        } else {
+            return false;
+        }
+
+    }
+}
+
+//比较的buf是否全部相等,不比较结束符'\0'
+bool Binary_buf::is_equal_all(const char *p_buf) {
+    if (p_buf == NULL) {
+        if (mp_buf == NULL || m_length <= 0) {
+            return true;
+        } else {
+            return false;
+        }
+    } else {
+        if (mp_buf != NULL && m_length > 0) {
+            int len = strlen(p_buf);
+            if (len != m_length) {
+                return false;
+            }
+            return (strncmp((const char *) mp_buf, p_buf, len) == 0);
+        } else {
+            return false;
+        }
+
+    }
+}
+
+
+char *Binary_buf::get_buf() const {
+    return mp_buf;
+}
+
+int Binary_buf::get_length() const {
+    return m_length;
+}
+
+
+
+
+

+ 99 - 0
thread/binary_buf.h

@@ -0,0 +1,99 @@
+
+/*
+ * binary_buf是二进制缓存
+ * 这里用字符串,来存储雷达的通信消息的原始数据
+ * Binary_buf 的内容格式:消息类型 + 消息数据
+ *
+ * 例如思科的雷达的消息类型
+ * ready->ready->start->data->data->data->stop->ready->ready
+ *
+ * 提供了 is_equal 系列的函数,来进行判断前面的消息类型
+ *
+ * 注意了:m_buf是中间可以允许有‘\0’的,不是单纯的字符串格式
+ * 			末尾也不一定是‘\0’
+ */
+
+#ifndef LIDARMEASURE_BINARY_BUF_H
+#define LIDARMEASURE_BINARY_BUF_H
+
+#include <iostream>
+
+
+//雷达消息的类型
+//在通信消息的前面一部分字符串,表示这条消息的类型。
+//在解析消息的时候,先解析前面的消息类型,来判断这条消息的功用
+enum Buf_type {
+    //默认值 BUF_UNKNOW = 0
+    BUF_UNKNOW = 0,    //未知消息
+    BUF_READY = 1,    //待机消息
+    BUF_START = 2,    //开始消息
+    BUF_DATA = 3,    //数据消息
+    BUF_STOP = 4,    //结束消息
+    BUF_ERROR = 5,    //错误消息
+};
+
+
+//二进制缓存,
+class Binary_buf {
+public:
+    Binary_buf();
+
+    Binary_buf(const Binary_buf &other);
+
+    ~Binary_buf();
+
+    //使用参数构造,深拷贝,len为0时,使用strlen(buf),不存储结束符'\0'
+    Binary_buf(const char *p_buf, int len = 0);
+
+    //使用参数构造,深拷贝,len为0时,使用strlen(buf),不存储结束符'\0'
+    Binary_buf(char *p_buf, int len = 0);
+
+    //重载=,深拷贝,
+    Binary_buf &operator=(const Binary_buf &other);
+
+    //重载=,深拷贝,使用strlen(buf),不存储结束符'\0'
+    Binary_buf &operator=(const char *p_buf);
+
+    //重载+,other追加在this的后面,
+    Binary_buf &operator+(Binary_buf &other);
+
+    //重载+,追加在this的后面,使用strlen(buf),不存储结束符'\0'
+    Binary_buf &operator+(const char *p_buf);
+
+    //重载[],允许直接使用数组的形式,直接访问buf的内存。注意,n值必须在0~m_length之间,
+    char &operator[](int n);
+
+    //判空
+    bool is_empty();
+
+    //清空
+    void clear();
+
+    //比较前面部分的buf是否相等,使用 other.m_length 为标准
+    bool is_equal_front(const Binary_buf &other);
+
+    //比较前面部分的buf是否相等,len为0时,使用strlen(buf)为标准,不比较结束符'\0'
+    bool is_equal_front(const char *p_buf, int len = 0);
+
+    //比较的buf是否全部相等,
+    bool is_equal_all(const Binary_buf &other);
+
+    //比较的buf是否全部相等,不比较结束符'\0'
+    bool is_equal_all(const char *p_buf);
+
+
+public:
+    char *get_buf() const;
+
+    int get_length() const;
+
+protected:
+    char *mp_buf;                //二进制缓存指针
+    int m_length;            //二进制缓存长度
+
+private:
+
+};
+
+
+#endif //LIDARMEASURE_BINARY_BUF_H

+ 165 - 0
thread/thread_condition.cpp

@@ -0,0 +1,165 @@
+
+
+/* Thread_condition 是多线程的条件控制类,主要是控制线程的启停和退出
+ * 线程创建后,一般是循环运行,
+ * 为了防止线程暂满整个cpu,那么需要线程在不工作的是否进入等待状态。
+ * Thread_condition 就可以控制线程的运行状态。
+ *
+	std::atomic<bool> m_pass_ever		//线程能否直接通过等待,对后面的线程也生效。
+	std::atomic<bool> m_pass_once		//线程能否直接通过等待,一次(通过一次之后,wait里面自动改为false)
+ * 外部调用notify系列的函数,唤醒等待的线程,让线程执行功能函数。
+ * 如果需要线程循环多次执行功能函数,那么就使用 notify_all(true),后面的线程可以直接通过等待了。
+ * 再使用 notify_all(false) ,即可停止线程,让其继续等待。
+ * 如果只想要线程执行一次,那就使用 notify_all(false, true)
+ * 注:notify_all(false, true)和notify_one(false, true) 一样,只能让其中一个线程执行一次
+ *
+ * m_kill_flag //是否杀死线程,让线程强制退出,
+ * 外部调用 kill_all() 函数,可以直接通知线程自动退出。
+	//杀死所有的线程,强制退出线程函数,只是操作受当前Thread_condition影响的所有线程
+	//唤醒所有线程,使其通过等待,但是不能运行功能函数,必须直接return
+	// 注:只是修改m_kill为true,需要线程函数实时检测kill的状态,来return线程。
+	//		通过等待之后,也要检查kill的状态,如果为真,那么就不能执行功能函数,应该直接return
+
+ 注:notify唤醒线程之后,wait里面的判断函数会重新判断。
+ */
+
+#include "thread_condition.h"
+
+Thread_condition::Thread_condition() {
+    m_kill_flag = false;
+    m_pass_ever = false;
+    m_pass_once = false;
+    m_working_flag = false;
+}
+
+Thread_condition::~Thread_condition() {
+    kill_all();
+}
+
+//无限等待,由 is_pass_wait 决定是否阻塞。
+//返回m_pass,
+bool Thread_condition::wait() {
+    std::unique_lock<std::mutex> loc(m_mutex);
+    m_condition_variable.wait(loc, std::bind(is_pass_wait, this));
+    bool t_pass = is_pass_wait(this);
+    m_pass_once = false;
+
+    //只要前面通过了, 那就进入工作状态
+    m_working_flag = true;
+
+    return t_pass;
+}
+
+//等待一定的时间(默认时间单位:毫秒ms),由 is_pass_wait 决定是否阻塞。
+//return:is_pass_wait的结果,	true:线程直接通过等待,false:线程超时了,然后通过等待。
+//注意了:线程阻塞期间,是不会return的。
+bool Thread_condition::wait_for_millisecond(unsigned int millisecond) {
+    std::unique_lock<std::mutex> loc(m_mutex);
+    m_condition_variable.wait_for(loc, std::chrono::milliseconds(millisecond), std::bind(is_pass_wait, this));
+    bool t_pass = is_pass_wait(this);
+    m_pass_once = false;
+
+    //只要前面通过了, 那就进入工作状态 , 超时通过也算通过
+    m_working_flag = true;
+
+    return t_pass;
+}
+
+
+//唤醒已经阻塞的线程,唤醒一个线程
+//pass_ever 或者 pass_once 为真时,才能唤醒线程。都为假时,线程进入等待。
+void Thread_condition::notify_one(bool pass_ever, bool pass_once) {
+    std::unique_lock<std::mutex> loc(m_mutex);
+    m_pass_ever = pass_ever;
+    m_pass_once = pass_once;
+    m_condition_variable.notify_one();
+}
+
+//唤醒已经阻塞的线程,唤醒全部线程
+//pass_ever 或者 pass_once 为真时,才能唤醒线程。都为假时,线程进入等待。
+void Thread_condition::notify_all(bool pass_ever, bool pass_once) {
+    std::unique_lock<std::mutex> loc(m_mutex);
+    m_pass_ever = pass_ever;
+    m_pass_once = pass_once;
+    m_condition_variable.notify_all();
+}
+//注:notify_all(false, true)和notify_one(false, true) 一样,只能让其中一个线程执行一次
+
+
+//杀死所有的线程,强制退出线程函数,只是操作受当前Thread_condition影响的所有线程
+//唤醒所有线程,使其通过等待,但是不能运行功能函数,必须直接return
+// 注:只是修改m_kill为true,需要线程函数实时检测kill的状态,来return线程。
+//		通过等待之后,也要检查kill的状态,如果为真,那么就不能执行功能函数,应该直接return
+void Thread_condition::kill_all() {
+    std::unique_lock<std::mutex> loc(m_mutex);
+    m_kill_flag = true;
+    m_condition_variable.notify_all();
+}
+
+//判断是否存活,只有活着才能继续支持子线程从功能函数,否则需要强制退出函数并结束子线程
+bool Thread_condition::is_alive() {
+    return !m_kill_flag;
+}
+
+
+//判断是否等待, 外部线程通过这个函数来查询this线程的工作状态,
+bool Thread_condition::is_waiting() {
+    return !m_working_flag;
+}
+
+//判断是否工作, 外部线程通过这个函数来查询this线程的工作状态,
+bool Thread_condition::is_working() {
+    return m_working_flag;
+}
+
+
+bool Thread_condition::get_kill_flag() {
+    return m_kill_flag;
+}
+
+bool Thread_condition::get_pass_ever() {
+    return m_pass_ever;
+}
+
+bool Thread_condition::get_pass_once() {
+    return m_pass_once;
+}
+
+void Thread_condition::set_kill_flag(bool kill) {
+    m_kill_flag = kill;
+}
+
+void Thread_condition::set_pass_ever(bool pass_ever) {
+    m_pass_ever = pass_ever;
+}
+
+void Thread_condition::set_pass_once(bool pass_once) {
+    m_pass_once = pass_once;
+}
+
+void Thread_condition::reset(bool kill, bool pass_ever, bool pass_once) {
+    m_kill_flag = kill;
+    m_pass_ever = pass_ever;
+    m_pass_once = pass_once;
+}
+
+
+//判断线程是否可以通过等待,wait系列函数的判断标志
+//注:m_kill或者m_pass为真时,return true
+bool Thread_condition::is_pass_wait(Thread_condition *other) {
+    if (other == NULL) {
+        throw (other);
+        return false;
+    }
+
+    bool result = (other->m_kill_flag || other->m_pass_ever || other->m_pass_once);
+
+    //如果不能通过等待, 那么线程状态改为等待中,
+    if (!result) {
+        other->m_working_flag = false;
+    }
+
+
+    return result;
+}
+

+ 193 - 0
thread/thread_condition.h

@@ -0,0 +1,193 @@
+
+
+/* Thread_condition 是多线程的条件控制类,主要是控制线程的启停和退出
+ * 线程创建后,一般是循环运行,
+ * 为了防止线程暂满整个cpu,那么需要线程在不工作的是否进入等待状态。
+ * Thread_condition 就可以控制线程的运行状态。
+ *
+	std::atomic<bool> m_pass_ever		//线程能否直接通过等待,对后面的线程也生效。
+	std::atomic<bool> m_pass_once		//线程能否直接通过等待,一次(通过一次之后,wait里面自动改为false)
+ * 外部调用notify系列的函数,唤醒等待的线程,让线程执行功能函数。
+ * 如果需要线程循环多次执行功能函数,那么就使用 notify_all(true),后面的线程可以直接通过等待了。
+ * 再使用 notify_all(false) ,即可停止线程,让其继续等待。
+ * 如果只想要线程执行一次,那就使用 notify_all(false, true)
+ * 注:notify_all(false, true)和notify_one(false, true) 一样,只能让其中一个线程执行一次
+ *
+ * m_kill_flag //是否杀死线程,让线程强制退出,
+ * 外部调用 kill_all() 函数,可以直接通知线程自动退出。
+	//杀死所有的线程,强制退出线程函数,只是操作受当前Thread_condition影响的所有线程
+	//唤醒所有线程,使其通过等待,但是不能运行功能函数,必须直接return
+	// 注:只是修改m_kill为true,需要线程函数实时检测kill的状态,来return线程。
+	//		通过等待之后,也要检查kill的状态,如果为真,那么就不能执行功能函数,应该直接return
+
+ 注:notify唤醒线程之后,wait里面的判断函数会重新判断。
+
+
+ 最下面有使用样例,
+
+ */
+
+#ifndef LIDARMEASURE_THREAD_CONDITION_H
+#define LIDARMEASURE_THREAD_CONDITION_H
+
+#include <ratio>
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <mutex>
+#include <condition_variable>
+#include <functional>
+
+class Thread_condition {
+public:
+    Thread_condition();
+
+    Thread_condition(const Thread_condition &other) = delete;
+
+    ~Thread_condition();
+
+    //无限等待,由 is_pass_wait 决定是否阻塞。
+    //返回m_pass,
+    bool wait();
+
+    //等待一定的时间(默认时间单位:毫秒ms),由 is_pass_wait 决定是否阻塞。
+    //return:is_pass_wait的结果,	true:线程直接通过等待,false:线程超时了,然后通过等待。
+    //注意了:线程阻塞期间,是不会return的。
+    bool wait_for_millisecond(unsigned int millisecond);
+
+    //等待一定的时间(时间单位可调),由 is_pass_wait 决定是否阻塞。
+    //return:is_pass_wait的结果,	true:线程直接通过等待,false:线程超时了,然后通过等待。
+    //注意了:线程阻塞期间,是不会return的。
+    template<typename _Rep, typename _Period>
+    bool wait_for_ex(const std::chrono::duration<_Rep, _Period> &time_duration);
+
+    //唤醒已经阻塞的线程,唤醒一个线程
+    //pass_ever 或者 pass_once 为真时,才能唤醒线程。都为假时,线程进入等待。
+    void notify_one(bool pass_ever, bool pass_once = false);
+
+    //唤醒已经阻塞的线程,唤醒全部线程
+    //pass_ever 或者 pass_once 为真时,才能唤醒线程。都为假时,线程进入等待。
+    void notify_all(bool pass_ever, bool pass_once = false);
+    //注:notify_all(false, true)和notify_one(false, true) 一样,只能让其中一个线程执行一次
+
+    //杀死所有的线程,强制退出线程函数,只是操作受当前Thread_condition影响的所有线程
+    //唤醒所有线程,使其通过等待,但是不能运行功能函数,必须直接return
+    // 注:只是修改m_kill为true,需要线程函数实时检测kill的状态,来return线程。
+    //		通过等待之后,也要检查kill的状态,如果为真,那么就不能执行功能函数,应该直接return
+    void kill_all();
+
+    //判断是否存活,只有活着才能继续支持子线程从功能函数,否则需要强制退出函数并结束子线程
+    bool is_alive();
+
+    //判断是否等待, 外部线程通过这个函数来查询this线程的工作状态,
+    bool is_waiting();
+
+    //判断是否工作, 外部线程通过这个函数来查询this线程的工作状态,
+    bool is_working();
+
+public:
+
+    bool get_kill_flag();
+
+    bool get_pass_ever();
+
+    bool get_pass_once();
+
+    void set_kill_flag(bool kill);
+
+    void set_pass_ever(bool pass_ever);
+
+    void set_pass_once(bool pass_once);
+
+    void reset(bool kill = false, bool pass_ever = false, bool pass_once = false);
+
+protected:
+    //判断线程是否可以通过等待,wait系列函数的判断标志
+    //注:m_kill或者m_pass为真时,return true
+    static bool is_pass_wait(Thread_condition *other);
+
+    std::atomic<bool> m_kill_flag;            //是否杀死线程,让线程强制退出,
+    std::atomic<bool> m_pass_ever;            //线程能否直接通过等待,对后面的线程也生效。
+    std::atomic<bool> m_pass_once;            //线程能否直接通过等待,一次(通过一次之后,wait里面自动改为false)
+
+    std::atomic<bool> m_working_flag;            //线程是否进入工作的标志位, false:表示线程进行进入wait等待, true:表示线程仍然在运行中,
+
+    std::mutex m_mutex;                //线程的锁
+    std::condition_variable m_condition_variable;    //线程的条件变量
+
+private:
+
+};
+
+//等待一定的时间(时间单位可调),由 is_pass_wait 决定是否阻塞。
+//return:is_pass_wait的结果,	true:线程直接通过等待,false:线程超时了,然后通过等待。
+//注意了:线程阻塞期间,是不会return的。
+template<typename _Rep, typename _Period>
+bool Thread_condition::wait_for_ex(const std::chrono::duration<_Rep, _Period> &time_duration) {
+    std::unique_lock<std::mutex> loc(m_mutex);
+    m_condition_variable.wait_for(loc, std::chrono::duration<_Rep, _Period>(time_duration),
+                                  std::bind(is_pass_wait, this));
+    bool t_pass = is_pass_wait(this);
+    m_pass_once = false;
+    return t_pass;
+}
+
+#endif //LIDARMEASURE_THREAD_CONDITION_H
+
+
+
+
+
+/*
+//使用样例:
+std::thread*						mp_thread_receive;    		//接受缓存的线程指针
+Thread_condition					m_condition_receive;		//接受缓存的条件变量
+
+void thread_receive()
+{
+	while (m_condition_receive.is_alive())
+	{
+		m_condition_receive.wait();
+		if ( m_condition_receive.is_alive() )
+		{
+			//do everything
+
+		}
+	}
+
+	return;
+}
+
+//main函数的主线程
+int main(int argc,char* argv[])
+{
+ 	//线程创建之后, 默认等待
+	m_condition_receive.reset(false, false, false);
+	mp_thread_receive = new std::thread(& thread_receive );
+
+
+	//唤醒所有线程, 然后线程可以一直通过wait等待, 线程进入无限制的循环工作.
+	m_condition_receive.notify_all(true);
+
+	//暂停所有线程, 然后线程还是继续工作, 直到下一次循环, 进入wait等待
+	m_condition_receive.notify_all(false);
+
+	//如果线程单次循环运行时间较长, 需要等待线程完全停止, 才能读写公共的内存,
+	if ( m_condition_receive.is_waiting() )
+	{
+	    // 读写公共的内存,
+	}
+
+	//唤醒一个线程, 然后线程循环一次, 然后下次循环进入等待
+	m_condition_receive.notify_all(false, true);
+
+
+	//杀死线程,
+	m_condition_receive.kill_all();
+
+	//在线程join结束之后, 就可以可以回收线程内存资源
+	mp_thread_receive->join();
+	delete mp_thread_receive;
+	mp_thread_receive = NULL;
+}
+*/

+ 133 - 0
thread/time_tool.cpp

@@ -0,0 +1,133 @@
+//
+// Created by wk on 2020/9/25.
+//
+
+#include "time_tool.h"
+Time_tool::Time_tool()
+{
+}
+Time_tool::~Time_tool()
+{
+	Time_tool_uninit();
+}
+void Time_tool::set_points_digits(int num)
+{
+	std::cout.precision(num);
+	std::cout.setf(std::ios::fixed);
+}
+std::chrono::system_clock::time_point Time_tool::get_system_point()
+{
+	return std::chrono::system_clock::now();
+}
+tm Time_tool::get_current_time_struct()
+{
+	auto now = std::chrono::system_clock::now();
+	time_t tt = std::chrono::system_clock::to_time_t(now);
+	tm time_tm=*localtime(&tt);
+	return time_tm;
+}
+std::string Time_tool::get_current_time_seconds()
+{
+	auto time_tm = get_current_time_struct();
+	char strTime[100] = "";
+	sprintf(strTime, "%d-%02d-%02d %02d:%02d:%02d", time_tm.tm_year + 1900,
+			time_tm.tm_mon + 1, time_tm.tm_mday, time_tm.tm_hour,
+			time_tm.tm_min, time_tm.tm_sec);
+	std::string str=strTime;
+	return str;
+}
+std::string Time_tool::get_current_time_millisecond()
+{
+
+	auto now = std::chrono::system_clock::now();
+	//通过不同精度获取相差的毫秒数
+	uint64_t dis_millseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count()
+							   - std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count() * 1000;
+	std::string strTime=get_current_time_seconds()+" "+std::to_string((int)dis_millseconds);
+	return strTime;
+}
+std::string Time_tool::get_current_time_microsecond()
+{
+
+	auto now = std::chrono::system_clock::now();
+	//通过不同精度获取相差的微秒
+	uint64_t dis_microseconds = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count()
+							   - std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count() * 1000;
+	std::string strTime=get_current_time_millisecond()+" "+std::to_string((int)dis_microseconds);
+	return strTime;
+}
+void Time_tool::time_start(int key)
+{
+	timetool_map[key].t_time_start=get_system_point();//保存开始的时间 //单位为微秒
+}
+
+void Time_tool::time_end(int key)
+{
+	if ( timetool_map.find(key)!=timetool_map.end() )
+	{
+		timetool_map[key].t_time_end = get_system_point();//保存结束的时间
+		timetool_map[key].t_time_difference = timetool_map[key].t_time_end - timetool_map[key].t_time_start;//保存时差
+	}
+	else
+	{
+	    std::cout << "计时器:" << key<<"还未开始"<<std::endl;
+	}
+
+}
+void Time_tool::cout_time_seconds(int key)
+{
+
+	if ( timetool_map.find(key)!=timetool_map.end() )
+	{
+		double dieoutTime=(double)timetool_map[key].t_time_difference.count()/1000000000;
+		std::cout << "计时器:"<<key<<" 计时的时间为:" <<dieoutTime<<" 秒" << std::endl;
+	}
+	else
+	{
+		std::cout<<"没有此计时器:"<<key<<std::endl;
+	}
+}
+void Time_tool::cout_time_millisecond(int key)
+{
+	if ( timetool_map.find(key)!=timetool_map.end() )
+	{
+		double dieoutTime=(double)timetool_map[key].t_time_difference.count()/1000000;
+		std::cout << "计时器:"<<key<<" 计时的时间为:" <<dieoutTime<<" 毫秒" << std::endl;
+	}
+	else
+	{
+		std::cout<<"没有此计时器:"<<key<<std::endl;
+	}
+}
+void Time_tool::cout_time_microsecond(int key)
+{
+	if ( timetool_map.find(key)!=timetool_map.end() )
+	{
+		double dieoutTime=(double)timetool_map[key].t_time_difference.count()/1000;
+
+		std::cout << "计时器:"<<key<<" 计时的时间为:" <<dieoutTime<<" 微秒" << std::endl;
+	}
+	else
+	{
+		std::cout<<"没有此计时器:"<<key<<std::endl;
+	}
+}
+void Time_tool::cout_time_nanosecond(int key)
+{
+	if ( timetool_map.find(key)!=timetool_map.end() )
+	{
+		std::cout << "计时器:"<<key<<" 计时的时间为:" <<timetool_map[key].t_time_difference.count()<<" 纳秒" << std::endl;
+	}
+	else
+	{
+		std::cout<<"没有此计时器:"<<key<<std::endl;
+	}
+}
+void Time_tool::clear_timer()
+{
+	Time_tool_uninit();
+}
+void Time_tool::Time_tool_uninit()
+{
+	timetool_map.clear();
+}

+ 81 - 0
thread/time_tool.h

@@ -0,0 +1,81 @@
+//
+// Created by wk on 2020/9/25.
+//
+
+#ifndef PKGNAME_TIME_TOOL_H
+#define PKGNAME_TIME_TOOL_H
+
+#include <thread>
+#include <map>
+#include <iostream>
+#include<time.h>
+#include <queue>
+#include "tool/singleton.hpp"
+
+class Time_tool:public Singleton<Time_tool>
+{
+// 子类必须把父类设定为友元函数,这样父类才能使用子类的私有构造函数。
+   friend class Singleton<Time_tool>;
+struct time_data
+{
+	std::chrono::system_clock::time_point		t_time_start;//计时开始
+	std::chrono::system_clock::time_point 		t_time_end;//计时结束
+	std::chrono::system_clock::duration			t_time_difference;//时差
+};
+private:
+ // 父类的构造函数必须保护,子类的构造函数必须私有。
+   Time_tool();
+public:
+    //必须关闭拷贝构造和赋值构造,只能通过 get_instance 函数来进行操作唯一的实例。
+    Time_tool(const Time_tool& other) = delete;
+    Time_tool& operator =(const Time_tool& other) = delete;
+    ~Time_tool();
+public://API functions
+	void Time_tool_uninit();
+
+    //获取当前系统时间点
+	std::chrono::system_clock::time_point get_system_point();
+
+	//设置输出格式  保留小数点后几位  不设置默认为系统输出
+	void set_points_digits(int);
+
+	//获取当前时间 精确到秒
+	 std::string get_current_time_seconds();
+
+	//获取当前时间 精确到毫秒
+	std::string get_current_time_millisecond();
+
+	//获取当前时间 精确到微妙
+	std::string get_current_time_microsecond();
+
+	//获取当前时间结构体
+	tm get_current_time_struct();
+
+	//计时开始
+	void time_start(int key=1);
+
+	//指定计时器计时结束
+	void time_end(int key=1);
+
+	//打印计时时差 精确到秒
+	void cout_time_seconds(int key=1);
+
+	//打印计时时差 精确到毫秒
+	void cout_time_millisecond(int key=1);
+
+	//打印计时时差 精确到微秒
+	void cout_time_microsecond(int key=1);
+
+	//打印计时时差 精确到纳秒
+	void cout_time_nanosecond(int key=1);
+
+	//清除计时器
+	void clear_timer();
+public://get or set member variable
+protected://member variable
+
+    
+private:
+	std::map<int,time_data> 			timetool_map;
+};
+#endif //PKGNAME_TIME_TOOL_H

+ 973 - 0
tool/error_code.hpp

@@ -0,0 +1,973 @@
+//Error_code是错误码的底层通用模块,
+//功能:用作故障分析和处理。
+
+//用法:所有的功能接口函数return错误管理类,
+//然后上层判断分析错误码,并进行故障处理。
+#pragma once
+
+#include <string>
+#include <string.h>
+#include<iostream>
+#include <stdio.h>	/* vsnprintf与vsprintf */
+#include <stdarg.h>	/* va... */
+
+#define MAIN_TEST 1
+//#define PROCESS_TEST 1
+//#define TIME_TEST 1
+
+#define PLC_S7_COMMUNICATION 1    //是否开启plc的通信
+#define WAIT_PLC_RESPONSE 1        //是否等待plc的答复
+#define MEASURE_TO_PLC_CORRECTION 1        //修正感测数据
+
+//错误管理类转化为字符串 的前缀,固定长度为58
+//这个是由显示格式来确定的,如果要修改格式或者 Error_code长度超过8位,Error_level长度超过2位,折需要重新计算
+#define ERROR_NAMAGER_TO_STRING_FRONT_LENGTH   58
+#define DESCRIPTION_BUF_SIZE 1024
+//进程加锁的状态,
+enum Lock_status {
+    UNLOCK = 0,
+    LOCK = 1,
+};
+
+//设备使能状态,
+enum Able_status {
+    UNABLE = 0,
+    ENABLE = 1,
+};
+
+//数据是否为空
+enum Empty_status {
+    NON_EMPTY = 0,
+    EMPTY = 1,
+};
+
+
+//错误码的枚举,用来做故障分析
+enum Error_code {
+    //成功,没有错误,默认值0
+    SUCCESS = 0x00000000,
+
+
+    //基本错误码,
+    ERROR = 0x00000001,//错误
+    PARTIAL_SUCCESS = 0x00000002,//部分成功
+    WARNING = 0x00000003,//警告
+    FAILED = 0x00000004,//失败
+
+    NODATA = 0x00000010,//没有数据,传入参数容器内部没有数据时,
+    INVALID_MESSAGE = 0x00000011, //无效的消息,
+    PARSE_FAILED = 0x00000012,//解析失败
+
+    PAUSE = 0x00000013,   //急停
+    TASK_CANCEL = 0x00000014,   //任务取消
+
+    DISCONNECT = 0x00000020,   //通讯中断/断开连接
+    UNKNOW_STATU = 0x00000021,   //未知状态
+
+    TASK_TIMEOVER = 0x00000020,//任务超时
+    RESPONSE_TIMEOUT = 0x00000021,//答复超时
+
+    POINTER_IS_NULL = 0x00000101,//空指针
+    PARAMETER_ERROR = 0x00000102,//参数错误,传入参数不符合规范时,
+    POINTER_MALLOC_FAIL = 0x00000103,//手动分配内存失败
+
+    CLASS_BASE_FUNCTION_CANNOT_USE = 0x00000201,//基类函数不允许使用,必须使用子类的
+
+    CONTAINER_IS_TERMINATE = 0x00000301,//容器被终止
+
+
+
+
+//    错误码的规范,
+//    错误码是int型,32位,十六进制。
+//    例如0x12345678
+//    12表示功能模块,例如:laser雷达模块               	框架制定
+//    34表示文件名称,例如:laser_livox.cpp             框架制定
+//    56表示具体的类,例如:class laser_livox           个人制定
+//    78表示类的函数,例如:laser_livox::start();       个人制定
+//    注:错误码的制定从1开始,不要从0开始,
+//        0用作错误码的基数,用来位运算,来判断错误码的范围。
+
+
+
+
+
+
+
+//    laser扫描模块
+    LASER_ERROR_BASE = 0x01000000,
+
+//    laser_base基类
+    LASER_BASE_ERROR_BASE = 0x01010000,
+    LASER_TASK_PARAMETER_ERROR = 0x01010001,   //雷达基类模块, 任务输入参数错误
+    LASER_CONNECT_FAILED,                            //雷达基类模块, 连接失败
+    LASER_START_FAILED,                                //雷达基类模块, 开始扫描失败
+    LASER_CHECK_FAILED,                                //雷达基类模块, 检查失败
+    LASER_STATUS_BUSY,                                //雷达基类模块, 状态正忙
+    LASER_STATUS_ERROR,                                //雷达基类模块, 状态错误
+    LASER_TASK_OVER_TIME,                            //雷达基类模块, 任务超时
+    LASER_QUEUE_ERROR,                                //雷达基类模块, 数据缓存错误
+
+
+//   livox  大疆雷达
+    LIVOX_ERROR_BASE = 0x01020000,
+    LIVOX_START_FAILE,                                //livox模块,开始扫描失败
+    LIVOX_TASK_TYPE_ERROR,                            //livox模块,任务类型错误
+    lIVOX_CANNOT_PUSH_DATA,                            //livox模块,不能添加扫描的数据
+    lIVOX_CHECK_FAILED,                                //livox模块,检查失败
+    lIVOX_STATUS_BUSY,                                //livox模块,状态正忙
+    lIVOX_STATUS_ERROR,                                //livox模块,状态错误
+
+    //laser_manager 雷达管理模块
+    LASER_MANAGER_ERROR_BASE = 0x01030000,
+    LASER_MANAGER_READ_PROTOBUF_ERROR,                //雷达管理模块,读取参数错误
+    LASER_MANAGER_STATUS_BUSY,                        //雷达管理模块,状态正忙
+    LASER_MANAGER_STATUS_ERROR,                        //雷达管理模块,状态错误
+    LASER_MANAGER_TASK_TYPE_ERROR,                    //雷达管理模块,任务类型错误
+    LASER_MANAGER_IS_NOT_READY,                        //雷达管理模块,不在准备状态
+    LASER_MANAGER_TASK_OVER_TIME,                    //雷达管理模块,任务超时
+    LASER_MANAGER_LASER_INDEX_ERRPR,                //雷达管理模块,雷达索引错误,编号错误。
+    LASER_MANAGER_LASER_INDEX_REPEAT,                //雷达管理模块,需要扫描的雷达索引重复,可忽略的错误,提示作用
+
+//livox_driver 雷达livox驱动模块
+    LIVOX_DRIVER_ERROR_BASE = 0x01040000,
+    LIVOX_DRIVER_SN_REPEAT,                            //livox驱动模块, 雷达广播码重复
+    LIVOX_DRIVER_SN_ERROR,                            //livox驱动模块, 雷达广播码错误
+    LIVOX_SKD_INIT_FAILED,                            //livox驱动模块, livox_sdk初始化失败
+    LIVOX_DRIVER_NOT_READY,                            //livox驱动模块, livox没有准备好.
+
+
+
+
+    //PLC error code  ...
+    PLC_ERROR_BASE = 0x02010000,
+    PLC_UNKNOWN_ERROR,                                //plc未知错误
+    PLC_EMPTY_TASK,                                    //plc任务为空
+    PLC_IP_PORT_ERROR,                                //plc的ip端口错误
+    PLC_SLAVE_ID_ERROR,                                //plc的身份id错误
+    PLC_CONNECTION_FAILED,                            //PLC连接失败
+    PLC_READ_FAILED,                                //plc读取失败
+    PLC_WRITE_FAILED,                                //plc写入失败
+    PLC_NOT_ENOUGH_DATA_ERROR,                        //PLC没有足够的数据错误
+
+
+
+    //locate 定位模块,
+    LOCATER_ERROR_BASE = 0x03000000,
+    //LASER_MANAGER 定位管理模块
+    LOCATER_MANAGER_ERROR_BASE = 0x03010000,
+    LOCATER_MANAGER_READ_PROTOBUF_ERROR,                //定位管理模块,读取参数错误
+    LOCATER_MANAGER_STATUS_BUSY,                        //定位管理模块,状态正忙
+    LOCATER_MANAGER_STATUS_ERROR,                        //定位管理模块,状态错误
+    LOCATER_MANAGER_TASK_TYPE_ERROR,                    //定位管理模块,任务类型错误
+    LOCATER_MANAGER_IS_NOT_READY,                        //定位管理模块,不在准备状态
+    LOCATER_MANAGER_CLOUD_MAP_ERROR,                    //定位管理模块,任务输入点云map的error
+    LOCATER_MANAGER_TASK_OVER_TIME,                        //定位管理模块,任务超时
+
+
+    //Locater  算法模块
+    LOCATER_ALGORITHM_ERROR_BASE = 0x03020000,
+    LOCATER_TASK_INIT_CLOUD_EMPTY,                        //定位任务初始化点云为空
+    LOCATER_TASK_ERROR,                                    //定位任务错误
+    LOCATER_TASK_INPUT_CLOUD_UNINIT,                    //定位任务输入点云为空
+    LOCATER_INPUT_CLOUD_EMPTY,                            //定位输入点云为空
+    LOCATER_YOLO_UNINIT,                                //定位yolo未初始化
+    LOCATER_POINTSIFT_UNINIT,                            //定位POINTSIFT未初始化
+    LOCATER_3DCNN_UNINIT,                                //定位3DCNN未初始化
+    LOCATER_INPUT_YOLO_CLOUD_EMPTY,                        //定位输入yolo点云为空
+    LOCATER_Y_OUT_RANGE_BY_PLC,                            //定位超出plc的限制范围
+    LOCATER_MEASURE_HEIGHT_CLOUD_UNINIT,                //定位测量高点云未初始化
+    LOCATER_MEASURE_HEIGHT_CLOUD_EMPTY,                    //定位测量高点云为空
+    LOCATER_INPUT_CLOUD_UNINIT,                            //定位输入点云未初始化
+
+
+    //point sift from 0x03010100-0x030101FF
+    LOCATER_SIFT_ERROR_BASE = 0x03020100,
+    LOCATER_SIFT_INIT_FAILED,                            //定位过滤模块,初始化失败
+    LOCATER_SIFT_INPUT_CLOUD_UNINIT,                    //定位过滤模块,输入点云未初始化
+    LOCATER_SIFT_INPUT_CLOUD_EMPTY,                        //定位过滤模块,输入点云为空
+    LOCATER_SIFT_GRID_ERROR,                            //定位过滤模块,筛选网格错误
+    LOCATER_SIFT_SELECT_ERROR,                            //定位过滤模块,筛选选择错误
+    LOCATER_SIFT_CLOUD_VERY_LITTLE,                        //定位过滤模块,筛选点云很少
+    LOCATER_SIFT_CREATE_INPUT_DATA_FAILED,                //定位过滤模块,筛选创建输入数据失败
+    LOCATER_SIFT_PREDICT_FAILED,                        //定位过滤模块,预测失败
+    LOCATER_SIFT_PREDICT_TIMEOUT,                        //定位过滤模块,预测超时
+    LOCATER_SIFT_PREDICT_NO_WHEEL_POINT,                //定位过滤模块,预测结果没有车轮点云
+    LOCATER_SIFT_PREDICT_NO_CAR_POINT,                    //定位过滤模块,预测结果没有车身点云
+
+    LOCATER_SIFT_FILTE_OBS_FAILED,                        //定位过滤模块,过滤OBS失败
+    LOCATER_SIFT_INPUT_BOX_PARAMETER_FAILED,            //定位过滤模块,输入范围参数错误
+
+//    //yolo 
+//    LOCATER_YOLO_ERROR_BASE						=0x03020200,
+//    LOCATER_YOLO_DETECT_FAILED,
+//    LOCATER_YOLO_DETECT_NO_TARGET,
+//    LOCATER_YOLO_PARAMETER_INVALID,
+//    LOCATER_YOLO_INPUT_CLOUD_UNINIT,
+
+    //3dcnn from 0x03010300-0x030103FF
+    LOCATER_3DCNN_ERROR_BASE = 0x03020300,
+    LOCATER_3DCNN_INIT_FAILED,                            //定位3DCNN模块,初始化失败
+    LOCATER_3DCNN_INPUT_CLOUD_UNINIT,                    //定位3DCNN模块,输入点云未初始化
+    LOCATER_3DCNN_INPUT_CLOUD_EMPTY,                    //定位3DCNN模块,输入点云为空
+    LOCATER_3DCNN_INPUT_CLOUD_MAP_ERROR,                //定位3DCNN模块,输入点云的map错误
+    LOCATER_3DCNN_PCA_OUT_ERROR,                        //定位3DCNN模块,pca错误
+    LOCATER_3DCNN_EXTRACT_RECT_ERROR,                    //定位3DCNN模块,提取矩形错误
+    LOCATER_3DCNN_RECT_SIZE_ERROR,                        //定位3DCNN模块,矩形范围错误
+
+    LOCATER_3DCNN_PREDICT_FAILED,                        //定位3DCNN模块,预测失败
+    LOCATER_3DCNN_VERIFY_RECT_FAILED_3,                    //定位3DCNN模块,验证矩形失败3
+    LOCATER_3DCNN_VERIFY_RECT_FAILED_4,                    //定位3DCNN模块,验证矩形失败4
+    LOCATER_3DCNN_KMEANS_FAILED,                        //定位3DCNN模块,k均值失败
+    LOCATER_3DCNN_IIU_FAILED,                            //定位3DCNN模块,IIU失败
+    LOCATER_3DCNN_PCA_OUT_CLOUD_EMPTY,                    //定位3DCNN模块,pca输出点云为空
+
+    //感测和主控通信
+    LOCATER_MSG_TABLE_NOT_EXIST = 0x03030100,
+    LOCATER_MSG_RESPONSE_TYPE_ERROR,                                //测量反馈消息类型错误(致命)
+    LOCATER_MSG_RESPONSE_INFO_ERROR,
+    LOCATER_MSG_REQUEST_CANCELED,
+    LOCATER_MSG_REQUEST_INVALID,
+    LOCATER_MSG_RESPONSE_HAS_NO_REQUEST,
+    LOCATER_MSG_REQUEST_REPEATED,
+
+    //System_manager error from 0x04010000-0x0401FFFF
+    SYSTEM_READ_PARAMETER_ERROR = 0x04010100,
+    SYSTEM_PARAMETER_ERROR,
+    SYSTEM_INPUT_TERMINOR_NO_LASERS,
+
+    //terminor_command_executor.cpp from 0x04010200-0x040102FF
+    TERMINOR_NOT_READY = 0x04010200,
+    TERMINOR_INPUT_LASER_NULL,
+    TERMINOR_NOT_CONTAINS_LASER,
+    TERMINOR_INPUT_PLC_NULL,
+    TERMINOR_INPUT_LOCATER_NULL,
+    TERMINOR_CREATE_WORKING_THREAD_FAILED,
+    TERMINOR_FORCE_QUIT,
+    TERMINOR_LASER_TIMEOUT,
+    TERMINOR_POST_PLC_TIMEOUT,
+    TERMINOR_CHECK_RESULTS_ERROR,
+
+    ////Hardware limit from 0x05010000 - 0x0501ffff
+    ///railing.cpp from 0x05010100-0x050101ff
+    HARDWARE_LIMIT_LEFT_RAILING = 0x05010100,         //左栏杆限制
+    HARDWARE_LIMIT_RAILING_PARAMETER_ERROR,
+    HARDWARE_LIMIT_RAILING_ERROR,
+    HARDWARE_LIMIT_CENTER_X_LEFT,
+    HARDWARE_LIMIT_CENTER_X_RIGHT,
+    HARDWARE_LIMIT_CENTER_Y_TOP,
+    HARDWARE_LIMIT_CENTER_Y_BOTTOM,
+    HARDWARE_LIMIT_HEIGHT_OUT_RANGE,
+    HARDWARE_LIMIT_ANGLE_OUT_RANGE,
+    //termonal_limit from 0x05010200-0x050102ff
+    HARDWARE_LIMIT_TERMINAL_LEFT_ERROR,
+    HARDWARE_LIMIT_TERMINAL_RIGHT_ERROR,
+    HARDWARE_LIMIT_TERMINAL_LR_ERROR,
+
+
+//万集设备模块,
+    WANJI_LIDAR_DEVICE_ERROR_BASE = 0x06080000,//万集设备模块,错误基类
+    WANJI_LIDAR_DEVICE_STATUS_BUSY,                                    //万集设备模块,状态正忙
+    WANJI_LIDAR_DEVICE_STATUS_ERROR,                                //万集设备模块,状态错误
+    WANJI_LIDAR_DEVICE_TASK_TYPE_ERROR,                                //万集设备模块,任务类型错误
+    WANJI_LIDAR_DEVICE_TASK_OVER_TIME,                                //万集设备模块,任务超时
+    WANJI_LIDAR_DEVICE_NO_CLOUD,                                    //万集设备模块,没有点云
+    // velodyne设备模块
+    VELODYNE_LIDAR_DEVICE_ERROR_BASE,                                //velodyne设备模块,错误基类
+    VELODYNE_LIDAR_DEVICE_STATUS_BUSY,                                    //velodyne设备模块,状态正忙
+    VELODYNE_LIDAR_DEVICE_STATUS_ERROR,                                //velodyne设备模块,状态错误
+    VELODYNE_LIDAR_DEVICE_TASK_TYPE_ERROR,                                //velodyne设备模块,任务类型错误
+    VELODYNE_LIDAR_DEVICE_TASK_OVER_TIME,                                //velodyne设备模块,任务超时
+    VELODYNE_LIDAR_DEVICE_NO_CLOUD,                                    //velodyne设备模块,没有点云
+
+
+    //万集通信wj_lidar error from 0x06010000-0x0601FFFF
+    WJ_LIDAR_COMMUNICATION_ERROR_BASE = 0x06010000,
+    WJ_LIDAR_COMMUNICATION_UNINITIALIZED,                            //万集通信,未初始化
+    WJ_LIDAR_COMMUNICATION_DISCONNECT,                                //万集通信,断连
+    WJ_LIDAR_COMMUNICATION_FAULT,                                    //万集通信,故障
+    WJ_LIDAR_CONNECT_FAILED,                                        //万集通信,连接失败
+    WJ_LIDAR_UNINITIALIZED,                                            //万集通信,未初始化
+    WJ_LIDAR_READ_FAILED,                                            //万集通信,读取失败
+    WJ_LIDAR_WRITE_FAILED,                                            //万集通信,写入失败
+    WJ_LIDAR_GET_CLOUD_TIMEOUT,                                        //万集通信,获取点云超时
+    // velodyne通信
+    VELODYNE_LIDAR_COMMUNICATION_UNINITIALIZED,                            //velodyne通信,未初始化
+    VELODYNE_LIDAR_COMMUNICATION_DISCONNECT,                                //velodyne通信,断连
+    VELODYNE_LIDAR_COMMUNICATION_FAULT,                                    //velodyne通信,故障
+    VELODYNE_LIDAR_CONNECT_FAILED,                                        //velodyne通信,连接失败
+    VELODYNE_LIDAR_UNINITIALIZED,                                            //velodyne通信,未初始化
+    VELODYNE_LIDAR_READ_FAILED,                                            //velodyne通信,读取失败
+    VELODYNE_LIDAR_WRITE_FAILED,                                            //velodyne通信,写入失败
+    VELODYNE_LIDAR_GET_CLOUD_TIMEOUT,                                        //velodyne通信,获取点云超时
+
+    //万集解析 wj lidar protocol error from 0x06020000-0x0602FFFF
+    WJ_PROTOCOL_ERROR_BASE = 0x06020000,
+    WJ_PROTOCOL_STATUS_BUSY,                                        //万集解析, 状态正忙
+    WJ_PROTOCOL_STATUS_ERROR,                                        //万集解析, 状态错误
+    WJ_PROTOCOL_INTEGRITY_ERROR,                                    //万集解析, 完整性错误
+    WJ_PROTOCOL_PARSE_FAILED,                                        //万集解析, 解析失败
+    WJ_PROTOCOL_EMPTY_PACKAGE,                                        //万集解析, 空包
+    WJ_PROTOCOL_EXCEED_MAX_SIZE,                                    //万集解析, 超出最大范围
+
+    //万集测量范围 wj region detect error from 0x06030000-0x0603FFFF
+    WJ_REGION_ERROR_BASE = 0x06030000,
+    WJ_REGION_EMPTY_CLOUD,                                            //万集测量,空点云
+    WJ_REGION_EMPTY_NO_WHEEL_INFORMATION,                            //万集测量,没有车轮信息
+    WJ_REGION_RECTANGLE_ANGLE_ERROR,                                //万集测量,矩形旋转角错误
+    WJ_REGION_RECTANGLE_SIZE_ERROR,                                    //万集测量,矩形大小错误
+    WJ_REGION_RECTANGLE_SYMMETRY_ERROR,                                //万集测量,矩形对称错误
+    WJ_REGION_CLUSTER_SIZE_ERROR,                                    //万集测量,簇大小错误
+    WJ_REGION_CERES_SOLVE_ERROR,                                    //万集测量,优化失败
+    //velodyne测量范围
+    VELODYNE_REGION_ERROR_BASE,
+    VELODYNE_REGION_EMPTY_CLOUD,                                            //velodyne测量,空点云
+    VELODYNE_REGION_EMPTY_NO_WHEEL_INFORMATION,                            //velodyne测量,没有车轮信息
+    VELODYNE_REGION_RECTANGLE_ANGLE_ERROR,                                //velodyne测量,矩形旋转角错误
+    VELODYNE_REGION_RECTANGLE_SIZE_ERROR,                                    //velodyne测量,矩形大小错误
+    VELODYNE_REGION_RECTANGLE_SYMMETRY_ERROR,                                //velodyne测量,矩形对称错误
+    VELODYNE_REGION_CLUSTER_SIZE_ERROR,                                    //velodyne测量,簇大小错误
+    VELODYNE_REGION_CERES_SOLVE_ERROR,                                    //velodyne测量,优化失败
+    VELODYNE_REGION_ANGLE_PRECHECK_FAILED,                                    //velodyne测量,rough angle check failed
+
+    //万集管理模块 wj manager error from 0x06040000-0x0604FFFF
+    WJ_MANAGER_ERROR_BASE = 0x06040000,
+    WJ_MANAGER_UNINITIALIZED,                                    //万集管理模块,未初始化
+    WJ_MANAGER_LIDAR_DISCONNECTED,                                //万集管理模块,雷达断链
+    WJ_MANAGER_PLC_DISCONNECTED,                                //万集管理模块,plc断链
+    WJ_MANAGER_EMPTY_CLOUD,                                        //万集管理模块,空点云
+    WJ_MANAGER_READ_PROTOBUF_ERROR,                                //万集管理模块,读取参数错误
+    WJ_MANAGER_INIT_ERROR,                                        //万集管理模块,初始化error
+    WJ_MANAGER_TASK_TYPE_ERROR,                                    //万集管理模块,任务类型错误
+    WJ_MANAGER_STATUS_BUSY,                                        //万集管理模块,状态正忙
+    WJ_MANAGER_STATUS_ERROR,                                    //万集管理模块,状态错误
+    WJ_MANAGER_LASER_INDEX_ERRPR,                                //万集管理模块,雷达索引错误,编号错误。
+    WJ_MANAGER_LASER_INDEX_REPEAT,                                //万集管理模块,需要扫描的雷达索引重复,可忽略的错误,提示作用
+    WJ_MANAGER_TASK_OVER_TIME,                                    //万集管理模块,任务超时
+    VELODYNE_MANAGER_ERROR_BASE,
+    VELODYNE_MANAGER_UNINITIALIZED,                                    //velodyne管理模块,未初始化
+    VELODYNE_MANAGER_LIDAR_DISCONNECTED,                                //velodyne管理模块,雷达断链
+    VELODYNE_MANAGER_EMPTY_CLOUD,                                        //velodyne管理模块,空点云
+    VELODYNE_MANAGER_READ_PROTOBUF_ERROR,                                //velodyne管理模块,读取参数错误
+    VELODYNE_MANAGER_INIT_ERROR,                                        //velodyne管理模块,初始化error
+    VELODYNE_MANAGER_TASK_TYPE_ERROR,                                    //velodyne管理模块,任务类型错误
+    VELODYNE_MANAGER_STATUS_BUSY,                                        //velodyne管理模块,状态正忙
+    VELODYNE_MANAGER_STATUS_ERROR,                                    //velodyne管理模块,状态错误
+    VELODYNE_MANAGER_LASER_INDEX_ERRPR,                                //velodyne管理模块,雷达索引错误,编号错误。
+    VELODYNE_MANAGER_LASER_INDEX_REPEAT,                                //velodyne管理模块,需要扫描的雷达索引重复,可忽略的错误,提示作用
+    VELODYNE_MANAGER_TASK_OVER_TIME,                                    //velodyne管理模块,任务超时
+
+//万集任务模块
+    WJ_LIDAR_TASK_ERROR_BASE = 0x06050000,
+    WJ_LIDAR_TASK_EMPTY_RESULT,                                    //万集任务模块,空结果
+    WJ_LIDAR_TASK_EMPTY_TASK,                                    //万集任务模块,空任务
+    WJ_LIDAR_TASK_WRONG_TYPE,                                    //万集任务模块,错误类型
+    WJ_LIDAR_TASK_INVALID_TASK,                                    //万集任务模块,无效任务
+    WJ_LIDAR_TASK_MEASURE_FAILED,                                //万集任务模块,测量失败
+
+    // 万集滤波
+    WJ_FILTER_ERROR_BASE = 0x06060000,
+    WJ_FILTER_LACK_OF_RESULT,                                   //万集滤波,结果不足以进行滤波
+    WJ_FILTER_FLUCTUATING,                                      //万集滤波,结果波动过大
+
+    //task module, 任务模块  error from 0x10010000-0x1001FFFF
+    TASK_MODULE_ERROR_BASE = 0x10010000,
+    TASK_TYPE_IS_UNKNOW,
+    TASK_NO_RECEIVER,
+
+
+    //Communication module, 通信模块
+    COMMUNICATION_BASE_ERROR_BASE = 0x11010000,
+    COMMUNICATION_READ_PROTOBUF_ERROR,                //模块,读取参数错误
+    COMMUNICATION_BIND_ERROR,
+    COMMUNICATION_CONNECT_ERROR,
+    COMMUNICATION_ANALYSIS_TIME_OUT,                                    //解析超时,
+    COMMUNICATION_EXCUTER_IS_BUSY,                                        //处理器正忙, 请稍等
+
+
+
+
+    //system module, 系统模块
+    SYSTEM_EXECUTOR_ERROR_BASE = 0x12010000,        //系统执行模块,
+    SYSTEM_EXECUTOR_PARSE_ERROR,                                        //系统执行模块, 解析消息错误
+    SYSTEM_EXECUTOR_STATUS_BUSY,                                        //系统执行模块, 状态正忙
+    SYSTEM_EXECUTOR_STATUS_ERROR,                                        //系统执行模块, 状态错误
+    SYSTEM_EXECUTOR_CHECK_ERROR,                                        //系统执行模块, 检查错误
+
+
+
+    //Dispatch 调度 模块 错误码
+    DISPATCH_ERROR_BASE = 0x13000000,
+
+    //Dispatch_manager 调度管理模块 错误码
+    DISPATCH_MANAGER_ERROR_BASE = 0x13010000,
+    DISPATCH_MANAGER_READ_PROTOBUF_ERROR,                //调度管理模块,读取参数错误
+    DISPATCH_MANAGER_STATUS_BUSY,                        //调度管理模块,状态正忙
+    DISPATCH_MANAGER_STATUS_ERROR,                        //调度管理模块,状态错误
+    DISPATCH_MANAGER_TASK_TYPE_ERROR,                    //调度管理模块,任务类型错误
+    DISPATCH_MANAGER_IS_NOT_READY,                        //调度管理模块,不在准备状态
+    DISPATCH_MANAGER_SPACE_LOCK_ERROR,                    //调度管理模块,空间锁错误
+
+
+    DISPATCH_PROCESS_ERROR_BASE = 0x13020000,
+    DISPATCH_PROCESS_IS_NOT_READY,                        //调度流程, 不在准备状态
+    DISPATCH_PROCESS_DEVICE_TYPE_ERROR,                    //调度流程, 设备类型错误
+    DISPATCH_PROCESS_DEVICE_STATUS_ERROR,                //调度流程, 设备状态错误
+    DISPATCH_PROCESS_TASK_STATUS_ERROR,                    //调度流程, 任务类型错误
+    DISPATCH_PROCESS_COMMAND_KEY_REPEAT,                //调度流程, 唯一码错误
+    DISPATCH_PROCESS_INIT_ERROR,                        //调度流程, 初始化错误
+
+
+    DISPATCH_DEVICE_ERROR_BASE = 0x13030000,
+    DISPATCH_DEVICE_READ_PROTOBUF_ERROR,                //调度设备模块,读取参数错误
+    DISPATCH_DEVICE_STATUS_BUSY,                        //调度设备模块,状态正忙
+    DISPATCH_DEVICE_STATUS_ERROR,                        //调度设备模块,状态错误
+    DISPATCH_DEVICE_STATUS_DISCONNECT,                    //调度设备模块,状态断连
+    DISPATCH_DEVICE_TASK_TYPE_ERROR,                    //调度设备模块,任务类型错误
+    DISPATCH_DEVICE_TASK_OVER_TIME,                        //调度设备模块,任务超时
+    DISPATCH_DEVICE_TASK_REPEAT,                        //调度设备模块,任务重复, 任务已经存在
+    DISPATCH_DEVICE_IS_NOT_READY,                        //调度设备模块,不在准备状态
+    DISPATCH_DEVICE_RESPONS_ERROR,                        //调度设备模块,指令的执行失败
+    DISPATCH_DEVICE_TASK_NOTHINGNESS,                    //调度设备模块,任务不存在
+    DISPATCH_DEVICE_TASK_LEVEL_ERROR,                    //调度设备模块,任务等级错误
+
+
+    CARRIER_ERROR_BASE = 0x13040000,
+    CARRIER_READ_PROTOBUF_ERROR,                //搬运器模块,读取参数错误
+    CARRIER_STATUS_BUSY,                        //搬运器模块,状态正忙
+    CARRIER_STATUS_ERROR,                        //搬运器模块,状态错误
+    CARRIER_STATUS_DISCONNECT,                    //搬运器模块,状态断连
+    CARRIER_TASK_TYPE_ERROR,                    //搬运器模块,任务类型错误
+    CARRIER_TASK_OVER_TIME,                        //搬运器模块,任务超时
+    CARRIER_IS_NOT_READY,                        //搬运器模块,不在准备状态
+    CARRIER_RESPONS_ERROR,                        //搬运器模块,指令的执行失败
+    CARRIER_TASK_NOTHINGNESS,                    //搬运器模块,任务不存在
+    CARRIER_POSE_ERROR,                            //搬运器模块,姿态错误
+    CARRIER_CONRTOL_PARAMETER_ERROR,            //搬运器模块,控制参数错误
+
+    CATCHER_ERROR_BASE = 0x13050000,
+    CATCHER_READ_PROTOBUF_ERROR,                //抓取器模块,读取参数错误
+    CATCHER_STATUS_BUSY,                        //抓取器模块,状态正忙
+    CATCHER_STATUS_ERROR,                        //抓取器模块,状态错误
+    CATCHER_STATUS_DISCONNECT,                    //抓取器模块,状态断连
+    CATCHER_TASK_TYPE_ERROR,                    //抓取器模块,任务类型错误
+    CATCHER_TASK_OVER_TIME,                        //抓取器模块,任务超时
+    CATCHER_IS_NOT_READY,                        //抓取器模块,不在准备状态
+    CATCHER_RESPONS_ERROR,                        //抓取器模块,指令的执行失败
+    CATCHER_TASK_NOTHINGNESS,                    //抓取器模块,任务不存在
+    CATCHER_POSE_ERROR,                            //抓取器模块,姿态错误
+    CATCHER_CONRTOL_PARAMETER_ERROR,            //抓取器模块,控制参数错误
+
+    PASSAGEWAY_ERROR_BASE = 0x13060000,
+    PASSAGEWAY_READ_PROTOBUF_ERROR,                //通道口模块,读取参数错误
+    PASSAGEWAY_STATUS_BUSY,                        //通道口模块,状态正忙
+    PASSAGEWAY_STATUS_ERROR,                        //通道口模块,状态错误
+    PASSAGEWAY_STATUS_DISCONNECT,                    //通道口模块,状态断连
+    PASSAGEWAY_TASK_TYPE_ERROR,                    //通道口模块,任务类型错误
+    PASSAGEWAY_TASK_OVER_TIME,                        //通道口模块,任务超时
+    PASSAGEWAY_IS_NOT_READY,                        //通道口模块,不在准备状态
+    PASSAGEWAY_RESPONS_ERROR,                        //通道口模块,指令的执行失败
+    PASSAGEWAY_TASK_NOTHINGNESS,                    //通道口模块,任务不存在
+
+
+    //DISPATCH_COORDINATES module, 通信模块
+    DISPATCH_COORDINATES_ERROR_BASE = 0x13060000,
+    DISPATCH_COORDINATES_READ_PROTOBUF_ERROR,                //调度坐标模块,读取参数错误
+    DISPATCH_COORDINATES_ID_ERROR,                            //调度坐标模块,坐标id错误
+    DISPATCH_COORDINATES_PATH_ERROR,                        //调度坐标模块,路径方向错误
+    DISPATCH_COORDINATES_CAN_NOT_LOCK,                        //调度坐标模块,不能加锁
+
+    //Dispatch_plc 调度plc模块
+    DISPATCH_PLC_ERROR_BASE = 0x13070000,
+    DISPATCH_PLC_REQUEST_ERROR,                                //调度plc模块,请求错误
+    DISPATCH_PLC_RESPONS_ERROR,                                //调度plc模块,指令的执行失败
+    DISPATCH_PLC_STATUS_ERROR,                                //调度plc模块,状态错误
+    DISPATCH_PLC_TIME_OUT,                                    //调度plc模块,超时
+
+
+    //snap7 通信模块 错误码
+    SNAP7_ERROR_BASE = 0x1401000,
+    SNAP7_READ_PROTOBUF_ERROR,                            //snap7通信模块,读取参数错误
+    SNAP7_CONNECT_ERROR,                                //snap7通信模块,连接错误
+    SNAP7_DISCONNECT_ERROR,                                //snap7通信模块,断连错误
+    SNAP7_READ_ERROR,                                    //snap7通信模块,读取错误
+    SNAP7_WRITE_ERROR,                                    //snap7通信模块,写入错误
+    SNAP7_ANALYSIS_TIME_OUT,                                    //解析超时,
+    SNAP7_EXCUTER_IS_BUSY,                                        //处理器正忙, 请稍等
+
+    TOF3D_VZENSE_DEVICE_INIT_SUCCESS = 0x1501000,
+    TOF3D_VZENSE_DEVICE_INIT_FAILED,
+
+
+
+    //parkspace allocator,车位分配模块
+    PARKSPACE_ALLOCATOR_ERROR_BASE = 0x20010000,
+    PARKSPACE_ALLOCATOR_MSG_REQUEST_TYPE_ERROR,    //反馈车位消息类型错误 
+    PARKSPACE_ALLOCATOR_MSG_RESPONSE_TYPE_ERROR,    //反馈车位消息类型错误
+    PARKSPACE_ALLOCATOR_MSG_PARSE_ERROR,            //请求消息解析错误
+    PARKSPACE_ALLOCATOR_SPACE_EMPTY,                //空车位异常,车库无车位。或许由模块初始化异常产生
+    PARKSPACE_ALLOCATOR_ALLOCATE_FAILED,            //无合适车位,分配失败
+    PARKSPACE_ALLOCATOR_SEARCH_FAILED,              //未找到车辆对应车位
+    PARKSPACE_ALLOCATOR_RELEASE_FAILED,             //未找到匹配的车位,车位未释放
+    PARKSPACE_ALLOCATOR_FORCE_UPDATE_FAILED,        //手动更新失败,未找到匹配车位
+    PARKSPACE_ALLOCATOR_PARAM_ERROR,                //传入参数错误
+    PARKSPACE_ALLOCATOR_CONFIRM_ALLOC_ERROR,        //确认分配车位错误
+    PARKSPACE_ALLOCATOR_CAR_ALREADY_EXIST,          //车辆已存在
+
+    // 数据库操作
+    DB_ERROR_BASE = 0x20020000,
+    DB_INIT_FAILED,                                    //数据库初始化失败
+    DB_CONNECT_FAILED,                              //数据库连接失败
+    DB_STATUS_ERROR,                                //数据库状态错误
+    DB_MANAGER_STATUS_ERROR,                        //数据库管理状态错误
+    DB_INSERT_FAILED,                               //数据库插入失败
+    DB_DELETE_FAILED,                               //数据库删除失败
+    DB_UPDATE_FAILED,                               //数据库更新失败
+    DB_QUERY_FAILED,                                //数据库查询失败
+    DB_UNINITIALIZED,                               //数据库外层未初始化
+    DB_DISCONNECTED,                                //数据库外层连接失去
+    DB_RESULT_SET_EMPTY,                            //数据库外层查询返回结果空指针
+    DB_RESULT_SET_PARSE_ERROR,                      //数据库外层查询结果解析失败
+    DB_CONNECT_CHANNEL_NOT_FOUND,                    //数据库连接通道未找到
+    DB_CONNECT_CHANNEL_NUMBER_ERROR,                //数据库连接通道数量错误
+    DB_QUERY_DATA_REPEAT,                            //数据库查询数据重复
+    DB_QUERY_NOT_DATA,                                //数据库未查询到数据
+    DB_QUERY_DATA_FAILED,                            //数据库查询数据错误
+    DB_NOT_QUERY_EMPTY_PARKSPACE,                    //数据库未查询到空车位
+    DB_PROTOBUF_ERROR,                                //数据库配置参数读取错误
+
+
+
+
+    //parkspace error code
+    PARKSPACE_REQUEST_MSG_TYPE_ERROR = 0x20030000,
+    PARKSPACE_ALLOCMSG_RESPONSE_HAS_NO_REQUEST,
+    PARKSPACE_SEARCHMSG_RESPONSE_HAS_NO_REQUEST,
+    PARKSPACE_RELEASEMSG_RESPONSE_HAS_NO_REQUEST,
+    PARKSPACE_ALLOC_REQUEST_INVALID,
+    PARKSPACE_SEARCH_REQUEST_INVALID,
+    PARKSPACE_RELEASE_REQUEST_INVALID,
+
+    PARKSPACE_ALLOC_REQUEST_REPEATED,
+    PARKSPACE_SEARCH_REQUEST_REPEATED,
+    PARKSPACE_RELEASE_REQUEST_REPEATED,
+
+    PARKSPACE_ALLOC_RESPONSE_TYPE_ERROR,
+    PARKSPACE_SEARCH_RESPONSE_TYPE_ERROR,
+    PARKSPACE_RELEASE_RESPONSE_TYPE_ERROR,
+
+    PARKSPACE_ALLOC_RESPONSE_INFO_ERROR,
+    PARKSPACE_SEARCH_RESPONSE_INFO_ERROR,
+    PARKSPACE_RELEASE_RESPONSE_INFO_ERROR,
+
+    PARKSPACE_ALLOC_REQUEST_CANCELED,
+    PARKSPACE_SEARCH_REQUEST_CANCELED,
+    PARKSPACE_RELEASE_REQUEST_CANCELED,
+
+    //rabbitmq module, 通信模块  新版通信
+    RABBITMQ_BASE_ERROR_BASE = 0x31010000,
+    RABBITMQ_READ_PROTOBUF_ERROR,                    //rabbitmq模块,读取PROTO参数错误
+    RABBITMQ_AMQP_NEW_CONNECTION_ERROR,                //rabbitmq模块,新建连接状态错误
+    RABBITMQ_AMQP_TCP_SOCKET_NEW_ERROR,                //rabbitmq模块,新建socket错误
+    RABBITMQ_PROTOBUF_LOSS_ERROR,                    //rabbitmq模块,PROTO缺少数据
+    RABBITMQ_AMQP_SOCKET_OPEN_ERROR,                //rabbitmq模块,打开socket错误
+    RABBITMQ_AMQP_LOGIN_ERROR,                        //rabbitmq模块,登录服务器错误
+    RABBITMQ_AMQP_CHANNEL_OPEN_ERROR,                //rabbitmq模块,打开通道错误
+    RABBITMQ_AMQP_QUEUE_BIND_ERROR,                    //rabbitmq模块,绑定队列错误
+    RABBITMQ_AMQP_NEW_CONSUME_ERROR,                //rabbitmq模块,新建消费者错误
+    RABBITMQ_AMQP_CONSUME_MESSAGE_ERROR,            //rabbitmq模块,接受消息错误
+    RABBITMQ_AMQP_BASIC_PUBLISH_ERROR,                //rabbitmq模块,发送消息错误
+    RABBITMQ_AMQP_BASIC_ACK_ERROR,                    //rabbitmq模块,确认消息错误
+
+
+    RABBITMQ_BIND_ERROR,
+    RABBITMQ_CONNECT_ERROR,
+    RABBITMQ_ANALYSIS_TIME_OUT,                                    //解析超时,
+    RABBITMQ_EXCUTER_IS_BUSY,                                        //处理器正忙, 请稍等
+
+
+};
+
+//错误等级,用来做故障处理
+enum Error_level {
+//    正常,没有错误,默认值0
+    NORMAL = 0,
+
+
+//    轻微故障,可忽略的故障,NEGLIGIBLE_ERROR
+//    提示作用,不做任何处理,不影响代码的流程,
+//    用作一些不重要的事件,即使出错也不会影响到系统功能,
+//    例如:文件保存错误,等
+    NEGLIGIBLE_ERROR = 1,
+
+
+//    一般故障,MINOR_ERROR
+//    用作底层功能函数的错误返回,表示该功能函数执行失败,
+//    返回给应用层之后,需要做故障分析和处理,
+//    例如:雷达数据传输失败,应用层就需要进行重新扫描,或者重连,或者重置参数等。
+    MINOR_ERROR = 2,
+
+
+//    严重故障,MAJOR_ERROR
+//    用作应用层的任务事件的结果,表示该功能模块失败。
+//    通常是底层函数返回一般故障之后,应用层无法处理并解决故障,此时就要进行故障升级,
+//    从一般故障升级为严重故障,然后进行回退流程,回退已经执行的操作,最终回到故障待机状态。
+//    需要外部清除故障,并复位至正常待机状态,才能恢复功能的使用。
+//    例如:雷达扫描任务失败,且无法自动恢复。
+    MAJOR_ERROR = 3,
+
+
+//    致命故障,CRITICAL_ERROR
+//    系统出现致命错误。导致系统无法正常运行,
+//    此时系统应该紧急停机,执行紧急流程,快速停机。
+//    此时不允许再执行任何函数和任务指令,防止系统故障更加严重。
+//    也不需要做任何错误处理了,快速执行紧急流程。
+//    例如:内存错误,进程挂死,关键设备失控,监控设备报警,等
+    CRITICAL_ERROR = 4,
+};
+
+
+class Error_manager {
+public://外部接口函数
+    //构造函数
+    Error_manager() {
+        m_error_code = SUCCESS;
+        m_error_level = NORMAL;
+    }
+
+    //拷贝构造
+    Error_manager(const Error_manager &error_manager) {
+        this->m_error_code = error_manager.m_error_code;
+        this->m_error_level = error_manager.m_error_level;
+        this->m_error_description = error_manager.m_error_description;
+    }
+
+    //赋值构造
+    Error_manager(Error_code error_code, Error_level error_level = NORMAL,
+                  const char *f = nullptr, ...) {
+        va_list arg_ptr;
+        char buf[DESCRIPTION_BUF_SIZE];
+        va_start(arg_ptr, f);
+        vsnprintf(buf, DESCRIPTION_BUF_SIZE, f, arg_ptr);
+        va_end(arg_ptr);
+
+        m_error_code = error_code;
+        m_error_level = error_level;
+        m_error_description = std::string(buf);
+    }
+
+    //赋值构造
+    Error_manager(Error_code error_code, Error_level error_level, std::string error_aggregate_string) {
+        m_error_code = error_code;
+        m_error_level = error_level;
+        m_error_description = error_aggregate_string;
+    }
+
+    //析构函数
+    ~Error_manager() = default;
+
+    //初始化
+    void error_manager_init() {
+        error_manager_clear_all();
+    }
+
+    //初始化
+    void error_manager_init(Error_code error_code, Error_level error_level = NORMAL,
+                            const char *f = nullptr, ...) {
+        va_list arg_ptr;
+        char buf[DESCRIPTION_BUF_SIZE];
+        va_start(arg_ptr, f);
+        vsnprintf(buf, DESCRIPTION_BUF_SIZE, f, arg_ptr);
+        va_end(arg_ptr);
+
+        m_error_code = error_code;
+        m_error_level = error_level;
+        m_error_description = std::string(buf);
+    }
+
+    //初始化
+    void error_manager_init(Error_code error_code, Error_level error_level, std::string error_aggregate_string) {
+        m_error_code = error_code;
+        m_error_level = error_level;
+        m_error_description = error_aggregate_string;
+    }
+
+    //重置
+    void error_manager_reset(Error_code error_code, Error_level error_level = NORMAL,
+                             const char *f = nullptr, ...) {
+        va_list arg_ptr;
+        char buf[DESCRIPTION_BUF_SIZE];
+        va_start(arg_ptr, f);
+        vsnprintf(buf, DESCRIPTION_BUF_SIZE, f, arg_ptr);
+        va_end(arg_ptr);
+
+        m_error_code = error_code;
+        m_error_level = error_level;
+        m_error_description = std::string(buf);
+    }
+
+    //重置
+    void error_manager_reset(Error_code error_code, Error_level error_level, std::string error_aggregate_string) {
+        m_error_code = error_code;
+        m_error_level = error_level;
+        m_error_description = error_aggregate_string;
+    }
+
+    //重置
+    void error_manager_reset(const Error_manager &error_manager) {
+        this->m_error_code = error_manager.m_error_code;
+        this->m_error_level = error_manager.m_error_level;
+        this->m_error_description = error_manager.m_error_description;
+    }
+
+    //清除所有内容
+    void error_manager_clear_all() {
+        m_error_code = SUCCESS;
+        m_error_level = NORMAL;
+        m_error_description.clear();
+    }
+
+    //重载=
+    Error_manager &operator=(const Error_manager &error_manager) {
+        error_manager_reset(error_manager);
+        return *this;
+    }
+
+    //重载=,支持Error_manager和Error_code的直接转化,会清空错误等级和描述
+    Error_manager &operator=(Error_code error_code) {
+        error_manager_clear_all();
+        set_error_code(error_code);
+        return *this;
+    }
+
+    //重载==
+    bool operator==(const Error_manager &error_manager) {
+        return is_equal_error_manager(error_manager);
+    }
+
+    //重载==,支持Error_manager和Error_code的直接比较
+    bool operator==(Error_code error_code) {
+        if (m_error_code == error_code) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    //重载!=
+    bool operator!=(const Error_manager &error_manager) {
+        return (!is_equal_error_manager(error_manager));
+    }
+
+    //重载!=,支持Error_manager和Error_code的直接比较
+    bool operator!=(Error_code error_code) {
+        if (m_error_code != error_code) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    //重载<<,支持cout<<
+    friend std::ostream &operator<<(std::ostream &out, Error_manager &error_manager) {
+        out << error_manager.to_string();
+        return out;
+    }
+
+
+    //获取错误码
+    Error_code get_error_code() {
+        return m_error_code;
+    }
+
+    //获取错误等级
+    Error_level get_error_level() {
+        return m_error_level;
+    }
+
+    //获取错误描述的指针,(浅拷贝)
+    std::string get_error_description() {
+        return m_error_description;
+    }
+
+    //复制错误描述,(深拷贝)
+    //output:p_error_description     错误描述的字符串指针,不可以为NULL,必须要有实际的内存
+    //output:description_length      错误描述的字符串长度,不可以为0,长度最好足够大,一般256即可。
+    void copy_error_description(const char *p_error_description) {
+        if (p_error_description != nullptr) {
+            m_error_description = std::string(p_error_description);
+        }
+    }
+
+    //复制错误描述,(深拷贝)
+    //output:error_description_string     错误描述的string
+    void copy_error_description(std::string error_description_string) {
+        m_error_description = error_description_string;
+    }
+
+    //设置错误码
+    void set_error_code(Error_code error_code) {
+        m_error_code = error_code;
+    }
+
+    //比较错误等级并升级,取高等级的结果
+    void set_error_level_up(Error_level error_level) {
+        if (m_error_level < error_level) {
+            m_error_level = error_level;
+        }
+    }
+
+    //比较错误等级并降级,取低等级的结果
+    void set_error_level_down(Error_level error_level) {
+        if (m_error_level > error_level) {
+            m_error_level = error_level;
+        }
+    }
+
+    //错误等级,设定到固定值
+    void set_error_level_location(Error_level error_level) {
+        m_error_level = error_level;
+    }
+
+    //设置错误描述
+    void set_error_description(const char *f, ...) {
+        va_list arg_ptr;
+        char buf[DESCRIPTION_BUF_SIZE];
+        va_start(arg_ptr, f);
+        vsnprintf(buf, DESCRIPTION_BUF_SIZE, f, arg_ptr);
+        va_end(arg_ptr);
+        m_error_description = std::string(buf);
+    }
+
+    //设置错误描述
+    void set_error_description(std::string error_description_string) {
+        m_error_description = error_description_string;
+    }
+
+    //尾部追加错误描述
+    void add_error_description(const char *f, ...) {
+        va_list arg_ptr;
+        char buf[DESCRIPTION_BUF_SIZE];
+        va_start(arg_ptr, f);
+        vsnprintf(buf, DESCRIPTION_BUF_SIZE, f, arg_ptr);
+        va_end(arg_ptr);
+        m_error_description += std::string(buf);
+    }
+
+    //尾部追加错误描述
+    void add_error_description(std::string error_description_string) {
+        m_error_description += error_description_string;
+    }
+
+    //比较错误是否相同,
+    // 注:只比较错误码和等级
+    bool is_equal_error_manager(const Error_manager &error_manager) {
+        if (this->m_error_code == error_manager.m_error_code
+            && this->m_error_level == error_manager.m_error_level) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    //比较并覆盖错误,讲低级错误转为字符串存放于描述中,
+    //如果错误相同,则保留this的,将输入参数转入描述。
+    void compare_and_cover_error(const Error_manager &error_manager) {
+        if (this->m_error_code == SUCCESS) {
+            error_manager_reset(error_manager);
+        } else if (error_manager.m_error_code == SUCCESS) {
+            return;
+        } else {
+            Error_manager t_error_manager_new;
+            std::string pt_string_inside;
+            if (this->m_error_level < error_manager.m_error_level) {
+                translate_error_to_string(pt_string_inside);
+                error_manager_reset(error_manager);
+                add_error_description(pt_string_inside);
+            } else {
+                ((Error_manager &) error_manager).translate_error_to_string(pt_string_inside);
+                add_error_description(pt_string_inside);
+            }
+        }
+    }
+
+    //比较并覆盖错误,讲低级错误转为字符串存放于描述中,
+    //如果错误相同,则保留this的,将输入参数转入描述。
+    void compare_and_cover_error(Error_manager *p_error_manager) {
+        if (this->m_error_code == SUCCESS) {
+            error_manager_reset(*p_error_manager);
+        } else if (p_error_manager->m_error_code == SUCCESS) {
+            return;
+        } else {
+            Error_manager t_error_manager_new;
+            std::string pt_string_inside;
+            if (this->m_error_level < p_error_manager->m_error_level) {
+                translate_error_to_string(pt_string_inside);
+                error_manager_reset(*p_error_manager);
+                add_error_description(pt_string_inside);
+            } else {
+                p_error_manager->translate_error_to_string(pt_string_inside);
+                add_error_description(pt_string_inside);
+            }
+        }
+    }
+
+    void compare_and_merge_up(const Error_manager &error) {
+        if (error.m_error_level > this->m_error_level) {
+            this->m_error_level = error.m_error_level;
+            this->m_error_code = error.m_error_code;
+            this->m_error_description += error.m_error_description;
+        }
+    }
+    void compare_and_merge_down(const Error_manager &error) {
+        if (error.m_error_level < this->m_error_level) {
+            this->m_error_level = error.m_error_level;
+            this->m_error_code = error.m_error_code;
+            this->m_error_description += error.m_error_description;
+        }
+    }
+
+
+    //output:error_description_string     错误汇总的string
+    void translate_error_to_string(std::string error_aggregate_string) {
+        char t_string_array[255] = {0};
+        sprintf(t_string_array, "error_code:0x%08x, error_level:%02d, error_description:",
+                m_error_code, m_error_level);
+
+        error_aggregate_string = t_string_array;
+        error_aggregate_string += m_error_description;
+    }
+
+    //错误码转字符串的简易版,可支持cout<<
+    //return     错误汇总的string
+    std::string to_string() {
+        char t_string_array[255] = {0};
+        sprintf(t_string_array, "error_code:0x%08x, error_level:%02d, error_description:",
+                m_error_code, m_error_level);
+        std::string error_aggregate_string = t_string_array;
+        error_aggregate_string += m_error_description;
+        return error_aggregate_string;
+    }
+
+
+protected:
+    Error_code m_error_code;               //错误码
+    Error_level m_error_level;              //错误等级
+    std::string m_error_description;        // 错误描述
+
+protected://内部功能函数
+public:
+    //释放错误描述的内存,
+    void free_description() {};
+
+    //重新分配错误描述的内存,并从外部拷贝新的(深拷贝)
+    //input:p_error_description     错误描述的字符串指针,可以为NULL,
+    //input:description_length      错误描述的字符串长度,如果为0,则从p_error_description里面获取有效的长度
+    void reallocate_memory_and_copy_string(const char *p_error_description, int description_length = 0) {
+    }
+
+    //重新分配错误描述的内存,并从外部拷贝新的(深拷贝)
+    //input:error_aggregate_string     错误描述的string
+    void reallocate_memory_and_copy_string(std::string error_aggregate_string) {
+    }
+};

+ 67 - 0
tool/ip.hpp

@@ -0,0 +1,67 @@
+#pragma once
+
+#include <sys/types.h>
+#include <ifaddrs.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <iostream>
+#include <vector>
+
+class INetInfo {
+public:
+    static std::vector<std::string> getIpv4List() {
+        return getIpList();
+    }
+
+    static std::vector<std::string> getIpv6List() {
+        return getIpList(AF_INET6);
+    }
+private:
+    static std::vector<std::string> getIpList(int ipv4_6 = AF_INET) {
+        int ret_val = 0;
+        std::vector<std::string> ip_list;
+
+        struct ifaddrs * ifAddrStruct 	= NULL;
+        void * tmpAddrPtr 				= NULL;
+
+        // 1.
+        ret_val 						= getifaddrs(&ifAddrStruct);
+        if (0 != ret_val) {
+            ret_val = errno;
+            return ip_list;
+        }
+
+        // 2.
+        std::string str_ipvX;
+
+        int padress_buf_len = 0;
+        char addressBuffer[INET6_ADDRSTRLEN] = {0};
+
+        if (AF_INET6 == ipv4_6)
+            padress_buf_len = INET6_ADDRSTRLEN;
+        else
+            padress_buf_len = INET_ADDRSTRLEN;
+
+
+        while (NULL != ifAddrStruct )
+        {
+            if (ipv4_6 == ifAddrStruct->ifa_addr->sa_family )
+            {
+                // is a valid IP4 Address
+                tmpAddrPtr = &((struct sockaddr_in *)ifAddrStruct->ifa_addr)->sin_addr;
+
+                inet_ntop(ipv4_6, tmpAddrPtr, addressBuffer, padress_buf_len);
+                str_ipvX = std::string(addressBuffer);
+
+                ip_list.push_back(str_ipvX);
+
+                memset(addressBuffer, 0, padress_buf_len);
+            }
+
+            ifAddrStruct=ifAddrStruct->ifa_next;
+        }
+
+        return ip_list;
+    }
+};

+ 79 - 0
tool/load_protobuf.hpp

@@ -0,0 +1,79 @@
+#pragma once
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <iostream>
+#include <fstream>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/text_format.h>
+#include "google/protobuf/util/json_util.h"
+
+#include "tool/error_code.hpp"
+
+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;
+
+
+static std::string getFileExtension(const std::string& file_path) {
+    size_t dot_pos = file_path.find_last_of('.');
+    if (dot_pos != std::string::npos && dot_pos < file_path.length() - 1) {
+        return file_path.substr(dot_pos + 1);
+    }
+    return "";
+}
+
+static bool readTxtProtobufFile(const std::string &file_path, ::google::protobuf::Message& message)
+{
+    int fd = open(file_path.c_str(), O_RDONLY);
+    if (fd == -1) return false;
+    auto* input = new FileInputStream(fd);
+    bool success = google::protobuf::TextFormat::Parse(input, &message);
+    delete input;
+    close(fd);
+    return success;
+}
+
+static bool readJsonProtobufFile(const std::string& file_path, ::google::protobuf::Message& message) {
+    std::ifstream file(file_path);
+    std::string json_data;
+
+    if (file.is_open()) {
+        // 将文件内容读取到字符串
+        std::string line;
+        while (std::getline(file, line)) {
+            json_data += line;
+        }
+        file.close();
+    } else {
+        return false;
+    }
+
+    return google::protobuf::util::JsonStringToMessage(json_data, &message) == absl::OkStatus();
+}
+
+static Error_manager loadProtobufFile(const std::string &file, ::google::protobuf::Message &message) {
+    std::string ext = getFileExtension(file);
+    if (ext.empty()) {
+        return {PARSE_FAILED, MINOR_ERROR, "prase protobuf file %s ext failed.", file.c_str()};
+    }
+
+    bool ret = false;
+    if (ext == "prototxt") {
+        ret = readTxtProtobufFile(file, message);
+    } else if (ext == "json") {
+        ret = readJsonProtobufFile(file, message);
+    } else {
+        return {PARSE_FAILED, MINOR_ERROR, "protobuf file %s type error.", file.c_str()};
+    }
+
+    if (ret) {
+        return {SUCCESS, NORMAL, "prase protobuf file %s success.", file.c_str()};
+    } else {
+        return {PARSE_FAILED, MINOR_ERROR, "prase protobuf file %s failed.", file.c_str()};
+    }
+}

+ 43 - 0
tool/log.hpp

@@ -0,0 +1,43 @@
+#pragma once
+
+#include <glog/logging.h>
+#include "tool/pathcreator.hpp"
+
+namespace ZX{
+    static void shut_down_logging(const char *data, size_t size) {
+        time_t tt;
+        time(&tt);
+        tt = tt + 8 * 3600;  // transform the time zone
+        tm *t = gmtime(&tt);
+        char buf[255] = {0};
+        sprintf(buf, "./%d%02d%02d-%02d%02d%02d-dump.txt",
+                t->tm_year + 1900,
+                t->tm_mon + 1,
+                t->tm_mday,
+                t->tm_hour,
+                t->tm_min,
+                t->tm_sec);
+
+        FILE *tp_file = fopen(buf, "w");
+        fprintf(tp_file, data, strlen(data));
+        fclose(tp_file);
+    }
+
+// 例子: ZX::InitGlog(PROJECT_NAME, ETC_PATH PROJECT_NAME "/LogInfo/");
+    static bool InitGlog(const char * project_name, const char *logPath) {
+        PathCreator pc;
+        if (!pc.Mkdir(logPath)) {return false;}
+        google::InitGoogleLogging(project_name);
+        google::SetStderrLogging(google::INFO);
+        google::SetLogDestination(google::INFO, logPath);
+        google::SetLogFilenameExtension(".log");
+        google::InstallFailureSignalHandler();
+        google::InstallFailureWriter(&ZX::shut_down_logging);
+        FLAGS_colorlogtostderr = true;        // Set log color
+        FLAGS_logbufsecs = 0;                // Set log output speed(s)
+        FLAGS_max_log_size = 32;            // Set max log file size(MB)
+        FLAGS_stop_logging_if_full_disk = true;
+        return true;
+    }
+}
+

+ 118 - 0
tool/pathcreator.hpp

@@ -0,0 +1,118 @@
+#pragma once
+
+#include <string>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fstream>
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+
+class PathCreator
+{
+public:
+    PathCreator() = default;
+    ~PathCreator() = default;
+
+    std::string GetCurPath() {
+        return m_current_path;
+    }
+
+    bool Mkdir(std::string dirName) {
+        uint32_t beginCmpPath = 0;
+        uint32_t endCmpPath = 0;
+        std::string fullPath = "";
+
+        if('/' != dirName[0])
+        {
+            fullPath = getcwd(nullptr, 0);
+            beginCmpPath = fullPath.size();
+            fullPath = fullPath + "/" + dirName;
+        }
+        else
+        {
+            //Absolute path
+            fullPath = dirName;
+            beginCmpPath = 1;
+        }
+        if (fullPath[fullPath.size() - 1] != '/')
+        {
+            fullPath += "/";
+        }
+        endCmpPath = fullPath.size();
+
+        //create dirs;
+        for(uint32_t i = beginCmpPath; i < endCmpPath ; i++ )
+        {
+            if('/' == fullPath[i])
+            {
+                std::string curPath = fullPath.substr(0, i);
+                if(access(curPath.c_str(), F_OK) != 0)
+                {
+                    if(mkdir(curPath.c_str(), /*S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH*/0777) == -1)
+                    {
+                        printf("mkdir(%s) failed\n", curPath.c_str());
+                        return false;
+                    }
+                }
+            }
+        }
+        m_current_path=fullPath;
+        return true;
+
+    }
+
+    bool CreateDatePath(std::string root, bool add_time = true) {
+        time_t tt;
+        time( &tt );
+        tt = tt + 8*3600;  // transform the time zone
+        tm* t= gmtime( &tt );
+        char buf[255]={0};
+        if (add_time)
+        {
+            sprintf(buf, "%s/%d%02d%02d-%02d%02d%02d", root.c_str(),
+                    t->tm_year + 1900,
+                    t->tm_mon + 1,
+                    t->tm_mday,
+                    t->tm_hour,
+                    t->tm_min,
+                    t->tm_sec);
+        }
+        else
+        {
+            sprintf(buf, "%s/%d%02d%02d", root.c_str(),
+                    t->tm_year + 1900,
+                    t->tm_mon + 1,
+                    t->tm_mday);
+        }
+        return Mkdir(buf);
+    }
+
+    static bool IsPathExist(const std::string& path) {
+        if (access(path.c_str(), 0) == F_OK) {
+            return true;
+        }
+        return false;
+    }
+
+    static bool IsFile(const std::string& path) {
+        if (!IsPathExist(path)) {
+            printf("%s:%d %s not exist\n", __FILE__, __LINE__, path.c_str());
+            return false;
+        }
+        struct stat buffer;
+        return (stat(path.c_str(), &buffer) == 0 && S_ISREG(buffer.st_mode));
+    }
+
+    static bool IsFolder(const std::string& path) {
+        if (!IsPathExist(path)) {
+            return false;
+        }
+        struct stat buffer;
+        return (stat(path.c_str(), &buffer) == 0 && S_ISDIR(buffer.st_mode));
+    }
+
+protected:
+    std::string m_current_path;
+};

+ 227 - 0
tool/point2D_tool.hpp

@@ -0,0 +1,227 @@
+//
+// Created by huli on 2020/9/1.
+//
+
+#ifndef POINT2D_TOOL_H
+#define POINT2D_TOOL_H
+
+
+
+//#define OPEN_PCL_FLAG
+#ifdef OPEN_PCL_FLAG
+#include <pcl/point_types.h>
+#include <pcl/PCLPointCloud2.h>
+#include <pcl/conversions.h>
+#include <pcl/common/common.h>
+#endif
+
+#include <stddef.h>
+#include <math.h>
+
+class Point2D_tool
+{
+public:
+	//极坐标
+	struct Polar_coordinates
+	{
+		float angle=0;				//弧度
+		float distance=0;				//距离
+	};
+
+	//极坐标的限定范围
+	struct Polar_coordinates_box
+	{
+		float angle_min=0;			//角度最小值
+		float angle_max=0;			//角度最大值
+		float distance_min=0;			//距离最小值
+		float distance_max=0;			//距离最大值
+	};
+
+	//平面坐标
+	struct Point2D
+	{
+		float x=0;				//x轴
+		float y=0;				//y轴
+	};
+
+	//平面坐标的限定范围
+	struct Point2D_box
+	{
+		float x_min=0;				//x轴最小值
+		float x_max=0;				//x轴最大值
+		float y_min=0;				//y轴最小值
+		float y_max=0;				//y轴最大值
+	};
+
+	//平面坐标的转换矩阵, 可以进行旋转和平移, 不能缩放
+	struct Point2D_transform
+	{
+		float m00=1;
+		float m01=0;
+		float m02=0;
+		float m10=0;
+		float m11=1;
+		float m12=0;
+	};
+
+	//极坐标 -> 平面坐标
+	static bool Polar_coordinates_to_point2D(Point2D_tool::Polar_coordinates* p_polar_coordinates, Point2D_tool::Point2D* p_point2D);
+	//平面坐标 -> 极坐标
+	static bool Point2D_to_polar_coordinates(Point2D_tool::Point2D* p_point2D, Point2D_tool::Polar_coordinates* p_polar_coordinates);
+
+
+	//判断极坐标点是否在限制范围
+	static bool limit_with_polar_coordinates_box(float distance, float angle, Point2D_tool::Polar_coordinates_box* p_polar_coordinates_box);
+	//判断平面坐标点是否在限制范围
+	static bool limit_with_point2D_box(float x, float y, Point2D_tool::Point2D_box* p_point2D_box);
+
+#ifdef OPEN_PCL_FLAG
+	//平面坐标的转换, 可以进行旋转和平移, 不能缩放
+	static bool transform_with_translation_rotation(float* p_x_in, float* p_y_in, float* p_x_out, float* p_y_out,
+													Point2D_tool::Point2D_transform* p_point2D_transform);
+	static bool transform_with_translation_rotation(pcl::PointXYZ* p_point3D_in, pcl::PointXYZ* p_point3D_out,
+													Point2D_tool::Point2D_transform* p_point2D_transform);
+	static bool transform_with_translation_rotation(pcl::PointCloud<pcl::PointXYZ>::Ptr p_cloud_in,
+													pcl::PointCloud<pcl::PointXYZ>::Ptr p_cloud_out,
+													Point2D_tool::Point2D_transform* p_point2D_transform);
+
+#endif
+};
+
+//极坐标 -> 平面坐标
+bool Point2D_tool::Polar_coordinates_to_point2D(Point2D_tool::Polar_coordinates* p_polar_coordinates, Point2D_tool::Point2D* p_point2D)
+{
+    if ( p_polar_coordinates == NULL || p_point2D == NULL)
+    {
+        return false;
+    }
+    else
+    {
+        p_point2D->x = p_polar_coordinates->distance * cos(p_polar_coordinates->angle);
+        p_point2D->y = p_polar_coordinates->distance * sin(p_polar_coordinates->angle);
+        return true;
+    }
+    return true;
+}
+//平面坐标 -> 极坐标
+bool Point2D_tool::Point2D_to_polar_coordinates(Point2D_tool::Point2D* p_point2D, Point2D_tool::Polar_coordinates* p_polar_coordinates)
+{
+    if ( p_polar_coordinates == NULL || p_point2D == NULL)
+    {
+        return false;
+    }
+    else
+    {
+        p_polar_coordinates->distance = sqrt(p_point2D->x * p_point2D->x + p_point2D->y * p_point2D->y);
+        p_polar_coordinates->angle = atan(p_point2D->y / p_point2D->x);
+        return true;
+    }
+    return true;
+}
+
+//判断极坐标点是否在限制范围
+bool Point2D_tool::limit_with_polar_coordinates_box(float distance, float angle, Point2D_tool::Polar_coordinates_box* p_polar_coordinates_box)
+{
+    if ( p_polar_coordinates_box == NULL )
+    {
+        return false;
+    }
+    else
+    {
+        if ( angle >= p_polar_coordinates_box->angle_min &&
+             angle <= p_polar_coordinates_box->angle_max &&
+             distance >= p_polar_coordinates_box->distance_min &&
+             distance <= p_polar_coordinates_box->distance_max )
+        {
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    }
+    return true;
+}
+
+//判断平面坐标点是否在限制范围
+bool Point2D_tool::limit_with_point2D_box(float x, float y, Point2D_tool::Point2D_box* p_point2D_box)
+{
+    if ( p_point2D_box == NULL )
+    {
+        return false;
+    }
+    else
+    {
+        if ( x >= p_point2D_box->x_min &&
+             x <= p_point2D_box->x_max &&
+             y >= p_point2D_box->y_min &&
+             y <= p_point2D_box->y_max )
+        {
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    }
+    return true;
+}
+
+#ifdef OPEN_PCL_FLAG
+//平面坐标的转换, 可以进行旋转和平移, 不能缩放
+bool Point2D_tool::transform_with_translation_rotation(float* p_x_in, float* p_y_in, float* p_x_out, float* p_y_out,
+										 Point2D_tool::Point2D_transform* p_point2D_transform)
+{
+	if ( p_x_in == NULL || p_x_in == NULL || p_x_out == NULL || p_y_out == NULL || p_point2D_transform == NULL)
+	{
+		return false;
+	}
+	else
+	{
+		*p_x_out = *p_x_in * p_point2D_transform->m00 + *p_y_in * p_point2D_transform->m01 + p_point2D_transform->m02;
+		*p_y_out = *p_x_in * p_point2D_transform->m10 + *p_y_in * p_point2D_transform->m11 + p_point2D_transform->m12;
+		return true;
+	}
+	return true;
+}
+bool Point2D_tool::transform_with_translation_rotation(pcl::PointXYZ* p_point3D_in, pcl::PointXYZ* p_point3D_out,
+												Point2D_tool::Point2D_transform* p_point2D_transform)
+{
+	if (p_point3D_in == NULL || p_point3D_out == NULL || p_point2D_transform == NULL)
+	{
+		return false;
+	}
+	else
+	{
+		p_point3D_out->x = p_point3D_in->x * p_point2D_transform->m00 + p_point3D_in->y * p_point2D_transform->m01 +
+						   p_point2D_transform->m02;
+		p_point3D_out->y = p_point3D_in->x * p_point2D_transform->m10 + p_point3D_in->y * p_point2D_transform->m11 +
+						   p_point2D_transform->m12;
+		return true;
+	}
+	return true;
+}
+bool Point2D_tool::transform_with_translation_rotation(pcl::PointCloud<pcl::PointXYZ>::Ptr p_cloud_in,
+												pcl::PointCloud<pcl::PointXYZ>::Ptr p_cloud_out,
+												Point2D_tool::Point2D_transform* p_point2D_transform)
+{
+	if ( p_cloud_in.get() == NULL || p_cloud_out.get() == NULL || p_point2D_transform == NULL)
+	{
+	    return false;
+	}
+	else
+	{
+		pcl::PointXYZ point;
+		for (int i = 0; i < p_cloud_in->size(); ++i)
+		{
+			point.x = p_cloud_in->points[i].x * p_point2D_transform->m00 + p_cloud_in->points[i].y * p_point2D_transform->m01 + p_point2D_transform->m02;
+			point.y = p_cloud_in->points[i].x * p_point2D_transform->m10 + p_cloud_in->points[i].y * p_point2D_transform->m11 + p_point2D_transform->m12;
+			p_cloud_out->push_back(point);
+		}
+		return true;
+	}
+	return true;
+}
+#endif
+
+#endif //POINT2D_TOOL_H

+ 155 - 0
tool/point3D_tool.hpp

@@ -0,0 +1,155 @@
+#pragma once
+
+#include <pcl/point_cloud.h>
+#include <pcl/point_types.h>
+#include <vector>
+#include <opencv4/opencv2/opencv.hpp>
+#include "tool/log.hpp"
+
+class Point3D_tool
+{
+public:
+    static bool WriteTxtCloud(const std::string &file, pcl::PointCloud<pcl::PointXYZ>::Ptr &cloud) {
+        DLOG(INFO) << "begain push point data to " << file;
+        std::fstream fileTxtCloud;
+        fileTxtCloud.open(file, std::ios_base::trunc | std::ios::in | std::ios::out);
+        for (auto &point: *cloud) {
+            fileTxtCloud << point.x << " " << point.y << " " << point.z << std::endl;
+        }
+        fileTxtCloud.close();
+        DLOG(INFO) << "Push point complete, push point number: " << cloud->size();
+        return true;
+    }
+
+    static bool WriteTxtCloud(const std::string &file, pcl::PointCloud<pcl::PointXYZI>::Ptr &cloud) {
+        DLOG(INFO) << "begain push point data to " << file;
+        std::fstream fileTxtCloud;
+        fileTxtCloud.open(file, std::ios_base::trunc | std::ios::in | std::ios::out);
+        for (auto &point: *cloud) {
+            fileTxtCloud << point.x << " " << point.y << " " << point.z << std::endl;
+        }
+        fileTxtCloud.close();
+        DLOG(INFO) << "Push point complete, push point number: " << cloud->size();
+        return true;
+    }
+
+    static bool ReadTxtCloud(const std::string &file, pcl::PointCloud<pcl::PointXYZ>::Ptr &cloud) {
+        DLOG(INFO) << "Load point data form file: " << file;
+        std::ifstream fin(file.c_str());
+        if (fin.bad()) {
+            LOG(WARNING) << "File " << file << " don't exist.";
+            return false;
+        }
+        const int line_length = 64;
+        char str[line_length] = {0};
+        while (fin.getline(str, line_length)) {
+            pcl::PointXYZ point{};
+            if (string2PointXYZ(str, point)) {
+                cloud->push_back(point);
+            }
+        }
+        DLOG(INFO) << "Load point complete, get point number: " << cloud->size();
+        return true;
+    }
+
+	template <typename PclPoint>
+	static int getMinRect(cv::RotatedRect &rect, pcl::shared_ptr<pcl::PointCloud<PclPoint>> &clouds) {
+        if (clouds->points.size() < 4) {
+            DLOG(ERROR) << "points to little.";
+            return false;
+        }
+        std::vector<cv::Point2f> cv_clouds;
+        for (auto &point: clouds->points) {
+            cv_clouds.emplace_back(point.x, point.y);
+        }
+        rect = cv::minAreaRect(cv_clouds);
+        return true;
+    }
+
+	// template <typename PclPoint>
+	static int getRectCloud(cv::RotatedRect &rect, pcl::shared_ptr<pcl::PointCloud<pcl::PointXYZ>> &points, float z_value = 0, int numbers = 100) {
+        cv::Point2f cv_points[4];
+        rect.points(cv_points);
+        // PclPoint point;
+        pcl::PointXYZ point;
+        // 左侧点
+        double step_x = (cv_points[1].x - cv_points[0].x) / numbers;
+        double step_y = (cv_points[1].y - cv_points[0].y) / numbers;
+        for (int i = 0; i < numbers; i++) {
+            point.x = cv_points[0].x + i * step_x;
+            point.y = cv_points[0].y + i * step_y;
+            point.z = z_value;
+            points->points.emplace_back(point);
+        }
+
+        // 上侧点
+        step_x = (cv_points[2].x - cv_points[1].x) / numbers;
+        step_y = (cv_points[2].y - cv_points[1].y) / numbers;
+        for (int i = 0; i < numbers; i++) {
+            point.x = cv_points[1].x + i * step_x;
+            point.y = cv_points[1].y + i * step_y;
+            point.z = z_value;
+            points->points.emplace_back(point);
+        }
+
+        // 右侧点
+        step_x = (cv_points[3].x - cv_points[2].x) / numbers;
+        step_y = (cv_points[3].y - cv_points[2].y) / numbers;
+        for (int i = 0; i < numbers; i++) {
+            point.x = cv_points[2].x + i * step_x;
+            point.y = cv_points[2].y + i * step_y;
+            point.z = z_value;
+            points->points.emplace_back(point);
+        }
+
+
+        // 下侧点
+        step_x = (cv_points[0].x - cv_points[3].x) / numbers;
+        step_y = (cv_points[0].y - cv_points[3].y) / numbers;
+        for (int i = 0; i < numbers; i++) {
+            point.x = cv_points[3].x + i * step_x;
+            point.y = cv_points[3].y + i * step_y;
+            point.z = z_value;
+            points->points.emplace_back(point);
+        }
+
+        return points->size();
+    }
+
+	template <typename PclPoint>
+	static int getRectCloud(pcl::shared_ptr<pcl::PointCloud<PclPoint>> &clouds, pcl::shared_ptr<pcl::PointCloud<pcl::PointXYZ>> &points, float z_value = 0, int numbers = 100) {
+        cv::RotatedRect rect = getMinRect(clouds);
+        getRectCloud(rect, points, z_value, numbers);
+        return points->size();
+    }
+
+	static void getRectWidthlength(cv::RotatedRect &rect, double &width, double &length) {
+        cv::Point2f points[4];
+        rect.points(points);
+
+        double a = sqrt((points[1].x - points[0].x) * (points[1].x - points[0].x) + (points[1].y - points[0].y) * (points[1].y - points[0].y));
+        double b = sqrt((points[2].x - points[1].x) * (points[2].x - points[1].x) + (points[2].y - points[1].y) * (points[2].y - points[1].y));
+
+        width = MIN(a, b);
+        length = MAX(a, b);
+    }
+
+private:
+    static bool string2PointXYZ(const std::string &str, pcl::PointXYZ &point) {
+        std::istringstream iss;
+        iss.str(str);
+        float value;
+        float data[3];
+        for (int i = 0; i < 3; ++i) {
+            if (iss >> value) {
+                data[i] = value;
+            } else {
+                return false;
+            }
+        }
+        point.x = data[0];
+        point.y = data[1];
+        point.z = data[2];
+        return true;
+    }
+};

+ 77 - 0
tool/singleton.hpp

@@ -0,0 +1,77 @@
+
+/* Singleton 是单例类的模板。
+ * https://www.cnblogs.com/sunchaothu/p/10389842.html
+ * 单例 Singleton 是设计模式的一种,其特点是只提供唯一一个类的实例,具有全局变量的特点,在任何位置都可以通过接口获取到那个唯一实例;
+ * 全局只有一个实例:static 特性,同时禁止用户自己声明并定义实例(把构造函数设为 private 或者 protected)
+ * Singleton 模板类对这种方法进行了一层封装。
+ * 单例类需要从Singleton继承。
+ * 子类需要将自己作为模板参数T 传递给 Singleton<T> 模板;
+ * 同时需要将基类声明为友元,这样Singleton才能调用子类的私有构造函数。
+// 子类必须把父类设定为友元函数,这样父类才能使用子类的私有构造函数。
+// 父类的构造函数必须保护,子类的构造函数必须私有。
+// 必须关闭拷贝构造和赋值构造,只能通过 get_instance 函数来操作 唯一的实例。
+ * */
+
+#ifndef __SINGLIETON_H
+#define __SINGLIETON_H
+
+//#include <iostream>
+
+template<typename T>
+class Singleton {
+public:
+    //获取单例的引用
+    static T &get_instance_references() {
+        static T instance;
+        return instance;
+    }
+
+    //获取单例的指针
+    static T *get_instance_pointer() {
+        return &(get_instance_references());
+    }
+
+    virtual ~Singleton() {
+//		std::cout<<"destructor called!"<<std::endl;
+    }
+
+    Singleton(const Singleton &) = delete;                    //关闭拷贝构造函数
+    Singleton &operator=(const Singleton &) = delete;        //关闭赋值函数
+
+protected:
+    //构造函数需要是 protected,这样子类才能继承;
+    Singleton() {
+//		std::cout<<"constructor called!"<<std::endl;
+    }
+
+};
+
+/*
+// 如下是 使用样例:
+// 子类必须把父类设定为友元函数,这样父类才能使用子类的私有构造函数。
+// 父类的构造函数必须保护,子类的构造函数必须私有。
+// 必须关闭拷贝构造和赋值构造,只能通过 get_instance 函数来进行操作唯一的实例。
+
+class DerivedSingle:public Singleton<DerivedSingle>
+{
+ // 子类必须把父类设定为友元函数,这样父类才能使用子类的私有构造函数。
+	friend class Singleton<DerivedSingle>;
+public:
+ // 必须关闭拷贝构造和赋值构造,只能通过 get_instance 函数来进行操作唯一的实例。
+	DerivedSingle(const DerivedSingle&)=delete;
+	DerivedSingle& operator =(const DerivedSingle&)= delete;
+ 	~DerivedSingle()=default;
+private:
+ // 父类的构造函数必须保护,子类的构造函数必须私有。
+	DerivedSingle()=default;
+};
+
+int main(int argc, char* argv[]){
+	DerivedSingle& instance1 = DerivedSingle::get_instance_references();
+	DerivedSingle* p_instance2 = DerivedSingle::get_instance_pointer();
+	return 0;
+}
+
+ */
+
+#endif

+ 262 - 0
tool/thread_pool.hpp

@@ -0,0 +1,262 @@
+/*
+ * Thread_pool 线程池,
+ *
+ * */
+
+
+
+//例如
+// Thread_pool thread_pool(4);
+// std::future<int> x =  thread_pool.enqueue( []{return 0;} );
+// std::cout << x.get() << std::endl;
+
+//例如
+/*
+Thread_pool pool(4);
+std::vector< std::future<int> > results;
+
+for(int i = 0; i < 8; ++i) {
+results.emplace_back(
+pool.enqueue([i] {
+std::cout << "hello " << i << std::endl;
+std::this_thread::sleep_for(std::chrono::seconds(1));
+std::cout << "world " << i << std::endl;
+return i*i;
+})
+);
+}
+
+for(auto && result: results)
+std::cout << result.get() << ' ';
+std::cout << std::endl;
+return 0;
+*/
+
+#ifndef THREAD_POOL_H
+#define THREAD_POOL_H
+
+#include <vector>
+#include <queue>
+#include <memory>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <future>
+#include <functional>
+#include <stdexcept>
+
+class Thread_pool {
+public:
+	//构造函数, 会自动初始化 threads_size 数量的线程
+    Thread_pool(size_t threads_size);
+
+	//构造函数,没有初始化的,后续需要调用init才能正常使用
+	Thread_pool();
+	//初始化,初始化 threads_size 数量的线程
+	void thread_pool_init(size_t threads_size);
+
+	//反初始化
+	void thread_pool_uninit();
+
+	//往线程池添加执行任务, 之后会唤醒一个线程去执行他.
+	//input: F&& f  			函数指针(函数名)
+	//input: Args&&... args		函数的参数, 自定义
+    template<class F, class... Args>
+    auto enqueue(F&& f, Args&&... args) 
+        -> std::future<typename std::result_of<F(Args...)>::type>;
+
+
+    ~Thread_pool();
+
+	//判断线程池是否超载
+    bool thread_is_full_load();
+
+private:
+    // 线程数组
+    std::vector< std::thread > workers;
+	//每个线程的工作状态, true:线程正在执行任务,  false:线程空闲等待
+	std::vector< bool > working_flag_vector;
+    // 任务函数 队列, 里面存入的是任务函数的指针
+    std::queue< std::function<void()> > tasks;
+    
+    // 线程锁和条件变量
+    std::mutex queue_mutex;
+    std::condition_variable condition;
+
+    //终止标志位
+    bool stop;
+};
+
+//构造函数, 会自动初始化 threads_size 数量的线程
+inline Thread_pool::Thread_pool(size_t threads_size)
+    :   stop(false)
+{
+	//每个线程的工作状态
+	for(size_t i = 0;i<threads_size;++i)
+	{
+		working_flag_vector.push_back(false);
+	}
+
+	//初始化 threads_size 数量的线程
+    for(size_t i = 0;i<threads_size;++i)
+        workers.emplace_back(
+            [i,this]   //每个线程的执行的基本函数,
+            {
+                for(;;)
+                {
+                    std::function<void()> task;
+
+					{
+						std::unique_lock<std::mutex> lock(this->queue_mutex);
+						this->working_flag_vector[i] = false;//线程等待
+						this->condition.wait(lock,
+											 [this]    //线程等待的判断函数
+											 { return this->stop || !this->tasks.empty(); });
+						if (this->stop )//&& this->tasks.empty()) //这里修改了, 不需要把任务池都执行完才退出, stop之后就可以退了.
+						{
+							return;//只有在终止标志位true, 那么就退出线程执行函数
+						}
+						this->working_flag_vector[i] = true;//线程工作
+						//从 任务池 里面取出 执行函数
+						task = std::move(this->tasks.front());
+						this->tasks.pop();
+					}
+
+					//运行执行函数
+                    task();
+                }
+            }
+        );
+}
+
+
+//构造函数,没有初始化的,后续需要调用init才能正常使用
+inline Thread_pool::Thread_pool()
+:   stop(false)
+{
+
+}
+//初始化,初始化 threads_size 数量的线程
+inline void Thread_pool::thread_pool_init(size_t threads_size)
+{
+	stop = false;
+
+	//每个线程的工作状态
+	for(size_t i = 0;i<threads_size;++i)
+	{
+		working_flag_vector.push_back(false);
+	}
+
+	//初始化 threads_size 数量的线程
+	for(size_t i = 0;i<threads_size;++i)
+		workers.emplace_back(
+		[i,this]   //每个线程的执行的基本函数,
+		{
+			for(;;)
+			{
+				std::function<void()> task;
+
+				{
+					std::unique_lock<std::mutex> lock(this->queue_mutex);
+					this->working_flag_vector[i] = false;//线程等待
+					this->condition.wait(lock,
+										 [this]    //线程等待的判断函数
+										 { return this->stop || !this->tasks.empty(); });
+					if (this->stop )//&& this->tasks.empty()) //这里修改了, 不需要把任务池都执行完才退出, stop之后就可以退了.
+					{
+						return;//只有在终止标志位true, 那么就退出线程执行函数
+					}
+					this->working_flag_vector[i] = true;//线程工作
+					//从 任务池 里面取出 执行函数
+					task = std::move(this->tasks.front());
+					this->tasks.pop();
+				}
+
+				//运行执行函数
+				task();
+			}
+		}
+		);
+}
+
+//反初始化
+inline void Thread_pool::thread_pool_uninit()
+{
+	{
+		std::unique_lock<std::mutex> lock(queue_mutex);
+		stop = true;
+	}
+	condition.notify_all();
+
+	for (auto iter = workers.begin(); iter != workers.end(); )
+	{
+		iter->join();
+		iter = workers.erase(iter);
+	}
+	working_flag_vector.clear();
+}
+
+
+//往线程池添加执行任务, 之后会唤醒一个线程去执行他.
+//input: F&& f  			函数指针(函数名)
+//input: Args&&... args		函数的参数, 自定义
+//注注注注注意了:::::  res是enqueue的返回值, 由于线程异步, 使用future, 可以返回未来的一个值,
+// 在子线程执行完成之后, 将结果返回给外部主线程
+// 外部主线程 调用时, 必须使用 std::future<return_type> 格式去接受
+template<class F, class... Args>
+auto Thread_pool::enqueue(F&& f, Args&&... args) 
+    -> std::future<typename std::result_of<F(Args...)>::type>
+{
+    using return_type = typename std::result_of<F(Args...)>::type;
+
+    auto task = std::make_shared< std::packaged_task<return_type()> >(
+            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
+        );
+
+
+    std::future<return_type> res = task->get_future();
+    {
+        std::unique_lock<std::mutex> lock(queue_mutex);
+
+        // don't allow enqueueing after stopping the pool
+        if(stop)
+            throw std::runtime_error("enqueue on stopped Thread_pool");
+
+        tasks.emplace([task](){ (*task)(); });
+    }
+    condition.notify_one();
+    return res;
+}
+
+// the destructor joins all threads
+inline Thread_pool::~Thread_pool()
+{
+    {
+        std::unique_lock<std::mutex> lock(queue_mutex);
+        stop = true;
+    }
+    condition.notify_all();
+    for(std::thread &worker: workers)
+	{  worker.join();}
+
+
+}
+
+//判断线程池是否超载
+inline bool Thread_pool::thread_is_full_load()
+{
+	//只要有一个线程wait, 那么就认为没有超载,
+	std::unique_lock<std::mutex> lock(queue_mutex);
+	bool result = true;
+	for(bool t_working_flag: working_flag_vector)
+	{
+		if ( !t_working_flag )
+		{
+			result = false;
+		}
+	}
+	return result;
+}
+
+
+#endif

+ 312 - 0
tool/thread_safe_list.hpp

@@ -0,0 +1,312 @@
+
+
+/*
+ * (1)这个实现要求构建工具支持C++11的atomic mutex condition_veriable功能。这是C++11的基础特性,一般2011年以后的C++编译器都能支持。 例如,visual studio 2012以上。
+
+(2)这个类的实现中有两处使用了unique_lock而不是lock_guard,这是data_cond.wait所需要的,unique_lock是lock_guard的增强版。
+
+通过std::move的使用(前提是我们实现的类型T定义了移动构造函数和移动赋值函数),能利用移动语义带来的性能优势。
+
+使用shared_ptr<T>返回元素,用户无需释放元素的内存。
+
+
+原文链接:https://blog.csdn.net/weixin_41855721/article/details/81703818
+ 增加了一些功能函数,
+ 补充了注释说明
+
+ termination_list
+ 	// 在退出状态下,所有的功能函数不可用,返回false或者null。
+	// wait_and_pop不会阻塞。让其直接通过,通过后直接return,不允许做其他的。
+
+ pop系列函数
+	//(1)没有调用termination时,每调用一次出队一个元素,直到队列为空本方法阻塞线程。
+	//(2)在调用了termination后,本方法永不阻塞,如果原本已经处于阻塞状态,解除阻塞状态。
+	//(3)返回true时,value值有效。返回false时,value值无效。调用了termination且队列为空时返回false.
+
+注注注注注意了:模板类不支持分离编译。 模板类的实现必须放在头文件
+ 为了方便阅读和编程规范,依然将声明和实现分开,就像是把cpp文件的代码复制到h文件的尾部。
+
+ 如果将实现放到cpp里面,那么就要为cpp文件加 ifndef define endif 防止重定义。
+ 然后在调用方include包含cpp文件,但是这样不好。
+
+
+ thread_safe_queue  就是在 Thread_safe_queue 的基础上修改的,
+
+ * */
+
+#ifndef __THREAD_SAFE_LIST_H__
+#define __THREAD_SAFE_LIST_H__
+
+#include <list>
+
+#include <atomic>
+#include <mutex>
+#include <condition_variable>
+
+template<class T>
+class Thread_safe_list {
+public:
+    Thread_safe_list();
+
+    Thread_safe_list(const Thread_safe_list &other);
+
+    ~Thread_safe_list();
+
+    //(1)没有调用termination时,每调用一次出队一个元素,直到队列为空本方法阻塞线程。
+    //(2)在调用了termination后,本方法永不阻塞,如果原本已经处于阻塞状态,解除阻塞状态。
+    //(3)返回true时,value值有效。返回false时,value值无效。调用了termination且队列为空时返回false.
+
+    //等待并弹出数据,成功弹出则返回true
+    // 队列为空则无限等待,termination终止队列,则返回false
+    bool wait_and_pop(T &value);
+
+    //尝试弹出数据,成功弹出则返回true
+    //队列为空 或者 termination终止队列,返回false
+    bool try_pop(T &value);
+
+    //等待并弹出数据,成功弹出则返回true
+    // 队列为空则无限等待,termination终止队列,则返回false
+    std::shared_ptr<T> wait_and_pop();
+
+    //尝试弹出数据,成功弹出则返回true
+    //队列为空 或者 termination终止队列,返回false
+    std::shared_ptr<T> try_pop();
+
+    //插入一项,并唤醒一个线程,
+    //如果成功插入,则返回true,  失败则返回false
+    //注:只能唤醒一个线程,防止多线程误判empty()
+    bool push(T new_value);
+
+    //清除队列,只是将队列的实例抛出。T是实例内存,系统自动回收的。
+    bool clear();
+
+    //清除队列,抛出之后还要delete指针。T是动态内存,需要手动回收的。
+    bool clear_and_delete();
+
+public:
+    //判空
+    bool empty();
+
+    //获取队列大小
+    size_t size();
+
+    //设置队列为退出状态。并唤醒所有的线程,使其通过wait
+    // 在退出状态下,所有的功能函数不可用,必须直接返回false或者null。
+    // wait_and_pop不会阻塞。让其直接通过,通过后直接return,不允许做其他的。
+    void termination_list();
+
+    //唤醒队列,恢复所有的功能函数。wait_and_pop会继续阻塞。
+    void wake_list();
+
+    //获取退出状态
+    bool get_termination_flag();
+
+    //判断是否可以直接通过wait,  m_data_list不为空或者m_termination终止时都可以通过等待。
+    bool is_pass();
+
+public:
+    std::mutex m_mutex;                //队列的锁
+    std::list<std::shared_ptr<T>> m_data_list;            //队列数据,使用智能指针shared_ptr
+    std::condition_variable m_data_cond;            //条件变量
+    std::atomic<bool> m_termination_flag;        //终止标志位
+
+private:
+
+
+};
+
+
+template<class T>
+Thread_safe_list<T>::Thread_safe_list() {
+    m_termination_flag = false;
+}
+
+template<class T>
+Thread_safe_list<T>::Thread_safe_list(const Thread_safe_list &other) {
+    std::unique_lock<std::mutex> lock_this(m_mutex);
+    std::unique_lock<std::mutex> lock_other(other.m_mutex);
+    m_data_list = other.data_list;
+    m_termination_flag = other.m_termination_flag;
+}
+
+template<class T>
+Thread_safe_list<T>::~Thread_safe_list() {
+    //析构时,终止队列,让线程通过等待,方便线程推出。
+    termination_list();
+}
+
+//(1)没有调用termination时,每调用一次出队一个元素,直到队列为空本方法阻塞线程。
+//(2)在调用了termination后,本方法永不阻塞,如果原本已经处于阻塞状态,解除阻塞状态。
+//(3)返回true时,value值有效。返回false时,value值无效。调用了termination且队列为空时返回false.
+
+//等待并弹出数据,成功弹出则返回true
+// 队列为空则无限等待,termination终止队列,则返回false
+template<class T>
+bool Thread_safe_list<T>::wait_and_pop(T &value) {
+    if (m_termination_flag) {
+        return false;
+    } else {
+        std::unique_lock<std::mutex> lk(m_mutex);
+        //无限等待,一直阻塞,除非有新的数据加入或者终止队列
+        m_data_cond.wait(lk, [this] { return ((!m_data_list.empty()) || m_termination_flag); });
+        if (m_termination_flag) {
+            return false;
+        } else {
+            value = std::move(*m_data_list.front());
+
+            m_data_list.pop_front();
+            return true;
+        }
+    }
+}
+
+//尝试弹出数据,成功弹出则返回true
+//队列为空 或者 termination终止队列,返回false
+template<class T>
+bool Thread_safe_list<T>::try_pop(T &value) {
+    if (m_termination_flag) {
+        return false;
+    } else {
+        std::unique_lock<std::mutex> lk(m_mutex);
+        if (m_data_list.empty()) {
+            return false;
+        } else {
+            value = std::move(*m_data_list.front());
+
+            m_data_list.pop();
+            return true;
+        }
+    }
+}
+
+
+//等待并弹出数据,成功弹出则返回true
+// 队列为空则无限等待,termination终止队列,则返回false
+template<class T>
+std::shared_ptr<T> Thread_safe_list<T>::wait_and_pop() {
+    if (m_termination_flag) {
+        return NULL;
+    } else {
+        std::unique_lock<std::mutex> lk(m_mutex);
+        //无限等待,一直阻塞,除非有新的数据加入或者终止队列
+        m_data_cond.wait(lk, [this] { return ((!m_data_list.empty()) || m_termination_flag); });
+        if (m_termination_flag) {
+            return NULL;
+        } else {
+            std::shared_ptr<T> res = m_data_list.front();
+            m_data_list.pop();
+            return res;
+        }
+    }
+}
+
+//尝试弹出数据,成功弹出则返回true
+//队列为空 或者 termination终止队列,返回false
+template<class T>
+std::shared_ptr<T> Thread_safe_list<T>::try_pop() {
+    if (m_termination_flag) {
+        return NULL;
+    } else {
+        std::unique_lock<std::mutex> lk(m_mutex);
+        if (m_data_list.empty()) {
+            return NULL;
+        } else {
+            std::shared_ptr<T> res = m_data_list.front();
+            m_data_list.pop();
+            return res;
+        }
+    }
+}
+
+//插入一项,并唤醒一个线程,
+//如果成功插入,则返回true,  失败则返回false
+//注:只能唤醒一个线程,防止多线程误判empty()
+template<class T>
+bool Thread_safe_list<T>::push(T new_value) {
+    if (m_termination_flag) {
+        return false;
+    } else {
+        std::shared_ptr<T> data(std::make_shared<T>(std::move(new_value)));
+        std::unique_lock<std::mutex> lk(m_mutex);
+        m_data_list.push_back(data);
+        m_data_cond.notify_one();
+        return true;
+    }
+}
+
+//清除队列,只是将队列的实例抛出。T是实例内存,系统自动回收的。
+template<class T>
+bool Thread_safe_list<T>::clear() {
+    std::unique_lock<std::mutex> lk(m_mutex);
+    while (!m_data_list.empty()) {
+        m_data_list.pop_front();
+    }
+    return true;
+
+}
+
+//清除队列,抛出之后还要delete指针。T是动态内存,需要手动回收的。
+template<class T>
+bool Thread_safe_list<T>::clear_and_delete() {
+
+    std::unique_lock<std::mutex> lk(m_mutex);
+    while (!m_data_list.empty()) {
+        T res = NULL;
+        res = std::move(*m_data_list.front());
+
+        m_data_list.pop_front();
+        if (res != NULL) {
+            delete (res);
+
+        }
+    }
+    return true;
+}
+
+
+//判空
+template<class T>
+bool Thread_safe_list<T>::empty() {
+    std::unique_lock<std::mutex> lk(m_mutex);
+    return m_data_list.empty();
+}
+
+//获取队列大小
+template<class T>
+size_t Thread_safe_list<T>::size() {
+    std::unique_lock<std::mutex> lk(m_mutex);
+    return m_data_list.size();
+}
+
+//设置队列为退出状态。并唤醒所有的线程,使其通过wait
+// 在退出状态下,所有的功能函数不可用,必须直接返回false或者null。
+// wait_and_pop不会阻塞。让其直接通过,通过后直接return,不允许做其他的。
+template<class T>
+void Thread_safe_list<T>::termination_list() {
+    std::unique_lock<std::mutex> lk(m_mutex);
+    m_termination_flag = true;
+    m_data_cond.notify_all();
+}
+
+//唤醒队列,恢复所有的功能函数。wait_and_pop会继续阻塞。
+template<class T>
+void Thread_safe_list<T>::wake_list() {
+    std::unique_lock<std::mutex> lk(m_mutex);
+    m_termination_flag = false;
+    m_data_cond.notify_all();
+}
+
+//获取退出状态
+template<class T>
+bool Thread_safe_list<T>::get_termination_flag() {
+    return m_termination_flag;
+}
+
+//判断是否可以直接通过wait,  m_data_list不为空或者m_termination终止时都可以通过等待。
+template<class T>
+bool Thread_safe_list<T>::is_pass() {
+    return (!m_data_list.empty() || m_termination_flag);
+}
+
+
+#endif //__THREAD_SAFE_LIST_H__

+ 305 - 0
tool/thread_safe_queue.hpp

@@ -0,0 +1,305 @@
+
+
+/*
+ * (1)这个实现要求构建工具支持C++11的atomic mutex condition_veriable功能。这是C++11的基础特性,一般2011年以后的C++编译器都能支持。 例如,visual studio 2012以上。
+
+(2)这个类的实现中有两处使用了unique_lock而不是lock_guard,这是data_cond.wait所需要的,unique_lock是lock_guard的增强版。
+
+通过std::move的使用(前提是我们实现的类型T定义了移动构造函数和移动赋值函数),能利用移动语义带来的性能优势。
+
+使用shared_ptr<T>返回元素,用户无需释放元素的内存。
+
+
+原文链接:https://blog.csdn.net/weixin_41855721/article/details/81703818
+ 增加了一些功能函数,
+ 补充了注释说明
+
+ termination_queue
+ 	// 在退出状态下,所有的功能函数不可用,返回false或者null。
+	// wait_and_pop不会阻塞。让其直接通过,通过后直接return,不允许做其他的。
+
+ pop系列函数
+	//(1)没有调用termination时,每调用一次出队一个元素,直到队列为空本方法阻塞线程。
+	//(2)在调用了termination后,本方法永不阻塞,如果原本已经处于阻塞状态,解除阻塞状态。
+	//(3)返回true时,value值有效。返回false时,value值无效。调用了termination且队列为空时返回false.
+
+注注注注注意了:模板类不支持分离编译。 模板类的实现必须放在头文件
+ 为了方便阅读和编程规范,依然将声明和实现分开,就像是把cpp文件的代码复制到h文件的尾部。
+
+ 如果将实现放到cpp里面,那么就要为cpp文件加 ifndef define endif 防止重定义。
+ 然后在调用方include包含cpp文件,但是这样不好。
+
+
+ * */
+
+#ifndef LIDARMEASURE_THREAD_SAFE_QUEUE_H
+#define LIDARMEASURE_THREAD_SAFE_QUEUE_H
+
+#include <queue>
+
+#include <atomic>
+#include <mutex>
+#include <condition_variable>
+
+template<class T>
+class Thread_safe_queue {
+public:
+    Thread_safe_queue();
+
+    Thread_safe_queue(const Thread_safe_queue &other);
+
+    ~Thread_safe_queue();
+
+    //(1)没有调用termination时,每调用一次出队一个元素,直到队列为空本方法阻塞线程。
+    //(2)在调用了termination后,本方法永不阻塞,如果原本已经处于阻塞状态,解除阻塞状态。
+    //(3)返回true时,value值有效。返回false时,value值无效。调用了termination且队列为空时返回false.
+
+    //等待并弹出数据,成功弹出则返回true
+    // 队列为空则无限等待,termination终止队列,则返回false
+    bool wait_and_pop(T &value);
+
+    //尝试弹出数据,成功弹出则返回true
+    //队列为空 或者 termination终止队列,返回false
+    bool try_pop(T &value);
+
+    //等待并弹出数据,成功弹出则返回true
+    // 队列为空则无限等待,termination终止队列,则返回false
+    std::shared_ptr<T> wait_and_pop();
+
+    //尝试弹出数据,成功弹出则返回true
+    //队列为空 或者 termination终止队列,返回false
+    std::shared_ptr<T> try_pop();
+
+    //插入一项,并唤醒一个线程,
+    //如果成功插入,则返回true,  失败则返回false
+    //注:只能唤醒一个线程,防止多线程误判empty()
+    bool push(T new_value);
+
+    //清除队列,只是将队列的实例抛出。T是实例内存,系统自动回收的。
+    bool clear();
+
+    //清除队列,抛出之后还要delete指针。T是动态内存,需要手动回收的。
+    bool clear_and_delete();
+
+public:
+    //判空
+    bool empty();
+
+    //获取队列大小
+    size_t size();
+
+    //设置队列为退出状态。并唤醒所有的线程,使其通过wait
+    // 在退出状态下,所有的功能函数不可用,必须直接返回false或者null。
+    // wait_and_pop不会阻塞。让其直接通过,通过后直接return,不允许做其他的。
+    void termination_queue();
+
+    //唤醒队列,恢复所有的功能函数。wait_and_pop会继续阻塞。
+    void wake_queue();
+
+    //获取退出状态
+    bool get_termination_flag();
+
+    //判断是否可以直接通过wait,  m_data_queue不为空或者m_termination终止时都可以通过等待。
+    bool is_pass();
+
+protected:
+    std::mutex m_mutex;                //队列的锁
+    std::queue<std::shared_ptr<T>> m_data_queue;            //队列数据,使用智能指针shared_ptr
+    std::condition_variable m_data_cond;            //条件变量
+    std::atomic<bool> m_termination_flag;        //终止标志位
+
+private:
+
+
+};
+
+
+template<class T>
+Thread_safe_queue<T>::Thread_safe_queue() {
+    m_termination_flag = false;
+}
+
+template<class T>
+Thread_safe_queue<T>::Thread_safe_queue(const Thread_safe_queue &other) {
+    std::unique_lock<std::mutex> lock_this(m_mutex);
+    std::unique_lock<std::mutex> lock_other(other.m_mutex);
+    m_data_queue = other.data_queue;
+    m_termination_flag = other.m_termination_flag;
+}
+
+template<class T>
+Thread_safe_queue<T>::~Thread_safe_queue() {
+    //析构时,终止队列,让线程通过等待,方便线程推出。
+    termination_queue();
+}
+
+//(1)没有调用termination时,每调用一次出队一个元素,直到队列为空本方法阻塞线程。
+//(2)在调用了termination后,本方法永不阻塞,如果原本已经处于阻塞状态,解除阻塞状态。
+//(3)返回true时,value值有效。返回false时,value值无效。调用了termination且队列为空时返回false.
+
+//等待并弹出数据,成功弹出则返回true
+// 队列为空则无限等待,termination终止队列,则返回false
+template<class T>
+bool Thread_safe_queue<T>::wait_and_pop(T &value) {
+    if (m_termination_flag) {
+        return false;
+    } else {
+        std::unique_lock<std::mutex> lk(m_mutex);
+        //无限等待,一直阻塞,除非有新的数据加入或者终止队列
+        m_data_cond.wait(lk, [this] { return ((!m_data_queue.empty()) || m_termination_flag); });
+        if (m_termination_flag) {
+            return false;
+        } else {
+            value = std::move(*m_data_queue.front());
+            m_data_queue.pop();
+            return true;
+        }
+    }
+}
+
+//尝试弹出数据,成功弹出则返回true
+//队列为空 或者 termination终止队列,返回false
+template<class T>
+bool Thread_safe_queue<T>::try_pop(T &value) {
+    if (m_termination_flag) {
+        return false;
+    } else {
+        std::unique_lock<std::mutex> lk(m_mutex);
+        if (m_data_queue.empty()) {
+            return false;
+        } else {
+            value = std::move(*m_data_queue.front());
+            m_data_queue.pop();
+            return true;
+        }
+    }
+}
+
+//等待并弹出数据,成功弹出则返回true
+// 队列为空则无限等待,termination终止队列,则返回false
+template<class T>
+std::shared_ptr<T> Thread_safe_queue<T>::wait_and_pop() {
+    if (m_termination_flag) {
+        return NULL;
+    } else {
+        std::unique_lock<std::mutex> lk(m_mutex);
+        //无限等待,一直阻塞,除非有新的数据加入或者终止队列
+        m_data_cond.wait(lk, [this] { return ((!m_data_queue.empty()) || m_termination_flag); });
+        if (m_termination_flag) {
+            return NULL;
+        } else {
+            std::shared_ptr<T> res = m_data_queue.front();
+            m_data_queue.pop();
+            return res;
+        }
+    }
+}
+
+//尝试弹出数据,成功弹出则返回true
+//队列为空 或者 termination终止队列,返回false
+template<class T>
+std::shared_ptr<T> Thread_safe_queue<T>::try_pop() {
+    if (m_termination_flag) {
+        return NULL;
+    } else {
+        std::unique_lock<std::mutex> lk(m_mutex);
+        if (m_data_queue.empty()) {
+            return NULL;
+        } else {
+            std::shared_ptr<T> res = m_data_queue.front();
+            m_data_queue.pop();
+            return res;
+        }
+    }
+}
+
+//插入一项,并唤醒一个线程,
+//如果成功插入,则返回true,  失败则返回false
+//注:只能唤醒一个线程,防止多线程误判empty()
+template<class T>
+bool Thread_safe_queue<T>::push(T new_value) {
+    if (m_termination_flag) {
+        return false;
+    } else {
+        std::shared_ptr<T> data(std::make_shared<T>(std::move(new_value)));
+        std::unique_lock<std::mutex> lk(m_mutex);
+        m_data_queue.push(data);
+        m_data_cond.notify_one();
+        return true;
+    }
+}
+
+//清除队列,只是将队列的实例抛出。T是实例内存,系统自动回收的。
+template<class T>
+bool Thread_safe_queue<T>::clear() {
+    std::unique_lock<std::mutex> lk(m_mutex);
+    while (!m_data_queue.empty()) {
+        m_data_queue.pop();
+    }
+    return true;
+
+}
+
+//清除队列,抛出之后还要delete指针。T是动态内存,需要手动回收的。
+template<class T>
+bool Thread_safe_queue<T>::clear_and_delete() {
+    std::unique_lock<std::mutex> lk(m_mutex);
+    while (!m_data_queue.empty()) {
+        T res = NULL;
+        res = std::move(*m_data_queue.front());
+        m_data_queue.pop();
+        if (res != NULL) {
+            delete (res);
+
+        }
+    }
+    return true;
+
+}
+
+//判空
+template<class T>
+bool Thread_safe_queue<T>::empty() {
+    std::unique_lock<std::mutex> lk(m_mutex);
+    return m_data_queue.empty();
+}
+
+//获取队列大小
+template<class T>
+size_t Thread_safe_queue<T>::size() {
+    std::unique_lock<std::mutex> lk(m_mutex);
+    return m_data_queue.size();
+}
+
+//设置队列为退出状态。并唤醒所有的线程,使其通过wait
+// 在退出状态下,所有的功能函数不可用,必须直接返回false或者null。
+// wait_and_pop不会阻塞。让其直接通过,通过后直接return,不允许做其他的。
+template<class T>
+void Thread_safe_queue<T>::termination_queue() {
+    std::unique_lock<std::mutex> lk(m_mutex);
+    m_termination_flag = true;
+    m_data_cond.notify_all();
+}
+
+//唤醒队列,恢复所有的功能函数。wait_and_pop会继续阻塞。
+template<class T>
+void Thread_safe_queue<T>::wake_queue() {
+    std::unique_lock<std::mutex> lk(m_mutex);
+    m_termination_flag = false;
+    m_data_cond.notify_all();
+}
+
+//获取退出状态
+template<class T>
+bool Thread_safe_queue<T>::get_termination_flag() {
+    return m_termination_flag;
+}
+
+//判断是否可以直接通过wait,  m_data_queue不为空或者m_termination终止时都可以通过等待。
+template<class T>
+bool Thread_safe_queue<T>::is_pass() {
+    return (!m_data_queue.empty() || m_termination_flag);
+}
+
+
+#endif //LIDARMEASURE_THREAD_SAFE_QUEUE_H

+ 62 - 0
tool/time.hpp

@@ -0,0 +1,62 @@
+#pragma once
+
+#include <chrono>
+#include <string>
+#include <iostream>
+#include <iomanip>
+
+class TimeTool {
+public:
+    enum TemporalPrecision {
+        TemporalPrecisionYear = 0,
+        TemporalPrecisionMount = 4,
+        TemporalPrecisionDay = 6,
+        TemporalPrecisionHour = 8,
+        TemporalPrecisionMin = 10,
+        TemporalPrecisionSec = 12,
+        TemporalPrecisionMs = 15,
+    };
+public:
+    static std::string timeDropoutStr(TemporalPrecision begain = TemporalPrecisionMs, TemporalPrecision end = TemporalPrecisionYear) {
+        std::stringstream time;
+        std::string t = getTime();
+        int max = begain;
+        int min = end;
+        if (begain == TemporalPrecisionYear) {
+            max += 3;
+        } else if (begain == TemporalPrecisionMs) {
+            max += 2;
+        } else {
+            max +=1;
+        }
+        for (int i = min; i < max; i++) {
+            time << t[i];
+        }
+        t.clear();
+        time >> t;
+        return t;
+    }
+
+    static unsigned long long timeDropoutLL(TemporalPrecision begain = TemporalPrecisionMs, TemporalPrecision end = TemporalPrecisionYear) {
+        std::string str = timeDropoutStr(begain, end);
+        if (str.empty()) {
+            return 0;
+        }
+        unsigned long long time_dropout = std::stoull(str);
+        return time_dropout;
+    }
+
+private:
+    static std::string getTime() {
+        auto now = std::chrono::system_clock::now();
+        auto timet = std::chrono::system_clock::to_time_t(now);
+        auto localTime = *std::gmtime(&timet);
+        auto ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now).time_since_epoch().count() % 1000;
+        std::stringstream ss;
+        std::string str;
+        ss << std::put_time(&localTime, "%Y%m%d%H%M%S") << std::setfill('0') << std::setw(3) << ms;
+        ss >> str;
+        return str;
+    }
+};
+

+ 55 - 0
tool/timedlockdata.hpp

@@ -0,0 +1,55 @@
+//
+// Created by zx on 22-12-1.
+//
+
+#pragma once
+#include <chrono>
+#include <mutex>
+
+template <typename T>
+class TimedLockData
+{
+ public:
+    TimedLockData();
+    TimedLockData<T>& operator=(const T& data);
+    bool timeout(const double &timeout = 0.2);
+    T operator()(void);
+
+ protected:
+    T data_;
+    std::chrono::steady_clock::time_point tp_;
+    std::mutex mutex_;
+
+};
+
+template <typename T>
+TimedLockData<T>::TimedLockData()
+{
+}
+
+
+template <typename T>
+TimedLockData<T>& TimedLockData<T>::operator=(const T& data){
+  std::lock_guard<std::mutex> lck (mutex_);
+  data_=data;
+  tp_=std::chrono::steady_clock::now();
+  return *this;
+}
+
+template <typename T>
+bool TimedLockData<T>::timeout(const double &timeout)
+{
+  auto now=std::chrono::steady_clock::now();
+  auto duration = std::chrono::duration_cast<std::chrono::microseconds>(now - tp_);
+  double time = double(duration.count()) * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den;
+  return time>timeout;
+}
+
+template <typename T>
+T TimedLockData<T>::operator()(void)
+{
+  std::lock_guard<std::mutex> lck (mutex_);
+  return data_;
+}
+
+

+ 90 - 0
tool/trans_coor.hpp

@@ -0,0 +1,90 @@
+#pragma once
+#include <math.h>
+#include <pcl/point_cloud.h>
+#include <pcl/point_types.h>
+
+//template <typename T>
+class CoordinateTransformation {
+public:
+    struct Point2D {
+        float x;
+        float y;
+    };
+
+    struct transInfo2D {
+        Point2D move;
+        float angle;
+    };
+
+    struct transInfo3D {
+        pcl::PointXYZ move;
+        float roll;
+        float pitch;
+        float yaw;
+
+        transInfo3D() = default;
+        transInfo3D(pcl::PointXYZ move_, float roll_, float pitch_, float yaw_): move(move_), roll(roll_), pitch(pitch_) {}
+    };
+
+    CoordinateTransformation() = default;
+    ~CoordinateTransformation() = default;
+
+    void setTranInfo(const transInfo2D &trans, bool radian = false) {
+
+    }
+
+    void setTranInfo(const transInfo3D &trans, bool radian = false) {
+        trans3D = true;
+        float cosYaw, sinYaw, cosPitch, sinPitch, cosRoll, sinRoll;
+        if (!radian) {
+            cosYaw = cos(trans.yaw * float(M_PI) / 180.0f);
+            sinYaw = sin(trans.yaw * float(M_PI) / 180.0f);
+            cosPitch = cos(trans.pitch * float(M_PI) / 180.0f);
+            sinPitch = sin(trans.pitch * float(M_PI) / 180.0f);
+            cosRoll = cos(trans.roll * float(M_PI) / 180.0f);
+            sinRoll = sin(trans.roll * float(M_PI) / 180.0f);
+        } else {
+            cosYaw = cos(trans.yaw);
+            sinYaw = sin(trans.yaw);
+            cosPitch = cos(trans.pitch);
+            sinPitch = sin(trans.pitch);
+            cosRoll = cos(trans.roll);
+            sinRoll = sin(trans.roll);
+        }
+        trans3D_move = trans.move;
+        rotationMatrix3D[0][0] = cosYaw * cosPitch;
+        rotationMatrix3D[0][1] = cosYaw * sinPitch * sinRoll - sinYaw * cosRoll;
+        rotationMatrix3D[0][2] = cosYaw * sinPitch * cosRoll + sinYaw * sinRoll;
+        rotationMatrix3D[1][0] = sinYaw * cosPitch;
+        rotationMatrix3D[1][1] = sinYaw * sinPitch * sinRoll + cosYaw * cosRoll;
+        rotationMatrix3D[1][2] = sinYaw * sinPitch * cosRoll - cosYaw * sinRoll;
+        rotationMatrix3D[2][0] = -sinPitch;
+        rotationMatrix3D[2][1] = cosPitch * sinRoll;
+        rotationMatrix3D[2][2] = cosPitch * cosRoll;
+    }
+
+    void transPoint(const Point2D &in, Point2D &out) {
+
+    }
+
+    void transPoint(const pcl::PointXYZ &in, pcl::PointXYZ &out) {
+        trans3DPoint(in, out);
+    }
+private:
+    bool trans3DPoint(const pcl::PointXYZ &in, pcl::PointXYZ &out) {
+        if (trans3D) {
+            out.x = rotationMatrix3D[0][0] * in.x + rotationMatrix3D[0][1] * in.y + rotationMatrix3D[0][2] * in.z + trans3D_move.x;
+            out.y = rotationMatrix3D[1][0] * in.x + rotationMatrix3D[1][1] * in.y + rotationMatrix3D[1][2] * in.z + trans3D_move.y;
+            out.z = rotationMatrix3D[2][0] * in.x + rotationMatrix3D[2][1] * in.y + rotationMatrix3D[2][2] * in.z + trans3D_move.z;
+        }
+        return trans3D;
+    }
+
+private:
+    bool trans2D = false;
+    bool trans3D = false;
+    Point2D trans2D_move{};
+    pcl::PointXYZ trans3D_move{0, 0, 0};
+    float rotationMatrix2D[1] = {0};
+    float rotationMatrix3D[3][3] = {0};
+};