Selaa lähdekoodia

添加plc通信

LiuZe 1 vuosi sitten
vanhempi
commit
87f1eba385

+ 34 - 0
CMakeLists.txt

@@ -13,10 +13,38 @@ execute_process(COMMAND bash ${PROJECT_SOURCE_DIR}/proto.sh ${PROJECT_SOURCE_DIR
 
 add_definitions(-DETC_PATH="${PROJECT_SOURCE_DIR}")
 
+
 find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
 find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
 find_package(Protobuf REQUIRED)
 
+include_directories(
+    /usr/local/include/snap7
+    ${PROJECT_SOURCE_DIR}/include
+    ${PROJECT_SOURCE_DIR}/include/plc
+)
+
+# Don't search with REQUIRED as we can continue without gflags.
+find_package(gflags 2.2.0)
+if (gflags_FOUND)
+    if (TARGET gflags)
+        message("-- Found Google Flags (gflags) version ${gflags_VERSION}: ${gflags_DIR}")
+        message("-- Found Google Flags (gflags) version ${gflags_VERSION}: ${GFLAGS_LIBRARIES}")
+    else()
+        message("-- Detected version of gflags: ${gflags_VERSION} does not define "
+                "expected gflags CMake target which should be exported by gflags 2.2+. "
+                "Building without gflags.")
+        update_cache_variable(GFLAGS OFF)
+    endif()
+else (gflags_FOUND)
+    message("-- Did not find Google Flags (gflags), Building without gflags.")
+    update_cache_variable(GFLAGS OFF)
+endif (gflags_FOUND)
+find_package(glog REQUIRED)
+
+aux_source_directory(${PROJECT_SOURCE_DIR}/include/plc PLC_SRC)
+aux_source_directory(${PROJECT_SOURCE_DIR}/include/tool TOOL_SRC)
+aux_source_directory(${PROJECT_SOURCE_DIR}/include/json JSON_SRC)
 
 set(PROJECT_SOURCES
         main.cpp
@@ -27,6 +55,9 @@ set(PROJECT_SOURCES
         proto_tool.h
         velodyne_config.pb.h
         velodyne_config.pb.cc
+        ${TOOL_SRC}
+        ${PLC_SRC}
+        ${JSON_SRC}
 )
 
 if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
@@ -54,6 +85,9 @@ endif()
 
 target_link_libraries(PCLRegistrationTool PRIVATE Qt${QT_VERSION_MAJOR}::Widgets
     ${PROTOBUF_LIBRARIES}
+    -lgflags
+    glog::glog
+    snap7
 )
 
 set_target_properties(PCLRegistrationTool PROPERTIES

+ 1 - 1
CMakeLists.txt.user

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE QtCreatorProject>
-<!-- Written by QtCreator 10.0.2, 2023-08-09T11:28:32. -->
+<!-- Written by QtCreator 10.0.2, 2023-08-10T20:56:38. -->
 <qtcreator>
  <data>
   <variable>EnvironmentId</variable>

+ 82 - 0
etc/clamp_safety_lidars.json

@@ -0,0 +1,82 @@
+[
+    {
+        "enable": true,
+        "angle_min": -2.35619449,
+        "angle_max": 2.35619449,
+        "angle_increment": 0.00582,
+        "time_increment": 0.000062,
+        "range_min": 0,
+        "range_max": 30,
+        "net_config": {
+            "ip_address": "10.211.31.150",
+            "port": 2110
+        },
+        "scan_box_limit": {
+            "dist_limit": 8,
+            "minx": -0.35,
+            "maxx": 0.35,
+            "miny": 0.05,
+            "maxy": 0.5
+        }
+    },
+    {
+        "enable": true,
+        "angle_min": -2.35619449,
+        "angle_max": 2.35619449,
+        "angle_increment": 0.00582,
+        "time_increment": 0.000062,
+        "range_min": 0,
+        "range_max": 30,
+        "net_config": {
+            "ip_address": "10.211.31.151",
+            "port": 2110
+        },
+        "scan_box_limit": {
+            "dist_limit": 8,
+            "minx": -0.35,
+            "maxx": 0.35,
+            "miny": 0.05,
+            "maxy": 0.5
+        }
+    },
+    {
+        "enable": true,
+        "angle_min": -2.35619449,
+        "angle_max": 2.35619449,
+        "angle_increment": 0.00582,
+        "time_increment": 0.000062,
+        "range_min": 0,
+        "range_max": 30,
+        "net_config": {
+            "ip_address": "10.211.31.152",
+            "port": 2110
+        },
+        "scan_box_limit": {
+            "dist_limit": 8,
+            "minx": -0.35,
+            "maxx": 0.35,
+            "miny": 0.05,
+            "maxy": 0.5
+        }
+    },
+    {
+        "enable": true,
+        "angle_min": -2.35619449,
+        "angle_max": 2.35619449,
+        "angle_increment": 0.00582,
+        "time_increment": 0.000062,
+        "range_min": 0,
+        "range_max": 30,
+        "net_config": {
+            "ip_address": "10.211.31.153",
+            "port": 2110
+        },
+        "scan_box_limit": {
+            "dist_limit": 8,
+            "minx": -0.35,
+            "maxx": 0.35,
+            "miny": 0.05,
+            "maxy": 0.5
+        }
+    }
+]

+ 3 - 0
etc/plc.json

@@ -0,0 +1,3 @@
+{
+    "ip": "10.211.31.1"
+}

+ 936 - 0
include/error_code/error_code.hpp

@@ -0,0 +1,936 @@
+//Error_code是错误码的底层通用模块,
+//功能:用作故障分析和处理。
+
+//用法:所有的功能接口函数return错误管理类,
+//然后上层判断分析错误码,并进行故障处理。
+#pragma once
+
+#include <string>
+#include <string.h>
+#include<iostream>
+
+#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
+
+//进程加锁的状态,
+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,                                        //处理器正忙, 请稍等
+
+
+
+
+
+    //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 *p_error_description = nullptr) {
+        m_error_code = error_code;
+        m_error_level = error_level;
+        if (p_error_description != nullptr) {
+            m_error_description = std::string(p_error_description);
+        }
+    }
+
+    //赋值构造
+    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 *p_error_description = nullptr) {
+        m_error_code = error_code;
+        m_error_level = error_level;
+        if (p_error_description != nullptr) {
+            m_error_description = std::string(p_error_description);
+        }
+    }
+
+    //初始化
+    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 *p_error_description = nullptr) {
+        m_error_code = error_code;
+        m_error_level = error_level;
+        if (p_error_description != nullptr) {
+            m_error_description = std::string(p_error_description);
+        }
+    }
+
+    //重置
+    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 *p_error_description) {
+        if (p_error_description != nullptr) {
+            m_error_description = std::string(p_error_description);
+        }
+    }
+
+    //设置错误描述
+    void set_error_description(std::string error_description_string) {
+        m_error_description = error_description_string;
+    }
+
+    //尾部追加错误描述
+    void add_error_description(const char *p_error_description) {
+        if (p_error_description != nullptr) {
+            m_error_description += std::string(p_error_description);
+        }
+    }
+
+    //尾部追加错误描述
+    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);
+            }
+        }
+    }
+
+    //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) {
+    }
+};

+ 89 - 0
include/json/allocator.h

@@ -0,0 +1,89 @@
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_ALLOCATOR_H_INCLUDED
+#define JSON_ALLOCATOR_H_INCLUDED
+
+#include <cstring>
+#include <memory>
+
+#pragma pack(push)
+#pragma pack()
+
+namespace Json {
+template <typename T> class SecureAllocator {
+public:
+  // Type definitions
+  using value_type = T;
+  using pointer = T*;
+  using const_pointer = const T*;
+  using reference = T&;
+  using const_reference = const T&;
+  using size_type = std::size_t;
+  using difference_type = std::ptrdiff_t;
+
+  /**
+   * Allocate memory for N items using the standard allocator.
+   */
+  pointer allocate(size_type n) {
+    // allocate using "global operator new"
+    return static_cast<pointer>(::operator new(n * sizeof(T)));
+  }
+
+  /**
+   * Release memory which was allocated for N items at pointer P.
+   *
+   * The memory block is filled with zeroes before being released.
+   */
+  void deallocate(pointer p, size_type n) {
+    // memset_s is used because memset may be optimized away by the compiler
+    memset_s(p, n * sizeof(T), 0, n * sizeof(T));
+    // free using "global operator delete"
+    ::operator delete(p);
+  }
+
+  /**
+   * Construct an item in-place at pointer P.
+   */
+  template <typename... Args> void construct(pointer p, Args&&... args) {
+    // construct using "placement new" and "perfect forwarding"
+    ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
+  }
+
+  size_type max_size() const { return size_t(-1) / sizeof(T); }
+
+  pointer address(reference x) const { return std::addressof(x); }
+
+  const_pointer address(const_reference x) const { return std::addressof(x); }
+
+  /**
+   * Destroy an item in-place at pointer P.
+   */
+  void destroy(pointer p) {
+    // destroy using "explicit destructor"
+    p->~T();
+  }
+
+  // Boilerplate
+  SecureAllocator() {}
+  template <typename U> SecureAllocator(const SecureAllocator<U>&) {}
+  template <typename U> struct rebind { using other = SecureAllocator<U>; };
+};
+
+template <typename T, typename U>
+bool operator==(const SecureAllocator<T>&, const SecureAllocator<U>&) {
+  return true;
+}
+
+template <typename T, typename U>
+bool operator!=(const SecureAllocator<T>&, const SecureAllocator<U>&) {
+  return false;
+}
+
+} // namespace Json
+
+#pragma pack(pop)
+
+#endif // JSON_ALLOCATOR_H_INCLUDED

+ 61 - 0
include/json/assertions.h

@@ -0,0 +1,61 @@
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_ASSERTIONS_H_INCLUDED
+#define JSON_ASSERTIONS_H_INCLUDED
+
+#include <cstdlib>
+#include <sstream>
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "config.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+/** It should not be possible for a maliciously designed file to
+ *  cause an abort() or seg-fault, so these macros are used only
+ *  for pre-condition violations and internal logic errors.
+ */
+#if JSON_USE_EXCEPTION
+
+// @todo <= add detail about condition in exception
+#define JSON_ASSERT(condition)                                                 \
+  do {                                                                         \
+    if (!(condition)) {                                                        \
+      Json::throwLogicError("assert json failed");                             \
+    }                                                                          \
+  } while (0)
+
+#define JSON_FAIL_MESSAGE(message)                                             \
+  do {                                                                         \
+    OStringStream oss;                                                         \
+    oss << message;                                                            \
+    Json::throwLogicError(oss.str());                                          \
+    abort();                                                                   \
+  } while (0)
+
+#else // JSON_USE_EXCEPTION
+
+#define JSON_ASSERT(condition) assert(condition)
+
+// The call to assert() will show the failure message in debug builds. In
+// release builds we abort, for a core-dump or debugger.
+#define JSON_FAIL_MESSAGE(message)                                             \
+  {                                                                            \
+    OStringStream oss;                                                         \
+    oss << message;                                                            \
+    assert(false && oss.str().c_str());                                        \
+    abort();                                                                   \
+  }
+
+#endif
+
+#define JSON_ASSERT_MESSAGE(condition, message)                                \
+  do {                                                                         \
+    if (!(condition)) {                                                        \
+      JSON_FAIL_MESSAGE(message);                                              \
+    }                                                                          \
+  } while (0)
+
+#endif // JSON_ASSERTIONS_H_INCLUDED

+ 150 - 0
include/json/config.h

@@ -0,0 +1,150 @@
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_CONFIG_H_INCLUDED
+#define JSON_CONFIG_H_INCLUDED
+#include <cstddef>
+#include <cstdint>
+#include <istream>
+#include <memory>
+#include <ostream>
+#include <sstream>
+#include <string>
+#include <type_traits>
+
+// If non-zero, the library uses exceptions to report bad input instead of C
+// assertion macros. The default is to use exceptions.
+#ifndef JSON_USE_EXCEPTION
+#define JSON_USE_EXCEPTION 1
+#endif
+
+// Temporary, tracked for removal with issue #982.
+#ifndef JSON_USE_NULLREF
+#define JSON_USE_NULLREF 1
+#endif
+
+/// If defined, indicates that the source file is amalgamated
+/// to prevent private header inclusion.
+/// Remarks: it is automatically defined in the generated amalgamated header.
+// #define JSON_IS_AMALGAMATION
+
+// Export macros for DLL visibility
+#if defined(JSON_DLL_BUILD)
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#define JSON_API __declspec(dllexport)
+#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
+#elif defined(__GNUC__) || defined(__clang__)
+#define JSON_API __attribute__((visibility("default")))
+#endif // if defined(_MSC_VER)
+
+#elif defined(JSON_DLL)
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#define JSON_API __declspec(dllimport)
+#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
+#endif // if defined(_MSC_VER)
+#endif // ifdef JSON_DLL_BUILD
+
+#if !defined(JSON_API)
+#define JSON_API
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1800
+#error                                                                         \
+    "ERROR:  Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities"
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+// As recommended at
+// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
+extern JSON_API int msvc_pre1900_c99_snprintf(char* outBuf, size_t size,
+                                              const char* format, ...);
+#define jsoncpp_snprintf msvc_pre1900_c99_snprintf
+#else
+#define jsoncpp_snprintf std::snprintf
+#endif
+
+// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
+// integer
+// Storages, and 64 bits integer support is disabled.
+// #define JSON_NO_INT64 1
+
+// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools.
+// C++11 should be used directly in JSONCPP.
+#define JSONCPP_OVERRIDE override
+
+#ifdef __clang__
+#if __has_extension(attribute_deprecated_with_message)
+#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
+#endif
+#elif defined(__GNUC__) // not clang (gcc comes later since clang emulates gcc)
+#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
+#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
+#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
+#endif                  // GNUC version
+#elif defined(_MSC_VER) // MSVC (after clang because clang on Windows emulates
+                        // MSVC)
+#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
+#endif // __clang__ || __GNUC__ || _MSC_VER
+
+#if !defined(JSONCPP_DEPRECATED)
+#define JSONCPP_DEPRECATED(message)
+#endif // if !defined(JSONCPP_DEPRECATED)
+
+#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6))
+#define JSON_USE_INT64_DOUBLE_CONVERSION 1
+#endif
+
+#if !defined(JSON_IS_AMALGAMATION)
+
+#include "allocator.h"
+#include "version.h"
+
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+namespace Json {
+using Int = int;
+using UInt = unsigned int;
+#if defined(JSON_NO_INT64)
+using LargestInt = int;
+using LargestUInt = unsigned int;
+#undef JSON_HAS_INT64
+#else                 // if defined(JSON_NO_INT64)
+// For Microsoft Visual use specific types as long long is not supported
+#if defined(_MSC_VER) // Microsoft Visual Studio
+using Int64 = __int64;
+using UInt64 = unsigned __int64;
+#else                 // if defined(_MSC_VER) // Other platforms, use long long
+using Int64 = int64_t;
+using UInt64 = uint64_t;
+#endif                // if defined(_MSC_VER)
+using LargestInt = Int64;
+using LargestUInt = UInt64;
+#define JSON_HAS_INT64
+#endif // if defined(JSON_NO_INT64)
+
+template <typename T>
+using Allocator =
+    typename std::conditional<JSONCPP_USING_SECURE_MEMORY, SecureAllocator<T>,
+                              std::allocator<T>>::type;
+using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
+using IStringStream =
+    std::basic_istringstream<String::value_type, String::traits_type,
+                             String::allocator_type>;
+using OStringStream =
+    std::basic_ostringstream<String::value_type, String::traits_type,
+                             String::allocator_type>;
+using IStream = std::istream;
+using OStream = std::ostream;
+} // namespace Json
+
+// Legacy names (formerly macros).
+using JSONCPP_STRING = Json::String;
+using JSONCPP_ISTRINGSTREAM = Json::IStringStream;
+using JSONCPP_OSTRINGSTREAM = Json::OStringStream;
+using JSONCPP_ISTREAM = Json::IStream;
+using JSONCPP_OSTREAM = Json::OStream;
+
+#endif // JSON_CONFIG_H_INCLUDED

+ 43 - 0
include/json/forwards.h

@@ -0,0 +1,43 @@
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_FORWARDS_H_INCLUDED
+#define JSON_FORWARDS_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "config.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+namespace Json {
+
+// writer.h
+class StreamWriter;
+class StreamWriterBuilder;
+class Writer;
+class FastWriter;
+class StyledWriter;
+class StyledStreamWriter;
+
+// reader.h
+class Reader;
+class CharReader;
+class CharReaderBuilder;
+
+// json_features.h
+class Features;
+
+// value.h
+using ArrayIndex = unsigned int;
+class StaticString;
+class Path;
+class PathArgument;
+class Value;
+class ValueIteratorBase;
+class ValueIterator;
+class ValueConstIterator;
+
+} // namespace Json
+
+#endif // JSON_FORWARDS_H_INCLUDED

+ 16 - 0
include/json/json.h

@@ -0,0 +1,16 @@
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_JSON_H_INCLUDED
+#define JSON_JSON_H_INCLUDED
+
+#include "config.h"
+#include "json_features.h"
+#include "reader.h"
+#include "value.h"
+#include "writer.h"
+#include "json_file.hpp"
+
+#endif // JSON_JSON_H_INCLUDED

+ 62 - 0
include/json/json_features.h

@@ -0,0 +1,62 @@
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_FEATURES_H_INCLUDED
+#define JSON_FEATURES_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "forwards.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+#pragma pack(push)
+#pragma pack()
+
+namespace Json {
+
+/** \brief Configuration passed to reader and writer.
+ * This configuration object can be used to force the Reader or Writer
+ * to behave in a standard conforming way.
+ */
+class JSON_API Features {
+public:
+  /** \brief A configuration that allows all features and assumes all strings
+   * are UTF-8.
+   * - C & C++ comments are allowed
+   * - Root object can be any JSON value
+   * - Assumes Value strings are encoded in UTF-8
+   */
+  static Features all();
+
+  /** \brief A configuration that is strictly compatible with the JSON
+   * specification.
+   * - Comments are forbidden.
+   * - Root object must be either an array or an object value.
+   * - Assumes Value strings are encoded in UTF-8
+   */
+  static Features strictMode();
+
+  /** \brief Initialize the configuration like JsonConfig::allFeatures;
+   */
+  Features();
+
+  /// \c true if comments are allowed. Default: \c true.
+  bool allowComments_{true};
+
+  /// \c true if root must be either an array or an object value. Default: \c
+  /// false.
+  bool strictRoot_{false};
+
+  /// \c true if dropped null placeholders are allowed. Default: \c false.
+  bool allowDroppedNullPlaceholders_{false};
+
+  /// \c true if numeric object key are allowed. Default: \c false.
+  bool allowNumericKeys_{false};
+};
+
+} // namespace Json
+
+#pragma pack(pop)
+
+#endif // JSON_FEATURES_H_INCLUDED

+ 48 - 0
include/json/json_file.hpp

