add,山地所贡嘎山8:
1、在类GonggashanTaskExecutor中状态机协调控制整个采集流程;
This commit is contained in:
@ -972,3 +972,241 @@ double OneMotionCoordinator::getErrorRate(double targetLoc, double actualLoc)
|
||||
|
||||
return errorRate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------------------------------
|
||||
OneMotorMultiPosCoordinator::OneMotorMultiPosCoordinator(
|
||||
IrisMultiMotorController* motorCtrl,
|
||||
ImagerOperationBase* cameraCtrl,
|
||||
QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_motorCtrl(motorCtrl)
|
||||
, m_cameraCtrl(cameraCtrl)
|
||||
, m_currentPos(0)
|
||||
, m_isRunning(false)
|
||||
, m_isZeroing(false)
|
||||
{
|
||||
//这些信号槽是按照逻辑顺序的
|
||||
connect(this, SIGNAL(moveTo(int, double, double, int)),
|
||||
m_motorCtrl, SLOT(moveTo(int, double, double, int)));
|
||||
|
||||
connect(this, &OneMotorMultiPosCoordinator::zeroStart,
|
||||
m_motorCtrl, &IrisMultiMotorController::zeroStart);
|
||||
|
||||
connect(m_motorCtrl, &IrisMultiMotorController::motorStopSignal,
|
||||
this, &OneMotorMultiPosCoordinator::handlePositionReached);
|
||||
//connect(m_motorCtrl, &IrisMultiMotorController::moveFailed,
|
||||
// this, &OneMotorMultiPosCoordinator::handleError);
|
||||
|
||||
connect(this, &OneMotorMultiPosCoordinator::getFocusIndexSobel,
|
||||
m_cameraCtrl, &ImagerOperationBase::auto_exposure);
|
||||
|
||||
connect(m_cameraCtrl, &ImagerOperationBase::autoExposureSignal,
|
||||
this, &OneMotorMultiPosCoordinator::handleCaptureComplete);
|
||||
//connect(m_cameraCtrl, &ImagerOperationBase::captureFailed,
|
||||
// this, &OneMotorMultiPosCoordinator::handleError);
|
||||
}
|
||||
|
||||
OneMotorMultiPosCoordinator::~OneMotorMultiPosCoordinator()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void OneMotorMultiPosCoordinator::startStepMotion(double speed, std::vector<double> locations)
|
||||
{
|
||||
QMutexLocker locker(&m_dataMutex);
|
||||
|
||||
if (locations.empty())
|
||||
{
|
||||
emit sequenceComplete(1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (m_isRunning)
|
||||
{
|
||||
emit errorOccurred("Sequence already running");
|
||||
return;
|
||||
}
|
||||
m_locations = locations;
|
||||
|
||||
m_counter = 0;
|
||||
|
||||
m_positionData.clear();
|
||||
|
||||
m_speed = speed;
|
||||
m_iStepIntervalRealTime = 1;
|
||||
|
||||
m_isRunning = true;
|
||||
m_isZeroing = true;
|
||||
|
||||
// 先执行归零操作
|
||||
emit zeroStart(0);
|
||||
qDebug() << "OneMotorMultiPosCoordinator::startStepMotion: Zeroing started.";
|
||||
}
|
||||
|
||||
void OneMotorMultiPosCoordinator::startMotionSequence()
|
||||
{
|
||||
QMutexLocker locker(&m_dataMutex);
|
||||
|
||||
m_isZeroing = false;
|
||||
qDebug() << "OneMotorMultiPosCoordinator::startMotionSequence: Zeroing complete. Starting motion sequence.";
|
||||
|
||||
processNextPosition();
|
||||
}
|
||||
|
||||
void OneMotorMultiPosCoordinator::handleZeroComplete(int motorID, double pos)
|
||||
{
|
||||
if (!m_isRunning || !m_isZeroing) return;
|
||||
|
||||
// 归零完成,开始分步运动
|
||||
startMotionSequence();
|
||||
}
|
||||
|
||||
void OneMotorMultiPosCoordinator::stopStepMotion()
|
||||
{
|
||||
QMutexLocker locker(&m_dataMutex);
|
||||
m_isRunning = false;
|
||||
emit sequenceStopped();
|
||||
}
|
||||
|
||||
QVector<PositionsLogData> OneMotorMultiPosCoordinator::getAllPositionData() const
|
||||
{
|
||||
//QMutexLocker locker(&m_dataMutex);
|
||||
return m_positionData;
|
||||
}
|
||||
|
||||
bool OneMotorMultiPosCoordinator::saveToCsv(const QString& filename)
|
||||
{
|
||||
//QMutexLocker locker(&m_dataMutex);
|
||||
|
||||
QFile file(filename);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QTextStream out(&file);
|
||||
out << "Timestamp,targetPosition,ActualPosition,exposureTime\n";
|
||||
|
||||
for (const auto& data : m_positionData)
|
||||
{
|
||||
out << data.timestamp.toString("yyyy-MM-dd HH:mm:ss.zzz") << ","
|
||||
<< QString::number(data.targetPosition, 'f', 4) << ","
|
||||
<< QString::number(data.actualPosition, 'f', 4) << ","
|
||||
<< QString::number(data.exposureTime, 'f', 4) << "\n";
|
||||
}
|
||||
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
void OneMotorMultiPosCoordinator::handlePositionReached(int motorID, double pos)
|
||||
{
|
||||
if (!m_isRunning) return;
|
||||
|
||||
// 如果正在等待归零完成,调用归零完成处理
|
||||
if (m_isZeroing)
|
||||
{
|
||||
handleZeroComplete(motorID, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker locker(&m_dataMutex);
|
||||
|
||||
//验证马达运动位置是否到达指定位置
|
||||
//if (pos != m_currentPos) return;
|
||||
|
||||
// 记录位置信息
|
||||
PositionsLogData data;
|
||||
data.targetPosition = m_currentPos;
|
||||
data.actualPosition = pos;
|
||||
data.timestamp = QDateTime::currentDateTime();
|
||||
m_positionData.append(data);
|
||||
|
||||
// 开始采集
|
||||
emit getFocusIndexSobel();
|
||||
}
|
||||
|
||||
void OneMotorMultiPosCoordinator::handleCaptureComplete(double index)
|
||||
{
|
||||
if (!m_isRunning) return;
|
||||
|
||||
QMutexLocker locker(&m_dataMutex);
|
||||
|
||||
// 更新最近一条记录的相机指数
|
||||
//if (!m_positionData.isEmpty() &&
|
||||
// m_positionData.last().targetPosition == m_positionData.last().actualPosition)
|
||||
//{
|
||||
// m_positionData.last().cameraIndex = index;
|
||||
//}
|
||||
m_positionData.last().exposureTime = index;
|
||||
|
||||
std::cout << "第" << m_counter << "次曝光:" << std::endl;
|
||||
std::cout << "目标位置:" << m_positionData.last().targetPosition << std::endl;
|
||||
std::cout << "实际位置:" << m_positionData.last().actualPosition << std::endl;
|
||||
std::cout << "曝光时间:" << m_positionData.last().exposureTime << std::endl;
|
||||
|
||||
processNextPosition();
|
||||
}
|
||||
|
||||
void OneMotorMultiPosCoordinator::handleError(const QString& error)
|
||||
{
|
||||
QMutexLocker locker(&m_dataMutex);
|
||||
m_isRunning = false;
|
||||
emit errorOccurred(error);
|
||||
}
|
||||
|
||||
void OneMotorMultiPosCoordinator::processNextPosition()
|
||||
{
|
||||
if (!m_isRunning) return;
|
||||
|
||||
if (m_locations.empty())
|
||||
{
|
||||
m_isRunning = false;
|
||||
emit sequenceComplete(0);
|
||||
|
||||
// 计算平均曝光时间
|
||||
double avgExposureTime = 0.0;
|
||||
for (const auto& data : m_positionData) {
|
||||
avgExposureTime += data.exposureTime;
|
||||
}
|
||||
if (!m_positionData.isEmpty()) {
|
||||
avgExposureTime /= m_positionData.size();
|
||||
}
|
||||
|
||||
emit hyperAutoExposureDoneSignal(avgExposureTime);
|
||||
m_cameraCtrl->setIntegrationTime(avgExposureTime);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_currentPos = m_locations.front();
|
||||
m_locations.erase(m_locations.begin());
|
||||
|
||||
emit moveTo(0, m_currentPos, m_speed, 1000);
|
||||
}
|
||||
|
||||
@ -300,3 +300,74 @@ private:
|
||||
int m_retryTimes;
|
||||
bool m_reached;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 数据记录结构体
|
||||
struct PositionsLogData
|
||||
{
|
||||
double targetPosition; // 目标位置
|
||||
double actualPosition; // 实际马达位置
|
||||
double exposureTime; //
|
||||
QDateTime timestamp; // 时间戳
|
||||
|
||||
PositionsLogData(double target = 0, double actual = 0.0, double exposure = 0.0)
|
||||
: targetPosition(target), actualPosition(actual),
|
||||
exposureTime(exposure ), timestamp(QDateTime::currentDateTime()) {
|
||||
}
|
||||
};
|
||||
|
||||
// 协调控制器
|
||||
class OneMotorMultiPosCoordinator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
OneMotorMultiPosCoordinator(IrisMultiMotorController* motorCtrl,
|
||||
ImagerOperationBase* cameraCtrl,
|
||||
QObject* parent = nullptr);
|
||||
~OneMotorMultiPosCoordinator();
|
||||
|
||||
QVector<PositionsLogData> getAllPositionData() const;
|
||||
bool saveToCsv(const QString& filename);
|
||||
|
||||
public slots:
|
||||
void startStepMotion(double speed, std::vector<double> locations);
|
||||
void stopStepMotion();
|
||||
|
||||
signals:
|
||||
void progressChanged(int progress);
|
||||
void sequenceComplete(int status);
|
||||
void sequenceStopped();
|
||||
void errorOccurred(const QString& error);
|
||||
void moveTo(int, double, double, int);
|
||||
void getFocusIndexSobel();
|
||||
void zeroStart(int motorID);
|
||||
|
||||
void hyperAutoExposureDoneSignal(double exposureTime);
|
||||
|
||||
private slots:
|
||||
void handlePositionReached(int motorID, double pos);
|
||||
void handleCaptureComplete(double index);
|
||||
void handleError(const QString& error);
|
||||
void handleZeroComplete(int motorID, double pos);
|
||||
|
||||
private:
|
||||
void processNextPosition();
|
||||
void startMotionSequence();
|
||||
|
||||
IrisMultiMotorController* m_motorCtrl;
|
||||
ImagerOperationBase* m_cameraCtrl;
|
||||
QVector<PositionsLogData> m_positionData;
|
||||
mutable QMutex m_dataMutex;
|
||||
|
||||
double m_currentPos;
|
||||
bool m_isRunning;
|
||||
double m_speed;
|
||||
|
||||
int m_iStepInterval;
|
||||
int m_iStepIntervalRealTime;
|
||||
int m_counter;
|
||||
bool m_isZeroing;
|
||||
|
||||
std::vector<double> m_locations;
|
||||
};
|
||||
|
||||
@ -55,6 +55,7 @@ void GonggaShanRecordCtl::logStatus(const QString& message, bool isHearderBlankL
|
||||
GonggaShanRecordCtl::GonggaShanRecordCtl(QWidget* parent)
|
||||
: QDialog(parent)
|
||||
, m_taskScheduler(new GonggashanTaskScheduler(this))
|
||||
, m_taskExecutor(new GonggashanTaskExecutor(this))
|
||||
{
|
||||
ui.setupUi(this);
|
||||
|
||||
@ -66,6 +67,20 @@ GonggaShanRecordCtl::GonggaShanRecordCtl(QWidget* parent)
|
||||
connect(ui.spinbox_Port, QOverload<int>::of(&QSpinBox::valueChanged), [this](int value) {
|
||||
AppSettings::instance().setGonggaShanRecordPort(value);
|
||||
});
|
||||
|
||||
// 连接 GonggashanTaskExecutor 信号
|
||||
connect(m_taskExecutor, &GonggashanTaskExecutor::finished,
|
||||
this, &GonggaShanRecordCtl::onTaskExecutorFinished);
|
||||
|
||||
connect(m_taskExecutor, &GonggashanTaskExecutor::hyperAutoExposureSignal_gonggashan,
|
||||
this, &GonggaShanRecordCtl::hyperAutoExposureSignal_gonggashan);
|
||||
connect(this, &GonggaShanRecordCtl::hyperAutoExposureDoneSignal_gonggashan,
|
||||
m_taskExecutor, &GonggashanTaskExecutor::onHyperExposureComplete);
|
||||
|
||||
connect(m_taskExecutor, &GonggashanTaskExecutor::fiberExposureSignal,
|
||||
this, &GonggaShanRecordCtl::fiberExposureSignal_gonggashan);
|
||||
connect(this, &GonggaShanRecordCtl::fiberExposureDoneSignal_gonggashan,
|
||||
m_taskExecutor, &GonggashanTaskExecutor::onFiberExposureComplete);
|
||||
}
|
||||
|
||||
GonggaShanRecordCtl::~GonggaShanRecordCtl()
|
||||
@ -112,18 +127,11 @@ void GonggaShanRecordCtl::stopListen()
|
||||
|
||||
void GonggaShanRecordCtl::startRecord(int position)
|
||||
{
|
||||
if (m_taskScheduler->isTaskRunning())
|
||||
{
|
||||
logStatus("Data collection has begun....");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
QString pos = QString::number(position);
|
||||
logStatus("Reach pos: " + pos, true);
|
||||
|
||||
m_taskScheduler->setTaskRunning(true);
|
||||
emit startRcordSignal("pos_" + pos);
|
||||
m_taskExecutor->start("pos_" + pos);
|
||||
}
|
||||
|
||||
void GonggaShanRecordCtl::onFiberImagerStartExposureSignal()
|
||||
@ -144,3 +152,262 @@ void GonggaShanRecordCtl::onRcordFinished()
|
||||
|
||||
logStatus("Record Finished.");
|
||||
}
|
||||
|
||||
void GonggaShanRecordCtl::onTaskExecutorFinished(bool success)
|
||||
{
|
||||
qDebug() << "GonggaShanRecordCtl: TaskExecutor finished, success:" << success;
|
||||
if (success) {
|
||||
logStatus("Task completed successfully.");
|
||||
} else {
|
||||
logStatus("Task failed or stopped.");
|
||||
}
|
||||
m_taskScheduler->setTaskRunning(false);
|
||||
}
|
||||
|
||||
// ==================== GonggashanTaskExecutor 实现 ====================
|
||||
|
||||
GonggashanTaskExecutor::GonggashanTaskExecutor(QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_machine(new QStateMachine(this))
|
||||
{
|
||||
buildStateMachine();
|
||||
}
|
||||
|
||||
GonggashanTaskExecutor::~GonggashanTaskExecutor()
|
||||
{
|
||||
if (m_machine) {
|
||||
m_machine->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void GonggashanTaskExecutor::buildStateMachine()
|
||||
{
|
||||
// ---- 创建所有状态 ----
|
||||
m_hyperExposureState = new QState(QState::ExclusiveStates);
|
||||
m_hyperExposureState->setObjectName("HyperExposure");
|
||||
|
||||
m_fiberExposureState = new QState(QState::ExclusiveStates);
|
||||
m_fiberExposureState->setObjectName("FiberExposure");
|
||||
|
||||
m_gpsAcquisitionState = new QState(QState::ExclusiveStates);
|
||||
m_gpsAcquisitionState->setObjectName("GpsAcquisition");
|
||||
|
||||
m_motorCalcState = new QState(QState::ExclusiveStates);
|
||||
m_motorCalcState->setObjectName("MotorCalc");
|
||||
|
||||
m_dataCollectionState = new QState(QState::ExclusiveStates);
|
||||
m_dataCollectionState->setObjectName("DataCollection");
|
||||
|
||||
m_finalState = new QFinalState();
|
||||
m_finalState->setObjectName("Completed");
|
||||
|
||||
// ---- HyperExposure 状态:等待外部硬件回调 ----
|
||||
connect(m_hyperExposureState, &QState::entered, this, [this]() {
|
||||
qDebug() << "GonggashanTaskExecutor: Enter m_hyperExposureState";
|
||||
emit hyperAutoExposureSignal_gonggashan();
|
||||
});
|
||||
connect(m_hyperExposureState, &QState::exited, this, [this]() {
|
||||
qDebug() << "GonggashanTaskExecutor: Exit m_hyperExposureState";
|
||||
leavePhase(m_hyperExposureState);
|
||||
});
|
||||
QSignalTransition* hyperDone = new QSignalTransition(this, &GonggashanTaskExecutor::hyperExposureDone);
|
||||
hyperDone->setTargetState(m_fiberExposureState);
|
||||
m_hyperExposureState->addTransition(hyperDone);
|
||||
|
||||
//m_hyperExposureState->addTransition(this, &GonggashanTaskExecutor::hyperExposureDone, m_fiberExposureState);
|
||||
|
||||
// ---- FiberExposure 状态:等待外部硬件回调 ----
|
||||
connect(m_fiberExposureState, &QState::entered, this, [this]() {
|
||||
qDebug() << "GonggashanTaskExecutor: Enter m_fiberExposureState";
|
||||
emit fiberExposureSignal();
|
||||
});
|
||||
connect(m_fiberExposureState, &QState::exited, this, [this]() {
|
||||
qDebug() << "GonggashanTaskExecutor: Exit m_fiberExposureState";
|
||||
leavePhase(m_fiberExposureState);
|
||||
});
|
||||
QSignalTransition* fiberDone = new QSignalTransition(this, &GonggashanTaskExecutor::fiberExposureDone);
|
||||
fiberDone->setTargetState(m_gpsAcquisitionState);
|
||||
m_fiberExposureState->addTransition(fiberDone);
|
||||
|
||||
// ---- GpsAcquisition 状态:等待外部硬件回调 ----
|
||||
connect(m_gpsAcquisitionState, &QState::entered, this, [this]() {
|
||||
qDebug() << "GonggashanTaskExecutor: Enter m_gpsAcquisitionState";
|
||||
emit gpsAcquisitionSignal();
|
||||
});
|
||||
connect(m_gpsAcquisitionState, &QState::exited, this, [this]() {
|
||||
qDebug() << "GonggashanTaskExecutor: Exit m_gpsAcquisitionState";
|
||||
leavePhase(m_gpsAcquisitionState);
|
||||
});
|
||||
QSignalTransition* gpsDone = new QSignalTransition(this, &GonggashanTaskExecutor::gpsAcquisitionDone);
|
||||
gpsDone->setTargetState(m_motorCalcState);
|
||||
m_gpsAcquisitionState->addTransition(gpsDone);
|
||||
|
||||
// ---- MotorCalc 状态:等待外部硬件回调 ----
|
||||
connect(m_motorCalcState, &QState::entered, this, [this]() {
|
||||
qDebug() << "GonggashanTaskExecutor: Enter m_motorCalcState";
|
||||
});
|
||||
connect(m_motorCalcState, &QState::exited, this, [this]() {
|
||||
qDebug() << "GonggashanTaskExecutor: Exit m_motorCalcState";
|
||||
leavePhase(m_motorCalcState);
|
||||
});
|
||||
QSignalTransition* motorDone = new QSignalTransition(this, &GonggashanTaskExecutor::motorSpeedDone);
|
||||
motorDone->setTargetState(m_dataCollectionState);
|
||||
m_motorCalcState->addTransition(motorDone);
|
||||
|
||||
// ---- DataCollection 状态:同步阶段 ----
|
||||
connect(m_dataCollectionState, &QState::entered, this, [this]() {
|
||||
qDebug() << "GonggashanTaskExecutor: Enter m_dataCollectionState";
|
||||
if (dataCollectionImpl()) {
|
||||
QTimer::singleShot(0, this, &GonggashanTaskExecutor::dataCollectionDone);
|
||||
}
|
||||
});
|
||||
connect(m_dataCollectionState, &QState::exited, this, [this]() {
|
||||
qDebug() << "GonggashanTaskExecutor: Exit m_dataCollectionState";
|
||||
leavePhase(m_dataCollectionState);
|
||||
});
|
||||
QSignalTransition* collDone = new QSignalTransition(this, &GonggashanTaskExecutor::dataCollectionDone);
|
||||
collDone->setTargetState(m_finalState);
|
||||
m_dataCollectionState->addTransition(collDone);
|
||||
|
||||
// ---- FinalState:完成 ----
|
||||
connect(m_finalState, &QState::entered, this, &GonggashanTaskExecutor::onFinalStateEntered);
|
||||
|
||||
// ---- stop() 可在任何状态下触发,直接跳转到 finalState ----
|
||||
QSignalTransition* stopTrans = new QSignalTransition(this, &GonggashanTaskExecutor::stopRequested);
|
||||
stopTrans->setTargetState(m_finalState);
|
||||
for (QState* s : {
|
||||
m_hyperExposureState,
|
||||
m_fiberExposureState, m_gpsAcquisitionState, m_motorCalcState,
|
||||
m_dataCollectionState
|
||||
}) {
|
||||
s->addTransition(stopTrans);
|
||||
}
|
||||
|
||||
// ---- 设置状态机 ----
|
||||
m_machine->addState(m_hyperExposureState);
|
||||
m_machine->addState(m_fiberExposureState);
|
||||
m_machine->addState(m_gpsAcquisitionState);
|
||||
m_machine->addState(m_motorCalcState);
|
||||
m_machine->addState(m_dataCollectionState);
|
||||
m_machine->addState(m_finalState);
|
||||
|
||||
m_machine->setInitialState(m_hyperExposureState);
|
||||
|
||||
}
|
||||
|
||||
QState* GonggashanTaskExecutor::currentState() const
|
||||
{
|
||||
if (!m_machine || m_machine->configuration().isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
return qobject_cast<QState*>(*m_machine->configuration().begin());
|
||||
}
|
||||
|
||||
void GonggashanTaskExecutor::leavePhase(QState* state)
|
||||
{
|
||||
Q_UNUSED(state);
|
||||
}
|
||||
|
||||
void GonggashanTaskExecutor::onFinalStateEntered()
|
||||
{
|
||||
qDebug() << "GonggashanTaskExecutor: Final state entered, stopRequested:" << m_stopRequested;
|
||||
emit finished(!m_stopRequested);
|
||||
m_stopRequested = false;
|
||||
}
|
||||
|
||||
void GonggashanTaskExecutor::start(const QString& posInfo)
|
||||
{
|
||||
if (isRunning()) {
|
||||
qDebug() << "GonggashanTaskExecutor: Task is running, ignore new signal";
|
||||
return;
|
||||
}
|
||||
|
||||
m_machine->start();
|
||||
|
||||
m_posInfo = posInfo;
|
||||
m_stopRequested = false;
|
||||
qDebug() << "GonggashanTaskExecutor: Starting with pos:" << posInfo;
|
||||
|
||||
QTimer::singleShot(0, this, [this, posInfo]() {
|
||||
emit taskStartRequested(posInfo);
|
||||
});
|
||||
}
|
||||
|
||||
void GonggashanTaskExecutor::stop()
|
||||
{
|
||||
if (!isRunning()) return;
|
||||
m_stopRequested = true;
|
||||
qDebug() << "GonggashanTaskExecutor: Stop requested";
|
||||
emit stopRequested();
|
||||
}
|
||||
|
||||
void GonggashanTaskExecutor::onHyperExposureComplete(int exposureTime)
|
||||
{
|
||||
if (currentState() != m_hyperExposureState) return;
|
||||
m_hyperExposureTime = exposureTime;
|
||||
hyperExposureImpl(exposureTime);
|
||||
emit hyperExposureDone();
|
||||
}
|
||||
|
||||
void GonggashanTaskExecutor::onFiberExposureComplete(int exposureTime)
|
||||
{
|
||||
if (currentState() != m_fiberExposureState) return;
|
||||
m_fiberExposureTime = exposureTime;
|
||||
fiberExposureImpl(exposureTime);
|
||||
emit fiberExposureDone();
|
||||
}
|
||||
|
||||
void GonggashanTaskExecutor::onGpsAcquired(double latitude, double longitude, double altitude)
|
||||
{
|
||||
if (currentState() != m_gpsAcquisitionState) return;
|
||||
m_gpsData = QString("%1,%2,%3").arg(latitude).arg(longitude).arg(altitude);
|
||||
gpsAcquisitionImpl(latitude, longitude, altitude);
|
||||
emit gpsAcquisitionDone();
|
||||
}
|
||||
|
||||
void GonggashanTaskExecutor::onMotorSpeedCalculated(double speed)
|
||||
{
|
||||
if (currentState() != m_motorCalcState) return;
|
||||
m_motorSpeed = speed;
|
||||
emit motorSpeedSignal(speed);
|
||||
if (motorCalcImpl()) {
|
||||
emit motorSpeedDone();
|
||||
}
|
||||
}
|
||||
|
||||
bool GonggashanTaskExecutor::preparationImpl()
|
||||
{
|
||||
qDebug() << "GonggashanTaskExecutor: Enter m_preparationState\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GonggashanTaskExecutor::hyperExposureImpl(int exposureTime)
|
||||
{
|
||||
qDebug() << "GonggashanTaskExecutor: Hyper exposure complete, time:" << exposureTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GonggashanTaskExecutor::fiberExposureImpl(int exposureTime)
|
||||
{
|
||||
qDebug() << "GonggashanTaskExecutor: Fiber exposure complete, time:" << exposureTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GonggashanTaskExecutor::gpsAcquisitionImpl(double lat, double lon, double alt)
|
||||
{
|
||||
qDebug() << "GonggashanTaskExecutor: GPS acquired:" << lat << lon << alt;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GonggashanTaskExecutor::motorCalcImpl()
|
||||
{
|
||||
qDebug() << "GonggashanTaskExecutor: Motor speed calculated:" << m_motorSpeed;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GonggashanTaskExecutor::dataCollectionImpl()
|
||||
{
|
||||
qDebug() << "GonggashanTaskExecutor: Start data collection";
|
||||
emit startCollectionSignal(m_posInfo, m_gpsData);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -10,12 +10,29 @@
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QDir>
|
||||
#include <QStateMachine>
|
||||
#include <QSignalTransition>
|
||||
#include <QFinalState>
|
||||
#include <QTimer>
|
||||
|
||||
#include "ui_gonggashanCtl.h"
|
||||
|
||||
#include "CommunicationViaTCP.h"
|
||||
#include "AppSettings.h"
|
||||
|
||||
// ============ 执行阶段枚举 ============
|
||||
enum class GonggaShanExecPhase {
|
||||
Idle, // 空闲
|
||||
Preparation, // 准备阶段
|
||||
HyperExposure, // 高光谱传感器曝光
|
||||
FiberExposure, // 光纤光谱仪曝光
|
||||
GpsAcquisition, // 获取GPS位置
|
||||
MotorCalc, // 计算马达速度
|
||||
DataCollection, // 开始采集:高光谱、FODIS、rgb相机
|
||||
Completed // 完成
|
||||
};
|
||||
|
||||
// ============ 任务调度器 ============
|
||||
class GonggashanTaskScheduler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -40,6 +57,79 @@ private:
|
||||
TaskState m_taskState;
|
||||
};
|
||||
|
||||
// ============ 任务执行器 ============
|
||||
class GonggashanTaskExecutor : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GonggashanTaskExecutor(QObject* parent = nullptr);
|
||||
~GonggashanTaskExecutor();
|
||||
|
||||
bool isRunning() const { return m_machine && m_machine->isRunning() && !m_machine->configuration().isEmpty(); }
|
||||
|
||||
public slots:
|
||||
void start(const QString& posInfo);
|
||||
void stop();
|
||||
|
||||
void onHyperExposureComplete(int exposureTime);
|
||||
void onFiberExposureComplete(int exposureTime);
|
||||
void onGpsAcquired(double latitude, double longitude, double altitude);
|
||||
void onMotorSpeedCalculated(double speed);
|
||||
|
||||
signals:
|
||||
void hyperAutoExposureSignal_gonggashan();
|
||||
void fiberExposureSignal();
|
||||
void gpsAcquisitionSignal();
|
||||
void motorSpeedSignal(double speed);
|
||||
void startCollectionSignal(const QString& posInfo, const QString& gpsData);
|
||||
|
||||
void finished(bool success);
|
||||
void errorOccurred(const QString& error);
|
||||
|
||||
// ---- 以下为状态机内部信号,供 transition 使用 ----
|
||||
void taskStartRequested(const QString& posInfo);
|
||||
void preparationComplete();
|
||||
void hyperExposureDone();
|
||||
void fiberExposureDone();
|
||||
void gpsAcquisitionDone();
|
||||
void motorSpeedDone();
|
||||
void dataCollectionDone();
|
||||
void stopRequested();
|
||||
|
||||
protected:
|
||||
virtual bool preparationImpl();
|
||||
virtual bool hyperExposureImpl(int exposureTime);
|
||||
virtual bool fiberExposureImpl(int exposureTime);
|
||||
virtual bool gpsAcquisitionImpl(double lat, double lon, double alt);
|
||||
virtual bool motorCalcImpl();
|
||||
virtual bool dataCollectionImpl();
|
||||
|
||||
private slots:
|
||||
void onFinalStateEntered();
|
||||
|
||||
private:
|
||||
void buildStateMachine();
|
||||
QState* currentState() const;
|
||||
void leavePhase(QState* state);
|
||||
|
||||
QString m_posInfo;
|
||||
QString m_gpsData;
|
||||
double m_motorSpeed = 0.0;
|
||||
int m_hyperExposureTime = 0;
|
||||
int m_fiberExposureTime = 0;
|
||||
bool m_stopRequested = false;
|
||||
|
||||
// Qt State Machine
|
||||
QStateMachine* m_machine = nullptr;
|
||||
QState* m_hyperExposureState = nullptr;
|
||||
QState* m_fiberExposureState = nullptr;
|
||||
QState* m_gpsAcquisitionState = nullptr;
|
||||
QState* m_motorCalcState = nullptr;
|
||||
QState* m_dataCollectionState = nullptr;
|
||||
QFinalState* m_finalState = nullptr;
|
||||
};
|
||||
|
||||
class GonggaShanRecordCtl : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -55,6 +145,12 @@ public Q_SLOTS:
|
||||
void onFiberImagerExposureCompleteSignal(int exposureTime);
|
||||
|
||||
Q_SIGNALS:
|
||||
void hyperAutoExposureSignal_gonggashan();
|
||||
void hyperAutoExposureDoneSignal_gonggashan(double exposureTime);
|
||||
|
||||
void fiberExposureSignal_gonggashan();
|
||||
void fiberExposureDoneSignal_gonggashan(double exposureTime);
|
||||
|
||||
// Emitted when user changes any of the R/G/B wavelength values
|
||||
void startRcordSignal(QString posInfo);
|
||||
|
||||
@ -64,12 +160,16 @@ private Q_SLOTS:
|
||||
|
||||
void startRecord(int position);
|
||||
|
||||
// GonggashanTaskExecutor 反馈槽
|
||||
void onTaskExecutorFinished(bool success);
|
||||
|
||||
private:
|
||||
void logStatus(const QString& message, bool isHearderBlankLine = false, bool isTailBlankLine = false);
|
||||
|
||||
Ui::gongga_control ui;
|
||||
QPointer<MotorParams::CommunicationViaTCP> tcpServer6005;
|
||||
GonggashanTaskScheduler* m_taskScheduler;
|
||||
GonggashanTaskExecutor* m_taskExecutor; // 新增
|
||||
QString m_logFilePath;
|
||||
QFile m_logFile;
|
||||
QTextStream m_logStream;
|
||||
|
||||
@ -1112,16 +1112,40 @@ void HPPA::initControlTabwidget()
|
||||
|
||||
void HPPA::setupGonggashanAutoRecordConnection()
|
||||
{
|
||||
connect(m_gonggaShanRecordCtl, &GonggaShanRecordCtl::startRcordSignal, this, &HPPA::onGonggashanRecord);
|
||||
connect(m_gonggaShanRecordCtl, &GonggaShanRecordCtl::hyperAutoExposureSignal_gonggashan, this, &HPPA::onGonggashanHyperAutoExposure);
|
||||
connect(m_omc, &OneMotorControl::hyperAutoExposureDoneSignal_gonggashan, m_gonggaShanRecordCtl, &GonggaShanRecordCtl::hyperAutoExposureDoneSignal_gonggashan);
|
||||
|
||||
connect(m_gonggaShanRecordCtl, &GonggaShanRecordCtl::fiberExposureSignal_gonggashan, this, &HPPA::onGonggashanFiberAutoExposure);
|
||||
connect(m_fodisWindow, &FodisWindow::exposureCompleteSignal, m_gonggaShanRecordCtl, &GonggaShanRecordCtl::fiberExposureDoneSignal_gonggashan);
|
||||
|
||||
connect(m_fodisWindow, &FodisWindow::startExposureSignal, m_gonggaShanRecordCtl, &GonggaShanRecordCtl::onFiberImagerStartExposureSignal);
|
||||
connect(m_fodisWindow, &FodisWindow::exposureCompleteSignal, this, &HPPA::onStartRecordStep1);
|
||||
connect(m_fodisWindow, &FodisWindow::exposureCompleteSignal, m_gonggaShanRecordCtl, &GonggaShanRecordCtl::onFiberImagerExposureCompleteSignal);
|
||||
|
||||
connect(m_gonggaShanRecordCtl, &GonggaShanRecordCtl::startRcordSignal, this, &HPPA::onGonggashanRecord);
|
||||
connect(m_omc, &OneMotorControl::sequenceComplete, m_fodisWindow, &FodisWindow::closeFiberImager);
|
||||
connect(m_omc, &OneMotorControl::sequenceComplete, m_gonggaShanRecordCtl, &GonggaShanRecordCtl::onRcordFinished);
|
||||
}
|
||||
|
||||
void HPPA::onGonggashanHyperAutoExposure()
|
||||
{
|
||||
//连接马达和光谱仪
|
||||
m_omc->connectMotor(false);
|
||||
|
||||
if (!testImagerVality())
|
||||
{
|
||||
onconnect();
|
||||
}
|
||||
|
||||
m_omc->setImager(m_Imager);
|
||||
m_omc->multiPosHyperAutoExposure();
|
||||
}
|
||||
|
||||
void HPPA::onGonggashanFiberAutoExposure()
|
||||
{
|
||||
//m_fodisWindow->openFiberImager_expose_record(posInfo);
|
||||
}
|
||||
|
||||
void HPPA::onGonggashanRecord(QString posInfo)
|
||||
{
|
||||
//设置文件名
|
||||
|
||||
@ -458,6 +458,8 @@ public Q_SLOTS:
|
||||
void onStretchProcessingError(int fileNumber, const QString& filePath, const QString& error);
|
||||
|
||||
void onGonggashanRecord(QString posInfo);
|
||||
void onGonggashanHyperAutoExposure();
|
||||
void onGonggashanFiberAutoExposure();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
|
||||
@ -68,11 +68,12 @@ double ImagerOperationBase::auto_exposure()
|
||||
|
||||
imagerStopCollect();
|
||||
|
||||
emit autoExposureSignal();
|
||||
double exposureTime = getIntegrationTime();
|
||||
emit autoExposureSignal(exposureTime);
|
||||
|
||||
//std::cout << "自动曝光:" << getIntegrationTime() << std::endl;
|
||||
//std::cout << "自动曝光:" << exposureTime << std::endl;
|
||||
|
||||
return getIntegrationTime();
|
||||
return exposureTime;
|
||||
}
|
||||
|
||||
void ImagerOperationBase::focus()
|
||||
|
||||
@ -117,7 +117,7 @@ signals:
|
||||
|
||||
|
||||
void testImagerStatus();//表示可以测试相机连接状态:是否连接,并反映到界面上
|
||||
void autoExposureSignal();
|
||||
void autoExposureSignal(double exposureTime);
|
||||
|
||||
// 新增:当一组影像文件(.bil/.hdr)写入完成后发出(会从采集线程发出,Qt 会做 queued connection)
|
||||
void ImageFileSaved(const QString& path, int fileIndex);
|
||||
|
||||
@ -248,6 +248,34 @@ void OneMotorControl::stop()
|
||||
emit stopStepMotionSignal();
|
||||
}
|
||||
|
||||
void OneMotorControl::multiPosHyperAutoExposure()
|
||||
{
|
||||
//所有该自动曝光的位置
|
||||
std::vector<double> maxRangeLocations = m_multiAxisController->getMaxPos();
|
||||
double maxPos = maxRangeLocations[0];
|
||||
|
||||
std::vector<double> locations;
|
||||
locations.push_back(maxPos * 0.2);
|
||||
locations.push_back(maxPos * 0.5);
|
||||
locations.push_back(maxPos * 0.8);
|
||||
|
||||
//创建协调器,并连接信号槽
|
||||
m_coordinator_gonggashan_autoexpose = new OneMotorMultiPosCoordinator(m_multiAxisController, m_Imager);
|
||||
|
||||
//connect(this, SIGNAL(stopStepMotionSignal()), m_coordinator_gonggashan_autoexpose, SLOT(stopStepMotion()));
|
||||
connect(m_coordinator_gonggashan_autoexpose, &OneMotorMultiPosCoordinator::sequenceComplete, this, &OneMotorControl::onSequenceComplete_gonggashan_autoexpose);
|
||||
connect(m_coordinator_gonggashan_autoexpose, &OneMotorMultiPosCoordinator::hyperAutoExposureDoneSignal, this, &OneMotorControl::hyperAutoExposureDoneSignal_gonggashan);
|
||||
|
||||
m_coordinator_gonggashan_autoexpose->startStepMotion(ui.speed_lineEdit->text().toDouble(), locations);
|
||||
}
|
||||
|
||||
void OneMotorControl::onSequenceComplete_gonggashan_autoexpose(int state)
|
||||
{
|
||||
emit sequenceComplete();
|
||||
|
||||
m_coordinator_gonggashan_autoexpose->deleteLater();
|
||||
}
|
||||
|
||||
void OneMotorControl::onSequenceComplete(int state)
|
||||
{
|
||||
emit sequenceComplete();
|
||||
|
||||
@ -26,6 +26,8 @@ public:
|
||||
void run();
|
||||
void stop();
|
||||
|
||||
void multiPosHyperAutoExposure();
|
||||
|
||||
void record_dark();
|
||||
void record_white();
|
||||
|
||||
@ -47,6 +49,7 @@ public Q_SLOTS:
|
||||
void onxMotorStop();
|
||||
|
||||
void onSequenceComplete(int state);
|
||||
void onSequenceComplete_gonggashan_autoexpose(int state);
|
||||
|
||||
signals:
|
||||
void moveSignal(int, bool, double, int);
|
||||
@ -65,6 +68,8 @@ signals:
|
||||
|
||||
void broadcastLocationSignal(std::vector<double>);
|
||||
|
||||
void hyperAutoExposureDoneSignal_gonggashan(double exposureTime);
|
||||
|
||||
private:
|
||||
Ui::OneMotorControl_UI ui;
|
||||
|
||||
@ -78,6 +83,8 @@ private:
|
||||
DarkAndWhiteCaptureCoordinator* m_whiteCaptureCoordinator = nullptr;
|
||||
|
||||
bool m_xMotorConnectionStatus = false;
|
||||
|
||||
QPointer<OneMotorMultiPosCoordinator> m_coordinator_gonggashan_autoexpose;
|
||||
};
|
||||
|
||||
class OneMotorControl_LiftingPlatform : public QDialog, public MotorWindowBase
|
||||
|
||||
@ -128,9 +128,12 @@ double ResononNirImager::auto_exposure()
|
||||
reConnectImage();
|
||||
//imagerStopCollect();
|
||||
|
||||
//std::cout << "自动曝光:" << getIntegrationTime() << std::endl;
|
||||
double exposureTime = getIntegrationTime();
|
||||
emit autoExposureSignal(exposureTime);
|
||||
|
||||
return getIntegrationTime();
|
||||
//std::cout << "自动曝光:" << exposureTime << std::endl;
|
||||
|
||||
return exposureTime;
|
||||
}
|
||||
|
||||
double ResononNirImager::getWavelengthAtBand(int band)
|
||||
|
||||
Reference in New Issue
Block a user