|
1 | | -import flatMap from 'lodash/flatMap' |
2 | | -import uuidv1 from 'uuid/v4' |
3 | | - |
4 | | -import { |
5 | | - FLEX_ROBOT_TYPE, |
6 | | - FLEX_STANDARD_DECKID, |
7 | | - getAllLiquidClassDefs, |
8 | | - getFlexNameConversion, |
9 | | -} from '@opentrons/shared-data' |
| 1 | +import { FLEX_ROBOT_TYPE } from '@opentrons/shared-data' |
10 | 2 | import { |
11 | | - getLiquidClassName, |
12 | | - getSlotInLocationStack, |
13 | 3 | pythonImports, |
14 | 4 | pythonMetadata, |
15 | 5 | pythonRequirements, |
16 | 6 | } from '@opentrons/step-generation' |
17 | 7 |
|
18 | | -import { generateQuickTransferArgs } from './generateQuickTransferArgs' |
19 | | -import { generateQuickTransferRobotStateTimeline } from './generateQuickTransferRobotStateTimeline' |
20 | 8 | import { pythonDef } from './pythonDef' |
21 | 9 |
|
22 | | -import type { |
23 | | - CommandAnnotationV1Mixin, |
24 | | - CommandV14Mixin, |
25 | | - CreateCommand, |
26 | | - DeckConfiguration, |
27 | | - LabwareDefinition2, |
28 | | - LabwareV2Mixin, |
29 | | - LiquidV1Mixin, |
30 | | - LoadLabwareCreateCommand, |
31 | | - LoadLiquidClassCreateCommand, |
32 | | - LoadPipetteCreateCommand, |
33 | | - OT3RobotMixin, |
34 | | -} from '@opentrons/shared-data' |
| 10 | +import type { DeckConfiguration } from '@opentrons/shared-data' |
35 | 11 | import type { QuickTransferSummaryState } from '../types' |
36 | 12 |
|
37 | | -const uuid: () => string = uuidv1 |
38 | | - |
39 | | -const QUICK_TRANSFER_VERSION = '2.1.0' |
40 | | - |
41 | | -export function createQuickTransferFile( |
42 | | - quickTransferState: QuickTransferSummaryState, |
43 | | - deckConfig: DeckConfiguration, |
44 | | - protocolName?: string, |
45 | | - enableQuickTransferProtocolContentsLog?: boolean |
46 | | -): File { |
47 | | - const { stepArgs, invariantContext, initialRobotState } = |
48 | | - generateQuickTransferArgs(quickTransferState, deckConfig) |
49 | | - const pipetteEntity = Object.values(invariantContext.pipetteEntities)[0] |
50 | | - const { name, id, spec } = pipetteEntity |
51 | | - |
52 | | - const loadPipetteCommand: LoadPipetteCreateCommand = { |
53 | | - key: uuid(), |
54 | | - commandType: 'loadPipette' as const, |
55 | | - params: { |
56 | | - pipetteName: name, |
57 | | - mount: quickTransferState.mount, |
58 | | - pipetteId: id, |
59 | | - }, |
60 | | - } |
61 | | - const labwareEntities = Object.values(invariantContext.labwareEntities) |
62 | | - const loadAdapterCommands = labwareEntities.reduce< |
63 | | - LoadLabwareCreateCommand[] |
64 | | - >((acc, entity) => { |
65 | | - const { def, id } = entity |
66 | | - const isAdapter = def.allowedRoles?.includes('adapter') |
67 | | - if (!isAdapter) return acc |
68 | | - acc.push({ |
69 | | - key: uuid(), |
70 | | - commandType: 'loadLabware' as const, |
71 | | - params: { |
72 | | - displayName: def.metadata.displayName, |
73 | | - labwareId: id, |
74 | | - loadName: def.parameters.loadName, |
75 | | - namespace: def.namespace, |
76 | | - version: def.version, |
77 | | - location: { |
78 | | - slotName: getSlotInLocationStack(initialRobotState.labware[id].stack), |
79 | | - }, |
80 | | - }, |
81 | | - }) |
82 | | - return acc |
83 | | - }, []) |
84 | | - |
85 | | - const loadLabwareCommands = labwareEntities.reduce< |
86 | | - LoadLabwareCreateCommand[] |
87 | | - >((acc, entity) => { |
88 | | - const { def, id } = entity |
89 | | - const isAdapter = def.allowedRoles?.includes('adapter') |
90 | | - if (isAdapter) return acc |
91 | | - const location = initialRobotState.labware[id].stack[1] |
92 | | - const isOnAdapter = |
93 | | - loadAdapterCommands.find( |
94 | | - command => command.params.labwareId === location |
95 | | - ) != null |
96 | | - |
97 | | - acc.push({ |
98 | | - key: uuid(), |
99 | | - commandType: 'loadLabware' as const, |
100 | | - params: { |
101 | | - displayName: def.metadata.displayName, |
102 | | - labwareId: id, |
103 | | - loadName: def.parameters.loadName, |
104 | | - namespace: def.namespace, |
105 | | - version: def.version, |
106 | | - location: isOnAdapter |
107 | | - ? { labwareId: location } |
108 | | - : { slotName: location }, |
109 | | - }, |
110 | | - }) |
111 | | - return acc |
112 | | - }, []) |
113 | | - const { loadName: currentTiprackLoadName } = |
114 | | - quickTransferState.tipRack.parameters |
115 | | - |
116 | | - const liquidClass = |
117 | | - stepArgs?.liquidClass != null && stepArgs.liquidClass !== 'none' |
118 | | - ? stepArgs.liquidClass |
119 | | - : null |
120 | | - const byTipTypeSettings = |
121 | | - liquidClass != null |
122 | | - ? getAllLiquidClassDefs() |
123 | | - [liquidClass]?.byPipette.find( |
124 | | - pipetteObject => |
125 | | - pipetteObject.pipetteModel === getFlexNameConversion(spec) |
126 | | - ) |
127 | | - ?.byTipType.find(tipObject => { |
128 | | - const tiprackLoadName = tipObject.tiprack.split('/')[1] |
129 | | - return tiprackLoadName === currentTiprackLoadName |
130 | | - }) |
131 | | - : null |
132 | | - const loadLiquidCommand: LoadLiquidClassCreateCommand | null = |
133 | | - liquidClass != null && byTipTypeSettings != null |
134 | | - ? { |
135 | | - key: uuid(), |
136 | | - commandType: 'loadLiquidClass' as const, |
137 | | - params: { |
138 | | - liquidClassRecord: { |
139 | | - ...byTipTypeSettings, |
140 | | - liquidClassName: getLiquidClassName(liquidClass), |
141 | | - pipetteModel: spec.model, |
142 | | - }, |
143 | | - }, |
144 | | - } |
145 | | - : null |
146 | | - |
147 | | - const robotStateTimeline = generateQuickTransferRobotStateTimeline({ |
148 | | - stepArgs, |
149 | | - initialRobotState, |
150 | | - invariantContext, |
151 | | - }) |
152 | | - const nonLoadCommands: CreateCommand[] = flatMap( |
153 | | - robotStateTimeline.timeline, |
154 | | - timelineFrame => timelineFrame.commands |
155 | | - ) |
156 | | - const commands: CreateCommand[] = [ |
157 | | - loadPipetteCommand, |
158 | | - ...loadAdapterCommands, |
159 | | - ...loadLabwareCommands, |
160 | | - ...(loadLiquidCommand != null ? [loadLiquidCommand] : []), |
161 | | - ...nonLoadCommands, |
162 | | - ] |
163 | | - const sourceLabwareName = quickTransferState.source.metadata.displayName |
164 | | - let destinationLabwareName = sourceLabwareName |
165 | | - if (quickTransferState.destination !== 'source') { |
166 | | - destinationLabwareName = quickTransferState.destination.metadata.displayName |
167 | | - } |
168 | | - const protocolBase = { |
169 | | - $otSharedSchema: '#/protocol/schemas/8', |
170 | | - schemaVersion: 8, |
171 | | - metadata: { |
172 | | - protocolName: |
173 | | - protocolName ?? `Quick Transfer ${quickTransferState.volume}µL`, |
174 | | - description: `This quick transfer moves liquids from a ${sourceLabwareName} to a ${destinationLabwareName}`, |
175 | | - category: null, |
176 | | - subcategory: null, |
177 | | - tags: [], |
178 | | - }, |
179 | | - // see QuickTransferFlow/README.md for versioning details |
180 | | - designerApplication: { |
181 | | - name: 'opentrons/quick-transfer', |
182 | | - version: '2.0.0', |
183 | | - data: quickTransferState, |
184 | | - }, |
185 | | - } |
186 | | - const flexDeckSpec: OT3RobotMixin = { |
187 | | - robot: { |
188 | | - model: FLEX_ROBOT_TYPE, |
189 | | - deckId: FLEX_STANDARD_DECKID, |
190 | | - }, |
191 | | - } |
192 | | - |
193 | | - const labwareDefinitions = Object.values( |
194 | | - invariantContext.labwareEntities |
195 | | - ).reduce<Record<string, LabwareDefinition2>>((acc, entity) => { |
196 | | - return { ...acc, [entity.labwareDefURI]: entity.def } |
197 | | - }, {}) |
198 | | - |
199 | | - const labwareV2Mixin: LabwareV2Mixin = { |
200 | | - labwareDefinitionSchemaId: 'opentronsLabwareSchemaV2', |
201 | | - labwareDefinitions, |
202 | | - } |
203 | | - |
204 | | - const liquidV1Mixin: LiquidV1Mixin = { |
205 | | - liquidSchemaId: 'opentronsLiquidSchemaV1', |
206 | | - liquids: {}, |
207 | | - } |
208 | | - |
209 | | - const commandv8Mixin: CommandV14Mixin = { |
210 | | - commandSchemaId: 'opentronsCommandSchemaV14', |
211 | | - commands, |
212 | | - } |
213 | | - |
214 | | - const commandAnnotionaV1Mixin: CommandAnnotationV1Mixin = { |
215 | | - commandAnnotationSchemaId: 'opentronsCommandAnnotationSchemaV1', |
216 | | - commandAnnotations: [], |
217 | | - } |
218 | | - const protocolContents = JSON.stringify({ |
219 | | - ...protocolBase, |
220 | | - ...flexDeckSpec, |
221 | | - ...labwareV2Mixin, |
222 | | - ...liquidV1Mixin, |
223 | | - ...commandv8Mixin, |
224 | | - ...commandAnnotionaV1Mixin, |
225 | | - }) |
226 | | - |
227 | | - // temporary logging for debugging |
228 | | - if (enableQuickTransferProtocolContentsLog) { |
229 | | - const protocolObject = { |
230 | | - ...protocolBase, |
231 | | - ...flexDeckSpec, |
232 | | - ...labwareV2Mixin, |
233 | | - ...liquidV1Mixin, |
234 | | - ...commandv8Mixin, |
235 | | - ...commandAnnotionaV1Mixin, |
236 | | - } |
237 | | - |
238 | | - console.group('🧪 Quick Transfer Protocol Contents') |
239 | | - console.log(JSON.stringify(protocolObject, null, 2)) |
240 | | - const downloadProtocolObject = (): void => { |
241 | | - const jsonString = JSON.stringify(protocolObject, null, 2) |
242 | | - const blob = new Blob([jsonString], { type: 'application/json' }) |
243 | | - const url = URL.createObjectURL(blob) |
244 | | - const link = document.createElement('a') |
245 | | - link.href = url |
246 | | - link.download = `debug-${protocolObject.metadata.protocolName |
247 | | - .replace(/[^a-z0-9]/gi, '_') |
248 | | - .toLowerCase()}-${Date.now()}.json` |
249 | | - document.body.appendChild(link) |
250 | | - link.click() |
251 | | - document.body.removeChild(link) |
252 | | - URL.revokeObjectURL(url) |
253 | | - } |
254 | | - ;(window as any).downloadjson = downloadProtocolObject |
255 | | - console.log('💾 Or copy/paste: downloadjson()') |
256 | | - console.groupEnd() |
257 | | - } |
258 | | - |
259 | | - return new File( |
260 | | - [protocolContents], |
261 | | - `${protocolBase.metadata.protocolName}.json` |
262 | | - ) |
263 | | -} |
| 13 | +const QUICK_TRANSFER_VERSION = '2.1.1' |
264 | 14 |
|
265 | 15 | export function createQuickTransferPythonFile( |
266 | 16 | quickTransferState: QuickTransferSummaryState, |
|
0 commit comments