@@ -0,0 +1,48 @@
+#ifndef JSON_FILE_H_
+#define JSON_FILE_H_
+
+#include "reader.h"
+#include <fstream>
+
+#define JV_STRING(_json_, _key_, _value_, _default_) (_json_.isMember(_key_) && _json_[_key_].isString()) ? _value_ = _json_[_key_].asString() : _value_ = _default_;
+#define JV_INT(_json_, _key_, _value_, _default_) (_json_.isMember(_key_) && _json_[_key_].isInt()) ? _value_ = _json_[_key_].asInt() : _value_ = _default_;
+#define JV_INT64(_json_, _key_, _value_, _default_) (_json_.isMember(_key_) && _json_[_key_].isInt64()) ? _value_ = _json_[_key_].asInt64() : _value_ = _default_;
+#define JV_UINT(_json_, _key_, _value_, _default_) (_json_.isMember(_key_) && _json_[_key_].isUInt()) ? _value_ = _json_[_key_].asUInt() : _value_ = _default_;
+#define JV_UINT64(_json_, _key_, _value_, _default_) (_json_.isMember(_key_) && _json_[_key_].isUInt64()) ? _value_ = _json_[_key_].asUInt64() : _value_ = _default_;
+#define JV_BOOL(_json_, _key_, _value_, _default_) (_json_.isMember(_key_) && _json_[_key_].isBool()) ? _value_ = _json_[_key_].asBool() : _value_ = _default_;
+#define JV_DOUBLE(_json_, _key_, _value_, _default_) (_json_.isMember(_key_) && _json_[_key_].isDouble()) ? _value_ = _json_[_key_].asDouble() : _value_ = _default_;
+
+const std::string DEFAULT_STRING = "";
+const int DEFAULT_INT = 0;
+const bool DEFAULT_BOOL = false;
+const double DEFAULT_DOUBLE = 0;
+
+static bool ReadJsonFile(std::string file, Json::Value &json_data, std::ios_base::openmode mode = std::ios::in | std::ios::binary) {
+    std::fstream json_file;
+    json_file.open(file.c_str(), mode);
+
+    if(false == json_file.is_open()) {
+        return false;
+    }
+
+    bool ret = false;
+    Json::Reader json_reader;
+    ret = json_reader.parse(json_file, json_data);
+    json_file.close();
+
+    return ret;
+}
+
+static bool WriteJsonFile(std::string file, Json::Value &json_data, std::ios_base::openmode mode = std::ios::out | std::ios::trunc) {
+    std::fstream json_file;
+    json_file.open(file.c_str(), mode);
+
+    if(false == json_file.is_open()) {
+        return false;
+    }
+
+    json_file << json_data;
+    json_file.close();
+    return true;
+}
+#endif

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 2004 - 0
include/json/json_reader.cpp


+ 138 - 0
include/json/json_tool.h

@@ -0,0 +1,138 @@
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED
+#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include <json/config.h>
+#endif
+
+// Also support old flag NO_LOCALE_SUPPORT
+#ifdef NO_LOCALE_SUPPORT
+#define JSONCPP_NO_LOCALE_SUPPORT
+#endif
+
+#ifndef JSONCPP_NO_LOCALE_SUPPORT
+#include <clocale>
+#endif
+
+/* This header provides common string manipulation support, such as UTF-8,
+ * portable conversion from/to string...
+ *
+ * It is an internal header that must not be exposed.
+ */
+
+namespace Json {
+static inline char getDecimalPoint() {
+#ifdef JSONCPP_NO_LOCALE_SUPPORT
+  return '\0';
+#else
+  struct lconv* lc = localeconv();
+  return lc ? *(lc->decimal_point) : '\0';
+#endif
+}
+
+/// Converts a unicode code-point to UTF-8.
+static inline String codePointToUTF8(unsigned int cp) {
+  String result;
+
+  // based on description from http://en.wikipedia.org/wiki/UTF-8
+
+  if (cp <= 0x7f) {
+    result.resize(1);
+    result[0] = static_cast<char>(cp);
+  } else if (cp <= 0x7FF) {
+    result.resize(2);
+    result[1] = static_cast<char>(0x80 | (0x3f & cp));
+    result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
+  } else if (cp <= 0xFFFF) {
+    result.resize(3);
+    result[2] = static_cast<char>(0x80 | (0x3f & cp));
+    result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
+    result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12)));
+  } else if (cp <= 0x10FFFF) {
+    result.resize(4);
+    result[3] = static_cast<char>(0x80 | (0x3f & cp));
+    result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
+    result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
+    result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
+  }
+
+  return result;
+}
+
+enum {
+  /// Constant that specify the size of the buffer that must be passed to
+  /// uintToString.
+  uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1
+};
+
+// Defines a char buffer for use with uintToString().
+using UIntToStringBuffer = char[uintToStringBufferSize];
+
+/** Converts an unsigned integer to string.
+ * @param value Unsigned integer to convert to string
+ * @param current Input/Output string buffer.
+ *        Must have at least uintToStringBufferSize chars free.
+ */
+static inline void uintToString(LargestUInt value, char*& current) {
+  *--current = 0;
+  do {
+    *--current = static_cast<char>(value % 10U + static_cast<unsigned>('0'));
+    value /= 10;
+  } while (value != 0);
+}
+
+/** Change ',' to '.' everywhere in buffer.
+ *
+ * We had a sophisticated way, but it did not work in WinCE.
+ * @see https://github.com/open-source-parsers/jsoncpp/pull/9
+ */
+template <typename Iter> Iter fixNumericLocale(Iter begin, Iter end) {
+  for (; begin != end; ++begin) {
+    if (*begin == ',') {
+      *begin = '.';
+    }
+  }
+  return begin;
+}
+
+template <typename Iter> void fixNumericLocaleInput(Iter begin, Iter end) {
+  char decimalPoint = getDecimalPoint();
+  if (decimalPoint == '\0' || decimalPoint == '.') {
+    return;
+  }
+  for (; begin != end; ++begin) {
+    if (*begin == '.') {
+      *begin = decimalPoint;
+    }
+  }
+}
+
+/**
+ * Return iterator that would be the new end of the range [begin,end), if we
+ * were to delete zeros in the end of string, but not the last zero before '.'.
+ */
+template <typename Iter>
+Iter fixZerosInTheEnd(Iter begin, Iter end, unsigned int precision) {
+  for (; begin != end; --end) {
+    if (*(end - 1) != '0') {
+      return end;
+    }
+    // Don't delete the last zero before the decimal point.
+    if (begin != (end - 1) && begin != (end - 2) && *(end - 2) == '.') {
+      if (precision) {
+        return end;
+      }
+      return end - 2;
+    }
+  }
+  return end;
+}
+
+} // namespace Json
+
+#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1634 - 0
include/json/json_value.cpp


+ 156 - 0
include/json/json_valueiterator.inl

@@ -0,0 +1,156 @@
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+// included by json_value.cpp
+
+namespace Json {
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class ValueIteratorBase
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+ValueIteratorBase::ValueIteratorBase() : current_() {}
+
+ValueIteratorBase::ValueIteratorBase(
+    const Value::ObjectValues::iterator& current)
+    : current_(current), isNull_(false) {}
+
+Value& ValueIteratorBase::deref() { return current_->second; }
+const Value& ValueIteratorBase::deref() const { return current_->second; }
+
+void ValueIteratorBase::increment() { ++current_; }
+
+void ValueIteratorBase::decrement() { --current_; }
+
+ValueIteratorBase::difference_type
+ValueIteratorBase::computeDistance(const SelfType& other) const {
+  // Iterator for null value are initialized using the default
+  // constructor, which initialize current_ to the default
+  // std::map::iterator. As begin() and end() are two instance
+  // of the default std::map::iterator, they can not be compared.
+  // To allow this, we handle this comparison specifically.
+  if (isNull_ && other.isNull_) {
+    return 0;
+  }
+
+  // Usage of std::distance is not portable (does not compile with Sun Studio 12
+  // RogueWave STL,
+  // which is the one used by default).
+  // Using a portable hand-made version for non random iterator instead:
+  //   return difference_type( std::distance( current_, other.current_ ) );
+  difference_type myDistance = 0;
+  for (Value::ObjectValues::iterator it = current_; it != other.current_;
+       ++it) {
+    ++myDistance;
+  }
+  return myDistance;
+}
+
+bool ValueIteratorBase::isEqual(const SelfType& other) const {
+  if (isNull_) {
+    return other.isNull_;
+  }
+  return current_ == other.current_;
+}
+
+void ValueIteratorBase::copy(const SelfType& other) {
+  current_ = other.current_;
+  isNull_ = other.isNull_;
+}
+
+Value ValueIteratorBase::key() const {
+  const Value::CZString czstring = (*current_).first;
+  if (czstring.data()) {
+    if (czstring.isStaticString())
+      return Value(StaticString(czstring.data()));
+    return Value(czstring.data(), czstring.data() + czstring.length());
+  }
+  return Value(czstring.index());
+}
+
+UInt ValueIteratorBase::index() const {
+  const Value::CZString czstring = (*current_).first;
+  if (!czstring.data())
+    return czstring.index();
+  return Value::UInt(-1);
+}
+
+String ValueIteratorBase::name() const {
+  char const* keey;
+  char const* end;
+  keey = memberName(&end);
+  if (!keey)
+    return String();
+  return String(keey, end);
+}
+
+char const* ValueIteratorBase::memberName() const {
+  const char* cname = (*current_).first.data();
+  return cname ? cname : "";
+}
+
+char const* ValueIteratorBase::memberName(char const** end) const {
+  const char* cname = (*current_).first.data();
+  if (!cname) {
+    *end = nullptr;
+    return nullptr;
+  }
+  *end = cname + (*current_).first.length();
+  return cname;
+}
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class ValueConstIterator
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+ValueConstIterator::ValueConstIterator() = default;
+
+ValueConstIterator::ValueConstIterator(
+    const Value::ObjectValues::iterator& current)
+    : ValueIteratorBase(current) {}
+
+ValueConstIterator::ValueConstIterator(ValueIterator const& other)
+    : ValueIteratorBase(other) {}
+
+ValueConstIterator& ValueConstIterator::
+operator=(const ValueIteratorBase& other) {
+  copy(other);
+  return *this;
+}
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class ValueIterator
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+ValueIterator::ValueIterator() = default;
+
+ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current)
+    : ValueIteratorBase(current) {}
+
+ValueIterator::ValueIterator(const ValueConstIterator& other)
+    : ValueIteratorBase(other) {
+  throwRuntimeError("ConstIterator to Iterator should never be allowed.");
+}
+
+ValueIterator::ValueIterator(const ValueIterator& other) = default;
+
+ValueIterator& ValueIterator::operator=(const SelfType& other) {
+  copy(other);
+  return *this;
+}
+
+} // namespace Json

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1259 - 0
include/json/json_writer.cpp


+ 406 - 0
include/json/reader.h

@@ -0,0 +1,406 @@
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_READER_H_INCLUDED
+#define JSON_READER_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "json_features.h"
+#include "value.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <deque>
+#include <iosfwd>
+#include <istream>
+#include <stack>
+#include <string>
+
+// Disable warning C4251: <data member>: <type> needs to have dll-interface to
+// be used by...
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(push)
+#pragma warning(disable : 4251)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#pragma pack(push)
+#pragma pack()
+
+namespace Json {
+
+/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a
+ * Value.
+ *
+ * \deprecated Use CharReader and CharReaderBuilder.
+ */
+
+class JSON_API Reader {
+public:
+  using Char = char;
+  using Location = const Char*;
+
+  /** \brief An error tagged with where in the JSON text it was encountered.
+   *
+   * The offsets give the [start, limit) range of bytes within the text. Note
+   * that this is bytes, not codepoints.
+   */
+  struct StructuredError {
+    ptrdiff_t offset_start;
+    ptrdiff_t offset_limit;
+    String message;
+  };
+
+  /** \brief Constructs a Reader allowing all features for parsing.
+    * \deprecated Use CharReader and CharReaderBuilder.
+   */
+  Reader();
+
+  /** \brief Constructs a Reader allowing the specified feature set for parsing.
+    * \deprecated Use CharReader and CharReaderBuilder.
+   */
+  Reader(const Features& features);
+
+  /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
+   * document.
+   *
+   * \param      document        UTF-8 encoded string containing the document
+   *                             to read.
+   * \param[out] root            Contains the root value of the document if it
+   *                             was successfully parsed.
+   * \param      collectComments \c true to collect comment and allow writing
+   *                             them back during serialization, \c false to
+   *                             discard comments.  This parameter is ignored
+   *                             if Features::allowComments_ is \c false.
+   * \return \c true if the document was successfully parsed, \c false if an
+   * error occurred.
+   */
+  bool parse(const std::string& document, Value& root,
+             bool collectComments = true);
+
+  /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
+   * document.
+   *
+   * \param      beginDoc        Pointer on the beginning of the UTF-8 encoded
+   *                             string of the document to read.
+   * \param      endDoc          Pointer on the end of the UTF-8 encoded string
+   *                             of the document to read.  Must be >= beginDoc.
+   * \param[out] root            Contains the root value of the document if it
+   *                             was successfully parsed.
+   * \param      collectComments \c true to collect comment and allow writing
+   *                             them back during serialization, \c false to
+   *                             discard comments.  This parameter is ignored
+   *                             if Features::allowComments_ is \c false.
+   * \return \c true if the document was successfully parsed, \c false if an
+   * error occurred.
+   */
+  bool parse(const char* beginDoc, const char* endDoc, Value& root,
+             bool collectComments = true);
+
+  /// \brief Parse from input stream.
+  /// \see Json::operator>>(std::istream&, Json::Value&).
+  bool parse(IStream& is, Value& root, bool collectComments = true);
+
+  /** \brief Returns a user friendly string that list errors in the parsed
+   * document.
+   *
+   * \return Formatted error message with the list of errors with their
+   * location in the parsed document. An empty string is returned if no error
+   * occurred during parsing.
+   * \deprecated Use getFormattedErrorMessages() instead (typo fix).
+   */
+  JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.")
+  String getFormatedErrorMessages() const;
+
+  /** \brief Returns a user friendly string that list errors in the parsed
+   * document.
+   *
+   * \return Formatted error message with the list of errors with their
+   * location in the parsed document. An empty string is returned if no error
+   * occurred during parsing.
+   */
+  String getFormattedErrorMessages() const;
+
+  /** \brief Returns a vector of structured errors encountered while parsing.
+   *
+   * \return A (possibly empty) vector of StructuredError objects. Currently
+   * only one error can be returned, but the caller should tolerate multiple
+   * errors.  This can occur if the parser recovers from a non-fatal parse
+   * error and then encounters additional errors.
+   */
+  std::vector<StructuredError> getStructuredErrors() const;
+
+  /** \brief Add a semantic error message.
+   *
+   * \param value   JSON Value location associated with the error
+   * \param message The error message.
+   * \return \c true if the error was successfully added, \c false if the Value
+   * offset exceeds the document size.
+   */
+  bool pushError(const Value& value, const String& message);
+
+  /** \brief Add a semantic error message with extra context.
+   *
+   * \param value   JSON Value location associated with the error
+   * \param message The error message.
+   * \param extra   Additional JSON Value location to contextualize the error
+   * \return \c true if the error was successfully added, \c false if either
+   * Value offset exceeds the document size.
+   */
+  bool pushError(const Value& value, const String& message, const Value& extra);
+
+  /** \brief Return whether there are any errors.
+   *
+   * \return \c true if there are no errors to report \c false if errors have
+   * occurred.
+   */
+  bool good() const;
+
+private:
+  enum TokenType {
+    tokenEndOfStream = 0,
+    tokenObjectBegin,
+    tokenObjectEnd,
+    tokenArrayBegin,
+    tokenArrayEnd,
+    tokenString,
+    tokenNumber,
+    tokenTrue,
+    tokenFalse,
+    tokenNull,
+    tokenArraySeparator,
+    tokenMemberSeparator,
+    tokenComment,
+    tokenError
+  };
+
+  class Token {
+  public:
+    TokenType type_;
+    Location start_;
+    Location end_;
+  };
+
+  class ErrorInfo {
+  public:
+    Token token_;
+    String message_;
+    Location extra_;
+  };
+
+  using Errors = std::deque<ErrorInfo>;
+
+  bool readToken(Token& token);
+  void skipSpaces();
+  bool match(const Char* pattern, int patternLength);
+  bool readComment();
+  bool readCStyleComment();
+  bool readCppStyleComment();
+  bool readString();
+  void readNumber();
+  bool readValue();
+  bool readObject(Token& token);
+  bool readArray(Token& token);
+  bool decodeNumber(Token& token);
+  bool decodeNumber(Token& token, Value& decoded);
+  bool decodeString(Token& token);
+  bool decodeString(Token& token, String& decoded);
+  bool decodeDouble(Token& token);
+  bool decodeDouble(Token& token, Value& decoded);
+  bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
+                              unsigned int& unicode);
+  bool decodeUnicodeEscapeSequence(Token& token, Location& current,
+                                   Location end, unsigned int& unicode);
+  bool addError(const String& message, Token& token, Location extra = nullptr);
+  bool recoverFromError(TokenType skipUntilToken);
+  bool addErrorAndRecover(const String& message, Token& token,
+                          TokenType skipUntilToken);
+  void skipUntilSpace();
+  Value& currentValue();
+  Char getNextChar();
+  void getLocationLineAndColumn(Location location, int& line,
+                                int& column) const;
+  String getLocationLineAndColumn(Location location) const;
+  void addComment(Location begin, Location end, CommentPlacement placement);
+  void skipCommentTokens(Token& token);
+
+  static bool containsNewLine(Location begin, Location end);
+  static String normalizeEOL(Location begin, Location end);
+
+  using Nodes = std::stack<Value*>;
+  Nodes nodes_;
+  Errors errors_;
+  String document_;
+  Location begin_{};
+  Location end_{};
+  Location current_{};
+  Location lastValueEnd_{};
+  Value* lastValue_{};
+  String commentsBefore_;
+  Features features_;
+  bool collectComments_{};
+}; // Reader
+
+/** Interface for reading JSON from a char array.
+ */
+class JSON_API CharReader {
+public:
+  virtual ~CharReader() = default;
+  /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
+   * document. The document must be a UTF-8 encoded string containing the
+   * document to read.
+   *
+   * \param      beginDoc Pointer on the beginning of the UTF-8 encoded string
+   *                      of the document to read.
+   * \param      endDoc   Pointer on the end of the UTF-8 encoded string of the
+   *                      document to read. Must be >= beginDoc.
+   * \param[out] root     Contains the root value of the document if it was
+   *                      successfully parsed.
+   * \param[out] errs     Formatted error messages (if not NULL) a user
+   *                      friendly string that lists errors in the parsed
+   *                      document.
+   * \return \c true if the document was successfully parsed, \c false if an
+   * error occurred.
+   */
+  virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
+                     String* errs) = 0;
+
+  class JSON_API Factory {
+  public:
+    virtual ~Factory() = default;
+    /** \brief Allocate a CharReader via operator new().
+     * \throw std::exception if something goes wrong (e.g. invalid settings)
+     */
+    virtual CharReader* newCharReader() const = 0;
+  }; // Factory
+};   // CharReader
+
+/** \brief Build a CharReader implementation.
+ *
+ * Usage:
+ *   \code
+ *   using namespace Json;
+ *   CharReaderBuilder builder;
+ *   builder["collectComments"] = false;
+ *   Value value;
+ *   String errs;
+ *   bool ok = parseFromStream(builder, std::cin, &value, &errs);
+ *   \endcode
+ */
+class JSON_API CharReaderBuilder : public CharReader::Factory {
+public:
+  // Note: We use a Json::Value so that we can add data-members to this class
+  // without a major version bump.
+  /** Configuration of this builder.
+   * These are case-sensitive.
+   * Available settings (case-sensitive):
+   * - `"collectComments": false or true`
+   *   - true to collect comment and allow writing them back during
+   *     serialization, false to discard comments.  This parameter is ignored
+   *     if allowComments is false.
+   * - `"allowComments": false or true`
+   *   - true if comments are allowed.
+   * - `"allowTrailingCommas": false or true`
+   *   - true if trailing commas in objects and arrays are allowed.
+   * - `"strictRoot": false or true`
+   *   - true if root must be either an array or an object value
+   * - `"allowDroppedNullPlaceholders": false or true`
+   *   - true if dropped null placeholders are allowed. (See
+   *     StreamWriterBuilder.)
+   * - `"allowNumericKeys": false or true`
+   *   - true if numeric object keys are allowed.
+   * - `"allowSingleQuotes": false or true`
+   *   - true if '' are allowed for strings (both keys and values)
+   * - `"stackLimit": integer`
+   *   - Exceeding stackLimit (recursive depth of `readValue()`) will cause an
+   *     exception.
+   *   - This is a security issue (seg-faults caused by deeply nested JSON), so
+   *     the default is low.
+   * - `"failIfExtra": false or true`
+   *   - If true, `parse()` returns false when extra non-whitespace trails the
+   *     JSON value in the input string.
+   * - `"rejectDupKeys": false or true`
+   *   - If true, `parse()` returns false when a key is duplicated within an
+   *     object.
+   * - `"allowSpecialFloats": false or true`
+   *   - If true, special float values (NaNs and infinities) are allowed and
+   *     their values are lossfree restorable.
+   * - `"skipBom": false or true`
+   *   - If true, if the input starts with the Unicode byte order mark (BOM),
+   *     it is skipped.
+   *
+   * You can examine 'settings_` yourself to see the defaults. You can also
+   * write and read them just like any JSON Value.
+   * \sa setDefaults()
+   */
+  Json::Value settings_;
+
+  CharReaderBuilder();
+  ~CharReaderBuilder() override;
+
+  CharReader* newCharReader() const override;
+
+  /** \return true if 'settings' are legal and consistent;
+   *   otherwise, indicate bad settings via 'invalid'.
+   */
+  bool validate(Json::Value* invalid) const;
+
+  /** A simple way to update a specific setting.
+   */
+  Value& operator[](const String& key);
+
+  /** Called by ctor, but you can use this to reset settings_.
+   * \pre 'settings' != NULL (but Json::null is fine)
+   * \remark Defaults:
+   * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults
+   */
+  static void setDefaults(Json::Value* settings);
+  /** Same as old Features::strictMode().
+   * \pre 'settings' != NULL (but Json::null is fine)
+   * \remark Defaults:
+   * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode
+   */
+  static void strictMode(Json::Value* settings);
+};
+
+/** Consume entire stream and use its begin/end.
+ * Someday we might have a real StreamReader, but for now this
+ * is convenient.
+ */
+bool JSON_API parseFromStream(CharReader::Factory const&, IStream&, Value* root,
+                              String* errs);
+
+/** \brief Read from 'sin' into 'root'.
+ *
+ * Always keep comments from the input JSON.
+ *
+ * This can be used to read a file into a particular sub-object.
+ * For example:
+ *   \code
+ *   Json::Value root;
+ *   cin >> root["dir"]["file"];
+ *   cout << root;
+ *   \endcode
+ * Result:
+ * \verbatim
+ * {
+ * "dir": {
+ *    "file": {
+ *    // The input stream JSON would be nested here.
+ *    }
+ * }
+ * }
+ * \endverbatim
+ * \throw std::exception on parse error.
+ * \see Json::operator<<()
+ */
+JSON_API IStream& operator>>(IStream&, Value&);
+
+} // namespace Json
+
+#pragma pack(pop)
+
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(pop)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#endif // JSON_READER_H_INCLUDED

