OK. It LOOKS like it’s working correctly in my simulator, but we shipped all our robots to Detroit for a tradeshow, so I can’t verify in reality lol.
Here’s the Polyscope I was using to test it:

The Waypoints are just a simple up-down motion so I could easily see what was “one part.” They are also taught to Plane_1. You’d replace everything in the loop with your programmed part. Making it a subprogram call might be a really clean way of implementing that. You’ll want to ensure that all those moves are taught to a Feature though (And I’d just teach that Feature with the same orientation as Base so it doesn’t get confusing).
So basically, just throw the script in at the top, use an Assignment node to create a “currentPart” variable and assign it to 1. (That naming is important. You can name it whatever you want, but you’ll have to change it in the script as well.)
When constructing the Assignment node at line 10, manually type the “shiftPlane(” part, then select the POSE version of your Feature (should come with the “_const” already attached to it) and then pass the VARIABLE version of the same Feature (won’t have the “_const” part). The last argument is the number of columns. From your picture, you had 4, so I put 4.
In the program, the robot dips down then back up. Then shifts in X direction. Dips down then up. Shift in X. It does that 4 times. Then it shifts a large amount in y, and resets its X offset. Then it dips down and up 4 more times, then shifts a small amount in y. It does this for as long as currentPart is less than TOTAL_PARTS. So you’ll have to decide how to terminate that loop. You can assign TOTAL_PARTS in the before start, or make it an installation variable, or just hard-code 40 parts or whatever.
Finally, after the loop, I reassigned the VARIABLE version of the Feature to the POSE version of the Feature. I’ve had some issues in the past if I forget to do this, otherwise subsequent runs of the program can start at the last offset point. Which is no bueno.
Here’s the script to try out:
global x_offset = 0.01
global y_offset_small = 0.01
global y_offset_large = 0.1
def shiftPlane(plane_const, plane_var, columns):
#check to see if you've reached the end of the row. If you have, reset your x position back to its original value. Otherwise, shift it
if(currentPart % columns == 0):
plane_var[0] = plane_const[0]
else:
plane_var[0] = plane_var[0] + x_offset
end
#check to see if you've run 2 rows of parts. If you have, shift Y by the small amount. Otherwise, shift by the large amount
if((currentPart) % (2 * columns) == 0):
plane_var[1] = plane_var[1] + y_offset_small
elif(currentPart % columns == 0):
plane_var[1] = plane_var[1] + y_offset_large
end
currentPart = currentPart + 1
return plane_var
end
I’ve got the offsets hardcoded at the top, so you would just replace those values with whatever your x and y offsets are (in METERS). If you need/want these to be more modular, you can add them as arguments to the shiftPlane() function, and pass them in from the main program.
Give it a go (preferably at less than 100% speed lol) and let me know if something isn’t working right/doesn’t make sense.