@opentrons/shared-data 0.1.2-alpha.2 → 0.1.2
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.
- package/package.json +1 -1
- package/js/__tests__/__snapshots__/pipettes.test.ts.snap +0 -5969
- package/js/__tests__/deckSchemas.test.ts +0 -87
- package/js/__tests__/errors.test.js +0 -21
- package/js/__tests__/getAreSlotsAdjacent.test.ts +0 -128
- package/js/__tests__/getWellNamePerMultiTip.test.ts +0 -162
- package/js/__tests__/labwareDefQuirks.test.ts +0 -38
- package/js/__tests__/labwareDefSchemaV1.test.ts +0 -82
- package/js/__tests__/labwareDefSchemaV2.test.ts +0 -298
- package/js/__tests__/moduleAccessors.test.ts +0 -50
- package/js/__tests__/moduleSpecsSchema.test.ts +0 -77
- package/js/__tests__/pipetteSchemaV2.test.ts +0 -107
- package/js/__tests__/pipetteSpecSchemas.test.ts +0 -48
- package/js/__tests__/pipettes.test.ts +0 -58
- package/js/__tests__/protocolSchemaV4.test.ts +0 -137
- package/js/__tests__/protocolSchemaV5.test.ts +0 -137
- package/js/__tests__/protocolSchemaV6.test.ts +0 -132
- package/js/__tests__/protocolSchemaV7.test.ts +0 -64
- package/js/__tests__/protocolValidation.test.ts +0 -83
- package/js/__tests__/sortWells.test.ts +0 -40
- package/js/__tests__/splitWellsOnColumn.test.ts +0 -21
- package/js/__tests__/validateErrors.test.js +0 -34
- package/js/constants.ts +0 -405
- package/js/cypressUtils.ts +0 -35
- package/js/errors.ts +0 -33
- package/js/fixtures.ts +0 -156
- package/js/getLabware.ts +0 -145
- package/js/gripper.ts +0 -29
- package/js/helpers/__tests__/getAdapterName.test.ts +0 -31
- package/js/helpers/__tests__/getDeckDefFromLoadedLabware.test.ts +0 -12
- package/js/helpers/__tests__/getSimplestFlexDeckConfig.test.ts +0 -176
- package/js/helpers/__tests__/getVectorDifference.test.ts +0 -25
- package/js/helpers/__tests__/getVectorSum.test.ts +0 -21
- package/js/helpers/__tests__/labwareInference.test.ts +0 -84
- package/js/helpers/__tests__/orderWells.test.ts +0 -87
- package/js/helpers/__tests__/parseProtocolData.test.ts +0 -165
- package/js/helpers/__tests__/volume.test.ts +0 -108
- package/js/helpers/__tests__/wellSets.test.ts +0 -352
- package/js/helpers/get96Channel384WellPlateWells.ts +0 -44
- package/js/helpers/getAddressableAreasInProtocol.ts +0 -98
- package/js/helpers/getLoadedLabwareDefinitionsByUri.ts +0 -30
- package/js/helpers/getModuleVizDims.ts +0 -118
- package/js/helpers/getOccludedSlotCountForModule.ts +0 -13
- package/js/helpers/getSimplestFlexDeckConfig.ts +0 -183
- package/js/helpers/getVectorDifference.ts +0 -12
- package/js/helpers/getVectorSum.ts +0 -12
- package/js/helpers/getWellNamePerMultiTip.ts +0 -86
- package/js/helpers/getWellTotalVolume.ts +0 -16
- package/js/helpers/index.ts +0 -348
- package/js/helpers/labwareInference.ts +0 -92
- package/js/helpers/orderWells.ts +0 -69
- package/js/helpers/parseProtocolData.ts +0 -141
- package/js/helpers/volume.ts +0 -35
- package/js/helpers/wellIsRect.ts +0 -6
- package/js/helpers/wellSets.ts +0 -155
- package/js/index.ts +0 -14
- package/js/labwareTools/__tests__/__snapshots__/createIrregularLabware.test.ts.snap +0 -3
- package/js/labwareTools/__tests__/__snapshots__/createLabware.test.ts.snap +0 -3
- package/js/labwareTools/__tests__/createDefaultDisplayName.test.ts +0 -111
- package/js/labwareTools/__tests__/createIrregularLabware.test.ts +0 -226
- package/js/labwareTools/__tests__/createLabware.test.ts +0 -147
- package/js/labwareTools/index.ts +0 -452
- package/js/modules.ts +0 -97
- package/js/pipettes.ts +0 -89
- package/js/protocols.ts +0 -290
- package/js/schema.ts +0 -80
- package/js/scripts/generateDeckLayersFromSVG.js +0 -46
- package/js/titleCase.ts +0 -30
- package/js/types.ts +0 -561
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { MOVABLE_TRASH_A3_ADDRESSABLE_AREA } from '../constants'
|
|
2
|
-
import { getAddressableAreaFromSlotId } from '../fixtures'
|
|
3
|
-
import type { AddressableAreaName } from '../../deck'
|
|
4
|
-
import type { ProtocolAnalysisOutput } from '../../protocol'
|
|
5
|
-
import type { CompletedProtocolAnalysis, DeckDefinition } from '../types'
|
|
6
|
-
|
|
7
|
-
export function getAddressableAreasInProtocol(
|
|
8
|
-
protocolAnalysis: CompletedProtocolAnalysis | ProtocolAnalysisOutput,
|
|
9
|
-
deckDef: DeckDefinition
|
|
10
|
-
): AddressableAreaName[] {
|
|
11
|
-
const { commands, labware } = protocolAnalysis
|
|
12
|
-
|
|
13
|
-
const addressableAreasFromCommands = commands.reduce<AddressableAreaName[]>(
|
|
14
|
-
(acc, command) => {
|
|
15
|
-
if (
|
|
16
|
-
command.commandType === 'moveLabware' &&
|
|
17
|
-
command.params.newLocation !== 'offDeck' &&
|
|
18
|
-
'slotName' in command.params.newLocation &&
|
|
19
|
-
!acc.includes(
|
|
20
|
-
command.params.newLocation.slotName as AddressableAreaName
|
|
21
|
-
)
|
|
22
|
-
) {
|
|
23
|
-
const addressableAreaName = getAddressableAreaFromSlotId(
|
|
24
|
-
command.params.newLocation.slotName,
|
|
25
|
-
deckDef
|
|
26
|
-
)?.id
|
|
27
|
-
|
|
28
|
-
if (addressableAreaName == null) {
|
|
29
|
-
return acc
|
|
30
|
-
} else {
|
|
31
|
-
return [...acc, addressableAreaName]
|
|
32
|
-
}
|
|
33
|
-
} else if (
|
|
34
|
-
command.commandType === 'moveLabware' &&
|
|
35
|
-
command.params.newLocation !== 'offDeck' &&
|
|
36
|
-
'addressableAreaName' in command.params.newLocation &&
|
|
37
|
-
!acc.includes(command.params.newLocation.addressableAreaName)
|
|
38
|
-
) {
|
|
39
|
-
return [...acc, command.params.newLocation.addressableAreaName]
|
|
40
|
-
} else if (
|
|
41
|
-
(command.commandType === 'loadLabware' ||
|
|
42
|
-
command.commandType === 'loadModule') &&
|
|
43
|
-
command.params.location !== 'offDeck' &&
|
|
44
|
-
'slotName' in command.params.location &&
|
|
45
|
-
!acc.includes(command.params.location.slotName as AddressableAreaName)
|
|
46
|
-
) {
|
|
47
|
-
const addressableAreaName = getAddressableAreaFromSlotId(
|
|
48
|
-
command.params.location.slotName,
|
|
49
|
-
deckDef
|
|
50
|
-
)?.id
|
|
51
|
-
|
|
52
|
-
// do not add addressable area name for legacy trash labware
|
|
53
|
-
if (
|
|
54
|
-
addressableAreaName == null ||
|
|
55
|
-
('loadName' in command.params &&
|
|
56
|
-
command.params.loadName === 'opentrons_1_trash_3200ml_fixed')
|
|
57
|
-
) {
|
|
58
|
-
return acc
|
|
59
|
-
} else {
|
|
60
|
-
return [...acc, addressableAreaName]
|
|
61
|
-
}
|
|
62
|
-
} else if (
|
|
63
|
-
command.commandType === 'loadLabware' &&
|
|
64
|
-
command.params.location !== 'offDeck' &&
|
|
65
|
-
'addressableAreaName' in command.params.location &&
|
|
66
|
-
!acc.includes(command.params.location.addressableAreaName)
|
|
67
|
-
) {
|
|
68
|
-
return [...acc, command.params.location.addressableAreaName]
|
|
69
|
-
} else if (
|
|
70
|
-
command.commandType === 'moveToAddressableArea' &&
|
|
71
|
-
!acc.includes(command.params.addressableAreaName)
|
|
72
|
-
) {
|
|
73
|
-
return [...acc, command.params.addressableAreaName]
|
|
74
|
-
} else if (
|
|
75
|
-
command.commandType === 'moveToAddressableAreaForDropTip' &&
|
|
76
|
-
!acc.includes(command.params.addressableAreaName)
|
|
77
|
-
) {
|
|
78
|
-
return [...acc, command.params.addressableAreaName]
|
|
79
|
-
} else {
|
|
80
|
-
return acc
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
[]
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
// special-case the Flex trash labware load name in A3 for back compatibility with the legacy fixed trash load labware command
|
|
87
|
-
const legacyTrashAddressableArea = labware.some(
|
|
88
|
-
({ loadName, location }) =>
|
|
89
|
-
loadName === 'opentrons_1_trash_3200ml_fixed' &&
|
|
90
|
-
location !== 'offDeck' &&
|
|
91
|
-
'slotName' in location &&
|
|
92
|
-
location.slotName === 'A3'
|
|
93
|
-
)
|
|
94
|
-
? MOVABLE_TRASH_A3_ADDRESSABLE_AREA
|
|
95
|
-
: []
|
|
96
|
-
|
|
97
|
-
return addressableAreasFromCommands.concat(legacyTrashAddressableArea)
|
|
98
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { getLabwareDefURI } from '.'
|
|
2
|
-
import type { RunTimeCommand, LabwareDefinition2 } from '..'
|
|
3
|
-
|
|
4
|
-
export interface LabwareDefinitionsByUri {
|
|
5
|
-
[defURI: string]: LabwareDefinition2
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function getLoadedLabwareDefinitionsByUri(
|
|
9
|
-
commands: RunTimeCommand[]
|
|
10
|
-
): LabwareDefinitionsByUri {
|
|
11
|
-
return commands.reduce((acc, command) => {
|
|
12
|
-
if (command.commandType === 'loadLabware') {
|
|
13
|
-
const labwareDef: LabwareDefinition2 | undefined =
|
|
14
|
-
command.result?.definition
|
|
15
|
-
if (labwareDef == null) {
|
|
16
|
-
console.warn(
|
|
17
|
-
`could not find a labware definition in the results of load labware command: ${JSON.stringify(
|
|
18
|
-
command
|
|
19
|
-
)}`
|
|
20
|
-
)
|
|
21
|
-
return acc
|
|
22
|
-
} else {
|
|
23
|
-
const definitionUri = getLabwareDefURI(labwareDef)
|
|
24
|
-
return { ...acc, [definitionUri]: labwareDef }
|
|
25
|
-
}
|
|
26
|
-
} else {
|
|
27
|
-
return acc
|
|
28
|
-
}
|
|
29
|
-
}, {})
|
|
30
|
-
}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
// NOTE: Ian 2019-10-24 these are by-eye numbers that intentionally
|
|
2
|
-
// ignore the real-life data to emphasize overhangs etc
|
|
3
|
-
import {
|
|
4
|
-
MAGNETIC_MODULE_TYPE,
|
|
5
|
-
TEMPERATURE_MODULE_TYPE,
|
|
6
|
-
THERMOCYCLER_MODULE_TYPE,
|
|
7
|
-
HEATERSHAKER_MODULE_TYPE,
|
|
8
|
-
STD_SLOT_X_DIM as SLOT_X,
|
|
9
|
-
STD_SLOT_Y_DIM as SLOT_Y,
|
|
10
|
-
STD_SLOT_DIVIDER_WIDTH as DIVIDER,
|
|
11
|
-
SPAN7_8_10_11_SLOT,
|
|
12
|
-
MAGNETIC_BLOCK_TYPE,
|
|
13
|
-
} from '../constants'
|
|
14
|
-
import { ModuleType, ModuleOrientation } from '../types'
|
|
15
|
-
// NOTE: all dims are in 'left' orientation. Rotate & transform to obtain 'right' orientation.
|
|
16
|
-
|
|
17
|
-
export interface ModuleVizDims {
|
|
18
|
-
xOffset: number
|
|
19
|
-
yOffset: number
|
|
20
|
-
xDimension: number
|
|
21
|
-
yDimension: number
|
|
22
|
-
childXOffset: number
|
|
23
|
-
childYOffset: number
|
|
24
|
-
childXDimension: number
|
|
25
|
-
childYDimension: number
|
|
26
|
-
}
|
|
27
|
-
const MODULE_VIZ_DIMS: Record<ModuleType, ModuleVizDims> = {
|
|
28
|
-
[MAGNETIC_MODULE_TYPE]: {
|
|
29
|
-
xOffset: -1 * (SLOT_X * 0.2 + DIVIDER),
|
|
30
|
-
yOffset: -1 * DIVIDER,
|
|
31
|
-
xDimension: SLOT_X * 1.2 + DIVIDER * 2,
|
|
32
|
-
yDimension: SLOT_Y + DIVIDER * 2,
|
|
33
|
-
childXOffset: 0,
|
|
34
|
-
childYOffset: 0,
|
|
35
|
-
childXDimension: SLOT_X,
|
|
36
|
-
childYDimension: SLOT_Y,
|
|
37
|
-
},
|
|
38
|
-
[TEMPERATURE_MODULE_TYPE]: {
|
|
39
|
-
xOffset: -1 * (SLOT_X * 0.4 + DIVIDER),
|
|
40
|
-
yOffset: -1 * DIVIDER,
|
|
41
|
-
xDimension: SLOT_X * 1.4 + DIVIDER * 2,
|
|
42
|
-
yDimension: SLOT_Y + DIVIDER * 2,
|
|
43
|
-
childXOffset: 0,
|
|
44
|
-
childYOffset: 0,
|
|
45
|
-
childXDimension: SLOT_X,
|
|
46
|
-
childYDimension: SLOT_Y,
|
|
47
|
-
},
|
|
48
|
-
[THERMOCYCLER_MODULE_TYPE]: {
|
|
49
|
-
xOffset: -8,
|
|
50
|
-
yOffset: 0,
|
|
51
|
-
xDimension: SLOT_X + DIVIDER + 32,
|
|
52
|
-
yDimension: SLOT_Y * 2.4,
|
|
53
|
-
childXOffset: 12,
|
|
54
|
-
childYOffset: SLOT_Y,
|
|
55
|
-
childXDimension: SLOT_X,
|
|
56
|
-
childYDimension: SLOT_Y,
|
|
57
|
-
},
|
|
58
|
-
// TODO(sh, 2022-02-28): This is stubbed out using magdeck dimensions, all of this logic should be deprecated
|
|
59
|
-
[HEATERSHAKER_MODULE_TYPE]: {
|
|
60
|
-
xOffset: -1 * (SLOT_X * 0.2 + DIVIDER),
|
|
61
|
-
yOffset: -1 * DIVIDER,
|
|
62
|
-
xDimension: SLOT_X * 1.2 + DIVIDER * 2,
|
|
63
|
-
yDimension: SLOT_Y + DIVIDER * 2,
|
|
64
|
-
childXOffset: 0,
|
|
65
|
-
childYOffset: 0,
|
|
66
|
-
childXDimension: SLOT_X,
|
|
67
|
-
childYDimension: SLOT_Y,
|
|
68
|
-
},
|
|
69
|
-
// TODO(sh, 2022-02-28): This is stubbed out using magdeck dimensions, all of this logic should be deprecated
|
|
70
|
-
[MAGNETIC_BLOCK_TYPE]: {
|
|
71
|
-
xOffset: -1 * (SLOT_X * 0.2 + DIVIDER),
|
|
72
|
-
yOffset: -1 * DIVIDER,
|
|
73
|
-
xDimension: SLOT_X * 1.2 + DIVIDER * 2,
|
|
74
|
-
yDimension: SLOT_Y + DIVIDER * 2,
|
|
75
|
-
childXOffset: 0,
|
|
76
|
-
childYOffset: 0,
|
|
77
|
-
childXDimension: SLOT_X,
|
|
78
|
-
childYDimension: SLOT_Y,
|
|
79
|
-
},
|
|
80
|
-
}
|
|
81
|
-
export const getModuleVizDims = (
|
|
82
|
-
orientation: ModuleOrientation,
|
|
83
|
-
moduleType: ModuleType
|
|
84
|
-
): ModuleVizDims => {
|
|
85
|
-
const dims = MODULE_VIZ_DIMS[moduleType]
|
|
86
|
-
if (orientation === 'left') return dims
|
|
87
|
-
// if orientation is 'right', get origins by rotating 180 about slot center
|
|
88
|
-
return {
|
|
89
|
-
...dims,
|
|
90
|
-
xOffset: SLOT_X - dims.xOffset - dims.xDimension,
|
|
91
|
-
yOffset: SLOT_Y - dims.yOffset - dims.yDimension,
|
|
92
|
-
childXOffset: SLOT_X - dims.childXOffset - dims.childXDimension,
|
|
93
|
-
childYOffset: SLOT_Y - dims.childYOffset - dims.childYDimension,
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
type DeckSlot = string
|
|
97
|
-
const LEFT_SIDE_SLOTS: DeckSlot[] = ['1', '4', '7', '10', SPAN7_8_10_11_SLOT]
|
|
98
|
-
export const inferModuleOrientationFromSlot = (
|
|
99
|
-
slot: DeckSlot
|
|
100
|
-
): ModuleOrientation => {
|
|
101
|
-
// NOTE: defaults to 'left' even for center slots and spanning slots.
|
|
102
|
-
// That's consistent with non-rotatable modules (thermocycler) being
|
|
103
|
-
// considered as 'left' all the time, like we do in MODULE_VIZ_DIMS.
|
|
104
|
-
if (!LEFT_SIDE_SLOTS.includes(slot)) {
|
|
105
|
-
return 'right'
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return 'left'
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export const inferModuleOrientationFromXCoordinate = (
|
|
112
|
-
xCoordinate: number
|
|
113
|
-
): ModuleOrientation => {
|
|
114
|
-
if (xCoordinate === 0) {
|
|
115
|
-
return 'left'
|
|
116
|
-
}
|
|
117
|
-
return 'right'
|
|
118
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { OT2_ROBOT_TYPE, THERMOCYCLER_MODULE_TYPE } from '..'
|
|
2
|
-
import type { ModuleType, RobotType } from '..'
|
|
3
|
-
|
|
4
|
-
export function getOccludedSlotCountForModule(
|
|
5
|
-
moduleType: ModuleType,
|
|
6
|
-
robotType: RobotType
|
|
7
|
-
): number {
|
|
8
|
-
if (moduleType === THERMOCYCLER_MODULE_TYPE) {
|
|
9
|
-
return robotType === OT2_ROBOT_TYPE ? 4 : 2
|
|
10
|
-
} else {
|
|
11
|
-
return 1
|
|
12
|
-
}
|
|
13
|
-
}
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
import { FLEX_ROBOT_TYPE } from '../constants'
|
|
2
|
-
import { getAddressableAreaFromSlotId } from '../fixtures'
|
|
3
|
-
import { getAddressableAreasInProtocol, getDeckDefFromRobotType } from '.'
|
|
4
|
-
|
|
5
|
-
import type { AddressableAreaName, CutoutId } from '../../deck'
|
|
6
|
-
import type { ProtocolAnalysisOutput } from '../../protocol'
|
|
7
|
-
import type {
|
|
8
|
-
CutoutConfig,
|
|
9
|
-
CutoutFixture,
|
|
10
|
-
DeckDefinition,
|
|
11
|
-
DeckConfiguration,
|
|
12
|
-
CompletedProtocolAnalysis,
|
|
13
|
-
} from '../types'
|
|
14
|
-
|
|
15
|
-
export interface CutoutConfigProtocolSpec extends CutoutConfig {
|
|
16
|
-
requiredAddressableAreas: AddressableAreaName[]
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const FLEX_SIMPLEST_DECK_CONFIG: DeckConfiguration = [
|
|
20
|
-
{ cutoutId: 'cutoutA1', cutoutFixtureId: 'singleLeftSlot' },
|
|
21
|
-
{ cutoutId: 'cutoutB1', cutoutFixtureId: 'singleLeftSlot' },
|
|
22
|
-
{ cutoutId: 'cutoutC1', cutoutFixtureId: 'singleLeftSlot' },
|
|
23
|
-
{ cutoutId: 'cutoutD1', cutoutFixtureId: 'singleLeftSlot' },
|
|
24
|
-
{ cutoutId: 'cutoutA2', cutoutFixtureId: 'singleCenterSlot' },
|
|
25
|
-
{ cutoutId: 'cutoutB2', cutoutFixtureId: 'singleCenterSlot' },
|
|
26
|
-
{ cutoutId: 'cutoutC2', cutoutFixtureId: 'singleCenterSlot' },
|
|
27
|
-
{ cutoutId: 'cutoutD2', cutoutFixtureId: 'singleCenterSlot' },
|
|
28
|
-
{ cutoutId: 'cutoutA3', cutoutFixtureId: 'singleRightSlot' },
|
|
29
|
-
{ cutoutId: 'cutoutB3', cutoutFixtureId: 'singleRightSlot' },
|
|
30
|
-
{ cutoutId: 'cutoutC3', cutoutFixtureId: 'singleRightSlot' },
|
|
31
|
-
{ cutoutId: 'cutoutD3', cutoutFixtureId: 'singleRightSlot' },
|
|
32
|
-
]
|
|
33
|
-
|
|
34
|
-
export const FLEX_SIMPLEST_DECK_CONFIG_PROTOCOL_SPEC: CutoutConfigProtocolSpec[] = FLEX_SIMPLEST_DECK_CONFIG.map(
|
|
35
|
-
config => ({ ...config, requiredAddressableAreas: [] })
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
export function getSimplestDeckConfigForProtocol(
|
|
39
|
-
protocolAnalysis: CompletedProtocolAnalysis | ProtocolAnalysisOutput | null
|
|
40
|
-
): CutoutConfigProtocolSpec[] {
|
|
41
|
-
// TODO(BC, 2023-11-06): abstract out the robot type
|
|
42
|
-
const deckDef = getDeckDefFromRobotType(FLEX_ROBOT_TYPE)
|
|
43
|
-
|
|
44
|
-
const addressableAreas =
|
|
45
|
-
protocolAnalysis != null
|
|
46
|
-
? getAddressableAreasInProtocol(protocolAnalysis, deckDef)
|
|
47
|
-
: []
|
|
48
|
-
const simplestDeckConfig = addressableAreas.reduce<
|
|
49
|
-
CutoutConfigProtocolSpec[]
|
|
50
|
-
>((acc, addressableArea) => {
|
|
51
|
-
const cutoutFixturesForAddressableArea = getCutoutFixturesForAddressableAreas(
|
|
52
|
-
[addressableArea],
|
|
53
|
-
deckDef.cutoutFixtures
|
|
54
|
-
)
|
|
55
|
-
const cutoutIdForAddressableArea = getCutoutIdForAddressableArea(
|
|
56
|
-
addressableArea,
|
|
57
|
-
cutoutFixturesForAddressableArea
|
|
58
|
-
)
|
|
59
|
-
const cutoutFixturesForCutoutId =
|
|
60
|
-
cutoutIdForAddressableArea != null
|
|
61
|
-
? getCutoutFixturesForCutoutId(
|
|
62
|
-
cutoutIdForAddressableArea,
|
|
63
|
-
deckDef.cutoutFixtures
|
|
64
|
-
)
|
|
65
|
-
: null
|
|
66
|
-
|
|
67
|
-
const existingCutoutConfig = acc.find(
|
|
68
|
-
cutoutConfig => cutoutConfig.cutoutId === cutoutIdForAddressableArea
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
if (
|
|
72
|
-
existingCutoutConfig != null &&
|
|
73
|
-
cutoutFixturesForCutoutId != null &&
|
|
74
|
-
cutoutIdForAddressableArea != null
|
|
75
|
-
) {
|
|
76
|
-
const indexOfExistingFixture = cutoutFixturesForCutoutId.findIndex(
|
|
77
|
-
({ id }) => id === existingCutoutConfig.cutoutFixtureId
|
|
78
|
-
)
|
|
79
|
-
const accIndex = acc.findIndex(
|
|
80
|
-
({ cutoutId }) => cutoutId === cutoutIdForAddressableArea
|
|
81
|
-
)
|
|
82
|
-
const previousRequiredAAs = acc[accIndex]?.requiredAddressableAreas
|
|
83
|
-
const allNextRequiredAddressableAreas = previousRequiredAAs.includes(
|
|
84
|
-
addressableArea
|
|
85
|
-
)
|
|
86
|
-
? previousRequiredAAs
|
|
87
|
-
: [...previousRequiredAAs, addressableArea]
|
|
88
|
-
const nextCompatibleCutoutFixture = getSimplestFixtureForAddressableAreas(
|
|
89
|
-
cutoutIdForAddressableArea,
|
|
90
|
-
allNextRequiredAddressableAreas,
|
|
91
|
-
cutoutFixturesForCutoutId
|
|
92
|
-
)
|
|
93
|
-
const indexOfCurrentFixture = cutoutFixturesForCutoutId.findIndex(
|
|
94
|
-
({ id }) => id === nextCompatibleCutoutFixture?.id
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
if (
|
|
98
|
-
nextCompatibleCutoutFixture != null &&
|
|
99
|
-
indexOfCurrentFixture > indexOfExistingFixture
|
|
100
|
-
) {
|
|
101
|
-
return [
|
|
102
|
-
...acc.slice(0, accIndex),
|
|
103
|
-
{
|
|
104
|
-
cutoutId: cutoutIdForAddressableArea,
|
|
105
|
-
cutoutFixtureId: nextCompatibleCutoutFixture.id,
|
|
106
|
-
requiredAddressableAreas: allNextRequiredAddressableAreas,
|
|
107
|
-
},
|
|
108
|
-
...acc.slice(accIndex + 1),
|
|
109
|
-
]
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return acc
|
|
113
|
-
}, FLEX_SIMPLEST_DECK_CONFIG_PROTOCOL_SPEC)
|
|
114
|
-
|
|
115
|
-
return simplestDeckConfig
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function getCutoutFixturesForAddressableAreas(
|
|
119
|
-
addressableAreas: AddressableAreaName[],
|
|
120
|
-
cutoutFixtures: CutoutFixture[]
|
|
121
|
-
): CutoutFixture[] {
|
|
122
|
-
return cutoutFixtures.filter(cutoutFixture =>
|
|
123
|
-
Object.values(cutoutFixture.providesAddressableAreas).some(providedAAs =>
|
|
124
|
-
addressableAreas.every(aa => providedAAs.includes(aa))
|
|
125
|
-
)
|
|
126
|
-
)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export function getCutoutFixturesForCutoutId(
|
|
130
|
-
cutoutId: CutoutId,
|
|
131
|
-
cutoutFixtures: CutoutFixture[]
|
|
132
|
-
): CutoutFixture[] {
|
|
133
|
-
return cutoutFixtures.filter(cutoutFixture =>
|
|
134
|
-
cutoutFixture.mayMountTo.some(mayMountTo => mayMountTo.includes(cutoutId))
|
|
135
|
-
)
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export function getCutoutIdForSlotName(
|
|
139
|
-
slotName: string,
|
|
140
|
-
deckDef: DeckDefinition
|
|
141
|
-
): CutoutId | null {
|
|
142
|
-
const addressableArea = getAddressableAreaFromSlotId(slotName, deckDef)
|
|
143
|
-
const cutoutIdForSlotName =
|
|
144
|
-
addressableArea != null
|
|
145
|
-
? getCutoutIdForAddressableArea(
|
|
146
|
-
addressableArea.id,
|
|
147
|
-
deckDef.cutoutFixtures
|
|
148
|
-
)
|
|
149
|
-
: null
|
|
150
|
-
|
|
151
|
-
return cutoutIdForSlotName
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export function getCutoutIdForAddressableArea(
|
|
155
|
-
addressableArea: AddressableAreaName,
|
|
156
|
-
cutoutFixtures: CutoutFixture[]
|
|
157
|
-
): CutoutId | null {
|
|
158
|
-
return cutoutFixtures.reduce<CutoutId | null>((acc, cutoutFixture) => {
|
|
159
|
-
const [cutoutId] =
|
|
160
|
-
Object.entries(
|
|
161
|
-
cutoutFixture.providesAddressableAreas
|
|
162
|
-
).find(([_cutoutId, providedAAs]) =>
|
|
163
|
-
providedAAs.includes(addressableArea)
|
|
164
|
-
) ?? []
|
|
165
|
-
return (cutoutId as CutoutId) ?? acc
|
|
166
|
-
}, null)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export function getSimplestFixtureForAddressableAreas(
|
|
170
|
-
cutoutId: CutoutId,
|
|
171
|
-
requiredAddressableAreas: AddressableAreaName[],
|
|
172
|
-
allCutoutFixtures: CutoutFixture[]
|
|
173
|
-
): CutoutFixture | null {
|
|
174
|
-
const cutoutFixturesForCutoutId = getCutoutFixturesForCutoutId(
|
|
175
|
-
cutoutId,
|
|
176
|
-
allCutoutFixtures
|
|
177
|
-
)
|
|
178
|
-
const nextCompatibleCutoutFixtures = getCutoutFixturesForAddressableAreas(
|
|
179
|
-
requiredAddressableAreas,
|
|
180
|
-
cutoutFixturesForCutoutId
|
|
181
|
-
)
|
|
182
|
-
return nextCompatibleCutoutFixtures?.[0] ?? null
|
|
183
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import range from 'lodash/range'
|
|
2
|
-
import { get96Channel384WellPlateWells } from './get96Channel384WellPlateWells'
|
|
3
|
-
import { getLabwareHasQuirk, orderWells, sortWells } from './index'
|
|
4
|
-
import type { LabwareDefinition2 } from '../types'
|
|
5
|
-
|
|
6
|
-
// TODO Ian 2018-03-13 pull pipette offsets/positions from some pipette definitions data
|
|
7
|
-
const OFFSET_8_CHANNEL = 9 // offset in mm between tips
|
|
8
|
-
|
|
9
|
-
const MULTICHANNEL_TIP_SPAN = OFFSET_8_CHANNEL * (8 - 1) // length in mm from first to last tip of multichannel
|
|
10
|
-
|
|
11
|
-
export function findWellAt(
|
|
12
|
-
labwareDef: LabwareDefinition2,
|
|
13
|
-
x: number,
|
|
14
|
-
y: number
|
|
15
|
-
): string | null | undefined {
|
|
16
|
-
return Object.keys(labwareDef.wells)
|
|
17
|
-
.sort(sortWells)
|
|
18
|
-
.find((wellName: string) => {
|
|
19
|
-
const well = labwareDef.wells[wellName]
|
|
20
|
-
|
|
21
|
-
if (well.shape === 'circular') {
|
|
22
|
-
return (
|
|
23
|
-
Math.sqrt(Math.pow(x - well.x, 2) + Math.pow(y - well.y, 2)) <
|
|
24
|
-
well.diameter / 2
|
|
25
|
-
)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Not circular, must be a rectangular well
|
|
29
|
-
// For rectangular wells, (x, y) is at the center.
|
|
30
|
-
return (
|
|
31
|
-
Math.abs(x - well.x) < well.xDimension / 2 &&
|
|
32
|
-
Math.abs(y - well.y) < well.yDimension / 2
|
|
33
|
-
)
|
|
34
|
-
})
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// "topWellName" means well at the "top" of the column we're accessing: usually A row, or B row for 384-format
|
|
38
|
-
export function getWellNamePerMultiTip(
|
|
39
|
-
labwareDef: LabwareDefinition2,
|
|
40
|
-
topWellName: string,
|
|
41
|
-
channels: 8 | 96
|
|
42
|
-
): string[] | null {
|
|
43
|
-
const topWell = labwareDef.wells[topWellName]
|
|
44
|
-
if (!topWell) {
|
|
45
|
-
console.warn(
|
|
46
|
-
`well "${topWellName}" does not exist in labware ${labwareDef?.namespace}/${labwareDef?.parameters?.loadName}, cannot getWellNamePerMultiTip`
|
|
47
|
-
)
|
|
48
|
-
return null
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const { x, y } = topWell
|
|
52
|
-
let offsetYTipPositions: number[] = range(0, 8).map(
|
|
53
|
-
tipNo => y - tipNo * OFFSET_8_CHANNEL
|
|
54
|
-
)
|
|
55
|
-
const orderedWells = orderWells(labwareDef.ordering, 't2b', 'l2r')
|
|
56
|
-
|
|
57
|
-
if (getLabwareHasQuirk(labwareDef, 'centerMultichannelOnWells')) {
|
|
58
|
-
// move multichannel up in Y by half the pipette's tip span to center it in the well
|
|
59
|
-
offsetYTipPositions = offsetYTipPositions.map(
|
|
60
|
-
tipPosY => tipPosY + MULTICHANNEL_TIP_SPAN / 2
|
|
61
|
-
)
|
|
62
|
-
}
|
|
63
|
-
// Return null for containers with any undefined wells
|
|
64
|
-
const wellsAccessed = offsetYTipPositions.reduce(
|
|
65
|
-
(acc: string[] | null, tipPosY) => {
|
|
66
|
-
const wellForTip = findWellAt(labwareDef, x, tipPosY)
|
|
67
|
-
if (acc === null || !wellForTip) {
|
|
68
|
-
return null
|
|
69
|
-
}
|
|
70
|
-
return acc.concat(wellForTip)
|
|
71
|
-
},
|
|
72
|
-
[]
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
let ninetySixChannelWells = orderedWells
|
|
76
|
-
// special casing 384 well plates to be every other well
|
|
77
|
-
// both on the x and y ases.
|
|
78
|
-
if (orderedWells.length === 384) {
|
|
79
|
-
ninetySixChannelWells = get96Channel384WellPlateWells(
|
|
80
|
-
orderedWells,
|
|
81
|
-
topWellName
|
|
82
|
-
)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return channels === 8 ? wellsAccessed : ninetySixChannelWells
|
|
86
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { LabwareDefinition2 } from '../types'
|
|
2
|
-
export const getWellTotalVolume = (
|
|
3
|
-
labwareDef: LabwareDefinition2,
|
|
4
|
-
wellName: string
|
|
5
|
-
): number | null | undefined => {
|
|
6
|
-
const well = labwareDef.wells[wellName]
|
|
7
|
-
|
|
8
|
-
if (!well) {
|
|
9
|
-
console.warn(
|
|
10
|
-
`No well "${wellName}" found for labware ${labwareDef?.namespace}/${labwareDef?.parameters?.loadName}/${labwareDef?.version}"`
|
|
11
|
-
)
|
|
12
|
-
return null
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return well.totalLiquidVolume
|
|
16
|
-
}
|