如何与子进程QProcess交互

How to interact with a child QProcess?

本文关键字:QProcess 交互 子进程      更新时间:2023-10-16

在我的程序中,我有一个子进程,当父进程需要它时,它与指定给它的串行端口交互。例如,父进程通过发送以下命令命令子进程在一定超时时间内从打开的端口读取一定数量的字节:read 100 1000。子进程启动并成功打开端口,我可以看到消息port openned successfully!,但从那里开始,它将不再读取父命令。

下面是子代码:

SerialPortHandler.h

#ifndef SERIALPORTHANDLER_H
#define SERIALPORTHANDLER_H
#include <QObject>
#include <QSocketNotifier>
#include <QTextStream>
#include <QSerialPort>
#include <QFile>
#include <QTimer>
#include <QDebug>
#include <QtCore>
enum CommandType { READ, WRITE, BAD, UNKNOWN };
class SerialPortHandler : public QObject
{
    Q_OBJECT
public:
    explicit SerialPortHandler(QString portname, QString baudrate, QObject *parent = 0);
signals:
public slots:
    void execmd();
    bool open(const QString& portname, const QString& baudrate);
    qint64 read(char * buff, const qint64 size, const qint32 timeout);
    QString convertToCaseInsensitiveRegExp(QString str);
    CommandType analyze(const QString& line);
    qint64 getNum(const QString &line, int index);
    void reply(char *buff);
private:
    QSocketNotifier *innotif;
    QSerialPort *sp;
    QTimer *timer;
};
#endif // SERIALPORTHANDLER_H

SerialPortHandler.cpp

#include "SerialPortHandler.h"
#include <unistd.h>
#include <limits>
SerialPortHandler::SerialPortHandler(QString portname, QString baudrate, QObject *parent) :
    QObject(parent)
{
    timer = new QTimer(this);
    sp = new QSerialPort(this);
    if(!open(portname, baudrate)) {
        qDebug() << sp->error() << sp->errorString();
        exit(sp->error());
    }
    innotif = new QSocketNotifier(STDIN_FILENO, QSocketNotifier::Read, this);
    connect(innotif, SIGNAL(activated(int)), this, SLOT(execmd()));
}
void SerialPortHandler::execmd()
{
    qDebug() << "command received. analyzing...";
//    qint64 nbr = -1, size = -1;
//    qint32 timeout = -1;
//    char * buff = 0;
//    QTextStream in(stdin);
//    QString ln = in.readAll();
//    switch (analyze(ln)) {
//    case READ:
//        size = getNum(ln, 1);
//        timeout = getNum(ln, 2);
//        if(size > -1 && timeout > -1)
//            nbr = read(buff, size, timeout);
//        if(nbr > -1)
//            reply(buff);
//        break;
//    default:
//        break;
//    }
}
bool SerialPortHandler::open(const QString &portname, const QString &baudrate)
{
    sp->setPortName(portname);
    if (!sp->open(QIODevice::ReadWrite) ||
            !sp->setBaudRate(baudrate.toInt()) ||
            !sp->setDataBits(QSerialPort::Data8) ||
            !sp->setParity(QSerialPort::NoParity) ||
            !sp->setStopBits(QSerialPort::OneStop) ||
            !sp->setFlowControl(QSerialPort::NoFlowControl)) {
        return false;
    }
    sp->clear();
    qDebug() << "port openned successfully!";
    return true;
}
//day light wont affect this timer so the system wont freeze
qint64 SerialPortHandler::read(char *buff, const qint64 size, const qint32 timeout)
{
    qint64 numbytesread = -1;
    timer->start(timeout);
    while (true) {
        if(timer->remainingTime() > 0) {
            return -1;
        }
        if((sp->isReadable() && sp->bytesAvailable() > 0) ||
                (sp->isReadable() && sp->waitForReadyRead(10))) {
            numbytesread += sp->read(buff, size);
        }
        if(numbytesread < 0) {
            return -1;
        }
        if(numbytesread == size) {
            break;
        }
    }
    return numbytesread;
}
void SerialPortHandler::notify()
{
}
QString SerialPortHandler::convertToCaseInsensitiveRegExp(QString str)
{
    QString result;
    for(int i = 0 ; i < str.size() ; ++i) {
        result.append("[");
        result.append(str.at(i).toLower());
        result.append(str.at(i).toUpper());
        result.append("]");
    }
    return result;
}
CommandType SerialPortHandler::analyze(const QString &line)
{
    QString read, write;
    read = convertToCaseInsensitiveRegExp("read");
    write = convertToCaseInsensitiveRegExp("write");
    if(line.contains(QRegExp(QString("^.*%1\s+[1-9]\d*\s+[1-9]\d*.*").arg(read)))) {
        return READ;
    }
    return UNKNOWN;
}
qint64 SerialPortHandler::getNum(const QString& line, int index) {
    QStringList args(line.split(QRegExp("\s+")));
    bool done;
    qint64 size = args.at(index).toInt(&done, 10);
    if(done) {
        return size;
    }
    return -1;
}
void SerialPortHandler::reply(char * buff) {
    QDataStream out(stdout);
    out << buff;
}

