Formal Protocol for camera and stage interfaces See merge request openflexure/openflexure-microscope-server!192 |
||
|---|---|---|
| .gitlab/issue_templates | ||
| docs | ||
| scripts/zenodo | ||
| src/openflexure_microscope_server | ||
| tests | ||
| webapp | ||
| .gitattributes | ||
| .gitignore | ||
| .gitlab-ci.yml | ||
| .tarignore | ||
| changelog.config.js | ||
| CHANGELOG.md | ||
| CONTRIBUTING.md | ||
| LICENSE | ||
| mypy.ini | ||
| ofm_config_full.json | ||
| ofm_config_simulation.json | ||
| ofm_config_stub.json | ||
| Pipfile | ||
| Pipfile.lock | ||
| pyproject.toml | ||
| pytest.ini | ||
| README.md | ||
| readthedocs.yaml | ||
OpenFlexure Microscope Software
The "server" is the main component of the OpenFlexure Microscope's software. It is responsible for controlling microscope hardware, data management, and allowing it to be controlled locally and over a network.
This repository includes the graphical interface, which is implemented as a web application served from the root of the Python web server. The simplest way to use it is via OpenFlexure eV, which should find your microscope on the network, and display the interface. The microscope's interface can also be accessed at http://microscope.local:5000/ in a web browser, assuming the hostname of your microscope is microscope.
This software runs on LabThings-FastAPI, which creates an HTTP server using FastAPI (which in turn relies on Starlette and pydantic).
Getting started
A general user-guide on setting up your microscope can be found here on our website. This includes basic installation instructions suitable for most users. The simplest way to set up a microscope is to download the pre-built Raspberry Pi SD card image, which has this server already installed, along with all of its dependencies. There are instructions on how to use the microscope once you have installed the software, either from OpenFlexure Connect, or through a web browser. The web server starts on port 5000 by default, and the microscope SD image uses the hostname "microscope" so you can usually access the web interface at http://microscope.local:5000/.
A user guide and developer documentation for v2 of the server can be found on ReadTheDocs, including some installation notes, a link to the HTTP API reference, and guidance for developing extensions. More information is also available in the handbook, and in the "development instructions" below.
Running directly
The Python package provides a command ofm-microscope-server that runs the server. This is what is used by systemd to run the service. You will need to provide some command-line arguments, see the output of --help for an up to date list. See /etc/systemd/system/openflexure-microscope-server.service for the command line used to run the server by default on the Raspberry Pi. In general, you are likely to want to specify a configuration file with -c, a host (--host 0.0.0.0 to serve on all addresses) and a port (--port 5000). The --fallback option will allow the server to start even if the hardware specified in the configuration file can't load. This serves an error page, rather than have the server fail. In the future, it should redirect users to a way to fix their configuration.
Settings
The microscope is initially configured by a LabThings config file. This specifies two important things:
- The Python classes (and initialisation arguments) to use for each
Thing. This sets the type of camera and stage, and enables/disables additional functionality like scanning and autofocus. - The location of the settings folder, where each Thing can store its settings.
By default, this configuration file should be read from /var/openflexure/settings/ofm_config.json when the microscope is run as a service. If it is run at the command line you should specify the configuration using the -c command line flag. This configuration file does not change often, and usually only needs to be updated when you change the physical hardware. It may be that this file should be made read-only, particularly in microscopes deployed for e.g. medical applications.
The settings folder is, by default, /var/openflexure/settings/ on the SD card, or ./settings/ if run elsewhere. It can be changed in the configuration file. This holds every other persistent setting. Camera settings, calibration data, default capture settings, stream resolution and so on. There is one folder per Thing, each with their own file. The settings files can change very regularly, and if you need to reset your settings, it's usually these files that you should remove or reset. If you delete one settings file this should reset the corresponding Thing, you don't have to delete the whole folder.
Developer guidelines
Developing on a Raspberry Pi
The easiest way to work on the software is to build an OpenFlexure Microscope around a Raspberry Pi, using our custom disk image. This includes a pre-installed copy of this server, and a pre-built copy of the web application, so it's ready to use. You can also develop directly on the Raspberry Pi, and this is the best way to test out changes to the Python code using actual hardware.
You can manage the server with the ofm command, using ofm start, ofm stop, and ofm restart to do the respective actions. Often, stopping the server and running it manually will make errors easier to spot. To do this, run:
ofm stop
ofm activate
cd /var/openflexure
sudo -u openflexure-ws openflexure-microscope-server -c microscope_configuration.json --host 0.0.0.0 --port 5000
Our favourite way of working with the server on a Pi is to follow the instructions above, then open a VSCode Remote session from another computer. This allows you to use your usual developer environment to write code, but everything runs on the Raspberry Pi with real hardware. Note that the repository is cloned by default over https, so you will probably need to change the "remote" URL to your fork of the repository, or to SSH, before you're able to push changes.
Updating just the web app
Installing and running node.js on a Raspberry Pi is slow and often frustrating. Our preference is to do node.js development on another computer, and connect either to a dummy server on that computer, or to a real microscope elsewhere on the network. If you only want to work on the Python code, it's possible to download a pre-built web application and use it with your modified Python code. To do this, locate the archive of the web application, and then run:
cd /var/openflexure/
sudo chown -R openflexure-ws.openflexure-ws .
sudo chmod -R g+w .
cd application/openflexure-microscope-server/
sudo rm -rf openflexure_microscope/api/static/dist/
curl <tarball URL> | tar -xz
NB the sudo chown and sudo chmod lines are probably unnecessary, but depending on the state of your system they may fix annoying permissions issues.
To find the <tarball URL> you should look for openflexure-microscope-webapp-<version>.tar.gz on the build server, or open the package job of a CI pipeline on this repository, and locate it by browsing the artifacts. That should work for any merge request that's currently open.
Installation on other platforms
The Raspberry Pi image we use currently ships with Python 3.7.3. For local development on a different platform, please use PyEnv or similar to make sure you're running on this version. For example, Windows users can use Scoop to install specific Python versions. This repository contains two closely related parts; a web server written in Python, that handles hardware control, and a web application using Vue.js that provides a graphical control interface. It is possible to work on either of these in isolation, but bear in mind that if you only set up Python development, you will need to host the web application elsewhere.
Most of our core team don't run the Javascript development on the Raspberry Pi, as npm can be quite slow to build the application. Instead, we set up the Python part of the project on a Raspberry Pi, and run the Javascript part on your development machine. You can then connect to the web application served from your local machine, and enter the address of the Raspberry Pi when the interface first loads.
To set up a development version of the software (most likely using emulated camera and stage if you're not running on a Raspberry Pi):
Clone the repository
git clone https://gitlab.com/openflexure/openflexure-microscope-server.gitcd openflexure-microscope-server
Set up the Python environment and run a test server
- (Optional) Set local Python version to 3.11
- Create a virtual environment and activate it:
python -m venv .venvsource .venv/bin/activate(on Linux) or.venv/Scripts/activate(on Windows)pip install -e .[dev](This will install development dependencies. If you don't need these, there is probably a simpler way to run the server than cloning this repo.)
- Finally, run the server: currently you can do this with
sudo systemctl start openflexure-microscope-serverif it's pre-installed on a Raspberry Pi, oropenflexure-microscope-server -c ofm_config_stub.jsonto run locally.
Run the server manually on a Raspberry Pi
cd /var/openflexure
sudo -u openflexure-ws PATH="/var/openflexure/application/openflexure-microscope-server/.venv/bin/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin" /var/openflexure/application/openflexure-microscope-server/.venv/bin/uvicorn --reload --reload-dir=./application --host 0.0.0.0 --port 5000 openflexure_microscope_server.server:app
Set up the Javascript environment and build
- The Flask web application, written in Python, serves a web application written in
Vue.js. This is distributed as part of the built version of the server, hosted on our build server. - You could extract the pre-built web app from this tarball, which saves you having to set up Node.js. However, it also means you're not able to change the interface, and it's possible your interface will get out of sync with your server.
- Building the web interface will require a valid Node.js installation. If you don't have Node.js (including
npm) the Node.js website offers downloads for all platforms, though see the instructions below for Raspberry Pi.- To install Node.js on a Raspberry Pi:
curl -sL https://deb.nodesource.com/setup_14.x | sudo bash -sudo apt install nodejs
- To install Node.js on a Raspberry Pi:
- To build the web application (this produces a set of static files, that are served by the Flask webserver)
cd webappnpm installnpm run build
- To create a Node.js development server (this will help various development tools to display more information, and auto-rebuilds when you change the source files)
npm run serve- You access the development server on a different port (it's printed on the command line when you run the above command). This means that when it starts up you will need to tell it where the microscope server is, using the "override API origin" field in the page that pops up. If you are running a test server on your computer, this is most likely
http://localhost:5000/.
Formatting, linting, and tests
All of the commands below assume that you are running in the OFM virtual environment, i.e. you have run ofm activate on an OpenFlexure SD card, or source .venv/bin/activate on Linux, or .venv/Scripts/activateon Windows.
Before committing you should auto-format your code:
- To auto-format the Python code run
poe format - To auto-format the Javascript code, run
cd webappnpm run lint
Before submitting a merge request/merging please auto-format your code and also run the quality checks (linting, static analysis, and unit tests)
- To auto-format and type-check the Python code run
poe check - To auto-format the Javascript code, run
cd webappnpm run lint
Details
We use several code analysis and formatting libraries in this project. Please run all of these before submitting a merge request.
Our CI will check each of these automatically, so ensuring they pass locally will save you time.
- Black - Code formatting with minimal configuration.
- While sometimes it's not perfect, its fine 90% of the time and prevents arguments about formatting.
- Automatically formats your code
- This will rewrite your files in-place, so if you want to be able to revert, make a backup first!
poe black
- Pylint - Static code analysis
- Analyses your code, failing if issues are detected.
- We've disabled some less severe warnings, so if anything fails your merge request will be blocked
poe pylint
- Mypy - Type checking
- Analyses your type hints and annotations to flag up potential bugs
- Where possible, use type hints in your code. Even if dependencies don't support it, it'll help identify issues.
poe mypy
- Pytest - Unit testing
- While unit testing is of limited use due to our dependence on real hardware, some simple isolated functions can (and should) be unit tested.
poe test
Though not in the CI, our format script also runs isort:
- Isort - Import sorting
- Automatically organises your imports to stop things getting out of hand
poe isort
Python environment, build, and dependencies
As of 2.10.0b0 we have switched to using pipenv for managing dependencies, and a standard setuptools based build system. See "local installation" above for instructions on how to install the project. Earlier versions of the project used poetry but we moved away because of difficulties getting it to work both on the Raspberry Pi and in our CI pipeline. The new arrangement for configuration files is:
- Dependencies, and dev-dependencies, are specified in
setup.pyin the usual way (usinginstall_requiresandextra_requires[dev]). - Package metadata is specified in
setup.pyin the usual way. pyproject.tomlis retained, but no longer includes package metadata or dependencies and does not have a[tool.poetry]table. It does define the build system as per PEP517, which issetuptools, and it also contains settings forblack,poeandisort.Pipfileis very minimal, and only declares dependencies on the current module, i.e. the dependencies declared insetup.py. It also specifies the Python version. This allows us to single-source dependency information fromsetup.pybut use the dependency resolution/locking functionality ofpipenvPipfile.locklocks the dependency versions in the same way aspoetry.lockused to, i.e. it specifies exact versions of everything, derived from the looser specifications insetup.py.- Dependency resolution is a pain, in part because compiling packages like scipy and numpy from source on a Pi is slow and unreliable. To avoid this, we usually lock the dependencies on a Raspberry Pi, using:
PIP_ONLY_BINARY="numpy, scipy, matplotlib" pipenv lock --devThis forces the use of wheels only, and thus restricts us to things that built OK on Piwheels. The resulting Pipfile.lock should then work nicely on other Raspberry Pis. - The Pipfile we use is now Raspberry Pi-specific so if you attempt to use it on other platforms it will probably fail. This is because we've removed
pypito avoid hash conflicts. The CI now runs on a Raspberry Pi image, so that the tests use a similar environment to our deployment. Currently, changing the[source]entry back to PyPi will allow you to re-lock the dependencies on other platforms. In the future we may need to maintain two Pipfiles and two Pipfile.lock files.
Creating releases
- Update the application's internal version number
- Edit
setup.pyto update the version number - Git commit and git push
- Edit
- Create a new version tag on GitLab (e.g.
v2.6.11)- Make sure you prefix a lower case 'v', otherwise it won't be recognised as a release!
- This tagging will trigger a CI pipeline that builds the JS client, tarballs up the server, and deploys it
- Note: This also updates the build server's nginx redirect map file
Changelog generation
npm install -g conventional-changelog-clinpx conventional-changelog -r 1 --config ./changelog.config.js -i CHANGELOG.md -s
Microscope extensions
The Microscope module, and Flask app, both support plugins for extending lower-level functionality not well suited to web API calls. The current documentation can be found here. If you want to add functions to the microscope software, this is probably the best mechanism to use if it works for you.