23 Commits

Author SHA1 Message Date
5372a7806c add,计划采集15:
优化定时采集:界面和模型
2026-06-18 10:09:38 +08:00
59abab3f5d add,计划采集14:
修改节点名
2026-06-17 11:24:24 +08:00
fc3853c3ca add,计划采集13:
优化逻辑
2026-06-16 15:31:06 +08:00
d9f1ed922b add,计划采集12:
在界面中显示任务和其子任务,并实时更新它们的状态;
2026-06-16 13:25:43 +08:00
8329c00165 add,计划采集11:
1、完善定时采集流程信息统计:时间;
2、添加状态:跳过;
2026-06-12 10:37:54 +08:00
f69edcf2c9 add,计划采集10:
解决偶发单反唤起失败问题;

任务文件要求:
1、高光谱必须在前面,没有顺序要求,因为在执行高光谱任务前会预热卤素灯,所有高光谱任务完成后才关闭卤素灯;
2、单反/深度必须在后面,没有顺序要求;
3、必须要有高光谱任务:单反存在唤醒程序和一定反应时间,所以必须依赖前一个任务进行唤醒;
 (1)当单反已经是唤醒状态时,重新上电(先下电后上电)后可马上重新唤醒单反:单反适配器剩余电量马上耗尽,所以才能马上重新唤醒单反;
 (2)当单反在上电的状态下,从唤醒状态自动进入休眠状态时,重新下电后需要等待2分15秒(135秒)后,再重新上电才能唤醒单反:等待135秒的目的是将适配器中电容的剩余电量耗尽;
2026-06-11 15:27:02 +08:00
509f4b0767 add,计划采集9:
基本计划采集功能:采集流程电源通断控制ok
2026-06-10 18:10:47 +08:00
4a62d9a007 add,计划采集8:
实现部分计划采集功能:电源通断控制
2026-06-09 11:08:02 +08:00
467bebe9dd add,计划采集7:
实现部分计划采集功能:定时任务可完整执行
2026-06-05 11:36:38 +08:00
41a1a938b9 add,计划采集6:
实现部分计划采集功能:定时任务可控制单反
2026-06-04 18:01:22 +08:00
3607913f13 add,计划采集5:
实现部分计划采集功能:
2026-06-04 16:41:51 +08:00
a8760652bd add,计划采集3:
实现部分计划采集功能
2026-06-03 15:04:03 +08:00
3521a7f225 add,计划采集2:
添加计划任务的数据结构,并实现读写数据结构
2026-06-02 18:00:01 +08:00
6111634eff add,计划采集1:
添加窗口
2026-06-02 14:38:56 +08:00
4d42314a84 修改:
分离TwoMotionCaptureCoordinator和ImagerOperationBase
2026-06-01 17:10:37 +08:00
6456232114 add,单反相机
1、分离功能:单反相机的实时视频流和拍照;
2、将视频流和拍照功能状态反馈到界面上,并做简单控制;
2026-05-28 13:51:15 +08:00
525b39851a 配置release 2026-05-28 10:49:15 +08:00
5f965f0d8e fix
兼容2种帧数显示逻辑:(1)采集时的实时帧数(2)打开影像时的帧数;
2026-05-28 09:27:25 +08:00
3568495aa9 add:
深度相机:改变数据保存路径;
2026-05-27 22:58:41 +08:00
410da482bc 采集过程中禁用图层树的选择功能 2026-05-27 22:37:54 +08:00
43acd5ba01 fix:
1、采集时,创建RasterImageLayer时,dataprovider延迟初始化;
2、采集时,不通过渲染器渲染;
2026-05-27 17:42:14 +08:00
5dc589aee0 add,单反相机2:
1、实现3s拍照;
2、设置照片保存路径;
3、轮播看板:显示单反实时视频流;
2026-05-27 16:03:16 +08:00
e8ae6aa3b9 add
单反相机:实现1s拍照,并将照片传回控制电脑
2026-05-26 14:54:50 +08:00
42 changed files with 5375 additions and 326 deletions

1
.gitignore vendored
View File

@ -8,6 +8,7 @@ HPPA - 副本.ui
icon
ignore_*
resources
*.bkp
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.

View File

