Click here to Skip to main content
16,022,332 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
private void button1_Click(object sender, EventArgs e)
{

    if (button1.Text == "Kết nối")
    {
        IPAdress = Convert.ToString(textBox1.Text);
        Port = Convert.ToInt16(textBox2.Text);
        modbus.IPAddress = IPAdress;
        modbus.Port = Port;
       try
        {
            modbus.Connect();
        }
        catch(Exception ex)
        {
            MessageBox.Show(ex.Message);
        }

        if (modbus.Connected == true)
        {

            button1.Text = "Ngắt kết nối";
            label5.Text = "Đang kết nối";
            label5.ForeColor = Color.ForestGreen;

                int[] D = modbus.ReadHoldingRegisters(0, 10);
                textBox4.Text = D[0].ToString();
        }
    }
    else if (button1.Text == "Ngắt kết nối")
    {
        modbus.Disconnect();
            button1.Text = "Kết nối";
            label5.Text = "Chưa kết nối";
            label5.ForeColor = Color.Red;

    }
}

private void button2_Click(object sender, EventArgs e)
{

    int[] D = modbus.ReadHoldingRegisters(0, 10);
    textBox4.Text = D[0].ToString();

}


What I have tried:

I am making an application to read data on PLC, but currently need to click on command button for value to be read, is there any way to make application always show the change of value?
Posted

If you can stand some latency, all you need is a timer; the easiest one would be a Windows.Forms.Timer with an interval of say 100 msec, so its Tick handler would be called approimately every 100 msec, mimicking a repeating button click.

The big advantage of this particular timer is its Tick handler executes on the GUI thread, so your Tick handler can manipulate your Controls freely. Other timers use threads and would require the use of Invoke().

:)
 
Share this answer
 
v2
Comments
Bekiro O.N 14-Aug-24 20:51pm    
If there is no other option I have to accept the delay to try this option, Thank you!
Luc Pattyn 14-Aug-24 21:14pm    
as modbus is a master-slave communication protocol, the only way of detecting a state change in the slave is by the master executing a polling mechanism (your "continuous read"). There are many ways to implement that, e.g. with threads, with a BackGroundWorker, and with timers. The timer I suggested is the simplest assuming it meets your requirements. And whatever you choose, you have to balance latency with CPU load as you are polling, not waiting for an event being signaled. Obviously my 100 msec is just an example, you can shorten that period, however you wanted to "show the change of value" so anything between 20 and 100 msec seems fine to me.

:)
Not an easy way, but it is possible - but you do have to think about it before you wade on in.

Some background to explain why: When you click a button, an event is raised in Windows. In fact, that isn't what happens: Windows is a message based system, so when you click the button, it sends a message which the UI thread message loop retrieves when it gets to it and calls the Button.Click event handler (after jumping through a few hoops). Once the handler exits, the message loop can look for another message and process that.
Which means that while your event handler is executing, nothing else happens on that thread: no Paint events, no clicks, no nothing ... the thread is fully occupied handling the Click event.

As a result, if your Click handler sits in a loop continuously reading data from your PLC the rest of your app is "frozen" and can't do anything.

If you want to continuously read data, you need to move that away from the UI thread onto a new thread - which isn't difficult to do: Switching From a BackgroundWorker To a Task - It's a Neater Solution, Particularly When Reporting Progress[^] shows the code for two solutions. But ... it's not that simple because once you move code to a non-UI thread, it can't interact directly with UI elements like Controls, Forms, or any other display / input elements. If you try, your app will crash with a "Cross-threading exception". You have to transfer the information to the UI thread to display it - and that means either Invoking controls, or passing progress messages between the two threads.

So you have to plan what the new thread will do, and how it interacts with the UI - because if you read from the PLC as fast as possible, your interactions with the UI thread will clog up the system with messages and the system responsiveness will slow to a crawl or even stop from a user perspective.

Start by thinking about the data you are reading, and what exactly you need to display: if it isn't changing, do you need to display it again? What part of the data does the user want to see? What part is "packaging" that should be "stripped out" so that only "whole data" is transferred? 10 minutes of planning can save hours of hair pulling!

BTW: Do yourself a favour, and stop using Visual Studio default names for everything - you may remember that "TextBox8" is the mobile number today, but when you have to modify it in three weeks time, will you then? Use descriptive names - "tbMobileNo" for example - and your code becomes easier to read, more self documenting, easier to maintain - and surprisingly quicker to code because Intellisense can get to to "tbMobile" in three keystrokes, where "TextBox8" takes thinking about and 8 keystrokes...
 
Share this answer
 
Comments
Bekiro O.N 14-Aug-24 20:41pm    
Thanks i will try to research more options.
Since i am writing a test code i just use the default names temporarily, with the usable code i always adjust it
OriginalGriff is giving you good advice here. I would add a couple of points to consider. First of all, these lines:
C#
IPAdress = Convert.ToString(textBox1.Text);
Port = Convert.ToInt16(textBox2.Text);
You don't need to convert the Text in a textbox to a string - it's already a string. The second line is worse as the conversion will throw an exception if you have anything other than a short in there. You should always look to use short.TryParse if you want to parse a short (there are variants for int, double, float, etc).

Something else to consider, you are setting global variables here, but I would guess that you aren't using them anywhere other than in that first Click handler. If I were writing this code, I would be very tempted to move the modbus code out of the form altogether. It would be very easy for me to put a class together that encapsulated the functionality I was interested in. Roughing out the shape of the class, I would be tempted to write something like this:
C#
internal class ModBusHandler : IDisposable
{
  private ModBus modbus;
  public event EventHandler<ModBusReadArgs> ReadingReceivedEvent;

  public void Connect(string address, ushort port) // port is constrained to be positive only
  {
    if (string.IsNullOrWhitespace(address)) throw new ArgumentException(nameof(address));
    if (modbus != null) return;
    modbus = new ModBus { IPAddress = address, Port = port };
    modbus.Connect();
    // Add your background worker here
  }

  private void RaiseReadEvent(int[] values)
  {
    ReadingReceivedEvent?.Invoke(this, new ModBusReadArgs(values));
  }

  public void Disconnect()
  {
    if (modbus == null) return;
    modbus.Disconnect();
    modbus = null;
  }

  public void Dispose()
  {
    this.Disconnect();
  }
}
This requires an event handler that looks like this:
public class ModBusReadArgs : EventArgs
{
    public int[] Values { get; set; }

    public CustomEventArgs(int[] values)
    {
        Values = values;
    }
}
Obviously, you're going to call the RaiseReadEvent from your background worker. It's a simple matter now to hook this up inside your form. I'll leave that part - and the adding the background worker in to you.

Final note: give your variables meaningful names. Using D for a value is completely meaningless and very confusing.
 
Share this answer
 
Comments
Bekiro O.N 14-Aug-24 20:49pm    
thank you, i will try this method.

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