Skip to content

Commit 6f6bbeb

Browse files
author
etet100
committed
Handle the disconnection - transition to the appropriate state
1 parent 5145c8b commit 6f6bbeb

9 files changed

Lines changed: 88 additions & 17 deletions

src/gpilot/core/state_behavior/abstractstatebehavior.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,14 @@ void AbstractStateBehavior::onMachineStateChanged(MachineState state)
237237
emit machineStateChangedSignal(state);
238238
}
239239

240+
void AbstractStateBehavior::onConnectionStateChanged(ConnectionState state)
241+
{
242+
if (state == ConnectionState::Disconnected) {
243+
qDebug() << QString("[%1] Connection lost, transitioning to Disconnecting").arg(name());
244+
emit transition(this, new DisconnectingBehavior());
245+
}
246+
}
247+
240248
AbstractStateBehavior::Result AbstractStateBehavior::onCommandResponse(QString command, CommandAttributes commandAttributes,
241249
CmdStatus cmdStatus, QString response,
242250
QStringList fullResponse)

src/gpilot/core/state_behavior/abstractstatebehavior.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,14 @@ class AbstractStateBehavior : public QObject
161161
// the buffer drains regardless of who sent the last command.
162162
virtual void onResponseProcessed() {}
163163

164-
virtual void onConnectionStateChanged(ConnectionState state) {
165-
Q_UNUSED(state);
166-
}
164+
// Default: on Disconnected, transitions to DisconnectingBehavior so the
165+
// base onExit() can run its standard cleanup (stops state polling, kills
166+
// timers, clears scheduled callbacks, fires asyncCompleted to wake any
167+
// co_awaiting coroutine). States that manage the connection lifecycle
168+
// themselves (Connecting, Reconnecting, Disconnecting) override to keep
169+
// their own logic. States with extra teardown (e.g. Running) override
170+
// to clean up their own bookkeeping before transitioning.
171+
virtual void onConnectionStateChanged(ConnectionState state);
167172

168173
// Registers a callback to fire when the machine reaches targetState (Unknown = any state).
169174
// If milliseconds > 0, fires callback(MachineState::Unknown) on timeout.

src/gpilot/core/state_behavior/initializationbehavior.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,6 @@ void InitializationBehavior::onMachineStateChanged(MachineState state)
3131
// // This could be used to transition to other behaviors based on command responses
3232
// }
3333

34-
void InitializationBehavior::onConnectionStateChanged(ConnectionState state)
35-
{
36-
if (state == ConnectionState::Disconnected) {
37-
// Handle disconnection if necessary
38-
emit transition(this, new AlarmBehavior()); // Example: transition to alarm on disconnect
39-
}
40-
}
41-
4234
AbstractStateBehavior::Result InitializationBehavior::doOnEntry(CommunicatorApi *communicator, const EntryContext &ctx)
4335
{
4436
qDebug() << "[Behavior][Initialization] Entry";

src/gpilot/core/state_behavior/initializationbehavior.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ class InitializationBehavior : public AbstractStateBehavior
1313
Type type() const override { return Type::Initialization; }
1414
void onMachineStateChanged(MachineState state) override;
1515
// bool onCommandResponse(QString command, QString response, QStringList fullResponse) override;
16-
void onConnectionStateChanged(ConnectionState state) override;
1716
Result doOnEntry(CommunicatorApi *communicator, const EntryContext &ctx) override;
1817
Result doOnExit(AbstractStateBehavior *next) override;
1918

src/gpilot/core/state_behavior/reconnectingbehavior.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ class ReconnectingBehavior : public AbstractStateBehavior
1717
QString description() override { return "Reconnecting"; }
1818
Type type() const override { return Type::Reconnecting; }
1919
Result doOnEntry(CommunicatorApi *communicator, const EntryContext &ctx) override;
20+
// Disconnected events during reconnection are expected — the polling
21+
// timer in doOnEntry drives the lifecycle. Don't fall through to the
22+
// base default that would transition to DisconnectingBehavior.
23+
void onConnectionStateChanged(ConnectionState state) override { Q_UNUSED(state); }
2024

2125
protected:
2226
QString name() const override { return "Reconnecting"; }

src/gpilot/core/state_behavior/runningbehavior.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "idlebehavior.h"
99
#include "pausebehavior.h"
1010
#include "alarmbehavior.h"
11+
#include "disconnectingbehavior.h"
1112
#include "toolchangebehavior.h"
1213
#include "userpromptbehavior.h"
1314
#include "core/communicator/communicator.h"
@@ -225,6 +226,29 @@ void RunningBehavior::onAlarm(int code)
225226
emit transition(this, new AlarmBehavior(code));
226227
}
227228