@ -1,10 +1,13 @@
#include "AppSettings.h"
#include <QCoreApplication>
const QString AppSettings::kDefaultDataFolder = QStringLiteral("C:\\HPPA_image");
const QString AppSettings::kDefaultFileName = QStringLiteral("test_image");
const int AppSettings::kDefaultFrameRate = 20;
const int AppSettings::kDefaultIntegrationTime = 1;
const int AppSettings::kDefaultGain = 0;
const QString AppSettings::kDefaultSLRDataFolder = QString();
const QString AppSettings::kDefaultDepthCameraDataFolder = QString();
AppSettings::AppSettings()
: m_settings(QSettings::IniFormat, QSettings::UserScope,
@ -32,6 +35,7 @@ QString AppSettings::fileName() const
{
return m_settings.value("General/FileName", kDefaultFileName).toString();
}
void AppSettings::setFileName(const QString& path)
{
m_settings.setValue("General/FileName", path);
@ -41,6 +45,7 @@ int AppSettings::frameRate() const
{
return m_settings.value("CameraParams/FrameRate", kDefaultFrameRate).toInt();
}
void AppSettings::setFrameRate(int value)
{
m_settings.setValue("CameraParams/FrameRate", value);
@ -50,6 +55,7 @@ int AppSettings::integrationTime() const
{
return m_settings.value("CameraParams/IntegrationTime", kDefaultIntegrationTime).toInt();
}
void AppSettings::setIntegrationTime(int value)
{
m_settings.setValue("CameraParams/IntegrationTime", value);
@ -59,7 +65,38 @@ int AppSettings::gain() const
{
return m_settings.value("CameraParams/Gain", kDefaultGain).toInt();
}
void AppSettings::setGain(int value)
{
m_settings.setValue("CameraParams/Gain", value);
}
QString AppSettings::slrDataFolder() const
{
QString path = m_settings.value("General/SLRDataFolder").toString();
if (path.isEmpty())
{
return QCoreApplication::applicationDirPath() + "/CapturedImages/";
}
return path;
}
void AppSettings::setSlrDataFolder(const QString& path)
{
m_settings.setValue("General/SLRDataFolder", path);
}
QString AppSettings::depthCameraDataFolder() const
{
QString path = m_settings.value("General/DepthCameraDataFolder").toString();
if (path.isEmpty())
{
return QCoreApplication::applicationDirPath() + "/CapturedDepthImages/";
}
return path;
}
void AppSettings::setDepthCameraDataFolder(const QString& path)
{
m_settings.setValue("General/DepthCameraDataFolder", path);
}

View File

@ -26,6 +26,14 @@ public:
// 增益
int gain() const;
void setGain(int value);
// 单反相机数据保存路径
QString slrDataFolder() const;
void setSlrDataFolder(const QString& path);
// 深度相机数据保存路径
QString depthCameraDataFolder() const;
void setDepthCameraDataFolder(const QString& path);
// 在此处添加更多参数的 getter/setter ...
private:
@ -41,4 +49,6 @@ private:
static const int kDefaultFrameRate;
static const int kDefaultIntegrationTime;
static const int kDefaultGain;
static const QString kDefaultSLRDataFolder;
static const QString kDefaultDepthCameraDataFolder;
};

View File

@ -2,12 +2,11 @@
TwoMotionCaptureCoordinator::TwoMotionCaptureCoordinator(
IrisMultiMotorController* motorCtrl,
ImagerOperationBase* cameraCtrl,
QObject* parent)
: QObject(parent)
, m_motorCtrl(motorCtrl)
, m_cameraCtrl(cameraCtrl)
, m_isRunning(false)
, m_isValidCapturing(false)
{
//这些信号槽是按照逻辑顺序的
connect(this, SIGNAL(moveTo(int, double, double, int)),
@ -20,35 +19,6 @@ TwoMotionCaptureCoordinator::TwoMotionCaptureCoordinator(
this, &TwoMotionCaptureCoordinator::handlePositionReached);
//connect(m_motorCtrl, &IrisMultiMotorController::moveFailed,
// this, &TwoMotionCaptureCoordinator::handleError);
connect(this, &TwoMotionCaptureCoordinator::startRecordHSISignal,
m_cameraCtrl, &ImagerOperationBase::start_record);
connect(this, &TwoMotionCaptureCoordinator::stopRecordHSISignal,
m_cameraCtrl, &ImagerOperationBase::stop_record);
connect(m_cameraCtrl, &ImagerOperationBase::RecordFinishedSignal_WhenFrameNumberMeet,
this, &TwoMotionCaptureCoordinator::handleCaptureCompleteWhenFrameNumberMeet);
//connect(m_cameraCtrl, &ImagerOperationBase::RecordFinishedSignal_WhenFrameNumberNotMeet,
// this, &TwoMotionCaptureCoordinator::handleCaptureCompleteWhenFrameNumberNotMeet);
//connect(m_cameraCtrl, &ImagerOperationBase::captureFailed,
// this, &TwoMotionCaptureCoordinator::handleError);
}
TwoMotionCaptureCoordinator::TwoMotionCaptureCoordinator(
IrisMultiMotorController* motorCtrl,
QObject* parent)
: QObject(parent)
, m_motorCtrl(motorCtrl)
, m_isRunning(false)
{
connect(this, SIGNAL(moveTo(int, double, double, int)),
m_motorCtrl, SLOT(moveTo(int, double, double, int)));
connect(this, SIGNAL(moveTo(const std::vector<double>, const std::vector<double>, int)),
m_motorCtrl, SLOT(moveTo(const std::vector<double>, const std::vector<double>, int)));
connect(m_motorCtrl, &IrisMultiMotorController::motorStopSignal,
this, &TwoMotionCaptureCoordinator::handlePositionReached);
//connect(m_motorCtrl, &IrisMultiMotorController::moveFailed,
// this, &TwoMotionCaptureCoordinator::handleError);
}
TwoMotionCaptureCoordinator::~TwoMotionCaptureCoordinator()
@ -98,11 +68,7 @@ void TwoMotionCaptureCoordinator::stop()
savePathLinesToCsv();
emit finishRecordLineNumSignal(m_numCurrentPathLine);
//emit stopRecordHSISignal(m_numCurrentPathLine);
if (m_cameraCtrl != nullptr)
{
m_cameraCtrl->stop_record();
}
emit stopRecordHSISignal(m_numCurrentPathLine);
move2LocBeforeStart();
@ -121,6 +87,7 @@ void TwoMotionCaptureCoordinator::getLocBeforeStart()
QMetaObject::Connection conn = QObject::connect(m_motorCtrl, &IrisMultiMotorController::locationSignal,
[&](std::vector<double> pos) {
m_locBeforeStart = pos;
std::cout << "start pos: "<< pos[0] <<", " << pos[1] << std::endl;
received = true;
loop.quit();
});
@ -148,7 +115,7 @@ void TwoMotionCaptureCoordinator::move2LocBeforeStart()
speed.push_back(tmp.speedTargetYPosition);
emit moveTo(m_locBeforeStart, speed, 1000);
m_isRunning = false;
m_isValidCapturing = false;
m_isMoving2XMin = false;
m_isMoving2XMax = false;
@ -230,12 +197,12 @@ void TwoMotionCaptureCoordinator::handlePositionReached(int motorID, double pos)
//QMutexLocker locker(&m_dataMutex);
PathLine &tmp = m_pathLines[m_numCurrentPathLine];
if (motorID == 1)//y马达
{
if (m_isMoving2YTargeLoc)
{
PathLine& tmp = m_pathLines[m_numCurrentPathLine];
double threshold = getThre(tmp.targetYPosition, pos);
if (threshold > 5)
@ -265,6 +232,7 @@ void TwoMotionCaptureCoordinator::handlePositionReached(int motorID, double pos)
if (m_isMoving2YStartLoc)
{
m_isMoving2YStartLoc = false;
isBack2Origin();
return;
}
@ -273,6 +241,8 @@ void TwoMotionCaptureCoordinator::handlePositionReached(int motorID, double pos)
{
if (m_isMoving2XMin)
{
PathLine& tmp = m_pathLines[m_numCurrentPathLine];
double threshold = getThre(tmp.targetXMinPosition, pos);
if (threshold > 5)
@ -301,6 +271,8 @@ void TwoMotionCaptureCoordinator::handlePositionReached(int motorID, double pos)
if (m_isMoving2XMax)
{
PathLine& tmp = m_pathLines[m_numCurrentPathLine];
double threshold = getThre(tmp.targetXMaxPosition, pos);
if (threshold > 5 && !m_isImagerFrameNumberMeet)//马达没到准确位置 && 【非】光谱仪因帧数限制主动停止采集
@ -324,11 +296,7 @@ void TwoMotionCaptureCoordinator::handlePositionReached(int motorID, double pos)
//停止采集高光谱数据
emit finishRecordLineNumSignal(m_numCurrentPathLine);
//emit stopRecordHSISignal(m_numCurrentPathLine);
if (m_cameraCtrl!=nullptr)
{
m_cameraCtrl->stop_record();
}
emit stopRecordHSISignal(m_numCurrentPathLine);
m_isMoving2XMax = false;
m_isImagerFrameNumberMeet = false;
@ -341,6 +309,7 @@ void TwoMotionCaptureCoordinator::handlePositionReached(int motorID, double pos)
if (m_isMoving2XStartLoc)
{
m_isMoving2XStartLoc = false;
isBack2Origin();
return;
}
@ -380,9 +349,25 @@ void TwoMotionCaptureCoordinator::startRecordHsi()
emit moveTo(0, tmp.targetXMaxPosition, tmp.speedTargetXMaxPosition, 1000);
emit startRecordHSISignal(m_numCurrentPathLine);
emit startRecordLineNumSignal(m_numCurrentPathLine);
}
}
void TwoMotionCaptureCoordinator::isBack2Origin()
{
if (!m_isRunning) return;
//QMutexLocker locker(&m_dataMutex);
if (!m_isMoving2XStartLoc && !m_isMoving2YStartLoc)
{
m_isRunning = false;
emit back2OriginSignal();
}
}
void TwoMotionCaptureCoordinator::handleCaptureCompleteWhenFrameNumberMeet()
{
m_isImagerFrameNumberMeet = true;
@ -426,7 +411,8 @@ void TwoMotionCaptureCoordinator::processNextPathLine()
}
std::cout << "\nNew path line: " << m_numCurrentPathLine << std::endl;
emit startRecordLineNumSignal(m_numCurrentPathLine);
emit gotoRecordLineNumSignal(m_numCurrentPathLine);
m_isValidCapturing = true;
PathLine &tmp = m_pathLines[m_numCurrentPathLine];
tmp.timestamp1 = QDateTime::currentDateTime();

View File

@ -38,9 +38,6 @@ class TwoMotionCaptureCoordinator : public QObject
{
Q_OBJECT
public:
TwoMotionCaptureCoordinator(IrisMultiMotorController* motorCtrl,
ImagerOperationBase* cameraCtrl,
QObject* parent = nullptr);
TwoMotionCaptureCoordinator(IrisMultiMotorController* motorCtrl,
QObject* parent = nullptr);
~TwoMotionCaptureCoordinator();
@ -49,6 +46,8 @@ public:
signals:
void sequenceComplete(int);//0所有采集线正常运行完成1用户主动取消采集
void back2OriginSignal();
void gotoRecordLineNumSignal(int lineNum);
void startRecordLineNumSignal(int lineNum);
void finishRecordLineNumSignal(int lineNum);
@ -62,13 +61,15 @@ signals:
void recordState(bool state);
public slots:
void handleCaptureCompleteWhenFrameNumberMeet();
private slots:
void start(QVector<PathLine> pathLines);
void stop();
void getRecordState();
void handlePositionReached(int motorID, double pos);
void handleCaptureCompleteWhenFrameNumberMeet();
void handleError(const QString& error);
void move2LocBeforeStart();
@ -76,6 +77,7 @@ private slots:
private:
void processNextPathLine();
void startRecordHsi();
void isBack2Origin();
void getLocBeforeStart();
double getThre(double targetLoc, double actualLoc);
@ -88,6 +90,7 @@ private:
mutable QMutex m_dataMutex;
bool m_isRunning;
bool m_isValidCapturing;
bool m_isMoving2YTargeLoc;
bool m_isMoving2XMin;
bool m_isMoving2XMax;

View File

@ -0,0 +1,14 @@
#include "CommunicationInterfaceBase.h"
using namespace MotorParams;
CommunicationInterfaceBase::CommunicationInterfaceBase(QObject* parent)
:QObject(parent)
{
}
CommunicationInterfaceBase::~CommunicationInterfaceBase()
{
}

View File

@ -0,0 +1,36 @@
#pragma once
#include <QObject>
#include <QString>
#include "motorParams.h"
namespace MotorParams {
struct TCPConnectionParams
{
QString serverIP;
int port;
};
class CommunicationInterfaceBase :public QObject
{
Q_OBJECT
public:
CommunicationInterfaceBase(QObject* parent = nullptr);
~CommunicationInterfaceBase();
virtual bool connect2Motor() = 0;
//virtual void disconnect() = 0;
//virtual bool isConnected() const = 0;
virtual int sendCommand(const QString command) = 0;
//virtual void sendCommandAsync(const QString& command) = 0;
virtual int recvData(QByteArray& dataRecv) = 0;
signals:
void dataReceived(const QByteArray& data);
void connected(); // <20><><EFBFBD>ܴ<EFBFBD><DCB4>ڻ<EFBFBD><DABB><EFBFBD>TCP<43><50><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E1B7A2><EFBFBD><EFBFBD><EFBFBD>ź<EFBFBD>
void disconnected();
void errorOccurred(QString msg);
};
} // namespace MotorParams

View File

@ -0,0 +1,95 @@
#include "CommunicationViaTCP.h"
using namespace MotorParams;
CommunicationViaTCP::CommunicationViaTCP(MotorParams::TCPConnectionParams connectionParams, QObject* parent)
:MotorParams::CommunicationInterfaceBase(parent)
{
m_bConnected = false;
m_tcpServer = new QTcpServer(this);
connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
m_tcpServer->listen(QHostAddress::Any, connectionParams.port);
}
CommunicationViaTCP::~CommunicationViaTCP()
{
m_tcpServer->close();
delete m_tcpServer;
//这两行代码要报错,为啥呢?????????????????
//m_tcpSocket->disconnectFromHost();
//delete m_tcpSocket;
}
bool CommunicationViaTCP::connect2Motor()
{
return true;
}
void CommunicationViaTCP::onNewConnection()
{
m_bConnected = true;
m_tcpSocket = m_tcpServer->nextPendingConnection();
connect(m_tcpSocket, SIGNAL(disconnected()), this, SLOT(onTcpSocketDisconnected()));
emit connected();
}
bool CommunicationViaTCP::isConnected() const
{
return m_bConnected;
}
//从拔掉客户端的电源客户端m_tcpSocket断开连接到这个函数被调用有延迟所以这个函数调用也有延迟导致拔掉电源后的一小段时间函数isConnected()还是返回true
void CommunicationViaTCP::onTcpSocketDisconnected()
{
int a = 1;
m_bConnected = false;
m_tcpSocket->deleteLater();
}
int CommunicationViaTCP::sendCommand(const QString cmd)
{
if (!isConnected()) {
QString error = "No client connected";
emit commandSendResult(-1, error);
qWarning() << error << "command:" << cmd;
return -1;
}
qint64 bytesWritten = m_tcpSocket->write(cmd.toUtf8().data());
m_tcpSocket->waitForBytesWritten(50);
return bytesWritten;
}
int CommunicationViaTCP::recvData(QByteArray& dataRecv)
{
if (!isConnected()) {
QString error = "No client connected";
return -1;
}
dataRecv.clear();
QByteArray temp;
temp = m_tcpSocket->readAll();
dataRecv.append(temp);
int counter = 0;
while (dataRecv.size() < 21)
{
counter++;
m_tcpSocket->waitForReadyRead(100);
temp = m_tcpSocket->readAll();
dataRecv.append(temp);
if (counter >= 5)
break;
}
//qDebug() << "Hex:" << dataRecv.toHex();
return dataRecv.size();
}

View File

@ -0,0 +1,45 @@
#pragma once
#include <QObject>
#include <QThread>
#include <QDebug>
#include <QTcpServer>
#include <QTcpSocket>
#include "CommunicationInterfaceBase.h"
namespace MotorParams {
class CommunicationViaTCP :
public CommunicationInterfaceBase
{
Q_OBJECT
public:
CommunicationViaTCP(TCPConnectionParams connectionParams, QObject* parent = nullptr);
~CommunicationViaTCP();
//继承基类
bool connect2Motor();
//void disconnect();
//bool isConnected() const;
int sendCommand(const QString cmd);
//void sendCommandAsync(const QString& command);
int recvData(QByteArray& dataRecv);
private:
QTcpServer* m_tcpServer;
QTcpSocket* m_tcpSocket;
int m_iCommunicationProtocol;
bool m_bConnected;
bool isConnected() const;
public Q_SLOTS:
void onNewConnection();
void onTcpSocketDisconnected();
signals:
void commandSendResult(int bytesWritten, const QString& error = QString());
};
}

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>416</width>
<height>219</height>
<width>857</width>
<height>477</height>
</rect>
</property>
<property name="sizePolicy">
@ -83,19 +83,6 @@ QPushButton:pressed
</property>
</spacer>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>135</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
@ -126,7 +113,20 @@ QPushButton:pressed
</item>
</layout>
</item>
<item row="1" column="2">
<item row="2" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>135</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -139,7 +139,52 @@ QPushButton:pressed
</property>
</spacer>
</item>
<item row="2" column="1">
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="styleSheet">
<string notr="true">QLabel {
color: rgb(255, 255, 255);
}</string>
</property>
<property name="text">
<string>数据路径</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="dataFolderLineEdit">
<property name="styleSheet">
<string notr="true">QLineEdit {
background-color: #142D7F;
color: #e6eeff;
border: 1px solid #2f6bff;
border-radius: 6px;
padding: 4px 8px;
min-width: 70px;
min-height: 20px;
font-size: 13px;
}</string>
</property>
<property name="text">
<string>./CapturedImages</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="dataFolderBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="1">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>

View File

@ -20,6 +20,11 @@ DepthCameraWindow::DepthCameraWindow(QWidget* parent)
connect(m_DepthCameraOperation, &DepthCameraOperation::PlotSignal, this, &DepthCameraWindow::PlotDepthImageSignal);
connect(m_DepthCameraOperation, &DepthCameraOperation::CamClosedSignal, this, &DepthCameraWindow::DepthCamClosedSignal);
connect(this->ui.dataFolderBtn, SIGNAL(clicked()), this, SLOT(onSelectDataFolder()));
// 初始化数据保存路径显示(从 AppSettings 恢复或使用默认)
ui.dataFolderLineEdit->setText(AppSettings::instance().depthCameraDataFolder());
}
DepthCameraWindow::~DepthCameraWindow()
@ -30,6 +35,29 @@ DepthCameraWindow::~DepthCameraWindow()
m_DepthCameraOperation = nullptr;
}
void DepthCameraWindow::onSelectDataFolder()
{
QString dir = QFileDialog::getExistingDirectory(this,
QString::fromLocal8Bit("选择数据保存路径"),
ui.dataFolderLineEdit->text());
setDataFolder(dir);
}
void DepthCameraWindow::setDataFolder(QString dir)
{
if (!dir.isEmpty())
{
ui.dataFolderLineEdit->setText(dir);
AppSettings::instance().setDepthCameraDataFolder(dir);
}
}
void DepthCameraWindow::setCaptureInterval(int captureIntervalSeconds)
{
m_DepthCameraOperation->setCaptureInterval(captureIntervalSeconds);
}
void DepthCameraWindow::openDepthCamera()
{
if (!m_DepthCameraOperation->getRecordStatus())
@ -65,6 +93,8 @@ DepthCameraOperation::DepthCameraOperation()
m_pipe = nullptr;
m_func = nullptr;
record = false;
m_captureIntervalMilliseconds = 3000;
}
DepthCameraOperation::~DepthCameraOperation()
@ -78,6 +108,11 @@ DepthCameraOperation::~DepthCameraOperation()
}
}
void DepthCameraOperation::setCaptureInterval(int captureIntervalSeconds)
{
m_captureIntervalMilliseconds = captureIntervalSeconds * 1000;
}
void DepthCameraOperation::OpenDepthCamera()
{
if (m_pipe)
@ -124,14 +159,14 @@ void DepthCameraOperation::OpenDepthCamera()
// Drop several frames
for (int i = 0; i < 15; ++i) {
auto lost = m_pipe->waitForFrameset(100);
auto lost = m_pipe->waitForFrameset(m_captureIntervalMilliseconds);
}
auto pointCloud = std::make_shared<ob::PointCloudFilter>();
int frameIndex = 0;
record = true;
QString fileNamePrefix = AppSettings::instance().dataFolder() + QDir::separator() + AppSettings::instance().fileName();
QString fileNamePrefix = AppSettings::instance().depthCameraDataFolder() + QDir::separator() + "Gemini336L";
QString imuFilePath = fileNamePrefix + "_IMU.txt";
std::ofstream imuFile(imuFilePath.toStdString(), std::ios::out | std::ios::trunc);
while (record)
@ -142,7 +177,7 @@ void DepthCameraOperation::OpenDepthCamera()
std::cout << "Start recording..." << std::endl;
}
auto frameSet = m_pipe->waitForFrameset(100);
auto frameSet = m_pipe->waitForFrameset(m_captureIntervalMilliseconds);
if (frameSet == nullptr)
{
std::cout << "No frames received in 100ms..." << std::endl;
@ -200,7 +235,7 @@ void DepthCameraOperation::OpenDepthCamera()
pointCloud->setCreatePointFormat(OB_FORMAT_RGB_POINT);
std::shared_ptr<ob::Frame> frame = pointCloud->process(frameSet);
QString plyPath = fileNamePrefix + "_"+ QString::number(frameIndex) + ".ply";
QString plyPath = fileNamePrefix + "_PointCloud_"+ QString::number(frameIndex) + ".ply";
ob::PointCloudHelper::savePointcloudToPly(plyPath.toStdString().c_str(), frame, false, false, 50);
//惯导数据

View File

@ -7,6 +7,8 @@
#include <QImage>
#include <Qthread>
#include <QDir>
//#include <QLabel>
#include <QFileDialog>
#include <iostream>
#include "ui_DepthCamera.h"
@ -31,6 +33,8 @@ public:
void setCallback(void(*func)());
bool getRecordStatus() const { return record; }
void setCaptureInterval(int captureIntervalSeconds);
private:
ob::Pipeline* m_pipe;
cv::Mat frame;
@ -44,6 +48,8 @@ private:
bool record;
int m_captureIntervalMilliseconds;
public slots:
void OpenDepthCamera();
void OpenDepthCamera_callback();//不使用信号而使用回调函数来通知界面刷新视频
@ -66,12 +72,17 @@ public:
DepthCameraOperation* m_DepthCameraOperation;
void setDataFolder(QString dir);
void setCaptureInterval(int captureIntervalSeconds);
public Q_SLOTS:
void openDepthCamera();
void onCamOpened();
void closeDepthCamera();
void onCamClosed();
void onSelectDataFolder();
signals:
void openDepthCameraSignal();
void PlotDepthImageSignal();

View File

@ -385,6 +385,7 @@ HPPA::HPPA(QWidget* parent)
m_carousel = new MyCarousel();
m_carousel->setObjectName(QString::fromUtf8("carousel"));
//---------------------------------------------------------------------
QScrollArea* sa = new QScrollArea();
sa->setObjectName("sa");
sa->setStyleSheet(R"(
@ -430,6 +431,29 @@ HPPA::HPPA(QWidget* parent)
m_carousel->addWidget(sa_depthCamera);
m_carousel->setContentsMargins(0, 0, 0, 0);
//---------------------------------------------------------------------
QScrollArea* sa_SingleLensReflexCamera = new QScrollArea();
sa_SingleLensReflexCamera->setObjectName("sa_SingleLensReflexCamera");
sa_SingleLensReflexCamera->setStyleSheet(R"(
border: none;
background-color: #0D1233;
)");
QGridLayout* gridLayout_sa_SingleLensReflexCamera = new QGridLayout(sa_SingleLensReflexCamera);
gridLayout_sa_SingleLensReflexCamera->setSpacing(6);
gridLayout_sa_SingleLensReflexCamera->setObjectName(QString::fromUtf8("gridLayout_sa_SingleLensReflexCamera"));
gridLayout_sa_SingleLensReflexCamera->setVerticalSpacing(0);
gridLayout_sa_SingleLensReflexCamera->setContentsMargins(0, 0, 0, 0);
m_SingleLensReflexCamera_label = new QLabel();
m_SingleLensReflexCamera_label->setAlignment(Qt::AlignHCenter);
m_SingleLensReflexCamera_label->setStyleSheet(R"(
background-color: #0D1233;
)");
gridLayout_sa_SingleLensReflexCamera->addWidget(m_SingleLensReflexCamera_label);
m_carousel->addWidget(sa_SingleLensReflexCamera);
m_carousel->setContentsMargins(0, 0, 0, 0);
m_carousel->play();
gridLayout_carouselContainer->addWidget(m_carousel);
@ -551,6 +575,9 @@ HPPA::HPPA(QWidget* parent)
initMapTools();
//定时采集
connect(this->ui.mActionTimedDataCollection, SIGNAL(triggered()), this, SLOT(onTimedDataCollection()));
QString strPath = QCoreApplication::applicationDirPath() + "/UILayout.ini";
QFile file(strPath);
if (file.open(QIODevice::ReadOnly))
@ -564,6 +591,155 @@ HPPA::HPPA(QWidget* parent)
this->showMaximized();
}
void HPPA::initTimedDataCollection()
{
if (m_tdc)
{
m_tdc->show();
m_tdc->activateWindow();
return;
}
m_tdc = new TimedDataCollection(this);
m_tdc->setWindowFlags(m_tdc->windowFlags() | Qt::Tool);
m_tdc->setAttribute(Qt::WA_DeleteOnClose);
m_tmc->connectMotor(false);
// 定时采集控制器 → 相机/马达
connect(m_tdc, &TimedDataCollection::hyperCamParm, this, &HPPA::setTimedDataCollectionHyperCamParm);
connect(m_tdc, &TimedDataCollection::camParm, this, &HPPA::setTimedDataCollectionCamParm);
connect(m_tdc, &TimedDataCollection::motorParm, this, &HPPA::setTimedDataCollectionMotorParm);
connect(m_tdc, &TimedDataCollection::startRecordSignal, this, &HPPA::onStartTimedDataCollection);
connect(m_tdc, &TimedDataCollection::switchHalogenLampSignal, m_pc3D, &PowerControl3D::switchHalogenLampPower);
connect(m_tdc, &TimedDataCollection::switchD65LampSignal, m_pc3D, &PowerControl3D::switchD65LampPower);
connect(m_tdc, &TimedDataCollection::switchSlrSignal, m_pc3D, &PowerControl3D::switchSlrPower);
// 相机/马达 → 定时采集控制器
connect(m_tmc, &TwoMotorControl::sequenceComplete, m_tdc, &TimedDataCollection::subTaskCompleted);
connect(m_tmc, &TwoMotorControl::back2OriginSignal_TimedDataCollection, m_tdc, &TimedDataCollection::onBack2Origin);
m_tdc->show();
}
void HPPA::setTimedDataCollectionHyperCamParm(int camType, double f, double e, QString filePath, QString fileName)
{
switch (camType)
{
case 0:
{
disconnectImagerAndCleanup();
ui.mActionPica_L->setChecked(true);
AppSettings::instance().setFrameRate(f);
AppSettings::instance().setIntegrationTime(e);
AppSettings::instance().setDataFolder(filePath);
AppSettings::instance().setFileName(fileName);
this->frame_number->setText("100000");
onconnect();
break;
}
case 1:
{
disconnectImagerAndCleanup();
ui.mActionPica_NIR->setChecked(true);
AppSettings::instance().setFrameRate(f);
AppSettings::instance().setIntegrationTime(e);
AppSettings::instance().setDataFolder(filePath);
AppSettings::instance().setFileName(fileName);
this->frame_number->setText("100000");
onconnect();
break;
}
}
}
void HPPA::setTimedDataCollectionCamParm(int camType, int captureIntervalSeconds, QString folder)
{
switch (camType)
{
case 2:
{
//唤醒单反相机
m_pc3D->slrPowerDown();
m_pc3D->slrPowerOn();
m_singleLensReflexCameraWindow->setDataFolder(folder);
m_singleLensReflexCameraWindow->setCaptureInterval(captureIntervalSeconds);
break;
}
case 3:
{
m_pc3D->d65LampPowerOn();
m_depthCameraWindow->setDataFolder(folder);
m_depthCameraWindow->setCaptureInterval(captureIntervalSeconds);
break;
}
}
}
void HPPA::setTimedDataCollectionMotorParm(QString pathLineFilePath)
{
m_tmc->readRecordLineFile(pathLineFilePath);
//m_tmc->onConnectMotor();
//QThread::msleep(1000);
}
void HPPA::onStartTimedDataCollection(int camType)
{
switch (camType)
{
case 0:
{
onStartRecordStep1();
break;
}
case 1:
{
onStartRecordStep1();
break;
}
case 2:
{
m_tmc->run2(m_singleLensReflexCameraWindow);
break;
}
case 3:
{
m_tmc->run3(m_depthCameraWindow);
break;
}
}
}
void HPPA::onTimedDataCollection()
{
QAction* checkedScenario = m_ScenarioActionGroup->checkedAction();
QString checkedScenarioName = checkedScenario->objectName();
if (checkedScenarioName == "mAction3DPlantPhenotypeScenario")//计划采集
{
initTimedDataCollection();
return;
}
else if (checkedScenarioName == "mActionPlantPhenotypeScenario")
{
}
}
void HPPA::initMenubarToolbar()
{
//自定义菜单栏和工具栏
@ -784,6 +960,9 @@ void HPPA::initControlTabwidget()
//单反相机
m_singleLensReflexCameraWindow = new SingleLensReflexCameraWindow();
connect(m_singleLensReflexCameraWindow, &SingleLensReflexCameraWindow::LiveViewImageReady, this, &HPPA::onLiveViewImageReady);
connect(m_singleLensReflexCameraWindow, &SingleLensReflexCameraWindow::LiveViewStopped, this, &HPPA::onLiveViewStopped);
ui.controlTabWidget->addTab(m_singleLensReflexCameraWindow, QString::fromLocal8Bit("单反相机"));
//rgb相机
@ -806,6 +985,11 @@ void HPPA::initControlTabwidget()
m_pc->setWindowFlags(Qt::Widget);
ui.controlTabWidget->addTab(m_pc, QString::fromLocal8Bit("电源控制"));
//电源控制3D
m_pc3D = new PowerControl3D();
m_pc3D->setWindowFlags(Qt::Widget);
ui.controlTabWidget->addTab(m_pc3D, QString::fromLocal8Bit("电源控制3D"));
//机械臂控制
m_rac = new RobotArmControl();
connect(m_rac->robotController, SIGNAL(hsiRecordSignal(int)), this, SLOT(recordFromRobotArm(int)));
@ -820,7 +1004,7 @@ void HPPA::initControlTabwidget()
//2轴马达控制
m_tmc = new TwoMotorControl(this);
//connect(m_tmc, SIGNAL(startLineNumSignal(int)), this, SLOT(onCreateTab(int)));
connect(m_tmc, SIGNAL(sequenceComplete()), this, SLOT(onsequenceComplete()));
connect(m_tmc, &TwoMotorControl::back2OriginSignal, this, &HPPA::onsequenceComplete);
m_tmc->setWindowFlags(Qt::Widget);
ui.controlTabWidget->addTab(m_tmc, QString::fromLocal8Bit("2轴控制"));
@ -1037,16 +1221,20 @@ void HPPA::removeLayerByNode(LayerTreeNode* node)
const int tabIndex = m_imageViewerTabWidget->indexOf(widget);
if (tabIndex >= 0)
{
if (m_recordFrameCounter)
{
m_recordFrameCounter->removeCounter(widget);
}
m_imageViewerTabWidget->removeTab(tabIndex);
}
widget->deleteLater();
}
}
if (!mapLayer || mapLayer->layerType() == MapLayer::LayerType::Raster)
{
if (m_recordFrameCounter)
{
m_recordFrameCounter->removeCounter(static_cast<RasterLayer*>(mapLayer));
}
}
// 删除 MapLayerStore 中的 mapLayer
m_MapLayerStore->removeLayer(mapLayer);
@ -1104,10 +1292,6 @@ void HPPA::removeImageByTreeIndex()
const int tabIndex = m_imageViewerTabWidget->indexOf(layerWidget);
if (tabIndex >= 0)
{
if (m_recordFrameCounter)
{
m_recordFrameCounter->removeCounter(layerWidget);
}
m_imageViewerTabWidget->removeTab(tabIndex);
}
layerWidget->deleteLater();
@ -1355,7 +1539,8 @@ void HPPA::create3DPlantPhenotypeScenario()
m_tabManager->showTab(m_singleLensReflexCameraWindow);
//m_tabManager->showTab(m_rgbCameraControlWindow);
m_tabManager->showTab(m_adt);
m_tabManager->showTab(m_pc);
//m_tabManager->showTab(m_pc);
m_tabManager->showTab(m_pc3D);
m_tabManager->showTab(m_tmc);
m_view3DModelManager->switchScenario(View3DModelManager::ScenarioType::PlantPhenotype);
@ -1533,7 +1718,7 @@ void HPPA::onStartRecordStep1()
//判断光谱仪
if(!testImagerVality())
{
showMessageBox(QString::fromLocal8Bit("找不到光谱仪!"));
showMessageBox(QString::fromLocal8Bit("找不到光谱仪!"));
return;
}
@ -1674,11 +1859,6 @@ QWidget* HPPA::onCreateTab(QString tabName)
m_imageViewerTabWidget->setCurrentIndex(m_imageViewerTabWidget->count() - 1);
if (m_recordFrameCounter)
{
m_recordFrameCounter->addCounter(tabTmp);
}
return tabTmp;
}
@ -1695,7 +1875,11 @@ void HPPA::onTabWidgetCurrentChanged(int index)//代码新建一个tab会调
if (m_recordFrameCounter)
{
QWidget* currentWidget = m_imageViewerTabWidget->widget(index);
m_recordFrameCounter->switchTo(currentWidget);
MapLayer* currentMapLayer = m_MapLayerStore->mapLayerForWidget(currentWidget);
if (currentMapLayer)
{
m_recordFrameCounter->switchTo(static_cast<RasterLayer*>(currentMapLayer));
}
}
//获取绘图控件
@ -1892,6 +2076,22 @@ void HPPA::onClearLabel()
m_cam_label->setText("closed");
}
void HPPA::onLiveViewImageReady(const QImage& image)
{
if (m_SingleLensReflexCamera_label && !image.isNull())
{
// 缩放图像以适应标签大小
QPixmap pixmap = QPixmap::fromImage(image);
pixmap = pixmap.scaled(m_SingleLensReflexCamera_label->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
m_SingleLensReflexCamera_label->setPixmap(pixmap);
}
}
void HPPA::onLiveViewStopped()
{
m_SingleLensReflexCamera_label->clear();
}
void HPPA::onPlotDepthImage()
{
QPixmap pixmap = QPixmap::fromImage(m_depthCameraWindow->m_DepthCameraOperation->m_depthImage);
@ -1985,6 +2185,14 @@ void HPPA::onOpenImg()
QString baseName = fi.completeBaseName();
addLayer(baseName, uri, true);
// 获取影像行数并更新帧数显示
MapLayer* mapLayer = m_MapLayerStore->getLayerByAbsolutePath(uri);
if (mapLayer && m_recordFrameCounter)
{
RasterLayer* rasterLayer = static_cast<RasterLayer*>(mapLayer);
m_recordFrameCounter->updateFrameCount(rasterLayer, rasterLayer->height());
}
}
void HPPA::disconnectImagerAndCleanup()
@ -2076,7 +2284,7 @@ void HPPA::onconnect()
}
else
{
bool res = showResultMessageBox(QString::fromLocal8Bit("相机连接"), QString::fromLocal8Bit("确定要重连相机吗?"));
bool res = showResultMessageBox(QString::fromLocal8Bit("高光谱仪连接"), QString::fromLocal8Bit("确定要重连高光谱仪吗?"));
if (res)
{
disconnectImagerAndCleanup();
@ -2112,7 +2320,7 @@ void HPPA::onconnect()
}
else
{
showMessageBox(QString::fromLocal8Bit("请选择相机类型!"));
showMessageBox(QString::fromLocal8Bit("请选择高光谱仪类型!"));
return;
}
@ -2193,7 +2401,7 @@ void HPPA::onconnect()
delete m_Imager;
m_Imager = nullptr;
showMessageBox(QString::fromLocal8Bit("请连接相机"));
showMessageBox(QString::fromLocal8Bit("请连接高光谱仪"));
}
catch (int e)//ximea相机异常
{
@ -2202,7 +2410,7 @@ void HPPA::onconnect()
delete m_Imager;
m_Imager = nullptr;
showMessageBox(QString::fromLocal8Bit("请连接相机"));
showMessageBox(QString::fromLocal8Bit("请连接高光谱仪"));
}
}
@ -2405,6 +2613,12 @@ void HPPA::onPlotHyperspectralImageRgbImage(int fileNumber, int frameNumber, QSt
return;
}
MapLayer* mapLayer = m_MapLayerStore->getLayerByAbsolutePath(filePath);
if (mapLayer && m_recordFrameCounter)
{
m_recordFrameCounter->updateFrameCount(static_cast<RasterLayer*>(mapLayer), m_Imager->getFrameCounter());
}
if (ui.mAction3DPlantPhenotypeScenario->isChecked())
return;
@ -2416,11 +2630,6 @@ void HPPA::onPlotHyperspectralImageRgbImage(int fileNumber, int frameNumber, QSt
QList<Mapcavas*> currentImageViewer = currentWidget->findChildren<Mapcavas*>();
currentImageViewer[0]->DisplayFrameNumber(m_Imager->getFrameCounter());//界面中显示已经采集的帧数
if (m_recordFrameCounter)
{
m_recordFrameCounter->updateFrameCount(currentWidget, m_Imager->getFrameCounter());
}
//创建需要显示的图像--opencv版本
ImageProcessor imageProcessor;
//cv::Mat rgbImage(*m_Imager->getRgbImage()->m_matRgbImage, cv::Range(0, m_Imager->getFrameCounter()), cv::Range::all());//2022.3.18重构的
@ -2622,9 +2831,14 @@ void HPPA::addLayer(const QString& baseName, const QString& filePath,bool refres
if (m_MapLayerStore) m_MapLayerStore->addLayer(ml);
if (m_recordFrameCounter)
{
m_recordFrameCounter->addCounter(ml);
}
if (isAddImage)
{
newImage(ml, RasterImageLayer::RendererType::Multiband, node);
newImage(ml, RasterImageLayer::RendererType::Multiband, node, refresh);
}
}
@ -2658,6 +2872,12 @@ void HPPA::newImage(RasterLayer* ml, RasterImageLayer::RendererType type, LayerT
void HPPA::onLayerTreeSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
{
//采集过程中禁用图层树的选择功能
if (m_RecordState % 2 == 1)
{
return;
}
Q_UNUSED(deselected);
if (selected.indexes().isEmpty())

View File

@ -13,6 +13,7 @@
#include <QChart>
#include <QChartView>
#include <QValueAxis>
#include <QPointer>
#include <QFileDialog>
#include <QNetworkRequest>
@ -83,6 +84,10 @@
#include "LayerTreeImageNode.h"
#include "TimedDataCollection.h"
#include "PowerControl3D.h"
#define PI 3.1415926
QT_CHARTS_USE_NAMESPACE//QChartView 使用 需要加宏, 否则无法使用
@ -274,6 +279,7 @@ private:
MyCarousel* m_carousel;
QLabel* m_cam_label;
QLabel* m_SingleLensReflexCamera_label;
QLabel* m_depthCamera_label;
QPushButton* m_open_rgb_camera_btn;
QPushButton* m_close_rgb_camera_btn;
@ -287,9 +293,11 @@ private:
SingleLensReflexCameraWindow* m_singleLensReflexCameraWindow;
adjustTable* m_adt;
PowerControl* m_pc;
PowerControl3D* m_pc3D;
RobotArmControl* m_rac;
OneMotorControl* m_omc;
TwoMotorControl* m_tmc;
QPointer<TimedDataCollection> m_tdc;
View3DModelManager* m_view3DModelManager;
@ -315,6 +323,8 @@ private:
bool showResultMessageBox(QString title, QString msg);
void disconnectImagerAndCleanup();
void initTimedDataCollection();
public Q_SLOTS:
void onPlotHyperspectralImageRgbImage(int fileNumber, int frameNumber, QString filePath);
void focusPlotSpectralImg(int state);
@ -358,6 +368,9 @@ public Q_SLOTS:
void onPlotRgbImage();
void onPlotDepthImage();
void onLiveViewImageReady(const QImage& image);
void HPPA::onLiveViewStopped();
void onClearLabel();
void onClearDepthLabel();
@ -389,6 +402,12 @@ public Q_SLOTS:
void onMapToolPanTriggered();
void onMapToolSpectralTriggered();
void onTimedDataCollection();
void setTimedDataCollectionHyperCamParm(int camType, double f, double e, QString filePath, QString fileName);
void setTimedDataCollectionCamParm(int camType, int captureIntervalSeconds, QString folder);
void setTimedDataCollectionMotorParm(QString pathLineFilePath);
void onStartTimedDataCollection(int camType);
protected:
void closeEvent(QCloseEvent* event) override;

View File

@ -105,6 +105,7 @@ color:white;
<addaction name="action_start_recording"/>
<addaction name="separator"/>
<addaction name="actionOpenDirectory"/>
<addaction name="mActionTimedDataCollection"/>
</widget>
<widget class="QMenu" name="menuhelp">
<property name="title">
@ -743,6 +744,11 @@ QPushButton:pressed
<string>3D植物表型</string>
</property>
</action>
<action name="mActionTimedDataCollection">
<property name="text">
<string>定时采集</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

View File

@ -55,18 +55,18 @@
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<IncludePath>D:\cpp_library\gdal2.2.3_vs2017\include;C:\Program Files\ResononAPI\include;D:\cpp_library\opencv3.4.11\opencv\build\include;D:\cpp_library\opencv3.4.11\opencv\build\include\opencv;D:\cpp_library\opencv3.4.11\opencv\build\include\opencv2;D:\cpp_project_vs2022\AutoFocus_InspireLinearMotor_DLL\AutoFocus_InspireLinearMotor_DLL\SDKs\PCOMM\Include;D:\cpp_project_vs2022\AutoFocus_InspireLinearMotor_DLL\AutoFocus_InspireLinearMotor_DLL\SDKs\PortControl;D:\cpp_project_vs2022\AutoFocus_InspireLinearMotor_DLL\AutoFocus_InspireLinearMotor_DLL;D:\cpp_project_vs2022\HPPA\HPPA;D:\cpp_library\libconfig-1.7.3\lib;D:\cpp_project_vs2022\HPPA\vincecontrol;D:\cpp_library\vincecontrol_vs2017;C:\XIMEA\API\xiAPI;D:\cpp_project_vs2022\HPPA\IrisMultiMotorController\IrisMultiMotorController;D:\cpp_library\eigen-3.4-rc1;C:\Program Files\OrbbecSDK 2.7.6\include;$(IncludePath)</IncludePath>
<LibraryPath>D:\cpp_library\opencv3.4.11\opencv\build\x64\vc15\lib;D:\cpp_library\gdal2.2.3_vs2017\lib;C:\Program Files\ResononAPI\lib64;D:\cpp_project_vs2022\AutoFocus_InspireLinearMotor_DLL\x64\Debug;D:\cpp_library\libconfig-1.7.3\build\x64;D:\cpp_project_vs2022\HPPA\x64\Debug;C:\XIMEA\API\xiAPI;D:\cpp_project_vs2022\HPPA\IrisMultiMotorController\x64\Debug;C:\Program Files\OrbbecSDK 2.7.6\lib;$(LibraryPath)</LibraryPath>
<IncludePath>D:\cpp_library\gdal2.2.3_vs2017\include;C:\Program Files\ResononAPI\include;D:\cpp_library\opencv3.4.11\opencv\build\include;D:\cpp_library\opencv3.4.11\opencv\build\include\opencv;D:\cpp_library\opencv3.4.11\opencv\build\include\opencv2;D:\cpp_project_vs2022\AutoFocus_InspireLinearMotor_DLL\AutoFocus_InspireLinearMotor_DLL\SDKs\PCOMM\Include;D:\cpp_project_vs2022\AutoFocus_InspireLinearMotor_DLL\AutoFocus_InspireLinearMotor_DLL\SDKs\PortControl;D:\cpp_project_vs2022\AutoFocus_InspireLinearMotor_DLL\AutoFocus_InspireLinearMotor_DLL;D:\cpp_project_vs2022\HPPA\HPPA;D:\cpp_library\libconfig-1.7.3\lib;D:\cpp_project_vs2022\HPPA\vincecontrol;D:\cpp_library\vincecontrol_vs2017;C:\XIMEA\API\xiAPI;D:\cpp_project_vs2022\HPPA\IrisMultiMotorController\IrisMultiMotorController;D:\cpp_library\eigen-3.4-rc1;C:\Program Files\OrbbecSDK 2.7.6\include;D:\cpp_library\EDSDK132010CD(13.20.10)\Windows\EDSDK_64\Header;$(IncludePath)</IncludePath>
<LibraryPath>D:\cpp_library\opencv3.4.11\opencv\build\x64\vc15\lib;D:\cpp_library\gdal2.2.3_vs2017\lib;C:\Program Files\ResononAPI\lib64;D:\cpp_project_vs2022\AutoFocus_InspireLinearMotor_DLL\x64\Debug;D:\cpp_library\libconfig-1.7.3\build\x64;D:\cpp_project_vs2022\HPPA\x64\Debug;C:\XIMEA\API\xiAPI;D:\cpp_project_vs2022\HPPA\IrisMultiMotorController\x64\Debug;C:\Program Files\OrbbecSDK 2.7.6\lib;D:\cpp_library\EDSDK132010CD(13.20.10)\Windows\EDSDK_64\Library;$(LibraryPath)</LibraryPath>
<TargetName>Spectral Insight</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<IncludePath>D:\cpp_library\gdal2.2.3_vs2017\include;C:\Program Files\ResononAPI\include;D:\cpp_library\opencv3.4.11\opencv\build\include;D:\cpp_library\opencv3.4.11\opencv\build\include\opencv;D:\cpp_library\opencv3.4.11\opencv\build\include\opencv2;D:\cpp_project_vs2022\AutoFocus_InspireLinearMotor_DLL\AutoFocus_InspireLinearMotor_DLL\SDKs\PCOMM\Include;D:\cpp_project_vs2022\AutoFocus_InspireLinearMotor_DLL\AutoFocus_InspireLinearMotor_DLL\SDKs\PortControl;D:\cpp_project_vs2022\AutoFocus_InspireLinearMotor_DLL\AutoFocus_InspireLinearMotor_DLL;D:\cpp_project_vs2022\HPPA\HPPA;D:\cpp_library\libconfig-1.7.3\lib;D:\cpp_project_vs2022\HPPA\vincecontrol;C:\XIMEA\API\xiAPI;D:\cpp_project_vs2022\HPPA\IrisMultiMotorController\IrisMultiMotorController;D:\cpp_library\eigen-3.4-rc1;C:\Program Files\OrbbecSDK 2.7.6\include;$(IncludePath)</IncludePath>
<LibraryPath>D:\cpp_library\opencv3.4.11\opencv\build\x64\vc15\lib;D:\cpp_library\vincecontrol_vs2017_release;D:\cpp_library\gdal2.2.3_vs2017\lib;C:\Program Files\ResononAPI\lib64;D:\cpp_project_vs2022\AutoFocus_InspireLinearMotor_DLL\x64\Release;D:\cpp_library\libconfig-1.7.3\build\x64;D:\cpp_project_vs2022\IrisMultiMotorController\x64\Release;C:\XIMEA\API\xiAPI;C:\Program Files\OrbbecSDK 2.7.6\lib;$(LibraryPath)</LibraryPath>
<IncludePath>D:\cpp_library\gdal2.2.3_vs2017\include;C:\Program Files\ResononAPI\include;D:\cpp_library\opencv3.4.11\opencv\build\include;D:\cpp_library\opencv3.4.11\opencv\build\include\opencv;D:\cpp_library\opencv3.4.11\opencv\build\include\opencv2;D:\cpp_project_vs2022\AutoFocus_InspireLinearMotor_DLL\AutoFocus_InspireLinearMotor_DLL\SDKs\PCOMM\Include;D:\cpp_project_vs2022\AutoFocus_InspireLinearMotor_DLL\AutoFocus_InspireLinearMotor_DLL\SDKs\PortControl;D:\cpp_project_vs2022\AutoFocus_InspireLinearMotor_DLL\AutoFocus_InspireLinearMotor_DLL;D:\cpp_project_vs2022\HPPA\HPPA;D:\cpp_library\libconfig-1.7.3\lib;D:\cpp_project_vs2022\HPPA\vincecontrol;C:\XIMEA\API\xiAPI;D:\cpp_project_vs2022\HPPA\IrisMultiMotorController\IrisMultiMotorController;D:\cpp_library\eigen-3.4-rc1;C:\Program Files\OrbbecSDK 2.7.6\include;D:\cpp_library\EDSDK132010CD(13.20.10)\Windows\EDSDK_64\Header;$(IncludePath)</IncludePath>
<LibraryPath>D:\cpp_library\opencv3.4.11\opencv\build\x64\vc15\lib;D:\cpp_library\vincecontrol_vs2017_release;D:\cpp_library\gdal2.2.3_vs2017\lib;C:\Program Files\ResononAPI\lib64;D:\cpp_project_vs2022\AutoFocus_InspireLinearMotor_DLL\x64\Release;D:\cpp_library\libconfig-1.7.3\build\x64;D:\cpp_project_vs2022\IrisMultiMotorController\x64\Release;C:\XIMEA\API\xiAPI;C:\Program Files\OrbbecSDK 2.7.6\lib;D:\cpp_library\EDSDK132010CD(13.20.10)\Windows\EDSDK_64\Library;$(LibraryPath)</LibraryPath>
<TargetName>Spectral Insight</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Link>
<AdditionalDependencies>opencv_world3411.lib;opencv_world3411d.lib;gdal_i.lib;resonon-basler.lib;AutoFocus_InspireLinearMotor_DLL.lib;libconfig++d.lib;vincecontrol.lib;resonon-allied.lib;xiapi64.lib;IrisMultiMotorController.lib;OrbbecSDK.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>opencv_world3411.lib;opencv_world3411d.lib;gdal_i.lib;resonon-basler.lib;AutoFocus_InspireLinearMotor_DLL.lib;libconfig++d.lib;vincecontrol.lib;resonon-allied.lib;xiapi64.lib;IrisMultiMotorController.lib;OrbbecSDK.lib;EDSDK.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>D:\cpp_project_vs2022\HPPA\x64\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<ClCompile>
@ -75,7 +75,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Link>
<AdditionalDependencies>opencv_world3411.lib;vincecontrol.lib;gdal_i.lib;resonon-basler.lib;resonon-allied.lib;AutoFocus_InspireLinearMotor_DLL.lib;libconfig++.lib;xiapi64.lib;IrisMultiMotorController.lib;OrbbecSDK.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>opencv_world3411.lib;vincecontrol.lib;gdal_i.lib;resonon-basler.lib;resonon-allied.lib;AutoFocus_InspireLinearMotor_DLL.lib;libconfig++.lib;xiapi64.lib;IrisMultiMotorController.lib;OrbbecSDK.lib;EDSDK.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>D:\cpp_project_vs2022\HPPA\x64\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
@ -112,6 +112,8 @@
<ClCompile Include="AspectRatioLabel.cpp" />
<ClCompile Include="CaptureCoordinator.cpp" />
<ClCompile Include="Carousel.cpp" />
<ClCompile Include="CommunicationInterfaceBase.cpp" />
<ClCompile Include="CommunicationViaTCP.cpp" />
<ClCompile Include="Corning410Imager.cpp" />
<ClCompile Include="CustomDockWidgetBase.cpp" />
<ClCompile Include="DepthCameraWindow.cpp" />
@ -138,8 +140,10 @@
<ClCompile Include="MapToolSpectral.cpp" />
<ClCompile Include="MotorWindowBase.cpp" />
<ClCompile Include="OneMotorControl.cpp" />
<ClCompile Include="PathLine.cpp" />
<ClCompile Include="path_tc.cpp" />
<ClCompile Include="PowerControl.cpp" />
<ClCompile Include="PowerControl3D.cpp" />
<ClCompile Include="QDoubleSlider.cpp" />
<ClCompile Include="QMotorDoubleSlider.cpp" />
<ClCompile Include="RasterDataProvider.cpp" />
@ -161,6 +165,9 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="TabManager.cpp" />
<ClCompile Include="TaskTreeModel.cpp" />
<ClCompile Include="TimedDataCollection.cpp" />
<ClCompile Include="TimedDataCollectionDataStructures.cpp" />
<ClCompile Include="TwoMotorControl.cpp" />
<ClCompile Include="utility_tc.cpp" />
<ClCompile Include="View3D.cpp" />
@ -187,12 +194,14 @@
<QtUic Include="oneMotorControl.ui" />
<QtUic Include="PathPlan.ui" />
<QtUic Include="PowerControl.ui" />
<QtUic Include="PowerControl3D.ui" />
<QtUic Include="RadianceConversion.ui" />
<QtUic Include="ReflectanceConversion.ui" />
<QtUic Include="rgbCamera.ui" />
<QtUic Include="RobotArmControl.ui" />
<QtUic Include="set.ui" />
<QtUic Include="SingleLensReflexCamera.ui" />
<QtUic Include="TimedDataCollection_ui.ui" />
<QtUic Include="twoMotorControl.ui" />
</ItemGroup>
<ItemGroup>
@ -220,6 +229,8 @@
<ClInclude Include="AppSettings.h" />
<QtMoc Include="FileNameLineEdit.h" />
<QtMoc Include="DepthCameraWindow.h" />
<QtMoc Include="CommunicationViaTCP.h" />
<QtMoc Include="CommunicationInterfaceBase.h" />
<ClInclude Include="imager_base.h" />
<ClInclude Include="irisximeaimager.h" />
<QtMoc Include="OneMotorControl.h" />
@ -241,6 +252,8 @@
<QtMoc Include="MapToolSpectral.h" />
<QtMoc Include="MapTools.h" />
<ClInclude Include="MotorWindowBase.h" />
<QtMoc Include="PowerControl3D.h" />
<ClInclude Include="PathLine.h" />
<ClInclude Include="RasterDataProvider.h" />
<ClInclude Include="MultibandRasterRenderer.h" />
<ClInclude Include="RasterImageLayer.h" />
@ -250,6 +263,9 @@
<QtMoc Include="setWindow.h" />
<QtMoc Include="rgbCameraWindow.h" />
<QtMoc Include="SingleLensReflexCameraWindow.h" />
<QtMoc Include="TimedDataCollection.h" />
<QtMoc Include="TimedDataCollectionDataStructures.h" />
<QtMoc Include="TaskTreeModel.h" />
<ClInclude Include="utility_tc.h" />
<QtMoc Include="aboutWindow.h" />
<ClInclude Include="hppaConfigFile.h" />

View File

@ -232,6 +232,27 @@
<ClCompile Include="RasterRendererBase.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="TimedDataCollection.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="TimedDataCollectionDataStructures.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CommunicationViaTCP.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CommunicationInterfaceBase.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PowerControl3D.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="TaskTreeModel.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PathLine.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<QtMoc Include="fileOperation.h">
@ -375,6 +396,24 @@
<QtMoc Include="LayerTreeImageNode.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="TimedDataCollection.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="TimedDataCollectionDataStructures.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="CommunicationViaTCP.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="CommunicationInterfaceBase.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="PowerControl3D.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="TaskTreeModel.h">
<Filter>Header Files</Filter>
</QtMoc>
</ItemGroup>
<ItemGroup>
<ClInclude Include="imageProcessor.h">
@ -431,6 +470,9 @@
<ClInclude Include="MotorWindowBase.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="PathLine.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<QtUic Include="FocusDialog.ui">
@ -481,6 +523,12 @@
<QtUic Include="SingleLensReflexCamera.ui">
<Filter>Form Files</Filter>
</QtUic>
<QtUic Include="TimedDataCollection_ui.ui">
<Filter>Form Files</Filter>
</QtUic>
<QtUic Include="PowerControl3D.ui">
<Filter>Form Files</Filter>
</QtUic>
</ItemGroup>
<ItemGroup>
<None Include="cpp.hint" />

View File

@ -125,6 +125,15 @@ MapLayer* MapLayerStore::getLayerAt(int index) const
return m_layers[index].mapLayer.get();
}
MapLayer* MapLayerStore::getLayerByAbsolutePath(const QString& absolutePath) const
{
for (const auto& entry : m_layers) {
if (entry.mapLayer->dataPath() == absolutePath)
return entry.mapLayer.get();
}
return nullptr;
}
int MapLayerStore::layerCount() const
{
return (int)m_layers.size();

View File

@ -35,6 +35,7 @@ public:
bool containsLayer(const QString& url, bool isAbsolutePath = true) const;
MapLayer* getLayer(const QString& name) const;
MapLayer* getLayerByAbsolutePath(const QString& absolutePath) const;
MapLayer* getLayerAt(int index) const;
int layerCount() const;

319
HPPA/PathLine.cpp Normal file
View File

@ -0,0 +1,319 @@
#include "PathLine.h"
#include <QTableWidgetItem>
#include <cstdio>
PathLineManager::PathLineManager()
{
}
PathLineManager::~PathLineManager()
{
}
QVector<PathLine2> PathLineManager::readFromTableWidget(QTableWidget* tableWidget)
{
QVector<PathLine2> pathLines;
if (tableWidget == nullptr)
return pathLines;
int rowCount = tableWidget->rowCount();
for (int i = 0; i < rowCount; i++)
{
PathLine2 tmp;
tmp.targetYPosition = tableWidget->item(i, 0)->text().toDouble();
tmp.speedTargetYPosition = tableWidget->item(i, 1)->text().toDouble();
tmp.targetXMinPosition = tableWidget->item(i, 2)->text().toDouble();
tmp.speedTargetXMinPosition = tableWidget->item(i, 3)->text().toDouble();
tmp.targetXMaxPosition = tableWidget->item(i, 4)->text().toDouble();
tmp.speedTargetXMaxPosition = tableWidget->item(i, 5)->text().toDouble();
pathLines.append(tmp);
}
return pathLines;
}
void PathLineManager::writeToTableWidget(QTableWidget* tableWidget, const QVector<PathLine2>& pathLines)
{
if (tableWidget == nullptr)
return;
// 清空现有行
int rowCount = tableWidget->rowCount();
for (int i = 0; i < rowCount; i++)
{
tableWidget->removeRow(0);
}
// 添加新行
for (int i = 0; i < pathLines.size(); i++)
{
tableWidget->insertRow(i);
QTableWidgetItem* item0 = new QTableWidgetItem(QString::number(pathLines[i].targetYPosition, 'f', 5));
QTableWidgetItem* item1 = new QTableWidgetItem(QString::number(pathLines[i].speedTargetYPosition, 'f', 5));
QTableWidgetItem* item2 = new QTableWidgetItem(QString::number(pathLines[i].targetXMinPosition, 'f', 5));
QTableWidgetItem* item3 = new QTableWidgetItem(QString::number(pathLines[i].speedTargetXMinPosition, 'f', 5));
QTableWidgetItem* item4 = new QTableWidgetItem(QString::number(pathLines[i].targetXMaxPosition, 'f', 5));
QTableWidgetItem* item5 = new QTableWidgetItem(QString::number(pathLines[i].speedTargetXMaxPosition, 'f', 5));
item0->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
item1->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
item2->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
item3->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
item4->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
item5->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
tableWidget->setItem(i, 0, item0);
tableWidget->setItem(i, 1, item1);
tableWidget->setItem(i, 2, item2);
tableWidget->setItem(i, 3, item3);
tableWidget->setItem(i, 4, item4);
tableWidget->setItem(i, 5, item5);
}
}
bool PathLineManager::saveToFile(const QString& filePath, QTableWidget* tableWidget)
{
if (tableWidget == nullptr || tableWidget->rowCount() <= 0)
return false;
FILE* fileHandle = fopen(filePath.toStdString().c_str(), "wb+");
if (fileHandle == nullptr)
return false;
double number = tableWidget->rowCount() * tableWidget->columnCount();
fwrite(&number, sizeof(double), 1, fileHandle);
double* data = new double[static_cast<int>(number)];
for (int i = 0; i < tableWidget->rowCount(); i++)
{
for (int j = 0; j < tableWidget->columnCount(); j++)
{
data[i * tableWidget->columnCount() + j] = tableWidget->item(i, j)->text().toDouble();
}
}
fwrite(data, sizeof(double), static_cast<size_t>(number), fileHandle);
fclose(fileHandle);
delete[] data;
return true;
}
bool PathLineManager::saveToFile(const QString& filePath, const QVector<PathLine2>& pathLines)
{
if (pathLines.isEmpty())
return false;
FILE* fileHandle = fopen(filePath.toStdString().c_str(), "wb+");
if (fileHandle == nullptr)
return false;
double number = pathLines.size() * COLUMN_COUNT;
fwrite(&number, sizeof(double), 1, fileHandle);
double* data = new double[static_cast<int>(number)];
for (int i = 0; i < pathLines.size(); i++)
{
data[i * COLUMN_COUNT + 0] = pathLines[i].targetYPosition;
data[i * COLUMN_COUNT + 1] = pathLines[i].speedTargetYPosition;
data[i * COLUMN_COUNT + 2] = pathLines[i].targetXMinPosition;
data[i * COLUMN_COUNT + 3] = pathLines[i].speedTargetXMinPosition;
data[i * COLUMN_COUNT + 4] = pathLines[i].targetXMaxPosition;
data[i * COLUMN_COUNT + 5] = pathLines[i].speedTargetXMaxPosition;
}
fwrite(data, sizeof(double), static_cast<size_t>(number), fileHandle);
fclose(fileHandle);
delete[] data;
return true;
}
bool PathLineManager::readFromFile(const QString& filePath, QTableWidget* tableWidget)
{
if (tableWidget == nullptr)
return false;
FILE* fileHandle = fopen(filePath.toStdString().c_str(), "rb");
if (fileHandle == nullptr)
return false;
double number;
fread(&number, sizeof(double), 1, fileHandle);
double* data = new double[static_cast<int>(number)];
for (int i = 0; i < static_cast<int>(number); i++)
{
fread(data + i, sizeof(double), 1, fileHandle);
}
// 清空现有行
int rowCount = tableWidget->rowCount();
for (int i = 0; i < rowCount; i++)
{
tableWidget->removeRow(0);
}
// 添加新行
int recordLineCount = static_cast<int>(number) / tableWidget->columnCount();
for (int i = 0; i < recordLineCount; i++)
{
tableWidget->insertRow(i);
}
// 填充数据
for (int i = 0; i < tableWidget->rowCount(); i++)
{
for (int j = 0; j < tableWidget->columnCount(); j++)
{
QTableWidgetItem* tmp = new QTableWidgetItem(
QString::number(data[i * tableWidget->columnCount() + j], 'f', 5));
tmp->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
tableWidget->setItem(i, j, tmp);
}
}
fclose(fileHandle);
delete[] data;
return true;
}
QVector<PathLine2> PathLineManager::readFromFile(const QString& filePath, bool* success)
{
QVector<PathLine2> pathLines;
FILE* fileHandle = fopen(filePath.toStdString().c_str(), "rb");
if (fileHandle == nullptr)
{
if (success) *success = false;
return pathLines;
}
double number;
fread(&number, sizeof(double), 1, fileHandle);
double* data = new double[static_cast<int>(number)];
for (int i = 0; i < static_cast<int>(number); i++)
{
fread(data + i, sizeof(double), 1, fileHandle);
}
int recordLineCount = static_cast<int>(number) / COLUMN_COUNT;
for (int i = 0; i < recordLineCount; i++)
{
PathLine2 tmp;
tmp.targetYPosition = data[i * COLUMN_COUNT + 0];
tmp.speedTargetYPosition = data[i * COLUMN_COUNT + 1];
tmp.targetXMinPosition = data[i * COLUMN_COUNT + 2];
tmp.speedTargetXMinPosition = data[i * COLUMN_COUNT + 3];
tmp.targetXMaxPosition = data[i * COLUMN_COUNT + 4];
tmp.speedTargetXMaxPosition = data[i * COLUMN_COUNT + 5];
pathLines.append(tmp);
}
fclose(fileHandle);
delete[] data;
if (success) *success = true;
return pathLines;
}
void PathLineManager::addRecordLine(QTableWidget* tableWidget,
double yPos, double ySpeed,
double xMinPos, double xMinSpeed,
double xMaxPos, double xMaxSpeed)
{
if (tableWidget == nullptr)
return;
int currentRow = tableWidget->currentRow();
int insertRow = (currentRow == -1) ? tableWidget->rowCount() : currentRow + 1;
tableWidget->insertRow(insertRow);
QTableWidgetItem* item0 = new QTableWidgetItem(QString::number(yPos, 'f', 2));
QTableWidgetItem* item1 = new QTableWidgetItem(QString::number(ySpeed, 'f', 2));
QTableWidgetItem* item2 = new QTableWidgetItem(QString::number(xMinPos, 'f', 2));
QTableWidgetItem* item3 = new QTableWidgetItem(QString::number(xMinSpeed, 'f', 2));
QTableWidgetItem* item4 = new QTableWidgetItem(QString::number(xMaxPos, 'f', 2));
QTableWidgetItem* item5 = new QTableWidgetItem(QString::number(xMaxSpeed, 'f', 2));
item0->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
item1->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
item2->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
item3->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
item4->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
item5->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
tableWidget->setItem(insertRow, 0, item0);
tableWidget->setItem(insertRow, 1, item1);
tableWidget->setItem(insertRow, 2, item2);
tableWidget->setItem(insertRow, 3, item3);
tableWidget->setItem(insertRow, 4, item4);
tableWidget->setItem(insertRow, 5, item5);
}
double PathLineManager::calculateTimeFromFile(const QString& filePath)
{
bool success = false;
QVector<PathLine2> pathLines = readFromFile(filePath, &success);
if (!success || pathLines.isEmpty())
return 0.0;
double totalTimeSecond = 0.0;
double currentX = 0.0;
double currentY = 0.0;
double yTime;
double xTime;
for (int i = 0; i < pathLines.size(); i++)
{
const PathLine2& line = pathLines[i];
double yDistance = qAbs(line.targetYPosition - currentY);
if (line.speedTargetYPosition > 0.0 && yDistance > 0.0)
{
yTime = yDistance / line.speedTargetYPosition;
}
double xMinDistance = qAbs(line.targetXMinPosition - currentX);
if (line.speedTargetXMinPosition > 0.0 && xMinDistance > 0.0)
{
xTime = xMinDistance / line.speedTargetXMinPosition;
}
totalTimeSecond += std::max(yTime, xTime);
double xMaxDistance = qAbs(line.targetXMaxPosition - line.targetXMinPosition);
if (line.speedTargetXMaxPosition > 0.0 && xMaxDistance > 0.0)
{
totalTimeSecond += xMaxDistance / line.speedTargetXMaxPosition;
}
currentX = line.targetXMaxPosition;
currentY = line.targetYPosition;
}
double returnYDistance = currentY;
double returnXDistance = currentX;
if (currentY > 0.0)
{
yTime += returnYDistance / pathLines[0].speedTargetYPosition;
}
if (currentX > 0.0)
{
xTime += returnXDistance / pathLines[0].speedTargetXMinPosition;
}
totalTimeSecond += std::max(yTime, xTime);
return totalTimeSecond / 60;
}

51
HPPA/PathLine.h Normal file
View File

@ -0,0 +1,51 @@
#pragma once
#include <QString>
#include <QVector>
#include <QTableWidget>
// PathLine 结构体定义(如果还没有在其他地方定义)
struct PathLine2
{
double targetYPosition;
double speedTargetYPosition;
double targetXMinPosition;
double speedTargetXMinPosition;
double targetXMaxPosition;
double speedTargetXMaxPosition;
};
class PathLineManager
{
public:
PathLineManager();
~PathLineManager();
// 从TableWidget读取PathLine数据
static QVector<PathLine2> readFromTableWidget(QTableWidget* tableWidget);
// 将PathLine数据写入TableWidget
static void writeToTableWidget(QTableWidget* tableWidget, const QVector<PathLine2>& pathLines);
// 保存PathLine到文件
static bool saveToFile(const QString& filePath, QTableWidget* tableWidget);
static bool saveToFile(const QString& filePath, const QVector<PathLine2>& pathLines);
// 从文件读取PathLine
static bool readFromFile(const QString& filePath, QTableWidget* tableWidget);
static QVector<PathLine2> readFromFile(const QString& filePath, bool* success = nullptr);
// 添加一行PathLine到TableWidget
static void addRecordLine(QTableWidget* tableWidget,
double yPos = 15.0,
double ySpeed = 1.0,
double xMinPos = 0.0,
double xMinSpeed = 1.0,
double xMaxPos = 50.0,
double xMaxSpeed = 1.0);
static double calculateTimeFromFile(const QString& filePath);
private:
static const int COLUMN_COUNT = 6;
};

100
HPPA/PowerControl3D.cpp Normal file
View File

@ -0,0 +1,100 @@
#include "PowerControl3D.h"
using namespace MotorParams;
PowerControl3D::PowerControl3D(QWidget* parent)
: QDialog(parent)
{
ui.setupUi(this);
connect(ui.halogenLampPowerOn_btn, &QPushButton::clicked, this, &PowerControl3D::halogenLampPowerOn);
connect(ui.halogenLampPowerDown_btn, &QPushButton::clicked, this, &PowerControl3D::halogenLampPowerDown);
connect(ui.d65LampPowerOn_btn, &QPushButton::clicked, this, &PowerControl3D::d65LampPowerOn);
connect(ui.d65LampPowerDown_btn, &QPushButton::clicked, this, &PowerControl3D::d65LampPowerDown);
connect(ui.slrPowerOn_btn, &QPushButton::clicked, this, &PowerControl3D::slrPowerOn);
connect(ui.slrPowerDown_btn, &QPushButton::clicked, this, &PowerControl3D::slrPowerDown);
MotorParams::TCPConnectionParams tcpConnectionParams6003;
tcpConnectionParams6003.port = 6003;
tcpConnectionParams6003.serverIP = "192.168.1.2";
tcpServer6003 = new CommunicationViaTCP(tcpConnectionParams6003, this);
MotorParams::TCPConnectionParams tcpConnectionParams6004;
tcpConnectionParams6004.port = 6004;
tcpConnectionParams6004.serverIP = "192.168.1.2";
tcpServer6004 = new CommunicationViaTCP(tcpConnectionParams6004, this);
}
PowerControl3D::~PowerControl3D()
{
delete tcpServer6003;
delete tcpServer6004;
}
void PowerControl3D::halogenLampPowerOn()
{
tcpServer6003->sendCommand(R"({"key1":1,"type":"event"})");
}
void PowerControl3D::halogenLampPowerDown()
{
tcpServer6003->sendCommand(R"({"key1":0,"type":"event"})");
}
void PowerControl3D::switchHalogenLampPower(int state)
{
if (state==0)
{
halogenLampPowerDown();
}
else if(state == 1)
{
halogenLampPowerOn();
}
}
void PowerControl3D::d65LampPowerOn()
{
tcpServer6004->sendCommand(R"({"key1":1,"type":"event"})");
}
void PowerControl3D::d65LampPowerDown()
{
tcpServer6004->sendCommand(R"({"key1":0,"type":"event"})");
}
void PowerControl3D::switchD65LampPower(int state)
{
if (state == 0)
{
d65LampPowerDown();
}
else if (state == 1)
{
d65LampPowerOn();
}
}
void PowerControl3D::slrPowerOn()
{
tcpServer6003->sendCommand(R"({"key2":1,"type":"event"})");
}
void PowerControl3D::slrPowerDown()
{
tcpServer6003->sendCommand(R"({"key2":0,"type":"event"})");
}
void PowerControl3D::switchSlrPower(int state)
{
if (state == 0)
{
slrPowerDown();
}
else if (state == 1)
{
slrPowerOn();
}
}

42
HPPA/PowerControl3D.h Normal file
View File

@ -0,0 +1,42 @@
#pragma once
#include <QDialog>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include "ui_PowerControl3D.h"
#include "CommunicationViaTCP.h"
//using namespace MotorParams;
class PowerControl3D : public QDialog
{
Q_OBJECT
public:
PowerControl3D(QWidget* parent = nullptr);
~PowerControl3D();
public Q_SLOTS:
void halogenLampPowerOn();
void halogenLampPowerDown();
void switchHalogenLampPower(int state);
void d65LampPowerOn();
void d65LampPowerDown();
void switchD65LampPower(int state);
void slrPowerOn();
void slrPowerDown();
void switchSlrPower(int state);
signals:
//void powerOpened();
private:
Ui::PowerControl3D_UI ui;
MotorParams::CommunicationViaTCP* tcpServer6003;
MotorParams::CommunicationViaTCP* tcpServer6004;
};

350
HPPA/PowerControl3D.ui Normal file
View File

@ -0,0 +1,350 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PowerControl3D_UI</class>
<widget class="QDialog" name="PowerControl3D_UI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>493</width>
<height>369</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>PowerControl3D</string>
</property>
<property name="styleSheet">
<string notr="true">QGroupBox
{
border: 12px solid transparent;
/*border-top: 12px solid transparent;
border-right: 0px solid transparent;
border-bottom: 0px solid transparent;
border-left: 0px solid transparent;*/
color: #ACCDFF;
}
QPushButton
{
/*width: 172px;
height: 56px;*/
font: 19pt &quot;新宋体&quot;;
background-color: qlineargradient(
spread:pad,
x1:0.5, y1:0, x2:0.5, y2:1,
stop:0 #283D86,
stop:1 #0F1A40
);
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
}
QPushButton:hover
{
background-color: qlineargradient(
spread:pad,
x1:0, y1:0, x2:1, y2:0,
stop:0 #3A4875,
stop:1 #5F6B91
);
}
/* 按下时的效果 */
QPushButton:pressed
{
background-color: qlineargradient(
spread:pad,
x1:0, y1:0, x2:1, y2:0,
stop:0 #1A254F,
stop:1 #3A466B
);
/* 可选:添加下压效果 */
padding-top: 9px;
padding-bottom: 7px;
}</string>
</property>
<layout class="QGridLayout" name="gridLayout_4" rowstretch="1,2,2,2,1" columnstretch="1,10,1">
<item row="0" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>66</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>63</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<widget class="QGroupBox" name="groupBox_8">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>卤素灯</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="horizontalSpacing">
<number>18</number>
</property>
<item row="0" column="0">
<widget class="QPushButton" name="halogenLampPowerOn_btn">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>打 开</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="halogenLampPowerDown_btn">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>关 闭</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="2">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>63</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>63</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="1">
<widget class="QGroupBox" name="groupBox_7">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>D65灯</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="horizontalSpacing">
<number>18</number>
</property>
<item row="0" column="0">
<widget class="QPushButton" name="d65LampPowerOn_btn">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>打 开</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="d65LampPowerDown_btn">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>关 闭</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="2">
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>63</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>63</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="1">
<widget class="QGroupBox" name="groupBox_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>单反</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="horizontalSpacing">
<number>18</number>
</property>
<item row="0" column="1">
<widget class="QPushButton" name="slrPowerDown_btn">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>下 电</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QPushButton" name="slrPowerOn_btn">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>上 电</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="2">
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>63</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="1">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>65</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

View File

@ -8,32 +8,46 @@
RasterImageLayer::RasterImageLayer(RasterLayer* layer, RendererType type)
: m_layer(layer)
, m_rendererType(type)
, m_rendererInitialized(false)
{
}
void RasterImageLayer::ensureRenderer()
{
if (m_rendererInitialized) return;
if (!m_layer) return;
RasterDataProvider* provider = nullptr;
if (m_layer->dataProvider()) {
if (m_layer->dataProvider())
{
provider = m_layer->dataProvider();
} else if (m_layer->openDataProvider()) {
}
else if (m_layer->openDataProvider())
{
provider = m_layer->dataProvider();
}
if (!provider) return;
switch (type) {
case RendererType::Multiband: {
switch (m_rendererType)
{
case RendererType::Multiband:
{
auto* multiRenderer = new MultibandRasterRenderer(provider);
m_renderer.reset(multiRenderer);
multiRenderer->setParams(m_multibandParams);
break;
}
case RendererType::Singleband: {
case RendererType::Singleband:
{
auto* singleRenderer = new SinglebandRasterRenderer(provider);
m_renderer.reset(singleRenderer);
singleRenderer->setParams(m_singlebandParams);
break;
}
}
m_rendererInitialized = true;
}
RasterImageLayer::~RasterImageLayer()
@ -43,6 +57,7 @@ RasterImageLayer::~RasterImageLayer()
QImage RasterImageLayer::render()
{
ensureRenderer();
if (!m_renderer) return QImage();
return m_renderer->render();
}
@ -50,7 +65,7 @@ QImage RasterImageLayer::render()
void RasterImageLayer::setMultibandParams(const MultibandRenderParams& params)
{
m_multibandParams = params;
if (m_rendererType == RendererType::Multiband) {
if (m_rendererInitialized && m_rendererType == RendererType::Multiband) {
auto* r = static_cast<MultibandRasterRenderer*>(m_renderer.get());
r->setParams(params);
}
@ -59,7 +74,7 @@ void RasterImageLayer::setMultibandParams(const MultibandRenderParams& params)
void RasterImageLayer::setSinglebandParams(const SinglebandRenderParams& params)
{
m_singlebandParams = params;
if (m_rendererType == RendererType::Singleband) {
if (m_rendererInitialized && m_rendererType == RendererType::Singleband) {
auto* r = static_cast<SinglebandRasterRenderer*>(m_renderer.get());
r->setParams(params);
}

View File

@ -22,6 +22,7 @@ public:
RasterImageLayer(RasterLayer* layer, RendererType type);
~RasterImageLayer();
void ensureRenderer();
QImage render();
RasterLayer* layer() const { return m_layer; }
@ -41,4 +42,5 @@ private:
std::unique_ptr<RasterRendererBase> m_renderer;
MultibandRenderParams m_multibandParams;
SinglebandRenderParams m_singlebandParams;
bool m_rendererInitialized = false;
};

View File

@ -78,3 +78,21 @@ std::vector<double> RasterLayer::bandWavelengths() const
}
return m_provider->bandWavelengths();
}
int RasterLayer::width() const
{
if (!m_provider) {
auto* self = const_cast<RasterLayer*>(this);
if (!self->openDataProvider()) return 0;
}
return m_provider->width();
}
int RasterLayer::height() const
{
if (!m_provider) {
auto* self = const_cast<RasterLayer*>(this);
if (!self->openDataProvider()) return 0;
}
return m_provider->height();
}

View File

@ -31,6 +31,10 @@ public:
// Get all band wavelengths
std::vector<double> bandWavelengths() const;
// Get dimensions
int width() const;
int height() const;
protected:
std::unique_ptr<RasterDataProvider> m_provider;
};

View File

@ -69,7 +69,7 @@ QPushButton:pressed
padding-bottom: 7px;
}</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
@ -97,21 +97,8 @@ QPushButton:pressed
</spacer>
</item>
<item row="1" column="1">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QPushButton" name="closeSLRCamera_btn">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>关 闭</string>
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="openSLRCamera_btn">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
@ -124,9 +111,22 @@ QPushButton:pressed
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="closeSLRCamera_btn">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>关 闭</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="2">
<item row="2" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -139,7 +139,52 @@ QPushButton:pressed
</property>
</spacer>
</item>
<item row="2" column="1">
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="styleSheet">
<string notr="true">QLabel {
color: rgb(255, 255, 255);
}</string>
</property>
<property name="text">
<string>数据路径</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="dataFolderLineEdit">
<property name="styleSheet">
<string notr="true">QLineEdit {
background-color: #142D7F;
color: #e6eeff;
border: 1px solid #2f6bff;
border-radius: 6px;
padding: 4px 8px;
min-width: 70px;
min-height: 20px;
font-size: 13px;
}</string>
</property>
<property name="text">
<string>./CapturedImages</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="dataFolderBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="1">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -152,7 +197,42 @@ QPushButton:pressed
</property>
</spacer>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QPushButton" name="takePhoto_btn">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>拍 摄</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="stopTakePhoto_btn">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>停 拍</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
<zorder>dataFolderLineEdit</zorder>
<zorder>dataFolderBtn</zorder>
<zorder>label</zorder>
<zorder>openSLRCamera_btn</zorder>
<zorder>layoutWidget</zorder>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>

File diff suppressed because it is too large Load Diff

View File

@ -5,8 +5,13 @@
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QImage>
#include <Qthread>
#include <QThread>
#include <QDir>
#include <QTimer>
#include <QMutex>
#include <QDateTime>
#include <QLabel>
#include <QFileDialog>
#include <iostream>
#include "ui_SingleLensReflexCamera.h"
@ -14,9 +19,15 @@
#include <fstream>
#include <opencv2/opencv.hpp>
// 包含Canon EDSDK头文件
#include "EDSDK.h"
#include "EDSDKTypes.h"
#include "EDSDKErrors.h"
typedef void(*func)();
class SingleLensReflexCameraOperation :public QObject
class SingleLensReflexCameraOperation : public QObject
{
Q_OBJECT
@ -27,25 +38,88 @@ public:
QImage m_colorImage;
QImage m_depthImage;
void setCallback(void(*func)());
bool getRecordStatus() const { return record; }
bool getRecordStatus() const { return m_isRecord; }
// 设置保存路径
void setSavePath(const QString& path);
// 获取当前实时取景图像
QImage getCurrentLiveViewImage();
void setCaptureInterval(int captureIntervalSeconds);
private:
cv::Mat frame;
func m_func;
bool m_isRecord;
bool record;
// Canon EDSDK相关成员
EdsCameraRef m_camera;
bool m_isSDKInitialized;
bool m_isSessionOpen;
bool m_isLiveViewActive;
QTimer* m_captureTimer; // 拍照定时器
QTimer* m_liveViewTimer; // 实时取景定时器
QString m_savePath;
int m_imageCounter;
int m_captureIntervalMilliseconds;
QMutex m_mutex;
QMutex m_liveViewMutex;
QImage m_liveViewImage; // 当前实时取景图像
// EDSDK辅助函数
EdsError initializeSDK();
EdsError terminateSDK();
EdsError openCamera();
EdsError closeCamera();
EdsError takePicture();
EdsError setupSaveToHost();
// 实时取景相关函数
EdsError startLiveView();
EdsError stopLiveView();
EdsError downloadEvfImage();
// 静态回调函数
static EdsError EDSCALLBACK handleObjectEvent(EdsObjectEvent event, EdsBaseRef object, EdsVoid* context);
static EdsError EDSCALLBACK handlePropertyEvent(EdsPropertyEvent event, EdsPropertyID property, EdsUInt32 param, EdsVoid* context);
static EdsError EDSCALLBACK handleStateEvent(EdsStateEvent event, EdsUInt32 parameter, EdsVoid* context);
// 下载图像
EdsError downloadImage(EdsDirectoryItemRef directoryItem);
public slots:
void OpenSLRCamera();
void OpenSLRCamera_callback();//不使用信号而使用回调函数来通知界面刷新视频
void takePhoto();
void OpenAndTakePhotoSLRCamera();
void stopTakePhoto();
void OpenSLRCamera_callback();
void CloseSLRCamera();
void onCaptureTimer();
void onLiveViewTimer();
// 控制拍照
void startCapture(); // 开始定时拍照
void stopCapture(); // 停止定时拍照
void captureOnce(); // 拍一张照片
signals:
void PlotSignal();
void CamOpenedSignal();
void CamClosedSignal();
void ImageCapturedSignal(const QString& filePath);
void ErrorSignal(const QString& errorMsg);
// 实时取景信号
void LiveViewImageReady(const QImage& image);
void LiveViewStarted();
void LiveViewStopped();
void CaptureStartedSignal();
void CaptureStoppedSignal();
};
class SingleLensReflexCameraWindow : public QDialog
@ -57,20 +131,59 @@ public:
~SingleLensReflexCameraWindow();
SingleLensReflexCameraOperation* m_SingleLensReflexCameraOperation;
void setDataFolder(QString dir);
void setCaptureInterval(int captureIntervalSeconds);
public Q_SLOTS:
void onSelectDataFolder();
void openSLRCamera();
void takePhoto();
void OpenAndTakePhotoSLRCamera();
void stopTakePhoto();
void onCamOpened();
void closeSLRCamera();
void onCamClosed();
void onImageCaptured(const QString& filePath);
void onError(const QString& errorMsg);
// 拍照控制
void onStartCapture();
void onStopCapture();
void onCaptureOnce();
void onCaptureStarted();
void onCaptureStopped();
void onStartTimedDataCollection(int lineNum);
void onStopTimedDataCollection();
signals:
void openSLRCameraSignal();
void takePhotoSignal();
void OpenAndTakePhotoSLRCameraSignal();
void stopTakePhotoSignal();
void closeSLRCameraSignal();
void PlotSLRImageSignal();
void SLRCamClosedSignal();
// 控制信号
void startCaptureSignal();
void stopCaptureSignal();
void captureOnceSignal();
// 实时取景信号
void LiveViewImageReady(const QImage& image);
void LiveViewStarted();
void LiveViewStopped();
private:
Ui::SingleLensReflexCameraClass ui;
QThread* m_SLRCameraThread;
// 用于显示实时取景的标签如果UI中没有可以动态创建
QLabel* m_liveViewLabel;
QString m_btnOldText; // 存储拍照按钮的原始文本
bool m_isCapturing; // 是否正在定时拍照
};

654
HPPA/TaskTreeModel.cpp Normal file
View File

@ -0,0 +1,654 @@
#include "TaskTreeModel.h"
#include <QBrush>
#include <QFont>
#include <QIcon>
// ==================== TaskTreeNode 实现 ====================
TaskTreeNode::TaskTreeNode(TaskTreeNode* parent)
: m_parent(parent)
{
}
TaskTreeNode::~TaskTreeNode()
{
qDeleteAll(m_children);
m_children.clear();
}
void TaskTreeNode::appendChild(TaskTreeNode* child)
{
m_children.append(child);
}
void TaskTreeNode::removeChild(int index)
{
if (index < 0 || index >= m_children.size())
return;
delete m_children.takeAt(index);
}
TaskTreeNode* TaskTreeNode::child(int row)
{
if (row < 0 || row >= m_children.size())
return nullptr;
return m_children.at(row);
}
int TaskTreeNode::childCount() const
{
return m_children.size();
}
int TaskTreeNode::row() const
{
if (m_parent) {
return m_parent->m_children.indexOf(const_cast<TaskTreeNode*>(this));
}
return 0;
}
TaskTreeNode* TaskTreeNode::parentNode()
{
return m_parent;
}
// ==================== TaskTreeModel 实现 ====================
TaskTreeModel::TaskTreeModel(QObject* parent)
: QAbstractItemModel(parent)
, m_rootNode(new TaskTreeNode())
, m_countdownTimer(new QTimer(this))
{
m_rootNode->nodeType = TreeNodeType::Root;
m_countdownTimer->setInterval(1000);
connect(m_countdownTimer, &QTimer::timeout, this, &TaskTreeModel::onCountdownTimerTimeout);
}
TaskTreeModel::~TaskTreeModel()
{
m_countdownTimer->stop();
delete m_rootNode;
}
QModelIndex TaskTreeModel::index(int row, int column, const QModelIndex& parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
TaskTreeNode* parentNode;
if (!parent.isValid())
parentNode = m_rootNode;
else
parentNode = static_cast<TaskTreeNode*>(parent.internalPointer());
TaskTreeNode* childNode = parentNode->child(row);
if (childNode)
return createIndex(row, column, childNode);
return QModelIndex();
}
QModelIndex TaskTreeModel::parent(const QModelIndex& index) const
{
if (!index.isValid())
return QModelIndex();
TaskTreeNode* childNode = static_cast<TaskTreeNode*>(index.internalPointer());
TaskTreeNode* parentNode = childNode->parentNode();
if (parentNode == m_rootNode)
return QModelIndex();
return createIndex(parentNode->row(), 0, parentNode);
}
int TaskTreeModel::rowCount(const QModelIndex& parent) const
{
TaskTreeNode* parentNode;
if (!parent.isValid())
parentNode = m_rootNode;
else
parentNode = static_cast<TaskTreeNode*>(parent.internalPointer());
return parentNode->childCount();
}
int TaskTreeModel::columnCount(const QModelIndex& parent) const
{
Q_UNUSED(parent)
return ColumnCount;
}
QVariant TaskTreeModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid())
return QVariant();
TaskTreeNode* node = static_cast<TaskTreeNode*>(index.internalPointer());
if (role == Qt::DisplayRole) {
if (node->nodeType == TreeNodeType::Task && node->taskData) {
const TimedTask& task = *node->taskData;
switch (index.column()) {
case ColName:
return QString::fromLocal8Bit("定时任务 %1").arg(task.id);
case ColScheduledTime:
return task.scheduledTime.toString("yyyy-MM-dd HH:mm:ss");
case ColCountdown: {
if (task.status == TaskStatus::Finished || task.status == TaskStatus::Running)
{
return QString::fromLocal8Bit("0");
}
qint64 seconds = QDateTime::currentDateTime().secsTo(task.scheduledTime);
if (seconds < 0)
{
return QString::fromLocal8Bit("已超时");
}
return formatCountdown(seconds);
}
case ColStartTime:
return task.startTime.isValid() ?
task.startTime.toString("HH:mm:ss") : "-";
case ColEndTime:
return task.endTime.isValid() ?
task.endTime.toString("HH:mm:ss") : "-";
case ColDuration:
return formatDuration(task.durationMinutes);
case ColEstimatedDuration:
return formatDuration(task.estimatedDurationMinutes);
case ColStatus:
return statusToString(task.status);
case ColProgress: {
int finished = 0;
for (const auto& sub : task.subTasks) {
if (sub.status == TaskStatus::Finished) finished++;
}
return QString("%1/%2").arg(finished).arg(task.subTasks.size());
}
case ColPath:
return task.savePath;
}
}
else if (node->nodeType == TreeNodeType::SubTask && node->subTaskData) {
const SubTask& subTask = *node->subTaskData;
switch (index.column()) {
case ColName:
return subTaskTypeToString(subTask.type);
case ColScheduledTime:
return "-";
case ColCountdown:
return "-";
case ColStartTime:
return subTask.startTime.isValid() ?
subTask.startTime.toString("HH:mm:ss") : "-";
case ColEndTime:
return subTask.endTime.isValid() ?
subTask.endTime.toString("HH:mm:ss") : "-";
case ColDuration:
return formatDuration(subTask.durationMinutes);
case ColEstimatedDuration:
return formatDuration(subTask.estimatedDurationMinutes);
case ColStatus:
return statusToString(subTask.status);
case ColProgress:
return "-";
case ColPath:
return "-";
}
}
}
else if (role == Qt::BackgroundRole) {
TaskStatus status = TaskStatus::Waiting;
if (node->nodeType == TreeNodeType::Task && node->taskData) {
status = node->taskData->status;
}
else if (node->nodeType == TreeNodeType::SubTask && node->subTaskData) {
status = node->subTaskData->status;
}
return QBrush(statusColor(status));
}
else if (role == Qt::ForegroundRole) {
TaskStatus status = TaskStatus::Waiting;
if (node->nodeType == TreeNodeType::Task && node->taskData) {
status = node->taskData->status;
}
else if (node->nodeType == TreeNodeType::SubTask && node->subTaskData) {
status = node->subTaskData->status;
}
if (status == TaskStatus::Running) {
return QBrush(QColor(0, 100, 0)); // 深绿色文字
}
}
else if (role == Qt::FontRole) {
QFont font;
if (node->nodeType == TreeNodeType::Task) {
font.setBold(true);
if (node->taskData && node->taskData->status == TaskStatus::Running) {
font.setItalic(true);
}
}
else if (node->nodeType == TreeNodeType::SubTask) {
if (node->subTaskData && node->subTaskData->status == TaskStatus::Running) {
font.setBold(true);
}
}
return font;
}
else if (role == Qt::DecorationRole && index.column() == ColName) {
if (node->nodeType == TreeNodeType::Task) {
// 可以返回任务图标
// return QIcon(":/icons/task.png");
}
else if (node->nodeType == TreeNodeType::SubTask && node->subTaskData) {
// 根据子任务类型返回不同图标
switch (node->subTaskData->type) {
case SubTaskType::HyperSpectual400_1000nm:
case SubTaskType::HyperSpectual1000_1700nm:
// return QIcon(":/icons/hyperspectral.png");
break;
case SubTaskType::SingleLensReflex:
// return QIcon(":/icons/camera.png");
break;
case SubTaskType::DepthCamera:
// return QIcon(":/icons/depth.png");
break;
}
}
}
else if (role == Qt::TextAlignmentRole) {
if (index.column() == ColName || index.column() == ColPath) {
return static_cast<int>(Qt::AlignLeft | Qt::AlignVCenter);
}
return Qt::AlignCenter;
}
else if (role == Qt::ToolTipRole) {
if (node->nodeType == TreeNodeType::Task && node->taskData) {
return QString::fromLocal8Bit("任务ID: %1\n保存路径: %2\n预热时间: %3 分钟")
.arg(node->taskData->id)
.arg(node->taskData->savePath)
.arg(node->taskData->HalogenLampPreheatingTime_Minute);
}
else if (node->nodeType == TreeNodeType::SubTask && node->subTaskData) {
QString tip = QString::fromLocal8Bit("类型: %1\n路径文件: %2")
.arg(subTaskTypeToString(node->subTaskData->type))
.arg(node->subTaskData->pathLineFilePath);
if (node->subTaskData->type == SubTaskType::HyperSpectual400_1000nm ||
node->subTaskData->type == SubTaskType::HyperSpectual1000_1700nm) {
tip += QString::fromLocal8Bit("\n帧率: %1\n曝光时间: %2")
.arg(node->subTaskData->frameRate)
.arg(node->subTaskData->exposureTime);
}
return tip;
}
}
return QVariant();
}
QVariant TaskTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
return QVariant();
switch (section) {
case ColName: return QString::fromLocal8Bit("名称");
case ColScheduledTime: return QString::fromLocal8Bit("计划时间");
case ColCountdown: return QString::fromLocal8Bit("倒计时");
case ColStartTime: return QString::fromLocal8Bit("开始时间");
case ColEndTime: return QString::fromLocal8Bit("结束时间");
case ColDuration: return QString::fromLocal8Bit("耗时");
case ColEstimatedDuration: return QString::fromLocal8Bit("预测耗时");
case ColStatus: return QString::fromLocal8Bit("状态");
case ColProgress: return QString::fromLocal8Bit("进度");
case ColPath: return QString::fromLocal8Bit("路径");
}
return QVariant();
}
Qt::ItemFlags TaskTreeModel::flags(const QModelIndex& index) const
{
if (!index.isValid())
return Qt::NoItemFlags;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
void TaskTreeModel::setTasks(const QVector<TimedTask>& tasks)
{
m_countdownTimer->stop();
beginResetModel();
m_tasks = tasks;
clearTree();
setupModelData();
endResetModel();
m_countdownTimer->start();
}
void TaskTreeModel::setupModelData()
{
for (int i = 0; i < m_tasks.size(); ++i) {
TimedTask& task = m_tasks[i];
// 创建任务节点
TaskTreeNode* taskNode = new TaskTreeNode(m_rootNode);
taskNode->nodeType = TreeNodeType::Task;
taskNode->taskId = task.id;
taskNode->taskData = &task;
m_rootNode->appendChild(taskNode);
// 为每个子任务创建节点
for (int j = 0; j < task.subTasks.size(); ++j) {
SubTask& subTask = task.subTasks[j];
TaskTreeNode* subTaskNode = new TaskTreeNode(taskNode);
subTaskNode->nodeType = TreeNodeType::SubTask;
subTaskNode->taskId = task.id;
subTaskNode->subTaskIndex = j;
subTaskNode->subTaskData = &subTask;
taskNode->appendChild(subTaskNode);
}
}
}
void TaskTreeModel::clearTree()
{
// 删除所有子节点
while (m_rootNode->childCount() > 0) {
//delete m_rootNode->child(0);
m_rootNode->removeChild(0);
}
}
void TaskTreeModel::updateTask(const TimedTask& task)
{
int dataIndex = findTaskDataIndex(task.id);
if (dataIndex < 0) return;
// 更新数据
m_tasks[dataIndex] = task;
// 重新设置指针(因为数据被复制了)
TaskTreeNode* taskNode = findTaskNode(task.id);
if (taskNode) {
taskNode->taskData = &m_tasks[dataIndex];
// 更新子任务指针
for (int i = 0; i < taskNode->childCount() && i < m_tasks[dataIndex].subTasks.size(); ++i) {
TaskTreeNode* subNode = taskNode->child(i);
if (subNode) {
subNode->subTaskData = &m_tasks[dataIndex].subTasks[i];
}
}
}
// 通知视图更新任务行
QModelIndex taskIndex = getTaskIndex(task.id);
if (taskIndex.isValid()) {
QModelIndex topLeft = index(taskIndex.row(), 0, taskIndex.parent());
QModelIndex bottomRight = index(taskIndex.row(), ColumnCount - 1, taskIndex.parent());
emit dataChanged(topLeft, bottomRight);
// 更新所有子任务行
int subCount = rowCount(taskIndex);
if (subCount > 0) {
QModelIndex subTopLeft = index(0, 0, taskIndex);
QModelIndex subBottomRight = index(subCount - 1, ColumnCount - 1, taskIndex);
emit dataChanged(subTopLeft, subBottomRight);
}
}
emit taskUpdated(task.id);
}
void TaskTreeModel::updateSubTask(int taskId, int subTaskIndex, const SubTask& subTask)
{
int dataIndex = findTaskDataIndex(taskId);
if (dataIndex < 0 || subTaskIndex >= m_tasks[dataIndex].subTasks.size())
return;
m_tasks[dataIndex].subTasks[subTaskIndex] = subTask;
// 重新设置指针
TaskTreeNode* subNode = findSubTaskNode(taskId, subTaskIndex);
if (subNode) {
subNode->subTaskData = &m_tasks[dataIndex].subTasks[subTaskIndex];
}
QModelIndex subTaskModelIndex = getSubTaskIndex(taskId, subTaskIndex);
if (subTaskModelIndex.isValid()) {
QModelIndex topLeft = index(subTaskModelIndex.row(), 0, subTaskModelIndex.parent());
QModelIndex bottomRight = index(subTaskModelIndex.row(), ColumnCount - 1, subTaskModelIndex.parent());
emit dataChanged(topLeft, bottomRight);
}
// 同时更新父任务的进度显示
QModelIndex taskIndex = getTaskIndex(taskId);
if (taskIndex.isValid()) {
QModelIndex progressIndex = index(taskIndex.row(), ColProgress, taskIndex.parent());
emit dataChanged(progressIndex, progressIndex);
}
emit subTaskUpdated(taskId, subTaskIndex);
}
void TaskTreeModel::setTaskAndSubtaskStatus(int taskId, TaskStatus status)
{
int dataIndex = findTaskDataIndex(taskId);
if (dataIndex < 0) return;
updateTaskStatus(taskId, status);
for (size_t i = 0; i < m_tasks[dataIndex].subTaskCount(); i++)
{
updateSubTaskStatus(taskId, i, status);
}
}
void TaskTreeModel::updateTaskStatus(int taskId, TaskStatus status)
{
int dataIndex = findTaskDataIndex(taskId);
if (dataIndex < 0) return;
m_tasks[dataIndex].status = status;
QModelIndex taskIndex = getTaskIndex(taskId);
if (taskIndex.isValid()) {
QModelIndex topLeft = index(taskIndex.row(), 0, taskIndex.parent());
QModelIndex bottomRight = index(taskIndex.row(), ColumnCount - 1, taskIndex.parent());
emit dataChanged(topLeft, bottomRight);
}
}
void TaskTreeModel::updateSubTaskStatusAndProgress(int taskId, int subTaskIndex, TaskStatus status)
{
updateSubTaskStatus(taskId, subTaskIndex, status);
updateProgress(taskId);
}
void TaskTreeModel::updateSubTaskStatus(int taskId, int subTaskIndex, TaskStatus status)
{
int dataIndex = findTaskDataIndex(taskId);
if (dataIndex < 0 || subTaskIndex >= m_tasks[dataIndex].subTasks.size())
return;
SubTask& subTask = m_tasks[dataIndex].subTasks[subTaskIndex];
subTask.status = status;
QModelIndex subTaskModelIndex = getSubTaskIndex(taskId, subTaskIndex);
if (subTaskModelIndex.isValid()) {
QModelIndex topLeft = index(subTaskModelIndex.row(), 0, subTaskModelIndex.parent());
QModelIndex bottomRight = index(subTaskModelIndex.row(), ColumnCount - 1, subTaskModelIndex.parent());
emit dataChanged(topLeft, bottomRight);
}
}
void TaskTreeModel::updateProgress(int taskId)
{
// 更新父任务进度
QModelIndex taskIndex = getTaskIndex(taskId);
if (taskIndex.isValid()) {
QModelIndex progressIndex = index(taskIndex.row(), ColProgress, taskIndex.parent());
emit dataChanged(progressIndex, progressIndex);
}
}
TimedTask* TaskTreeModel::getTask(int taskId)
{
int idx = findTaskDataIndex(taskId);
if (idx >= 0) {
return &m_tasks[idx];
}
return nullptr;
}
SubTask* TaskTreeModel::getSubTask(int taskId, int subTaskIndex)
{
TimedTask* task = getTask(taskId);
if (task && subTaskIndex >= 0 && subTaskIndex < task->subTasks.size()) {
return &task->subTasks[subTaskIndex];
}
return nullptr;
}
QModelIndex TaskTreeModel::getTaskIndex(int taskId) const
{
for (int i = 0; i < m_rootNode->childCount(); ++i) {
TaskTreeNode* node = m_rootNode->child(i);
if (node && node->taskId == taskId) {
return createIndex(i, 0, node);
}
}
return QModelIndex();
}
QModelIndex TaskTreeModel::getSubTaskIndex(int taskId, int subTaskIndex) const
{
TaskTreeNode* taskNode = findTaskNode(taskId);
if (taskNode && subTaskIndex < taskNode->childCount()) {
TaskTreeNode* subNode = taskNode->child(subTaskIndex);
if (subNode) {
return createIndex(subTaskIndex, 0, subNode);
}
}
return QModelIndex();
}
TaskTreeNode* TaskTreeModel::findTaskNode(int taskId) const
{
for (int i = 0; i < m_rootNode->childCount(); ++i) {
TaskTreeNode* node = m_rootNode->child(i);
if (node && node->taskId == taskId) {
return node;
}
}
return nullptr;
}
TaskTreeNode* TaskTreeModel::findSubTaskNode(int taskId, int subTaskIndex) const
{
TaskTreeNode* taskNode = findTaskNode(taskId);
if (taskNode && subTaskIndex < taskNode->childCount()) {
return taskNode->child(subTaskIndex);
}
return nullptr;
}
int TaskTreeModel::findTaskDataIndex(int taskId) const
{
for (int i = 0; i < m_tasks.size(); ++i) {
if (m_tasks[i].id == taskId) {
return i;
}
}
return -1;
}
QString TaskTreeModel::statusToString(TaskStatus status) const
{
switch (status) {
case TaskStatus::default: return QString::fromLocal8Bit("未调度");
case TaskStatus::Waiting: return QString::fromLocal8Bit("等待中");
case TaskStatus::Running: return QString::fromLocal8Bit("运行中");
case TaskStatus::Finished: return QString::fromLocal8Bit("已完成");
case TaskStatus::Skiped: return QString::fromLocal8Bit("已跳过");
case TaskStatus::Timeout: return QString::fromLocal8Bit("超时");
}
return "";
}
QString TaskTreeModel::subTaskTypeToString(SubTaskType type) const
{
switch (type) {
case SubTaskType::HyperSpectual400_1000nm: return QString::fromLocal8Bit("高光谱 400-1000nm");
case SubTaskType::HyperSpectual1000_1700nm: return QString::fromLocal8Bit("高光谱 1000-1700nm");
case SubTaskType::SingleLensReflex: return QString::fromLocal8Bit("单反相机");
case SubTaskType::DepthCamera: return QString::fromLocal8Bit("深度相机");
}
return "未知类型";
}
QString TaskTreeModel::formatDuration(double minutes) const
{
if (minutes <= 0) return "-";
int hours = static_cast<int>(minutes) / 60;
int mins = static_cast<int>(minutes) % 60;
int secs = static_cast<int>((minutes - static_cast<int>(minutes)) * 60);
if (hours > 0) {
return QString::fromLocal8Bit(("%1时%2分%3秒")).arg(hours).arg(mins).arg(secs);
}
else if (mins > 0) {
return QString::fromLocal8Bit(("%1分%2秒")).arg(mins).arg(secs);
}
else {
return QString::fromLocal8Bit(("%1秒")).arg(secs);
}
}
QColor TaskTreeModel::statusColor(TaskStatus status) const
{
switch (status) {
case TaskStatus::Waiting: return QColor(255, 255, 230); // 浅黄
case TaskStatus::Running: return QColor(200, 255, 200); // 浅绿
case TaskStatus::Finished: return QColor(220, 220, 255); // 浅蓝
case TaskStatus::Skiped: return QColor(220, 220, 220); // 浅灰
}
return QColor(255, 255, 255);
}
QString TaskTreeModel::formatCountdown(qint64 seconds) const
{
if (seconds < 0) {
return QString::fromLocal8Bit("已超时");
}
int hours = static_cast<int>(seconds) / 3600;
int mins = (static_cast<int>(seconds) % 3600) / 60;
int secs = static_cast<int>(seconds) % 60;
return QString("%1:%2:%3")
.arg(hours, 2, 10, QChar('0'))
.arg(mins, 2, 10, QChar('0'))
.arg(secs, 2, 10, QChar('0'));
}
void TaskTreeModel::onCountdownTimerTimeout()
{
for (int i = 0; i < m_tasks.size(); ++i)
{
const TimedTask& task = m_tasks[i];
QModelIndex taskIndex = getTaskIndex(task.id);
if (taskIndex.isValid())
{
QModelIndex countdownIndex = index(taskIndex.row(), ColCountdown, taskIndex.parent());
emit dataChanged(countdownIndex, countdownIndex);
}
}
}

118
HPPA/TaskTreeModel.h Normal file
View File

@ -0,0 +1,118 @@
#pragma once
#include <QAbstractItemModel>
#include <QModelIndex>
#include <QVariant>
#include <QTimer>
#include "TimedDataCollectionDataStructures.h"
// 树节点类型
enum class TreeNodeType {
Root,
Task,
SubTask
};
// 树节点数据
class TaskTreeNode
{
public:
explicit TaskTreeNode(TaskTreeNode* parent = nullptr);
~TaskTreeNode();
void appendChild(TaskTreeNode* child);
void removeChild(int index);
TaskTreeNode* child(int row);
int childCount() const;
int row() const;
TaskTreeNode* parentNode();
TreeNodeType nodeType = TreeNodeType::Root;
int taskId = -1; // 任务ID
int subTaskIndex = -1; // 子任务索引(仅子任务节点有效)
// 直接存储数据指针,方便更新
TimedTask* taskData = nullptr;
SubTask* subTaskData = nullptr;
private:
QVector<TaskTreeNode*> m_children;
TaskTreeNode* m_parent;
};
// 树形模型
class TaskTreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
enum Column {
ColName = 0, // 任务名称/子任务类型
ColScheduledTime, // 计划时间
ColCountdown, // 倒计时
ColStartTime, // 开始时间
ColEndTime, // 结束时间
ColDuration, // 耗时
ColEstimatedDuration, // 预测耗时
ColStatus, // 状态
ColProgress, // 进度(仅任务有效)
ColPath, // 路径
ColumnCount
};
explicit TaskTreeModel(QObject* parent = nullptr);
~TaskTreeModel();
// QAbstractItemModel 接口实现
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex& index) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
// 数据操作
void setTasks(const QVector<TimedTask>& tasks);
void updateTask(const TimedTask& task);
void updateSubTask(int taskId, int subTaskIndex, const SubTask& subTask);
void updateTaskStatus(int taskId, TaskStatus status);
void setTaskAndSubtaskStatus(int taskId, TaskStatus status);
void updateSubTaskStatusAndProgress(int taskId, int subTaskIndex, TaskStatus status);
void updateSubTaskStatus(int taskId, int subTaskIndex, TaskStatus status);
void updateProgress(int taskId);
// 获取数据
QVector<TimedTask>& tasks() { return m_tasks; }
const QVector<TimedTask>& tasks() const { return m_tasks; }
TimedTask* getTask(int taskId);
SubTask* getSubTask(int taskId, int subTaskIndex);
// 获取模型索引
QModelIndex getTaskIndex(int taskId) const;
QModelIndex getSubTaskIndex(int taskId, int subTaskIndex) const;
signals:
void taskUpdated(int taskId);
void subTaskUpdated(int taskId, int subTaskIndex);
private slots:
void onCountdownTimerTimeout();
private:
void setupModelData();
void clearTree();
TaskTreeNode* findTaskNode(int taskId) const;
TaskTreeNode* findSubTaskNode(int taskId, int subTaskIndex) const;
int findTaskDataIndex(int taskId) const;
QString statusToString(TaskStatus status) const;
QString subTaskTypeToString(SubTaskType type) const;
QString formatDuration(double minutes) const;
QColor statusColor(TaskStatus status) const;
QString formatCountdown(qint64 seconds) const;
TaskTreeNode* m_rootNode;
QVector<TimedTask> m_tasks;
QTimer* m_countdownTimer;
};

View File

@ -0,0 +1,506 @@
#include "TimedDataCollection.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QMessageBox>
#include <QGroupBox>
#include <QSplitter>
TimedDataCollection::TimedDataCollection(QWidget* parent)
: QDialog(parent)
{
ui.setupUi(this);
setupUI();
// 初始化调度器
m_scheduler = new TaskScheduler(this);
setupConnections();
// 连接按钮信号
connect(ui.start_btn, &QPushButton::clicked, this, &TimedDataCollection::startScheduler);
connect(ui.stop_btn, &QPushButton::clicked, this, &TimedDataCollection::stopScheduler);
connect(ui.expandAll_btn, &QPushButton::clicked, ui.taskTreeView, &QTreeView::expandAll);
connect(ui.collapseAll_btn, &QPushButton::clicked, ui.taskTreeView, &QTreeView::collapseAll);
connect(ui.taskFileSelect_btn, &QPushButton::clicked, this, &TimedDataCollection::onSelectTaskFile);
// 树视图交互信号
connect(ui.taskTreeView, &QTreeView::clicked,
this, &TimedDataCollection::onTreeItemClicked);
connect(ui.taskTreeView, &QTreeView::doubleClicked,
this, &TimedDataCollection::onTreeItemDoubleClicked);
connect(ui.taskTreeView->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &TimedDataCollection::onTreeSelectionChanged);
}
void TimedDataCollection::setupUI()
{
// 设置窗口大小
//resize(1200, 600);
//setWindowTitle("定时数据采集系统");
// 创建任务树形模型
m_taskModel = new TaskTreeModel(this);
// 创建树形视图
ui.taskTreeView->setModel(m_taskModel);
// 配置树形视图
ui.taskTreeView->setSelectionBehavior(QAbstractItemView::SelectRows);
ui.taskTreeView->setSelectionMode(QAbstractItemView::SingleSelection);
ui.taskTreeView->setAlternatingRowColors(true);
ui.taskTreeView->setAnimated(true);
ui.taskTreeView->setExpandsOnDoubleClick(true);
ui.taskTreeView->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui.taskTreeView->setUniformRowHeights(true);
ui.taskTreeView->setIndentation(25);
// 配置表头
QHeaderView* header = ui.taskTreeView->header();
header->setStretchLastSection(true);
header->setSectionResizeMode(QHeaderView::Interactive);
header->setDefaultAlignment(Qt::AlignCenter);
// 设置列宽
ui.taskTreeView->setColumnWidth(TaskTreeModel::ColName, 200);
ui.taskTreeView->setColumnWidth(TaskTreeModel::ColScheduledTime, 150);
ui.taskTreeView->setColumnWidth(TaskTreeModel::ColCountdown, 100);
ui.taskTreeView->setColumnWidth(TaskTreeModel::ColStartTime, 100);
ui.taskTreeView->setColumnWidth(TaskTreeModel::ColEndTime, 100);
ui.taskTreeView->setColumnWidth(TaskTreeModel::ColDuration, 100);
ui.taskTreeView->setColumnWidth(TaskTreeModel::ColEstimatedDuration, 100);
ui.taskTreeView->setColumnWidth(TaskTreeModel::ColStatus, 100);
ui.taskTreeView->setColumnWidth(TaskTreeModel::ColProgress, 80);
// 创建状态标签
ui.statusLabel->setStyleSheet(
"QLabel {"
" background-color: #f0f0f0;"
" border: 1px solid #ccc;"
" border-radius: 4px;"
" padding: 8px;"
" font-size: 12px;"
"}"
);
}
void TimedDataCollection::setupConnections()
{
// 调度器信号连接
connect(m_scheduler, &TaskScheduler::taskStarted,
this, &TimedDataCollection::onTaskStarted);
connect(m_scheduler, &TaskScheduler::taskFinished,
this, &TimedDataCollection::onTaskFinished);
connect(m_scheduler, &TaskScheduler::subTaskStarted,
this, &TimedDataCollection::onSubTaskStarted);
connect(m_scheduler, &TaskScheduler::subTaskFinished,
this, &TimedDataCollection::onSubTaskFinished);
connect(m_scheduler, &TaskScheduler::taskDataChanged,
this, &TimedDataCollection::onTaskDataChanged);
connect(m_scheduler, &TaskScheduler::errorOccurred,
this, &TimedDataCollection::onErrorOccurred);
// 采集相关信号透传
connect(m_scheduler, &TaskScheduler::hyperCamParm,
this, &TimedDataCollection::hyperCamParm);
connect(m_scheduler, &TaskScheduler::camParm,
this, &TimedDataCollection::camParm);
connect(m_scheduler, &TaskScheduler::motorParm,
this, &TimedDataCollection::motorParm);
connect(m_scheduler, &TaskScheduler::startRecordSignal,
this, &TimedDataCollection::startRecordSignal);
connect(m_scheduler, &TaskScheduler::switchHalogenLampSignal,
this, &TimedDataCollection::switchHalogenLampSignal);
connect(m_scheduler, &TaskScheduler::switchD65LampSignal,
this, &TimedDataCollection::switchD65LampSignal);
connect(m_scheduler, &TaskScheduler::switchSlrSignal,
this, &TimedDataCollection::switchSlrSignal);
connect(this, &TimedDataCollection::sequenceCompleteSignal,
m_scheduler, &TaskScheduler::sequenceCompleteSignal);
connect(this, &TimedDataCollection::Back2OriginSignal,
m_scheduler, &TaskScheduler::Back2OriginSignal);
}
TimedDataCollection::~TimedDataCollection()
{
if (m_scheduler) {
m_scheduler->stop();
}
}
void TimedDataCollection::onSelectTaskFile()
{
if (m_scheduler->isRunning())
{
return;
}
QString filePath = QFileDialog::getOpenFileName(this,
tr("Select Task File"),
QString(),
tr("JSON Files (*.json);;All Files (*)"));
if (filePath.isEmpty()) {
return;
}
ui.taskFileSelect_lineEdit->setText(filePath);
readTimedTaskFromFile(filePath);
}
// ==================== 任务状态更新槽实现 ====================
void TimedDataCollection::onTaskStarted(int taskId)
{
m_currentTaskId = taskId;
m_currentSubTaskIndex = -1;
// 展开当前运行的任务
expandRunningTask();
updateStatusBar();
emit taskStarted(taskId);
}
void TimedDataCollection::onTaskFinished(int taskId, bool success)
{
if (m_currentTaskId == taskId) {
m_currentTaskId = -1;
m_currentSubTaskIndex = -1;
}
updateStatusBar();
emit taskFinished(taskId, success);
}
void TimedDataCollection::onSubTaskStarted(int taskId, int subTaskIndex)
{
m_currentSubTaskIndex = subTaskIndex;
m_taskModel->updateProgress(taskId);
// 滚动到当前子任务
QModelIndex subTaskIndex_model = m_taskModel->getSubTaskIndex(taskId, subTaskIndex);
if (subTaskIndex_model.isValid()) {
ui.taskTreeView->scrollTo(subTaskIndex_model);
ui.taskTreeView->setCurrentIndex(subTaskIndex_model);
}
updateStatusBar();
emit subTaskStarted(taskId, subTaskIndex);
}
void TimedDataCollection::onSubTaskFinished(int taskId, int subTaskIndex)
{
m_taskModel->updateSubTaskStatusAndProgress(taskId, subTaskIndex, TaskStatus::Finished);
updateStatusBar();
emit subTaskFinished(taskId, subTaskIndex);
}
void TimedDataCollection::onTaskDataChanged(const TimedTask& task)
{
// 核心:从 TaskExecutor 同步回来的完整任务数据
m_taskModel->updateTask(task);
updateStatusBar();
}
void TimedDataCollection::onErrorOccurred(const QString& error)
{
ui.statusLabel->setText(QString::fromLocal8Bit("错误: %1").arg(error));
ui.statusLabel->setStyleSheet(
"QLabel {"
" background-color: #ffebee;"
" border: 1px solid #f44336;"
" border-radius: 4px;"
" padding: 8px;"
" color: #c62828;"
"}"
);
QMessageBox::warning(this, "错误", error);
emit errorOccurred(error);
}
void TimedDataCollection::onTreeItemClicked(const QModelIndex& index)
{
if (!index.isValid()) return;
TaskTreeNode* node = static_cast<TaskTreeNode*>(index.internalPointer());
if (!node) return;
QString info;
if (node->nodeType == TreeNodeType::Task && node->taskData) {
info = QString::fromLocal8Bit("选中任务 %1 - 状态: %2")
.arg(node->taskData->id)
.arg(node->taskData->status == TaskStatus::Running ? "运行中" :
node->taskData->status == TaskStatus::Finished ? "已完成" : "等待中");
}
else if (node->nodeType == TreeNodeType::SubTask && node->subTaskData) {
info = QString::fromLocal8Bit("选中子任务 - 类型: %1")
.arg(m_taskModel->data(m_taskModel->index(index.row(),
TaskTreeModel::ColName, index.parent())).toString());
}
// 可以更新状态栏或其他UI
}
void TimedDataCollection::onTreeItemDoubleClicked(const QModelIndex& index)
{
if (!index.isValid()) return;
TaskTreeNode* node = static_cast<TaskTreeNode*>(index.internalPointer());
if (!node) return;
if (node->nodeType == TreeNodeType::Task && node->taskData) {
const TimedTask& task = *node->taskData;
QString details = QString::fromLocal8Bit(
"【任务详情】\n\n"
"任务ID: %1\n"
"计划时间: %2\n"
"开始时间: %3\n"
"结束时间: %4\n"
"耗时: %5 分钟\n"
"状态: %6\n"
"子任务数: %7\n"
"保存路径: %8\n"
"卤素灯预热时间: %9 分钟"
).arg(task.id)
.arg(task.scheduledTime.toString("yyyy-MM-dd HH:mm:ss"))
.arg(task.startTime.isValid() ? task.startTime.toString("yyyy-MM-dd HH:mm:ss") : "-")
.arg(task.endTime.isValid() ? task.endTime.toString("yyyy-MM-dd HH:mm:ss") : "-")
.arg(task.durationMinutes, 0, 'f', 2)
.arg(static_cast<int>(task.status))
.arg(task.subTasks.size())
.arg(task.savePath)
.arg(task.HalogenLampPreheatingTime_Minute);
QMessageBox::information(this, QString::fromLocal8Bit("任务详情"), details);
}
else if (node->nodeType == TreeNodeType::SubTask && node->subTaskData) {
const SubTask& subTask = *node->subTaskData;
QString typeStr;
switch (subTask.type) {
case SubTaskType::HyperSpectual400_1000nm: typeStr = QString::fromLocal8Bit("高光谱 400-1000nm"); break;
case SubTaskType::HyperSpectual1000_1700nm: typeStr = QString::fromLocal8Bit("高光谱 1000-1700nm"); break;
case SubTaskType::SingleLensReflex: typeStr = QString::fromLocal8Bit("单反相机"); break;
case SubTaskType::DepthCamera: typeStr = QString::fromLocal8Bit("深度相机"); break;
}
QString details = QString::fromLocal8Bit(
"【子任务详情】\n\n"
"类型: %1\n"
"开始时间: %2\n"
"结束时间: %3\n"
"耗时: %4 分钟\n"
"路径文件: %5\n"
"状态: %6"
).arg(typeStr)
.arg(subTask.startTime.isValid() ? subTask.startTime.toString("yyyy-MM-dd HH:mm:ss") : "-")
.arg(subTask.endTime.isValid() ? subTask.endTime.toString("yyyy-MM-dd HH:mm:ss") : "-")
.arg(subTask.durationMinutes, 0, 'f', 2)
.arg(subTask.pathLineFilePath)
.arg(static_cast<int>(subTask.status));
if (subTask.type == SubTaskType::HyperSpectual400_1000nm ||
subTask.type == SubTaskType::HyperSpectual1000_1700nm) {
details += QString::fromLocal8Bit("\n帧率: %1\n曝光时间: %2")
.arg(subTask.frameRate)
.arg(subTask.exposureTime);
}
QMessageBox::information(this, QString::fromLocal8Bit("子任务详情"), details);
}
}
void TimedDataCollection::onTreeSelectionChanged()
{
// 可以根据选择更新其他UI组件
}
void TimedDataCollection::updateStatusBar()
{
QString statusText;
QString styleSheet;
if (m_currentTaskId > 0) {
TimedTask* task = m_taskModel->getTask(m_currentTaskId);
if (task) {
int finished = 0;
for (const auto& sub : task->subTasks) {
if (sub.status == TaskStatus::Finished) finished++;
}
statusText = QString::fromLocal8Bit("▶ 正在执行任务 %1 | 子任务进度: %2/%3")
.arg(m_currentTaskId)
.arg(finished)
.arg(task->subTasks.size());
if (m_currentSubTaskIndex >= 0 && m_currentSubTaskIndex < task->subTasks.size()) {
QString subTypeStr;
switch (task->subTasks[m_currentSubTaskIndex].type) {
case SubTaskType::HyperSpectual400_1000nm: subTypeStr = QString::fromLocal8Bit("高光谱400-1000nm"); break;
case SubTaskType::HyperSpectual1000_1700nm: subTypeStr = QString::fromLocal8Bit("高光谱1000-1700nm"); break;
case SubTaskType::SingleLensReflex: subTypeStr = QString::fromLocal8Bit("单反相机"); break;
case SubTaskType::DepthCamera: subTypeStr = QString::fromLocal8Bit("深度相机"); break;
}
statusText += QString::fromLocal8Bit(" | 当前: %1").arg(subTypeStr);
}
styleSheet =
"QLabel {"
" background-color: #e8f5e9;"
" border: 1px solid #4CAF50;"
" border-radius: 4px;"
" padding: 8px;"
" color: #2e7d32;"
" font-weight: bold;"
"}";
}
}
else {
// 统计任务状态
int waiting = 0, running = 0, finished = 0;
for (const auto& task : m_taskModel->tasks()) {
switch (task.status) {
case TaskStatus::Waiting: waiting++; break;
case TaskStatus::Running: running++; break;
case TaskStatus::Finished: finished++; break;
default: break;
}
}
statusText = QString::fromLocal8Bit("任务统计 | 等待: %1 | 运行中: %2 | 已完成: %3 | 总计: %4")
.arg(waiting).arg(running).arg(finished)
.arg(m_taskModel->tasks().size());
styleSheet =
"QLabel {"
" background-color: #f5f5f5;"
" border: 1px solid #ccc;"
" border-radius: 4px;"
" padding: 8px;"
"}";
}
ui.statusLabel->setText(statusText);
ui.statusLabel->setStyleSheet(styleSheet);
}
void TimedDataCollection::expandRunningTask()
{
if (m_currentTaskId > 0) {
QModelIndex taskIndex = m_taskModel->getTaskIndex(m_currentTaskId);
if (taskIndex.isValid()) {
ui.taskTreeView->expand(taskIndex);
ui.taskTreeView->scrollTo(taskIndex);
ui.taskTreeView->setCurrentIndex(taskIndex);
}
}
}
void TimedDataCollection::readTimedTaskFromFile(const QString& filePath)
{
QVector<TimedTask> loadedTasks;
if (TimedDataCollectionDataStructuresReaderWriter::loadTasksFromFile(filePath, loadedTasks)) {
qDebug() << "Tasks loaded successfully, count:" << loadedTasks.size();
// 预测耗时:计算每个子任务和整个任务的预计耗时
for (int i = 0; i < loadedTasks.size(); ++i)
{
double totalEstimatedMinutes = 0.0;
for (int j = 0; j < loadedTasks[i].subTasks.size(); ++j)
{
QString pathLineFilePath = loadedTasks[i].subTasks[j].pathLineFilePath;
if (!pathLineFilePath.isEmpty())
{
double estimatedMinutes = PathLineManager::calculateTimeFromFile(pathLineFilePath);
loadedTasks[i].subTasks[j].estimatedDurationMinutes = estimatedMinutes;
totalEstimatedMinutes += loadedTasks[i].subTasks[j].estimatedDurationMinutes;
}
}
double slrTimeMinute = 135 / 60;
loadedTasks[i].estimatedDurationMinutes = totalEstimatedMinutes + loadedTasks[i].HalogenLampPreheatingTime_Minute + slrTimeMinute;
}
m_taskModel->setTasks(loadedTasks);
ui.taskTreeView->expandAll(); // 默认展开所有任务
// 更新调度器
if (m_scheduler)
{
m_scheduler->loadTasks(loadedTasks);
}
updateStatusBar();
}
else {
qDebug() << "Failed to load tasks from:" << filePath;
QMessageBox::warning(this, "加载失败",
QString("无法从文件加载任务:\n%1").arg(filePath));
}
}
void TimedDataCollection::startScheduler()
{
if (m_scheduler)
{
if (m_scheduler->isRunning())
{
return;
}
for (size_t i = 0; i < m_taskModel->tasks().size(); i++)
{
m_taskModel->setTaskAndSubtaskStatus(m_taskModel->tasks()[i].id, TaskStatus::Waiting);
}
m_scheduler->loadTasks(m_taskModel->tasks());
m_scheduler->start();
ui.statusLabel->setText(QString::fromLocal8Bit("调度器已启动,等待任务执行..."));
ui.statusLabel->setStyleSheet(
"QLabel {"
" background-color: #fff3e0;"
" border: 1px solid #ff9800;"
" border-radius: 4px;"
" padding: 8px;"
" color: #e65100;"
"}");
}
}
void TimedDataCollection::stopScheduler()
{
if (m_scheduler) {
m_scheduler->stop();
m_currentTaskId = -1;
m_currentSubTaskIndex = -1;
ui.statusLabel->setText(QString::fromLocal8Bit("调度器已停止"));
ui.statusLabel->setStyleSheet(
"QLabel {"
" background-color: #fce4ec;"
" border: 1px solid #e91e63;"
" border-radius: 4px;"
" padding: 8px;"
" color: #880e4f;"
"}");
}
}
void TimedDataCollection::subTaskCompleted(int status)
{
emit sequenceCompleteSignal(status);
}
void TimedDataCollection::onBack2Origin()
{
emit Back2OriginSignal();
}

View File

@ -0,0 +1,77 @@
#pragma once
#include <QDialog>
#include <QTreeView>
#include <QHeaderView>
#include <QLabel>
#include <QFileDialog>
#include "ui_TimedDataCollection_ui.h"
#include "TimedDataCollectionDataStructures.h"
#include "TaskTreeModel.h"
#include "PathLine.h"
class TimedDataCollection : public QDialog
{
Q_OBJECT
public:
TimedDataCollection(QWidget* parent = nullptr);
~TimedDataCollection();
void readTimedTaskFromFile(const QString& filePath);
public Q_SLOTS:
void startScheduler();
void stopScheduler();
void subTaskCompleted(int status);
void onBack2Origin();
private Q_SLOTS:
// 任务状态更新槽
void onTaskStarted(int taskId);
void onTaskFinished(int taskId, bool success);
void onSubTaskStarted(int taskId, int subTaskIndex);
void onSubTaskFinished(int taskId, int subTaskIndex);
void onTaskDataChanged(const TimedTask& task);
void onErrorOccurred(const QString& error);
// 树视图交互
void onTreeItemClicked(const QModelIndex& index);
void onTreeItemDoubleClicked(const QModelIndex& index);
void onTreeSelectionChanged();
Q_SIGNALS:
void taskStarted(int taskId);
void taskFinished(int taskId, bool success);
void subTaskStarted(int taskId, int subTaskIndex);
void subTaskFinished(int taskId, int subTaskIndex);
void errorOccurred(const QString& error);
void hyperCamParm(int camType, double f, double e, QString filePath, QString fileName);
void camParm(int camType, int captureIntervalSeconds, QString folder);
void motorParm(QString pathLineFilePath);
void startRecordSignal(int camType);
void switchHalogenLampSignal(int state);
void switchD65LampSignal(int state);
void switchSlrSignal(int state);
void sequenceCompleteSignal(int status);
void Back2OriginSignal();
private:
void setupUI();
void setupConnections();
void updateStatusBar();
void expandRunningTask();
void onSelectTaskFile();
Ui::TimedDataCollection_ui ui;
TaskTreeModel* m_taskModel; // 任务树形模型
TaskScheduler* m_scheduler;
int m_currentTaskId = -1;
int m_currentSubTaskIndex = -1;
};

View File

@ -0,0 +1,673 @@
#include "TimedDataCollectionDataStructures.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QFile>
#include <QTextStream>
#include <QDebug>
#include <algorithm>
// ==================== 公共接口实现 ====================
bool TimedDataCollectionDataStructuresReaderWriter::saveTasksToFile(const QString& filePath, const QVector<TimedTask>& tasks)
{
QJsonObject root;
root["version"] = "1.0";
root["taskCount"] = tasks.size();
QJsonArray tasksArray;
for (const auto& task : tasks) {
tasksArray.append(timedTaskToJson(task));
}
root["tasks"] = tasksArray;
QJsonDocument doc(root);
QFile file(filePath);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qWarning() << "Failed to open file for writing:" << filePath;
return false;
}
QTextStream out(&file);
//out.setEncoding(QStringConverter::Utf8);
out << doc.toJson(QJsonDocument::Indented);
file.close();
return true;
}
bool TimedDataCollectionDataStructuresReaderWriter::loadTasksFromFile(const QString& filePath, QVector<TimedTask>& tasks)
{
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning() << "Failed to open file for reading:" << filePath;
return false;
}
QByteArray jsonData = file.readAll();
file.close();
QJsonParseError parseError;
QJsonDocument doc = QJsonDocument::fromJson(jsonData, &parseError);
if (parseError.error != QJsonParseError::NoError) {
qWarning() << "JSON parse error:" << parseError.errorString();
return false;
}
if (!doc.isObject()) {
qWarning() << "Invalid JSON root object";
return false;
}
QJsonObject root = doc.object();
QJsonArray tasksArray = root["tasks"].toArray();
tasks.clear();
tasks.reserve(tasksArray.size());
for (const auto& taskValue : tasksArray) {
if (!taskValue.isObject()) continue;
TimedTask task;
if (jsonToTimedTask(taskValue.toObject(), task)) {
tasks.append(task);
}
}
return true;
}
// ==================== 枚举转换函数 ====================
QString TimedDataCollectionDataStructuresReaderWriter::taskStatusToString(TaskStatus status)
{
switch (status) {
case TaskStatus::Waiting: return "Waiting";
case TaskStatus::Running: return "Running";
case TaskStatus::Finished: return "Finished";
default: return "Waiting";
}
}
TaskStatus TimedDataCollectionDataStructuresReaderWriter::stringToTaskStatus(const QString& str)
{
//if (str == "Running") return TaskStatus::Running;
//if (str == "Finished") return TaskStatus::Finished;
return TaskStatus::default;
}
QString TimedDataCollectionDataStructuresReaderWriter::subTaskTypeToString(SubTaskType type)
{
switch (type) {
case SubTaskType::HyperSpectual400_1000nm: return "HyperSpectual400_1000nm";
case SubTaskType::HyperSpectual1000_1700nm: return "HyperSpectual1000_1700nm";
case SubTaskType::SingleLensReflex: return "SingleLensReflex";
case SubTaskType::DepthCamera: return "DepthCamera";
default: return "Unknown";
}
}
SubTaskType TimedDataCollectionDataStructuresReaderWriter::stringToSubTaskType(const QString& str)
{
if (str == "HyperSpectual400_1000nm") return SubTaskType::HyperSpectual400_1000nm;
if (str == "HyperSpectual1000_1700nm") return SubTaskType::HyperSpectual1000_1700nm;
if (str == "SingleLensReflex") return SubTaskType::SingleLensReflex;
if (str == "DepthCamera") return SubTaskType::DepthCamera;
return SubTaskType::SingleLensReflex;
}
// ==================== SubTask序列化 ====================
QJsonObject TimedDataCollectionDataStructuresReaderWriter::subTaskToJson(const SubTask& subTask)
{
QJsonObject obj;
obj["type"] = subTaskTypeToString(subTask.type);
obj["startTime"] = subTask.startTime.toString(Qt::ISODate);
obj["endTime"] = subTask.endTime.toString(Qt::ISODate);
obj["durationMinutes"] = subTask.durationMinutes;
obj["estimatedDurationMinutes"] = subTask.estimatedDurationMinutes;
obj["pathLineFilePath"] = subTask.pathLineFilePath;
obj["status"] = taskStatusToString(subTask.status);
obj["frameRate"] = subTask.frameRate;
obj["exposureTime"] = subTask.exposureTime;
obj["defaultRenderBand"] = subTask.defaultRenderBand;
obj["captureIntervalSeconds"] = subTask.captureIntervalSeconds;
return obj;
}
bool TimedDataCollectionDataStructuresReaderWriter::jsonToSubTask(const QJsonObject& json, SubTask& subTask)
{
subTask.type = stringToSubTaskType(json["type"].toString());
subTask.startTime = QDateTime::fromString(QString(), Qt::ISODate);
subTask.endTime = QDateTime::fromString(QString(), Qt::ISODate);
subTask.durationMinutes = json["durationMinutes"].toDouble();
subTask.estimatedDurationMinutes = json["estimatedDurationMinutes"].toDouble();
subTask.pathLineFilePath = json["pathLineFilePath"].toString();
subTask.status = stringToTaskStatus(json["status"].toString());
subTask.frameRate = json["frameRate"].toDouble();
subTask.exposureTime = json["exposureTime"].toDouble();
subTask.defaultRenderBand = json["defaultRenderBand"].toInt();
subTask.captureIntervalSeconds = json["captureIntervalSeconds"].toDouble();
return true;
}
// ==================== TimedTask序列化 ====================
QJsonObject TimedDataCollectionDataStructuresReaderWriter::timedTaskToJson(const TimedTask& task)
{
QJsonObject obj;
obj["id"] = task.id;
obj["scheduledTime"] = task.scheduledTime.toString(Qt::ISODate);
obj["startTime"] = task.startTime.toString(Qt::ISODate);
obj["endTime"] = task.endTime.toString(Qt::ISODate);
obj["durationMinutes"] = task.durationMinutes;
obj["estimatedDurationMinutes"] = task.estimatedDurationMinutes;
obj["savePath"] = task.savePath;
obj["status"] = taskStatusToString(task.status);
obj["HalogenLampPreheatingTime_Minute"] = task.HalogenLampPreheatingTime_Minute;
QJsonArray subTasksArray;
for (const auto& subTask : task.subTasks) {
subTasksArray.append(subTaskToJson(subTask));
}
obj["subTasks"] = subTasksArray;
return obj;
}
bool TimedDataCollectionDataStructuresReaderWriter::jsonToTimedTask(const QJsonObject& json, TimedTask& task)
{
task.id = json["id"].toInt();
task.scheduledTime = QDateTime::fromString(json["scheduledTime"].toString(), Qt::ISODate);
task.startTime = QDateTime::fromString(QString(), Qt::ISODate);
task.endTime = QDateTime::fromString(QString(), Qt::ISODate);
task.durationMinutes = json["durationMinutes"].toDouble();
task.estimatedDurationMinutes = json["estimatedDurationMinutes"].toDouble();
task.savePath = json["savePath"].toString();
task.status = stringToTaskStatus(json["status"].toString());
task.HalogenLampPreheatingTime_Minute = json["HalogenLampPreheatingTime_Minute"].toDouble();
QJsonArray subTasksArray = json["subTasks"].toArray();
task.subTasks.clear();
task.subTasks.reserve(subTasksArray.size());
for (const auto& subTaskValue : subTasksArray) {
if (!subTaskValue.isObject()) continue;
SubTask subTask;
if (jsonToSubTask(subTaskValue.toObject(), subTask)) {
task.subTasks.append(subTask);
}
}
return true;
}
// ==================== TaskExecutor 实现 ====================
TaskExecutor::TaskExecutor(QObject* parent)
: QObject(parent)
, m_currentSubTaskIndex(0)
, m_isRunning(false)
{
}
TaskExecutor::~TaskExecutor()
{
stop();
}
void TaskExecutor::execute(const TimedTask& task)
{
if (m_isRunning) {
qWarning() << "TaskExecutor: Already running, ignoring execute request";
return;
}
m_task = task;
m_task.startTime = QDateTime::currentDateTime();
m_task.status = TaskStatus::Running;
emit taskUpdated(m_task);
m_currentSubTaskIndex = 0;
m_isRunning = true;
qDebug() << "TaskExecutor: Starting task" << task.id;
// 打开卤素灯预热
emit switchHalogenLampSignal(1);
printMsgAndTime("open HalogenLamp");
makeFolder(m_task.savePath);
// 开始执行第一个子任务
double sleepTimeSecond = m_task.HalogenLampPreheatingTime_Minute * 60;
QTimer::singleShot(sleepTimeSecond *1000, this, &TaskExecutor::executeNextSubTask);
}
void TaskExecutor::printMsgAndTime(QString msg)
{
QDateTime now = QDateTime::currentDateTime();
QString timeString = now.toString("yyyy-MM-dd hh:mm:ss.zzz");
qDebug() << msg + " time:" << timeString;
}
void TaskExecutor::stop()
{
if (!m_isRunning) return;
qDebug() << "TaskExecutor: Stopping task" << m_task.id;
m_isRunning = false;
emit finished(false);
}
void TaskExecutor::makeFolder(QString savePath)
{
QDir dir(savePath);
if (!dir.exists()) {
if (dir.mkpath(".")) {
qDebug() << "TaskExecutor: Created data folder:" << savePath;
} else {
qWarning() << "TaskExecutor: Failed to create data folder:" << savePath;
}
} else {
qDebug() << "TaskExecutor: Data folder already exists:" << savePath;
}
}
QString TaskExecutor::makeSubTaskDataFolder(QString suffix)
{
QString dateStr = QDateTime::currentDateTime().toString("yyyy-MM-dd_HH-mm-ss");
QString folderPath = m_task.savePath + QDir::separator() + dateStr + "_" + suffix;
makeFolder(folderPath);
return folderPath;
}
void TaskExecutor::onSequenceComplete(int status)
{
if (!m_isRunning) return;
qDebug() << "TaskExecutor: Sequence complete, status:" << status;
// 更新当前子任务状态
if (m_currentSubTaskIndex < m_task.subTasks.size()) {
SubTask& subTask = m_task.subTasks[m_currentSubTaskIndex];
subTask.status = (status == 0) ? TaskStatus::Finished : TaskStatus::Waiting;
subTask.endTime = QDateTime::currentDateTime();
subTask.durationMinutes = (double)subTask.startTime.secsTo(subTask.endTime) / 60;
qDebug() << "TaskExecutor: subtask "<< m_currentSubTaskIndex<< " time consuming(Minutes): "<< subTask.durationMinutes;
emit subTaskFinished(m_currentSubTaskIndex, subTask.type, (status == 0));
emit taskUpdated(m_task);
}
//
switch (m_task.subTasks[m_currentSubTaskIndex].type)
{
case SubTaskType::SingleLensReflex:
{
emit switchD65LampSignal(0);
emit switchSlrSignal(0);
break;
}
case SubTaskType::DepthCamera:
{
emit switchD65LampSignal(0);
break;
}
}
// 判断下一次的任务是否为高光谱任务,如果不是关闭卤素灯
int nestSubTaskIndex = m_currentSubTaskIndex + 1;
if (nestSubTaskIndex >= m_task.subTasks.size())
{
emit switchD65LampSignal(0);
emit switchSlrSignal(0);
emit switchHalogenLampSignal(0);
return;
}
switch (m_task.subTasks[nestSubTaskIndex].type)
{
case SubTaskType::SingleLensReflex:
{
emit switchHalogenLampSignal(0);
break;
}
case SubTaskType::DepthCamera:
{
emit switchHalogenLampSignal(0);
break;
}
}
emit taskUpdated(m_task);
}
void TaskExecutor::onBack2Origin()
{
// 检查是否还有更多子任务
m_currentSubTaskIndex++;
if (m_currentSubTaskIndex < m_task.subTasks.size()) {
// 执行下一个子任务
if(m_task.subTasks[m_currentSubTaskIndex].type== SubTaskType::SingleLensReflex)
{
printMsgAndTime("Slr taskfor weak upplease wait 135 seconds!");
emit switchSlrSignal(0);
QTimer::singleShot(135*1000, this, &TaskExecutor::executeNextSubTask);
}
else
{
QTimer::singleShot(1000, this, &TaskExecutor::executeNextSubTask);
}
}
else {
// 所有子任务完成
m_task.endTime = QDateTime::currentDateTime();
m_task.durationMinutes = (double)m_task.startTime.secsTo(m_task.endTime) / 60;
m_task.status = TaskStatus::Finished;
qDebug() << "TaskExecutor: task time consuming(Minutes): " << m_task.durationMinutes;
m_isRunning = false;
qDebug() << "TaskExecutor: All subtasks completed";
emit finished(true);
}
emit taskUpdated(m_task);
}
void TaskExecutor::onError(const QString& error)
{
if (!m_isRunning) return;
qWarning() << "TaskExecutor: Error occurred:" << error;
m_isRunning = false;
emit errorOccurred(error);
emit finished(false);
}
void TaskExecutor::executeNextSubTask()
{
if (!m_isRunning || m_currentSubTaskIndex >= m_task.subTasks.size()) {
return;
}
SubTask& subTask = m_task.subTasks[m_currentSubTaskIndex];
subTask.status = TaskStatus::Running;
subTask.startTime = QDateTime::currentDateTime();
emit taskUpdated(m_task);
QString tmp = "TaskExecutor: Starting subtask" + QString::number(m_currentSubTaskIndex) + "type:" + static_cast<int>(subTask.type);
printMsgAndTime(tmp);
//printMsgAndTime("excute " + QString::number(m_currentSubTaskIndex) + " subTask: ");
//qDebug() << "TaskExecutor: Starting subtask" << m_currentSubTaskIndex
// << "type:" << static_cast<int>(subTask.type);
emit subTaskStarted(m_currentSubTaskIndex, subTask.type);
emit motorParm(subTask.pathLineFilePath);
int camType;
switch (subTask.type)
{
case SubTaskType::HyperSpectual400_1000nm:
{
camType = 0;
emit hyperCamParm(camType, subTask.frameRate, subTask.exposureTime, makeSubTaskDataFolder("L"), "test");
break;
}
case SubTaskType::HyperSpectual1000_1700nm:
{
camType = 1;
emit hyperCamParm(camType, subTask.frameRate, subTask.exposureTime, makeSubTaskDataFolder("NIR"), "test");
break;
}
case SubTaskType::SingleLensReflex:
{
camType = 2;
emit camParm(camType, 3, makeSubTaskDataFolder("SLR"));
emit switchD65LampSignal(1);
emit switchSlrSignal(1);
break;
}
case SubTaskType::DepthCamera:
{
camType = 3;
emit camParm(camType, 3, makeSubTaskDataFolder("DepthCamera"));
emit switchD65LampSignal(1);
break;
}
}
emit startRecordSignal(camType);
}
// ==================== TaskScheduler 实现 ====================
TaskScheduler::TaskScheduler(QObject* parent)
: QObject(parent)
, m_timer(nullptr)
, m_currentExecutor(nullptr)
, m_currentTaskId(-1)
{
}
TaskScheduler::~TaskScheduler()
{
stop();
}
void TaskScheduler::loadTasks(const QVector<TimedTask>& tasks)
{
m_tasks = tasks;
qDebug() << "TaskScheduler: Loaded" << tasks.size() << "tasks";
}
void TaskScheduler::start()
{
if (m_timer) {
qDebug() << "TaskScheduler: Already running";
return;
}
qDebug() << "TaskScheduler: Starting";
m_timer = new QTimer(this);
connect(m_timer, &QTimer::timeout, this, &TaskScheduler::checkTasks);
m_timer->start(1000); // 每秒检查一次
emit schedulerStateChanged(true);
}
void TaskScheduler::stop()
{
if (m_timer) {
m_timer->stop();
delete m_timer;
m_timer = nullptr;
}
if (m_currentExecutor) {
m_currentExecutor->stop();
m_currentExecutor->deleteLater();
m_currentExecutor = nullptr;
}
qDebug() << "TaskScheduler: Stopped";
emit schedulerStateChanged(false);
}
void TaskScheduler::checkTasks()
{
// 检查是否所有任务都不需要执行
bool allTasksDone = std::all_of(m_tasks.begin(), m_tasks.end(),
[](const TimedTask& task) {
return task.status != TaskStatus::Waiting;
});
if (allTasksDone)
{
std::cerr << "TaskScheduler::checkTasks没有任务等待执行停止调度器" << std::endl;
stop();
}
QDateTime now = QDateTime::currentDateTime();
if (m_currentTaskId >= 0)//有任务正在执行
{
return;
}
for (auto& task : m_tasks)
{
if (task.status != TaskStatus::Waiting) continue;
// 超过计划时间threSecs认为任务已过时跳过
qint64 threSecs = 30;
if (task.scheduledTime.addSecs(threSecs) < now)
{
std::cerr << "TaskScheduler::checkTasks任务已过时跳过:" << task.id << std::endl;
task.status = TaskStatus::Timeout;
for (size_t i = 0; i < task.subTaskCount(); i++)
{
task.subTasks[i].status = TaskStatus::Timeout;
}
emit taskDataChanged(task);
continue;
}
if (task.scheduledTime > now) continue;
qint64 fireThreSecs = 5;
if (task.scheduledTime.addSecs(-1*fireThreSecs) < now && task.scheduledTime.addSecs(fireThreSecs) > now)// 到达计划时间,启动任务
{
std::cerr << "TaskScheduler::checkTasks到达计划时间启动任务" << std::endl;
executeTask(task);
break; // 一次只执行一个任务
}
}
}
void TaskScheduler::onTaskFinished(bool success)
{
if (m_currentTaskId > 0) {
TaskStatus status = success ? TaskStatus::Finished : TaskStatus::Waiting;
updateTaskStatus(m_currentTaskId, status);
emit taskFinished(m_currentTaskId, success);
}
// 清理执行器
if (m_currentExecutor) {
m_currentExecutor->deleteLater();
m_currentExecutor = nullptr;
}
m_currentTaskId = -1;
}
void TaskScheduler::onSubTaskStarted(int subTaskIndex, SubTaskType type)
{
if (m_currentTaskId > 0) {
emit subTaskStarted(m_currentTaskId, subTaskIndex);
}
}
void TaskScheduler::onSubTaskFinished(int subTaskIndex, SubTaskType type, bool success)
{
if (m_currentTaskId > 0) {
emit subTaskFinished(m_currentTaskId, subTaskIndex);
}
}
void TaskScheduler::onExecutorError(const QString& error)
{
emitError(error);
}
void TaskScheduler::executeTask(TimedTask& task)
{
qDebug() << "TaskScheduler: Executing task" << task.id;
//updateTaskStatus(task.id, TaskStatus::Running);
m_currentTaskId = task.id;
//emit taskStarted(task.id);
// 创建任务执行器
m_currentExecutor = new TaskExecutor(this);
// 连接信号
connect(m_currentExecutor, &TaskExecutor::finished,
this, &TaskScheduler::onTaskFinished);
connect(m_currentExecutor, &TaskExecutor::subTaskStarted,
this, &TaskScheduler::onSubTaskStarted);
connect(m_currentExecutor, &TaskExecutor::subTaskFinished,
this, &TaskScheduler::onSubTaskFinished);
connect(m_currentExecutor, &TaskExecutor::errorOccurred,
this, &TaskScheduler::onExecutorError);
// 采集相关信号透传
connect(m_currentExecutor, &TaskExecutor::hyperCamParm,
this, &TaskScheduler::hyperCamParm);
connect(m_currentExecutor, &TaskExecutor::camParm,
this, &TaskScheduler::camParm);
connect(m_currentExecutor, &TaskExecutor::motorParm,
this, &TaskScheduler::motorParm);
connect(m_currentExecutor, &TaskExecutor::startRecordSignal,
this, &TaskScheduler::startRecordSignal);
connect(m_currentExecutor, &TaskExecutor::switchHalogenLampSignal, this, &TaskScheduler::switchHalogenLampSignal);
connect(m_currentExecutor, &TaskExecutor::switchD65LampSignal, this, &TaskScheduler::switchD65LampSignal);
connect(m_currentExecutor, &TaskExecutor::switchSlrSignal, this, &TaskScheduler::switchSlrSignal);
connect(this, &TaskScheduler::sequenceCompleteSignal, m_currentExecutor, &TaskExecutor::onSequenceComplete);
connect(this, &TaskScheduler::Back2OriginSignal, m_currentExecutor, &TaskExecutor::onBack2Origin);
connect(m_currentExecutor, &TaskExecutor::taskUpdated,
this, [this](const TimedTask& updatedTask) {
for (int i = 0; i < m_tasks.size(); ++i) {
if (m_tasks[i].id == updatedTask.id) {
m_tasks[i] = updatedTask;
break;
}
}
emit taskDataChanged(updatedTask);
});
// 开始执行
m_currentExecutor->execute(task);
}
void TaskScheduler::updateTaskStatus(int taskId, TaskStatus status)
{
for (auto& task : m_tasks) {
if (task.id == taskId) {
task.status = status;
if (status == TaskStatus::Running) {
task.startTime = QDateTime::currentDateTime();
} else if (status == TaskStatus::Finished) {
task.endTime = QDateTime::currentDateTime();
}
break;
}
}
}
void TaskScheduler::emitError(const QString& error)
{
qWarning() << "TaskScheduler: Error:" << error;
emit errorOccurred(error);
}

