PDA

View Full Version : Serial Connection is not stable



saman_artorious
12th June 2012, 14:34
I am dealing with Serial connections between two system for testing my project.

Here's the implementation of the serial object:

(it is attached)

Here are the problem which are making me crazy, I would thank if you could also change the code for a better understanding:

1) Packets get lost, if I write a packet to the serial port, I may sometimes miss the ACK pakcet and I ReadData() fails.
the more packets I append and emit to the serial port the less probable the chance of success.
I tried to delay for quite some moments before sending or receiving another packets, but I came to the point now, that when I use delay between sending packet as you see in the object ReceiveData() SLOT, the chance of
success fails. When i removed it was better.

consider this:



if(data.at(4) == 0x01)
{
qDebug() << "Lower Sensor= 0x01";
qDebug() << "STARTING PUMP..";
command_startPump.append(FUEL_SYSTEM);
command_startPump.append(FUEL_PUMP);
command_startPump.append(0x01);

emit DSLFL_SendToRS485(command_startPump);

qDebug() << "OPENING SV_15";

command_SV15.append(FUEL_SYSTEM);
command_SV15.append(DSLFL_ST_SV_15);
command_SV15.append(0x01);

emit DSLFL_SendToRS485(command_SV15);
}

Here, if I send the first packet using a function like start_pump() and then send the second packets, the code would not work, But, when I pasted the code from the function here without using any delay between the two emits, it works, though ReadData often fails.!

2) When I use the singleShot function, it does not execute the SLOTS, it doesn't work at all!

I would thank if you could show me how to make this Object work correctly without any data loss, and with timer slots and serperate codes work.

Yours

saman_artorious
12th June 2012, 19:34
Moreover, I am a few object which may even access rs485 serial port simultaneously, sending packet and waiting for the ACK and the info packet from serial port.
shall I use synchronization this way? I've done with C but honestly I have no idea how to do it in qt as m new to it.

If I should, I would thank if you could show me a sample code explaining how I can fix my code.

Yours

kuroi_neko
12th June 2012, 19:51
I'm sorry to say that your code shows, in my opinion, a major conception problem.

Asynchronous interfaces like the RS485 you are using require asynchronous processes to be handled properly.
Sure you can try to handle emission and reception inside the same thread, but that will lead to the exact kind of problems that seem to drive you mad at the moment :).

Basically, you are at the mercy of the timings of a remote device. You cannot force your own timings to it. So if you try to send a packet and read the response in a sequential process, you will have to guess the remote device response time. Even if you manage to find a timing that seems to work in some cases, the device is never guaranteed to answer within a precisely fixed delay, so you will never be sure to handle all the possible variations.

What could make your code (barely) work is the buffering effect of your RS485 serial driver, which will store received bytes for a while. However, if you wait too long (especially on a full-duplex link), the driver's buffer will eventually overflow before you can read the responses.

The usual way to handle this is as follows :
create a separate reader thread that is constantly listening to the RS485 (with blocking reads to the serial device). The thread will handle incoming packets (decoding received bytes into a buffer, rejecting incorrect packets, etc) and put properly identified device response packets into a queue.
Each time a complete valid packet is read, this thread will signal the reception to whatever thread is actually controlling the pump. The usual synchronization object used to control such kind of queues is a semaphore.

The controller thread will write a command and wait on the semaphore/queue for an answer. As soon as the remote device is done answering, the reader thread will place the answer into the queue and free one semaphore unit.
This will wakeup the controller thread, which can then get the device response packet from the queue.

Usually, the wait is protected by a timeout in case the remote device fails to answer or the answer is lost due to transmission errors. So if the wait on the semaphore/queue times out, the controller will have to do whatever the device control protocol recomends in case of packet loss. Usually it will repeat the command a few times and enter error state after a few consecutive failures.

By handling device communications that way, the waiting time will be decided by the remote device and not your sequential code.
Using this approach would make your controller code a lot more robust, readable and easy to maintain. Unfortunately, it requires a bit of familiarity with multithreading and asynchronous preogramming.

saman_artorious
12th June 2012, 21:03
Thank You for the explanation. Though, there are still some ambiguities.
How should this queue be implemented? shall we define a general queue only for RS485? so that, all the other objects (Controller Threads) get access to the queue from within their code. In general, I think we only need one thread (Reader Thread), controller threads are simply Objects, no need to define a thread for them.

-Secondly, the address of every objects shall be included in the packets read from the RS485, so that, every controller thread would pick up it's own packet! this sounds a little weird though, once I pop an element outta Q, n check if the packet does not belong to me!

- What about the Condition Variable? imagine we got 6 Objects. Do you say that every object should increment the RS485 condition variable when attempting to send a packet? or its own condition variable? if so, how could this condition variable affect the process of locking n unlocking?

I believe everything would become clear if you could explain the above ambiguities in code. As, I am new to qt, this would save a lot of time as well.

Regards

kuroi_neko
13th June 2012, 11:56
Okay... My previous post was a bit vague and too general-purpose, I admit.

First of all, RS485 is just a physical interface specification. Your architecture will depend on the protocol used over it. From what I saw in your code, it looks like you're using a kind of Modbus dialect. That's good news since this antiquated protocol is one of the simplest out there :).
The rest of this post is based on this assumption.

Modbus (in its simple variants) is a pure master-slave protocol. Slave devices are seen as a collection of registers that can be read or written. A transaction on the bus is a single request/response exchange.

Write transaction :
Master requests a slave to change some register value
Slave responds with an acknowledge

Read transaction:
Master asks a slave the value of some register
Slave responds with the requested value

However, the media being error-prone, protocol software must guarantee data integrity by computing and checking packet CRCs, re-sending packets when appropriate and reporting a broken link to application software after a few unsuccessful correction attempts. This requires a few safeguard timeouts too.

Read requests usually require controller software to poll each single readable device at various intervals. Again more design decisions to take, more code to write...

Another important factor is the physical transaction speed. It can wary widely depending on the lenght of wire. For short wires (a few meters or less) the RS485 speed may seem compatible with user interface timings (a few milliseconds per transaction), which means you may be tempted to handle device commands as sequential functions integrated in your GUI.
Be aware however that these designs are very weak and difficult to maintain. Various unpleasantnesses can occur, for instance GUI lock-ups in case of link degradation (generating numerous lenghty safeguard timeouts), response times increasing with the complexity of device transactions, etc. Not to mention the mess your code will look like, with low-level device control strewn all over the GUI, protocol timeouts handled at the same level as GUI events and so on...

I once worked for a company producing medical analysis devices. Their software was entirely based on the Win32 message pump, from GUI to device control. 10 years later, the 100.000 lines Moloch required 10 engineers paid full time to squash the ever-respawning bugs. Believe me, you don't want to start this :).

It's been a while since I quit industrial software consulting, but your problem is just of the kind I was paid to tackle a decade ago or so. It has little to do with Qt, however, so I think this discussion would probably better be continued in private.
If you're interrested, you could send me some documentation on the protocol and devices you're using and I'll have a look. I have a lot of free time these days, and I don't charge for advices :).

kuzulis
15th June 2012, 07:51
2 saman_artorious,

I looked your example and did not see anything unusual in the initialization of the RS-485, ie,
in your case - this is the usual "standard" interface without an RS-485-specific ioctl().
(For example, in contrast to the devices from MOXA where there are specific ioctl() calls to initialize between
the RS-485 <-> RS-232).

So you can easily use ready-made components for the serial port, such as QtSerialPort (http://qt-project.org/wiki/QtSerialPort),
as the best at the moment, IMHO.

Do not use threads - use the Qt signals-slots.