modbus communication decrease wait time to timeout - c++

I'm writing a C++ app that will need to connect to various PLCs over modbus, the IPs of these PLCs is given by user input. Currently when the user puts in an IP that cannot be connected to, my program hangs for around 2 minutes in an attempt to connect and having my app hang for 2 minutes is not an option.
An example program illustrates the issue and my attempted fixes:
#include <modbus/modbus.h>
#include <string>
#include <errno.h>
#include <iostream>
#define PRINT_TIMEVAL(timeval) std::cout << "timeval sec: " << timeval.tv_sec << " usec: " << timeval.tv_usec << std::endl;
int main()
{
std::string ip = "192.168.2.5";
int port = 502;
int slaveNum = 1;
int address = 1;
int nb = 1;
struct timeval currentTimeout;
struct timeval responseTimeout;
responseTimeout.tv_sec = 1;
responseTimeout.tv_usec = 0;
struct timeval byteTimeout;
byteTimeout.tv_sec = 1;
byteTimeout.tv_usec = 0;
modbus_t *mb = modbus_new_tcp(ip.c_str(), port);
modbus_set_debug(mb, true);
modbus_set_error_recovery(mb, MODBUS_ERROR_RECOVERY_NONE);
modbus_flush(mb);
modbus_set_slave(mb, slaveNum);
modbus_get_response_timeout(mb, &currentTimeout);
PRINT_TIMEVAL(currentTimeout);
modbus_set_response_timeout(mb, &responseTimeout);
modbus_get_response_timeout(mb, &currentTimeout);
PRINT_TIMEVAL(currentTimeout);
modbus_get_byte_timeout(mb, &currentTimeout);
PRINT_TIMEVAL(currentTimeout);
modbus_set_byte_timeout(mb, &byteTimeout);
modbus_get_byte_timeout(mb, &currentTimeout);
PRINT_TIMEVAL(currentTimeout);
std::cout << "About to connect to " << ip << std::endl;
int errno;
if((errno = modbus_connect(mb)))
{
std::cout << "Error when connecting: " << modbus_strerror(errno) << std::endl;
}
std::cout << "Done connecting to " << ip << std::endl;
modbus_close(mb);
modbus_free(mb);
return 0;
}
As you can see I've tried setting both the response and byte timeout variables to 1 second (I've also tried 500 and 5000 microseconds). When I read the timeout values they have been set properly so I'm assuming that they don't have anything to do with the initial connection attempt. I've also tried explicitly setting the error recovery mode to none in case it was trying to reconnect on its own.
I would like something that will either stop modbus_connect after x amount of time or another command that will allow me to check to see if the IP is valid before attempting to connect through modbus, this would also need to timeout after a short amount of time.
I'm using libmodbus version 3.0.1-2

The issue was with my version of libmodbus (3.0.1), which is the current release version. In that version they were using the linux connect command but they weren't passing a NONBLOCKING flag, thus connect would become blocked for 2m7s. We resolved this issue by upgrading to libmodbus v3.1.1 which is marked as unstable but is not under active development (they're developing on v3.1.2). Unfortunately that version of libmodbus does not work for windows.

Use threads to listen for each device and push those messages into a queue that can be processed without holding up the other threads.

Related

Disable CPU package idle states in Windows from C++ code

