Tecan Initialization Error

I ran it again today with the new coordinates C5PAA7545,3100,90,[2100] and got a collision error. Since I already initialized the Tecan before running the set up code, the ROMA and LiHA both starts off at the far right of the Tecan and the MCA96 starts off at the far left.

Traceback:

Traceback (most recent call last):
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\trysetup.py", line 11, in <module>
    asyncio.run(setup())
  File "C:\Program Files\Python311\Lib\asyncio\runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\trysetup.py", line 9, in setup
    await lh.setup()
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\pylabrobot\liquid_handling\liquid_handler.py", line 117, in setup
    await super().setup()
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\pylabrobot\machine.py", line 63, in setup
    await self.backend.setup()
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\pylabrobot\liquid_handling\backends\tecan\EVO.py", line 260, in setup
    await self.liha.position_absolute_all_axis(7545, 3100, 90, [2100] * self.num_channels)
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\pylabrobot\liquid_handling\backends\tecan\EVO.py", line 879, in position_absolute_all_axis       
    raise TecanError("Invalid command (collision)", self.module, 2)
pylabrobot.liquid_handling.backends.tecan.errors.TecanError: ('Invalid command (collision)', 'C5', 2)
PS C:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main>

Log file:

2024-03-26 14:37:54,377 - pylabrobot - INFO - Finding USB device...
2024-03-26 14:37:54,385 - pylabrobot - INFO - Found USB device.
2024-03-26 14:37:54,385 - pylabrobot - INFO - Found endpoints. 
Write:
       ENDPOINT 0x2: Bulk OUT ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x2 OUT
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x0 
Read:
       ENDPOINT 0x81: Bulk IN ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x81 IN
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x0
2024-03-26 14:39:54,402 - pylabrobot - INFO - Sent command: C5PIA
2024-03-26 14:40:21,117 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C5\x80\x00')
2024-03-26 14:40:21,117 - pylabrobot - INFO - Sent command: C5BMX2
2024-03-26 14:40:21,123 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C5\x80\x00')
2024-03-26 14:40:21,131 - pylabrobot - INFO - Sent command: C1PIA
2024-03-26 14:41:10,973 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C1\x80\x00')
2024-03-26 14:41:10,978 - pylabrobot - INFO - Sent command: C1BMX2
2024-03-26 14:41:10,984 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C1\x80\x00')
2024-03-26 14:41:10,985 - pylabrobot - INFO - Sent command: C1PIX
2024-03-26 14:41:12,396 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C1\x80\x00')
2024-03-26 14:41:12,396 - pylabrobot - INFO - Sent command: C1RPX0
2024-03-26 14:41:12,404 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C1\x803556\x00')
2024-03-26 14:41:12,404 - pylabrobot - INFO - Sent command: C1SAA1,9000,2000,2464,1800,,1,0,0
2024-03-26 14:41:12,413 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C1\x80\x00')
2024-03-26 14:41:12,413 - pylabrobot - INFO - Sent command: C1AAC
2024-03-26 14:41:13,808 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C1\x80\x00')
2024-03-26 14:41:13,810 - pylabrobot - INFO - Sent command: C1RPX0
2024-03-26 14:41:13,816 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C1\x808999\x00')
2024-03-26 14:41:13,819 - pylabrobot - INFO - Sent command: C5PIX
2024-03-26 14:41:15,028 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C5\x80\x00')
2024-03-26 14:41:15,028 - pylabrobot - INFO - Sent command: C5RNT1
2024-03-26 14:41:15,028 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C5\x808\x00')
2024-03-26 14:41:15,028 - pylabrobot - INFO - Sent command: C5RPX5
2024-03-26 14:41:15,037 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C5\x809849\x00')
2024-03-26 14:41:15,037 - pylabrobot - INFO - Sent command: C5RPY5
2024-03-26 14:41:15,037 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C5\x802824,2824\x00')
2024-03-26 14:41:15,037 - pylabrobot - INFO - Sent command: C5RPZ5
2024-03-26 14:41:15,052 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C5\x802100,2100,2100,2100,2100,2100,2100,2100\x00')
2024-03-26 14:41:15,052 - pylabrobot - INFO - Sent command: C5SHZ2100,2100,2100,2100,2100,2100,2100,2100
2024-03-26 14:41:15,061 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C5\x80\x00')
2024-03-26 14:41:15,061 - pylabrobot - INFO - Sent command: C5RPX0
2024-03-26 14:41:15,069 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C5\x802140\x00')
1 Like

