Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion pylabrobot/resources/plate.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def __init__(
lid: Optional[Lid] = None,
model: Optional[str] = None,
plate_type: Literal["skirted", "semi-skirted", "non-skirted"] = "skirted",
stacking_z_height: Optional[float] = None,
):
"""Initialize a Plate resource.

Expand All @@ -89,6 +90,10 @@ def __init__(
lid: Immediately assign a lid to the plate.
plate_type: Type of the plate. One of "skirted", "semi-skirted", or "non-skirted". A
more complete description is still pending.
stacking_z_height: The vertical pitch in mm between two identical plates stacked directly on
top of each other (i.e. the height a plate adds to a stack, equal to ``size_z`` minus the
overlap with the plate below). Required by some stacker devices (e.g. the Agilent BenchCel)
and left as ``None`` when unknown.
"""

super().__init__(
Expand All @@ -103,6 +108,7 @@ def __init__(
)
self._lid: Optional[Lid] = None
self.plate_type = plate_type
self.stacking_z_height = stacking_z_height

if lid is not None:
self.assign_child_resource(lid)
Expand Down Expand Up @@ -144,10 +150,31 @@ def unassign_child_resource(self, resource):
self._lid = None
return super().unassign_child_resource(resource)

def serialize(self) -> dict:
return {
**super().serialize(),
"plate_type": self.plate_type,
"stacking_z_height": self.stacking_z_height,
}

def __eq__(self, other) -> bool:
# `stacking_z_height` is a physical dimension (the pitch a plate adds to a stack), so plates
# that differ in it are different labware and must not compare equal.
return (
super().__eq__(other)
and isinstance(other, Plate)
and self.stacking_z_height == other.stacking_z_height
)

# Defining `__eq__` sets `__hash__` to None; restore the `Resource` (repr-based) hash. Equal
# plates share the same repr, so the hash/eq invariant holds.
__hash__ = Resource.__hash__

def __repr__(self) -> str:
return (
f"{self.__class__.__name__}(name={self.name!r}, size_x={self._size_x}, "
f"size_y={self._size_y}, size_z={self._size_z}, location={self.location})"
f"size_y={self._size_y}, size_z={self._size_z}, "
f"stacking_z_height={self.stacking_z_height}, location={self.location})"
)

def get_well(self, identifier: Union[str, int, Tuple[int, int]]) -> "Well":
Expand Down
51 changes: 51 additions & 0 deletions pylabrobot/resources/plate_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,57 @@ def test_add_lid_with_existing_lid(self):
self.assertIsNone(plate.lid)


class TestStackingZHeight(unittest.TestCase):
def test_default_is_none(self):
plate = Plate("plate", size_x=1, size_y=1, size_z=15, ordered_items={})
self.assertIsNone(plate.stacking_z_height)

def test_stored(self):
plate = Plate("plate", size_x=1, size_y=1, size_z=15, ordered_items={}, stacking_z_height=12.5)
self.assertEqual(plate.stacking_z_height, 12.5)

def test_serialize_round_trip(self):
plate = Plate(
"plate",
size_x=1,
size_y=1,
size_z=15,
ordered_items={},
stacking_z_height=12.5,
plate_type="non-skirted",
)
serialized = plate.serialize()
self.assertEqual(serialized["stacking_z_height"], 12.5)
self.assertEqual(serialized["plate_type"], "non-skirted")

restored = Plate.deserialize(serialized)
self.assertEqual(restored.stacking_z_height, 12.5)
self.assertEqual(restored.plate_type, "non-skirted")

def test_differs_in_equality(self):
# `stacking_z_height` is a physical dimension, so plates that differ in it are not equal.
def make(stacking_z_height):
return Plate(
"plate",
size_x=1,
size_y=1,
size_z=15,
ordered_items={},
stacking_z_height=stacking_z_height,
)

self.assertEqual(make(12.5), make(12.5))
self.assertNotEqual(make(12.5), make(13.0))
self.assertNotEqual(make(12.5), make(None))

def test_remains_hashable(self):
plate = Plate("plate", size_x=1, size_y=1, size_z=15, ordered_items={}, stacking_z_height=12.5)
# equal plates hash equally; the object stays usable in sets/dicts.
other = Plate("plate", size_x=1, size_y=1, size_z=15, ordered_items={}, stacking_z_height=12.5)
self.assertEqual(hash(plate), hash(other))
self.assertIn(plate, {plate})


class TestQuadrants(unittest.TestCase):
def setUp(self):
self.example_6_wellplate = Cor_Cos_6_wellplate_16800ul_Fb(name="example_6_wellplate")
Expand Down
Loading