Clocks and elapsed time

  • RW 6.12.


    I'm running into a rather silly issue that's proving to be annoyingly intractable. Basically, I'm changing between different tools, and each tool has its own Cognex camera. I have to allow ~60sec for the Cognex to boot up when the tool is picked, but I want to bury as much of that time as possible in robot motion time.


    So, I created a Clock var and hit it with ClkReset and ClkStart as soon as the tool is attached. The robot moves back Home, then moves to a vision operation robtarget, and at this point I have a wait for the Clock to exceed 60sec. And when I tested this, it worked.


    :justice: HOWEVER! After this test completed successfully, I left the robot overnight -- no reboots, no manual mode operations. And in the morning, when I tried running the vision operation program again, the robot got stuck on the wait for the Clock variable. It's as if the value of the Clock got reset or lost overnight. The Clock value shows as Unknown in the RobotStudio Watch Window (even when the test worked), and N/A in the pendant Program Data window, so I can't see what's going on. I've done a sweep of the entire program module to ensure that there's no stray ClkReset commands that might be sneaking a reset on me.


    At any rate, I need a way to make this bulletproof. Either I need a way to make the Clock value bulletproof, no matter what happens between the Tool -Pick program and the Vision operation program, or I need an alternate method to determine that 60sec have passed since the tool was picked up. I've looked at CTime and GetTime, and my rough thoughts are to try something like:

    I'm sure this isn't bulletproof, though -- for example, if the Pick event was at 15:59:59, I've just created a wait time of an hour. Does anyone know of a good algorithm for handling this issue?

  • I am going to suggest that you replace the clock with the ITimer, timed interrupt. I am guessing that there is a bit or handshake done with the camera to see when it is ready. You could set it for 60 seconds when you pick, or set it with a shorter time to keep polling the camera.

  • I am going to suggest that you replace the clock with the ITimer, timed interrupt. I am guessing that there is a bit or handshake done with the camera to see when it is ready. You could set it for 60 seconds when you pick, or set it with a shorter time to keep polling the camera.

    Oooooo... I never knew about that one!


    So, looking at the documentation for that function, it looks like I should be able to create the INTNUM as a global variable, do the CONNECT in my Main routine, then put the ITimer\Single, 60, into the Tool-Pick routine. The Trap routine sets a global Bool flag, and the Vision program has a Wait for that flag. That looks very promising.


    As for a "camera connected" value... it's somewhat bizarre. I've asked ABB several times now, and not gotten an answer. I've heard from certain parties that claim that Integrated Vision was never intended for use with "droppable" Cognexi, and as such ABB never implemented a command to check the camera connection. But that's purely speculative.


    What I do know is that if I fire a vision request to ivBase before the camera has finished booting, IV just halts on a fatal timeout error with no recovery option. I probably could hack ivBase to repeat the attempt multiple times, but I'd rather avoid meddling with ABB system modules if I can avoid it.

  • Have you poked around in the IO for maybe something that looks like camera communication bits?

    No camera I/O at all. IV appears to be either using Telnet "under the hood", or is using a copy of the Cognex API. The camera is not among any of the devices in EIO.CFG, and I can account for all the declared Signals as belonging to other devices (PLC, Safety Controller, Local I/O module (unwired), or "virtual" signals I added myself.


    ivBase doesn't use Signals, just black-box system calls like CamSetProgramMode, CamSetExposure, CamReqImage, etc.

  • So, of course, that ended up being the last day I had access to that robot for a couple weeks. I did set up a test program that seemed to work, but ran out of time to really bang on it.


    I'm back on that robot again, but there's a rush on to get the final buyoff completed. I'm wondering about the CONNECT command. Once a Connection is made, what breaks it? Based on what I saw during my (brief) testing, it looked like PPtoMain or PPtoRoutine would break it, but I'm not sure about PPtoCursor.


    Mainly, I want to ensure that I don't lose the Connection if someone uses PPtoCursor and then resumes production. PPtoMain or PPtoCursor I'm not too concerned about, since I can just have the CONNECT command at the top of Main.


    But it looks like I should avoid including the CONNECT command in my Main loop, as the manual says repeating a CONNECT that's already connected will throw an error. Yes, I could trap the error, but I'd rather avoid that complexity -- I'm trying to keep this program as simplistic as possible b/c the end customer has zero robot experience. If I need to later, I can always go back and move the CONNECT inside my Main loop, then trap any resulting errors. But that seems kind of kludgy -- is there a cleaner way to handle this?


    Right now, my process is:

    1. CONNECT the interrupt in the "init" section of my Main routine, above the infinite loop
    2. Start the ITimer in my Tool Pick routing
    3. At the start of the Vision subroutine, I start a Clock, then wait for either the boolean flag set by the ITimer Trap routine, or that Clock to exceed 60sec -- that way, I should be covered if the Connection gets broken somehow. A bit fugly, but it should prevent the robot from either faulting or getting stuck forever.
  • ...I'm an idiot. My current process might work for production, but it breaks my manual testing, when I'm using PPtoRoutine. :wallbash:


    So, I guess I'll have to bit the complexity bullet and work on how error-trap the CONNECT command.


    ...actually, that raises another question: if I run the CONNECT command in a lower-level subroutine, what's the scope? What I mean is, I my Main calls PROC A and PROC B, and I use the Connect in PROC A, does the Connection stay connected when the program pointer goes back up into Main and then down into PROC B? I'm digging through the RAPID reference manual, but haven't found an answer to that yet.

  • Okay, thanks for the homework assignment. 😁

    I had to pop on over to the library to read a manual. Then I threw together a little test. When an interrupt variable is declared, it has null value. Upon executing the Connect instruction, it is assigned a value. The value is random and will change, so it is not possible to check for the specific number. What is useful in knowing that the connection is still made is that there is a value which is not zero. I made a little screen grab of my RS test. I think that you can figure out the rest. 👍


  • More testing regarding your further questions: I was typing my first reply when you were also posting.

    We knew already that PP to Main breaks the connection, but I was surprised that a mere PP to routine also does. Call routine does not. Once the connection is made, it remains made throughout the calling heirarcheis you described until it is broken or deleted, as in IDelete.

  • Some test code

  • Heh. While you were doing that homework (thanks, BTW!), I was trying some brute-force testing.


    Oddly enough, while CONNECT fails on a "redundant" execution, IDelete apparently does not. I ran the same IDelete command multiple times in a row, with no errors. So my current "duct-tape" fix is move the CONNECT command to the Tool Pick subroutine, and put an IDelete immediately before it. I then forced some tests, calling the routine with the Interrupt already connected and unconnected. It seems to work.


    But checking InterrtupNumber<>0 looks a lot cleaner. I'll try switching over to that. Thanks!


    EDIT: looks good. I removed all my IDeletes and just did:

    IF CognexConnectedInterrupt=0 CONNECT CognexConnectedInterrupt WITH zCognexConnectInterrupt;


    With my test program, that seems to be running perfectly well. Thanks again!

  • Hm... spoke too soon. I don't know why it didn't show up with my test program, but my first production run hit a snag: the second time I picked up a tool, this code:

    IF CognexConnectedInterrupt=0 CONNECT CognexConnectedInterrupt WITH zCognexConnectInterrupt;

    CognexConnectedTime:=FALSE;

    ITimer\Single,60,CognexConnectedInterrupt;


    the ITimer\Single line generated a message 41452, that the Interrupt number was already in use for other purposes. The IF statement skipped the connect on the 2nd pickup, and I was able to see that the IntNum still had a valid non-zero variable.


    So... either I need a way to reset the ITimer, or I need to go back to using IDelete. Maybe use the IDelete if the InNum is non-zero?


    EDIT: no time to faff around, we have to run test parts, so I just made the IDelete and CONNECT non-optional:

    IDelete CognexConnectedInterrupt;

    CONNECT CognexConnectedInterrupt WITH zCognexConnectInterrupt;

    CognexConnectedTime:=FALSE;

    ITimer\Single,60,CognexConnectedInterrupt;


    That seems to work, but I thought the previous version was working fine until I did two Tool Picks in a row. What I think happened was that my test program only called the Tool Pick once, and I did repetition tests by simply hitting Run again and letting the test program "loop" naturally. I think that maybe implicitly deleted the ITimer or the Connection? I'm not sure. Regardless, when I can get the robot back from the test runs, I'll have to change my test program to deliberately call the Tool Pick twice in a row and see if I've managed to make this thing bulletproof.

  • Took a quick look at my backup, I just went with the brute-force approach. Once the tool change locked, I executed:

    Code
          ! Start background timer for Cognex connect/boot time
          ! CognexConnectedTime should go True after 60sec
          !      IF CognexConnectedInterrupt<>0 THEN
          IDelete CognexConnectedInterrupt;
          CONNECT CognexConnectedInterrupt WITH zCognexConnectInterrupt;
          !      ENDIF
          CognexConnectedTime:=FALSE;
          ITimer\Single,60,CognexConnectedInterrupt;

    I had the TRAP setup:

    Code
      TRAP zCognexConnectInterrupt
        ! EXPERIMENTAL
        ! Called by an ITimer interrupt 60sec after Tool Attached
        ! Sets flag to indicate Cognex has had enough time to connect
        CognexConnectedTime:=TRUE;
      ENDTRAP

    Then, when I got to the program that actually tried to use the Cognex, I called this subroutine before actually trying to trigger the camera:

  • Awesome! This is very helpful. I'm doing sort of the opposite. Looking to send the robot to home and place it in an idle state if no parts are presented for X amount of time.


    But seeing how you utilized all of these really clarifies how to use the ITimer and ClkRead, so thanks!

  • Yeah, I've got one robot serving two lines, each with it's own bagger, camera, and AsyCube.


    Operators are free to load parts into either hopper to start a batch, and the system will regularly check both lines to see if there are parts to be processed. But I wanted to have something more distinct happen if no parts are detected after a certain amount of time. That way, I can automatically turn off vacuum generators, bagger heat seal bars, etc.


    So typically after finishing a batch on one side, the system would check the other side for available work. If not found, then enters a WaitUntil statement looking for either side to be loaded. What I'm trying to do is EXIT that Wait condition if nothing is found after whatever arbitrary amount of time, and enter the quieter, less power hungry IDLE state.

  • WaitDI di08_PartGripped,1\MaxTime:=10\TimeFlag:=WaitTimedOut;

    IF WaitTimedOut or di08_PartGripped=0 THEN


    Waits for di08 for 10sec, then sets WaitTimedOut (which is a user-created Bool variable) to True and continues to the next line.


    So if you use the MaxTime option, your next program step needs to check how the program pointer got past the WaitTime command, because now there are two options: the input hit the right state, or it waited more than X seconds and gave up. My IF statement there is a bit redundant, but I was trying to cover all my bases.

Advertising from our partners