Skip to content

Commit

Permalink
Merge branch 'main' into proscan
Browse files Browse the repository at this point in the history
  • Loading branch information
rwb27 committed Dec 20, 2024
2 parents f3022d4 + 06bc8b8 commit a4d8859
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 9 deletions.
77 changes: 77 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: Publish to PyPI

on: push

#on:
# release:
# types: [published]

jobs:
build:
name: Build distribution 📦
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Install pypa/build
run: >-
python3 -m
pip install
build
--user
- name: Build a binary wheel and a source tarball
run: python3 -m build
- name: Store the distribution packages
uses: actions/upload-artifact@v4
with:
name: python-package-distributions
path: dist/

publish-to-testpypi:
name: Publish Python 🐍 distribution 📦 to TestPyPI
needs:
- build
runs-on: ubuntu-latest

environment:
name: testpypi
url: https://test.pypi.org/p/labthings-picamera2

permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing

steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Publish distribution 📦 to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/

publish-to-pypi:
name: >-
Publish Python 🐍 distribution 📦 to PyPI
if: startsWith(github.ref, 'refs/tags/v') # only publish to PyPI on tag pushes
needs:
- build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/labthings-picamera2
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Publish distribution 📦 to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "labthings-sangaboard"
version = "0.0.0"
version = "0.0.1"
authors = [
{ name="Richard Bowman", email="[email protected]" },
]
Expand Down
77 changes: 69 additions & 8 deletions src/labthings_sangaboard/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
from labthings_fastapi.descriptors.property import PropertyDescriptor
from labthings_fastapi.thing import Thing
from labthings_fastapi.decorators import thing_action, thing_property
from typing import Iterator
from labthings_fastapi.dependencies.invocation import CancelHook, InvocationCancelledError
from typing import Iterator, Literal
from contextlib import contextmanager
from collections.abc import Sequence, Mapping
import sangaboard
import threading
import time
import numpy as np

class SangaboardThing(Thing):
_axis_names = ("x", "y", "z") # TODO: handle 4th axis gracefully
Expand All @@ -26,6 +29,10 @@ def __init__(self, port: str=None, **kwargs):
def __enter__(self):
self._sangaboard = sangaboard.Sangaboard(**self.sangaboard_kwargs)
self._sangaboard_lock = threading.RLock()
with self.sangaboard() as sb:
if sb.version_tuple[0] != 1:
raise RuntimeError("labthings-sangaboard requires firmware v1")
sb.query("blocking_moves false")
self.update_position()

def __exit__(self, _exc_type, _exc_value, _traceback):
Expand Down Expand Up @@ -77,16 +84,30 @@ def thing_state(self):
}

@thing_action
def move_relative(self, **kwargs: Mapping[str, int]):
def move_relative(self, cancel: CancelHook, block_cancellation: bool=False, **kwargs: Mapping[str, int]):
"""Make a relative move. Keyword arguments should be axis names."""
displacement = [kwargs.get(k, 0) for k in self.axis_names]
with self.sangaboard() as sb:
self.moving = True
sb.move_rel([kwargs.get(k, 0) for k in self.axis_names])
self.moving=False
self.update_position()
try:
sb.move_rel(displacement)
if block_cancellation:
sb.query("notify_on_stop")
else:
while sb.query("moving?") == "true":
cancel.sleep(0.1)
except InvocationCancelledError as e:
# If the move has been cancelled, stop it but don't handle the exception.
# We need the exception to propagate in order to stop any calling tasks,
# and to mark the invocation as "cancelled" rather than stopped.
sb.query("stop")
raise e
finally:
self.moving=False
self.update_position()

@thing_action
def move_absolute(self, **kwargs: Mapping[str, int]):
def move_absolute(self, cancel: CancelHook, block_cancellation: bool=False, **kwargs: Mapping[str, int]):
"""Make an absolute move. Keyword arguments should be axis names."""
with self.sangaboard():
self.update_position()
Expand All @@ -95,7 +116,7 @@ def move_absolute(self, **kwargs: Mapping[str, int]):
for k, v in kwargs.items()
if k in self.axis_names
}
self.move_relative(**displacement)
self.move_relative(cancel, block_cancellation=block_cancellation, **displacement)

@thing_action
def abort_move(self):
Expand All @@ -108,4 +129,44 @@ def abort_move(self):
tc = self._sangaboard.termination_character
self._sangaboard._ser.write(("stop" + tc).encode())
else:
raise HTTPException(status_code=409, detail="Stage is not moving.")
raise HTTPException(status_code=409, detail="Stage is not moving.")

@thing_action
def set_zero_position(self):
"""Make the current position zero in all axes
This action does not move the stage, but resets the position to zero.
It is intended for use after manually or automatically recentring the
stage.
"""
with self.sangaboard() as sb:
sb.zero_position()
self.update_position()

@thing_action
def flash_led(
self,
number_of_flashes: int = 10,
dt: float = 0.5,
led_channel: Literal["cc"]="cc",
) -> None:
"""Flash the LED to identify the board
This is intended to be useful in situations where there are multiple
Sangaboards in use, and it is necessary to identify which one is
being addressed.
"""
with self.sangaboard() as sb:
r = sb.query("led_cc?")
if not r.startswith('CC LED:'):
raise IOError("The sangaboard does not support LED control")
# This suffers from repeated reads and writes decreasing it, so for
# now, I'll fix it at the default value.
# TODO: proper LED control from python
#on_brightness = float(r[7:])
on_brightness = 0.32
for i in range(number_of_flashes):
sb.query("led_cc 0")
time.sleep(dt)
sb.query(f"led_cc {on_brightness}")
time.sleep(dt)

0 comments on commit a4d8859

Please sign in to comment.