@opentrons/shared-data 0.1.2-alpha.0 → 0.1.2-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/js/__tests__/__snapshots__/pipettes.test.ts.snap +5969 -0
  2. package/js/__tests__/deckSchemas.test.ts +87 -0
  3. package/js/__tests__/errors.test.js +21 -0
  4. package/js/__tests__/getAreSlotsAdjacent.test.ts +128 -0
  5. package/js/__tests__/getWellNamePerMultiTip.test.ts +162 -0
  6. package/js/__tests__/labwareDefQuirks.test.ts +38 -0
  7. package/js/__tests__/labwareDefSchemaV1.test.ts +82 -0
  8. package/js/__tests__/labwareDefSchemaV2.test.ts +298 -0
  9. package/js/__tests__/moduleAccessors.test.ts +50 -0
  10. package/js/__tests__/moduleSpecsSchema.test.ts +77 -0
  11. package/js/__tests__/pipetteSchemaV2.test.ts +107 -0
  12. package/js/__tests__/pipetteSpecSchemas.test.ts +48 -0
  13. package/js/__tests__/pipettes.test.ts +58 -0
  14. package/js/__tests__/protocolSchemaV4.test.ts +137 -0
  15. package/js/__tests__/protocolSchemaV5.test.ts +137 -0
  16. package/js/__tests__/protocolSchemaV6.test.ts +132 -0
  17. package/js/__tests__/protocolSchemaV7.test.ts +64 -0
  18. package/js/__tests__/protocolValidation.test.ts +83 -0
  19. package/js/__tests__/sortWells.test.ts +40 -0
  20. package/js/__tests__/splitWellsOnColumn.test.ts +21 -0
  21. package/js/__tests__/validateErrors.test.js +34 -0
  22. package/js/constants.ts +405 -0
  23. package/js/cypressUtils.ts +35 -0
  24. package/js/errors.ts +33 -0
  25. package/js/fixtures.ts +156 -0
  26. package/js/getLabware.ts +145 -0
  27. package/js/gripper.ts +29 -0
  28. package/js/helpers/__tests__/getAdapterName.test.ts +31 -0
  29. package/js/helpers/__tests__/getDeckDefFromLoadedLabware.test.ts +12 -0
  30. package/js/helpers/__tests__/getSimplestFlexDeckConfig.test.ts +176 -0
  31. package/js/helpers/__tests__/getVectorDifference.test.ts +25 -0
  32. package/js/helpers/__tests__/getVectorSum.test.ts +21 -0
  33. package/js/helpers/__tests__/labwareInference.test.ts +84 -0
  34. package/js/helpers/__tests__/orderWells.test.ts +87 -0
  35. package/js/helpers/__tests__/parseProtocolData.test.ts +165 -0
  36. package/js/helpers/__tests__/volume.test.ts +108 -0
  37. package/js/helpers/__tests__/wellSets.test.ts +352 -0
  38. package/js/helpers/get96Channel384WellPlateWells.ts +44 -0
  39. package/js/helpers/getAddressableAreasInProtocol.ts +98 -0
  40. package/js/helpers/getLoadedLabwareDefinitionsByUri.ts +30 -0
  41. package/js/helpers/getModuleVizDims.ts +118 -0
  42. package/js/helpers/getOccludedSlotCountForModule.ts +13 -0
  43. package/js/helpers/getSimplestFlexDeckConfig.ts +183 -0
  44. package/js/helpers/getVectorDifference.ts +12 -0
  45. package/js/helpers/getVectorSum.ts +12 -0
  46. package/js/helpers/getWellNamePerMultiTip.ts +86 -0
  47. package/js/helpers/getWellTotalVolume.ts +16 -0
  48. package/js/helpers/index.ts +348 -0
  49. package/js/helpers/labwareInference.ts +92 -0
  50. package/js/helpers/orderWells.ts +69 -0
  51. package/js/helpers/parseProtocolData.ts +141 -0
  52. package/js/helpers/volume.ts +35 -0
  53. package/js/helpers/wellIsRect.ts +6 -0
  54. package/js/helpers/wellSets.ts +155 -0
  55. package/js/index.ts +14 -0
  56. package/js/labwareTools/__tests__/__snapshots__/createIrregularLabware.test.ts.snap +3 -0
  57. package/js/labwareTools/__tests__/__snapshots__/createLabware.test.ts.snap +3 -0
  58. package/js/labwareTools/__tests__/createDefaultDisplayName.test.ts +111 -0
  59. package/js/labwareTools/__tests__/createIrregularLabware.test.ts +226 -0
  60. package/js/labwareTools/__tests__/createLabware.test.ts +147 -0
  61. package/js/labwareTools/index.ts +452 -0
  62. package/js/modules.ts +97 -0
  63. package/js/pipettes.ts +89 -0
  64. package/js/protocols.ts +290 -0
  65. package/js/schema.ts +80 -0
  66. package/js/scripts/generateDeckLayersFromSVG.js +46 -0
  67. package/js/titleCase.ts +30 -0
  68. package/js/types.ts +561 -0
  69. package/lib/js/__tests__/deckSchemas.test.d.ts +1 -0
  70. package/lib/js/__tests__/getAreSlotsAdjacent.test.d.ts +1 -0
  71. package/lib/js/__tests__/getWellNamePerMultiTip.test.d.ts +1 -0
  72. package/lib/js/__tests__/labwareDefQuirks.test.d.ts +1 -0
  73. package/lib/js/__tests__/labwareDefSchemaV1.test.d.ts +1 -0
  74. package/lib/js/__tests__/labwareDefSchemaV2.test.d.ts +1 -0
  75. package/lib/js/__tests__/moduleAccessors.test.d.ts +1 -0
  76. package/lib/js/__tests__/moduleSpecsSchema.test.d.ts +1 -0
  77. package/lib/js/__tests__/pipetteSchemaV2.test.d.ts +1 -0
  78. package/lib/js/__tests__/pipetteSpecSchemas.test.d.ts +1 -0
  79. package/lib/js/__tests__/pipettes.test.d.ts +1 -0
  80. package/lib/js/__tests__/protocolSchemaV4.test.d.ts +1 -0
  81. package/lib/js/__tests__/protocolSchemaV5.test.d.ts +1 -0
  82. package/lib/js/__tests__/protocolSchemaV6.test.d.ts +1 -0
  83. package/lib/js/__tests__/protocolSchemaV7.test.d.ts +1 -0
  84. package/lib/js/__tests__/protocolValidation.test.d.ts +1 -0
  85. package/lib/js/__tests__/sortWells.test.d.ts +1 -0
  86. package/lib/js/__tests__/splitWellsOnColumn.test.d.ts +1 -0
  87. package/lib/js/constants.d.ts +156 -0
  88. package/lib/js/cypressUtils.d.ts +1 -0
  89. package/lib/js/errors.d.ts +13 -0
  90. package/lib/js/fixtures.d.ts +13 -0
  91. package/lib/js/getLabware.d.ts +8 -0
  92. package/lib/js/gripper.d.ts +3 -0
  93. package/lib/js/helpers/__tests__/getAdapterName.test.d.ts +1 -0
  94. package/lib/js/helpers/__tests__/getDeckDefFromLoadedLabware.test.d.ts +1 -0
  95. package/lib/js/helpers/__tests__/getSimplestFlexDeckConfig.test.d.ts +1 -0
  96. package/lib/js/helpers/__tests__/getVectorDifference.test.d.ts +1 -0
  97. package/lib/js/helpers/__tests__/getVectorSum.test.d.ts +1 -0
  98. package/lib/js/helpers/__tests__/labwareInference.test.d.ts +1 -0
  99. package/lib/js/helpers/__tests__/orderWells.test.d.ts +1 -0
  100. package/lib/js/helpers/__tests__/parseProtocolData.test.d.ts +1 -0
  101. package/lib/js/helpers/__tests__/volume.test.d.ts +1 -0
  102. package/lib/js/helpers/__tests__/wellSets.test.d.ts +1 -0
  103. package/lib/js/helpers/get96Channel384WellPlateWells.d.ts +1 -0
  104. package/lib/js/helpers/getAddressableAreasInProtocol.d.ts +4 -0
  105. package/lib/js/helpers/getLoadedLabwareDefinitionsByUri.d.ts +5 -0
  106. package/lib/js/helpers/getModuleVizDims.d.ts +16 -0
  107. package/lib/js/helpers/getOccludedSlotCountForModule.d.ts +2 -0
  108. package/lib/js/helpers/getSimplestFlexDeckConfig.d.ts +14 -0
  109. package/lib/js/helpers/getVectorDifference.d.ts +2 -0
  110. package/lib/js/helpers/getVectorSum.d.ts +2 -0
  111. package/lib/js/helpers/getWellNamePerMultiTip.d.ts +3 -0
  112. package/lib/js/helpers/getWellTotalVolume.d.ts +2 -0
  113. package/lib/js/helpers/index.d.ts +44 -0
  114. package/lib/js/helpers/labwareInference.d.ts +16 -0
  115. package/lib/js/helpers/orderWells.d.ts +2 -0
  116. package/lib/js/helpers/parseProtocolData.d.ts +23 -0
  117. package/lib/js/helpers/volume.d.ts +4 -0
  118. package/lib/js/helpers/wellIsRect.d.ts +3 -0
  119. package/lib/js/helpers/wellSets.d.ts +9 -0
  120. package/lib/js/index.d.ts +14 -0
  121. package/lib/js/labwareTools/__tests__/createDefaultDisplayName.test.d.ts +1 -0
  122. package/lib/js/labwareTools/__tests__/createIrregularLabware.test.d.ts +1 -0
  123. package/lib/js/labwareTools/__tests__/createLabware.test.d.ts +1 -0
  124. package/lib/js/labwareTools/index.d.ts +65 -0
  125. package/lib/js/modules.d.ts +6 -0
  126. package/lib/js/pipettes.d.ts +13 -0
  127. package/lib/js/protocols.d.ts +3 -0
  128. package/lib/js/schema.d.ts +90 -0
  129. package/lib/js/titleCase.d.ts +1 -0
  130. package/lib/js/types.d.ts +398 -0
  131. package/package.json +1 -1
