Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

In headless mode however, the robot should be able to palletize without using the teach pendant. This means an external interface will be used to select product, continue or terminate existing pallet, display current progress, etc.

To run Pally without the teach pendant we recommend using the Order Processing functions, deactivate the Operator Interface, and Redirect all Popups using registers. During progra program run it is also possible to send a detailed progress report via Callbacks.

...

It is recommended to select Operator Interface: “None”. This will instruct the code generator to exclude all script functions related to the Pally operator interface. As a result, the robot program will be shorter, and start faster.

...

This will instruct the Pally program to get the pattern name and requested number of boxes from the Order Management API. The program will keep running until terminated by external control signals or a fatal error occurs.

...

Using the product selection variables rf_product_selection_strategy and rf_product_selection_predefined is still possible, but not recommended when using Pally in headless mode. These variables are useful when creating a simple palletizer application with a fixed pallet pattern, but won’t provide a flexible workflow that can be easily controlled by an external controller.

Easy pattern selection

In headless mode it is a common practice to use a special naming convention in pattern names to make pattern selection via external control systems easy. In the following example we use 4-digit integer number in the pattern file name to identify patterns, assuming every pattern file name is in the format

P_nnnn_patternname.json

where nnnn is a 4-digit integer in the middle of a file name.

The function goes through the list of available patterns in the patterns folder, and return the file name that matches the search criteria.

Code Block
def find_pattern_by_nr(pattern_number):
  # convert number to string with zero padding
  local p_str = str_cat("0000", pattern_number)
  # make sure the string is not longer than 4 digits
  p_str = str_sub(p_str, str_len(p_str)-4, 4)
  local nr_patterns = palletmanager_daemon.init_pattern_cache()
  local pattern_nr = 0
  while (pattern_nr < nr_patterns):
    local file_name = palletmanager_daemon.get_file_name(pattern_nr)
    textmsg("Testing pattern name: ", file_name)
    if (str_len(file_name) >= 6):
      if (str_sub(file_name, 2, 4) == p_str):
        textmsg("Found pattern name: ", file_name)
        return file_name
      end
    end
    pattern_nr = pattern_nr + 1
  end
  return ""
end

Pallet confirmation signals

...

Info

The pallet confirmation signal must go from LOW to HIGH and remain HIGH for at least 300 msec to confirm a new empty pallet.

...

Use Callbacks to report the current progress back to the external control system. Use the global Pally variables such as ProductName, PalletNr and ProductCount. You can also get more detailed information via from the Pallet Manager Daemon.

Suppress popups

It is possible to suppress Pally popups and use IO registers to indicate an error/warning. An integer code and an optional integer parameter is provided.

...

  • This position cannot be palletized (2002)

  • Unable to find a collision-free path (2011)

  • this pattern hasn’t been fully verified (2023)

  • controlling the external hardware failed (2005)

  • vacuum not detected (1016)

  • … (more to come)

Example

Control loop via OPC UA

A script code is provided to receive commands from OPC UA. The command sequence is the following:

  • set the requested command in the OPC UA variable “COMMAND”

  • set the command parameters (specific to each command)

  • increment the variable value “COMMAND_SEQNO” by one

  • wait until “COMMAND_RESULT_SEQNO” is updated to the same value as “COMMAND_SEQNO”

  • read command result from the variable “COMMAND_RESULT” (possible values: 1=OK, 0=not OK)

Currently the following commands are supported:

  • “NEW_ORDER”: create a new palletizing task (pattern name and number of boxes)

  • “TERMINATE_PALLET”: immediately terminate the current pallet

  • “DELETE_ORDERS”: remove all orders from the queue

  • “EXIT”: terminate the robot program

To create a new palletizing task (NEW_ORDER):

  • set the requested command “NEW_ORDER” in the OPC UA variable “COMMAND”

  • set the pallet manager instance (optionally) in the variable “INSTANCE” or leave it to 0

  • set the requested pattern in the variable “PATTERN” (4-digits integer format)

  • set the requested number of boxes in the variable “BOXES” or leave it to 0 for a full pallet

  • increment the variable value “COMMAND_SEQNO”

  • wait until “COMMAND_RESULT_SEQNO” is updated to the same value as “COMMAND_SEQNO”

  • read command result from the variable “COMMAND_RESULT” (1: OK, 0: not OK)

Sample code to control Pally via OPC UA.

Code Block

def find_pattern_by_nr(pattern_number):
  # convert number to string with zero padding
  local p_str = str_cat("0000", pattern_number)
  # make sure the string is not longer than 4 digits
  p_str = str_sub(p_str, str_len(p_str)-4, 4)
  local nr_patterns = palletmanager_daemon.init_pattern_cache()
  local pattern_nr = 0
  while (pattern_nr < nr_patterns):
    local file_name = palletmanager_daemon.get_file_name(pattern_nr)
    textmsg("Testing pattern name: ", file_name)
    if (str_len(file_name) >= 6):
      if (str_sub(file_name, 2, 4) == p_str):
        textmsg("Found pattern name: ", file_name)
        return file_name
      end
    end
    pattern_nr = pattern_nr + 1
  end
  return ""
end

def do_insert_order():
  local instance = opcua_server_read_byname("INSTANCE")
  local pattern = opcua_server_read_byname("PATTERN")
  local boxes = opcua_server_read_byname("BOXES")
  local pattern_name = find_pattern_by_nr(pattern)
  local result = 0
  if (pattern_name != ""):
    local nr_orders = palletmanager_daemon.get_nr_orders(instance)
    if (palletmanager_daemon.insert_order(instance, nr_orders, "SOME_ID", "My Order", pattern_name, [1, boxes])):
      textmsg(str_cat("Inserted order ", pattern_name), str_cat(" with boxes ", boxes))
      result = 1
    end
  end
  return result
