Skip to content

Commit fd99904

Browse files
committed
Route adapter.diagnostic notifications to diagnostics log
AdapterClient now connects to AdapterProcess::notificationReceived and emits diagnosticReceived(level, message) for adapter.diagnostic notifications. ModbusPoll connects to this signal and routes the message to the existing scopeComm logging category using the appropriate qCDebug/qCInfo/qCWarning call, so adapter-side log messages flow through ScopeLogging into DiagnosticModel alongside host-side entries. Tests updated: notificationIgnored now also verifies diagnosticReceived stays silent for non-diagnostic notifications. Two new tests cover warning and debug level forwarding. https://claude.ai/code/session_01UhRoTiKzKdkuurvzKQBVjr
1 parent 180a3bd commit fd99904

6 files changed

Lines changed: 78 additions & 1 deletion

File tree

src/ProtocolAdapter/adapterclient.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ AdapterClient::AdapterClient(AdapterProcess* pProcess, QObject* parent) : QObjec
1616
connect(_pProcess, &AdapterProcess::errorReceived, this, &AdapterClient::onErrorReceived);
1717
connect(_pProcess, &AdapterProcess::processError, this, &AdapterClient::onProcessError);
1818
connect(_pProcess, &AdapterProcess::processFinished, this, &AdapterClient::onProcessFinished);
19+
connect(_pProcess, &AdapterProcess::notificationReceived, this, &AdapterClient::onNotificationReceived);
1920
}
2021

2122
AdapterClient::~AdapterClient() = default;
@@ -159,6 +160,16 @@ void AdapterClient::onHandshakeTimeout()
159160
emit sessionError("Adapter handshake timed out");
160161
}
161162

163+
void AdapterClient::onNotificationReceived(QString method, QJsonValue params)
164+
{
165+
if (method == QStringLiteral("adapter.diagnostic"))
166+
{
167+
QJsonObject obj = params.toObject();
168+
emit diagnosticReceived(obj.value(QStringLiteral("level")).toString(),
169+
obj.value(QStringLiteral("message")).toString());
170+
}
171+
}
172+
162173
void AdapterClient::handleLifecycleResponse(const QString& method, const QJsonObject& result)
163174
{
164175
if (method == "adapter.initialize" && _state == State::INITIALIZING)

src/ProtocolAdapter/adapterclient.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ class AdapterClient : public QObject
114114
*/
115115
void sessionStopped();
116116

117+
/*!
118+
* \brief Emitted when an adapter.diagnostic notification is received from the adapter.
119+
* \param level Severity level string: "debug", "info", or "warning".
120+
* \param message The diagnostic message from the adapter.
121+
*/
122+
void diagnosticReceived(QString level, QString message);
123+
117124
protected:
118125
enum class State
119126
{
@@ -135,6 +142,7 @@ private slots:
135142
void onProcessError(const QString& message);
136143
void onProcessFinished();
137144
void onHandshakeTimeout();
145+
void onNotificationReceived(QString method, QJsonValue params);
138146

139147
private:
140148
void handleLifecycleResponse(const QString& method, const QJsonObject& result);

src/communication/modbuspoll.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ ModbusPoll::ModbusPoll(SettingsModel* pSettingsModel, QObject* parent) : QObject
3030
_bPollActive = false;
3131
});
3232
connect(_pAdapterClient, &AdapterClient::sessionStopped, this, &ModbusPoll::initAdapter);
33+
connect(_pAdapterClient, &AdapterClient::diagnosticReceived, this, &ModbusPoll::onAdapterDiagnostic);
3334
}
3435

3536
ModbusPoll::~ModbusPoll() = default;
@@ -119,6 +120,30 @@ void ModbusPoll::onDescribeResult(const QJsonObject& description)
119120
_pSettingsModel->updateAdapterFromDescribe("modbus", description);
120121
}
121122

