Merge remote-tracking branch 'origin/master' into auto-exposure-gain-awb
This was primarily done to disable the warning about f strings in logging statements, globally rather than per-file.
This commit is contained in:
commit
42f00b69a4
3 changed files with 61 additions and 35 deletions
|
|
@ -1,50 +1,47 @@
|
||||||
import logging
|
import logging
|
||||||
from typing import List, Tuple
|
|
||||||
|
|
||||||
from labthings import fields, find_component
|
from labthings import fields, find_component
|
||||||
from labthings.views import ActionView
|
from labthings.views import ActionView
|
||||||
|
|
||||||
from openflexure_microscope.utilities import axes_to_array
|
|
||||||
|
|
||||||
|
|
||||||
class MoveStageAPI(ActionView):
|
class MoveStageAPI(ActionView):
|
||||||
args = {
|
args = {
|
||||||
"absolute": fields.Boolean(
|
"absolute": fields.Boolean(
|
||||||
missing=False, example=False, description="Move to an absolute position"
|
missing=False, example=False, description="Move to an absolute position"
|
||||||
),
|
),
|
||||||
"x": fields.Int(missing=0, example=100),
|
"x": fields.Int(missing=None, example=100),
|
||||||
"y": fields.Int(missing=0, example=100),
|
"y": fields.Int(missing=None, example=100),
|
||||||
"z": fields.Int(missing=0, example=20),
|
"z": fields.Int(missing=None, example=20),
|
||||||
}
|
}
|
||||||
|
|
||||||
def post(self, args):
|
def post(self, args):
|
||||||
"""
|
"""
|
||||||
Move the microscope stage in x, y, z
|
Move the microscope stage in x, y, z
|
||||||
|
|
||||||
|
Any axes that are not specifed will not move.
|
||||||
"""
|
"""
|
||||||
microscope = find_component("org.openflexure.microscope")
|
microscope = find_component("org.openflexure.microscope")
|
||||||
|
|
||||||
# Handle absolute positioning (calculate a relative move from current position and target)
|
if not microscope.stage:
|
||||||
if (args.get("absolute")) and (microscope.stage): # Only if stage exists
|
|
||||||
target_position: List[int] = axes_to_array(args, ["x", "y", "z"])
|
|
||||||
logging.debug("TARGET: %s", (target_position))
|
|
||||||
position: Tuple[int, int, int] = (
|
|
||||||
target_position[i] - microscope.stage.position[i] for i in range(3)
|
|
||||||
)
|
|
||||||
logging.debug("DELTA: %s", (position))
|
|
||||||
|
|
||||||
else:
|
|
||||||
# Get coordinates from payload
|
|
||||||
position = axes_to_array(args, ["x", "y", "z"], [0, 0, 0])
|
|
||||||
|
|
||||||
logging.debug(position)
|
|
||||||
|
|
||||||
# Move if stage exists
|
|
||||||
if microscope.stage:
|
|
||||||
# Explicitally acquire lock with 1s timeout
|
|
||||||
with microscope.stage.lock(timeout=1):
|
|
||||||
microscope.stage.move_rel(position)
|
|
||||||
else:
|
|
||||||
logging.warning("Unable to move. No stage found.")
|
logging.warning("Unable to move. No stage found.")
|
||||||
|
return microscope.state["stage"]["position"]
|
||||||
|
|
||||||
|
absolute_move = args.get("absolute")
|
||||||
|
move = [0, 0, 0] # Default to no motion
|
||||||
|
for i, axis in enumerate(["x", "y", "z"]):
|
||||||
|
if axis in args and args[axis] is not None:
|
||||||
|
if absolute_move:
|
||||||
|
# We emulate absolute moves by calculating a relative move that
|
||||||
|
# will take us to the right position.
|
||||||
|
move[i] = args[axis] - microscope.stage.position[i]
|
||||||
|
else:
|
||||||
|
move[i] = args[axis]
|
||||||
|
|
||||||
|
logging.debug(f"Moving stage by {move}, request was {args}")
|
||||||
|
|
||||||
|
# Explicitly acquire lock with 1s timeout
|
||||||
|
with microscope.stage.lock(timeout=1):
|
||||||
|
microscope.stage.move_rel(move)
|
||||||
|
|
||||||
return microscope.state["stage"]["position"]
|
return microscope.state["stage"]["position"]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import logging
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from typing import Dict, List, Optional, Tuple, Type, Union
|
from typing import Dict, List, Optional, Sequence, Tuple, Type, Union
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
@ -102,12 +102,29 @@ def set_properties(obj, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
def axes_to_array(
|
def axes_to_array(
|
||||||
coordinate_dictionary: Dict[str, int],
|
coordinate_dictionary: Dict[str, Optional[int]],
|
||||||
axis_keys=("x", "y", "z"),
|
axis_keys: Sequence[str] = ("x", "y", "z"),
|
||||||
base_array: Optional[List[int]] = None,
|
base_array: Optional[List[int]] = None,
|
||||||
asint: bool = True,
|
asint: bool = True,
|
||||||
) -> List[int]:
|
) -> List[int]:
|
||||||
"""Takes key-value pairs of a JSON value, and maps onto an array"""
|
"""Takes key-value pairs of a JSON value, and maps onto an array
|
||||||
|
|
||||||
|
This is designed to take a dictionary like `{"x": 1, "y":2, "z":3}`
|
||||||
|
and return a list like `[1, 2, 3]` to convert between the argument
|
||||||
|
format expected by most of our stages, and the usual argument
|
||||||
|
format in JSON.
|
||||||
|
|
||||||
|
`axis_keys` is an ordered sequence of key names to extract from
|
||||||
|
the input dictionary.
|
||||||
|
|
||||||
|
`base_array` specifies a default value for each axis. It must
|
||||||
|
have the same length as `axis_keys`.
|
||||||
|
|
||||||
|
`asint` casts values to integers if it is `True` (default).
|
||||||
|
|
||||||
|
Missing keys, or keys that have a `None` value will be left
|
||||||
|
at the specified default value, or zero if none is specified.
|
||||||
|
"""
|
||||||
# If no base array is given
|
# If no base array is given
|
||||||
if not base_array:
|
if not base_array:
|
||||||
# Create an array of zeros
|
# Create an array of zeros
|
||||||
|
|
@ -119,8 +136,14 @@ def axes_to_array(
|
||||||
# Do the mapping
|
# Do the mapping
|
||||||
for axis, key in enumerate(axis_keys):
|
for axis, key in enumerate(axis_keys):
|
||||||
if key in coordinate_dictionary:
|
if key in coordinate_dictionary:
|
||||||
base_array[axis] = (
|
value = coordinate_dictionary[key]
|
||||||
int(coordinate_dictionary[key]) if asint else coordinate_dictionary[key]
|
if value is None:
|
||||||
)
|
# Values set to None should be treated as if they
|
||||||
|
# are missing
|
||||||
|
# i.e. we leave the default value in place.
|
||||||
|
break
|
||||||
|
if asint:
|
||||||
|
value = int(value)
|
||||||
|
base_array[axis] = value
|
||||||
|
|
||||||
return base_array
|
return base_array
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,13 @@ ensure_newline_before_comments = true
|
||||||
line_length = 88
|
line_length = 88
|
||||||
|
|
||||||
[tool.pylint.'MESSAGES CONTROL']
|
[tool.pylint.'MESSAGES CONTROL']
|
||||||
disable = "fixme,C,R"
|
# W1203 warns about using f strings in logging statements.
|
||||||
|
# I'm really not concerned about the performance implications of this,
|
||||||
|
# particularly as we often have the log level set quite high. I think
|
||||||
|
# for our current purposes, using f strings in logging statements is
|
||||||
|
# more readable and thus a good idea in many places - I've disabled
|
||||||
|
# this warning code globally for that reason.
|
||||||
|
disable = "fixme,C,R,W1203"
|
||||||
max-line-length = 88
|
max-line-length = 88
|
||||||
|
|
||||||
[tool.poe.executor]
|
[tool.poe.executor]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue