locked
Return control to UI after launching a continuously running sub RRS feed

  • Question

  • Hi,  My app has a form with several button controls.  One of them launches a continuously running sub run(), which, of course,  freezes the form.  The other buttons are designed to set flags to interact with sub run().  I have tried to put this routine in a thread in order to release control back to the form, but can't quite seem to get it right.  Also, within sub run(), I am accessing controls on other forms, as well as passing data to other subs & functions.  Any ideas?  I'm stuck.

    THANKS!!!

    SAMPLE OF ACTIVITY WITHIN THE RUN SUB

    Private Sub btnRun_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRun.Click

     

     'initialize AKD status textbox

            frmGripper.tbxStatus.Text = ""

            frmGripper.tbxStatus.Refresh()

    'wait CNC OK to Unload/Load

            While sRet <> "1"           'wait for "ready" bit

                sCmd = "DIN3.STATE"

                frmGripper.SendAKD(sServerAddress, iPortNumber, sCmd)

                sRet = frmGripper.ReceivedData

                sRet = Microsoft.VisualBasic.Left(sRet, 1)

            End While

            sRet = "0"

            'wait Robot ready

            pval0 = 0

            While pval0 < 128      'DO[25] = ON

                Sleep(100)

                iError0 = RP_OpenDIO(SN, hDIO)

                iError1 = RP_ReadPort(hDIO, 1, pval0, errMsg)   'read port 1

                RP_CloseDIO(hDIO)

                pval0 = 255 - pval0

            End While

    Monday, June 30, 2014 5:51 PM