+ 936 - 0
include/json/value.h

@@ -0,0 +1,936 @@
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_H_INCLUDED
+#define JSON_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "forwards.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+// Conditional NORETURN attribute on the throw functions would:
+// a) suppress false positives from static code analysis
+// b) possibly improve optimization opportunities.
+#if !defined(JSONCPP_NORETURN)
+#if defined(_MSC_VER) && _MSC_VER == 1800
+#define JSONCPP_NORETURN __declspec(noreturn)
+#else
+#define JSONCPP_NORETURN [[noreturn]]
+#endif
+#endif
+
+// Support for '= delete' with template declarations was a late addition
+// to the c++11 standard and is rejected by clang 3.8 and Apple clang 8.2
+// even though these declare themselves to be c++11 compilers.
+#if !defined(JSONCPP_TEMPLATE_DELETE)
+#if defined(__clang__) && defined(__apple_build_version__)
+#if __apple_build_version__ <= 8000042
+#define JSONCPP_TEMPLATE_DELETE
+#endif
+#elif defined(__clang__)
+#if __clang_major__ == 3 && __clang_minor__ <= 8
+#define JSONCPP_TEMPLATE_DELETE
+#endif
+#endif
+#if !defined(JSONCPP_TEMPLATE_DELETE)
+#define JSONCPP_TEMPLATE_DELETE = delete
+#endif
+#endif
+
+#include <array>
+#include <exception>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+// Disable warning C4251: <data member>: <type> needs to have dll-interface to
+// be used by...
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(push)
+#pragma warning(disable : 4251 4275)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#pragma pack(push)
+#pragma pack()
+
+/** \brief JSON (JavaScript Object Notation).
+ */
+namespace Json {
+
+#if JSON_USE_EXCEPTION
+/** Base class for all exceptions we throw.
+ *
+ * We use nothing but these internally. Of course, STL can throw others.
+ */
+class JSON_API Exception : public std::exception {
+public:
+  Exception(String msg);
+  ~Exception() noexcept override;
+  char const* what() const noexcept override;
+
+protected:
+  String msg_;
+};
+
+/** Exceptions which the user cannot easily avoid.
+ *
+ * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input
+ *
+ * \remark derived from Json::Exception
+ */
+class JSON_API RuntimeError : public Exception {
+public:
+  RuntimeError(String const& msg);
+};
+
+/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros.
+ *
+ * These are precondition-violations (user bugs) and internal errors (our bugs).
+ *
+ * \remark derived from Json::Exception
+ */
+class JSON_API LogicError : public Exception {
+public:
+  LogicError(String const& msg);
+};
+#endif
+
+/// used internally
+JSONCPP_NORETURN void throwRuntimeError(String const& msg);
+/// used internally
+JSONCPP_NORETURN void throwLogicError(String const& msg);
+
+/** \brief Type of the value held by a Value object.
+ */
+enum ValueType {
+  nullValue = 0, ///< 'null' value
+  intValue,      ///< signed integer value
+  uintValue,     ///< unsigned integer value
+  realValue,     ///< double value
+  stringValue,   ///< UTF-8 string value
+  booleanValue,  ///< bool value
+  arrayValue,    ///< array value (ordered list)
+  objectValue    ///< object value (collection of name/value pairs).
+};
+
+enum CommentPlacement {
+  commentBefore = 0,      ///< a comment placed on the line before a value
+  commentAfterOnSameLine, ///< a comment just after a value on the same line
+  commentAfter, ///< a comment on the line after a value (only make sense for
+  /// root value)
+  numberOfCommentPlacement
+};
+
+/** \brief Type of precision for formatting of real values.
+ */
+enum PrecisionType {
+  significantDigits = 0, ///< we set max number of significant digits in string
+  decimalPlaces          ///< we set max number of digits after "." in string
+};
+
+/** \brief Lightweight wrapper to tag static string.
+ *
+ * Value constructor and objectValue member assignment takes advantage of the
+ * StaticString and avoid the cost of string duplication when storing the
+ * string or the member name.
+ *
+ * Example of usage:
+ * \code
+ * Json::Value aValue( StaticString("some text") );
+ * Json::Value object;
+ * static const StaticString code("code");
+ * object[code] = 1234;
+ * \endcode
+ */
+class JSON_API StaticString {
+public:
+  explicit StaticString(const char* czstring) : c_str_(czstring) {}
+
+  operator const char*() const { return c_str_; }
+
+  const char* c_str() const { return c_str_; }
+
+private:
+  const char* c_str_;
+};
+
+/** \brief Represents a <a HREF="http://www.json.org">JSON</a> value.
+ *
+ * This class is a discriminated union wrapper that can represents a:
+ * - signed integer [range: Value::minInt - Value::maxInt]
+ * - unsigned integer (range: 0 - Value::maxUInt)
+ * - double
+ * - UTF-8 string
+ * - boolean
+ * - 'null'
+ * - an ordered list of Value
+ * - collection of name/value pairs (javascript object)
+ *
+ * The type of the held value is represented by a #ValueType and
+ * can be obtained using type().
+ *
+ * Values of an #objectValue or #arrayValue can be accessed using operator[]()
+ * methods.
+ * Non-const methods will automatically create the a #nullValue element
+ * if it does not exist.
+ * The sequence of an #arrayValue will be automatically resized and initialized
+ * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue.
+ *
+ * The get() methods can be used to obtain default value in the case the
+ * required element does not exist.
+ *
+ * It is possible to iterate over the list of member keys of an object using
+ * the getMemberNames() method.
+ *
+ * \note #Value string-length fit in size_t, but keys must be < 2^30.
+ * (The reason is an implementation detail.) A #CharReader will raise an
+ * exception if a bound is exceeded to avoid security holes in your app,
+ * but the Value API does *not* check bounds. That is the responsibility
+ * of the caller.
+ */
+class JSON_API Value {
+  friend class ValueIteratorBase;
+
+public:
+  using Members = std::vector<String>;
+  using iterator = ValueIterator;
+  using const_iterator = ValueConstIterator;
+  using UInt = Json::UInt;
+  using Int = Json::Int;
+#if defined(JSON_HAS_INT64)
+  using UInt64 = Json::UInt64;
+  using Int64 = Json::Int64;
+#endif // defined(JSON_HAS_INT64)
+  using LargestInt = Json::LargestInt;
+  using LargestUInt = Json::LargestUInt;
+  using ArrayIndex = Json::ArrayIndex;
+
+  // Required for boost integration, e. g. BOOST_TEST
+  using value_type = std::string;
+
+#if JSON_USE_NULLREF
+  // Binary compatibility kludges, do not use.
+  static const Value& null;
+  static const Value& nullRef;
+#endif
+
+  // null and nullRef are deprecated, use this instead.
+  static Value const& nullSingleton();
+
+  /// Minimum signed integer value that can be stored in a Json::Value.
+  static constexpr LargestInt minLargestInt =
+      LargestInt(~(LargestUInt(-1) / 2));
+  /// Maximum signed integer value that can be stored in a Json::Value.
+  static constexpr LargestInt maxLargestInt = LargestInt(LargestUInt(-1) / 2);
+  /// Maximum unsigned integer value that can be stored in a Json::Value.
+  static constexpr LargestUInt maxLargestUInt = LargestUInt(-1);
+
+  /// Minimum signed int value that can be stored in a Json::Value.
+  static constexpr Int minInt = Int(~(UInt(-1) / 2));
+  /// Maximum signed int value that can be stored in a Json::Value.
+  static constexpr Int maxInt = Int(UInt(-1) / 2);
+  /// Maximum unsigned int value that can be stored in a Json::Value.
+  static constexpr UInt maxUInt = UInt(-1);
+
+#if defined(JSON_HAS_INT64)
+  /// Minimum signed 64 bits int value that can be stored in a Json::Value.
+  static constexpr Int64 minInt64 = Int64(~(UInt64(-1) / 2));
+  /// Maximum signed 64 bits int value that can be stored in a Json::Value.
+  static constexpr Int64 maxInt64 = Int64(UInt64(-1) / 2);
+  /// Maximum unsigned 64 bits int value that can be stored in a Json::Value.
+  static constexpr UInt64 maxUInt64 = UInt64(-1);
+#endif // defined(JSON_HAS_INT64)
+  /// Default precision for real value for string representation.
+  static constexpr UInt defaultRealPrecision = 17;
+  // The constant is hard-coded because some compiler have trouble
+  // converting Value::maxUInt64 to a double correctly (AIX/xlC).
+  // Assumes that UInt64 is a 64 bits integer.
+  static constexpr double maxUInt64AsDouble = 18446744073709551615.0;
+// Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler
+// when using gcc and clang backend compilers.  CZString
+// cannot be defined as private.  See issue #486
+#ifdef __NVCC__
+public:
+#else
+private:
+#endif
+#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
+  class CZString {
+  public:
+    enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy };
+    CZString(ArrayIndex index);
+    CZString(char const* str, unsigned length, DuplicationPolicy allocate);
+    CZString(CZString const& other);
+    CZString(CZString&& other) noexcept;
+    ~CZString();
+    CZString& operator=(const CZString& other);
+    CZString& operator=(CZString&& other) noexcept;
+
+    bool operator<(CZString const& other) const;
+    bool operator==(CZString const& other) const;
+    ArrayIndex index() const;
+    // const char* c_str() const; ///< \deprecated
+    char const* data() const;
+    unsigned length() const;
+    bool isStaticString() const;
+
+  private:
+    void swap(CZString& other);
+
+    struct StringStorage {
+      unsigned policy_ : 2;
+      unsigned length_ : 30; // 1GB max
+    };
+
+    char const* cstr_; // actually, a prefixed string, unless policy is noDup
+    union {
+      ArrayIndex index_;
+      StringStorage storage_;
+    };
+  };
+
+public:
+  typedef std::map<CZString, Value> ObjectValues;
+#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
+
+public:
+  /**
+   * \brief Create a default Value of the given type.
+   *
+   * This is a very useful constructor.
+   * To create an empty array, pass arrayValue.
+   * To create an empty object, pass objectValue.
+   * Another Value can then be set to this one by assignment.
+   * This is useful since clear() and resize() will not alter types.
+   *
+   * Examples:
+   *   \code
+   *   Json::Value null_value; // null
+   *   Json::Value arr_value(Json::arrayValue); // []
+   *   Json::Value obj_value(Json::objectValue); // {}
+   *   \endcode
+   */
+  Value(ValueType type = nullValue);
+  Value(Int value);
+  Value(UInt value);
+#if defined(JSON_HAS_INT64)
+  Value(Int64 value);
+  Value(UInt64 value);
+#endif // if defined(JSON_HAS_INT64)
+  Value(double value);
+  Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.)
+  Value(const char* begin, const char* end); ///< Copy all, incl zeroes.
+  /**
+   * \brief Constructs a value from a static string.
+   *
+   * Like other value string constructor but do not duplicate the string for
+   * internal storage. The given string must remain alive after the call to
+   * this constructor.
+   *
+   * \note This works only for null-terminated strings. (We cannot change the
+   * size of this class, so we have nowhere to store the length, which might be
+   * computed later for various operations.)
+   *
+   * Example of usage:
+   *   \code
+   *   static StaticString foo("some text");
+   *   Json::Value aValue(foo);
+   *   \endcode
+   */
+  Value(const StaticString& value);
+  Value(const String& value);
+  Value(bool value);
+  Value(std::nullptr_t ptr) = delete;
+  Value(const Value& other);
+  Value(Value&& other) noexcept;
+  ~Value();
+
+  /// \note Overwrite existing comments. To preserve comments, use
+  /// #swapPayload().
+  Value& operator=(const Value& other);
+  Value& operator=(Value&& other) noexcept;
+
+  /// Swap everything.
+  void swap(Value& other);
+  /// Swap values but leave comments and source offsets in place.
+  void swapPayload(Value& other);
+
+  /// copy everything.
+  void copy(const Value& other);
+  /// copy values but leave comments and source offsets in place.
+  void copyPayload(const Value& other);
+
+  ValueType type() const;
+
+  /// Compare payload only, not comments etc.
+  bool operator<(const Value& other) const;
+  bool operator<=(const Value& other) const;
+  bool operator>=(const Value& other) const;
+  bool operator>(const Value& other) const;
+  bool operator==(const Value& other) const;
+  bool operator!=(const Value& other) const;
+  int compare(const Value& other) const;
+
+  const char* asCString() const; ///< Embedded zeroes could cause you trouble!
+#if JSONCPP_USING_SECURE_MEMORY
+  unsigned getCStringLength() const; // Allows you to understand the length of
+                                     // the CString
+#endif
+  String asString() const; ///< Embedded zeroes are possible.
+  /** Get raw char* of string-value.
+   *  \return false if !string. (Seg-fault if str or end are NULL.)
+   */
+  bool getString(char const** begin, char const** end) const;
+  Int asInt() const;
+  UInt asUInt() const;
+#if defined(JSON_HAS_INT64)
+  Int64 asInt64() const;
+  UInt64 asUInt64() const;
+#endif // if defined(JSON_HAS_INT64)
+  LargestInt asLargestInt() const;
+  LargestUInt asLargestUInt() const;
+  float asFloat() const;
+  double asDouble() const;
+  bool asBool() const;
+
+  bool isNull() const;
+  bool isBool() const;
+  bool isInt() const;
+  bool isInt64() const;
+  bool isUInt() const;
+  bool isUInt64() const;
+  bool isIntegral() const;
+  bool isDouble() const;
+  bool isNumeric() const;
+  bool isString() const;
+  bool isArray() const;
+  bool isObject() const;
+
+  /// The `as<T>` and `is<T>` member function templates and specializations.
+  template <typename T> T as() const JSONCPP_TEMPLATE_DELETE;
+  template <typename T> bool is() const JSONCPP_TEMPLATE_DELETE;
+
+  bool isConvertibleTo(ValueType other) const;
+
+  /// Number of values in array or object
+  ArrayIndex size() const;
+
+  /// \brief Return true if empty array, empty object, or null;
+  /// otherwise, false.
+  bool empty() const;
+
+  /// Return !isNull()
+  explicit operator bool() const;
+
+  /// Remove all object members and array elements.
+  /// \pre type() is arrayValue, objectValue, or nullValue
+  /// \post type() is unchanged
+  void clear();
+
+  /// Resize the array to newSize elements.
+  /// New elements are initialized to null.
+  /// May only be called on nullValue or arrayValue.
+  /// \pre type() is arrayValue or nullValue
+  /// \post type() is arrayValue
+  void resize(ArrayIndex newSize);
+
+  ///@{
+  /// Access an array element (zero based index). If the array contains less
+  /// than index element, then null value are inserted in the array so that
+  /// its size is index+1.
+  /// (You may need to say 'value[0u]' to get your compiler to distinguish
+  /// this from the operator[] which takes a string.)
+  Value& operator[](ArrayIndex index);
+  Value& operator[](int index);
+  ///@}
+
+  ///@{
+  /// Access an array element (zero based index).
+  /// (You may need to say 'value[0u]' to get your compiler to distinguish
+  /// this from the operator[] which takes a string.)
+  const Value& operator[](ArrayIndex index) const;
+  const Value& operator[](int index) const;
+  ///@}
+
+  /// If the array contains at least index+1 elements, returns the element
+  /// value, otherwise returns defaultValue.
+  Value get(ArrayIndex index, const Value& defaultValue) const;
+  /// Return true if index < size().
+  bool isValidIndex(ArrayIndex index) const;
+  /// \brief Append value to array at the end.
+  ///
+  /// Equivalent to jsonvalue[jsonvalue.size()] = value;
+  Value& append(const Value& value);
+  Value& append(Value&& value);
+
+  /// \brief Insert value in array at specific index
+  bool insert(ArrayIndex index, const Value& newValue);
+  bool insert(ArrayIndex index, Value&& newValue);
+
+  /// Access an object value by name, create a null member if it does not exist.
+  /// \note Because of our implementation, keys are limited to 2^30 -1 chars.
+  /// Exceeding that will cause an exception.
+  Value& operator[](const char* key);
+  /// Access an object value by name, returns null if there is no member with
+  /// that name.
+  const Value& operator[](const char* key) const;
+  /// Access an object value by name, create a null member if it does not exist.
+  /// \param key may contain embedded nulls.
+  Value& operator[](const String& key);
+  /// Access an object value by name, returns null if there is no member with
+  /// that name.
+  /// \param key may contain embedded nulls.
+  const Value& operator[](const String& key) const;
+  /** \brief Access an object value by name, create a null member if it does not
+   * exist.
+   *
+   * If the object has no entry for that name, then the member name used to
+   * store the new entry is not duplicated.
+   * Example of use:
+   *   \code
+   *   Json::Value object;
+   *   static const StaticString code("code");
+   *   object[code] = 1234;
+   *   \endcode
+   */
+  Value& operator[](const StaticString& key);
+  /// Return the member named key if it exist, defaultValue otherwise.
+  /// \note deep copy
+  Value get(const char* key, const Value& defaultValue) const;
+  /// Return the member named key if it exist, defaultValue otherwise.
+  /// \note deep copy
+  /// \note key may contain embedded nulls.
+  Value get(const char* begin, const char* end,
+            const Value& defaultValue) const;
+  /// Return the member named key if it exist, defaultValue otherwise.
+  /// \note deep copy
+  /// \param key may contain embedded nulls.
+  Value get(const String& key, const Value& defaultValue) const;
+  /// Most general and efficient version of isMember()const, get()const,
+  /// and operator[]const
+  /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
+  Value const* find(char const* begin, char const* end) const;
+  /// Most general and efficient version of object-mutators.
+  /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
+  /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue.
+  Value* demand(char const* begin, char const* end);
+  /// \brief Remove and return the named member.
+  ///
+  /// Do nothing if it did not exist.
+  /// \pre type() is objectValue or nullValue
+  /// \post type() is unchanged
+  void removeMember(const char* key);
+  /// Same as removeMember(const char*)
+  /// \param key may contain embedded nulls.
+  void removeMember(const String& key);
+  /// Same as removeMember(const char* begin, const char* end, Value* removed),
+  /// but 'key' is null-terminated.
+  bool removeMember(const char* key, Value* removed);
+  /** \brief Remove the named map member.
+   *
+   *  Update 'removed' iff removed.
+   *  \param key may contain embedded nulls.
+   *  \return true iff removed (no exceptions)
+   */
+  bool removeMember(String const& key, Value* removed);
+  /// Same as removeMember(String const& key, Value* removed)
+  bool removeMember(const char* begin, const char* end, Value* removed);
+  /** \brief Remove the indexed array element.
+   *
+   *  O(n) expensive operations.
+   *  Update 'removed' iff removed.
+   *  \return true if removed (no exceptions)
+   */
+  bool removeIndex(ArrayIndex index, Value* removed);
+
+  /// Return true if the object has a member named key.
+  /// \note 'key' must be null-terminated.
+  bool isMember(const char* key) const;
+  /// Return true if the object has a member named key.
+  /// \param key may contain embedded nulls.
+  bool isMember(const String& key) const;
+  /// Same as isMember(String const& key)const
+  bool isMember(const char* begin, const char* end) const;
+
+  /// \brief Return a list of the member names.
+  ///
+  /// If null, return an empty list.
+  /// \pre type() is objectValue or nullValue
+  /// \post if type() was nullValue, it remains nullValue
+  Members getMemberNames() const;
+
+  /// \deprecated Always pass len.
+  JSONCPP_DEPRECATED("Use setComment(String const&) instead.")
+  void setComment(const char* comment, CommentPlacement placement) {
+    setComment(String(comment, strlen(comment)), placement);
+  }
+  /// Comments must be //... or /* ... */
+  void setComment(const char* comment, size_t len, CommentPlacement placement) {
+    setComment(String(comment, len), placement);
+  }
+  /// Comments must be //... or /* ... */
+  void setComment(String comment, CommentPlacement placement);
+  bool hasComment(CommentPlacement placement) const;
+  /// Include delimiters and embedded newlines.
+  String getComment(CommentPlacement placement) const;
+
+  String toStyledString() const;
+
+  const_iterator begin() const;
+  const_iterator end() const;
+
+  iterator begin();
+  iterator end();
+
+  // Accessors for the [start, limit) range of bytes within the JSON text from
+  // which this value was parsed, if any.
+  void setOffsetStart(ptrdiff_t start);
+  void setOffsetLimit(ptrdiff_t limit);
+  ptrdiff_t getOffsetStart() const;
+  ptrdiff_t getOffsetLimit() const;
+
+private:
+  void setType(ValueType v) {
+    bits_.value_type_ = static_cast<unsigned char>(v);
+  }
+  bool isAllocated() const { return bits_.allocated_; }
+  void setIsAllocated(bool v) { bits_.allocated_ = v; }
+
+  void initBasic(ValueType type, bool allocated = false);
+  void dupPayload(const Value& other);
+  void releasePayload();
+  void dupMeta(const Value& other);
+
+  Value& resolveReference(const char* key);
+  Value& resolveReference(const char* key, const char* end);
+
+  // struct MemberNamesTransform
+  //{
+  //   typedef const char *result_type;
+  //   const char *operator()( const CZString &name ) const
+  //   {
+  //      return name.c_str();
+  //   }
+  //};
+
+  union ValueHolder {
+    LargestInt int_;
+    LargestUInt uint_;
+    double real_;
+    bool bool_;
+    char* string_; // if allocated_, ptr to { unsigned, char[] }.
+    ObjectValues* map_;
+  } value_;
+
+  struct {
+    // Really a ValueType, but types should agree for bitfield packing.
+    unsigned int value_type_ : 8;
+    // Unless allocated_, string_ must be null-terminated.
+    unsigned int allocated_ : 1;
+  } bits_;
+
+  class Comments {
+  public:
+    Comments() = default;
+    Comments(const Comments& that);
+    Comments(Comments&& that) noexcept;
+    Comments& operator=(const Comments& that);
+    Comments& operator=(Comments&& that) noexcept;
+    bool has(CommentPlacement slot) const;
+    String get(CommentPlacement slot) const;
+    void set(CommentPlacement slot, String comment);
+
+  private:
+    using Array = std::array<String, numberOfCommentPlacement>;
+    std::unique_ptr<Array> ptr_;
+  };
+  Comments comments_;
+
+  // [start, limit) byte offsets in the source JSON text from which this Value
+  // was extracted.
+  ptrdiff_t start_;
+  ptrdiff_t limit_;
+};
+
+template <> inline bool Value::as<bool>() const { return asBool(); }
+template <> inline bool Value::is<bool>() const { return isBool(); }
+
+template <> inline Int Value::as<Int>() const { return asInt(); }
+template <> inline bool Value::is<Int>() const { return isInt(); }
+
+template <> inline UInt Value::as<UInt>() const { return asUInt(); }
+template <> inline bool Value::is<UInt>() const { return isUInt(); }
+
+#if defined(JSON_HAS_INT64)
+template <> inline Int64 Value::as<Int64>() const { return asInt64(); }
+template <> inline bool Value::is<Int64>() const { return isInt64(); }
+
+template <> inline UInt64 Value::as<UInt64>() const { return asUInt64(); }
+template <> inline bool Value::is<UInt64>() const { return isUInt64(); }
+#endif
+
+template <> inline double Value::as<double>() const { return asDouble(); }
+template <> inline bool Value::is<double>() const { return isDouble(); }
+
+template <> inline String Value::as<String>() const { return asString(); }
+template <> inline bool Value::is<String>() const { return isString(); }
+
+/// These `as` specializations are type conversions, and do not have a
+/// corresponding `is`.
+template <> inline float Value::as<float>() const { return asFloat(); }
+template <> inline const char* Value::as<const char*>() const {
+  return asCString();
+}
+
+/** \brief Experimental and untested: represents an element of the "path" to
+ * access a node.
+ */
+class JSON_API PathArgument {
+public:
+  friend class Path;
+
+  PathArgument();
+  PathArgument(ArrayIndex index);
+  PathArgument(const char* key);
+  PathArgument(String key);
+
+private:
+  enum Kind { kindNone = 0, kindIndex, kindKey };
+  String key_;
+  ArrayIndex index_{};
+  Kind kind_{kindNone};
+};
+
+/** \brief Experimental and untested: represents a "path" to access a node.
+ *
+ * Syntax:
+ * - "." => root node
+ * - ".[n]" => elements at index 'n' of root node (an array value)
+ * - ".name" => member named 'name' of root node (an object value)
+ * - ".name1.name2.name3"
+ * - ".[0][1][2].name1[3]"
+ * - ".%" => member name is provided as parameter
+ * - ".[%]" => index is provided as parameter
+ */
+class JSON_API Path {
+public:
+  Path(const String& path, const PathArgument& a1 = PathArgument(),
+       const PathArgument& a2 = PathArgument(),
+       const PathArgument& a3 = PathArgument(),
+       const PathArgument& a4 = PathArgument(),
+       const PathArgument& a5 = PathArgument());
+
+  const Value& resolve(const Value& root) const;
+  Value resolve(const Value& root, const Value& defaultValue) const;
+  /// Creates the "path" to access the specified node and returns a reference on
+  /// the node.
+  Value& make(Value& root) const;
+
+private:
+  using InArgs = std::vector<const PathArgument*>;
+  using Args = std::vector<PathArgument>;
+
+  void makePath(const String& path, const InArgs& in);
+  void addPathInArg(const String& path, const InArgs& in,
+                    InArgs::const_iterator& itInArg, PathArgument::Kind kind);
+  static void invalidPath(const String& path, int location);
+
+  Args args_;
+};
+
+/** \brief base class for Value iterators.
+ *
+ */
+class JSON_API ValueIteratorBase {
+public:
+  using iterator_category = std::bidirectional_iterator_tag;
+  using size_t = unsigned int;
+  using difference_type = int;
+  using SelfType = ValueIteratorBase;
+
+  bool operator==(const SelfType& other) const { return isEqual(other); }
+
+  bool operator!=(const SelfType& other) const { return !isEqual(other); }
+
+  difference_type operator-(const SelfType& other) const {
+    return other.computeDistance(*this);
+  }
+
+  /// Return either the index or the member name of the referenced value as a
+  /// Value.
+  Value key() const;
+
+  /// Return the index of the referenced Value, or -1 if it is not an
+  /// arrayValue.
+  UInt index() const;
+
+  /// Return the member name of the referenced Value, or "" if it is not an
+  /// objectValue.
+  /// \note Avoid `c_str()` on result, as embedded zeroes are possible.
+  String name() const;
+
+  /// Return the member name of the referenced Value. "" if it is not an
+  /// objectValue.
+  /// \deprecated This cannot be used for UTF-8 strings, since there can be
+  /// embedded nulls.
+  JSONCPP_DEPRECATED("Use `key = name();` instead.")
+  char const* memberName() const;
+  /// Return the member name of the referenced Value, or NULL if it is not an
+  /// objectValue.
+  /// \note Better version than memberName(). Allows embedded nulls.
+  char const* memberName(char const** end) const;
+
+protected:
+  /*! Internal utility functions to assist with implementing
+   *   other iterator functions. The const and non-const versions
+   *   of the "deref" protected methods expose the protected
+   *   current_ member variable in a way that can often be
+   *   optimized away by the compiler.
+   */
+  const Value& deref() const;
+  Value& deref();
+
+  void increment();
+
+  void decrement();
+
+  difference_type computeDistance(const SelfType& other) const;
+
+  bool isEqual(const SelfType& other) const;
+
+  void copy(const SelfType& other);
+
+private:
+  Value::ObjectValues::iterator current_;
+  // Indicates that iterator is for a null value.
+  bool isNull_{true};
+
+public:
+  // For some reason, BORLAND needs these at the end, rather
+  // than earlier. No idea why.
+  ValueIteratorBase();
+  explicit ValueIteratorBase(const Value::ObjectValues::iterator& current);
+};
+
+/** \brief const iterator for object and array value.
+ *
+ */
+class JSON_API ValueConstIterator : public ValueIteratorBase {
+  friend class Value;
+
+public:
+  using value_type = const Value;
+  // typedef unsigned int size_t;
+  // typedef int difference_type;
+  using reference = const Value&;
+  using pointer = const Value*;
+  using SelfType = ValueConstIterator;
+
+  ValueConstIterator();
+  ValueConstIterator(ValueIterator const& other);
+
+private:
+  /*! \internal Use by Value to create an iterator.
+   */
+  explicit ValueConstIterator(const Value::ObjectValues::iterator& current);
+
+public:
+  SelfType& operator=(const ValueIteratorBase& other);
+
+  SelfType operator++(int) {
+    SelfType temp(*this);
+    ++*this;
+    return temp;
+  }
+
+  SelfType operator--(int) {
+    SelfType temp(*this);
+    --*this;
+    return temp;
+  }
+
+  SelfType& operator--() {
+    decrement();
+    return *this;
+  }
+
+  SelfType& operator++() {
+    increment();
+    return *this;
+  }
+
+  reference operator*() const { return deref(); }
+
+  pointer operator->() const { return &deref(); }
+};
+
+/** \brief Iterator for object and array value.
+ */
+class JSON_API ValueIterator : public ValueIteratorBase {
+  friend class Value;
+
+public:
+  using value_type = Value;
+  using size_t = unsigned int;
+  using difference_type = int;
+  using reference = Value&;
+  using pointer = Value*;
+  using SelfType = ValueIterator;
+
+  ValueIterator();
+  explicit ValueIterator(const ValueConstIterator& other);
+  ValueIterator(const ValueIterator& other);
+
+private:
+  /*! \internal Use by Value to create an iterator.
+   */
+  explicit ValueIterator(const Value::ObjectValues::iterator& current);
+
+public:
+  SelfType& operator=(const SelfType& other);
+
+  SelfType operator++(int) {
+    SelfType temp(*this);
+    ++*this;
+    return temp;
+  }
+
+  SelfType operator--(int) {
+    SelfType temp(*this);
+    --*this;
+    return temp;
+  }
+
+  SelfType& operator--() {
+    decrement();
+    return *this;
+  }
+
+  SelfType& operator++() {
+    increment();
+    return *this;
+  }
+
+  /*! The return value of non-const iterators can be
+   *  changed, so the these functions are not const
+   *  because the returned references/pointers can be used
+   *  to change state of the base class.
+   */
+  reference operator*() const { return const_cast<reference>(deref()); }
+  pointer operator->() const { return const_cast<pointer>(&deref()); }
+};
+
+inline void swap(Value& a, Value& b) { a.swap(b); }
+
+} // namespace Json
+
+#pragma pack(pop)
+
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(pop)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#endif // JSON_H_INCLUDED

