Search code examples
c#winformstcpeasy-modbus

EasyModbus TCP communication slowing down windows application


The WinForms application slows down when the number of methods inside the timer for EasyModbus TCP increases. What should be done to increase the speed of the application?

I'm connecting a slave device from a WinForms application using Modbus TCP/IP. The EasyModbus TCP/IP library is used to build communication between the WinForms application and the slave device, The data (float) should be continuously read from and written to the client, so the methods are written in a timer for Modbus communication. When the number of methods increases, the whole application slows down.

using EasyModbus;

ModbusClient modbusClient;

tmr_Modbus_Com.Enabled = false;
timer1.Enabled = false;
private void tmr_Modbus_Com_Tick(object sender, EventArgs e)
    {
        tmr_Modbus_Com.Enabled = false;

        modbusClient.WriteMultipleRegisters(2, ModbusClient.ConvertFloatToRegisters((float)variable, ModbusClient.RegisterOrder.HighLow)); //writing float data in "variable" to client register 40001 and 40002

        textbox1.Text = ModbusClient.ConvertRegistersToFloat(modbusClient.ReadHoldingRegisters(394, 2), ModbusClient.RegisterOrder.HighLow).ToString(); //reading data from two registers 393 and 394 and displays on a textbox, "textbox1"

        tmr_Modbus_Com.Enabled = true

    }

The above code returns expected results, It writes float data from WinForms to register number 40001 Also, it reads the data from client ModBus register 40395 and displays it in the text box.

The whole application slows down when the number of methods to read and write data increases since data from a number of registers need to be accessed simultaneously.

Can anyone please help me with this issue, how to access multiple registers at a time without slowing down the whole application?


Solution

  • Your Application is unresponsive, because you are performing I/O on the Event Dispatching Thread. This is blocking it for doing other tasks like drawing your UI, reacting to mouse-events ...

    To execute I/O on the Threadpool, you can use async/await like so (untested!)

    // 1. Run the Write - Part on a Threadpool Thread ...
    private Task WriteRegAsync( float variable , ModbusClient client )
    {
        return Task.Run(() => {
            client.WriteMultipleRegisters(
                         2,  
                         ModbusClient.ConvertFloatToRegisters( variable,
                                                               ModbusClient.RegisterOrder.HighLow)
             );
        });
    }
    
    // 2. Run the Read-Part on a Threadpool Thread
    private Task<string> ReadRegAsync( int address, ModbusClient client )
    {
        return Task.Run( () => {
            return ModbusClient.ConvertRegistersToFloat( 
                                      client.ReadHoldingRegisters(address, 2), 
                                      ModbusClient.RegisterOrder.HighLow)
                               .ToString();
        });
    }
    
    // "async" makes "await" available for use. Since this is an EventHandler, "async void" is ok. (Usually, you make that "async Task") 
    private async void tmr_Modbus_Com_Tick(object sender, EventArgs e)
    {
        tmr_Modbus_Com.Enabled = false;
    
        // Write _asynchronously_, your app will stay responsive.
        await WriteRegAsync( (float)variable, modbusClient );
        // then read asynchronously. Again, App will stay responsive.
        textbox1.Text = await ReadRegAsync(384, modbusClient);
    
        tmr_Modbus_Com.Enabled = true
    }
    

    Mind that this is not production ready code! It could be possible that adjacent calls to the event handler may "overtake" each other. You may want to add some error handling, logging, ...

    Another point: If ModbusClient provides async Methods already, then use them instead. The example above is more or less just using an extra thread to handle I/O concurrently to your UI stuff.