#include #include #include #include "ProjectionModel.h" #include "Screen.h" #include "Scene.h" #include "Channel.h" #include "Exporter.h" #include "GUIControler.h" #include "RSync.h" using namespace projection; /** * Constructor. * * @param pModel Projection model. */ RSync::RSync(ProjectionModel* pModel) : QObject() { m_pModel = pModel; m_hostName = QHostInfo::localHostName(); m_pTcpServer = NULL; m_pTcpClient = NULL; m_portNo = DEFAULT_PORT_NO; m_serverAddress = QHostInfo::localHostName(); m_bBlockSync = false; } /** * Destructor. */ RSync::~RSync() { if (!m_bServer) sendChanges(RSYNC_COMMAND_DISCONNECT, "Disconnect"); if (m_pTcpServer) delete m_pTcpServer; if (m_pTcpClient) delete m_pTcpClient; } /** * Initialize as a server. * * @param portNo Port number. */ void RSync::initServer(int portNo) { m_bServer = true; if (portNo != 0) m_portNo = portNo; m_bServer = true; //return; m_pTcpServer = new QTcpServer(m_pModel); if (!m_pTcpServer->listen(QHostAddress::Any, m_portNo)) { QMessageBox::critical(NULL, APPLICATION_NAME, QString("Unable to start the server: %1.").arg(m_pTcpServer->errorString())); return; } connect(m_pTcpServer, SIGNAL(newConnection()), this, SLOT(acceptNewClient())); } /** * Initialize as a client. * * @param address Client address. * @param portNo Port number. */ void RSync::initClient(const QString& address, int portNo) { m_bServer = false; if (!address.isEmpty()) m_serverAddress = address; if (portNo != 0) m_portNo = portNo; m_pTcpClient = new QTcpSocket(this); m_pTcpClient->setReadBufferSize(0); connect(m_pTcpClient, SIGNAL(readyRead()), this, SLOT(recvSettings())); connect(m_pTcpClient, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayNetworkError(QAbstractSocket::SocketError))); connectToServer(); } /** * Send a sync command to clients. * * @param command Synchronize command. * @param command Synchronize data. */ void RSync::sendChanges(RSYNC_COMMAND command, const QString& data) { if (m_bBlockSync) return; QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_4_5); out << (quint32)0; out << (int)command; out << data; out.device()->seek(0); out << (quint32)(block.size() - sizeof(quint32)); for (int i=0; iisValid()) { m_pClientConnections[i]->write(block); m_pClientConnections[i]->flush(); } } } /** * Send a reload command to clients. */ void RSync::sendReloadSettings() { if (m_bBlockSync) return; if (m_bServer) { QDomDocument doc; m_pModel->storeSettings(doc); sendChanges(RSYNC_COMMAND_RELOAD, doc.toString(0)); } } /** * Connect to a server. */ void RSync::connectToServer() { m_pTcpClient->abort(); m_pTcpClient->connectToHost(m_serverAddress, m_portNo); } /** * Called when a new client connects to the server. Send all states to synchronize. */ void RSync::acceptNewClient() { QDomDocument doc; m_pModel->storeSettings(doc); QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_4_5); out << (quint32)0; out << (int)RSYNC_COMMAND_INIT; out << doc.toString(0); out.device()->seek(0); out << (quint32)(block.size() - sizeof(quint32)); QTcpSocket* pClientConnection = m_pTcpServer->nextPendingConnection(); // connect(pClientConnection, SIGNAL(disconnected()), // pClientConnection, SLOT(deleteLater())); m_pClientConnections.push_back(pClientConnection); m_pModel->getGUI()->showStatusBarMessage(QString("%1 connected").arg(pClientConnection->peerAddress().toString()), 2000); pClientConnection->write(block); pClientConnection->flush(); // pClientConnection->disconnectFromHost(); // Inform Clients about acutal loaded Scene/Model/Screen m_pModel->getScreen()->notifyRedraw(); } /** * Called when a new synchronize message is received. */ void RSync::recvSettings() { QDataStream in(m_pTcpClient); in.setVersion(QDataStream::Qt_4_5); while (m_pTcpClient->bytesAvailable() > 0) { static quint32 blockSize = 0; if (blockSize == 0) { if (m_pTcpClient->bytesAvailable() < (int)sizeof(quint32)) return; in >> blockSize; } if (m_pTcpClient->bytesAvailable() < blockSize) return; int command; in >> command; QString data; in >> data; blockSize = 0; switch (command) { case RSYNC_COMMAND_INIT: { m_lastErrorString = ""; m_pModel->getGUI()->appendClientLog(tr("Connected to server : %1:%2.").arg(m_serverAddress).arg(m_portNo)); QDomDocument doc; doc.setContent(data); m_pModel->restoreSettings(doc); break; } case RSYNC_COMMAND_VIEWPOINT: { m_pModel->getScene()->setCameraViewpoint(data); break; } case RSYNC_COMMAND_EXPORT: { QDomDocument doc; doc.setContent(data); m_pModel->getExporter()->initFromDOMElement(doc.firstChildElement("Exporter")); break; } case RSYNC_COMMAND_MODEL: { QDomDocument doc; doc.setContent(data); m_pModel->initFromDOMElement(doc.firstChildElement("Model")); break; } case RSYNC_COMMAND_SCENE: { QDomDocument doc; doc.setContent(data); m_pModel->getScene()->initFromDOMElement(doc.firstChildElement("Scene")); break; } case RSYNC_COMMAND_SCREEN: { QDomDocument doc; doc.setContent(data); m_pModel->getScreen()->initFromDOMElement(doc.firstChildElement("Screen")); break; } case RSYNC_COMMAND_CHANNEL: { QDomDocument doc; doc.setContent(data); QDomElement channel = doc.firstChildElement("Channel"); QString channelName = channel.attribute("name"); for (unsigned int i=0; igetNumChannels(); ++i) { if (channelName == m_pModel->getChannel(i)->getName()) m_pModel->getChannel(i)->initFromDOMElement(channel, APPLICATION_VERSION); } break; } case RSYNC_COMMAND_GUI: { QDomDocument doc; doc.setContent(data); m_pModel->getGUI()->initFromDOMElement(doc.firstChildElement("GUI")); break; } case RSYNC_COMMAND_RELOAD: { QDomDocument doc; doc.setContent(data); m_pModel->restoreSettings(doc); break; } case RSYNC_COMMAND_EXPORTFILE: { m_pModel->getExporter()->exportDataset(data); break; } case RSYNC_COMMAND_DISCONNECT: { m_pModel->removeAllChannels(); m_lastErrorString = ""; m_pModel->getGUI()->appendClientLog(tr("Connection shutdowned.")); connectToServer(); break; } case RSYNC_COMMAND_END: { qApp->closeAllWindows(); } } } } /** * Called when a network error is occured. */ void RSync::displayNetworkError(QAbstractSocket::SocketError socketError) { /* switch (socketError) { case QAbstractSocket::RemoteHostClosedError: break; case QAbstractSocket::HostNotFoundError: QMessageBox::information(NULL, APPLICATION_NAME, tr("The host was not found. Please check the " "host name and port settings.")); break; case QAbstractSocket::ConnectionRefusedError: QMessageBox::information(NULL, APPLICATION_NAME, tr("The connection was refused by the peer. " "Make sure the Multichannnel Projection Designer server is running, " "and check that the host name and port settings are correct.")); break; default: QMessageBox::information(NULL, APPLICATION_NAME, tr("The following error occurred: %1.") .arg(m_pTcpClient->errorString())); } */ if (socketError != QAbstractSocket::UnknownSocketError && m_lastErrorString != m_pTcpClient->errorString()) { m_pModel->removeAllChannels(); m_lastErrorString = m_pTcpClient->errorString(); m_pModel->getGUI()->appendClientLog(m_lastErrorString); m_pModel->getGUI()->appendClientLog(tr("Try connecting again...")); } QTimer* pTimer = new QTimer(this); connect(pTimer, SIGNAL(timeout()), this, SLOT(connectToServer())); pTimer->setSingleShot(true); pTimer->start(1000); } /** * Set sync message blocking. * * @param bBlock True to block sending sync data to clients. */ void RSync::setBlockSync(bool bBlock) { m_bBlockSync = bBlock; } /** * Check whether sync message is blocked or not. * * @param bBlock True to block sending sync data to clients. */ bool RSync::getBlockSync() const { return m_bBlockSync; }