+ 28 - 0
include/json/version.h

@@ -0,0 +1,28 @@
+#ifndef JSON_VERSION_H_INCLUDED
+#define JSON_VERSION_H_INCLUDED
+
+// Note: version must be updated in three places when doing a release. This
+// annoying process ensures that amalgamate, CMake, and meson all report the
+// correct version.
+// 1. /meson.build
+// 2. /include/json/version.h
+// 3. /CMakeLists.txt
+// IMPORTANT: also update the SOVERSION!!
+
+#define JSONCPP_VERSION_STRING "1.9.5"
+#define JSONCPP_VERSION_MAJOR 1
+#define JSONCPP_VERSION_MINOR 9
+#define JSONCPP_VERSION_PATCH 5
+#define JSONCPP_VERSION_QUALIFIER
+#define JSONCPP_VERSION_HEXA                                                   \
+  ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) |             \
+   (JSONCPP_VERSION_PATCH << 8))
+
+#ifdef JSONCPP_USING_SECURE_MEMORY
+#undef JSONCPP_USING_SECURE_MEMORY
+#endif
+#define JSONCPP_USING_SECURE_MEMORY 0
+// If non-zero, the library zeroes any memory that it has allocated before
+// it frees its memory.
+
+#endif // JSON_VERSION_H_INCLUDED

+ 370 - 0
include/json/writer.h

@@ -0,0 +1,370 @@
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_WRITER_H_INCLUDED
+#define JSON_WRITER_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "value.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <ostream>
+#include <string>
+#include <vector>
+
+// Disable warning C4251: <data member>: <type> needs to have dll-interface to
+// be used by...
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) && defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4251)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#pragma pack(push)
+#pragma pack()
+
+namespace Json {
+
+class Value;
+
+/**
+ *
+ * Usage:
+ *  \code
+ *  using namespace Json;
+ *  void writeToStdout(StreamWriter::Factory const& factory, Value const& value)
+ * { std::unique_ptr<StreamWriter> const writer( factory.newStreamWriter());
+ *    writer->write(value, &std::cout);
+ *    std::cout << std::endl;  // add lf and flush
+ *  }
+ *  \endcode
+ */
+class JSON_API StreamWriter {
+protected:
+  OStream* sout_; // not owned; will not delete
+public:
+  StreamWriter();
+  virtual ~StreamWriter();
+  /** Write Value into document as configured in sub-class.
+   *   Do not take ownership of sout, but maintain a reference during function.
+   *   \pre sout != NULL
+   *   \return zero on success (For now, we always return zero, so check the
+   *   stream instead.) \throw std::exception possibly, depending on
+   * configuration
+   */
+  virtual int write(Value const& root, OStream* sout) = 0;
+
+  /** \brief A simple abstract factory.
+   */
+  class JSON_API Factory {
+  public:
+    virtual ~Factory();
+    /** \brief Allocate a CharReader via operator new().
+     * \throw std::exception if something goes wrong (e.g. invalid settings)
+     */
+    virtual StreamWriter* newStreamWriter() const = 0;
+  }; // Factory
+};   // StreamWriter
+
+/** \brief Write into stringstream, then return string, for convenience.
+ * A StreamWriter will be created from the factory, used, and then deleted.
+ */
+String JSON_API writeString(StreamWriter::Factory const& factory,
+                            Value const& root);
+
+/** \brief Build a StreamWriter implementation.
+
+* Usage:
+*   \code
+*   using namespace Json;
+*   Value value = ...;
+*   StreamWriterBuilder builder;
+*   builder["commentStyle"] = "None";
+*   builder["indentation"] = "   ";  // or whatever you like
+*   std::unique_ptr<Json::StreamWriter> writer(
+*      builder.newStreamWriter());
+*   writer->write(value, &std::cout);
+*   std::cout << std::endl;  // add lf and flush
+*   \endcode
+*/
+class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
+public:
+  // Note: We use a Json::Value so that we can add data-members to this class
+  // without a major version bump.
+  /** Configuration of this builder.
+   *  Available settings (case-sensitive):
+   *  - "commentStyle": "None" or "All"
+   *  - "indentation":  "<anything>".
+   *  - Setting this to an empty string also omits newline characters.
+   *  - "enableYAMLCompatibility": false or true
+   *  - slightly change the whitespace around colons
+   *  - "dropNullPlaceholders": false or true
+   *  - Drop the "null" string from the writer's output for nullValues.
+   *    Strictly speaking, this is not valid JSON. But when the output is being
+   *    fed to a browser's JavaScript, it makes for smaller output and the
+   *    browser can handle the output just fine.
+   *  - "useSpecialFloats": false or true
+   *  - If true, outputs non-finite floating point values in the following way:
+   *    NaN values as "NaN", positive infinity as "Infinity", and negative
+   *  infinity as "-Infinity".
+   *  - "precision": int
+   *  - Number of precision digits for formatting of real values.
+   *  - "precisionType": "significant"(default) or "decimal"
+   *  - Type of precision for formatting of real values.
+   *  - "emitUTF8": false or true
+   *  - If true, outputs raw UTF8 strings instead of escaping them.
+
+   *  You can examine 'settings_` yourself
+   *  to see the defaults. You can also write and read them just like any
+   *  JSON Value.
+   *  \sa setDefaults()
+   */
+  Json::Value settings_;
+
+  StreamWriterBuilder();
+  ~StreamWriterBuilder() override;
+
+  /**
+   * \throw std::exception if something goes wrong (e.g. invalid settings)
+   */
+  StreamWriter* newStreamWriter() const override;
+
+  /** \return true if 'settings' are legal and consistent;
+   *   otherwise, indicate bad settings via 'invalid'.
+   */
+  bool validate(Json::Value* invalid) const;
+  /** A simple way to update a specific setting.
+   */
+  Value& operator[](const String& key);
+
+  /** Called by ctor, but you can use this to reset settings_.
+   * \pre 'settings' != NULL (but Json::null is fine)
+   * \remark Defaults:
+   * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults
+   */
+  static void setDefaults(Json::Value* settings);
+};
+
+/** \brief Abstract class for writers.
+ * \deprecated Use StreamWriter. (And really, this is an implementation detail.)
+ */
+class JSON_API Writer {
+public:
+  virtual ~Writer();
+
+  virtual String write(const Value& root) = 0;
+};
+
+/** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format
+ *without formatting (not human friendly).
+ *
+ * The JSON document is written in a single line. It is not intended for 'human'
+ *consumption,
+ * but may be useful to support feature such as RPC where bandwidth is limited.
+ * \sa Reader, Value
+ * \deprecated Use StreamWriterBuilder.
+ */
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4996) // Deriving from deprecated class
+#endif
+class JSON_API FastWriter
+    : public Writer {
+public:
+  FastWriter();
+  ~FastWriter() override = default;
+
+  void enableYAMLCompatibility();
+
+  /** \brief Drop the "null" string from the writer's output for nullValues.
+   * Strictly speaking, this is not valid JSON. But when the output is being
+   * fed to a browser's JavaScript, it makes for smaller output and the
+   * browser can handle the output just fine.
+   */
+  void dropNullPlaceholders();
+
+  void omitEndingLineFeed();
+
+public: // overridden from Writer
+  String write(const Value& root) override;
+
+private:
+  void writeValue(const Value& value);
+
+  String document_;
+  bool yamlCompatibilityEnabled_{false};
+  bool dropNullPlaceholders_{false};
+  bool omitEndingLineFeed_{false};
+};
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
+ *human friendly way.
+ *
+ * The rules for line break and indent are as follow:
+ * - Object value:
+ *     - if empty then print {} without indent and line break
+ *     - if not empty the print '{', line break & indent, print one value per
+ *line
+ *       and then unindent and line break and print '}'.
+ * - Array value:
+ *     - if empty then print [] without indent and line break
+ *     - if the array contains no object value, empty array or some other value
+ *types,
+ *       and all the values fit on one lines, then print the array on a single
+ *line.
+ *     - otherwise, it the values do not fit on one line, or the array contains
+ *       object or non empty array, then print one value per line.
+ *
+ * If the Value have comments then they are outputted according to their
+ *#CommentPlacement.
+ *
+ * \sa Reader, Value, Value::setComment()
+ * \deprecated Use StreamWriterBuilder.
+ */
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4996) // Deriving from deprecated class
+#endif
+class JSON_API
+    StyledWriter : public Writer {
+public:
+  StyledWriter();
+  ~StyledWriter() override = default;
+
+public: // overridden from Writer
+  /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
+   * \param root Value to serialize.
+   * \return String containing the JSON document that represents the root value.
+   */
+  String write(const Value& root) override;
+
+private:
+  void writeValue(const Value& value);
+  void writeArrayValue(const Value& value);
+  bool isMultilineArray(const Value& value);
+  void pushValue(const String& value);
+  void writeIndent();
+  void writeWithIndent(const String& value);
+  void indent();
+  void unindent();
+  void writeCommentBeforeValue(const Value& root);
+  void writeCommentAfterValueOnSameLine(const Value& root);
+  static bool hasCommentForValue(const Value& value);
+  static String normalizeEOL(const String& text);
+
+  using ChildValues = std::vector<String>;
+
+  ChildValues childValues_;
+  String document_;
+  String indentString_;
+  unsigned int rightMargin_{74};
+  unsigned int indentSize_{3};
+  bool addChildValues_{false};
+};
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
+ human friendly way,
+     to a stream rather than to a string.
+ *
+ * The rules for line break and indent are as follow:
+ * - Object value:
+ *     - if empty then print {} without indent and line break
+ *     - if not empty the print '{', line break & indent, print one value per
+ line
+ *       and then unindent and line break and print '}'.
+ * - Array value:
+ *     - if empty then print [] without indent and line break
+ *     - if the array contains no object value, empty array or some other value
+ types,
+ *       and all the values fit on one lines, then print the array on a single
+ line.
+ *     - otherwise, it the values do not fit on one line, or the array contains
+ *       object or non empty array, then print one value per line.
+ *
+ * If the Value have comments then they are outputted according to their
+ #CommentPlacement.
+ *
+ * \sa Reader, Value, Value::setComment()
+ * \deprecated Use StreamWriterBuilder.
+ */
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4996) // Deriving from deprecated class
+#endif
+class JSON_API
+    StyledStreamWriter {
+public:
+  /**
+   * \param indentation Each level will be indented by this amount extra.
+   */
+  StyledStreamWriter(String indentation = "\t");
+  ~StyledStreamWriter() = default;
+
+public:
+  /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
+   * \param out Stream to write to. (Can be ostringstream, e.g.)
+   * \param root Value to serialize.
+   * \note There is no point in deriving from Writer, since write() should not
+   * return a value.
+   */
+  void write(OStream& out, const Value& root);
+
+private:
+  void writeValue(const Value& value);
+  void writeArrayValue(const Value& value);
+  bool isMultilineArray(const Value& value);
+  void pushValue(const String& value);
+  void writeIndent();
+  void writeWithIndent(const String& value);
+  void indent();
+  void unindent();
+  void writeCommentBeforeValue(const Value& root);
+  void writeCommentAfterValueOnSameLine(const Value& root);
+  static bool hasCommentForValue(const Value& value);
+  static String normalizeEOL(const String& text);
+
+  using ChildValues = std::vector<String>;
+
+  ChildValues childValues_;
+  OStream* document_;
+  String indentString_;
+  unsigned int rightMargin_{74};
+  String indentation_;
+  bool addChildValues_ : 1;
+  bool indented_ : 1;
+};
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#if defined(JSON_HAS_INT64)
+String JSON_API valueToString(Int value);
+String JSON_API valueToString(UInt value);
+#endif // if defined(JSON_HAS_INT64)
+String JSON_API valueToString(LargestInt value);
+String JSON_API valueToString(LargestUInt value);
+String JSON_API valueToString(
+    double value, unsigned int precision = Value::defaultRealPrecision,
+    PrecisionType precisionType = PrecisionType::significantDigits);
+String JSON_API valueToString(bool value);
+String JSON_API valueToQuotedString(const char* value);
+
+/// \brief Output using the StyledStreamWriter.
+/// \see Json::operator>>()
+JSON_API OStream& operator<<(OStream&, const Value& root);
+
+} // namespace Json
+
+#pragma pack(pop)
+
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(pop)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#endif // JSON_WRITER_H_INCLUDED

