Hello Community!
Our development team is planning to add support for variable length lists to URScript.
Variable length list still needs to declare maximum capacity. Runtime dynamic heap allocations are not allowed due to the real-time nature of URScript.
We would like to get your input on use cases from your fields of expertise where this feature would be useful. Additionally we would like to get input for list operations you would need.
Functionality will be introduced in Polyscope 5.15.
Creating lists
Fixed length lists can be created with square bracket operator:
aa = [11, 22, 33, 44, 55, 66, 77]
Both length and capacity of this list is equal to 7
New script function accepts length, and capacity separately:
bb = make_list(length = 7, initial_value = 11, capacity = 20)
Length of this list is 7, but capacity is 20. List can be extended and contracted between 0, and 20 numeric elements
Types of values
Lists can hold any type that URScript supports. This includes complex values created with struct()
keyword.
List can hold only one type of value.
aa = [1, 2, 3.5, 4, 5.5]
bb = make_list(10, struct(p1 = 1, p2 = "text"), 10)
cc = ["a", "b", "c", "d"]
List properties
Length, and capacity properties can be accessed by built-in methods
a = aa.length()
b = aa.capacity()
Modifying lists
Lists can be modified with built-in methods:
aa.append(88) # add element to the end of the list, length increases, exception thrown if capacity exceeded
a = aa.pop() # pop last element of the list, and decrease length
aa.insert(1, 22.5) # inserts new value on index 1, increase list length, and shift elements after inserted value
aa.clear()
Assignments between lists
List can be assigned only to existing list of greater or equal capacity to the length of source list
aa = [1, 2, 3, 4, 5, 6] # aa.length() == 6, aa.capacity() == 6
bb = make_list(5, 0, 100) # bb.length() == 5, bb.capacity() == 100
aa = bb # aa.length() == 5, aa.capacity() == 6, aa.to_string() == "[0, 0, 0, 0, 0]"
aa = [1, 2, 3, 4, 5, 6]
bb = aa # bb.length() == 6, bb.capacity() == 100
Accessing structured data types
List can hold structs (aka complex data types). All structs in the list have to be of exactly the same type.
aa = make_list(10, struct(p1 = 1, p2 = "text"), 10)
a = aa[4].p1 # a = 1
b = aa[4].p2 # b = "text"
aa[3].p1 = 22.5
aa[4] = struct(p1 = 99, p2 = "different text")
Error handling
All errors are reported as Runtime Exceptions, and stop the program.
length()
, and capacity()
methods on the lists aid in defensive programming to avoid stopping the program.
Limitations
We already have a list of limitations on what lists would not be able to do:
- lists can be passed to, and returned from functions only as copy by value
- list elements can’t change type
- if list is returned from a function or list method, then target list have to be earlier initialized with enough capacity
- list of lists is not supported as this is how matrices are implemented in URScript
We’re waiting for your feedback.
Hi,
This is surely a useful extension to URScript.
Application example from “my world”:
- Cameras can locate more than one objects per image.
- With dynamic lists, a list of poses that is as long as the number of objects can be created
=> This facilitates the programming for this use case and allows to create solutions that are more flexible with less constraints.
Best regards
Klaus
This is great to hear of this feature.
I am applying adhesive onto beams. The number of areas to apply adhesive to varies. Whilst I can use lists of a large size and fill in additional areas with “skip” commands, it would be easier to just have a list that varies in length depending upon what the user requires.
A variable list would solve this.
Hi
This is excellent news!
With this feature/possibility, users of iSee Ui will be able to do so much more.
Basically, they can then create an “add” button, and with this script code they can add a configuration/item/position/ … to a list without having the need to specify the length, and be limited to that specific length.
For instance:
- Add a user, if a user login HMI is created
- Add a pallet layout, if a palletizer HMI is created
- Add a product, in any kind of project: palletizing, machine tending, pick and place, …
- Add a position, for custom path planning. Can be useful in welding, palletizing, … .
- …
You mentioned list of lists is not supported, but I assume that a list of pose “list” will still be possible?
And will it be possible to initialize a list of poses?
So: make_list(length = 50, initial_value = p[0,0,0,0,0,0], capacity = 100)
For the required operations:
- adding an item. Both to the end of the list, or inserting on a specific index and all items from that index shift 1 index to the back. => should already be possible with the “.append” and “.insert”, from what I read in your message.
- Removing an item and decrease length. Both from the last index (=> should already be possible with the “.pop”) but also from a specific index. And from what I read, this removing from a specific index is not possible?
- Clearing a list. => should be possible with the “.clear”
Looking forward to this update!
Kind regards
Dieter Verhofstadt
What will be the max capacity of a dynamic list?
This is wonderful news and great progress for the UR script functionality.
In some of our applications the users will be able to add list items to an existing list during runtime via external input.
Additional list functionality I’d love to see would be sort lists. In same cases finding the biggest number or smallest number in a list is practical to choose what to do first.
Being able to shift all items in a list one position or remove a specific item in a list.
Thes functions are some of the core functions in Python 3.0 and are very practical when working with lists.
Med vennlig hilsen/Best regards
Magnus
Kameleon Solutions AS
Sure, that will be possible. It actually looks like variable list of poses is primary use case
HMI on a robot is not in scope, but I can see that this would be a great enabler.
It is limited by a stack size for a robot program. Program needs preallocated fixed size stack for each thread that has size in range of single MBytes. Without going into too many details, if there is reasonable number of lists then they can be thousands of elements long.
We’re looking into that function as well. Problem is that some of our types do not have very well defined compare operations (poses, international strings). Interesting topic for further discussions.
How about a list of “String” elements?
This is as of now not possible in URScript, but there are cases where this is desired.
Will this also be made possible?
Hi, I would like to see tuples introduced as return values for functions.
Would structs be able to solve your use case?
Following program returns 3 different data types as a single structure:
def return_struct():
return struct(int_val = -1, str_val = "connection error", data = [11, 22, 33, 44])
end
result = return_struct()
textmsg("int_val = ", result.int_val)
textmsg("str_val = ", result.str_val)
textmsg("data = ", result.data)
Preview of structs feature will already be available in 5.14.0 (for prototyping purposes).
Sure, support is coming
str_list = make_list(5, "initial text")
str_list[0] = "another text"
str_list[1] = ""
str_list[2] = "very very very very very very very very long text"
Could you add description of your use case?
Excellent!
The use case is the same, being iSee Ui.
So with an array of “String”, the users of iSee Ui will be able to do much more.
For instance, a “List” item of “Combobox” item on the HMI can show the content of an Array, but at the moment only arrays of type “Integers”, “Double” and “Boolean” are possible.
Having the possibility to also use an array of “String” will be much more easier. (For instance in production to use names instead of id’s).
Hi mmi…
are you planing on extending this functionality to Matrices or nested lists?
Best Regards Casper
Yes, but not in the first phase. Due to higher technical complexity, it would require more significant pull from the market.
It would be possible to “emulate” list of lists with structs. Struct is mutable, indexable, but not extendable.
list_of_lists = struct(l1 = [1, 2, 3, 4, 5, 6], l2 =make_list(100, 0))
list_of_lists[0] = [11, 22, 33]
list_of_lists[0][2] = 333
However it is not possible to extend struct by another list on runtime.
We’ll be happy to add it if there is strong demand.
The proposed functionality overall seems good. Initializing lists of poses easily is important. If sorting could be limited to lists of integers I think that would be sufficient.
Great feedback everyone, and thank you UR guys for reaching out.
A lot of our issues are solved by structs and the lists improvements will make up for the matrix shortcomings.
I would like to also add that python-style list slicing/indexing functionality would be nice and allow for a lot cleaner code:
a = [0, 1, 2, 3, 4]
# Specify a range
>>> a[0:2]
$ [0, 1, 2]
# reverse index for indexing from last element
>>> a[-1]
$ 4
# Start -> end
>>> a[0:]
$ [0, 1, 2, 3, 4]
# anything before 3rd element
>>> a[:3]
$ [0, 1, 2]
# Start -> end -> taking every 2nd element
>>> a[0::2]
$ [0, 2, 4]
I’m happy to announce that variable length lists, and object-like methods on lists, structs, and matrices were introduced in 5.15.0 (UR20/30 only, but for evaluation purposes runs on all eSeries).
5.15.1 with support for all eSeries will be available shortly.
In the meantime dockerized simulator is already available with tag 5.15.0.
Check out new section “Lists and Structs” in attached script manual.
scriptmanualG5_5.15.0.pdf (951.3 KB)
Let me know if you have any use cases not covered.