Author Topic: Read String From Holding Registers  (Read 7806 times)

aquilmustafa

  • Full Member
  • ***
  • Posts: 121
    • View Profile
Read String From Holding Registers
« on: July 08, 2016, 08:30:35 AM »
Hi Archie,

I'm using AHMIV399m. I'm connecting to Telemecanique PLC TM218 via Modbus TCP. Will that be possible to read String Values passed from PLC in Holding Registers to be able to read in AHMI. If yes could you please guide me as to how that could be done.
« Last Edit: March 23, 2018, 09:29:39 AM by Archie »

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5317
    • View Profile
    • AdvancedHMI
Re: Read String From Holding Regiters
« Reply #1 on: July 08, 2016, 10:47:33 AM »
Modbus does not support reading strings directly, but you can read a group of integer values and convert it to a string

Dim v() As String = ModbusTCPCom1.Read("40001" , 10)
MsgBox(MfgControl.AdvancedHMI.Drivers.Common.CalculationsAndConversions.WordsToString(v))

MrPike

  • Sr. Member
  • ****
  • Posts: 297
    • View Profile
Re: Read String From Holding Regiters
« Reply #2 on: March 16, 2018, 11:53:40 PM »
Archie, as I understand it, "synchronous" means the program waits for a return from the device for a request sent.  The other method is asynchronous where the request comes back out of time with the request.  If that is true, is there a way to do these reads asynchronously to prevent the app from freezing up waiting for the data to be returned?  I have an app that uses your code example to read several strings(about 100 ASCII characters total) that freezes for a second or two while the values are read and converted.  Is the DataSubscriber 2 or other control capable of this asynchronously?  How would that code look if so, thanks.   

Godra

  • Hero Member
  • *****
  • Posts: 1446
    • View Profile
Re: Read String From Holding Regiters
« Reply #3 on: March 17, 2018, 08:56:33 AM »
MrPike,

Take a look at Archie's Reply #5 which states to use the BeginRead function: https://www.advancedhmi.com/forum/index.php?topic=1103.msg5762#msg5762 :

        ModbusTCPCom1.BeginRead("40001", 10)

Then you might be able to use a code like this to receive values:

    Private Sub ModbusTCPCom1_DataReceived(sender As Object, e As Drivers.Common.PlcComEventArgs) Handles ModbusTCPCom1.DataReceived
        If e.PlcAddress.Equals("40001") Then
            Dim ints(e.Values.Count - 1) As String
            For i = 0 To e.Values.Count - 1
                ints(i) = e.Values(i)
            Next
            Label3.Text = MfgControl.AdvancedHMI.Drivers.Common.CalculationsAndConversions.WordsToString(ints)
        End If
    End Sub

Archie might suggest something different.
« Last Edit: March 17, 2018, 02:43:49 PM by Godra »

MrPike

  • Sr. Member
  • ****
  • Posts: 297
    • View Profile
Re: Read String From Holding Regiters
« Reply #4 on: March 18, 2018, 11:15:30 PM »
Hi Godra, I did some experimenting with this solution but found that the BeginRead returns an error that the que is full.  I adjusted poll rates out to 2000ms which should be plenty of time to read the basic string I have in the pic.  Besides, this poll rate would not be satisfactory in my environment and I need to read 3-4 times this amount in my application.  Hopefully Archie can suggest another method.  Thank you. 

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5317
    • View Profile
    • AdvancedHMI
Re: Read String From Holding Regiters
« Reply #5 on: March 18, 2018, 11:37:59 PM »
The difficulty with BeginRead is that queues the request, then returns to the code immediately. This allows for potential flooding of the queue because your code can send requests in the fraction of a ms, but the PLC may take 10ms to respond and allow advancing to the next item in the queue.

You need to either throttle your BeginReads to go no faster than the PLC responds or perform the reads on a background thread.

A simple solution may be to use a timer with something like a 50ms interval. On each timer tick, read one string.

The other option is to use the BackgroundWorker component.

MrPike

  • Sr. Member
  • ****
  • Posts: 297
    • View Profile
Re: Read String From Holding Regiters
« Reply #6 on: March 20, 2018, 11:34:34 PM »
Hi Archie, doing a little experimenting with the BeginRead option and I am noticing some unusual numbers when i call the method.  The pics attached show the different values returned from the read and the beginread.  These code snippets are called in the same sub and the correct value is the read(value 11).  The Beginread value changes each time I call it.  What am I doing wrong?  Thanks. 

Godra

  • Hero Member
  • *****
  • Posts: 1446
    • View Profile
Re: Read String From Holding Regiters
« Reply #7 on: March 20, 2018, 11:44:32 PM »
BeginRead is a function that returns integer value (not the value of the address) which in your case shows as 274.

Check my previous post for using the ModbusTCPCom1.DataReceived event to receive value of the address.

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5317
    • View Profile
    • AdvancedHMI