+ 19 - 0
include/plc/plcJsonConfig.cpp

@@ -0,0 +1,19 @@
+/**
+  * @project shutter_verify
+  * @brief   $BRIEF$
+  * @author  lz
+  * @data    2023/4/13
+ **/
+#include "plcJsonConfig.h"
+#include <utility>
+
+plcJsonConfig::plcJsonConfig(std::string path) {
+    m_path = path;
+}
+
+std::string plcJsonConfig::ip() {
+    std::string ip;
+    ReadJsonFile(m_path, m_config);
+    JV_STRING(m_config, "ip", ip, DEFAULT_STRING);
+    return ip;
+}

+ 21 - 0
include/plc/plcJsonConfig.h

@@ -0,0 +1,21 @@
+/**
+  * @project shutter_verify
+  * @brief   $BRIEF$
+  * @author  lz
+  * @data    2023/4/13
+ **/
+#pragma once
+
+#include "json/json.h"
+
+class plcJsonConfig {
+public:
+    explicit plcJsonConfig(std::string path);
+    ~plcJsonConfig() = default;
+
+    std::string ip();
+
+private:
+    std::string m_path;
+    Json::Value m_config;
+};

+ 78 - 0
include/plc/s7_plc.cpp

@@ -0,0 +1,78 @@
+#include "s7_plc.h"
+
+S7PLC::S7PLC() : bConnected_(false) {
+}
+
+S7PLC::~S7PLC() {
+    disconnect();
+}
+
+bool S7PLC::getConnection() {
+    return bConnected_;
+}
+
+bool S7PLC::connect(std::string ip) {
+    std::lock_guard<std::mutex> lck(mutex_);
+    int ret = client_.ConnectTo(ip.c_str(), 0, 1);
+    bConnected_ = (ret == 0);
+    return bConnected_;
+}
+
+bool S7PLC::ReadShorts(int DBNumber, int start, int size, short *pdata) {
+    short *plc_data = (short *) malloc(size * sizeof(short));
+    bool ret = read_dbs(DBNumber, start * sizeof(short), size * sizeof(short), pdata);
+    if (ret) {
+        reverse_byte(pdata, size * sizeof(short), plc_data);
+        for (int i = 0; i < size; ++i)
+            pdata[i] = plc_data[size - i - 1];
+    }
+    free(plc_data);
+    return ret;
+
+}
+
+bool S7PLC::WriteShorts(int DBNumber, int start, int size, short *pdata) {
+    short *plc_data = (short *) malloc(size * sizeof(short));
+    memcpy(plc_data, pdata, size * sizeof(short));
+    for (int i = 0; i < size; ++i)
+        plc_data[i] = HTON(plc_data[i]);
+
+    bool ret = write_dbs(DBNumber, start * sizeof(short), size * sizeof(short), plc_data);
+    free(plc_data);
+    return ret;
+}
+
+bool S7PLC::read_dbs(int DBNumber, int start, int size, void *pdata) {
+    std::lock_guard<std::mutex> lck(mutex_);
+    usleep(1000 * 50);
+    if (bConnected_ == false)
+        return false;
+
+    int ret = client_.AsDBRead(DBNumber, start, size, pdata);
+
+    return ret == 0;
+}
+
+bool S7PLC::write_dbs(int DBNumber, int start, int size, void *pdata) {
+    std::lock_guard<std::mutex> lck(mutex_);
+    usleep(1000 * 50);
+    if (bConnected_ == false)
+        return false;
+
+    int ret = client_.AsDBWrite(DBNumber, start, size, pdata);
+
+    return ret == 0;
+}
+
+void S7PLC::disconnect() {
+    std::lock_guard<std::mutex> lck(mutex_);
+    client_.Disconnect();
+}
+
+void S7PLC::reverse_byte(void *pdata, int num_byte, void *out) {
+    char *pin = (char *) pdata;
+    char *pout = (char *) out;
+    for (int i = 0; i < num_byte; ++i) {
+        pout[i] = pin[num_byte - i - 1];
+    }
+}

+ 39 - 0
include/plc/s7_plc.h

@@ -0,0 +1,39 @@
+#ifndef S7__PLC__H
+#define S7__PLC__H
+
+#include <s7_client.h>
+#include <mutex>
+#include <iostream>
+
+class S7PLC {
+public:
+#define HTON(T) ((T) << 8) | ((T) >> 8)
+protected:
+    bool bConnected_;
+    std::mutex mutex_;
+    TSnap7Client client_;
+public:
+    S7PLC();
+
+    ~S7PLC();
+
+    bool connect(std::string ip);
+
+    bool getConnection();
+
+    bool ReadShorts(int DBNumber, int start, int size, short *pdata);
+
+    bool WriteShorts(int DBNumber, int start, int size, short *pdata);
+
+    void disconnect();
+
+private:
+    bool read_dbs(int DBNumber, int start, int size, void *pdata);
+
+    bool write_dbs(int DBNumber, int start, int size, void *pdata);
+
+    void reverse_byte(void *pdata, int num_byte, void *out);
+
+};
+
+#endif // !S7__PLC__H

+ 217 - 0
include/plc/snap7_buf.cpp

@@ -0,0 +1,217 @@
+#include "snap7_buf.h"
+#include <string>
+#include <string.h>
+
+Snap7_buf::Snap7_buf() {
+    m_id = 0;
+    m_start_index = 0;
+    m_size = 0;
+    mp_buf_obverse = nullptr;
+    mp_buf_reverse = nullptr;
+    m_communication_mode = NO_COMMUNICATION;
+
+}
+
+Snap7_buf::Snap7_buf(const Snap7_buf &other) {
+    m_id = other.m_id;
+    m_start_index = other.m_start_index;
+    m_communication_mode = other.m_communication_mode;
+
+    if (other.m_size > 0 && other.mp_buf_obverse != nullptr && other.mp_buf_reverse != nullptr) {
+        mp_buf_obverse = (void *) malloc(other.m_size);
+        memcpy(mp_buf_obverse, other.mp_buf_obverse, other.m_size);
+        mp_buf_reverse = (void *) malloc(other.m_size);
+        memcpy(mp_buf_reverse, other.mp_buf_reverse, other.m_size);
+        m_size = other.m_size;
+        m_variable_information_vector = other.m_variable_information_vector;
+    }
+}
+
+Snap7_buf &Snap7_buf::operator=(const Snap7_buf &other) {
+    m_id = other.m_id;
+    m_start_index = other.m_start_index;
+    m_communication_mode = other.m_communication_mode;
+
+    if (other.m_size > 0 && other.mp_buf_obverse != nullptr && other.mp_buf_reverse != nullptr) {
+        mp_buf_obverse = (void *) malloc(other.m_size);
+        memcpy(mp_buf_obverse, other.mp_buf_obverse, other.m_size);
+        mp_buf_reverse = (void *) malloc(other.m_size);
+        memcpy(mp_buf_reverse, other.mp_buf_reverse, other.m_size);
+        m_size = other.m_size;
+        m_variable_information_vector = other.m_variable_information_vector;
+    }
+    return *this;
+}
+
+Snap7_buf::~Snap7_buf() {
+    if (mp_buf_obverse) {
+        free(mp_buf_obverse);
+        mp_buf_obverse = NULL;
+    }
+    if (mp_buf_reverse) {
+        free(mp_buf_reverse);
+        mp_buf_reverse = NULL;
+    }
+}
+
+Snap7_buf::Snap7_buf(int id, int start_index, int size,
+                     std::vector<Snap7_buf::Variable_information> &variable_information_vector,
+                     Communication_mode communication_mode) {
+    m_id = id;
+    m_start_index = start_index;
+    m_communication_mode = communication_mode;
+
+    if (size > 0) {
+        mp_buf_obverse = (void *) malloc(size);
+        memset(mp_buf_obverse, 0, size);
+        mp_buf_reverse = (void *) malloc(size);
+        memset(mp_buf_reverse, 0, size);
+        m_size = size;
+        m_variable_information_vector = variable_information_vector;
+    }
+}
+
+Snap7_buf::Snap7_buf(int id, int start_index, int size, void *p_buf_obverse, void *p_buf_reverse,
+                     std::vector<Snap7_buf::Variable_information> &variable_information_vector,
+                     Communication_mode communication_mode) {
+    m_id = id;
+    m_start_index = start_index;
+    m_communication_mode = communication_mode;
+
+    if (size > 0 && p_buf_obverse != nullptr && p_buf_reverse != nullptr) {
+        mp_buf_obverse = (void *) malloc(size);
+        memcpy(mp_buf_obverse, p_buf_obverse, size);
+        mp_buf_reverse = (void *) malloc(size);
+        memcpy(mp_buf_reverse, p_buf_reverse, size);
+        m_size = size;
+        m_variable_information_vector = variable_information_vector;
+    }
+}
+
+void Snap7_buf::init(int id, int start_index, int size,
+                     std::vector<Snap7_buf::Variable_information> &variable_information_vector,
+                     Communication_mode communication_mode) {
+    m_id = id;
+    m_start_index = start_index;
+    m_communication_mode = communication_mode;
+
+    if (mp_buf_obverse) {
+        free(mp_buf_obverse);
+        mp_buf_obverse = NULL;
+    }
+    if (mp_buf_reverse) {
+        free(mp_buf_reverse);
+        mp_buf_reverse = NULL;
+    }
+
+    if (size > 0) {
+        mp_buf_obverse = (void *) malloc(size);
+        memset(mp_buf_obverse, 0, size);
+        mp_buf_reverse = (void *) malloc(size);
+        memset(mp_buf_reverse, 0, size);
+        m_size = size;
+        m_variable_information_vector = variable_information_vector;
+    }
+}
+
+void Snap7_buf::init(int id, int start_index, int size, void *p_buf_obverse, void *p_buf_reverse,
+                     std::vector<Snap7_buf::Variable_information> &variable_information_vector,
+                     Communication_mode communication_mode) {
+    m_id = id;
+    m_start_index = start_index;
+    m_communication_mode = communication_mode;
+
+    if (mp_buf_obverse) {
+        free(mp_buf_obverse);
+        mp_buf_obverse = NULL;
+    }
+    if (mp_buf_reverse) {
+        free(mp_buf_reverse);
+        mp_buf_reverse = NULL;
+    }
+
+    if (size > 0 && p_buf_obverse != nullptr && p_buf_reverse != nullptr) {
+        mp_buf_obverse = (void *) malloc(size);
+        memcpy(mp_buf_obverse, p_buf_obverse, size);
+        mp_buf_reverse = (void *) malloc(size);
+        memcpy(mp_buf_reverse, p_buf_reverse, size);
+        m_size = size;
+        m_variable_information_vector = variable_information_vector;
+    }
+}
+
+//正序数据 转为 倒序数据
+void Snap7_buf::obverse_to_reverse() {
+    char *p_in = (char *) mp_buf_obverse;
+    char *p_out = (char *) mp_buf_reverse;
+
+    for (auto &iter: m_variable_information_vector) {
+        for (int i = 0; i < iter.m_variable_count; ++i) {
+            for (int j = 0; j < iter.m_variable_size; ++j) {
+                p_out[iter.m_variable_index + iter.m_variable_size * i + j] = p_in[iter.m_variable_index +
+                                                                                   iter.m_variable_size * (i + 1) - j -
+                                                                                   1];
+            }
+        }
+//		for (int i = 0; i < iter.m_variable_count; ++i)
+//		{
+//			for (int j = iter.m_variable_index*i; j < iter.m_variable_index*i+iter.m_variable_size ; ++j)
+//			{
+//				p_out[j] = p_in[iter.m_variable_index*i+iter.m_variable_size - j -1];
+//			}
+//		}
+    }
+}
+
+//倒序数据 转为 正序数据
+void Snap7_buf::reverse_to_obverse() {
+    char *p_in = (char *) mp_buf_reverse;
+    char *p_out = (char *) mp_buf_obverse;
+
+
+    for (auto &iter: m_variable_information_vector) {
+        for (int i = 0; i < iter.m_variable_count; ++i) {
+            for (int j = 0; j < iter.m_variable_size; ++j) {
+                p_out[iter.m_variable_index + iter.m_variable_size * i + j] = p_in[iter.m_variable_index +
+                                                                                   iter.m_variable_size * (i + 1) - j -
+                                                                                   1];
+            }
+        }
+
+//		for (int i = 0; i < iter.m_variable_count; ++i)
+//		{
+//			for (int j = iter.m_variable_index*i; j < iter.m_variable_index*i+iter.m_variable_size ; ++j)
+//			{
+//				p_out[j] = p_in[iter.m_variable_index*i+iter.m_variable_size - j -1];
+//			}
+//		}
+    }
+}
+
+int Snap7_buf::get_id() {
+    return m_id;
+}
+
+int Snap7_buf::get_start_index() {
+    return m_start_index;
+}
+
+int Snap7_buf::get_size() {
+    return m_size;
+}
+
+void *Snap7_buf::get_buf_obverse() {
+    return mp_buf_obverse;
+}
+
+void *Snap7_buf::get_buf_reverse() {
+    return mp_buf_reverse;
+}
+
+Snap7_buf::Communication_mode Snap7_buf::get_communication_mode() {
+    return m_communication_mode;
+}
+
+void Snap7_buf::set_communication_mode(Communication_mode communication_mode) {
+    m_communication_mode = communication_mode;
+}

+ 98 - 0
include/plc/snap7_buf.h

@@ -0,0 +1,98 @@
+#ifndef NNXX_TESTS_SNAP7_BUF_H
+#define NNXX_TESTS_SNAP7_BUF_H
+
+#include <string>
+#include <map>
+#include <vector>
+#include <iostream>
+
+//Snap7协议的数据结构
+class Snap7_buf {
+public:
+    //通信模式
+    enum Communication_mode {
+        NO_COMMUNICATION = 0,    //不通信
+        ONCE_COMMUNICATION = 1,    //一次通信
+        LOOP_COMMUNICATION = 2,    //循环通信
+    };
+
+    //变量信息
+    struct Variable_information {
+        std::string m_variable_name;        //变量名称
+        std::string m_variable_type;        //变量类型, 使用 typeid(a).name() 获取
+        int m_variable_index;        //变量下标, 偏移量
+        int m_variable_size;        //变量类型大小
+        int m_variable_count;        //变量个数
+    };
+public:
+    Snap7_buf();
+
+    Snap7_buf(const Snap7_buf &other);
+
+    Snap7_buf &operator=(const Snap7_buf &other);
+
+    ~Snap7_buf();
+
+public://API functions
+    Snap7_buf(int id, int start_index, int size,
+              std::vector<Snap7_buf::Variable_information> &variable_information_vector,
+              Communication_mode communication_mode = NO_COMMUNICATION);
+
+    Snap7_buf(int id, int start_index, int size, void *p_buf_obverse, void *p_buf_reverse,
+              std::vector<Snap7_buf::Variable_information> &variable_information_vector,
+              Communication_mode communication_mode = NO_COMMUNICATION);
+
+    void init(int id, int start_index, int size,
+              std::vector<Snap7_buf::Variable_information> &variable_information_vector,
+              Communication_mode communication_mode = NO_COMMUNICATION);
+
+    void init(int id, int start_index, int size, void *p_buf_obverse, void *p_buf_reverse,
+              std::vector<Snap7_buf::Variable_information> &variable_information_vector,
+              Communication_mode communication_mode = NO_COMMUNICATION);
+
+
+    //正序数据 转为 倒序数据
+    void obverse_to_reverse();
+
+    //倒序数据 转为 正序数据
+    void reverse_to_obverse();
+
+public://get or set member variable
+    int get_id();
+
+    int get_start_index();
+
+    int get_size();
+
+    void *get_buf_obverse();
+
+    void *get_buf_reverse();
+
+    Communication_mode get_communication_mode();
+
+    void set_communication_mode(Communication_mode communication_mode);
+
+protected://member functions
+
+public://member variable
+
+    int m_id;                    //Snap7协议的数据块的编号
+    int m_start_index;            //Snap7协议的数据起始位下标
+    int m_size;                    //Snap7协议的数据字节大小
+    void *mp_buf_obverse;            //Snap7协议的正序数据指针, 和数据结构体进行强转, 内存由本类管理
+    void *mp_buf_reverse;            //Snap7协议的倒序数据指针, 用作s7通信, 内存由本类管理
+    //注:s7的通信的数据必须要倒序之后才能进行通信,
+
+//	std::map<std::string, Variable_information>			m_variable_information_map;
+    std::vector<Variable_information> m_variable_information_vector;
+
+    Communication_mode m_communication_mode;    //Snap7协议的通信模式
+    //注:s7协议传输很慢, 防止相同的数据重复发送...
+
+
+private:
+
+};
+
+
+#endif //NNXX_TESTS_SNAP7_BUF_H