View File

@ -0,0 +1,240 @@
#pragma once
#include <QDateTime>
#include <QString>
#include <QVector>
#include <QMetaType>
#include <QTimer>
#include "CaptureCoordinator.h"
// ==================== 枚举定义 ====================
// 任务状态
enum class TaskStatus {
default, //未调度
Waiting, // 等待
Running, // 运行中
Finished, // 结束
Skiped, //跳过
Timeout //超时
};
// 子任务类型
enum class SubTaskType {
HyperSpectual400_1000nm, // 400nm-1000nm高光谱相机
HyperSpectual1000_1700nm, // 1000nm-1700nm高光谱相机
SingleLensReflex, // 单反相机
DepthCamera // 深度相机
};
// ==================== 统一子任务封装 ====================
struct SubTask {
SubTaskType type; // 子任务类型
// 共享属性
QDateTime startTime;
QDateTime endTime;
double durationMinutes = 0;
double estimatedDurationMinutes = 0;
QString pathLineFilePath;
TaskStatus status = TaskStatus::Waiting;
// 类型特有属性根据type选择使用
double frameRate = 0.0; // 高光谱相机用
double exposureTime = 0.0; // 高光谱相机用
int defaultRenderBand = 550; // 1000-1700nm高光谱用
int captureIntervalSeconds = 5; // 单反/深度相机用
};
// ==================== 定时任务 ====================
struct TimedTask {
int id = 0;
QDateTime scheduledTime;
QDateTime startTime;
QDateTime endTime;
double durationMinutes = 0;
double estimatedDurationMinutes = 0;
QString savePath;
QVector<SubTask> subTasks;
TaskStatus status = TaskStatus::Waiting;
double HalogenLampPreheatingTime_Minute;
// 计算所有子任务的预计总时间
int totalEstimatedDuration() const {
int total = 0;
for (const auto& subTask : subTasks) {
total += subTask.estimatedDurationMinutes;
}
return total;
}
// 获取子任务数量
int subTaskCount() const {
return subTasks.size();
}
};
// ==================== Qt元类型声明 ====================
Q_DECLARE_METATYPE(TaskStatus)
Q_DECLARE_METATYPE(SubTaskType)
Q_DECLARE_METATYPE(SubTask)
Q_DECLARE_METATYPE(TimedTask)
// ==================== 任务文件读写类 ====================
class TimedDataCollectionDataStructuresReaderWriter
{
public:
// 保存任务到文件
static bool saveTasksToFile(const QString& filePath, const QVector<TimedTask>& tasks);
// 从文件读取任务
static bool loadTasksFromFile(const QString& filePath, QVector<TimedTask>& tasks);
private:
// SubTask序列化
static QJsonObject subTaskToJson(const SubTask& subTask);
static bool jsonToSubTask(const QJsonObject& json, SubTask& subTask);
// TimedTask序列化
static QJsonObject timedTaskToJson(const TimedTask& task);
static bool jsonToTimedTask(const QJsonObject& json, TimedTask& task);
// 枚举转换
static QString taskStatusToString(TaskStatus status);
static TaskStatus stringToTaskStatus(const QString& str);
static QString subTaskTypeToString(SubTaskType type);
static SubTaskType stringToSubTaskType(const QString& str);
};
// ==================== 任务执行器 ====================
class TaskExecutor : public QObject
{
Q_OBJECT
public:
explicit TaskExecutor(QObject* parent = nullptr);
~TaskExecutor();
// 执行任务
void execute(const TimedTask& task);
void makeFolder(QString savePath);
QString makeSubTaskDataFolder(QString suffix);
// 获取当前执行的子任务索引
int currentSubTaskIndex() const { return m_currentSubTaskIndex; }
// 获取正在执行的任务
const TimedTask& currentTask() const { return m_task; }
// 是否正在执行
bool isRunning() const { return m_isRunning; }
// 停止执行
void stop();
signals:
void finished(bool success); // 任务完成
void taskUpdated(const TimedTask& task); // 确保有这个信号
void subTaskStarted(int subTaskIndex, SubTaskType type); // 子任务开始
void subTaskFinished(int subTaskIndex, SubTaskType type, bool success); // 子任务完成
void errorOccurred(const QString& error); // 错误发生
// 采集相关信号
void hyperCamParm(int camType, double f, double e, QString filePath, QString fileName);
void camParm(int camType, int captureIntervalSeconds, QString folder);
void motorParm(QString pathLineFilePath);
void startRecordSignal(int camType);
void switchHalogenLampSignal(int state);
void switchD65LampSignal(int state);
void switchSlrSignal(int state);
public slots:
void onSequenceComplete(int status);
void onBack2Origin();
void onError(const QString& error);
private:
void executeNextSubTask();
void printMsgAndTime(QString msg);
TimedTask m_task;
int m_currentSubTaskIndex;
bool m_isRunning;
};
// ==================== 任务调度器 ====================
class TaskScheduler : public QObject
{
Q_OBJECT
public:
explicit TaskScheduler(QObject* parent = nullptr);
~TaskScheduler();
// 加载任务列表
void loadTasks(const QVector<TimedTask>& tasks);
// 获取任务列表
QVector<TimedTask> tasks() const { return m_tasks; }
// 开始调度
void start();
// 停止调度
void stop();
// 是否正在运行
bool isRunning() const { return m_timer != nullptr && m_timer->isActive(); }
signals:
void taskStarted(int taskId); // 任务开始
void taskFinished(int taskId, bool success); // 任务完成
void taskDataChanged(const TimedTask& task);
void schedulerStateChanged(bool running); // 调度器状态变化
// 子任务的信号
void subTaskStarted(int taskId, int subTaskIndex); // 子任务开始
void subTaskFinished(int taskId, int subTaskIndex); // 子任务完成
void errorOccurred(const QString& error); // 错误发生
// 采集相关信号 (透传 TaskExecutor)
void hyperCamParm(int camType, double f, double e, QString filePath, QString fileName);
void camParm(int camType, int captureIntervalSeconds, QString folder);
void motorParm(QString pathLineFilePath);
void startRecordSignal(int camType);
void switchHalogenLampSignal(int state);
void switchD65LampSignal(int state);
void switchSlrSignal(int state);
// 马达反馈的信号
void sequenceCompleteSignal(int status);
void Back2OriginSignal();
private slots:
void checkTasks(); // 检查任务是否该启动
void onTaskFinished(bool success);
void onSubTaskStarted(int subTaskIndex, SubTaskType type);
void onSubTaskFinished(int subTaskIndex, SubTaskType type, bool success);
void onExecutorError(const QString& error);
private:
void executeTask(TimedTask& task);
void updateTaskStatus(int taskId, TaskStatus status);
void emitError(const QString& error);
QTimer* m_timer;
QVector<TimedTask> m_tasks;
TaskExecutor* m_currentExecutor;
int m_currentTaskId;
};

