Splitting a file into several parts - c++

I'm trying to make a File copying application in Qt. I need to split the source file into several files so that my copy function can copy portions of data one by one and update the QProgressBar. To update the progress accurately I need to split the source file in 1% of its original size. Is my approach wrong. I'm unable to find much resources on this topic.How can I split the source file into several parts of equal size?

The following is a self-contained sketch of such an asynchronous file copier. There are some shortcomings:
The error reporting needs to report QFile error codes as well.
A custom mutex locker class is really needed to deal with mutex RAII.
When looking at the code, ensure that you consider the implementation and the interface (its use) separately. The interface makes it easy to use. The implementation's relative complexity makes the easy to use interface possible.
#include <QApplication>
#include <QByteArray>
#include <QProgressDialog>
#include <QFileDialog>
#include <QBasicTimer>
#include <QElapsedTimer>
#include <QThread>
#include <QMutex>
#include <QFile>
#include <limits>
class FileCopier : public QObject {
Q_OBJECT
QMutex mutable m_mutex;
QByteArray m_buf;
QBasicTimer m_copy, m_progress;
QString m_error;
QFile m_fi, m_fo;
qint64 m_total, m_done;
int m_shift;
void close() {
m_copy.stop();
m_progress.stop();
m_fi.close();
m_fo.close();
m_mutex.unlock();
}
/// Takes the error string from given file and emits an error indication.
/// Closes the files and stops the copy. Always returns false
bool error(QFile & f) {
m_error = f.errorString();
m_error.append(QStringLiteral("(in %1 file").arg(f.objectName()));
emit finished(false, m_error);
close();
return false;
}
void finished() {
emitProgress();
emit finished(m_done == m_total, m_error);
close();
}
void emitProgress() {
emit progressed(m_done, m_total);
emit hasProgressValue(m_done >> m_shift);
}
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() == m_copy.timerId()) {
// Do the copy
qint64 read = m_fi.read(m_buf.data(), m_buf.size());
if (read == -1) { error(m_fi); return; }
if (read == 0) return finished();
qint64 written = m_fo.write(m_buf.constData(), read);
if (written == -1) { error(m_fo); return; }
Q_ASSERT(written == read);
m_done += read;
}
else if (ev->timerId() == m_progress.timerId())
emitProgress();
}
Q_INVOKABLE void cancelImpl() {
if (!m_fi.isOpen()) return;
m_error = "Canceled";
finished();
}
public:
explicit FileCopier(QObject * parent = 0) :
QObject(parent),
// Copy 64kbytes at a time. On a modern hard drive, we'll copy
// on the order of 1000 such blocks per second.
m_buf(65536, Qt::Uninitialized)
{
m_fi.setObjectName("source");
m_fo.setObjectName("destination");
}
/// Copies a file to another with progress indication.
/// Returns false if the files cannot be opened.
/// This method is thread safe.
Q_SLOT bool copy(const QString & src, const QString & dst) {
bool locked = m_mutex.tryLock();
Q_ASSERT_X(locked, "copy",
"Another copy is already in progress");
m_error.clear();
// Open the files
m_fi.setFileName(src);
m_fo.setFileName(dst);
if (! m_fi.open(QIODevice::ReadOnly)) return error(m_fi);
if (! m_fo.open(QIODevice::WriteOnly)) return error(m_fo);
m_total = m_fi.size();
if (m_total < 0) return error(m_fi);
// File size might not fit into an integer, calculate the number of
// binary digits to shift it right by. Recall that QProgressBar etc.
// all use int, not qint64!
m_shift = 0;
while ((m_total>>m_shift) >= std::numeric_limits<int>::max()) m_shift++;
emit hasProgressMaximum(m_total>>m_shift);
m_done = 0;
m_copy.start(0, this);
m_progress.start(100, this); // Progress is emitted at 10Hz rate
return true;
}
/// This method is thread safe only when a copy is not in progress.
QString lastError() const {
bool locked = m_mutex.tryLock();
Q_ASSERT_X(locked, "lastError",
"A copy is in progress. This method can only be used when"
"a copy is done");
QString error = m_error;
m_mutex.unlock();
return error;
}
/// Cancels a pending copy operation. No-op if no copy is underway.
/// This method is thread safe.
Q_SLOT void cancel() {
QMetaObject::invokeMethod(this, "cancelImpl");
}
/// Signal for progress indication with number of bytes
Q_SIGNAL void progressed(qint64 done, qint64 total);
/// Signals for progress that uses abstract integer values
Q_SIGNAL void hasProgressMaximum(int total);
Q_SIGNAL void hasProgressValue(int done);
///
Q_SIGNAL void finished(bool ok, const QString & error);
};
/// A thread that is always destructible: if quits the event loop and waits
/// for it to finish.
class Thread : public QThread {
public:
~Thread() { quit(); wait(); }
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QString src = QFileDialog::getOpenFileName(0, "Source File");
if (src.isEmpty()) return 1;
QString dst = QFileDialog::getSaveFileName(0, "Destination File");
if (dst.isEmpty()) return 1;
QProgressDialog dlg("File Copy Progress", "Cancel", 0, 100);
Q_ASSERT(!dlg.isModal());
Thread thread;
FileCopier copier;
copier.moveToThread(&thread);
thread.start();
dlg.connect(&copier, SIGNAL(hasProgressMaximum(int)),
SLOT(setMaximum(int)));
dlg.connect(&copier, SIGNAL(hasProgressValue(int)),
SLOT(setValue(int)));
copier.connect(&dlg, SIGNAL(canceled()), SLOT(cancel()));
a.connect(&copier, SIGNAL(finished(bool,QString)), SLOT(quit()));
// The copy method is thread safe.
copier.copy(src, dst);
return a.exec();
}
#include "main.moc"