+ 117 - 0
include/plc/snap7_clamp.cpp

@@ -0,0 +1,117 @@
+//
+// Created by huli on 2020/9/25.
+//
+
+#include "snap7_clamp.h"
+
+Snap7Clamp::Snap7Clamp() = default;
+Snap7Clamp::~Snap7Clamp() = default;
+
+//初始化 通信 模块。如下三选一
+Error_manager Snap7Clamp::communication_init()
+{
+	int t_index = 0;
+	std::vector<Snap7_buf::Variable_information>		t_variable_information_vector;
+
+	//往map通信缓存里面添加所需要的buf
+	std::unique_lock<std::mutex> t_lock1(m_receive_buf_lock);
+	std::unique_lock<std::mutex> t_lock2(m_send_buf_lock);
+	Snap7_buf t_snap7_buf;
+
+    // plcData
+    t_index = 0;
+    t_variable_information_vector.clear();
+    t_variable_information_vector.push_back(Snap7_buf::Variable_information{"pingpong", typeid(unsigned short ).name(), t_index,sizeof(unsigned short), 1 });
+    t_index += sizeof(unsigned short)*1;
+    t_variable_information_vector.push_back(Snap7_buf::Variable_information{"wheel_exist", typeid(unsigned short ).name(), t_index,sizeof(unsigned short), 1 });
+    t_index += sizeof(unsigned short)*1;
+    t_variable_information_vector.push_back(Snap7_buf::Variable_information{"offset", typeid(float ).name(), t_index,sizeof(float), 1 });
+    t_index += sizeof(float)*1;
+    t_variable_information_vector.push_back(Snap7_buf::Variable_information{"gap", typeid(float ).name(), t_index,sizeof(float), 1 });
+    t_index += sizeof(float)*1;
+    t_variable_information_vector.push_back(Snap7_buf::Variable_information{"clamp_completed", typeid(unsigned short ).name(), t_index,sizeof(unsigned short), 1 });
+    t_index += sizeof(unsigned short)*1;
+    t_variable_information_vector.push_back(Snap7_buf::Variable_information{"wheel_exist", typeid(unsigned short ).name(), t_index,sizeof(unsigned short), 1 });
+    t_index += sizeof(unsigned short)*1;
+    t_variable_information_vector.push_back(Snap7_buf::Variable_information{"offset", typeid(float ).name(), t_index,sizeof(float), 1 });
+    t_index += sizeof(float)*1;
+    t_variable_information_vector.push_back(Snap7_buf::Variable_information{"gap", typeid(float ).name(), t_index,sizeof(float), 1 });
+    t_index += sizeof(float)*1;
+    t_variable_information_vector.push_back(Snap7_buf::Variable_information{"clamp_completed", typeid(unsigned short ).name(), t_index,sizeof(unsigned short), 1 });
+    t_index += sizeof(unsigned short)*1;
+    t_variable_information_vector.push_back(Snap7_buf::Variable_information{"wheel_exist", typeid(unsigned short ).name(), t_index,sizeof(unsigned short), 1 });
+    t_index += sizeof(unsigned short)*1;
+    t_variable_information_vector.push_back(Snap7_buf::Variable_information{"offset", typeid(float ).name(), t_index,sizeof(float), 1 });
+    t_index += sizeof(float)*1;
+    t_variable_information_vector.push_back(Snap7_buf::Variable_information{"gap", typeid(float ).name(), t_index,sizeof(float), 1 });
+    t_index += sizeof(float)*1;
+    t_variable_information_vector.push_back(Snap7_buf::Variable_information{"clamp_completed", typeid(unsigned short ).name(), t_index,sizeof(unsigned short), 1 });
+    t_index += sizeof(unsigned short)*1;
+    t_variable_information_vector.push_back(Snap7_buf::Variable_information{"wheel_exist", typeid(unsigned short ).name(), t_index,sizeof(unsigned short), 1 });
+    t_index += sizeof(unsigned short)*1;
+    t_variable_information_vector.push_back(Snap7_buf::Variable_information{"offset", typeid(float ).name(), t_index,sizeof(float), 1 });
+    t_index += sizeof(float)*1;
+    t_variable_information_vector.push_back(Snap7_buf::Variable_information{"gap", typeid(float ).name(), t_index,sizeof(float), 1 });
+    t_index += sizeof(float)*1;
+    t_variable_information_vector.push_back(Snap7_buf::Variable_information{"clamp_completed", typeid(unsigned short ).name(), t_index,sizeof(unsigned short), 1 });
+    t_index += sizeof(unsigned short)*1;
+
+    LOG(INFO) << t_index;
+
+    t_snap7_buf.init(CLAMP_SAFETY_PLC_DBNUMBER , 0, sizeof(PLCData), t_variable_information_vector, Snap7_buf::LOOP_COMMUNICATION);
+//	m_send_buf_map[0] = t_snap7_buf;
+    m_receive_buf_map[0] = t_snap7_buf;
+
+//    t_index = 0;
+//    t_variable_information_vector.clear();
+//    t_variable_information_vector.push_back(Snap7_buf::Variable_information{"heart", typeid(unsigned short).name(), t_index,sizeof(unsigned short), 1 });
+//
+//    t_snap7_buf.init(CLAMP_SAFETY_PLC_DBNUMBER , 50, sizeof(unsigned short), t_variable_information_vector, Snap7_buf::LOOP_COMMUNICATION);
+//    m_receive_buf_map[0] = t_snap7_buf;
+
+    plcJsonConfig config(ETC_PATH"/etc/plc.json");
+    return Snap7_communication_base::communication_init(config.ip());
+    return SUCCESS;
+}
+
+//反初始化 通信 模块。
+Error_manager Snap7Clamp::communication_uninit()
+{
+	return Snap7_communication_base::communication_uninit();
+}
+
+//更新数据
+Error_manager Snap7Clamp::updata_receive_buf()
+{
+    std::unique_lock<std::mutex> t_lock1(m_receive_buf_lock);
+    std::unique_lock<std::mutex> t_lock2(m_data_lock);
+//    static unsigned short heart;
+    memcpy(&plcData, m_receive_buf_map[0].mp_buf_obverse, m_receive_buf_map[0].m_size);
+
+    Error_manager ret = Error_code::SUCCESS;
+
+//    if (heart == m_heart) {
+//        printf("---Debug %s %d : heart not change %d ---> %d\n", __func__, __LINE__, m_heart, heart);
+//        ret = Error_code::FAILED;
+//    }
+//    m_heart = heart;
+	return ret;
+}
+
+Error_manager Snap7Clamp::updata_send_buf()
+{
+	std::unique_lock<std::mutex> t_lock1(m_send_buf_lock);
+	std::unique_lock<std::mutex> t_lock2(m_data_lock);
+
+    if (plcData.pingpong != 0) {
+        plcData.info();
+    }
+
+	memcpy(m_send_buf_map[0].mp_buf_obverse, &plcData, m_send_buf_map[0].m_size);
+
+    return Error_code::SUCCESS;
+}
+
+
+
+

+ 105 - 0
include/plc/snap7_clamp.h

@@ -0,0 +1,105 @@
+//
+// Created by huli on 2020/9/25.
+//
+#pragma once
+
+#include "tool/singleton.h"
+#include "snap7_communication_base.h"
+#include <glog/logging.h>
+
+class Snap7Clamp:public Singleton<Snap7Clamp>, public Snap7_communication_base
+{
+public:
+	//发送db快的标记位, 保证先发数据再发唯一码
+	enum Send_database_flag
+	{
+		E_SEND_UNKNOW					= 0,
+		E_SEND_DATA_START              	= 1,    //开始发送数据
+		E_SEND_DATA_END              	= 2,    //结束发送数据
+		E_SEND_KEY_START				= 3,	//开始发送唯一码
+		E_SEND_KEY_END					= 4,	//结束发送唯一码
+	};
+
+
+#pragma pack(push, 1)	//struct按照1个byte对齐
+#define CLAMP_SAFETY_HEART_DBNUMBER		9070
+#define CLAMP_SAFETY_PLC_DBNUMBER		9070
+    struct WheeLData {
+        unsigned short wheel_exist;
+        float offset;
+        float gap;
+        unsigned short clamp_completed;
+
+        void info() const {
+            if (wheel_exist == 0) {
+                return;
+            }
+            LOG(INFO) << "wheel_exist = " << wheel_exist
+                       << ", offset = " << offset
+                       << ", gap = " << gap
+                       << ", clamp_completed = " << clamp_completed;
+        }
+
+        void clear() {
+            wheel_exist = false;
+            offset = 0;
+            gap = 0;
+            clamp_completed = 0;
+        }
+    };
+
+    struct PLCData {
+        unsigned short pingpong;
+
+        struct WheeLData wheels[4];
+
+        void info() {
+            LOG(INFO) << "pingdong = " << pingpong;
+            wheels[0].info();
+            wheels[1].info();
+            wheels[2].info();
+            wheels[3].info();
+        }
+
+        void clear() {
+            wheels[0].clear();
+            wheels[1].clear();
+            wheels[2].clear();
+            wheels[3].clear();
+        }
+    };
+#pragma pack(pop)		//取消对齐
+
+    // 子类必须把父类设定为友元函数,这样父类才能使用子类的私有构造函数。
+	friend class Singleton<Snap7Clamp>;
+
+private:
+	// 父类的构造函数必须保护,子类的构造函数必须私有。
+    Snap7Clamp();
+
+public:
+	//必须关闭拷贝构造和赋值构造,只能通过 get_instance 函数来进行操作唯一的实例。
+    Snap7Clamp(const Snap7Clamp& other) = delete;
+    Snap7Clamp& operator =(const Snap7Clamp& other) = delete;
+	~Snap7Clamp() override;
+
+public://API functions
+	//初始化 通信 模块。如下三选一
+	Error_manager communication_init();
+	//反初始化 通信 模块。
+	Error_manager communication_uninit() override;
+
+protected://member functions
+	//更新数据
+    Error_manager updata_receive_buf() override;
+    Error_manager updata_send_buf() override;
+
+protected://member variable
+public:
+	std::mutex      m_data_lock;						//数据锁
+
+	PLCData         plcData{};
+    unsigned short            m_heart;
+private:
+
+};

+ 308 - 0
include/plc/snap7_communication_base.cpp

@@ -0,0 +1,308 @@
+//
+// Created by huli on 2020/9/25.
+//
+
+#include "snap7_communication_base.h"
+
+Snap7_communication_base::Snap7_communication_base() {
+    m_communication_status = SNAP7_COMMUNICATION_UNKNOWN;
+    m_communication_delay_time_ms = SNAP7_COMMUNICATION_DELAY_TIME_MS;
+    mp_communication_thread = NULL;
+}
+
+Snap7_communication_base::~Snap7_communication_base() {
+    communication_uninit();
+}
+
+//初始化 通信 模块。如下三选一
+Error_manager Snap7_communication_base::communication_init() {
+    plcJsonConfig config(ETC_PATH"/etc/plc.json");
+    return communication_init(config.ip());
+}
+
+Error_manager Snap7_communication_base::communication_init(std::string ip) {
+    m_ip_string = ip;
+    Error_manager t_error = communication_connect(m_ip_string);
+    if (t_error != Error_code::SUCCESS) {
+        //连接失败, 不要直接返回, 而是改为断连, 后面继续启动线程, (线程内部有重连功能)
+        printf("---Debug %s %d : CONNECT ERROR !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n", __func__, __LINE__);
+        m_communication_status = SNAP7_COMMUNICATION_DISCONNECT;
+    } else {
+
+        printf("---Debug %s %d : CONNECT SUCCESS !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n", __func__, __LINE__);
+        m_communication_status = SNAP7_COMMUNICATION_READY;
+    }
+    return communication_run();
+}
+
+//反初始化 通信 模块。
+Error_manager Snap7_communication_base::communication_uninit() {
+    //关闭线程并回收资源
+    if (mp_communication_thread) {
+        m_communication_condition.kill_all();
+    }
+    if (mp_communication_thread) {
+        mp_communication_thread->join();
+        delete mp_communication_thread;
+        mp_communication_thread = NULL;
+    }
+
+    //清空map
+    {
+        std::unique_lock<std::mutex> t_lock(m_receive_buf_lock);
+        m_receive_buf_map.clear();
+    }
+    {
+        std::unique_lock<std::mutex> t_lock(m_send_buf_lock);
+        m_send_buf_map.clear();
+    }
+
+    communication_disconnect();
+    m_communication_status = SNAP7_COMMUNICATION_UNKNOWN;
+
+    return Error_code::SUCCESS;
+}
+
+
+//唤醒s7通信线程
+Error_manager Snap7_communication_base::communication_start() {
+    m_communication_condition.notify_all(true);
+    return Error_code::SUCCESS;
+}
+
+//停止s7通信线程
+Error_manager Snap7_communication_base::communication_stop() {
+    m_communication_condition.notify_all(false);
+    return Error_code::SUCCESS;
+}
+
+
+Snap7_communication_base::Snap7_communication_statu Snap7_communication_base::get_status() {
+    return m_communication_status;
+}
+
+//通信连接
+Error_manager Snap7_communication_base::communication_connect(std::string ip_string) {
+    std::unique_lock<std::mutex> t_lock(m_communication_lock);
+    int result = m_snap7_client.ConnectTo(ip_string.c_str(), 0, 1);
+    std::this_thread::sleep_for(std::chrono::milliseconds(m_communication_delay_time_ms));
+    printf("---Debug %s %d : connect %s result is %d \n", __func__, __LINE__, ip_string.c_str(), result);
+    if (result == 0) {
+        return Error_code::SUCCESS;
+    } else {
+        return Error_manager(Error_code::SNAP7_CONNECT_ERROR, Error_level::MINOR_ERROR,
+                             " Snap7_communication_base::communication_connect error ");
+    }
+}
+
+//启动通信, run thread
+Error_manager Snap7_communication_base::communication_run() {
+    //启动4个线程。
+    //接受线程默认循环, 内部的nn_recv进行等待, 超时1ms
+    m_communication_condition.reset(false, false, false);
+    mp_communication_thread = new std::thread(&Snap7_communication_base::communication_thread, this);
+
+    return Error_code::SUCCESS;
+}
+
+//通信断连
+Error_manager Snap7_communication_base::communication_disconnect() {
+    std::unique_lock<std::mutex> t_lock(m_communication_lock);
+    int result = m_snap7_client.Disconnect();
+    std::this_thread::sleep_for(std::chrono::milliseconds(m_communication_delay_time_ms));
+    if (result == 0) {
+        return Error_code::SUCCESS;
+    } else {
+        return Error_manager(Error_code::SNAP7_DISCONNECT_ERROR, Error_level::MINOR_ERROR,
+                             " Snap7_communication_base::communication_disconnect error ");
+    }
+    return Error_code::SUCCESS;
+}
+
+//mp_communication_thread线程的执行函数, 负责进行s7的通信
+void Snap7_communication_base::communication_thread() {
+    //LOG(INFO) << " ---Snap7_communication_base::communication_thread()--- "<< this;
+    Error_manager t_error;
+    while (m_communication_condition.is_alive()) {
+        m_communication_condition.wait_for_millisecond(1);
+
+        //s7的通信时间较长, 所以将发送和接受分开
+        //发送多个时, 必须加锁后一起发送, 不允许分段写入, 防止数据错误
+        if (m_communication_condition.is_alive()) {
+            std::this_thread::sleep_for(std::chrono::milliseconds(m_communication_delay_time_ms));
+            std::this_thread::yield();
+            switch (m_communication_status) {
+                case SNAP7_COMMUNICATION_READY:
+                case SNAP7_COMMUNICATION_RECEIVE: {
+                    {
+                        std::unique_lock<std::mutex> t_lock(m_receive_buf_lock);
+                        auto iter = m_receive_buf_map.begin();
+                        for (; iter != m_receive_buf_map.end(); ++iter) {
+                            //接受数据, 读取DB块,
+                            t_error = read_data_buf(iter->second);
+                            if (t_error == Error_code::SNAP7_READ_ERROR) {
+                                m_communication_status = SNAP7_COMMUNICATION_DISCONNECT;
+                                printf("---Debug %s %d : t_error = %s \n", __func__, __LINE__, t_error.to_string().c_str());
+                                break;
+                            }
+                        }
+                        if (iter != m_receive_buf_map.end()) {
+                            break;
+                        }
+                    }
+                    //注:数据更新放在锁的外面, 防止重复加锁....
+                    if(updata_receive_buf() != Error_code::SUCCESS) {
+                        m_communication_status = SNAP7_COMMUNICATION_DISCONNECT;
+                    }
+                    else {
+                        m_communication_status = SNAP7_COMMUNICATION_SEND;
+                    }
+                    break;
+                }
+                case SNAP7_COMMUNICATION_SEND: {
+                    //注:数据更新放在锁的外面, 防止重复加锁....
+                    updata_send_buf();
+                    {
+                        std::unique_lock<std::mutex> t_lock(m_send_buf_lock);
+                        auto iter = m_send_buf_map.begin();
+                        for (; iter != m_send_buf_map.end(); ++iter) {
+                            //发送数据, 写入DB块,
+                            t_error = write_data_buf(iter->second);
+                            if (t_error == Error_code::SNAP7_WRITE_ERROR) {
+                                m_communication_status = SNAP7_COMMUNICATION_DISCONNECT;
+                                printf("---Debug %s %d : t_error = %s \n", __func__, __LINE__, t_error.to_string().c_str());
+                                break;
+                            }
+                        }
+                        if (iter != m_send_buf_map.end()) {
+                            break;
+                        }
+                    }
+                    m_communication_status = SNAP7_COMMUNICATION_RECEIVE;
+                    break;
+                }
+                case SNAP7_COMMUNICATION_DISCONNECT: {
+                    //重连
+                    printf("---Debug %s %d : find plc connection error, trying to reconnect.\n", __func__, __LINE__);
+                    communication_disconnect();
+                    std::this_thread::sleep_for(std::chrono::milliseconds(m_communication_delay_time_ms));
+                    t_error = communication_connect(m_ip_string);
+                    if (t_error != Error_code::SUCCESS) {
+                        //连接失败, 不要直接返回, 而是改为断连, 后面继续启动线程, (线程内部有重连功能)
+                        m_communication_status = SNAP7_COMMUNICATION_DISCONNECT;
+                    } else {
+                        m_communication_status = SNAP7_COMMUNICATION_READY;
+                    }
+                    std::this_thread::sleep_for(std::chrono::milliseconds(m_communication_delay_time_ms));
+
+                    break;
+                }
+                default: {
+
+                    break;
+                }
+            }
+        }
+    }
+
+    //LOG(INFO) << " Communication_socket_base::send_data_thread end "<< this;
+    return;
+}
+
+//接受数据, 读取DB块,
+Error_manager Snap7_communication_base::read_data_buf(Snap7_buf &snap7_buf) {
+    Error_manager t_error;
+    if (snap7_buf.m_communication_mode != Snap7_buf::NO_COMMUNICATION) {
+        if (snap7_buf.m_communication_mode == Snap7_buf::ONCE_COMMUNICATION) {
+            snap7_buf.m_communication_mode = Snap7_buf::NO_COMMUNICATION;
+        }
+
+        std::unique_lock<std::mutex> lck(m_communication_lock);
+//        printf("---Debug %s %d : snap7_buf,m_id %d \n", __func__, __LINE__, snap7_buf.m_id);
+//        printf("---Debug %s %d : snap7_buf,m_start_index %d\n", __func__, __LINE__, snap7_buf.m_start_index);
+//        printf("---Debug %s %d : snap7_buf,m_size %d\n", __func__, __LINE__, snap7_buf.m_size);
+        int result = m_snap7_client.AsDBRead(snap7_buf.m_id, snap7_buf.m_start_index, snap7_buf.m_size,
+                                             snap7_buf.mp_buf_reverse);
+
+//		std::this_thread::sleep_for(std::chrono::milliseconds(10));
+        if (result == 0) {
+            m_snap7_client.WaitAsCompletion(100);
+            //倒序数据 转为 正序数据
+            snap7_buf.reverse_to_obverse();
+        } else {
+            std::cout << " huli test :::: " << " result = " << result << std::endl;
+            return Error_manager(Error_code::SNAP7_READ_ERROR, Error_level::MINOR_ERROR,
+                                 " Snap7_communication_base::read_data_buf error ");
+        }
+    }
+
+    return Error_code::SUCCESS;
+}
+
+//发送数据, 写入DB块,
+Error_manager Snap7_communication_base::write_data_buf(Snap7_buf &snap7_buf) {
+    Error_manager t_error;
+    if (snap7_buf.m_communication_mode != Snap7_buf::NO_COMMUNICATION) {
+        if (snap7_buf.m_communication_mode == Snap7_buf::ONCE_COMMUNICATION) {
+            snap7_buf.m_communication_mode = Snap7_buf::NO_COMMUNICATION;
+        }
+
+        //正序数据 转为 倒序数据
+        snap7_buf.obverse_to_reverse();
+        std::unique_lock<std::mutex> lck(m_communication_lock);
+        unsigned short a = 0;
+        memcpy(&a, snap7_buf.mp_buf_reverse, 2);
+        //printf("id %d  start %d  size :%d   value :%d\n",snap7_buf.m_id, snap7_buf.m_start_index, snap7_buf.m_size,a);
+        int result = m_snap7_client.AsDBWrite(snap7_buf.m_id, snap7_buf.m_start_index, snap7_buf.m_size,
+                                              snap7_buf.mp_buf_reverse);
+
+//		std::this_thread::sleep_for(std::chrono::milliseconds(10));
+        if (result == 0) {
+            if (0 != m_snap7_client.WaitAsCompletion(1000)) {
+//                printf("---Debug %s %d : snap7_buf,m_id %d \n", __func__, __LINE__, snap7_buf.m_id);
+//                printf("---Debug %s %d : snap7_buf,m_start_index %d\n", __func__, __LINE__, snap7_buf.m_start_index);
+//                printf("---Debug %s %d : snap7_buf,m_size %d\n", __func__, __LINE__, snap7_buf.m_size);
+//                return Error_manager(Error_code::SNAP7_WRITE_ERROR, Error_level::MINOR_ERROR,
+//                                     " Snap7_communication_base::write_data_buf error ");
+            }
+        } else {
+            printf("---Debug %s %d : snap7_buf,m_id %d \n", __func__, __LINE__, snap7_buf.m_id);
+            printf("---Debug %s %d : snap7_buf,m_start_index %d\n", __func__, __LINE__, snap7_buf.m_start_index);
+            printf("---Debug %s %d : snap7_buf,m_size %d\n", __func__, __LINE__, snap7_buf.m_size);
+            return Error_manager(Error_code::SNAP7_WRITE_ERROR, Error_level::MINOR_ERROR,
+                                 " Snap7_communication_base::write_data_buf error ");
+        }
+    }
+    return Error_code::SUCCESS;
+}
+
+//数据颠倒
+Error_manager Snap7_communication_base::reverse_byte(void *p_buf_in, void *p_buf_out, int size) {
+    if (p_buf_in == NULL || p_buf_out == NULL) {
+        return Error_manager(Error_code::POINTER_IS_NULL, Error_level::MINOR_ERROR,
+                             "  POINTER IS NULL ");
+    }
+    char *tp_in = (char *) p_buf_in;
+    char *tp_out = (char *) p_buf_out;
+//	for(int i=0;i<size;++i)
+//	{
+//		tp_out[i]=tp_in[size-i-1];
+//	}
+    for (int i = 0; i < size; ++i) {
+        tp_out[i] = tp_in[i];
+    }
+    return Error_code::SUCCESS;
+}
+
+//更新数据
+Error_manager Snap7_communication_base::updata_receive_buf() {
+    return Error_code::SUCCESS;
+}
+
+Error_manager Snap7_communication_base::updata_send_buf() {
+
+    return Error_code::SUCCESS;
+}
+
+
+