Yes, this is exactly the problem Iā€™m having. As a work-around, Iā€™m initializing with EVOware, then switching to PLR. But Iā€™m still trying to figure out what EVOware is doing to initialize that is missing in PLR.

Thanks

1 Like

I see, so PLR works after initializing the Tecan? If thatā€™s the case I think it would help with my project, does it work normally with the LiHa and ROMA on your end?

Can this be broken out into C5PAX, PAY, PAZ(1,2,3,4,5,6,7,8) to isolate the axes? Once isolating, run C5REE to pull error states and PIX/PIY/PIZ to reinitialize the axes with error (if present). Iā€™m not sure this will provide a solution, but it will add some additional info to the troubleshooting. At the least, it could inform us if thereā€™s a error relevant to the deck configuration or the actual arm config.

If REE provides no error, attempting different PA[X/Y/Z] could suss out a positional issue.

it may also be worth removing carriers while running firmware commands to avoid any potential collisions that could damage the system.

Yes, after I connect w/ EVOware I can switch to PLR. I can use PLR to initialize, then any other command.

Iā€™m still trying to figure out some PLR details, and it seems like the EVO backend is far less developed than the STAR. But I was able to pick up some tips.

Sorry, what do you mean by this though? Iā€™m not really sure how to test out your suggestion since I kind of just edit the coordinates in the EVO.py under the liquid handling backends.

Also, does anyone happen to know how I can get rid of the wash station at the left? The Tecan Iā€™m using has a te-stack attached so Iā€™m thinking of adding 2 carriers at the far left but the wash station seems to be part of the EVO150Deck()?

you can unassign the wash station with deck.get_resource("wash_station").unassign().

I added a with_wash_station (default True) argument to TecanDeck.__init__ to allow you to initialize the deck without the wash station. add with_wash_station param to TecanDeck.__init__ Ā· PyLabRobot/pylabrobot@af2784d Ā· GitHub

Thank you very much :pray: :pray:

Also, do you happen to know why this error shows up?

PS C:\Users\Name\Downloads\plr test> & "C:/Program Files/Python311/python.exe" "c:/Users/Name/Downloads/plr test/visualisertest.py"
Resource wash_station was unassigned from the robot.
Resource testack was assigned to the robot.
WARNING: total_tip_length <= 0.
Please get in touch at https://forums.pylabrobot.org/c/pylabrobot/23
WARNING: total_tip_length <= 0.
Please get in touch at https://forums.pylabrobot.org/c/pylabrobot/23
WARNING: total_tip_length <= 0.
Please get in touch at https://forums.pylabrobot.org/c/pylabrobot/23
WARNING: total_tip_length <= 0.
Please get in touch at https://forums.pylabrobot.org/c/pylabrobot/23
WARNING: total_tip_length <= 0.
Please get in touch at https://forums.pylabrobot.org/c/pylabrobot/23
WARNING: total_tip_length <= 0.
Please get in touch at https://forums.pylabrobot.org/c/pylabrobot/23
WARNING: total_tip_length <= 0.
Please get in touch at https://forums.pylabrobot.org/c/pylabrobot/23
WARNING: total_tip_length <= 0.
Please get in touch at https://forums.pylabrobot.org/c/pylabrobot/23
WARNING: total_tip_length <= 0.

what tips are you using? Also, since this seems to be unrelated to the initialization error, could you create a new thread so we keep everything searchable and productive for future users? thanks

link for future reference: Total Tip Length Error

I was trying around with the Tecan again, and wanted to ask is it possible to change the destination where the LiHa thinks the wash station is in the EVO.py for setup?

    # Initialize plungers. Assumes wash station assigned at rail 1.
    await self.liha.set_z_travel_height([self._z_range] * self.num_channels)
    await self.liha.position_absolute_all_axis(7545, 3100, 90, [2100] * self.num_channels)
    await self.liha.initialize_plunger(self._bin_use_channels(list(range(self.num_channels))))
    await self.liha.position_valve_logical([1] * self.num_channels)
    await self.liha.move_plunger_relative([100] * self.num_channels)
    await self.liha.position_valve_logical([0] * self.num_channels)
    await self.liha.set_end_speed_plunger([1800] * self.num_channels)
    await self.liha.move_plunger_relative([-100] * self.num_channels)
    await self.liha.position_absolute_all_axis(7545, 3100, 90, [self._z_range] * self.num_channels)

Also, maybe it could be that I typed the code wrongly for Tecan, but what should be the right way to send commands to the Tecan? Here is my code I tried today:

from pylabrobot.liquid_handling import LiquidHandler
from pylabrobot.liquid_handling.backends import ChatterBoxBackend
from pylabrobot.liquid_handling.backends.tecan.EVO import LiHa, RoMa, EVO
from pylabrobot.visualizer.visualizer import Visualizer
from pylabrobot.resources.tecan import EVO150Deck
import asyncio
from pylabrobot.resources import (
    wash,
    tip_racks,
    tip_creators,
    tip_carriers,
    tecan_decks,
    plate_carriers,
    plates
)
from pylabrobot.liquid_handling.standard import (
  Pickup,
  Drop,
  Aspiration,
  Dispense,
  Move,
  GripDirection
)

lh = LiquidHandler(backend=EVO(), deck=EVO150Deck(with_wash_station = False))
#lh.deck.get_resource("wash_station").unassign()

dt1 = tip_carriers.DiTi_SBS_3_Pos_MCA96(name = "tip rack carrier")
lh.deck.assign_child_resource(resource = dt1, rails = 6)

dt = tip_carriers.DiTi_SBS_4_Pos_MCA96(name = "tip rack carrier1")
dt[3] = dttwo = tip_racks.DiTi_200ul_Filter_LiHa(name = "tip rack")
lh.deck.assign_child_resource(resource = dt, rails = 12)

plate = plate_carriers.MP_4Pos_flat(name = "plate carrier1")
plate[3] = plates.DeepWell_96_Well(name = "plate", with_lid= False)
plate[1] = plates.CaCo2_Plate_24_Well(name = "24 plate", with_lid = False)
plate[0] = plates.CaCo2_Plate_24_Well(name = "24 plate2", with_lid = False)
lh.deck.assign_child_resource(resource = plate, rails = 18)

station = wash.Wash_Station(name = "wash station")
lh.deck.assign_child_resource(resource = station, rails = 26)

dt2 = tip_carriers.DiTi_3Pos___Waste(name = "plate carrier2")
lh.deck.assign_child_resource(resource = dt2, rails = 27)

plate3 = plate_carriers.MP_3Pos_PCR(name = "plate carrier3")
plate3[0] = plates.DeepWell_96_Well(name = "well3", with_lid = False)
lh.deck.assign_child_resource(resource = plate3, rails = 33)

async def visualiser():
    await lh.setup()
    evo = EVO(diti_count= 96)
    EVO.num_channels = 8
    evo.liha = LiHa(backend = evo, module = EVO.LIHA)
    op = Pickup(
        resource= dttwo.get_items("A1:H1"),
        offset= 0,
        tip=("A1:H1")
    )
    await evo.liha.get_disposable_tip(tips= [op], z_start = 877.0, z_search = 5.0)

asyncio.run(visualiser())

And this is the traceback (managed to run the setup code by commenting out the plunger initialization):

