No websocket response when running lh.pick_up_tips()

I’m running through the tutorial notebooks and everything is mostly working, but when I get to the step in the simulator notebook

await lh.pick_up_tips(tip_0["A1", "B2", "C3", "D4"])

the cell will run forever and not return anything.

I’ve traced it down to the websocket send_command() function, specifically the section

if wait_for_response:
        while True:
          if len(self.received) > 0:
            message = self.received.pop()
            if "id" in message and message["id"] == id_:
              break
          time.sleep(0.1)

What seems to be happening is that self.received is filling up with lots of dictionary messages (either {‘event’: ‘ping’} or {‘event’: ‘pong’, ‘success’:True} but since none of those have a ‘id’ key, we get stuck in that loop forever.

Is this intended? Any ideas about what I’m doing wrong? Thanks :slight_smile:

Definitely not supposed to happen.

Could you check the JS console of your browser for errors? Sometimes, when a fatal error occurs, the client library is unable to send a response to the server, causing the server to wait indefinitely. (Actually I think I’ll just add a timeout parameter.)

Yes! I do see the ‘pick_up_tips’ message on this side, which I don’t remember seeing logged in the notebook:

[message] Data received from server: 
Object { event: "pick_up_tips", id: "0011", version: "0.1.0", channels: (4) […], use_channels: (4) […] }
sim.js:563:13
[event] pick_up_tips 
Object { event: "pick_up_tips", id: "0011", version: "0.1.0", channels: (4) […], use_channels: (4) […] }
sim.js:434:11

And there’s this error:

Uncaught (in promise) Error: Too many channels (4)
    pickUpTips http://127.0.0.1:1337/sim.js:166
    handleEvent http://127.0.0.1:1337/sim.js:462
    openSocket http://127.0.0.1:1337/sim.js:564
sim.js:166:11
    handleEvent http://127.0.0.1:1337/sim.js:524
    InterpretGeneratorResume self-hosted:1469
    AsyncFunctionNext self-hosted:852
    (Async: async)
    openSocket http://127.0.0.1:1337/sim.js:564

Client errors are not automatically sent to the server (notebook).

In this case, the error occurs because the Opentrons simulator is set up to work with only one channel (because that’s the Opentrons we had available for testing). See: https://github.com/PyLabRobot/pylabrobot/blob/main/pylabrobot/liquid_handling/backends/simulation/simulator/sim.js#L446

Since Opentrons have the option of mounting different pipetting heads with a different number of channels, there should really be an option to configure that in the simulator (as I wrote in the comment in the linked code), which does not exist yet. If you have ideas on how to best implement this, I’m happy to hear them!

More immediate solutions:

  • You could change the code to load more channels for the OT, analogous to STAR. Reload the page and you should be good.

  • Depending on your setup, you may also want to perform a single operation at a time (await lh.pick_up_tips(tip_0["A1"]))

Hmmm, I’m just running the basic ‘using the simulator’ doc with minimal changes, (https://github.com/PyLabRobot/pylabrobot/blob/main/docs/using-the-simulator.ipynb) which is set up to use a STARletDeck I think.

switch (event) {
    case "resource_assigned":
      resource = loadResource(data.resource);
      resource.draw(resourceLayer);
      print('Is this thing on? prints from backends/simulation/simulator/sim.js')
      if (data.resource.name === "deck") {
        // infer the system from the deck.
        if (data.resource.type === "OTDeck") {
          system = SYSTEM_OPENTRONS;
          // Just one channel for Opentrons right now. Should create a UI to select the config.
          for (let i = 0; i < 8; i++) {
            mainHead.push(new PipettingChannel(`Channel: ${i + 1}`));
        } else if (data.resource.type === "HamiltonDeck") {
          system = SYSTEM_HAMILTON;
          for (let i = 0; i < 8; i++) {
            mainHead.push(new PipettingChannel(`Channel: ${i + 1}`));
          }
        }
      }
      break;

I adjusted the Opentrons line to match the same strategy for loading multiple channels as with Hamilton decks (is this what you meant?) but it doesn’t seem to have changed anything. In fact, the print statement I put in doesn’t even seem to be triggering, so I’m not sure this code in sim.js is getting run at any point when I run through using-the-simulator.ipynb? (when i change other files it changes the result as expected)

Thanks for the help!!

The simulator actually determines the liquid handler that is running depending on the deck. That means if you change the STARLet deck to OT, the simulator will run with a slightly different configuration.

print in Python would actually be console.log in JS. Further, I think you’re missing a closing bracket for the for loop. Given that that did not give a syntax error, could you force reload the page? (Sometimes the browser will cache JS files.)

Thanks for the help! I think I found something that might have been causing the issue. I looked into the JS event that’s triggering for resource_assigned and for me the resource object named ‘deck’ has type ‘HamiltonSTARDeck’, where handleEvent is only looking for either ‘OTDeck’ or ‘HamiltonDeck’. I edited the javascript to look for a ‘HamiltonSTARDeck’ and everything seems to be working for me now. Appreciate the guidance!

2 Likes