Opentrons / opentrons

Software for writing protocols and running them on the Opentrons Flex and Opentrons OT-2
https://opentrons.com
Apache License 2.0
423 stars 178 forks source link

feat: allow specifying port in robot url, so that user could use port other than 31950 #11820

Open arogozhnikov opened 1 year ago

arogozhnikov commented 1 year ago

Overview

solutions for tunneling like cloudflared and ngrok do not provide freedom to choose which port used to expose service:

tunnel.at.cloudflare.com:80 -> [opentrons]localhost:31950

Now, app needs only one port to be exposed, so I'm running an additional redirection to get it working:

[laptop]:localhost:31950 -> tunnel.at.cloudflare.com:80 

Now I can specify 127.0.0.1 as an address in opentrons app and it seems to work normally (robot is seen, I can operate with it).

Screen Shot 2022-12-03 at 7 27 49 PM

Side comment: Interestingly, it understands IP, but does not understand localhost.

I've tried to specify here both address and port (first line, partially covered name), but that does not work. Tried with http://, https:// and without protocol - no success. That would be more convenient and more reliable than an additional SSH forward.

Importance: 3/5.

Implementation details

No response

Design

No response

Acceptance criteria

No response

koji commented 1 year ago

Hi @arogozhnikov Thank you for posting this.

I have a question for you. The screenshot you posted said localhost Not Found.
Did this happen to you when you run a robot-server on your laptop/desktop (make -C robot-server dev)?

arogozhnikov commented 1 year ago

There is some hidden dependency in your libs that is not resolved by makefile:

$ make -C robot-server dev

Courtesy Notice: Pipenv found itself running within a virtual environment, so it will automatically use that environment, instead of creating its own for any project. You can set PIPENV_IGNORE_VIRTUALENVS=1 to force pipenv to ignore that environment and create its own instead. You can set PIPENV_VERBOSITY=-1 to suppress this warning.
Courtesy Notice: Pipenv found itself running within a virtual environment, so it will automatically use that environment, instead of creating its own for any project. You can set PIPENV_IGNORE_VIRTUALENVS=1 to force pipenv to ignore that environment and create its own instead. You can set PIPENV_VERBOSITY=-1 to suppress this warning.
Courtesy Notice: Pipenv found itself running within a virtual environment, so it will automatically use that environment, instead of creating its own for any project. You can set PIPENV_IGNORE_VIRTUALENVS=1 to force pipenv to ignore that environment and create its own instead. You can set PIPENV_VERBOSITY=-1 to suppress this warning.
Courtesy Notice: Pipenv found itself running within a virtual environment, so it will automatically use that environment, instead of creating its own for any project. You can set PIPENV_IGNORE_VIRTUALENVS=1 to force pipenv to ignore that environment and create its own instead. You can set PIPENV_VERBOSITY=-1 to suppress this warning.
python -m pipenv run uvicorn "robot_server:app" --host localhost --port 31950 --ws wsproto --lifespan on --reload
Courtesy Notice: Pipenv found itself running within a virtual environment, so it will automatically use that environment, instead of creating its own for any project. You can set PIPENV_IGNORE_VIRTUALENVS=1 to force pipenv to ignore that environment and create its own instead. You can set PIPENV_VERBOSITY=-1 to suppress this warning.
INFO:     Will watch for changes in these directories: ['/Users/axelr/projects/trash/opentrons/robot-server']
INFO:     Uvicorn running on http://localhost:31950 (Press CTRL+C to quit)
INFO:     Started reloader process [64272] using StatReload
Process SpawnProcess-1:
Traceback (most recent call last):
  File "/opt/homebrew/Cellar/python@3.10/3.10.8/Frameworks/Python.framework/Versions/3.10/lib/python3.10/multiprocessing/process.py", line 314, in _bootstrap
    self.run()
  File "/opt/homebrew/Cellar/python@3.10/3.10.8/Frameworks/Python.framework/Versions/3.10/lib/python3.10/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "/Users/axelr/pll/venv/lib/python3.10/site-packages/uvicorn/_subprocess.py", line 76, in subprocess_started
    target(sockets=sockets)
  File "/Users/axelr/pll/venv/lib/python3.10/site-packages/uvicorn/server.py", line 60, in run
    return asyncio.run(self.serve(sockets=sockets))
  File "/opt/homebrew/Cellar/python@3.10/3.10.8/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/opt/homebrew/Cellar/python@3.10/3.10.8/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
    return future.result()
  File "/Users/axelr/pll/venv/lib/python3.10/site-packages/uvicorn/server.py", line 67, in serve
    config.load()
  File "/Users/axelr/pll/venv/lib/python3.10/site-packages/uvicorn/config.py", line 474, in load
    self.loaded_app = import_from_string(self.app)
  File "/Users/axelr/pll/venv/lib/python3.10/site-packages/uvicorn/importer.py", line 24, in import_from_string
    raise exc from None
  File "/Users/axelr/pll/venv/lib/python3.10/site-packages/uvicorn/importer.py", line 21, in import_from_string
    module = importlib.import_module(module_str)
  File "/opt/homebrew/Cellar/python@3.10/3.10.8/Frameworks/Python.framework/Versions/3.10/lib/python3.10/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/Users/axelr/projects/trash/opentrons/robot-server/./robot_server/__init__.py", line 6, in <module>
    from .app_setup import app
  File "/Users/axelr/projects/trash/opentrons/robot-server/./robot_server/app_setup.py", line 10, in <module>
    from .hardware import initialize_hardware, cleanup_hardware
  File "/Users/axelr/projects/trash/opentrons/robot-server/./robot_server/hardware.py", line 16, in <module>
    from notify_server.clients import publisher
