Flex Automation HMI for Industrial Applications – 2024 Report

  • Post author:
  • Post last modified:April 25, 2024
  • Reading time:74 mins read
  • Post category:Uncategorized

HMI For ABB RobotsC:\Users\swoberlo\AppData\Local\Microsoft\Windows\INetCache\Content.MSO\3178110.tmp

Prepared by: 

Ben Grumann, Joshua Parkinson, Madelyn Brown


Figure 1: Main Product Image

Product Description:

HMI for ABB Robots is a project that has created an open-source Human Machine Interface(HMI) to work with ABB robotic systems. This project is sponsored by Flex Automation and has been in progress since September 2023. The HMI uses a Raspberry Pi 4 and a 7 inch touchscreen display to interface with the robot and instruct it to do certain tasks, such as controlling I/O (Inputs/Outputs) or moving to different positions. HMI for ABB Robots was developed to be a low cost alternative to industrial programmable logic controllers (PLC) in a maintenance or manufacturing environment, where all aspects of a PLC are not required.

Methodology in Brief:

The first half of this project was spent gathering the tools and technology needed to complete this project. The team acquired a Raspberry Pi and touch screen due to its availability, customization options, and easy to understand interface. Python was also used to build the HMI screen for similar reasons and, as such, the display is completely customizable. 

The second half was focused on more technical goals, such as connecting over Ethernet to a loaner robot provided to the team by Flex Automation so the Pi could send and receive signals to and from the machine. Unfortunately, the robot lacked the PC Interface option, which is the software needed to communicate with another device over Ethernet, so resorting to a simulated robot was necessary. Installing PC Interface onto the provided robot would be a near $2000 expense, and Flex Automation advised the team to turn to RobotStudio, ABB’s simulation software. The robot also had physical limitations that were investigated later on regarding Joint 1 which would have restricted what was possible with the HMI screen as well.

As soon as a simulation was deemed a better testing option, the team researched how to send signals back and forth between two separate programs using socketing. Setting up socketing through a Raspberry Pi is like setting up a small server that listens for devices that want to connect to it, and it was determined that this was the most straightforward way to send signals.

On the Python side of the device, the HMI Screen was designed to handle 6 I/O Controls, 6 Move Controls, and 6 Programmed Actions (Figure 2). This interface is entirely customizable to the customer’s needs, and can support more or less of each category, as well as new categories if desired. For each button, a command string was assigned when the window was clicked or pressed, and this string was sent through socketing to the RobotStudio code. This ensures that no unauthorized operators can simply plug into the robot with a computer and begin performing actions, as a majority of the movements must be implemented by a trusted engineer within the robots own code.

Figure 2: HMI Screen Design

On the RobotStudio side, each position’s coordinates are pre-determined and hard coded into the beginning of the program. These coordinates define the positions the robot will move to when a string from Python is received, and can be swapped around as desired. In the current code there are 6 movements the robot can go through, as well as 6 actions that have the robot build and move a simulated block tower. This proves that socketing is able to control I/O on the robot, as the gripper can open and close through button presses on the HMI. Once again, these actions and sequences can be programmed to whatever the customer desires, such as a home position, a maintenance position, turning end effectors on or off, or anything else a regular PLC is capable of.

Bill Of Materials

NameCostLink
Raspberry Pi 4From $35Link
Raspberry Pi Screen V1.1$60Link
Raspberry Pi Case$35Link

Tools Used

NameLink
RobotStudio (Free Trial)Link
VSCodeLink

Assembly (On Single Test Device)

  1. Download both RobotStudio and Visual Studio Code. Create a Python file in VSCode and paste the Python Code [Appendix A] into the file. [Tutorial on creating a Python file in VSCode]
  2. Open File Explorer and navigate to the Stations Folder in RobotStudio (Usually under Documents > RobotStudio > Stations). Download the station file (HMI Station.rsstn) from the repository [Link] and place them in this folder.
  3. Open RobotStudio. Click Open > Open, until File Explorer opens. Navigate to the HMI Station file and double click to open the station [Figure 3].