Re: Read String From Holding Regiters
« Reply #8 on: March 20, 2018, 11:46:42 PM »
BeginRead returns a transaction number since it returns back to the calling code before the device responds with its data. The actual value will be returned in the DataReceived event handler which you can get to by double clicking the driver instance.

MrPike

  • Sr. Member
  • ****
  • Posts: 297
    • View Profile
Re: Read String From Holding Regiters
« Reply #9 on: March 22, 2018, 10:40:18 PM »
Archie, I played around with a timer but this didn't seem to work reliably for me because the tick event may not be raised when an event occurs which results in nothing being processed.  I would like to explore the background worker component but not sure how to incorporate its DoWork sub if the BeginRead methods have to be in the ModbusTCPCom1_DataReceived sub.  Sorry for the basic .net question, but how do I utilize both these subs? As I understand it I need to run my code in the DoWork but the values come from the DataReceived sub?!?!?!?!?  Thanks for the education:)
 
Private Sub bwEventData_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bwEventData.DoWork
                           ??????????????

 Public Sub ModbusTCPCom1_DataReceived(sender As Object, e As Drivers.Common.PlcComEventArgs) Handles ModbusTCPCom1.DataReceived
                          ModbusTCPCom1.BeginRead("40801",5)

MrPike

  • Sr. Member
  • ****
  • Posts: 297
    • View Profile
Re: Read String From Holding Regiters
« Reply #10 on: March 22, 2018, 10:49:26 PM »
What will happen if I use the ModbusTCPCom1.Read method in the DoWork sub and call BackgroundWorker1.RunWorkerAsync()? Or do I need to use the BeginRead??  Thanks.   

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5317
    • View Profile
    • AdvancedHMI
Re: Read String From Holding Regiters
« Reply #11 on: March 23, 2018, 07:47:28 AM »
The next release of 3.99y is currently being beta tested and one of the new features is string support in Modbus. This could a good opportunity to test it and provide some feedback.

The address formatting for a string is 40001@S10 , where the 10 designates the maximum number of bytes in the string. For example, if you wanted to display a string encoded in words starting at 40001, you would add a BasicLabel, then set PLCAddressValue to 40001@S10

If you want to test this, you can download the current beta version here:

https://www.advancedhmi.com/forum/index.php?topic=2058.0
« Last Edit: March 26, 2018, 10:56:54 AM by Archie »

MrPike

  • Sr. Member
  • ****
  • Posts: 297
    • View Profile
Re: Read String From Holding Regiters
« Reply #12 on: March 23, 2018, 08:34:59 AM »
I will gladly give it a try.  Is there an easy way to upgrade my existing project to 399y?  So if I wanted to populate a list view this will work with a DS2 as well?  I would like to try to complete the project I have now since its just one step away.  How can I incorporate the background worker into my current application?  Thank you.

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5317
    • View Profile
    • AdvancedHMI
Re: Read String From Holding Regiters
« Reply #13 on: March 23, 2018, 08:49:07 AM »
How can I incorporate the background worker into my current application?  Thank you.
- From the Toolbox, add a BackgroundWorker to your form
- Add code similar to the following:
Code: [Select]
    Private StopThread As Boolean
    Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        While Not StopThread
            Dim MyValues() As String = ModbusTCPCom1.Read("40001", 10)
            Dim StringResult As String = MfgControl.AdvancedHMI.Drivers.Common.CalculationsAndConversions.WordsToString(MyValues)

            Label1.Test = StringResult

            '* Prevent from using excessive CPU
            Threading.Thread.Sleep(50)
        End While
    End Sub

    Private Sub MainForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
        StopThread = True
    End Sub

- In the form design view, double click a blank area to get to the Form Load event handler
- Add this line of code:
Code: [Select]
BackgroundWorker1.RunWorkerAsync()
« Last Edit: March 23, 2018, 08:51:05 AM by Archie »

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5317
    • View Profile
    • AdvancedHMI
Re: Read String From Holding Regiters
« Reply #14 on: March 23, 2018, 09:27:47 AM »
I missed something critical in the code above. You cannot change UI controls on  background thread, so you must invoke the displaying of them onto the UI thread. This code should work;
Code: [Select]
    Private StopThread As Boolean
    Private StringResult As String
        Private mi As System.Windows.Forms.MethodInvoker = AddressOf ShowValue
    Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        While Not StopThread
            Dim MyValues() As String = ModbusTCPCom1.Read("40001", 10)
            StringResult = MfgControl.AdvancedHMI.Drivers.Common.CalculationsAndConversions.WordsToString(MyValues)

            Me.Invoke(mi)

            '* Prevent from using excessive CPU
            Threading.Thread.Sleep(50)
        End While
    End Sub

    Private Sub MainForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
        StopThread = True
    End Sub

    Private Sub ShowValue()
        Label1.Text = StringResult
    End Sub
« Last Edit: March 23, 2018, 09:29:20 AM by Archie »