229+
void RunningBehavior::onConnectionStateChanged(ConnectionState state)
230+
{
231+
if (state != ConnectionState::Disconnected) {
232+
return;
233+
}
234+
235+
qDebug() << "[Behavior][Running] Connection lost, aborting program";
236+
237+
Core::instance().timer().stopExecution();
238+
239+
// Mark every still-unacknowledged program command as aborted so the program
240+
// table reflects what actually happened — those commands will never get a
241+
// response now that the controller is gone.
242+
for (auto &cmd : m_communicator->commandBuffer()->commands()) {
243+
if (cmd.tableIndex >= 0) {
244+
m_program.setCommandAborted(cmd.tableIndex);
245+
}
246+
}
247+
m_communicator->clearCommandsAndQueue();
248+
249+
emit transition(this, new DisconnectingBehavior());
250+
}
251+
228252
void RunningBehavior::doOnMachineState(MachineState state)
229253
{
230254
// Fallback for the missed-Run race: a very short program (e.g. M5 + M30)

src/gpilot/core/state_behavior/runningbehavior.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class RunningBehavior : public AbstractStateBehavior
4343
Result onCommandResponse(QString command, CommandAttributes commandAttributes, CmdStatus cmdStatus, QString response, QStringList fullResponse) override;
4444
void onResponseProcessed() override;
4545
void onAlarm(int code) override;
46+
void onConnectionStateChanged(ConnectionState state) override;
4647
Result doOnEntry(CommunicatorApi *communicator, const EntryContext &ctx) override;
4748
// Running-specific methods
4849
void handleFeedOverride(int percentage);

src/gpilot/io/connection/virtualconnection.cpp

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,16 @@ void VirtualConnection::startProcess()
180180
<< "type:" << simulatorType();
181181

182182
m_process = new QProcess(this);
183+
connect(m_process, &QProcess::finished, this, &VirtualConnection::onProcessFinished);
184+
connect(m_process, &QProcess::errorOccurred, this, &VirtualConnection::onProcessErrorOccurred);
185+
183186
m_process->start(exePath, {m_server->serverName(), simulatorType()});
184187

185188
if (!m_process->waitForStarted(3000)) {
186189
qWarning() << qPrintable(QString("[IO][%1]").arg(m_deviceName))
187190
<< "Failed to start simulator process:" << m_process->errorString();
188-
delete m_process;
191+
m_process->disconnect(this);
192+
m_process->deleteLater();
189193
m_process = nullptr;
190194
setState(ConnectionState::Disconnected);
191195
}
@@ -197,14 +201,44 @@ void VirtualConnection::killProcess()
197201
return;
198202
}
199203

200-
qDebug() << qPrintable(QString("[IO][%1]").arg(m_deviceName)) << "Killing simulator process...";
204+
// Disconnect signals first to avoid re-entering cleanup() via onProcessFinished().
205+
m_process->disconnect(this);
206+
207+
if (m_process->state() != QProcess::NotRunning) {
208+
qDebug() << qPrintable(QString("[IO][%1]").arg(m_deviceName)) << "Killing simulator process...";
209+
m_process->kill();
210+
if (!m_process->waitForFinished(2000)) {
211+
qWarning() << qPrintable(QString("[IO][%1]").arg(m_deviceName))
212+
<< "Simulator process did not terminate within timeout.";
213+
}
214+
} else {
215+
qDebug() << qPrintable(QString("[IO][%1]").arg(m_deviceName)) << "Simulator process already stopped.";
216+
}
201217

202-
m_process->kill();
203-
m_process->waitForFinished(2000);
204-
delete m_process;
218+
m_process->deleteLater();
205219
m_process = nullptr;
206220
}
207221

222+
void VirtualConnection::onProcessFinished(int exitCode, QProcess::ExitStatus status)
223+
{
224+
qDebug() << qPrintable(QString("[IO][%1]").arg(m_deviceName))
225+
<< "Simulator process finished. exitCode:" << exitCode
226+
<< "status:" << (status == QProcess::NormalExit ? "NormalExit" : "CrashExit");
227+
228+
cleanup();
229+
}
230+
231+
void VirtualConnection::onProcessErrorOccurred(QProcess::ProcessError error)
232+
{
233+
qWarning() << qPrintable(QString("[IO][%1]").arg(m_deviceName))
234+
<< "Simulator process error:" << error
235+
<< (m_process ? m_process->errorString() : QString());
236+
237+
if (error == QProcess::Crashed || error == QProcess::FailedToStart) {
238+
cleanup();
239+
}
240+
}
241+
208242
#endif // VIRTUAL_SIMULATOR_PROCESS
209243

210244
// Data transfer

src/gpilot/io/connection/virtualconnection.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ private slots:
9393
void onDisconnected();
9494
void onControlDisconnected();
9595
void onReadyRead();
96+
#ifdef VIRTUAL_SIMULATOR_PROCESS
97+
void onProcessFinished(int exitCode, QProcess::ExitStatus status);
98+
void onProcessErrorOccurred(QProcess::ProcessError error);
99+
#endif
96100
};
97101

98102
#endif // VIRTUALCONNECTION_H

0 commit comments

Comments
 (0)