Skip to content

Commit bffacd8

Browse files
authored
Persisted containers xy are center (#143)
* container files' XY reference center of Well, not lower-left corner * updated tests * added 'type' to calibration delta * checking calibration data version and overwriting with blank file if version doesn't match * fixes saving and leading new calibration json * fixes tests * overwrites file instead of appending to; checks for 'data' key in calibrations file * removes print statement * adds tests to vector
1 parent e3a98a5 commit bffacd8

File tree

10 files changed

+83
-19
lines changed

10 files changed

+83
-19
lines changed

opentrons/containers/calibrator.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ def calibrate(self,
6767
current = children[name]
6868

6969
current['delta'] = delta
70+
current['type'] = placeable.get_type()
7071
self.calibration_data = calibration_data
7172

7273
self._apply_calibration(calibration_data, self.root_placeable)

opentrons/containers/persisted_containers.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,12 @@ def create_container_obj_from_dict(container_data: dict) -> Container:
184184

185185
well = Well(properties=well_properties)
186186

187+
# subtract half the size, because
188+
# Placeable assigns X-Y to bottom-left corner, but
189+
# persisted container files assign X-Y to center of each Well
190+
x -= (well.x_size() / 2)
191+
y -= (well.y_size() / 2)
192+
187193
well_coordinates = (
188194
x + origin_offset_x,
189195
y + origin_offset_y,

opentrons/containers/placeable.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,12 @@ def get_name(self):
135135

136136
return self.parent.children_by_reference[self]
137137

138+
def get_type(self):
139+
"""
140+
Returns the Placeable's type or class name
141+
"""
142+
return self.properties.get('type', self.__class__.__name__)
143+
138144
def get_children_list(self):
139145
"""
140146
Returns the list of children in the order they were added

opentrons/instruments/instrument.py

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ class Instrument(object):
2828
It gives the instruments ability to CRUD their calibration data,
2929
and gives access to some common methods across instruments
3030
"""
31+
32+
calibration_data_version = 1
33+
3134
calibration_key = "unique_name"
3235
persisted_attributes = []
3336
persisted_defaults = {}
@@ -121,18 +124,18 @@ def init_calibrations(self, key, attributes=None):
121124
if not os.path.isdir(self._get_calibration_dir()):
122125
os.mkdir(self._get_calibration_dir())
123126

124-
file_path = self._get_calibration_file_path()
125-
if not os.path.isfile(file_path):
126-
with open(file_path, 'a') as f:
127-
f.write(json.dumps({}))
127+
if not os.path.isfile(self._get_calibration_file_path()):
128+
self._write_blank_calibrations_file()
129+
else:
130+
self._check_calibrations_version()
128131

129132
def update_calibrations(self):
130133
"""
131134
Saves the instrument's peristed attributes to file
132135
"""
133136
last_persisted_data = self._read_calibrations()
134137

135-
last_persisted_data[self.calibration_key] = (
138+
last_persisted_data['data'][self.calibration_key] = (
136139
self._strip_vector(
137140
self._build_calibration_data())
138141
)
@@ -161,14 +164,22 @@ def delete_calibration_data(self):
161164
setattr(self, key, val)
162165
self.update_calibrations()
163166

164-
def delete_calibration_file(self):
167+
def _delete_calibration_file(self):
165168
"""
166169
Deletes the entire calibrations file
167170
"""
168171
file_path = self._get_calibration_file_path()
169172
if os.path.exists(file_path):
170173
os.remove(file_path)
171174

175+
def _write_blank_calibrations_file(self):
176+
self._delete_calibration_file()
177+
with open(self._get_calibration_file_path(), 'w') as f:
178+
f.write(json.dumps({
179+
'version': self.calibration_data_version,
180+
'data': {}
181+
}))
182+
172183
def _get_calibration_dir(self):
173184
"""
174185
:return: the directory to save calibration data
@@ -186,7 +197,8 @@ def _get_calibration(self):
186197
"""
187198
:return: this instrument's saved calibrations data
188199
"""
189-
return self._read_calibrations().get(self.calibration_key)
200+
data = self._read_calibrations()['data']
201+
return data.get(self.calibration_key)
190202

191203
def _build_calibration_data(self):
192204
"""
@@ -206,9 +218,25 @@ def _read_calibrations(self):
206218
try:
207219
loaded_json = json.load(f)
208220
except json.decoder.JSONDecodeError:
209-
loaded_json = {}
221+
self._write_blank_calibrations_file()
222+
return self._read_calibrations()
210223
return self._restore_vector(loaded_json)
211224

225+
def _check_calibrations_version(self):
226+
"""
227+
Read calibration file, and checks for version number
228+
If no version number, file is replaced with version number
229+
"""
230+
with open(self._get_calibration_file_path()) as f:
231+
try:
232+
file = json.load(f)
233+
version = file.get('version')
234+
data = file.get('data')
235+
if not version or not data or len(file.keys()) > 2:
236+
self._write_blank_calibrations_file()
237+
except json.decoder.JSONDecodeError:
238+
self._write_blank_calibrations_file()
239+
212240
def _strip_vector(self, obj, root=True):
213241
"""
214242
Iterates through a dictionary, converting Vector classes

tests/opentrons/containers/test_calibrator.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,21 @@ def test_apply_calibration(self):
2727
calibration_data = {
2828
'A1':
2929
{
30+
'type': 'Slot',
3031
'delta': (1, 1, 1),
3132
'children': {
3233
'tube_rack': {
34+
'type': 'tube-rack-2ml',
3335
'delta': (1, 1, 1),
3436
'children': {
3537
'Red':
3638
{
39+
'type': 'Well',
3740
'delta': (1, 1, 1)
3841
},
3942
'Blue':
4043
{
44+
'type': 'Well',
4145
'delta': (2, 2, 2)
4246
}
4347
}
@@ -62,17 +66,21 @@ def test_convert(self):
6266
calibration_data = {
6367
'A1':
6468
{
69+
'type': 'Slot',
6570
'delta': (1, 1, 1),
6671
'children': {
6772
'tube_rack': {
73+
'type': 'tube-rack-2ml',
6874
'delta': (1, 1, 1),
6975
'children': {
7076
'Red':
7177
{
78+
'type': 'Well',
7279
'delta': (1, 1, 1)
7380
},
7481
'Blue':
7582
{
83+
'type': 'Well',
7684
'delta': (2, 2, 2)
7785
}
7886
}
@@ -108,7 +116,8 @@ def test_calibrate(self):
108116
'A1': {
109117
'children': {
110118
'tube_rack': {
111-
'delta': (-1.0, -1.0, -1.0)
119+
'delta': (-1.0, -1.0, -1.0),
120+
'type': 'Container'
112121
}
113122
}
114123
}

tests/opentrons/containers/test_grid.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ def test_rows_cols(self):
2929

3030
def test_placeable(self):
3131
plate = self.plate
32-
self.assertEqual(plate.rows[0].center(plate), (14.44, 17.54, 5.25))
33-
self.assertEqual(plate.rows[1].center(plate), (14.44, 26.54, 5.25))
32+
self.assertEqual(plate.rows[0].center(plate), (11.24, 14.34, 5.25))
33+
self.assertEqual(plate.rows[1].center(plate), (11.24, 23.34, 5.25))
3434
self.assertEqual(plate.rows[0].center(plate),
3535
plate.cols[0].center(plate))
3636

tests/opentrons/containers/test_persisted_containers.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ def test_load_persisted_container(self):
7171
well_1 = wells[0]
7272
well_2 = wells[1]
7373

74-
self.assertEqual(well_1.coordinates(), (13.67 + 0, 16.0 + 0, 0))
75-
self.assertEqual(well_2.coordinates(), (13.67 + 0, 16.0 + 19.3, 0))
74+
self.assertEqual(well_1.coordinates(), (5.86 + 0, 8.19 + 0, 0))
75+
self.assertEqual(well_2.coordinates(), (5.86 + 0, 8.19 + 19.3, 0))
7676

7777
def test_load_all_persisted_containers(self):
7878
all_persisted_containers = \
@@ -121,5 +121,5 @@ def test_create_container_obj_from_dict(self):
121121
well_1 = wells[0]
122122
well_2 = wells[1]
123123

124-
self.assertEqual(well_1.coordinates(), (13.3 + 0, 17.5 + 0, 0))
125-
self.assertEqual(well_2.coordinates(), (13.3 + 0, 17.5 + 19.3, 0))
124+
self.assertEqual(well_1.coordinates(), (5.49 + 0, 9.69 + 0, 0))
125+
self.assertEqual(well_2.coordinates(), (5.49 + 0, 9.69 + 19.3, 0))

tests/opentrons/labware/test_crud_calibrations.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ def test_save_load_calibration_data(self):
3838
self.p200 = instruments.Pipette(name="p200", axis="b")
3939

4040
expected_delta = {
41-
'delta': (1.0, 2.0, 3.0)
41+
'delta': (1.0, 2.0, 3.0),
42+
'type': '96-flat'
4243
}
4344

4445
self.assertTrue('A1' in self.p200.calibration_data)
@@ -52,7 +53,8 @@ def test_delete_calibrations_data(self):
5253
self.p200 = instruments.Pipette(name="p200", axis="b")
5354

5455
expected_delta = {
55-
'delta': (1.0, 2.0, 3.0)
56+
'delta': (1.0, 2.0, 3.0),
57+
'type': '96-flat'
5658
}
5759

5860
self.assertTrue('A1' in self.p200.calibration_data)

tests/opentrons/labware/test_pipette.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ def test_calibrate_placeable(self):
128128
'A2': {
129129
'children': {
130130
'96-flat': {
131-
'delta': (1.0, 2.0, 3.0)
131+
'delta': (1.0, 2.0, 3.0),
132+
'type': '96-flat'
132133
}}}}
133134

134135
self.assertDictEqual(

tests/opentrons/util/test_vector.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import unittest
22

3-
from opentrons.util.vector import (Vector, VectorEncoder)
3+
from opentrons.util.vector import (Vector, VectorEncoder, VectorValue)
44
import json
55

66

@@ -23,6 +23,17 @@ def test_add(self):
2323

2424
self.assertEqual(res, Vector(5, 7, 9))
2525

26+
def test_to_iterable(self):
27+
28+
v1 = Vector(1, 2, 3)
29+
iterable = v1.to_iterable()
30+
self.assertTrue(hasattr(iterable, '__iter__'))
31+
32+
def test_zero_coordinates(self):
33+
34+
zero_coords = Vector(1, 2, 3).zero_coordinates()
35+
self.assertEquals(zero_coords, VectorValue(0, 0, 0))
36+
2637
def test_substract(self):
2738
v1 = Vector(1, 2, 3)
2839
v2 = Vector(4, 5, 6)

0 commit comments

Comments
 (0)