Results 1 to 8 of 8

Thread: [RESOLVED] Continuous data update on a background worker thread?

  1. #1

    Thread Starter
    Junior Member
    Join Date
    Sep 2016
    Posts
    21

    Resolved [RESOLVED] Continuous data update on a background worker thread?

    Hello,

    [Application Background}
    My application is reading data from a PLC (programmable logic controller) using the MODBUS RTU protocol. The data is used to update digital and analog gauges on a form and also to make decisions of pass / fail based on tests of the data values. The nature of the application requires a continuous update of the data and the faster the update the better the application will be. For instance, if I need to know how long it takes for a vessel to get up to pressure, I am reading the analog input from the PLC at some interval and checking the pressure data. The resolution of my time calculation is basically equal to the time interval at which I read the data. I am currently using the System.Windows.Forms.Timer with an interval of 100 milliseconds. In the Timer_Tick event, I am reading the data from the PLC, updating my display elements (digital and analog gauges), and checking the data values for conditions pertinent to the particular test I am running. I use this method in timer objects on a couple different forms but the Timer_Tick events for all the forms do the same thing (reading data, updating display elements, and making decisions) at regular intervals. The data gets read into a a custom object that is public and can be accessed from the different forms. All forms update the display elements and make the decisions based on the same data object but they each read the same data separately

    [Application Goals]
    1) I would like to have the data read operation in only one place (not in multiple Timer_Tick events from multiple Timers, each on a separate form)
    2) I would like to read the data continuously and as fast as the MODBUS protocol allows. Currently this takes 25 milliseconds of every 100 milliseconds that the Timer_Tick event fires. So I could have 4 times the speed and time resolution if I read data continuously.
    3) I don't want any random UI thread operations causing a lag in the data update rate

    [My Question]
    Although I have never used a background worker thread, I believe this may be the case where it makes sense. But, before I invested the time learning to use the worker thread and modifying my currently working code, I wanted to ask if this is the best approach.
    It seems like the background worker is mostly designed to perform a lengthy task outside of the UI thread. My application would require it to perform a short task (25 mS) over and over for the lifetime of the application continually updating the public data object (Mostly a collection of arrays containing integer data read from the PLC). Then, I would use the Forms.Timer, running much slower, to update the display elements by accessing the public custom data object.
    Can the background worker thread be used in this manner?
    Or, is there a better way?
    Has anyone done this and have a little sample code they don't mind sharing?

    Thanks in advance - TigerWereWolfMonkey

  2. #2
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: Continuous data update on a background worker thread?

    I do this kind of thing all the time.
    A background worker is really like you said, for performing a task in the background that will usually finish up and return a result.

    Instead of a background worker, the pattern I've used over and over multiple times is to have a background thread who's sole responsibility is to receive data as it comes in and depending on how many different types of messages may come in and how complex they are to parse, usually have a class associated with each message type that the raw data can be sent to a parse method.
    If I have multiple sources of data coming from different machines, then I would have a background thread for each receiver to process messages from those machines (usually).
    I have a module in each project lately, called SharedData, that holds all the data that needs to be displayed on the various "pages" the user can select and the data they select to be displayed.

    I do have a timer (called guiTimer) that I run at a reasonable rate like 100 ms to update the current pages displayed independent of the speed the data is received. I typically receive and process a few hundred messages per second, but have run some number of thousand messages per second.

    Some example code, I guess I can strip out of one of the projects.
    There isn't that much to creating a thread that runs in the background.
    Just need to be aware it can't directly access the GUI (which you don't want anyway), but it can write to the public memory.
    To access the GUI from a background thread, if needed, is done by invoking a delegate that will run on the GUI thread to complete the action.

    All .Net intrinsic types (byte, Integer, Single, etc..) are immutable so you don't have to worry about one thread writing and another thread reading from the variable with one thread getting a half modified value. But, for your object types, i.e. instances of classes you might create and Lists and Dictionaries etc., you do need to implement access controls or use concurrent versions which have the access controls built-in.

    Ok, here is the Load method from the main form (this was an adjunct program, so the form wasn't renamed to something meaningful), and I stripped out some stuff which is not important to the design.
    Code:
     Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    
        TerrainHeightInterface.Init()
        VehicleMsgReceiver.Init()
    
        guiTimer.Interval = 50
        guiTimer.Start()
      End Sub
    So it looks like in this case, I update the GUI at around 20hz.

    Since I usually only need one instance of a particular class to run a background thread and receive data, I keep it simple by using a Static Class (although VB doesn't provide a Static Class type, other than the module) you achieve the same result by taking all the public functions Shared and public data Shared, and you don't need to create an instance of the class, just use those methods and access the data.
    I don't bother creating properties in the classes for the majority of this type of program.

    The Load event handler is calling two Shared Public methods (TerrainHeightInterface.Init() and VehicleMsgReceiver.Init() ) in those two classes to kick the receivers off in background threads.
    Let's look at the simpler one to see what it looks like.
    Code:
    Imports System.Net
    Imports System.Net.Sockets
    Imports System.IO
    Imports System.Threading
    
    Public Class VehicleMsgReceiver
      'Not instantiated. All access through the shared methods and properties
      Private Shared SimControlEndPoint As New IPEndPoint(IPAddress.Parse("127.0.0.1"), 12330)
    
      Private Shared socketIn As New UdpClient(12341)               'Listen on UDP port 12421
    
      Private Shared destIPnt As IPEndPoint
    
    
      Private Shared receiveThread As New Thread(AddressOf Receiver)
    
      Public Shared Sub Init()
        receiveThread.IsBackground = True
        receiveThread.Start()
      End Sub
    
      Private Shared Sub Receiver()
        Dim remoteIP As New IPEndPoint(IPAddress.Any, 0)  'will hold who sent the data 
        Dim msg() As Byte 'buffer to receive data
        Dim pktID As Int32
        Dim vehID As Int32
        Dim RunState As Int32
        Dim SimTOD As Int32
    
        Do Until leaving
          remoteIP = New IPEndPoint(IPAddress.Any, 0)
          Try
            msg = socketIn.Receive(remoteIP)
    
            pktID = msg(0)
            '  ===============================================
            If pktID = TIMETICK_MSG Then  
              '===============================================
              ' parse the message, writting the values out to SharedData variables
    
              '===============================================
            ElseIf pktID = COMMAND_MSG Then
              '===============================================
              '
              'Forward the message on to another process
              'If the message is an update Then
              '  Call a parser to do a local update
              'End if
    
              '===============================================
            ElseIf pktID = etc... Then
              '===============================================
            End If 'we got a hearbeat message, et al
    
          Catch ex As Exception
            If Complain Then
              MsgBox(ex.Message)
            End If
    
          End Try
        Loop
    
      End Sub
    
      Public Shared Sub RemoveVehicle(id As Int32)
    
        SyncLock VehListLock
          VehicleList.Remove(id)
          BuildIndexCrossReferences()
        End SyncLock
    
        SyncLock TelemetryListLock
          TelemetryList.Remove(id)
        End SyncLock
    
        If SelectedVehicle = id Then
          SelectedVehicle = -1  'SelectedVehicle no longer valid
        End If
    
      End Sub
    End Class
    I removed the majority of the code.
    But the .Init method that was called from the Form Load event is all there.
    Let's repeat it here, plus the declaration of the thread.
    Code:
      Private Shared receiveThread As New Thread(AddressOf Receiver)
    
      Public Shared Sub Init()
        receiveThread.IsBackground = True
        receiveThread.Start()
      End Sub
    You just need to declare the variable of type thread, and point it at what code you want to run in the thread.
    Then set it to background (so it will exit when the main (i.e. GUI) thread exits.
    Then start it up, and it runs in the background until you choose to exit (I have the Leaving boolean it is looping on), or the application exits, which will stop all background threads. (If it wasn't a background thread it could be left running
    even after the Form closes).
    That is pretty simple and I really like it a lot and use background threads for interface processing all the time.

    I shouldn't need to post the SharedData Module. It is just a Module named SharedData, and has all the public variables that need to be accessed by code in various parts.
    Which reminds me, I left a stripped down version of a sub (RemoveVehicle) in the above example so you can see the synchronization method I generally use (synclock) in action to guard access to objects from multiple threads.
    Those synclocks objects I declared in SharedData, so I might as well show some of that module after all.
    Code:
    Module SharedData
    
      Public leaving As Boolean
    
      Public FltMdlInterface As New FlightModelsInterface
    
      Public SimElapsedTime As Integer       'in millisecond
      Public AcceleratedTime As Integer = 1
    
      Public SimTimeOfDay As TimeSpan
      Public TimeTickAge As Integer
    
      Public VehicleList As New Dictionary(Of Int32, Vehicle) 'Vehicle ID, Vehicle Class
      Public TelemetryList As New Dictionary(Of Int32, Telemetry) 'Vehicle ID, Telemetry
    
      Public Complain As Boolean = False
      Public VehListLock As New Object  'To lock the vehicle list for single thread access
      Public TelemetryListLock As New Object 'To lock the telemtry list for access
    
    End Module
    Last edited by passel; Sep 30th, 2016 at 08:45 PM.

  3. #3

    Thread Starter
    Junior Member
    Join Date
    Sep 2016
    Posts
    21

    Re: Continuous data update on a background worker thread?

    Thank you for your lengthy response and code snippets. It appears that your response will be very helpful. Also, i gather from the names of your routines that you are (or were) working with automated vehicles. That is very cool! Although my current project is a pneumatic test bench, most of my work is with industrial robots (not mobile). I started using VB for making HMIs (Human Machine Interfaces) since a lot of the robots I program do not have any or very good HMI options.

  4. #4
    Junior Member
    Join Date
    Aug 2008
    Posts
    24

    Re: [RESOLVED] Continuous data update on a background worker thread?

    You may want to look at AdvancedHMI. It has Modbus drivers that run subscriptions on background threads that give you continuous updates of data at a rate you select with a PollRateOverride property. AdvancedHMI is a Visual Studio solution which has the majority of the leg work done for you and it is free. That way you only need to focus on what you want to do with the data once the driver returns it to you.

  5. #5

    Thread Starter
    Junior Member
    Join Date
    Sep 2016
    Posts
    21

    Re: [RESOLVED] Continuous data update on a background worker thread?

    Nothing is ever really "free" what is the catch? I have looked at that before and it looks really cool. I don't remember the reason now but I do remember there was some reason that I could not use it for the particular project I had at the time.

  6. #6
    Junior Member
    Join Date
    Aug 2008
    Posts
    24

    Re: [RESOLVED] Continuous data update on a background worker thread?

    Quote Originally Posted by tigerWereWolfMonkey View Post
    Nothing is ever really "free" what is the catch?
    The only thing that some may consider a "catch" is that it is licensed under GPL meaning that you have to distribute the full source to the end user. if you are using it for internal projects, then that doesn't play any factor.

  7. #7

    Thread Starter
    Junior Member
    Join Date
    Sep 2016
    Posts
    21

    Re: [RESOLVED] Continuous data update on a background worker thread?

    I think I remembered what the issue was. I was only looking for an Ethernet IP driver to add to an already 90% developed project and I think there was a stipulation that the driver could not be used without the rest of the project. I still would very much like to check it out whenever I get some free time.

  8. #8
    Junior Member
    Join Date
    Aug 2008
    Posts
    24

    Re: [RESOLVED] Continuous data update on a background worker thread?

    Quote Originally Posted by tigerWereWolfMonkey View Post
    I think I remembered what the issue was. I was only looking for an Ethernet IP driver to add to an already 90% developed project and I think there was a stipulation that the driver could not be used without the rest of the project. I still would very much like to check it out whenever I get some free time.
    Yes, that is also true. The license requires the complete solution to stay together with all drivers and controls. If you have an already developed project, you can add your existing project to the solution in order to use the drivers and controls while still maintaining license compliance.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width