Author Topic: Ability to Stop\Start Modbus Coms, trying to find modbus address at test station  (Read 3399 times)

ianfinlay_aus

  • Newbie
  • *
  • Posts: 21
  • www.earthed.net.au
    • View Profile
    • Earthed Solutions Pty Ltd [Custom Software for Engineering]
Hi Support,

I am on my first advancedHMI project - so apologies in adv. for not knowing much about your product

I have an application which is a underwater thruster testing station (units are sealed and modbus ID is not always documented)

so need to poll device each address 1-254 sequentially and function read address 40001

The problem is I don't have access to the timeout, and it takes a REALLY long time to get through all the polls,
whereas in Python code is extremely fast  (I am trying to get rid of the Python)


Below is what I have tried:
(Is there anything else I can try? have played with as I imagine I need to adjust the timeout, but this isn't exposed)


    Private Sub BtnFindModbusId_Click(sender As Object, e As EventArgs) Handles Button4.Click
        Try
            'adress 1-254
            Dim bFound As Boolean = False

            ModbusRTUComFind.PollRateOverride = 100
            For id As Byte = 1 To 254
                ModbusRTUComFind.StationAddress = id
                Try
                    lblFindStatus.Text = String.Format("Try ID={0}", id)
                    Dim sVal As String = ModbusRTUComFind.Read("40001")
                    'if unable to read generates exception here ^^^
                    bFound = True
                    tbFoundId.Text = id.ToString()
                    Exit For
                Catch ex As Exception
                    'not read from device, so try next
                End Try
            Next
        Catch ex As Exception
        End Try
    End Sub

Any guidance appreciated
Former Citect SCADA Developer, and integrator
now specialist in bespoke\custom engineering software for scientific and automation sectors

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5262
    • View Profile
    • AdvancedHMI
Try this to see if it works:

- Edit AdvancedHMIDrivers\Modbus\ModbusBase.vb
- Go to line 638
    Private MaxTicks As Integer = 300  '* 100 ticks per second

- Change the 300 (which is 3 seconds) to something lower

ianfinlay_aus

  • Newbie
  • *
  • Posts: 21
  • www.earthed.net.au
    • View Profile
    • Earthed Solutions Pty Ltd [Custom Software for Engineering]
Thanks for getting back so fast

I exposed a function in ModbusBase.vb:
 
#Region "Public Methods"
    Public Sub SetResponseWait(imSec As Integer)
        Try
            'default = 300 (3sec)
            '1 tick = 10msec (i.e. 100 ticks per second)
            MaxTicks = Convert.ToInt32(imSec / 10)
        Catch ex As Exception

        End Try
    End Sub
#End Region

Test1:
I only have one device on test at ID 123, if I do read requests on id=123 for 1000 times before I start the discovery method (below) I get valid read responses,
but then inside the discovery method even id=123 does not respond to a read()

Test 2:
restart program
If however I call modbus id 1-122 (non of which exist) and then follow by calling modbus id 123 (exists) , even 123 does not respond and generates an exception from the Read() call (same exception as though it doesn't exist "No Response from the PLC, Ensure driver settings are correct."

Must be something residual, between changing modbus id, and successive reads that needs top be reset

(unfortunately, cant find a similar forum thread dealing with this, as generally modbus id is hard coded, unless in a redundancy situation, when need to fail-over to another modbus plc)

Baud rate is 115200, which gets back pretty fast when talking to the device

My Discovery Code:

    Private Sub BtnFindModbusId_Click(sender As Object, e As EventArgs) Handles Button4.Click
        Try
            'adress 1-254
            Dim bFound As Boolean = False
            Timer1.Enabled = True

            For id As Byte = 1 To 254
                ModbusRTUComFind.StationAddress = id
                ModbusRTUComFind.SetResponseWait(50) ' 500msec, must set every timeout, as base code sets to 100, after a timeout
                Try
                    sTestingId = String.Format("Try Modbus ID={0}", id)
                    Application.DoEvents()
                    Dim sVal() As String = ModbusRTUComFind.Read("40001", 1)
                    'if unable to read generates exception here ^^^
                    bFound = True
                    tbFoundId.Text = id.ToString()
                    Exit For
                Catch ex As Exception
                    'not read from device, so try next
                    Thread.Sleep(100)
                End Try
                Thread.Sleep(50)
            Next
            Timer1.Enabled = False

            ModbusRTUComFind.SetResponseWait(300) 'return to default

        Catch ex As Exception
        End Try
    End Sub
Former Citect SCADA Developer, and integrator
now specialist in bespoke\custom engineering software for scientific and automation sectors

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5262
    • View Profile
    • AdvancedHMI
Can you run a serial port monitor and post the results of the capture?

ianfinlay_aus

  • Newbie
  • *
  • Posts: 21
  • www.earthed.net.au
    • View Profile
    • Earthed Solutions Pty Ltd [Custom Software for Engineering]
OK Solved it! after a day of hacking

Had to modify core to achive it:

a) modbusbase.vb:
#Region "Public Methods"
    Public Sub SetResponseWait(imSec As Integer)
        Try
            'default = 300 (3sec)
            '1 tick = 10msec (i.e. 100 ticks per second)
            MaxTicks = Convert.ToInt32(imSec / 10)
        Catch ex As Exception

        End Try
    End Sub

    Public Function GetResponseWait() As Integer
        Return MaxTicks
    End Function
#End Region



b) ModbusRTUCom.vb Added:

