[4] | 1 | #include <QtNetwork/QtNetwork> |
---|
| 2 | #include <QApplication> |
---|
| 3 | #include <QMessageBox> |
---|
| 4 | |
---|
| 5 | #include "ProjectionModel.h" |
---|
| 6 | #include "Screen.h" |
---|
| 7 | #include "Scene.h" |
---|
| 8 | #include "Channel.h" |
---|
| 9 | #include "Exporter.h" |
---|
| 10 | #include "GUIControler.h" |
---|
| 11 | |
---|
| 12 | #include "RSync.h" |
---|
| 13 | |
---|
| 14 | using namespace projection; |
---|
| 15 | |
---|
| 16 | /** |
---|
| 17 | * Constructor. |
---|
| 18 | * |
---|
| 19 | * @param pModel Projection model. |
---|
| 20 | */ |
---|
| 21 | RSync::RSync(ProjectionModel* pModel) : QObject() |
---|
| 22 | { |
---|
| 23 | m_pModel = pModel; |
---|
| 24 | |
---|
| 25 | m_hostName = QHostInfo::localHostName(); |
---|
| 26 | m_pTcpServer = NULL; |
---|
| 27 | m_pTcpClient = NULL; |
---|
| 28 | m_portNo = DEFAULT_PORT_NO; |
---|
| 29 | m_serverAddress = QHostInfo::localHostName(); |
---|
| 30 | |
---|
| 31 | m_bBlockSync = false; |
---|
| 32 | } |
---|
| 33 | |
---|
| 34 | /** |
---|
| 35 | * Destructor. |
---|
| 36 | */ |
---|
| 37 | RSync::~RSync() |
---|
| 38 | { |
---|
| 39 | if (!m_bServer) |
---|
| 40 | sendChanges(RSYNC_COMMAND_DISCONNECT, "Disconnect"); |
---|
| 41 | |
---|
| 42 | if (m_pTcpServer) |
---|
| 43 | delete m_pTcpServer; |
---|
| 44 | if (m_pTcpClient) |
---|
| 45 | delete m_pTcpClient; |
---|
| 46 | } |
---|
| 47 | |
---|
| 48 | /** |
---|
| 49 | * Initialize as a server. |
---|
| 50 | * |
---|
| 51 | * @param portNo Port number. |
---|
| 52 | */ |
---|
| 53 | void RSync::initServer(int portNo) |
---|
| 54 | { |
---|
| 55 | m_bServer = true; |
---|
| 56 | |
---|
| 57 | if (portNo != 0) |
---|
| 58 | m_portNo = portNo; |
---|
| 59 | |
---|
| 60 | m_bServer = true; |
---|
| 61 | |
---|
| 62 | //return; |
---|
| 63 | |
---|
| 64 | m_pTcpServer = new QTcpServer(m_pModel); |
---|
| 65 | if (!m_pTcpServer->listen(QHostAddress::Any, m_portNo)) { |
---|
| 66 | QMessageBox::critical(NULL, APPLICATION_NAME, |
---|
| 67 | QString("Unable to start the server: %1.").arg(m_pTcpServer->errorString())); |
---|
| 68 | return; |
---|
| 69 | } |
---|
| 70 | connect(m_pTcpServer, SIGNAL(newConnection()), this, SLOT(acceptNewClient())); |
---|
| 71 | } |
---|
| 72 | |
---|
| 73 | /** |
---|
| 74 | * Initialize as a client. |
---|
| 75 | * |
---|
| 76 | * @param address Client address. |
---|
| 77 | * @param portNo Port number. |
---|
| 78 | */ |
---|
| 79 | void RSync::initClient(const QString& address, int portNo) |
---|
| 80 | { |
---|
| 81 | m_bServer = false; |
---|
| 82 | |
---|
| 83 | if (!address.isEmpty()) m_serverAddress = address; |
---|
| 84 | if (portNo != 0) m_portNo = portNo; |
---|
| 85 | |
---|
| 86 | m_pTcpClient = new QTcpSocket(this); |
---|
| 87 | m_pTcpClient->setReadBufferSize(0); |
---|
| 88 | connect(m_pTcpClient, SIGNAL(readyRead()), this, SLOT(recvSettings())); |
---|
| 89 | connect(m_pTcpClient, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayNetworkError(QAbstractSocket::SocketError))); |
---|
| 90 | |
---|
| 91 | connectToServer(); |
---|
| 92 | } |
---|
| 93 | |
---|
| 94 | /** |
---|
| 95 | * Send a sync command to clients. |
---|
| 96 | * |
---|
| 97 | * @param command Synchronize command. |
---|
| 98 | * @param command Synchronize data. |
---|
| 99 | */ |
---|
| 100 | void RSync::sendChanges(RSYNC_COMMAND command, const QString& data) |
---|
| 101 | { |
---|
| 102 | if (m_bBlockSync) |
---|
| 103 | return; |
---|
| 104 | |
---|
| 105 | QByteArray block; |
---|
| 106 | QDataStream out(&block, QIODevice::WriteOnly); |
---|
| 107 | out.setVersion(QDataStream::Qt_4_5); |
---|
| 108 | out << (quint32)0; |
---|
| 109 | out << (int)command; |
---|
| 110 | out << data; |
---|
| 111 | out.device()->seek(0); |
---|
| 112 | out << (quint32)(block.size() - sizeof(quint32)); |
---|
| 113 | |
---|
| 114 | for (int i=0; i<m_pClientConnections.size(); ++i) |
---|
| 115 | { |
---|
| 116 | if (m_pClientConnections[i]->isValid()) { |
---|
| 117 | m_pClientConnections[i]->write(block); |
---|
| 118 | m_pClientConnections[i]->flush(); |
---|
| 119 | } |
---|
| 120 | } |
---|
| 121 | } |
---|
| 122 | |
---|
| 123 | /** |
---|
| 124 | * Send a reload command to clients. |
---|
| 125 | */ |
---|
| 126 | void RSync::sendReloadSettings() |
---|
| 127 | { |
---|
| 128 | if (m_bBlockSync) |
---|
| 129 | return; |
---|
| 130 | |
---|
| 131 | if (m_bServer) |
---|
| 132 | { |
---|
| 133 | QDomDocument doc; |
---|
| 134 | m_pModel->storeSettings(doc); |
---|
| 135 | sendChanges(RSYNC_COMMAND_RELOAD, doc.toString(0)); |
---|
| 136 | } |
---|
| 137 | } |
---|
| 138 | |
---|
| 139 | /** |
---|
| 140 | * Connect to a server. |
---|
| 141 | */ |
---|
| 142 | void RSync::connectToServer() |
---|
| 143 | { |
---|
| 144 | m_pTcpClient->abort(); |
---|
| 145 | m_pTcpClient->connectToHost(m_serverAddress, m_portNo); |
---|
| 146 | } |
---|
| 147 | |
---|
| 148 | /** |
---|
| 149 | * Called when a new client connects to the server. Send all states to synchronize. |
---|
| 150 | */ |
---|
| 151 | void RSync::acceptNewClient() |
---|
| 152 | { |
---|
| 153 | QDomDocument doc; |
---|
| 154 | m_pModel->storeSettings(doc); |
---|
| 155 | |
---|
| 156 | QByteArray block; |
---|
| 157 | QDataStream out(&block, QIODevice::WriteOnly); |
---|
| 158 | out.setVersion(QDataStream::Qt_4_5); |
---|
| 159 | out << (quint32)0; |
---|
| 160 | out << (int)RSYNC_COMMAND_INIT; |
---|
| 161 | out << doc.toString(0); |
---|
| 162 | out.device()->seek(0); |
---|
| 163 | out << (quint32)(block.size() - sizeof(quint32)); |
---|
| 164 | |
---|
| 165 | QTcpSocket* pClientConnection = m_pTcpServer->nextPendingConnection(); |
---|
| 166 | // connect(pClientConnection, SIGNAL(disconnected()), |
---|
| 167 | // pClientConnection, SLOT(deleteLater())); |
---|
| 168 | m_pClientConnections.push_back(pClientConnection); |
---|
| 169 | |
---|
| 170 | m_pModel->getGUI()->showStatusBarMessage(QString("%1 connected").arg(pClientConnection->peerAddress().toString()), 2000); |
---|
| 171 | |
---|
| 172 | pClientConnection->write(block); |
---|
| 173 | pClientConnection->flush(); |
---|
| 174 | // pClientConnection->disconnectFromHost(); |
---|
| 175 | |
---|
| 176 | // Inform Clients about acutal loaded Scene/Model/Screen |
---|
| 177 | m_pModel->getScreen()->notifyRedraw(); |
---|
| 178 | } |
---|
| 179 | |
---|
| 180 | /** |
---|
| 181 | * Called when a new synchronize message is received. |
---|
| 182 | */ |
---|
| 183 | void RSync::recvSettings() |
---|
| 184 | { |
---|
| 185 | QDataStream in(m_pTcpClient); |
---|
| 186 | in.setVersion(QDataStream::Qt_4_5); |
---|
| 187 | |
---|
| 188 | |
---|
| 189 | while (m_pTcpClient->bytesAvailable() > 0) |
---|
| 190 | { |
---|
| 191 | static quint32 blockSize = 0; |
---|
| 192 | if (blockSize == 0) { |
---|
| 193 | if (m_pTcpClient->bytesAvailable() < (int)sizeof(quint32)) |
---|
| 194 | return; |
---|
| 195 | in >> blockSize; |
---|
| 196 | } |
---|
| 197 | |
---|
| 198 | if (m_pTcpClient->bytesAvailable() < blockSize) |
---|
| 199 | return; |
---|
| 200 | |
---|
| 201 | int command; |
---|
| 202 | in >> command; |
---|
| 203 | |
---|
| 204 | QString data; |
---|
| 205 | in >> data; |
---|
| 206 | |
---|
| 207 | blockSize = 0; |
---|
| 208 | |
---|
| 209 | switch (command) |
---|
| 210 | { |
---|
| 211 | case RSYNC_COMMAND_INIT: |
---|
| 212 | { |
---|
| 213 | m_lastErrorString = ""; |
---|
| 214 | m_pModel->getGUI()->appendClientLog(tr("Connected to server : %1:%2.").arg(m_serverAddress).arg(m_portNo)); |
---|
| 215 | |
---|
| 216 | QDomDocument doc; |
---|
| 217 | doc.setContent(data); |
---|
| 218 | m_pModel->restoreSettings(doc); |
---|
| 219 | break; |
---|
| 220 | } |
---|
| 221 | case RSYNC_COMMAND_VIEWPOINT: |
---|
| 222 | { |
---|
| 223 | m_pModel->getScene()->setCameraViewpoint(data); |
---|
| 224 | break; |
---|
| 225 | } |
---|
| 226 | case RSYNC_COMMAND_EXPORT: |
---|
| 227 | { |
---|
| 228 | QDomDocument doc; |
---|
| 229 | doc.setContent(data); |
---|
| 230 | m_pModel->getExporter()->initFromDOMElement(doc.firstChildElement("Exporter")); |
---|
| 231 | break; |
---|
| 232 | } |
---|
| 233 | case RSYNC_COMMAND_MODEL: |
---|
| 234 | { |
---|
| 235 | QDomDocument doc; |
---|
| 236 | doc.setContent(data); |
---|
| 237 | m_pModel->initFromDOMElement(doc.firstChildElement("Model")); |
---|
| 238 | break; |
---|
| 239 | } |
---|
| 240 | case RSYNC_COMMAND_SCENE: |
---|
| 241 | { |
---|
| 242 | QDomDocument doc; |
---|
| 243 | doc.setContent(data); |
---|
| 244 | m_pModel->getScene()->initFromDOMElement(doc.firstChildElement("Scene")); |
---|
| 245 | break; |
---|
| 246 | } |
---|
| 247 | case RSYNC_COMMAND_SCREEN: |
---|
| 248 | { |
---|
| 249 | QDomDocument doc; |
---|
| 250 | doc.setContent(data); |
---|
| 251 | m_pModel->getScreen()->initFromDOMElement(doc.firstChildElement("Screen")); |
---|
| 252 | break; |
---|
| 253 | } |
---|
| 254 | case RSYNC_COMMAND_CHANNEL: |
---|
| 255 | { |
---|
| 256 | QDomDocument doc; |
---|
| 257 | doc.setContent(data); |
---|
| 258 | QDomElement channel = doc.firstChildElement("Channel"); |
---|
| 259 | QString channelName = channel.attribute("name"); |
---|
| 260 | for (unsigned int i=0; i<m_pModel->getNumChannels(); ++i) { |
---|
| 261 | if (channelName == m_pModel->getChannel(i)->getName()) |
---|
| 262 | m_pModel->getChannel(i)->initFromDOMElement(channel, APPLICATION_VERSION); |
---|
| 263 | } |
---|
| 264 | break; |
---|
| 265 | } |
---|
| 266 | case RSYNC_COMMAND_GUI: |
---|
| 267 | { |
---|
| 268 | QDomDocument doc; |
---|
| 269 | doc.setContent(data); |
---|
| 270 | m_pModel->getGUI()->initFromDOMElement(doc.firstChildElement("GUI")); |
---|
| 271 | break; |
---|
| 272 | } |
---|
| 273 | case RSYNC_COMMAND_RELOAD: |
---|
| 274 | { |
---|
| 275 | QDomDocument doc; |
---|
| 276 | doc.setContent(data); |
---|
| 277 | m_pModel->restoreSettings(doc); |
---|
| 278 | break; |
---|
| 279 | } |
---|
| 280 | case RSYNC_COMMAND_EXPORTFILE: |
---|
| 281 | { |
---|
| 282 | m_pModel->getExporter()->exportDataset(data); |
---|
| 283 | break; |
---|
| 284 | } |
---|
| 285 | case RSYNC_COMMAND_DISCONNECT: |
---|
| 286 | { |
---|
| 287 | m_pModel->removeAllChannels(); |
---|
| 288 | m_lastErrorString = ""; |
---|
| 289 | m_pModel->getGUI()->appendClientLog(tr("Connection shutdowned.")); |
---|
| 290 | |
---|
| 291 | connectToServer(); |
---|
| 292 | break; |
---|
| 293 | } |
---|
| 294 | case RSYNC_COMMAND_END: |
---|
| 295 | { |
---|
| 296 | qApp->closeAllWindows(); |
---|
| 297 | } |
---|
| 298 | } |
---|
| 299 | } |
---|
| 300 | } |
---|
| 301 | |
---|
| 302 | /** |
---|
| 303 | * Called when a network error is occured. |
---|
| 304 | */ |
---|
| 305 | void RSync::displayNetworkError(QAbstractSocket::SocketError socketError) |
---|
| 306 | { |
---|
| 307 | /* |
---|
| 308 | switch (socketError) { |
---|
| 309 | case QAbstractSocket::RemoteHostClosedError: |
---|
| 310 | break; |
---|
| 311 | case QAbstractSocket::HostNotFoundError: |
---|
| 312 | QMessageBox::information(NULL, APPLICATION_NAME, |
---|
| 313 | tr("The host was not found. Please check the " |
---|
| 314 | "host name and port settings.")); |
---|
| 315 | break; |
---|
| 316 | case QAbstractSocket::ConnectionRefusedError: |
---|
| 317 | QMessageBox::information(NULL, APPLICATION_NAME, |
---|
| 318 | tr("The connection was refused by the peer. " |
---|
| 319 | "Make sure the Multichannnel Projection Designer server is running, " |
---|
| 320 | "and check that the host name and port settings are correct.")); |
---|
| 321 | break; |
---|
| 322 | default: |
---|
| 323 | QMessageBox::information(NULL, APPLICATION_NAME, |
---|
| 324 | tr("The following error occurred: %1.") |
---|
| 325 | .arg(m_pTcpClient->errorString())); |
---|
| 326 | } |
---|
| 327 | */ |
---|
| 328 | |
---|
| 329 | if (socketError != QAbstractSocket::UnknownSocketError && |
---|
| 330 | m_lastErrorString != m_pTcpClient->errorString()) |
---|
| 331 | { |
---|
| 332 | m_pModel->removeAllChannels(); |
---|
| 333 | m_lastErrorString = m_pTcpClient->errorString(); |
---|
| 334 | m_pModel->getGUI()->appendClientLog(m_lastErrorString); |
---|
| 335 | m_pModel->getGUI()->appendClientLog(tr("Try connecting again...")); |
---|
| 336 | } |
---|
| 337 | |
---|
| 338 | QTimer* pTimer = new QTimer(this); |
---|
| 339 | connect(pTimer, SIGNAL(timeout()), this, SLOT(connectToServer())); |
---|
| 340 | pTimer->setSingleShot(true); |
---|
| 341 | pTimer->start(1000); |
---|
| 342 | } |
---|
| 343 | |
---|
| 344 | /** |
---|
| 345 | * Set sync message blocking. |
---|
| 346 | * |
---|
| 347 | * @param bBlock True to block sending sync data to clients. |
---|
| 348 | */ |
---|
| 349 | void RSync::setBlockSync(bool bBlock) |
---|
| 350 | { |
---|
| 351 | m_bBlockSync = bBlock; |
---|
| 352 | } |
---|
| 353 | |
---|
| 354 | /** |
---|
| 355 | * Check whether sync message is blocked or not. |
---|
| 356 | * |
---|
| 357 | * @param bBlock True to block sending sync data to clients. |
---|
| 358 | */ |
---|
| 359 | bool RSync::getBlockSync() const |
---|
| 360 | { |
---|
| 361 | return m_bBlockSync; |
---|
| 362 | } |
---|