How are you solving signals/slots mechanism between two classes, if you do not make an object from class(only inherit from class) etc? QTimer, QSerialPort, which is the source of SIGNAL, and in the second class you make connection?
Is such an approach even possible?
In my case. I have two classes usb2can_driver and canview. The usb2can_driver inherit from QSerialPort, which inherit QIODevice contained SIGNAL(readyRead()). This is used in the canview for connection with handler subroutine read_u2c()
I know, in the code is a lot of garbage from testing.
In the canview.cpp void CANview::on_connectPort_released() is made connection, and in the usb2can_driver.cpp int USB2CAN_driver::connectToPort(QString portName) is part of inherit from QSerialPort.
I will be pleasure for every answer. If you think, that question is posed incorrectly, please give me feedback.
usb2can_driver.h
#ifndef USB2CAN_DRIVER_H
#define USB2CAN_DRIVER_H
#include "QSerialPort"
#include "QTimer"
#include "QObject"
#include "QSignalSpy"
class USB2CAN_driver : public QSerialPort
{
Q_OBJECT;
public:
USB2CAN_driver();
//virtual ~USB2CAN_driver();
//QSerialPort *port_USB2CAN = new QSerialPort();
int temporary_init_Counter = 0;
int init();
void USB_LoopBack();
void Boot_Mode();
void Config_Mode();
void Normal_Mode();
void LoopBack_Mode();
QByteArray Get_Mode();
void WriteReg(QByteArray regAdress, QByteArray value[]);
QByteArray WriteCMD(QByteArray CMD_name, QByteArray value);
QByteArray ReadReg(QByteArray regAdress);
QString portName;
int connectToPort(QString portName);
int disconnectedFromPort();
QTimer *tim;
int tim_counter = 0;
public: signals:
void readyRead();
private slots:
QByteArray read_USB2CAN();
void initSend();
//void timEvent();
};
#endif // USB2CAN_DRIVER_H
usb2can_driver.cpp
#include "USB2CAN_define.h"
#include "QSerialPort"
#include "QSerialPort"
#include "QObject"
#include "QDebug"
#include <QSignalSpy>
USB2CAN_driver::USB2CAN_driver()
{
//USB2CAN_driver:: = new QSerialPort();
//USB2CAN_driver::Baud9600;
//USB2CAN_driver::AllDirections;
//qDebug() << "Open port" << USB2CAN_driver::open(QIODevice::ReadWrite);
}
/*
USB2CAN_driver::~USB2CAN_driver(){
QObject::disconnect(USB2CAN_driver::,SIGNAL(readyRead()),USB2CAN_driver::,SLOT(QByteArray read_USB2CAN()));
}
*/
int USB2CAN_driver::connectToPort(QString portName){
//port_USB2CAN.setPortName(portName);
USB2CAN_driver::setPortName(portName);
USB2CAN_driver::setBaudRate(QSerialPort::Baud9600,QSerialPort::AllDirections);
USB2CAN_driver::setPortName(portName);
//Reimplemented separately as signal of driver. !!!
//qDebug() << "connect S&S in the driver, status: " << QObject::connect(this,SIGNAL(readyRead),this,SLOT(read_USB2CAN));
//qDebug() << "connect S&S in the driver, status: " << connect(this,SIGNAL(readyRead()),this,SLOT(read_USB2CAN()));
//QSignalSpy spy(this, SIGNAL(readyRead()));
//qDebug() << "from driver" << spy.wait(5000) << "----" << spy.signal();
tim = new QTimer;
return USB2CAN_driver::open(QIODevice::ReadWrite);
}
/*
void USB2CAN_driver::timEvent(){
qDebug() << "Tim" << tim_counter++;
if(tim_counter >= 5){
tim_counter = 0;
tim->stop();
}
}
*/
int USB2CAN_driver::disconnectedFromPort(){
//QObject::disconnect(this,SIGNAL(readyRead()),this,SLOT(read_USB2CAN()));
USB2CAN_driver::close();
if(USB2CAN_driver::isOpen()){
return 1;
}
else{
qDebug() << "------------------Port is diconected-----------------";
return 0;
}
}
void USB2CAN_driver::USB_LoopBack(){
}
void USB2CAN_driver::Boot_Mode(){
}
void USB2CAN_driver::Config_Mode(){
}
void USB2CAN_driver::Normal_Mode(){
}
void USB2CAN_driver::LoopBack_Mode(){
}
QByteArray USB2CAN_driver::Get_Mode(){
while(!USB2CAN_driver::waitForBytesWritten(300)){
USB2CAN_driver::write(getMode);
}
return USB2CAN_driver::readAll(); //In progress...
}
void USB2CAN_driver::WriteReg(QByteArray regAdress, QByteArray value[]){
int length = regAdress.length() + value->length();
QByteArray len;
len.setNum(length);
QByteArray sendVal[] = { writeReg, len, regAdress, *value };
QByteArray sendData;
sendData.fromRawData(*sendVal,sizeof (sendVal));
while(!USB2CAN_driver::waitForBytesWritten(300)){
USB2CAN_driver::write(sendData);
}
}
QByteArray USB2CAN_driver::WriteCMD(QByteArray CMD_name, QByteArray value){
}
QByteArray USB2CAN_driver::ReadReg(QByteArray regAdress){
}
int USB2CAN_driver::init(){
}
QByteArray USB2CAN_driver::read_USB2CAN(){
qDebug() <<"From driver RX" << USB2CAN_driver::readAll();
return USB2CAN_driver::readAll();
}
void USB2CAN_driver::initSend(){
}
canview.h
#define CANVIEW_H
#include <QDialog>
#include <usb2can_driver.h>
namespace Ui {
class CANview;
}
class CANview : public QDialog
{
Q_OBJECT
public:
explicit CANview(QWidget *parent = nullptr);
~CANview();
USB2CAN_driver *u2c;
QTimer *time;
private: signals:
friend void USB2CAN_driver::readyRead();
private slots:
void on_connectPort_released();
void on_pushButton_released();
QByteArray read_u2c();
void timerSubrutine();
private:
Ui::CANview *ui;
};
#endif // CANVIEW_H
canview.cpp
#include "ui_canview.h"
#include "QSignalSpy"
CANview::CANview(QWidget *parent) : QDialog(parent),ui(new Ui::CANview)
{
ui->setupUi(this);
u2c = new USB2CAN_driver;
}
CANview::~CANview()
{
delete ui;
}
//connect fcn
void CANview::on_connectPort_released()
{
if(u2c->isOpen()){
u2c->disconnectedFromPort();
}
else{
u2c->connectToPort(ui->inputNamePort->text());
qDebug() << "Connect rx task, status: " << connect(???,SIGNAL(readyRead()),this,SLOT(read_u2c()));
connect(u2c->tim,SIGNAL(timeout()),this,SLOT(timerSubrutine()));
u2c->tim->start(800);
}
//Controll of opened/close port
if(u2c->isOpen()){
ui->connectPort->setCheckState(Qt::CheckState::Checked);
}
else{
ui->connectPort->setCheckState(Qt::CheckState::Unchecked);
}
}
//Send function
void CANview::on_pushButton_released()
{
u2c->write(ui->TX_textBrowser->toPlainText().toLatin1(),static_cast<int>(ui->TX_textBrowser->toPlainText().length()));
qDebug() << "Send: " << static_cast<int> (ui->TX_textBrowser->toPlainText().length());
QSignalSpy spy(u2c,SIGNAL(readyRead()));
qDebug() << spy.signal() << spy.signalsBlocked() << spy.isValid();
}
QByteArray CANview::read_u2c(){
qDebug() << "RX:" << u2c->readAll();
ui->RX_textBrowser_2->setPlainText(u2c->readAll());
return u2c->readAll();
}
void CANview::timerSubrutine(){
qDebug() << "TimerEvent" << u2c->tim_counter++;
if(u2c->tim_counter >= 5){
u2c->tim->stop();
}
}```
It is impossible to connect classes as mentioned by #Scheff's Cat.
The solution is do not use inheritance from QSerialPort in the usb2can_driver. If I want connect signal of QSerialPort with slot (which is part of second class), I had to create a object from QSerialPort in the constructor of USB2CAN_driver.
This object to allow use signal/slot mechanism.
So in short: USB2CAN_driver:: was replaced by object port_USB2CAN
For the connection in the second class (canview), i used this syntax:
connect(u2c->port_USB2CAN,SIGNAL(readyRead()),this,SLOT(read_u2c()));
Thank to Scheff's Cat, your comment was helpfully. This solution is working, but if somebody see the non-standard syntax please warning me.
Related
I'm making a server-client system for a client placed within a loop to continuously query a predetermined set of information from a server. I've cooked up a bit of code from what I could understand about how the TCP implementation of the Qt framework works but I'm not sure if what I have written is the right way to do it.
At first, I made an enum with a bunch of QByteArray variables in the client QTcp class to pass as a query from the client side to the server as shown below
enum datamap
{
QByteArray data1 = 1;
QByteArray data2 = 2;
QByteArray data3 = 3;
// and so on...
};
Then I make the function that takes in an enum datamap variable to pass to the server and a variable to hold the current request(to avoid mixup between data received for the wrong request) as shown below
datamap current_request = 0;
int client::setDataToGet(datamap& data)
{
if(socket->state() == QAbstractSocket::ConnectedState)
{
current_request = data;
socket->write(data);
return socket->waitForBytesWritten();
}
else
return -1;
}
After this, I create the readyRead() slot that connects to the readyread signal to handle responses from the server and send to functions that will display the received data in the respective text box based on the current_request variable as shown below
void client::readyRead()
{
QTcpSocket *m_socket = static_cast<QTcpSocket*>(sender());
while(m_socket->bytesAvailable() > 0)
{
QByteArray buf = socket->readAll();
}
switch(current_request):
case 1:
dispToTextBox1(buf);
case 2:
dispToTextBox2(buf);
case 3:
dispToTextBox3(buf);
// and so on....
}
Now, for the server side, I make the readyRead() slot that connects to the readyread() signal from a socket listener called in the newConnection() function.
This takes in the handle from the client and accordingly is supposed to return back data associated with the handle. The code for the slot is as follows
void server::readyRead()
{
QTcpSocket *m_socket = static_cast<QTcpSocket*>(sender());
while(m_socket->bytesAvailable() > 0)
{
QByteArray buf = m_socket->readAll();
}
switch(buf):
case 1:
data = collectDatafromStream1();
m_socket->write(data); m_socket->flush();
case 2:
data = collectDatafromStream2();
m_socket->write(data); m_socket->flush();
case 3:
data = collectDatafromStream3();
m_socket->write(data); m_socket->flush();
//and so on.....
}
Could someone please verify if this is the right way to do this or if there is a better alternative for handling the task.
Got the concept model to work with a few changes. Used the response given by "sashoalm" in this question to pass the data using QdataStream and used signals & slots to cycle through the read-write sequence for each request type.
my server class is as follows
tcpserver.h
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include<qt5/QtNetwork/QTcpServer>
#include<qt5/QtNetwork/QTcpSocket>
#include<qt5/QtCore/QObject>
class TCPServer : public QObject
{
Q_OBJECT
public:
explicit TCPServer(QObject *parent = nullptr);
signals:
void dataReceived(QByteArray);
private slots:
void newConnection();
void disconnected();
void readyRead();
private:
QTcpServer *server;
QTcpSocket *socket;
QHash<QString, int> reverse_hash;
};
#endif // TCPSERVER_H
tcpserver.cpp
#include <iostream>
#include "tcpserver.h"
#include <qt5/QtCore/QDataStream>
#include <qt5/QtCore/QBuffer>
#include <qt5/QtCore/QString>
class BlockWriter
{
public:
BlockWriter(QIODevice *io)
{
buffer.open(QIODevice::WriteOnly);
this->io = io;
_stream.setVersion(QDataStream::Qt_4_8);
_stream.setDevice(&buffer);
_stream << quint64(0);
}
~BlockWriter()
{
_stream.device()->seek(0);
_stream << static_cast<quint64>(buffer.size());
io->write(buffer.buffer());
}
QDataStream &stream()
{
return _stream;
}
private:
QBuffer buffer;
QDataStream _stream;
QIODevice *io;
};
class BlockReader
{
public:
BlockReader(QIODevice *io)
{
buffer.open(QIODevice::ReadWrite);
_stream.setVersion(QDataStream::Qt_4_8);
_stream.setDevice(&buffer);
qint64 blockSize;
readMax(io, sizeof(blockSize));
buffer.seek(0);
_stream >> blockSize;
readMax(io, blockSize);
buffer.seek(sizeof(blockSize));
}
QDataStream& stream()
{
return _stream;
}
private:
void readMax(QIODevice *io, qint64 n)
{
while (buffer.size() < n) {
buffer.write(io->read(n - buffer.size()));
}
}
QBuffer buffer;
QDataStream _stream;
};
TCPServer::TCPServer(QObject *parent) : QObject(parent)
{
server = new QTcpServer(this);
connect(server, SIGNAL(newConnection()), SLOT(newConnection()));
qDebug() << "Listening:" << server->listen(QHostAddress::Any, 5404);
reverse_hash.insert("data1", 1);
reverse_hash.insert("data2", 2);
}
void TCPServer::newConnection()
{
while (server->hasPendingConnections())
{
qDebug()<<"incoming connection!";
socket = server->nextPendingConnection();
connect(socket, SIGNAL(readyRead()), SLOT(readyRead()));
connect(socket, SIGNAL(disconnected()), SLOT(disconnected()));
}
}
void TCPServer::disconnected()
{
qDebug() << "disconnected!";
disconnect(socket, SIGNAL(readyRead()));
disconnect(socket, SIGNAL(disconnected()));
socket->deleteLater();
}
void TCPServer::readyRead()
{
qDebug() << "Read!";
QString data;
BlockReader(socket).stream() >> data;
qDebug() <<"received data request: " << data;
switch(reverse_hash.value(data))
{
case 1: //call sequence to respond to request.(write to data)
qDebug() << "responding go data1 request!";
break;
case 2://call sequence to respond to request.(write to data)
qDebug() << "responding go data2 request!";
break;
}
BlockWriter(socket).stream()<<data;
socket->flush();
}
my client GUI class is as follows
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtNetwork>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QTcpSocket *socket;
QHash<int, QString> hash;
int current_slot, starting_slot, ending_slot;
signals:
void dataSet();
public slots:
void connectToHost();
void connected();
void disconnected();
void setDatatoGet();
void getData();
//bool writeData(QByteArray data);
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <iostream>
class BlockWriter
{
public:
BlockWriter(QIODevice *io)
{
buffer.open(QIODevice::WriteOnly);
this->io = io;
_stream.setVersion(QDataStream::Qt_4_8);
_stream.setDevice(&buffer);
_stream << quint64(0);
}
~BlockWriter()
{
_stream.device()->seek(0);
_stream << static_cast<quint64>(buffer.size());
io->write(buffer.buffer());
}
QDataStream &stream()
{
return _stream;
}
private:
QBuffer buffer;
QDataStream _stream;
QIODevice *io;
};
class BlockReader
{
public:
BlockReader(QIODevice *io)
{
buffer.open(QIODevice::ReadWrite);
_stream.setVersion(QDataStream::Qt_4_8);
_stream.setDevice(&buffer);
qint64 blockSize;
readMax(io, sizeof(blockSize));
buffer.seek(0);
_stream >> blockSize;
readMax(io, blockSize);
buffer.seek(sizeof(blockSize));
}
QDataStream& stream()
{
return _stream;
}
private:
void readMax(QIODevice *io, qint64 n)
{
while (buffer.size() < n) {
buffer.write(io->read(n - buffer.size()));
}
}
QBuffer buffer;
QDataStream _stream;
};
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
starting_slot = 1;
current_slot = starting_slot;
ending_slot = 2;
ui->setupUi(this);
ui->status_label->setStyleSheet("background-color:red;");
ui->receive_btn->setEnabled(false);
socket = new QTcpSocket(this);
connectToHost();
connect(ui->conn_btn, SIGNAL(clicked()), this, SLOT(connectToHost()));
connect(ui->receive_btn, SIGNAL(clicked()), this, SLOT(setDatatoGet()));
connect(this, SIGNAL(dataSet()), this , SLOT(setDatatoGet()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::connectToHost()
{
socket->deleteLater();
socket = new QTcpSocket(this);
socket->connectToHost(QHostAddress("192.168.0.127"), 5404);
connect(socket, SIGNAL(connected()), this, SLOT(connected()));
connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()));
//ADD to hash table
hash.insert(1, "data1");
hash.insert(2, "data2");
}
void MainWindow::connected()
{
ui->status_label->setStyleSheet("background-color:green;");
ui->receive_btn->setEnabled(true);
connect(socket, SIGNAL(readyRead()),this, SLOT(getData()));
}
void MainWindow::disconnected()
{
ui->status_label->setStyleSheet("background-color:red;");
ui->receive_btn->setEnabled(false);
disconnect(socket, SIGNAL(readyRead()),this, SLOT(getData()));
}
void MainWindow::setDatatoGet()
{
if(current_slot == ending_slot + 1)
{
current_slot = starting_slot;
}
qDebug() <<"calling request data slot " << current_slot;
BlockWriter(socket).stream() << hash.value(current_slot);
socket->flush();
current_slot++;
}
void MainWindow::getData()
{
QString data;
BlockReader(socket).stream() >> data;
//qDebug() <<"received response, current received data is for slot "<< data <<"and current number is" << current_slot;
switch (current_slot - 1)
{
case 1:
//display in respective label
qDebug() << "display data1 to label!";
break;
case 2:
//display in respective label
qDebug() << "display data2 to label!";
break;
}
emit dataSet();
}
I looked for examples everywhere, and I just can't seem to find a solution.
Issue
So : I have this receiving class called DataReceiver. Basically, I just want to receive data from a TCP Socket on the localhost (127.0.0.1) network. It seems that I can write w/ this socket (I checked w/ the Wireshark software) but it looks like the program does not receive anything.
I placed a qDebug() statement in the onDataReceived QT SLOT. This slot does not seem to be triggered at any moment. I tried editing the mTcpSocket->readyRead() QT SIGNAL manually, and the slot is correctly called upon, which means that the signal is not emitted because the QTcpSocket doesn't receive anything (or at least I this so).
My computer is a Mac w/ macOS High Sierra. I scanned open ports, and found that 51470 & 50911 are open. Those are the ports I tested.
DataReceiver class
The code for this class is the following :
datareceiver.h :
class DataReceiver : QObject
{
Q_OBJECT
public:
// Con/Destructors
explicit DataReceiver(QObject *parent = nullptr);
explicit DataReceiver(const QString &pSourceAddress,
const unsigned int &pSourcePort,
QObject *parent = nullptr);
~DataReceiver();
// Network Management
bool connectToHost(void);
// Getters
QVector<float> *getData(void) const;
QTcpSocket *getTcpSocket(void) const;
QString getSourceAddress(void) const;
unsigned int getSourcePort(void) const;
// Setters
void setData(const QVector<float> *pData);
void setSourceAddress(const QString &pSourceAddress);
void setSourcePort(const unsigned int &pSourcePort);
signals:
public slots:
void onConnect();
void onDisconnect();
void onBytesWritten(qint64 bytes);
void onDataReceived();
private slots:
private:
void decodeData(const QByteArray &pMessage);
QTcpSocket *mTcpSocket;
QString mSourceAddress;
unsigned int mSourcePort;
const unsigned int mDataSize = 30;
QVector<float> *mData;
};
And the implementation : datareceiver.cpp
// Con/Destructors
DataReceiver::DataReceiver(QObject *parent) :
QObject(parent),
mTcpSocket(new QTcpSocket(this)),
mSourceAddress("127.0.0.1"),
mSourcePort(51470),
mData(new QVector<float>)
{
for( unsigned int i = 0; i < mDataSize; i++)
mData->append(.0);
}
DataReceiver::DataReceiver(const QString &pSourceAddress,
const unsigned int &pSourcePort,
QObject *parent) :
QObject(parent),
mTcpSocket(new QTcpSocket(this)),
mSourceAddress(pSourceAddress),
mSourcePort(pSourcePort),
mData(new QVector<float>)
{
for( unsigned int i = 0; i < mDataSize; i++)
mData->append(.0);
}
DataReceiver::~DataReceiver(){
mTcpSocket->disconnectFromHost();
mTcpSocket->waitForDisconnected();
delete mTcpSocket;
delete mData;
}
// Network Management
bool DataReceiver::connectToHost(void){
connect(mTcpSocket, SIGNAL(connected()), this, SLOT(onConnect()));
connect(mTcpSocket, SIGNAL(disconnected()), this, SLOT(onDisconnect()));
connect(mTcpSocket, SIGNAL(bytesWritten(qint64)), this, SLOT(onBytesWritten(qint64)));
connect(mTcpSocket, SIGNAL(readyRead()), this, SLOT(onDataReceived()));
qDebug() << "connecting...";
//emit mTcpSocket->readyRead(); // For testing. We correctly trigger the onDataReceived slot
mTcpSocket->open(QAbstractSocket::ReadWrite);
mTcpSocket->connectToHost(getSourceAddress(), getSourcePort());
if(!mTcpSocket->waitForConnected(1000))
{
qDebug() << "Error: " << mTcpSocket->errorString();
return false;
}
mTcpSocket->write("Hello ?"); // Test
return true;
}
// Getters
QVector<float> *DataReceiver::getData(void) const{
return mData;
}
QTcpSocket *DataReceiver::getTcpSocket(void) const{
return mTcpSocket;
}
QString DataReceiver::getSourceAddress(void) const{
return mSourceAddress;
}
unsigned int DataReceiver::getSourcePort(void) const{
return mSourcePort;
}
// Setters
void DataReceiver::setData(const QVector<float> *pData){
// Not yet implemented
Q_UNUSED(pData);
}
void DataReceiver::setSourceAddress(const QString &pSourceAddress){
mSourceAddress = pSourceAddress;
}
void DataReceiver::setSourcePort(const unsigned int &pSourcePort){
mSourcePort = pSourcePort;
}
// Public Slots
void DataReceiver::onConnect(){
qDebug() << "connected...";
}
void DataReceiver::onDisconnect(){
qDebug() << "disconnected...";
}
void DataReceiver::onBytesWritten(qint64 bytes){
qDebug() << bytes << " bytes written...";
}
// Private Slots
void DataReceiver::onDataReceived(){
// Not yet implemented, code is for testing
qDebug() << "onDataReceived called !";
while(mTcpSocket->bytesAvailable()){
qInfo() << mTcpSocket->read(mTcpSocket->bytesAvailable());
qDebug() << mTcpSocket->readAll();
}
}
// Private Methods
void DataReceiver::decodeData(const QByteArray &pMessage){
// Not yet implemented
Q_UNUSED(pMessage);
}
The mData/mDataSize are here for future usage, and are thus to be ignored here.
Sender
To send data, I tried using netcat :
cat testfile.txt | nc 127.0.0.1 51470
I also created a DataSender class, w/ the same structure as the DataReceiver class.
According to Wireshark, both methods seem to write data via TCP.
DataSender class
datasender.h
class DataSender : QObject
{
Q_OBJECT
public:
// Con/Destructors
explicit DataSender(QObject *parent = nullptr);
~DataSender();
// Network Management
bool connectToHost(void);
void sendData(void) const;
// Getters
QString getDestinationAddress(void) const;
unsigned int getDestinationPort(void) const;
// Setters
void setDestinationAddress(const QString &pDestinationAddress);
void setDestinationPort(const unsigned int &pDestinationPort);
signals:
public slots:
void onConnect();
void onDisconnect();
void onBytesWritten(qint64 bytes);
void onDataReceived();
private:
QTcpSocket *mTcpSocket;
QString mDestinationAddress;
unsigned int mDestinationPort;
};
datasender.cpp
DataSender::DataSender(QObject *parent) :
QObject(parent),
mTcpSocket(new QTcpSocket(this)),
mDestinationAddress("127.0.0.1"),
mDestinationPort(50911)
{
}
DataSender::~DataSender(){
mTcpSocket->disconnectFromHost();
mTcpSocket->waitForDisconnected();
delete mTcpSocket;
}
// Network Management
bool DataSender::connectToHost(void){
connect(mTcpSocket, SIGNAL(connected()), this, SLOT(onConnect()));
connect(mTcpSocket, SIGNAL(disconnected()), this, SLOT(onDisconnect()));
connect(mTcpSocket, SIGNAL(bytesWritten(qint64)), this, SLOT(onBytesWritten(qint64)));
connect(mTcpSocket, SIGNAL(readyRead()), this, SLOT(onDataReceived()));
qDebug() << "connecting...";
mTcpSocket->setSocketOption(QAbstractSocket::KeepAliveOption, true);
mTcpSocket->connectToHost(getDestinationAddress(), getDestinationPort());
if(!mTcpSocket->waitForConnected(1000))
{
qDebug() << "Error: " << mTcpSocket->errorString();
return false;
}
return true;
}
void DataSender::sendData(void) const{
QByteArray lData("Hello, this is DataSender ! Do you copy ? I repeat, do you copy ?");
QByteArray lTemp;
QDataStream lData2(&lTemp, QIODevice::ReadWrite);
lData2 << lData.size();
if(mTcpSocket->state() == QAbstractSocket::ConnectedState)
{
mTcpSocket->write(lTemp); //write size of data
mTcpSocket->write(lData); //write the data itself
mTcpSocket->waitForBytesWritten();
}
}
// Getters
QString DataSender::getDestinationAddress(void) const{
return mDestinationAddress;
}
unsigned int DataSender::getDestinationPort(void) const{
return mDestinationPort;
}
// Setters
void DataSender::setDestinationAddress(const QString &pDestinationAddress){
mDestinationAddress = pDestinationAddress;
}
void DataSender::setDestinationPort(const unsigned int &pDestinationPort){
mDestinationPort = pDestinationPort;
}
// Public Slots
void DataSender::onConnect(){
qDebug() << "connected...";
}
void DataSender::onDisconnect(){
qDebug() << "disconnected...";
}
void DataSender::onBytesWritten(qint64 bytes){
qDebug() << bytes << " bytes written...";
}
void DataSender::onDataReceived(){
// Not yet implemented, code is for testing
qDebug() << "onDataReceived called !";
//while(mTcpSocket->bytesAvailable()){
//qInfo() << mTcpSocket->read(mTcpSocket->bytesAvailable());
//qDebug() << mTcpSocket->readAll();
//}
}
Client side Main
// Main routine ---------------------------------
int main(int argc, char **argv){
// Initializing application.
QApplication lApplication(argc, argv);
CLIENT::DataReceiver dataReceiver;
dataReceiver.connectToHost();
return lApplication.exec();
}
Server side Main
// Main routine ---------------------------------
int main(int argc, char **argv){
QApplication lApplication(argc, argv);
SERVER::DataSender lDataSender;
lDataSender.connectToHost();
lDataSender.sendData();
return lApplication.exec();
}
Behavior
Basically, when I run the DataReceiver class in the main, I get the following behavior :
connecting...
connected...
7 bytes written... # This is the write("Hello ?") I inserted just for testing
# "connected..." may occur after "7 bytes written...",
# I don't remember, I am not in front of my computer right now.
Conclusion
I believe I have simply forgotten something important, or that there is something I do not know of. This is a personal project, so a little exterior insight would be welcome !
Thank you very much !
Clovel
A QTcpSocket can communicate with another QTcpSocket, but in order for this to happen, an initial connection must be made, utilising a client-server model.
It's not possible for a QTcpSocket to listen for an incoming connection. Instead, QTcpServer can be used (or QLocalServer, if using QLocalSocket).
The QTcpSocket is set to listen on a port for incoming connections and when a connection is made, will emit a signal to notify of the newConnection. Calling nextPendingConnection returns the QTcpSocket for the server side, allowing it to communicate with client from the incoming connection.
So this is OP, and here is the solution I implemented. I'd like to thank #TheDarkKnight & #G.M. for their clarifications.
Solution
Instead of using a QTcpSocket for my DataReceiver class, I used a combination of QTcpSocket AND QTcpServer. Basically, the QTcpServer listens for new connections & set the QTcpSocket when a connection is made.
Then, the class simply connects the QTcpSocket signal readyRead() to a personal slot. Here is the implementation of the class :
DataReceiver class
datareceiver.h
class DataReceiver : QObject
{
Q_OBJECT
public:
// Con/Destructors
explicit DataReceiver(QObject *parent = nullptr);
explicit DataReceiver(const QString &pSourceAddress,
const unsigned int &pSourcePort,
QObject *parent = nullptr);
~DataReceiver();
// Getters
QVector<float> *getData(void) const;
QTcpServer *getTcpServer(void) const;
QString getSourceAddress(void) const;
unsigned int getSourcePort(void) const;
// Setters
void setData(const QVector<float> *pData);
void setSourceAddress(const QString &pSourceAddress);
void setSourcePort(const unsigned int &pSourcePort);
signals:
public slots:
void onConnect();
void onDisconnect();
void onBytesWritten(qint64 bytes);
void onDataReceived();
void onNewConnection();
private:
void decodeData(const QByteArray &pMessage);
QTcpServer *mTcpServer;
QTcpSocket *mTcpSocket;
QString mSourceAddress;
unsigned int mSourcePort;
const unsigned int mDataSize = 30;
QVector<float> *mData;
};
datareceiver.cpp
// Con/Destructors
DataReceiver::DataReceiver(QObject *parent) :
QObject(parent),
mTcpServer(new QTcpServer(this)),
mSourceAddress("127.0.0.1"),
mSourcePort(51470),
mData(new QVector<float>)
{
for( unsigned int i = 0; i < mDataSize; i++)
mData->append(.0);
connect(mTcpServer, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
if(!mTcpServer->listen(QHostAddress(getSourceAddress()), getSourcePort()))
qDebug() << "<DataReceiver> Server could not start. ";
else
qDebug() << "<DataReceiver> Server started !";
}
DataReceiver::DataReceiver(const QString &pSourceAddress,
const unsigned int &pSourcePort,
QObject *parent) :
QObject(parent),
mTcpServer(new QTcpServer(this)),
mSourceAddress(pSourceAddress),
mSourcePort(pSourcePort),
mData(new QVector<float>)
{
for( unsigned int i = 0; i < mDataSize; i++)
mData->append(.0);
connect(mTcpServer, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
if(!mTcpServer->listen(QHostAddress(getSourceAddress())), getSourcePort())
qDebug() << "<DataReceiver> Server could not start. ";
else
qDebug() << "<DataReceiver> Server started !";
}
DataReceiver::~DataReceiver(){
delete mTcpServer;
delete mData;
}
// Getters
QVector<float> *DataReceiver::getData(void) const{
return mData;
}
QTcpServer *DataReceiver::getTcpServer(void) const{
return mTcpServer;
}
QString DataReceiver::getSourceAddress(void) const{
return mSourceAddress;
}
unsigned int DataReceiver::getSourcePort(void) const{
return mSourcePort;
}
// Setters
void DataReceiver::setData(const QVector<float> *pData){
// Not yet implemented
Q_UNUSED(pData);
}
void DataReceiver::setSourceAddress(const QString &pSourceAddress){
mSourceAddress = pSourceAddress;
}
void DataReceiver::setSourcePort(const unsigned int &pSourcePort){
mSourcePort = pSourcePort;
}
// Public Slots
void DataReceiver::onConnect(){
qDebug() << "QTcpSocket connected...";
}
void DataReceiver::onDisconnect(){
qDebug() << "QTcpSocket disconnected...";
disconnect(mTcpSocket, SIGNAL(readyRead()), this, SLOT(onDataReceived()));
disconnect(mTcpSocket, SIGNAL(disconnected()), this, SLOT(onDisconnect()));
}
void DataReceiver::onBytesWritten(qint64 bytes){
qDebug() << bytes << " bytes written to QTcpSocket...";
}
void DataReceiver::onDataReceived(){
// Not yet implemented, code is for testing
qDebug() << "onDataReceived called !";
while(mTcpSocket->bytesAvailable()){
qInfo() << mTcpSocket->read(mTcpSocket->bytesAvailable());
qDebug() << mTcpSocket->readAll();
}
}
void DataReceiver::onNewConnection(){
qDebug() << "onNewConnection called !";
mTcpSocket = mTcpServer->nextPendingConnection();
connect(mTcpSocket, SIGNAL(readyRead()), this, SLOT(onDataReceived()));
connect(mTcpSocket, SIGNAL(disconnected()), this, SLOT(onDisconnect()));
}
// Private Methods
void DataReceiver::decodeData(const QByteArray &pMessage){
// Not yet implemented
Q_UNUSED(pMessage);
}
I try to get serial communication working, but struggle at waiting for the response. The logic should be to send data, wait for a response, get the response and then repeat.
To speed up my code and preventing it from blocking other independent parts of the code I run the serial communication in a separate thread.
The problem is in the send function, where I always get a "Timeout" message instead of an "Success" message.
serial.h
#ifndef SERIAL_H
#define SERIAL_H
#include <QThread>
#include <QDebug>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QStringList>
class Serial : public QThread
{
Q_OBJECT
public:
Serial();
bool send(QString text);
bool connectSerial();
void disconnectSerial();
signals:
void dataReceived(QByteArray receivedData);
protected:
void run();
private slots:
void onGetData();
private:
void readAngles();
QSerialPort qsp;
QSerialPortInfo qspi;
QByteArray receivedData;
};
#endif // SERIAL_H
serial.cpp
#include "serial.h"
Serial::Serial()
{
connect(&qsp, SIGNAL(readyRead()), this, SLOT(onGetData()));
}
bool Serial::send(QString text)
{
if (qsp.isWritable()) {
QByteArray buffer = text.toLatin1();
if (buffer.size() != qsp.write(buffer))
qDebug() << "Send does not work";
qsp.flush();
if(!qsp.waitForReadyRead(2500)) {
qDebug() << "Timeout";
} else {
qDebug() << "Success";
}
return true;
} else {
return false;
}
}
void Serial::onGetData()
{
qDebug() << "onGetData called";
qDebug() << qsp.readAll();
}
bool Serial::connectSerial(int port)
{
qDebug() << qspi.availablePorts().count();
for(int i=0; i<qspi.availablePorts().count(); i++) {
qDebug() << qspi.availablePorts().at(i).description();
}
qsp.setPort(qspi.availablePorts().at(port));
qsp.setBaudRate(QSerialPort::Baud9600);
qsp.setDataBits(QSerialPort::Data7);
qsp.setFlowControl(QSerialPort::NoFlowControl);
qsp.setParity(QSerialPort::NoParity);
qsp.setStopBits(QSerialPort::OneStop);
return qsp.open(QIODevice::ReadWrite);
}
/**
* #brief Disconnecting from serial port.
*/
void Serial::disconnectSerial()
{
if (qsp.isOpen()) {
qsp.close();
}
}
void Serial::readAngles()
{
send("012345");
}
I created a small server/client application, and for testing I put the server/client function into a separate application.
The main client functions are
Client::Client(QString purpose) : networkSession(0)
{
Client::purpose = purpose;
tcpSocket = new QTcpSocket;
Client::blockSize = 0;
connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readData()));
connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(displayError(QAbstractSocket::SocketError)));
QNetworkConfigurationManager manager;
if (manager.capabilities() & QNetworkConfigurationManager::NetworkSessionRequired)
{
// Get saved network configuration
QSettings settings(QSettings::UserScope, QLatin1String("QtProject"));
settings.beginGroup(QLatin1String("QtNetwork"));
const QString id = settings.value(QLatin1String("DefaultNetworkConfiguration")).toString();
settings.endGroup();
// If the saved network configuration is not currently discovered use the system default
QNetworkConfiguration config = manager.configurationFromIdentifier(id);
if ((config.state() & QNetworkConfiguration::Discovered) !=
QNetworkConfiguration::Discovered) {
config = manager.defaultConfiguration();
}
networkSession = new QNetworkSession(config, this);
connect(networkSession, SIGNAL(opened()), this, SLOT(sessionOpened()));
}
qDebug() << "Client set up, waiting";
}
void Client::connectToServer(QString ipAddr, quint32 port)
{
qDebug() << "Connecting to Host on port " << port << ' ' << (quint16)port;
tcpSocket->connectToHost(ipAddr, port);
emit this->connectionResult((tcpSocket->state() == QAbstractSocket::UnconnectedState)?false:true);
if (tcpSocket->waitForConnected(1000))
qDebug("Connected!");
qDebug() << "Am I connected" << tcpSocket->state();
std::cout << "Am I not connected" << tcpSocket->state();
}
and the server-functions:
Server::Server(QString ipAddr, quint32 port, QString purpose)
: tcpServer(0), networkSession(0)
{
Server::clientConnection = NULL;
Server::purpose = purpose;
Server::port = port;
QNetworkConfigurationManager manager;
if (manager.capabilities() & QNetworkConfigurationManager::NetworkSessionRequired) {
// Get saved network configuration
QSettings settings(QSettings::UserScope, QLatin1String("QtProject"));
settings.beginGroup(QLatin1String("QtNetwork"));
const QString id = settings.value(QLatin1String("DefaultNetworkConfiguration")).toString();
settings.endGroup();
// If the saved network configuration is not currently discovered use the system default
QNetworkConfiguration config = manager.configurationFromIdentifier(id);
if ((config.state() & QNetworkConfiguration::Discovered) !=
QNetworkConfiguration::Discovered) {
config = manager.defaultConfiguration();
}
networkSession = new QNetworkSession(config, this);
connect(networkSession, SIGNAL(opened()), this, SLOT(sessionOpened()));
//statusLabel->setText(tr("Opening network session."));
networkSession->open();
} else {
sessionOpened();
}
//connect(tcpServer, SIGNAL(newConnection()), this, SLOT(sendFortune()));
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(openNewConnection()));
//connect(tcpServer, &QTcpServer::newConnection, this, &Server::openNewConnection);
}
void Server::sessionOpened()
{
// Save the used configuration
if (networkSession) {
QNetworkConfiguration config = networkSession->configuration();
QString id;
if (config.type() == QNetworkConfiguration::UserChoice)
id = networkSession->sessionProperty(QLatin1String("UserChoiceConfiguration")).toString();
else
id = config.identifier();
QSettings settings(QSettings::UserScope, QLatin1String("QtProject"));
settings.beginGroup(QLatin1String("QtNetwork"));
settings.setValue(QLatin1String("DefaultNetworkConfiguration"), id);
settings.endGroup();
}
tcpServer = new QTcpServer(this);
if (!tcpServer->listen(QHostAddress::Any, Server::port)) {
return;
}
qDebug() << "Server listening on: " << tcpServer->serverPort();
//! [0]
QString ipAddress;
QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
// use the first non-localhost IPv4 address
for (int i = 0; i < ipAddressesList.size(); ++i) {
if (ipAddressesList.at(i) != QHostAddress::LocalHost &&
ipAddressesList.at(i).toIPv4Address()) {
ipAddress = ipAddressesList.at(i).toString();
break;
}
}
// if we did not find one, use IPv4 localhost
if (ipAddress.isEmpty())
ipAddress = QHostAddress(QHostAddress::LocalHost).toString();
//! [1]
}
void Server::openNewConnection(void)
{
qDebug() << "New conn incoming!";
Server::clientConnection = tcpServer->nextPendingConnection();
QVariant ipAddr_QVar(clientConnection->peerAddress().toString());
qDebug() << "Got new connection!";
emit gotNewConnection(ipAddr_QVar);
}
The main problem here is that even if I get a "Connected" from
if (tcpSocket->waitForConnected(1000))
qDebug("Connected!");
qDebug() << "Am I connected" << tcpSocket->state();
in the client function, but the server function openNewConnection() never gets called. Why? How can I find the bug?
If a minimal working example is necessary, I can provide the whole code, but here I just wanted to provide the most important functions.
Edit:
client.h:
#ifndef CLIENT_H
#define CLIENT_H
//#include <QDialog>
#include <iostream>
#include <QTcpSocket>
class QComboBox;
class QDialogButtonBox;
class QLabel;
class QLineEdit;
class QPushButton;
class QTcpSocket;
class QNetworkSession;
class Client : public QObject
{
Q_OBJECT
private:
QTcpSocket *tcpSocket;
QString currentFortune;
quint16 blockSize;
QPair<QString, QPair<QString, QVariant> > data;
QNetworkSession *networkSession;
QString purpose;
signals:
void gotData(QPair<QString, QPair<QString, QVariant> >);
void noConnection(void);
void connectionResult(bool);
void isDisconnect(bool);
public slots:
void displayError(QAbstractSocket::SocketError socketError);
void sessionOpened();
void getInfo();
void readData();
void connectToServer(QString ipAddr, quint32 port);
void disconnectFromServer();
private:
public:
Client(QString purpose);
};
#endif // CLIENT_H
server.h:
#ifndef SERVER_H
#define SERVER_H
#include <QTcpServer>
#include <iostream>
//#include <QtTest/QTest>
#include <QSignalSpy>
#include <QTcpSocket>
#include <QDebug>
//#include <QMessageBox>
#include <QNetworkInterface>
#include <typeinfo>
#include <QStringList>
//#include <QSignalSpy>
QT_BEGIN_NAMESPACE
class QTcpServer;
class QNetworkSession;
QT_END_NAMESPACE
class Server : public QObject
{
Q_OBJECT
public slots:
void sessionOpened();
void sendFortune(void);
void sendData(QPair<QString, QPair<QString, QVariant> > data);
void sendFile(QVariant fileName);
void disconnectServer(void);
void openNewConnection(void);
signals:
void gotNewConnection(QVariant);
private:
QString purpose;
QTcpServer *tcpServer;
QString ipAddr;
quint32 port;
QTcpSocket *clientConnection;
quint32 BlockSize;
bool firstTime;
QSignalSpy * m_pSignalSpy;
QStringList fortunes;
QNetworkSession *networkSession;
//QNetworkConfiguration config;
public:
Server(QString ipAddr, quint32 port, QString purpose = "");
};
#endif // SERVER_H
The problem lies with your QNetworkSession, you have declared it in scope. Meaning once you leave that function the QNetworkSession gets destroyed. A destroyed object cannot emit a signal. Maybe make it a member variable or construct it in your header.
I am trying to implement a simple state machine within a console application. The signals that are supposed to trigger the state transitions are being emitted, however, the state machine is not reacting to those signals.
This state machine works perfectly when running within a QApplication (i.e. a GUI application), however I am wanting to develop a console application. I suspect there is an issue in the way I have implemented the event loop, as the QStateMachine is not emitting the started() signal.
What is the correct way to execute the application in order for the state machine to function correctly?
main.cpp:
#include <QCoreApplication>
#include "test.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Test test;
QMetaObject::invokeMethod( &test, "Run", Qt::QueuedConnection );
return a.exec();
}
test.h:
#ifndef TEST_H
#define TEST_H
#include <QObject>
#include <QStateMachine>
class Test : public QObject
{
Q_OBJECT
public:
explicit Test(QObject *parent = 0) : QObject(parent) {}
public slots:
void Run();
signals:
void stateChanged();
void debugSignal();
private:
void buildStateMachine();
QStateMachine machine;
private slots:
void runS1();
void runS2();
void runS3();
void debugSlot();
};
#endif // TEST_H
test.cpp:
#include "test.h"
#include <QDebug>
void Test::Run()
{
buildStateMachine();
QTextStream qin(stdin);
while (true)
{
QString line = qin.readLine();
qDebug() << "line: " << line;
if (line == "A")
{
qDebug() << "emit stateChanged signal";
emit stateChanged();
}
else if (line == "B")
{
qDebug() << "emit debugSignal";
emit debugSignal();
}
}
}
void Test::buildStateMachine()
{
connect(&machine, SIGNAL(started()), this, SLOT(debugSlot())); // doesn't seem to get triggered... (why is machine not starting?)
connect(this, SIGNAL(debugSignal()), this, SLOT(debugSlot())); // works as expected
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
QState *s3 = new QState(&machine);
s1->addTransition(this, SIGNAL(stateChanged()), s2);
s2->addTransition(this, SIGNAL(stateChanged()), s3);
s3->addTransition(this, SIGNAL(stateChanged()), s1);
connect(s1, SIGNAL(entered()), this, SLOT(runS1())); // these are never triggered
connect(s2, SIGNAL(entered()), this, SLOT(runS2()));
connect(s3, SIGNAL(entered()), this, SLOT(runS3()));
s1->assignProperty(&machine, "state", 1);
s2->assignProperty(&machine, "state", 2);
s3->assignProperty(&machine, "state", 3);
machine.setInitialState(s1);
machine.start();
}
void Test::runS1()
{
qDebug() << "entered state S1";
}
void Test::runS2()
{
qDebug() << "entered state S2";
}
void Test::runS3()
{
qDebug() << "entered state S3";
}
void Test::debugSlot()
{
qDebug() << "slot was triggered!";
}
Solved my problem. The issue was caused by the infinite while loop. The slot can only be called once the Run function ends, which obviously never occurs.
Here is a working solution. The change to the Qt 5 style signals and slots syntax is optional. If main.cpp is kept as-is from the version shown in the question above, the application will not quit correctly.
main.cpp:
#include <QCoreApplication>
#include <QTimer>
#include "test.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Test test;
QObject::connect(&test, &Test::finished, &a, &QCoreApplication::quit, Qt::QueuedConnection);
QTimer::singleShot(0, &test, &Test::Run);
return a.exec();
}
test.h:
#ifndef TEST_H
#define TEST_H
#include <QObject>
#include <QStateMachine>
class Test : public QObject
{
Q_OBJECT
public:
explicit Test(QObject *parent = 0);
signals:
void next_state();
void finished();
private:
void buildStateMachine();
QStateMachine machine;
public slots:
void Run();
void runS1();
void runS2();
void runS3();
void debugSlot();
};
#endif // TEST_H
test.cpp:
#include "test.h"
#include <iostream>
#include <QTextStream>
Test::Test(QObject * parent) : QObject(parent)
{
buildStateMachine();
}
void Test::Run()
{
QTextStream qin(stdin);
std::cout << "line: ";
QString line = qin.readLine();
if (line == "A")
{
std::cout << "emit stateChanged signal" << std::endl;
emit next_state();
}
else if (line == "q")
{
emit finished();
}
else
{
Run();
}
}
void Test::buildStateMachine()
{
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
QState *s3 = new QState(&machine);
s1->addTransition(this, SIGNAL(next_state()), s2);
s2->addTransition(this, SIGNAL(next_state()), s3);
s3->addTransition(this, SIGNAL(next_state()), s1);
connect(s1, &QState::entered, this, &Test::runS1);
connect(s2, &QState::entered, this, &Test::runS2);
connect(s3, &QState::entered, this, &Test::runS3);
machine.setInitialState(s1);
machine.start();
}
void Test::runS1()
{
std::cout << "entered state S1" << std::endl;
Run();
}
void Test::runS2()
{
std::cout << "entered state S2" << std::endl;
Run();
}
void Test::runS3()
{
std::cout << "entered state S3" << std::endl;
Run();
}
void Test::debugSlot()
{
std::cout << "debug slot was triggered!" << std::endl;
Run();
}