View File

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TimedDataCollection_ui</class>
<widget class="QDialog" name="TimedDataCollection_ui">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1182</width>
<height>576</height>
</rect>
</property>
<property name="windowTitle">
<string>定时采集</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="3">
<widget class="QTreeView" name="taskTreeView"/>
</item>
<item row="1" column="0">
<widget class="QLineEdit" name="taskFileSelect_lineEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="taskFileSelect_btn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="start_btn">
<property name="text">
<string>开始调度</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="expandAll_btn">
<property name="text">
<string>展开</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QPushButton" name="stop_btn">
<property name="text">
<string>停止调度</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="collapseAll_btn">
<property name="text">
<string>折叠</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="3">
<widget class="QLabel" name="statusLabel">
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -103,6 +103,113 @@ void TwoMotorControl::record_white()
m_whiteCaptureCoordinator->startStepMotion(s);
}
void TwoMotorControl::run2(SingleLensReflexCameraWindow* window)
{
int rowCount = ui.recordLine_tableWidget->rowCount();
if (rowCount == 0)
{
QMessageBox::information(this, QString::fromLocal8Bit("提示"), QString::fromLocal8Bit("请至少添加一行采集线!"));
emit sequenceComplete(0);
return;
}
m_coordinator_TimedDataCollection = new TwoMotionCaptureCoordinator(m_multiAxisController);
connect(this, SIGNAL(start(QVector<PathLine>)), m_coordinator_TimedDataCollection, SLOT(start(QVector<PathLine>)));
connect(m_coordinator_TimedDataCollection, &TwoMotionCaptureCoordinator::startRecordLineNumSignal, this, &TwoMotorControl::receiveStartRecordLineNum);
connect(m_coordinator_TimedDataCollection, &TwoMotionCaptureCoordinator::finishRecordLineNumSignal, this, &TwoMotorControl::receiveFinishRecordLineNum);
connect(m_coordinator_TimedDataCollection, &TwoMotionCaptureCoordinator::startRecordLineNumSignal, window, &SingleLensReflexCameraWindow::onStartTimedDataCollection);
connect(m_coordinator_TimedDataCollection, &TwoMotionCaptureCoordinator::sequenceComplete, window, &SingleLensReflexCameraWindow::onStopTimedDataCollection);
connect(m_coordinator_TimedDataCollection, &TwoMotionCaptureCoordinator::sequenceComplete, this, &TwoMotorControl::sequenceComplete);
connect(m_coordinator_TimedDataCollection, &TwoMotionCaptureCoordinator::back2OriginSignal, this, &TwoMotorControl::onBack2Origin2);
QVector<PathLine> pathLines;
//int columnCount = ui.recordLine_tableWidget->columnCount();
for (size_t i = 0; i < rowCount; i++)
{
PathLine tmp;
tmp.targetYPosition = ui.recordLine_tableWidget->item(i, 0)->text().toDouble();
tmp.speedTargetYPosition = ui.recordLine_tableWidget->item(i, 1)->text().toDouble();
tmp.targetXMinPosition = ui.recordLine_tableWidget->item(i, 2)->text().toDouble();
tmp.speedTargetXMinPosition = ui.recordLine_tableWidget->item(i, 3)->text().toDouble();
tmp.targetXMaxPosition = ui.recordLine_tableWidget->item(i, 4)->text().toDouble();
tmp.speedTargetXMaxPosition = ui.recordLine_tableWidget->item(i, 5)->text().toDouble();
pathLines.append(tmp);
}
for (size_t i = 0; i < ui.recordLine_tableWidget->rowCount(); i++)
{
for (size_t j = 0; j < ui.recordLine_tableWidget->columnCount(); j++)
{
ui.recordLine_tableWidget->item(i, j)->setBackgroundColor(QColor("#0E1C4C"));
}
}
emit start(pathLines);
}
void TwoMotorControl::run3(DepthCameraWindow* window)
{
int rowCount = ui.recordLine_tableWidget->rowCount();
if (rowCount == 0)
{
QMessageBox::information(this, QString::fromLocal8Bit("提示"), QString::fromLocal8Bit("请至少添加一行采集线!"));
emit sequenceComplete(0);
return;
}
m_coordinator_TimedDataCollection = new TwoMotionCaptureCoordinator(m_multiAxisController);
connect(this, SIGNAL(start(QVector<PathLine>)), m_coordinator_TimedDataCollection, SLOT(start(QVector<PathLine>)));
connect(m_coordinator_TimedDataCollection, &TwoMotionCaptureCoordinator::startRecordLineNumSignal, this, &TwoMotorControl::receiveStartRecordLineNum);
connect(m_coordinator_TimedDataCollection, &TwoMotionCaptureCoordinator::finishRecordLineNumSignal, this, &TwoMotorControl::receiveFinishRecordLineNum);
connect(m_coordinator_TimedDataCollection, &TwoMotionCaptureCoordinator::startRecordLineNumSignal, window, &DepthCameraWindow::openDepthCamera);
connect(m_coordinator_TimedDataCollection, &TwoMotionCaptureCoordinator::sequenceComplete, window, &DepthCameraWindow::closeDepthCamera);
connect(m_coordinator_TimedDataCollection, &TwoMotionCaptureCoordinator::sequenceComplete, this, &TwoMotorControl::sequenceComplete);
connect(m_coordinator_TimedDataCollection, &TwoMotionCaptureCoordinator::back2OriginSignal, this, &TwoMotorControl::onBack2Origin2);
QVector<PathLine> pathLines;
//int columnCount = ui.recordLine_tableWidget->columnCount();
for (size_t i = 0; i < rowCount; i++)
{
PathLine tmp;
tmp.targetYPosition = ui.recordLine_tableWidget->item(i, 0)->text().toDouble();
tmp.speedTargetYPosition = ui.recordLine_tableWidget->item(i, 1)->text().toDouble();
tmp.targetXMinPosition = ui.recordLine_tableWidget->item(i, 2)->text().toDouble();
tmp.speedTargetXMinPosition = ui.recordLine_tableWidget->item(i, 3)->text().toDouble();
tmp.targetXMaxPosition = ui.recordLine_tableWidget->item(i, 4)->text().toDouble();
tmp.speedTargetXMaxPosition = ui.recordLine_tableWidget->item(i, 5)->text().toDouble();
pathLines.append(tmp);
}
for (size_t i = 0; i < ui.recordLine_tableWidget->rowCount(); i++)
{
for (size_t j = 0; j < ui.recordLine_tableWidget->columnCount(); j++)
{
ui.recordLine_tableWidget->item(i, j)->setBackgroundColor(QColor("#0E1C4C"));
}
}
emit start(pathLines);
}
void TwoMotorControl::onBack2Origin2()
{
m_coordinator_TimedDataCollection->deleteLater();
m_coordinator_TimedDataCollection = nullptr;
emit back2OriginSignal_TimedDataCollection();
}
void TwoMotorControl::run()
{
if (getState())
@ -116,19 +223,26 @@ void TwoMotorControl::run()
{
QMessageBox::information(this, QString::fromLocal8Bit("提示"), QString::fromLocal8Bit("请至少添加一行采集线!"));
emit sequenceComplete();
emit sequenceComplete(0);
return;
}
qRegisterMetaType<QVector<PathLine>>("QVector<PathLine>");
m_coordinator = new TwoMotionCaptureCoordinator(m_multiAxisController, m_Imager);
m_coordinator = new TwoMotionCaptureCoordinator(m_multiAxisController);
m_coordinator->moveToThread(&m_coordinatorThread);
connect(&m_coordinatorThread, SIGNAL(finished()), m_coordinator, SLOT(deleteLater()));
connect(this, SIGNAL(start(QVector<PathLine>)), m_coordinator, SLOT(start(QVector<PathLine>)));
connect(this, SIGNAL(stopSignal()), m_coordinator, SLOT(stop()));
connect(m_coordinator, SIGNAL(startRecordLineNumSignal(int)), this, SLOT(receiveStartRecordLineNum(int)));
connect(m_coordinator, SIGNAL(finishRecordLineNumSignal(int)), this, SLOT(receiveFinishRecordLineNum(int)));
connect(m_coordinator, SIGNAL(sequenceComplete(int)), this, SLOT(onSequenceComplete()));
connect(m_coordinator, SIGNAL(sequenceComplete(int)), this, SLOT(onSequenceComplete(int)));
connect(m_coordinator, &TwoMotionCaptureCoordinator::back2OriginSignal, this, &TwoMotorControl::onBack2Origin);
connect(m_coordinator, &TwoMotionCaptureCoordinator::startRecordHSISignal, m_Imager, &ImagerOperationBase::start_record);
connect(m_coordinator, &TwoMotionCaptureCoordinator::stopRecordHSISignal, this, &TwoMotorControl::stop_record);
connect(m_Imager, &ImagerOperationBase::RecordFinishedSignal_WhenFrameNumberMeet, m_coordinator, &TwoMotionCaptureCoordinator::handleCaptureCompleteWhenFrameNumberMeet);
m_coordinatorThread.start();
QVector<PathLine> pathLines;
@ -158,6 +272,11 @@ void TwoMotorControl::run()
emit start(pathLines);
}
void TwoMotorControl::stop_record()
{
m_Imager->stop_record();
}
void TwoMotorControl::stop()
{
emit stopSignal();
@ -173,12 +292,21 @@ TwoMotorControl::~TwoMotorControl()
}
void TwoMotorControl::onConnectMotor()
{
connectMotor(true);
}
void TwoMotorControl::connectMotor(bool isNotification)
{
if (getMotorsConnectionStatus())
{
if (isNotification)
{
QMessageBox msgBox;
msgBox.setText(QString::fromLocal8Bit("马达已连接!"));
msgBox.exec();
}
return;
}
@ -252,16 +380,22 @@ void TwoMotorControl::receiveFinishRecordLineNum(int lineNum)
}
}
void TwoMotorControl::onSequenceComplete()
void TwoMotorControl::onSequenceComplete(int status)
{
isWritePosFile = false;
fclose(m_posFileHandle);
emit sequenceComplete(status);
}
void TwoMotorControl::onBack2Origin()
{
m_coordinatorThread.quit();
m_coordinatorThread.wait();
m_coordinator = nullptr;
emit sequenceComplete();
emit back2OriginSignal();
emit back2OriginSignal_TimedDataCollection();
}
bool TwoMotorControl::getMotorsConnectionStatus()
@ -419,51 +553,15 @@ void TwoMotorControl::on_rangeMeasurement()
void TwoMotorControl::onAddRecordLine_btn()
{
//准备数据获取y马达的当前位置获取x马达的当前位置和最大位置
// 准备数据获取y马达的当前位置获取x马达的当前位置和最大位置
double currentPosOfYmotor = 15;
double currentPosOfXmotor = 0;
double maxRangeOfXmotor = 50;
//获取选中行的索引
int currentRow = ui.recordLine_tableWidget->currentRow();
std::cout << "currentRow" << currentRow << std::endl;
QTableWidgetItem* Item1 = new QTableWidgetItem(QString::number(currentPosOfYmotor, 10, 2));
QTableWidgetItem* Item2 = new QTableWidgetItem(QString::number(1, 10, 2));
QTableWidgetItem* Item3 = new QTableWidgetItem(QString::number(currentPosOfXmotor, 10, 2));
QTableWidgetItem* Item4 = new QTableWidgetItem(QString::number(1, 10, 2));
QTableWidgetItem* Item5 = new QTableWidgetItem(QString::number(maxRangeOfXmotor, 10, 2));
QTableWidgetItem* Item6 = new QTableWidgetItem(QString::number(1, 10, 2));
Item1->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
Item2->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
Item3->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
Item4->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
Item5->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
Item6->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
if (currentRow == -1)//当没有选中行时
{
int RowCount = ui.recordLine_tableWidget->rowCount();//Returns the number of rows. 从1开始的
ui.recordLine_tableWidget->insertRow(RowCount);//增加一行形参是从0开始的
ui.recordLine_tableWidget->setItem(RowCount, 0, Item1);
ui.recordLine_tableWidget->setItem(RowCount, 1, Item2);
ui.recordLine_tableWidget->setItem(RowCount, 2, Item3);
ui.recordLine_tableWidget->setItem(RowCount, 3, Item4);
ui.recordLine_tableWidget->setItem(RowCount, 4, Item5);
ui.recordLine_tableWidget->setItem(RowCount, 5, Item6);
}
else
{
ui.recordLine_tableWidget->insertRow(currentRow + 1);//增加一行形参是从0开始的
ui.recordLine_tableWidget->setItem(currentRow + 1, 0, Item1);
ui.recordLine_tableWidget->setItem(currentRow + 1, 1, Item2);
ui.recordLine_tableWidget->setItem(currentRow + 1, 2, Item3);
ui.recordLine_tableWidget->setItem(currentRow + 1, 3, Item4);
ui.recordLine_tableWidget->setItem(currentRow + 1, 4, Item5);
ui.recordLine_tableWidget->setItem(currentRow + 1, 5, Item6);
}
PathLineManager::addRecordLine(ui.recordLine_tableWidget,
currentPosOfYmotor, 1.0,
currentPosOfXmotor, 1.0,
maxRangeOfXmotor, 1.0);
}
void TwoMotorControl::onRemoveRecordLine_btn()
@ -484,103 +582,40 @@ void TwoMotorControl::onDeleteRecordLine_btn()
void TwoMotorControl::onSaveRecordLine2File_btn()
{
//确保采集线存在
// 确保采集线存在
if (ui.recordLine_tableWidget->rowCount() <= 0)
{
QMessageBox::information(this, QString::fromLocal8Bit("提示"), QString::fromLocal8Bit("请先生成轨迹!"));
QMessageBox::information(this, QString::fromLocal8Bit("提示"),
QString::fromLocal8Bit("请先生成轨迹!"));
return;
}
FileOperation* fileOperation = new FileOperation();
string directory = fileOperation->getDirectoryOfExe();
QString RecordLineFilePath = QFileDialog::getSaveFileName(this, tr("Save RecordLine3 File"),
QString::fromStdString(directory),
tr("RecordLineFile3 (*.RecordLine3)"));
if (RecordLineFilePath.isEmpty())
{
return;
}
FILE* RecordLineFileHandle = fopen(RecordLineFilePath.toStdString().c_str(), "wb+");
double number = ui.recordLine_tableWidget->rowCount() * ui.recordLine_tableWidget->columnCount();
fwrite(&number, sizeof(double), 1, RecordLineFileHandle);
double* data = new double[number];
//double data[number];
for (size_t i = 0; i < ui.recordLine_tableWidget->rowCount(); i++)
{
for (size_t j = 0; j < ui.recordLine_tableWidget->columnCount(); j++)
{
data[i * ui.recordLine_tableWidget->columnCount() + j] = ui.recordLine_tableWidget->item(i, j)->text().toDouble();
}
}
fwrite(data, sizeof(double), number, RecordLineFileHandle);
fclose(RecordLineFileHandle);
delete[] data;
//QMessageBox::information(this, QString::fromLocal8Bit("提示"), QString::fromLocal8Bit("保存成功!"));
PathLineManager::saveToFile(RecordLineFilePath, ui.recordLine_tableWidget);
}
void TwoMotorControl::onReadRecordLineFile_btn()
{
//打开文件
FileOperation* fileOperation = new FileOperation();
string directory = fileOperation->getDirectoryOfExe();
QString RecordLineFilePath = QFileDialog::getOpenFileName(this, tr("Open RecordLine3 File"),
QString::fromStdString(directory),
tr("RecordLineFile (*.RecordLine3)"));
if (RecordLineFilePath.isEmpty())
{
return;
}
FILE* RecordLineFileHandle = fopen(RecordLineFilePath.toStdString().c_str(), "rb");
double number;
//读取数据
fread(&number, sizeof(double), 1, RecordLineFileHandle);
double* data = new double[number];
for (size_t i = 0; i < number; i++)
{
fread(data + i, sizeof(double), 1, RecordLineFileHandle);
//std::cout << *(data + i) << std::endl;
}
//向tableWidget添加采集线
//1去掉tableWidget中所有的行
int rowCount = ui.recordLine_tableWidget->rowCount();
for (size_t i = 0; i < rowCount; i++)
{
ui.recordLine_tableWidget->removeRow(0);
}
//2添加行采集线
int RecordLineCount = number / ui.recordLine_tableWidget->columnCount();
for (size_t i = 0; i < RecordLineCount; i++)
{
ui.recordLine_tableWidget->insertRow(0);
}
//3向tableWidget填充数据
for (size_t i = 0; i < ui.recordLine_tableWidget->rowCount(); i++)
{
for (size_t j = 0; j < ui.recordLine_tableWidget->columnCount(); j++)
{
QTableWidgetItem* tmp = new QTableWidgetItem(QString::number(data[i * ui.recordLine_tableWidget->columnCount() + j], 10, 5));
tmp->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
ui.recordLine_tableWidget->setItem(i, j, tmp);
}
}
fclose(RecordLineFileHandle);
delete[] data;
//QMessageBox::information(this, QString::fromLocal8Bit("提示"), QString::fromLocal8Bit("读取成功!"));
readRecordLineFile(RecordLineFilePath);
}
void TwoMotorControl::readRecordLineFile(QString RecordLineFilePath)
{
PathLineManager::readFromFile(RecordLineFilePath, ui.recordLine_tableWidget);
}