end

def do_terminate_pallet():
  local result = 0
  local instance = opcua_server_read_byname( "INSTANCE")
  if (instance==0):
    rf_P1_terminate = True
    rf_P2_terminate = True
    result = 1
  end
  return result
end

def do_delete_orders():
  local instance = opcua_server_read_byname("INSTANCE")
  local result = 0
  if (palletmanager_daemon.clear_orders(instance)):
    result = 1
  end
  return result
end

thread OpcPallyThread():
  local command = ""
  local can_go = True
  while (can_go):
    local last_command_seqno = opcua_server_read_byname("COMMAND_SEQNO")
    local command_seqno = last_command_seqno
    while (command_seqno == last_command_seqno):
      sleep(1)
      command_seqno = opcua_server_read_byname("COMMAND_SEQNO")
    end
    last_command_seqno = command_seqno
    command = opcua_server_read_byname("COMMAND")
    local result = -999 # command not supported
    if (command == "NEW_ORDER"):
      result = do_insert_order()
    elif (command == "TERMINATE_PALLET"):
      result = do_terminate_pallet()
    elif (command == "DELETE_ORDERS"):
      result = do_delete_orders()
    elif (command == "EXIT"):
      can_go = False
      result = 1
    end
    opcua_server_write_byname("COMMAND_RESULT", result)
    opcua_server_write_byname("COMMAND_RESULT_SEQNO", command_seqno)
  end
  # received EXIT command, terminate program
  halt
end


opc_pally_thid = run OpcPallyThread()

Info

The example is not suitable for Dual Product mode.

Palletizing progress via OPC UA

A script code is provided that updates some OPC UA variables during palletizing.

  • The actual pattern name will be visible in the string variable “CURRENT_PATTERN”.

  • The number of boxes being picked (1 for single pick, 2 for double pick, etc) in the “CURRENT_PICK” variable.

  • The variable “CURRENT_BOXES” contains the number of boxes already on the pallet.

  • The remaining boxes are indicated in “REMAINING_BOXES”.

  • The variable “PALLET_NUMBER” contains 1 when palletizing on the right pallet, 2 for the left pallet.

Code Block
def update_progress_opc(pallet_nr, pattern_name, current_pick, current_boxes, remaining_boxes):
  opcua_server_write_byname("CURRENT_PALLET", pallet_nr)
  opcua_server_write_byname("CURRENT_PATTERN", pattern_name)
  opcua_server_write_byname("CURRENT_PICK", current_pick)
  opcua_server_write_byname("CURRENT_BOXES", current_boxes)
  opcua_server_write_byname("REMAINING_BOXES", remaining_boxes)
end

def update_progress(after_pallet=False):
  # OBS instance number is always 0 in single product mode
  # use instance 0 or 1 in Dual product mode
  local st = palletmanager_daemon.get_pallet_state(0)
  local current_pick = st[0]-st[1]-st[2]
  local current_boxes = st[1]
  local remaining_boxes = 0
  if (not after_pallet):
    remaining_boxes = st[0]-st[1]
  end
  update_progress_opc(PalletNr, ProductName, current_pick, current_boxes, remaining_boxes)
end

def BeforePalletAction():
  update_progress()
end

def AfterGrabAction():
  update_progress()
end

def AfterReleaseAction():
  update_progress()
end

def AfterPalletAction():
  update_progress(True)
end 

The script functions are called from Pally callbacks:

  • beforePallet: call the function BeforePalletAction()

  • afterGrab: call the function AfterGrabAction()

  • afterRelease: call the function AfterReleaseAction()

  • afterPallet : call the function AfterPalletAction()

Add more function calls if needed.

...

Error codes via OPC UA

A script code is provided to suppress app popups and redirect error codes into registers.

The error/warning code will be set in the OPC UA integer variable “STATE_CODE”. Optional parameter of the error/warning can be accessed in the “STATE_PARAM” integer variable. If an error/warning occurs, the boolean variable “STATE_FLAG” will be set to true.

Info

Please note: the sample program will not simulate user acknowledgement of errors.

Code Block
rf_out_state_error = [3, 64]
rf_out_state_warning = [3, 64]
rf_out_state_info = [3, 64]
rf_out_state_code = [5, 24]
rf_out_state_param = [5, 25]

# Cannot simulate robot inputs
# program will continue after 1 sec. anyway
rf_in_state_ack = [-1, -1]

thread PopupToOpcConverterThread():
  opcua_server_write_byname("STATE_FLAG", False)
  while (True):
    # wait until state out
    while (not read_output_boolean_register(64)):
      sleep(0.1)
    end
    opcua_server_write_byname("STATE_CODE", read_output_integer_register(24))
    opcua_server_write_byname("STATE_PARAM", read_output_integer_register(25))
    opcua_server_write_byname("STATE_FLAG", True)
    textmsg("waiting for user ACK")
    # user must acknowledge via OPC UA by clearing STATE FLAG value
    while (opcua_server_read_byname("STATE_FLAG")):
      sleep(0.1)
    end
    # send ACK back to Pally
    # TODO cannot simulate input ACK
    # write...register()
    while (read_output_boolean_register(64)):
      sleep(0.1)
    end
    # TODO cannot simulate input 
    # write...register()
    textmsg("continue after user ACK")
  end
end

popcthid = run PopupToOpcConverterThread()

Running the example

See headless Pally in action here:

...

Download the example

Download the complete example here:

View file
nameHeadless.zip

Current limitations

Some popups cannot be suppressed.

...

Image Added