Figure 3: Opening the HMI Station [‘Recent’ will only be available after opening for the first time]

  1. To navigate to the RobotStudio code, click RAPID [Top bar] > RAPID [Side bar] > T_ROB > Socket > Main, then double click to open [Figure 4]

Figure 4: Accessing Main Code

  1. Double check that all code is present. If not, insert RobotStudio Code [Appendix A] into the code window. Navigate to the Simulation tab on the top bar and click on it, then press Play. There should be a message that lets the user know the program has started [Figure 5].

Figure 5: Play Button Indication and Program Start Message

  1. Navigate back to the Python file and click the Play button in the top left corner. The Flex Automation HMI Screen should appear in full screen. The ‘X’ in the top left corner will exit full screen to allow the user to see the robot’s movements when on a single device.
  2. The HMI should be fully operational. Refer to the move guide below as a reference to what each button should do.

Move Guide

I/O ControlsMove ControlsProgrammed Actions
Open GripperHome (Pointed Forward)Pick Top Block
Close GripperJ2 Bent BackMove Top Block
UnusedAll Joints Straight UpPick Bottom Block
UnusedTwist J4Move Bottom Block
UnusedPoint J5 UpMove Bottom Block Back
UnusedPick Bottom BlockMove Top Block Back
  1. There is an ‘Unpack and Work’ option for the robot as well. In RobotStudio go to File > Share > Unpack and Work [Figure 6], then select the downloaded station file (HMI Station.rspag) from the OSF Repository [Link]. This is a secondary option if the primary setup does not work.

Figure 6: Unpack and Work Option

Programming a Custom Button (VS Code side)

  1. Within the python code there are three for loops going from 0 to 6 (for i in range(6)), each  loop has a comment above for which column of buttons it is creating.
  2. If more or less buttons are needed, the value currently set to 6 can be changed to the number of buttons needed in that specific column.
    1. If the text displayed on the button is desired to be something different, the code would need to be modified slightly:
      for i in range(6):

    button_number = button_number + 1

    if button_number = x:

        button = PushButton(move_controls_button_box, args=(“Move”,button_number), text=”desired button text”, command=move_button_click, align=”top”, width=”fill”, height=”fill”)

    else:

    button = PushButton(move_controls_button_box, args=(“Move”,button_number), text=”Move {button_number}”, command=move_button_click, align=”top”, width=”fill”, height=”fill”)

  1. In the case above, more if statements could be added for each button desired to have different text, x needs to be the number of the button, and the text= in the if statement can be changed to the desired text to display on button number x.
  1. At the beginning of the code there are three function definitions to handle the actions of each column of buttons, declared as def io_button_click, def move_button_click, or def programmed_button_click. Currently these all just have laddered if statements to ensure there is no unintended signal being sent. Each if button has the value of i set to its position counting up from one starting at the top (for example the top button has an i value of 1, and the current bottom button has an i value of 6).
  2. Any added buttons will need to have another if statement added for its position in the column (for example if one more button was added, its i value would be 7, an if statement, if i == 7:, will need to be added).
  3. The command string can be anything you wish, as long as the RobotStudio code expects the command string the Python code is sending.


Programming a Custom Button (RobotStudio side)

  1. Navigate to the series of If statements. These statements are where the button press commands are processed. 
  2. Copy and paste the code from a previous button.
  3. In the quotation marks (“”) put in the command string that your Python program is sending.
  4. Now, in between the THEN and ENDIF, add the code you wish to be activated when the button is pressed. This should allow the program to be run.
  5. These instructions can be used to process any inputs from python and allow whatever is inside the IF statements to be run including: I/O, robot movements, and anything else that is able to be coded in RAPID.

Sponsors/Acknowledgements

Sponsored by Flex Automation