Usually file copying with progress bar is done in this way:
open the source file (for read)
open the destination file (for write)
read a block (64 kB or so) from source file and write it to destination file
update the progress bar
repeat steps 3. and 4. until end of source file
done :) close the files
No need to split file into multiple files. Just process the file by small blocks (block after block), not whole file at once.
The "split file into several files before copying" approach is wrong - this split approach is equally expensive as copying, whole operation would took twice as long and you would need to update progress bar during this splitting too.

Related

How do you run a function in the background of your program (Specifically an autosave function)? QT / C++

In my code I would like to integrate an auto-save function that runs every couple seconds or so. I would like this to run in the background because I have other stuff that I am going to be running at the same time. So how would I do this?
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <fstream>
#include <QFile>
#include <QDebug>
using namespace std;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow) {
ui->setupUi(this);
// Setup code
ui->textEdit->setReadOnly(true);
ui->textEdit->append("Select one of the buttons on the left to pick a log");
}
MainWindow::~MainWindow() {
delete ui;
}
string lastSavedText[] = {
" ",
" "
};
QString qLastSavedTextHome, qLastSavedTextWork;
This is my first button
void MainWindow::on_homeButton_clicked() {
// Preparing text edit
ui->textEdit->setReadOnly(false);
ui->textEdit->clear();
ui->textEdit->setOverwriteMode(true);
// Loading previously saved text
QFile file { "home.apl" };
if ( !file.open(QIODevice::ReadOnly | QIODevice::Text) ) {
qDebug() << "Could not open file!";
return;
}
const auto& lastSavedText = file.readAll();
file.close();
ui->textEdit->setPlainText( lastSavedText );
}
This is my second one
void MainWindow::on_workButton_clicked() {
// Preparing text edit
ui->textEdit->setReadOnly(false);
ui->textEdit->clear();
ui->textEdit->setOverwriteMode(true);
// Loading previously saved text
QFile file2 { "work.apl" };
if ( !file2.open(QIODevice::ReadOnly | QIODevice::Text) ) {
qDebug() << "Could not open file!";
return;
}
const auto& lastSavedText = file2.readAll();
file2.close();
ui->textEdit->setPlainText( lastSavedText );
}
This is the save button I hope to eliminate with an autosave
void MainWindow::on_saveButton_clicked() {
// Converts textEdit to string
QString textEditText = ui->textEdit->toPlainText();
lastSavedText[0] = textEditText.toStdString();
// Saving files
ofstream home;
home.open("home.apl");
home << lastSavedText[0];
home.close();
ofstream work;
work.open("work.apl");
work << lastSavedText[1];
work.close();
}
There is 2 solutions.
Easy one
Use simply a timer that will execute the code of your save button. You can set the timer to execute any period of time.
QTimer
But this might cause the software to freeze if this operation takes too much time. In which case, you can put the function that saves inside a thread.
Threads
You can use threads to do that.
Thread, is basically a process that will detach from your main process and can be run at the same time, each thread doing its own work.
Note that to communicate between thread, the safest method is to use signals.
Qt Threads Documentation
Example
void MyObject::startWorkInAThread()
{
WorkerThread *workerThread = new WorkerThread(this);
connect(workerThread, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));
connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
workerThread->start();
}
You can use a QTimer with QtConcurrent::run, and then you get the simplicity with the benefit of running the saving on a different thread you don't need to manage.
Practically, try
QTimer::singleShot(time, this, Qt::TimerType::CoarseTime, QtConcurrent::run(this,&MainWindow::on_saveButton_clicked));
Here's a first approximation using a background thread (for the sake of brevity, it inherits QThread - for your real application, consider decoupling the QThread base-class from this worker thread object. That will also make it possible to give a father-object for t).
class Thread: public QThread {
Q_OBJECT
public:
Thread(QTextEdit *textEdit):textEdit(textEdit) {
QTimer *t = new QTimer;
connect(t, SIGNAL(timeout()), SLOT(saveOnce()));
t->moveToThread(this);
t->start(2000);
}
protected:
QTextEdit *textEdit;
std::string lastSavedText[2];
private slots:
QString text() const { return textEdit->toPlainText(); }
void saveOnce() {
QString textEditText;
QMetaObject::invokeMethod(this,
"text", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QString,textEditText));
lastSavedText[0] = textEditText.toStdString();
// Saving files
ofstream home;
home.open("home.apl");
home << lastSavedText[0];
home.close();
ofstream work;
work.open("work.apl");
work << lastSavedText[1];
work.close();
}
};
Care must be taken, when taking this approach with BlockingQueuedConnection, that the thread does not call invokeMethod while the main thread is waiting for it to exit - then a deadlock happens because the main-thread cannot process the text() queued call anymore.

How to correctly use QSerialPort to write and read data constantly? [duplicate]