Note: The NEXT execution of SendRequest() will then call CreateDLLInstance() to create a new instance , problem seemed to be recycling teh existing instance of the dll whilst changing the modbus id

        Public Sub RemoveAllDLLConnection()
            Dim instance As Integer = MyDLLInstance


            '* The handle linked to the DataLink Layer has to be removed, otherwise it causes a problem when a form is closed
            If DLL.ContainsKey(instance) AndAlso DLL(instance) IsNot Nothing Then
                RemoveHandler DLL(instance).DataReceived, AddressOf DataLinkLayerDataReceived
                RemoveHandler DLL(instance).ComError, AddressOf DataLinkLayerComError
                RemoveHandler DLL(instance).ConnectionEstablished, AddressOf DataLinkLayerConnectionEstablished
                EventHandlerDLLInstance = 0

                DLL(instance).ConnectionCount -= 1

                If DLL(instance).ConnectionCount <= 0 Then
                    DLL(instance).Dispose()
                    DLL(instance) = Nothing
                    Dim x As MfgControl.AdvancedHMI.Drivers.ModbusRTU.ModbusRTUDataLinkLayer = Nothing
                    DLL.TryRemove(instance, x)
                End If
            End If
        End Sub


c) Winform code:

Private sTestingId As String = ""
    Private sRetry As String = ""
    Private sTestDuration As String = ""
    Private Sub BtnFindModbusId_Click(sender As Object, e As EventArgs) Handles Button4.Click
        Try
            'adress 1-254
            Dim bFound As Boolean = False
            Dim iRetry As Integer = 0

            'Timer1.Enabled = True
            Dim tStart As DateTime = DateTime.MinValue
            Dim tEnd As DateTime = DateTime.MinValue

            tStart = DateTime.Now

            Const RETRY_MAX As Integer = 1
            Const SCAN_ID_MIN As Integer = 119
            Const SCAN_ID_MAX As Integer = 254
            For id As Byte = SCAN_ID_MIN To SCAN_ID_MAX
                iRetry = 0
                ModbusRTUComFind.PollRateOverride = 100
                ModbusRTUComFind.StationAddress = id

                Application.DoEvents()
                For i As Integer = 0 To (RETRY_MAX - 1)
                    Try
                        ModbusRTUComFind.SetResponseWait(100)
                        sTestingId = String.Format("Read Id={0}, Tag={1}", id.ToString(), "40001")
                        sRetry = String.Format("Try {0}/{1}", i + 1, RETRY_MAX)

                        Dim sVal As String = ModbusRTUComFind.Read("40001")
                        'if unable to read generates timeout exception here ^^^
                        bFound = True
                        tbFoundId.Text = id.ToString()

                    Catch ex As Exception
                        'not read from device, so try next
                        Thread.Sleep(100)
                    End Try

                    Dim bIdReceived As Byte = ModbusRTUComFind.GetModbusId_ReceivedLast()
                    If (bIdReceived > 0) Then
                        ModbusRTUComFind.SetModbusId_ReceivedLast(0)
                        bFound = True
                        bMOdbus_FoundId = bIdReceived
                        Application.DoEvents()
                        Exit For
                    End If

                    ModbusRTUComFind.RemoveAllDLLConnection()
                Next

                If bFound Then
                    Exit For
                End If

                Thread.Sleep(100)
            Next

            tEnd = DateTime.Now
            Dim tDiff As TimeSpan = tEnd - tStart

            sTestDuration = String.Format("Duration = {0} sec", tDiff.TotalSeconds)

            If bFound Then
                sTestingId = String.Format("End Test: Found @ {0}", bMOdbus_FoundId)
            Else
                sTestingId = String.Format("End Test: Not Found in range [{0}-{1}]", SCAN_ID_MIN, SCAN_ID_MAX)
            End If


            'Timer1.Enabled = False
            Application.DoEvents()


            ModbusRTUComFind.SetResponseWait(300) 'return to default

        Catch ex As Exception
        End Try
    End Sub

    Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
        Try
            lblFindStatus.Text = sTestingId
            lblRetry.Text = sRetry
            tbFoundId.Text = bMOdbus_FoundId.ToString()
            lblDuration.Text = sTestDuration
        Catch ex As Exception

        End Try
    End Sub
Former Citect SCADA Developer, and integrator
now specialist in bespoke\custom engineering software for scientific and automation sectors

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5262
    • View Profile
    • AdvancedHMI
I'm still not quite sure why this made it work. I checked the StationAddress and the only place it is used is in the SendRequest function for creating a new RTU frame. The value is pushed to the DLL, but it is not used at all.

