Compare commits
4 Commits
8329c00165
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 5372a7806c | |||
| 59abab3f5d | |||
| fc3853c3ca | |||
| d9f1ed922b |
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,6 +8,7 @@ HPPA - 副本.ui
|
|||||||
icon
|
icon
|
||||||
ignore_*
|
ignore_*
|
||||||
resources
|
resources
|
||||||
|
*.bkp
|
||||||
|
|
||||||
## Ignore Visual Studio temporary files, build results, and
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
## files generated by popular Visual Studio add-ons.
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|||||||
@ -576,7 +576,7 @@ HPPA::HPPA(QWidget* parent)
|
|||||||
initMapTools();
|
initMapTools();
|
||||||
|
|
||||||
//定时采集
|
//定时采集
|
||||||
initTimedDataCollection();
|
connect(this->ui.mActionTimedDataCollection, SIGNAL(triggered()), this, SLOT(onTimedDataCollection()));
|
||||||
|
|
||||||
QString strPath = QCoreApplication::applicationDirPath() + "/UILayout.ini";
|
QString strPath = QCoreApplication::applicationDirPath() + "/UILayout.ini";
|
||||||
QFile file(strPath);
|
QFile file(strPath);
|
||||||
@ -593,22 +593,35 @@ HPPA::HPPA(QWidget* parent)
|
|||||||
|
|
||||||
void HPPA::initTimedDataCollection()
|
void HPPA::initTimedDataCollection()
|
||||||
{
|
{
|
||||||
connect(this->ui.mActionTimedDataCollection, SIGNAL(triggered()), this, SLOT(onTimedDataCollection()));
|
if (m_tdc)
|
||||||
mTimedDataCollectionWindow = new TimedDataCollection();
|
{
|
||||||
|
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(mTimedDataCollectionWindow, &TimedDataCollection::hyperCamParm, this, &HPPA::setTimedDataCollectionHyperCamParm);
|
connect(m_tdc, &TimedDataCollection::hyperCamParm, this, &HPPA::setTimedDataCollectionHyperCamParm);
|
||||||
connect(mTimedDataCollectionWindow, &TimedDataCollection::camParm, this, &HPPA::setTimedDataCollectionCamParm);
|
connect(m_tdc, &TimedDataCollection::camParm, this, &HPPA::setTimedDataCollectionCamParm);
|
||||||
connect(mTimedDataCollectionWindow, &TimedDataCollection::motorParm, this, &HPPA::setTimedDataCollectionMotorParm);
|
connect(m_tdc, &TimedDataCollection::motorParm, this, &HPPA::setTimedDataCollectionMotorParm);
|
||||||
connect(mTimedDataCollectionWindow, &TimedDataCollection::startRecordSignal, this, &HPPA::onStartTimedDataCollection);
|
connect(m_tdc, &TimedDataCollection::startRecordSignal, this, &HPPA::onStartTimedDataCollection);
|
||||||
|
|
||||||
connect(mTimedDataCollectionWindow, &TimedDataCollection::switchHalogenLampSignal, m_pc3D, &PowerControl3D::switchHalogenLampPower);
|
connect(m_tdc, &TimedDataCollection::switchHalogenLampSignal, m_pc3D, &PowerControl3D::switchHalogenLampPower);
|
||||||
connect(mTimedDataCollectionWindow, &TimedDataCollection::switchD65LampSignal, m_pc3D, &PowerControl3D::switchD65LampPower);
|
connect(m_tdc, &TimedDataCollection::switchD65LampSignal, m_pc3D, &PowerControl3D::switchD65LampPower);
|
||||||
connect(mTimedDataCollectionWindow, &TimedDataCollection::switchSlrSignal, m_pc3D, &PowerControl3D::switchSlrPower);
|
connect(m_tdc, &TimedDataCollection::switchSlrSignal, m_pc3D, &PowerControl3D::switchSlrPower);
|
||||||
|
|
||||||
// 相机/马达 → 定时采集控制器
|
// 相机/马达 → 定时采集控制器
|
||||||
connect(m_tmc, &TwoMotorControl::sequenceComplete, mTimedDataCollectionWindow, &TimedDataCollection::subTaskCompleted);
|
connect(m_tmc, &TwoMotorControl::sequenceComplete, m_tdc, &TimedDataCollection::subTaskCompleted);
|
||||||
connect(m_tmc, &TwoMotorControl::back2OriginSignal_TimedDataCollection, mTimedDataCollectionWindow, &TimedDataCollection::onBack2Origin);
|
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)
|
void HPPA::setTimedDataCollectionHyperCamParm(int camType, double f, double e, QString filePath, QString fileName)
|
||||||
@ -716,8 +729,7 @@ void HPPA::onTimedDataCollection()
|
|||||||
QString checkedScenarioName = checkedScenario->objectName();
|
QString checkedScenarioName = checkedScenario->objectName();
|
||||||
if (checkedScenarioName == "mAction3DPlantPhenotypeScenario")//计划采集
|
if (checkedScenarioName == "mAction3DPlantPhenotypeScenario")//计划采集
|
||||||
{
|
{
|
||||||
mTimedDataCollectionWindow->show();
|
initTimedDataCollection();
|
||||||
//mTimedDataCollectionWindow->exec();
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1706,7 +1718,7 @@ void HPPA::onStartRecordStep1()
|
|||||||
//判断光谱仪
|
//判断光谱仪
|
||||||
if(!testImagerVality())
|
if(!testImagerVality())
|
||||||
{
|
{
|
||||||
showMessageBox(QString::fromLocal8Bit("找不到光谱仪!"));
|
showMessageBox(QString::fromLocal8Bit("找不到高光谱仪!"));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2272,7 +2284,7 @@ void HPPA::onconnect()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bool res = showResultMessageBox(QString::fromLocal8Bit("相机连接"), QString::fromLocal8Bit("确定要重连相机吗?"));
|
bool res = showResultMessageBox(QString::fromLocal8Bit("高光谱仪连接"), QString::fromLocal8Bit("确定要重连高光谱仪吗?"));
|
||||||
if (res)
|
if (res)
|
||||||
{
|
{
|
||||||
disconnectImagerAndCleanup();
|
disconnectImagerAndCleanup();
|
||||||
@ -2308,7 +2320,7 @@ void HPPA::onconnect()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
showMessageBox(QString::fromLocal8Bit("请选择相机类型!"));
|
showMessageBox(QString::fromLocal8Bit("请选择高光谱仪类型!"));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2389,7 +2401,7 @@ void HPPA::onconnect()
|
|||||||
delete m_Imager;
|
delete m_Imager;
|
||||||
m_Imager = nullptr;
|
m_Imager = nullptr;
|
||||||
|
|
||||||
showMessageBox(QString::fromLocal8Bit("请连接相机!"));
|
showMessageBox(QString::fromLocal8Bit("请连接高光谱仪!"));
|
||||||
}
|
}
|
||||||
catch (int e)//ximea相机异常
|
catch (int e)//ximea相机异常
|
||||||
{
|
{
|
||||||
@ -2398,7 +2410,7 @@ void HPPA::onconnect()
|
|||||||
delete m_Imager;
|
delete m_Imager;
|
||||||
m_Imager = nullptr;
|
m_Imager = nullptr;
|
||||||
|
|
||||||
showMessageBox(QString::fromLocal8Bit("请连接相机!"));
|
showMessageBox(QString::fromLocal8Bit("请连接高光谱仪!"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
#include <QChart>
|
#include <QChart>
|
||||||
#include <QChartView>
|
#include <QChartView>
|
||||||
#include <QValueAxis>
|
#include <QValueAxis>
|
||||||
|
#include <QPointer>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
@ -296,6 +297,7 @@ private:
|
|||||||
RobotArmControl* m_rac;
|
RobotArmControl* m_rac;
|
||||||
OneMotorControl* m_omc;
|
OneMotorControl* m_omc;
|
||||||
TwoMotorControl* m_tmc;
|
TwoMotorControl* m_tmc;
|
||||||
|
QPointer<TimedDataCollection> m_tdc;
|
||||||
|
|
||||||
View3DModelManager* m_view3DModelManager;
|
View3DModelManager* m_view3DModelManager;
|
||||||
|
|
||||||
@ -321,7 +323,6 @@ private:
|
|||||||
bool showResultMessageBox(QString title, QString msg);
|
bool showResultMessageBox(QString title, QString msg);
|
||||||
void disconnectImagerAndCleanup();
|
void disconnectImagerAndCleanup();
|
||||||
|
|
||||||
TimedDataCollection* mTimedDataCollectionWindow;
|
|
||||||
void initTimedDataCollection();
|
void initTimedDataCollection();
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
|
|||||||
@ -140,6 +140,7 @@
|
|||||||
<ClCompile Include="MapToolSpectral.cpp" />
|
<ClCompile Include="MapToolSpectral.cpp" />
|
||||||
<ClCompile Include="MotorWindowBase.cpp" />
|
<ClCompile Include="MotorWindowBase.cpp" />
|
||||||
<ClCompile Include="OneMotorControl.cpp" />
|
<ClCompile Include="OneMotorControl.cpp" />
|
||||||
|
<ClCompile Include="PathLine.cpp" />
|
||||||
<ClCompile Include="path_tc.cpp" />
|
<ClCompile Include="path_tc.cpp" />
|
||||||
<ClCompile Include="PowerControl.cpp" />
|
<ClCompile Include="PowerControl.cpp" />
|
||||||
<ClCompile Include="PowerControl3D.cpp" />
|
<ClCompile Include="PowerControl3D.cpp" />
|
||||||
@ -164,6 +165,7 @@
|
|||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="TabManager.cpp" />
|
<ClCompile Include="TabManager.cpp" />
|
||||||
|
<ClCompile Include="TaskTreeModel.cpp" />
|
||||||
<ClCompile Include="TimedDataCollection.cpp" />
|
<ClCompile Include="TimedDataCollection.cpp" />
|
||||||
<ClCompile Include="TimedDataCollectionDataStructures.cpp" />
|
<ClCompile Include="TimedDataCollectionDataStructures.cpp" />
|
||||||
<ClCompile Include="TwoMotorControl.cpp" />
|
<ClCompile Include="TwoMotorControl.cpp" />
|
||||||
@ -251,6 +253,7 @@
|
|||||||
<QtMoc Include="MapTools.h" />
|
<QtMoc Include="MapTools.h" />
|
||||||
<ClInclude Include="MotorWindowBase.h" />
|
<ClInclude Include="MotorWindowBase.h" />
|
||||||
<QtMoc Include="PowerControl3D.h" />
|
<QtMoc Include="PowerControl3D.h" />
|
||||||
|
<ClInclude Include="PathLine.h" />
|
||||||
<ClInclude Include="RasterDataProvider.h" />
|
<ClInclude Include="RasterDataProvider.h" />
|
||||||
<ClInclude Include="MultibandRasterRenderer.h" />
|
<ClInclude Include="MultibandRasterRenderer.h" />
|
||||||
<ClInclude Include="RasterImageLayer.h" />
|
<ClInclude Include="RasterImageLayer.h" />
|
||||||
@ -262,6 +265,7 @@
|
|||||||
<QtMoc Include="SingleLensReflexCameraWindow.h" />
|
<QtMoc Include="SingleLensReflexCameraWindow.h" />
|
||||||
<QtMoc Include="TimedDataCollection.h" />
|
<QtMoc Include="TimedDataCollection.h" />
|
||||||
<QtMoc Include="TimedDataCollectionDataStructures.h" />
|
<QtMoc Include="TimedDataCollectionDataStructures.h" />
|
||||||
|
<QtMoc Include="TaskTreeModel.h" />
|
||||||
<ClInclude Include="utility_tc.h" />
|
<ClInclude Include="utility_tc.h" />
|
||||||
<QtMoc Include="aboutWindow.h" />
|
<QtMoc Include="aboutWindow.h" />
|
||||||
<ClInclude Include="hppaConfigFile.h" />
|
<ClInclude Include="hppaConfigFile.h" />
|
||||||
|
|||||||
@ -247,6 +247,12 @@
|
|||||||
<ClCompile Include="PowerControl3D.cpp">
|
<ClCompile Include="PowerControl3D.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="TaskTreeModel.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="PathLine.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<QtMoc Include="fileOperation.h">
|
<QtMoc Include="fileOperation.h">
|
||||||
@ -405,6 +411,9 @@
|
|||||||
<QtMoc Include="PowerControl3D.h">
|
<QtMoc Include="PowerControl3D.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</QtMoc>
|
</QtMoc>
|
||||||
|
<QtMoc Include="TaskTreeModel.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</QtMoc>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="imageProcessor.h">
|
<ClInclude Include="imageProcessor.h">
|
||||||
@ -461,6 +470,9 @@
|
|||||||
<ClInclude Include="MotorWindowBase.h">
|
<ClInclude Include="MotorWindowBase.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="PathLine.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<QtUic Include="FocusDialog.ui">
|
<QtUic Include="FocusDialog.ui">
|
||||||
|
|||||||
319
HPPA/PathLine.cpp
Normal file
319
HPPA/PathLine.cpp
Normal 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
51
HPPA/PathLine.h
Normal 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;
|
||||||
|
};
|
||||||
654
HPPA/TaskTreeModel.cpp
Normal file
654
HPPA/TaskTreeModel.cpp
Normal 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
118
HPPA/TaskTreeModel.h
Normal 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;
|
||||||
|
};
|
||||||
@ -1,50 +1,128 @@
|
|||||||
#include "TimedDataCollection.h"
|
#include "TimedDataCollection.h"
|
||||||
#include <QDateTime>
|
#include <QVBoxLayout>
|
||||||
#include <QDebug>
|
#include <QHBoxLayout>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QGroupBox>
|
||||||
|
#include <QSplitter>
|
||||||
|
|
||||||
TimedDataCollection::TimedDataCollection(QWidget* parent)
|
TimedDataCollection::TimedDataCollection(QWidget* parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, m_scheduler(nullptr)
|
|
||||||
{
|
{
|
||||||
ui.setupUi(this);
|
ui.setupUi(this);
|
||||||
|
|
||||||
ui.treeWidget->setDragEnabled(true); // 启用拖拽
|
setupUI();
|
||||||
ui.treeWidget->setAcceptDrops(true); // 接受拖放
|
|
||||||
ui.treeWidget->setDropIndicatorShown(true); // 显示插入位置指示线
|
|
||||||
ui.treeWidget->setDragDropMode(QAbstractItemView::InternalMove); // 内部移动
|
|
||||||
|
|
||||||
// 初始化调度器
|
// 初始化调度器
|
||||||
m_scheduler = new TaskScheduler(this);
|
m_scheduler = new TaskScheduler(this);
|
||||||
|
|
||||||
// 连接调度器信号
|
setupConnections();
|
||||||
connect(m_scheduler, &TaskScheduler::taskStarted, this, &TimedDataCollection::taskStarted);
|
|
||||||
connect(m_scheduler, &TaskScheduler::taskFinished, this, &TimedDataCollection::taskFinished);
|
// 连接按钮信号
|
||||||
connect(m_scheduler, &TaskScheduler::subTaskStarted, this, &TimedDataCollection::subTaskStarted);
|
connect(ui.start_btn, &QPushButton::clicked, this, &TimedDataCollection::startScheduler);
|
||||||
connect(m_scheduler, &TaskScheduler::subTaskFinished, this, &TimedDataCollection::subTaskFinished);
|
connect(ui.stop_btn, &QPushButton::clicked, this, &TimedDataCollection::stopScheduler);
|
||||||
connect(m_scheduler, &TaskScheduler::errorOccurred, this, &TimedDataCollection::errorOccurred);
|
|
||||||
|
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::hyperCamParm,
|
||||||
connect(m_scheduler, &TaskScheduler::camParm, this, &TimedDataCollection::camParm);
|
this, &TimedDataCollection::hyperCamParm);
|
||||||
connect(m_scheduler, &TaskScheduler::motorParm, this, &TimedDataCollection::motorParm);
|
connect(m_scheduler, &TaskScheduler::camParm,
|
||||||
connect(m_scheduler, &TaskScheduler::startRecordSignal, this, &TimedDataCollection::startRecordSignal);
|
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::switchHalogenLampSignal,
|
||||||
connect(m_scheduler, &TaskScheduler::switchD65LampSignal, this, &TimedDataCollection::switchD65LampSignal);
|
this, &TimedDataCollection::switchHalogenLampSignal);
|
||||||
connect(m_scheduler, &TaskScheduler::switchSlrSignal, this, &TimedDataCollection::switchSlrSignal);
|
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::sequenceCompleteSignal,
|
||||||
connect(this, &TimedDataCollection::Back2OriginSignal, m_scheduler, &TaskScheduler::Back2OriginSignal);
|
m_scheduler, &TaskScheduler::sequenceCompleteSignal);
|
||||||
|
connect(this, &TimedDataCollection::Back2OriginSignal,
|
||||||
//writeRead();
|
m_scheduler, &TaskScheduler::Back2OriginSignal);
|
||||||
readTimedTaskFromFile("D:/0tmp/3Dtest/task.json");
|
|
||||||
|
|
||||||
// 加载任务到调度器
|
|
||||||
m_scheduler->loadTasks(m_loadedTasks);
|
|
||||||
|
|
||||||
connect(ui.run_btn, &QPushButton::clicked, this, &TimedDataCollection::startScheduler);
|
|
||||||
|
|
||||||
startScheduler();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TimedDataCollection::~TimedDataCollection()
|
TimedDataCollection::~TimedDataCollection()
|
||||||
@ -54,6 +132,369 @@ TimedDataCollection::~TimedDataCollection()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
void TimedDataCollection::subTaskCompleted(int status)
|
||||||
{
|
{
|
||||||
emit sequenceCompleteSignal(status);
|
emit sequenceCompleteSignal(status);
|
||||||
@ -63,107 +504,3 @@ void TimedDataCollection::onBack2Origin()
|
|||||||
{
|
{
|
||||||
emit Back2OriginSignal();
|
emit Back2OriginSignal();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimedDataCollection::startScheduler()
|
|
||||||
{
|
|
||||||
if (m_scheduler) {
|
|
||||||
m_scheduler->start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TimedDataCollection::stopScheduler()
|
|
||||||
{
|
|
||||||
if (m_scheduler) {
|
|
||||||
m_scheduler->stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TimedDataCollection::readTimedTaskFromFile(const QString& filePath)
|
|
||||||
{
|
|
||||||
// 从文件读取
|
|
||||||
if (TimedDataCollectionDataStructuresReaderWriter::loadTasksFromFile(filePath, m_loadedTasks)) {
|
|
||||||
qDebug() << "Tasks loaded successfully, count:" << m_loadedTasks.size();
|
|
||||||
for (const auto& t : m_loadedTasks) {
|
|
||||||
qDebug() << " Task ID:" << t.id << "SubTasks:" << t.subTasks.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
qDebug() << "Failed to load tasks from:" << filePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
int a = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TimedDataCollection::writeRead()
|
|
||||||
{
|
|
||||||
// 创建2个定时任务测试
|
|
||||||
QVector<TimedTask> tasks;
|
|
||||||
|
|
||||||
for (int i = 0; i < 2; ++i) {
|
|
||||||
TimedTask task;
|
|
||||||
task.id = i + 1;
|
|
||||||
task.scheduledTime = QDateTime::currentDateTime().addDays(i + 1);
|
|
||||||
task.savePath = QString("D:/0tmp/data");
|
|
||||||
task.status = TaskStatus::Waiting;
|
|
||||||
|
|
||||||
// 创建4种子任务
|
|
||||||
QVector<SubTaskType> types = {
|
|
||||||
SubTaskType::HyperSpectual400_1000nm,
|
|
||||||
SubTaskType::HyperSpectual1000_1700nm,
|
|
||||||
SubTaskType::SingleLensReflex,
|
|
||||||
SubTaskType::DepthCamera
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int j = 0; j < types.size(); ++j) {
|
|
||||||
SubTask subTask;
|
|
||||||
subTask.type = types[j];
|
|
||||||
subTask.startTime = task.scheduledTime.addSecs(j * 3600);
|
|
||||||
subTask.endTime = subTask.startTime.addSecs(1800);
|
|
||||||
subTask.durationMinutes = 1800;
|
|
||||||
subTask.estimatedDurationMinutes = 1800;
|
|
||||||
subTask.pathLineFilePath = QString("D:/0tmp/3Dtest/pathLine/%1.RecordLine3").arg(j);
|
|
||||||
subTask.status = TaskStatus::Waiting;
|
|
||||||
|
|
||||||
// 根据类型设置特有属性
|
|
||||||
if (subTask.type == SubTaskType::HyperSpectual400_1000nm ||
|
|
||||||
subTask.type == SubTaskType::HyperSpectual1000_1700nm) {
|
|
||||||
subTask.frameRate = 30.0;
|
|
||||||
subTask.exposureTime = 1;
|
|
||||||
}
|
|
||||||
if (subTask.type == SubTaskType::HyperSpectual1000_1700nm) {
|
|
||||||
subTask.defaultRenderBand = 1200;
|
|
||||||
}
|
|
||||||
if (subTask.type == SubTaskType::SingleLensReflex ||
|
|
||||||
subTask.type == SubTaskType::DepthCamera) {
|
|
||||||
subTask.captureIntervalSeconds = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
task.subTasks.append(subTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.append(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存到文件
|
|
||||||
QString filePath = "D:/0tmp/3Dtest/task.json";
|
|
||||||
if (TimedDataCollectionDataStructuresReaderWriter::saveTasksToFile(filePath, tasks)) {
|
|
||||||
qDebug() << "Tasks saved to:" << filePath;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
qDebug() << "Failed to save tasks to:" << filePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从文件读取
|
|
||||||
QVector<TimedTask> loadedTasks;
|
|
||||||
if (TimedDataCollectionDataStructuresReaderWriter::loadTasksFromFile(filePath, loadedTasks)) {
|
|
||||||
qDebug() << "Tasks loaded successfully, count:" << loadedTasks.size();
|
|
||||||
for (const auto& t : loadedTasks) {
|
|
||||||
qDebug() << " Task ID:" << t.id << "SubTasks:" << t.subTasks.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
qDebug() << "Failed to load tasks from:" << filePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
int a = 1;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,12 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QNetworkRequest>
|
#include <QTreeView>
|
||||||
#include <QNetworkReply>
|
#include <QHeaderView>
|
||||||
#include <QNetworkAccessManager>
|
#include <QLabel>
|
||||||
|
#include <QFileDialog>
|
||||||
#include "ui_TimedDataCollection_ui.h"
|
#include "ui_TimedDataCollection_ui.h"
|
||||||
#include "TimedDataCollectionDataStructures.h"
|
#include "TimedDataCollectionDataStructures.h"
|
||||||
|
#include "TaskTreeModel.h"
|
||||||
|
#include "PathLine.h"
|
||||||
|
|
||||||
class TimedDataCollection : public QDialog
|
class TimedDataCollection : public QDialog
|
||||||
{
|
{
|
||||||
@ -24,15 +26,27 @@ public Q_SLOTS:
|
|||||||
void subTaskCompleted(int status);
|
void subTaskCompleted(int status);
|
||||||
void onBack2Origin();
|
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:
|
Q_SIGNALS:
|
||||||
void taskStarted(int taskId);
|
void taskStarted(int taskId);
|
||||||
void taskFinished(int taskId, bool success);
|
void taskFinished(int taskId, bool success);
|
||||||
|
|
||||||
void subTaskStarted(int taskId, int subTaskIndex);
|
void subTaskStarted(int taskId, int subTaskIndex);
|
||||||
void subTaskFinished(int taskId, int subTaskIndex);
|
void subTaskFinished(int taskId, int subTaskIndex);
|
||||||
void errorOccurred(const QString& error);
|
void errorOccurred(const QString& error);
|
||||||
|
|
||||||
// 采集相关信号 (透传 TaskScheduler)
|
|
||||||
void hyperCamParm(int camType, double f, double e, QString filePath, QString fileName);
|
void hyperCamParm(int camType, double f, double e, QString filePath, QString fileName);
|
||||||
void camParm(int camType, int captureIntervalSeconds, QString folder);
|
void camParm(int camType, int captureIntervalSeconds, QString folder);
|
||||||
void motorParm(QString pathLineFilePath);
|
void motorParm(QString pathLineFilePath);
|
||||||
@ -42,15 +56,22 @@ Q_SIGNALS:
|
|||||||
void switchD65LampSignal(int state);
|
void switchD65LampSignal(int state);
|
||||||
void switchSlrSignal(int state);
|
void switchSlrSignal(int state);
|
||||||
|
|
||||||
// 马达反馈的信号
|
|
||||||
void sequenceCompleteSignal(int status);
|
void sequenceCompleteSignal(int status);
|
||||||
void Back2OriginSignal();
|
void Back2OriginSignal();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void setupUI();
|
||||||
|
void setupConnections();
|
||||||
|
void updateStatusBar();
|
||||||
|
void expandRunningTask();
|
||||||
|
|
||||||
|
void onSelectTaskFile();
|
||||||
|
|
||||||
Ui::TimedDataCollection_ui ui;
|
Ui::TimedDataCollection_ui ui;
|
||||||
|
|
||||||
void writeRead();
|
TaskTreeModel* m_taskModel; // 任务树形模型
|
||||||
|
|
||||||
QVector<TimedTask> m_loadedTasks;
|
|
||||||
TaskScheduler* m_scheduler;
|
TaskScheduler* m_scheduler;
|
||||||
|
|
||||||
|
int m_currentTaskId = -1;
|
||||||
|
int m_currentSubTaskIndex = -1;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
// ==================== 公共接口实现 ====================
|
// ==================== 公共接口实现 ====================
|
||||||
|
|
||||||
@ -88,9 +89,9 @@ QString TimedDataCollectionDataStructuresReaderWriter::taskStatusToString(TaskSt
|
|||||||
|
|
||||||
TaskStatus TimedDataCollectionDataStructuresReaderWriter::stringToTaskStatus(const QString& str)
|
TaskStatus TimedDataCollectionDataStructuresReaderWriter::stringToTaskStatus(const QString& str)
|
||||||
{
|
{
|
||||||
if (str == "Running") return TaskStatus::Running;
|
//if (str == "Running") return TaskStatus::Running;
|
||||||
if (str == "Finished") return TaskStatus::Finished;
|
//if (str == "Finished") return TaskStatus::Finished;
|
||||||
return TaskStatus::Waiting;
|
return TaskStatus::default;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TimedDataCollectionDataStructuresReaderWriter::subTaskTypeToString(SubTaskType type)
|
QString TimedDataCollectionDataStructuresReaderWriter::subTaskTypeToString(SubTaskType type)
|
||||||
@ -135,8 +136,8 @@ QJsonObject TimedDataCollectionDataStructuresReaderWriter::subTaskToJson(const S
|
|||||||
bool TimedDataCollectionDataStructuresReaderWriter::jsonToSubTask(const QJsonObject& json, SubTask& subTask)
|
bool TimedDataCollectionDataStructuresReaderWriter::jsonToSubTask(const QJsonObject& json, SubTask& subTask)
|
||||||
{
|
{
|
||||||
subTask.type = stringToSubTaskType(json["type"].toString());
|
subTask.type = stringToSubTaskType(json["type"].toString());
|
||||||
subTask.startTime = QDateTime::fromString(json["startTime"].toString(), Qt::ISODate);
|
subTask.startTime = QDateTime::fromString(QString(), Qt::ISODate);
|
||||||
subTask.endTime = QDateTime::fromString(json["endTime"].toString(), Qt::ISODate);
|
subTask.endTime = QDateTime::fromString(QString(), Qt::ISODate);
|
||||||
subTask.durationMinutes = json["durationMinutes"].toDouble();
|
subTask.durationMinutes = json["durationMinutes"].toDouble();
|
||||||
subTask.estimatedDurationMinutes = json["estimatedDurationMinutes"].toDouble();
|
subTask.estimatedDurationMinutes = json["estimatedDurationMinutes"].toDouble();
|
||||||
subTask.pathLineFilePath = json["pathLineFilePath"].toString();
|
subTask.pathLineFilePath = json["pathLineFilePath"].toString();
|
||||||
@ -158,6 +159,7 @@ QJsonObject TimedDataCollectionDataStructuresReaderWriter::timedTaskToJson(const
|
|||||||
obj["startTime"] = task.startTime.toString(Qt::ISODate);
|
obj["startTime"] = task.startTime.toString(Qt::ISODate);
|
||||||
obj["endTime"] = task.endTime.toString(Qt::ISODate);
|
obj["endTime"] = task.endTime.toString(Qt::ISODate);
|
||||||
obj["durationMinutes"] = task.durationMinutes;
|
obj["durationMinutes"] = task.durationMinutes;
|
||||||
|
obj["estimatedDurationMinutes"] = task.estimatedDurationMinutes;
|
||||||
obj["savePath"] = task.savePath;
|
obj["savePath"] = task.savePath;
|
||||||
obj["status"] = taskStatusToString(task.status);
|
obj["status"] = taskStatusToString(task.status);
|
||||||
obj["HalogenLampPreheatingTime_Minute"] = task.HalogenLampPreheatingTime_Minute;
|
obj["HalogenLampPreheatingTime_Minute"] = task.HalogenLampPreheatingTime_Minute;
|
||||||
@ -175,9 +177,10 @@ bool TimedDataCollectionDataStructuresReaderWriter::jsonToTimedTask(const QJsonO
|
|||||||
{
|
{
|
||||||
task.id = json["id"].toInt();
|
task.id = json["id"].toInt();
|
||||||
task.scheduledTime = QDateTime::fromString(json["scheduledTime"].toString(), Qt::ISODate);
|
task.scheduledTime = QDateTime::fromString(json["scheduledTime"].toString(), Qt::ISODate);
|
||||||
task.startTime = QDateTime::fromString(json["startTime"].toString(), Qt::ISODate);
|
task.startTime = QDateTime::fromString(QString(), Qt::ISODate);
|
||||||
task.endTime = QDateTime::fromString(json["endTime"].toString(), Qt::ISODate);
|
task.endTime = QDateTime::fromString(QString(), Qt::ISODate);
|
||||||
task.durationMinutes = json["durationMinutes"].toDouble();
|
task.durationMinutes = json["durationMinutes"].toDouble();
|
||||||
|
task.estimatedDurationMinutes = json["estimatedDurationMinutes"].toDouble();
|
||||||
task.savePath = json["savePath"].toString();
|
task.savePath = json["savePath"].toString();
|
||||||
task.status = stringToTaskStatus(json["status"].toString());
|
task.status = stringToTaskStatus(json["status"].toString());
|
||||||
task.HalogenLampPreheatingTime_Minute = json["HalogenLampPreheatingTime_Minute"].toDouble();
|
task.HalogenLampPreheatingTime_Minute = json["HalogenLampPreheatingTime_Minute"].toDouble();
|
||||||
@ -220,6 +223,10 @@ void TaskExecutor::execute(const TimedTask& task)
|
|||||||
|
|
||||||
m_task = task;
|
m_task = task;
|
||||||
m_task.startTime = QDateTime::currentDateTime();
|
m_task.startTime = QDateTime::currentDateTime();
|
||||||
|
m_task.status = TaskStatus::Running;
|
||||||
|
|
||||||
|
emit taskUpdated(m_task);
|
||||||
|
|
||||||
|
|
||||||
m_currentSubTaskIndex = 0;
|
m_currentSubTaskIndex = 0;
|
||||||
m_isRunning = true;
|
m_isRunning = true;
|
||||||
@ -293,6 +300,7 @@ void TaskExecutor::onSequenceComplete(int status)
|
|||||||
qDebug() << "TaskExecutor: subtask "<< m_currentSubTaskIndex<< " time consuming(Minutes): "<< subTask.durationMinutes;
|
qDebug() << "TaskExecutor: subtask "<< m_currentSubTaskIndex<< " time consuming(Minutes): "<< subTask.durationMinutes;
|
||||||
|
|
||||||
emit subTaskFinished(m_currentSubTaskIndex, subTask.type, (status == 0));
|
emit subTaskFinished(m_currentSubTaskIndex, subTask.type, (status == 0));
|
||||||
|
emit taskUpdated(m_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -315,6 +323,8 @@ void TaskExecutor::onSequenceComplete(int status)
|
|||||||
int nestSubTaskIndex = m_currentSubTaskIndex + 1;
|
int nestSubTaskIndex = m_currentSubTaskIndex + 1;
|
||||||
if (nestSubTaskIndex >= m_task.subTasks.size())
|
if (nestSubTaskIndex >= m_task.subTasks.size())
|
||||||
{
|
{
|
||||||
|
emit switchD65LampSignal(0);
|
||||||
|
emit switchSlrSignal(0);
|
||||||
emit switchHalogenLampSignal(0);
|
emit switchHalogenLampSignal(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -331,6 +341,8 @@ void TaskExecutor::onSequenceComplete(int status)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit taskUpdated(m_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskExecutor::onBack2Origin()
|
void TaskExecutor::onBack2Origin()
|
||||||
@ -355,12 +367,15 @@ void TaskExecutor::onBack2Origin()
|
|||||||
// 所有子任务完成
|
// 所有子任务完成
|
||||||
m_task.endTime = QDateTime::currentDateTime();
|
m_task.endTime = QDateTime::currentDateTime();
|
||||||
m_task.durationMinutes = (double)m_task.startTime.secsTo(m_task.endTime) / 60;
|
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;
|
qDebug() << "TaskExecutor: task time consuming(Minutes): " << m_task.durationMinutes;
|
||||||
|
|
||||||
m_isRunning = false;
|
m_isRunning = false;
|
||||||
qDebug() << "TaskExecutor: All subtasks completed";
|
qDebug() << "TaskExecutor: All subtasks completed";
|
||||||
emit finished(true);
|
emit finished(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit taskUpdated(m_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskExecutor::onError(const QString& error)
|
void TaskExecutor::onError(const QString& error)
|
||||||
@ -383,6 +398,8 @@ void TaskExecutor::executeNextSubTask()
|
|||||||
subTask.status = TaskStatus::Running;
|
subTask.status = TaskStatus::Running;
|
||||||
subTask.startTime = QDateTime::currentDateTime();
|
subTask.startTime = QDateTime::currentDateTime();
|
||||||
|
|
||||||
|
emit taskUpdated(m_task);
|
||||||
|
|
||||||
QString tmp = "TaskExecutor: Starting subtask" + QString::number(m_currentSubTaskIndex) + "type:" + static_cast<int>(subTask.type);
|
QString tmp = "TaskExecutor: Starting subtask" + QString::number(m_currentSubTaskIndex) + "type:" + static_cast<int>(subTask.type);
|
||||||
printMsgAndTime(tmp);
|
printMsgAndTime(tmp);
|
||||||
//printMsgAndTime("excute " + QString::number(m_currentSubTaskIndex) + " subTask: ");
|
//printMsgAndTime("excute " + QString::number(m_currentSubTaskIndex) + " subTask: ");
|
||||||
@ -494,26 +511,55 @@ void TaskScheduler::stop()
|
|||||||
|
|
||||||
void TaskScheduler::checkTasks()
|
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();
|
QDateTime now = QDateTime::currentDateTime();
|
||||||
|
|
||||||
for (auto& task : m_tasks) {
|
if (m_currentTaskId >= 0)//有任务正在执行
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& task : m_tasks)
|
||||||
|
{
|
||||||
if (task.status != TaskStatus::Waiting) continue;
|
if (task.status != TaskStatus::Waiting) continue;
|
||||||
|
|
||||||
// 超过计划时间1分钟以上,认为任务已过时,跳过
|
// 超过计划时间threSecs,认为任务已过时,跳过
|
||||||
if (task.scheduledTime.addSecs(60) < now) {
|
qint64 threSecs = 30;
|
||||||
|
if (task.scheduledTime.addSecs(threSecs) < now)
|
||||||
|
{
|
||||||
std::cerr << "TaskScheduler::checkTasks,任务已过时,跳过:" << task.id << std::endl;
|
std::cerr << "TaskScheduler::checkTasks,任务已过时,跳过:" << task.id << std::endl;
|
||||||
task.status = TaskStatus::Finished;
|
task.status = TaskStatus::Timeout;
|
||||||
|
for (size_t i = 0; i < task.subTaskCount(); i++)
|
||||||
|
{
|
||||||
|
task.subTasks[i].status = TaskStatus::Timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit taskDataChanged(task);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task.scheduledTime > now) 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;
|
std::cerr << "TaskScheduler::checkTasks,到达计划时间,启动任务" << std::endl;
|
||||||
executeTask(task);
|
executeTask(task);
|
||||||
break; // 一次只执行一个任务
|
break; // 一次只执行一个任务
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TaskScheduler::onTaskFinished(bool success)
|
void TaskScheduler::onTaskFinished(bool success)
|
||||||
{
|
{
|
||||||
@ -554,10 +600,10 @@ void TaskScheduler::executeTask(TimedTask& task)
|
|||||||
{
|
{
|
||||||
qDebug() << "TaskScheduler: Executing task" << task.id;
|
qDebug() << "TaskScheduler: Executing task" << task.id;
|
||||||
|
|
||||||
updateTaskStatus(task.id, TaskStatus::Running);
|
//updateTaskStatus(task.id, TaskStatus::Running);
|
||||||
m_currentTaskId = task.id;
|
m_currentTaskId = task.id;
|
||||||
|
|
||||||
emit taskStarted(task.id);
|
//emit taskStarted(task.id);
|
||||||
|
|
||||||
// 创建任务执行器
|
// 创建任务执行器
|
||||||
m_currentExecutor = new TaskExecutor(this);
|
m_currentExecutor = new TaskExecutor(this);
|
||||||
@ -589,6 +635,18 @@ void TaskScheduler::executeTask(TimedTask& task)
|
|||||||
connect(this, &TaskScheduler::sequenceCompleteSignal, m_currentExecutor, &TaskExecutor::onSequenceComplete);
|
connect(this, &TaskScheduler::sequenceCompleteSignal, m_currentExecutor, &TaskExecutor::onSequenceComplete);
|
||||||
connect(this, &TaskScheduler::Back2OriginSignal, m_currentExecutor, &TaskExecutor::onBack2Origin);
|
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);
|
m_currentExecutor->execute(task);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,10 +12,12 @@
|
|||||||
|
|
||||||
// 任务状态
|
// 任务状态
|
||||||
enum class TaskStatus {
|
enum class TaskStatus {
|
||||||
|
default, //未调度
|
||||||
Waiting, // 等待
|
Waiting, // 等待
|
||||||
Running, // 运行中
|
Running, // 运行中
|
||||||
Finished, // 结束
|
Finished, // 结束
|
||||||
Skiped //跳过
|
Skiped, //跳过
|
||||||
|
Timeout //超时
|
||||||
};
|
};
|
||||||
|
|
||||||
// 子任务类型
|
// 子任务类型
|
||||||
@ -49,14 +51,15 @@ struct SubTask {
|
|||||||
// ==================== 定时任务 ====================
|
// ==================== 定时任务 ====================
|
||||||
|
|
||||||
struct TimedTask {
|
struct TimedTask {
|
||||||
int id = 0; // 任务ID
|
int id = 0;
|
||||||
QDateTime scheduledTime; // 计划时间
|
QDateTime scheduledTime;
|
||||||
QDateTime startTime; // 开始时间
|
QDateTime startTime;
|
||||||
QDateTime endTime; // 结束时间
|
QDateTime endTime;
|
||||||
double durationMinutes = 0; // 耗时(秒)
|
double durationMinutes = 0;
|
||||||
QString savePath; // 数据保存路径
|
double estimatedDurationMinutes = 0;
|
||||||
QVector<SubTask> subTasks; // 子任务列表
|
QString savePath;
|
||||||
TaskStatus status = TaskStatus::Waiting; // 状态
|
QVector<SubTask> subTasks;
|
||||||
|
TaskStatus status = TaskStatus::Waiting;
|
||||||
double HalogenLampPreheatingTime_Minute;
|
double HalogenLampPreheatingTime_Minute;
|
||||||
|
|
||||||
// 计算所有子任务的预计总时间
|
// 计算所有子任务的预计总时间
|
||||||
@ -137,6 +140,7 @@ public:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void finished(bool success); // 任务完成
|
void finished(bool success); // 任务完成
|
||||||
|
void taskUpdated(const TimedTask& task); // 确保有这个信号
|
||||||
void subTaskStarted(int subTaskIndex, SubTaskType type); // 子任务开始
|
void subTaskStarted(int subTaskIndex, SubTaskType type); // 子任务开始
|
||||||
void subTaskFinished(int subTaskIndex, SubTaskType type, bool success); // 子任务完成
|
void subTaskFinished(int subTaskIndex, SubTaskType type, bool success); // 子任务完成
|
||||||
void errorOccurred(const QString& error); // 错误发生
|
void errorOccurred(const QString& error); // 错误发生
|
||||||
@ -194,6 +198,7 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void taskStarted(int taskId); // 任务开始
|
void taskStarted(int taskId); // 任务开始
|
||||||
void taskFinished(int taskId, bool success); // 任务完成
|
void taskFinished(int taskId, bool success); // 任务完成
|
||||||
|
void taskDataChanged(const TimedTask& task);
|
||||||
|
|
||||||
void schedulerStateChanged(bool running); // 调度器状态变化
|
void schedulerStateChanged(bool running); // 调度器状态变化
|
||||||
|
|
||||||
|
|||||||
@ -6,269 +6,66 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1160</width>
|
<width>1182</width>
|
||||||
<height>576</height>
|
<height>576</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Dialog</string>
|
<string>定时采集</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
|
||||||
<item row="0" column="0" colspan="3">
|
|
||||||
<widget class="QWidget" name="widget" native="true">
|
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0" colspan="3">
|
||||||
<widget class="QTreeWidget" name="treeWidget">
|
<widget class="QTreeView" name="taskTreeView"/>
|
||||||
<property name="allColumnsShowFocus">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<attribute name="headerVisible">
|
|
||||||
<bool>true</bool>
|
|
||||||
</attribute>
|
|
||||||
<column>
|
|
||||||
<property name="text">
|
|
||||||
<string>任务</string>
|
|
||||||
</property>
|
|
||||||
</column>
|
|
||||||
<column>
|
|
||||||
<property name="text">
|
|
||||||
<string>计划时间</string>
|
|
||||||
</property>
|
|
||||||
</column>
|
|
||||||
<column>
|
|
||||||
<property name="text">
|
|
||||||
<string>开始时间</string>
|
|
||||||
</property>
|
|
||||||
</column>
|
|
||||||
<column>
|
|
||||||
<property name="text">
|
|
||||||
<string>结束时间</string>
|
|
||||||
</property>
|
|
||||||
</column>
|
|
||||||
<column>
|
|
||||||
<property name="text">
|
|
||||||
<string>耗时</string>
|
|
||||||
</property>
|
|
||||||
</column>
|
|
||||||
<column>
|
|
||||||
<property name="text">
|
|
||||||
<string>状态</string>
|
|
||||||
</property>
|
|
||||||
</column>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>1</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>a</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>b</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>2</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>a</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>b</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>3</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>a</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>b</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>4</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>a</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>b</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>5</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>a</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>b</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QStackedWidget" name="stackedWidget">
|
|
||||||
<property name="currentIndex">
|
|
||||||
<number>1</number>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="page"/>
|
|
||||||
<widget class="QWidget" name="page_2"/>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QPushButton" name="addToptask_btn">
|
<widget class="QLineEdit" name="taskFileSelect_lineEdit">
|
||||||
<property name="text">
|
<property name="readOnly">
|
||||||
<string>添加总计划任务</string>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="1" column="1">
|
||||||
<widget class="QPushButton" name="delToptask_btn">
|
<widget class="QPushButton" name="taskFileSelect_btn">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>删除总计划任务</string>
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="2" column="1">
|
||||||
<widget class="QPushButton" name="addSubtask_btn">
|
<widget class="QPushButton" name="start_btn">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>添加子任务</string>
|
<string>开始调度</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="2">
|
||||||
|
<widget class="QPushButton" name="expandAll_btn">
|
||||||
|
<property name="text">
|
||||||
|
<string>展开</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="QComboBox" name="comboBox"/>
|
<widget class="QPushButton" name="stop_btn">
|
||||||
</item>
|
|
||||||
<item row="4" column="0">
|
|
||||||
<widget class="QPushButton" name="delSubtask_btn">
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>删除子任务</string>
|
<string>停止调度</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="2">
|
<item row="3" column="2">
|
||||||
<widget class="QPushButton" name="run_btn">
|
<widget class="QPushButton" name="collapseAll_btn">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>运行</string>
|
<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>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@ -292,12 +292,21 @@ TwoMotorControl::~TwoMotorControl()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TwoMotorControl::onConnectMotor()
|
void TwoMotorControl::onConnectMotor()
|
||||||
|
{
|
||||||
|
connectMotor(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TwoMotorControl::connectMotor(bool isNotification)
|
||||||
{
|
{
|
||||||
if (getMotorsConnectionStatus())
|
if (getMotorsConnectionStatus())
|
||||||
|
{
|
||||||
|
if (isNotification)
|
||||||
{
|
{
|
||||||
QMessageBox msgBox;
|
QMessageBox msgBox;
|
||||||
msgBox.setText(QString::fromLocal8Bit("马达已连接!"));
|
msgBox.setText(QString::fromLocal8Bit("马达已连接!"));
|
||||||
msgBox.exec();
|
msgBox.exec();
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,49 +555,13 @@ void TwoMotorControl::onAddRecordLine_btn()
|
|||||||
{
|
{
|
||||||
// 准备数据:获取y马达的当前位置,获取x马达的当前位置和最大位置
|
// 准备数据:获取y马达的当前位置,获取x马达的当前位置和最大位置
|
||||||
double currentPosOfYmotor = 15;
|
double currentPosOfYmotor = 15;
|
||||||
|
|
||||||
double currentPosOfXmotor = 0;
|
double currentPosOfXmotor = 0;
|
||||||
double maxRangeOfXmotor = 50;
|
double maxRangeOfXmotor = 50;
|
||||||
|
|
||||||
//获取选中行的索引
|
PathLineManager::addRecordLine(ui.recordLine_tableWidget,
|
||||||
int currentRow = ui.recordLine_tableWidget->currentRow();
|
currentPosOfYmotor, 1.0,
|
||||||
std::cout << "currentRow:" << currentRow << std::endl;
|
currentPosOfXmotor, 1.0,
|
||||||
|
maxRangeOfXmotor, 1.0);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwoMotorControl::onRemoveRecordLine_btn()
|
void TwoMotorControl::onRemoveRecordLine_btn()
|
||||||
@ -612,103 +585,37 @@ void TwoMotorControl::onSaveRecordLine2File_btn()
|
|||||||
// 确保采集线存在
|
// 确保采集线存在
|
||||||
if (ui.recordLine_tableWidget->rowCount() <= 0)
|
if (ui.recordLine_tableWidget->rowCount() <= 0)
|
||||||
{
|
{
|
||||||
QMessageBox::information(this, QString::fromLocal8Bit("提示"), QString::fromLocal8Bit("请先生成轨迹!"));
|
QMessageBox::information(this, QString::fromLocal8Bit("提示"),
|
||||||
|
QString::fromLocal8Bit("请先生成轨迹!"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileOperation* fileOperation = new FileOperation();
|
FileOperation* fileOperation = new FileOperation();
|
||||||
string directory = fileOperation->getDirectoryOfExe();
|
string directory = fileOperation->getDirectoryOfExe();
|
||||||
|
|
||||||
QString RecordLineFilePath = QFileDialog::getSaveFileName(this, tr("Save RecordLine3 File"),
|
QString RecordLineFilePath = QFileDialog::getSaveFileName(this, tr("Save RecordLine3 File"),
|
||||||
QString::fromStdString(directory),
|
QString::fromStdString(directory),
|
||||||
tr("RecordLineFile3 (*.RecordLine3)"));
|
tr("RecordLineFile3 (*.RecordLine3)"));
|
||||||
|
|
||||||
if (RecordLineFilePath.isEmpty())
|
if (RecordLineFilePath.isEmpty())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
PathLineManager::saveToFile(RecordLineFilePath, ui.recordLine_tableWidget);
|
||||||
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("保存成功!"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwoMotorControl::onReadRecordLineFile_btn()
|
void TwoMotorControl::onReadRecordLineFile_btn()
|
||||||
{
|
{
|
||||||
//打开文件
|
|
||||||
FileOperation* fileOperation = new FileOperation();
|
FileOperation* fileOperation = new FileOperation();
|
||||||
string directory = fileOperation->getDirectoryOfExe();
|
string directory = fileOperation->getDirectoryOfExe();
|
||||||
|
|
||||||
QString RecordLineFilePath = QFileDialog::getOpenFileName(this, tr("Open RecordLine3 File"),
|
QString RecordLineFilePath = QFileDialog::getOpenFileName(this, tr("Open RecordLine3 File"),
|
||||||
QString::fromStdString(directory),
|
QString::fromStdString(directory),
|
||||||
tr("RecordLineFile (*.RecordLine3)"));
|
tr("RecordLineFile (*.RecordLine3)"));
|
||||||
|
|
||||||
if (RecordLineFilePath.isEmpty())
|
if (RecordLineFilePath.isEmpty())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
readRecordLineFile(RecordLineFilePath);
|
readRecordLineFile(RecordLineFilePath);
|
||||||
}
|
}
|
||||||
void TwoMotorControl::readRecordLineFile(QString RecordLineFilePath)
|
void TwoMotorControl::readRecordLineFile(QString RecordLineFilePath)
|
||||||
{
|
{
|
||||||
FILE* RecordLineFileHandle = fopen(RecordLineFilePath.toStdString().c_str(), "rb");
|
PathLineManager::readFromFile(RecordLineFilePath, ui.recordLine_tableWidget);
|
||||||
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("读取成功!"));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,8 @@
|
|||||||
#include "SingleLensReflexCameraWindow.h"
|
#include "SingleLensReflexCameraWindow.h"
|
||||||
#include "DepthCameraWindow.h"
|
#include "DepthCameraWindow.h"
|
||||||
|
|
||||||
|
#include "PathLine.h"
|
||||||
|
|
||||||
#define PI 3.1415926
|
#define PI 3.1415926
|
||||||
|
|
||||||
class TwoMotorControl : public QDialog, public MotorWindowBase
|
class TwoMotorControl : public QDialog, public MotorWindowBase
|
||||||
@ -33,6 +35,8 @@ public:
|
|||||||
|
|
||||||
void readRecordLineFile(QString RecordLineFilePath);
|
void readRecordLineFile(QString RecordLineFilePath);
|
||||||
|
|
||||||
|
void connectMotor(bool isNotification);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ImagerOperationBase* m_Imager;
|
ImagerOperationBase* m_Imager;
|
||||||
bool getState();
|
bool getState();
|
||||||
|
|||||||
Reference in New Issue
Block a user