@@ -0,0 +1,155 @@
1
+ // A "well set" is an array of wells corresponding to each tip of an 8 channel pipette.
2
+ // Eg ['A1', 'C1', 'E1', 'G1', 'I1', 'K1', 'M1', 'O1'] is a well set in a 384 plate.
3
+ //
4
+ // A trough-like well that encompasses all 8 tips at once has a well set
5
+ // ['A1', 'A1', 'A1', 'A1', 'A1', 'A1', 'A1', 'A1']
6
+ //
7
+ // Well sets are determined by geometry.
8
+ //
9
+ // Labware with multiple positions for an 8-channel pipette have multiple well sets.
10
+ // For example, a 96 plate has 12 well sets, one for each column.
11
+ // A 384 plate has 48 well sets, 2 for each column b/c it has staggered columns.
12
+ //
13
+ // If a labware has no possible well sets, then it is not compatible with multi-channel pipettes.
14
+ import uniq from 'lodash/uniq'
15
+
16
+ import { getWellNamePerMultiTip } from './getWellNamePerMultiTip'
17
+ import { get96Channel384WellPlateWells, getLabwareDefURI, orderWells } from '.'
18
+ import type { LabwareDefinition2, PipetteNameSpecs } from '../types'
19
+
20
+ type WellSetByPrimaryWell = string[][]
21
+
22
+ // Compute all well sets for a labware def (non-memoized)
23
+ function _getAllWellSetsForLabware(
24
+ labwareDef: LabwareDefinition2
25
+ ): WellSetByPrimaryWell {
26
+ const allWells: string[] = Object.keys(labwareDef.wells)
27
+
28
+ return allWells.reduce(
29
+ (acc: WellSetByPrimaryWell, well: string): WellSetByPrimaryWell => {
30
+ const wellSet = getWellNamePerMultiTip(labwareDef, well, 8)
31
+ if (wellSet === null) {
32
+ return acc
33
+ } else {
34
+ return [...acc, wellSet]
35
+ }
36
+ },
37
+ []
38
+ )
39
+ }
40
+
41
+ // creates memoized getAllWellSetsForLabware + getWellSetForMultichannel fns.
42
+ export interface WellSetHelpers {
43
+ getAllWellSetsForLabware: (
44
+ labwareDef: LabwareDefinition2
45
+ ) => WellSetByPrimaryWell
46
+
47
+ getWellSetForMultichannel: (
48
+ labwareDef: LabwareDefinition2,
49
+ well: string,
50
+ channels: 8 | 96
51
+ ) => string[] | null | undefined
52
+
53
+ canPipetteUseLabware: (
54
+ pipetteSpec: PipetteNameSpecs,
55
+ labwareDef?: LabwareDefinition2,
56
+ trashName?: string
57
+ ) => boolean
58
+ }
59
+
60
+ export const makeWellSetHelpers = (): WellSetHelpers => {
61
+ const cache: Partial<{
62
+ [labwareDefURI: string]: {
63
+ labwareDef: LabwareDefinition2
64
+ wellSetByPrimaryWell: WellSetByPrimaryWell
65
+ } | null
66
+ }> = {}
67
+
68
+ const getAllWellSetsForLabware = (
69
+ labwareDef: LabwareDefinition2
70
+ ): WellSetByPrimaryWell => {
71
+ const labwareDefURI = getLabwareDefURI(labwareDef)
72
+ const c = cache[labwareDefURI]
73
+
74
+ // use cached version only if labwareDef is shallowly equal, in case
75
+ // custom labware defs are changed without giving them a new URI
76
+ if (c && c.labwareDef === labwareDef) {
77
+ return c.wellSetByPrimaryWell
78
+ }
79
+
80
+ const wellSetByPrimaryWell = _getAllWellSetsForLabware(labwareDef)
81
+
82
+ cache[labwareDefURI] = {
83
+ labwareDef,
84
+ wellSetByPrimaryWell,
85
+ }
86
+ return wellSetByPrimaryWell
87
+ }
88
+
89
+ const getWellSetForMultichannel = (
90
+ labwareDef: LabwareDefinition2,
91
+ well: string,
92
+ channels: 8 | 96
93
+ ): string[] | null | undefined => {
94
+ /** Given a well for a labware, returns the well set it belongs to (or null)
95
+ * for 8-channel access.
96
+ * Ie: C2 for 96-flat => ['A2', 'B2', 'C2', ... 'H2']
97
+ * Or A1 for trough => ['A1', 'A1', 'A1', ...]
98
+ **/
99
+ const allWellSetsFor8Channel = getAllWellSetsForLabware(labwareDef)
100
+ /** getting all wells from the plate and turning into 1D array for 96-channel
101
+ */
102
+ const orderedWellsFor96Channel = orderWells(
103
+ labwareDef.ordering,
104
+ 't2b',
105
+ 'l2r'
106
+ )
107
+
108
+ let ninetySixChannelWells = orderedWellsFor96Channel
109
+ /** special casing 384 well plates to be every other well
110
+ * both on the x and y ases.
111
+ */
112
+ if (orderedWellsFor96Channel.length === 384) {
113
+ ninetySixChannelWells = get96Channel384WellPlateWells(
114
+ orderedWellsFor96Channel,
115
+ well
116
+ )
117
+ }
118
+ return channels === 8
119
+ ? allWellSetsFor8Channel.find((wellSet: string[]) =>
120
+ wellSet.includes(well)
121
+ )
122
+ : ninetySixChannelWells
123
+ }
124
+
125
+ const canPipetteUseLabware = (
126
+ pipetteSpec: PipetteNameSpecs,
127
+ labwareDef?: LabwareDefinition2,
128
+ trashName?: string
129
+ ): boolean => {
130
+ if (pipetteSpec.channels === 1 || trashName != null) {
131
+ // assume all labware can be used by single-channel
132
+ return true
133
+ }
134
+ if (labwareDef != null) {
135
+ const allWellSets = getAllWellSetsForLabware(labwareDef)
136
+ return allWellSets.some(wellSet => {
137
+ const uniqueWells = uniq(wellSet)
138
+ // if all wells are non-null, and there are either 1 (reservoir-like)
139
+ // or 8 (well plate-like) unique wells in the set,
140
+ // then assume both 8 and 96 channel pipettes will work
141
+ return (
142
+ uniqueWells.every(well => well != null) &&
143
+ [1, 8].includes(uniqueWells.length)
144
+ )
145
+ })
146
+ } else {
147
+ return false
148
+ }
149
+ }
150
+ return {
151
+ getAllWellSetsForLabware,
152
+ getWellSetForMultichannel,
153
+ canPipetteUseLabware,
154
+ }
155
+ }
package/js/index.ts ADDED
@@ -0,0 +1,14 @@
1
+ export * from './constants'
2
+ export * from './getLabware'
3
+ export * from './helpers'
4
+ export * from './pipettes'
5
+ export * from './types'
6
+ export * from './labwareTools'
7
+ export * from './modules'
8
+ export * from './fixtures'
9
+ export * from './gripper'
10
+ export * from '../protocol'
11
+ export * from '../deck'
12
+ export * from './titleCase'
13
+ export * from './errors'
14
+ export * from './fixtures'
@@ -0,0 +1,3 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`test createIrregularLabware function failing to validate against labware schema throws w/o "strict" 1`] = `"Generated labware failed to validate, please check your inputs"`;
@@ -0,0 +1,3 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`createLabware failing to validate against labware schema throws w/o "strict" 1`] = `"Generated labware failed to validate, please check your inputs"`;
@@ -0,0 +1,111 @@
1
+ import { createDefaultDisplayName } from '..'
2
+
3
+ import type { RegularNameProps } from '..'
4
+
5
+ interface TestCase {
6
+ testName: string
7
+ args: RegularNameProps
8
+ expected: string
9
+ }
10
+
11
+ describe('createDefaultDisplayName', () => {
12
+ const testCases: TestCase[] = [
13
+ {
14
+ testName: 'handle the minimal case',
15
+ args: {
16
+ displayCategory: 'wellPlate',
17
+ displayVolumeUnits: 'µL',
18
+ gridRows: 2,
19
+ gridColumns: 3,
20
+ totalLiquidVolume: 123,
21
+ },
22
+ expected: 'Generic 6 Well Plate 123 µL',
23
+ },
24
+ {
25
+ testName: 'handle a decimal in volume',
26
+ args: {
27
+ displayCategory: 'wellPlate',
28
+ displayVolumeUnits: 'µL',
29
+ gridRows: 2,
30
+ gridColumns: 3,
31
+ totalLiquidVolume: 12.3,
32
+ },
33
+ expected: 'Generic 6 Well Plate 12.3 µL',
34
+ },
35
+ {
36
+ testName: 'calculate wells = rows x cols',
37
+ args: {
38
+ displayCategory: 'wellPlate',
39
+ displayVolumeUnits: 'µL',
40
+ gridRows: 8,
41
+ gridColumns: 10,
42
+ totalLiquidVolume: 123,
43
+ },
44
+ expected: 'Generic 80 Well Plate 123 µL',
45
+ },
46
+ {
47
+ testName: 'handle tube rack (example of a different displayCategory)',
48
+ args: {
49
+ displayCategory: 'tubeRack',
50
+ displayVolumeUnits: 'µL',
51
+ gridRows: 2,
52
+ gridColumns: 3,
53
+ totalLiquidVolume: 123,
54
+ },
55
+ expected: 'Generic 6 Tube Rack 123 µL',
56
+ },
57
+ {
58
+ testName: 'append loadNamePostfix',
59
+ args: {
60
+ displayCategory: 'wellPlate',
61
+ displayVolumeUnits: 'µL',
62
+ gridRows: 2,
63
+ gridColumns: 3,
64
+ totalLiquidVolume: 123,
65
+ loadNamePostfix: ['spam ', ' blah', 'PCR'],
66
+ },
67
+ expected: 'Generic 6 Well Plate 123 µL Spam Blah PCR',
68
+ },
69
+ {
70
+ testName: 'support brand name when specified (ignore extra whitespace)',
71
+ args: {
72
+ displayCategory: 'wellPlate',
73
+ displayVolumeUnits: 'µL',
74
+ gridRows: 2,
75
+ gridColumns: 3,
76
+ totalLiquidVolume: 123,
77
+ brandName: ' cool brand ',
78
+ },
79
+ expected: 'Cool Brand 6 Well Plate 123 µL',
80
+ },
81
+ {
82
+ testName: 'support brand name when specified (capitalization)',
83
+ args: {
84
+ displayCategory: 'wellPlate',
85
+ displayVolumeUnits: 'µL',
86
+ gridRows: 2,
87
+ gridColumns: 3,
88
+ totalLiquidVolume: 123,
89
+ brandName: ' BIO tech',
90
+ },
91
+ expected: 'BIO Tech 6 Well Plate 123 µL',
92
+ },
93
+ {
94
+ testName: 'support mL when specified (volume number always in µL!)',
95
+ args: {
96
+ displayCategory: 'wellPlate',
97
+ displayVolumeUnits: 'mL',
98
+ gridRows: 2,
99
+ gridColumns: 3,
100
+ totalLiquidVolume: 123,
101
+ },
102
+ expected: 'Generic 6 Well Plate 0.123 mL',
103
+ },
104
+ ]
105
+
106
+ testCases.forEach(({ testName, args, expected }) => {
107
+ it(`should ${testName}`, () => {
108
+ expect(createDefaultDisplayName(args)).toEqual(expected)
109
+ })
110
+ })
111
+ })
@@ -0,0 +1,226 @@
1
+ import omit from 'lodash/omit'
2
+ import range from 'lodash/range'
3
+
4
+ import { splitWellsOnColumn, sortWells } from '../../helpers/index'
5
+ import fixture_irregular_example_1 from '../../../labware/fixtures/2/fixture_irregular_example_1.json'
6
+
7
+ import {
8
+ createIrregularLabware,
9
+ _irregularWellName,
10
+ _generateIrregularLoadName,
11
+ _calculateWellCoord,
12
+ } from '..'
13
+
14
+ import type { LabwareDefinition2, LabwareWellProperties } from '../../types'
15
+ import type { IrregularLabwareProps } from '..'
16
+
17
+ // NOTE: loadName needs to be replaced here b/c fixture has a non-default loadName
18
+ const exampleLabware1 = {
19
+ ...fixture_irregular_example_1,
20
+ parameters: {
21
+ ...fixture_irregular_example_1.parameters,
22
+ loadName: 'generic_55_tuberack_50x3ml_5x10ml',
23
+ },
24
+ } as LabwareDefinition2
25
+
26
+ describe('test helper functions', () => {
27
+ it('Well name generated correctly', () => {
28
+ const grid = { row: 2, column: 2 }
29
+
30
+ const gridStart = [
31
+ { rowStart: 'A', colStart: '1', rowStride: 1, colStride: 2 },
32
+ { rowStart: 'B', colStart: '1', rowStride: 3, colStride: 1 },
33
+ ]
34
+ const expected1 = ['A1', 'B1', 'A3', 'B3']
35
+ const expected2 = ['B1', 'E1', 'B2', 'E2']
36
+
37
+ range(grid.column).forEach(colIdx => {
38
+ range(grid.row).forEach(rowIdx => {
39
+ const idx = colIdx * grid.row + rowIdx
40
+
41
+ const wellName1 = _irregularWellName(rowIdx, colIdx, gridStart[0])
42
+
43
+ expect(expected1[idx]).toEqual(wellName1)
44
+
45
+ const wellName2 = _irregularWellName(rowIdx, colIdx, gridStart[1])
46
+
47
+ expect(expected2[idx]).toEqual(wellName2)
48
+ })
49
+ })
50
+ })
51
+
52
+ it('XYZ generates correctly for each grid', () => {
53
+ const grid = { row: 1, column: 5 }
54
+ const offset = { x: 1, y: 0.5, z: 55.5 }
55
+ const spacing = [
56
+ { row: 10, column: 10 },
57
+ { row: 5, column: 14 },
58
+ ]
59
+ const well = [
60
+ omit(exampleLabware1.wells.A1, ['x', 'y', 'z']),
61
+ omit(exampleLabware1.wells.B1, ['x', 'y', 'z']),
62
+ ] as LabwareWellProperties[]
63
+
64
+ const expectedX1 = [1, 11, 21, 31, 41]
65
+ const expectedY1 = [0.5]
66
+ const expectedX2 = [1, 15, 29, 43, 57]
67
+ const expectedY2 = [0.5]
68
+
69
+ range(grid.column).forEach(colIdx => {
70
+ range(grid.row).forEach(rowIdx => {
71
+ const well1 = _calculateWellCoord(
72
+ rowIdx,
73
+ colIdx,
74
+ spacing[0],
75
+ offset,
76
+ well[0]
77
+ )
78
+
79
+ expect(well1.x).toBeCloseTo(expectedX1[colIdx], 2)
80
+ expect(well1.y).toBeCloseTo(expectedY1[rowIdx], 2)
81
+ expect(well1.z).toBeCloseTo(offset.z - well[0].depth, 2)
82
+
83
+ const well2 = _calculateWellCoord(
84
+ rowIdx,
85
+ colIdx,
86
+ spacing[1],
87
+ offset,
88
+ well[1]
89
+ )
90
+
91
+ expect(well2.x).toBeCloseTo(expectedX2[colIdx], 2)
92
+ expect(well2.y).toBeCloseTo(expectedY2[rowIdx], 2)
93
+ expect(well2.z).toBeCloseTo(offset.z - well[1].depth, 2)
94
+ })
95
+ })
96
+ })
97
+ })
98
+
99
+ describe('test createIrregularLabware function', () => {
100
+ let labware1: LabwareDefinition2
101
+ let labware1Args: IrregularLabwareProps
102
+
103
+ beforeEach(() => {
104
+ labware1Args = {
105
+ namespace: 'fixture',
106
+ metadata: {
107
+ displayName: 'Fake Irregular Container',
108
+ displayCategory: 'tubeRack',
109
+ displayVolumeUnits: 'mL',
110
+ tags: ['fake', 'opentrons'],
111
+ },
112
+ parameters: {
113
+ format: 'irregular',
114
+ isTiprack: false,
115
+ isMagneticModuleCompatible: false,
116
+ },
117
+ dimensions: {
118
+ xDimension: 127.76,
119
+ yDimension: 85.48,
120
+ zDimension: 64.48,
121
+ },
122
+ well: [
123
+ {
124
+ depth: 10.54,
125
+ shape: 'circular',
126
+ diameter: 10,
127
+ totalLiquidVolume: 3000,
128
+ },
129
+ {
130
+ depth: 20.54,
131
+ shape: 'circular',
132
+ diameter: 15,
133
+ totalLiquidVolume: 10000,
134
+ },
135
+ ],
136
+ offset: [
137
+ { x: 10, y: 10, z: 69.48 },
138
+ { x: 15, y: 15, z: 69.48 },
139
+ ],
140
+ grid: [
141
+ { row: 5, column: 10 },
142
+ { row: 1, column: 5 },
143
+ ],
144
+ spacing: [
145
+ { row: 5, column: 10 },
146
+ { row: 5, column: 10 },
147
+ ],
148
+ gridStart: [
149
+ { rowStart: 'A', colStart: '1', rowStride: 2, colStride: 1 },
150
+ { rowStart: 'B', colStart: '1', rowStride: 1, colStride: 1 },
151
+ ],
152
+ }
153
+ labware1 = createIrregularLabware(labware1Args)
154
+ })
155
+
156
+ it('irregular ordering generates as expected', () => {
157
+ const keyList = Object.keys(labware1.wells)
158
+ const generatedOrdering = splitWellsOnColumn(keyList.sort(sortWells))
159
+ expect(labware1.ordering).toEqual(generatedOrdering)
160
+ })
161
+
162
+ it('check labware matches fixture', () => {
163
+ expect(labware1).toEqual(exampleLabware1)
164
+ })
165
+
166
+ it('labware loadName generated correctly for multi-grid labware', () => {
167
+ const loadName = _generateIrregularLoadName({
168
+ grid: [
169
+ { row: 3, column: 2 },
170
+ { row: 1, column: 4 },
171
+ ],
172
+ well: [
173
+ {
174
+ depth: 20,
175
+ shape: 'circular',
176
+ totalLiquidVolume: 400,
177
+ } as LabwareWellProperties,
178
+ {
179
+ depth: 110,
180
+ shape: 'circular',
181
+ totalLiquidVolume: 2000,
182
+ } as LabwareWellProperties,
183
+ ],
184
+ totalWellCount: 10,
185
+ units: 'µL',
186
+ displayCategory: 'wellPlate',
187
+ brand: 'some brand',
188
+ })
189
+
190
+ expect(loadName).toEqual('somebrand_10_wellplate_6x400ul_4x2000ul')
191
+ })
192
+
193
+ it('labware loadName generated correctly for multi-grid labware in other units', () => {
194
+ const loadName = _generateIrregularLoadName({
195
+ grid: [{ row: 3, column: 2 }],
196
+ well: [
197
+ {
198
+ depth: 20,
199
+ shape: 'circular',
200
+ totalLiquidVolume: 4000,
201
+ } as LabwareWellProperties,
202
+ ],
203
+ totalWellCount: 6,
204
+ units: 'mL',
205
+ displayCategory: 'wellPlate',
206
+ brand: 'some brand',
207
+ })
208
+
209
+ expect(loadName).toEqual('somebrand_6_wellplate_6x4ml')
210
+ })
211
+
212
+ it('failing to validate against labware schema throws w/o "strict"', () => {
213
+ const args = {
214
+ ...labware1Args,
215
+ // negative y offset should fail schema validation by making well `y` negative
216
+ offset: [
217
+ { x: 10, y: -9999, z: 69.48 },
218
+ { x: 15, y: -9999, z: 69.48 },
219
+ ],
220
+ }
221
+ expect(() => createIrregularLabware(args)).toThrowErrorMatchingSnapshot()
222
+ expect(() =>
223
+ createIrregularLabware({ ...args, strict: false })
224
+ ).not.toThrow()
225
+ })
226
+ })
@@ -0,0 +1,147 @@
1
+ import omit from 'lodash/omit'
2
+ import range from 'lodash/range'
3
+ import { createRegularLabware } from '..'
4
+ import fixture_regular_example_1 from '../../../labware/fixtures/2/fixture_regular_example_1.json'
5
+ import fixture_regular_example_2 from '../../../labware/fixtures/2/fixture_regular_example_2.json'
6
+
7
+ import type {
8
+ LabwareDefinition2,
9
+ LabwareWellProperties,
10
+ LabwareOffset,
11
+ } from '../../types'
12
+
13
+ import type { RegularLabwareProps } from '..'
14
+
15
+ // NOTE: loadName needs to be replaced here b/c fixture has a non-default loadName
16
+ const exampleLabware1 = {
17
+ ...fixture_regular_example_1,
18
+ parameters: {
19
+ ...fixture_regular_example_1.parameters,
20
+ loadName: 'opentrons_2_wellplate_100ul',
21
+ },
22
+ } as LabwareDefinition2
23
+
24
+ const exampleLabware2 = {
25
+ ...fixture_regular_example_2,
26
+ parameters: {
27
+ ...fixture_regular_example_2.parameters,
28
+ loadName: 'generic_6_wellplate_1ml',
29
+ },
30
+ } as LabwareDefinition2
31
+
32
+ describe('createLabware', () => {
33
+ let labware1: LabwareDefinition2
34
+ let labware2: LabwareDefinition2
35
+ let labware2Args: RegularLabwareProps
36
+ let well1: LabwareWellProperties
37
+ let well2: LabwareWellProperties
38
+ let offset1: LabwareOffset
39
+ let offset2: LabwareOffset
40
+
41
+ beforeEach(() => {
42
+ well1 = omit(exampleLabware1.wells.A1, [
43
+ 'x',
44
+ 'y',
45
+ 'z',
46
+ ]) as LabwareWellProperties
47
+
48
+ well2 = omit(exampleLabware2.wells.A1, [
49
+ 'x',
50
+ 'y',
51
+ 'z',
52
+ ]) as LabwareWellProperties
53
+
54
+ offset1 = { x: 10, y: 10, z: 55 }
55
+ offset2 = { x: 10, y: 10, z: 40 }
56
+
57
+ labware1 = createRegularLabware({
58
+ metadata: exampleLabware1.metadata,
59
+ parameters: exampleLabware1.parameters,
60
+ dimensions: exampleLabware1.dimensions,
61
+ offset: offset1,
62
+ grid: { row: 1, column: 2 },
63
+ spacing: { row: 10, column: 10 },
64
+ well: well1,
65
+ brand: exampleLabware1.brand,
66
+ namespace: 'fixture',
67
+ })
68
+ labware2Args = {
69
+ metadata: exampleLabware2.metadata,
70
+ parameters: exampleLabware2.parameters,
71
+ dimensions: exampleLabware2.dimensions,
72
+ offset: offset2,
73
+ grid: {
74
+ row: 3,
75
+ column: 2,
76
+ },
77
+ spacing: {
78
+ row: 10,
79
+ column: 10,
80
+ },
81
+ well: well2,
82
+ namespace: 'fixture',
83
+ }
84
+ labware2 = createRegularLabware(labware2Args)
85
+ })
86
+
87
+ it('snapshot tests', () => {
88
+ expect(labware1).toEqual(exampleLabware1)
89
+ expect(labware2).toEqual(exampleLabware2)
90
+ })
91
+
92
+ it('ordering generates as expected', () => {
93
+ expect(exampleLabware2.ordering).toEqual(labware2.ordering)
94
+ })
95
+
96
+ it('well XYZ generates correctly', () => {
97
+ const spacing = { row: 10, column: 10 }
98
+ const grid = { row: 3, column: 2 }
99
+ const { yDimension } = exampleLabware2.dimensions
100
+
101
+ const labware3 = createRegularLabware({
102
+ metadata: exampleLabware2.metadata,
103
+ parameters: exampleLabware2.parameters,
104
+ dimensions: exampleLabware2.dimensions,
105
+ offset: offset2,
106
+ grid,
107
+ spacing,
108
+ well: well2,
109
+ })
110
+
111
+ const expectedXByCol = range(
112
+ offset2.x,
113
+ offset2.x + grid.column * spacing.column,
114
+ spacing.column
115
+ )
116
+
117
+ const expectedYByRow = range(
118
+ yDimension - offset2.y,
119
+ yDimension - offset2.y - spacing.row * grid.row,
120
+ -spacing.row
121
+ )
122
+
123
+ labware3.ordering.forEach((column, cIndex) => {
124
+ column.forEach((wellName, rIndex) => {
125
+ const well = labware3.wells[wellName]
126
+
127
+ expect(well.x).toBeCloseTo(expectedXByCol[cIndex], 2)
128
+ expect(well.y).toBeCloseTo(expectedYByRow[rIndex], 2)
129
+ expect(well.z).toBeCloseTo(offset2.z - well.depth, 2)
130
+ })
131
+ })
132
+ })
133
+
134
+ it('failing to validate against labware schema throws w/o "strict"', () => {
135
+ const args = {
136
+ ...labware2Args,
137
+ // this spacing should make negative well `y` value and fail schema validation
138
+ spacing: {
139
+ row: 999,
140
+ column: 999,
141
+ },
142
+ }
143
+
144
+ expect(() => createRegularLabware(args)).toThrowErrorMatchingSnapshot()
145
+ expect(() => createRegularLabware({ ...args, strict: false })).not.toThrow()
146
+ })
147
+ })