I am successfully disabling CPU core C-states using this code (I’m working on Win10 and use Qt):
#include <QCoreApplication>
#include <QDebug>
#include "Windows.h"
extern "C" {
#include "powrprof.h"
}
#pragma comment(lib, "powrprof.lib")
int main()
{
const DWORD DISABLED = 1;
const DWORD ENABLED = 0;
GUID *scheme;
int error;
error = PowerGetActiveScheme(NULL, &scheme);
qDebug() << "PowerGetActiveScheme error code = " << error;
error = PowerWriteACValueIndex(NULL, scheme, &GUID_PROCESSOR_SETTINGS_SUBGROUP, &GUID_PROCESSOR_IDLE_DISABLE, DISABLED);
qDebug() << "PowerWriteACValueIndex error code = " << error;
error = PowerWriteDCValueIndex(NULL, scheme, &GUID_PROCESSOR_SETTINGS_SUBGROUP, &GUID_PROCESSOR_IDLE_DISABLE, DISABLED);
qDebug() << "PowerWriteDCValueIndex error code = " << error;
error = PowerSetActiveScheme(NULL, scheme);
qDebug() << "PowerSetActiveScheme error code = " << error;
return 0;
}
The reason behind this is that I am running an USB camera and figured out that I’m losing data packets when the processor enters idle modes. The code above works fine and overcomes this issue successfully. But it’s actually a bit too much (disabling all C states appears to be unnecessary). I made some tests with the vendor software of the camera and found out that during acquisition not the core C-states stop, but the package C-states (if it is of any interest, I posted the analysis of this problem in the answer here https://superuser.com/questions/1648849/monitor-used-usb-bandwidth-in-win10).
So my question is: Can I adapt the above code to only disable package idle states? In case that’s not possible, can I selectively disable core C-states?
Update:
Based on the suggestion of #1201ProgramAlarm I tried to use SetThreadPriority() as in the minimal example below:
#include <QDebug>
#include <windows.h>
#include <conio.h>
#include "processthreadsapi.h"
int main()
{
bool ok = false;
ok = SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
qDebug() << "SetPriorityClass ok = " << ok;
ok = SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
qDebug() << "SetThreadPriority ok = " << ok;
for (int i=1;i<100;i++) {
qDebug() << "Here I am in some dummy loop...";
if (_kbhit()) {
break;
}
Sleep(1000);
}
return 0;
}
Unfortunately, this doesn't help and when monitoring the cpu package idle states (using HWiNFO64) I see no effect (package goes still idle as before).
The system is shutting down the USB device to save power.
This link provides the solution USB system power issue.
Open the Device manager as administrator.
Find the camera.
On the "Power Management" tab, deselect "Allow the computer to turn off this device to save power".
This can be done programmatically if the device ID is known.

Paho MQTT (C++) client fails to connect to Mosquitto

I've got C++ code using the Paho MQTTPacket Embedded C++ library to connect to an MQTT broker. When that broker is io.adafruit.com, it works perfectly fine. But when it's my own Mosquitto instance running on my Raspberry Pi, the connection fails. I've narrowed it down to this line in MQTTClient.h, in the MQTT::Client::connect method:
// this will be a blocking call, wait for the connack
if (waitfor(CONNACK, connect_timer) == CONNACK)
The app hangs here for about 30 seconds, and then gets a result other than CONNACK (specifically 0 rather than 2).
I have tried both protocol version 3 (i.e. 3.1) and 4 (i.e. 3.1.1); same result.
My Mosquitto instance has no authentication or passwords set up. I've tried turning on debug messages in the Mosquitto log, but they're not showing anything useful. I'm at a loss. Why might I be unable to connect to Mosquitto from my C++ Paho code?
EDIT: Here's the client code... again, this works fine with Adafruit, but when I point it to my Mosquitto at localhost, it hangs as described. (I've elided the username and password -- I am sending them, but I really don't think those are the issue, since with mosquitto_pub or mosquitto_sub on the command line, I can connect regardless of this, since mosquitto is configured to allow anonymous connections.)
const char* host = "127.0.0.1";
int port = 1883;
const char* clientId = "ZoomBridge";
const char* username = "...";
const char* password = "...";
MQTT::QoS subsqos = MQTT::QOS2;
ipstack = new IPStack();
client = new MQTT::Client<IPStack, Countdown, 30000>(*ipstack);
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
data.willFlag = 1;
data.MQTTVersion = 3;
data.clientID.cstring = (char*)clientId;
data.username.cstring = (char*)username;
data.password.cstring = (char*)password;
data.keepAliveInterval = 20;
data.cleansession = 1;
int rc = ipstack->connect(host, port);
if (rc != MQTT::SUCCESS) {
cout << "Failed [1] (result " << rc << ")" << endl;
return rc;
}
rc = client->connect(data);
if (rc != MQTT::SUCCESS) {
cout << "Failed [2] (result " << rc << ")" << endl;
ipstack->disconnect();
return rc;
}
As hashed out in the comments.
It looks like you are setting the flag to indicate you want to set a Last Will and Testament for the client (data.willFlag = 1;) but then not passing any topic or payload for the LWT.
If you don't need the LWT then set the flag to 0 (or remove the line settings flag) as it should default to disabled.
Also worth pointing out to clarity, this is all with the Paho Embedded C++ MQTTPacket client not the full blown Paho C++ client.

AMQP-CPP RabbitMQ async event based consumer not consuming anything

I'm using the AMQ-CPP library (https://github.com/CopernicaMarketingSoftware/AMQP-CPP) to connect to an existing queue I've created but I'm unable to read anything. I've tested that the queue works using another library (https://github.com/alanxz/SimpleAmqpClient, it works and I consume messages), but it uses a polling approach and I need an event based one.
My code looks like (based on the provided example):
int main()
{
auto *poll = EV_DEFAULT;
// handler for libev (so we don't have to implement AMQP::TcpHandler!)
AMQP::LibEvHandler handler(poll);
// make a connection
AMQP::TcpConnection connection(&handler, AMQP::Address("amqp://localhost/"));
// we need a channel too
AMQP::TcpChannel channel(&connection);
// Define callbacks and start
auto messageCb = [&channel](
const AMQP::Message &message, uint64_t deliveryTag,
bool redelivered)
{
std::cout << "message received" << std::endl;
// acknowledge the message
channel.ack(deliveryTag);
processMessage(message.routingKey(), message.body());
};
// callback function that is called when the consume operation starts
auto startCb = [](const std::string &consumertag) {
std::cout << "consume operation started: " << consumertag << std::endl;
};
// callback function that is called when the consume operation failed
auto errorCb = [](const char *message) {
std::cout << "consume operation failed" << std::endl;
};
channel.consume("domoqueue")
.onReceived(messageCb)
.onSuccess(startCb)
.onError(errorCb);
// run the poll
ev_run(poll, 0);
// done
return 0;
}
I'm running the code in a Raspberry Pi having :
Linux raspberrypi 4.4.26-v7+ #915 SMP Thu Oct 20 17:08:44 BST 2016 armv7l GNU/Linux
What can be the problem? Probably I'm missing some configuration parameters for the queue... I've placed some debug traces and the channel creation does not take place. It blocks in the connection statement:
AMQP::TcpConnection connection(&handler, AMQP::Address("amqp://localhost/"));
cout << "I never show up" << endl;
// we need a channel too
AMQP::TcpChannel channel(&connection)
I've found my problem: I wasn't using the declareQueue() method! In fact, I had to use it but specifying the following parameters (the same as I did when I created the queue manually):
AMQP::Table arguments;
arguments["x-message-ttl"] = 120 * 1000;
// declare the queue
channel.declareQueue("domoqueue", AMQP::durable + AMQP::passive, arguments).onSuccess(callback);

libserial error: cannot set baud rate as 115200

I'm trying to communicate with an USB-uart module using Libserial.
Following is my code for initial part:
serial_port.Open("/dev/ttyUSB0");
if ( ! serial_port.good() )
{
std::cerr << "[" << __FILE__ << ":" << __LINE__ << "] "
<< "Error: Could not open serial port."
<< std::endl ;
exit(1) ;
}
serial_port.SetBaudRate( SerialStreamBuf::BAUD_115200 ) ;
if ( ! serial_port.good() )
{
std::cerr << "Error: Could not set the baud rate." <<
std::endl ;
exit(1) ;
}
When I run it on Ubuntu 12.04 and 13.04 with the same USB module, they all say
Error: Could not set the baud rate.
I did some tests and finally found this error would occur if I set the baud rate as or higher than 115200. It works well on 57600 and 19200.
But I'm wondering is there any possible way for me to set the baud rate as 115200?
I downloaded a serial test tool, it can work as the 115200(but I didn't checked the msg content, I just notice the transmit led is flash).
Or is it the hardware limit so I need to buy another module if I want a higher baud rate?
Thanks
===========
UPDATE:
There is no problem with the hardware. I tested it in Windows VS using 115200 and it works well. But it failed on two Ubuntu desktop(12.04 and 13.04).
I print the baudrate out after I set it
serial_port.SetBaudRate( SerialStreamBuf::BAUD_115200) ;
int rate = serial_port.BaudRate();
cout << SerialStreamBuf::BAUD_115200 << endl;
cout << rate << endl;
the result shows their values are the same, both are 4098.
Then I tried to comment all the .good() part with and after the SetBaudRate part, the program start successfully but the transmit LED doesn't flash. So I think there is really something wrong with the baudrate set so the serial initial failed, although the baudrate it returns is correct.
Now I have no idea what to do next...
in case you need to see all my
code
I'm guessing it's this bug, but haven't verified it.
http://ehc.ac/p/libserial/bugs/10/
Now in SerialStreamBuf.h
enum BaudRateEnum {
BAUD_50 = SerialPort::BAUD_50,
BAUD_75 = SerialPort::BAUD_75,
BAUD_110 = SerialPort::BAUD_110,
BAUD_134 = SerialPort::BAUD_134,
BAUD_150 = SerialPort::BAUD_150,
BAUD_200 = SerialPort::BAUD_200,
BAUD_300 = SerialPort::BAUD_300,
BAUD_600 = SerialPort::BAUD_600,
BAUD_1200 = SerialPort::BAUD_1200,
BAUD_1800 = SerialPort::BAUD_1800,
BAUD_2400 = SerialPort::BAUD_2400,
BAUD_4800 = SerialPort::BAUD_4800,
BAUD_9600 = SerialPort::BAUD_9600,
BAUD_19200 = SerialPort::BAUD_19200,
BAUD_38400 = SerialPort::BAUD_38400,
BAUD_57600 = SerialPort::BAUD_57600,
BAUD_115200 = SerialPort::BAUD_115200, // 4098
BAUD_230400 = SerialPort::BAUD_230400,
#ifdef __linux__
BAUD_460800 = SerialPort::BAUD_460800,
#endif
BAUD_DEFAULT = SerialPort::BAUD_DEFAULT, // 4097
BAUD_INVALID
} ;
So BAUD_INVALID will be 4098, exactly the same as BAUD_115200. That's why you get error.
hello i had the same problem and even i tried everything using c++ API for libSerial couldn't solve until i used the bellow code in my serial initialization!!
I used the system call once at the initialization and that worked GREAT!!
NOTE instead of /dev/ttyACM0 use the name of your serial device /dev/ttyXXX
LibSerial::SerialStream serial;
//serial.SetBaudRate(LibSerial::SerialStreamBuf::BAUD_9600);//THAT DOESNT WORKS
serial.SetCharSize( LibSerial::SerialStreamBuf::CHAR_SIZE_8);
serial.Open("/dev/ttyACM0");
system("sudo stty -F /dev/ttyACM0 115200");//YOU HAVE TO RUN THE EXCECUTABLE FROM COMMAND LINE WITH SU PRIVILEGES

Flush queued GPIB responses

Architecture ->GBIP from external interface is connected to target ( linux) system via gpib bus.
Inside Linux box , there is ethernet cable from GPIB to motherboard.
The PIC_GPIB card on external interface is IEEE 488.2
I am sending a query from external interface to linux box.
Few scenarios
1) If I send a query which does not expect a response back , then next query send will work.
2) If I send a query which expect response back , and when I have received the response and read it and then fire next query it works fine.
3) BUT if I send a query from external interface and got response back and I ignore to read the response , then Next query fails.
I am requesting help for scenario 3.
The coding is done on linux side and its a socket programming , which uses linux inbuilt function from unistd.h for read and write.
My investigation : I have found there is a internal memory on gbib card on external interface which stores the value of previous response until we have the read. Generally I use IEEE string utility software to write commands that goes to linux box and read reposne via read button .
Could someone please direct me how to clean input buffer or memory which stores value so that write from external command contiunues without bothering to read it.
My code on linux side has been developed in C++ and socket programming. I have used in bulit write and read function to write and read to the gpib and to json server.
Sample code is shown below
bool GpibClass::ReadWriteFromGPIB()
{
bool check = true;
int n = 0;
char buffer[BUFFER_SIZE];
fd_set read_set;
struct timeval lTimeOut;
// Reset the read mask for the select
FD_ZERO(&read_set);
FD_SET(mGpibFd, &read_set);
FD_SET(mdiffFd, &read_set);
// Set Timeout to check the status of the connection
// when no data is being received
lTimeOut.tv_sec = CONNECTION_STATUS_CHECK_TIMEOUT_SECONDS;
lTimeOut.tv_usec = 0;
cout << "Entered into this function" << endl;
// Look for sockets with available data
if (-1 == select(FD_SETSIZE, &read_set, NULL, NULL, &lTimeOut))
{
cout << "Select failed" << endl;
// We don't know the cause of select's failure.
// Close everything and start from scratch:
CloseConnection(mGpibFd);
CloseConnection(mdifferntServer); // this is different server
check = false;
}
// Check if data is available from GPIB server,
// and if any read and push it to gpib
if(true == check)
{
cout << "Check data from GPIB after select" << endl;
if (FD_ISSET(mGpibFd, &read_set))
{
n = read(mGpibFd, buffer, BUFFER_SIZE);
cout << "Read from GPIB" << n << " bytes" << endl;
if(0 < n)
{
// write it to different server and check if we get response from it
}
else
{
// Something failed on socket read - most likely
// connection dropped. Close socket and retry later
CloseConnection(mGpibFd);
check = false;
}
}
}
// Check if data is available from different server,
// and if any read and push it to gpib
if(true == check)
{
cout << "Check data from diff server after select" << endl;
if (FD_ISSET(mdiffFd, &read_set))
{
n = read(mdiffFd, buffer, BUFFER_SIZE);
cout << "Read from diff servewr " << n << " bytes" << endl;
if (0 < n)
{
// Append, just in case - makes sure data is sent.
// Extra cr/lf shouldn't cause any problem if the json
// server has already added them
strcpy(buffer + n, "\r\n");
write(mGpibFd, buffer, n + 2);
std::cout <<" the buffer sixze = " << buffer << std::endl;
}
else
{
// Something failed on socket read - most likely
// connection dropped. Close socket and retry later
CloseConnection(mdiffFd);
check = false;
}
}
}
return check;
}
You should ordinarily be reading responses after any operation which could generate them.
If you fail to do that, an easy solution would be to read responses in a loop until you have drained the queue to empty.
You can reset the instrument (probably *RST), but you would probably loose other state as well. You will have to check it's documentation to see if there is a command to reset only the response queue. Checking the documentation is always a good idea, because the number of instruments which precisely comply with the spec is dwarfed by the number which augment or omit parts in unique ways.