feat: v0.1.0更新

This commit is contained in:
2026-01-14 18:07:26 +08:00
commit efd8a7cc20
55 changed files with 6200 additions and 0 deletions

12
.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
.vscode/**
bin/**
build/**
installer/output/
installer/*.wixobj
installer/*.msi
installer/*.wixpdb
*/.venv
CLAUDE.md

160
CMakeLists.txt Normal file
View File

@@ -0,0 +1,160 @@
cmake_minimum_required(VERSION 3.15)
project(D330Viewer VERSION 1.0.0 LANGUAGES CXX C)
# 设置C++标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Qt6特定配置
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
# 设置输出目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_SOURCE_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_SOURCE_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_SOURCE_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_SOURCE_DIR}/bin)
# 添加include目录
include_directories(${CMAKE_SOURCE_DIR}/include)
include_directories(${CMAKE_SOURCE_DIR}/src)
# ==================== 查找依赖库 ====================
# 查找Qt6
find_package(Qt6 REQUIRED COMPONENTS
Core
Widgets
OpenGL
OpenGLWidgets
Network
Concurrent
)
# 查找PCL
find_package(PCL REQUIRED COMPONENTS common io visualization)
if(PCL_FOUND)
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})
message(STATUS "PCL found: ${PCL_VERSION}")
endif()
# 查找OpenCV
find_package(OpenCV REQUIRED COMPONENTS core highgui imgproc)
if(OpenCV_FOUND)
include_directories(${OpenCV_INCLUDE_DIRS})
message(STATUS "OpenCV found: ${OpenCV_VERSION}")
endif()
# 查找OpenCL
find_package(OpenCL REQUIRED)
if(OpenCL_FOUND)
include_directories(${OpenCL_INCLUDE_DIRS})
message(STATUS "OpenCL found")
endif()
# ==================== 源文件配置 ====================
set(GUI_SOURCES
src/main.cpp
src/gui/MainWindow.cpp
src/gui/PointCloudWidget.cpp
src/gui/PointCloudGLWidget.cpp
)
set(CONFIG_SOURCES
src/config/ConfigManager.cpp
)
set(CORE_SOURCES
src/core/NetworkManager.cpp
src/core/DeviceScanner.cpp
src/core/GVSPParser.cpp
src/core/Logger.cpp
src/core/PointCloudProcessor.cpp
)
set(HEADERS
src/gui/MainWindow.h
include/gui/PointCloudWidget.h
include/gui/PointCloudGLWidget.h
src/config/ConfigManager.h
src/core/NetworkManager.h
src/core/DeviceScanner.h
include/core/Logger.h
include/core/GVSPParser.h
include/core/PointCloudProcessor.h
)
# 资源文件
set(RESOURCES
resources/resources.qrc
resources/app_icon.rc
)
# 创建可执行文件
add_executable(${PROJECT_NAME} WIN32
${GUI_SOURCES}
${CONFIG_SOURCES}
${CORE_SOURCES}
${HEADERS}
${RESOURCES}
)
# 链接库
target_link_libraries(${PROJECT_NAME}
Qt6::Core
Qt6::Widgets
Qt6::OpenGL
Qt6::OpenGLWidgets
Qt6::Network
Qt6::Concurrent
${PCL_LIBRARIES}
${OpenCV_LIBS}
${OpenCL_LIBRARIES}
ws2_32
)
# Windows特定配置
if(WIN32)
target_compile_definitions(${PROJECT_NAME} PRIVATE
_WINSOCK_DEPRECATED_NO_WARNINGS
_CRT_SECURE_NO_WARNINGS
)
endif()
# ==================== 安装配置 ====================
# 安装可执行文件
install(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION bin
)
# 安装所有DLL
install(DIRECTORY ${CMAKE_SOURCE_DIR}/bin/
DESTINATION bin
FILES_MATCHING PATTERN "*.dll"
)
# 安装Qt平台插件
install(DIRECTORY ${CMAKE_SOURCE_DIR}/bin/platforms/
DESTINATION bin/platforms
FILES_MATCHING PATTERN "*.dll"
)
# ==================== CPack配置 - MSI安装程序 ====================
set(CPACK_PACKAGE_NAME "D330Viewer")
set(CPACK_PACKAGE_VENDOR "Lorenzo Zhao")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "D330M Depth Camera Control System")
set(CPACK_PACKAGE_VERSION "0.1.0")
set(CPACK_PACKAGE_VERSION_MAJOR "0")
set(CPACK_PACKAGE_VERSION_MINOR "1")
set(CPACK_PACKAGE_VERSION_PATCH "0")
set(CPACK_PACKAGE_INSTALL_DIRECTORY "D330Viewer")
# WiX生成器配置用于MSI
set(CPACK_GENERATOR "WIX")
set(CPACK_WIX_UPGRADE_GUID "12345678-1234-1234-1234-123456789012")
set(CPACK_WIX_PROGRAM_MENU_FOLDER "D330Viewer")
# 包含CPack模块
include(CPack)

277
README.md Normal file
View File