123+
/*! \brief Route an adapter.diagnostic notification to the diagnostics log.
124+
*
125+
* Maps the adapter's level string to the appropriate Qt logging severity so
126+
* the message flows through ScopeLogging into DiagnosticModel.
127+
*
128+
* \param level Severity string from the adapter: "debug", "info", or "warning".
129+
* \param message The diagnostic message text.
130+
*/
131+
void ModbusPoll::onAdapterDiagnostic(const QString& level, const QString& message)
132+
{
133+
if (level == QStringLiteral("debug"))
134+
{
135+
qCDebug(scopeComm) << message;
136+
}
137+
else if (level == QStringLiteral("info"))
138+
{
139+
qCInfo(scopeComm) << message;
140+
}
141+
else
142+
{
143+
qCWarning(scopeComm) << message;
144+
}
145+
}
146+
122147
QStringList ModbusPoll::buildRegisterExpressions(const QList<DataPoint>& registerList)
123148
{
124149
QStringList expressions;

src/communication/modbuspoll.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ private slots:
3434
void triggerRegisterRead();
3535
void onReadDataResult(ResultDoubleList results);
3636
void onDescribeResult(const QJsonObject& description);
37+
void onAdapterDiagnostic(const QString& level, const QString& message);
3738

3839
private:
3940
QStringList buildRegisterExpressions(const QList<DataPoint>& registerList);

tests/ProtocolAdapter/tst_adapterclient.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,13 +282,43 @@ void TestAdapterClient::notificationIgnored()
282282
QSignalSpy spyStarted(&client, &AdapterClient::sessionStarted);
283283
QSignalSpy spyError(&client, &AdapterClient::sessionError);
284284
QSignalSpy spyData(&client, &AdapterClient::readDataResult);
285+
QSignalSpy spyDiagnostic(&client, &AdapterClient::diagnosticReceived);
285286

286-
/* Simulate a server-initiated notification and verify AdapterClient stays silent */
287+
/* Simulate a non-diagnostic notification and verify all signals stay silent */
287288
mock->injectNotification("adapter.progress", QJsonObject{ { "percent", 50 } });
288289

289290
QCOMPARE(spyStarted.count(), 0);
290291
QCOMPARE(spyError.count(), 0);
291292
QCOMPARE(spyData.count(), 0);
293+
QCOMPARE(spyDiagnostic.count(), 0);
294+
}
295+
296+
void TestAdapterClient::diagnosticNotificationForwarded()
297+
{
298+
auto* mock = new MockAdapterProcess();
299+
AdapterClient client(mock);
300+
301+
QSignalSpy spyDiagnostic(&client, &AdapterClient::diagnosticReceived);
302+
303+
mock->injectNotification("adapter.diagnostic", QJsonObject{ { "level", "warning" }, { "message", "connection lost" } });
304+
305+
QCOMPARE(spyDiagnostic.count(), 1);
306+
QCOMPARE(spyDiagnostic.at(0).at(0).toString(), QStringLiteral("warning"));
307+
QCOMPARE(spyDiagnostic.at(0).at(1).toString(), QStringLiteral("connection lost"));
308+
}
309+
310+
void TestAdapterClient::diagnosticNotificationDebugLevel()
311+
{
312+
auto* mock = new MockAdapterProcess();
313+
AdapterClient client(mock);
314+
315+
QSignalSpy spyDiagnostic(&client, &AdapterClient::diagnosticReceived);
316+
317+
mock->injectNotification("adapter.diagnostic", QJsonObject{ { "level", "debug" }, { "message", "polling started" } });
318+
319+
QCOMPARE(spyDiagnostic.count(), 1);
320+
QCOMPARE(spyDiagnostic.at(0).at(0).toString(), QStringLiteral("debug"));
321+
QCOMPARE(spyDiagnostic.at(0).at(1).toString(), QStringLiteral("polling started"));
292322
}
293323

294324
void TestAdapterClient::processErrorEmitsSessionError()

tests/ProtocolAdapter/tst_adapterclient.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ private slots:
1818
void errorResponseEmitsSessionError();
1919
void unexpectedResponseEmitsNoSignals();
2020
void notificationIgnored();
21+
void diagnosticNotificationForwarded();
22+
void diagnosticNotificationDebugLevel();
2123
void processErrorEmitsSessionError();
2224
void stopSessionDuringLifecycle();
2325
void doubleStopSession();

0 commit comments

Comments
 (0)