Contacts: Scott Pink and Keith Weller

Thank you for consistent guidance and support.

References

Appendix A – Code [Python, RobotStudio]

Python Code

from guizero import App, Box, PushButton, Text, Window, Picture

from PIL import Image, ImageTk

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

sock.connect((‘127.0.0.1’, 55555))

# Functions to handle button click

def io_button_click(value, i):

    print(f”{value} Button {i} clicked”)

    if i == 1:

        command = “io 1”

        print(socket.gethostbyname(socket.gethostname()))

        sock.send(command.encode())

        print(sock.recv(4096).decode(‘UTF-8’))

    if i == 2:

        command = “io 2”

        print(socket.gethostbyname(socket.gethostname()))

        sock.send(command.encode())

        print(sock.recv(4096).decode(‘UTF-8’))

    if i == 3:

        command = “io 3”

        print(socket.gethostbyname(socket.gethostname()))

        sock.send(command.encode())

        print(sock.recv(4096).decode(‘UTF-8’))

    if i == 4:

        command = “io 4”

        print(socket.gethostbyname(socket.gethostname()))

        sock.send(command.encode())

        print(sock.recv(4096).decode(‘UTF-8’))

    if i == 5:

        command = “io 5”

        print(socket.gethostbyname(socket.gethostname()))

        sock.send(command.encode())

        print(sock.recv(4096).decode(‘UTF-8’))

    if i == 6:

        command = “io 6”

        print(socket.gethostbyname(socket.gethostname()))

        sock.send(command.encode())

        print(sock.recv(4096).decode(‘UTF-8’))

def move_button_click(value, i):

    print(f”{value} Button {i} clicked”)

    if i == 1:

        command = “move 1”

        print(socket.gethostbyname(socket.gethostname()))

        sock.send(command.encode())

        print(sock.recv(4096).decode(‘UTF-8’))

    if i == 2:

        command = “move 2”

        print(socket.gethostbyname(socket.gethostname()))

        sock.send(command.encode())

        print(sock.recv(4096).decode(‘UTF-8’))

    if i == 3:

        command = “move 3”

        print(socket.gethostbyname(socket.gethostname()))

        sock.send(command.encode())

        print(sock.recv(4096).decode(‘UTF-8’))

    if i == 4:

        command = “move 4”

        print(socket.gethostbyname(socket.gethostname()))

        sock.send(command.encode())

        print(sock.recv(4096).decode(‘UTF-8’))

    if i == 5:

        command = “move 5”

        print(socket.gethostbyname(socket.gethostname()))

        sock.send(command.encode())

        print(sock.recv(4096).decode(‘UTF-8’))

    if i == 6:

        command = “move 6”

        print(socket.gethostbyname(socket.gethostname()))

        sock.send(command.encode())

        print(sock.recv(4096).decode(‘UTF-8’))

def programmed_button_click(value, i):

    print(f”{value} Button {i} clicked”)

    if i == 1:

        command = “program 1”

        print(socket.gethostbyname(socket.gethostname()))

        sock.send(command.encode())

        print(sock.recv(4096).decode(‘UTF-8’))

    if i == 2:

        command = “program 2”

        print(socket.gethostbyname(socket.gethostname()))

        sock.send(command.encode())

        print(sock.recv(4096).decode(‘UTF-8’))

    if i == 3:

        command = “program 3”

        print(socket.gethostbyname(socket.gethostname()))

        sock.send(command.encode())

        print(sock.recv(4096).decode(‘UTF-8’))

    if i == 4:

        command = “program 4”

        print(socket.gethostbyname(socket.gethostname()))

        sock.send(command.encode())

        print(sock.recv(4096).decode(‘UTF-8’))

    if i == 5:

        command = “program 5”

        print(socket.gethostbyname(socket.gethostname()))

        sock.send(command.encode())

        print(sock.recv(4096).decode(‘UTF-8’))

    if i == 6:

        command = “program 6”

        print(socket.gethostbyname(socket.gethostname()))

        sock.send(command.encode())

        print(sock.recv(4096).decode(‘UTF-8’))

