GCC Code Coverage Report


source/XpertMassCore/src/
File: source/XpertMassCore/src/MassDataClient.cpp
Date: 2025-11-20 01:41:33
Lines:
0/61
0.0%
Functions:
0/10
0.0%
Branches:
0/42
0.0%

Line Branch Exec Source
1 /* BEGIN software license
2 *
3 * MsXpertSuite - mass spectrometry software suite
4 * -----------------------------------------------
5 * Copyright (C) 2009--2020 Filippo Rusconi
6 *
7 * http://www.msxpertsuite.org
8 *
9 * This file is part of the MsXpertSuite project.
10 *
11 * The MsXpertSuite project is the successor of the massXpert project. This
12 * project now includes various independent modules:
13 *
14 * - massXpert, model polymer chemistries and simulate mass spectrometric data;
15 * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
16 *
17 * This program is free software: you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation, either version 3 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29 *
30 * *****************************************************************************
31 * This specific code is a port to C++ of the Envemind Python code by Radzinski
32 * and colleagues of IsoSpec fame (Lacki, Startek and company :-)
33 *
34 * See https://github.com/PiotrRadzinski/envemind.
35 * *****************************************************************************
36 *
37 * END software license
38 */
39
40
41 #include <QDebug>
42 #include <QtNetwork>
43
44 #include "MsXpS/libXpertMassCore/MassDataClient.hpp"
45
46 namespace MsXpS
47 {
48 namespace libXpertMassCore
49 {
50
51 /*!
52 \class MsXpS::libXpertMassCore::MassDataClient
53 \inmodule libXpertMassCore
54 \ingroup XpertMassCoreUtilities
55 \inheaderfile MassDataClient.hpp
56
57 \brief The MassDataClient class provides a network client.
58 */
59
60 /*!
61 \variable MsXpS::libXpertMassCore::MassDataClient::m_inStream
62
63 \brief The data stream in input into this MassDataClient instance.
64 */
65
66 /*!
67 \variable MsXpS::libXpertMassCore::MassDataClient::m_data
68
69 \brief The data being transported.
70 */
71
72 /*!
73 \variable MsXpS::libXpertMassCore::MassDataClient::m_ipAddress
74
75 \brief The server IP address to which the connection must be made.
76 */
77
78 /*!
79 \variable MsXpS::libXpertMassCore::MassDataClient::m_portNumber
80
81 \brief The port number to which to attach during the connection.
82 */
83
84 /*!
85 \variable MsXpS::libXpertMassCore::MassDataClient::mp_tcpSocket
86
87 \brief The socket that is used for the connection to the server.
88 */
89
90 /*!
91 * \variable MsXpS::libXpertMassCore::MassDataClient::m_forcefulDisconnection
92 *
93 * \brief Tells if the disconnection was forceful.
94 */
95
96 /*!
97 \brief Constructs a MassDataClient instance.
98
99 \list
100 \li \a ip_address: the server (\l{MassDataServer}) IP address at which the client
101 needs to try a connection.
102 \li \a port_number: the port number for the connection.
103 \li \a parent: the parent QObject.
104 \endlist
105 */
106 MassDataClient::MassDataClient(const QString &ip_address,
107 int port_number,
108 QObject *parent)
109 : QObject(parent), m_ipAddress(ip_address), m_portNumber(port_number)
110 {
111 mp_tcpSocket = new QTcpSocket(parent);
112
113 if(mp_tcpSocket == nullptr)
114 qFatal("Failed to allocate a socket.");
115
116 // When the connection below (see call mpa_tcpSocket->connectToHost())
117 // will actually work, this signal will be emitted and we respond
118 // by sending to the socket the READY string so that the server
119 // know we are ready to receive data.
120 connect(mp_tcpSocket, &QTcpSocket::connected, [this]() {
121 mp_tcpSocket->write("READY\n");
122 mp_tcpSocket->flush();
123 emit connectedSignal();
124 });
125
126 connect(mp_tcpSocket, &QTcpSocket::hostFound, [this]() {
127 emit hostFoundSignal();
128 });
129
130 connect(mp_tcpSocket,
131 &QAbstractSocket::errorOccurred,
132 this,
133 &MassDataClient::reportError);
134
135 connect(mp_tcpSocket, &QTcpSocket::disconnected, [this]() {
136 // This signal can be sent when the user forcefully
137 // disconnects from the host. In this case, the socket
138 // is destroyed. So we need to ensure we only try to
139 // reconnect if the disconnection was not voluntary.
140 if(mp_tcpSocket != nullptr)
141 {
142 qDebug() << "Lost connection, the socket is not destroyed."
143 << QDateTime::currentDateTime();
144
145 if(m_forcefulDisconnection)
146 {
147 qDebug() << "The disconnection was forceful. Do not restart."
148 << QDateTime::currentDateTime();
149 }
150 else
151 {
152 qDebug() << "The disconnection was not forceful, retrying "
153 "connection in 5s..."
154 << QDateTime::currentDateTime() << mp_tcpSocket;
155 QTimer::singleShot(5000, [this]() {
156 mp_tcpSocket->connectToHost(m_ipAddress, m_portNumber);
157 });
158 }
159 }
160 else
161 {
162 qDebug() << "The socket was destroyed already. Doing nothing."
163 << QDateTime::currentDateTime()
164 << QDateTime::currentDateTime();
165 }
166
167 qDebug() << "Now emitting disconnectedSignal() for the user of this client."
168 << QDateTime::currentDateTime();
169
170 emit disconnectedSignal();
171 });
172
173 // When data will be ready to read from the server, they well be served
174 // in this m_inStream.
175 m_inStream.setDevice(mp_tcpSocket);
176 m_inStream.setVersion(QDataStream::Qt_5_0);
177
178 connect(mp_tcpSocket, &QIODevice::readyRead, this, &MassDataClient::readData);
179
180 mp_tcpSocket->connectToHost(ip_address, port_number);
181 }
182
183 /*!
184 \brief Destructs this MassDataClient instance.
185 */
186 MassDataClient::~MassDataClient()
187 {
188 }
189
190 /*!
191 \brief Reads the data into the QByteArray m_data member using the QDataStream
192 m_inStream member.
193
194 Emits the newDataSignal() signal with m_data;.
195 */
196 void
197 MassDataClient::readData()
198 {
199 // qDebug() << "Reading data.";
200
201 // We need to have the same version, FIXME, this should go as a macro
202 // somewhere.
203
204 // qDebug() << "The number of bytes available in this readData call:"
205 //<< mp_tcpSocket->bytesAvailable() << "at"
206 //<< QDateTime::currentDateTime().toString();
207
208 // This version uses the readAll() function.
209
210 // QByteArray byte_array = mp_tcpSocket->readAll();
211
212 // QDataStream stream(byte_array);
213 // stream.setVersion(QDataStream::Qt_5_0);
214 // stream.startTransaction();
215 // stream >> m_data;
216
217 // if(!stream.commitTransaction())
218 //{
219 // qDebug() << "Failed to commit the data read transaction.";
220
221 // return ;
222 //}
223
224 // This version uses the stream operator.
225 // Seems to work equivalently to the version above.
226 // stream.setVersion(QDataStream::Qt_5_0);
227 m_inStream.startTransaction();
228
229 m_inStream >> m_data;
230
231 // qDebug() << "Atomically read" << m_data.size();
232
233 if(!m_inStream.commitTransaction())
234 {
235 qDebug() << "Could NOT commit the transaction fine.";
236 return;
237 }
238 // else
239 // qDebug() << "Could commit the transaction fine.";
240
241 emit newDataSignal(m_data);
242
243 // qDebug() << "Got these data:" << QString(m_data);
244 }
245
246 /*!
247 \brief Reports the error \a socket_error.
248 */
249 void
250 MassDataClient::reportError(QAbstractSocket::SocketError socket_error)
251 {
252 switch(socket_error)
253 {
254 case QAbstractSocket::RemoteHostClosedError:
255 // qDebug() << "Error: QAbstractSocket::RemoteHostClosedError.";
256 // emit reportErrorSignal("RemoteHostClosedError");
257 break;
258 case QAbstractSocket::HostNotFoundError:
259 // qDebug() << "Error: QAbstractSocket::HostNotFoundError.";
260 // emit reportErrorSignal("HostNotFoundError");
261 break;
262 case QAbstractSocket::ConnectionRefusedError:
263 qDebug() << "Error: QAbstractSocket::ConnectionRefusedError.";
264 emit reportErrorSignal("ConnectionRefusedError");
265 break;
266 default:
267 // qDebug() << "Error:" << mp_tcpSocket->errorString();
268 // emit reportErrorSignal(mp_tcpSocket->errorString());
269 break;
270 }
271 }
272
273 void
274 MassDataClient::forcefullyDisconnect()
275 {
276 qDebug() << "Forcefully disconnecting client."
277 << QDateTime::currentDateTime();
278
279 m_forcefulDisconnection = true;
280
281 if(mp_tcpSocket != nullptr)
282 {
283 // Maybe we do not need this step, deleting the socket will
284 // emit the disconnection signal.
285 // mp_tcpSocket->disconnectFromHost();
286
287 qDebug() << "Deleting the socket that was disconnected"
288 << QDateTime::currentDateTime() << mp_tcpSocket;
289
290 delete mp_tcpSocket;
291 mp_tcpSocket = nullptr;
292
293 qDebug() << "Deletedg the socket that was disconnected"
294 << QDateTime::currentDateTime() << mp_tcpSocket;
295
296 // This will emit the disconnected() signal that we have
297 // connected to the emission of disconnectedSignal().
298 // The user of this MassDataClient instance should connect
299 // a slot this disconnectedSignal() and handle the destruction of
300 // this very instance.
301 }
302 }
303
304 QString
305 MassDataClient::getIpAddress() const
306 {
307 return m_ipAddress;
308 }
309
310 int
311 MassDataClient::getPortNumber() const
312 {
313 return m_portNumber;
314 }
315
316 /*!
317 \brief Returns a string display the connection details.
318 */
319 QString
320 MassDataClient::getStatus()
321 {
322 return QString("Doing well, connection should be to IP address: %1, port %2.")
323 .arg(m_ipAddress)
324 .arg(m_portNumber);
325 }
326
327 } // namespace libXpertMassCore
328
329 } // namespace MsXpS
330