ModuleNotFoundError: No module named 'notify_server'
^CINFO:     Stopping reloader process [64272]

Maybe you have a docker with robot-server?

arogozhnikov commented 1 year ago

Also, I'm on mac if that simplifies the story, and here is a relevant section of /etc/hosts

##
127.0.0.1   localhost
255.255.255.255 broadcasthost
::1             localhost
# Added by Docker Desktop
# To allow the same kube context to work on the host and the container:
127.0.0.1 kubernetes.docker.internal
# End of section
koji commented 1 year ago

@arogozhnikov Could you answer the following questions?

Thank you!

arogozhnikov commented 1 year ago
koji commented 1 year ago

@arogozhnikov we will update python env (currently the pr is open) so I think once the PR is merged into edge, you will have no issue on running the robot-server. Otherwise, I recommend you to set up py3.7 env with venv.

arogozhnikov commented 1 year ago

may I remind that issue is about supporting specifying a dnsname+port in the app?

No difference for me if localhost is resolved or not, and I do not develop opentrons server.

mcous commented 1 year ago

Hi @arogozhnikov, thanks for the request! We'd be happy to accept a PR for this issue. This is the location where the list of address strings from that UI is turned into a host + port pair. A solution that accounts for the optional presence of a colon seems like it would do the trick, as long as it accounts for IPv6 addresses: https://github.com/Opentrons/opentrons/blob/7a2e56a19a5e2371a1b032fd1412331f0f172fae/app-shell/src/discovery.ts#L49-L53

arogozhnikov commented 1 year ago

Thanks for pointer @mcous

I've tried to meaningfully contribute, but I wasn't able to install dev environment on mac.

I have stable versions of nodejs and python, npm i even for app shell can't complete because of version conflicts. I blamed wrong nodejs version and tried installing deprecated version with nix-shell without success (nix basically tries to compile nodejs from scratch).

mcous commented 1 year ago

@arogozhnikov I no longer work at Opentrons, so I won't be able to be of much help, but I can tell you that much of the Opentrons development team works on macOS, so the development environment actually tends to work pretty well on mac.

The development setup guide has pretty detailed instructions for setting up a development environment on macOS. If you're still interested in contributing, you might have some luck following that guide

arogozhnikov commented 1 year ago

I've managed to set things up.

Changing function above to handle user-provided ports had only partial effect.

In this example I forwarded 127.0.0.1:3033 to ot2:31950. App does not reflect that it is reachable in 'Connect to a Robot via IP Address' menu (it is!):

Screenshot 2023-06-24 at 11 46 11 PM



Device at the same time appears in the list of available devices:


Screenshot 2023-06-24 at 11 43 21 PM

Interface does not reflect recent runs and pipettes, and behaves weird.

Screenshot 2023-06-24 at 11 49 02 PM

I am not sure I can help with fixing those, so I'd be glad if someone more familiar with codebase takes a look at this.