PS C:\Users\Name\Downloads\pylabrobot-main> & "C:/Program Files/Python311/python.exe" "c:/Users/Name/Downloads/pylabrobot-main/pylabrobot-main/trysetup.py"
WARNING: total_tip_length <= 0.
Please get in touch at https://forums.pylabrobot.org/c/pylabrobot/23
Traceback (most recent call last):
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\trysetup.py", line 67, in <module>
    asyncio.run(visualiser())
  File "C:\Program Files\Python311\Lib\asyncio\runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\trysetup.py", line 61, in visualiser
    await evo.liha.get_disposable_tip(tips= [op], z_start = 877.0, z_search = 5.0)
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\pylabrobot\liquid_handling\backends\tecan\EVO.py", line 1052, in get_disposable_tip
    await self.backend.send_command(module=self.module,
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\pylabrobot\liquid_handling\backends\tecan\EVO.py", line 138, in send_command    
    self.write(cmd, timeout=write_timeout)
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\pylabrobot\liquid_handling\backends\USBBackend.py", line 80, in write
    assert self.dev is not None and self.read_endpoint is not None, "Device not connected."
                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: Device not connected.

Throughout this, the LiHa was sitting beside the MCA96 with MCA96 parked at rail 1, could it be that the LiHa doesnā€™t know the existence of the MCA96? However, the tips to pick up is at rail 12, which is a little to the right from where the LiHa was sitting?

yes, change the 7545, 3100, 90, [2100] numbers. Preferably, we actually use the coordinates from the deck layout later but this is the way for now.

your code looks good at first glance.

Actually this is an issue with the USB connection. For some reason the read (tecan > computer) endpoint is not initialized correctly. Could you share the first part of the log? This should print out the endpoints found by libusb.

Hereā€™s the log for that run:

2024-04-02 13:58:21,179 - pylabrobot - INFO - Finding USB device...
2024-04-02 13:58:21,195 - pylabrobot - INFO - Found USB device.
2024-04-02 13:58:21,195 - pylabrobot - INFO - Found endpoints. 
Write:
       ENDPOINT 0x2: Bulk OUT ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x2 OUT
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x0 
Read:
       ENDPOINT 0x81: Bulk IN ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x81 IN
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x0
2024-04-02 14:00:21,200 - pylabrobot - INFO - Sent command: C5PIA
2024-04-02 14:00:26,681 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C5\x80\x00')
2024-04-02 14:00:26,681 - pylabrobot - INFO - Sent command: C5BMX2
2024-04-02 14:00:26,689 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C5\x80\x00')
2024-04-02 14:00:26,689 - pylabrobot - INFO - Sent command: C1PIA
2024-04-02 14:01:14,888 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C1\x80\x00')
2024-04-02 14:01:14,888 - pylabrobot - INFO - Sent command: C1BMX2
2024-04-02 14:01:14,896 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C1\x80\x00')
2024-04-02 14:01:14,896 - pylabrobot - INFO - Sent command: C1PIX
2024-04-02 14:01:16,320 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C1\x80\x00')
2024-04-02 14:01:16,320 - pylabrobot - INFO - Sent command: C1RPX0
2024-04-02 14:01:16,332 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C1\x803556\x00')
2024-04-02 14:01:16,332 - pylabrobot - INFO - Sent command: C1SAA1,9000,2000,2464,1800,,1,0,0
2024-04-02 14:01:16,336 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C1\x80\x00')
2024-04-02 14:01:16,336 - pylabrobot - INFO - Sent command: C1AAC
2024-04-02 14:01:17,734 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C1\x80\x00')
2024-04-02 14:01:17,735 - pylabrobot - INFO - Sent command: C1RPX0
2024-04-02 14:01:17,737 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C1\x808999\x00')
2024-04-02 14:01:17,743 - pylabrobot - INFO - Sent command: C5PIX
2024-04-02 14:01:18,947 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C5\x80\x00')
2024-04-02 14:01:18,947 - pylabrobot - INFO - Sent command: C5RNT1
2024-04-02 14:01:18,955 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C5\x808\x00')
2024-04-02 14:01:18,955 - pylabrobot - INFO - Sent command: C5RPX5
2024-04-02 14:01:18,955 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C5\x809849\x00')
2024-04-02 14:01:18,955 - pylabrobot - INFO - Sent command: C5RPY5
2024-04-02 14:01:18,963 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C5\x802824,2824\x00')
2024-04-02 14:01:18,963 - pylabrobot - INFO - Sent command: C5RPZ5
2024-04-02 14:01:18,971 - pylabrobot - DEBUG - Received data: bytearray(b'\x02C5\x802100,2100,2100,2100,2100,2100,2100,2100\x00')
1 Like

very curious, both endpoints are there, and many commands are being transmitted correctly before this error. Youā€™re not calling stop anywhere right?

Actually on second look, one mistake in your code is that get_disposable_tip( doesnā€™t take an Pickup object. Instead, it takes a ā€œBinary coded tip selectā€. See EVO._bin_use_channels for a utility function. Something like get_disposable_tip(<your evo instance>._bin_use_channels([1, 3]), 877.0, 5.0) would pick up using channels 1 and 3 (0-indexed!) Does that work?

I saw that Wilson already implemented the ā€˜surroundingā€™ methods in EVO.pick_up_tips, the EVO-backend implementation of LiquidHandlerBackend.pick_up_tips. This method will take a Pickup object and should send all commands necessary for a pickup. Itā€™s actually what LiquidHandler.pick_up_tips uses as well.

Sending individual firmware commands like youā€™re doing in this example is definitely good for debugging, but in the long term youā€™ll probably prefer making these higher level functions work.

Hm, nope I didnā€™t call for any stops.
When I use evo.pick_up_tips(ops = [op], use_channels= [0]) referencing from the backend EVO_tests.py (everything before is similar), I get an error that the location isnā€™t specified.

WARNING: total_tip_length <= 0.
Please get in touch at https://forums.pylabrobot.org/c/pylabrobot/23
Traceback (most recent call last):
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\trysetup.py", line 67, in <module>
    asyncio.run(visualiser())
  File "C:\Program Files\Python311\Lib\asyncio\runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\trysetup.py", line 62, in visualiser
    await evo.pick_up_tips(ops = [op], use_channels= [0])
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\pylabrobot\liquid_handling\backends\tecan\EVO.py", line 434, in pick_up_tips    
    x_positions, y_positions, _ = self._liha_positions(ops, use_channels)
                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\pylabrobot\liquid_handling\backends\tecan\EVO.py", line 601, in _liha_positions 
    location = ops[i].resource.get_absolute_location() + ops[i].resource.center()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'list' object has no attribute 'get_absolute_location'

I have yet to try _bin_use_channels, Iā€™ll do so when I can, thank you very much!

Pickup(
        resource= dttwo.get_items("A1:H1"),
        offset= 0,
        tip=("A1:H1")
    )

resource takes a single resource, whereas dttwo.get_items("A1:H1") returns a list.

Something like this is what you want:

pickups = [Pickup(
        resource= item,
        offset= 0,
        tip=("A1:H1")
    )
    for item in dttwo.get_items("A1:H1")]

But really Iā€™d advice to just use lh.pick_up_tips(dttwo["A1:H1"]) so we can take care of this automatically!

Managed to be able to run some code today, but still meeting with errors. When I used lh.pick_up_tips(dttwo["A1:H1"], this was the traceback I got:

Traceback (most recent call last):
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\trysetup.py", line 78, in <module>
    asyncio.run(visualiser())
  File "C:\Program Files\Python311\Lib\asyncio\runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\trysetup.py", line 76, in visualiser
    await lh.pick_up_tips(dttwo["A1:H1"])
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\pylabrobot\machine.py", line 23, in wrapper
    return await func(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\pylabrobot\liquid_handling\liquid_handler.py", line 399, in pick_up_tips        
    self._trigger_callback(
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\pylabrobot\liquid_handling\liquid_handler.py", line 1748, in _trigger_callback  
    raise error
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\pylabrobot\liquid_handling\liquid_handler.py", line 383, in pick_up_tips        
    await self.backend.pick_up_tips(ops=pickups, use_channels=use_channels, **backend_kwargs)
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\pylabrobot\liquid_handling\backends\tecan\EVO.py", line 431, in pick_up_tips    
    assert min(use_channels) >= self.num_channels - self.diti_count, \
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: DiTis can only be configured for the last 0 channels

And when I tried evo.liha.get_disposable_tip(evo._bin_use_channels([1,3]), 877.0, 5.0), this was the traceback I got:

Traceback (most recent call last):
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\trysetup.py", line 70, in <module>
    asyncio.run(visualiser())
  File "C:\Program Files\Python311\Lib\asyncio\runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\trysetup.py", line 63, in visualiser
    await evo.liha.get_disposable_tip(evo._bin_use_channels([1,3]), 877.0, 5.0)
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\pylabrobot\liquid_handling\backends\tecan\EVO.py", line 1052, in get_disposable_tip
    await self.backend.send_command(module=self.module,
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\pylabrobot\liquid_handling\backends\tecan\EVO.py", line 138, in send_command    
    self.write(cmd, timeout=write_timeout)
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\pylabrobot\liquid_handling\backends\USBBackend.py", line 80, in write
    assert self.dev is not None and self.read_endpoint is not None, "Device not connected."

Also, I was trying to troubleshoot by looking for the x,y,z parameters the LiHa seems to be stuck at (converted to be 132221456360160, not so sure what it means):

<bound method EVOArm.report_x_param of <pylabrobot.liquid_handling.backends.tecan.EVO.LiHa object at 0x000001F3A38A68D0>>
<bound method EVOArm.report_y_param of <pylabrobot.liquid_handling.backends.tecan.EVO.LiHa object at 0x000001F3A38A68D0>>
<bound method LiHa.report_z_param of <pylabrobot.liquid_handling.backends.tecan.EVO.LiHa object at 0x000001F3A38A68D0>>

And trying to park the LiHa, but it seems like there are parameters missing? Is this normal?

  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\trysetup.py", line 78, in <module>
    asyncio.run(visualiser())
  File "C:\Program Files\Python311\Lib\asyncio\runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\trysetup.py", line 74, in visualiser
    await evo._park_liha()
  File "c:\Users\Name\Downloads\pylabrobot-main\pylabrobot-main\pylabrobot\liquid_handling\backends\tecan\EVO.py", line 281, in _park_liha      
    await self.liha.set_z_travel_height([self._z_range] * self.num_channels)
                                         ^^^^^^^^^^^^^
AttributeError: 'EVO' object has no attribute '_z_range'

Initialize EVO with diti_count=8

it appears EVO.setup did not run correctly. why would _z_range not be set?