1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "codaruncontrol.h"
35 #include "s60deployconfiguration.h"
36 #include "s60devicerunconfiguration.h"
38 #include "codadevice.h"
39 #include "codamessage.h"
41 #include "qt4buildconfiguration.h"
42 #include "qt4symbiantarget.h"
43 #include "qt4target.h"
44 #include "qtoutputformatter.h"
45 #include "symbiandevicemanager.h"
47 #include <coreplugin/icore.h>
48 #include <utils/qtcassert.h>
50 #include <symbianutils/symbiandevicemanager.h>
52 #include <QtCore/QDir>
53 #include <QtCore/QFileInfo>
54 #include <QtCore/QScopedPointer>
55 #include <QtCore/QTimer>
57 #include <QtGui/QMessageBox>
58 #include <QtGui/QMainWindow>
60 #include <QtNetwork/QTcpSocket>
62 using namespace ProjectExplorer;
63 using namespace Qt4ProjectManager;
64 using namespace Qt4ProjectManager::Internal;
69 CodaRunControl::CodaRunControl(RunConfiguration *runConfiguration, const QString &mode) :
70 S60RunControlBase(runConfiguration, mode),
74 const S60DeviceRunConfiguration *s60runConfig = qobject_cast<S60DeviceRunConfiguration *>(runConfiguration);
75 QTC_ASSERT(s60runConfig, return);
76 const S60DeployConfiguration *activeDeployConf = qobject_cast<S60DeployConfiguration *>(s60runConfig->qt4Target()->activeDeployConfiguration());
77 QTC_ASSERT(activeDeployConf, return);
79 S60DeployConfiguration::CommunicationChannel channel = activeDeployConf->communicationChannel();
80 if (channel == S60DeployConfiguration::CommunicationCodaTcpConnection) {
81 m_address = activeDeployConf->deviceAddress();
82 m_port = activeDeployConf->devicePort().toInt();
83 } else if (channel == S60DeployConfiguration::CommunicationCodaSerialConnection) {
84 m_serialPort = activeDeployConf->serialPortName();
86 QTC_ASSERT(false, return);
90 CodaRunControl::~CodaRunControl()
94 bool CodaRunControl::doStart()
96 if (m_address.isEmpty() && m_serialPort.isEmpty()) {
98 QString msg = tr("No device is connected. Please connect a device and try again.");
99 appendMessage(msg, NormalMessageFormat);
102 appendMessage(tr("Executable file: %1").arg(msgListFile(executableFileName())),
103 NormalMessageFormat);
107 bool CodaRunControl::isRunning() const
109 return m_state >= StateConnecting;
112 bool CodaRunControl::setupLauncher()
114 QTC_ASSERT(!m_codaDevice, return false);
116 if (m_serialPort.length()) {
117 // We get the port from SymbianDeviceManager
118 appendMessage(tr("Connecting to '%1'...").arg(m_serialPort), NormalMessageFormat);
119 m_codaDevice = SymbianUtils::SymbianDeviceManager::instance()->getCodaDevice(m_serialPort);
120 if (m_codaDevice.isNull()) {
121 appendMessage(tr("Unable to create CODA connection. Please try again."), ErrorMessageFormat);
124 if (!m_codaDevice->device()->isOpen()) {
125 appendMessage(tr("Could not open serial device: %1").arg(m_codaDevice->device()->errorString()), ErrorMessageFormat);
128 connect(SymbianUtils::SymbianDeviceManager::instance(), SIGNAL(deviceRemoved(const SymbianUtils::SymbianDevice)),
129 this, SLOT(deviceRemoved(SymbianUtils::SymbianDevice)));
130 connect(m_codaDevice.data(), SIGNAL(error(QString)), this, SLOT(slotError(QString)));
131 connect(m_codaDevice.data(), SIGNAL(logMessage(QString)), this, SLOT(slotTrkLogMessage(QString)));
132 connect(m_codaDevice.data(), SIGNAL(tcfEvent(Coda::CodaEvent)), this, SLOT(slotCodaEvent(Coda::CodaEvent)));
133 connect(m_codaDevice.data(), SIGNAL(serialPong(QString)), this, SLOT(slotSerialPong(QString)));
134 m_state = StateConnecting;
135 m_codaDevice->sendSerialPing(false);
137 // For TCP we don't use device manager, we just set it up directly
138 m_codaDevice = QSharedPointer<Coda::CodaDevice>(new Coda::CodaDevice, &QObject::deleteLater); // finishRunControl, which deletes m_codaDevice, can get called from within a coda callback, so need to use deleteLater
139 connect(m_codaDevice.data(), SIGNAL(error(QString)), this, SLOT(slotError(QString)));
140 connect(m_codaDevice.data(), SIGNAL(logMessage(QString)), this, SLOT(slotTrkLogMessage(QString)));
141 connect(m_codaDevice.data(), SIGNAL(tcfEvent(Coda::CodaEvent)), this, SLOT(slotCodaEvent(Coda::CodaEvent)));
143 const QSharedPointer<QTcpSocket> codaSocket(new QTcpSocket);
144 m_codaDevice->setDevice(codaSocket);
145 codaSocket->connectToHost(m_address, m_port);
146 m_state = StateConnecting;
147 appendMessage(tr("Connecting to %1:%2...").arg(m_address).arg(m_port), NormalMessageFormat);
149 QTimer::singleShot(5000, this, SLOT(checkForTimeout()));
151 m_codaDevice->setVerbose(debug);
156 void CodaRunControl::doStop()
160 case StateConnecting:
164 case StateProcessRunning:
165 QTC_ASSERT(!m_runningProcessId.isEmpty(), return);
166 m_codaDevice->sendRunControlTerminateCommand(CodaCallback(),
167 m_runningProcessId.toAscii());
172 void CodaRunControl::slotError(const QString &error)
174 appendMessage(tr("Error: %1").arg(error), ErrorMessageFormat);
178 void CodaRunControl::slotTrkLogMessage(const QString &log)
181 qDebug("CODA log: %s", qPrintable(log.size()>200?log.left(200).append(QLatin1String(" ...")): log));
184 void CodaRunControl::slotSerialPong(const QString &message)
187 qDebug() << "CODA serial pong:" << message;
191 void CodaRunControl::slotCodaEvent(const CodaEvent &event)
194 qDebug() << "CODA event:" << "Type:" << event.type() << "Message:" << event.toString();
196 switch (event.type()) {
197 case CodaEvent::LocatorHello:
200 case CodaEvent::RunControlContextRemoved:
201 handleContextRemoved(event);
203 case CodaEvent::RunControlContextAdded:
204 m_state = StateProcessRunning;
205 reportLaunchFinished();
206 handleContextAdded(event);
208 case CodaEvent::RunControlSuspended:
209 handleContextSuspended(event);
211 case CodaEvent::RunControlModuleLoadSuspended:
212 handleModuleLoadSuspended(event);
214 case CodaEvent::LoggingWriteEvent:
215 handleLogging(event);
219 qDebug() << "CODA event not handled" << event.type();
224 void CodaRunControl::initCommunication()
226 m_codaDevice->sendLoggingAddListenerCommand(CodaCallback(this, &CodaRunControl::handleAddListener));
229 void CodaRunControl::handleConnected()
231 if (m_state >= StateConnected)
233 m_state = StateConnected;
234 appendMessage(tr("Connected."), NormalMessageFormat);
235 setProgress(maxProgress()*0.80);
239 void CodaRunControl::handleContextRemoved(const CodaEvent &event)
241 const QVector<QByteArray> removedItems
242 = static_cast<const CodaRunControlContextRemovedEvent &>(event).ids();
243 if (!m_runningProcessId.isEmpty()
244 && removedItems.contains(m_runningProcessId.toAscii())) {
245 appendMessage(tr("Process has finished."), NormalMessageFormat);
250 void CodaRunControl::handleContextAdded(const CodaEvent &event)
252 typedef CodaRunControlContextAddedEvent TcfAddedEvent;
254 const TcfAddedEvent &me = static_cast<const TcfAddedEvent &>(event);
255 foreach (const RunControlContext &context, me.contexts()) {
256 if (context.parentId == "root") //is the created context a process
257 m_runningProcessId = QLatin1String(context.id);
261 void CodaRunControl::handleContextSuspended(const CodaEvent &event)
263 typedef CodaRunControlContextSuspendedEvent TcfSuspendEvent;
265 const TcfSuspendEvent &me = static_cast<const TcfSuspendEvent &>(event);
267 switch (me.reason()) {
268 case TcfSuspendEvent::Other:
269 case TcfSuspendEvent::Crash:
270 appendMessage(tr("Thread has crashed: %1").arg(QString::fromLatin1(me.message())), ErrorMessageFormat);
272 if (me.reason() == TcfSuspendEvent::Crash)
275 m_codaDevice->sendRunControlResumeCommand(CodaCallback(), me.id()); //TODO: Should I resume automatically
279 qDebug() << "Context suspend not handled:" << "Reason:" << me.reason() << "Message:" << me.message();
284 void CodaRunControl::handleModuleLoadSuspended(const CodaEvent &event)
286 // Debug mode start: Continue:
287 typedef CodaRunControlModuleLoadContextSuspendedEvent TcfModuleLoadSuspendedEvent;
289 const TcfModuleLoadSuspendedEvent &me = static_cast<const TcfModuleLoadSuspendedEvent &>(event);
290 if (me.info().requireResume)
291 m_codaDevice->sendRunControlResumeCommand(CodaCallback(), me.id());
294 void CodaRunControl::handleLogging(const CodaEvent &event)
296 const CodaLoggingWriteEvent &me = static_cast<const CodaLoggingWriteEvent &>(event);
297 appendMessage(me.message(), StdOutFormat);
300 void CodaRunControl::handleAddListener(const CodaCommandResult &result)
303 m_codaDevice->sendSymbianOsDataFindProcessesCommand(CodaCallback(this, &CodaRunControl::handleFindProcesses),
305 QByteArray::number(executableUid(), 16));
308 void CodaRunControl::handleFindProcesses(const CodaCommandResult &result)
310 if (result.values.size() && result.values.at(0).type() == JsonValue::Array && result.values.at(0).children().count()) {
311 //there are processes running. Cannot run mine
312 appendMessage(tr("The process is already running on the device. Please first close it."), ErrorMessageFormat);
315 setProgress(maxProgress()*0.90);
316 m_codaDevice->sendProcessStartCommand(CodaCallback(this, &CodaRunControl::handleCreateProcess),
319 commandLineArguments().split(' '),
322 appendMessage(tr("Launching: %1").arg(executableName()), NormalMessageFormat);
326 void CodaRunControl::handleCreateProcess(const CodaCommandResult &result)
328 const bool ok = result.type == CodaCommandResult::SuccessReply;
330 setProgress(maxProgress());
331 appendMessage(tr("Launched."), NormalMessageFormat);
333 appendMessage(tr("Launch failed: %1").arg(result.toString()), ErrorMessageFormat);
338 void CodaRunControl::finishRunControl()
340 m_runningProcessId.clear();
342 disconnect(m_codaDevice.data(), 0, this, 0);
343 SymbianUtils::SymbianDeviceManager::instance()->releaseCodaDevice(m_codaDevice);
345 m_state = StateUninit;
349 QMessageBox *CodaRunControl::createCodaWaitingMessageBox(QWidget *parent)
351 const QString title = tr("Waiting for CODA");
352 const QString text = tr("Qt Creator is waiting for the CODA application to connect.<br>"
353 "Please make sure the application is running on "
354 "your mobile phone and the right IP address and/or port are "
355 "configured in the project settings.");
356 QMessageBox *mb = new QMessageBox(QMessageBox::Information, title, text, QMessageBox::Cancel, parent);
360 void CodaRunControl::checkForTimeout()
362 if (m_state != StateConnecting)
365 QMessageBox *mb = createCodaWaitingMessageBox(Core::ICore::instance()->mainWindow());
366 connect(this, SIGNAL(finished()), mb, SLOT(close()));
367 connect(mb, SIGNAL(finished(int)), this, SLOT(cancelConnection()));
371 void CodaRunControl::cancelConnection()
373 if (m_state != StateConnecting)
377 appendMessage(tr("Canceled."), ErrorMessageFormat);
381 void CodaRunControl::deviceRemoved(const SymbianUtils::SymbianDevice &device)
383 if (m_codaDevice && device.portName() == m_serialPort) {
384 QString msg = tr("The device '%1' has been disconnected").arg(device.friendlyName());
385 appendMessage(msg, ErrorMessageFormat);