+ 117 - 0
include/plc/snap7_communication_base.h

@@ -0,0 +1,117 @@
+//
+// Created by huli on 2020/9/25.
+//
+
+#ifndef NNXX_TESTS_SNAP7_E_BASE_H
+#define NNXX_TESTS_SNAP7_E_BASE_H
+
+#include <s7_client.h>
+#include <mutex>
+#include <map>
+
+#include "error_code/error_code.hpp"
+#include "tool/thread_condition.h"
+#include "snap7_buf.h"
+#include "s7_plc.h"
+#include "plcJsonConfig.h"
+
+class Snap7_communication_base {
+public:
+    //snap7的通信延时, 默认50ms
+#define SNAP7_COMMUNICATION_DELAY_TIME_MS    10
+//snap7的通信参数路径
+#define SNAP7_COMMUNICATION_PARAMETER_PATH    "../etc/snap7_communication.prototxt"
+    //通信状态
+    enum Snap7_communication_statu {
+        SNAP7_COMMUNICATION_UNKNOWN = 0,            //通信状态 未知
+        SNAP7_COMMUNICATION_READY = 1,            //通信状态 正常
+        SNAP7_COMMUNICATION_RECEIVE = 2,        //接受
+        SNAP7_COMMUNICATION_SEND = 3,        //发送
+        SNAP7_COMMUNICATION_DISCONNECT = 4,        //断连
+        SNAP7_COMMUNICATION_FAULT = 10,         //通信状态 错误
+    };
+protected:
+    Snap7_communication_base();
+
+    Snap7_communication_base(const Snap7_communication_base &other) = default;
+
+    Snap7_communication_base &operator=(const Snap7_communication_base &other) = default;
+
+    ~Snap7_communication_base();
+
+public://API functions
+    //初始化 通信 模块。如下三选一
+    virtual Error_manager communication_init();
+
+    virtual Error_manager communication_init(std::string ip);
+
+    //反初始化 通信 模块。
+    virtual Error_manager communication_uninit();
+
+    //唤醒s7通信线程
+    virtual Error_manager communication_start();
+
+    //停止s7通信线程
+    virtual Error_manager communication_stop();
+
+    //更新数据
+    virtual Error_manager updata_receive_buf();
+
+    virtual Error_manager updata_send_buf();
+
+public://get or set member variable
+    Snap7_communication_statu get_status();
+
+protected://member functions
+    //接受数据, 读取DB块,
+    Error_manager read_data_buf(Snap7_buf &snap7_buf);
+
+    //发送数据, 写入DB块,
+    Error_manager write_data_buf(Snap7_buf &snap7_buf);
+
+    //通信连接
+    Error_manager communication_connect(std::string ip_string);
+
+    //启动通信, run thread
+    Error_manager communication_run();
+
+    //通信断连
+    Error_manager communication_disconnect();
+
+    //mp_communication_thread线程的执行函数, 负责进行s7的通信
+    void communication_thread();
+
+
+    //数据颠倒
+    Error_manager reverse_byte(void *p_buf_in, void *p_buf_out, int size);
+
+
+protected://member variable
+public:
+    //状态
+    Snap7_communication_statu m_communication_status;    //通信状态
+    std::string m_ip_string;            //通信ip
+
+    //通信模块
+    std::mutex m_communication_lock;    //通信锁
+    TSnap7Client m_snap7_client;            //通信的客户端
+    int m_communication_delay_time_ms;//通信延时, 单位ms
+    //注:s7协议通信很不稳定, 在每次使用 TSnap7Client 之后, 都需要加延时
+
+    //数据
+    std::mutex m_receive_buf_lock;        //接受的锁
+    std::map<int, Snap7_buf> m_receive_buf_map;        //接受的map容器
+    std::mutex m_send_buf_lock;        //发送的锁
+    std::map<int, Snap7_buf> m_send_buf_map;        //发送的map容器
+
+    //线程, snap7的通信核心就是对 发送和接受内存的 周期性读写, 所以使用一个线程即可.
+    std::thread *mp_communication_thread;            //通信的线程指针
+    Thread_condition m_communication_condition;            //通信的条件变量
+
+
+private:
+
+};
+
+
+#endif //NNXX_TESTS_SNAP7_E_BASE_H

+ 4 - 0
include/tool/singleton.cpp

@@ -0,0 +1,4 @@
+
+#include "singleton.h"
+
+

+ 77 - 0
include/tool/singleton.h

@@ -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

+ 165 - 0
include/tool/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
include/tool/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;
+}
+*/

+ 192 - 60
mainwindow.cpp

