CustusX  2023.01.05-dev+develop.0da12
An IGT application
cxSocketConnection.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 
12 #include "cxSocketConnection.h"
13 #include <QTcpServer>
14 #include <QTcpSocket>
15 #include <QNetworkInterface>
16 #include <QThread>
17 
18 #include "cxSocket.h"
19 #include "cxLogger.h"
20 
22 {
23  "inactive",
24  "connected",
25  "listening",
26  "connecting"
27 }
29 
30 
31 
32 namespace cx
33 {
34 
35 
37  {
38  return (this->role == rhs.role)
39  && (this->protocol == rhs.protocol)
40  && (this->host == rhs.host)
41  && (this->port == rhs.port);
42  }
43 
44  bool SocketConnection::ConnectionInfo::isServer() const { return role.toLower()=="server"; }
45  bool SocketConnection::ConnectionInfo::isClient() const { return !this->isServer(); }
46  bool SocketConnection::ConnectionInfo::isLocalhostConnection() const { return host.toLower().contains("localhost"); }
47 
49  {
50  QString postfix;
51  QString name = host;
52  if (isServer())
53  name = "listen";
54  if (!protocol.isEmpty())
55  postfix = QString("[%1]").arg(protocol);
56  return QString("%1:%2%3").arg(name).arg(port).arg(postfix);
57  }
58 
62 
63 
65  QObject(parent)
66 {
68  qRegisterMetaType<boost::function<void()> >("boost::function<void()>");
69  qRegisterMetaType<CX_SOCKETCONNECTION_STATE>("CX_SOCKETCONNECTION_STATE");
70 
71  mNextConnectionInfo.host = "localhost";
72  mNextConnectionInfo.port = 18944;
73  mNextConnectionInfo.role = "client";
74 
75  mSocket = new QTcpSocket(this);
76  connect(mSocket, &QTcpSocket::connected, this, &SocketConnection::internalConnected);
77  connect(mSocket, &QTcpSocket::disconnected, this, &SocketConnection::internalDisconnected);
78  connect(mSocket, &QTcpSocket::readyRead, this, &SocketConnection::internalDataAvailable);
79  connect(mSocket, &QTcpSocket::readyRead, this, &SocketConnection::dataAvailable);
80 
81  //see http://stackoverflow.com/questions/26062397/qt-connect-function-signal-disambiguation-using-lambdas
82  void (QTcpSocket::* errorOverloaded)(QAbstractSocket::SocketError) = &QTcpSocket::error;
83  connect(mSocket, errorOverloaded, this, &SocketConnection::error);
84  connect(mSocket, errorOverloaded, this, &SocketConnection::internalError);
85 }
86 
88 {
89  QMutexLocker locker(&mNextConnectionInfoMutex);
90  return mNextConnectionInfo;
91 }
92 
94 {
95  QMutexLocker locker(&mNextConnectionInfoMutex);
96  if (info==mNextConnectionInfo)
97  return;
98  mNextConnectionInfo = info;
99  locker.unlock();
100 
101  emit connectionInfoChanged();
102 }
103 
105 {
106  if (mCurrentState==newState)
107  return;
108 
109  if (newState==scsCONNECTED)
110  emit connected();
111  else if (mCurrentState==scsCONNECTED)
112  emit disconnected();
113 
114  mCurrentState = newState;
115 
117 }
118 
120 {
121  return mCurrentState;
122 }
123 
125 {
127 
128  ConnectionInfo info = this->getConnectionInfo();
129  mConnector = this->createConnector(info);
130  this->setProtocol(info.protocol);
131  mConnector->activate();
132 }
133 
135 {
136  SocketConnectorPtr retval;
137 
138  if (info.isClient())
139  retval.reset(new SocketClientConnector(info, mSocket));
140  else
141  retval.reset(new SocketServerConnector(info, mSocket));
142 
143  connect(retval.get(), &SocketConnector::stateChanged, this, &SocketConnection::stateChange);
144  return retval;
145 }
146 
148 {
150 
151  if (mConnector)
152  {
153  mConnector->deactivate();
154  mConnector.reset();
155  }
156  mSocket->close();
157 }
158 
159 bool SocketConnection::sendData(const char *data, qint64 maxSize)
160 {
161  if(!this->socketIsConnected())
162  return false;
163  qint64 writtenBytes = mSocket->write(data, maxSize);
164  if(writtenBytes != maxSize)
165  return false;
166  return true;
167 }
168 
169 void SocketConnection::internalConnected()
170 {
171 // CX_LOG_SUCCESS() << "Connected to " << mCurrentConnectionInfo.getDescription();
172  this->stateChange(this->computeState());
173 }
174 
175 void SocketConnection::internalDisconnected()
176 {
177 // CX_LOG_SUCCESS() << "Disconnected";
178  this->stateChange(this->computeState());
179 }
180 
181 void SocketConnection::internalError(QAbstractSocket::SocketError socketError)
182 {
183  CX_LOG_ERROR() << QString("[%1] Socket error [code=%2]: %3")
184  .arg(mSocket->peerName())
185  .arg(QString::number(socketError))
186  .arg(mSocket->errorString());
187 
188  this->stateChange(this->computeState());
189 }
190 
191 void SocketConnection::internalDataAvailable()
192 {
193  //TODO
194  //CX_LOG_DEBUG() << "Read data";
195 }
196 
197 void SocketConnection::setProtocol(QString protocolname)
198 {
199  //TODO?
200 }
201 
203 {
204  return mSocket->state() == QAbstractSocket::ConnectedState;
205 }
206 
208 {
209  bool enoughBytes = mSocket->bytesAvailable() >= bytes;
210 // if(!enoughBytes)
211 // CX_LOG_DEBUG() << "Want " << bytes << " but only "<< mSocket->bytesAvailable() << " are available on the socket atm.";
212  return enoughBytes;
213 }
214 
215 bool SocketConnection::socketReceive(void *packPointer, int packSize) const
216 {
217  if(!this->enoughBytesAvailableOnSocket(packSize))
218  return false;
219 
220  char* charPointer = reinterpret_cast<char*>(packPointer);
221  int r = mSocket->read(charPointer, packSize);
222  if(r <= 0)
223  {
224  CX_LOG_ERROR() << "Error when receiving data from socket.";
225  return false;
226  }
227  return true;
228 }
229 
231 {
232  if (mConnector)
233  return mConnector->getState();
234  return scsINACTIVE;
235 }
236 
237 
238 } //namespace cx
virtual void setConnectionInfo(ConnectionInfo info)
thread-safe
CX_SOCKETCONNECTION_STATE
bool sendData(const char *data, qint64 maxSize)
not thread-safe
bool socketReceive(void *packPointer, int packSize) const
bool enoughBytesAvailableOnSocket(int bytes) const
CX_SOCKETCONNECTION_STATE getState()
thread-safe
ConnectionInfo mNextConnectionInfo
info to be used for the next connect(), mutexed.
void stateChange(CX_SOCKETCONNECTION_STATE newState)
CX_SOCKETCONNECTION_STATE mCurrentState
void stateChanged(CX_SOCKETCONNECTION_STATE status)
virtual void requestDisconnect()
not thread-safe: use invoke
ConnectionInfo getConnectionInfo()
thread-safe
SocketConnection(QObject *parent=0)
DEFINE_ENUM_STRING_CONVERTERS_BEGIN(cx, CX_SOCKETCONNECTION_STATE, scsCOUNT)
virtual void requestConnect()
not thread-safe: use invoke
DEFINE_ENUM_STRING_CONVERTERS_END(cx, ORIENTATION_TYPE, otCOUNT)
#define CX_LOG_ERROR
Definition: cxLogger.h:99
SocketConnectorPtr createConnector(ConnectionInfo info)
boost::shared_ptr< class SocketConnector > SocketConnectorPtr
void stateChanged(CX_SOCKETCONNECTION_STATE)
#define assertRunningInObjectThread()
CX_SOCKETCONNECTION_STATE computeState()
SocketConnectorPtr mConnector
bool operator==(const ConnectionInfo &rhs) const
virtual void setProtocol(QString protocolname)
Namespace for all CustusX production code.