def open_io_window():

    IO_window.set_full_screen()

    IO_window.show(wait = True)

def exit_fullscreen():

    main_window.exit_full_screen()

def close_window():

    IO_window.hide()

def ESTOP():

    ESTOP_window.set_full_screen()

    ESTOP_window.show(wait = True)

# Window Definitions

main_window = App(title=”Flex Automation HMI”, bg=”#8a8c8c”)

IO_window = Window(main_window, title=”IO Window”)

IO_window.hide()

ESTOP_window = Window(main_window, title=”ESTOP Window”)

ESTOP_window.hide()

# Main Window Layout

main_window_top_bar = Box(main_window, width = “fill”, align = “top”)

image = Image.open(“OSHE_Gear_MTUColors_Square.png”)

resized_image = image.resize((40, 40))

scaled_image = ImageTk.PhotoImage(resized_image)

oshe = Picture(main_window_top_bar, align = “left”, image = scaled_image)

open_io_button = PushButton(main_window_top_bar, command = open_io_window, text = “Open IO Window”, align = “left”)

close_button = PushButton(main_window_top_bar, command = exit_fullscreen, text = “x”, align = “right”)

ESTOP_button_main = PushButton(main_window_top_bar, command = ESTOP, text = “ESTOP”, width = “fill”, align = “right”)

# Create a box for each column

io_controls_box = Box(main_window, align=”left”, width=”fill”, height=”fill”)

move_controls_box = Box(main_window, align=”left”, width=”fill”, height=”fill”)

programmed_actions_box = Box(main_window, align=”left”, width=”fill”, height=”fill”)

# Text labels for each column

io_controls_label = Text(io_controls_box, text=”I/O Controls”, align=”top”, height=1, size=20)

io_controls_button_box = Box(io_controls_box, align=”top”, width=”fill”, height=”fill”)

move_controls_label = Text(move_controls_box, text=”Move Controls”, align=”top”, height=1, size=20)

move_controls_button_box = Box(move_controls_box, align=”top”, width=”fill”, height=”fill”)

programmed_actions_label = Text(programmed_actions_box, text=”Programmed Actions”, align=”top”, height=1, size=20)

programmed_actions_button_box = Box(programmed_actions_box, align=”top”, width=”fill”, height=”fill”)

# Buttons in the first column

button_number = 0

for i in range(6):

    button_number = button_number + 1

    button = PushButton(io_controls_button_box, args=(“I/O”,button_number), text=f”IO Control {button_number}”, command=io_button_click, align=”top”, width=”fill”, height=”fill”)

# Buttons in the second column

button_number = 0

for i in range(6):

    button_number = button_number + 1

    if button_number = x:

        button = PushButton(move_controls_button_box, args=(“Move”,button_number), text=f”Move Control {button_number}”, command=move_button_click, align=”top”, width=”fill”, height=”fill”)

# Buttons in the third column

button_number = 0

for i in range(6):

    button_number = button_number + 1

    button = PushButton(programmed_actions_button_box, args=(“Program”,button_number), text=f”Action {button_number}”, command=programmed_button_click, align=”top”, width=”fill”, height=”fill”)

# Display the app

main_window.set_full_screen()

main_window.display()

RobotStudio Code

MODULE Socket

    VAR jointtarget home:=[[0,0,0,0,0,0],[0, 9E9, 9E9, 9E9, 9E9, 9E9]];

    VAR jointtarget away:=[[0,12,12,0,12,0],[0, 9E9, 9E9, 9E9, 9E9, 9E9]];

    VAR jointtarget pos1:=[[-91.7959,0,0,0,0,0],[9E+9,9E+9,9E+9,9E+9,9E+9,9E+9]];

    VAR jointtarget pos2:=[[-2.11694,-64.0494,0,0,0,0],[9E+9,9E+9,9E+9,9E+9,9E+9,9E+9]];

    VAR jointtarget pos3:=[[-0.0616317,-1.86471,-82.989,0,0,0],[9E+9,9E+9,9E+9,9E+9,9E+9,9E+9]];

    VAR jointtarget pos4:=[[-0.00181666,-0.0549641,-2.44619,93.6866,0,0],[9E+9,9E+9,9E+9,9E+9,9E+9,9E+9]];

    VAR jointtarget pos5:=[[-9.85037E-6,1.05668,1.04371,0.507992,-94.4349,0],[9E+9,9E+9,9E+9,9E+9,9E+9,9E+9]];

    VAR jointtarget pos6:=[[0.0143926,59.5328,-3.24122,-3.14195E-07,33.7084,0.340009],[9E+09,9E+09,9E+09,9E+09,9E+09,9E+09]];

    VAR jointtarget pos7:=[[0.0144536,62.6616,-3.89281,7.60414E-07,31.2312,0.340069],[9E+09,9E+09,9E+09,9E+09,9E+09,9E+09]];

    VAR jointtarget safepos:=[[0.0144539,54.6234,-1.23053,6.61144E-07,36.6071,0.340069],[9E+09,9E+09,9E+09,9E+09,9E+09,9E+09]];

    VAR jointtarget safepos1:=[[0.0144539,55.3065,-1.76975,-2.02767E-06,36.4632,0.185804],[9E+09,9E+09,9E+09,9E+09,9E+09,9E+09]];

    VAR jointtarget newloc1:=[[-48.1214,63.4881,-4.74863,-1.58072E-5,31.1176,0.807374],[9E+9,9E+9,9E+9,9E+9,9E+9,9E+9]];

    VAR jointtarget safenewloc1:=[[-48.1214,55.3114,-1.7799,-1.3745E-5,36.4684,0.807372],[9E+9,9E+9,9E+9,9E+9,9E+9,9E+9]];

    VAR jointtarget newloc2:=[[-48.1214,57.8405,-2.46682,-1.62857E-5,34.6262,0.807365],[9E+9,9E+9,9E+9,9E+9,9E+9,9E+9]];

    VAR jointtarget safenewloc2:=[[-48.1214,49.1011,-0.958213,-1.28171E-5,41.857,0.807371],[9E+9,9E+9,9E+9,9E+9,9E+9,9E+9]];

    PROC main()

        VAR socketdev server_socket;

        VAR socketdev client_socket;

        VAR string receive_string;

        VAR string client_ip; 

        VAR bool safe;

        VAR bool grabbed;

        VAR bool safe2;

        VAR bool safe3;

        grabbed:=FALSE;

        safe :=FALSE;

        safe2:=FALSE;

        safe3:=FALSE;

        SocketCreate server_socket;

        SocketBind server_socket, “127.0.0.1”, 55555;

        SocketListen server_socket;

        SocketAccept server_socket, client_socket\ClientAddress:=client_ip\Time:=WAIT_MAX;

        SetDO Attach, 0; 

        SetDO Detach,1;

        ! Waiting for a connection request

        WHILE TRUE DO

            SocketReceive client_socket \Str := receive_string;

            SocketSend client_socket \Str := receive_string;

            IF receive_string = “home” THEN

                MoveAbsJ home, v4000, z15, tool0;

            ENDIF

            IF receive_string = “go away” THEN

                 MoveAbsJ away, v4000, z15, tool0;

            ENDIF

            IF receive_string = “move 1” THEN

                 MoveAbsJ pos1, v4000, z15, tool0;

            ENDIF

             IF receive_string = “move 2” THEN

                 MoveAbsJ pos2, v4000, z15, tool0;

            ENDIF

            IF receive_string = “move 3” THEN

                 MoveAbsJ pos3, v4000, z15, tool0;

            ENDIF 

            IF receive_string = “move 4” THEN

                 MoveAbsJ pos4, v4000, z15, tool0;

            ENDIF 

            IF receive_string = “move 5” THEN

                 MoveAbsJ pos5, v4000, z15, tool0;

            ENDIF 

            IF receive_string = “move 6” THEN

                 MoveAbsJ pos7, v4000, z15, tool0;

            ENDIF 

             IF receive_string = “program 5” THEN

                 !Release original bottom block in original location 011

                 IF safe2=TRUE THEN

                    IF safe = FALSE  THEN

                        IF grabbed=FALSE THEN

                            grabbed:=TRUE;

                            MoveAbsJ safenewloc2, v4000, z15, tool0;

                            MoveAbsJ newloc2, v4000, z15, tool0;

                            WaitTime 1;

                            SetDO Attach, 1; 

                            SetDO Detach,0;

                            WaitTime 1;

                            MoveAbsJ safenewloc2, v4000, z15, tool0;

                            MoveAbsJ safepos, v4000, z15, tool0;

                            MoveAbsJ pos7, v4000, z15, tool0;

                            WaitTime 1;

                            SetDO Attach, 0; 

                            SetDO Detach,1;

                            WaitTime 1;

                            MoveAbsJ safepos, v4000, z15, tool0;

                        ELSE

                            receive_string := “Top block not moved”;

                            SocketSend client_socket \Str := receive_string;

                        ENDIF

                    ELSE

                        receive_string := “Top block not moved”;

                        SocketSend client_socket \Str := receive_string;

                    ENDIF

                 ELSE

                    receive_string := “You have the top block”;

                    SocketSend client_socket \Str := receive_string;

                 ENDIF

            ENDIF

            IF receive_string = “program 3” THEN

!                 MoveAbsJ pos6, v4000, z15, tool0;

                !Grab original bottom block from original location

               IF safe2=FALSE THEN

                    IF safe = TRUE  THEN

                        IF grabbed = FALSE THEN

                            MoveAbsJ safepos, v4000, z15, tool0;

                            MoveAbsJ pos7, v4000, z15, tool0;

                            WaitTime 1;

                            SetDO Detach,0;

                            SetDO Attach,1;

                            WaitTime 1;

                            grabbed:=TRUE;

                            MoveAbsJ safepos, v4000, z15, tool0;

                        ELSE

                            receive_string := “Top block not released”;

                            SocketSend client_socket \Str := receive_string;

                        ENDIF

                    ELSE

                        receive_string := “Top block not released”;

                        SocketSend client_socket \Str := receive_string;

                    ENDIF

                ELSE

                    receive_string := “Top block not moved”;

                    SocketSend client_socket \Str := receive_string;

                ENDIF

            ENDIF

            IF receive_string = “program 1” THEN

                !Grab original top block from original location

                 IF safe2=FALSE THEN

                     IF safe=FALSE THEN

                         IF grabbed=FALSE THEN

                             grabbed:=TRUE;

                             MoveAbsJ safepos1, v4000, z15, tool0;

                             MoveAbsJ pos6, v4000, z15, tool0;

                             WaitTime 1;

                             SetDO Attach, 1;

                             SetDO Detach,0;

                             WaitTime 1;

                             MoveAbsJ pos6, v4000, z15, tool0;

                             MoveAbsJ safepos1, v4000, z15, tool0;

                         ELSE

                            receive_string := “Block in Gripper”;

                            SocketSend client_socket \Str := receive_string;

                         ENDIF

                     ELSE

                            receive_string := “Block in Gripper”;

                            SocketSend client_socket \Str := receive_string;

                    ENDIF

                 ELSE

                    receive_string := “Bottom block not placed”;

                    SocketSend client_socket \Str := receive_string;

                 ENDIF

            ENDIF

            IF receive_string = “program 2” THEN

                !Move original top block to new location

                IF safe2=FALSE THEN

                    IF safe=FALSE THEN

                        IF grabbed = TRUE  THEN

                                MoveAbsJ safenewloc1, v4000, z15, tool0;

                                MoveAbsJ newloc1, v4000, z15, tool0;

                                WaitTime 1;

                                SetDO Attach, 0; 

                                SetDO Detach,1;

                                WaitTime 1;

                                grabbed:=FALSE;

                                safe:=TRUE;

                                MoveAbsJ safenewloc1, v4000, z15, tool0;

                        ELSE

                            receive_string := “Top block not grabbed”;

                            SocketSend client_socket \Str := receive_string;

                        ENDIF

                    ELSE

                        receive_string := “Top block not grabbed”;

                        SocketSend client_socket \Str := receive_string;

                    ENDIF

                ELSE

                    receive_string := “Top block not moved”;

                    SocketSend client_socket \Str := receive_string;

                ENDIF

            ENDIF

            IF receive_string = “program 4” THEN

                !Move original bottom block to new location

                IF safe2=FALSE THEN

                    IF safe = TRUE  THEN

                        IF grabbed = TRUE  THEN

                                MoveAbsJ safenewloc2, v4000, z15, tool0;

                                MoveAbsJ newloc2, v4000, z15, tool0;

                                WaitTime 1;

                                SetDO Attach, 0; 

                                SetDO Detach,1;

                                WaitTime 1;

                                grabbed:=FALSE;

                                safe:=FALSE;

                                safe2:=TRUE;

                                MoveAbsJ safenewloc2, v4000, z15, tool0;

                        ELSE

                            receive_string := “Bottom Block not grabbed”;

                            SocketSend client_socket \Str := receive_string;

                        ENDIF

                    ELSE

                        receive_string := “Bottom Block not grabbed”;

                        SocketSend client_socket \Str := receive_string;

                    ENDIF

                ELSE

                    receive_string := “Top block not moved”;

                    SocketSend client_socket \Str := receive_string;

                ENDIF

            ENDIF

            IF receive_string = “program 6” THEN

                !Release original top block in original location

                IF safe2 =TRUE THEN

                    IF safe=FALSE THEN

                        IF grabbed = TRUE  THEN

                                    MoveAbsJ safenewloc1, v4000, z15, tool0;

                                    MoveAbsJ newloc1, v4000, z15, tool0;

                                    WaitTime 1;

                                    SetDO Attach, 1; 

                                    SetDO Detach,0;

                                    WaitTime 1;

                                    MoveAbsJ safenewloc1, v4000, z15, tool0;

                                    MoveAbsJ safepos1, v4000, z15, tool0;

                                    WaitTime 1;

                                    SetDO Attach, 0; 

                                    SetDO Detach,1;

                                    WaitTime 1;

                                    MoveAbsJ safepos1, v4000, z15, tool0;

                                    safe2:=FALSE;

                                    safe:=FALSE;

                                    grabbed:=FALSE;

                            ELSE

                                receive_string := “Top block not grabbed”;

                                SocketSend client_socket \Str := receive_string;

                            ENDIF

                         ELSE

                            receive_string := “Top block not grabbed”;

                            SocketSend client_socket \Str := receive_string;   

                        ENDIF

                ELSE

                        receive_string := “Top block not grabbed”;

                        SocketSend client_socket \Str := receive_string;

                ENDIF

            ENDIF

            IF receive_string = “io 1” THEN

                SetDO Attach,0; 

                SetDO Detach,1;

                grabbed:=FALSE;

            ENDIF

            IF receive_string = “io 2” THEN

                SetDO Attach,1; 

                SetDO Detach,0;

                grabbed:=TRUE;

            ENDIF

            IF receive_string = “end” THEN

                SocketClose client_socket;

            ENDIF

        ENDWHILE

    ERROR

        RETRY;

    UNDO

        SocketClose server_socket;

        SocketClose client_socket;

    ENDPROC

ENDMODULE