@@ -7,34 +7,22 @@ MainWindow::MainWindow(QWidget *parent)
 {
     ui->setupUi(this);
 
-    this->init(ETC_PATH"/velodyne_manager.prototxt");
+    connect(ui->InPut, SIGNAL(triggered()), this, SLOT(onInputTriggered()));
+    connect(ui->OutPut, SIGNAL(triggered()), this, SLOT(onOutputTriggered()));
+
+    initPLC();
+    onInputTriggered();
 }
 
 MainWindow::~MainWindow()
 {
-    /**************** 作缓存记录 ****************/
-
-    velodyne::velodyneManagerParams velodyne_parameters;
-
-    if (!proto_tool::read_proto_param(ETC_PATH"/velodyne_manager.prototxt", velodyne_parameters)) {
-        std::cout << "失败" << std::endl;
-            return;
-    }
-
-    velodyne_parameters.clear_region();
-    for (auto &region: m_ground_region_map) {
-        velodyne_parameters.add_region()->CopyFrom(region.second);
-    }
-
-    proto_tool::write_proto_param(ETC_PATH"/velodyne_manager.prototxt", velodyne_parameters);
+    onOutputTriggered();
 
     delete ui;
 }
 
 bool MainWindow::init(const std::string &filename) {
-    velodyne::velodyneManagerParams velodyne_parameters;
-
-    if (!proto_tool::read_proto_param(ETC_PATH"/velodyne_manager.prototxt", velodyne_parameters)) {
+    if (!proto_tool::read_proto_param(filename, velodyne_parameters)) {
         std::cout << "失败" << std::endl;
         return false;
     }
@@ -42,17 +30,28 @@ bool MainWindow::init(const std::string &filename) {
     ui->Forward->setChecked(true);
     ui->EntranceComboBox->clear();
 
-    m_ground_region_map.clear();
     for (int i = 0; i < velodyne_parameters.region_size(); ++i) {
-//        std::cout << velodyne_parameters.region(i).region_id() << std::endl;
-        m_ground_region_map.insert(std::pair<int, velodyne::Region>(velodyne_parameters.region(i).region_id(), velodyne_parameters.region(i)));
         ui->EntranceComboBox->addItem(QString::number(velodyne_parameters.region(i).region_id()));
     }
 
+    proto_tool::write_proto_param(ETC_PATH"/record/record_" + std::to_string(record_id) + ".prototxt", velodyne_parameters);
     showStatu();
+
+    return true;
+}
+
+bool MainWindow::initPLC() {
+    m_snap7_client = &Snap7Clamp::get_instance_references();
+    m_snap7_client->communication_init();
+    Snap7Clamp::Snap7_communication_statu status = m_snap7_client->get_status();
+    while (status == Snap7Clamp::SNAP7_COMMUNICATION_READY || status == Snap7Clamp::SNAP7_COMMUNICATION_RECEIVE) {
+        sleep(1);
+        status = m_snap7_client->get_status();
+    }
     return true;
 }
 
+
 void MainWindow::showStatu() {
     QString entrance_txt = "入口id: " + ui->EntranceComboBox->currentText();
 
@@ -71,10 +70,46 @@ void MainWindow::showStatu() {
                                record_txt);
 }
 
+void MainWindow::saveRecord(const std::string &fileName) {
+    /**************** 作缓存记录 ****************/
+    proto_tool::write_proto_param(fileName, velodyne_parameters);
+}
+
+int MainWindow::findRegionIndex(const int &region_id) {
+    for (int i = 0; i < velodyne_parameters.region().size(); i++) {
+        if (velodyne_parameters.region(i).has_region_id() && velodyne_parameters.region(i).region_id() == region_id) {
+            return i;
+        }
+    }
+
+    return -1;
+}
 
 void MainWindow::on_ComputeButton_clicked()
 {
-    auto iter = m_ground_region_map.find(ui->EntranceComboBox->currentText().toInt());
+    WheelDistance wd;
+    wd.currentText = ui->EntranceComboBox->currentText();
+    wd.lf = ui->LeftFrontDIstance->value();
+    wd.lr = ui->LeftRearDistance->value();
+    wd.rf = ui->RightFrontDistance->value();
+    wd.rr = ui->RightRearDistance->value();
+    wd.wheel_base = ui->WheelBase->value();
+
+    if (wd.lf == 0 && wd.lr == 0 && wd.rf == 0 && wd.rr == 0) {
+        ui->textEdit->append("*?* 大哥,四个0懒得算了");
+        return;
+    }
+
+
+    if (ui->Forward->isChecked()) {
+        wd.forward = true;
+    } else {
+        wd.forward = false;
+    }
+
+    m_distance_map.insert(std::pair<int, WheelDistance>(record_id, wd));
+//    auto iter = m_ground_region_map.find(ui->EntranceComboBox->currentText().toInt());
+    int region_index = findRegionIndex(ui->EntranceComboBox->currentText().toInt());
 
     /**************** 作计算处理 ****************/
     double left_dis = ui->LeftFrontDIstance->value() - ui->LeftRearDistance->value();
@@ -84,28 +119,37 @@ void MainWindow::on_ComputeButton_clicked()
 
     double front_dis = ui->LeftFrontDIstance->value() - ui->RightFrontDistance->value();
 //    double rear_dis = ui->LeftRearDistance->value() - ui->RightRearDistance->value();
-    double offset_x = -((front_dis) * 0.5 - (left_dis - right_dis) * 0.25) / 1000;
+    double offset_x = ((front_dis) * 0.5 - (left_dis - right_dis) * 0.25) / 1000;
 
     record_id++;
+    for (int i = record_id; i < record_id_max; i++) {
+        m_distance_map.erase(i);
+    }
     record_id_max = record_id;
 
     velodyne::PlcOffset offset;
     if (ui->Forward->isChecked()) {
-        offset.CopyFrom(iter->second.plc_forward_offset());
-        offset.set_plc_offsetx(iter->second.plc_forward_offset().plc_offsetx() + offset_x);
-        offset.set_plc_offset_degree(iter->second.plc_forward_offset().plc_offset_degree() + offset_angle);
+        offset.CopyFrom(velodyne_parameters.region(region_index).plc_forward_offset());
+        offset.set_plc_offsetx(offset.plc_offsetx() + offset_x);
+        offset.set_plc_offset_degree(offset.plc_offset_degree() + offset_angle);
 
-        iter->second.mutable_plc_forward_offset()->CopyFrom(offset);
+        velodyne_parameters.mutable_region(region_index)->mutable_plc_forward_offset()->CopyFrom(offset);
         ui->textEdit->append("*" + QString::number(record_id) + "* " + ui->EntranceComboBox->currentText() + "正向 计算结果: x轴需要调整" + QString::number(offset_x, 'f', 4)  + ", 角度需要调整" + QString::number(offset_angle, 'f', 4) + "度。");
     }
     else if (ui->Reverse->isChecked()) {
-        offset.CopyFrom(iter->second.plc_reverse_offset());
-        offset.set_plc_offsetx(iter->second.plc_reverse_offset().plc_offsetx() + offset_x);
-        offset.set_plc_offset_degree(iter->second.plc_reverse_offset().plc_offset_degree() + offset_angle);
+        offset.CopyFrom(velodyne_parameters.region(region_index).plc_reverse_offset());
+        offset.set_plc_offsetx(offset.plc_offsetx() + offset_x);
+        offset.set_plc_offset_degree(offset.plc_offset_degree() + offset_angle);
 
-        iter->second.mutable_plc_reverse_offset()->CopyFrom(offset);
+        velodyne_parameters.mutable_region(region_index)->mutable_plc_reverse_offset()->CopyFrom(offset);
         ui->textEdit->append("*" + QString::number(record_id) + "* " + ui->EntranceComboBox->currentText() + "反向 计算结果: x轴需要调整" + QString::number(offset_x, 'f', 4) + ", 角度需要调整" + QString::number(offset_angle, 'f', 4) + "度。");
     }
+
+    ui->plc_offsetx->setText(QString::number(offset.plc_offsetx(), 'f', 4));
+    ui->plc_offsety->setText(QString::number(offset.plc_offsety(), 'f', 4));
+    ui->plc_offset_degree->setText(QString::number(offset.plc_offset_degree(), 'f', 4));
+    ui->plc_offset_wheel_base->setText(QString::number(offset.plc_offset_wheel_base(), 'f', 4));
+
     /**************** 计算参数清零,防止多次误计算 ****************/
     ui->LeftFrontDIstance->setValue(0);
     ui->LeftRearDistance->setValue(0);
@@ -114,19 +158,7 @@ void MainWindow::on_ComputeButton_clicked()
 
     /**************** 作缓存记录 ****************/
 
-    velodyne::velodyneManagerParams velodyne_parameters;
-
-    if (!proto_tool::read_proto_param(ETC_PATH"/velodyne_manager.prototxt", velodyne_parameters)) {
-        std::cout << "失败" << std::endl;
-            return;
-    }
-
-    velodyne_parameters.clear_region();
-    for (auto &region: m_ground_region_map) {
-            velodyne_parameters.add_region()->CopyFrom(region.second);
-    }
-
-    proto_tool::write_proto_param(ETC_PATH"/record/record_" + std::to_string(record_id) + ".prototxt", velodyne_parameters);
+    saveRecord(ETC_PATH"/record/record_" + std::to_string(record_id) + ".prototxt");
 
     showStatu();
 }
@@ -134,24 +166,48 @@ void MainWindow::on_ComputeButton_clicked()
 
 void MainWindow::on_EntranceComboBox_currentTextChanged(const QString &arg1)
 {
-    auto iter = m_ground_region_map.find(arg1.toInt());
+    int region_index = findRegionIndex(ui->EntranceComboBox->currentText().toInt());
+    if (region_index == -1) {
+        ui->textEdit->append("find region id error");
+        return;
+    } else {
+//        ui->textEdit->append("on_EntranceComboBox_currentTextChanged "+ arg1);
+    }
 
-    ui->LeftFrontDIstance->setValue(0);
-    ui->LeftRearDistance->setValue(0);
-    ui->RightFrontDistance->setValue(0);
-    ui->RightRearDistance->setValue(0);
+    auto iter_dis = m_distance_map.find(record_id);
+    if (iter_dis != m_distance_map.end()) {
+        ui->LeftFrontDIstance->setValue(iter_dis->second.lf);
+        ui->LeftRearDistance->setValue(iter_dis->second.lr);
+        ui->RightFrontDistance->setValue(iter_dis->second.rf);
+        ui->RightRearDistance->setValue(iter_dis->second.rr);
+        ui->WheelBase->setValue(iter_dis->second.wheel_base);
+    } else {
+        ui->LeftFrontDIstance->setValue(0);
+        ui->LeftRearDistance->setValue(0);
+        ui->RightFrontDistance->setValue(0);
+        ui->RightRearDistance->setValue(0);
+    }
 
     if (ui->Forward->isChecked()) {
-        ui->plc_offsetx->setText(QString::number(iter->second.plc_forward_offset().plc_offsetx(), 'f', 4));
-        ui->plc_offsety->setText(QString::number(iter->second.plc_forward_offset().plc_offsety(), 'f', 4));
-        ui->plc_offset_degree->setText(QString::number(iter->second.plc_forward_offset().plc_offset_degree(), 'f', 4));
-        ui->plc_offset_wheel_base->setText(QString::number(iter->second.plc_forward_offset().plc_offset_wheel_base(), 'f', 4));
+        ui->LF_Label->setText("Y3(mm)");
+        ui->RF_Label->setText("Y4(mm)");
+        ui->LR_Label->setText("Y5(mm)");
+        ui->RR_Label->setText("Y6(mm)");
+
+        ui->plc_offsetx->setText(QString::number(velodyne_parameters.region(region_index).plc_forward_offset().plc_offsetx(), 'f', 4));
+        ui->plc_offsety->setText(QString::number(velodyne_parameters.region(region_index).plc_forward_offset().plc_offsety(), 'f', 4));
+        ui->plc_offset_degree->setText(QString::number(velodyne_parameters.region(region_index).plc_forward_offset().plc_offset_degree(), 'f', 4));
+        ui->plc_offset_wheel_base->setText(QString::number(velodyne_parameters.region(region_index).plc_forward_offset().plc_offset_wheel_base(), 'f', 4));
     }
     else if (ui->Reverse->isChecked()) {
-        ui->plc_offsetx->setText(QString::number(iter->second.plc_reverse_offset().plc_offsetx(), 'f', 4));
-        ui->plc_offsety->setText(QString::number(iter->second.plc_reverse_offset().plc_offsety(), 'f', 4));
-        ui->plc_offset_degree->setText(QString::number(iter->second.plc_reverse_offset().plc_offset_degree(), 'f', 4));
-        ui->plc_offset_wheel_base->setText(QString::number(iter->second.plc_reverse_offset().plc_offset_wheel_base(), 'f', 4));
+        ui->LF_Label->setText("Y6(mm)");
+        ui->RF_Label->setText("Y5(mm)");
+        ui->LR_Label->setText("Y4(mm)");
+        ui->RR_Label->setText("Y3(mm)");
+        ui->plc_offsetx->setText(QString::number(velodyne_parameters.region(region_index).plc_reverse_offset().plc_offsetx(), 'f', 4));
+        ui->plc_offsety->setText(QString::number(velodyne_parameters.region(region_index).plc_reverse_offset().plc_offsety(), 'f', 4));
+        ui->plc_offset_degree->setText(QString::number(velodyne_parameters.region(region_index).plc_reverse_offset().plc_offset_degree(), 'f', 4));
+        ui->plc_offset_wheel_base->setText(QString::number(velodyne_parameters.region(region_index).plc_reverse_offset().plc_offset_wheel_base(), 'f', 4));
     }
 
     showStatu();
@@ -173,8 +229,26 @@ void MainWindow::on_RedoButton_clicked()
 {
     record_id++;
     if (record_id <= record_id_max && record_id >= 0) {
-        init(ETC_PATH"/record/record_" + std::to_string(record_id) + ".prototxt");
+        if (!proto_tool::read_proto_param(ETC_PATH"/record/record_" + std::to_string(record_id) + ".prototxt", velodyne_parameters)) {
+            std::cout << "失败" << std::endl;
+        }
+
+        auto iter = m_distance_map.find(record_id);
+        if (iter != m_distance_map.end()) {
+            if (iter->second.forward) {
+                ui->Forward->setChecked(true);
+                ui->Reverse->setChecked(false);
+            } else {
+                ui->Forward->setChecked(false);
+                ui->Reverse->setChecked(true);
+            }
+            ui->EntranceComboBox->setCurrentText(iter->second.currentText);
+        }
+
+        on_EntranceComboBox_currentTextChanged(ui->EntranceComboBox->currentText());
+        ui->textEdit->append("往后进行一次重做");
     } else {
+        ui->textEdit->append("*?* 大哥,没动西重做了!!!!别点了!!!");
         record_id = record_id_max;
     }
 }
@@ -184,9 +258,67 @@ void MainWindow::on_QuashButton_clicked()
 {
     record_id--;
     if (record_id <= record_id_max && record_id >= 0) {
-        init(ETC_PATH"/record/record_" + std::to_string(record_id) + ".prototxt");
+        if (!proto_tool::read_proto_param(ETC_PATH"/record/record_" + std::to_string(record_id) + ".prototxt", velodyne_parameters)) {
+            std::cout << "失败" << std::endl;
+        }
+
+        auto iter = m_distance_map.find(record_id);
+        if (iter != m_distance_map.end()) {
+            if (iter->second.forward) {
+                ui->Forward->setChecked(1);
+            } else {
+                ui->Reverse->setChecked(1);
+            }
+            ui->EntranceComboBox->setCurrentText(iter->second.currentText);
+        }
+
+        on_EntranceComboBox_currentTextChanged(ui->EntranceComboBox->currentText());
+        ui->textEdit->append("撤销到上一次计算");
     } else {
+        ui->textEdit->append("*?* 大哥,没动西撤销了!!!!别点了!!!");
         record_id = 0;
     }
 }
 
+void MainWindow::onInputTriggered() {
+    QString fileName = QFileDialog::getOpenFileName(
+        this,
+        tr("Open File"),
+        "~/",
+        tr("protobuf文件 (*.prototxt *.txt)"));
+
+    if (fileName.isEmpty()) {
+        return;
+    }
+
+    init(fileName.toStdString());
+}
+
+void MainWindow::onOutputTriggered() {
+    QString fileName = QFileDialog::getSaveFileName(
+        this,
+        tr("Save File"),
+        "~/",
+        tr("protobuf文件 (*.prototxt *.txt)"));
+
+    if (fileName.isEmpty()) {
+        return;
+    }
+
+    saveRecord(fileName.toStdString());
+}
+
+
+void MainWindow::on_AutoGetDistance_clicked()
+{
+    if (m_snap7_client == nullptr) {
+        return;
+    }
+    std::unique_lock<std::mutex> t_lock1(m_snap7_client->m_data_lock);
+    Snap7Clamp::PLCData plc_value = m_snap7_client->plcData;
+    std::cout << plc_value.wheels[0].gap << std::endl;
+    std::cout << plc_value.wheels[1].gap << std::endl;
+    std::cout << plc_value.wheels[2].gap << std::endl;
+    std::cout << plc_value.wheels[3].gap << std::endl;
+}
+

+ 34 - 1
mainwindow.h

@@ -5,6 +5,9 @@
 #include "proto_tool.h"
 #include "velodyne_config.pb.h"
 #include <cmath>
+#include <QFileDialog>   //文件对话框
+#include "plc/snap7_clamp.h"
+
 
 QT_BEGIN_NAMESPACE
 namespace Ui { class MainWindow; }
@@ -31,14 +34,44 @@ private slots:
 
     void on_QuashButton_clicked();
 
+    void onInputTriggered();
+
+    void onOutputTriggered();
+
+    void on_AutoGetDistance_clicked();
+
 private:
+    struct WheelDistance {
+        QString currentText;
+        bool forward;
+        double lf;
+        double rf;
+        double lr;
+        double rr;
+        double wheel_base;
+    };
+
     bool init(const std::string &filename);
+
+    bool initPLC();
+
     void showStatu();
 
+    void saveRecord(const std::string &fileName);
+
+    int findRegionIndex(const int &region_id);
+
 private:
     Ui::MainWindow *ui;
     int record_id = 0;
     int record_id_max = 0;
-    std::map<int, velodyne::Region> m_ground_region_map;            // 区域功能实例指针数组, 内存由本类管理
+
+    velodyne::velodyneManagerParams velodyne_parameters;
+
+
+//    std::map<int, velodyne::Region> m_ground_region_map;            // 区域功能实例指针数组, 内存由本类管理
+    std::map<int, WheelDistance> m_distance_map;            // 区域功能实例指针数组, 内存由本类管理
+
+    Snap7Clamp *m_snap7_client = nullptr;
 };
 #endif // MAINWINDOW_H

+ 219 - 212
mainwindow.ui

@@ -27,214 +27,7 @@
      <enum>Qt::NoFocus</enum>
     </property>
    </widget>
-   <widget class="QWidget" name="">
-    <property name="geometry">
-     <rect>
-      <x>630</x>
-      <y>0</y>
-      <width>148</width>
-      <height>585</height>
-     </rect>
-    </property>
-    <layout class="QVBoxLayout" name="verticalLayout_2">
-     <item>
-      <spacer name="verticalSpacer_3">
-       <property name="orientation">
-        <enum>Qt::Vertical</enum>
-       </property>
-       <property name="sizeHint" stdset="0">
-        <size>
-         <width>20</width>
-         <height>40</height>
-        </size>
-       </property>
-      </spacer>
-     </item>
-     <item>
-      <layout class="QFormLayout" name="formLayout">
-       <item row="0" column="0" colspan="2">
-        <widget class="QComboBox" name="EntranceComboBox"/>
-       </item>
-       <item row="1" column="0" colspan="2">
-        <widget class="QLabel" name="label_9">
-         <property name="text">
-          <string>入口</string>
-         </property>
-         <property name="alignment">
-          <set>Qt::AlignCenter</set>
-         </property>
-        </widget>
-       </item>
-       <item row="2" column="0">
-        <widget class="QRadioButton" name="Forward">
-         <property name="text">
-          <string>正向</string>
-         </property>
-        </widget>
-       </item>
-       <item row="2" column="1">
-        <widget class="QRadioButton" name="Reverse">
-         <property name="text">
-          <string>反向</string>
-         </property>
-        </widget>
-       </item>
-      </layout>
-     </item>
-     <item>
-      <spacer name="verticalSpacer">
-       <property name="orientation">
-        <enum>Qt::Vertical</enum>
-       </property>
-       <property name="sizeHint" stdset="0">
-        <size>
-         <width>20</width>
-         <height>40</height>
-        </size>
-       </property>
-      </spacer>
-     </item>
-     <item>
-      <layout class="QGridLayout" name="gridLayout">
-       <item row="0" column="0">
-        <layout class="QVBoxLayout" name="verticalLayout">
-         <item>
-          <widget class="QPushButton" name="ComputeButton">
-           <property name="text">
-            <string>计算</string>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <widget class="QPushButton" name="QuashButton">
-           <property name="text">
-            <string>撤销</string>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <widget class="QPushButton" name="RedoButton">
-           <property name="text">
-            <string>重做</string>
-           </property>
-          </widget>
-         </item>
-        </layout>
-       </item>
-       <item row="1" column="0">
-        <layout class="QVBoxLayout" name="verticalLayout_6">
-         <item>
-          <widget class="QLineEdit" name="plc_offsetx">
-           <property name="focusPolicy">
-            <enum>Qt::NoFocus</enum>
-           </property>
-           <property name="alignment">
-            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <widget class="QLabel" name="label_5">
-           <property name="text">
-            <string>X轴偏移(米)</string>
-           </property>
-           <property name="alignment">
-            <set>Qt::AlignCenter</set>
-           </property>
-          </widget>
-         </item>
-        </layout>
-       </item>
-       <item row="2" column="0">
-        <layout class="QVBoxLayout" name="verticalLayout_7">
-         <item>
-          <widget class="QLineEdit" name="plc_offsety">
-           <property name="focusPolicy">
-            <enum>Qt::NoFocus</enum>
-           </property>
-           <property name="alignment">
-            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <widget class="QLabel" name="label_6">
-           <property name="text">
-            <string>Y轴偏移(米)</string>
-           </property>
-           <property name="alignment">
-            <set>Qt::AlignCenter</set>
-           </property>
-          </widget>
-         </item>
-        </layout>
-       </item>
-       <item row="3" column="0">
-        <layout class="QVBoxLayout" name="verticalLayout_8">
-         <item>
-          <widget class="QLineEdit" name="plc_offset_degree">
-           <property name="focusPolicy">
-            <enum>Qt::NoFocus</enum>
-           </property>
-           <property name="alignment">
-            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <widget class="QLabel" name="label_7">
-           <property name="text">
-            <string>角度偏移(米)</string>
-           </property>
-           <property name="alignment">
-            <set>Qt::AlignCenter</set>
-           </property>
-          </widget>
-         </item>
-        </layout>
-       </item>
-       <item row="4" column="0">
-        <layout class="QVBoxLayout" name="verticalLayout_9">
-         <item>
-          <widget class="QLineEdit" name="plc_offset_wheel_base">
-           <property name="focusPolicy">
-            <enum>Qt::NoFocus</enum>
-           </property>
-           <property name="alignment">
-            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <widget class="QLabel" name="label_8">
-           <property name="text">
-            <string>轴距偏移(米)</string>
-           </property>
-           <property name="alignment">
-            <set>Qt::AlignCenter</set>
-           </property>
-          </widget>
-         </item>
-        </layout>
-       </item>
-      </layout>
-     </item>
-     <item>
-      <spacer name="verticalSpacer_2">
-       <property name="orientation">
-        <enum>Qt::Vertical</enum>
-       </property>
-       <property name="sizeHint" stdset="0">
-        <size>
-         <width>20</width>
-         <height>40</height>
-        </size>
-       </property>
-      </spacer>
-     </item>
-    </layout>
-   </widget>
-   <widget class="QWidget" name="">
+   <widget class="QWidget" name="layoutWidget">
     <property name="geometry">
      <rect>
       <x>21</x>
@@ -260,7 +53,7 @@
         </widget>
        </item>
        <item>
-        <widget class="QLabel" name="label">
+        <widget class="QLabel" name="LF_Label">
          <property name="text">
           <string>左前(毫米)</string>
          </property>
@@ -284,7 +77,7 @@
         </widget>
        </item>
        <item>
-        <widget class="QLabel" name="label_2">
+        <widget class="QLabel" name="RF_Label">
          <property name="text">
           <string>右前(毫米)</string>
          </property>
@@ -308,7 +101,7 @@
         </widget>
        </item>
        <item>
-        <widget class="QLabel" name="label_3">
+        <widget class="QLabel" name="LR_Label">
          <property name="text">
           <string>左后(毫米)</string>
          </property>
@@ -332,7 +125,7 @@
         </widget>
        </item>
        <item>
-        <widget class="QLabel" name="label_4">
+        <widget class="QLabel" name="RR_Label">
          <property name="text">
           <string>右后(毫米)</string>
          </property>
@@ -356,6 +149,9 @@
          <property name="maximum">
           <double>3200.000000000000000</double>
          </property>
+         <property name="value">
+          <double>2700.000000000000000</double>
+         </property>
         </widget>
        </item>
        <item>
@@ -372,8 +168,219 @@
      </item>
     </layout>
    </widget>
+   <widget class="QWidget" name="">
+    <property name="geometry">
+     <rect>
+      <x>631</x>
+      <y>173</y>
+      <width>146</width>
+      <height>411</height>
+     </rect>
+    </property>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <layout class="QVBoxLayout" name="verticalLayout">
+       <item>
+        <widget class="QPushButton" name="ComputeButton">
+         <property name="text">
+          <string>计算</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="QuashButton">
+         <property name="text">
+          <string>撤销</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="RedoButton">
+         <property name="text">
+          <string>重做</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item row="1" column="0">
+      <layout class="QVBoxLayout" name="verticalLayout_6">
+       <item>
+        <widget class="QLineEdit" name="plc_offsetx">
+         <property name="focusPolicy">
+          <enum>Qt::NoFocus</enum>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="label_5">
+         <property name="text">
+          <string>X轴偏移(米)</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignCenter</set>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item row="2" column="0">
+      <layout class="QVBoxLayout" name="verticalLayout_7">
+       <item>
+        <widget class="QLineEdit" name="plc_offsety">
+         <property name="focusPolicy">
+          <enum>Qt::NoFocus</enum>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="label_6">
+         <property name="text">
+          <string>Y轴偏移(米)</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignCenter</set>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item row="3" column="0">
+      <layout class="QVBoxLayout" name="verticalLayout_8">
+       <item>
+        <widget class="QLineEdit" name="plc_offset_degree">
+         <property name="focusPolicy">
+          <enum>Qt::NoFocus</enum>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="label_7">
+         <property name="text">
+          <string>角度偏移(米)</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignCenter</set>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item row="4" column="0">
+      <layout class="QVBoxLayout" name="verticalLayout_9">
+       <item>
+        <widget class="QLineEdit" name="plc_offset_wheel_base">
+         <property name="focusPolicy">
+          <enum>Qt::NoFocus</enum>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="label_8">
+         <property name="text">
+          <string>轴距偏移(米)</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignCenter</set>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </widget>
+   <widget class="QWidget" name="">
+    <property name="geometry">
+     <rect>
+      <x>630</x>
+      <y>10</y>
+      <width>141</width>
+      <height>141</height>
+     </rect>
+    </property>
+    <layout class="QFormLayout" name="formLayout">
+     <item row="0" column="0" colspan="2">
+      <widget class="QPushButton" name="AutoGetDistance">
+       <property name="text">
+        <string>自动获取Y值</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="0" colspan="2">
+      <widget class="QComboBox" name="EntranceComboBox">
+       <property name="layoutDirection">
+        <enum>Qt::RightToLeft</enum>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="0" colspan="2">
+      <widget class="QLabel" name="label_9">
+       <property name="text">
+        <string>入口</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="0">
+      <widget class="QRadioButton" name="Forward">
+       <property name="text">
+        <string>正向</string>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="1">
+      <widget class="QRadioButton" name="Reverse">
+       <property name="text">
+        <string>反向</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </widget>
   </widget>
   <widget class="QStatusBar" name="statusbar"/>
+  <widget class="QMenuBar" name="menuBar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>800</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menu">
+    <property name="title">
+     <string>文件</string>
+    </property>
+    <addaction name="InPut"/>
+    <addaction name="OutPut"/>
+   </widget>
+   <addaction name="menu"/>
+  </widget>
+  <action name="InPut">
+   <property name="text">
+    <string>导入</string>
+   </property>
+  </action>
+  <action name="OutPut">
+   <property name="text">
+    <string>导出</string>
+   </property>
+  </action>
  </widget>
  <resources/>
  <connections/>

+ 8 - 8
velodyne_manager.prototxt

@@ -121,15 +121,15 @@ region {
     lidar_id: 6314
   }
   plc_forward_offset {
-    plc_offsetx: 0.033364
+    plc_offsetx: 0.0196140017
     plc_offsety: -6.35546398
-    plc_offset_degree: -89.32
+    plc_offset_degree: -91.6478882
     plc_offset_wheel_base: -0.01
   }
   plc_reverse_offset {
-    plc_offsetx: 0.013081
+    plc_offsetx: 0.008081
     plc_offsety: -6.35911417
-    plc_offset_degree: -90.13
+    plc_offset_degree: -90.0176544
     plc_offset_wheel_base: -0.01
   }
   car_range_info {
@@ -214,15 +214,15 @@ region {
     lidar_id: 6316
   }
   plc_forward_offset {
-    plc_offsetx: 0.0405550115
+    plc_offsetx: -0.00869498868
     plc_offsety: -6.31662941
-    plc_offset_degree: -89.2456
+    plc_offset_degree: -90.4027481
     plc_offset_wheel_base: -0
   }
   plc_reverse_offset {
-    plc_offsetx: 0.00274304
+    plc_offsetx: -0.00325696
     plc_offsety: -6.31662941
-    plc_offset_degree: -90.07
+    plc_offset_degree: -90.8564377
     plc_offset_wheel_base: -0
   }
   car_range_info {