I have already create a Modbus server program in my PC with the Modbus library from Qt, using as example the code that is provided for the platform (I don't want a program with graphical interface so my program is not totally like the example) and I'm capable of read and write values in the holding registers using a simulator (I can see the exchange of information is ok in the simulation program).
My problem is that, once the simulator has written a new value in a holding register of my PC, I need to write this value in a file and for doing this I need to access the value that has been written in the ModbusDataUnit
that I created.
I'm trying to do this using the method of the class
QModbusServer
.
bool QModbusServer::data(QModbusDataUnit::Register Type, quint16 address, quint16 *data) const
but the program crash when the signal DataWritten
calls the slot that I defined in which I'm using the method.
Can anyone tell me why this is not working and how to use this method? Thanks!!
I share the code (see myserver.cpp, the slot named showData please)
myserver.h
#ifndef MYSERVER_H
#define MYSERVER_H
#include <QObject>
#include <QModbusDevice>
#include <QModbusTcpServer>
class myServer: public QObject
{
Q_OBJECT
public:
explicit myServer(QObject *parent=0);
~myServer();
void CreateServer();
signals:
void error_signal(QString errorString);
public slots:
private:
QModbusTcpServer * server;
private slots:
void handleDeviceError(QModbusDevice::Error newError);
void onStateChanged(int state);
bool ShowData(QModbusDataUnit::RegisterType table, int address, quint16 size);
};
#endif // MYSERVER_H
myserver.cpp
#include "myserver.h"
#include <QModbusServer>
#include <QModbusDevice>
#include <QModbusTcpServer>
#include <QVariant>
#include <QModbusDataUnit>
#include <QDebug>
#include <QVector>
#include<iostream>
using namespace std;
myServer::myServer(QObject *parent) : //Definimos el constructor de la clase
QObject(parent), server(nullptr) //El constructor directamente llama a la función para crear el servidor y ponerlo a la escucha
{
CreateServer();
}
myServer::~myServer() //Definimos el destructor de la clase
{
if (server)
{
server->disconnectDevice();
}
delete server;
}
void myServer::CreateServer() //Definimos la función para crear el servidor
{
if (server)
{
server->disconnect();
delete server;
server=nullptr;
}
QModbusTcpServer* server;
server= new QModbusTcpServer();
if(!server) //Comprobamos que se crea bien el servidor
{
qDebug()<<"Could not create modbus slave";
}
else
{
//Ahora hay que modelar unos registros de memoria, como si fueran los del plc
QModbusDataUnitMap reg; //Creamos la unidad de datos, se usa para las operaciones de lectura y escritura
//Los tipos de registros soportados son los que aparecen en la enumeración, los insertamos en la unidad de memoria
reg.insert(QModbusDataUnit::Coils, { QModbusDataUnit::Coils, 0, 10 }); //This type of data can be alterable by an application program.
reg.insert(QModbusDataUnit::DiscreteInputs, { QModbusDataUnit::DiscreteInputs, 0, 10 }); //This type of data can be provided by an I/O system.
reg.insert(QModbusDataUnit::InputRegisters, { QModbusDataUnit::InputRegisters, 0, 10 }); //This type of data can be provided by an I/O system.
reg.insert(QModbusDataUnit::HoldingRegisters, { QModbusDataUnit::HoldingRegisters, 0, 10 }); //This type of data can be alterable by an application program.
//Sets the registered map structure for requests from other ModBus clients to map.
//The register values are initialized with zero. Returns true on success; otherwise false
//Calling this function discards any register value that was previously set.
bool ok;
ok=server->setMap(reg);
if(!ok) //Comprobamos que el mapa de datos se ha creado correctamente
{
qDebug() << "SetMap error";
}
else
{
cout<<"Unit map created"<<endl;
}
//Conectamos las señales con sus slots
//Nota: los slots no se llaman, se activan solos si se emite la señal que los llama
//Estado de la conexión. Asociamos la señal stateChanged con el slot onStateChanged.
connect(server, &QModbusServer::stateChanged, this, &myServer::onStateChanged);
// Error que ocurre: cuando ocurra un error (se emita la señal errorOccurred), se activa el slot y muestra por pantalla el error.
connect(server, &QModbusServer::errorOccurred, this, &myServer::handleDeviceError);
//Cada vez que se escribe un valor en la memoria se emite la señal dataWritten (que es una señal predefinida) Y lo que hacemos
//es conectar esa señal con que aparezcan ciertos datos en pantalla
connect(server, &QModbusServer::dataWritten, [&] (QModbusDataUnit::RegisterType table, int address, quint16 size)
{
qDebug() << "onDataWritten: table: " << table
<< " | " << "address: " << address
<< " | " << "size: " << size
<< endl;
}
);
connect(server, &QModbusServer::dataWritten,this, &myServer::ShowData);
//Indicamos los parámetros de la conexión
server->setConnectionParameter(QModbusDevice::NetworkAddressParameter,"192.168.0.121"); //Aquí hay que meter la ip del SERVIDOR (la nuestra)
server->setConnectionParameter(QModbusDevice::NetworkPortParameter,502); //Puerto por el que se comunica
server->setServerAddress(1); //Esta dirección es por si hay más servidores
qDebug()<<"Modbus slave was created successfully";
}
//Esta función conecta el servidor, quiere decir que lo pone a la escucha. La función devuelve el estado de conexión por pantalla.
server->connectDevice();
}
void myServer::handleDeviceError(QModbusDevice::Error newError)
{
if(newError == QModbusDevice::NoError || !server)
{
emit error_signal(server->errorString()); //en el ejemplo no se emite ninguna señal
qDebug() <<server->errorString();
cout<<"Esta emitiendo señal de error"<<endl;
}
}
//El slot onStateChanged se activa cuando se emite la señal stateChanged (que es una señal predefinida). Saca por pantalla el estado de la conexión.
void myServer::onStateChanged(int state)
{
if(state == QModbusDevice::UnconnectedState)
{
// emit stateChanged_signal(0); //NO HACE FALTA EMITIR NINGUNA SEÑAL, ADEMÁS QUE ÉSTA NO ESTÁ ASOCIADA CON NINGÚN SLOT
qDebug()<<QString("OFF state");
qDebug() <<QString(server->errorString()+"prueba");
}
else if(state == QModbusDevice::ConnectedState)
{
//emit stateChanged_signal(1);
qDebug()<<QString("ON state");
}
}
bool myServer::ShowData(QModbusDataUnit::RegisterType table, int address, quint16 size)
{
for(int i=0; i<size; i++)
{
quint16 value;
value=0;
bool key_1;
switch (table)
{
case 2:
server->data(QModbusDataUnit::Coils, address+i, &value);
qDebug()<<value<<endl;
break;
case 4:
cout<<"Por lo menos sabe que es el caso 4"<<endl;
key_1=server->data(QModbusDataUnit::HoldingRegisters, address+i, &value); //THIS FUNCION MAKES THE PROGRAM CRASH
if(!key_1)
{
qDebug()<<"esto no funciona"<<endl;
}
break;
default:
qDebug()<<QString("Could not get value");
break;
}
}
return true;
}
main.cpp
#include <QCoreApplication>
#include <QLoggingCategory>
#include<iostream>
using namespace std;
#include <QObject>
#include <QDebug>
#include <QVector>
#include <QtSerialBus>
#include <QModbusClient>
#include <QModbusDataUnit>
#include <QModbusResponse>
#include <QModbusDevice>
#include "myclient.h"
#include "myserver.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//MyClient mClient;
myServer mServer;
return a.exec();
}
I'm trying to use the QModbusServer::data method as in the Qt ModbusServer example but the example is to large and It's contain a lot of code about Widgets. I'm not an expert and I get loss when trying to diferenciate the widgets part from the modbus part.