snx
snx

Reputation: 61

C# - SerialPort.read() speed issue

My code is designed to get data from a serial device and print its contents to a MS Forms Application. The IDE i use is Visual Studio 2019 - Community.

The device does send a variable size packet. First i have to decode the packet "header" to get crucial information for further processing which is the first packet channel as well as the packet length.

Since the packet does neither contain a line ending, nor a fixed character at the end, the functions SerialPort.ReadTo() and SerialPort.ReadLine() are not useful. Therefore only SerialPort.Read(buf,offset,count) can be used

Since sending rather large packets (512bytes) does take time, I've implemented a function for calculation of a desired wait time, which is defined as (1000ms/baud-rate*(8*byte-count))+100ms

While testing, I've experienced delay, much more than the desired wait times, so implemented a measure function for different parts of the function.

In regular cases (with desired wait times) i except a log to console like this: Load Header(+122ms) Load Data (+326ms) Transform (+3ms)

But its only like this for a few variable amount of records, usually 10, after that, the execution times are much worse:

Load Header(+972ms) Load Data (+990ms) Transform (+2ms)

Here you can see the complete function:

private void decodeWriteResponse(int identifier, object sender, SerialDataReceivedEventArgs e)
    {
        /* MEASURE TIME NOW */
        long start = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds();

        var serialPort = (SerialPort)sender;                    //new serial port object
        int delay = ComPort.getWaitTime(7);                     //Returns the wait time (1s/baudrate * bytecount *8) +100ms Additional
        Task.Delay(delay).Wait();                               //wait until the device has send all its data!
        byte[] databytes = new byte[6];                          //new buffer
        try
        {
            serialPort.Read(databytes, 0, 6);                    //read the data
        }
        catch (Exception) { };

        /* MEASURE TIME NOW */
        long between_header = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds();

        /* Read the Data from Port */
        int rec_len = databytes[1] | databytes[2] << 8;         //Extract number of channels
        int start_chnl = databytes[3] | databytes[4] << 8;      //Extract the first channel

        delay = ComPort.getWaitTime(rec_len+7);                 //get wait time
        Task.Delay(delay).Wait();                               //wait until the device has send all its data!
        byte[] buf = new byte[rec_len-3];                       //new buffer

        try
        {
            serialPort.Read(buf, 0, rec_len-3);                  //read the data
        }
        catch (Exception) {}


        /* MEASURE TIME NOW */
        long after_load = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds();

        /* Now perform spectrum analysis */
        decodeSpectrumData(buf, start_chnl, rec_len-4);

       /*MEASURE TIME NOW */
       long end = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds();
       Form.rtxtDataArea.AppendText("Load Header(+" + (between_header - start).ToString() + "ms) Load Data (+" + (after_load - between_header).ToString() + "ms) Transform (+" + (end - after_load) + "ms)\n");



        /*Update the Write handler */
        loadSpectrumHandler(1);

    }

What could cause this issue? I already tested this with "debug" in Visual Studio, and as "Release" standalone, but there is no difference.

Upvotes: 0

Views: 742

Answers (1)

001
001

Reputation: 13533

Instead of trying to figure out how long a message will take to arrive at the port, why not just read the data in a loop until you have it all? For example, read the header and calculate the msg size. Then read that number of bytes. Ex:

// See if there are at least enough bytes for a header
if (serialPort.BytesToRead >= 6) {
    byte[] databytes = new byte[6];
    serialPort.Read(databytes, 0, 6);
    // Parse the header - you have to create this function
    int calculatedMsgSize = ValidateHeader(databytes);
    byte [] msg = new byte[calculatedMsgSize];
    int bytesRead = 0;
    while (bytesRead < calculatedMsgSize) {
        if (serialPort.BytesToRead) {
            bytesRead += serialPort.Read(msg, bytesRead,
                Math.min(calculatedMsgSize - bytesRead, serialPort.BytesToRead));
        }
    }
    // You should now have a complete message
    HandleMsg(msg);
}

Upvotes: 1

Related Questions