main.cpp

#include <QCoreApplication>
#include <QDebug>
#include "SerialPortHandler.h"
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    if(argc != 3) {
        qDebug() << "usage:" << argv[0] << "port" << "baudrate";
    } else {
        SerialPortHandler *sph = new SerialPortHandler(argv[1], argv[2]);
    }
    return a.exec();
}

父进程包含以下内容:

ParentProcess.h

#ifndef PARENTPROCESS_H
#define PARENTPROCESS_H
#include <QObject>
#include <QtCore>
class ParentProcess : public QObject
{
    Q_OBJECT
public:
    explicit ParentProcess(QObject *parent = 0);
signals:
public slots:
private slots:
    void sendRead();
    void writeSomething();
    void handleError(QProcess::ProcessError error);
private:
    QProcess *p;
};
#endif // PARENTPROCESS_H

ParentProcess.cpp

#include "ParentProcess.h"
#include <QDebug>
ParentProcess::ParentProcess(QObject *parent) :
    QObject(parent)
{
    p = new QProcess(this);
    connect(p, SIGNAL(readyReadStandardOutput()), this, SLOT(sendRead()));
    connect(p, SIGNAL(readyReadStandardError()), this, SLOT(sendRead()));
    connect(p, SIGNAL(started()), this, SLOT(writeSomething()));
    connect(p, SIGNAL(error(QProcess::ProcessError)), this, SLOT(handleError(QProcess::ProcessError)));
    QStringList args;
    args << "/dev/ttyUSB0" << "115200";
    p->start("/home/moki/Work/Programs/build-serialio-Desktop_Qt_5_3_0_GCC_64bit-Debug/serialio", args, QProcess::ReadWrite);
}
void ParentProcess::sendRead() {
    qDebug() << "data:" << p->readAllStandardError() << p->readAllStandardOutput();
}
void ParentProcess::writeSomething() {
    qDebug() << "writing";
    QString cmd = "read 10 10000n";
    qint64 a = p->write(cmd.toStdString().c_str());
    qDebug() << "wrote:" << a;
}
void ParentProcess::handleError(QProcess::ProcessError error)
{
    switch (error) {
    case QProcess::FailedToStart:
        qDebug() << "failed to start";
        break;
    case QProcess::Crashed:
        qDebug() << "crashed.";
        break;
    default:
        break;
    }
}

main.cpp

#include <QCoreApplication>
#include "ParentProcess.h"
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    ParentProcess p;
    return a.exec();
}

我在SO中看到了其他几个答案,但没有一个解决我的问题。正如您所看到的,我的子进程不应该完成并退出。只要父进程希望,它将保持启动状态。这样使用qprocess启动的进程是否正确?

我将代码更改为以下内容,现在它可以工作了。但是我不明白为什么这段代码可以工作,而我原来的代码不行。

在父进程源文件

中,我修改了构造函数,如下所示:

ParentProcess::ParentProcess(QObject *parent) :
    QObject(parent)
{
    p = new QProcess(this);
    connect(p, SIGNAL(error(QProcess::ProcessError)), this, SLOT(handleError(QProcess::ProcessError)));
    connect(p, SIGNAL(readyRead()), this, SLOT(sendRead()));
    connect(p, SIGNAL(started()), this, SLOT(writeSomething()));
    QStringList args;
    args << "/dev/ttyUSB0" << "115200";
    p->setProcessChannelMode(QProcess::MergedChannels);
    p->start("/home/moki/Work/Programs/build-serialio-Desktop_Qt_5_3_0_GCC_64bit-Debug/serialio", args, QProcess::ReadWrite);
}

sendRead()函数到此:

void ParentProcess::sendRead() {
    int bytes = p->bytesAvailable();
    qDebug() << "data:" << p->read(bytes);
}

最后,writeSomething() to:

void ParentProcess::writeSomething() {
    qDebug() << "gonna write.";
    if(p->state() == QProcess::Running) {
        qDebug() << "writing";
        QString cmd = "read 10 10000n";
        qint64 a = p->write(cmd.toStdString().c_str());
        qDebug() << "wrote:" << a << "bytes.";
    }
}

,现在它工作了。如果有人能给我解释一下,我将非常感激。