Commit e48f5209 authored by Pawel Polanski's avatar Pawel Polanski
Browse files

Symbian: Separating CODA's and TRK's RunControl

Reviewed-by: Tobias Hunger
parent 315fc34f
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** No Commercial Usage
**
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
#include "codaruncontrol.h"
#include "s60deployconfiguration.h"
#include "s60devicerunconfiguration.h"
#include "tcftrkdevice.h"
#include "tcftrkmessage.h"
#include "qt4buildconfiguration.h"
#include "qt4symbiantarget.h"
#include "qt4target.h"
#include "qtoutputformatter.h"
#include <coreplugin/icore.h>
#include <utils/qtcassert.h>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QScopedPointer>
#include <QtGui/QMessageBox>
#include <QtGui/QMainWindow>
#include <QtNetwork/QTcpSocket>
using namespace ProjectExplorer;
using namespace Qt4ProjectManager;
using namespace Qt4ProjectManager::Internal;
using namespace tcftrk;
enum { debug = 0 };
static inline bool isProcessRunning(const TcfTrkCommandResult &result, const QString &processName)
{
if (result.values.size() && result.values.at(0).type() == JsonValue::Array) {
foreach(const JsonValue &threadValue, result.values.at(0).children()) {
for (int i = threadValue.children().count()-1; i >= 0; --i) { //Usually our process will be near the end of the list
const JsonValue &value(threadValue.childAt(i));
if (value.hasName("p_name") && QString::fromLatin1(value.data()).startsWith(processName, Qt::CaseInsensitive))
return true;
}
}
}
return false;
}
CodaRunControl::CodaRunControl(RunConfiguration *runConfiguration, const QString &mode) :
S60RunControlBase(runConfiguration, mode),
m_tcfTrkDevice(0),
m_port(0),
m_state(StateUninit)
{
const S60DeviceRunConfiguration *s60runConfig = qobject_cast<S60DeviceRunConfiguration *>(runConfiguration);
QTC_ASSERT(s60runConfig, return);
const S60DeployConfiguration *activeDeployConf = qobject_cast<S60DeployConfiguration *>(s60runConfig->qt4Target()->activeDeployConfiguration());
QTC_ASSERT(activeDeployConf, return);
m_address = activeDeployConf->deviceAddress();
m_port = activeDeployConf->devicePort().toInt();
}
CodaRunControl::~CodaRunControl()
{
if (m_tcfTrkDevice)
m_tcfTrkDevice->deleteLater();
}
bool CodaRunControl::doStart()
{
if (m_address.isEmpty()) {
cancelProgress();
QString msg = tr("No device is connected. Please connect a device and try again.");
appendMessage(msg, NormalMessageFormat);
return false;
}
appendMessage(tr("Executable file: %1").arg(msgListFile(executableFileName())),
NormalMessageFormat);
return true;
}
bool CodaRunControl::isRunning() const
{
return m_state >= StateConnecting;
}
bool CodaRunControl::setupLauncher()
{
QTC_ASSERT(!m_tcfTrkDevice, return false);
m_tcfTrkDevice = new TcfTrkDevice;
if (debug)
m_tcfTrkDevice->setVerbose(1);
connect(m_tcfTrkDevice, SIGNAL(error(QString)), this, SLOT(slotError(QString)));
connect(m_tcfTrkDevice, SIGNAL(logMessage(QString)), this, SLOT(slotTrkLogMessage(QString)));
connect(m_tcfTrkDevice, SIGNAL(tcfEvent(tcftrk::TcfTrkEvent)), this, SLOT(slotTcftrkEvent(tcftrk::TcfTrkEvent)));
connect(m_tcfTrkDevice, SIGNAL(serialPong(QString)), this, SLOT(slotSerialPong(QString)));
const QSharedPointer<QTcpSocket> tcfTrkSocket(new QTcpSocket);
m_tcfTrkDevice->setDevice(tcfTrkSocket);
tcfTrkSocket->connectToHost(m_address, m_port);
m_state = StateConnecting;
appendMessage(tr("Connecting to %1:%2...").arg(m_address).arg(m_port), NormalMessageFormat);
QTimer::singleShot(4000, this, SLOT(checkForTimeout()));
return true;
}
void CodaRunControl::doStop()
{
switch (m_state) {
case StateUninit:
case StateConnecting:
case StateConnected:
finishRunControl();
break;
case StateProcessRunning:
QTC_ASSERT(!m_runningProcessId.isEmpty(), return);
m_tcfTrkDevice->sendRunControlTerminateCommand(TcfTrkCallback(),
m_runningProcessId.toAscii());
break;
}
}
void CodaRunControl::slotError(const QString &error)
{
appendMessage(tr("Error: %1").arg(error), ErrorMessageFormat);
finishRunControl();
}
void CodaRunControl::slotTrkLogMessage(const QString &log)
{
if (debug) {
qDebug("CODA log: %s", qPrintable(log.size()>200?log.left(200).append(QLatin1String(" ...")): log));
}
}
void CodaRunControl::slotSerialPong(const QString &message)
{
if (debug)
qDebug() << "CODA serial pong:" << message;
}
void CodaRunControl::slotTcftrkEvent(const TcfTrkEvent &event)
{
if (debug)
qDebug() << "CODA event:" << "Type:" << event.type() << "Message:" << event.toString();
switch (event.type()) {
case TcfTrkEvent::LocatorHello: { // Commands accepted now
m_state = StateConnected;
appendMessage(tr("Connected."), NormalMessageFormat);
setProgress(maxProgress()*0.80);
initCommunication();
}
break;
case TcfTrkEvent::RunControlContextRemoved:
handleContextRemoved(event);
break;
case TcfTrkEvent::RunControlContextAdded:
m_state = StateProcessRunning;
reportLaunchFinished();
handleContextAdded(event);
break;
case TcfTrkEvent::RunControlSuspended:
handleContextSuspended(event);
break;
case TcfTrkEvent::RunControlModuleLoadSuspended:
handleModuleLoadSuspended(event);
break;
case TcfTrkEvent::LoggingWriteEvent:
handleLogging(event);
break;
default:
if (debug)
qDebug() << "CODA event not handled" << event.type();
break;
}
}
void CodaRunControl::initCommunication()
{
m_tcfTrkDevice->sendLoggingAddListenerCommand(TcfTrkCallback(this, &CodaRunControl::handleAddListener));
}
void CodaRunControl::handleContextRemoved(const TcfTrkEvent &event)
{
const QVector<QByteArray> removedItems
= static_cast<const TcfTrkRunControlContextRemovedEvent &>(event).ids();
if (!m_runningProcessId.isEmpty()
&& removedItems.contains(m_runningProcessId.toAscii())) {
appendMessage(tr("Process has finished."), NormalMessageFormat);
finishRunControl();
}
}
void CodaRunControl::handleContextAdded(const TcfTrkEvent &event)
{
typedef TcfTrkRunControlContextAddedEvent TcfAddedEvent;
const TcfAddedEvent &me = static_cast<const TcfAddedEvent &>(event);
foreach (const RunControlContext &context, me.contexts()) {
if (context.parentId == "root") //is the created context a process
m_runningProcessId = QLatin1String(context.id);
}
}
void CodaRunControl::handleContextSuspended(const TcfTrkEvent &event)
{
typedef TcfTrkRunControlContextSuspendedEvent TcfSuspendEvent;
const TcfSuspendEvent &me = static_cast<const TcfSuspendEvent &>(event);
switch (me.reason()) {
case TcfSuspendEvent::Crash:
appendMessage(tr("Process has crashed: %1").arg(QString::fromLatin1(me.message())), ErrorMessageFormat);
m_tcfTrkDevice->sendRunControlResumeCommand(TcfTrkCallback(), me.id()); //TODO: Should I resume automaticly
break;
default:
if (debug)
qDebug() << "Context suspend not handled:" << "Reason:" << me.reason() << "Message:" << me.message();
break;
}
}
void CodaRunControl::handleModuleLoadSuspended(const TcfTrkEvent &event)
{
// Debug mode start: Continue:
typedef TcfTrkRunControlModuleLoadContextSuspendedEvent TcfModuleLoadSuspendedEvent;
const TcfModuleLoadSuspendedEvent &me = static_cast<const TcfModuleLoadSuspendedEvent &>(event);
if (me.info().requireResume)
m_tcfTrkDevice->sendRunControlResumeCommand(TcfTrkCallback(), me.id());
}
void CodaRunControl::handleLogging(const TcfTrkEvent &event)
{
const TcfTrkLoggingWriteEvent &me = static_cast<const TcfTrkLoggingWriteEvent &>(event);
appendMessage(me.message(), StdOutFormat);
}
void CodaRunControl::handleAddListener(const TcfTrkCommandResult &result)
{
Q_UNUSED(result)
m_tcfTrkDevice->sendSymbianOsDataGetThreadsCommand(TcfTrkCallback(this, &CodaRunControl::handleGetThreads));
}
void CodaRunControl::handleGetThreads(const TcfTrkCommandResult &result)
{
if (isProcessRunning(result, targetName())) {
appendMessage(tr("The process is already running on the device. Please first close it."), ErrorMessageFormat);
finishRunControl();
} else {
setProgress(maxProgress()*0.90);
const QString runFileName = QString::fromLatin1("%1.exe").arg(targetName());
m_tcfTrkDevice->sendProcessStartCommand(TcfTrkCallback(this, &CodaRunControl::handleCreateProcess),
runFileName, executableUid(), commandLineArguments().split(" "), QString(), true);
appendMessage(tr("Launching: %1").arg(runFileName), NormalMessageFormat);
}
}
void CodaRunControl::handleCreateProcess(const TcfTrkCommandResult &result)
{
const bool ok = result.type == TcfTrkCommandResult::SuccessReply;
if (ok) {
setProgress(maxProgress());
appendMessage(tr("Launched."), NormalMessageFormat);
} else {
appendMessage(tr("Launch failed: %1").arg(result.toString()), ErrorMessageFormat);
finishRunControl();
}
}
void CodaRunControl::finishRunControl()
{
m_runningProcessId.clear();
if (m_tcfTrkDevice)
m_tcfTrkDevice->deleteLater();
m_tcfTrkDevice = 0;
m_state = StateUninit;
emit finished();
}
QMessageBox *CodaRunControl::createCodaWaitingMessageBox(QWidget *parent)
{
const QString title = tr("Waiting for CODA");
const QString text = tr("Qt Creator is waiting for the CODA application to connect. "
"Please make sure the application is running on "
"your mobile phone and the right IP address and port are "
"configured in the project settings.");
QMessageBox *mb = new QMessageBox(QMessageBox::Information, title, text, QMessageBox::Cancel, parent);
return mb;
}
void CodaRunControl::checkForTimeout()
{
if (m_state >= StateConnected)
return;
QMessageBox *mb = createCodaWaitingMessageBox(Core::ICore::instance()->mainWindow());
connect(this, SIGNAL(finished()), mb, SLOT(close()));
connect(mb, SIGNAL(finished(int)), this, SLOT(cancelConnection()));
mb->open();
}
void CodaRunControl::cancelConnection()
{
if (m_state >= StateConnected)
return;
stop();
appendMessage(tr("Canceled."), ErrorMessageFormat);
emit finished();
}
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** No Commercial Usage
**
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
#ifndef CODARUNCONTROL_H
#define CODARUNCONTROL_H
#include "s60runcontrolbase.h"
QT_BEGIN_NAMESPACE
class QMessageBox;
class QWidget;
QT_END_NAMESPACE
namespace tcftrk {
struct TcfTrkCommandResult;
class TcfTrkDevice;
class TcfTrkEvent;
}
namespace Qt4ProjectManager {
namespace Internal {
// CodaRunControl configures tcftrk to run the application
class CodaRunControl : public S60RunControlBase
{
Q_OBJECT
public:
CodaRunControl(ProjectExplorer::RunConfiguration *runConfiguration, const QString &mode);
~CodaRunControl();
virtual bool isRunning() const;
static QMessageBox *createCodaWaitingMessageBox(QWidget *parent = 0);
protected:
virtual bool doStart();
virtual void doStop();
virtual bool setupLauncher();
protected slots:
void finishRunControl();
void checkForTimeout();
void cancelConnection();
private slots:
void slotError(const QString &error);
void slotTrkLogMessage(const QString &log);
void slotTcftrkEvent(const tcftrk::TcfTrkEvent &event);
void slotSerialPong(const QString &message);
private:
void initCommunication();
void handleModuleLoadSuspended(const tcftrk::TcfTrkEvent &event);
void handleContextSuspended(const tcftrk::TcfTrkEvent &event);
void handleContextAdded(const tcftrk::TcfTrkEvent &event);
void handleContextRemoved(const tcftrk::TcfTrkEvent &event);
void handleLogging(const tcftrk::TcfTrkEvent &event);
private:
void handleCreateProcess(const tcftrk::TcfTrkCommandResult &result);
void handleAddListener(const tcftrk::TcfTrkCommandResult &result);
void handleGetThreads(const tcftrk::TcfTrkCommandResult &result);
private:
enum State {
StateUninit,
StateConnecting,
StateConnected,
StateProcessRunning
};
tcftrk::TcfTrkDevice *m_tcfTrkDevice;
QString m_address;
unsigned short m_port;
QString m_runningProcessId;
State m_state;
};
} // namespace Internal
} // namespace Qt4ProjectManager
#endif // CODARUNCONTROL_H
......@@ -27,7 +27,11 @@ SOURCES += $$PWD/s60devices.cpp \
$$PWD/s60symbiancertificate.cpp \
$$PWD/s60certificatedetailsdialog.cpp \
$$PWD/qt4symbiantargetfactory.cpp \
$$PWD/qt4symbiantarget.cpp
$$PWD/qt4symbiantarget.cpp \
$$PWD/s60runcontrolfactory.cpp \
$$PWD/codaruncontrol.cpp \
$$PWD/trkruncontrol.cpp \
$$PWD/s60runcontrolbase.cpp
HEADERS += $$PWD/s60devices.h \
$$PWD/s60devicespreferencepane.h \
......@@ -55,7 +59,11 @@ HEADERS += $$PWD/s60devices.h \
$$PWD/s60symbiancertificate.h \
$$PWD/s60certificatedetailsdialog.h \
$$PWD/qt4symbiantargetfactory.h \
$$PWD/qt4symbiantarget.h
$$PWD/qt4symbiantarget.h \
$$PWD/s60runcontrolfactory.h \
$$PWD/codaruncontrol.h \
$$PWD/trkruncontrol.h \
$$PWD/s60runcontrolbase.h
FORMS += $$PWD/s60devicespreferencepane.ui \
$$PWD/s60createpackagestep.ui \
......
......@@ -163,7 +163,7 @@ bool Qt4SymbianTarget::isSymbianConnectionAvailable(QString &tooltipText)
if (!s60DeployConf)
return false;
switch (s60DeployConf->communicationChannel()) {
case S60DeployConfiguration::CommunicationSerialConnection: {
case S60DeployConfiguration::CommunicationTrkSerialConnection: {
const SymbianUtils::SymbianDeviceManager *sdm = SymbianUtils::SymbianDeviceManager::instance();
const int deviceIndex = sdm->findByPortName(s60DeployConf->serialPortName());
if (deviceIndex == -1) {
......@@ -179,7 +179,7 @@ bool Qt4SymbianTarget::isSymbianConnectionAvailable(QString &tooltipText)
}
}
break;
case S60DeployConfiguration::CommunicationTcpConnection: {
case S60DeployConfiguration::CommunicationCodaTcpConnection: {
if (!s60DeployConf->deviceAddress().isEmpty() && !s60DeployConf->devicePort().isEmpty()) {
tooltipText = tr("<b>IP address:</b> %1:%2").arg(s60DeployConf->deviceAddress(), s60DeployConf->devicePort());
return true;
......
......@@ -93,7 +93,7 @@ S60DeployConfiguration::S60DeployConfiguration(Target *parent) :
m_installationDrive('C'),
m_silentInstall(true),
m_devicePort(QLatin1String(DEFAULT_TCF_TRK_TCP_PORT)),
m_communicationChannel(CommunicationSerialConnection)
m_communicationChannel(CommunicationTrkSerialConnection)
{
ctor();
}
......@@ -338,7 +338,7 @@ bool S60DeployConfiguration::fromMap(const QVariantMap &map)
m_deviceAddress = map.value(QLatin1String(DEVICE_ADDRESS_KEY)).toString();
m_devicePort = map.value(QLatin1String(DEVICE_PORT_KEY), QString(QLatin1String(DEFAULT_TCF_TRK_TCP_PORT))).toString();
m_communicationChannel = static_cast<CommunicationChannel>(map.value(QLatin1String(COMMUNICATION_CHANNEL_KEY),
QVariant(CommunicationSerialConnection)).toInt());
QVariant(CommunicationTrkSerialConnection)).toInt());
setDefaultDisplayName(defaultDisplayName());
return true;
......
......@@ -58,8 +58,9 @@ class S60DeployConfiguration : public ProjectExplorer::DeployConfiguration
public:
enum CommunicationChannel {
CommunicationSerialConnection,
CommunicationTcpConnection
CommunicationTrkSerialConnection,
CommunicationCodaSerialConnection,
CommunicationCodaTcpConnection
};
explicit S60DeployConfiguration(ProjectExplorer::Target *parent);
......
......@@ -42,6 +42,8 @@
#include <symbianutils/bluetoothlistener.h>
#include <symbianutils/symbiandevicemanager.h>
#include "trkruncontrol.h"
#include <utils/detailswidget.h>
#include <utils/ipaddresslineedit.h>
#include <utils/qtcassert.h>
......@@ -200,12 +202,12 @@ QWidget *S60DeployConfigurationWidget::createCommunicationChannel()
communicationChannelFormLayout->setLayout(1, QFormLayout::FieldRole, wlanChannelLayout);
switch (m_deployConfiguration->communicationChannel()) {
case S60DeployConfiguration::CommunicationSerialConnection:
case S60DeployConfiguration::CommunicationTrkSerialConnection:
m_serialRadioButton->setChecked(true);
m_ipAddress->setDisabled(true);
m_serialPortsCombo->setDisabled(false);
break;
case S60DeployConfiguration::CommunicationTcpConnection:
case S60DeployConfiguration::CommunicationCodaTcpConnection:
m_wlanRadioButton->setChecked(true);
m_ipAddress->setDisabled(false);
m_serialPortsCombo->setDisabled(true);
......@@ -261,7 +263,7 @@ void S60DeployConfigurationWidget::updateSerialDevices()
const QString newPortName = device(newIndex).portName();
m_deployConfiguration->setSerialPortName(newPortName);
}
if (m_deployConfiguration->communicationChannel() != S60DeployConfiguration::CommunicationSerialConnection)
if (m_deployConfiguration->communicationChannel() != S60DeployConfiguration::CommunicationTrkSerialConnection)
m_deviceInfoButton->setEnabled(false);
}
......@@ -306,7 +308,7 @@ void S60DeployConfigurationWidget::updateCommunicationChannel()
if (m_serialRadioButton->isChecked()) {
m_ipAddress->setDisabled(true);
m_serialPortsCombo->setDisabled(false);
m_deployConfiguration->setCommunicationChannel(S60DeployConfiguration::CommunicationSerialConnection);
m_deployConfiguration->setCommunicationChannel(S60DeployConfiguration::CommunicationTrkSerialConnection);
updateSerialDevices();
} else if(m_wlanRadioButton->isChecked()) {
QMessageBox::information(this, tr("CODA required"),
......@@ -315,7 +317,7 @@ void S60DeployConfigurationWidget::updateCommunicationChannel()
m_ipAddress->setDisabled(false);
m_serialPortsCombo->setDisabled(true);
m_deviceInfoButton->setEnabled(false);
m_deployConfiguration->setCommunicationChannel(S60DeployConfiguration::CommunicationTcpConnection);
m_deployConfiguration->setCommunicationChannel(S60DeployConfiguration::CommunicationCodaTcpConnection);
}
}
......@@ -361,7 +363,7 @@ void S60DeployConfigurationWidget::slotLauncherStateChanged(int s)
switch (s) {
case trk::Launcher::WaitingForTrk: {
// Entered trk wait state..open message box