On another note, the line where you set the PollRateOverride may not apply for your code unless you are using subscriptions some where else in your code. That value is used to determined the delay in between requests of the subscriptions.

ianfinlay_aus

  • Newbie
  • *
  • Posts: 21
  • www.earthed.net.au
    • View Profile
    • Earthed Solutions Pty Ltd [Custom Software for Engineering]
Hi Archie,

design time:
modbus rtu control property 'Disable Subscription' = false

I found that without the previous call, I would only get a response from the device (@address 123) being processed when I was already queueing read requests for device 129 or device 130, some latency added in queue
but haven't access to this code

I wanted to add a trace of protocol on code, but the dll doesnt have source code (MfgControl.AdvancedHMI.drivers.dll)
Is it possible to get it?
Former Citect SCADA Developer, and integrator
now specialist in bespoke\custom engineering software for scientific and automation sectors

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5262
    • View Profile
    • AdvancedHMI
I think I know what is happening. When you delete the DLL instance, it clears the Que and closed the port. Otherwise each packet will attempt to be sent 2 times with a delay in between, so when you send the next request the first probably still has not cleared the que.

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5262
    • View Profile
    • AdvancedHMI
design time:
modbus rtu control property 'Disable Subscription' = false

I found that without the previous call, I would only get a response from the device (@address 123) being processed when I was already queueing read requests for device 129 or device 130, some latency added in queue
Are you using visual controls that are linked to the driver? If so, these are creating the subscriptions.

I will create a new project for the Modbus drivers and publish the complete code.

ianfinlay_aus

  • Newbie
  • *
  • Posts: 21
  • www.earthed.net.au
    • View Profile
    • Earthed Solutions Pty Ltd [Custom Software for Engineering]
I sincerely appreciate your efforts
Former Citect SCADA Developer, and integrator
now specialist in bespoke\custom engineering software for scientific and automation sectors

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5262
    • View Profile
    • AdvancedHMI
You can download the complete source for the ModbusRTU driver from here:

https://sourceforge.net/projects/modbusrtu

I think the area you are interested in is ModbusRTUDataLinkLayer.vb in the SendQueProcessor subroutine. In this code:
Code: [Select]
                            While i < WaitCycles And (Not ResponseReceived And Not ChecksumFailed)
                                '* Wait for 2xInterpacketDelay (7 characters of time)
                                System.Threading.Thread.Sleep(SleepTime)


                                '* Once the data stream started coming, change our wait
                                If WaitCycles = 1500 AndAlso ReceivedDataPacket.Count > 0 Then
                                    i = 0
                                    WaitCycles = 600
                                End If
                                i += 1
                            End While


ianfinlay_aus

  • Newbie
  • *
  • Posts: 21
  • www.earthed.net.au
    • View Profile
    • Earthed Solutions Pty Ltd [Custom Software for Engineering]
Hi Archie,
I am a little confused, how does the above ModbusRTU project fits into the AdvancedHMI 3.5 solution (v398t or v399)?

I can only see the following DLL's that don't have source code for in project AdvancedHMI:
a) ..\AdvancedHMIControls\Support\MfgControl.AdvancedHMI.Controls.dll
b) ..\AdvancedHMIDrivers\Support\MfgControl.AdvancedHMI.Drivers.dll

something is different as the new project uses .NET framework 4.5.2 and not 3.5

Thanks for your support
Ian
« Last Edit: September 06, 2015, 08:30:06 PM by ianfinlay_aus »
Former Citect SCADA Developer, and integrator
now specialist in bespoke\custom engineering software for scientific and automation sectors

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5262
    • View Profile
    • AdvancedHMI
That project does not go back into AdvancedHMI. It is the Modbus driver from the AdvancedHMI project, but as a stand alone driver. I thought maybe you wanted to run your code against the driver and dig deeper into it to understand the problems you were seeing.

The full source code to the base DLLs are no longer published.

ianfinlay_aus

  • Newbie
  • *
  • Posts: 21
  • www.earthed.net.au
    • View Profile
    • Earthed Solutions Pty Ltd [Custom Software for Engineering]
HI Archie,
Is there a way to purchase source for the Dll's, for maintenance purposes, much of my work requires me to enter into long term maintenance and support agreements,
and without full source I wouldn't be able to rebuild dll's in a new Visual Studio IDE's, .NET frameworks or O/S's (if and when they are released)
I Hope you understand
Sincerely
Ian
Former Citect SCADA Developer, and integrator
now specialist in bespoke\custom engineering software for scientific and automation sectors

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5262
    • View Profile
    • AdvancedHMI
Is there a way to purchase source for the Dll's, for maintenance purposes, much of my work requires me to enter into long term maintenance and support agreements,
and without full source I wouldn't be able to rebuild dll's in a new Visual Studio IDE's, .NET frameworks or O/S's (if and when they are released)
I Hope you understand
Sincerely
Ian
Ian

I am sorry, but we just don't release the source to those DLLs. We maintain all updates to those underlying classes. There are a number of reasons for that including financial reasons to our company.