Symbian: Separating CODA's and TRK's RunControl

Reviewed-by: Tobias Hunger
This commit is contained in:
Pawel Polanski
2011-01-18 16:45:42 +01:00
parent 315fc34f3b
commit e48f520999
18 changed files with 1363 additions and 698 deletions

View File

@@ -43,13 +43,6 @@
#include "qtoutputformatter.h"
#include "qt4symbiantarget.h"
#include "tcftrkdevice.h"
#include "tcftrkmessage.h"
#include <symbianutils/bluetoothlistener_gui.h>
#include <symbianutils/launcher.h>
#include <symbianutils/symbiandevicemanager.h>
#include <utils/qtcassert.h>
#include <coreplugin/icore.h>
@@ -69,7 +62,6 @@
using namespace ProjectExplorer;
using namespace Qt4ProjectManager;
using namespace Qt4ProjectManager::Internal;
using namespace tcftrk;
namespace {
@@ -80,23 +72,8 @@ const char * const PRO_FILE_KEY("Qt4ProjectManager.S60DeviceRunConfiguration.Pro
const char * const COMMUNICATION_TYPE_KEY("Qt4ProjectManager.S60DeviceRunConfiguration.CommunicationType");
const char * const COMMAND_LINE_ARGUMENTS_KEY("Qt4ProjectManager.S60DeviceRunConfiguration.CommandLineArguments");
const int PROGRESS_MAX = 200;
enum { debug = 0 };
// Format information about a file
static inline QString msgListFile(const QString &f)
{
QString rc;
const QFileInfo fi(f);
QTextStream str(&rc);
if (fi.exists())
str << fi.size() << ' ' << fi.lastModified().toString(Qt::ISODate) << ' ' << QDir::toNativeSeparators(fi.absoluteFilePath());
else
str << "<non-existent> " << QDir::toNativeSeparators(fi.absoluteFilePath());
return rc;
}
QString pathFromId(const QString &id)
{
if (!id.startsWith(QLatin1String(S60_DEVICE_RC_PREFIX)))
@@ -109,20 +86,6 @@ QString pathToId(const QString &path)
return QString::fromLatin1(S60_DEVICE_RC_PREFIX) + path;
}
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;
}
} // anonymous namespace
// ======== S60DeviceRunConfiguration
@@ -516,519 +479,6 @@ RunConfiguration *S60DeviceRunConfigurationFactory::clone(Target *parent, RunCon
return new S60DeviceRunConfiguration(t, old);
}
// ======== S60DeviceRunControlBase
S60DeviceRunControl::S60DeviceRunControl(RunConfiguration *runConfiguration, QString mode) :
RunControl(runConfiguration, mode),
m_toolChain(ProjectExplorer::ToolChain_INVALID),
m_tcfTrkDevice(0),
m_launcher(0),
m_state(StateUninit)
{
// connect for automatically reporting the "finished deploy" state to the progress manager
connect(this, SIGNAL(finished()), this, SLOT(reportLaunchFinished()));
S60DeviceRunConfiguration *s60runConfig = qobject_cast<S60DeviceRunConfiguration *>(runConfiguration);
const Qt4BuildConfiguration *activeBuildConf = s60runConfig->qt4Target()->activeBuildConfiguration();
S60DeployConfiguration *activeDeployConf = qobject_cast<S60DeployConfiguration *>(s60runConfig->qt4Target()->activeDeployConfiguration());
QTC_ASSERT(s60runConfig, return);
m_toolChain = s60runConfig->toolChainType();
m_serialPortName = activeDeployConf->serialPortName();
m_serialPortFriendlyName = SymbianUtils::SymbianDeviceManager::instance()->friendlyNameForPort(m_serialPortName);
m_executableUid = s60runConfig->executableUid();
m_targetName = s60runConfig->targetName();
m_commandLineArguments = s60runConfig->commandLineArguments();
m_qtDir = activeBuildConf->qtVersion()->versionInfo().value("QT_INSTALL_DATA");
m_installationDrive = activeDeployConf->installationDrive();
if (const QtVersion *qtv = activeDeployConf->qtVersion())
m_qtBinPath = qtv->versionInfo().value(QLatin1String("QT_INSTALL_BINS"));
QTC_ASSERT(!m_qtBinPath.isEmpty(), return);
m_executableFileName = s60runConfig->localExecutableFileName();
m_runSmartInstaller = activeDeployConf->runSmartInstaller();
switch (activeDeployConf->communicationChannel()) {
case S60DeployConfiguration::CommunicationSerialConnection:
break;
case S60DeployConfiguration::CommunicationTcpConnection:
m_address = activeDeployConf->deviceAddress();
m_port = activeDeployConf->devicePort().toInt();
}
m_useOldTrk = activeDeployConf->communicationChannel() == S60DeployConfiguration::CommunicationSerialConnection;
if (debug)
qDebug() << "S60DeviceRunControl::CT" << m_targetName << ProjectExplorer::ToolChain::toolChainName(m_toolChain)
<< m_serialPortName << "Use old TRK" << m_useOldTrk;
}
S60DeviceRunControl::~S60DeviceRunControl()
{
if (m_tcfTrkDevice) {
m_tcfTrkDevice->deleteLater();
m_tcfTrkDevice = 0;
}
if (m_launcher) {
m_launcher->deleteLater();
m_launcher = 0;
}
}
void S60DeviceRunControl::start()
{
m_launchProgress = new QFutureInterface<void>;
Core::ICore::instance()->progressManager()->addTask(m_launchProgress->future(),
tr("Launching"),
QLatin1String("Symbian.Launch"));
m_launchProgress->setProgressRange(0, PROGRESS_MAX);
m_launchProgress->setProgressValue(0);
m_launchProgress->reportStarted();
emit started();
if (m_runSmartInstaller) { //Smart Installer does the running by itself
appendMessage(tr("Please finalize the installation on your device."), NormalMessageFormat);
emit finished();
return;
}
if (m_serialPortName.isEmpty() && m_address.isEmpty()) {
m_launchProgress->reportCanceled();
QString msg = tr("No device is connected. Please connect a device and try again.");
appendMessage(msg, NormalMessageFormat);
emit finished();
return;
}
appendMessage(tr("Executable file: %1").arg(msgListFile(m_executableFileName)),
NormalMessageFormat);
QString errorMessage;
QString settingsCategory;
QString settingsPage;
if (!checkConfiguration(&errorMessage, &settingsCategory, &settingsPage)) {
m_launchProgress->reportCanceled();
appendMessage(errorMessage, ErrorMessageFormat);
emit finished();
Core::ICore::instance()->showWarningWithOptions(tr("Debugger for Symbian Platform"),
errorMessage, QString(),
settingsCategory, settingsPage);
return;
}
startLaunching();
}
RunControl::StopResult S60DeviceRunControl::stop()
{
if (m_useOldTrk) {
if (m_launcher)
m_launcher->terminate();
} else {
doStop();
}
return AsynchronousStop;
}
bool S60DeviceRunControl::isRunning() const
{
if (m_useOldTrk) {
return m_launcher && (m_launcher->state() == trk::Launcher::Connecting
|| m_launcher->state() == trk::Launcher::Connected
|| m_launcher->state() == trk::Launcher::WaitingForTrk);
} else {
return m_tcfTrkDevice && !m_tcfTrkDevice->device().isNull() && m_state >= StateConnecting;
}
}
bool S60DeviceRunControl::promptToStop(bool *) const
{
// We override the settings prompt
QTC_ASSERT(isRunning(), return true;)
const QString question = tr("<html><head/><body><center><i>%1</i> is still running on the device.<center/>"
"<center>Terminating it can leave the target in an inconsistent state.</center>"
"<center>Would you still like to terminate it?</center></body></html>").arg(displayName());
return showPromptToStopDialog(tr("Application Still Running"), question,
tr("Force Quit"), tr("Keep Running"));
}
void S60DeviceRunControl::startLaunching()
{
QString errorMessage;
if (setupLauncher(errorMessage)) {
if (m_launchProgress)
m_launchProgress->setProgressValue(PROGRESS_MAX/2);
} else {
if (!errorMessage.isEmpty())
appendMessage(errorMessage, ErrorMessageFormat);
stop();
emit finished();
}
}
bool S60DeviceRunControl::setupLauncher(QString &errorMessage)
{
connect(SymbianUtils::SymbianDeviceManager::instance(), SIGNAL(deviceRemoved(const SymbianUtils::SymbianDevice)),
this, SLOT(deviceRemoved(SymbianUtils::SymbianDevice)));
if(!m_useOldTrk) { //FIXME: Remove old TRK
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);
} else {
m_launcher = trk::Launcher::acquireFromDeviceManager(m_serialPortName, 0, &errorMessage);
if (!m_launcher)
return false;
connect(m_launcher, SIGNAL(finished()), this, SLOT(launcherFinished()));
connect(m_launcher, SIGNAL(canNotConnect(QString)), this, SLOT(printConnectFailed(QString)));
connect(m_launcher, SIGNAL(stateChanged(int)), this, SLOT(slotLauncherStateChanged(int)));
connect(m_launcher, SIGNAL(processStopped(uint,uint,uint,QString)),
this, SLOT(processStopped(uint,uint,uint,QString)));
if (!m_commandLineArguments.isEmpty())
m_launcher->setCommandLineArgs(m_commandLineArguments);
const QString runFileName = QString::fromLatin1("%1:\\sys\\bin\\%2.exe").arg(m_installationDrive).arg(m_targetName);
initLauncher(runFileName, m_launcher);
const trk::PromptStartCommunicationResult src =
S60RunConfigBluetoothStarter::startCommunication(m_launcher->trkDevice(),
0, &errorMessage);
if (src != trk::PromptStartCommunicationConnected)
return false;
if (!m_launcher->startServer(&errorMessage)) {
errorMessage = tr("Could not connect to phone on port '%1': %2\n"
"Check if the phone is connected and App TRK is running.").arg(m_serialPortName, errorMessage);
return false;
}
}
return true;
}
void S60DeviceRunControl::doStop()
{
if (!m_tcfTrkDevice) {
finishRunControl();
return;
}
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 S60DeviceRunControl::slotError(const QString &error)
{
appendMessage(tr("Error: %1").arg(error), ErrorMessageFormat);
finishRunControl();
}
void S60DeviceRunControl::slotTrkLogMessage(const QString &log)
{
if (debug) {
qDebug("CODA log: %s", qPrintable(log.size()>200?log.left(200).append(QLatin1String(" ...")): log));
}
}
void S60DeviceRunControl::slotSerialPong(const QString &message)
{
if (debug)
qDebug() << "CODA serial pong:" << message;
}
void S60DeviceRunControl::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);
if (m_launchProgress)
m_launchProgress->setProgressValue(PROGRESS_MAX*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() << __FUNCTION__ << "Event not handled" << event.type();
break;
}
}
void S60DeviceRunControl::initCommunication()
{
m_tcfTrkDevice->sendLoggingAddListenerCommand(TcfTrkCallback(this, &S60DeviceRunControl::handleAddListener));
}
void S60DeviceRunControl::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 S60DeviceRunControl::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 S60DeviceRunControl::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 S60DeviceRunControl::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 S60DeviceRunControl::handleLogging(const TcfTrkEvent &event)
{
const TcfTrkLoggingWriteEvent &me = static_cast<const TcfTrkLoggingWriteEvent &>(event);
appendMessage(me.message(), StdOutFormat);
}
void S60DeviceRunControl::handleAddListener(const TcfTrkCommandResult &result)
{
if (debug)
qDebug() << __FUNCTION__ <<"Add log listener" << result.toString();
m_tcfTrkDevice->sendSymbianOsDataGetThreadsCommand(TcfTrkCallback(this, &S60DeviceRunControl::handleGetThreads));
}
void S60DeviceRunControl::handleGetThreads(const TcfTrkCommandResult &result)
{
if (isProcessRunning(result, m_targetName)) {
appendMessage(tr("The process is already running on the device. Please first close it."), ErrorMessageFormat);
finishRunControl();
} else {
if (m_launchProgress)
m_launchProgress->setProgressValue(PROGRESS_MAX*0.90);
const QString runFileName = QString::fromLatin1("%1.exe").arg(m_targetName);
m_tcfTrkDevice->sendProcessStartCommand(TcfTrkCallback(this, &S60DeviceRunControl::handleCreateProcess),
runFileName, m_executableUid, m_commandLineArguments.split(" "), QString(), true);
appendMessage(tr("Launching: %1").arg(runFileName), NormalMessageFormat);
}
}
void S60DeviceRunControl::handleCreateProcess(const TcfTrkCommandResult &result)
{
const bool ok = result.type == TcfTrkCommandResult::SuccessReply;
if (ok) {
if (m_launchProgress)
m_launchProgress->setProgressValue(PROGRESS_MAX);
appendMessage(tr("Launched."), NormalMessageFormat);
} else {
appendMessage(tr("Launch failed: %1").arg(result.toString()), ErrorMessageFormat);
finishRunControl();
}
}
void S60DeviceRunControl::finishRunControl()
{
m_runningProcessId.clear();
if (m_tcfTrkDevice)
m_tcfTrkDevice->deleteLater();
m_tcfTrkDevice = 0;
m_state = StateUninit;
handleRunFinished();
}
//////// Launcher code - to be removed
void S60DeviceRunControl::printConnectFailed(const QString &errorMessage)
{
appendMessage(tr("Could not connect to App TRK on device: %1. Restarting App TRK might help.").arg(errorMessage),
ErrorMessageFormat);
}
void S60DeviceRunControl::launcherFinished()
{
trk::Launcher::releaseToDeviceManager(m_launcher);
m_launcher->deleteLater();
m_launcher = 0;
handleRunFinished();
}
void S60DeviceRunControl::reportLaunchFinished()
{
if (m_launchProgress) {
m_launchProgress->reportFinished();
delete m_launchProgress;
m_launchProgress = 0;
}
}
void S60DeviceRunControl::processStopped(uint pc, uint pid, uint tid, const QString &reason)
{
appendMessage(trk::Launcher::msgStopped(pid, tid, pc, reason), StdOutFormat);
m_launcher->terminate();
}
QMessageBox *S60DeviceRunControl::createTrkWaitingMessageBox(const QString &port, QWidget *parent)
{
const QString title = tr("Waiting for App TRK");
const QString text = tr("Qt Creator is waiting for the TRK application to connect.<br>"
"Please make sure the application is running on "
"your mobile phone and the right port is "
"configured in the project settings.").arg(port);
QMessageBox *rc = new QMessageBox(QMessageBox::Information, title, text,
QMessageBox::Cancel, parent);
return rc;
}
void S60DeviceRunControl::slotLauncherStateChanged(int s)
{
if (s == trk::Launcher::WaitingForTrk) {
QMessageBox *mb = S60DeviceRunControl::createTrkWaitingMessageBox(m_launcher->trkServerName(),
Core::ICore::instance()->mainWindow());
connect(m_launcher, SIGNAL(stateChanged(int)), mb, SLOT(close()));
connect(mb, SIGNAL(finished(int)), this, SLOT(slotWaitingForTrkClosed()));
mb->open();
}
}
void S60DeviceRunControl::slotWaitingForTrkClosed()
{
if (m_launcher && m_launcher->state() == trk::Launcher::WaitingForTrk) {
stop();
appendMessage(tr("Canceled."), ErrorMessageFormat);
emit finished();
}
}
void S60DeviceRunControl::printApplicationOutput(const QString &output)
{
printApplicationOutput(output, false);
}
void S60DeviceRunControl::printApplicationOutput(const QString &output, bool onStdErr)
{
appendMessage(output, onStdErr ? StdErrFormat : StdOutFormat);
}
void S60DeviceRunControl::deviceRemoved(const SymbianUtils::SymbianDevice &d)
{
if (m_launcher && d.portName() == m_serialPortName) {
trk::Launcher::releaseToDeviceManager(m_launcher);
m_launcher->deleteLater();
m_launcher = 0;
QString msg = tr("The device '%1' has been disconnected").arg(d.friendlyName());
appendMessage(msg, ErrorMessageFormat);
emit finished();
}
}
void S60DeviceRunControl::initLauncher(const QString &executable, trk::Launcher *launcher)
{
connect(launcher, SIGNAL(startingApplication()), this, SLOT(printStartingNotice()));
connect(launcher, SIGNAL(applicationRunning(uint)), this, SLOT(applicationRunNotice(uint)));
connect(launcher, SIGNAL(canNotRun(QString)), this, SLOT(applicationRunFailedNotice(QString)));
connect(launcher, SIGNAL(applicationOutputReceived(QString)), this, SLOT(printApplicationOutput(QString)));
launcher->addStartupActions(trk::Launcher::ActionRun);
launcher->setFileName(executable);
}
void S60DeviceRunControl::printStartingNotice()
{
appendMessage(tr("Starting application..."), NormalMessageFormat);
}
void S60DeviceRunControl::applicationRunNotice(uint pid)
{
appendMessage(tr("Application running with pid %1.").arg(pid), NormalMessageFormat);
if (m_launchProgress)
m_launchProgress->setProgressValue(PROGRESS_MAX);
}
void S60DeviceRunControl::applicationRunFailedNotice(const QString &errorMessage)
{
appendMessage(tr("Could not start application: %1").arg(errorMessage), NormalMessageFormat);
}
// End of Launcher code - to be removed
bool S60DeviceRunControl::checkConfiguration(QString * /* errorMessage */,
QString * /* settingsCategory */,
QString * /* settingsPage */) const
{
return true;
}
void S60DeviceRunControl::handleRunFinished()
{
emit finished();
appendMessage(tr("Finished."), NormalMessageFormat);
}
// ======== S60DeviceDebugRunControl
// Return symbol file which should co-exist with the executable.
@@ -1073,7 +523,7 @@ static Debugger::DebuggerStartParameters s60DebuggerStartParams(const S60DeviceR
sp.serverPort = activeDeployConf->devicePort().toInt();
//FIXME: there should be only one... trkAdapter
sp.communicationChannel = activeDeployConf->communicationChannel() == S60DeployConfiguration::CommunicationSerialConnection?
sp.communicationChannel = activeDeployConf->communicationChannel() == S60DeployConfiguration::CommunicationTrkSerialConnection?
Debugger::DebuggerStartParameters::CommunicationChannelUsb:
Debugger::DebuggerStartParameters::CommunicationChannelTcpIp;
QTC_ASSERT(sp.executableUid, return sp);