View File

@ -10,6 +10,11 @@
#include "CaptureCoordinator.h"
#include "MotorWindowBase.h"
#include "SingleLensReflexCameraWindow.h"
#include "DepthCameraWindow.h"
#include "PathLine.h"
#define PI 3.1415926
class TwoMotorControl : public QDialog, public MotorWindowBase
@ -28,6 +33,10 @@ public:
bool getMotorsConnectionStatus();
void readRecordLineFile(QString RecordLineFilePath);
void connectMotor(bool isNotification);
private:
ImagerOperationBase* m_Imager;
bool getState();
@ -68,7 +77,14 @@ public Q_SLOTS:
void stop();
void receiveStartRecordLineNum(int lineNum);
void receiveFinishRecordLineNum(int lineNum);
void onSequenceComplete();
void onSequenceComplete(int status);
void onBack2Origin();
void run2(SingleLensReflexCameraWindow* w);
void run3(DepthCameraWindow* window);
void onBack2Origin2();
void stop_record();
signals:
void moveSignal(int, bool, double, int);
@ -84,7 +100,9 @@ signals:
void stopSignal();
void startLineNumSignal(int lineNum);
void sequenceComplete();//所有采集线正常运行完成
void sequenceComplete(int status);//所有采集线正常运行完成
void back2OriginSignal();
void back2OriginSignal_TimedDataCollection();
void broadcastLocationSignal(std::vector<double>);
@ -92,6 +110,7 @@ private:
Ui::twoMotorControl_UI ui;
QThread m_coordinatorThread;
TwoMotionCaptureCoordinator* m_coordinator = nullptr;
TwoMotionCaptureCoordinator* m_coordinator_TimedDataCollection = nullptr;
DarkAndWhiteCaptureCoordinator* m_darkCaptureCoordinator = nullptr;
DarkAndWhiteCaptureCoordinator* m_whiteCaptureCoordinator = nullptr;