All replies

  • Why do I see always such difficult code to do these simple things, backgroundworkers, multithreading and whatever and that while using a timer in these cases is so easy.

    Not that it is your solution but to give you an idea.

        Private WithEvents tim As New Timer With {.Interval = 100, .enabled = true}
    
        Private Sub Whatever() Handles tim.Tick
            If pval0 < 128 Then      'DO[25] = ON
                iError0 = RP_OpenDIO(SN, hDIO)
                iError1 = RP_ReadPort(hDIO, 1, pval0, errMsg)   'read port 1
                RP_CloseDIO(hDIO)
                pval0 = 255 - pval0
            End If
        End Sub


    Success
    Cor

    • Proposed as answer by Cadefia Monday, June 30, 2014 7:52 PM
    Monday, June 30, 2014 5:59 PM
  • As Cor says, always consider if a Timer can do what you need before you start multithreading.  However, that said, this may not be appropriate for a timer because of variances in how long each remote operation takes to execute.

    The overall process appears to be mostly I/O bound so it may be a good candidate for async.  There's quite likely some polling involved somewhere as well to keep track of the CNC state, but that should also be possible to implement as an async method (or it may be that a background process is determined to be necessary).

    It would appear that the ultimate solution may begin with a designing a good domain model; right now with the logic coupled to the UI like it is, it may be very difficult to implement the asynchronous processing routines.

    Architecting the entire application is probably outside of the scope of a single forum thread, but if we can get an idea of what the requirements are and what the desired UI should look like then we may be able to offer some suggestions to get you started.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Monday, June 30, 2014 9:08 PM
  • Thank you both for your input.

    My UI is:


    The only continuously running routine is the "RUN", launched by the RUN button.  All others will complete and return control to the UI.

    While the "RUN" routine is running, I need to be able to interact with it with the "PAUSE", "RESUME" and "ABORT" buttons.

    Again, the "RUN" routine does not pass arguments, however, the components inside the routine do.

    Any suggestions would be greatly appreciated!!!

    Wednesday, July 2, 2014 1:33 PM
  • So Run is no Task which cannot be stopped and or resumed. It even can be aborted.

    It means that as soon as Run is clicked all other buttons should be disabled (or hide whatever you like)

    Then you the process can start, however that has to be able to keep on contact with the UI. Like I started with a task (something which has a start and end point and nothing in between) is in my perception not the best solution (A task can run as a separate thread). Most use then DoEvents to see if the three still available buttons are clicked. If that sleep is needed, then I would go for a timer, because in that case it goes smoother.

    JMO



    Success
    Cor

    Wednesday, July 2, 2014 2:47 PM
  • Hi Cor,

    Thanks for your reply!

    This RUN process actually communicates to a robot via a digital I/O interface, as well as 3 other servo motor drives.  It contains all of the sequencing for the process, e.g., load part, open/close gripper, unload part, etc.

    The PAUSE, RESUME and ABORT buttons apply to interrupting the RUN sub in VB.net and send the appropriate digital I/O signals to the robot, where I have traps to achieve these actions within the robot code.

    Once the RUN routine is initiated (button pressed), it will continue to be active for PAUSE and RESUME, but will terminate on an ABORT after the robot code confirms receipt of the ABORT command via the digital I/O interface between the robot and PC.

    I have all issues working for the normal running of the system.  The issue is being able to interrupt the RUN routine with these three buttons, PAUSE, RESUME and ABORT.

    My plan has been to write to a text box when clicking the PAUSE, RESUME or ABORT buttons and pick this up within the RUN sub and take the appropriate actions.

    The key here is that the RUN sub must continue to run while allowing the other three buttons to execute their respective subs concurrently.  I will inhibit the other controls from being active while the RUN sub is running.

    I hope this better explains my need here.  Also, it seems that you have ideas of how to best help, and I appreciate your efforts.

    It seems that this is a fundamental need for a lot of applications, however, the text that I have found so far online has been quite convoluted and complicated.

    There is one tutorial at dotnetperls that looks interesting as a simple implementation of backgroundworker.  I am working out a test on this today and will post the results.  Link below:

    http://www.dotnetperls.com/backgroundworker-vbnet

    Meanwhile, any additional ideas you have would be quite helpful to me.

    Thanks, again, for your time and efforts on this problem!

    jge

    Wednesday, July 2, 2014 3:31 PM
  • Can we get an idea of what is happening in frmGripper (particuarly SendAKD and ReceivedData) as well as RP_ReadPort?  I assume that SendAKD is the primary blocking operation so that by the time it returns, the call to ReceivedData will contain whatever result was returned from the CNC after executing the command in SendAKD.  If the code to write out the command and read back the result is executing against some stream, then you can likely make those calls async and use await operators in your code.  The RP_ related code may need to be factored into something async compatible, but that may not be a big deal.

    A timer just scares me with what appears to be a program that drives a CNC... I'm still not sure if you are just uploading g-code and then telling the machine to execute, then polling for a completion status, or if you are actually sending instructions one at a time for each operation in the CNC program.  If you are actually controlling the machine step-by-step then accuracy is extremely important; you absolutely must never take the next step without analyzing the result of the last... the consequences of doing otherwise could be very, very expensive.

    If you are just executing a whole series of pre-written g-code though, it is another story.  You could then get away with loosely polling for status to determine if a program executed completely or encountered an error.  However, I would expect that polling period to be fairly long since even the simplest program will still likely take at least a few minutes to execute.

    Either way, if you can implement the code using async it becomes very clean and easy to interact with the process to pause/resume or cancel by just tracking a couple of state flags defined on the local form.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Wednesday, July 2, 2014 5:19 PM
  • 'wait CNC OK to Unload/Load

            While sRet <> "1"           'wait for "ready" bit

                sCmd = "DIN3.STATE"

                frmGripper.SendAKD(sServerAddress, iPortNumber, sCmd)

                sRet = frmGripper.ReceivedData

                sRet = Microsoft.VisualBasic.Left(sRet, 1)

            End While

            sRet = "0"

            'wait Robot ready

            pval0 = 0

            While pval0 < 128      'DO[25] = ON

                Sleep(100)

                iError0 = RP_OpenDIO(SN, hDIO)

                iError1 = RP_ReadPort(hDIO, 1, pval0, errMsg)   'read port 1

                RP_CloseDIO(hDIO)

                pval0 = 255 - pval0

            End While

    You really need to explain what the top two pieces of code are doing. Also you wouldn't be happening to be using .Net 4.5 framework for your app would you? As it has some new capabilities that Reed Kimble knows about that could perhaps assist you.

    The top while statement looks like it keeps sending something to the CNC machine or robot, whatever. Wouldn't you only need to send that once and wait for a response? Doesn't the CNC machine or robot store the initial received info in a buffer until it's ready to respond or something? So you would need to just keep checking frmGripper.Received data until it = 1 or something?

    Never mind. I suppose you just keep checking the state until a 1 response is received.

    I don't know anything about ports but in the bottom code couldn't you leave the port open and just continue reading it until Pval = 128 or something? Then when that occurs do something and close the port?


    La vida loca


    Wednesday, July 2, 2014 6:15 PM
  • Ya know, now that I re-read it, I wonder if we are looking at two separate bits of automation...  first there is some unseen initialization of the CNC and Robot (perhaps a robot is loading/unloading the CNC) and the first loop is checking for the CNC to be ready.  Then the second loop polls for the robot to be ready.  Then more unseen code goes about loading a program into the CNC and then instructing the robot to do its thing.

    But you are right, there's no sense in us guessing further... we just need to be told what the overall design is.  If the operations to interface with the external hardware can be implemented in async, then the whole "run" process can be async and I really think that would result in the easiest code to create and maintain.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Wednesday, July 2, 2014 7:16 PM
  • You are right.  There are a few different channels of communication going on, all sequenced by the "RUN" sub, which runs continuously unless the process is aborted.  I have all of these parts working fine.  The issue is that I can't sort out a good way to interrupt the RUN sub with these 3 buttons, PAUSE, RESUME and ABORT, which are established on the same form as the RUN button.  Two other forms exist, one that controls the USB digital I/O board, and is read with this code in the above example.

     'wait Robot ready

            pval0 = 0

            While pval0 < 128      'DO[25] = ON

                Sleep(100)

                iError0 = RP_OpenDIO(SN, hDIO)

                iError1 = RP_ReadPort(hDIO, 1, pval0, errMsg)   'read port 1

                RP_CloseDIO(hDIO)

                pval0 = 255 - pval0

            End While

    This method is used to communicate with the robot controller via. 16 digital inputs and 16 digital outputs.

    The other form communicates with 3 servo drives via TCP/IP and sets parameters with this SendAKD( ) method code in the above example

    'initialize AKD status textbox

            frmGripper.tbxStatus.Text = ""

            frmGripper.tbxStatus.Refresh()

    'wait CNC OK to Unload/Load

            While sRet <> "1"           'wait for "ready" bit

                sCmd = "DIN3.STATE"

                frmGripper.SendAKD(sServerAddress, iPortNumber, sCmd)

                sRet = frmGripper.ReceivedData

                sRet = Microsoft.VisualBasic.Left(sRet, 1)

            End While

     This method is used to access two digital I/O bits on the drive to communicate to the CNC machine as well as set parameters and actions on the drives.

    Text boxes and number boxes are also used to pass certain information between the forms and methods.

    With regard to running the main RUN method async, I would think yes, this would work.  Each of the processes run autonomously, and there is confirmation when one process has to wait for another.

    How would you suggest this be structured?

    Thanks!

    Wednesday, July 2, 2014 10:38 PM
  • Sorry, one more point.

    The communication with the CNC is dead simple and quite dumb.  The CNC sets a digital output when ready to Load/unload and receives a digital input when the CNC is loaded and ready to process the part(s).  These I/O bits are handled by G-code and only the interface to them is handled by my code.  The robot is a bit like a simple indexer interface.  The complexity is really in the communication to the robot and servo drives for the handling of the parts, along with the sequencing logic.

    Hope this helps explain the role of the CNC in this project.

    Wednesday, July 2, 2014 10:46 PM