OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / qt4projectmanager / qt-s60 / codaruncontrol.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
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.
18 **
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.
22 **
23 ** Other Usage
24 **
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.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #include "codaruncontrol.h"
34
35 #include "s60deployconfiguration.h"
36 #include "s60devicerunconfiguration.h"
37
38 #include "codadevice.h"
39 #include "codamessage.h"
40
41 #include "qt4buildconfiguration.h"
42 #include "qt4symbiantarget.h"
43 #include "qt4target.h"
44 #include "qtoutputformatter.h"
45 #include "symbiandevicemanager.h"
46
47 #include <coreplugin/icore.h>
48 #include <utils/qtcassert.h>
49
50 #include <symbianutils/symbiandevicemanager.h>
51
52 #include <QtCore/QDir>
53 #include <QtCore/QFileInfo>
54 #include <QtCore/QScopedPointer>
55 #include <QtCore/QTimer>
56
57 #include <QtGui/QMessageBox>
58 #include <QtGui/QMainWindow>
59
60 #include <QtNetwork/QTcpSocket>
61
62 using namespace ProjectExplorer;
63 using namespace Qt4ProjectManager;
64 using namespace Qt4ProjectManager::Internal;
65 using namespace Coda;
66
67 enum { debug = 0 };
68
69 CodaRunControl::CodaRunControl(RunConfiguration *runConfiguration, const QString &mode) :
70     S60RunControlBase(runConfiguration, mode),
71     m_port(0),
72     m_state(StateUninit)
73 {
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);
78
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();
85     } else {
86         QTC_ASSERT(false, return);
87     }
88 }
89
90 CodaRunControl::~CodaRunControl()
91 {
92 }
93
94 bool CodaRunControl::doStart()
95 {
96     if (m_address.isEmpty() && m_serialPort.isEmpty()) {
97         cancelProgress();
98         QString msg = tr("No device is connected. Please connect a device and try again.");
99         appendMessage(msg, NormalMessageFormat);
100         return false;
101     }
102     appendMessage(tr("Executable file: %1").arg(msgListFile(executableFileName())),
103                   NormalMessageFormat);
104     return true;
105 }
106
107 bool CodaRunControl::isRunning() const
108 {
109     return m_state >= StateConnecting;
110 }
111
112 bool CodaRunControl::setupLauncher()
113 {
114     QTC_ASSERT(!m_codaDevice, return false);
115
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);
122             return false;
123         }
124         if (!m_codaDevice->device()->isOpen()) {
125             appendMessage(tr("Could not open serial device: %1").arg(m_codaDevice->device()->errorString()), ErrorMessageFormat);
126             return false;
127         }
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);
136     } else {
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)));
142
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);
148     }
149     QTimer::singleShot(5000, this, SLOT(checkForTimeout()));
150     if (debug)
151         m_codaDevice->setVerbose(debug);
152
153     return true;
154 }
155
156 void CodaRunControl::doStop()
157 {
158     switch (m_state) {
159     case StateUninit:
160     case StateConnecting:
161     case StateConnected:
162         finishRunControl();
163         break;
164     case StateProcessRunning:
165         QTC_ASSERT(!m_runningProcessId.isEmpty(), return);
166         m_codaDevice->sendRunControlTerminateCommand(CodaCallback(),
167                                                        m_runningProcessId.toAscii());
168         break;
169     }
170 }
171
172 void CodaRunControl::slotError(const QString &error)
173 {
174     appendMessage(tr("Error: %1").arg(error), ErrorMessageFormat);
175     finishRunControl();
176 }
177
178 void CodaRunControl::slotTrkLogMessage(const QString &log)
179 {
180     if (debug > 1)
181         qDebug("CODA log: %s", qPrintable(log.size()>200?log.left(200).append(QLatin1String(" ...")): log));
182 }
183
184 void CodaRunControl::slotSerialPong(const QString &message)
185 {
186     if (debug > 1)
187         qDebug() << "CODA serial pong:" << message;
188     handleConnected();
189 }
190
191 void CodaRunControl::slotCodaEvent(const CodaEvent &event)
192 {
193     if (debug)
194         qDebug() << "CODA event:" << "Type:" << event.type() << "Message:" << event.toString();
195
196     switch (event.type()) {
197     case CodaEvent::LocatorHello:
198         handleConnected();
199         break;
200     case CodaEvent::RunControlContextRemoved:
201         handleContextRemoved(event);
202         break;
203     case CodaEvent::RunControlContextAdded:
204         m_state = StateProcessRunning;
205         reportLaunchFinished();
206         handleContextAdded(event);
207         break;
208     case CodaEvent::RunControlSuspended:
209         handleContextSuspended(event);
210         break;
211     case CodaEvent::RunControlModuleLoadSuspended:
212         handleModuleLoadSuspended(event);
213         break;
214     case CodaEvent::LoggingWriteEvent:
215         handleLogging(event);
216         break;
217     default:
218         if (debug)
219             qDebug() << "CODA event not handled" << event.type();
220         break;
221     }
222 }
223
224 void CodaRunControl::initCommunication()
225 {
226     m_codaDevice->sendLoggingAddListenerCommand(CodaCallback(this, &CodaRunControl::handleAddListener));
227 }
228
229 void CodaRunControl::handleConnected()
230 {
231     if (m_state >= StateConnected)
232         return;
233     m_state = StateConnected;
234     appendMessage(tr("Connected."), NormalMessageFormat);
235     setProgress(maxProgress()*0.80);
236     initCommunication();
237 }
238
239 void CodaRunControl::handleContextRemoved(const CodaEvent &event)
240 {
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);
246         finishRunControl();
247     }
248 }
249
250 void CodaRunControl::handleContextAdded(const CodaEvent &event)
251 {
252     typedef CodaRunControlContextAddedEvent TcfAddedEvent;
253
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);
258     }
259 }
260
261 void CodaRunControl::handleContextSuspended(const CodaEvent &event)
262 {
263     typedef CodaRunControlContextSuspendedEvent TcfSuspendEvent;
264
265     const TcfSuspendEvent &me = static_cast<const TcfSuspendEvent &>(event);
266
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);
271
272         if (me.reason() == TcfSuspendEvent::Crash)
273             stop();
274         else
275             m_codaDevice->sendRunControlResumeCommand(CodaCallback(), me.id()); //TODO: Should I resume automatically
276         break;
277     default:
278         if (debug)
279             qDebug() << "Context suspend not handled:" << "Reason:" << me.reason() << "Message:" << me.message();
280         break;
281     }
282 }
283
284 void CodaRunControl::handleModuleLoadSuspended(const CodaEvent &event)
285 {
286     // Debug mode start: Continue:
287     typedef CodaRunControlModuleLoadContextSuspendedEvent TcfModuleLoadSuspendedEvent;
288
289     const TcfModuleLoadSuspendedEvent &me = static_cast<const TcfModuleLoadSuspendedEvent &>(event);
290     if (me.info().requireResume)
291         m_codaDevice->sendRunControlResumeCommand(CodaCallback(), me.id());
292 }
293
294 void CodaRunControl::handleLogging(const CodaEvent &event)
295 {
296     const CodaLoggingWriteEvent &me = static_cast<const CodaLoggingWriteEvent &>(event);
297     appendMessage(me.message(), StdOutFormat);
298 }
299
300 void CodaRunControl::handleAddListener(const CodaCommandResult &result)
301 {
302     Q_UNUSED(result)
303     m_codaDevice->sendSymbianOsDataFindProcessesCommand(CodaCallback(this, &CodaRunControl::handleFindProcesses),
304                                                           QByteArray(),
305                                                           QByteArray::number(executableUid(), 16));
306 }
307
308 void CodaRunControl::handleFindProcesses(const CodaCommandResult &result)
309 {
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);
313         finishRunControl();
314     } else {
315         setProgress(maxProgress()*0.90);
316         m_codaDevice->sendProcessStartCommand(CodaCallback(this, &CodaRunControl::handleCreateProcess),
317                                               executableName(),
318                                               executableUid(),
319                                               commandLineArguments().split(' '),
320                                               QString(),
321                                               true);
322         appendMessage(tr("Launching: %1").arg(executableName()), NormalMessageFormat);
323     }
324 }
325
326 void CodaRunControl::handleCreateProcess(const CodaCommandResult &result)
327 {
328     const bool ok = result.type == CodaCommandResult::SuccessReply;
329     if (ok) {
330         setProgress(maxProgress());
331         appendMessage(tr("Launched."), NormalMessageFormat);
332     } else {
333         appendMessage(tr("Launch failed: %1").arg(result.toString()), ErrorMessageFormat);
334         finishRunControl();
335     }
336 }
337
338 void CodaRunControl::finishRunControl()
339 {
340     m_runningProcessId.clear();
341     if (m_codaDevice) {
342         disconnect(m_codaDevice.data(), 0, this, 0);
343         SymbianUtils::SymbianDeviceManager::instance()->releaseCodaDevice(m_codaDevice);
344     }
345     m_state = StateUninit;
346     emit finished();
347 }
348
349 QMessageBox *CodaRunControl::createCodaWaitingMessageBox(QWidget *parent)
350 {
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);
357     return mb;
358 }
359
360 void CodaRunControl::checkForTimeout()
361 {
362     if (m_state != StateConnecting)
363         return;
364
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()));
368     mb->open();
369 }
370
371 void CodaRunControl::cancelConnection()
372 {
373     if (m_state != StateConnecting)
374         return;
375
376     stop();
377     appendMessage(tr("Canceled."), ErrorMessageFormat);
378     emit finished();
379 }
380
381 void CodaRunControl::deviceRemoved(const SymbianUtils::SymbianDevice &device)
382 {
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);
386         finishRunControl();
387     }
388 }