View File

@ -1,5 +1,6 @@
#include "stdafx.h"
#include "recordFrameCounter.h"
#include "RasterLayer.h"
recordFrameCounter::recordFrameCounter(QWidget* parent)
: QWidget(parent)
@ -16,19 +17,19 @@ recordFrameCounter::recordFrameCounter(QWidget* parent)
layout->addWidget(m_stackedWidget);
}
void recordFrameCounter::addCounter(QWidget* tabWidget)
void recordFrameCounter::addCounter(RasterLayer* mapLayer)
{
QLabel* label = new QLabel("0");
label->setStyleSheet("color: white;");
label->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
m_labelMap.insert(tabWidget, label);
m_labelMap.insert(mapLayer, label);
m_stackedWidget->addWidget(label);
m_stackedWidget->setCurrentWidget(label);
}
void recordFrameCounter::removeCounter(QWidget* tabWidget)
void recordFrameCounter::removeCounter(RasterLayer* mapLayer)
{
auto it = m_labelMap.find(tabWidget);
auto it = m_labelMap.find(mapLayer);
if (it != m_labelMap.end())
{
QLabel* label = it.value();
@ -38,18 +39,18 @@ void recordFrameCounter::removeCounter(QWidget* tabWidget)
}
}
void recordFrameCounter::switchTo(QWidget* tabWidget)
void recordFrameCounter::switchTo(RasterLayer* mapLayer)
{
auto it = m_labelMap.find(tabWidget);
auto it = m_labelMap.find(mapLayer);
if (it != m_labelMap.end())
{
m_stackedWidget->setCurrentWidget(it.value());
}
}
void recordFrameCounter::updateFrameCount(QWidget* tabWidget, int frameCount)
void recordFrameCounter::updateFrameCount(RasterLayer* mapLayer, int frameCount)
{
auto it = m_labelMap.find(tabWidget);
auto it = m_labelMap.find(mapLayer);
if (it != m_labelMap.end())
{
it.value()->setText(QString::number(frameCount));

View File

@ -6,19 +6,20 @@
#include <QHBoxLayout>
#include <QMap>
class RasterLayer;
class recordFrameCounter : public QWidget
{
Q_OBJECT
public:
explicit recordFrameCounter(QWidget* parent = nullptr);
void addCounter(QWidget* tabWidget);
void removeCounter(QWidget* tabWidget);
void switchTo(QWidget* tabWidget);
void updateFrameCount(QWidget* tabWidget, int frameCount);
void addCounter(RasterLayer* mapLayer);
void removeCounter(RasterLayer* mapLayer);
void switchTo(RasterLayer* mapLayer);
void updateFrameCount(RasterLayer* mapLayer, int frameCount);
private:
QStackedWidget* m_stackedWidget = nullptr;
QMap<QWidget*, QLabel*> m_labelMap;
QMap<RasterLayer*, QLabel*> m_labelMap;
};