@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,145 @@
1
+ import mapValues from 'lodash/mapValues'
2
+
3
+ import {
4
+ FIXED_TRASH_RENDER_HEIGHT,
5
+ OPENTRONS_LABWARE_NAMESPACE,
6
+ SLOT_RENDER_HEIGHT,
7
+ } from './constants'
8
+
9
+ import type {
10
+ LabwareDefinition1,
11
+ LabwareDefinition2,
12
+ WellDefinition,
13
+ } from './types'
14
+
15
+ // do not list in any "available labware" UI.
16
+ // TODO(mc, 2019-12-3): how should this correspond to RETIRED_LABWARE?
17
+ // see shared-data/js/helpers/index.js
18
+ export const LABWAREV2_DO_NOT_LIST = [
19
+ // Labware definitions only used for back-compat with legacy v1 defs:
20
+ 'opentrons_40_aluminumblock_eppendorf_24x2ml_safelock_snapcap_generic_16x0.2ml_pcr_strip',
21
+ 'opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap_acrylic',
22
+ 'opentrons_24_tuberack_generic_0.75ml_snapcap_acrylic',
23
+ 'opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical_acrylic',
24
+ 'tipone_96_tiprack_200ul',
25
+ 'eppendorf_96_tiprack_1000ul_eptips',
26
+ 'eppendorf_96_tiprack_10ul_eptips',
27
+ // Replaced by opentrons_96_wellplate_200ul_pcr_full_skirt
28
+ // (https://opentrons.atlassian.net/browse/RLAB-230):
29
+ 'armadillo_96_wellplate_200ul_pcr_full_skirt',
30
+ 'opentrons_96_pcr_adapter_armadillo_wellplate_200ul',
31
+ // Special labware that users probably shouldn't know about or load:
32
+ // these all should eventually be given a new namespace like "opentrons-internal"
33
+ // so that they can be filtered out by namespace rather than a block list like this
34
+ 'opentrons_1_trash_850ml_fixed',
35
+ 'opentrons_1_trash_1100ml_fixed',
36
+ 'opentrons_1_trash_3200ml_fixed',
37
+ 'opentrons_calibrationblock_short_side_left',
38
+ 'opentrons_calibrationblock_short_side_right',
39
+ 'opentrons_calibration_adapter_heatershaker_module',
40
+ 'opentrons_calibration_adapter_temperature_module',
41
+ 'opentrons_calibration_adapter_thermocycler_module',
42
+ 'opentrons_ot3_96_tiprack_20ul',
43
+ 'opentrons_ot3_96_tiprack_200ul',
44
+ 'opentrons_ot3_96_tiprack_1000ul',
45
+ 'opentrons_ot3_96_tiprack_50ul',
46
+ ]
47
+ // NOTE(sa, 2020-7-14): in PD we do not want to list calibration blocks
48
+ // but we still might want the rest of the labware in LABWAREV2_DO_NOT_LIST
49
+ // because of legacy protocols that might use them
50
+ export const PD_DO_NOT_LIST = [
51
+ 'opentrons_calibrationblock_short_side_left',
52
+ 'opentrons_calibrationblock_short_side_right',
53
+ 'opentrons_96_aluminumblock_biorad_wellplate_200ul',
54
+ 'opentrons_96_aluminumblock_nest_wellplate_100ul',
55
+ ]
56
+
57
+ export function getIsLabwareV1Tiprack(def: LabwareDefinition1): boolean {
58
+ return Boolean(def?.metadata?.isTiprack)
59
+ }
60
+
61
+ export function getIsTiprack(labwareDef: LabwareDefinition2): boolean {
62
+ return labwareDef.parameters.isTiprack
63
+ }
64
+
65
+ // NOTE: these labware definitions in _SHORT_MM_LABWARE_DEF_LOADNAMES
66
+ // were written in "short mm" = 0.5mm, but
67
+ // we will write all future definitions in actual mm.
68
+ // These allowed labware also have engage heights measured from home switch
69
+ // instead of from labware bottom, which is why we add ENGAGE_HEIGHT_OFFSET.
70
+ //
71
+ // Ideally instead of using this allow-list, we would publish a new version
72
+ // of these definitions with corrected labware heights. However, we don't
73
+ // support labware versioning well enough yet.
74
+ const _SHORT_MM_LABWARE_DEF_LOADNAMES = [
75
+ 'biorad_96_wellplate_200ul_pcr',
76
+ 'nest_96_wellplate_100ul_pcr_full_skirt',
77
+ 'usascientific_96_wellplate_2.4ml_deep',
78
+ ]
79
+
80
+ // offset added to parameters.magneticModuleEngageHeight to convert older labware
81
+ // definitions from "distance from home switch" to "distance from labware bottom"
82
+ // Note: this is in actual mm, not "short mm" :)
83
+ const ENGAGE_HEIGHT_OFFSET = -4
84
+
85
+ export function getLabwareDefaultEngageHeight(
86
+ labwareDef: LabwareDefinition2
87
+ ): number | null {
88
+ const rawEngageHeight: number | null | undefined =
89
+ labwareDef.parameters.magneticModuleEngageHeight
90
+
91
+ if (
92
+ labwareDef.namespace === OPENTRONS_LABWARE_NAMESPACE &&
93
+ _SHORT_MM_LABWARE_DEF_LOADNAMES.includes(labwareDef.parameters.loadName)
94
+ ) {
95
+ return rawEngageHeight == null
96
+ ? null
97
+ : rawEngageHeight / 2 + ENGAGE_HEIGHT_OFFSET
98
+ }
99
+
100
+ return rawEngageHeight == null ? null : rawEngageHeight
101
+ }
102
+
103
+ /* Render Helpers */
104
+ // NOTE: this doesn't account for the differing footprints of labware
105
+ // the fixed trash render height is the first bandaid to partially
106
+ // mend this, but overall the labware definitions in shared-data are
107
+ // insufficient to render labware at the resolution we'd like to
108
+ // achieve going forward.
109
+ // TODO: BC 2019-02-28 The height constants used here should be replaced with the heights
110
+ // in the dimensions field of the corresponding labware in definitions
111
+ const _getSvgYValueForWell = (
112
+ def: LabwareDefinition1,
113
+ wellDef: WellDefinition
114
+ ): number => {
115
+ const labwareName = def.metadata.name
116
+ const renderHeight =
117
+ labwareName === 'fixed-trash'
118
+ ? FIXED_TRASH_RENDER_HEIGHT
119
+ : SLOT_RENDER_HEIGHT
120
+ return renderHeight - wellDef.y
121
+ }
122
+
123
+ /** For display. Flips Y axis to match SVG, applies offset to wells */
124
+ export function getWellPropsForSVGLabwareV1(
125
+ def: LabwareDefinition1
126
+ ): Record<string, WellDefinition> {
127
+ const wellDefs = def && def.wells
128
+ // Most labware defs have a weird offset,
129
+ // but tips are mostly OK.
130
+ // This is a HACK to make the offset less "off"
131
+ const isTiprack = getIsLabwareV1Tiprack(def)
132
+ let xCorrection = 0
133
+ let yCorrection = 0
134
+
135
+ if (!isTiprack) {
136
+ xCorrection = 1
137
+ yCorrection = -3
138
+ }
139
+
140
+ return mapValues(wellDefs, (wellDef: WellDefinition) => ({
141
+ ...wellDef,
142
+ x: wellDef.x + xCorrection,
143
+ y: _getSvgYValueForWell(def, wellDef) + yCorrection,
144
+ }))
145
+ }
package/js/gripper.ts ADDED
@@ -0,0 +1,29 @@
1
+ import gripperV1 from '../gripper/definitions/1/gripperV1.json'
2
+ import gripperV1_1 from '../gripper/definitions/1/gripperV1.1.json'
3
+ import gripperV1_2 from '../gripper/definitions/1/gripperV1.2.json'
4
+
5
+ import { GRIPPER_V1, GRIPPER_V1_1, GRIPPER_V1_2 } from './constants'
6
+
7
+ import type { GripperModel, GripperDefinition } from './types'
8
+
9
+ export const getGripperDef = (
10
+ gripperModel: GripperModel
11
+ ): GripperDefinition => {
12
+ switch (gripperModel) {
13
+ case GRIPPER_V1:
14
+ return gripperV1 as GripperDefinition
15
+ case GRIPPER_V1_1:
16
+ return gripperV1_1 as GripperDefinition
17
+ case GRIPPER_V1_2:
18
+ return gripperV1_2 as GripperDefinition
19
+ default:
20
+ console.warn(
21
+ `Could not find a gripper with model ${gripperModel}, falling back to most recent definition: ${GRIPPER_V1_1}`
22
+ )
23
+ return gripperV1_1 as GripperDefinition
24
+ }
25
+ }
26
+
27
+ export function getGripperDisplayName(gripperModel: GripperModel): string {
28
+ return getGripperDef(gripperModel).displayName
29
+ }
@@ -0,0 +1,31 @@
1
+ import { getAdapterName } from '../index'
2
+
3
+ describe('getAdapterName', () => {
4
+ it(`should return the PCR adapter name`, () => {
5
+ expect(
6
+ getAdapterName(
7
+ 'opentrons_96_pcr_adapter_nest_wellplate_100ul_pcr_full_skirt'
8
+ )
9
+ ).toEqual('PCR Adapter')
10
+ })
11
+ it(`should return the Deep well adapter name`, () => {
12
+ expect(
13
+ getAdapterName('opentrons_96_deep_well_adapter_nest_wellplate_2ml_deep')
14
+ ).toEqual('Deep Well Adapter')
15
+ })
16
+ it(`should return the 96 flat bottom adapter name`, () => {
17
+ expect(
18
+ getAdapterName(
19
+ 'opentrons_96_flat_bottom_adapter_nest_wellplate_200ul_flat'
20
+ )
21
+ ).toEqual('96 Flat Bottom Adapter')
22
+ })
23
+ it(`should return universal flat adapter name when loadname is not 1 of the 3 specified loadnames`, () => {
24
+ expect(
25
+ getAdapterName(
26
+ 'opentrons_universal_flat_adapter_corning_384_wellplate_112ul_flat'
27
+ )
28
+ ).toEqual('Universal Flat Adapter')
29
+ expect(getAdapterName('random labware')).toEqual('Universal Flat Adapter')
30
+ })
31
+ })
@@ -0,0 +1,12 @@
1
+ import ot2DeckDef from '../../../deck/definitions/4/ot2_standard.json'
2
+ import ot3DeckDef from '../../../deck/definitions/4/ot3_standard.json'
3
+ import { getDeckDefFromRobotType } from '..'
4
+
5
+ describe('getDeckDefFromRobotType', () => {
6
+ it('should return an OT-2 deck when the protocol is for an OT-2', () => {
7
+ expect(getDeckDefFromRobotType('OT-2 Standard')).toBe(ot2DeckDef)
8
+ })
9
+ it('should return an OT-3 deck when the protocol is for an OT-3', () => {
10
+ expect(getDeckDefFromRobotType('OT-3 Standard')).toBe(ot3DeckDef)
11
+ })
12
+ })
@@ -0,0 +1,176 @@
1
+ import {
2
+ FLEX_SIMPLEST_DECK_CONFIG_PROTOCOL_SPEC,
3
+ getSimplestDeckConfigForProtocol,
4
+ } from '../getSimplestFlexDeckConfig'
5
+
6
+ import type { RunTimeCommand } from '../../../protocol'
7
+ import type { CompletedProtocolAnalysis } from '../../types'
8
+
9
+ const RUN_TIME_COMMAND_STUB_MIXIN: Pick<
10
+ RunTimeCommand,
11
+ 'id' | 'createdAt' | 'startedAt' | 'completedAt' | 'status'
12
+ > = {
13
+ id: 'fake_id',
14
+ createdAt: 'fake_createdAt',
15
+ startedAt: 'fake_startedAt',
16
+ completedAt: 'fake_createdAt',
17
+ status: 'succeeded',
18
+ }
19
+
20
+ describe('getSimplestDeckConfigForProtocol', () => {
21
+ it('returns simplest deck if no commands alter addressable areas', () => {
22
+ expect(getSimplestDeckConfigForProtocol(null)).toEqual(
23
+ FLEX_SIMPLEST_DECK_CONFIG_PROTOCOL_SPEC
24
+ )
25
+ })
26
+ it('returns staging area fixtures if commands address column 4 areas', () => {
27
+ const cutoutConfigs = getSimplestDeckConfigForProtocol(({
28
+ commands: [
29
+ {
30
+ ...RUN_TIME_COMMAND_STUB_MIXIN,
31
+ commandType: 'loadLabware',
32
+ params: {
33
+ loadName: 'fake_load_name',
34
+ location: { slotName: 'A4' },
35
+ version: 1,
36
+ namespace: 'fake_namespace',
37
+ },
38
+ },
39
+ {
40
+ ...RUN_TIME_COMMAND_STUB_MIXIN,
41
+ commandType: 'loadLabware',
42
+ params: {
43
+ loadName: 'fake_load_name',
44
+ location: { slotName: 'B4' },
45
+ version: 1,
46
+ namespace: 'fake_namespace',
47
+ },
48
+ },
49
+ {
50
+ ...RUN_TIME_COMMAND_STUB_MIXIN,
51
+ commandType: 'loadLabware',
52
+ params: {
53
+ loadName: 'fake_load_name',
54
+ location: { slotName: 'C4' },
55
+ version: 1,
56
+ namespace: 'fake_namespace',
57
+ },
58
+ },
59
+ {
60
+ ...RUN_TIME_COMMAND_STUB_MIXIN,
61
+ commandType: 'loadLabware',
62
+ params: {
63
+ loadName: 'fake_load_name',
64
+ location: { slotName: 'D4' },
65
+ version: 1,
66
+ namespace: 'fake_namespace',
67
+ },
68
+ },
69
+ ],
70
+ labware: [],
71
+ } as unknown) as CompletedProtocolAnalysis)
72
+ expect(cutoutConfigs).toEqual([
73
+ ...FLEX_SIMPLEST_DECK_CONFIG_PROTOCOL_SPEC.slice(0, 8),
74
+ {
75
+ cutoutId: 'cutoutA3',
76
+ cutoutFixtureId: 'stagingAreaRightSlot',
77
+ requiredAddressableAreas: ['A4'],
78
+ },
79
+ {
80
+ cutoutId: 'cutoutB3',
81
+ cutoutFixtureId: 'stagingAreaRightSlot',
82
+ requiredAddressableAreas: ['B4'],
83
+ },
84
+ {
85
+ cutoutId: 'cutoutC3',
86
+ cutoutFixtureId: 'stagingAreaRightSlot',
87
+ requiredAddressableAreas: ['C4'],
88
+ },
89
+ {
90
+ cutoutId: 'cutoutD3',
91
+ cutoutFixtureId: 'stagingAreaRightSlot',
92
+ requiredAddressableAreas: ['D4'],
93
+ },
94
+ ])
95
+ })
96
+ it('returns simplest cutout fixture where many are possible', () => {
97
+ const cutoutConfigs = getSimplestDeckConfigForProtocol(({
98
+ commands: [
99
+ {
100
+ ...RUN_TIME_COMMAND_STUB_MIXIN,
101
+ commandType: 'moveLabware',
102
+ params: {
103
+ newLocation: { addressableAreaName: 'gripperWasteChute' },
104
+ labwareId: 'fake_labwareId',
105
+ strategy: 'usingGripper',
106
+ },
107
+ },
108
+ ],
109
+ labware: [],
110
+ } as unknown) as CompletedProtocolAnalysis)
111
+ expect(cutoutConfigs).toEqual([
112
+ ...FLEX_SIMPLEST_DECK_CONFIG_PROTOCOL_SPEC.slice(0, 11),
113
+ {
114
+ cutoutId: 'cutoutD3',
115
+ cutoutFixtureId: 'wasteChuteRightAdapterNoCover',
116
+ requiredAddressableAreas: ['gripperWasteChute'],
117
+ },
118
+ ])
119
+ })
120
+ it('returns compatible cutout fixture where multiple addressable requirements present', () => {
121
+ const cutoutConfigs = getSimplestDeckConfigForProtocol(({
122
+ commands: [
123
+ {
124
+ ...RUN_TIME_COMMAND_STUB_MIXIN,
125
+ commandType: 'moveLabware',
126
+ params: {
127
+ newLocation: { addressableAreaName: 'gripperWasteChute' },
128
+ labwareId: 'fake_labwareId',
129
+ strategy: 'usingGripper',
130
+ },
131
+ },
132
+ {
133
+ ...RUN_TIME_COMMAND_STUB_MIXIN,
134
+ commandType: 'moveLabware',
135
+ params: {
136
+ newLocation: { addressableAreaName: 'D4' },
137
+ labwareId: 'fake_labwareId',
138
+ strategy: 'usingGripper',
139
+ },
140
+ },
141
+ ],
142
+ labware: [],
143
+ } as unknown) as CompletedProtocolAnalysis)
144
+ expect(cutoutConfigs).toEqual([
145
+ ...FLEX_SIMPLEST_DECK_CONFIG_PROTOCOL_SPEC.slice(0, 11),
146
+ {
147
+ cutoutId: 'cutoutD3',
148
+ cutoutFixtureId: 'stagingAreaSlotWithWasteChuteRightAdapterNoCover',
149
+ requiredAddressableAreas: ['gripperWasteChute', 'D4'],
150
+ },
151
+ ])
152
+ })
153
+ it('returns deck with trash in A3 when legacy trash labware is present', () => {
154
+ const cutoutConfigs = getSimplestDeckConfigForProtocol(({
155
+ commands: [],
156
+ labware: [
157
+ {
158
+ id: 'trash_id',
159
+ definitionUri: 'opentrons/opentrons_1_trash_3200ml_fixed/1',
160
+ displayName: 'Trash',
161
+ loadName: 'opentrons_1_trash_3200ml_fixed',
162
+ location: { slotName: 'A3' },
163
+ },
164
+ ],
165
+ } as unknown) as CompletedProtocolAnalysis)
166
+ expect(cutoutConfigs).toEqual([
167
+ ...FLEX_SIMPLEST_DECK_CONFIG_PROTOCOL_SPEC.slice(0, 8),
168
+ {
169
+ cutoutId: 'cutoutA3',
170
+ cutoutFixtureId: 'trashBinAdapter',
171
+ requiredAddressableAreas: ['movableTrashA3'],
172
+ },
173
+ ...FLEX_SIMPLEST_DECK_CONFIG_PROTOCOL_SPEC.slice(9),
174
+ ])
175
+ })
176
+ })
@@ -0,0 +1,25 @@
1
+ import { getVectorDifference } from '../getVectorDifference'
2
+
3
+ describe('getVectorDifference', () => {
4
+ it(`should calculate difference betwen pointA and pointB with integer values`, () => {
5
+ expect(
6
+ getVectorDifference({ x: 1, y: 2, z: 3 }, { x: 1, y: 2, z: 3 })
7
+ ).toEqual({ x: 0, y: 0, z: 0 })
8
+ })
9
+ it(`should calculate difference betwen pointA and pointB with decimal values`, () => {
10
+ expect(
11
+ getVectorDifference(
12
+ { x: 1.0, y: 2.0, z: 3.0 },
13
+ { x: 1.0, y: 2.0, z: 3.0 }
14
+ )
15
+ ).toEqual({ x: 0, y: 0, z: 0 })
16
+ })
17
+ it(`should calculate difference betwen pointA and pointB with mix of negative and positive`, () => {
18
+ expect(
19
+ getVectorDifference(
20
+ { x: -1.0, y: -2.0, z: -3.0 },
21
+ { x: 1.0, y: 2.0, z: 3.0 }
22
+ )
23
+ ).toEqual({ x: -2, y: -4, z: -6 })
24
+ })
25
+ })
@@ -0,0 +1,21 @@
1
+ import { getVectorSum } from '../getVectorSum'
2
+
3
+ describe('getVectorSum', () => {
4
+ it(`should calculate sum betwen pointA and pointB with integer values`, () => {
5
+ expect(getVectorSum({ x: 1, y: 2, z: 3 }, { x: 1, y: 2, z: 3 })).toEqual({
6
+ x: 2,
7
+ y: 4,
8
+ z: 6,
9
+ })
10
+ })
11
+ it(`should calculate sum betwen pointA and pointB with decimal values`, () => {
12
+ expect(
13
+ getVectorSum({ x: 1.0, y: 2.0, z: 3.0 }, { x: 1.0, y: 2.0, z: 3.0 })
14
+ ).toEqual({ x: 2, y: 4, z: 6 })
15
+ })
16
+ it(`should calculate sum betwen pointA and pointB with mix of negative and positive`, () => {
17
+ expect(
18
+ getVectorSum({ x: -1.0, y: -2.0, z: -3.0 }, { x: 1.0, y: 2.0, z: 3.0 })
19
+ ).toEqual({ x: 0, y: 0, z: 0 })
20
+ })
21
+ })
@@ -0,0 +1,84 @@
1
+ import { getIfConsistent, getSpacingIfUniform } from '../labwareInference'
2
+
3
+ describe('getSpacingIfUniform', () => {
4
+ const testCases = [
5
+ {
6
+ testLabel: '1 well case: return 0',
7
+ wells: [{ x: 10 }],
8
+ expected: 0,
9
+ },
10
+ {
11
+ testLabel: '2 well case',
12
+ wells: [{ x: 10 }, { x: 20 }],
13
+ expected: 10,
14
+ },
15
+ {
16
+ testLabel:
17
+ '2 well case with overlapping wells (eg, single-column labware): return 0',
18
+ wells: [{ x: 10 }, { x: 10 }],
19
+ expected: 0,
20
+ },
21
+ {
22
+ testLabel: '3 well case',
23
+ wells: [{ x: 0 }, { x: 25 }, { x: 50 }],
24
+ expected: 25,
25
+ },
26
+ {
27
+ testLabel:
28
+ '3 well case with overlapping wells (eg, single-column labware): return 0',
29
+ wells: [{ x: 10 }, { x: 10 }, { x: 10 }],
30
+ expected: 0,
31
+ },
32
+ {
33
+ testLabel: '3 well case: out of order is non-uniform, return null',
34
+ wells: [{ x: 50 }, { x: 0 }, { x: 25 }],
35
+ expected: null,
36
+ },
37
+ {
38
+ testLabel: '3 well case: non-uniform with some duplicate values',
39
+ wells: [
40
+ { x: 0 },
41
+ { x: 25 },
42
+ { x: 25, spam: 'spam' },
43
+ { x: 50 },
44
+ { x: 50, foo: 'foo' },
45
+ ],
46
+ expected: 25,
47
+ },
48
+ {
49
+ testLabel: 'returns null if wells have irregular spacing',
50
+ wells: [{ x: 10 }, { x: 20 }, { x: 21 }],
51
+ expected: null,
52
+ },
53
+ {
54
+ testLabel: 'returns 0 with no wells',
55
+ wells: [],
56
+ expected: 0,
57
+ },
58
+ ]
59
+ testCases.forEach(({ wells, expected, testLabel }) =>
60
+ it(testLabel, () =>
61
+ expect(getSpacingIfUniform(wells as any[], 'x')).toBe(expected)
62
+ )
63
+ )
64
+ })
65
+
66
+ describe('getIfConsistent', () => {
67
+ it('deep equal', () => {
68
+ const items = [
69
+ { a: 123, b: [1, 2, [3]] },
70
+ { a: 123, b: [1, 2, [3]] },
71
+ { a: 123, b: [1, 2, [3]] },
72
+ ]
73
+ expect(getIfConsistent(items)).toEqual(items[0])
74
+ })
75
+
76
+ it('deep difference', () => {
77
+ const items = [
78
+ { a: 123, b: [1, 2, [3]] },
79
+ { a: 123, b: [1, 2, [999999]] },
80
+ { a: 123, b: [1, 2, [3]] },
81
+ ]
82
+ expect(getIfConsistent(items)).toBe(null)
83
+ })
84
+ })
@@ -0,0 +1,87 @@
1
+ import { orderWells } from '../orderWells'
2
+ import type { WellOrderOption } from '../orderWells'
3
+
4
+ describe('orderWells', () => {
5
+ const orderTuples: Array<[WellOrderOption, WellOrderOption]> = [
6
+ ['t2b', 'l2r'],
7
+ ['t2b', 'r2l'],
8
+ ['b2t', 'l2r'],
9
+ ['b2t', 'r2l'],
10
+ ['l2r', 't2b'],
11
+ ['l2r', 'b2t'],
12
+ ['r2l', 't2b'],
13
+ ['r2l', 'b2t'],
14
+ ]
15
+
16
+ describe('regular labware', () => {
17
+ const regularOrdering = [
18
+ ['A1', 'B1'],
19
+ ['A2', 'B2'],
20
+ ]
21
+ const regularAnswerMap: Record<
22
+ WellOrderOption,
23
+ Partial<Record<WellOrderOption, string[]>>
24
+ > = {
25
+ t2b: {
26
+ l2r: ['A1', 'B1', 'A2', 'B2'],
27
+ r2l: ['A2', 'B2', 'A1', 'B1'],
28
+ },
29
+ b2t: {
30
+ l2r: ['B1', 'A1', 'B2', 'A2'],
31
+ r2l: ['B2', 'A2', 'B1', 'A1'],
32
+ },
33
+ l2r: {
34
+ t2b: ['A1', 'A2', 'B1', 'B2'],
35
+ b2t: ['B1', 'B2', 'A1', 'A2'],
36
+ },
37
+ r2l: {
38
+ t2b: ['A2', 'A1', 'B2', 'B1'],
39
+ b2t: ['B2', 'B1', 'A2', 'A1'],
40
+ },
41
+ }
42
+ orderTuples.forEach(tuple => {
43
+ it(`first ${tuple[0]} then ${tuple[1]}`, () => {
44
+ expect(orderWells(regularOrdering, ...tuple)).toEqual(
45
+ regularAnswerMap[tuple[0]][tuple[1]]
46
+ )
47
+ })
48
+ })
49
+ describe('irregular labware', () => {
50
+ const irregularOrdering = [
51
+ ['A1', 'B1'],
52
+ ['A2', 'B2', 'C2'],
53
+ ['A3'],
54
+ ['A4', 'B4', 'C4', 'D4'],
55
+ ]
56
+ const irregularAnswerMap: Record<
57
+ WellOrderOption,
58
+ Partial<Record<WellOrderOption, string[]>>
59
+ > = {
60
+ t2b: {
61
+ l2r: ['A1', 'B1', 'A2', 'B2', 'C2', 'A3', 'A4', 'B4', 'C4', 'D4'],
62
+ r2l: ['A4', 'B4', 'C4', 'D4', 'A3', 'A2', 'B2', 'C2', 'A1', 'B1'],
63
+ },
64
+ b2t: {
65
+ l2r: ['B1', 'A1', 'C2', 'B2', 'A2', 'A3', 'D4', 'C4', 'B4', 'A4'],
66
+ r2l: ['D4', 'C4', 'B4', 'A4', 'A3', 'C2', 'B2', 'A2', 'B1', 'A1'],
67
+ },
68
+ l2r: {
69
+ t2b: ['A1', 'A2', 'A3', 'A4', 'B1', 'B2', 'B4', 'C2', 'C4', 'D4'],
70
+ b2t: ['D4', 'C2', 'C4', 'B1', 'B2', 'B4', 'A1', 'A2', 'A3', 'A4'],
71
+ },
72
+ r2l: {
73
+ t2b: ['A4', 'A3', 'A2', 'A1', 'B4', 'B2', 'B1', 'C4', 'C2', 'D4'],
74
+ b2t: ['D4', 'C4', 'C2', 'B4', 'B2', 'B1', 'A4', 'A3', 'A2', 'A1'],
75
+ },
76
+ }
77
+
78
+ orderTuples.forEach(tuple => {
79
+ it(`first ${tuple[0]} then ${tuple[1]}`, () => {
80
+ expect(orderWells(irregularOrdering, ...tuple)).toEqual(
81
+ irregularAnswerMap[tuple[0]][tuple[1]]
82
+ )
83
+ })
84
+ })
85
+ })
86
+ })
87
+ })