@@ -0,0 +1,277 @@
# D330Viewer - D330M深度相机控制系统
## 项目简介
D330Viewer 是一个基于Qt6的深度相机控制和可视化系统用于D330M深度相机的实时数据采集、处理和显示。系统采用OpenCL GPU加速进行点云计算支持实时显示深度图像和3D点云。
## 技术栈
### 核心库版本
- **Qt6**: 6.10.1 (msvc2022_64)
- **PCL**: 1.15.1 (Point Cloud Library)
- **OpenCV**: 4.13.0
- **OpenCL**: 3.0 (GPU加速)
- **DkamSDK**: 1.6.83 (D330M相机SDK)
### 开发环境
- **操作系统**: Windows 10/11 (64位)
- **编译器**: Visual Studio 2026 (MSVC)
- **构建工具**: CMake 3.15+
- **GPU**: 支持OpenCL的GPUNVIDIA/AMD/Intel
## 快速开始
### 1. 环境准备
确保已安装以下软件:
- Visual Studio 2022 以上包含C++桌面开发工作负载)
- CMake 3.15或更高版本
- 支持OpenCL的GPU驱动
### 2. 库依赖安装
**必需的库**
- Qt6 6.10.1 (msvc2022_64)
- PCL 1.15.1
- OpenCV 4.13.0
- OpenCL 3.0通常随GPU驱动自动安装
**安装路径**(默认):
- Qt6: `C:\Qt\6.10.1\msvc2022_64`
- PCL: `C:\Program Files\PCL 1.15.1`
- OpenCV: `C:\opencv\build`
### 3. 配置项目
**方式1使用配置脚本推荐**
```bash
# 需根据本地情况修改库路径编辑configure.bat
configure.bat
```
**方式2手动配置**
```bash
cmake -B build -S . ^
-G "Visual Studio 18 2026" ^
-A x64 ^
-DPCL_DIR="C:/Program Files/PCL 1.15.1/cmake" ^
-DOpenCV_DIR="C:/opencv/build" ^
-DCMAKE_PREFIX_PATH="C:/Qt/6.10.1/msvc2022_64"
```
**注意**
- 如果系统安装了VS 2022可使用 `-G "Visual Studio 17 2022"`
### 4. 编译项目
```bash
# Release版本推荐
cmake --build build --config Release
# Debug版本 需自行复制Debug版本库文件
cmake --build build --config Debug
```
编译输出:
- Release: `bin/D330Viewer.exe`
- Debug: `bin/Debug/D330Viewer.exe`
### 5. 运行程序
```bash
cd bin
D330Viewer.exe
```
**注意**首次运行需要确保所有DLL文件都在bin目录下。
### 6. 生成MSI安装程序可选
项目支持生成Windows MSI安装程序方便在其他电脑上部署。
**前置要求**:需要安装 WiX Toolset v3.x
**安装WiX Toolset**
1. 访问 GitHub Release 页面https://github.com/wixtoolset/wix3/releases
2. 下载最新版本的安装包:
- `wix3xx-binaries.zip`
-`wix3xx.exe`
3. 如果下载zip文件
- 解压到 `C:\Program Files (x86)\WiX Toolset v3.14\`
- 添加bin目录到系统PATH`C:\Program Files (x86)\WiX Toolset v3.14\bin`
4. 如果下载exe文件
- 直接运行安装程序安装后会自动添加到PATH
**验证安装**
```bash
candle.exe -?
```
**生成MSI安装程序**
**方式1使用CPack推荐**
```bash
# 编译完成后在build目录运行
cd build
cpack -G WIX -C Release
```
生成的安装程序:`build/D330Viewer-0.1.0-win64.msi`
**方式2使用WiX Toolset手动构建**
```bash
build_installer.bat
```
生成的安装程序:`installer/output/D330Viewer.msi`
**MSI安装程序包含**
- D330Viewer.exe主程序
- 所有必需的DLL37个
- Qt平台插件platforms目录
- 开始菜单快捷方式
## 功能特性
### ✅ 已完成功能
#### 网络通信
- ✅ UDP网络通信GVSP协议解析
- ✅ 自动设备扫描和发现
- ✅ 相机连接管理(连接/断开)
- ✅ 命令发送START/STOP/ONCE
#### 可视化
- ✅ 实时深度图显示OpenCV垂直翻转校正
- ✅ 实时3D点云显示自定义OpenGL渲染
- ✅ 正交投影视角(平面视图)
- ✅ 交互式点云操作:
- 左键拖动:旋转视角
- 右键拖动:平移
- 滚轮缩放范围100-10000
#### 用户界面
- ✅ Qt6 GUI主窗口三栏布局
- ✅ 相机控制面板START/STOP/ONCE按钮
- ✅ 曝光时间调节滑块范围460-100000μs
- ✅ 网络配置IP地址、端口设置
- ✅ 连接状态指示
- ✅ 配置持久化QSettings
### 🚧 当前开发计划
根据需求文档和用户反馈,后续待添加功能如下:
- 录制功能(连续保存多帧)
- 点云颜色映射(深度着色)
- 点云滤波选项(降噪、平滑)
- 测量工具(距离、角度测量)
- 多视角预设(正视、侧视、俯视)
- 性能监控CPU/GPU使用率、内存使用
- 其他相机参数调节(增益、白平衡等)
## 项目结构
```
d330viewer/
├── src/ # 源代码
│ ├── main.cpp # 程序入口
│ ├── gui/ # GUI相关
│ │ ├── MainWindow.cpp # 主窗口
│ │ ├── PointCloudWidget.cpp # 点云显示组件
│ │ └── PointCloudGLWidget.cpp # OpenGL点云渲染
│ ├── core/ # 核心功能
│ │ ├── NetworkManager.cpp # 网络通信
│ │ ├── GVSPParser.cpp # GVSP协议解析
│ │ ├── PointCloudProcessor.cpp # OpenCL点云计算
│ │ └── DeviceScanner.cpp # 设备扫描
│ └── config/ # 配置管理
│ └── ConfigManager.cpp # 配置持久化
├── include/ # 头文件
│ ├── gui/ # GUI头文件
│ └── core/ # 核心功能头文件
├── bin/ # 可执行文件和DLL
│ ├── D330Viewer.exe # 主程序
│ └── *.dll # 依赖库37个
├── build/ # CMake构建目录
├── installer/ # MSI安装程序配置
├── CMakeLists.txt # CMake配置
├── configure.bat # 快速配置脚本
├── build_installer.bat # MSI安装程序构建脚本
└── README.md # 本文件
```
## 相机配置
### 硬件信息
- **相机型号**: D330M
- **控制端口**: 6790 (UDP)
- **数据端口**: 3957 (UDP)
- **分辨率**: 1224 x 1024
- **深度范围**: 155mm - 3000mm
### 网络配置
确保PC和相机在同一网段即可。
### 相机参数
- **曝光时间**: 460 - 100000 μs
- **Z缩放因子**: 0.2 (深度值需除以5)
- **相机内参**:
- fx = 1432.8957
- fy = 1432.6590
- cx = 637.5117
- cy = 521.8720
### 下位机程序修改
下位机程序保存在`/root/msk/20260113/` 。具体安装和使用详见根目录内`INSTALL.md`文件。当前版本程序修改功能如下:
- 添加`DISCOVER`命令允许上位机对相机进行自动扫描。保持上位机下位机均DHCP能够正常使用。
- 将程序添加到系统服务中,可以使用`systemctl`进行运行和管理
## 使用说明
### 基本操作流程
1. **启动程序**
- 运行 `bin/D330Viewer.exe`
- 程序会自动扫描网络中的D330M相机
2. **连接相机**
- 在设备列表中选择相机
- 点击"连接"按钮
- 状态指示变为绿色"已连接"
3. **开始采集**
- 点击"启动"按钮开始实时采集
- 深度图显示在中间区域
- 点云显示在右侧区域
4. **调整参数**
- 拖动曝光滑块调整曝光时间
- 使用鼠标操作点云视角
5. **停止采集**
- 点击"停止"按钮停止采集
- 点击"单次"按钮采集单帧
### 点云交互操作
- **旋转视角**: 左键拖动
- **平移**: 右键拖动
- **缩放**: 滚轮上下滚动
- **复位**: 重新启动采集
## 故障排除
### 常见问题
#### 1. 无法接收数据
**问题**: 点击"启动"后没有图像和点云显示
**解决方案**:
- 图像在长时间传输后有概率卡住,不相应任何命令,具体可监控下位机日志。重启系统服务即可解决。
## 开发文档
### 代码规范
- 使用Qt6信号槽机制进行模块间通信
- OpenCL kernel代码内联在C++源文件中
- 配置使用QSettings持久化
- 日志输出到 `bin/d330viewer.log`

51
build_installer.bat Normal file
View File

@@ -0,0 +1,51 @@
@echo off
echo ========================================
echo D330Viewer MSI Installer Builder
echo ========================================
echo.
REM 检查WiX Toolset是否安装
where candle.exe >nul 2>&1
if %ERRORLEVEL% NEQ 0 (
echo ERROR: WiX Toolset not found!
echo Please install WiX Toolset from: https://wixtoolset.org/
echo After installation, add WiX bin directory to PATH
pause
exit /b 1
)
echo [1/5] Checking build output...
if not exist "bin\D330Viewer.exe" (
echo ERROR: D330Viewer.exe not found in bin directory
echo Please build the project first: cmake --build build --config Release
pause
exit /b 1
)
echo [2/5] Creating installer directory...
if not exist "installer\output" mkdir installer\output
echo [3/5] Harvesting DLL files...
REM 使用heat.exe自动收集bin目录下的所有文件
heat.exe dir bin -cg BinFiles -gg -scom -sreg -sfrag -srd -dr INSTALLFOLDER -var var.BinDir -out installer\BinFiles.wxs
echo [4/5] Compiling WiX source...
candle.exe -dBinDir=bin installer\UpperControl.wxs installer\BinFiles.wxs -out installer\output\
echo [5/5] Linking MSI package...
light.exe -ext WixUIExtension installer\output\UpperControl.wixobj installer\output\BinFiles.wixobj -out installer\output\D330Viewer.msi
if exist "installer\output\D330Viewer.msi" (
echo.
echo ========================================
echo SUCCESS! MSI installer created:
echo installer\output\D330Viewer.msi
echo ========================================
) else (
echo.
echo ERROR: Failed to create MSI installer
pause
exit /b 1
)
pause

62
configure.bat Normal file
View File

@@ -0,0 +1,62 @@
@echo off
chcp 65001 >nul
REM CMake配置脚本 - Windows版本
REM 请根据实际安装路径修改以下变量
echo ========================================
echo D330Viewer CMake配置脚本
echo ========================================
echo.
REM 设置库路径(请根据实际安装路径修改)
set PCL_DIR=C:\Program Files\PCL 1.15.1\cmake
set OpenCV_DIR=C:\opencv\build
set Qt6_DIR=C:\Qt\6.10.1\msvc2022_64
echo 检查库路径...
if not exist "%PCL_DIR%" (
echo [警告] PCL路径不存在: %PCL_DIR%
echo 请修改configure.bat中的PCL_DIR变量
)
if not exist "%OpenCV_DIR%" (
echo [警告] OpenCV路径不存在: %OpenCV_DIR%
echo 请修改configure.bat中的OpenCV_DIR变量
)
if not exist "%Qt6_DIR%" (
echo [警告] Qt6路径不存在: %Qt6_DIR%
echo 请修改configure.bat中的Qt6_DIR变量
)
echo.
echo 开始配置CMake...
echo.
cmake -B build -S . ^
-DPCL_DIR="%PCL_DIR%" ^
-DOpenCV_DIR="%OpenCV_DIR%" ^
-DCMAKE_PREFIX_PATH="%Qt6_DIR%" ^
-G "Visual Studio 18 2026" ^
-A x64
if %ERRORLEVEL% EQU 0 (
echo.
echo ========================================
echo CMake配置成功
echo ========================================
echo.
echo 下一步:
echo 1. 打开 build\D330Viewer.sln 使用Visual Studio编译
echo 2. 或运行: cmake --build build --config Release
echo.
) else (
echo.
echo ========================================
echo CMake配置失败
echo ========================================
echo 请检查库路径是否正确
echo.
)
pause

17
include/1225pc_viewer.h Normal file
View File

@@ -0,0 +1,17 @@
#pragma once
#include <cstddef>
#include <cstdint>
// 初始化显示模块(内部创建线程)
bool pc_viewer_start();
// 停止显示模块
void pc_viewer_stop();
// 推送一帧 XYZ 数据floatxyzxyz...
// 只显示最新一帧,旧数据会被覆盖
void pc_viewer_update(
const float* xyz,
size_t point_count,
uint32_t block_id
);

View File

@@ -0,0 +1,42 @@
#ifndef RAW_IMAGE_VIEWER_H
#define RAW_IMAGE_VIEWER_H
// 防止C/C++混合调用时的名称修饰问题
#ifdef __cplusplus
extern "C" {
#endif
// 引入必要的标准类型声明(保证参数类型可识别)
#include <cstdint>
/**
* @brief 启动RAW图像可视化线程初始化+启动后台刷新)
* @return bool 启动成功返回true失败返回false
*/
bool raw_image_viewer_start();
/**
* @brief 停止RAW图像可视化线程安全退出+资源清理)
*/
void raw_image_viewer_stop();
/**
* @brief 更新RAW图像数据线程安全与后台可视化线程解耦
* @param data 16位RAW图像数据指针不可修改
* @param width 图像宽度(必须>0
* @param height 图像高度(必须>0
* @param block_id 当前图像块ID用于窗口标题显示
*/
void raw_image_viewer_update(const uint16_t* data, int width, int height, uint32_t block_id);
/**
* @brief 清理RAW图像可视化相关资源兼容原接口可选调用
* @details 内部会先调用raw_image_viewer_stop(),再释放图像缓存内存
*/
void raw_image_viewer_cleanup();
#ifdef __cplusplus
}
#endif
#endif // RAW_IMAGE_VIEWER_H

121
include/core/GVSPParser.h Normal file
View File

@@ -0,0 +1,121 @@
#ifndef GVSPPARSER_H
#define GVSPPARSER_H
#include <QObject>
#include <QByteArray>
#include <QImage>
#include <cstdint>
// GVSP packet types
#define GVSP_LEADER_PACKET 0x01
#define GVSP_PAYLOAD_PACKET 0x02
#define GVSP_TRAILER_PACKET 0x03
// Payload types
#define PAYLOAD_TYPE_IMAGE 0x0001
#define PAYLOAD_TYPE_BINARY 0x0003
// Image format
#define PIXEL_FORMAT_12BIT_GRAY 0x010C0001
// Image dimensions
#define IMAGE_WIDTH 1224
#define IMAGE_HEIGHT 1024
#pragma pack(push, 1)
// GVSP packet header
struct GVSPPacketHeader {
uint16_t status;
uint16_t block_id;
uint32_t packet_fmt_id;
};
// Image data leader
struct GVSPImageDataLeader {
uint16_t reserved;
uint16_t payload_type;
uint32_t timestamp_high;
uint32_t timestamp_low;
uint32_t pixel_format;
uint32_t size_x;
uint32_t size_y;
uint32_t offset_x;
uint32_t offset_y;
uint16_t padding_x;
uint16_t padding_y;
};
// Image data trailer
struct GVSPImageDataTrailer {
uint32_t reserved;
uint16_t payload_type;
uint32_t size_y;
};
// Binary data leader (for depth data)
struct GVSPBinaryDataLeader {
uint16_t reserved;
uint16_t payload_type;
uint32_t timestamp_high;
uint32_t timestamp_low;
uint32_t file_size;
uint32_t name_len;
char file_name[256];
};
// Binary data trailer
struct GVSPBinaryDataTrailer {
uint32_t reserved;
uint16_t payload_type;
uint32_t checksum;
};
#pragma pack(pop)
class GVSPParser : public QObject
{
Q_OBJECT
public:
explicit GVSPParser(QObject *parent = nullptr);
~GVSPParser();
void parsePacket(const QByteArray &packet);
void reset();
signals:
void imageReceived(const QImage &image, uint32_t blockId);
void depthDataReceived(const QByteArray &depthData, uint32_t blockId);
void parseError(const QString &error);
private:
void handleLeaderPacket(const uint8_t *data, size_t size);
void handlePayloadPacket(const uint8_t *data, size_t size);
void handleTrailerPacket(const uint8_t *data, size_t size);
void processImageData();
void processDepthData();
private:
// Reception state
bool m_isReceiving;
int m_dataType; // 0=unknown, 1=image, 3=depth
uint32_t m_currentBlockId;
// Data buffer
QByteArray m_dataBuffer;
size_t m_expectedSize;
size_t m_receivedSize;
// Image info
uint32_t m_imageWidth;
uint32_t m_imageHeight;
uint32_t m_pixelFormat;
// Statistics
uint32_t m_lastBlockId;
int m_packetCount;
};
#endif // GVSPPARSER_H

41
include/core/Logger.h Normal file
View File

@@ -0,0 +1,41 @@
#ifndef LOGGER_H
#define LOGGER_H
#include <QObject>
#include <QString>
#include <QFile>
#include <QTextStream>
#include <QMutex>
class Logger : public QObject
{
Q_OBJECT
public:
static Logger* instance();
void setLogFile(const QString &filePath);
void setMaxLines(int maxLines);
void log(const QString &message);
void debug(const QString &message);
void info(const QString &message);
void warning(const QString &message);
void error(const QString &message);
private:
explicit Logger(QObject *parent = nullptr);
~Logger();
void writeLog(const QString &level, const QString &message);
void checkAndTrimLog();
static Logger *s_instance;
QString m_logFilePath;
int m_maxLines;
QMutex m_mutex;
int m_currentLines;
};
#endif // LOGGER_H

View File

@@ -0,0 +1,64 @@
#ifndef POINTCLOUDPROCESSOR_H
#define POINTCLOUDPROCESSOR_H
#include <QObject>
#include <QByteArray>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <CL/cl.h>
class PointCloudProcessor : public QObject
{
Q_OBJECT
public:
explicit PointCloudProcessor(QObject *parent = nullptr);
~PointCloudProcessor();
// 初始化OpenCL
bool initializeOpenCL();
// 设置相机内参
void setCameraIntrinsics(float fx, float fy, float cx, float cy);
// 设置Z缩放因子
void setZScaleFactor(float scale);
// 将深度数据转换为点云使用OpenCL GPU加速
void processDepthData(const QByteArray &depthData, uint32_t blockId);
signals:
void pointCloudReady(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, uint32_t blockId);
void errorOccurred(const QString &error);
private:
// 清理OpenCL资源
void cleanupOpenCL();
// 相机内参
float m_fx;
float m_fy;
float m_cx;
float m_cy;
// Z缩放因子
float m_zScale;
// 图像尺寸
int m_imageWidth;
int m_imageHeight;
int m_totalPoints;
// OpenCL资源
cl_platform_id m_platform;
cl_device_id m_device;
cl_context m_context;
cl_command_queue m_queue;
cl_program m_program;
cl_kernel m_kernel;
cl_mem m_depthBuffer;
cl_mem m_xyzBuffer;
bool m_clInitialized;
};
#endif // POINTCLOUDPROCESSOR_H

143
include/dkam_asyncqueue.h Normal file
View File

@@ -0,0 +1,143 @@
#pragma once
#include <stdio.h>
#include <stdbool.h>
#ifdef _WIN32
//Head files that WIN Visual Studio needs
#include <tchar.h>
#include <synchapi.h>
#else
//Head files that linux system needs
//TRUE FALSE BOOL that linux not support
#include <pthread.h>
#define TRUE 1
#define FALSE 0
#define BOOL _Bool
#endif
#ifdef _WIN32
#define DLLEXPORT_API extern __declspec(dllexport)
#define DLL_VOID void __stdcall
#define DLL_INT int __stdcall
#else
#define DLLEXPORT_API
#define DLL_VOID void
#define DLL_INT int
#endif
typedef struct _List List;
struct _List
{
void* data;
List *next;
List *prev;
};
typedef struct _Queue Queue;
struct _Queue
{
List *head;
List *tail;
unsigned int length;
};
typedef struct _AsyncQueue AsyncQueue;
#ifdef _WIN32
//mutex and cond are different in WIN and Linux.
struct _AsyncQueue
{
SRWLOCK mutex;
CONDITION_VARIABLE cond;
Queue queue;
unsigned int waiting_threads;
int ref_count;
};
#else
struct _AsyncQueue
{
pthread_mutex_t mutex;
pthread_cond_t cond;
Queue queue;
unsigned int waiting_threads;
int ref_count;
};
#endif
#ifdef __cplusplus
extern "C"
{
#endif
//add a new point to the List
//向队列中增加一个指针
DLLEXPORT_API List* list_append(List *list, void* data);
// Initialization of the queue
// 队列初始化函数
DLLEXPORT_API DLL_VOID queue_init(Queue *queue);
// Push a data point to the queue headand increase the leagth for 1.
// 将一个数据指针推送到队列头并将队列长度增加1。
DLLEXPORT_API DLL_VOID queue_push_head(Queue *queue, void *data);
// Pop a data From the tail of the queue and return the point, If there isn't a data, Then return NUll.
// 从队列尾推出一个数据并返回这个数据指针。如果没有数据则返回NULL。
DLLEXPORT_API void* queue_pop_tail(Queue *queue);
// Release all the queue, but not the data point.
// If the data point is generate by malloc, it should free first.
// 释放队列但不是队列中data指向的数据如果队列的data是开辟的空间应先释放。
DLLEXPORT_API DLL_VOID queue_clear(Queue *queue);
// Check If there is data or not in the queue.
// 查看队列是否有数据
DLLEXPORT_API List* queue_peek_tail_link(Queue *queue);
// Creat a AsyncQueue.
// If successed, it return a point to a AsyncQueue.
// Otherwise, it return a NULL point.
// 创建一个异步队列。如果成功返回队列的指针否则返回NULL。
DLLEXPORT_API AsyncQueue* async_queue_new();
// Push a point data to the AsyncQueue head. After this, the queue length will increase one.
// input AsyncQueue point; data point.
// 将数据指针推送到异步队形头然后队列长度增加1。 输入参数: 队列指针,数据指针。
DLLEXPORT_API DLL_VOID async_queue_push(AsyncQueue *queue, void *data);
// Pop a point of the data from AsyncQueue tail, After this, the queue length will decrease one and return a point.
// It will waiting point until there is one. It is a blocking function.
// 将一个数据从队列中弹出然后队列长度减少1返回数据的指针。 这个函数会一直等到有数据,这是一个阻塞函数。
DLLEXPORT_API void* async_queue_pop(AsyncQueue *queue);
// Try Pop a point of the data from AsyncQueue tail, If successed, the queue length will decrease one and return a point.
// If not successed, it return NULL. This function return immediately.
// 尝试将一数据从队列中弹出如果成功队列长度减少1返回数据指针。 如果无数据就返回NULL这个函数是立即返回。
DLLEXPORT_API void* async_queue_try_pop(AsyncQueue *queue);
// Pop a point of the data from AsyncQueue tail until the timeout.
// If during the timeout, there is data, the queue length will decrease one and return a point.
// If during the timeout, there is no data, it return NULL.
// the unit of timeout is us.
// 在等待的时间内尝试将一数据从队列中弹出如果成功队列长度减少1返回数据指针。 如果无数据就返回NULL。
// 这个函数的返回时间是小于等于等待时间的。
DLLEXPORT_API void* async_queue_timeout_pop(AsyncQueue *queue,
long long timeout);
// return the length of AsyncQueue
// 返回异步队列的长度
DLLEXPORT_API DLL_INT async_queue_length(AsyncQueue *queue);
// release the AsyncQueue
// 释放异步队列资源
DLLEXPORT_API DLL_VOID async_queue_destroy(AsyncQueue *queue);
#ifdef __cplusplus
}
#endif

308
include/dkam_base_type.h Normal file
View File

@@ -0,0 +1,308 @@
/********************************************************************************************
Copyright : XianZhisensorTechnologiesCo.,Ltd
File Name : discovery.h
Description: Privide base type of gige camera
Author : Yaming Wang
Version : 1.0
Data : 2019-11-18
History :
*********************************************************************************************/
#ifndef DKAM_BASE_TYPE_H
#define DKAM_BASE_TYPE_H
#define IP_HEADER_LEN 20
#define UDP_HEADER_LEN 8
#define IMAGE_PACKET_HEADER_LEN 8
#define BLOCK_HEAD_LEN 44
#define GVCP_PORT 3956
#define SORT_BY_IP 0
#define SORT_BY_SERIAL_NUMBER 1
//发送命令宏定义
#define PACK_START 0x42
#define DISCOVERY_CMD 0x0002
//#define DISCOVERY_ACK 0x0003
#define FORCEIP_CMD 0x0004
//#define FORCEIP_ACK 0x0005
#define READREG_CMD 0x0080
//#define READREG_ACK 0x0081
#define WRITEREG_CMD 0x0082
//#define WRITEREG_ACK 0x0083
#define READMEM_CMD 0x0084
//#define READMEM_ACK 0x0085
#define WRITEMEM_CMD 0x0086
//#define WRITEMEM_ACK 0x0087
#define PACKETRESEND_CMD 0x0040
//GIGE version协议标准寄存器
#define Control_Channel_Privilage_Reg 0x00000A00
#define GVCP_Capability_Reg 0x00000934
#define Number_of_Message_Channels_Reg 0x00000900
#define Number_of_Stream_Channels_Reg 0x00000904
#define First_Url_Reg 0x00000200
#define Second_Url_Reg 0x00000400
#define Heartbeat_Timeout_Reg 0x00000938
#define Source_Port_Reg 0x00000D1C
#define Stream_Channel_0_Port_Reg 0x00000D00
#define Stream_Channel_0_Destination_Address_Reg 0x00000D18
//#define Stream_Channel_1_Port_Reg 0x00000D40
//#define Stream_Channel_1_Destination_Address_Reg 0x00000D58
//
//#define Stream_Channel_2_Port_Reg 0x00000D80
//#define Stream_Channel_2_Destination_Address_Reg 0x00000D98
#define Stream_Channel_Packet_Size_Reg 0x00000D04
//自定义寄存器
#define Model_Adder 0x00000014
#define IP_Adder 0x0000064C
#define Mask_Adder 0x0000065C
#define Gate_Adder 0x0000066C
typedef struct RoiPoint {
unsigned int size_x;
unsigned int size_y;
unsigned int offset_x;
unsigned int offset_y;
}RoiPoint;
#define RECIEVE_TIME_STATISTICS_LENGTH 1000 //统计直方图划分的尺寸默认1000个
typedef struct {
int offset; //初始偏量
unsigned int bin_steps; //步长
unsigned int n_bins; //总步数
int max; //最大值
int min; //最小值
long long count; //总数量
long long add_more; //大于统计范围的数量
long long add_less; //小于统计范围的数量
long long bins[RECIEVE_TIME_STATISTICS_LENGTH];
}StatisticsData;
//C#
typedef struct {
int offset; //初始偏量
unsigned int bin_steps; //步长
unsigned int n_bins; //总步数
int max; //最大值
int min; //最小值
long long count; //总数量
long long add_more; //大于统计范围的数量
long long add_less; //小于统计范围的数量
}StatisticsDataCSharp;
//照片信息结构体
typedef struct PhotoInfo {
char* pixel;
unsigned int pixel_length;
unsigned int pixel_format;
unsigned int pixel_width;
unsigned int pixel_height;
unsigned int cloud_unit;
unsigned int payload_size;
unsigned long int timestamp_high;
unsigned long int timestamp_low;
RoiPoint roi;
unsigned short block_id;
unsigned short stream_channel_index_;
unsigned int gvsp_payload_size;
int buffer_status; // 当前包传输状态
}PhotoInfo;
//照片信息结构体C#专用
typedef struct PhotoInfoCSharp {
unsigned int pixel_length;
unsigned int pixel_format;
unsigned int pixel_width;
unsigned int pixel_height;
unsigned int cloud_unit;
unsigned int payload_size;
unsigned long int timestamp_high;
unsigned long int timestamp_low;
RoiPoint roi;
unsigned short block_id;
unsigned short stream_channel_index_;
unsigned int gvsp_payload_size;
int buffer_status; // 当前包传输状态
}PhotoInfoCSharp;
//暂存区数据结构体
typedef struct RecvDataBuff {
unsigned short status;
char head_packet_flag;
char* data;
char block_head[BLOCK_HEAD_LEN];
struct RecvDataBuff* next;
}RecvDataBuff;
//数据缓冲池结构体
typedef struct RecvDataTemp {
char head_packet_flag;
char resend_check_point;
char out_of_time_check;
unsigned short block_id;
unsigned int recv_packet_num;
unsigned int all_data_size;
unsigned int all_packet_num;
char* recv_packet_id;
char* data;
char *block_head;
struct RecvDataTemp* next;
}RecvDataTemp;
//相机信息结构体
typedef struct DiscoveryInfo {
unsigned int camera_ip;
unsigned int camera_mask;
unsigned int camera_gateway;
unsigned int camera_mac_low;
unsigned short camera_mac_high;
char device_vendor_name[32];
char device_model_name[32];
char device_version[32];
char device_manufacturer_info[48];
char device_serial_number[16];
char device_user_id[16];
unsigned int computer_ip;
unsigned int computer_mask;
unsigned int computer_gateway;
int mtu;
char computer_adapter[256];
}DiscoveryInfo;
typedef struct PacketInfo {
unsigned short in_status;
unsigned short in_block_id;
unsigned short in_packet_id;
unsigned char packet_format;
int data_packet_len;
}PacketInfo;
//相机参数
typedef struct CameraParameter {
float Kc[5];
float K[9];
float R[9];
float T[3];
}CameraParameter;
//连接相机信息
typedef struct InstanceDevice {
unsigned int camera_ip;
unsigned int camera_mask;
unsigned int computer_ip;
unsigned int computer_mask;
int computer_mtu;
int recv_buffer_num;
char computer_adapter[256];
}InstanceDevice;
//命令包
//discover_cmd
typedef struct DiscoverCmdPack {
unsigned char start;
unsigned char flag;
unsigned short command;
unsigned short length;
unsigned short req_id;
}DiscoverCmdPack;
//force_ip_cmd
typedef struct ForceIpCmdPack {
unsigned char start;
unsigned char flag;
unsigned short command;
unsigned short length;
unsigned short req_id;
unsigned short reserved;
unsigned short mac_addr_high;
unsigned int mac_addr_low;
unsigned int reserved1[3];
unsigned int ip;
unsigned int reserved2[3];
unsigned int mask;
unsigned int reserved3[3];
unsigned int gateway;
}ForceIpCmdPack;
//force_ip_reboot
typedef struct ForceIpRebootCmdPack {
unsigned char start;
unsigned char flag;
unsigned short command;
unsigned short length;
unsigned short req_id;
unsigned int model_addr;
unsigned int model;
unsigned int ip_addr;
unsigned int ip;
unsigned int mask_addr;
unsigned int mask;
unsigned int gateway_addr;
unsigned int gateway;
unsigned int restart_addr;
unsigned int restart;
}ForceIpRebootCmdPack;
//write_register_cmd
typedef struct WriteRegCmdPack {
unsigned char start;
unsigned char flag;
unsigned short command;
unsigned short length;
unsigned short req_id;
unsigned int register_addr;
unsigned int data;
}WriteRegCmdPack;
//read_register_cmd
typedef struct ReadRegCmdPack {
unsigned char start;
unsigned char flag;
unsigned short command;
unsigned short length;
unsigned short req_id;
unsigned int register_addr;
}ReadRegCmdPack;
//read_mem_cmd
typedef struct ReadMemCmdPack {
unsigned char start;
unsigned char flag;
unsigned short command;
unsigned short length;
unsigned short req_id;
unsigned int address;
unsigned short reserved;
unsigned short count;
}ReadMemCmdPack;
//write_mem_cmd
typedef struct WriteMemCmdPack {
unsigned char start;
unsigned char flag;
unsigned short command;
unsigned short length;
unsigned short req_id;
unsigned int address;
char data[536];
}WriteMemCmdPack;
//resend_cmd_pack
typedef struct PacketResendCmdPack {
unsigned char start;
unsigned char flag;
unsigned short command;
unsigned short length;
unsigned short req_id;
unsigned short stream_channel_index;
unsigned short block_id;
unsigned int first_packet_id;
unsigned int last_packet_id;
}PacketResendCmdPack;
#endif //!DKAM_BASE_TYPE_H

130
include/dkam_camera_error.h Normal file
View File

@@ -0,0 +1,130 @@
/********************************************************************************
Copyright : XianZhisensorTechnologiesCo.,Ltd
File Name :camera_error
Description: Privide information of error
Author : Yaming Wang
Version : 1.0
Data : 2019-11-
History :
*********************************************************************************/
#ifndef DKAM_CAMERA_ERROR_H_
#define DKAM_CAMERA_ERROR_H_
//局域网内未发现相机
#define SUCCESS 0
//#define MALLOC_ERROR -1
#define INVALID_PARAMETER -2
#define NETWORK_INTERFACE_CONTROLLER_ERROR -3 //网卡错误
#define FORCE_IP_TIMEOUT -4
#define NO_CAMERA -5
#define INIT_SOCKET_ERROR -6
#define SOCKET_BIND_ERROR -7
#define TEST_PACKET_SEND_ERROR -8
//#define GET_XML_URL_ERROR -9
#define XML_DATA_ERROR -10
#define CAMERA_DISCONNECT -11
//#define CREATE_LOG_ERROR -12
#define GET_MTU_ERROR -13
//#define GET_PACK_SIZE_ERROR -14
#define USER_CONFIG_FILE_ERROR -15
//#define SET_CAMERA_ATTRIBUTION_ERROR -16
#define CAMERA_ATTRIBUTION_ERROR -17
//#define TRIGGER_ERROR -18
#define CREATE_THREAD_ERROR -19
//#define STREAM_ON_ERROR -20
#define NO_REGISTER -21
#define UNIMPLEMENTED_PIXEL_FORMAT -22
//#define ACQUISITION_START_ERROR -23
//#define ACQUISITION_STOP_ERROR -24
//#define SET_ROI_ERROR -25
//#define STREAM_OFF_ERROR -26
//#define DISCONNECT_ERROR -27
//#define GET_NODE_MAX_VALUE_ERROR -28
//#define GET_NODE_MIN_VALUE_ERROR -29
//#define SET_HEART_BEAT_TIMEOUT_ERROR -30
//#define GET_HEART_BEAT_TIMEOUT_ERROR -31
//gvsp
//#define SELECT_TIMEOUT -32
//#define INVALID_CHANNEL_INDEX -33
#define CAPTURE_TIMEOUT -34
#define CAPTURE_ERROR -35
#define INET_PTON_ERROR -36
#define WRITE_REG_TIMEOUT -37
#define READ_REG_TIMEOUT -38
#define WRITE_MEM_TIMEOUT -39
#define READ_MEM_TIMEOUT -40
//save_data
#define SAVE_ERROR -41
#define OPEN_FILE_ERROR -42
//#define SAVE_XML_ERROR -43
#define FTP_OPEN_ERROR -44
#define FTP_CONNECT_ERROR -45
#define FTP_PUT_ERROR -46
#define FTP_GET_ERROR -47
//#define CAMERA_REBOOT_ERROR -48
#define DIFFERENT_NETWORK_SEGMENT -49
#define NULL_PTR -50
//#define GET_NODE_INC_VALUE_ERROR -51
//#define WRITE_REG_ERROR -52 //修改完未用
//#define READ_REG_ERROR -53 //修改完未用
//#define WRITE_MEM_ERROR -54 //修改完未用
//#define READ_MEM_ERROR -55 //修改完未用
#define RECEIVE_ERROR -56
//#define GET_CAM_INTERN_PARAM_ERROR -57
//#define GET_CAM_EXTERN_PARAM_ERROR -58
//#define FUSION_ERROR -59 //修改完未用
#define GET_CCP_STATUS_ERROR -60
#define LAST_TRIGGER_NOT_END -61
#define CONNECTED_BY_OTHERS -62
#define REGISTER_ACCESS_ERROR -63
#define CREATE_DISCOVER_OBJ_ERROR -97
#define CREATE_STREAM_OBJ_ERROR -98
#define CREATE_CAMERA_OBJ_ERROR -99
#define GEV_STATUS_SUCCESS 0x0000 //命令执行成功
#define GEV_STATUS_PACKET_RESEND 0x0100 //重发包
#define GEV_STATUS_NOT_IMPLEMENTED 0x8001 //设备不支持该命令
#define GEV_STATUS_INVALID_PARAMETER 0x8002 //参数无效
#define GEV_STATUS_INVALID_ADDRESS 0x8003 //试图访问不存在的地址空间位置
#define GEV_STATUS_WRITE_PROTECT 0x8004 //寄存器地址无法写入
#define GEV_STATUS_BAD_ALIGNMENT 0x8005 //地址偏移量或数据大小对齐错误
#define GEV_STATUS_ACCESS_DENIED 0x8006 //试图访问永久/暂时无法访问的地址
#define GEV_STATUS_BUSY 0x8007 //请求繁忙
#define GEV_STATUS_LOCAL_PROBLEM 0x8008
#define GEV_STATUS_MSG_MISMATCH 0x8009
#define GEV_STATUS_INVALID_PROTOCOL 0x800A
#define GEV_STATUS_NO_MSG 0x800B
#define GEV_STATUS_PACKET_UNAVAILABLE 0x800C //请求的数据包不可用
#define GEV_STATUS_DATA_OVERRUN 0x800D //GVSP发送器内部存储器溢出
#define GEV_STATUS_INVALID_HEADER 0x800E
#define GEV_STATUS_WRONG_CONFIG 0x800F
#define GEV_STATUS_PACKET_NOT_YET_AVAILABLE 0x8010 //尚未获取请求的数据包
#define GEV_STATUS_PACKET_AND_PREV_REMOVED_FROM_MEMORY 0x8011 //请求的数据包和所有先前的数据包不再可用并已从GVSP发送器存储器中丢失
#define GEV_STATUS_PACKET_REMOVED_FROM_MEMORY 0x8012 //请求的数据包不可用且从GVSP发送器存储器中丢失
#define GEV_STATUS_NO_REF_TIME 0x8013 //设备未与主时钟同步以用作时间参考
#define GEV_STATUS_PACKET_TEMPORARILY_UNAVAILABLE 0x8014 //由于临时宽带问题,此时无法重新发送数据包,应在将来再次请求
#define GEV_STATUS_OVERFLOW 0x8015 //设备队列或数据包数据已溢出
#define GEV_STATUS_ACTION_LATE 0x8016 //在已经过去的时间请求里请求的预定动作命令
#define GEV_STATUS_LEADER_TRAILER_OVERFLOW 0x8017 //数据头包或数据尾包的数据包大小不足以放入用于传输该块的所有信息
#define GEV_STATUS_SLERROR 0xC001 //光机异常
#define GEV_STATUS_CMOSERROR 0xC002 //CMOS异常
#define GEV_STATUS_GPUERROR 0xC003 //GPU异常
#define GEV_STATUS_OTHERERROR 0xC004 //其他异常
#define GEV_STATUS_UPDATEERROR 0xC005
#define GEV_STATUS_ERROR 0xCFFF //一般错误
#endif //!DKAM_CAMERA_ERROR_H_

View File

@@ -0,0 +1,32 @@
/**************************************************************************************************************
Copyright : XianZhisensorTechnologiesCo.,Ltd
File Name : common_socket.h
Description: Privide Definition of class
Author : Yaming Wang
Version : 1.0
Data : 2019-11-3
History :
***************************************************************************************************************/
#ifndef DKAM_COMMON_SOCKET_H
#define DKAM_COMMON_SOCKET_H
#ifdef _WIN32
#include <winsock2.h>
#else
#include <netinet/in.h>
#include <sys/select.h>
#endif
class CommonSocket {
public:
CommonSocket();
~CommonSocket();
int InitSocket();
int Bind(int socket_fd, unsigned int ip, unsigned short port);
int Send(int socket_fd, unsigned int ip, unsigned short port, const char* buffer, unsigned int buffer_len);
int Receive(int socket_fd, char* buffer, int buffer_len);
int Select(int socket_fd, fd_set *rd_fds, fd_set *wd_fds);
};
#endif //!DKAM_COMMON_SOCKET_H

54
include/dkam_discovery.h Normal file
View File

@@ -0,0 +1,54 @@
/********************************************************************************************
Copyright : XianZhisensorTechnologiesCo.,Ltd
File Name : discovery.h
Description: Privide class of discover
Author : Yaming Wang
Version : 1.0
Data : 2019-11-18
History :
*********************************************************************************************/
#ifndef DKAM_DISCOVERY_H
#define DKAM_DISCOVERY_H
#include "dkam_log.h"
#include "dkam_base_type.h"
#include "dkam_common_socket.h"
#include <string>
#include <vector>
class Discovery :private CommonSocket{
public:
Discovery();
~Discovery();
//设置发现设备时log日志的等级开关决定是否开启某一个等级的日志打印默认关闭(0:关闭 1:开启)
void SetLogLevelSwitch(int error, int debug, int warnning, int info);
//发现相机
int DiscoverCamera(std::vector<DiscoveryInfo>* discovery_info);
//对发现的相机排序 0 IP 1 序列号
int CameraSort(std::vector <DiscoveryInfo>* discovery_info, int sort_mode);
//设置相机ip
int ForceIp(DiscoveryInfo discovery_info, char* ip, char* mask, char* gateway);
//两点分十进制ip转化为int型
unsigned int ConvertIpStringToInt(char* str_ip);
//将int型ip转换为点分十进制形式
char *ConvertIpIntToString(unsigned int ip);
//判断相机的IP和PC的IP是否在同一网段
bool WhetherIsSameSegment(DiscoveryInfo discovery_info);
private:
//win32平台发现相机
int DiscoverCameraWin32(std::vector<DiscoveryInfo>** discovery_info);
//linux平台发现相机
int DiscoverCameraLinux(std::vector<DiscoveryInfo>** discovery_info);
private:
int camera_num_;
unsigned short req_id_;
char *camera_ip_;
cameralog logq;
};
#endif //!DKAM_DISCOVERY_H

50
include/dkam_ftpserver.h Normal file
View File

@@ -0,0 +1,50 @@
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>
#ifdef _WIN32
#include <io.h>
#include <WinSock2.h>
#include <wininet.h>
//链接wininet.lib库
#pragma comment(lib,"wininet.lib")
#else
#include <fcntl.h>
#include <netdb.h>
#include <unistd.h>
#include <dirent.h>
#include <arpa/inet.h>
#endif // _WIN32
class FtpServer
{
public:
FtpServer();
~FtpServer();
// ftp 文件上传
int FtpUpload(const char* ip, const char* localPathName, const char* uploadPath);
int FtpDownload(const char* ip, const char* LocalPath, const char* LocalName, const char* downPath);
int FtpGetFileList(const char* ip, const char* filePath, std::vector<std::string>* fileNames);
private:
int FtpUploadWin(const char* ip, const char* localPathName, const char* uploadPathName);
int FtpDownloadWin(const char* ip, const char* LocalPathName, const char* downPathName);
int FtpGetFileListWin(const char* ip, const char* filePath, std::vector<std::string>* fileNames);
int FtpUploadLin(const char* ip, const char* localPathName, const char* uploadPathName);
int FtpDownloadLin(const char* ip, const char* LocalPathName, const char* downPathName);
int FtpGetFileListLin(const char* ip, const char* filePath, std::vector<std::string>* fileNames);
private:
int ftpServerProt_m_;
char* ftpUserName_m_;
char* ftpUserPwd_m_;
#ifdef _WIN32
char* szAppname_m_;
char* szUsername_m_;
char* szPassword_m_;
#else
#endif // _WIN32
};

385
include/dkam_gige_camera.h Normal file
View File

@@ -0,0 +1,385 @@
/*************************************************************************************
Copyright : XianZhisensorTechnologiesCo.,Ltd
File Name : gige_camera.h
Description: Privide Definition of class
Author : Yaming Wang
Version : 1.0
Data : 2019-11-3
History :
*************************************************************************************/
#ifndef DKAM_GIGE_CAMERA_H
#define DKAM_GIGE_CAMERA_H
#include "dkam_log.h"
#include "dkam_base_type.h"
#include "dkam_common_socket.h"
#include "dkam_ftpserver.h"
#include <iostream>
#include <stdbool.h>
#include <vector>
#include <cstring>
#include <map>
#ifdef _WIN32
#include<windows.h>
#include <WS2tcpip.h>
#else
#include <semaphore.h>
#endif
typedef struct {
int socket_id;
unsigned int payload_size;
unsigned int width;
unsigned int height;
RoiPoint roi;
unsigned int pixel_length;
unsigned int pixel_format;
unsigned int pack_num;
unsigned int cloud_unit;
}DataStreamPrivateInfo;
typedef struct {
RecvDataBuff recv_data_buff[3];//暂存区buffer
RecvDataBuff *read_position[3]; //指向暂存区buffer的读指针
RecvDataBuff *write_position[3]; // 指向暂存区buffer的写指针
}DataStreamBufferInfo;
class GigeStream;
extern char* sdk_version;
//相机控制,接收数据类
class GigeCamera :private CommonSocket{
public:
GigeCamera();
~GigeCamera();
//获取相机的CCP状态(返回0为可连接)
int GetCameraCCPStatus(DiscoveryInfo* discovery_info, int *data);
//连接相机
int CameraConnect(DiscoveryInfo* discovery_info);
//建立通道和com之间对应关系
int ChannalCorrespondComs();
//获取相机xml所有节点名称
int GetCameraXMLNodeNames(std::vector<std::string>* node_names);
//验证ip是否合法
bool IsIPAddressValid(unsigned int ip_int, unsigned int camera_mask_int);
//获取节点最大值
int GetNodeMaxValue(const char* key);
//获取节点最小值
int GetNodeMinValue(const char* key);
//获取int节点增量
int GetNodeIncValue(const char* key);
//获取相机内参
int GetCamInternelParameter(int camera_cnt, float *Kc, float *K);
//获取相机外参
int GetCamExternelParameter(int camera_cnt, float *R, float *T);
//图像膨胀
void PixelSwell(int *roi_output, PhotoInfo &target_data);
//图像腐蚀
void PixelCorrosion(int *roi_output, PhotoInfo &target_data);
//ROI映射区域坐标
void ROIMappingCoordinate(int *roi_output, PhotoInfo &target_data, RoiPoint &point_output);
//ROI检索映射
void ROIPixelMapping(PhotoInfo &point_data, PhotoInfo &source_data, PhotoInfo &target_data, RoiPoint &roi_input, int *ROI_output);
//写寄存器 返回写寄存器是否成功的状态码
int WriteRegister(unsigned int register_addr, int data);
//保存用户配置文件
int SaveUserConfig(char* fliename);
//加载用户配置文件
int LoadUserConfig(char* fliename);
//获取节点类型
int GetNodeType(const char* key);
//获取节点访问模式
int GetNodeAccessMode(const char* key);
//设置Int类型节点值
int SetIntNodeValue(const char* key, unsigned int value);
//设置Bool类型节点值
int SetBoolNodeValue(const char* key, int value);
//设置Command类型节点值
int SetCommandNodeValue(const char* key);
//设置Float类型节点值
int SetFloatNodeValue(const char* key, float value);
//设置String类型节点值
int SetStringNodeValue(const char* key, char* value);
//设置Enumeration类型节点值
int SetEnumNodeValue(const char* key, int value);
//获取Int类型节点值
int GetIntNodeValue(const char* key, int* value);
//获取相机节点value
int GetRegisterAddr(const char* key);
//读string类型的寄存器
int ReadStringRegister(const char* key, char* reg_str);
//写string类型的寄存器
int WriteStringRegister(const char* key, unsigned short datasize, char* reg_str);
//获取Bool类型节点值
int GetBoolNodeValue(const char* key, int* value);
//获取Command类型节点值
int GetCommandNodeValue(const char* key, char* value);
//获取Float类型节点值
int GetFloatNodeValue(const char* key, float* value);
//获取String类型节点值
int GetStringNodeValue(const char* key, char* value);
//获取Enumeration类型节点值
int GetEnumNodeValue(const char* key, int* value);
//读寄存器 返回读寄存器是否成功的状态码 register_addr:寄存器地址 data:读取的寄存器的值
int ReadRegister(unsigned int register_addr, int *data);
//设置相机种类 (0红外1RGB)
int SetCameraType(int camera_cnt);
//获取相机种类 (0红外1RGB)
int GetCameraType();
//获取相机的宽
int GetCameraWidth(int* width, int camera_cnt);
//设置相机的宽
int SetCameraWidth(int width, int camera_cnt);
//获取相机的高
int GetCameraHeight(int* height, int camera_cnt);
//设置相机的高
int SetCameraHeight(int height, int camera_cnt);
////设置超时时间
int SetHeartBeatTimeout(int value);
//获取超时时间
int GetHeartBeatTimeout(void);
//设置相机曝光模式1手动曝光0自动曝光 camera_cnt:0是红外 1是RGB
int SetAutoExposure(int status, int camera_cnt);
//获取相机曝光模式 camera_cnt:0是红外 1是RGB
int GetAutoExposure(int camera_cnt);
//设置RGB摄像头自动曝光增益的级别(>=1 仅支持RGB摄像头) (camera_cnt:相机的摄像头0红外摄像头 1:RGB摄像头 level:曝光增益等级)
int SetCamExposureGainLevel(int camera_cnt, int level);
//获取RGB摄像头自动曝光增益的级别仅支持RGB摄像头(camera_cnt:相机的摄像头0红外摄像头 1:RGB摄像头)
int GetCamExposureGainLevel(int camera_cnt);
//设置相机曝光类型int status > 0 多曝光 不能是0
int SetMutipleExposure(int status);
//获取相机曝光类型
int GetMutipleExposure(void);
//设置曝光时间utimes:曝光时间: 红外镜头范围1000 - 100000um, RGB镜头范围1000 - 56000um, 默认16600camera_cnt:0是红外 1是RGB
int SetExposureTime(int utime, int camera_cnt);
//获取相机曝光次数 0是红外 1是RGB
int GetExposureTime(int camera_cnt);
//设置多曝光模式 0等差1等比
int SetMultiExpoMode(int mode);
//获取多曝光模式
int GetMultiExpoMode();
//设置多曝光起点value 范围0-100000
int SetMultiExpoMin(int value);
//获取多曝光起点
int GetMultiExpoMin();
//设置多曝光终点value 范围0-100000
int SetMultiExpoMax(int value);
//获取多曝光终点
int GetMultiExpoMax();
//设置增益 model: 1 模拟增益量 2 数据增益量 value: 增益值 times:缺省参数缺省为1 第二次增益times = 2 camera_cnt:0是红外 1是RGB
int SetGain(int mode, int value, int camera_cnt);
//获取相机增益值 mode: 1 模拟增益量 2 数字增益量 camera_cnt:0是红外 1是RGB
int GetGain(int mode, int camera_cnt);
//设置相机触发模式mode: 0 连拍模式 1 触发模式
int SetTriggerMode(int mode);
//设置相机触发模式信号来源: 0 软触发 1 硬触发
int SetTriggerSource(int sourcetype);
//设置相机RGB触发模式mode: 0 连拍模式 1 触发模式
int SetRGBTriggerMode(int mode);
//设置相机触发模式下的触发帧数
int SetTriggerCount();
//获取相机触发模式下的触发帧数
int GetTriggerCount();
//设置相机RGB触发模式下的触发帧数
int SetRGBTriggerCount();
//设置ROI channel_index :数据流通道索引
int SetRoi(int channel_index, int size_x, int size_y, int offset_x, int offset_y);
//开启数据流通道 channel_index :数据流通道索引
int StreamOn(unsigned short channel_index, GigeStream** gigestream);
// 设置激光模型 1line 0plane
int SetLaserMode(int mode);
// 获取激光模型
int GetLaserMode();
//设置点云后处理模型
int SetPointCloudPostProcessMode(int mode);
//获取点云后处理模型
int GetPointCloudPostProcessMode();
//设置点云增益值取值范围0-30只有当点云自动增益等级为0时才可以设置该值
int SetPointCloudThresholdValue(int value);
//获取点云增益值
int GetPointCloudThresholdValue(int* value);
//设置点云自动增益等级: 0-20
int SetPointCloudThresholdLevel(int level);
//获取点云自动增益等级
int GetPointCloudThresholdLevel(int* level);
// 获取xml buffer size
int GetXMLBufferSize(int* size);
//获取xml buffer
int GetXMLBuffer(char* buffer);
//开启或关闭时间戳同步status: 0 关闭时间戳同步 1 开启时间戳同步
int SetTimestamp(int status);
//获取时间戳是否开启
int GetTimestamp();
//获取PTPD状态码
int GetTimestampStatus();
//控制锁存时间戳
int SetTimestampControlLatch();
//获取时间戳
unsigned long long GetTimestampValue();
//获取时间戳频率
unsigned long long GetTimestampTickFrequency();
//开始接受数据
int AcquisitionStart(void);
//停止接受数据
int AcquisitionStop(void);
//关闭数据流通道
int StreamOff(unsigned short channel_index, GigeStream* gigestream);
//相机断开连接
int CameraDisconnect();
//保存xml到本地
int SaveXmlToLocal(std::string pathname);
//点云从char *转成float
void Convert3DPointFromCharToFloat(PhotoInfo &raw_data, float* output);
//rawdata转RGB888图,输出的数据仍存放在PhotoInfo结构体的pixel中
int RawdataToRgb888(PhotoInfo &rgb_data);
//点云滤波(基于空间密度的点云去噪)
void FilterPointCloud(PhotoInfo &raw_data, double level);
//空间滤波(基于空间网格的点云去噪) 20220225: 弃用, FilterPointCloud为该API的升级版
int SpatialFilterPointcloud(PhotoInfo &raw_data, int Area_PointCloudCount);
//获取点云的X平面数据
int GetCloudPlaneX(PhotoInfo &raw_data, short *imagedata);
//获取点云的Y平面数据
int GetCloudPlaneY(PhotoInfo &raw_data, short *imagedata);
//获取点云的Z平面数据
int GetCloudPlaneZ(PhotoInfo &raw_data, short *imagedata);
//保存点云某个平面数据
int SaveCloudPlane(PhotoInfo &raw_data, short *imagedata, char* path_name);
//保存点云 to pcd 格式
int SavePointCloudToPcd(PhotoInfo &raw_data, char* path_name);
//保存点云 to txt 格式
int SavePointCloudToTxt(PhotoInfo &raw_data, char* path_name);
//保存点云 to ply 格式
int SavePointCloudToPly(PhotoInfo &raw_data, char* path_name);
//保存BMP图片
int SaveToBMP(PhotoInfo &data, char *path_name);
//保存点云深度图 to png
int SaveDepthToPng(PhotoInfo &raw_data, char* path_name);
//点云与图片融合(image_data:图片数据, raw_data:点云数据, image_cloud点云图片融合后的数据, is_filter是否进行滤波默认滤波)
int FusionImageTo3D(PhotoInfo &image_data, PhotoInfo &raw_data, float * image_cloud);
//点云RGB进行融合(以RGB为标准重排点云重排后的点云index对应无畸变的rgb图像)
int Fusion3DToRGBWithOutDistortion(PhotoInfo& rgb_data, PhotoInfo& raw_data, PhotoInfo& xyz);
//保存与图片融合后的点云txt
int SavePointCloudWithImageToTxt(PhotoInfo &raw_data, float * image_cloud, char *path_name);
//保存与图片融合后的点云Ply
int SavePointCloudWithImageToPly(PhotoInfo &raw_data, float * image_cloud, char *path_name);
//保存与图片融合后的点云Pcd
int SavePointCloudWithImageToPcd(PhotoInfo &raw_data, float * image_cloud, char *path_name);
//相机固件的版本号
char* CameraVerion(DiscoveryInfo discovery_info);
//SDK的版本号
char* SDKVersion();
//点云RGB进行融合(以RGB为标准重排点云)
int Fusion3DToRGB(PhotoInfo &rgb_data, PhotoInfo &raw_data, PhotoInfo &xyz);
//固件升级
int FirmwareUpgrade(DiscoveryInfo discovery_info, const char *localfilename);
//内核升级
int KernelUpgrade(DiscoveryInfo discovery_info, const char* localfilename);
//获取下位机日志
int DownloadCameraLog(DiscoveryInfo discovery_info, const char* path, const char* name);
//获取下位机日志目录
int CameraLogList(DiscoveryInfo discovery_info, std::vector<std::string>* filename_s, int* len);
//根据roi对数据进行裁剪可以传入rgb和gray数据
int ImageRoiCrop(PhotoInfo& source_data, RoiPoint roi, PhotoInfo& target_data);
//获取相机盖状态,此接口只对特定型号相机适用, 0相机盖关闭1相机盖打开 其他:查看错误码
int GetCameraCoverStatus(int* status);
//打开相机盖,此接口只对特定型号相机适用
int TurnOnCameraCover();
//关闭相机盖,此接口只对特定型号相机适用
int TurnOffCameraCover();
// 设置激光器正常/关闭接口1关闭0正常默认是正常状态
int SetLaserStatus(int status);
// 获取激光器正常/关闭接口1关闭0正常返回值0成功其他查看错误码
int GetLaserStatus(int *status);
private:
#ifdef _WIN32
static unsigned int _stdcall HeartBeat(void* arg);
#else
static void* HeartBeat(void* arg);
#endif
//读内存
int ReadMem(unsigned int mem_addr, unsigned short count, char* recv_buf);
//写内存
int WriteMem(unsigned int mem_addr, unsigned short count, char* recv_buf);
//获取相机xml配置文件
int GetXMLfromCamera();
////获取节点int类型的属性值
//int GetNodeProperty(const char* key, char *property);
//协商数据流包大小
int GetPacketSize();
//YUYV转RGB888图,输出的数据仍存放在PhotoInfo结构体的pixel中
int YuyvToRgb888(PhotoInfo &rgb_data);
//JPEG转RGB888图,输出的数据仍存放在PhotoInfo结构体的pixel中
int JpegToRgb888(PhotoInfo &rgb_data);
//BayerRG8转RGB888图,输出的数据仍存放在PhotoInfo结构体的pixel中
int BayerRG8ToRgb888(PhotoInfo &rgb_data);
//获取点云的某个平面数据
int GetCloudPlane(PhotoInfo &raw_data, short *imagedata, int plane);
//保存红外图 to bmp 格式
int SaveGrayImageToBmp(PhotoInfo &gray_data, char *path_name);
//保存rgb图 (RGB888)
int SaveRgb888ToBmp(PhotoInfo &rgb_data, char* path_name);
//保存rgb图 (RGB565)
int SaveRgb565ToBmp(PhotoInfo &rgb_data, char* path_name);
//保存rgb图(YUYV)
int SaveYuyvRgbToBmp(PhotoInfo &rgb_data, char* path_name);
int SaveBayerRG8ToBmp(PhotoInfo &rgb_data, char* path_name);
int SaveJpegDataToJpeg(PhotoInfo &rgb_data, char* path_name);
//清空socket
void flush_socket_buffer(int skt);
char *CameraIP(unsigned int Camera_IP);
private:
#ifdef _WIN32
//读写寄存器信号量
HANDLE rw_reg_sem = NULL;
//心跳线程线程句柄
HANDLE ccp_thread_id = NULL;
#else
//读写寄存器信号量
sem_t rw_reg_sem;
//心跳线程线程ID
pthread_t ccp_thread_id = 0;
#endif
unsigned short req_id_;
int camera_num_;
int gvcp_socket_id_;
int ccp_flag_;
//保存相机配置文件xml
char* xml_buffer_;
//相机配置文件名
std::string xml_name_;
//相机配置文件xml的后缀: xml or zip
std::string xml_extension_;
//相机配置文件xml的文件大小
int xml_size_;
//PC端MTU
int mtu_;
//流通道索引
//unsigned short stream_channel_index_;
//接收数据流包大小
int pack_size_; //包大小
//心跳超时时间
int heart_beat_timeout_;
//记录主机和客户端信息
InstanceDevice device_info_;
cameralog logfile;
std::vector<CameraParameter> cam_para;
//通道和cmos对应关系
int* streamList;
int* comsList;
int streamSize;
int comsSize;
char *camera_ip_;
FtpServer ftpserver;
public:
void *node_map;
void *Register_Data;
};
#endif //!DKAM_GIGE_CAMERA_H

258
include/dkam_gige_stream.h Normal file
View File

@@ -0,0 +1,258 @@
#pragma once
#include "dkam_asyncqueue.h"
#include "dkam_base_type.h"
#include "dkam_gige_camera.h"
#include "dkam_log.h"
#define GVSP_MAX_INCOMIN_SIZE 65535 //收到数据最大尺寸其实巨帧也不过9000多个这最为了方便直接用16位的最大值 。
#define SOCKET_SELECT_TIMEOUT_US_DEFAULT 10000 //select超时时间
#define PACKET_TIMEOUT_US_DEFAULT 400000 //重发包超时时间
#define BLOCK_TIMEOUT_US_DEFAULT 1000000 //block完整性检查评估的超时时间
#define PACKET_RESEND_FLAG_DEFAILT TRUE //重发包使用
#define PACKET_RESEND_RATIO_DEFAULT 1 //重发比例默认100%
#define MAX_BUFFER_LENGTH_DEFAULT 4 //最长的缓冲区大小默认4
#define NET_SPEED_STATISTICS_UNIT_TIME_US 1000000 //网速计时的默认时间区间1s
#define SEND_TO_SOURCE_SOURCE_TIME_US_DEFAULT 3000000 //默认发包破防火墙的间隔 3s
#define MAX_DISCARD_BLOCK_NUM 2000 //最大认为系统丢弃block数量
//block数据类型目前主要用IMAGE
typedef enum {
BUFFER_PAYLOAD_TYPE_UNKNOWN = -1,
BUFFER_PAYLOAD_TYPE_IMAGE = 0x0001,
BUFFER_PAYLOAD_TYPE_RAWDATA = 0x0002,
BUFFER_PAYLOAD_TYPE_FILE = 0x0003,
BUFFER_PAYLOAD_TYPE_CHUNK_DATA = 0x0004,
BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA = 0x0005, /* Deprecated */
BUFFER_PAYLOAD_TYPE_JPEG = 0x0006,
BUFFER_PAYLOAD_TYPE_JPEG2000 = 0x0007,
BUFFER_PAYLOAD_TYPE_H264 = 0x0008,
BUFFER_PAYLOAD_TYPE_MULTIZONE_IMAGE = 0x0009
} BufferPayloadType;
//block收到数据的状态
typedef enum {
BUFFER_STATUS_SUCCESS = 0,
BUFFER_STATUS_UNKNOWN = -1,
BUFFER_STATUS_CLEARED = -2,
BUFFER_STATUS_TIMEOUT = -3,
BUFFER_STATUS_MISSING_PACKETS = -4,
BUFFER_STATUS_WRONG_PACKET_ID = -5,
BUFFER_STATUS_WRONG_PACKET = -6,
BUFFER_STATUS_SIZE_MISMATCH = -7,
BUFFER_STATUS_FILLING = -8,
BUFFER_STATUS_ABORTED = -9
} BufferStatus;
typedef struct {
unsigned short status;
unsigned short block_id;
unsigned int packet_infos;
} GvspHeader;
typedef struct {
unsigned short flags;
unsigned short payload_type;
unsigned int timestamp_high;
unsigned int timestamp_low;
unsigned int pixel_format;
unsigned int width;
unsigned int height;
unsigned int x_offset;
unsigned int y_offset;
unsigned int gvsp_payload_size;
} GvspDataLeader;
typedef struct {
unsigned int payload_type;
unsigned int data0;
} GvspDataTrailer;
typedef struct {
GvspHeader header;
unsigned char data[1];
} GvspPacket;
typedef struct
{
AsyncQueue *in_buffer;
AsyncQueue *out_buffer;
}StreamDataBuffer;
typedef struct {
int received; //是否收到包的布尔量
long long time_us; //重发该包请求的时间
} StreamPacketData;
typedef struct {
PhotoInfo *buffer;
unsigned int block_id;
int buffer_status; //当前包的状态最好并入到PhotoInfo中发便用户层查看
int last_valid_packet; //连续的有效包中的最后一包,用于重发时检查的起点
long long first_packet_time_us; //收到首包的时间,用于后面的统计
long long last_packet_time_us; //收到最后一包的时间,在完整性检查是查看是否超时
int error_packet_received; //收到错误包,以此决定是不是可以将此包送到用户层
int packet_resend_num; //每次重发包的次数避免过多重发包请求干扰正常的GVCP通信。
unsigned int n_packets; //一个block的总包数
StreamPacketData *packet_data; //每包的信息
} StreamBlockData;
class GigeStream
{
public:
GigeStream(DataStreamPrivateInfo *stream_private_info,
unsigned short channel_index,
int gvsp_socket_id,
int interface_address,
int device_address,
int source_stream_port,
int stream_port,
int packet_size,
long long timestamp_tick_frequency,
cameralog *logfile);
~GigeStream();
int Capture(PhotoInfo *buffer);
int TryCapture(PhotoInfo *buffer);
int TimeoutCapture(PhotoInfo *buffer, long long timeout);
void FlushBuffer();
int SetMaxBufferLength(unsigned int length);
int GetMaxBufferLength();
void SetPacketResendFlag(unsigned int flag);
int GetPacketResendFlag();
int SetPacketResendRatio(double ratio);
double GetPacketResendRatio();
int SetSocketSelectTimeout(unsigned int timeout);
int GetSocketSelectTimeout();
int SetPacketTimeout(unsigned int timeout);
int GetPacketTimeout();
int SetBlockTimeout(unsigned int timeout);
int GetBlockTimeout();
int SetSendtoStreamPortBreakFirewallTimeout(unsigned int timeout);
int GetSendtoStreamPortBreakFirewallTimeout();
//Statistics();
void GetBlockStatistics(
unsigned int *completed_buffers,
unsigned int *failures,
unsigned int *timeouts,
unsigned int *underruns,
unsigned int *aborteds,
unsigned int *missing_frames,
unsigned int *block_camera_wrong,
unsigned int *size_mismatch_errors);
void GetPacketStatistics(
unsigned int *received_packets,
unsigned int *missing_packets,
unsigned int *error_packets,
unsigned int *ignored_packets,
unsigned int *resend_requests,
unsigned int *resent_packets,
unsigned int *duplicated_packets);
void GetRecieveTimeStatistics(StatisticsData *o_statistics_data);
int GetNetSpeed();
float GetBlockRate();
int thread_exit;
private:
#ifdef _WIN32
//接收点云数据线程
static unsigned int _stdcall Stream_Thread(void* arg);
#else
static void* Stream_Thread(void* arg);
#endif
void loop();
StreamBlockData* process_packet(GvspPacket *packet, int recv_num, long long time_us);
StreamBlockData* find_block_data(GvspPacket *packet, unsigned int block_id, unsigned int packet_id, int recv_num, long long time_us);
void process_packet_leader(StreamBlockData *block, GvspPacket *packet, unsigned int packet_id);
void process_packet_payload(StreamBlockData *block, GvspPacket *packet, unsigned int packet_id, unsigned int recv_num);
void process_packet_tailer(StreamBlockData *block, GvspPacket *packet, unsigned int packet_id);
void packet_resend_check(StreamBlockData *block, unsigned int packet_id, long long time_us);
void send_packet_request(unsigned int block_id, int resend_first_packet_id, int resend_last_packet_id);
void write_recive_data(StreamBlockData *block);
void check_block_complete(StreamBlockData *block,long long time_us);
void capture_data_process(PhotoInfo *buffer, PhotoInfo *temp_buffer);
void flush_blocks();
void sendto_stream_source_data();
void RecieveTimeStatistics(int data);
void NetSpeedStatistics(int recv_num, long long time_us);
void BlockRateStatistics(long long time_us);
#ifdef _WIN32
HANDLE stream_on_thread_id;
#else
pthread_t stream_on_thread_id;
#endif
unsigned short channel_index_;
StreamDataBuffer stream_data;
StatisticsData statistics_data;
DataStreamPrivateInfo stream_private_info_;
List *all_blocks;
int gvsp_socket_id_;
int resend_socket_id_;
int interface_address_;
//int *interface_socket_address;
int device_address_;
//int *device_socket_address;
struct sockaddr_in resend_addr;
struct sockaddr_in sendto_source_addr;
unsigned short source_stream_port_;
unsigned short stream_port_;
long long timestamp_tick_frequency_;
unsigned short resend_req_id;
unsigned int last_block_id;
unsigned int max_buffer_length_;
unsigned int packet_payload_size_;
unsigned int max_payload_size_;
unsigned int current_buffer_length;
int packet_resend_flag_;
double packet_resend_ratio_;
unsigned int socket_select_timeout_us_;
unsigned int packet_timeout_us_;
unsigned int block_timeout_us_;
unsigned int sento_stream_source_time_us_;
/* Statistics */
//block部分的数据统计
unsigned int n_completed_buffers;
unsigned int n_failures;
unsigned int n_timeouts;
unsigned int n_underruns;
unsigned int n_aborteds;
unsigned int n_missing_frames;
unsigned int n_block_camera_wrong;
unsigned int n_size_mismatch_errors;
//packet部分的数据统计
unsigned int n_received_packets;
unsigned int n_missing_packets;
unsigned int n_error_packets;
unsigned int n_ignored_packets;
unsigned int n_resend_requests;
unsigned int n_resent_packets;
unsigned int n_duplicated_packets;
//网速部分的统计,这里主要是时间,这段时间收到的数据量和以此计算出来的网速;
long long n_start_time_us;
long long n_recv_num_unit_time;
unsigned int n_net_speed;
long long n_block_start_time_us;
unsigned int n_block_recv_num_unit_time;
float n_block_rate;
cameralog *logfilestream;
//重发包计时
time_t start_time_resend;
time_t end_time_resend;
};

41
include/dkam_log.h Normal file
View File

@@ -0,0 +1,41 @@
#pragma once
#include <stdio.h>
//这可以在主函数中直接修改全局变量的值,以便可查找,也可以定义宏变量的值,然后把宏变量的值写一下。
extern int gvsp_log_error_level;
extern int gvsp_log_warnning_level;
extern int gvsp_log_info_level;
extern int gvsp_log_debug_level;
extern int gvcp_log_error_level;
extern int gvcp_log_warnning_level;
extern int gvcp_log_info_level;
extern int gvcp_log_debug_level;
class cameralog {
public:
cameralog();
~cameralog();
void log_error(int level, const char *format, ...);
int log_enable(char *logname);
void log_warnning(int level, const char *format, ...);
void log_info(int level, const char *format, ...);
void log_debug(int level, const char *format, ...);
void log_disable(void);
private:
FILE *fd;
};
#define log_error_gvsp(...) log_error (gvsp_log_error_level, __VA_ARGS__)
#define log_warnning_gvsp(...) log_warnning (gvsp_log_warnning_level, __VA_ARGS__)
#define log_info_gvsp(...) log_info (gvsp_log_info_level, __VA_ARGS__)
#define log_debug_gvsp(...) log_debug (gvsp_log_debug_level, __VA_ARGS__)
#define log_error_gvcp(...) log_error (gvcp_log_error_level, __VA_ARGS__)
#define log_warnning_gvcp(...) log_warnning (gvcp_log_warnning_level, __VA_ARGS__)
#define log_info_gvcp(...) log_info (gvcp_log_info_level, __VA_ARGS__)
#define log_debug_gvcp(...) log_debug (gvcp_log_debug_level, __VA_ARGS__)

View File

@@ -0,0 +1,394 @@
/********************************************************************************************
Copyright : XianZhisensorTechnologiesCo.,Ltd
File Name : zhicamera_api.cpp
Description: APIs accessed by users
Version :
Author : ss.chen
Data : 2020-4-16
History :
*********************************************************************************************/
#pragma once
#include "dkam_base_type.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C"
{
#endif
//using namespace std;
typedef struct CAMERA_OBJECT Camera_Object_C;
#ifdef _WIN32
#define DLLEXPORT_API extern __declspec(dllexport)
#define DLL_INT int __stdcall
#define DLL_CHAR_POINT char* __stdcall
#define DLL_BOOL bool __stdcall
#define DLL_UNSIGNED_LONG_LONG unsigned long long __stdcall
#define DLL_UNSIGNED_INT unsigned int __stdcall
#define DLL_VOID void __stdcall
#define DLL_CAMERA_OBJECT_POINT Camera_Object_C* __stdcall
#define DLL_DISCOVERYINFO DiscoveryInfo __stdcall
#define DLL_DOUBLE double __stdcall
#define DLL_FLOAT float __stdcall
#else
#define DLLEXPORT_API
#define DLL_INT int
#define DLL_CHAR_POINT char*
#define DLL_BOOL bool
#define DLL_UNSIGNED_LONG_LONG unsigned long long
#define DLL_UNSIGNED_INT unsigned int
#define DLL_VOID void
#define DLL_CAMERA_OBJECT_POINT Camera_Object_C*
#define DLL_DISCOVERYINFO DiscoveryInfo
#define DLL_DOUBLE double
#define DLL_FLOAT float
#endif
/*/////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
******************************************相机相关操作*****************************************
///////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////*/
//发现相机
DLLEXPORT_API DLL_INT DiscoverCamera();
//创建相机
DLLEXPORT_API DLL_CAMERA_OBJECT_POINT CreateCamera(int camera_index);
//销毁相机
DLLEXPORT_API DLL_VOID DestroyCamera(Camera_Object_C* camera_obj);
//相机排序(0:IP 1:series number)
DLLEXPORT_API DLL_INT CameraSort(int sort_mode);
//获取相机的信息
DLLEXPORT_API DLL_DISCOVERYINFO GetCameraInfo(Camera_Object_C* camera_obj);
//获取相机的CCP状态(data输出0为可连接)
DLLEXPORT_API DLL_INT GetCameraCCPStatus(Camera_Object_C* camera_obj, int *data);
//获取相机xml所有节点名称
DLLEXPORT_API DLL_VOID GetCameraXMLNodeNames(Camera_Object_C* camera_obj, char node_names[][255], int* len);
//获取节点最大值
DLLEXPORT_API DLL_INT GetNodeMaxValue(Camera_Object_C* camera_obj, const char* key);
//获取节点最小值
DLLEXPORT_API DLL_INT GetNodeMinValue(Camera_Object_C* camera_obj, const char* key);
//获取int节点增量
DLLEXPORT_API DLL_INT GetNodeIncValue(Camera_Object_C* camera_obj, const char* key);
//获取相机节点value(camera_ret:相机索引号 key:寄存器的名称)
DLLEXPORT_API DLL_INT GetRegisterAddr(Camera_Object_C* camera_obj, const char* key);
//读string类型的寄存器
DLLEXPORT_API DLL_INT ReadStringRegister(Camera_Object_C* camera_obj, const char* key, char* reg_str);
//写string类型的寄存器
DLLEXPORT_API DLL_INT WriteStringRegister(Camera_Object_C* camera_obj, const char* key, unsigned short datasize, char* reg_str);
//相机IP(camera_ret:相机索引号)
DLLEXPORT_API DLL_CHAR_POINT CameraIP(int camera_index);
//相机的序列号
DLLEXPORT_API DLL_CHAR_POINT CameraSeriesNumberByIndex(int camera_index);
//设置log日志的等级开关决定是否开启某一个等级的日志打印默认关闭(0:关闭 1:开启)
DLLEXPORT_API DLL_VOID SetLogLevel(int error, int debug, int warnning, int info);
//连接相机(camera_ret:相机索引号)
DLLEXPORT_API DLL_INT CameraConnect(Camera_Object_C* camera_obj);
//开启数据流通道 (camera_ret:相机索引号 channel_index :数据流通道索引)
DLLEXPORT_API DLL_INT StreamOn(Camera_Object_C* camera_obj, unsigned short channel_index);
//开始接受数据 (camera_ret:相机索引号)
DLLEXPORT_API DLL_INT AcquisitionStart(Camera_Object_C* camera_obj);
//清空缓存区
DLLEXPORT_API DLL_VOID FlushBuffer(Camera_Object_C* camera_obj, unsigned short channel_index);
//设置最大的缓冲区
DLLEXPORT_API DLL_INT SetMaxBufferLength(Camera_Object_C* camera_obj, unsigned short channel_index, unsigned int size);
//获取buffer缓冲区大小
DLLEXPORT_API DLL_INT GetMaxBufferLength(Camera_Object_C* camera_obj, unsigned short channel_index);
//设置重发包比例
DLLEXPORT_API DLL_INT SetPacketResendRatio(Camera_Object_C* camera_obj, unsigned short channel_index, double ratio);
//获取重发包比例
DLLEXPORT_API DLL_DOUBLE GetPacketResendRatio(Camera_Object_C* camera_obj, unsigned short channel_index);
//设置select的超时时间
DLLEXPORT_API DLL_INT SetSocketSelectTimeout(Camera_Object_C* camera_obj, unsigned short channel_index, unsigned int timeout);
//获取select的超时时间
DLLEXPORT_API DLL_INT GetSocketSelectTimeout(Camera_Object_C* camera_obj, unsigned short channel_index);
//设置重发包超时时间
DLLEXPORT_API DLL_INT SetPacketTimeout(Camera_Object_C* camera_obj, unsigned short channel_index, unsigned int timeout);
//获取重发包超时时间
DLLEXPORT_API DLL_INT GetPacketTimeout(Camera_Object_C* camera_obj, unsigned short channel_index);
//设置block超时时间
DLLEXPORT_API DLL_INT SetBlockTimeout(Camera_Object_C* camera_obj, unsigned short channel_index, unsigned int timeout);
//获取block超时时间
DLLEXPORT_API DLL_INT GetBlockTimeout(Camera_Object_C* camera_obj, unsigned short channel_index);
//获取帧方面的统计数据
DLLEXPORT_API DLL_VOID GetBlockStatistics(Camera_Object_C* camera_obj, unsigned short channel_index,
unsigned int *completed_buffers,
unsigned int *failures,
unsigned int *timeouts,
unsigned int *underruns,
unsigned int *aborteds,
unsigned int *missing_frames,
unsigned int *block_camera_wrong,
unsigned int *size_mismatch_errors);
//获取包方面的统计数据
DLLEXPORT_API DLL_VOID GetPacketStatistics(Camera_Object_C* camera_obj, unsigned short channel_index,
unsigned int *received_packets,
unsigned int *missing_packets,
unsigned int *error_packets,
unsigned int *ignored_packets,
unsigned int *resend_requests,
unsigned int *resent_packets,
unsigned int *duplicated_packets);
//获取接收花费时间方面的统计数据
DLLEXPORT_API DLL_VOID GetRecieveTimeStatistics(Camera_Object_C* camera_obj, unsigned short channel_index, StatisticsData *o_statistics_data);
//获取网速的统计数据
DLLEXPORT_API DLL_INT GetNetSpeed(Camera_Object_C* camera_obj, unsigned short channel_index);
//获取帧率的统计数据
DLLEXPORT_API DLL_FLOAT GetBlockRate(Camera_Object_C* camera_obj, unsigned short channel_index);
//获取数据(camera_ret:相机索引号 channel_index :数据流通道索引 raw_data:接收数据的结构体 )
/*C++*/
//阻塞式抓取数据
DLLEXPORT_API DLL_INT Capture(Camera_Object_C* camera_obj, unsigned short channel_index, PhotoInfo *raw_data);
//try
DLLEXPORT_API DLL_INT TryCapture(Camera_Object_C* camera_obj, unsigned short channel_index, PhotoInfo *raw_data);
//超时抓取数据
DLLEXPORT_API DLL_INT TimeoutCapture(Camera_Object_C* camera_obj, unsigned short channel_index, PhotoInfo *raw_data, long long timeout);
//将char数据转为float类型
DLLEXPORT_API DLL_VOID Convert3DPointFromCharToFloat(Camera_Object_C* camera_obj, PhotoInfo *raw_data, float* output);
//将Rawdata数据转换成RGB888的图像数据
DLLEXPORT_API DLL_INT RawdataToRgb888(Camera_Object_C* camera_obj, PhotoInfo *rgb_data);
//获取点云的X平面数据
DLLEXPORT_API DLL_INT GetCloudPlaneX(Camera_Object_C* camera_obj, PhotoInfo *raw_data, short *imagedata);
//获取点云的Y平面数据
DLLEXPORT_API DLL_INT GetCloudPlaneY(Camera_Object_C* camera_obj, PhotoInfo *raw_data, short *imagedata);
//获取点云的Z平面数据
DLLEXPORT_API DLL_INT GetCloudPlaneZ(Camera_Object_C* camera_obj, PhotoInfo *raw_data, short *imagedata);
//保存点云某个平面数据
DLLEXPORT_API DLL_INT SaveCloudPlane(Camera_Object_C* camera_obj, PhotoInfo *raw_data, short *imagedata, char* path_name);
/*C++*/
//停止接受数据(camera_ret:相机索引号)
DLLEXPORT_API DLL_INT AcquisitionStop(Camera_Object_C* camera_obj);
//关闭数据流通道 (camera_ret:相机索引号 channel_index :数据流通道索引)
DLLEXPORT_API DLL_INT StreamOff(Camera_Object_C* camera_obj, unsigned short channel_index);
//相机断开连接 (camera_ret:相机索引号)
DLLEXPORT_API DLL_INT CameraDisconnect(Camera_Object_C* camera_obj);
/*/////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
****************************************GVCP相关操作*******************************************
///////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////*/
//获取相机内参
DLLEXPORT_API DLL_INT GetCamInternelParameter(Camera_Object_C* camera_obj, int camera_cnt, float *Kc, float *K);
//获取相机外参
DLLEXPORT_API DLL_INT GetCamExternelParameter(Camera_Object_C* camera_obj, int camera_cnt, float *R, float *T);
//设置相机ip (camera_ret:相机索引号 ip mask gateway)
DLLEXPORT_API DLL_INT ForceIP(Camera_Object_C* camera_obj, char* ip, char* mask, char* gateway);
//判断相机的IP和PC的IP是否在同一网段
DLLEXPORT_API DLL_BOOL WhetherIsSameSegment(Camera_Object_C* camera_obj);
//设置相机种类 (0红外1RGB)
DLLEXPORT_API DLL_INT SetCameraType(Camera_Object_C* camera_obj, int camera_cnt);
//获取相机种类 (0红外1RGB)
DLLEXPORT_API DLL_INT GetCameraType(Camera_Object_C* camera_obj);
//获取相机的宽
DLLEXPORT_API DLL_INT GetCameraWidth(Camera_Object_C* camera_obj, int* width, int camera_cnt);
//设置相机的宽
DLLEXPORT_API DLL_INT SetCameraWidth(Camera_Object_C* camera_obj, int width, int camera_cnt);
//获取相机的高
DLLEXPORT_API DLL_INT GetCameraHeight(Camera_Object_C* camera_obj, int* height, int camera_cnt);
//设置相机的高
DLLEXPORT_API DLL_INT SetCameraHeight(Camera_Object_C* camera_obj, int height, int camera_cnt);
//写寄存器 (camera_ret:相机索引号 register_addr:寄存器的地址 data:接收寄存器的值)
DLLEXPORT_API DLL_INT WriteRegister(Camera_Object_C* camera_obj, unsigned int register_addr, int data);
//读寄存器 (camera_ret:相机索引号 register_addr:寄存器的地址 data:接收寄存器的值)
DLLEXPORT_API DLL_INT ReadRegister(Camera_Object_C* camera_obj, unsigned int register_addr, int* data);
//设置超时时间
DLLEXPORT_API DLL_INT SetHeartBeatTimeout(Camera_Object_C* camera_obj, int value);
//获取超时时间
DLLEXPORT_API DLL_INT GetHeartBeatTimeout(Camera_Object_C* camera_obj);
//设置相机曝光模式(camera_index:相机索引号 status:1手动曝光0自动曝光 camera_cnt:相机的摄像头0红外摄像头 1:RGB摄像头)
DLLEXPORT_API DLL_INT SetAutoExposure(Camera_Object_C* camera_obj, int status, int camera_cnt);
//获取相机曝光模式 (camera_index:相机索引号 camera_cnt:相机的摄像头0红外摄像头 1:RGB摄像头)
DLLEXPORT_API DLL_INT GetAutoExposure(Camera_Object_C* camera_obj, int camera_cnt);
//设置RGB摄像头自动曝光增益的级别(>=1 仅支持RGB摄像头) (camera_index:相机索引号 camera_cnt:相机的摄像头0红外摄像头 1:RGB摄像头 level:曝光增益等级)
DLLEXPORT_API DLL_INT SetCamExposureGainLevel(Camera_Object_C* camera_obj, int camera_cnt, int level);
//获取RGB摄像头自动曝光增益的级别仅支持RGB摄像头(camera_index:相机索引号 camera_cnt:相机的摄像头0红外摄像头 1:RGB摄像头)
DLLEXPORT_API DLL_INT GetCamExposureGainLevel(Camera_Object_C* camera_obj, int camera_cnt);
//设置相机曝光类型int status >0 多重曝光 不能是0
DLLEXPORT_API DLL_INT SetMutipleExposure(Camera_Object_C* camera_obj, int status);
//获取相机曝光类型
DLLEXPORT_API DLL_INT GetMutipleExposure(Camera_Object_C* camera_obj);
//设置曝光时间utimes:曝光时间: 红外镜头范围1000 - 100000um, RGB镜头范围1000 - 56000um, 默认16600, camera_cnt:0是红外 1是RGB
DLLEXPORT_API DLL_INT SetExposureTime(Camera_Object_C* camera_obj, int utime, int camera_cnt);
//获取相机曝光时间 camera_cnt:0是红外 1是RGB
DLLEXPORT_API DLL_INT GetExposureTime(Camera_Object_C* camera_obj, int camera_cnt);
//设置相机多曝光模式, 0:等差1等比
DLLEXPORT_API DLL_INT SetMultiExpoMode(Camera_Object_C* camera_obj, int mode);
//获取相机多曝光模式
DLLEXPORT_API DLL_INT GetMultiExpoMode(Camera_Object_C* camera_obj);
//设置多曝光起点value 范围0-100000
DLLEXPORT_API DLL_INT SetMultiExpoMin(Camera_Object_C* camera_obj, int value);
//获取多曝光起点
DLLEXPORT_API DLL_INT GetMultiExpoMin(Camera_Object_C* camera_obj);
//设置多曝光终点value 范围0-100000
DLLEXPORT_API DLL_INT SetMultiExpoMax(Camera_Object_C* camera_obj, int value);
//获取多曝光终点
DLLEXPORT_API DLL_INT GetMultiExpoMax(Camera_Object_C* camera_obj);
//设置增益 model: 1 模拟增益量 2 数据增益量 value: 增益值 times:缺省参数缺省为1 第二次增益times = 2
DLLEXPORT_API DLL_INT SetGain(Camera_Object_C* camera_obj, int mode, int value, int camera_cnt);
//获取相机增益值 mode: 1 模拟增益量 2 数据增益量
DLLEXPORT_API DLL_INT GetGain(Camera_Object_C* camera_obj, int mode, int camera_cnt);
//设置相机触发模式mode: 0 连拍模式 1 触发模式
DLLEXPORT_API DLL_INT SetTriggerMode(Camera_Object_C* camera_obj, int mode);
//设置相机触发模式信号来源: 0 软触发 1 硬触发
DLLEXPORT_API DLL_INT SetTriggerSource(Camera_Object_C* camera_obj, int sourcetype);
//设置相机RGB触发模式mode: 0 连拍模式 1 触发模式
DLLEXPORT_API DLL_INT SetRGBTriggerMode(Camera_Object_C* camera_obj, int mode);
//设置相机触发模式下的触发帧数
DLLEXPORT_API DLL_INT SetTriggerCount(Camera_Object_C* camera_obj);
//获取相机触发模式下的触发帧数
DLLEXPORT_API DLL_INT GetTriggerCount(Camera_Object_C* camera_obj);
//设置相机RGB触发模式下的触发帧数
DLLEXPORT_API DLL_INT SetRGBTriggerCount(Camera_Object_C* camera_obj);
//设置重发请求: 0 关闭, 1 打开(默认开启)
DLLEXPORT_API DLL_VOID SetResendRequest(Camera_Object_C* camera_obj, int channel_index, int resend_flag);
//获取重发请求: 0 关闭, 1 打开(默认开启)
DLLEXPORT_API DLL_INT GetResendRequest(Camera_Object_C* camera_obj, int channel_index);
//设置红外摄像头ROI
DLLEXPORT_API DLL_INT SetRoi(Camera_Object_C* camera_obj, int channel_index, int size_x, int size_y, int offset_x, int offset_y);
//设置激光模式: 1 line 0 plane(默认plane)
DLLEXPORT_API DLL_INT SetLaserMode(Camera_Object_C* camera_obj, int mode);
//获取激光模式
DLLEXPORT_API DLL_INT GetLaserMode(Camera_Object_C* camera_obj);
//设置点云后处理模型
DLLEXPORT_API DLL_INT SetPointCloudPostProcessMode(Camera_Object_C* camera_obj, int mode);
//获取点云后处理模型
DLLEXPORT_API DLL_INT GetPointCloudPostProcessMode(Camera_Object_C* camera_obj);
//设置点云增益值取值范围0-30只有当点云自动增益等级为0时才可以设置该值
DLLEXPORT_API DLL_INT SetPointCloudThresholdValue(Camera_Object_C* camera_obj, int value);
//获取点云增益值
DLLEXPORT_API DLL_INT GetPointCloudThresholdValue(Camera_Object_C* camera_obj, int* value);
//设置点云自动增益等级: 0-20
DLLEXPORT_API DLL_INT SetPointCloudThresholdLevel(Camera_Object_C* camera_obj, int level);
//获取点云自动增益等级
DLLEXPORT_API DLL_INT GetPointCloudThresholdLevel(Camera_Object_C* camera_obj, int* level);
//获取xml buffer size
DLLEXPORT_API DLL_INT GetXMLBufferSize(Camera_Object_C* camera_obj, int* xml_size);
//获取xml buffer
DLLEXPORT_API DLL_INT GetXMLBuffer(Camera_Object_C* camera_obj, char* buffer);
//保存用户配置文件
DLLEXPORT_API DLL_INT SaveUserConfig(Camera_Object_C* camera_obj, char* fliename);
//加载用户配置文件
DLLEXPORT_API DLL_INT LoadUserConfig(Camera_Object_C* camera_obj,char* fliename);
//获取节点类型
DLLEXPORT_API DLL_INT GetNodeType(Camera_Object_C* camera_obj, const char* key);
//获取节点访问模式
DLLEXPORT_API DLL_INT GetNodeAccessMode(Camera_Object_C* camera_obj, const char* key);
//设置Int类型节点值
DLLEXPORT_API DLL_INT SetIntNodeValue(Camera_Object_C* camera_obj, const char* key, unsigned int value);
//设置Bool类型节点值
DLLEXPORT_API DLL_INT SetBoolNodeValue(Camera_Object_C* camera_obj, const char* key, bool value);
//设置Command类型节点值
DLLEXPORT_API DLL_INT SetCommandNodeValue(Camera_Object_C* camera_obj, const char* key);
//设置Float类型节点值
DLLEXPORT_API DLL_INT SetFloatNodeValue(Camera_Object_C* camera_obj, const char* key, float value);
//设置String类型节点值
DLLEXPORT_API DLL_INT SetStringNodeValue(Camera_Object_C* camera_obj, const char* key, char* value);
//设置Enumeration类型节点值
DLLEXPORT_API DLL_INT SetEnumNodeValue(Camera_Object_C* camera_obj, const char* key, int value);
//获取Int类型节点值
DLLEXPORT_API DLL_INT GetIntNodeValue(Camera_Object_C* camera_obj, const char* key, int* value);
//获取Bool类型节点值
DLLEXPORT_API DLL_INT GetBoolNodeValue(Camera_Object_C* camera_obj, const char* key, int* value);
//获取Command类型节点值
DLLEXPORT_API DLL_INT GetCommandNodeValue(Camera_Object_C* camera_obj, const char* key, char* value);
//获取Float类型节点值
DLLEXPORT_API DLL_INT GetFloatNodeValue(Camera_Object_C* camera_obj, const char* key, float* value);
//获取String类型节点值
DLLEXPORT_API DLL_INT GetStringNodeValue(Camera_Object_C* camera_obj, const char* key, char* value);
//获取Enumeration类型节点值
DLLEXPORT_API DLL_INT GetEnumNodeValue(Camera_Object_C* camera_obj, const char* key, int* value);
//开启或关闭时间戳同步status: 0 关闭时间戳同步 1 开启时间戳同步
DLLEXPORT_API DLL_INT SetTimestamp(Camera_Object_C* camera_obj, int status);
//获取时间戳是否开启
DLLEXPORT_API DLL_INT GetTimestamp(Camera_Object_C* camera_obj);
//获取PTPD状态码
DLLEXPORT_API DLL_INT GetTimestampStatus(Camera_Object_C* camera_obj);
//控制锁存时间戳
DLLEXPORT_API DLL_INT SetTimestampControlLatch(Camera_Object_C* camera_obj);
//获取时间戳
DLLEXPORT_API DLL_UNSIGNED_LONG_LONG GetTimestampValue(Camera_Object_C* camera_obj);
//获取时间戳频率
DLLEXPORT_API DLL_UNSIGNED_LONG_LONG GetTimestampTickFrequency(Camera_Object_C* camera_obj);
//固件升级
DLLEXPORT_API DLL_INT FirmwareUpgrade(Camera_Object_C* camera_obj, const char *localfilename);
//内核升级
DLLEXPORT_API DLL_INT KernelUpgrade(Camera_Object_C* camera_obj, const char* localfilename);
//获取下位置日志
DLLEXPORT_API DLL_INT DownloadCameraLog(Camera_Object_C* camera_obj, const char* filepath, const char* filename);
//获取下位机日志目录
DLLEXPORT_API DLL_INT CameraLogList(Camera_Object_C* camera_obj, char filename_s[][255], int* len);
//图片裁剪
DLLEXPORT_API DLL_INT ImageRoiCrop(Camera_Object_C* camera_obj, PhotoInfo* source_data, RoiPoint roi, PhotoInfo* target_data);
//获取相机盖状态,此接口只对特定型号相机适用, 0相机盖关闭1相机盖打开 其他:查看错误码
DLLEXPORT_API DLL_INT GetCameraCoverStatus(Camera_Object_C* camera_obj, int* status);
//相机盖状态,此接口只对特定型号相机适用
DLLEXPORT_API DLL_INT TurnOnCameraCover(Camera_Object_C* camera_obj);
//关闭相机盖,此接口只对特定型号相机适用
DLLEXPORT_API DLL_INT TurnOffCameraCover(Camera_Object_C* camera_obj);
// 设置激光器正常/关闭接口1关闭0正常默认是正常状态
DLLEXPORT_API DLL_INT SetLaserStatus(Camera_Object_C* camera_obj, int status);
// 获取激光器正常/关闭接口1关闭0正常返回值0成功其他查看错误码
DLLEXPORT_API DLL_INT GetLaserStatus(Camera_Object_C* camera_obj, int* status);
/*/////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
*******************************************数据保存********************************************
///////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////*/
//保存xml到本地
DLLEXPORT_API DLL_INT SaveXmlToLocal(Camera_Object_C* camera_obj, char* pathname);
/*C++*/
//保存点云 to pcd 格式
DLLEXPORT_API DLL_INT SavePointCloudToPcd(Camera_Object_C* camera_obj, PhotoInfo *raw_data, char* path_name);
//保存点云 to txt 格式
DLLEXPORT_API DLL_INT SavePointCloudToTxt(Camera_Object_C* camera_obj, PhotoInfo *raw_data, char* path_name);
//保存点云 to ply 格式
DLLEXPORT_API DLL_INT SavePointCloudToPly(Camera_Object_C* camera_obj, PhotoInfo *raw_data, char* path_name);
//点云滤波(基于空间密度的点云去噪)
DLLEXPORT_API DLL_VOID FilterPointCloud(Camera_Object_C* camera_obj, PhotoInfo *raw_data, double level);
//空间滤波(基于空间网格的点云去噪) 20220225: 弃用, FilterPointCloud为该API的升级版
DLLEXPORT_API DLL_INT SpatialFilterPointcloud(Camera_Object_C* camera_obj, PhotoInfo *raw_data, int Area_PointCloudCount);
//保存bmp图
DLLEXPORT_API DLL_INT SaveToBMP(Camera_Object_C* camera_obj, PhotoInfo *data, char* path_name);
//保存点云深度图 to png
DLLEXPORT_API DLL_INT SaveDepthToPng(Camera_Object_C* camera_obj, PhotoInfo *raw_data, char* path_name);
//点云融合
DLLEXPORT_API DLL_INT FusionImageTo3D(Camera_Object_C* camera_obj, PhotoInfo *image_data, PhotoInfo *raw_data, float * image_cloud);
//根据RGB重排点云
DLLEXPORT_API DLL_INT Fusion3DToRGB(Camera_Object_C* camera_obj, PhotoInfo *rgb_data, PhotoInfo *raw_data, PhotoInfo *xyz);
//根据RGB重排点云
DLLEXPORT_API DLL_INT Fusion3DToRGBWithOutDistortion(Camera_Object_C* camera_obj, PhotoInfo* rgb_data, PhotoInfo* raw_data, PhotoInfo* xyz);
//图像膨胀
DLLEXPORT_API DLL_VOID PixelSwell(Camera_Object_C* camera_obj, int *roi_output, PhotoInfo *target_data);
//图像腐蚀
DLLEXPORT_API DLL_VOID PixelCorrosion(Camera_Object_C* camera_obj, int *roi_output, PhotoInfo *target_data);
//ROI映射区域坐标
DLLEXPORT_API DLL_VOID ROIMappingCoordinate(Camera_Object_C* camera_obj, int *roi_output, PhotoInfo *target_data, RoiPoint *point_output);
//ROI检索映射
DLLEXPORT_API DLL_VOID ROIPixelMapping(Camera_Object_C* camera_obj, PhotoInfo *point_data, PhotoInfo *source_data, PhotoInfo *target_data, RoiPoint *roi_input, int *ROI_output);
//保存点云 with image to pcd
DLLEXPORT_API DLL_INT SavePointCloudWithImageToTxt(Camera_Object_C* camera_obj, PhotoInfo *raw_data, float * rgb_cloud, char *path_name);
//保存与图片融合后的点云Ply保留无效点
DLLEXPORT_API DLL_INT SavePointCloudWithImageToPly(Camera_Object_C* camera_obj, PhotoInfo *raw_data, float * image_cloud, char *path_name);
//保存与图片融合后的点云Pcd
DLLEXPORT_API DLL_INT SavePointCloudWithImageToPcd(Camera_Object_C* camera_obj, PhotoInfo *raw_data, float * image_cloud, char *path_name);
/*C++*/
/*/////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
*******************************************获取版本号******************************************
///////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////*/
//相机固件的版本号
DLLEXPORT_API DLL_CHAR_POINT CameraVerions(Camera_Object_C* camera_obj);
//SDK的版本号
DLLEXPORT_API DLL_CHAR_POINT SDKVersion(Camera_Object_C* camera_obj);
DLLEXPORT_API DLL_CHAR_POINT sdkversion();
//相机的序列号
DLLEXPORT_API DLL_CHAR_POINT CameraSeriesNumber(Camera_Object_C* camera_obj);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,93 @@
#pragma once
#include "dkam_base_type.h"
#include <vector>
#include <string>
#ifdef _WIN32
#define DLLEXPORT_API extern __declspec(dllexport)
#define DLL_VOID void __stdcall
#define DLL_INT int __stdcall
#else
#define DLLEXPORT_API
#define DLL_VOID void
#define DLL_INT int
#endif
#ifdef __cplusplus
extern "C"
{
#endif
/*C#*/
DLLEXPORT_API DLL_VOID GetCameraXMLNodeNamesCSharp(Camera_Object_C* camera_obj, std::vector<std::string>* node_names);
//获取下位机日志目录
DLLEXPORT_API DLL_INT CameraLogListCSharp(Camera_Object_C* camera_obj, std::vector<std::string>* file_names, int* len);
//阻塞式抓取数据
DLLEXPORT_API DLL_INT CaptureCSharp(Camera_Object_C* camera_obj, unsigned short channel_index, PhotoInfoCSharp &raw_data_info, char *xyz, int pixel_size);
//try
DLLEXPORT_API DLL_INT TryCaptureCSharp(Camera_Object_C* camera_obj, unsigned short channel_index, PhotoInfoCSharp &raw_data_info, char *xyz, int pixel_size);
//超时抓取数据
DLLEXPORT_API DLL_INT TimeoutCaptureCSharp(Camera_Object_C* camera_obj, unsigned short channel_index, PhotoInfoCSharp &raw_data_info, char *xyz, int pixel_size, long long timeout);
//将char数据转为float类型
DLLEXPORT_API DLL_VOID Convert3DPointFromCharToFloatCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp &raw_data_info, char *xyz, int pixel_size, float* output,int outputsize);
//将Rawdata数据转换成RGB888的图像数据
DLLEXPORT_API DLL_VOID RawdataToRgb888CSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp &raw_data_info, char *xyz, int pixel_size);
//获取点云的X平面数据
DLLEXPORT_API DLL_INT GetCloudPlaneXCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp &raw_data_info, char *xyz, int pixel_size, short *imagedata, int datasize);
//获取点云的Y平面数据
DLLEXPORT_API DLL_INT GetCloudPlaneYCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp &raw_data_info, char *xyz, int pixel_size, short *imagedata, int datasize);
//获取点云的Z平面数据
DLLEXPORT_API DLL_INT GetCloudPlaneZCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp &raw_data_info, char *xyz, int pixel_size, short *imagedata, int datasize);
//保存点云某个平面数据
DLLEXPORT_API DLL_INT SaveCloudPlaneCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp &raw_data_info, char *xyz, int pixel_size, short *imagedata, int datasize, char* path_name);
//保存点云 to pcd 格式
DLLEXPORT_API DLL_INT SavePointCloudToPcdCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp &raw_data_info, char *xyz, int pixel_size, char* path_name);
//保存点云 to txt 格式
DLLEXPORT_API DLL_INT SavePointCloudToTxtCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp &raw_data_info, char *xyz, int pixel_size, char* path_name);
//保存点云 to ply 格式
DLLEXPORT_API DLL_INT SavePointCloudToPlyCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp &raw_data_info, char *xyz, int pixel_size, char* path_name);
//点云滤波(基于空间密度的点云去噪)
DLLEXPORT_API DLL_VOID FilterPointCloudCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp &raw_data_info, char *xyz, int pixel_size, double level);
//空间滤波(基于空间网格的点云去噪) 20220225: 弃用, FilterPointCloud为该API的升级版
DLLEXPORT_API DLL_INT SpatialFilterPointcloudCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp &raw_data_info, char *xyz, int pixel_size, int Area_PointCloudCount);
//保存bmp图
DLLEXPORT_API DLL_INT SaveToBMPCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp &raw_data_info, char *xyz, int pixel_size, char *path_name);
//图片裁剪
DLLEXPORT_API DLL_INT ImageRoiCropCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp& source_data, char* source_pixel, int source_size, RoiPoint roi, PhotoInfoCSharp& target_data, char* target_pixel, int target_size);
//保存点云深度图 to png
DLLEXPORT_API DLL_INT SaveDepthToPngCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp &raw_data_info, char *xyz, int pixel_size, char* path_name);
//点云融合
DLLEXPORT_API DLL_INT FusionImageTo3DCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp &image_data_info, char *image_pixel, int image_pixel_size,
PhotoInfoCSharp &point_data_info, char *point_pixel, int point_pixel_size, float * image_cloud, int image_cloud_size);
//根据RGB重排点云
DLLEXPORT_API DLL_INT Fusion3DToRGBCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp &image_data_info, char *image_pixel, int image_pixel_size,
PhotoInfoCSharp &point_data_info, char *point_pixel, int point_pixel_size,
PhotoInfoCSharp &xyz_data_info, char *xyz, int xyz_size);
//根据RGB重排点云
DLLEXPORT_API DLL_INT Fusion3DToRGBWithOutDistortionCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp& image_data_info, char* image_pixel, int image_pixel_size,
PhotoInfoCSharp& point_data_info, char* point_pixel, int point_pixel_size,
PhotoInfoCSharp& xyz_data_info, char* xyz, int xyz_size);
//图像膨胀
DLLEXPORT_API DLL_VOID PixelSwellCSharp(Camera_Object_C* camera_obj, int roi_output_size, int *roi_output, PhotoInfoCSharp &target_data, int target_pixel_size);
//图像腐蚀
DLLEXPORT_API DLL_VOID PixelCorrosionCSharp(Camera_Object_C* camera_obj, int roi_output_size, int *roi_output, PhotoInfoCSharp &target_data, int target_pixel_size);
//ROI映射区域坐标
DLLEXPORT_API DLL_VOID ROIMappingCoordinateCSharp(Camera_Object_C* camera_obj,int roi_output_size, int *roi_output, PhotoInfoCSharp &target_data, int target_pixel_size, RoiPoint &point_output);
//ROI检索映射
DLLEXPORT_API DLL_VOID ROIPixelMappingCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp &point_data, char *point_pixel, int point_pixel_size, PhotoInfoCSharp &source_data,
char *source_data_pixel, int source_pixel_size, PhotoInfoCSharp &target_data, char *target_data_pixel, int target_pixel_size,
RoiPoint &roi_input, int ROI_output_size, int *ROI_output);
//保存点云 with image to pcd
DLLEXPORT_API DLL_INT SavePointCloudWithImageToTxtCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp &point_data_info, char *point_pixel, int point_pixel_size,
float * rgb_cloud,int rgb_cloud_size, char *path_name);
//保存与图片融合后的点云Ply
DLLEXPORT_API DLL_INT SavePointCloudWithImageToPlyCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp &point_data_info, char *point_pixel, int point_pixel_size,
float * rgb_cloud, int rgb_cloud_size, char *path_name);
//保存与图片融合后的点云Pcd
DLLEXPORT_API DLL_INT SavePointCloudWithImageToPcdCSharp(Camera_Object_C* camera_obj, PhotoInfoCSharp &point_data_info, char *point_pixel, int point_pixel_size,
float * rgb_cloud, int rgb_cloud_size, char *path_name);
/*C#*/
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,68 @@
#ifndef POINTCLOUDGLWIDGET_H
#define POINTCLOUDGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>
#include <QMatrix4x4>
#include <QVector3D>
#include <QMouseEvent>
#include <QWheelEvent>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <vector>
class PointCloudGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
explicit PointCloudGLWidget(QWidget *parent = nullptr);
~PointCloudGLWidget();
void updatePointCloud(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud);
protected:
void initializeGL() override;
void resizeGL(int w, int h) override;
void paintGL() override;
// 鼠标交互
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
private:
void setupShaders();
void updateBuffers();
private:
// OpenGL资源
QOpenGLShaderProgram *m_program;
QOpenGLBuffer *m_vertexBuffer;
QOpenGLVertexArrayObject *m_vao;
// 点云数据
std::vector<float> m_vertices;
int m_pointCount;
// 相机参数
QMatrix4x4 m_projection;
QMatrix4x4 m_view;
QMatrix4x4 m_model;
float m_orthoSize; // 正交投影视野大小(控制缩放)
float m_rotationX; // X轴旋转角度
float m_rotationY; // Y轴旋转角度
QVector3D m_translation; // 平移
// 鼠标交互状态
QPoint m_lastMousePos;
bool m_leftButtonPressed;
bool m_rightButtonPressed;
};
#endif // POINTCLOUDGLWIDGET_H

View File

@@ -0,0 +1,27 @@
#ifndef POINTCLOUDWIDGET_H
#define POINTCLOUDWIDGET_H
#include <QWidget>
#include <QLabel>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
class PointCloudGLWidget;
class PointCloudWidget : public QWidget
{
Q_OBJECT
public:
explicit PointCloudWidget(QWidget *parent = nullptr);
~PointCloudWidget();
// 更新点云显示
void updatePointCloud(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud);
private:
QLabel *m_statusLabel;
PointCloudGLWidget *m_glWidget;
};
#endif // POINTCLOUDWIDGET_H

173
installer/BinFiles.wxs Normal file
View File

@@ -0,0 +1,173 @@
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="INSTALLFOLDER">
<Component Id="cmpC3C56006B21D7A9B1C3D970559A58022" Guid="{CC08CF56-C517-49D0-ABCF-9F873190FB5D}">
<File Id="filA38101C31D3FE72B90DABBD659FA3749" KeyPath="yes" Source="$(var.BinDir)\CLAllSerial_MD_VC120_v3_1.dll" />
</Component>
<Component Id="cmp94AE231A385A67D66B846FBD16D60636" Guid="{AF528182-E790-4F99-865F-96A8BAC63C64}">
<File Id="fil24783C0C3176434BE2433E90A26639B5" KeyPath="yes" Source="$(var.BinDir)\CLProtocol_MD_VC120_v3_1.dll" />
</Component>
<Component Id="cmp5BBD8A0A8D3848D3C63356E9ADDC3DAB" Guid="{49379FC3-4074-47AC-912F-9AF48F097D4A}">
<File Id="fil96040E48A1CEC0DD77E92374F304C405" KeyPath="yes" Source="$(var.BinDir)\clsercom.dll" />
</Component>
<Component Id="cmp41DC9741013CA4AD62A4A5FF421F08BC" Guid="{2D62ADE6-D030-4233-9A5B-140D90B3D415}">
<File Id="fil84B847A9F8DABE6B95AF875695C9E6D1" KeyPath="yes" Source="$(var.BinDir)\d3dcompiler_47.dll" />
</Component>
<Component Id="cmp5CADE23B444DBEA082D543627C541D33" Guid="{DA896890-8E2B-4061-86F6-321716A99339}">
<File Id="fil8F54BE958899A2BFF99AC72F9A899AF9" KeyPath="yes" Source="$(var.BinDir)\DkamSDK.dll" />
</Component>
<Component Id="cmpFC9F1FCE7DF02EBFFBBB05A2D04DF5C9" Guid="{621784F4-1F1E-445B-B77F-4E22E28EA9AB}">
<File Id="fil5B0CA26742B9001E819A6C24AB1DFE4E" KeyPath="yes" Source="$(var.BinDir)\DkamSDK_CSharp.dll" />
</Component>
<Component Id="cmp0A946DD3BC4835FBF8A86E6F14AFF450" Guid="{438AC89E-097C-4091-BA8F-C3292A615684}">
<File Id="filC769392EA3BDC8D6035070F861669864" KeyPath="yes" Source="$(var.BinDir)\DkamSDK_CSharp_Namespace.dll" />
</Component>
<Component Id="cmp304DB2D25065C3D5B1F99012C3F097B3" Guid="{FD24F279-1A97-4F5F-893B-A8DBC387F094}">
<File Id="fil47D910A70309D29213F27AFB1B0B9421" KeyPath="yes" Source="$(var.BinDir)\FirmwareUpdate_MD_VC120_v3_1.dll" />
</Component>
<Component Id="cmpA1FAB8CA5E7B238E225525086D59754B" Guid="{D61BBE30-31BB-44BA-9AAC-62F888F0FA0C}">
<File Id="filEBA00F3DBC3F68DCF9B825874F6738CE" KeyPath="yes" Source="$(var.BinDir)\GCBase_MD_VC120_v3_1.dll" />
</Component>
<Component Id="cmp0D1074CD4A4DC67B6653DCF74385DF0E" Guid="{94C5E933-E14B-4AF4-B172-78663F76989A}">
<File Id="fil30A6FD8B9DD3FC13C5C579A4DBBDBC92" KeyPath="yes" Source="$(var.BinDir)\GenApi_MD_VC120_v3_1.dll" />
</Component>
<Component Id="cmp9A9D29623257FA41663F84EAA2612688" Guid="{CEA04915-E4F8-4531-AE8D-41CAFE22C6D8}">
<File Id="filB94200E89E3B121E295845B256282799" KeyPath="yes" Source="$(var.BinDir)\GenCP_MD_VC120_v3_1.dll" />
</Component>
<Component Id="cmp45C131D18AAA1AE3EF9B48BCFCC28F6D" Guid="{F96A1BAD-8C80-4802-B549-481D5C246567}">
<File Id="fil8354EA4C33A6C488882967BAC14C59EA" KeyPath="yes" Source="$(var.BinDir)\log4cpp_MD_VC120_v3_1.dll" />
</Component>
<Component Id="cmpA829C4573EB498556AC47A36290EBF20" Guid="{0016F5BD-D8CE-40D1-A8BC-0D01F9D6AD49}">
<File Id="fil4D74A6B62C6930C4CE69FA293FA47754" KeyPath="yes" Source="$(var.BinDir)\Log_MD_VC120_v3_1.dll" />
</Component>
<Component Id="cmp61CA0314C827E13940ED8588B8A8BA13" Guid="{574DB953-993C-46E7-94ED-CC770194A6B9}">
<File Id="filBB5A2C2AEC26359B2C6F23420800AE9D" KeyPath="yes" Source="$(var.BinDir)\MathParser_MD_VC120_v3_1.dll" />
</Component>
<Component Id="cmpEDDED65BD9A61BB47433821AA149DC39" Guid="{9AAE661B-0960-4D6A-B64E-B3E838DADD4E}">
<File Id="fil9EAA71401D5302036DABA47B8EF6B434" KeyPath="yes" Source="$(var.BinDir)\NodeMapData_MD_VC120_v3_1.dll" />
</Component>
<Component Id="cmpDAC39AC2BB8715FBE58F904CC2E9F698" Guid="{CF6BC55C-FF3E-421D-A7BF-98435449455E}">
<File Id="fil3940E64D4079916BAF62ED6C54607F0E" KeyPath="yes" Source="$(var.BinDir)\opencv_world4130.dll" />
</Component>
<Component Id="cmp316B7EADCCFA5F980015637E294FFC4B" Guid="{F99C0165-0728-4384-A782-7659F766FA25}">
<File Id="fil07CE2ABE9F28D446757A019F07E1898F" KeyPath="yes" Source="$(var.BinDir)\opengl32sw.dll" />
</Component>
<Component Id="cmp0C61783999259F0182CE807EF9128FF4" Guid="{184991A1-36A0-4830-B9DD-A58E07FBC1A1}">
<File Id="fil449440E65BE6F95AC788ED910D6D3FB4" KeyPath="yes" Source="$(var.BinDir)\pcl_common.dll" />
</Component>
<Component Id="cmpB9B2B93E50693FCD82547A4EEDF59C47" Guid="{60CA5622-9531-43B7-BD6B-C82625D7B805}">
<File Id="filC346A45D1A8B2E29F1558CA7C1156D48" KeyPath="yes" Source="$(var.BinDir)\pcl_filters.dll" />
</Component>
<Component Id="cmpE0368B606CE94148F4CA5F032A18B8AA" Guid="{F8F62E47-F16E-4FC1-BA4C-2A786A73A80B}">
<File Id="filE14680FDD24600E0C7BC68717F06DB13" KeyPath="yes" Source="$(var.BinDir)\pcl_io.dll" />
</Component>
<Component Id="cmpA478CC827F5FA0CCF27AAD818849CBD7" Guid="{0C9073A4-A6FA-4BAB-9733-8A704E8ECF01}">
<File Id="fil3B0E0708353E72AE7456B38B7F5BC970" KeyPath="yes" Source="$(var.BinDir)\pcl_io_ply.dll" />
</Component>
<Component Id="cmp5878BDE96D855AFAF94266F3B9677E88" Guid="{279F0111-C41A-4501-BA4B-A750DD69B0F4}">
<File Id="fil7470D204801B87A04BF47FD21B3750BF" KeyPath="yes" Source="$(var.BinDir)\pcl_kdtree.dll" />
</Component>
<Component Id="cmp0759BB73FE6272908152EAACE25509E2" Guid="{62BD1558-338F-46F7-9B67-4C7F18FD443C}">
<File Id="filCE419FFD12F6A6C0A0ED57B48BC8D9D8" KeyPath="yes" Source="$(var.BinDir)\pcl_octree.dll" />
</Component>
<Component Id="cmp6B64D18EAB4E2AA54759D9616AB99FC6" Guid="{5972BA2F-7BED-4D8D-8C73-8E6131DAD884}">
<File Id="fil82FA0626A335B90C9078B5F4C52872B3" KeyPath="yes" Source="$(var.BinDir)\pcl_outofcore.dll" />
</Component>
<Component Id="cmpE6ECB1DBD11FF2337C376EACE8AC5BC6" Guid="{9CC56B62-4A75-4583-B94B-09DE77D057D3}">
<File Id="fil750E9427DCF40DCEB7349243CE6BCCFE" KeyPath="yes" Source="$(var.BinDir)\pcl_sample_consensus.dll" />
</Component>
<Component Id="cmpE6D8206385B0C897A99FF044F618C63E" Guid="{5F5D35F9-F55E-4D38-8AD2-991EA8C65DB5}">
<File Id="fil360C6C1D4A2DB31173843F5596F2C54F" KeyPath="yes" Source="$(var.BinDir)\pcl_search.dll" />
</Component>
<Component Id="cmpD5BFC0D57560599B60E42E5501314FEB" Guid="{DB78A410-C481-4E70-B94C-02FCB980E63E}">
<File Id="fil263B8621AC2C78E67B986C7BC1E576CD" KeyPath="yes" Source="$(var.BinDir)\pcl_segmentation.dll" />
</Component>
<Component Id="cmp82FF9A9C17F8D75F054D5CDDB8768C5C" Guid="{38967235-8639-4C79-9E7C-724504137A1E}">
<File Id="filD3270351B41213BA8E4318BBB4E5F2E9" KeyPath="yes" Source="$(var.BinDir)\pcl_surface.dll" />
</Component>
<Component Id="cmp396E4B41C7A33BE56F778473828172F7" Guid="{B219C434-6BE7-45F0-8831-22D44291DF41}">
<File Id="filCE07DA5E17AE0C265C2A1738147B6059" KeyPath="yes" Source="$(var.BinDir)\Qt6Concurrent.dll" />
</Component>
<Component Id="cmp02DF6A9A3BF862D35391F764FE67153F" Guid="{0244645B-D7B2-42C9-8D61-09EBDE868F3D}">
<File Id="fil494DCCE77F31DE27CD7C03A13FD938B0" KeyPath="yes" Source="$(var.BinDir)\Qt6Core.dll" />
</Component>
<Component Id="cmp701A262B1CE5139B2E57DF21A5AA3B94" Guid="{53E7B97E-8BD1-4EFB-B232-D80A305CE61E}">
<File Id="filD9F54CB97035337EAF318E4E55DD2F5F" KeyPath="yes" Source="$(var.BinDir)\Qt6Gui.dll" />
</Component>
<Component Id="cmp1EAE9F3A379CF532AF1EBC883CDCD1D2" Guid="{5267D680-F1AD-4648-84D2-F68A45111EC0}">
<File Id="fil15AC8A9B3B6A6F1FE6BBDDAE2042D5D0" KeyPath="yes" Source="$(var.BinDir)\Qt6Network.dll" />
</Component>
<Component Id="cmp6C95449DAD236B0C9989FA43DD56CA85" Guid="{9BFC4239-AAD9-4D40-9058-FB26E9E2F8A9}">
<File Id="fil93AD72205F2074362FE1D69894AF2E4E" KeyPath="yes" Source="$(var.BinDir)\Qt6OpenGL.dll" />
</Component>
<Component Id="cmp11109A2A7F5FEC28295B469293762519" Guid="{B83D1B28-0FB2-4BDC-BE57-0943E296AAC0}">
<File Id="fil9BB89E2877718991879476005F911AF2" KeyPath="yes" Source="$(var.BinDir)\Qt6OpenGLWidgets.dll" />
</Component>
<Component Id="cmp5B0A9912E5069950C30FC5FF8EAA16D4" Guid="{70A602C1-1808-4DC5-9822-ECF2A321A750}">
<File Id="fil9F3707482AFAB8A22C7B5C23D8C7CE69" KeyPath="yes" Source="$(var.BinDir)\Qt6Widgets.dll" />
</Component>
<Component Id="cmp1B37E98B173B46E1E5315143EF8D7728" Guid="{89E23A4E-DACC-4A16-AC16-98C696218F10}">
<File Id="fil27A091E66DC753FB25B8F53726B5D53D" KeyPath="yes" Source="$(var.BinDir)\Qt6Xml.dll" />
</Component>
<Component Id="cmp2F414B6DDC2ADD0824C2DDE58E9102FA" Guid="{B50C2D2D-629F-4688-8D16-271CB92626B7}">
<File Id="filB3ED52B8FA49C0BD845D3E52B9F7F279" KeyPath="yes" Source="$(var.BinDir)\uppercontrol.log" />
</Component>
<Component Id="cmp3FB3F428740E959925E49E3155B50A4B" Guid="{D5A06D7C-FD95-456E-861D-EEC0ED327631}">
<File Id="filF458411F7CE6D0DFCA440791D37E30EA" KeyPath="yes" Source="$(var.BinDir)\UpperControlGUI.exe" />
</Component>
<Component Id="cmpACC01C1F599CA1C44DC57D4D6199427D" Guid="{DA47AC68-F657-4FDD-90B9-5B3E82088F21}">
<File Id="filBC2C4C6DBCF2DD007E8E416D1CFFBDC7" KeyPath="yes" Source="$(var.BinDir)\XmlParser_MD_VC120_v3_1.dll" />
</Component>
<Directory Id="dir908DFC228501FBE723FC009E659CB04F" Name="platforms">
<Component Id="cmp3E80143A725EE7380FA78AA9E8BD2283" Guid="{04575A8F-DF0B-4345-BBCE-CB9B5BCD7AF6}">
<File Id="fil702C83C4DEF82CE8980ED1100FE61CE2" KeyPath="yes" Source="$(var.BinDir)\platforms\qwindows.dll" />
</Component>
</Directory>
</DirectoryRef>
</Fragment>
<Fragment>
<ComponentGroup Id="BinFiles">
<ComponentRef Id="cmpC3C56006B21D7A9B1C3D970559A58022" />
<ComponentRef Id="cmp94AE231A385A67D66B846FBD16D60636" />
<ComponentRef Id="cmp5BBD8A0A8D3848D3C63356E9ADDC3DAB" />
<ComponentRef Id="cmp41DC9741013CA4AD62A4A5FF421F08BC" />
<ComponentRef Id="cmp5CADE23B444DBEA082D543627C541D33" />
<ComponentRef Id="cmpFC9F1FCE7DF02EBFFBBB05A2D04DF5C9" />
<ComponentRef Id="cmp0A946DD3BC4835FBF8A86E6F14AFF450" />
<ComponentRef Id="cmp304DB2D25065C3D5B1F99012C3F097B3" />
<ComponentRef Id="cmpA1FAB8CA5E7B238E225525086D59754B" />
<ComponentRef Id="cmp0D1074CD4A4DC67B6653DCF74385DF0E" />
<ComponentRef Id="cmp9A9D29623257FA41663F84EAA2612688" />
<ComponentRef Id="cmp45C131D18AAA1AE3EF9B48BCFCC28F6D" />
<ComponentRef Id="cmpA829C4573EB498556AC47A36290EBF20" />
<ComponentRef Id="cmp61CA0314C827E13940ED8588B8A8BA13" />
<ComponentRef Id="cmpEDDED65BD9A61BB47433821AA149DC39" />
<ComponentRef Id="cmpDAC39AC2BB8715FBE58F904CC2E9F698" />
<ComponentRef Id="cmp316B7EADCCFA5F980015637E294FFC4B" />
<ComponentRef Id="cmp0C61783999259F0182CE807EF9128FF4" />
<ComponentRef Id="cmpB9B2B93E50693FCD82547A4EEDF59C47" />
<ComponentRef Id="cmpE0368B606CE94148F4CA5F032A18B8AA" />
<ComponentRef Id="cmpA478CC827F5FA0CCF27AAD818849CBD7" />
<ComponentRef Id="cmp5878BDE96D855AFAF94266F3B9677E88" />
<ComponentRef Id="cmp0759BB73FE6272908152EAACE25509E2" />
<ComponentRef Id="cmp6B64D18EAB4E2AA54759D9616AB99FC6" />
<ComponentRef Id="cmpE6ECB1DBD11FF2337C376EACE8AC5BC6" />
<ComponentRef Id="cmpE6D8206385B0C897A99FF044F618C63E" />
<ComponentRef Id="cmpD5BFC0D57560599B60E42E5501314FEB" />
<ComponentRef Id="cmp82FF9A9C17F8D75F054D5CDDB8768C5C" />
<ComponentRef Id="cmp396E4B41C7A33BE56F778473828172F7" />
<ComponentRef Id="cmp02DF6A9A3BF862D35391F764FE67153F" />
<ComponentRef Id="cmp701A262B1CE5139B2E57DF21A5AA3B94" />
<ComponentRef Id="cmp1EAE9F3A379CF532AF1EBC883CDCD1D2" />
<ComponentRef Id="cmp6C95449DAD236B0C9989FA43DD56CA85" />
<ComponentRef Id="cmp11109A2A7F5FEC28295B469293762519" />
<ComponentRef Id="cmp5B0A9912E5069950C30FC5FF8EAA16D4" />
<ComponentRef Id="cmp1B37E98B173B46E1E5315143EF8D7728" />
<ComponentRef Id="cmp2F414B6DDC2ADD0824C2DDE58E9102FA" />
<ComponentRef Id="cmp3FB3F428740E959925E49E3155B50A4B" />
<ComponentRef Id="cmpACC01C1F599CA1C44DC57D4D6199427D" />
<ComponentRef Id="cmp3E80143A725EE7380FA78AA9E8BD2283" />
</ComponentGroup>
</Fragment>
</Wix>

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*"
Name="D330Viewer"
Language="1033"
Version="0.1.0.0"
Manufacturer="Your Company"
UpgradeCode="12345678-1234-1234-1234-123456789012">
<Package InstallerVersion="200"
Compressed="yes"
InstallScope="perMachine"
Description="D330M Depth Camera Control System"
Comments="Qt6-based depth camera control and visualization system" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes" />
<!-- 安装目录 -->
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="D330Viewer" />
</Directory>
<Directory Id="ProgramMenuFolder">
<Directory Id="ApplicationProgramsFolder" Name="D330Viewer"/>
</Directory>
</Directory>
<!-- 开始菜单快捷方式 -->
<DirectoryRef Id="ApplicationProgramsFolder">
<Component Id="ApplicationShortcut" Guid="12345678-1234-1234-1234-123456789013">
<Shortcut Id="ApplicationStartMenuShortcut"
Name="D330Viewer"
Description="D330M Depth Camera Control System"
Target="[INSTALLFOLDER]D330Viewer.exe"
WorkingDirectory="INSTALLFOLDER"/>
<Shortcut Id="UninstallProduct"
Name="卸载 D330Viewer"
Description="卸载 D330M 深度相机控制系统"
Target="[SystemFolder]msiexec.exe"
Arguments="/x [ProductCode]"/>
<RemoveFolder Id="CleanUpShortCut" Directory="ApplicationProgramsFolder" On="uninstall"/>
<RegistryValue Root="HKCU"
Key="Software\D330Viewer"
Name="installed"
Type="integer"
Value="1"
KeyPath="yes"/>
</Component>
</DirectoryRef>
<!-- 用户配置注册表项(卸载时删除) -->
<DirectoryRef Id="TARGETDIR">
<Component Id="UserSettings" Guid="12345678-1234-1234-1234-123456789015">
<RegistryKey Root="HKCU" Key="Software\D330Viewer\D330Viewer" Action="createAndRemoveOnUninstall">
<RegistryValue Type="string" Name="placeholder" Value="1" KeyPath="yes"/>
</RegistryKey>
</Component>
</DirectoryRef>
<Feature Id="ProductFeature" Title="D330Viewer" Level="1">
<ComponentGroupRef Id="ProductComponents" />
<ComponentGroupRef Id="BinFiles" />
<ComponentRef Id="ApplicationShortcut" />
<ComponentRef Id="UserSettings" />
</Feature>
</Product>
<!-- 主程序文件 -->
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="MainExecutable" Guid="12345678-1234-1234-1234-123456789014">
<File Id="D330Viewer.exe"
Source="$(var.BinDir)\D330Viewer.exe"
KeyPath="yes" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>

BIN
resources/app_icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

1
resources/app_icon.rc Normal file
View File

@@ -0,0 +1 @@
IDI_ICON1 ICON "app_icon.ico"

View File

@@ -0,0 +1,16 @@
#!/usr/bin/env python3
"""将PNG图标转换为ICO格式"""
from PIL import Image
import os
def png_to_ico(png_path, ico_path):
"""将PNG转换为ICO"""
img = Image.open(png_path)
# ICO文件支持多种尺寸Windows会根据需要选择合适的尺寸
img.save(ico_path, format='ICO', sizes=[(16, 16), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)])
print(f"Created: {ico_path}")
if __name__ == "__main__":
png_to_ico("icons/app_icon.png", "app_icon.ico")
print("\nICO file generated successfully!")

View File

@@ -0,0 +1,85 @@
#!/usr/bin/env python3
"""生成D330Viewer所需的图标"""
from PIL import Image, ImageDraw
import os
def create_icon(size, bg_color, fg_color, shape, filename):
"""创建一个简单的图标"""
img = Image.new('RGBA', (size, size), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
margin = size // 8
if shape == 'play':
# 播放三角形
points = [(margin, margin), (margin, size - margin), (size - margin, size // 2)]
draw.polygon(points, fill=fg_color)
elif shape == 'stop':
# 停止方块
draw.rectangle([margin, margin, size - margin, size - margin], fill=fg_color)
elif shape == 'camera':
# 相机图标
draw.ellipse([margin, margin, size - margin, size - margin], outline=fg_color, width=3)
draw.ellipse([size//3, size//3, 2*size//3, 2*size//3], fill=fg_color)
elif shape == 'save':
# 保存图标
draw.rectangle([margin, margin, size - margin, size - margin], outline=fg_color, width=3)
draw.rectangle([margin*2, margin, size - margin*2, margin*3], fill=fg_color)
elif shape == 'folder':
# 文件夹图标
draw.rectangle([margin, size//3, size - margin, size - margin], outline=fg_color, width=3)
draw.rectangle([margin, size//3, size//2, size//3 + margin], fill=fg_color)
elif shape == 'refresh':
# 刷新图标
draw.arc([margin, margin, size - margin, size - margin], 45, 315, fill=fg_color, width=3)
draw.polygon([(size - margin, margin*2), (size - margin, margin*3), (size - margin*2, margin*2.5)], fill=fg_color)
elif shape == 'connect':
# 连接图标
draw.ellipse([margin, margin, size//2, size//2], fill=fg_color)
draw.ellipse([size//2, size//2, size - margin, size - margin], fill=fg_color)
draw.line([size//4, size//4, 3*size//4, 3*size//4], fill=fg_color, width=3)
elif shape == 'clear':
# 清除图标
draw.line([margin, margin, size - margin, size - margin], fill=fg_color, width=3)
draw.line([size - margin, margin, margin, size - margin], fill=fg_color, width=3)
elif shape == 'app':
# 应用图标
draw.rectangle([margin, margin*2, size - margin, size - margin], fill=bg_color, outline=fg_color, width=3)
draw.ellipse([size//4, size//3, 3*size//4, 2*size//3], fill=fg_color)
draw.rectangle([size//3, margin, 2*size//3, margin*2], fill=fg_color)
img.save(filename)
print(f"Created: {filename}")
if __name__ == "__main__":
icons_dir = "icons"
os.makedirs(icons_dir, exist_ok=True)
# 颜色方案 - 使用白色/浅色图标,确保在任何背景上都可见
white_color = (255, 255, 255, 255) # 白色
light_gray = (220, 220, 220, 255) # 浅灰色
transparent = (0, 0, 0, 0) # 透明背景
# 为应用图标使用深色背景
app_bg_color = (33, 150, 243, 255) # 蓝色背景
# 生成图标 - 所有按钮图标使用白色,透明背景
create_icon(64, transparent, white_color, 'play', f'{icons_dir}/start.png')
create_icon(64, transparent, white_color, 'stop', f'{icons_dir}/stop.png')
create_icon(64, transparent, white_color, 'camera', f'{icons_dir}/camera.png')
create_icon(64, transparent, white_color, 'save', f'{icons_dir}/save.png')
create_icon(64, transparent, white_color, 'folder', f'{icons_dir}/folder.png')
create_icon(64, transparent, white_color, 'refresh', f'{icons_dir}/refresh.png')
create_icon(64, transparent, white_color, 'connect', f'{icons_dir}/connect.png')
create_icon(64, transparent, white_color, 'clear', f'{icons_dir}/clear.png')
create_icon(128, app_bg_color, white_color, 'app', f'{icons_dir}/app_icon.png')
print("\nAll icons generated successfully!")

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 B

BIN
resources/icons/camera.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

BIN
resources/icons/clear.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

BIN
resources/icons/connect.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

BIN
resources/icons/folder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

BIN
resources/icons/refresh.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 B

BIN
resources/icons/save.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

BIN
resources/icons/start.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 B

BIN
resources/icons/stop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

13
resources/resources.qrc Normal file
View File

@@ -0,0 +1,13 @@
<RCC>
<qresource prefix="/icons">
<file>icons/app_icon.png</file>
<file>icons/start.png</file>
<file>icons/stop.png</file>
<file>icons/camera.png</file>
<file>icons/save.png</file>
<file>icons/folder.png</file>
<file>icons/refresh.png</file>
<file>icons/connect.png</file>
<file>icons/clear.png</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,106 @@
#include "config/ConfigManager.h"
ConfigManager::ConfigManager()
: m_settings(std::make_unique<QSettings>("D330Viewer", "D330Viewer"))
{
// 构造函数初始化QSettings
}
ConfigManager::~ConfigManager()
{
// 析构函数QSettings会自动保存
}
// ========== 网络配置 ==========
QString ConfigManager::getIpAddress() const
{
return m_settings->value("Network/IpAddress", "192.168.0.90").toString();
}
void ConfigManager::setIpAddress(const QString &ip)
{
m_settings->setValue("Network/IpAddress", ip);
}
int ConfigManager::getControlPort() const
{
return m_settings->value("Network/ControlPort", 6790).toInt();
}
void ConfigManager::setControlPort(int port)
{
m_settings->setValue("Network/ControlPort", port);
}
int ConfigManager::getDataPort() const
{
return m_settings->value("Network/DataPort", 3957).toInt();
}
void ConfigManager::setDataPort(int port)
{
m_settings->setValue("Network/DataPort", port);
}
// ========== 相机配置 ==========
int ConfigManager::getExposureTime() const
{
return m_settings->value("Camera/ExposureTime", 10000).toInt();
}
void ConfigManager::setExposureTime(int exposure)
{
m_settings->setValue("Camera/ExposureTime", exposure);
}
// ========== 拍照配置 ==========
QString ConfigManager::getSavePath() const
{
return m_settings->value("Capture/SavePath", "").toString();
}
void ConfigManager::setSavePath(const QString &path)
{
m_settings->setValue("Capture/SavePath", path);
}
QString ConfigManager::getDepthFormat() const
{
return m_settings->value("Capture/DepthFormat", "both").toString();
}
void ConfigManager::setDepthFormat(const QString &format)
{
m_settings->setValue("Capture/DepthFormat", format);
}
QString ConfigManager::getPointCloudFormat() const
{
return m_settings->value("Capture/PointCloudFormat", "both").toString();
}
void ConfigManager::setPointCloudFormat(const QString &format)
{
m_settings->setValue("Capture/PointCloudFormat", format);
}
// ========== 窗口配置 ==========
QByteArray ConfigManager::getWindowGeometry() const
{
return m_settings->value("Window/Geometry").toByteArray();
}
void ConfigManager::setWindowGeometry(const QByteArray &geometry)
{
m_settings->setValue("Window/Geometry", geometry);
}
QByteArray ConfigManager::getWindowState() const
{
return m_settings->value("Window/State").toByteArray();
}
void ConfigManager::setWindowState(const QByteArray &state)
{
m_settings->setValue("Window/State", state);
}

View File

@@ -0,0 +1,49 @@
#ifndef CONFIGMANAGER_H
#define CONFIGMANAGER_H
#include <QSettings>
#include <QString>
#include <memory>
class ConfigManager
{
public:
ConfigManager();
~ConfigManager();
// 网络配置
QString getIpAddress() const;
void setIpAddress(const QString &ip);
int getControlPort() const;
void setControlPort(int port);
int getDataPort() const;
void setDataPort(int port);
// 相机配置
int getExposureTime() const;
void setExposureTime(int exposure);
// 拍照配置
QString getSavePath() const;
void setSavePath(const QString &path);
QString getDepthFormat() const;
void setDepthFormat(const QString &format);
QString getPointCloudFormat() const;
void setPointCloudFormat(const QString &format);
// 窗口配置
QByteArray getWindowGeometry() const;
void setWindowGeometry(const QByteArray &geometry);
QByteArray getWindowState() const;
void setWindowState(const QByteArray &state);
private:
std::unique_ptr<QSettings> m_settings;
};
#endif // CONFIGMANAGER_H

183
src/core/DeviceScanner.cpp Normal file
View File

@@ -0,0 +1,183 @@
#include "DeviceScanner.h"
#include <QNetworkDatagram>
#include <QNetworkInterface>
#include <QDebug>
DeviceScanner::DeviceScanner(QObject *parent)
: QObject(parent)
, m_socket(new QUdpSocket(this))
, m_timeoutTimer(new QTimer(this))
, m_scanTimer(new QTimer(this))
, m_currentHost(HOST_START)
, m_totalHosts(HOST_END - HOST_START + 1)
, m_isScanning(false)
{
connect(m_socket, &QUdpSocket::readyRead, this, &DeviceScanner::onReadyRead);
connect(m_timeoutTimer, &QTimer::timeout, this, &DeviceScanner::onTimeout);
connect(m_scanTimer, &QTimer::timeout, this, &DeviceScanner::scanNextHost);
m_timeoutTimer->setSingleShot(true);
m_scanTimer->setSingleShot(false);
m_scanTimer->setInterval(SCAN_TIMEOUT);
}
DeviceScanner::~DeviceScanner()
{
stopScan();
}
void DeviceScanner::startScan(const QString &subnet)
{
if (m_isScanning) {
qDebug() << "Scan already in progress";
return;
}
m_subnet = subnet.isEmpty() ? getLocalSubnet() : subnet;
m_currentHost = HOST_START;
m_foundDevices.clear();
m_isScanning = true;
if (!m_socket->bind(QHostAddress::Any, 0)) {
emit scanError("Failed to bind socket");
m_isScanning = false;
return;
}
qDebug() << "Starting fast device scan on subnet:" << m_subnet;
// Send DISCOVER to all hosts at once (batch mode)
for (int host = HOST_START; host <= HOST_END; host++) {
QString ip = m_subnet + "." + QString::number(host);
sendDiscoveryPacket(ip);
}
// Wait 3 seconds for all responses
m_timeoutTimer->start(3000);
qDebug() << "Sent discovery packets to all hosts, waiting for responses...";
}
void DeviceScanner::stopScan()
{
if (!m_isScanning) {
return;
}
m_scanTimer->stop();
m_timeoutTimer->stop();
m_socket->close();
m_isScanning = false;
qDebug() << "Scan stopped. Found" << m_foundDevices.size() << "devices";
emit scanFinished(m_foundDevices.size());
}
void DeviceScanner::onReadyRead()
{
while (m_socket->hasPendingDatagrams()) {
QNetworkDatagram datagram = m_socket->receiveDatagram();
QHostAddress senderAddr = datagram.senderAddress();
// Convert IPv6-mapped IPv4 address to IPv4
if (senderAddr.protocol() == QAbstractSocket::IPv6Protocol) {
QHostAddress ipv4Addr(senderAddr.toIPv4Address());
if (!ipv4Addr.isNull()) {
senderAddr = ipv4Addr;
}
}
QString senderIp = senderAddr.toString();
QString response = QString::fromUtf8(datagram.data());
qDebug() << "Received response from" << senderIp << ":" << response;
if (response.contains("D330M_CAMERA")) {
DeviceInfo device;
device.ipAddress = senderIp;
device.deviceName = "D330M Camera";
device.port = SCAN_PORT;
device.responseTime = 0;
m_foundDevices.append(device);
emit deviceFound(device);
}
}
}
void DeviceScanner::onTimeout()
{
qDebug() << "Scan timeout reached";
stopScan();
}
void DeviceScanner::scanNextHost()
{
// This function is no longer used in batch mode
// Kept for compatibility
}
void DeviceScanner::sendDiscoveryPacket(const QString &ip)
{
QByteArray data = "DISCOVER";
m_socket->writeDatagram(data, QHostAddress(ip), SCAN_PORT);
}
QString DeviceScanner::getLocalSubnet()
{
// Get all network interfaces
QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
// Priority 1: Look for Ethernet adapter (以太网)
for (const QNetworkInterface &iface : interfaces) {
QString name = iface.humanReadableName().toLower();
// Skip virtual adapters
if (name.contains("virtual") || name.contains("vmware") ||
name.contains("virtualbox") || name.contains("hyper-v") ||
name.contains("vpn") || name.contains("tap") || name.contains("tun")) {
continue;
}
// Look for Ethernet adapter
if (name.contains("ethernet") || name.contains("以太网")) {
QList<QNetworkAddressEntry> entries = iface.addressEntries();
for (const QNetworkAddressEntry &entry : entries) {
QHostAddress addr = entry.ip();
if (addr.protocol() == QAbstractSocket::IPv4Protocol && !addr.isLoopback()) {
QString ip = addr.toString();
QStringList parts = ip.split('.');
if (parts.size() == 4) {
qDebug() << "Found Ethernet adapter:" << iface.humanReadableName() << "IP:" << ip;
return parts[0] + "." + parts[1] + "." + parts[2];
}
}
}
}
}
// Priority 2: Any non-virtual adapter
for (const QNetworkInterface &iface : interfaces) {
QString name = iface.humanReadableName().toLower();
if (name.contains("virtual") || name.contains("vmware") ||
name.contains("virtualbox") || name.contains("hyper-v") ||
name.contains("vpn") || name.contains("tap") || name.contains("tun")) {
continue;
}
QList<QNetworkAddressEntry> entries = iface.addressEntries();
for (const QNetworkAddressEntry &entry : entries) {
QHostAddress addr = entry.ip();
if (addr.protocol() == QAbstractSocket::IPv4Protocol && !addr.isLoopback()) {
QString ip = addr.toString();
QStringList parts = ip.split('.');
if (parts.size() == 4) {
qDebug() << "Found adapter:" << iface.humanReadableName() << "IP:" << ip;
return parts[0] + "." + parts[1] + "." + parts[2];
}
}
}
}
return "192.168.0";
}

62
src/core/DeviceScanner.h Normal file
View File

@@ -0,0 +1,62 @@
#ifndef DEVICESCANNER_H
#define DEVICESCANNER_H
#include <QObject>
#include <QUdpSocket>
#include <QTimer>
#include <QHostAddress>
#include <QList>
#include <QString>
struct DeviceInfo {
QString ipAddress;
QString deviceName;
int port;
qint64 responseTime;
};
class DeviceScanner : public QObject
{
Q_OBJECT
public:
explicit DeviceScanner(QObject *parent = nullptr);
~DeviceScanner();
void startScan(const QString &subnet = "");
void stopScan();
bool isScanning() const { return m_isScanning; }
signals:
void deviceFound(const DeviceInfo &device);
void scanProgress(int current, int total);
void scanFinished(int devicesFound);
void scanError(const QString &error);
private slots:
void onReadyRead();
void onTimeout();
void scanNextHost();
private:
void sendDiscoveryPacket(const QString &ip);
QString getLocalSubnet();
QUdpSocket *m_socket;
QTimer *m_timeoutTimer;
QTimer *m_scanTimer;
QString m_subnet;
int m_currentHost;
int m_totalHosts;
bool m_isScanning;
QList<DeviceInfo> m_foundDevices;
static constexpr int SCAN_PORT = 6790; // Control port for device discovery
static constexpr int SCAN_TIMEOUT = 10;
static constexpr int HOST_START = 1;
static constexpr int HOST_END = 254;
};
#endif // DEVICESCANNER_H

View File

@@ -0,0 +1,59 @@
QString DeviceScanner::getLocalSubnet()
{
// Get all network interfaces
QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
// Priority 1: Look for Ethernet adapter (以太网)
for (const QNetworkInterface &iface : interfaces) {
QString name = iface.humanReadableName().toLower();
// Skip virtual adapters
if (name.contains("virtual") || name.contains("vmware") ||
name.contains("virtualbox") || name.contains("hyper-v") ||
name.contains("vpn") || name.contains("tap") || name.contains("tun")) {
continue;
}
// Look for Ethernet adapter
if (name.contains("ethernet") || name.contains("以太网")) {
QList<QNetworkAddressEntry> entries = iface.addressEntries();
for (const QNetworkAddressEntry &entry : entries) {
QHostAddress addr = entry.ip();
if (addr.protocol() == QAbstractSocket::IPv4Protocol && !addr.isLoopback()) {
QString ip = addr.toString();
QStringList parts = ip.split('.');
if (parts.size() == 4) {
qDebug() << "Found Ethernet adapter:" << iface.humanReadableName() << "IP:" << ip;
return parts[0] + "." + parts[1] + "." + parts[2];
}
}
}
}
}
// Priority 2: Any non-virtual adapter
for (const QNetworkInterface &iface : interfaces) {
QString name = iface.humanReadableName().toLower();
if (name.contains("virtual") || name.contains("vmware") ||
name.contains("virtualbox") || name.contains("hyper-v") ||
name.contains("vpn") || name.contains("tap") || name.contains("tun")) {
continue;
}
QList<QNetworkAddressEntry> entries = iface.addressEntries();
for (const QNetworkAddressEntry &entry : entries) {
QHostAddress addr = entry.ip();
if (addr.protocol() == QAbstractSocket::IPv4Protocol && !addr.isLoopback()) {
QString ip = addr.toString();
QStringList parts = ip.split('.');
if (parts.size() == 4) {
qDebug() << "Found adapter:" << iface.humanReadableName() << "IP:" << ip;
return parts[0] + "." + parts[1] + "." + parts[2];
}
}
}
}
return "192.168.0";
}

229
src/core/GVSPParser.cpp Normal file
View File

@@ -0,0 +1,229 @@
#include "core/GVSPParser.h"
#include <QDebug>
#include <cstring>
#include <winsock2.h>
GVSPParser::GVSPParser(QObject *parent)
: QObject(parent)
, m_isReceiving(false)
, m_dataType(0)
, m_currentBlockId(0)
, m_expectedSize(0)
, m_receivedSize(0)
, m_imageWidth(0)
, m_imageHeight(0)
, m_pixelFormat(0)
, m_lastBlockId(0)
, m_packetCount(0)
{
}
GVSPParser::~GVSPParser()
{
}
void GVSPParser::reset()
{
m_isReceiving = false;
m_dataType = 0;
m_currentBlockId = 0;
m_dataBuffer.clear();
m_expectedSize = 0;
m_receivedSize = 0;
m_packetCount = 0;
}
void GVSPParser::parsePacket(const QByteArray &packet)
{
if (packet.size() < sizeof(GVSPPacketHeader)) {
return;
}
const uint8_t *data = reinterpret_cast<const uint8_t*>(packet.constData());
const GVSPPacketHeader *header = reinterpret_cast<const GVSPPacketHeader*>(data);
// 注释掉调试日志以提高性能
// static int debugCount = 0;
// if (debugCount < 3) {
// qDebug() << "Packet" << debugCount << "first 16 bytes (hex):";
// QString hexStr;
// for (int i = 0; i < qMin(16, packet.size()); i++) {
// hexStr += QString("%1 ").arg(data[i], 2, 16, QChar('0'));
// }
// qDebug() << hexStr;
// debugCount++;
// }
// GVSP包头格式status(2) + block_id(2) + packet_format(4)
// 包类型在packet_format的高字节
uint32_t packet_fmt = ntohl(header->packet_fmt_id);
uint8_t packetType = (packet_fmt >> 24) & 0xFF;
uint16_t blockId = ntohs(header->block_id);
// 注释掉频繁的日志输出以提高性能
// static int leaderCount = 0;
// static int trailerCount = 0;
switch (packetType) {
case GVSP_LEADER_PACKET:
handleLeaderPacket(data, packet.size());
break;
case GVSP_PAYLOAD_PACKET:
handlePayloadPacket(data, packet.size());
break;
case GVSP_TRAILER_PACKET:
handleTrailerPacket(data, packet.size());
break;
default:
break;
}
}
void GVSPParser::handleLeaderPacket(const uint8_t *data, size_t size)
{
if (size < sizeof(GVSPPacketHeader) + sizeof(GVSPImageDataLeader)) {
return;
}
const GVSPPacketHeader *header = reinterpret_cast<const GVSPPacketHeader*>(data);
m_currentBlockId = ntohs(header->block_id);
// Check payload type
const uint16_t *payload_type_ptr = reinterpret_cast<const uint16_t*>(data + sizeof(GVSPPacketHeader) + 2);
uint16_t payload_type = ntohs(*payload_type_ptr);
if (payload_type == PAYLOAD_TYPE_IMAGE) {
// Image data leader
const GVSPImageDataLeader *leader = reinterpret_cast<const GVSPImageDataLeader*>(data + sizeof(GVSPPacketHeader));
m_dataType = 1;
m_imageWidth = ntohl(leader->size_x);
m_imageHeight = ntohl(leader->size_y);
m_pixelFormat = ntohl(leader->pixel_format);
m_expectedSize = m_imageWidth * m_imageHeight * 2; // 12-bit packed in 16-bit
m_dataBuffer.clear();
m_dataBuffer.reserve(m_expectedSize);
m_receivedSize = 0;
m_isReceiving = true;
m_packetCount = 0;
// 注释掉频繁的日志输出
// qDebug() << "Image Leader: Block" << m_currentBlockId
// << "Size:" << m_imageWidth << "x" << m_imageHeight;
}
else if (payload_type == PAYLOAD_TYPE_BINARY) {
// Depth data leader
const GVSPBinaryDataLeader *leader = reinterpret_cast<const GVSPBinaryDataLeader*>(data + sizeof(GVSPPacketHeader));
m_dataType = 3;
m_expectedSize = ntohl(leader->file_size);
m_dataBuffer.clear();
m_dataBuffer.reserve(m_expectedSize);
m_receivedSize = 0;
m_isReceiving = true;
m_packetCount = 0;
// 注释掉频繁的日志输出
// qDebug() << "Depth Leader: Block" << m_currentBlockId
// << "Size:" << m_expectedSize << "bytes";
}
}
void GVSPParser::handlePayloadPacket(const uint8_t *data, size_t size)
{
if (!m_isReceiving) {
return;
}
if (size <= sizeof(GVSPPacketHeader)) {
return;
}
// Extract payload data (skip header)
const uint8_t *payload = data + sizeof(GVSPPacketHeader);
size_t payload_size = size - sizeof(GVSPPacketHeader);
// Append to buffer
m_dataBuffer.append(reinterpret_cast<const char*>(payload), payload_size);
m_receivedSize += payload_size;
m_packetCount++;
}
void GVSPParser::handleTrailerPacket(const uint8_t *data, size_t size)
{
if (!m_isReceiving) {
return;
}
// 注释掉频繁的日志输出
// qDebug() << "Trailer received: Block" << m_currentBlockId
// << "Received" << m_receivedSize << "/" << m_expectedSize << "bytes"
// << "Packets:" << m_packetCount;
// Process complete data
if (m_dataType == 1) {
processImageData();
} else if (m_dataType == 3) {
processDepthData();
}
// Reset state
m_isReceiving = false;
m_lastBlockId = m_currentBlockId;
}
void GVSPParser::processImageData()
{
if (m_dataBuffer.size() < m_expectedSize) {
// 注释掉频繁的警告日志
// qDebug() << "Warning: Incomplete image data" << m_dataBuffer.size() << "/" << m_expectedSize;
return;
}
// Convert 16-bit depth data to 8-bit grayscale for display
const uint16_t *src = reinterpret_cast<const uint16_t*>(m_dataBuffer.constData());
QImage image(m_imageWidth, m_imageHeight, QImage::Format_Grayscale8);
// Find min/max for normalization
uint16_t minVal = 65535, maxVal = 0;
for (size_t i = 0; i < m_imageWidth * m_imageHeight; i++) {
uint16_t val = src[i];
if (val > 0) {
if (val < minVal) minVal = val;
if (val > maxVal) maxVal = val;
}
}
// Normalize to 0-255 and flip vertically
uint8_t *dst = image.bits();
float scale = (maxVal > minVal) ? (255.0f / (maxVal - minVal)) : 0.0f;
for (size_t y = 0; y < m_imageHeight; y++) {
for (size_t x = 0; x < m_imageWidth; x++) {
size_t src_idx = y * m_imageWidth + x;
size_t dst_idx = (m_imageHeight - 1 - y) * m_imageWidth + x; // 垂直翻转
uint16_t val = src[src_idx];
if (val == 0) {
dst[dst_idx] = 0;
} else {
dst[dst_idx] = static_cast<uint8_t>((val - minVal) * scale);
}
}
}
emit imageReceived(image, m_currentBlockId);
}
void GVSPParser::processDepthData()
{
if (m_dataBuffer.size() < m_expectedSize) {
// 注释掉频繁的警告日志
// qDebug() << "Warning: Incomplete depth data" << m_dataBuffer.size() << "/" << m_expectedSize;
return;
}
emit depthDataReceived(m_dataBuffer, m_currentBlockId);
}

130
src/core/Logger.cpp Normal file
View File

@@ -0,0 +1,130 @@
#include "core/Logger.h"
#include <QDateTime>
#include <QFileInfo>
#include <QDir>
Logger* Logger::s_instance = nullptr;
Logger* Logger::instance()
{
if (!s_instance) {
s_instance = new Logger();
}
return s_instance;
}
Logger::Logger(QObject *parent)
: QObject(parent)
, m_maxLines(10000) // 保留最新10000行
, m_currentLines(0)
{
}
Logger::~Logger()
{
}
void Logger::setLogFile(const QString &filePath)
{
QMutexLocker locker(&m_mutex);
m_logFilePath = filePath;
// Count existing lines
QFile file(m_logFilePath);
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
m_currentLines = 0;
while (!file.atEnd()) {
file.readLine();
m_currentLines++;
}
file.close();
}
}
void Logger::setMaxLines(int maxLines)
{
QMutexLocker locker(&m_mutex);
m_maxLines = maxLines;
}
void Logger::log(const QString &message)
{
writeLog("LOG", message);
}
void Logger::debug(const QString &message)
{
writeLog("DEBUG", message);
}
void Logger::info(const QString &message)
{
writeLog("INFO", message);
}
void Logger::warning(const QString &message)
{
writeLog("WARN", message);
}
void Logger::error(const QString &message)
{
writeLog("ERROR", message);
}
void Logger::writeLog(const QString &level, const QString &message)
{
QMutexLocker locker(&m_mutex);
if (m_logFilePath.isEmpty()) {
return;
}
QString timestamp = QDateTime::currentDateTime().toString("[yyyy-MM-dd hh:mm:ss.zzz]");
QString logLine = QString("%1 [%2] %3\n").arg(timestamp).arg(level).arg(message);
QFile file(m_logFilePath);
if (file.open(QIODevice::Append | QIODevice::Text)) {
QTextStream out(&file);
out << logLine;
file.close();
m_currentLines++;
// Check if we need to trim the log
if (m_currentLines > m_maxLines) {
checkAndTrimLog();
}
}
}
void Logger::checkAndTrimLog()
{
// Read all lines
QFile file(m_logFilePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
return;
}
QStringList lines;
QTextStream in(&file);
while (!in.atEnd()) {
lines.append(in.readLine());
}
file.close();
// Keep only the last m_maxLines
if (lines.size() > m_maxLines) {
lines = lines.mid(lines.size() - m_maxLines);
}
// Write back
if (file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
QTextStream out(&file);
for (const QString &line : lines) {
out << line << "\n";
}
file.close();
m_currentLines = lines.size();
}
}

174
src/core/NetworkManager.cpp Normal file
View File

@@ -0,0 +1,174 @@
#include "core/NetworkManager.h"
#include "core/GVSPParser.h"
#include <QDebug>
#include <QVariant>
NetworkManager::NetworkManager(QObject *parent)
: QObject(parent)
, m_controlSocket(new QUdpSocket(this))
, m_dataSocket(new QUdpSocket(this))
, m_gvspParser(new GVSPParser(this))
, m_controlPort(6790)
, m_dataPort(3957)
, m_isConnected(false)
{
// 连接数据接收信号
connect(m_dataSocket, &QUdpSocket::readyRead, this, &NetworkManager::onReadyRead);
connect(m_dataSocket, &QUdpSocket::errorOccurred, this, &NetworkManager::onError);
// 连接GVSP解析器信号
connect(m_gvspParser, &GVSPParser::imageReceived, this, &NetworkManager::imageReceived);
connect(m_gvspParser, &GVSPParser::depthDataReceived, this, &NetworkManager::depthDataReceived);
}
NetworkManager::~NetworkManager()
{
disconnectFromCamera();
}
// ========== 连接和断开 ==========
bool NetworkManager::connectToCamera(const QString &ip, int controlPort, int dataPort)
{
m_cameraIp = ip;
m_controlPort = controlPort;
m_dataPort = dataPort;
// 绑定控制Socket到任意端口让系统自动分配
if (!m_controlSocket->bind(QHostAddress::Any, 0)) {
QString error = QString("Failed to bind control socket: %1")
.arg(m_controlSocket->errorString());
qDebug() << error;
emit errorOccurred(error);
return false;
}
qDebug() << "Successfully bound control socket to port" << m_controlSocket->localPort();
// 绑定数据接收端口
if (!m_dataSocket->bind(QHostAddress::Any, m_dataPort)) {
QString error = QString("Failed to bind data port %1: %2")
.arg(m_dataPort)
.arg(m_dataSocket->errorString());
qDebug() << error;
emit errorOccurred(error);
return false;
}
// 设置UDP接收缓冲区大小为64MB减少丢包
m_dataSocket->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, QVariant(64 * 1024 * 1024));
qDebug() << "Successfully bound data port" << m_dataPort;
qDebug() << "Data socket state:" << m_dataSocket->state();
qDebug() << "UDP receive buffer size:" << m_dataSocket->socketOption(QAbstractSocket::ReceiveBufferSizeSocketOption).toInt();
m_isConnected = true;
qDebug() << "Connected to camera:" << m_cameraIp << "Control port:" << m_controlPort << "Data port:" << m_dataPort;
// Send STOP command to register client IP on camera
sendStopCommand();
qDebug() << "Sent STOP command to register client IP";
emit connected();
return true;
}
void NetworkManager::disconnectFromCamera()
{
if (m_isConnected) {
m_controlSocket->close();
m_dataSocket->close();
m_isConnected = false;
qDebug() << "Disconnected from camera";
emit disconnected();
}
}
bool NetworkManager::isConnected() const
{
return m_isConnected;
}
// ========== 发送控制命令 ==========
bool NetworkManager::sendCommand(const QString &command)
{
if (!m_isConnected) {
qDebug() << "Not connected to camera";
return false;
}
QByteArray data = command.toUtf8();
qDebug() << "Sending command to" << m_cameraIp << ":" << m_controlPort << "data:" << command;
qDebug() << "Control socket state:" << m_controlSocket->state();
qDebug() << "Control socket error:" << m_controlSocket->errorString();
qDebug() << "Data to send (hex):" << data.toHex();
qDebug() << "Data size:" << data.size();
qint64 sent = m_controlSocket->writeDatagram(data, QHostAddress(m_cameraIp), m_controlPort);
qDebug() << "writeDatagram returned:" << sent;
if (sent == -1) {
QString error = QString("Failed to send command: %1").arg(m_controlSocket->errorString());
qDebug() << error;
qDebug() << "Socket error code:" << m_controlSocket->error();
emit errorOccurred(error);
return false;
}
qDebug() << "Command sent successfully, bytes:" << sent;
qDebug() << "Sent command:" << command;
return true;
}
bool NetworkManager::sendStartCommand()
{
return sendCommand("START");
}
bool NetworkManager::sendStopCommand()
{
return sendCommand("STOP");
}
bool NetworkManager::sendOnceCommand()
{
return sendCommand("ONCE");
}
bool NetworkManager::sendExposureCommand(int exposureTime)
{
QString command = QString("EXPOSURE:%1").arg(exposureTime);
return sendCommand(command);
}
// ========== 槽函数 ==========
void NetworkManager::onReadyRead()
{
static int packetCount = 0;
while (m_dataSocket->hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(m_dataSocket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
m_dataSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
// 临时添加日志以诊断问题
if (packetCount < 5) {
qDebug() << "[NetworkManager] Received packet" << packetCount << "from" << sender.toString() << ":" << senderPort << "size:" << datagram.size();
}
packetCount++;
// 将数据包传递给GVSP解析器
m_gvspParser->parsePacket(datagram);
// 仍然发出原始数据信号(用于调试)
emit dataReceived(datagram);
}
}
void NetworkManager::onError(QAbstractSocket::SocketError socketError)
{
QString error = QString("Socket error: %1").arg(m_dataSocket->errorString());
qDebug() << error;
emit errorOccurred(error);
}

55
src/core/NetworkManager.h Normal file
View File

@@ -0,0 +1,55 @@
#ifndef NETWORKMANAGER_H
#define NETWORKMANAGER_H
#include <QObject>
#include <QUdpSocket>
#include <QString>
#include <QImage>
#include <memory>
class GVSPParser;
class NetworkManager : public QObject
{
Q_OBJECT
public:
explicit NetworkManager(QObject *parent = nullptr);
~NetworkManager();
// 连接和断开
bool connectToCamera(const QString &ip, int controlPort, int dataPort);
void disconnectFromCamera();
bool isConnected() const;
// 发送控制命令
bool sendCommand(const QString &command);
bool sendStartCommand();
bool sendStopCommand();
bool sendOnceCommand();
bool sendExposureCommand(int exposureTime);
signals:
void connected();
void disconnected();
void errorOccurred(const QString &error);
void dataReceived(const QByteArray &data);
void imageReceived(const QImage &image, uint32_t blockId);
void depthDataReceived(const QByteArray &depthData, uint32_t blockId);
private slots:
void onReadyRead();
void onError(QAbstractSocket::SocketError socketError);
private:
QUdpSocket *m_controlSocket;
QUdpSocket *m_dataSocket;
GVSPParser *m_gvspParser;
QString m_cameraIp;
int m_controlPort;
int m_dataPort;
bool m_isConnected;
};
#endif // NETWORKMANAGER_H

View File

@@ -0,0 +1,277 @@
#include "core/PointCloudProcessor.h"
#include <QDebug>
#include <vector>
PointCloudProcessor::PointCloudProcessor(QObject *parent)
: QObject(parent)
, m_fx(1432.8957f)
, m_fy(1432.6590f)
, m_cx(637.5117f)
, m_cy(521.8720f)
, m_zScale(0.2f)
, m_imageWidth(1224)
, m_imageHeight(1024)
, m_totalPoints(1224 * 1024)
, m_platform(nullptr)
, m_device(nullptr)
, m_context(nullptr)
, m_queue(nullptr)
, m_program(nullptr)
, m_kernel(nullptr)
, m_depthBuffer(nullptr)
, m_xyzBuffer(nullptr)
, m_clInitialized(false)
{
}
PointCloudProcessor::~PointCloudProcessor()
{
cleanupOpenCL();
}
void PointCloudProcessor::setCameraIntrinsics(float fx, float fy, float cx, float cy)
{
m_fx = fx;
m_fy = fy;
m_cx = cx;
m_cy = cy;
}
void PointCloudProcessor::setZScaleFactor(float scale)
{
m_zScale = scale;
}
bool PointCloudProcessor::initializeOpenCL()
{
if (m_clInitialized) {
return true;
}
cl_int err;
qDebug() << "[OpenCL] Starting initialization...";
// 1. 获取平台
err = clGetPlatformIDs(1, &m_platform, nullptr);
if (err != CL_SUCCESS) {
emit errorOccurred("Failed to get OpenCL platform");
return false;
}
// 2. 获取设备优先GPU
err = clGetDeviceIDs(m_platform, CL_DEVICE_TYPE_GPU, 1, &m_device, nullptr);
if (err != CL_SUCCESS) {
qDebug() << "[OpenCL] GPU not available, using CPU";
err = clGetDeviceIDs(m_platform, CL_DEVICE_TYPE_CPU, 1, &m_device, nullptr);
if (err != CL_SUCCESS) {
emit errorOccurred("Failed to get OpenCL device");
return false;
}
}
// 3. 创建上下文
m_context = clCreateContext(nullptr, 1, &m_device, nullptr, nullptr, &err);
if (err != CL_SUCCESS) {
emit errorOccurred("Failed to create OpenCL context");
return false;
}
// 4. 创建命令队列
#if CL_TARGET_OPENCL_VERSION >= 200
cl_queue_properties props[] = { CL_QUEUE_ON_DEVICE_DEFAULT, 0 };
m_queue = clCreateCommandQueueWithProperties(m_context, m_device, props, &err);
#else
m_queue = clCreateCommandQueue(m_context, m_device, 0, &err);
#endif
if (err != CL_SUCCESS) {
emit errorOccurred("Failed to create command queue");
cleanupOpenCL();
return false;
}
qDebug() << "[OpenCL] Platform, device, context, and queue created successfully";
// 5. 创建并编译OpenCL程序
const char* kernelSource =
"__kernel void compute_xyz(__global const float* depth, "
"__global float* xyz, int width, int height, "
"float inv_fx, float inv_fy, float cx, float cy, float z_scale) { "
"int idx = get_global_id(0); "
"if (idx >= width * height) return; "
"int y = idx / width; "
"int x = idx % width; "
"float z = depth[idx] * z_scale; "
// 完全平面的圆柱投影X和Y直接使用像素坐标缩放到合适的范围
"xyz[idx*3] = (x - cx) * 2.0f; " // X坐标缩放系数2.0
"xyz[idx*3+1] = (y - cy) * 2.0f; " // Y坐标缩放系数2.0
"xyz[idx*3+2] = z; "
"}";
m_program = clCreateProgramWithSource(m_context, 1, &kernelSource, nullptr, &err);
if (err != CL_SUCCESS) {
emit errorOccurred("Failed to create OpenCL program");
cleanupOpenCL();
return false;
}
err = clBuildProgram(m_program, 1, &m_device, nullptr, nullptr, nullptr);
if (err != CL_SUCCESS) {
char buildLog[4096];
clGetProgramBuildInfo(m_program, m_device, CL_PROGRAM_BUILD_LOG, sizeof(buildLog), buildLog, nullptr);
emit errorOccurred(QString("Failed to build OpenCL program: %1").arg(buildLog));
cleanupOpenCL();
return false;
}
qDebug() << "[OpenCL] Program compiled successfully";
// 6. 创建kernel
m_kernel = clCreateKernel(m_program, "compute_xyz", &err);
if (err != CL_SUCCESS) {
emit errorOccurred("Failed to create OpenCL kernel");
cleanupOpenCL();
return false;
}
// 7. 创建缓冲区
m_depthBuffer = clCreateBuffer(m_context, CL_MEM_READ_ONLY,
sizeof(float) * m_totalPoints, nullptr, &err);
if (err != CL_SUCCESS) {
emit errorOccurred("Failed to create depth buffer");
cleanupOpenCL();
return false;
}
m_xyzBuffer = clCreateBuffer(m_context, CL_MEM_WRITE_ONLY,
sizeof(float) * m_totalPoints * 3, nullptr, &err);
if (err != CL_SUCCESS) {
emit errorOccurred("Failed to create XYZ buffer");
cleanupOpenCL();
return false;
}
m_clInitialized = true;
qDebug() << "[OpenCL] Initialization complete";
return true;
}
void PointCloudProcessor::processDepthData(const QByteArray &depthData, uint32_t blockId)
{
if (!m_clInitialized) {
if (!initializeOpenCL()) {
return;
}
}
// 验证数据大小
if (depthData.size() != m_totalPoints * sizeof(int16_t)) {
qDebug() << "[PointCloud] Invalid depth data size:" << depthData.size()
<< "expected:" << (m_totalPoints * sizeof(int16_t));
return;
}
// 转换int16_t到float
const int16_t* depthShort = reinterpret_cast<const int16_t*>(depthData.constData());
std::vector<float> depthFloat(m_totalPoints);
for (size_t i = 0; i < m_totalPoints; i++) {
depthFloat[i] = static_cast<float>(depthShort[i]);
}
// 注释掉频繁的日志输出
// qDebug() << "[PointCloud] Processing block" << blockId << "with" << m_totalPoints << "points";
// 上传深度数据到GPU
cl_int err = clEnqueueWriteBuffer(m_queue, m_depthBuffer, CL_TRUE, 0,
sizeof(float) * m_totalPoints, depthFloat.data(),
0, nullptr, nullptr);
if (err != CL_SUCCESS) {
emit errorOccurred(QString("Failed to upload depth data: %1").arg(err));
return;
}
// 设置kernel参数
float inv_fx = 1.0f / m_fx;
float inv_fy = 1.0f / m_fy;
int width = m_imageWidth;
int height = m_imageHeight;
clSetKernelArg(m_kernel, 0, sizeof(cl_mem), &m_depthBuffer);
clSetKernelArg(m_kernel, 1, sizeof(cl_mem), &m_xyzBuffer);
clSetKernelArg(m_kernel, 2, sizeof(int), &width);
clSetKernelArg(m_kernel, 3, sizeof(int), &height);
clSetKernelArg(m_kernel, 4, sizeof(float), &inv_fx);
clSetKernelArg(m_kernel, 5, sizeof(float), &inv_fy);
clSetKernelArg(m_kernel, 6, sizeof(float), &m_cx);
clSetKernelArg(m_kernel, 7, sizeof(float), &m_cy);
clSetKernelArg(m_kernel, 8, sizeof(float), &m_zScale);
// 执行kernel
size_t local = 256;
size_t global = ((m_totalPoints + local - 1) / local) * local;
err = clEnqueueNDRangeKernel(m_queue, m_kernel, 1, nullptr,
&global, &local, 0, nullptr, nullptr);
if (err != CL_SUCCESS) {
emit errorOccurred(QString("Failed to execute kernel: %1").arg(err));
return;
}
clFinish(m_queue);
// 读取结果
std::vector<float> xyz(m_totalPoints * 3);
err = clEnqueueReadBuffer(m_queue, m_xyzBuffer, CL_TRUE, 0,
sizeof(float) * m_totalPoints * 3, xyz.data(),
0, nullptr, nullptr);
if (err != CL_SUCCESS) {
emit errorOccurred(QString("Failed to read XYZ data: %1").arg(err));
return;
}
// 创建PCL点云
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->width = m_imageWidth;
cloud->height = m_imageHeight;
cloud->is_dense = false;
cloud->points.resize(m_totalPoints);
// 填充点云数据(过滤全零点)
for (size_t i = 0; i < m_totalPoints; i++) {
cloud->points[i].x = xyz[i * 3];
cloud->points[i].y = xyz[i * 3 + 1];
cloud->points[i].z = xyz[i * 3 + 2];
}
// 注释掉频繁的日志输出
// qDebug() << "[PointCloud] Block" << blockId << "processed successfully";
emit pointCloudReady(cloud, blockId);
}
void PointCloudProcessor::cleanupOpenCL()
{
if (m_depthBuffer) {
clReleaseMemObject(m_depthBuffer);
m_depthBuffer = nullptr;
}
if (m_xyzBuffer) {
clReleaseMemObject(m_xyzBuffer);
m_xyzBuffer = nullptr;
}
if (m_kernel) {
clReleaseKernel(m_kernel);
m_kernel = nullptr;
}
if (m_program) {
clReleaseProgram(m_program);
m_program = nullptr;
}
if (m_queue) {
clReleaseCommandQueue(m_queue);
m_queue = nullptr;
}
if (m_context) {
clReleaseContext(m_context);
m_context = nullptr;
}
m_clInitialized = false;
qDebug() << "[OpenCL] Cleanup complete";
}

1138
src/gui/MainWindow.cpp Normal file

File diff suppressed because it is too large Load Diff

159
src/gui/MainWindow.h Normal file
View File

@@ -0,0 +1,159 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
#include <QDateTime>
#include <memory>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
class ConfigManager;
class NetworkManager;
class DeviceScanner;
class GVSPParser;
class PointCloudProcessor;
class PointCloudWidget;
struct DeviceInfo;
class QListWidget;
class QListWidgetItem;
class QPushButton;
class QSlider;
class QSpinBox;
class QLabel;
class QImage;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
// 相机控制槽函数
void onStartClicked();
void onStopClicked();
void onOnceClicked();
void onExposureChanged(int value);
void onCaptureClicked();
void onBrowseSavePathClicked();
// 网络配置槽函数
void onConnectClicked();
void onIpAddressChanged(const QString &ip);
void onControlPortChanged(int port);
void onDataPortChanged(int port);
void onNetworkConnected();
void onNetworkDisconnected();
void onDataReceived(const QByteArray &data);
// GVSP数据处理槽函数
void onImageReceived(const QImage &image, uint32_t blockId);
void onDepthDataReceived(const QByteArray &depthData, uint32_t blockId);
void onPointCloudReady(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, uint32_t blockId);
// 设备扫描槽函数
void onRefreshClicked();
void onDeviceFound(const DeviceInfo &device);
void onScanProgress(int current, int total);
void onScanFinished(int devicesFound);
void onDeviceSelected(QListWidgetItem *item);
// 定时器更新
void updateUI();
// 日志槽函数
void onClearLogClicked();
void onSaveLogClicked();
protected:
// 重写事件处理函数以监听系统主题变化
void changeEvent(QEvent *event) override;
private:
void setupUI();
void setupConnections();
void loadSettings();
void saveSettings();
// 拍照辅助函数
bool saveDepthImage(const QString &dir, const QString &baseName, const QString &format);
bool savePointCloud(const QString &dir, const QString &baseName, const QString &format);
void performBackgroundSave(const QString &saveDir, const QString &baseName,
const QString &depthFormat, const QString &pointCloudFormat,
const QImage &image, pcl::PointCloud<pcl::PointXYZ>::Ptr cloud);
// 日志辅助函数
void addLog(const QString &message, const QString &level = "INFO");
void updateStatistics();
private:
// 核心管理器
std::unique_ptr<ConfigManager> m_configManager;
std::unique_ptr<NetworkManager> m_networkManager;
std::unique_ptr<DeviceScanner> m_deviceScanner;
std::unique_ptr<PointCloudProcessor> m_pointCloudProcessor;
// UI更新定时器
QTimer *m_updateTimer;
// 状态变量
bool m_isConnected;
bool m_autoSaveOnNextFrame;
// 当前帧数据(用于拍照)
QImage m_currentImage;
pcl::PointCloud<pcl::PointXYZ>::Ptr m_currentPointCloud;
uint32_t m_currentFrameId;
// UI控件指针
class QWidget *m_centralWidget;
class QSplitter *m_mainSplitter;
class QWidget *m_controlPanel;
class QLabel *m_imageDisplay;
PointCloudWidget *m_pointCloudWidget;
// 按钮控件
QPushButton *m_refreshBtn;
QPushButton *m_connectBtn;
QPushButton *m_startBtn;
QPushButton *m_stopBtn;
QPushButton *m_onceBtn;
QPushButton *m_captureBtn;
// 输入控件
QSlider *m_exposureSlider;
QSpinBox *m_exposureSpinBox;
QSpinBox *m_ctrlPortSpinBox;
QSpinBox *m_dataPortSpinBox;
// 拍照参数控件
class QLineEdit *m_savePathEdit;
QPushButton *m_browseSavePathBtn;
class QComboBox *m_depthFormatCombo;
class QComboBox *m_pointCloudFormatCombo;
// 显示控件
QLabel *m_statusLabel;
QListWidget *m_deviceList;
// 统计信息控件
QLabel *m_fpsLabel;
QLabel *m_resolutionLabel;
QLabel *m_queueLabel;
// 日志显示控件
class QTextEdit *m_logDisplay;
QPushButton *m_clearLogBtn;
QPushButton *m_saveLogBtn;
// 统计数据
QDateTime m_lastFrameTime;
int m_frameCount;
int m_totalFrameCount;
double m_currentFps;
};
#endif // MAINWINDOW_H

View File

@@ -0,0 +1,9 @@
void MainWindow::onDataReceived(const QByteArray &data)
{
static int packetCount = 0;
packetCount++;
if (packetCount % 100 == 0) {
qDebug() << "Received" << packetCount << "packets, latest size:" << data.size() << "bytes";
}
}

View File

@@ -0,0 +1,259 @@
#include "gui/PointCloudGLWidget.h"
#include <QDebug>
#include <cmath>
#include <cfloat>
PointCloudGLWidget::PointCloudGLWidget(QWidget *parent)
: QOpenGLWidget(parent)
, m_program(nullptr)
, m_vertexBuffer(nullptr)
, m_vao(nullptr)
, m_pointCount(0)
, m_orthoSize(2000.0f) // 正交投影视野大小
, m_rotationX(0.0f) // 从正面看0度
, m_rotationY(0.0f) // 从正面看0度
, m_translation(0.0f, 0.0f, 0.0f)
, m_leftButtonPressed(false)
, m_rightButtonPressed(false)
{
setMinimumSize(400, 400);
}
PointCloudGLWidget::~PointCloudGLWidget()
{
makeCurrent();
if (m_vao) {
m_vao->destroy();
delete m_vao;
}
if (m_vertexBuffer) {
m_vertexBuffer->destroy();
delete m_vertexBuffer;
}
if (m_program) {
delete m_program;
}
doneCurrent();
}
void PointCloudGLWidget::initializeGL()
{
initializeOpenGLFunctions();
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glEnable(GL_DEPTH_TEST);
glEnable(GL_PROGRAM_POINT_SIZE);
setupShaders();
// 创建VAO和VBO
m_vao = new QOpenGLVertexArrayObject(this);
m_vao->create();
m_vertexBuffer = new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
m_vertexBuffer->create();
qDebug() << "[PointCloudGLWidget] OpenGL initialized";
}
void PointCloudGLWidget::setupShaders()
{
m_program = new QOpenGLShaderProgram(this);
// 顶点着色器
const char *vertexShaderSource = R"(
#version 330 core
layout(location = 0) in vec3 position;
uniform mat4 mvp;
void main() {
gl_Position = mvp * vec4(position, 1.0);
gl_PointSize = 2.0;
}
)";
// 片段着色器
const char *fragmentShaderSource = R"(
#version 330 core
out vec4 fragColor;
void main() {
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
)";
if (!m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource)) {
qDebug() << "[PointCloudGLWidget] Vertex shader error:" << m_program->log();
}
if (!m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource)) {
qDebug() << "[PointCloudGLWidget] Fragment shader error:" << m_program->log();
}
if (!m_program->link()) {
qDebug() << "[PointCloudGLWidget] Shader link error:" << m_program->log();
} else {
qDebug() << "[PointCloudGLWidget] Shaders compiled and linked successfully";
}
}
void PointCloudGLWidget::resizeGL(int w, int h)
{
m_projection.setToIdentity();
// 使用正交投影代替透视投影,避免"喷射状"效果
float aspect = float(w) / float(h);
m_projection.ortho(-m_orthoSize * aspect, m_orthoSize * aspect,
-m_orthoSize, m_orthoSize,
-50000.0f, 50000.0f); // 近平面和远平面
}
void PointCloudGLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (m_pointCount == 0 || !m_program) {
return;
}
// 每帧重新计算正交投影矩阵确保使用最新的m_orthoSize
m_projection.setToIdentity();
float aspect = float(width()) / float(height());
m_projection.ortho(-m_orthoSize * aspect, m_orthoSize * aspect,
-m_orthoSize, m_orthoSize,
-50000.0f, 50000.0f);
// 设置view矩阵
m_view.setToIdentity();
m_view.rotate(m_rotationX, 1.0f, 0.0f, 0.0f);
m_view.rotate(m_rotationY, 0.0f, 1.0f, 0.0f);
m_view.translate(m_translation);
// 设置model矩阵
m_model.setToIdentity();
// 计算MVP矩阵
QMatrix4x4 mvp = m_projection * m_view * m_model;
// 绑定shader和设置uniform
m_program->bind();
m_program->setUniformValue("mvp", mvp);
// 绑定VAO和绘制
m_vao->bind();
glDrawArrays(GL_POINTS, 0, m_pointCount);
m_vao->release();
m_program->release();
}
void PointCloudGLWidget::mousePressEvent(QMouseEvent *event)
{
m_lastMousePos = event->pos();
if (event->button() == Qt::LeftButton) {
m_leftButtonPressed = true;
} else if (event->button() == Qt::RightButton) {
m_rightButtonPressed = true;
}
}
void PointCloudGLWidget::mouseMoveEvent(QMouseEvent *event)
{
QPoint delta = event->pos() - m_lastMousePos;
m_lastMousePos = event->pos();
if (m_leftButtonPressed) {
// 左键:旋转
m_rotationX += delta.y() * 0.5f;
m_rotationY += delta.x() * 0.5f;
update();
} else if (m_rightButtonPressed) {
// 右键:平移(根据正交投影视野大小调整平移速度)
float scale = m_orthoSize * 0.002f;
m_translation.setX(m_translation.x() + delta.x() * scale);
m_translation.setY(m_translation.y() - delta.y() * scale);
update();
}
}
void PointCloudGLWidget::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
m_leftButtonPressed = false;
} else if (event->button() == Qt::RightButton) {
m_rightButtonPressed = false;
}
}
void PointCloudGLWidget::wheelEvent(QWheelEvent *event)
{
// 滚轮:缩放(调整正交投影视野大小)
float delta = event->angleDelta().y() / 120.0f;
m_orthoSize *= (1.0f - delta * 0.1f);
m_orthoSize = qMax(100.0f, qMin(m_orthoSize, 10000.0f)); // 范围100-10000
update(); // 触发重绘paintGL会使用新的m_orthoSize
}
void PointCloudGLWidget::updatePointCloud(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud)
{
if (!cloud) {
return;
}
// 过滤全零点并转换为顶点数组
m_vertices.clear();
float minX = FLT_MAX, maxX = -FLT_MAX;
float minY = FLT_MAX, maxY = -FLT_MAX;
float minZ = FLT_MAX, maxZ = -FLT_MAX;
for (const auto& point : cloud->points) {
if (point.z > 0.01f) {
m_vertices.push_back(point.x);
m_vertices.push_back(point.y);
m_vertices.push_back(point.z);
// 统计坐标范围
if (point.x < minX) minX = point.x;
if (point.x > maxX) maxX = point.x;
if (point.y < minY) minY = point.y;
if (point.y > maxY) maxY = point.y;
if (point.z < minZ) minZ = point.z;
if (point.z > maxZ) maxZ = point.z;
}
}
m_pointCount = m_vertices.size() / 3;
// 添加调试日志
static int updateCount = 0;
if (updateCount < 3 || updateCount % 100 == 0) {
qDebug() << "[PointCloudGLWidget] Update" << updateCount << "- Points:" << m_pointCount
<< "Total cloud size:" << cloud->size();
qDebug() << " X range:" << minX << "to" << maxX;
qDebug() << " Y range:" << minY << "to" << maxY;
qDebug() << " Z range:" << minZ << "to" << maxZ;
}
updateCount++;
updateBuffers();
update();
}
void PointCloudGLWidget::updateBuffers()
{
if (m_vertices.empty() || !m_vao || !m_vertexBuffer) {
return;
}
makeCurrent();
m_vao->bind();
m_vertexBuffer->bind();
m_vertexBuffer->allocate(m_vertices.data(), m_vertices.size() * sizeof(float));
// 设置顶点属性指针
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
m_vertexBuffer->release();
m_vao->release();
doneCurrent();
}

View File

@@ -0,0 +1,56 @@
#include "gui/PointCloudWidget.h"
#include "gui/PointCloudGLWidget.h"
#include <QVBoxLayout>
#include <QDebug>
PointCloudWidget::PointCloudWidget(QWidget *parent)
: QWidget(parent)
, m_statusLabel(nullptr)
, m_glWidget(nullptr)
{
// 创建布局
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(5);
// 创建状态标签
m_statusLabel = new QLabel("点云显示 (等待数据...)", this);
m_statusLabel->setAlignment(Qt::AlignCenter);
m_statusLabel->setStyleSheet("QLabel { background-color: #1C1C1C; color: white; font-size: 12px; padding: 5px; }");
m_statusLabel->setMaximumHeight(30);
layout->addWidget(m_statusLabel);
// 创建OpenGL点云显示widget
m_glWidget = new PointCloudGLWidget(this);
layout->addWidget(m_glWidget);
setLayout(layout);
qDebug() << "[PointCloudWidget] Embedded OpenGL widget created";
}
PointCloudWidget::~PointCloudWidget()
{
// OpenGL widget会自动清理
}
void PointCloudWidget::updatePointCloud(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud)
{
if (!cloud || !m_glWidget) {
return;
}
// 统计有效点数
int validPoints = 0;
for (const auto& point : cloud->points) {
if (point.z > 0.01f) {
validPoints++;
}
}
// 更新状态标签
m_statusLabel->setText(QString("点云显示 | 有效点数: %1").arg(validPoints));
// 更新OpenGL显示
m_glWidget->updatePointCloud(cloud);
}

58
src/main.cpp Normal file
View File

@@ -0,0 +1,58 @@
#include <QApplication>
#include <QDateTime>
#include <QDir>
#include "gui/MainWindow.h"
#include "core/Logger.h"
// Custom message handler to redirect qDebug output to Logger
void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
Logger *logger = Logger::instance();
switch (type) {
case QtDebugMsg:
logger->debug(msg);
break;
case QtInfoMsg:
logger->info(msg);
break;
case QtWarningMsg:
logger->warning(msg);
break;
case QtCriticalMsg:
case QtFatalMsg:
logger->error(msg);
break;
}
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// 设置应用程序信息
app.setOrganizationName("UpperControl");
app.setApplicationName("UpperControl GUI");
app.setApplicationVersion("1.0.0");
// 初始化Logger在可执行文件同目录下
QString logPath = QCoreApplication::applicationDirPath() + "/d330viewer.log";
Logger::instance()->setLogFile(logPath);
Logger::instance()->setMaxLines(10000); // 保留最新10000行
// 安装消息处理器
qInstallMessageHandler(messageHandler);
qDebug() << "D330Viewer started";
qDebug() << "Log file:" << logPath;
// 创建并显示主窗口
MainWindow mainWindow;
mainWindow.show();
int result = app.exec();
qDebug() << "D330Viewer exiting";
return result;
}