Click here to Skip to main content
15,884,011 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Faced a problem. I need to poll the device by serial port using the Modbus protocol.

But polling the device takes a long time - about 2 seconds.

True, my program still additionally constantly polls devices in a separate thread, but I wanted it to be faster.

Maybe someone can help me how to optimize my code. I would be very grateful.


What I have tried:

Baud rate = 19200.This is what my program displays: Request: 07.04.2020 12:51.48.038 01 03 04 00 00 03 04 FB Answer: 07.04.2020 12:51.49.949 (1,911 seconds) 01 03 06 51 47 07 12 20 84 20 29 00


C#
public override RcResult ExecuteModBus(RcModBusDevice device, byte[] request, out byte[] answer)
    {
        answer = null;
        var result = new RcResult();
        OnLogMessage?.Invoke(this, "ExecuteModBus LOCK?");
        lock (communication)
        {
            OnLogMessage?.Invoke(this, "ExecuteModBus LOCK!");
            var dt = DateTime.Now;
            if (device != null)
            {
                serialPort.WriteTimeout = device.SendTimeout;
                serialPort.ReadTimeout = device.ReceiveTimeout;
            }
            serialPort.DiscardOutBuffer();
            serialPort.DiscardInBuffer();

            OnLogMessage?.Invoke(this, "REQUEST->:" + Utils.BytesToHex(request));
            try
            {
                serialPort.Write(request, 0, request.Length);
            }
            catch(Exception ex)
            {
                result.ErrorCode = 1;
                result.ErrorText = ex.Message;
                return result;
            }
            var tmp = new byte[0x2000];
            int dataLength = 0;
            try
            {
                for (int i = 0; i < tmp.Length; i++)
                    tmp[dataLength++] = (byte)serialPort.ReadByte();
            }
            catch (Exception ex)
            {
            }

            var crc = Utils.GetCRC(tmp, 0, dataLength - 2);
            if (crc[0] != tmp[dataLength - 2] || crc[1] != tmp[dataLength - 1])
            {
                result.ErrorCode = 1;
                result.ErrorText = "Bad CRC";
            }
            answer = new byte[dataLength];
            Array.Copy(tmp, 0, answer, 0, dataLength);
            OnLogMessage?.Invoke(this, "ANSWER<-:" + Utils.BytesToHex(answer));
            if (device != null)
                SaveToLog(DbID, device.DbID, dt, Utils.BytesToHex(request), DateTime.Now, Utils.BytesToHex(answer));
            if (dataLength == 0)
                result.ErrorCode = 1;
        }
        OnLogMessage?.Invoke(this, "ExecuteModBus UNLOCK");
        return result;
    }
Posted
Updated 21-Apr-20 6:33am
v2

After all discussions I would try something like below in your receiver code section to speed up communication.
I hope it helps and that I did not make to much mistakes.
// The following expects that serialPort.ReadTimeout is setup
// for "message timeout" which is usually much bigger than character 
// timeout after the first byte has been  received
//
var tmp = new byte[0x2000];
int dataLength = 0;
try
{
   while(true)
   {
      byte receiveByte= (byte)serialPort.ReadByte();

      // We received one byte. Check buffer size
      if (dataLength >= tmp.Length) 
      {
         // Buffer to small
         result.ErrorCode = 1; // Appropriate ErrorCode
         result.ErrorText = "Buffer Overrun";
         return result;
      }
      
      // Add it to receive buffer
      tmp[dataLength++] = receiveByte;

      // Set character Timeout after we received the first byte of the answer
      if (dataLength == 1)
      {
         // Set new character timeout according to Modbus spec.
         // At least 3.5 characters silence but here we take about 
         // 5 times more chars to be on the safe side
         //
         //                   10Bit/Byte     mS      ca. 5x3.5
         serialPort.ReadTimeout= (10    *   1000   *   18)      /   serialPort.Baudrate; 
      }

      // Don't eat CPU too aggressive (has to be proofed whether it really helps)
      Thread.Sleep(0);
   }
}
catch (TimeoutException ex)
{
   // Expected exception. No more character received in the specified
   // character timeout period
}
catch (Exception ex)
{
   // Other than (TimeoutException is an error condition
   result.ErrorCode = 1; // Appropriate ErrorCode
   result.ErrorText = ex.Message;
   return result;
}
 
Share this answer
 
v3
Comments
Maciej Los 23-Apr-20 14:49pm    
5ed!
[no name] 23-Apr-20 14:56pm    
Thank you Maciej
Demuter 28-May-20 4:33am    
Good day. I would like to thank you for your help with my past problem, if there is an opportunity, could you look at one more of my questions. He is on a similar topic ... I can not understand what the problem is ...
Start by finding out where it's spending it#s time: pepper it with Stopwatch Class (System.Diagnostics) | Microsoft Docs[^] instances and log the various parts of the operation to a file at the end. Then focus on the longest part, and add more Stopwatches to try and narrow down where it's spending it's time.

When you have that, you can start looking at speeding it up, but until you have actual numbers it's all guesswork. When you know, you can focus on the "why is this slow?" - but not until you have data and a reference to check your improvements against.
 
Share this answer
 
Comments
Demuter 21-Apr-20 6:47am    
This code runs the longest. I tried to reduce the size of the buffer, but the time did not change ...

var tmp = new byte[0x2000];
                //var tmp = new byte[0x11];
                int dataLength = 0;
                try
                {
                    for (int i = 0; i < tmp.Length; i++)
                        tmp[dataLength++] = (byte)serialPort.ReadByte();
                }
                catch (Exception ex)
                {
                }
[no name] 21-Apr-20 6:51am    
What is the value for ReceiveTimeout resp, serialPort.ReadTimeout?

Also I would suggest to log the time for the first byte received. This will show you how long the other part needs to answer your request.
OriginalGriff 21-Apr-20 7:12am    
Sensible advice. :thumbsup:
[no name] 21-Apr-20 13:13pm    
Thank you :happy:
Demuter 21-Apr-20 7:55am    
Value for serialPort.ReadTimeout = 1000.
I made a conclusion as you suggested - the first answer in 0.033 seconds.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900