修改BUG:保存图像数据,分相机保存
This commit is contained in:
parent
c96fc7fb63
commit
92fe7b1345
132
camhandler.cpp
132
camhandler.cpp
@ -4,9 +4,13 @@
|
|||||||
// 构造/析构
|
// 构造/析构
|
||||||
// ----------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
CamHandler::CamHandler(QObject *parent)
|
CamHandler::CamHandler(const QString &name,QObject *parent)
|
||||||
: QObject(parent), mDevice(nullptr), mStream(nullptr), mDeviceInfo(nullptr)
|
: QObject(parent)
|
||||||
|
, mDevice(nullptr)
|
||||||
|
, mStream(nullptr)
|
||||||
|
, mDeviceInfo(nullptr)
|
||||||
{
|
{
|
||||||
|
camName = name; // <<< 增加 camName 的初始化
|
||||||
state = false;
|
state = false;
|
||||||
saveFlag = false;
|
saveFlag = false;
|
||||||
// 连接定时器的超时信号到采集槽
|
// 连接定时器的超时信号到采集槽
|
||||||
@ -19,16 +23,14 @@ CamHandler::CamHandler(QObject *parent)
|
|||||||
CamHandler::~CamHandler()
|
CamHandler::~CamHandler()
|
||||||
{
|
{
|
||||||
// 确保在销毁对象时清理资源
|
// 确保在销毁对象时清理资源
|
||||||
if (mStream)
|
if (mStream) {
|
||||||
{
|
|
||||||
stopAcquisition(); // 确保流和缓冲区已清理
|
stopAcquisition(); // 确保流和缓冲区已清理
|
||||||
mStream->Close();
|
mStream->Close();
|
||||||
PvStream::Free(mStream);
|
PvStream::Free(mStream);
|
||||||
mStream = nullptr;
|
mStream = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mDevice)
|
if (mDevice) {
|
||||||
{
|
|
||||||
mDevice->Disconnect();
|
mDevice->Disconnect();
|
||||||
PvDevice::Free(mDevice);
|
PvDevice::Free(mDevice);
|
||||||
mDevice = nullptr;
|
mDevice = nullptr;
|
||||||
@ -53,13 +55,13 @@ QList<QString> CamHandler::listAvailableDevices()
|
|||||||
mSystem.Find();
|
mSystem.Find();
|
||||||
|
|
||||||
// 遍历发现的设备
|
// 遍历发现的设备
|
||||||
for (uint32_t i = 0; i < mSystem.GetDeviceCount(); i++)
|
for (uint32_t i = 0; i < mSystem.GetDeviceCount(); i++) {
|
||||||
{
|
|
||||||
const PvDeviceInfo *lInfo = mSystem.GetDeviceInfo(i);
|
const PvDeviceInfo *lInfo = mSystem.GetDeviceInfo(i);
|
||||||
// 获取设备的显示ID (例如:型号/序列号)
|
// 获取设备的显示ID (例如:型号/序列号)
|
||||||
QString lDisplayID = QString::fromLocal8Bit(lInfo->GetDisplayID().GetAscii());
|
QString lDisplayID = QString::fromLocal8Bit(lInfo->GetDisplayID().GetAscii());
|
||||||
|
|
||||||
deviceList.append(lDisplayID + " (ID: " + QString::fromLocal8Bit(lInfo->GetConnectionID().GetAscii()) + ")");
|
deviceList.append(lDisplayID + " (ID: "
|
||||||
|
+ QString::fromLocal8Bit(lInfo->GetConnectionID().GetAscii()) + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
return deviceList;
|
return deviceList;
|
||||||
@ -68,8 +70,7 @@ QList<QString> CamHandler::listAvailableDevices()
|
|||||||
bool CamHandler::connectToDevice(const QString &aConnectionID)
|
bool CamHandler::connectToDevice(const QString &aConnectionID)
|
||||||
{
|
{
|
||||||
QString errMsg;
|
QString errMsg;
|
||||||
if (mDevice)
|
if (mDevice) {
|
||||||
{
|
|
||||||
emit logMsg("Already connected. Please disconnect first.");
|
emit logMsg("Already connected. Please disconnect first.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -78,8 +79,7 @@ bool CamHandler::connectToDevice(const QString &aConnectionID)
|
|||||||
// 确保在 selectDevice 前调用 Find() (虽然 listAvailableDevices 可能已经调用,但再次调用更安全)
|
// 确保在 selectDevice 前调用 Find() (虽然 listAvailableDevices 可能已经调用,但再次调用更安全)
|
||||||
mDeviceInfo = selectDevice(aConnectionID);
|
mDeviceInfo = selectDevice(aConnectionID);
|
||||||
|
|
||||||
if (!mDeviceInfo)
|
if (!mDeviceInfo) {
|
||||||
{
|
|
||||||
errMsg = "Error: Device not found for ID:" + aConnectionID;
|
errMsg = "Error: Device not found for ID:" + aConnectionID;
|
||||||
emit logMsg(errMsg);
|
emit logMsg(errMsg);
|
||||||
return false;
|
return false;
|
||||||
@ -90,15 +90,14 @@ bool CamHandler::connectToDevice(const QString &aConnectionID)
|
|||||||
PvResult lResult;
|
PvResult lResult;
|
||||||
mDevice = PvDevice::CreateAndConnect(mDeviceInfo, &lResult);
|
mDevice = PvDevice::CreateAndConnect(mDeviceInfo, &lResult);
|
||||||
|
|
||||||
if (mDevice == nullptr)
|
if (mDevice == nullptr) {
|
||||||
{
|
emit logMsg("Error: Unable to connect to device. Result:"
|
||||||
emit logMsg( "Error: Unable to connect to device. Result:" + QString(lResult.GetCodeString()));
|
+ QString(lResult.GetCodeString()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 打开流
|
// 3. 打开流
|
||||||
if (!openStream())
|
if (!openStream()) {
|
||||||
{
|
|
||||||
disconnectDevice();
|
disconnectDevice();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -118,8 +117,7 @@ void CamHandler::startAcquisition()
|
|||||||
{
|
{
|
||||||
// 5. 创建和队列缓冲区
|
// 5. 创建和队列缓冲区
|
||||||
createStreamBuffers();
|
createStreamBuffers();
|
||||||
if (!mDevice || !mStream)
|
if (!mDevice || !mStream) {
|
||||||
{
|
|
||||||
emit logMsg("Not connected. Cannot start acquisition.");
|
emit logMsg("Not connected. Cannot start acquisition.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -128,17 +126,14 @@ void CamHandler::startAcquisition()
|
|||||||
PvGenParameterArray *lDeviceParams = mDevice->GetParameters();
|
PvGenParameterArray *lDeviceParams = mDevice->GetParameters();
|
||||||
PvGenCommand *lStart = dynamic_cast<PvGenCommand *>(lDeviceParams->Get("AcquisitionStart"));
|
PvGenCommand *lStart = dynamic_cast<PvGenCommand *>(lDeviceParams->Get("AcquisitionStart"));
|
||||||
|
|
||||||
if (lStart != nullptr)
|
if (lStart != nullptr) {
|
||||||
{
|
|
||||||
emit logMsg("Enabling streaming and sending AcquisitionStart command.");
|
emit logMsg("Enabling streaming and sending AcquisitionStart command.");
|
||||||
mDevice->StreamEnable(); // 启用流
|
mDevice->StreamEnable(); // 启用流
|
||||||
lStart->Execute(); // 发送命令
|
lStart->Execute(); // 发送命令
|
||||||
|
|
||||||
// 3. 启动定时器,驱动采集循环
|
// 3. 启动定时器,驱动采集循环
|
||||||
mAcquireTimer.start();
|
mAcquireTimer.start();
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
emit logMsg("Error: AcquisitionStart command not found on device.");
|
emit logMsg("Error: AcquisitionStart command not found on device.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,8 +144,7 @@ void CamHandler::stopAcquisition()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// 1. 停止计时器
|
// 1. 停止计时器
|
||||||
if (mAcquireTimer.isActive())
|
if (mAcquireTimer.isActive()) {
|
||||||
{
|
|
||||||
mAcquireTimer.stop();
|
mAcquireTimer.stop();
|
||||||
emit logMsg("Acquisition timer stopped.");
|
emit logMsg("Acquisition timer stopped.");
|
||||||
}
|
}
|
||||||
@ -160,8 +154,7 @@ void CamHandler::stopAcquisition()
|
|||||||
PvGenCommand *lStop = dynamic_cast<PvGenCommand *>(lDeviceParams->Get("AcquisitionStop"));
|
PvGenCommand *lStop = dynamic_cast<PvGenCommand *>(lDeviceParams->Get("AcquisitionStop"));
|
||||||
|
|
||||||
// 3. 停止采集和禁用流
|
// 3. 停止采集和禁用流
|
||||||
if (lStop != nullptr)
|
if (lStop != nullptr) {
|
||||||
{
|
|
||||||
emit logMsg("Sending AcquisitionStop command to the device");
|
emit logMsg("Sending AcquisitionStop command to the device");
|
||||||
lStop->Execute();
|
lStop->Execute();
|
||||||
}
|
}
|
||||||
@ -176,8 +169,7 @@ void CamHandler::stopAcquisition()
|
|||||||
PvBuffer *lBuffer = NULL;
|
PvBuffer *lBuffer = NULL;
|
||||||
PvResult lOperationResult;
|
PvResult lOperationResult;
|
||||||
// 必须清空队列,防止下次采集时使用错误的旧缓冲区
|
// 必须清空队列,防止下次采集时使用错误的旧缓冲区
|
||||||
while (mStream->GetQueuedBufferCount() > 0)
|
while (mStream->GetQueuedBufferCount() > 0) {
|
||||||
{
|
|
||||||
mStream->RetrieveBuffer(&lBuffer, &lOperationResult);
|
mStream->RetrieveBuffer(&lBuffer, &lOperationResult);
|
||||||
// 注意:这里没有释放 lBuffer,因为缓冲区资源将在析构或断开时处理
|
// 注意:这里没有释放 lBuffer,因为缓冲区资源将在析构或断开时处理
|
||||||
}
|
}
|
||||||
@ -188,8 +180,7 @@ void CamHandler::stopAcquisition()
|
|||||||
void CamHandler::disconnectDevice()
|
void CamHandler::disconnectDevice()
|
||||||
{
|
{
|
||||||
// 停止采集,清理流资源
|
// 停止采集,清理流资源
|
||||||
if (mStream)
|
if (mStream) {
|
||||||
{
|
|
||||||
stopAcquisition(); // 确保流和缓冲区已清理
|
stopAcquisition(); // 确保流和缓冲区已清理
|
||||||
emit logMsg("Closing stream");
|
emit logMsg("Closing stream");
|
||||||
mStream->Close();
|
mStream->Close();
|
||||||
@ -198,8 +189,7 @@ void CamHandler::disconnectDevice()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 断开设备连接,释放设备资源
|
// 断开设备连接,释放设备资源
|
||||||
if (mDevice)
|
if (mDevice) {
|
||||||
{
|
|
||||||
emit logMsg("Disconnecting device");
|
emit logMsg("Disconnecting device");
|
||||||
mDevice->Disconnect();
|
mDevice->Disconnect();
|
||||||
PvDevice::Free(mDevice);
|
PvDevice::Free(mDevice);
|
||||||
@ -231,38 +221,29 @@ void CamHandler::onAcquireTimerTimeout()
|
|||||||
// 尝试检索下一个缓冲区,使用较短的超时时间 (10ms)
|
// 尝试检索下一个缓冲区,使用较短的超时时间 (10ms)
|
||||||
PvResult lResult = mStream->RetrieveBuffer(&lBuffer, &lOperationResult, 10);
|
PvResult lResult = mStream->RetrieveBuffer(&lBuffer, &lOperationResult, 10);
|
||||||
|
|
||||||
if (lResult.IsOK())
|
if (lResult.IsOK()) {
|
||||||
{
|
if (lOperationResult.IsOK()) {
|
||||||
if (lOperationResult.IsOK())
|
|
||||||
{
|
|
||||||
// 成功采集到有效图像
|
// 成功采集到有效图像
|
||||||
PvPayloadType lType = lBuffer->GetPayloadType();
|
PvPayloadType lType = lBuffer->GetPayloadType();
|
||||||
|
|
||||||
if (lType == PvPayloadTypeImage)
|
if (lType == PvPayloadTypeImage) {
|
||||||
{
|
|
||||||
// 转换为 QImage 并发送信号
|
// 转换为 QImage 并发送信号
|
||||||
QImage image = convertPvBufferToQImage(lBuffer);
|
QImage image = convertPvBufferToQImage(lBuffer);
|
||||||
if (!image.isNull())
|
if (!image.isNull()) {
|
||||||
{
|
|
||||||
emit imageReady(image);
|
emit imageReady(image);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
emit logMsg("Image conversion failed.");
|
emit logMsg("Image conversion failed.");
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
emit logMsg("Buffer retrieved, but does not contain an image payload. Requeuing.");
|
emit logMsg("Buffer retrieved, but does not contain an image payload. Requeuing.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重新排队缓冲区
|
// 重新排队缓冲区
|
||||||
mStream->QueueBuffer(lBuffer);
|
mStream->QueueBuffer(lBuffer);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// 检索到缓冲区但操作结果不成功(例如:超时、重发过多)
|
// 检索到缓冲区但操作结果不成功(例如:超时、重发过多)
|
||||||
emit logMsg( "RetrieveBuffer operation result is not OK:" + QString(lOperationResult.GetCodeString()) + ". Requeuing.");
|
emit logMsg("RetrieveBuffer operation result is not OK:"
|
||||||
|
+ QString(lOperationResult.GetCodeString()) + ". Requeuing.");
|
||||||
mStream->QueueBuffer(lBuffer); // 必须重新排队
|
mStream->QueueBuffer(lBuffer); // 必须重新排队
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -278,13 +259,11 @@ const PvDeviceInfo *CamHandler::selectDevice(const QString &aConnectionID)
|
|||||||
{
|
{
|
||||||
mSystem.Find();
|
mSystem.Find();
|
||||||
// mSystem.Find() 已经在 connectToDevice 中调用
|
// mSystem.Find() 已经在 connectToDevice 中调用
|
||||||
for (uint32_t i = 0; i < mSystem.GetDeviceCount(); i++)
|
for (uint32_t i = 0; i < mSystem.GetDeviceCount(); i++) {
|
||||||
{
|
|
||||||
const PvDeviceInfo *lInfo = mSystem.GetDeviceInfo(i); // 从成员变量 mSystem 获取
|
const PvDeviceInfo *lInfo = mSystem.GetDeviceInfo(i); // 从成员变量 mSystem 获取
|
||||||
QString tmp = QString::fromLocal8Bit(lInfo->GetConnectionID().GetAscii());
|
QString tmp = QString::fromLocal8Bit(lInfo->GetConnectionID().GetAscii());
|
||||||
|
|
||||||
if (aConnectionID == tmp)
|
if (aConnectionID == tmp) {
|
||||||
{
|
|
||||||
// 找到匹配的设备,返回的指针由 mSystem 管理,在 CamHandler 生命周期内有效
|
// 找到匹配的设备,返回的指针由 mSystem 管理,在 CamHandler 生命周期内有效
|
||||||
return lInfo;
|
return lInfo;
|
||||||
}
|
}
|
||||||
@ -300,24 +279,26 @@ bool CamHandler::openStream()
|
|||||||
// 使用静态工厂方法 PvStream::CreateAndOpen
|
// 使用静态工厂方法 PvStream::CreateAndOpen
|
||||||
mStream = PvStream::CreateAndOpen(mDeviceInfo->GetConnectionID(), &lResult);
|
mStream = PvStream::CreateAndOpen(mDeviceInfo->GetConnectionID(), &lResult);
|
||||||
|
|
||||||
if (mStream == nullptr)
|
if (mStream == nullptr) {
|
||||||
{
|
emit logMsg("Error: Unable to stream from device. Result:"
|
||||||
emit logMsg( "Error: Unable to stream from device. Result:" + QString(lResult.GetCodeString()));
|
+ QString(lResult.GetCodeString()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CamHandler::createStreamBuffers()
|
void CamHandler::createStreamBuffers()
|
||||||
{
|
{
|
||||||
// 从设备获取 payload size (图像/数据的字节大小)
|
// 从设备获取 payload size (图像/数据的字节大小)
|
||||||
uint32_t lSize = mDevice->GetPayloadSize();
|
uint32_t lSize = mDevice->GetPayloadSize();
|
||||||
|
|
||||||
// 确定要创建的缓冲区数量
|
// 确定要创建的缓冲区数量
|
||||||
uint32_t lBufferCount = (mStream->GetQueuedBufferMaximum() < BUFFER_COUNT) ? mStream->GetQueuedBufferMaximum() : BUFFER_COUNT;
|
uint32_t lBufferCount = (mStream->GetQueuedBufferMaximum() < BUFFER_COUNT)
|
||||||
|
? mStream->GetQueuedBufferMaximum()
|
||||||
|
: BUFFER_COUNT;
|
||||||
|
|
||||||
emit logMsg( "Allocating" + QString::number(lBufferCount) + " buffers of size " + QString::number(lSize) + " bytes.");
|
emit logMsg("Allocating" + QString::number(lBufferCount) + " buffers of size "
|
||||||
|
+ QString::number(lSize) + " bytes.");
|
||||||
|
|
||||||
// 分配 PvBuffer 数组
|
// 分配 PvBuffer 数组
|
||||||
// 注意:在析构函数或 disconnectDevice 中需要释放这个内存!
|
// 注意:在析构函数或 disconnectDevice 中需要释放这个内存!
|
||||||
@ -326,8 +307,7 @@ void CamHandler::createStreamBuffers()
|
|||||||
// **重要提示:您的原始代码没有在析构函数中释放 lBuffers 指针!** // 这是一个内存泄漏风险。要解决这个问题,您需要将 lBuffers 存储为一个成员变量 (例如 QList<PvBuffer*>)
|
// **重要提示:您的原始代码没有在析构函数中释放 lBuffers 指针!** // 这是一个内存泄漏风险。要解决这个问题,您需要将 lBuffers 存储为一个成员变量 (例如 QList<PvBuffer*>)
|
||||||
// 或者只在析构函数/disconnectDevice 中确保释放它们。
|
// 或者只在析构函数/disconnectDevice 中确保释放它们。
|
||||||
|
|
||||||
for (uint32_t i = 0; i < lBufferCount; i++)
|
for (uint32_t i = 0; i < lBufferCount; i++) {
|
||||||
{
|
|
||||||
// 为每个缓冲区分配内存
|
// 为每个缓冲区分配内存
|
||||||
(lBuffers + i)->Alloc(static_cast<uint32_t>(lSize));
|
(lBuffers + i)->Alloc(static_cast<uint32_t>(lSize));
|
||||||
|
|
||||||
@ -343,38 +323,28 @@ QImage CamHandler::convertPvBufferToQImage(PvBuffer *aBuffer)
|
|||||||
uint32_t lWidth = lImage->GetWidth();
|
uint32_t lWidth = lImage->GetWidth();
|
||||||
uint32_t lHeight = lImage->GetHeight();
|
uint32_t lHeight = lImage->GetHeight();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 使用全局线程池启动任务 (异步执行)
|
// 使用全局线程池启动任务 (异步执行)
|
||||||
// 简化实现:仅支持 Mono8 (8位灰度) 格式
|
// 简化实现:仅支持 Mono8 (8位灰度) 格式
|
||||||
if (lImage->GetPixelType() == PvPixelMono8)
|
if (lImage->GetPixelType() == PvPixelMono8) {
|
||||||
{
|
|
||||||
if (saveFlag) {
|
if (saveFlag) {
|
||||||
const void *lDataPointer = lImage->GetDataPointer();
|
const void *lDataPointer = lImage->GetDataPointer();
|
||||||
quint32 lDataSize = lImage->GetImageSize();
|
quint32 lDataSize = lImage->GetImageSize();
|
||||||
// 3. 复制原始数据到 QByteArray(线程安全的关键)
|
// 3. 复制原始数据到 QByteArray(线程安全的关键)
|
||||||
QByteArray rawDataCopy(
|
QByteArray rawDataCopy(reinterpret_cast<const char *>(lDataPointer), lDataSize);
|
||||||
reinterpret_cast<const char*>(lDataPointer),
|
|
||||||
lDataSize
|
|
||||||
);
|
|
||||||
|
|
||||||
// 4. 创建 SaveTask 并提交给全局线程池
|
// 4. 创建 SaveTask 并提交给全局线程池
|
||||||
SaveTask *task = new SaveTask(
|
SaveTask *task = new SaveTask(rawDataCopy,camName);
|
||||||
rawDataCopy
|
|
||||||
);
|
|
||||||
|
|
||||||
QThreadPool::globalInstance()->start(task);
|
QThreadPool::globalInstance()->start(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 直接使用图像数据创建 QImage
|
// 直接使用图像数据创建 QImage
|
||||||
return QImage(
|
return QImage((uchar *) lImage->GetDataPointer(),
|
||||||
(uchar *)lImage->GetDataPointer(),
|
|
||||||
lWidth,
|
lWidth,
|
||||||
lHeight,
|
lHeight,
|
||||||
lWidth, // 步长/字节数
|
lWidth, // 步长/字节数
|
||||||
QImage::Format_Grayscale8
|
QImage::Format_Grayscale8)
|
||||||
).copy(); // .copy() 确保 QImage 拥有数据,安全地在 Qt 环境中使用
|
.copy(); // .copy() 确保 QImage 拥有数据,安全地在 Qt 环境中使用
|
||||||
}
|
}
|
||||||
|
|
||||||
emit logMsg("Unsupported pixel format");
|
emit logMsg("Unsupported pixel format");
|
||||||
|
|||||||
24
camhandler.h
24
camhandler.h
@ -1,25 +1,25 @@
|
|||||||
#ifndef CAMHANDLER_H
|
#ifndef CAMHANDLER_H
|
||||||
#define CAMHANDLER_H
|
#define CAMHANDLER_H
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QString>
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QImage> // 用于在信号中传输图像数据
|
#include <QImage> // 用于在信号中传输图像数据
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
#include <QTimer> // 用于驱动采集循环
|
#include <QTimer> // 用于驱动采集循环
|
||||||
|
|
||||||
// eBUS SDK 核心头文件
|
// eBUS SDK 核心头文件
|
||||||
#include <PvSystem.h>
|
#include <QThreadPool> // 需要包含 QThreadPool
|
||||||
|
#include "savetask.h"
|
||||||
|
#include <PvBuffer.h>
|
||||||
#include <PvDevice.h>
|
#include <PvDevice.h>
|
||||||
#include <PvDeviceU3V.h>
|
#include <PvDeviceU3V.h>
|
||||||
|
#include <PvGenCommand.h>
|
||||||
|
#include <PvGenParameter.h>
|
||||||
|
#include <PvGenParameterArray.h>
|
||||||
|
#include <PvImage.h>
|
||||||
#include <PvStream.h>
|
#include <PvStream.h>
|
||||||
#include <PvStreamU3V.h>
|
#include <PvStreamU3V.h>
|
||||||
#include <PvBuffer.h>
|
#include <PvSystem.h>
|
||||||
#include <PvImage.h>
|
|
||||||
#include <PvGenParameter.h>
|
|
||||||
#include <PvGenCommand.h>
|
|
||||||
#include <PvGenParameterArray.h>
|
|
||||||
#include "savetask.h"
|
|
||||||
#include <QThreadPool> // 需要包含 QThreadPool
|
|
||||||
|
|
||||||
// 用于流缓冲区数量的定义 (参考例程)
|
// 用于流缓冲区数量的定义 (参考例程)
|
||||||
#define BUFFER_COUNT 16
|
#define BUFFER_COUNT 16
|
||||||
@ -29,7 +29,7 @@ class CamHandler : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CamHandler(QObject *parent = nullptr);
|
explicit CamHandler(const QString &name,QObject *parent = nullptr);
|
||||||
~CamHandler();
|
~CamHandler();
|
||||||
|
|
||||||
// 公有接口:连接/断开/发现
|
// 公有接口:连接/断开/发现
|
||||||
@ -43,7 +43,7 @@ public:
|
|||||||
void stopAcquisition();
|
void stopAcquisition();
|
||||||
bool state;
|
bool state;
|
||||||
bool saveFlag;
|
bool saveFlag;
|
||||||
|
QString camName;
|
||||||
|
|
||||||
// ------------------- 信号 (Signals) -------------------
|
// ------------------- 信号 (Signals) -------------------
|
||||||
signals:
|
signals:
|
||||||
|
|||||||
@ -23,6 +23,4 @@ void DialogCamSet::on_pushButton_2_clicked()
|
|||||||
} else {
|
} else {
|
||||||
QMessageBox::information(this, "Success", "Connect " + portName + " Successfully");
|
QMessageBox::information(this, "Success", "Connect " + portName + " Successfully");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,8 +2,8 @@
|
|||||||
#define DIALOGCAMSET_H
|
#define DIALOGCAMSET_H
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include "parameterhandler.h"
|
|
||||||
#include "QMessageBox"
|
#include "QMessageBox"
|
||||||
|
#include "parameterhandler.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class DialogCamSet;
|
class DialogCamSet;
|
||||||
|
|||||||
@ -6,18 +6,16 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
, ui(new Ui::MainWindow)
|
, ui(new Ui::MainWindow)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
cam1 = new CamHandler();
|
cam1 = new CamHandler("cam1",this);
|
||||||
cam2 = new CamHandler();
|
cam2 = new CamHandler("cam2",this);
|
||||||
connect(cam1, &CamHandler::logMsg, this, &MainWindow::onLogPrint1);
|
connect(cam1, &CamHandler::logMsg, this, &MainWindow::onLogPrint1);
|
||||||
connect(cam1, &CamHandler::imageReady, this, &MainWindow::picUpdate1);
|
connect(cam1, &CamHandler::imageReady, this, &MainWindow::picUpdate1);
|
||||||
|
|
||||||
|
|
||||||
connect(cam2, &CamHandler::logMsg, this, &MainWindow::onLogPrint2);
|
connect(cam2, &CamHandler::logMsg, this, &MainWindow::onLogPrint2);
|
||||||
|
|
||||||
m_scene1 = new QGraphicsScene(this);
|
m_scene1 = new QGraphicsScene(this);
|
||||||
ui->graphicsView->setScene(m_scene1);
|
ui->graphicsView->setScene(m_scene1);
|
||||||
m_imageItem1 = nullptr; // 初始化图像项指针
|
m_imageItem1 = nullptr; // 初始化图像项指针
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
@ -30,7 +28,6 @@ void MainWindow::onLogPrint1(const QString &msg)
|
|||||||
QDateTime currentDateTime = QDateTime::currentDateTime();
|
QDateTime currentDateTime = QDateTime::currentDateTime();
|
||||||
QString dateTimeString = currentDateTime.toString("yyyy--MM--dd HH-mm-ss");
|
QString dateTimeString = currentDateTime.toString("yyyy--MM--dd HH-mm-ss");
|
||||||
ui->textBrowser->append(dateTimeString + " [cam1] --> " + msg);
|
ui->textBrowser->append(dateTimeString + " [cam1] --> " + msg);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onLogPrint2(const QString &msg)
|
void MainWindow::onLogPrint2(const QString &msg)
|
||||||
@ -38,14 +35,12 @@ void MainWindow::onLogPrint2(const QString &msg)
|
|||||||
QDateTime currentDateTime = QDateTime::currentDateTime();
|
QDateTime currentDateTime = QDateTime::currentDateTime();
|
||||||
QString dateTimeString = currentDateTime.toString("yyyy--MM--dd HH-mm-ss");
|
QString dateTimeString = currentDateTime.toString("yyyy--MM--dd HH-mm-ss");
|
||||||
ui->textBrowser->append(dateTimeString + " [cam2] --> " + msg);
|
ui->textBrowser->append(dateTimeString + " [cam2] --> " + msg);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::picUpdate1(const QImage &image)
|
void MainWindow::picUpdate1(const QImage &image)
|
||||||
{
|
{
|
||||||
// onLogPrint1("here rev");
|
// onLogPrint1("here rev");
|
||||||
if (image.isNull())
|
if (image.isNull()) {
|
||||||
{
|
|
||||||
qWarning() << "Received null image.";
|
qWarning() << "Received null image.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -57,8 +52,7 @@ void MainWindow::picUpdate1(const QImage &image)
|
|||||||
QPixmap pixmap = QPixmap::fromImage(image);
|
QPixmap pixmap = QPixmap::fromImage(image);
|
||||||
|
|
||||||
// 2. 将 QPixmap 放入 QGraphicsPixmapItem 中
|
// 2. 将 QPixmap 放入 QGraphicsPixmapItem 中
|
||||||
if (m_imageItem1 == nullptr)
|
if (m_imageItem1 == nullptr) {
|
||||||
{
|
|
||||||
// 第一次显示图像:创建 QGraphicsPixmapItem 并添加到 Scene
|
// 第一次显示图像:创建 QGraphicsPixmapItem 并添加到 Scene
|
||||||
m_imageItem1 = m_scene1->addPixmap(pixmap);
|
m_imageItem1 = m_scene1->addPixmap(pixmap);
|
||||||
|
|
||||||
@ -68,9 +62,7 @@ void MainWindow::picUpdate1(const QImage &image)
|
|||||||
// 可选:调整 QGraphicsView 视口以适应图像
|
// 可选:调整 QGraphicsView 视口以适应图像
|
||||||
// 如果您希望图像完整显示在 View 中,可以调用 fitInView
|
// 如果您希望图像完整显示在 View 中,可以调用 fitInView
|
||||||
ui->graphicsView->fitInView(m_scene1->sceneRect(), Qt::KeepAspectRatio);
|
ui->graphicsView->fitInView(m_scene1->sceneRect(), Qt::KeepAspectRatio);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// 更新现有图像(更高效):直接更新 QGraphicsPixmapItem 的 QPixmap
|
// 更新现有图像(更高效):直接更新 QGraphicsPixmapItem 的 QPixmap
|
||||||
m_imageItem1->setPixmap(pixmap);
|
m_imageItem1->setPixmap(pixmap);
|
||||||
}
|
}
|
||||||
@ -83,10 +75,8 @@ void MainWindow::on_pushButton_2_clicked()
|
|||||||
{
|
{
|
||||||
dc = new DialogCamSet(this);
|
dc = new DialogCamSet(this);
|
||||||
dc->show();
|
dc->show();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MainWindow::on_pushButton_3_clicked()
|
void MainWindow::on_pushButton_3_clicked()
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -107,13 +97,9 @@ void MainWindow::on_pushButton_3_clicked()
|
|||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// cam2->startAcquisition();
|
// cam2->startAcquisition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MainWindow::on_pushButton_4_clicked()
|
void MainWindow::on_pushButton_4_clicked()
|
||||||
{
|
{
|
||||||
if (cam1->state) {
|
if (cam1->state) {
|
||||||
@ -124,13 +110,11 @@ void MainWindow::on_pushButton_4_clicked()
|
|||||||
// cam2->stopAcquisition();
|
// cam2->stopAcquisition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MainWindow::on_pushButton_5_clicked()
|
void MainWindow::on_pushButton_5_clicked()
|
||||||
{
|
{
|
||||||
ui->textBrowser->clear();
|
ui->textBrowser->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MainWindow::on_pushButton_6_clicked()
|
void MainWindow::on_pushButton_6_clicked()
|
||||||
{
|
{
|
||||||
if (cam1->state) {
|
if (cam1->state) {
|
||||||
@ -140,20 +124,14 @@ void MainWindow::on_pushButton_6_clicked()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MainWindow::on_pushButton_clicked()
|
void MainWindow::on_pushButton_clicked()
|
||||||
{
|
{
|
||||||
cam1->saveFlag = true;
|
cam1->saveFlag = true;
|
||||||
onLogPrint1("start saving ...");
|
onLogPrint1("start saving ...");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MainWindow::on_pushButton_7_clicked()
|
void MainWindow::on_pushButton_7_clicked()
|
||||||
{
|
{
|
||||||
|
|
||||||
cam1->saveFlag = false;
|
cam1->saveFlag = false;
|
||||||
onLogPrint1("stop saving ...");
|
onLogPrint1("stop saving ...");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
mainwindow.h
13
mainwindow.h
@ -1,13 +1,12 @@
|
|||||||
#ifndef MAINWINDOW_H
|
#ifndef MAINWINDOW_H
|
||||||
#define MAINWINDOW_H
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
#include <QMainWindow>
|
|
||||||
#include "dialogcamset.h"
|
|
||||||
#include "camhandler.h"
|
|
||||||
#include "QDateTime"
|
|
||||||
#include <QGraphicsScene>
|
|
||||||
#include <QGraphicsPixmapItem>
|
#include <QGraphicsPixmapItem>
|
||||||
|
#include <QGraphicsScene>
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include "QDateTime"
|
||||||
|
#include "camhandler.h"
|
||||||
|
#include "dialogcamset.h"
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
@ -28,7 +27,6 @@ public slots:
|
|||||||
void onLogPrint2(const QString &msg);
|
void onLogPrint2(const QString &msg);
|
||||||
void picUpdate1(const QImage &image);
|
void picUpdate1(const QImage &image);
|
||||||
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_pushButton_2_clicked();
|
void on_pushButton_2_clicked();
|
||||||
|
|
||||||
@ -52,6 +50,5 @@ private:
|
|||||||
|
|
||||||
QGraphicsScene *m_scene1;
|
QGraphicsScene *m_scene1;
|
||||||
QGraphicsPixmapItem *m_imageItem1; // 用于保存图像项的指针
|
QGraphicsPixmapItem *m_imageItem1; // 用于保存图像项的指针
|
||||||
|
|
||||||
};
|
};
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
|||||||
@ -1,22 +1,23 @@
|
|||||||
#include "ParameterHandler.h"
|
#include "ParameterHandler.h"
|
||||||
#include <QDebug>
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
#include <QDebug>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
ParameterHandler::ParameterHandler(QObject *parent)
|
ParameterHandler::ParameterHandler(QObject *parent)
|
||||||
: QObject(parent), m_serialPort(new QSerialPort(this))
|
: QObject(parent)
|
||||||
|
, m_serialPort(new QSerialPort(this))
|
||||||
{
|
{
|
||||||
// 将 QSerialPort 的 readyRead 信号连接到槽函数,用于数据接收
|
// 将 QSerialPort 的 readyRead 信号连接到槽函数,用于数据接收
|
||||||
connect(m_serialPort, &QSerialPort::readyRead,
|
connect(m_serialPort, &QSerialPort::readyRead, this, &ParameterHandler::handleReadyRead);
|
||||||
this, &ParameterHandler::handleReadyRead);
|
|
||||||
|
|
||||||
// 将 QSerialPort 的 errorOccurred 信号连接到槽函数,用于错误处理
|
// 将 QSerialPort 的 errorOccurred 信号连接到槽函数,用于错误处理
|
||||||
// 使用 QOverload 确保连接到正确的重载函数
|
// 使用 QOverload 确保连接到正确的重载函数
|
||||||
connect(m_serialPort, QOverload<QSerialPort::SerialPortError>::of(&QSerialPort::errorOccurred),
|
connect(m_serialPort,
|
||||||
this, &ParameterHandler::handleError);
|
QOverload<QSerialPort::SerialPortError>::of(&QSerialPort::errorOccurred),
|
||||||
|
this,
|
||||||
|
&ParameterHandler::handleError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ParameterHandler::~ParameterHandler()
|
ParameterHandler::~ParameterHandler()
|
||||||
{
|
{
|
||||||
// 析构时确保串口关闭
|
// 析构时确保串口关闭
|
||||||
@ -51,7 +52,8 @@ bool ParameterHandler::openPort(const QString &portName)
|
|||||||
|
|
||||||
// 1. 尝试打开串口,以读写模式
|
// 1. 尝试打开串口,以读写模式
|
||||||
if (!m_serialPort->open(QIODevice::ReadWrite)) {
|
if (!m_serialPort->open(QIODevice::ReadWrite)) {
|
||||||
QString errorMsg = QString("无法打开串口 %1: %2").arg(portName).arg(m_serialPort->errorString());
|
QString errorMsg
|
||||||
|
= QString("无法打开串口 %1: %2").arg(portName).arg(m_serialPort->errorString());
|
||||||
qWarning() << errorMsg;
|
qWarning() << errorMsg;
|
||||||
emit errorOccurred(errorMsg);
|
emit errorOccurred(errorMsg);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
#ifndef PARAMETERHANDLER_H
|
#ifndef PARAMETERHANDLER_H
|
||||||
#define PARAMETERHANDLER_H
|
#define PARAMETERHANDLER_H
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QSerialPort>
|
#include <QSerialPort>
|
||||||
#include <QByteArray>
|
|
||||||
#include <QString>
|
|
||||||
#include <QSerialPortInfo> // <-- 增加此头文件
|
#include <QSerialPortInfo> // <-- 增加此头文件
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
class ParameterHandler : public QObject
|
class ParameterHandler : public QObject
|
||||||
{
|
{
|
||||||
|
|||||||
31
savetask.cpp
31
savetask.cpp
@ -1,30 +1,35 @@
|
|||||||
|
// SaveTask.cpp 文件中
|
||||||
|
|
||||||
#include "SaveTask.h"
|
#include "SaveTask.h"
|
||||||
|
|
||||||
// 实际的 run() 方法在 SaveTask.cpp 中实现
|
|
||||||
void SaveTask::run()
|
void SaveTask::run()
|
||||||
{
|
{
|
||||||
if (m_rawData.isEmpty())
|
if (m_rawData.isEmpty())
|
||||||
{
|
{
|
||||||
qWarning("SaveTask received empty raw data. Aborting save.");
|
qWarning("SaveTask for camera %s received empty raw data. Aborting save.", qPrintable(m_camName));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 耗时的文件 I/O 操作在此处执行,不阻塞主线程 ---
|
// --- 耗时的文件 I/O 操作在此处执行,在线程池中 ---
|
||||||
|
|
||||||
QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmsszzz");
|
QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmsszzz");
|
||||||
|
|
||||||
// 构造文件名:时间戳_宽度x高度_像素类型.raw
|
// 构造文件名:相机名称_时间戳.raw
|
||||||
QString filename = QString("%1.raw")
|
QString filename = QString("%1_%2.raw")
|
||||||
|
.arg(m_camName)
|
||||||
.arg(timestamp);
|
.arg(timestamp);
|
||||||
|
|
||||||
|
// 使用相机名称创建保存路径的子目录
|
||||||
// 设置保存路径
|
QString savePath = QDir::currentPath() + "/RawSavedImages/" + m_camName + "/"; // <<< 增加目录
|
||||||
QString savePath = QDir::currentPath() + "/RawSavedImages/";
|
|
||||||
|
|
||||||
QDir dir(savePath);
|
QDir dir(savePath);
|
||||||
if (!dir.exists())
|
if (!dir.exists())
|
||||||
{
|
{
|
||||||
dir.mkpath(".");
|
if (!dir.mkpath("."))
|
||||||
|
{
|
||||||
|
qWarning("Error: Failed to create save directory: %s", qPrintable(savePath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString fullPath = savePath + filename;
|
QString fullPath = savePath + filename;
|
||||||
@ -36,16 +41,12 @@ void SaveTask::run()
|
|||||||
qint64 bytesWritten = file.write(m_rawData);
|
qint64 bytesWritten = file.write(m_rawData);
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
if (bytesWritten == m_rawData.size())
|
if (bytesWritten != m_rawData.size())
|
||||||
{
|
|
||||||
// 保存成功,可以发送日志或简单地使用 qInfo()
|
|
||||||
// qInfo() << "Raw data saved successfully to:" << fullPath;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
qWarning() << QString("Error: Only %1 of %2 bytes written to %3")
|
qWarning() << QString("Error: Only %1 of %2 bytes written to %3")
|
||||||
.arg(bytesWritten).arg(m_rawData.size()).arg(fullPath);
|
.arg(bytesWritten).arg(m_rawData.size()).arg(fullPath);
|
||||||
}
|
}
|
||||||
|
// else { qInfo() << "Raw data saved successfully to:" << fullPath; }
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
10
savetask.h
10
savetask.h
@ -1,12 +1,12 @@
|
|||||||
#ifndef SAVETASK_H
|
#ifndef SAVETASK_H
|
||||||
#define SAVETASK_H
|
#define SAVETASK_H
|
||||||
|
|
||||||
#include <QRunnable>
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QDebug>
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QRunnable>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief SaveTask 封装了将原始图像字节数据异步保存到文件的任务。
|
* @brief SaveTask 封装了将原始图像字节数据异步保存到文件的任务。
|
||||||
@ -22,8 +22,8 @@ public:
|
|||||||
* @param height 图像高度。
|
* @param height 图像高度。
|
||||||
* @param pixelType 图像的原始像素格式(例如 PvPixelMono8 的整数值)。
|
* @param pixelType 图像的原始像素格式(例如 PvPixelMono8 的整数值)。
|
||||||
*/
|
*/
|
||||||
SaveTask(const QByteArray &data)
|
SaveTask(const QByteArray &data, const QString &camName)
|
||||||
: m_rawData(data)
|
: m_rawData(data), m_camName(camName) // <<< 增加 camName 的初始化
|
||||||
{
|
{
|
||||||
// 设置为 true,以便任务执行完成后,QThreadPool 自动清理内存
|
// 设置为 true,以便任务执行完成后,QThreadPool 自动清理内存
|
||||||
setAutoDelete(true);
|
setAutoDelete(true);
|
||||||
@ -36,7 +36,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QByteArray m_rawData;
|
QByteArray m_rawData;
|
||||||
|
QString m_camName; // <<< 增加成员变量来存储相机名称
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SAVETASK_H
|
#endif // SAVETASK_H
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user