I have to update firmware and settings on a device connected to a serial port.
Since this is done by a sequence of commands, I send a command and wait until I recive an answer. Inside the answere (many lines) I search for a string that indicates if the operation is finished successfully.
Serial->write(“boot”, 1000);
Serial->waitForKeyword(“boot successful”);
Serial->sendFile(“image.dat”);
…
So I’ve created a new Thread for this blocking read/write method. Inside the thread I make use of the waitForX() functions.
If I call watiForKeyword() it will call readLines() until it detects the keyword or timesout
bool waitForKeyword(const QString &keyword)
{
QString str;
// read all lines
while(serial->readLines(10000))
{
// check each line
while((str = serial->getLine()) != "")
{
// found!
if(str.contains(keyword))
return true;
}
}
// timeout
return false;
}
readLines() reads everything available and separates it into lines , each line is placed inside a QStringList and to get a string I call getLine() which returns the first string in the list and deletes it.
bool SerialPort::readLines(int waitTimeout)
{
if(!waitForReadyRead(waitTimeout))
{
qDebug() << "Timeout reading" << endl;
return false;
}
QByteArray data = readAll();
while (waitForReadyRead(100))
data += readAll();
char* begin = data.data();
char* ptr = strstr(data, "\r\n");
while(ptr != NULL)
{
ptr+=2;
buffer.append(begin, ptr - begin);
emit readyReadLine(buffer);
lineBuffer.append(QString(buffer)); // store line in Qstringlist
buffer.clear();
begin = ptr;
ptr = strstr(begin, "\r\n");
}
// rest
buffer.append(begin, -1);
return true;
}
The problem is if I send a file via terminal to test the app readLines() will only read a smale part of the file ( 5 Lines or so). Since these lines do not contain the keyword. the function will run once again, but this time it dosnt wait for timeout, readLines just return false immediately.
Whats wrong ?
Also I'm not shure if this is the right approach... Does anyone know how to send a sequenze of commands and wait for a response each time?
Let's use QStateMachine to make this simple. Let's recall how you wished such code would look:
Serial->write("boot", 1000);
Serial->waitForKeyword("boot successful");
Serial->sendFile("image.dat");
Let's put it in a class that has explicit state members for each state the programmer could be in. We'll also have action generators send, expect, etc. that attach given actions to states.
// https://github.com/KubaO/stackoverflown/tree/master/questions/comm-commands-32486198
#include <QtWidgets>
#include <private/qringbuffer_p.h>
#include <type_traits>
[...]
class Programmer : public StatefulObject {
Q_OBJECT
AppPipe m_port { nullptr, QIODevice::ReadWrite, this };
State s_boot { &m_mach, "s_boot" },
s_send { &m_mach, "s_send" };
FinalState s_ok { &m_mach, "s_ok" },
s_failed { &m_mach, "s_failed" };
public:
Programmer(QObject * parent = 0) : StatefulObject(parent) {
connectSignals();
m_mach.setInitialState(&s_boot);
send (&s_boot, &m_port, "boot\n");
expect(&s_boot, &m_port, "boot successful", &s_send, 1000, &s_failed);
send (&s_send, &m_port, ":HULLOTHERE\n:00000001FF\n");
expect(&s_send, &m_port, "load successful", &s_ok, 1000, &s_failed);
}
AppPipe & pipe() { return m_port; }
};
This is fully functional, complete code for the programmer! Completely asynchronous, non-blocking, and it handles timeouts, too.
It's possible to have infrastructure that generates the states on-the-fly, so that you don't have to manually create all the states. The code is much smaller and IMHO easier to comperehend if you have explicit states. Only for complex communication protocols with 50-100+ states would it make sense to get rid of explicit named states.
The AppPipe is a simple intra-process bidirectional pipe that can be used as a stand-in for a real serial port:
// See http://stackoverflow.com/a/32317276/1329652
/// A simple point-to-point intra-process pipe. The other endpoint can live in any
/// thread.
class AppPipe : public QIODevice {
[...]
};
The StatefulObject holds a state machine, some basic signals useful for monitoring the state machine's progress, and the connectSignals method used to connect the signals with the states:
class StatefulObject : public QObject {
Q_OBJECT
Q_PROPERTY (bool running READ isRunning NOTIFY runningChanged)
protected:
QStateMachine m_mach { this };
StatefulObject(QObject * parent = 0) : QObject(parent) {}
void connectSignals() {
connect(&m_mach, &QStateMachine::runningChanged, this, &StatefulObject::runningChanged);
for (auto state : m_mach.findChildren<QAbstractState*>())
QObject::connect(state, &QState::entered, this, [this, state]{
emit stateChanged(state->objectName());
});
}
public:
Q_SLOT void start() { m_mach.start(); }
Q_SIGNAL void runningChanged(bool);
Q_SIGNAL void stateChanged(const QString &);
bool isRunning() const { return m_mach.isRunning(); }
};
The State and FinalState are simple named state wrappers in the style of Qt 3. They allow us to declare the state and give it a name in one go.
template <class S> struct NamedState : S {
NamedState(QState * parent, const char * name) : S(parent) {
this->setObjectName(QLatin1String(name));
}
};
typedef NamedState<QState> State;
typedef NamedState<QFinalState> FinalState;
The action generators are quite simple, too. The meaning of an action generator is "do something when a given state is entered". The state to act on is always given as the first argument. The second and subsequent arguments are specific to the given action. Sometimes, an action might need a target state as well, e.g. if it succeeds or fails.
void send(QAbstractState * src, QIODevice * dev, const QByteArray & data) {
QObject::connect(src, &QState::entered, dev, [dev, data]{
dev->write(data);
});
}
QTimer * delay(QState * src, int ms, QAbstractState * dst) {
auto timer = new QTimer(src);
timer->setSingleShot(true);
timer->setInterval(ms);
QObject::connect(src, &QState::entered, timer, static_cast<void (QTimer::*)()>(&QTimer::start));
QObject::connect(src, &QState::exited, timer, &QTimer::stop);
src->addTransition(timer, SIGNAL(timeout()), dst);
return timer;
}
void expect(QState * src, QIODevice * dev, const QByteArray & data, QAbstractState * dst,
int timeout = 0, QAbstractState * dstTimeout = nullptr)
{
addTransition(src, dst, dev, SIGNAL(readyRead()), [dev, data]{
return hasLine(dev, data);
});
if (timeout) delay(src, timeout, dstTimeout);
}
The hasLine test simply checks all lines that can be read from the device for a given needle. This works fine for this simple communications protocol. You'd need more complex machinery if your communications were more involved. It is necessary to read all the lines, even if you find your needle. That's because this test is invoked from the readyRead signal, and in that signal you must read all the data that fulfills a chosen criterion. Here, the criterion is that the data forms a full line.
static bool hasLine(QIODevice * dev, const QByteArray & needle) {
auto result = false;
while (dev->canReadLine()) {
auto line = dev->readLine();
if (line.contains(needle)) result = true;
}
return result;
}
Adding guarded transitions to states is a bit cumbersome with the default API, so we will wrap it to make it easier to use, and to keep the action generators above readable:
template <typename F>
class GuardedSignalTransition : public QSignalTransition {
F m_guard;
protected:
bool eventTest(QEvent * ev) Q_DECL_OVERRIDE {
return QSignalTransition::eventTest(ev) && m_guard();
}
public:
GuardedSignalTransition(const QObject * sender, const char * signal, F && guard) :
QSignalTransition(sender, signal), m_guard(std::move(guard)) {}
GuardedSignalTransition(const QObject * sender, const char * signal, const F & guard) :
QSignalTransition(sender, signal), m_guard(guard) {}
};
template <typename F> static GuardedSignalTransition<F> *
addTransition(QState * src, QAbstractState *target,
const QObject * sender, const char * signal, F && guard) {
auto t = new GuardedSignalTransition<typename std::decay<F>::type>
(sender, signal, std::forward<F>(guard));
t->setTargetState(target);
src->addTransition(t);
return t;
}
That's about it - if you had a real device, that's all you need. Since I don't have your device, I'll create another StatefulObject to emulate the presumed device behavior:
class Device : public StatefulObject {
Q_OBJECT
AppPipe m_dev { nullptr, QIODevice::ReadWrite, this };
State s_init { &m_mach, "s_init" },
s_booting { &m_mach, "s_booting" },
s_firmware { &m_mach, "s_firmware" };
FinalState s_loaded { &m_mach, "s_loaded" };
public:
Device(QObject * parent = 0) : StatefulObject(parent) {
connectSignals();
m_mach.setInitialState(&s_init);
expect(&s_init, &m_dev, "boot", &s_booting);
delay (&s_booting, 500, &s_firmware);
send (&s_firmware, &m_dev, "boot successful\n");
expect(&s_firmware, &m_dev, ":00000001FF", &s_loaded);
send (&s_loaded, &m_dev, "load successful\n");
}
Q_SLOT void stop() { m_mach.stop(); }
AppPipe & pipe() { return m_dev; }
};
Now let's make it all nicely visualized. We'll have a window with a text browser showing the contents of the communications. Below it will be buttons to start/stop the programmer or the device, and labels indicating the state of the emulated device and the programmer:
int main(int argc, char ** argv) {
using Q = QObject;
QApplication app{argc, argv};
Device dev;
Programmer prog;
QWidget w;
QGridLayout grid{&w};
QTextBrowser comms;
QPushButton devStart{"Start Device"}, devStop{"Stop Device"},
progStart{"Start Programmer"};
QLabel devState, progState;
grid.addWidget(&comms, 0, 0, 1, 3);
grid.addWidget(&devState, 1, 0, 1, 2);
grid.addWidget(&progState, 1, 2);
grid.addWidget(&devStart, 2, 0);
grid.addWidget(&devStop, 2, 1);
grid.addWidget(&progStart, 2, 2);
devStop.setDisabled(true);
w.show();
We'll connect the device's and programmer's AppPipes. We'll also visualize what the programmer is sending and receiving:
dev.pipe().addOther(&prog.pipe());
prog.pipe().addOther(&dev.pipe());
Q::connect(&prog.pipe(), &AppPipe::hasOutgoing, &comms, [&](const QByteArray & data){
comms.append(formatData(">", "blue", data));
});
Q::connect(&prog.pipe(), &AppPipe::hasIncoming, &comms, [&](const QByteArray & data){
comms.append(formatData("<", "green", data));
});
Finally, we'll connect the buttons and labels:
Q::connect(&devStart, &QPushButton::clicked, &dev, &Device::start);
Q::connect(&devStop, &QPushButton::clicked, &dev, &Device::stop);
Q::connect(&dev, &Device::runningChanged, &devStart, &QPushButton::setDisabled);
Q::connect(&dev, &Device::runningChanged, &devStop, &QPushButton::setEnabled);
Q::connect(&dev, &Device::stateChanged, &devState, &QLabel::setText);
Q::connect(&progStart, &QPushButton::clicked, &prog, &Programmer::start);
Q::connect(&prog, &Programmer::runningChanged, &progStart, &QPushButton::setDisabled);
Q::connect(&prog, &Programmer::stateChanged, &progState, &QLabel::setText);
return app.exec();
}
#include "main.moc"
The Programmer and Device could live in any thread. I've left them in the main thread since there's no reason to move them out, but you could put both into a dedicated thread, or each into its own thread, or into threads shared with other objects, etc. It's completely transparent since AppPipe supports communications across the threads. This would also be the case if QSerialPort was used instead of AppPipe. All that matters is that each instance of a QIODevice is used from one thread only. Everything else happens via signal/slot connections.
E.g. if you wanted the Programmer to live in a dedicated thread, you'd add the following somewhere in main:
// fix QThread brokenness
struct Thread : QThread { ~Thread() { quit(); wait(); } };
Thread progThread;
prog.moveToThread(&progThread);
progThread.start();
A little helper formats the data to make it easier to read:
static QString formatData(const char * prefix, const char * color, const QByteArray & data) {
auto text = QString::fromLatin1(data).toHtmlEscaped();
if (text.endsWith('\n')) text.truncate(text.size() - 1);
text.replace(QLatin1Char('\n'), QString::fromLatin1("<br/>%1 ").arg(QLatin1String(prefix)));
return QString::fromLatin1("<font color=\"%1\">%2 %3</font><br/>")
.arg(QLatin1String(color)).arg(QLatin1String(prefix)).arg(text);
}
I'm not sure indeed this is the right approach.
You're polling with waitForReadyRead(). But since the serial port is a QIODevice, it will emit a void QIODevice::readyRead() signal when something will arrive on the serial port. Why not connect this signal to your input parsing code? No need for waitForReadyRead().
Also/on the other hand: "...this time it doesn't wait for timeout, readLines just return false immediately. Whats wrong ?"
Quoting the documentation:
If waitForReadyRead() returns false, the connection has been
closed or an error has occurred.
(emphasis mine)
From my experience as an embedded developer, it is not impossible that you put the device into kind of a "firmware upgrade" mode, and that by doing so the device rebooted into a special boot mode (not running the firmware you're about to update) and thus closed the connection. No way to tell unless it's documented/you have contact with the device developers. Not so obvious to check using a serial terminal to type your commands and witness that, I use minicom daily connected to my devices and it's pretty resilient across reboot - good for me.

readAll() from QSerialPort doesn't include the last response sent

I'm using Qt to control a serial device. If I send a command to my serial device, I do something like serial->write("command \r\n"). I made a push button which changes the text inside a plain text widget to the response of the serial port. To get the response of the serial port, I'm using serial->readAll(). The problem is it shows the 2nd to last response rather than the one I was expecting. Does Qt have some sort of buffer which is keeping hold of this response?
EDIT
I botched it by using recursion and compared the strings recieved
You might be calling readAll before the response is available. You should hook your code to the readyRead signal to be notified each time new chunk of data is ready to be read. Keep in mind that readyRead can be emitted with any number of bytes available to read - at a minimum, it'll be just one byte. You can't expect the data to be chunked/blocked in any particular way, since the serial port doesn't act as a message-based communication device. Your receiver code must be able to piece the data together from small chunks and act accordingly when it got all the data it needs.
For example, suppose that the device responses have a fixed, known length. You'd only want to react when a complete response has arrived. E.g.:
class Protocol : public QObject {
Q_OBJECT
QBasicTimer m_timer;
QPointer<QIODevice> m_port;
int m_responseLength = 0;
int m_read = 0;
void timerEvent(QTimerEvent * ev) override {
if (ev->timerId() != m_timer.timerId()) return;
m_timer.stop();
emit timedOut();
}
void onData() {
m_read += m_port->bytesAvailable();
if (m_read < m_responseLength)
return;
m_timer.stop();
emit gotResponse(m_port->read(m_responseLength));
m_read -= m_responseLength;
m_responseLength = 0;
}
public:
Q_SIGNAL void gotResponse(const QByteArray &);
Q_SIGNAL void timedOut();
Q_SLOT void sendCommand(const QByteArray & cmd, int responseLength, int cmdTimeout) {
m_responseLength = responseLength;
m_port->write(cmd);
m_timer.start(cmdTimeout, this);
}
explicit Protocol(QIODevice * port, QObject * parent = nullptr) :
QObject(parent), m_port(port) {
connect(m_port, &QIODevice::readyRead, this, &Protocol::onData);
}
};
...
Protocol protocol(0,0);
protocol.sendCommand({"foo"}, 10, 500);
QMetaObject::Connection cmd1;
cmd1 = QObject::connect(&protocol, &Protocol::gotResponse, [&]{
QObject::disconnect(cmd1);
qDebug() << "got response to foo";
});
QObject::connect(&protocol, &Protocol::timedOut, []{ qDebug() << "timed out :("; });

Sending a sequence of commands and wait for response

I have to update firmware and settings on a device connected to a serial port.
Since this is done by a sequence of commands, I send a command and wait until I recive an answer. Inside the answere (many lines) I search for a string that indicates if the operation is finished successfully.
Serial->write(“boot”, 1000);
Serial->waitForKeyword(“boot successful”);
Serial->sendFile(“image.dat”);
…
So I’ve created a new Thread for this blocking read/write method. Inside the thread I make use of the waitForX() functions.
If I call watiForKeyword() it will call readLines() until it detects the keyword or timesout
bool waitForKeyword(const QString &keyword)
{
QString str;
// read all lines
while(serial->readLines(10000))
{
// check each line
while((str = serial->getLine()) != "")
{
// found!
if(str.contains(keyword))
return true;
}
}
// timeout
return false;
}
readLines() reads everything available and separates it into lines , each line is placed inside a QStringList and to get a string I call getLine() which returns the first string in the list and deletes it.
bool SerialPort::readLines(int waitTimeout)
{
if(!waitForReadyRead(waitTimeout))
{
qDebug() << "Timeout reading" << endl;
return false;
}
QByteArray data = readAll();
while (waitForReadyRead(100))
data += readAll();
char* begin = data.data();
char* ptr = strstr(data, "\r\n");
while(ptr != NULL)
{
ptr+=2;
buffer.append(begin, ptr - begin);
emit readyReadLine(buffer);
lineBuffer.append(QString(buffer)); // store line in Qstringlist
buffer.clear();
begin = ptr;
ptr = strstr(begin, "\r\n");
}
// rest
buffer.append(begin, -1);
return true;
}
The problem is if I send a file via terminal to test the app readLines() will only read a smale part of the file ( 5 Lines or so). Since these lines do not contain the keyword. the function will run once again, but this time it dosnt wait for timeout, readLines just return false immediately.
Whats wrong ?
Also I'm not shure if this is the right approach... Does anyone know how to send a sequenze of commands and wait for a response each time?
Let's use QStateMachine to make this simple. Let's recall how you wished such code would look:
Serial->write("boot", 1000);
Serial->waitForKeyword("boot successful");
Serial->sendFile("image.dat");
Let's put it in a class that has explicit state members for each state the programmer could be in. We'll also have action generators send, expect, etc. that attach given actions to states.
// https://github.com/KubaO/stackoverflown/tree/master/questions/comm-commands-32486198
#include <QtWidgets>
#include <private/qringbuffer_p.h>
#include <type_traits>
[...]
class Programmer : public StatefulObject {
Q_OBJECT
AppPipe m_port { nullptr, QIODevice::ReadWrite, this };
State s_boot { &m_mach, "s_boot" },
s_send { &m_mach, "s_send" };
FinalState s_ok { &m_mach, "s_ok" },
s_failed { &m_mach, "s_failed" };
public:
Programmer(QObject * parent = 0) : StatefulObject(parent) {
connectSignals();
m_mach.setInitialState(&s_boot);
send (&s_boot, &m_port, "boot\n");
expect(&s_boot, &m_port, "boot successful", &s_send, 1000, &s_failed);
send (&s_send, &m_port, ":HULLOTHERE\n:00000001FF\n");
expect(&s_send, &m_port, "load successful", &s_ok, 1000, &s_failed);
}
AppPipe & pipe() { return m_port; }
};
This is fully functional, complete code for the programmer! Completely asynchronous, non-blocking, and it handles timeouts, too.
It's possible to have infrastructure that generates the states on-the-fly, so that you don't have to manually create all the states. The code is much smaller and IMHO easier to comperehend if you have explicit states. Only for complex communication protocols with 50-100+ states would it make sense to get rid of explicit named states.
The AppPipe is a simple intra-process bidirectional pipe that can be used as a stand-in for a real serial port:
// See http://stackoverflow.com/a/32317276/1329652
/// A simple point-to-point intra-process pipe. The other endpoint can live in any
/// thread.
class AppPipe : public QIODevice {
[...]
};
The StatefulObject holds a state machine, some basic signals useful for monitoring the state machine's progress, and the connectSignals method used to connect the signals with the states:
class StatefulObject : public QObject {
Q_OBJECT
Q_PROPERTY (bool running READ isRunning NOTIFY runningChanged)
protected:
QStateMachine m_mach { this };
StatefulObject(QObject * parent = 0) : QObject(parent) {}
void connectSignals() {
connect(&m_mach, &QStateMachine::runningChanged, this, &StatefulObject::runningChanged);
for (auto state : m_mach.findChildren<QAbstractState*>())
QObject::connect(state, &QState::entered, this, [this, state]{
emit stateChanged(state->objectName());
});
}
public:
Q_SLOT void start() { m_mach.start(); }
Q_SIGNAL void runningChanged(bool);
Q_SIGNAL void stateChanged(const QString &);
bool isRunning() const { return m_mach.isRunning(); }
};
The State and FinalState are simple named state wrappers in the style of Qt 3. They allow us to declare the state and give it a name in one go.
template <class S> struct NamedState : S {
NamedState(QState * parent, const char * name) : S(parent) {
this->setObjectName(QLatin1String(name));
}
};
typedef NamedState<QState> State;
typedef NamedState<QFinalState> FinalState;
The action generators are quite simple, too. The meaning of an action generator is "do something when a given state is entered". The state to act on is always given as the first argument. The second and subsequent arguments are specific to the given action. Sometimes, an action might need a target state as well, e.g. if it succeeds or fails.
void send(QAbstractState * src, QIODevice * dev, const QByteArray & data) {
QObject::connect(src, &QState::entered, dev, [dev, data]{
dev->write(data);
});
}
QTimer * delay(QState * src, int ms, QAbstractState * dst) {
auto timer = new QTimer(src);
timer->setSingleShot(true);
timer->setInterval(ms);
QObject::connect(src, &QState::entered, timer, static_cast<void (QTimer::*)()>(&QTimer::start));
QObject::connect(src, &QState::exited, timer, &QTimer::stop);
src->addTransition(timer, SIGNAL(timeout()), dst);
return timer;
}
void expect(QState * src, QIODevice * dev, const QByteArray & data, QAbstractState * dst,
int timeout = 0, QAbstractState * dstTimeout = nullptr)
{
addTransition(src, dst, dev, SIGNAL(readyRead()), [dev, data]{
return hasLine(dev, data);
});
if (timeout) delay(src, timeout, dstTimeout);
}
The hasLine test simply checks all lines that can be read from the device for a given needle. This works fine for this simple communications protocol. You'd need more complex machinery if your communications were more involved. It is necessary to read all the lines, even if you find your needle. That's because this test is invoked from the readyRead signal, and in that signal you must read all the data that fulfills a chosen criterion. Here, the criterion is that the data forms a full line.
static bool hasLine(QIODevice * dev, const QByteArray & needle) {
auto result = false;
while (dev->canReadLine()) {
auto line = dev->readLine();
if (line.contains(needle)) result = true;
}
return result;
}
Adding guarded transitions to states is a bit cumbersome with the default API, so we will wrap it to make it easier to use, and to keep the action generators above readable:
template <typename F>
class GuardedSignalTransition : public QSignalTransition {
F m_guard;
protected:
bool eventTest(QEvent * ev) Q_DECL_OVERRIDE {
return QSignalTransition::eventTest(ev) && m_guard();
}
public:
GuardedSignalTransition(const QObject * sender, const char * signal, F && guard) :
QSignalTransition(sender, signal), m_guard(std::move(guard)) {}
GuardedSignalTransition(const QObject * sender, const char * signal, const F & guard) :
QSignalTransition(sender, signal), m_guard(guard) {}
};
template <typename F> static GuardedSignalTransition<F> *
addTransition(QState * src, QAbstractState *target,
const QObject * sender, const char * signal, F && guard) {
auto t = new GuardedSignalTransition<typename std::decay<F>::type>
(sender, signal, std::forward<F>(guard));
t->setTargetState(target);
src->addTransition(t);
return t;
}
That's about it - if you had a real device, that's all you need. Since I don't have your device, I'll create another StatefulObject to emulate the presumed device behavior:
class Device : public StatefulObject {
Q_OBJECT
AppPipe m_dev { nullptr, QIODevice::ReadWrite, this };
State s_init { &m_mach, "s_init" },
s_booting { &m_mach, "s_booting" },
s_firmware { &m_mach, "s_firmware" };
FinalState s_loaded { &m_mach, "s_loaded" };
public:
Device(QObject * parent = 0) : StatefulObject(parent) {
connectSignals();
m_mach.setInitialState(&s_init);
expect(&s_init, &m_dev, "boot", &s_booting);
delay (&s_booting, 500, &s_firmware);
send (&s_firmware, &m_dev, "boot successful\n");
expect(&s_firmware, &m_dev, ":00000001FF", &s_loaded);
send (&s_loaded, &m_dev, "load successful\n");
}
Q_SLOT void stop() { m_mach.stop(); }
AppPipe & pipe() { return m_dev; }
};
Now let's make it all nicely visualized. We'll have a window with a text browser showing the contents of the communications. Below it will be buttons to start/stop the programmer or the device, and labels indicating the state of the emulated device and the programmer:
int main(int argc, char ** argv) {
using Q = QObject;
QApplication app{argc, argv};
Device dev;
Programmer prog;
QWidget w;
QGridLayout grid{&w};
QTextBrowser comms;
QPushButton devStart{"Start Device"}, devStop{"Stop Device"},
progStart{"Start Programmer"};
QLabel devState, progState;
grid.addWidget(&comms, 0, 0, 1, 3);
grid.addWidget(&devState, 1, 0, 1, 2);
grid.addWidget(&progState, 1, 2);
grid.addWidget(&devStart, 2, 0);
grid.addWidget(&devStop, 2, 1);
grid.addWidget(&progStart, 2, 2);
devStop.setDisabled(true);
w.show();
We'll connect the device's and programmer's AppPipes. We'll also visualize what the programmer is sending and receiving:
dev.pipe().addOther(&prog.pipe());
prog.pipe().addOther(&dev.pipe());
Q::connect(&prog.pipe(), &AppPipe::hasOutgoing, &comms, [&](const QByteArray & data){
comms.append(formatData(">", "blue", data));
});
Q::connect(&prog.pipe(), &AppPipe::hasIncoming, &comms, [&](const QByteArray & data){
comms.append(formatData("<", "green", data));
});
Finally, we'll connect the buttons and labels:
Q::connect(&devStart, &QPushButton::clicked, &dev, &Device::start);
Q::connect(&devStop, &QPushButton::clicked, &dev, &Device::stop);
Q::connect(&dev, &Device::runningChanged, &devStart, &QPushButton::setDisabled);
Q::connect(&dev, &Device::runningChanged, &devStop, &QPushButton::setEnabled);
Q::connect(&dev, &Device::stateChanged, &devState, &QLabel::setText);
Q::connect(&progStart, &QPushButton::clicked, &prog, &Programmer::start);
Q::connect(&prog, &Programmer::runningChanged, &progStart, &QPushButton::setDisabled);
Q::connect(&prog, &Programmer::stateChanged, &progState, &QLabel::setText);
return app.exec();
}
#include "main.moc"
The Programmer and Device could live in any thread. I've left them in the main thread since there's no reason to move them out, but you could put both into a dedicated thread, or each into its own thread, or into threads shared with other objects, etc. It's completely transparent since AppPipe supports communications across the threads. This would also be the case if QSerialPort was used instead of AppPipe. All that matters is that each instance of a QIODevice is used from one thread only. Everything else happens via signal/slot connections.
E.g. if you wanted the Programmer to live in a dedicated thread, you'd add the following somewhere in main:
// fix QThread brokenness
struct Thread : QThread { ~Thread() { quit(); wait(); } };
Thread progThread;
prog.moveToThread(&progThread);
progThread.start();
A little helper formats the data to make it easier to read:
static QString formatData(const char * prefix, const char * color, const QByteArray & data) {
auto text = QString::fromLatin1(data).toHtmlEscaped();
if (text.endsWith('\n')) text.truncate(text.size() - 1);
text.replace(QLatin1Char('\n'), QString::fromLatin1("<br/>%1 ").arg(QLatin1String(prefix)));
return QString::fromLatin1("<font color=\"%1\">%2 %3</font><br/>")
.arg(QLatin1String(color)).arg(QLatin1String(prefix)).arg(text);
}
I'm not sure indeed this is the right approach.
You're polling with waitForReadyRead(). But since the serial port is a QIODevice, it will emit a void QIODevice::readyRead() signal when something will arrive on the serial port. Why not connect this signal to your input parsing code? No need for waitForReadyRead().
Also/on the other hand: "...this time it doesn't wait for timeout, readLines just return false immediately. Whats wrong ?"
Quoting the documentation:
If waitForReadyRead() returns false, the connection has been
closed or an error has occurred.
(emphasis mine)
From my experience as an embedded developer, it is not impossible that you put the device into kind of a "firmware upgrade" mode, and that by doing so the device rebooted into a special boot mode (not running the firmware you're about to update) and thus closed the connection. No way to tell unless it's documented/you have contact with the device developers. Not so obvious to check using a serial terminal to type your commands and witness that, I use minicom daily connected to my devices and it's pretty resilient across reboot - good for me.

Signal-Slot makes a mess

I am trying to create a image-saving application using Qt. Now the stub
class ImageSaver:public QObject
{
int index;
QWebPage * main_Page;
QNetworkAccessManager * manager;
QNetworkReply * reply;
QString file_Name;
QSet<QString> image_Addresses;
QString web_Address;
Q_OBJECT
signals:
void image_Saved();
public slots:
void request_Image();
void on_Finished(bool status);
void got_Reply(QNetworkReply * reply);
public:
ImageSaver();
void start();
};
ImageSaver::ImageSaver()
{
index = 0;
manager = new QNetworkAccessManager;
reply = NULL;
connect(main_Page,SIGNAL(loadFinished(bool)),this,SLOT(on_Finished(bool)));
connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(got_Reply(QNetworkReply*)));
connect(this,SIGNAL(image_Saved()),this,SLOT(request_Image()));
}
void ImageSaver::start()
{
//loads the url
// In the end of the loading it will emit load_Finished(bool)
// So that signal will execute on_Finished(bool)
}
void ImageSaver::request_Image()
{
QString temp_Address = *(image_Addresses.begin()+index);
//makes a request to the server to give the image "temp_Address"
//When the server gives the reply signal finished(QNetworkReply*) will be emitted
// this in turn will call the got_Reply(QNetworkReply*)
}
void ImageSaver::on_Finished(bool status)
{
//collects all the images's url addresses, and pushes them in the list
//"image_Addresses"
//Then emits image_Saved();
//This signal will wake up the function request_Image()
}
void ImageSaver::got_Reply(QNetworkReply * reply)
{
//Image is extracted from the reply and got saved in the same name as in the page
//index got increased;
//emits the signal image_Saved();
//This signal will activate the function request_Image()
}
int main(int argc,char * argv[])
{
QApplication app(argc,argv);
ImageSaver a;
a.start();
return app.exec();
}
#include "main.moc"
In short First call is to "start".That calls "on_Finished" and there is no problem untill this. So all the image files's addresses got pushed in the list. Next is one by one request for image[i] made, and the reply image got saved. This thing is happening repeatedly. Here only I am getting problem. Crashes are appearing in this operation especially in saving the image.
My assumption is "signal-slot" is not like function call, thy are more or less like thread but operates on the same function( pointer). So when one signal requests for painter, which is already rendering something then the crash will appear.
Can anybody say the fact behind the crash and how to save all the images without crash?
EDIT:
Hi, This is the full code. Run this one, and click the message boxes contineously
#include <QApplication>
#include <QDir>
#include <QImage>
#include <QObject>
#include <QMessageBox>
#include <QPainter>
#include <QPixmap>
#include <QSet>
#include <QTimer>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <QtWebKit/QWebElement>
#include <QtWebKit/QWebFrame>
#include <QtWebKit/QWebPage>
#include <QUrl>
class ImageSaver:public QObject
{
int index;
QWebPage * main_Page;
QNetworkAccessManager * manager;
QNetworkReply * reply;
QString file_Name;
QSet<QString> image_Addresses;
QString web_Address;
Q_OBJECT
signals:
void image_Saved();
public slots:
void request_Image();
void on_Finished(bool status);
void got_Reply(QNetworkReply * reply);
public:
ImageSaver();
void start();
protected:
//void show_Frame(QWebFrame * frame);
};
ImageSaver::ImageSaver()
{
index = 0;
this->main_Page = new QWebPage;
manager = new QNetworkAccessManager;
reply = NULL;
connect(main_Page,SIGNAL(loadFinished(bool)),this,SLOT(on_Finished(bool)));
connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(got_Reply(QNetworkReply*)));
connect(this,SIGNAL(image_Saved()),this,SLOT(request_Image()));
}
void ImageSaver::start()
{
web_Address = "yahoo.com";
QDir dir;
dir.mkdir(web_Address);
QUrl url = QUrl::fromUserInput(web_Address);
main_Page->mainFrame()->load(url);
}
void ImageSaver::request_Image()
{
QString temp_Address = *(image_Addresses.begin()+index);
int a = temp_Address.lastIndexOf("/");
file_Name = temp_Address.mid(a+1);
//Without the below message box, the program closes shortly
//This message box is slowing down that effect
QMessageBox hh;
hh.setText(file_Name);
hh.exec();
QNetworkRequest request= QNetworkRequest(QUrl(temp_Address));
request.setRawHeader("img","src");
manager->get(request);
}
void ImageSaver::on_Finished(bool status)
{
if(status)
{
QMessageBox mm;
mm.setText("Finished");
mm.exec();
QWebElementCollection temp_Collection= main_Page->mainFrame()->findAllElements("*");
for(int i=0;i<temp_Collection.count();++i)
{
QWebElement temp_Element = temp_Collection[i];
if(temp_Element.tagName().contains("img",Qt::CaseInsensitive) && temp_Element.attributeNames().contains("src",Qt::CaseInsensitive))
{
QString image_Web_Address = temp_Element.attribute("src");
if(!image_Addresses.contains(image_Web_Address))
image_Addresses.insert(image_Web_Address);
}
}
emit image_Saved();
QMessageBox kk;
kk.setText("Image is going to be saved");
kk.exec();
}
else
{
QMessageBox mm;
mm.setText("Not ready");
mm.exec();
}
QMessageBox mm;
mm.setText("Getting out of finished");
mm.exec();
}
void ImageSaver::got_Reply(QNetworkReply * reply)
{
QImage image;
if(image.load(static_cast<QIODevice *>(reply),0))
image.save(web_Address+QDir::separator()+file_Name,0);
++index;
emit image_Saved();
}
/*
void ImageSaver::show_Frame(QWebFrame * temp_Frame)
{
QImage image(temp_Frame->contentsSize(),QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter painter(&image);
painter.setRenderHint(QPainter::Antialiasing,true);
painter.setRenderHint(QPainter::TextAntialiasing,true);
painter.setRenderHint(QPainter::SmoothPixmapTransform,true);
temp_Frame->documentElement().render(&painter);
painter.end();
foreach(QWebFrame * temp_Frame0,temp_Frame->childFrames())
show_Frame(temp_Frame0);
}
*/
int main(int argc,char * argv[])
{
QApplication app(argc,argv);
ImageSaver a;
a.start();
return app.exec();
}
#include "main.moc"
This is the pro file
QT += webkit network
SOURCES += \
main.cpp
Your code crashes because you read beyond the boundaries of the image_Addresses set.
void ImageSaver::request_Image()
{
QString temp_Address = *(image_Addresses.begin()+index);
...
You increment index after every image received, but there isn't any check anywhere in the code whether index is still less than image_Addresses.size(), so it crashes once dereferencing image_Addresses.begin()+index for index == image_Addresses.size().
There may be many solutions to this problem. I think that you should have a look at The State Machine Framework. In easy situations you can just use boolean variable to check if you can go on. You should also think what to do when you're busy processing the image. You can queue request or just reject them. Also you can implement threading, so that new requests are served by new threads.
P.S. Signals are more like events than threads to me.
What's the error and why do you have a #include at the end?
FYI there is a QImage class you can use instead which includes saving and loading from a QIODevice* such as QNetworkReply for example. It's extremely rare to need to reinvent the wheel in the massive framework that is Qt.