From 11c8328134998a117698085e58f1e891008c583b Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Mon, 3 May 2021 09:00:28 -0400 Subject: [PATCH] refactor(shared-data): port shared-data JS source to TypeScript (#7731) --- .eslintignore | 2 + .eslintrc.js | 2 + .flowconfig | 1 + .../src/labware/__tests__/validation.test.ts | 23 +- app-shell/tsconfig.json | 3 + app-shell/typings/opentrons__shared-data.d.ts | 6 - app/src/redux/protocol/types.js | 5 +- babel.config.js | 1 + components/flow-types/deck/Module.js.flow | 4 +- .../instrument/InstrumentDiagram.js.flow | 7 +- .../instrument/InstrumentInfo.js.flow | 10 +- .../flow-types/lists/CollapsibleItem.js.flow | 4 +- components/flow-types/lists/ListItem.js.flow | 6 +- components/flow-types/robot-types.js.flow | 6 +- .../deck/labwareInternals/StaticLabware.tsx | 2 + .../src/instrument/InstrumentDiagram.tsx | 10 +- components/src/instrument/InstrumentInfo.tsx | 13 +- components/src/instrument/PipetteSelect.tsx | 4 +- .../__tests__/PipetteSelect.test.tsx | 4 +- components/src/robot-types.ts | 7 +- components/tsconfig.json | 6 +- .../typings/opentrons__shared-data.d.ts | 135 --------- discovery-client/.gitignore | 3 +- .../index.js.flow} | 0 discovery-client/package.json | 2 +- .../src/labware-creator/fieldsToLabware.ts | 3 +- labware-library/tsconfig.json | 3 + protocol-designer/src/labware-defs/actions.js | 2 +- shared-data/.gitignore | 1 + shared-data/Makefile | 56 ++-- .../js/__tests__/deckSchemas.test.js.flow | 8 + .../getWellNamePerMultiTip.test.js.flow | 8 + .../__tests__/labwareDefQuirks.test.js.flow | 8 + .../__tests__/labwareDefSchemaV1.test.js.flow | 8 + .../__tests__/labwareDefSchemaV2.test.js.flow | 8 + .../js/__tests__/moduleAccessors.test.js.flow | 8 + .../__tests__/moduleSpecsSchema.test.js.flow | 8 + .../__tests__/pipetteSpecSchemas.test.js.flow | 8 + .../js/__tests__/pipettes.test.js.flow | 8 + .../__tests__/protocolSchemaV4.test.js.flow | 8 + .../__tests__/protocolSchemaV5.test.js.flow | 8 + .../js/__tests__/sortWells.test.js.flow | 8 + .../__tests__/splitWellsOnColumn.test.js.flow | 8 + shared-data/flow-types/js/constants.js.flow | 50 ++++ .../flow-types/js/cypressUtils.js.flow | 8 + shared-data/flow-types/js/getLabware.js.flow | 29 ++ .../js/helpers/__tests__/volume.test.js.flow | 8 + .../helpers/__tests__/wellSets.test.js.flow | 8 + .../js/helpers/getWellNamePerMultiTip.js.flow | 17 ++ .../js/helpers/getWellTotalVolume.js.flow | 12 + .../flow-types/js/helpers/index.js.flow | 48 ++++ .../flow-types/js/helpers/volume.js.flow | 19 ++ .../flow-types/js/helpers/wellIsRect.js.flow | 13 + .../flow-types/js/helpers/wellSets.js.flow | 24 ++ shared-data/flow-types/js/index.js.flow | 15 + .../createDefaultDisplayName.test.js.flow | 8 + .../createIrregularLabware.test.js.flow | 8 + .../__tests__/createLabware.test.js.flow | 8 + .../flow-types/js/labwareTools/index.js.flow | 105 +++++++ shared-data/flow-types/js/modules.js.flow | 46 +++ shared-data/flow-types/js/pipettes.js.flow | 24 ++ shared-data/flow-types/js/schema.js.flow | 97 +++++++ .../types.js => flow-types/js/types.js.flow} | 267 +++++++----------- .../protocol/flowTypes/schemaV1.js.flow | 106 +++++++ .../protocol/flowTypes/schemaV3.js.flow | 120 ++++++++ .../protocol/flowTypes/schemaV4.js.flow | 140 +++++++++ .../protocol/flowTypes/schemaV5.js.flow | 35 +++ .../protocol/flowTypes/schemaV6.js.flow | 24 ++ shared-data/flow-types/protocol/index.js.flow | 20 ++ .../protocol/types/schemaV1.js.flow | 106 +++++++ .../protocol/types/schemaV3.js.flow | 120 ++++++++ .../protocol/types/schemaV4.js.flow | 140 +++++++++ .../protocol/types/schemaV5.js.flow | 35 +++ .../protocol/types/schemaV6.js.flow | 24 ++ ...tes.test.js.snap => pipettes.test.ts.snap} | 0 ...eckSchemas.test.js => deckSchemas.test.ts} | 19 ++ ...test.js => getWellNamePerMultiTip.test.ts} | 23 +- ...uirks.test.js => labwareDefQuirks.test.ts} | 4 + ...aV1.test.js => labwareDefSchemaV1.test.ts} | 27 +- ...aV2.test.js => labwareDefSchemaV2.test.ts} | 41 +-- ...essors.test.js => moduleAccessors.test.ts} | 8 +- ...hema.test.js => moduleSpecsSchema.test.ts} | 18 +- ...mas.test.js => pipetteSpecSchemas.test.ts} | 11 +- .../{pipettes.test.js => pipettes.test.ts} | 4 +- ...emaV4.test.js => protocolSchemaV4.test.ts} | 23 +- ...emaV5.test.js => protocolSchemaV5.test.ts} | 23 +- .../{sortWells.test.js => sortWells.test.ts} | 1 - ...umn.test.js => splitWellsOnColumn.test.ts} | 0 shared-data/js/{constants.js => constants.ts} | 12 +- .../js/{cypressUtils.js => cypressUtils.ts} | 9 +- .../js/{getLabware.js => getLabware.ts} | 24 +- .../{volume.test.js => volume.test.ts} | 24 +- .../{wellSets.test.js => wellSets.test.ts} | 51 ++-- ...rMultiTip.js => getWellNamePerMultiTip.ts} | 16 +- ...llTotalVolume.js => getWellTotalVolume.ts} | 6 +- shared-data/js/helpers/{index.js => index.ts} | 26 +- .../js/helpers/{volume.js => volume.ts} | 10 +- .../helpers/{wellIsRect.js => wellIsRect.ts} | 1 - .../js/helpers/{wellSets.js => wellSets.ts} | 48 ++-- shared-data/js/{index.js => index.ts} | 2 - ...ap => createIrregularLabware.test.ts.snap} | 0 ...est.js.snap => createLabware.test.ts.snap} | 0 ...st.js => createDefaultDisplayName.test.ts} | 22 +- ...test.js => createIrregularLabware.test.ts} | 43 ++- ...eLabware.test.js => createLabware.test.ts} | 65 +++-- .../js/labwareTools/{index.js => index.ts} | 217 +++++++------- shared-data/js/{modules.js => modules.ts} | 77 ++--- shared-data/js/{pipettes.js => pipettes.ts} | 45 +-- shared-data/js/{schema.js => schema.ts} | 44 +-- shared-data/{index.d.ts => js/types.ts} | 84 ++---- shared-data/package.json | 5 +- shared-data/pipette/fixtures/name/index.js | 5 +- shared-data/protocol/README.md | 4 +- shared-data/protocol/index.js | 13 - shared-data/protocol/index.ts | 18 ++ shared-data/protocol/types/schemaV1.ts | 108 +++++++ shared-data/protocol/types/schemaV3.ts | 128 +++++++++ shared-data/protocol/types/schemaV4.ts | 144 ++++++++++ shared-data/protocol/types/schemaV5.ts | 32 +++ shared-data/protocol/types/schemaV6.ts | 20 ++ shared-data/tsconfig-data.json | 19 ++ shared-data/tsconfig.json | 10 + tsconfig.json | 3 + 123 files changed, 2826 insertions(+), 830 deletions(-) delete mode 100644 app-shell/typings/opentrons__shared-data.d.ts delete mode 100644 components/typings/opentrons__shared-data.d.ts rename discovery-client/{lib/index.flow.js => flow-types/index.js.flow} (100%) create mode 100644 shared-data/.gitignore create mode 100644 shared-data/flow-types/js/__tests__/deckSchemas.test.js.flow create mode 100644 shared-data/flow-types/js/__tests__/getWellNamePerMultiTip.test.js.flow create mode 100644 shared-data/flow-types/js/__tests__/labwareDefQuirks.test.js.flow create mode 100644 shared-data/flow-types/js/__tests__/labwareDefSchemaV1.test.js.flow create mode 100644 shared-data/flow-types/js/__tests__/labwareDefSchemaV2.test.js.flow create mode 100644 shared-data/flow-types/js/__tests__/moduleAccessors.test.js.flow create mode 100644 shared-data/flow-types/js/__tests__/moduleSpecsSchema.test.js.flow create mode 100644 shared-data/flow-types/js/__tests__/pipetteSpecSchemas.test.js.flow create mode 100644 shared-data/flow-types/js/__tests__/pipettes.test.js.flow create mode 100644 shared-data/flow-types/js/__tests__/protocolSchemaV4.test.js.flow create mode 100644 shared-data/flow-types/js/__tests__/protocolSchemaV5.test.js.flow create mode 100644 shared-data/flow-types/js/__tests__/sortWells.test.js.flow create mode 100644 shared-data/flow-types/js/__tests__/splitWellsOnColumn.test.js.flow create mode 100644 shared-data/flow-types/js/constants.js.flow create mode 100644 shared-data/flow-types/js/cypressUtils.js.flow create mode 100644 shared-data/flow-types/js/getLabware.js.flow create mode 100644 shared-data/flow-types/js/helpers/__tests__/volume.test.js.flow create mode 100644 shared-data/flow-types/js/helpers/__tests__/wellSets.test.js.flow create mode 100644 shared-data/flow-types/js/helpers/getWellNamePerMultiTip.js.flow create mode 100644 shared-data/flow-types/js/helpers/getWellTotalVolume.js.flow create mode 100644 shared-data/flow-types/js/helpers/index.js.flow create mode 100644 shared-data/flow-types/js/helpers/volume.js.flow create mode 100644 shared-data/flow-types/js/helpers/wellIsRect.js.flow create mode 100644 shared-data/flow-types/js/helpers/wellSets.js.flow create mode 100644 shared-data/flow-types/js/index.js.flow create mode 100644 shared-data/flow-types/js/labwareTools/__tests__/createDefaultDisplayName.test.js.flow create mode 100644 shared-data/flow-types/js/labwareTools/__tests__/createIrregularLabware.test.js.flow create mode 100644 shared-data/flow-types/js/labwareTools/__tests__/createLabware.test.js.flow create mode 100644 shared-data/flow-types/js/labwareTools/index.js.flow create mode 100644 shared-data/flow-types/js/modules.js.flow create mode 100644 shared-data/flow-types/js/pipettes.js.flow create mode 100644 shared-data/flow-types/js/schema.js.flow rename shared-data/{js/types.js => flow-types/js/types.js.flow} (53%) create mode 100644 shared-data/flow-types/protocol/flowTypes/schemaV1.js.flow create mode 100644 shared-data/flow-types/protocol/flowTypes/schemaV3.js.flow create mode 100644 shared-data/flow-types/protocol/flowTypes/schemaV4.js.flow create mode 100644 shared-data/flow-types/protocol/flowTypes/schemaV5.js.flow create mode 100644 shared-data/flow-types/protocol/flowTypes/schemaV6.js.flow create mode 100644 shared-data/flow-types/protocol/index.js.flow create mode 100644 shared-data/flow-types/protocol/types/schemaV1.js.flow create mode 100644 shared-data/flow-types/protocol/types/schemaV3.js.flow create mode 100644 shared-data/flow-types/protocol/types/schemaV4.js.flow create mode 100644 shared-data/flow-types/protocol/types/schemaV5.js.flow create mode 100644 shared-data/flow-types/protocol/types/schemaV6.js.flow rename shared-data/js/__tests__/__snapshots__/{pipettes.test.js.snap => pipettes.test.ts.snap} (100%) rename shared-data/js/__tests__/{deckSchemas.test.js => deckSchemas.test.ts} (99%) rename shared-data/js/__tests__/{getWellNamePerMultiTip.test.js => getWellNamePerMultiTip.test.ts} (83%) rename shared-data/js/__tests__/{labwareDefQuirks.test.js => labwareDefQuirks.test.ts} (99%) rename shared-data/js/__tests__/{labwareDefSchemaV1.test.js => labwareDefSchemaV1.test.ts} (82%) rename shared-data/js/__tests__/{labwareDefSchemaV2.test.js => labwareDefSchemaV2.test.ts} (86%) rename shared-data/js/__tests__/{moduleAccessors.test.js => moduleAccessors.test.ts} (98%) rename shared-data/js/__tests__/{moduleSpecsSchema.test.js => moduleSpecsSchema.test.ts} (91%) rename shared-data/js/__tests__/{pipetteSpecSchemas.test.js => pipetteSpecSchemas.test.ts} (86%) rename shared-data/js/__tests__/{pipettes.test.js => pipettes.test.ts} (98%) rename shared-data/js/__tests__/{protocolSchemaV4.test.js => protocolSchemaV4.test.ts} (89%) rename shared-data/js/__tests__/{protocolSchemaV5.test.js => protocolSchemaV5.test.ts} (89%) rename shared-data/js/__tests__/{sortWells.test.js => sortWells.test.ts} (98%) rename shared-data/js/__tests__/{splitWellsOnColumn.test.js => splitWellsOnColumn.test.ts} (100%) rename shared-data/js/{constants.js => constants.ts} (95%) rename shared-data/js/{cypressUtils.js => cypressUtils.ts} (84%) rename shared-data/js/{getLabware.js => getLabware.ts} (92%) rename shared-data/js/helpers/__tests__/{volume.test.js => volume.test.ts} (76%) rename shared-data/js/helpers/__tests__/{wellSets.test.js => wellSets.test.ts} (77%) rename shared-data/js/helpers/{getWellNamePerMultiTip.js => getWellNamePerMultiTip.ts} (90%) rename shared-data/js/helpers/{getWellTotalVolume.js => getWellTotalVolume.ts} (92%) rename shared-data/js/helpers/{index.js => index.ts} (94%) rename shared-data/js/helpers/{volume.js => volume.ts} (83%) rename shared-data/js/helpers/{wellIsRect.js => wellIsRect.ts} (96%) rename shared-data/js/helpers/{wellSets.js => wellSets.ts} (84%) rename shared-data/js/{index.js => index.ts} (95%) rename shared-data/js/labwareTools/__tests__/__snapshots__/{createIrregularLabware.test.js.snap => createIrregularLabware.test.ts.snap} (100%) rename shared-data/js/labwareTools/__tests__/__snapshots__/{createLabware.test.js.snap => createLabware.test.ts.snap} (100%) rename shared-data/js/labwareTools/__tests__/{createDefaultDisplayName.test.js => createDefaultDisplayName.test.ts} (84%) rename shared-data/js/labwareTools/__tests__/{createIrregularLabware.test.js => createIrregularLabware.test.ts} (91%) rename shared-data/js/labwareTools/__tests__/{createLabware.test.js => createLabware.test.ts} (79%) rename shared-data/js/labwareTools/{index.js => index.ts} (79%) rename shared-data/js/{modules.js => modules.ts} (69%) rename shared-data/js/{pipettes.js => pipettes.ts} (54%) rename shared-data/js/{schema.js => schema.ts} (66%) rename shared-data/{index.d.ts => js/types.ts} (76%) delete mode 100644 shared-data/protocol/index.js create mode 100644 shared-data/protocol/index.ts create mode 100644 shared-data/protocol/types/schemaV1.ts create mode 100644 shared-data/protocol/types/schemaV3.ts create mode 100644 shared-data/protocol/types/schemaV4.ts create mode 100644 shared-data/protocol/types/schemaV5.ts create mode 100644 shared-data/protocol/types/schemaV6.ts create mode 100644 shared-data/tsconfig-data.json create mode 100644 shared-data/tsconfig.json diff --git a/.eslintignore b/.eslintignore index dec37cf69b7..89e5119e967 100644 --- a/.eslintignore +++ b/.eslintignore @@ -10,7 +10,9 @@ shared-data/python/** # flow flow-typed/npm/** +discovery-client/flow-types/** components/flow-types/** +shared-data/flow-types/** # typescript app-shell/lib/** diff --git a/.eslintrc.js b/.eslintrc.js index 92f5ed6d453..daf265f4d5d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -66,6 +66,7 @@ module.exports = { '@typescript-eslint/prefer-nullish-coalescing': 'warn', '@typescript-eslint/prefer-optional-chain': 'warn', '@typescript-eslint/restrict-plus-operands': 'warn', + '@typescript-eslint/restrict-template-expressions': 'warn', }, }, { @@ -87,6 +88,7 @@ module.exports = { 'jest/no-disabled-tests': 'error', 'jest/consistent-test-it': 'error', '@typescript-eslint/consistent-type-assertions': 'off', + '@typescript-eslint/no-var-requires': 'off', // TODO(mc, 2021-01-29): fix these and remove warning overrides 'jest/no-deprecated-functions': 'warn', diff --git a/.flowconfig b/.flowconfig index 73cc679b000..83705fe154a 100644 --- a/.flowconfig +++ b/.flowconfig @@ -15,6 +15,7 @@ ; read type signatures from node_modules but ignore flow errors within .*/node_modules/.* /components/flow-types/.* +/shared-data/flow-types/.* [include] diff --git a/app-shell/src/labware/__tests__/validation.test.ts b/app-shell/src/labware/__tests__/validation.test.ts index 7502c1f72a0..de21b4e887b 100644 --- a/app-shell/src/labware/__tests__/validation.test.ts +++ b/app-shell/src/labware/__tests__/validation.test.ts @@ -1,10 +1,15 @@ import { validateLabwareFiles, validateNewLabwareFile } from '../validation' -import validLabwareA from '@opentrons/shared-data/labware/fixtures/2/fixture_96_plate.json' -import validLabwareB from '@opentrons/shared-data/labware/fixtures/2/fixture_12_trough.json' +import uncheckedLabwareA from '@opentrons/shared-data/labware/fixtures/2/fixture_96_plate.json' +import uncheckedLabwareB from '@opentrons/shared-data/labware/fixtures/2/fixture_12_trough.json' import type { CheckedLabwareFile } from '@opentrons/app/src/redux/custom-labware/types' +import type { LabwareDefinition2 } from '@opentrons/shared-data' + +const validLabwareA = uncheckedLabwareA as LabwareDefinition2 +const validLabwareB = uncheckedLabwareB as LabwareDefinition2 + describe('validateLabwareFiles', () => { it('handles unparseable and invalid labware files', () => { const files = [ @@ -28,8 +33,8 @@ describe('validateLabwareFiles', () => { it('handles valid labware files', () => { const files = [ - { filename: 'a.json', data: validLabwareA, modified: Date.now() }, - { filename: 'b.json', data: validLabwareB, modified: Date.now() }, + { filename: 'a.json', data: uncheckedLabwareA, modified: Date.now() }, + { filename: 'b.json', data: uncheckedLabwareB, modified: Date.now() }, ] expect(validateLabwareFiles(files)).toEqual([ @@ -50,9 +55,9 @@ describe('validateLabwareFiles', () => { it('handles non-unique labware files', () => { const files = [ - { filename: 'a.json', data: validLabwareA, modified: 3 }, - { filename: 'b.json', data: validLabwareB, modified: 2 }, - { filename: 'c.json', data: validLabwareA, modified: 1 }, + { filename: 'a.json', data: uncheckedLabwareA, modified: 3 }, + { filename: 'b.json', data: uncheckedLabwareB, modified: 2 }, + { filename: 'c.json', data: uncheckedLabwareA, modified: 1 }, ] expect(validateLabwareFiles(files)).toEqual([ @@ -100,7 +105,7 @@ describe('validateNewLabwareFile', () => { const existing: CheckedLabwareFile[] = [] const newFile = { filename: 'a.json', - data: validLabwareA, + data: uncheckedLabwareA, modified: 42, } @@ -123,7 +128,7 @@ describe('validateNewLabwareFile', () => { ] const newFile = { filename: 'a.json', - data: validLabwareA, + data: uncheckedLabwareA, modified: 21, } diff --git a/app-shell/tsconfig.json b/app-shell/tsconfig.json index 1e63d6564e6..58241ce4dd0 100644 --- a/app-shell/tsconfig.json +++ b/app-shell/tsconfig.json @@ -3,6 +3,9 @@ "references": [ { "path": "../discovery-client" + }, + { + "path": "../shared-data" } ], "compilerOptions": { diff --git a/app-shell/typings/opentrons__shared-data.d.ts b/app-shell/typings/opentrons__shared-data.d.ts deleted file mode 100644 index 640e7ab3b65..00000000000 --- a/app-shell/typings/opentrons__shared-data.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -// TODO(mc, 2021-02-17): remove this ambient declarations file for -// @opentrons/shared-data when it is rewritten in TypeScript - -declare module '@opentrons/shared-data' { - export type LabwareDefinition2 = Record -} diff --git a/app/src/redux/protocol/types.js b/app/src/redux/protocol/types.js index f6c2240e32f..727e5361e23 100644 --- a/app/src/redux/protocol/types.js +++ b/app/src/redux/protocol/types.js @@ -1,9 +1,6 @@ // @flow // protocol type defs -import type { - JsonProtocolFile, - ProtocolFileV1, -} from '@opentrons/shared-data/protocol' +import type { JsonProtocolFile, ProtocolFileV1 } from '@opentrons/shared-data' import typeof { TYPE_JSON, TYPE_PYTHON, TYPE_ZIP } from './constants' diff --git a/babel.config.js b/babel.config.js index d729ef3a050..c1a76fb5168 100644 --- a/babel.config.js +++ b/babel.config.js @@ -30,6 +30,7 @@ module.exports = { alias: { '^@opentrons/discovery-client$': `@opentrons/discovery-client/src/index.ts`, '^@opentrons/components$': `@opentrons/components/src/index.ts`, + '^@opentrons/shared-data$': `@opentrons/shared-data/js/index.ts`, }, }, ], diff --git a/components/flow-types/deck/Module.js.flow b/components/flow-types/deck/Module.js.flow index bba76a152d2..cda9fac33d6 100644 --- a/components/flow-types/deck/Module.js.flow +++ b/components/flow-types/deck/Module.js.flow @@ -23,8 +23,8 @@ export type ModuleProps = {| slot: DeckSlot, /** - * USB port details of this module - */ + * USB port detail of the connected module + */ usbInfoString?: string, |}; declare export function Module(props: ModuleProps): React$Node; diff --git a/components/flow-types/instrument/InstrumentDiagram.js.flow b/components/flow-types/instrument/InstrumentDiagram.js.flow index 2601dd4b42c..2d2e07aff8a 100644 --- a/components/flow-types/instrument/InstrumentDiagram.js.flow +++ b/components/flow-types/instrument/InstrumentDiagram.js.flow @@ -5,13 +5,10 @@ * @flow */ -import type { - PipetteNameSpecs, - PipetteModelSpecs, -} from "@opentrons/shared-data"; +import type { PipetteNameSpecs } from "@opentrons/shared-data"; import type { Mount } from "../robot-types"; export type InstrumentDiagramProps = {| - pipetteSpecs?: PipetteNameSpecs | PipetteModelSpecs | null, + pipetteSpecs?: Pick | null, className?: string, mount: Mount, |}; diff --git a/components/flow-types/instrument/InstrumentInfo.js.flow b/components/flow-types/instrument/InstrumentInfo.js.flow index caa4535a0f6..b99d1665445 100644 --- a/components/flow-types/instrument/InstrumentInfo.js.flow +++ b/components/flow-types/instrument/InstrumentInfo.js.flow @@ -6,11 +6,8 @@ */ import * as React from "react"; -import type { - PipetteNameSpecs, - PipetteModelSpecs, -} from "@opentrons/shared-data"; import type { Mount } from "../robot-types"; +import type { InstrumentDiagramProps } from "./InstrumentDiagram"; export type InstrumentInfoProps = {| /** * 'left' or 'right' @@ -40,7 +37,10 @@ export type InstrumentInfoProps = {| /** * specs of mounted pipette */ - pipetteSpecs?: PipetteModelSpecs | PipetteNameSpecs | null | void, + pipetteSpecs?: $PropertyType< + InstrumentDiagramProps, + "pipetteSpecs" + > | null | void, /** * classes to apply diff --git a/components/flow-types/lists/CollapsibleItem.js.flow b/components/flow-types/lists/CollapsibleItem.js.flow index 1b1bddae96c..02fa306bfcb 100644 --- a/components/flow-types/lists/CollapsibleItem.js.flow +++ b/components/flow-types/lists/CollapsibleItem.js.flow @@ -8,8 +8,8 @@ import * as React from "react"; export type CollapsibleItemProps = {| /** - * header - */ + * header + */ header?: string, /** diff --git a/components/flow-types/lists/ListItem.js.flow b/components/flow-types/lists/ListItem.js.flow index 18fd6fe0bc8..5dd76ff76c2 100644 --- a/components/flow-types/lists/ListItem.js.flow +++ b/components/flow-types/lists/ListItem.js.flow @@ -77,10 +77,9 @@ declare type ListItemProps = {| declare export var ListItem: React.ForwardRefExoticComponent< Pick< ListItemProps, + | "exact" | "children" | "url" - | "activeClassName" - | "exact" | "className" | "aria-describedby" | "onClick" @@ -88,8 +87,9 @@ declare export var ListItem: React.ForwardRefExoticComponent< | "onMouseLeave" | "onPointerEnter" | "onPointerLeave" - | "isDisabled" | "iconName" + | "isDisabled" + | "activeClassName" > & React.RefAttributes >; diff --git a/components/flow-types/robot-types.js.flow b/components/flow-types/robot-types.js.flow index e1eed93b8c0..58e41df8214 100644 --- a/components/flow-types/robot-types.js.flow +++ b/components/flow-types/robot-types.js.flow @@ -5,5 +5,7 @@ * @flow */ -export type Channels = 1 | 8; -export type Mount = "left" | "right"; +declare export { + PipetteChannels as Channels, + PipetteMount as Mount, +} from "@opentrons/shared-data"; diff --git a/components/src/deck/labwareInternals/StaticLabware.tsx b/components/src/deck/labwareInternals/StaticLabware.tsx index 8543083b943..c80770e26c4 100644 --- a/components/src/deck/labwareInternals/StaticLabware.tsx +++ b/components/src/deck/labwareInternals/StaticLabware.tsx @@ -22,7 +22,9 @@ const TipDecoration = React.memo(function TipDecoration(props: { well: LabwareWell }) { const { well } = props + // @ts-expect-error(mc, 2021-04-27): refine well type before accessing `diameter` if (well.diameter) { + // @ts-expect-error(mc, 2021-04-27): refine well type before accessing `diameter` const radius = well.diameter / 2 return ( diff --git a/components/src/instrument/InstrumentDiagram.tsx b/components/src/instrument/InstrumentDiagram.tsx index ad48493097a..4ecb6a6b888 100644 --- a/components/src/instrument/InstrumentDiagram.tsx +++ b/components/src/instrument/InstrumentDiagram.tsx @@ -1,19 +1,17 @@ import * as React from 'react' import cx from 'classnames' -import type { - PipetteNameSpecs, - PipetteModelSpecs, -} from '@opentrons/shared-data' -import type { Mount } from '../robot-types' import singleSrc from './pipetteSingle.png' import multiSrc from './pipetteMulti.png' import singleGEN2Src from './pipetteGEN2Single.png' import multiGEN2Src from './pipetteGEN2Multi.png' import styles from './instrument.css' +import type { PipetteNameSpecs } from '@opentrons/shared-data' +import type { Mount } from '../robot-types' + export interface InstrumentDiagramProps { - pipetteSpecs?: PipetteNameSpecs | PipetteModelSpecs | null + pipetteSpecs?: Pick | null className?: string mount: Mount } diff --git a/components/src/instrument/InstrumentInfo.tsx b/components/src/instrument/InstrumentInfo.tsx index 7e922b2b0f2..bd9bccd2f68 100644 --- a/components/src/instrument/InstrumentInfo.tsx +++ b/components/src/instrument/InstrumentInfo.tsx @@ -1,21 +1,18 @@ import * as React from 'react' import cx from 'classnames' -import type { - PipetteNameSpecs, - PipetteModelSpecs, -} from '@opentrons/shared-data' -import type { Mount } from '../robot-types' import { InfoItem } from './InfoItem' import { InstrumentDiagram } from './InstrumentDiagram' - import styles from './instrument.css' +import type { Mount } from '../robot-types' +import type { InstrumentDiagramProps } from './InstrumentDiagram' + export interface InstrumentInfoProps { /** 'left' or 'right' */ mount: Mount /** if true, show labels 'LEFT PIPETTE' / 'RIGHT PIPETTE' */ - showMountLabel?: boolean | null | undefined + showMountLabel?: boolean | null /** human-readable description, eg 'p300 Single-channel' */ description: string /** paired tiprack model */ @@ -23,7 +20,7 @@ export interface InstrumentInfoProps { /** if disabled, pipette & its info are grayed out */ isDisabled: boolean /** specs of mounted pipette */ - pipetteSpecs?: PipetteModelSpecs | PipetteNameSpecs | null | undefined + pipetteSpecs?: InstrumentDiagramProps['pipetteSpecs'] | null /** classes to apply */ className?: string /** classes to apply to the info group child */ diff --git a/components/src/instrument/PipetteSelect.tsx b/components/src/instrument/PipetteSelect.tsx index d0744eae029..08aec137101 100644 --- a/components/src/instrument/PipetteSelect.tsx +++ b/components/src/instrument/PipetteSelect.tsx @@ -34,7 +34,9 @@ export interface PipetteSelectProps { const NONE = 'None' const OPTION_NONE = { value: '', label: NONE } -const PIPETTE_SORT = ['maxVolume', 'channels'] +const PIPETTE_SORT = ['maxVolume', 'channels'] as const + +// @ts-expect-error(mc, 2021-04-27): use TS type guard for filter const allPipetteNameSpecs: PipetteNameSpecs[] = getAllPipetteNames( ...PIPETTE_SORT ) diff --git a/components/src/instrument/__tests__/PipetteSelect.test.tsx b/components/src/instrument/__tests__/PipetteSelect.test.tsx index f748cad50f0..deeb4a6f340 100644 --- a/components/src/instrument/__tests__/PipetteSelect.test.tsx +++ b/components/src/instrument/__tests__/PipetteSelect.test.tsx @@ -46,7 +46,7 @@ describe('PipetteSelect', () => { 'channels' ) .map(getPipetteNameSpecs) - .filter(Boolean) + .filter((specs): specs is PipetteNameSpecs => specs !== null) const gen2Specs = pipetteSpecs.filter(s => s.displayCategory === GEN2) const gen1Specs = pipetteSpecs.filter(s => s.displayCategory === GEN1) @@ -67,7 +67,7 @@ describe('PipetteSelect', () => { 'channels' ) .map(getPipetteNameSpecs) - .filter(Boolean) + .filter((specs): specs is PipetteNameSpecs => specs !== null) const gen2Specs = pipetteSpecs.filter(s => s.displayCategory === GEN2) const nameBlocklist = pipetteSpecs diff --git a/components/src/robot-types.ts b/components/src/robot-types.ts index 76bd4334ed7..3a32ffd3aa7 100644 --- a/components/src/robot-types.ts +++ b/components/src/robot-types.ts @@ -1,3 +1,4 @@ -export type Channels = 1 | 8 - -export type Mount = 'left' | 'right' +export type { + PipetteChannels as Channels, + PipetteMount as Mount, +} from '@opentrons/shared-data' diff --git a/components/tsconfig.json b/components/tsconfig.json index 2c33e292dbe..e7ff0df06b7 100644 --- a/components/tsconfig.json +++ b/components/tsconfig.json @@ -1,6 +1,10 @@ { "extends": "../tsconfig-base.json", - "references": [], + "references": [ + { + "path": "../shared-data" + } + ], "compilerOptions": { "composite": true, "rootDir": "src", diff --git a/components/typings/opentrons__shared-data.d.ts b/components/typings/opentrons__shared-data.d.ts deleted file mode 100644 index 6d8ec19c260..00000000000 --- a/components/typings/opentrons__shared-data.d.ts +++ /dev/null @@ -1,135 +0,0 @@ -// TODO(bc, 2021-03-03): remove/move this ambient declarations file for -// @opentrons/shared-data when it is rewritten in TypeScript - -declare module '@opentrons/shared-data' { - export type CoordinateTuple = [number, number, number] - export type UnitDirection = 1 | -1 - export type UnitVectorTuple = [UnitDirection, UnitDirection, UnitDirection] - - export interface Dimensions { - xDimension: number - yDimension: number - zDimension: number - } - - export const SLOT_RENDER_WIDTH: number // stubbed - export const SLOT_RENDER_HEIGHT: number // stubbed - - // LABWARE DEF TYPES - - export function wellIsRect(well: {}): any // stubbed - export function getWellPropsForSVGLabwareV1(def: {}): any // stubbed - export function getLabwareV1Def(type: string): any // stubbed - export type LabwareDefinition1 = Record // stubbed - export type LabwareDefinition2 = Record // stubbed - export type LabwareWell = Record // stubbed - - export interface WellDefinition { - diameter?: number // NOTE: presence of diameter indicates a circular well - depth?: number // TODO Ian 2018-03-12: depth should be required, but is missing in MALDI-plate - height: number - length: number - width: number - x: number - y: number - z: number - 'total-liquid-volume': number - } - - // PIPETTE DEF TYPES - export const GEN1: 'stub' // stubbed - export const GEN2: 'stub' // stubbed - - export type PipetteChannels = 1 | 8 - - export type PipetteDisplayCategory = typeof GEN1 | typeof GEN2 - export interface FlowRateSpec { - value: number - min: number - max: number - } - export interface PipetteNameSpecs { - name: string - displayName: string - displayCategory: PipetteDisplayCategory - minVolume: number - maxVolume: number - channels: PipetteChannels - defaultAspirateFlowRate: FlowRateSpec - defaultDispenseFlowRate: FlowRateSpec - defaultBlowOutFlowRate: FlowRateSpec - defaultTipracks: string[] - smoothieConfigs?: { - stepsPerMM: number - homePosition: number - travelDistance: number - } - } - - export type PipetteModelSpecs = Record // stubbed - export function getAllPipetteNames(...a: any[]): any // stubbed - export function getPipetteNameSpecs(s: string): any // stubbed - - // MODULE DEF TYPES - - export type ModuleModel = string // stubbed - export function getModuleDisplayName(type: string): any // stubbed - export const MAGNETIC_MODULE_V1: 'stub' // stubbed - export const MAGNETIC_MODULE_V2: 'stub' // stubbed - export const TEMPERATURE_MODULE_V1: 'stub' // stubbed - export const TEMPERATURE_MODULE_V2: 'stub' // stubbed - export const THERMOCYCLER_MODULE_V1: 'stub' // stubbed - - // DECK DEF TYPES - - export type DeckSlotId = string - - export interface DeckSlot { - id: DeckSlotId - position: CoordinateTuple - matingSurfaceUnitVector?: UnitVectorTuple - boundingBox: Dimensions - displayName: string - compatibleModules: string[] - } - export interface DeckRobot { - model: string - } - export interface DeckCalibrationPoint { - id: string - position: CoordinateTuple - displayName: string - } - export interface DeckFixture { - id: string - slot: string - labware: string - displayName: string - } - export interface DeckLocations { - orderedSlots: DeckSlot[] - calibrationPoints: DeckCalibrationPoint[] - fixtures: DeckFixture[] - } - - export interface DeckMetadata { - displayName: string - tags: string[] - } - - export interface DeckLayerFeature { - footprint: string - } - - export type DeckLayer = DeckLayerFeature[] - - export interface DeckDefinition { - otId: string - cornerOffsetFromOrigin: CoordinateTuple - dimensions: CoordinateTuple - robot: DeckRobot - locations: DeckLocations - metadata: DeckMetadata - layers: Record - } -} diff --git a/discovery-client/.gitignore b/discovery-client/.gitignore index 06f30ac5dec..a65b41774ad 100644 --- a/discovery-client/.gitignore +++ b/discovery-client/.gitignore @@ -1,2 +1 @@ -lib/* -!lib/index.flow.js +lib diff --git a/discovery-client/lib/index.flow.js b/discovery-client/flow-types/index.js.flow similarity index 100% rename from discovery-client/lib/index.flow.js rename to discovery-client/flow-types/index.js.flow diff --git a/discovery-client/package.json b/discovery-client/package.json index 102f6a5e638..599c8129a7f 100644 --- a/discovery-client/package.json +++ b/discovery-client/package.json @@ -3,7 +3,7 @@ "version": "4.3.0-beta.0", "description": "Node.js client for discovering Opentrons robots on the network", "main": "lib/index.js", - "flow:main": "lib/index.flow.js", + "flow:main": "flow-types/index.js.flow", "types": "lib/index.d.ts", "source": "src/index.ts", "bin": { diff --git a/labware-library/src/labware-creator/fieldsToLabware.ts b/labware-library/src/labware-creator/fieldsToLabware.ts index 609b34da9b8..c4ae02c1bf6 100644 --- a/labware-library/src/labware-creator/fieldsToLabware.ts +++ b/labware-library/src/labware-creator/fieldsToLabware.ts @@ -7,6 +7,7 @@ import { DISPLAY_VOLUME_UNITS } from './fields' import type { LabwareDefinition2, LabwareDisplayCategory, + LabwareWellProperties, } from '@opentrons/shared-data' import type { ProcessedLabwareFields } from './fields' @@ -43,7 +44,7 @@ export function fieldsToLabware( depth: fields.wellDepth, totalLiquidVolume, } - const wellProperties = + const wellProperties: LabwareWellProperties = fields.wellShape === 'circular' ? { ...commonWellProperties, diff --git a/labware-library/tsconfig.json b/labware-library/tsconfig.json index 34fce4bc780..cd8628020c7 100644 --- a/labware-library/tsconfig.json +++ b/labware-library/tsconfig.json @@ -3,6 +3,9 @@ "references": [ { "path": "../components" + }, + { + "path": "../shared-data" } ], "compilerOptions": { diff --git a/protocol-designer/src/labware-defs/actions.js b/protocol-designer/src/labware-defs/actions.js index 1a051eb38ea..0f14d01a9c4 100644 --- a/protocol-designer/src/labware-defs/actions.js +++ b/protocol-designer/src/labware-defs/actions.js @@ -8,9 +8,9 @@ import uniqBy from 'lodash/uniqBy' import labwareSchema from '@opentrons/shared-data/labware/schemas/2.json' import { getLabwareDefURI, + getIsTiprack, OPENTRONS_LABWARE_NAMESPACE, } from '@opentrons/shared-data' -import { getIsTiprack } from '../../../shared-data/js/getLabware' import * as labwareDefSelectors from './selectors' import { getAllWellSetsForLabware } from '../utils' import type { LabwareDefinition2 } from '@opentrons/shared-data' diff --git a/shared-data/.gitignore b/shared-data/.gitignore new file mode 100644 index 00000000000..a65b41774ad --- /dev/null +++ b/shared-data/.gitignore @@ -0,0 +1 @@ +lib diff --git a/shared-data/Makefile b/shared-data/Makefile index 5943e9e0720..c3b3b55b331 100644 --- a/shared-data/Makefile +++ b/shared-data/Makefile @@ -3,53 +3,61 @@ # using bash instead of /bin/bash in SHELL prevents macOS optimizing away our PATH update SHELL := bash -# add node_modules/.bin to PATH -PATH := $(shell cd .. && yarn bin):$(PATH) - # TODO(mc, 2018-10-25): use dist to match other projects BUILD_DIR := build +# type definitions +typedefs := $(shell yarn -s shx find "lib/**/*.d.ts") +flow_out := $(patsubst lib/%.d.ts,flow-types/%.js.flow,$(typedefs)) -####################################### +# Top level targets .PHONY: all -all: clean dist dist-py +all: clean dist -.PHONY: setup-js -setup-js: - $(MAKE) dist +.PHONY: setup +setup: setup-py setup-js -.PHONY: setup-py -setup-py: - $(MAKE) -C python setup-py +.PHONY: dist +dist: dist-js dist-py +.PHONY: clean +clean: clean-js clean-py +# JavaScript targets +.PHONY: setup-js +setup-js: dist-js -.PHONY: setup -setup: setup-py setup-js +.PHONY: dist-js +dist-js: + @yarn shx mkdir -p $(BUILD_DIR) + node js/scripts/build.js $(BUILD_DIR) +.PHONY: clean-js +clean-js: + yarn shx rm -rf $(BUILD_DIR) -.PHONY: dist -dist: - @shx mkdir -p $(BUILD_DIR) - node js/scripts/build.js $(BUILD_DIR) +.PHONY: flow-types +flow-types: $(flow_out) + +flow-types/%.js.flow: lib/%.d.ts + yarn flowgen $< --add-flow-header --interface-records --no-inexact --output-file $@ + +# Python targets +.PHONY: setup-py +setup-py: + $(MAKE) -C python setup-py .PHONY: dist-py dist-py: $(MAKE) -C python wheel - .PHONY: clean-py clean-py: $(MAKE) -C python clean - -.PHONY: clean -clean: clean-py - shx rm -rf $(BUILD_DIR) - .PHONY: teardown-py teardown-py: $(MAKE) -C python teardown @@ -62,7 +70,6 @@ lint-py: push-no-restart: $(MAKE) -C python push-no-restart - .PHONY: push push: $(MAKE) -C python push @@ -71,7 +78,6 @@ push: deploy-py: $(MAKE) -C python deploy - .PHONY: test-py test-py: $(MAKE) -C python test diff --git a/shared-data/flow-types/js/__tests__/deckSchemas.test.js.flow b/shared-data/flow-types/js/__tests__/deckSchemas.test.js.flow new file mode 100644 index 00000000000..561829e2644 --- /dev/null +++ b/shared-data/flow-types/js/__tests__/deckSchemas.test.js.flow @@ -0,0 +1,8 @@ +/** + * Flowtype definitions for deckSchemas.test + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export {}; diff --git a/shared-data/flow-types/js/__tests__/getWellNamePerMultiTip.test.js.flow b/shared-data/flow-types/js/__tests__/getWellNamePerMultiTip.test.js.flow new file mode 100644 index 00000000000..578671e38cd --- /dev/null +++ b/shared-data/flow-types/js/__tests__/getWellNamePerMultiTip.test.js.flow @@ -0,0 +1,8 @@ +/** + * Flowtype definitions for getWellNamePerMultiTip.test + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export {}; diff --git a/shared-data/flow-types/js/__tests__/labwareDefQuirks.test.js.flow b/shared-data/flow-types/js/__tests__/labwareDefQuirks.test.js.flow new file mode 100644 index 00000000000..f63ee114506 --- /dev/null +++ b/shared-data/flow-types/js/__tests__/labwareDefQuirks.test.js.flow @@ -0,0 +1,8 @@ +/** + * Flowtype definitions for labwareDefQuirks.test + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export {}; diff --git a/shared-data/flow-types/js/__tests__/labwareDefSchemaV1.test.js.flow b/shared-data/flow-types/js/__tests__/labwareDefSchemaV1.test.js.flow new file mode 100644 index 00000000000..4e6e638dbda --- /dev/null +++ b/shared-data/flow-types/js/__tests__/labwareDefSchemaV1.test.js.flow @@ -0,0 +1,8 @@ +/** + * Flowtype definitions for labwareDefSchemaV1.test + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export {}; diff --git a/shared-data/flow-types/js/__tests__/labwareDefSchemaV2.test.js.flow b/shared-data/flow-types/js/__tests__/labwareDefSchemaV2.test.js.flow new file mode 100644 index 00000000000..71707100505 --- /dev/null +++ b/shared-data/flow-types/js/__tests__/labwareDefSchemaV2.test.js.flow @@ -0,0 +1,8 @@ +/** + * Flowtype definitions for labwareDefSchemaV2.test + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export {}; diff --git a/shared-data/flow-types/js/__tests__/moduleAccessors.test.js.flow b/shared-data/flow-types/js/__tests__/moduleAccessors.test.js.flow new file mode 100644 index 00000000000..15c313c6bb7 --- /dev/null +++ b/shared-data/flow-types/js/__tests__/moduleAccessors.test.js.flow @@ -0,0 +1,8 @@ +/** + * Flowtype definitions for moduleAccessors.test + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export {}; diff --git a/shared-data/flow-types/js/__tests__/moduleSpecsSchema.test.js.flow b/shared-data/flow-types/js/__tests__/moduleSpecsSchema.test.js.flow new file mode 100644 index 00000000000..4e1567c22bd --- /dev/null +++ b/shared-data/flow-types/js/__tests__/moduleSpecsSchema.test.js.flow @@ -0,0 +1,8 @@ +/** + * Flowtype definitions for moduleSpecsSchema.test + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export {}; diff --git a/shared-data/flow-types/js/__tests__/pipetteSpecSchemas.test.js.flow b/shared-data/flow-types/js/__tests__/pipetteSpecSchemas.test.js.flow new file mode 100644 index 00000000000..52241deec75 --- /dev/null +++ b/shared-data/flow-types/js/__tests__/pipetteSpecSchemas.test.js.flow @@ -0,0 +1,8 @@ +/** + * Flowtype definitions for pipetteSpecSchemas.test + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export {}; diff --git a/shared-data/flow-types/js/__tests__/pipettes.test.js.flow b/shared-data/flow-types/js/__tests__/pipettes.test.js.flow new file mode 100644 index 00000000000..2def6ee39d0 --- /dev/null +++ b/shared-data/flow-types/js/__tests__/pipettes.test.js.flow @@ -0,0 +1,8 @@ +/** + * Flowtype definitions for pipettes.test + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export {}; diff --git a/shared-data/flow-types/js/__tests__/protocolSchemaV4.test.js.flow b/shared-data/flow-types/js/__tests__/protocolSchemaV4.test.js.flow new file mode 100644 index 00000000000..67c8220b573 --- /dev/null +++ b/shared-data/flow-types/js/__tests__/protocolSchemaV4.test.js.flow @@ -0,0 +1,8 @@ +/** + * Flowtype definitions for protocolSchemaV4.test + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export {}; diff --git a/shared-data/flow-types/js/__tests__/protocolSchemaV5.test.js.flow b/shared-data/flow-types/js/__tests__/protocolSchemaV5.test.js.flow new file mode 100644 index 00000000000..09e2f194229 --- /dev/null +++ b/shared-data/flow-types/js/__tests__/protocolSchemaV5.test.js.flow @@ -0,0 +1,8 @@ +/** + * Flowtype definitions for protocolSchemaV5.test + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export {}; diff --git a/shared-data/flow-types/js/__tests__/sortWells.test.js.flow b/shared-data/flow-types/js/__tests__/sortWells.test.js.flow new file mode 100644 index 00000000000..5cab73f0f55 --- /dev/null +++ b/shared-data/flow-types/js/__tests__/sortWells.test.js.flow @@ -0,0 +1,8 @@ +/** + * Flowtype definitions for sortWells.test + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export {}; diff --git a/shared-data/flow-types/js/__tests__/splitWellsOnColumn.test.js.flow b/shared-data/flow-types/js/__tests__/splitWellsOnColumn.test.js.flow new file mode 100644 index 00000000000..379daa8d2e8 --- /dev/null +++ b/shared-data/flow-types/js/__tests__/splitWellsOnColumn.test.js.flow @@ -0,0 +1,8 @@ +/** + * Flowtype definitions for splitWellsOnColumn.test + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export {}; diff --git a/shared-data/flow-types/js/constants.js.flow b/shared-data/flow-types/js/constants.js.flow new file mode 100644 index 00000000000..635c28e84bf --- /dev/null +++ b/shared-data/flow-types/js/constants.js.flow @@ -0,0 +1,50 @@ +/** + * Flowtype definitions for constants + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export var SLOT_LENGTH_MM: any; // 127.76 +declare export var SLOT_WIDTH_MM: any; // 85.48 +declare export var SLOT_RENDER_WIDTH: any; // 127.76 +declare export var SLOT_RENDER_HEIGHT: any; // 85.48 +declare export var FIXED_TRASH_RENDER_HEIGHT: any; // 165.86 +declare export var OPENTRONS_LABWARE_NAMESPACE: any; // "opentrons" +declare export var THERMOCYCLER: "thermocycler"; +declare export var TEMPDECK: "tempdeck"; +declare export var MAGDECK: "magdeck"; +declare export var MAGNETIC_MODULE_V1: "magneticModuleV1"; +declare export var MAGNETIC_MODULE_V2: "magneticModuleV2"; +declare export var TEMPERATURE_MODULE_V1: "temperatureModuleV1"; +declare export var TEMPERATURE_MODULE_V2: "temperatureModuleV2"; +declare export var THERMOCYCLER_MODULE_V1: "thermocyclerModuleV1"; +declare export var GEN2: "GEN2"; +declare export var GEN1: "GEN1"; +declare export var LEFT: "left"; +declare export var RIGHT: "right"; +declare export var TEMPERATURE_MODULE_TYPE: "temperatureModuleType"; +declare export var MAGNETIC_MODULE_TYPE: "magneticModuleType"; +declare export var THERMOCYCLER_MODULE_TYPE: "thermocyclerModuleType"; +declare export var MAGNETIC_MODULE_MODELS: ( + | "magneticModuleV1" + | "magneticModuleV2" +)[]; +declare export var TEMPERATURE_MODULE_MODELS: ( + | "temperatureModuleV1" + | "temperatureModuleV2" +)[]; +declare export var THERMOCYCLER_MODULE_MODELS: "thermocyclerModuleV1"[]; +declare export var MODULE_MODELS: ( + | "magneticModuleV1" + | "magneticModuleV2" + | "temperatureModuleV1" + | "temperatureModuleV2" + | "thermocyclerModuleV1" +)[]; +declare export var MODULE_TYPES: ( + | "temperatureModuleType" + | "magneticModuleType" + | "thermocyclerModuleType" +)[]; +declare export var GEN_ONE_MULTI_PIPETTES: string[]; diff --git a/shared-data/flow-types/js/cypressUtils.js.flow b/shared-data/flow-types/js/cypressUtils.js.flow new file mode 100644 index 00000000000..ce6b85ea682 --- /dev/null +++ b/shared-data/flow-types/js/cypressUtils.js.flow @@ -0,0 +1,8 @@ +/** + * Flowtype definitions for cypressUtils + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export var expectDeepEqual: (assert: any, a: any, b: any) => void; diff --git a/shared-data/flow-types/js/getLabware.js.flow b/shared-data/flow-types/js/getLabware.js.flow new file mode 100644 index 00000000000..192ba4251ee --- /dev/null +++ b/shared-data/flow-types/js/getLabware.js.flow @@ -0,0 +1,29 @@ +/** + * Flowtype definitions for getLabware + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { + LabwareDefinition1, + LabwareDefinition2, + WellDefinition, +} from "./types"; +declare export var LABWAREV2_DO_NOT_LIST: string[]; +declare export var PD_DO_NOT_LIST: string[]; +declare export function getLabwareV1Def( + labwareName: string +): LabwareDefinition1 | null | void; +declare export function getIsLabwareV1Tiprack(def: LabwareDefinition1): boolean; +declare export function getIsTiprack(labwareDef: LabwareDefinition2): boolean; +declare export function getLabwareDefaultEngageHeight( + labwareDef: LabwareDefinition2 +): number | null; + +/** + * For display. Flips Y axis to match SVG, applies offset to wells + */ +declare export function getWellPropsForSVGLabwareV1( + def: LabwareDefinition1 +): { [key: string]: WellDefinition }; diff --git a/shared-data/flow-types/js/helpers/__tests__/volume.test.js.flow b/shared-data/flow-types/js/helpers/__tests__/volume.test.js.flow new file mode 100644 index 00000000000..eb79960f35c --- /dev/null +++ b/shared-data/flow-types/js/helpers/__tests__/volume.test.js.flow @@ -0,0 +1,8 @@ +/** + * Flowtype definitions for volume.test + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export {}; diff --git a/shared-data/flow-types/js/helpers/__tests__/wellSets.test.js.flow b/shared-data/flow-types/js/helpers/__tests__/wellSets.test.js.flow new file mode 100644 index 00000000000..b5c7dadf668 --- /dev/null +++ b/shared-data/flow-types/js/helpers/__tests__/wellSets.test.js.flow @@ -0,0 +1,8 @@ +/** + * Flowtype definitions for wellSets.test + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export {}; diff --git a/shared-data/flow-types/js/helpers/getWellNamePerMultiTip.js.flow b/shared-data/flow-types/js/helpers/getWellNamePerMultiTip.js.flow new file mode 100644 index 00000000000..2fd46087eba --- /dev/null +++ b/shared-data/flow-types/js/helpers/getWellNamePerMultiTip.js.flow @@ -0,0 +1,17 @@ +/** + * Flowtype definitions for getWellNamePerMultiTip + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { LabwareDefinition2 } from "../types"; +declare export function findWellAt( + labwareDef: LabwareDefinition2, + x: number, + y: number +): string | null | void; +declare export function getWellNamePerMultiTip( + labwareDef: LabwareDefinition2, + topWellName: string +): string[] | null; diff --git a/shared-data/flow-types/js/helpers/getWellTotalVolume.js.flow b/shared-data/flow-types/js/helpers/getWellTotalVolume.js.flow new file mode 100644 index 00000000000..329661fed04 --- /dev/null +++ b/shared-data/flow-types/js/helpers/getWellTotalVolume.js.flow @@ -0,0 +1,12 @@ +/** + * Flowtype definitions for getWellTotalVolume + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { LabwareDefinition2 } from "../types"; +declare export var getWellTotalVolume: ( + labwareDef: LabwareDefinition2, + wellName: string +) => number | null | void; diff --git a/shared-data/flow-types/js/helpers/index.js.flow b/shared-data/flow-types/js/helpers/index.js.flow new file mode 100644 index 00000000000..61ee8130a7b --- /dev/null +++ b/shared-data/flow-types/js/helpers/index.js.flow @@ -0,0 +1,48 @@ +/** + * Flowtype definitions for index + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { LabwareDefinition2 } from "../types"; +declare export { getWellNamePerMultiTip } from "./getWellNamePerMultiTip"; +declare export { getWellTotalVolume } from "./getWellTotalVolume"; +declare export { wellIsRect } from "./wellIsRect"; +declare export * from "./volume" +declare export * from "./wellSets" +declare export var getLabwareDefIsStandard: ( + def: LabwareDefinition2 +) => boolean; +declare export var getLabwareDefURI: (def: LabwareDefinition2) => string; +declare export var getLabwareDisplayName: ( + labwareDef: LabwareDefinition2 +) => string; +declare export var getTiprackVolume: (labwareDef: LabwareDefinition2) => number; +declare export function getLabwareHasQuirk( + labwareDef: LabwareDefinition2, + quirk: string +): boolean; +declare export var intToAlphabetLetter: ( + i: number, + lowerCase?: boolean +) => string; +declare export var toWellName: (x: { + rowNum: number, + colNum: number, +}) => string; +/** + * A compareFunction for sorting an array of well names + * Goes down the columns (A1 to H1 on 96 plate) + * Then L to R across rows (1 to 12 on 96 plate) + */ +declare export function sortWells(a: string, b: string): number; +declare export function splitWellsOnColumn(sortedArray: string[]): string[][]; +declare export var getWellDepth: ( + labwareDef: LabwareDefinition2, + well: string +) => number; +declare export var getWellsDepth: ( + labwareDef: LabwareDefinition2, + wells: string[] +) => number; diff --git a/shared-data/flow-types/js/helpers/volume.js.flow b/shared-data/flow-types/js/helpers/volume.js.flow new file mode 100644 index 00000000000..d322497ec42 --- /dev/null +++ b/shared-data/flow-types/js/helpers/volume.js.flow @@ -0,0 +1,19 @@ +/** + * Flowtype definitions for volume + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { LabwareVolumeUnits } from "../types"; +declare export function getDisplayVolume( + volumeInMicroliters: number, + displayUnits?: LabwareVolumeUnits, + digits?: number +): string; +declare export function getAsciiVolumeUnits( + displayUnits: LabwareVolumeUnits +): string; +declare export function ensureVolumeUnits( + maybeUnits: string | null | void +): LabwareVolumeUnits; diff --git a/shared-data/flow-types/js/helpers/wellIsRect.js.flow b/shared-data/flow-types/js/helpers/wellIsRect.js.flow new file mode 100644 index 00000000000..33d20d8624f --- /dev/null +++ b/shared-data/flow-types/js/helpers/wellIsRect.js.flow @@ -0,0 +1,13 @@ +/** + * Flowtype definitions for wellIsRect + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { WellDefinition } from "../types"; + +/** + * Well is either rect or circle. Depends on whether `diameter` exists + */ +declare export function wellIsRect(wellDef: WellDefinition): boolean; diff --git a/shared-data/flow-types/js/helpers/wellSets.js.flow b/shared-data/flow-types/js/helpers/wellSets.js.flow new file mode 100644 index 00000000000..7e67c2ea50f --- /dev/null +++ b/shared-data/flow-types/js/helpers/wellSets.js.flow @@ -0,0 +1,24 @@ +/** + * Flowtype definitions for wellSets + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { LabwareDefinition2, PipetteNameSpecs } from "../types"; +declare type WellSetByPrimaryWell = string[][]; +export type WellSetHelpers = {| + getAllWellSetsForLabware: ( + labwareDef: LabwareDefinition2 + ) => WellSetByPrimaryWell, + getWellSetForMultichannel: ( + labwareDef: LabwareDefinition2, + well: string + ) => string[] | null | void, + canPipetteUseLabware: ( + pipetteSpec: PipetteNameSpecs, + labwareDef: LabwareDefinition2 + ) => boolean, +|}; +declare export var makeWellSetHelpers: () => WellSetHelpers; +declare export {}; diff --git a/shared-data/flow-types/js/index.js.flow b/shared-data/flow-types/js/index.js.flow new file mode 100644 index 00000000000..1a6ff5cc3fb --- /dev/null +++ b/shared-data/flow-types/js/index.js.flow @@ -0,0 +1,15 @@ +/** + * Flowtype definitions for index + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export * from "./constants" +declare export * from "./getLabware" +declare export * from "./helpers" +declare export * from "./pipettes" +declare export * from "./types" +declare export * from "./labwareTools" +declare export * from "./modules" +declare export * from "../protocol" diff --git a/shared-data/flow-types/js/labwareTools/__tests__/createDefaultDisplayName.test.js.flow b/shared-data/flow-types/js/labwareTools/__tests__/createDefaultDisplayName.test.js.flow new file mode 100644 index 00000000000..8a649cb3065 --- /dev/null +++ b/shared-data/flow-types/js/labwareTools/__tests__/createDefaultDisplayName.test.js.flow @@ -0,0 +1,8 @@ +/** + * Flowtype definitions for createDefaultDisplayName.test + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export {}; diff --git a/shared-data/flow-types/js/labwareTools/__tests__/createIrregularLabware.test.js.flow b/shared-data/flow-types/js/labwareTools/__tests__/createIrregularLabware.test.js.flow new file mode 100644 index 00000000000..5e5930b4aad --- /dev/null +++ b/shared-data/flow-types/js/labwareTools/__tests__/createIrregularLabware.test.js.flow @@ -0,0 +1,8 @@ +/** + * Flowtype definitions for createIrregularLabware.test + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export {}; diff --git a/shared-data/flow-types/js/labwareTools/__tests__/createLabware.test.js.flow b/shared-data/flow-types/js/labwareTools/__tests__/createLabware.test.js.flow new file mode 100644 index 00000000000..6034cd65337 --- /dev/null +++ b/shared-data/flow-types/js/labwareTools/__tests__/createLabware.test.js.flow @@ -0,0 +1,8 @@ +/** + * Flowtype definitions for createLabware.test + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export {}; diff --git a/shared-data/flow-types/js/labwareTools/index.js.flow b/shared-data/flow-types/js/labwareTools/index.js.flow new file mode 100644 index 00000000000..7e94917ec02 --- /dev/null +++ b/shared-data/flow-types/js/labwareTools/index.js.flow @@ -0,0 +1,105 @@ +/** + * Flowtype definitions for index + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { + LabwareDefinition2 as Definition, + LabwareMetadata as Metadata, + LabwareDimensions as Dimensions, + LabwareBrand as Brand, + LabwareParameters as Params, + LabwareWell as Well, + LabwareWellProperties as InputWell, + LabwareWellGroup as WellGroup, + LabwareOffset as Offset, + LabwareVolumeUnits as VolumeUnits, +} from "../types"; +declare export var DEFAULT_CUSTOM_NAMESPACE: any; // "custom_beta" +declare type Cell = {| + row: number, + column: number, +|}; +declare type GridStart = {| + rowStart: string, + colStart: string, + rowStride: number, + colStride: number, +|}; +declare type InputParams = Omit; +declare type InputWellGroup = Omit; +export type BaseLabwareProps = {| + metadata: Metadata, + parameters: InputParams, + dimensions: Dimensions, + brand?: Brand, + version?: number, + namespace?: string, + loadNamePostfix?: string[], + strict?: boolean | null, +|}; +export type RegularLabwareProps = {| + ...$Exact, + + offset: Offset, + grid: Cell, + spacing: Cell, + well: InputWell, + group?: InputWellGroup, +|}; +export type IrregularLabwareProps = {| + ...$Exact, + + offset: Offset[], + grid: Cell[], + spacing: Cell[], + well: InputWell[], + gridStart: GridStart[], + group?: InputWellGroup[], +|}; +declare export function _irregularWellName( + rowIdx: number, + colIdx: number, + gridStart: GridStart +): string; +declare export function _calculateWellCoord( + rowIdx: number, + colIdx: number, + spacing: Cell, + offset: Offset, + well: InputWell +): Well; +declare export function _generateIrregularLoadName(args: { + grid: Cell[], + well: InputWell[], + totalWellCount: number, + units: VolumeUnits, + brand: string, + displayCategory: string, + loadNamePostfix?: string[], +}): string; +declare export function determineIrregularOrdering( + wellsArray: string[] +): string[][]; +export type RegularNameProps = {| + displayCategory: string, + displayVolumeUnits: VolumeUnits, + gridRows: number, + gridColumns: number, + totalLiquidVolume: number, + brandName?: string, + loadNamePostfix?: string[], +|}; +declare export function createRegularLoadName(args: RegularNameProps): string; +declare export function createDefaultDisplayName( + args: RegularNameProps +): string; +declare export function createRegularLabware( + args: RegularLabwareProps +): Definition; +declare export function createIrregularLabware( + args: IrregularLabwareProps +): Definition; +declare export {}; diff --git a/shared-data/flow-types/js/modules.js.flow b/shared-data/flow-types/js/modules.js.flow new file mode 100644 index 00000000000..7f715aca25b --- /dev/null +++ b/shared-data/flow-types/js/modules.js.flow @@ -0,0 +1,46 @@ +/** + * Flowtype definitions for modules + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { ModuleModel, ModuleRealType, ModuleType } from "./types"; +declare type Coordinates = {| + x: number, + y: number, + z?: number, +|}; +declare type AffineTransform = [number, number, number]; +export type ModuleDef2 = {| + moduleType: ModuleRealType, + model: ModuleModel, + labwareOffset: Coordinates, + dimensions: { + bareOverallHeight: number, + overLabwareHeight: number, + lidHeight?: number, + }, + calibrationPoint: Coordinates, + displayName: string, + quirks: string[], + slotTransforms: { + [deckDef: string]: { + [slot: string]: { + [transformName: string]: AffineTransform, + }, + }, + }, + compatibleWith: ModuleModel[], +|}; +declare export var getModuleDef2: (moduleModel: ModuleModel) => ModuleDef2; +declare export function normalizeModuleModel( + legacyModule: ModuleType +): ModuleModel; +declare export function getModuleType(moduleModel: ModuleModel): ModuleRealType; +declare export function getModuleDisplayName(moduleModel: ModuleModel): string; +declare export function checkModuleCompatibility( + modelA: ModuleModel, + modelB: ModuleModel +): boolean; +declare export {}; diff --git a/shared-data/flow-types/js/pipettes.js.flow b/shared-data/flow-types/js/pipettes.js.flow new file mode 100644 index 00000000000..1bbc2d12757 --- /dev/null +++ b/shared-data/flow-types/js/pipettes.js.flow @@ -0,0 +1,24 @@ +/** + * Flowtype definitions for pipettes + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import pipetteNameSpecs from "../pipette/definitions/pipetteNameSpecs.json"; +import pipetteModelSpecs from "../pipette/definitions/pipetteModelSpecs.json"; +import type { PipetteNameSpecs, PipetteModelSpecs } from "./types"; +declare type SortableProps = "maxVolume" | "channels"; +export type PipetteName = $Keys; +export type PipetteModel = $Keys; +declare export function getPipetteNameSpecs( + name: PipetteName +): PipetteNameSpecs | null; +declare export function getPipetteModelSpecs( + model: PipetteModel +): PipetteModelSpecs | null | void; +declare export function getAllPipetteNames( + ...sortBy: SortableProps[] +): PipetteName[]; +declare export function shouldLevel(specs: PipetteNameSpecs): boolean; +declare export {}; diff --git a/shared-data/flow-types/js/schema.js.flow b/shared-data/flow-types/js/schema.js.flow new file mode 100644 index 00000000000..44a1922f268 --- /dev/null +++ b/shared-data/flow-types/js/schema.js.flow @@ -0,0 +1,97 @@ +/** + * Flowtype definitions for schema + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +declare export var labwareSchemaV1: { + type: string, + required: string[], + additionalProperties: boolean, + properties: { + metadata: { + type: string, + required: string[], + properties: { + name: { + type: string, + }, + format: { + enum: string[], + }, + deprecated: { + enum: boolean[], + }, + displayName: { + type: string, + }, + displayCategory: { + type: string, + }, + isValidSource: { + enum: boolean[], + }, + isTiprack: { + enum: boolean[], + }, + tipVolume: { + type: string, + minimum: number, + }, + }, + }, + ordering: { + type: string, + items: { + type: string, + items: { + type: string, + }, + }, + }, + wells: { + type: string, + patternProperties: { + ".*": { + type: string, + required: string[], + properties: { + height: { + type: string, + }, + length: { + type: string, + minimum: number, + }, + width: { + type: string, + minimum: number, + }, + x: { + type: string, + }, + y: { + type: string, + }, + z: { + type: string, + }, + diameter: { + type: string, + minimum: number, + }, + depth: { + type: string, + minimum: number, + }, + "total-liquid-volume": { + type: string, + minimum: number, + }, + }, + }, + }, + }, + }, +}; diff --git a/shared-data/js/types.js b/shared-data/flow-types/js/types.js.flow similarity index 53% rename from shared-data/js/types.js rename to shared-data/flow-types/js/types.js.flow index 2a40252b3d3..7ab436f3a0c 100644 --- a/shared-data/js/types.js +++ b/shared-data/flow-types/js/types.js.flow @@ -1,5 +1,11 @@ -// @flow -import typeof { +/** + * Flowtype definitions for types + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import { MAGDECK, TEMPDECK, THERMOCYCLER, @@ -13,22 +19,21 @@ import typeof { THERMOCYCLER_MODULE_TYPE, GEN1, GEN2, -} from './constants' -// TODO Ian 2019-06-04 split this out into eg ../labware/flowTypes/labwareV1.js -export type WellDefinition = { - diameter?: number, // NOTE: presence of diameter indicates a circular well - depth?: number, // TODO Ian 2018-03-12: depth should be required, but is missing in MALDI-plate + LEFT, + RIGHT, +} from "./constants"; +export type WellDefinition = {| + diameter?: number, + depth?: number, height: number, length: number, width: number, x: number, y: number, z: number, - 'total-liquid-volume': number, -} - -// typedef for labware definitions under v1 labware schema -export type LabwareDefinition1 = { + "total-liquid-volume": number, +|}; +export type LabwareDefinition1 = {| metadata: { name: string, format: string, @@ -39,52 +44,37 @@ export type LabwareDefinition1 = { isTiprack?: boolean, tipVolume?: number, }, - ordering: Array>, + ordering: string[][], wells: { [well: string]: WellDefinition, }, -} - -// TODO(mc, 2019-05-29): Remove this enum in favor of string + exported -// constants + unit tests to catch typos in our definitions. Make changes -// here and in shared-data/labware/schemas/2.json +|}; export type LabwareDisplayCategory = - | 'wellPlate' - | 'tipRack' - | 'tubeRack' - | 'reservoir' - | 'aluminumBlock' - | 'trash' - | 'other' - -export type LabwareVolumeUnits = 'µL' | 'mL' | 'L' - -// TODO(mc, 2019-05-29): Remove this enum in favor of string + exported -// constants + unit tests to catch typos in our definitions. Make changes -// here and in shared-data/labware/schemas/2.json -export type WellBottomShape = 'flat' | 'u' | 'v' - + | "wellPlate" + | "tipRack" + | "tubeRack" + | "reservoir" + | "aluminumBlock" + | "trash" + | "other"; +export type LabwareVolumeUnits = "µL" | "mL" | "L"; +export type WellBottomShape = "flat" | "u" | "v"; export type LabwareMetadata = {| displayName: string, displayCategory: LabwareDisplayCategory, displayVolumeUnits: LabwareVolumeUnits, - tags?: Array, -|} - + tags?: string[], +|}; export type LabwareDimensions = {| xDimension: number, yDimension: number, zDimension: number, -|} - +|}; export type LabwareOffset = {| x: number, y: number, z: number, -|} - -// 1. Valid pipette type for a container (i.e. is there multi channel access?) -// 2. Is the container a tiprack? +|}; export type LabwareParameters = {| loadName: string, format: string, @@ -92,59 +82,43 @@ export type LabwareParameters = {| tipLength?: number, isMagneticModuleCompatible: boolean, magneticModuleEngageHeight?: number, - quirks?: Array, -|} - + quirks?: string[], +|}; export type LabwareBrand = {| brand: string, - brandId?: Array, - links?: Array, -|} - + brandId?: string[], + links?: string[], +|}; export type LabwareWellShapeProperties = - | {| - shape: 'circular', + | { + shape: "circular", diameter: number, - |} - | {| - shape: 'rectangular', + } + | { + shape: "rectangular", xDimension: number, yDimension: number, - |} - -// well without x,y,z -export type LabwareWellProperties = {| - ...LabwareWellShapeProperties, + }; +export type LabwareWellProperties = LabwareWellShapeProperties & { depth: number, totalLiquidVolume: number, -|} - -export type LabwareWell = {| - ...LabwareWellProperties, +}; +export type LabwareWell = LabwareWellProperties & { x: number, y: number, z: number, -|} - -// TODO(mc, 2019-03-21): exact object is tough to use with the initial value in -// reduce, so leaving this inexact (e.g. `const a: {||} = {}` errors) -export type LabwareWellMap = { - [wellName: string]: LabwareWell, -} - +}; +export type LabwareWellMap = { [key: string]: LabwareWell }; export type LabwareWellGroupMetadata = {| displayName?: string, displayCategory?: LabwareDisplayCategory, wellBottomShape?: WellBottomShape, -|} - +|}; export type LabwareWellGroup = {| - wells: Array, + wells: string[], metadata: LabwareWellGroupMetadata, brand?: LabwareBrand, -|} - -// NOTE: must be synced with shared-data/labware/schemas/2.json +|}; export type LabwareDefinition2 = {| version: number, schemaVersion: 2, @@ -154,99 +128,80 @@ export type LabwareDefinition2 = {| cornerOffsetFromSlot: LabwareOffset, parameters: LabwareParameters, brand: LabwareBrand, - ordering: Array>, + ordering: string[][], wells: LabwareWellMap, - groups: Array, -|} - -// Module Type corresponds to `moduleType` key in a module definition. Is NOT model. -// TODO: IL 2020-02-20 ModuleType is DEPRECATED. Replace all instances with ModuleRealType -// (then finally rename ModuleRealType -> ModuleType) -export type ModuleType = MAGDECK | TEMPDECK | THERMOCYCLER + groups: LabwareWellGroup[], +|}; +export type ModuleType = typeof MAGDECK | typeof TEMPDECK | typeof THERMOCYCLER; export type ModuleRealType = - | MAGNETIC_MODULE_TYPE - | TEMPERATURE_MODULE_TYPE - | THERMOCYCLER_MODULE_TYPE - -// ModuleModel corresponds to top-level keys in shared-data/module/definitions/2 - -export type MagneticModuleModel = MAGNETIC_MODULE_V1 | MAGNETIC_MODULE_V2 + | typeof MAGNETIC_MODULE_TYPE + | typeof TEMPERATURE_MODULE_TYPE + | typeof THERMOCYCLER_MODULE_TYPE; +export type MagneticModuleModel = + | typeof MAGNETIC_MODULE_V1 + | typeof MAGNETIC_MODULE_V2; export type TemperatureModuleModel = - | TEMPERATURE_MODULE_V1 - | TEMPERATURE_MODULE_V2 -export type ThermocyclerModuleModel = THERMOCYCLER_MODULE_V1 - + | typeof TEMPERATURE_MODULE_V1 + | typeof TEMPERATURE_MODULE_V2; +export type ThermocyclerModuleModel = typeof THERMOCYCLER_MODULE_V1; export type ModuleModel = | MagneticModuleModel | TemperatureModuleModel - | ThermocyclerModuleModel - + | ThermocyclerModuleModel; export type ModuleModelWithLegacy = | ModuleModel - | THERMOCYCLER - | MAGDECK - | TEMPDECK - + | typeof THERMOCYCLER + | typeof MAGDECK + | typeof TEMPDECK; export type DeckOffset = {| x: number, y: number, z: number, -|} - +|}; export type Dimensions = {| xDimension: number, yDimension: number, zDimension: number, -|} - +|}; export type DeckRobot = {| model: string, -|} - +|}; export type DeckFixture = {| id: string, slot: string, labware: string, displayName: string, -|} - -export type CoordinateTuple = [number, number, number] - -export type UnitDirection = 1 | -1 -export type UnitVectorTuple = [UnitDirection, UnitDirection, UnitDirection] - -export type DeckSlotId = string - +|}; +export type CoordinateTuple = [number, number, number]; +export type UnitDirection = 1 | -1; +export type UnitVectorTuple = [UnitDirection, UnitDirection, UnitDirection]; +export type DeckSlotId = string; export type DeckSlot = {| id: DeckSlotId, position: CoordinateTuple, matingSurfaceUnitVector?: UnitVectorTuple, boundingBox: Dimensions, displayName: string, - compatibleModules: Array, -|} + compatibleModules: ModuleType[], +|}; export type DeckCalibrationPoint = {| id: string, position: CoordinateTuple, displayName: string, -|} +|}; export type DeckLocations = {| - orderedSlots: Array, - calibrationPoints: Array, - fixtures: Array, -|} - + orderedSlots: DeckSlot[], + calibrationPoints: DeckCalibrationPoint[], + fixtures: DeckFixture[], +|}; export type DeckMetadata = {| displayName: string, - tags: Array, -|} - + tags: string[], +|}; export type DeckLayerFeature = {| footprint: string, -|} - -export type DeckLayer = Array - +|}; +export type DeckLayer = DeckLayerFeature[]; export type DeckDefinition = {| otId: string, cornerOffsetFromOrigin: CoordinateTuple, @@ -254,40 +209,34 @@ export type DeckDefinition = {| robot: DeckRobot, locations: DeckLocations, metadata: DeckMetadata, - layers: { [string]: DeckLayer }, -|} - + layers: { [key: string]: DeckLayer }, +|}; export type ModuleDimensions = {| bareOverallHeight: number, overLabwareHeight: number, lidHeight: number, -|} - +|}; export type ModuleCalibrationPoint = {| x: number, y: number, z?: number, -|} - +|}; export type ModuleDefinition = {| labwareOffset: LabwareOffset, dimensions: ModuleDimensions, calibrationPoint: ModuleCalibrationPoint, displayName: string, loadName: string, - quirks: Array, -|} - -export type PipetteChannels = 1 | 8 - -export type PipetteDisplayCategory = GEN1 | GEN2 - + quirks: string[], +|}; +export type PipetteChannels = 1 | 8; +export type PipetteDisplayCategory = typeof GEN1 | typeof GEN2; +export type PipetteMount = typeof LEFT | typeof RIGHT; export type FlowRateSpec = {| value: number, min: number, max: number, -|} - +|}; export type PipetteNameSpecs = {| name: string, displayName: string, @@ -298,19 +247,19 @@ export type PipetteNameSpecs = {| defaultAspirateFlowRate: FlowRateSpec, defaultDispenseFlowRate: FlowRateSpec, defaultBlowOutFlowRate: FlowRateSpec, - defaultTipracks: Array, - smoothieConfigs?: {| + defaultTipracks: string[], + smoothieConfigs?: { stepsPerMM: number, homePosition: number, travelDistance: number, - |}, -|} + }, +|}; +export type PipetteModelSpecs = {| + ...$Exact, -// TODO(mc, 2019-10-14): update this type according to the schema -export type PipetteModelSpecs = { - ...PipetteNameSpecs, model: string, - backCompatNames?: Array, - tipLength: { value: number }, - ... -} + backCompatNames?: string[], + tipLength: { + value: number, + }, +|}; diff --git a/shared-data/flow-types/protocol/flowTypes/schemaV1.js.flow b/shared-data/flow-types/protocol/flowTypes/schemaV1.js.flow new file mode 100644 index 00000000000..4703a08abcc --- /dev/null +++ b/shared-data/flow-types/protocol/flowTypes/schemaV1.js.flow @@ -0,0 +1,106 @@ +/** + * Flowtype definitions for schemaV1 + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { DeckSlotId, PipetteMount as Mount } from "../../js/types"; +export type PipetteLabwareFields = {| + pipette: string, + labware: string, + well: string, +|}; +export type AspirateDispenseArgs = {| + ...$Exact, + + volume: number, + offsetFromBottomMm?: number | null | void, + "flow-rate"?: number | null | void, +|}; +export type Command = + | { + command: "aspirate" | "dispense", + params: AspirateDispenseArgs, + } + | { + command: "pick-up-tip" | "drop-tip" | "blowout", + params: PipetteLabwareFields, + } + | { + command: "touch-tip", + params: PipetteLabwareFields & { + offsetFromBottomMm?: number | null | void, + }, + } + | { + command: "delay", + + /** + * number of seconds to delay (fractional values OK), + * or `true` for delay until user input + */ + params: { + wait: number | true, + message: string | null | void, + }, + } + | { + command: "air-gap", + params: { + pipette: string, + volume: number, + }, + }; +declare type VersionString = string; +declare type PipetteModel = string; +declare type PipetteName = string; +export type FilePipette = {| + mount: Mount, + model: PipetteModel, + name?: PipetteName, +|}; +export type FileLabware = {| + slot: DeckSlotId, + model: string, + "display-name"?: string, +|}; +declare type FlowRateForPipettes = { [key: PipetteModel]: number }; +export type ProtocolFile = {| + "protocol-schema": VersionString, + metadata: { + "protocol-name"?: string, + author?: string, + description?: string, + created?: number, + "last-modified"?: number | null, + category?: string | null, + subcategory?: string | null, + tags?: string[], + }, + "default-values": { + "aspirate-flow-rate": FlowRateForPipettes, + "dispense-flow-rate": FlowRateForPipettes, + "aspirate-mm-from-bottom": number, + "dispense-mm-from-bottom": number, + "touch-tip-mm-from-top"?: number, + }, + "designer-application": { + "application-name": string, + "application-version": string | null | void, + data: DesignerApplicationData, + }, + robot: { + model: "OT-2 Standard", + }, + pipettes: { [key: string]: FilePipette }, + labware: { [key: string]: FileLabware }, + procedure: Array<{ + annotation: { + name: string, + description: string, + }, + subprocedure: Command[], + }>, +|}; +declare export {}; diff --git a/shared-data/flow-types/protocol/flowTypes/schemaV3.js.flow b/shared-data/flow-types/protocol/flowTypes/schemaV3.js.flow new file mode 100644 index 00000000000..0ae87a6718d --- /dev/null +++ b/shared-data/flow-types/protocol/flowTypes/schemaV3.js.flow @@ -0,0 +1,120 @@ +/** + * Flowtype definitions for schemaV3 + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { + DeckSlotId, + LabwareDefinition2, + PipetteMount as Mount, +} from "../../js/types"; +declare type PipetteName = string; +export type FilePipette = {| + mount: Mount, + name: PipetteName, +|}; +export type FileLabware = {| + slot: DeckSlotId, + definitionId: string, + displayName?: string, +|}; +declare type FlowRateParams = {| + flowRate: number, +|}; +export type PipetteAccessParams = {| + pipette: string, + labware: string, + well: string, +|}; +declare type VolumeParams = {| + volume: number, +|}; +declare type OffsetParams = {| + offsetFromBottomMm: number, +|}; +export type AspDispAirgapParams = FlowRateParams & + PipetteAccessParams & + VolumeParams & + OffsetParams; +export type AspirateParams = AspDispAirgapParams; +export type DispenseParams = AspDispAirgapParams; +export type AirGapParams = AspDispAirgapParams; +export type BlowoutParams = FlowRateParams & PipetteAccessParams & OffsetParams; +export type TouchTipParams = PipetteAccessParams & OffsetParams; +export type PickUpTipParams = PipetteAccessParams; +export type DropTipParams = PipetteAccessParams; +export type MoveToSlotParams = {| + pipette: string, + slot: string, + offset?: { + x: number, + y: number, + z: number, + }, + minimumZHeight?: number, + forceDirect?: boolean, +|}; +export type DelayParams = {| + wait: number | true, + message?: string, +|}; +export type Command = + | { + command: "aspirate" | "dispense" | "airGap", + params: AspDispAirgapParams, + } + | { + command: "blowout", + params: BlowoutParams, + } + | { + command: "touchTip", + params: TouchTipParams, + } + | { + command: "pickUpTip" | "dropTip", + params: PipetteAccessParams, + } + | { + command: "moveToSlot", + params: MoveToSlotParams, + } + | { + command: "delay", + params: DelayParams, + }; +export type ProtocolFile = {| + schemaVersion: 3, + metadata: { + protocolName?: string, + author?: string, + description?: string | null | void, + created?: number, + lastModified?: number | null | void, + category?: string | null | void, + subcategory?: string | null | void, + tags?: string[], + }, + designerApplication?: { + name?: string, + version?: string, + data?: DesignerApplicationData, + }, + robot: { + model: "OT-2 Standard", + }, + pipettes: { [key: string]: FilePipette }, + labwareDefinitions: { [key: string]: LabwareDefinition2 }, + labware: { + [key: string]: { + slot: string, + definitionId: string, + displayName?: string, + }, + }, + commands: Command[], + commandAnnotations?: { [key: string]: any }, +|}; +declare export {}; diff --git a/shared-data/flow-types/protocol/flowTypes/schemaV4.js.flow b/shared-data/flow-types/protocol/flowTypes/schemaV4.js.flow new file mode 100644 index 00000000000..a040e303f4f --- /dev/null +++ b/shared-data/flow-types/protocol/flowTypes/schemaV4.js.flow @@ -0,0 +1,140 @@ +/** + * Flowtype definitions for schemaV4 + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { DeckSlotId, ModuleModel } from "../../js/types"; +import type { + ProtocolFile as V3ProtocolFile, + AspDispAirgapParams, + BlowoutParams, + TouchTipParams, + PipetteAccessParams, + MoveToSlotParams, + DelayParams, +} from "./schemaV3"; +declare export { BlowoutParams, FilePipette, FileLabware } from "./schemaV3"; +export type FileModule = {| + slot: DeckSlotId, + model: ModuleModel, +|}; +export type EngageMagnetParams = {| + module: string, + engageHeight: number, +|}; +export type TemperatureParams = {| + module: string, + temperature: number, +|}; +export type AtomicProfileStep = {| + holdTime: number, + temperature: number, +|}; +export type TCProfileParams = {| + module: string, + profile: AtomicProfileStep[], + volume: number, +|}; +export type ModuleOnlyParams = {| + module: string, +|}; +export type ThermocyclerSetTargetBlockTemperatureArgs = {| + module: string, + temperature: number, + volume?: number, +|}; +export type Command = + | { + command: "aspirate" | "dispense" | "airGap", + params: AspDispAirgapParams, + } + | { + command: "blowout", + params: BlowoutParams, + } + | { + command: "touchTip", + params: TouchTipParams, + } + | { + command: "pickUpTip" | "dropTip", + params: PipetteAccessParams, + } + | { + command: "moveToSlot", + params: MoveToSlotParams, + } + | { + command: "delay", + params: DelayParams, + } + | { + command: "magneticModule/engageMagnet", + params: EngageMagnetParams, + } + | { + command: "magneticModule/disengageMagnet", + params: ModuleOnlyParams, + } + | { + command: "temperatureModule/setTargetTemperature", + params: TemperatureParams, + } + | { + command: "temperatureModule/deactivate", + params: ModuleOnlyParams, + } + | { + command: "temperatureModule/awaitTemperature", + params: TemperatureParams, + } + | { + command: "thermocycler/setTargetBlockTemperature", + params: ThermocyclerSetTargetBlockTemperatureArgs, + } + | { + command: "thermocycler/setTargetLidTemperature", + params: TemperatureParams, + } + | { + command: "thermocycler/awaitBlockTemperature", + params: TemperatureParams, + } + | { + command: "thermocycler/awaitLidTemperature", + params: TemperatureParams, + } + | { + command: "thermocycler/openLid", + params: ModuleOnlyParams, + } + | { + command: "thermocycler/closeLid", + params: ModuleOnlyParams, + } + | { + command: "thermocycler/deactivateBlock", + params: ModuleOnlyParams, + } + | { + command: "thermocycler/deactivateLid", + params: ModuleOnlyParams, + } + | { + command: "thermocycler/runProfile", + params: TCProfileParams, + } + | { + command: "thermocycler/awaitProfileComplete", + params: ModuleOnlyParams, + }; +export type ProtocolFile< + DesignerApplicationData +> = V3ProtocolFile & { + $otSharedSchema: "#/protocol/schemas/4", + schemaVersion: 4, + modules: { [key: string]: FileModule }, + commands: Command[], +}; diff --git a/shared-data/flow-types/protocol/flowTypes/schemaV5.js.flow b/shared-data/flow-types/protocol/flowTypes/schemaV5.js.flow new file mode 100644 index 00000000000..0825b568609 --- /dev/null +++ b/shared-data/flow-types/protocol/flowTypes/schemaV5.js.flow @@ -0,0 +1,35 @@ +/** + * Flowtype definitions for schemaV5 + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { ProtocolFile as V3ProtocolFile } from "./schemaV3"; +import type { Command as V4Command, FileModule } from "./schemaV4"; +export type MoveToWellParams = {| + pipette: string, + labware: string, + well: string, + offset?: { + x: number, + y: number, + z: number, + }, + minimumZHeight?: number, + forceDirect?: boolean, +|}; +export type Command = + | V4Command + | { + command: "moveToWell", + params: MoveToWellParams, + }; +export type ProtocolFile< + DesignerApplicationData +> = V3ProtocolFile & { + $otSharedSchema: "#/protocol/schemas/5", + schemaVersion: 5, + modules: { [key: string]: FileModule }, + commands: Command[], +}; diff --git a/shared-data/flow-types/protocol/flowTypes/schemaV6.js.flow b/shared-data/flow-types/protocol/flowTypes/schemaV6.js.flow new file mode 100644 index 00000000000..21b5b609fed --- /dev/null +++ b/shared-data/flow-types/protocol/flowTypes/schemaV6.js.flow @@ -0,0 +1,24 @@ +/** + * Flowtype definitions for schemaV6 + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { ProtocolFile as V3ProtocolFile, AirGapParams } from "./schemaV3"; +import type { FileModule } from "./schemaV4"; +import type { Command as V5Command } from "./schemaV5"; +export type Command = + | V5Command + | { + command: "dispenseAirGap", + params: AirGapParams, + }; +export type ProtocolFile< + DesignerApplicationData +> = V3ProtocolFile & { + $otSharedSchema: "#/protocol/schemas/5", + schemaVersion: 5, + modules: { [key: string]: FileModule }, + commands: Command[], +}; diff --git a/shared-data/flow-types/protocol/index.js.flow b/shared-data/flow-types/protocol/index.js.flow new file mode 100644 index 00000000000..f68c71cd134 --- /dev/null +++ b/shared-data/flow-types/protocol/index.js.flow @@ -0,0 +1,20 @@ +/** + * Flowtype definitions for index + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { ProtocolFile as _ProtocolFileV1 } from "./types/schemaV1"; +import type { ProtocolFile as _ProtocolFileV3 } from "./types/schemaV3"; +import type { ProtocolFile as _ProtocolFileV4 } from "./types/schemaV4"; +import type { ProtocolFile as _ProtocolFileV5 } from "./types/schemaV5"; +export type ProtocolFileV1 = _ProtocolFileV1; +export type ProtocolFileV3 = _ProtocolFileV3; +export type ProtocolFileV4 = _ProtocolFileV4; +export type ProtocolFileV5 = _ProtocolFileV5; +export type JsonProtocolFile = + | $ReadOnly> + | $ReadOnly> + | $ReadOnly> + | $ReadOnly>; diff --git a/shared-data/flow-types/protocol/types/schemaV1.js.flow b/shared-data/flow-types/protocol/types/schemaV1.js.flow new file mode 100644 index 00000000000..a46c929e5c1 --- /dev/null +++ b/shared-data/flow-types/protocol/types/schemaV1.js.flow @@ -0,0 +1,106 @@ +/** + * Flowtype definitions for schemaV1 + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { DeckSlotId, PipetteMount as Mount } from "../../js/types"; +export type PipetteLabwareFields = {| + pipette: string, + labware: string, + well: string, +|}; +export type AspirateDispenseArgs = {| + ...$Exact, + + volume: number, + offsetFromBottomMm?: number | null, + "flow-rate"?: number | null, +|}; +export type Command = + | { + command: "aspirate" | "dispense", + params: AspirateDispenseArgs, + } + | { + command: "pick-up-tip" | "drop-tip" | "blowout", + params: PipetteLabwareFields, + } + | { + command: "touch-tip", + params: PipetteLabwareFields & { + offsetFromBottomMm?: number | null, + }, + } + | { + command: "delay", + + /** + * number of seconds to delay (fractional values OK), + * or `true` for delay until user input + */ + params: { + wait: number | true, + message: string | null | void, + }, + } + | { + command: "air-gap", + params: { + pipette: string, + volume: number, + }, + }; +declare type VersionString = string; +declare type PipetteModel = string; +declare type PipetteName = string; +export type FilePipette = {| + mount: Mount, + model: PipetteModel, + name?: PipetteName, +|}; +export type FileLabware = {| + slot: DeckSlotId, + model: string, + "display-name"?: string, +|}; +declare type FlowRateForPipettes = { [key: PipetteModel]: number }; +export type ProtocolFile = {| + "protocol-schema": VersionString, + metadata: { + "protocol-name"?: string, + author?: string, + description?: string, + created?: number, + "last-modified"?: number | null, + category?: string | null, + subcategory?: string | null, + tags?: string[], + }, + "default-values": { + "aspirate-flow-rate": FlowRateForPipettes, + "dispense-flow-rate": FlowRateForPipettes, + "aspirate-mm-from-bottom": number, + "dispense-mm-from-bottom": number, + "touch-tip-mm-from-top"?: number, + }, + "designer-application": { + "application-name": string, + "application-version": string | null | void, + data: DesignerApplicationData, + }, + robot: { + model: "OT-2 Standard", + }, + pipettes: { [key: string]: FilePipette }, + labware: { [key: string]: FileLabware }, + procedure: Array<{ + annotation: { + name: string, + description: string, + }, + subprocedure: Command[], + }>, +|}; +declare export {}; diff --git a/shared-data/flow-types/protocol/types/schemaV3.js.flow b/shared-data/flow-types/protocol/types/schemaV3.js.flow new file mode 100644 index 00000000000..0ae87a6718d --- /dev/null +++ b/shared-data/flow-types/protocol/types/schemaV3.js.flow @@ -0,0 +1,120 @@ +/** + * Flowtype definitions for schemaV3 + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { + DeckSlotId, + LabwareDefinition2, + PipetteMount as Mount, +} from "../../js/types"; +declare type PipetteName = string; +export type FilePipette = {| + mount: Mount, + name: PipetteName, +|}; +export type FileLabware = {| + slot: DeckSlotId, + definitionId: string, + displayName?: string, +|}; +declare type FlowRateParams = {| + flowRate: number, +|}; +export type PipetteAccessParams = {| + pipette: string, + labware: string, + well: string, +|}; +declare type VolumeParams = {| + volume: number, +|}; +declare type OffsetParams = {| + offsetFromBottomMm: number, +|}; +export type AspDispAirgapParams = FlowRateParams & + PipetteAccessParams & + VolumeParams & + OffsetParams; +export type AspirateParams = AspDispAirgapParams; +export type DispenseParams = AspDispAirgapParams; +export type AirGapParams = AspDispAirgapParams; +export type BlowoutParams = FlowRateParams & PipetteAccessParams & OffsetParams; +export type TouchTipParams = PipetteAccessParams & OffsetParams; +export type PickUpTipParams = PipetteAccessParams; +export type DropTipParams = PipetteAccessParams; +export type MoveToSlotParams = {| + pipette: string, + slot: string, + offset?: { + x: number, + y: number, + z: number, + }, + minimumZHeight?: number, + forceDirect?: boolean, +|}; +export type DelayParams = {| + wait: number | true, + message?: string, +|}; +export type Command = + | { + command: "aspirate" | "dispense" | "airGap", + params: AspDispAirgapParams, + } + | { + command: "blowout", + params: BlowoutParams, + } + | { + command: "touchTip", + params: TouchTipParams, + } + | { + command: "pickUpTip" | "dropTip", + params: PipetteAccessParams, + } + | { + command: "moveToSlot", + params: MoveToSlotParams, + } + | { + command: "delay", + params: DelayParams, + }; +export type ProtocolFile = {| + schemaVersion: 3, + metadata: { + protocolName?: string, + author?: string, + description?: string | null | void, + created?: number, + lastModified?: number | null | void, + category?: string | null | void, + subcategory?: string | null | void, + tags?: string[], + }, + designerApplication?: { + name?: string, + version?: string, + data?: DesignerApplicationData, + }, + robot: { + model: "OT-2 Standard", + }, + pipettes: { [key: string]: FilePipette }, + labwareDefinitions: { [key: string]: LabwareDefinition2 }, + labware: { + [key: string]: { + slot: string, + definitionId: string, + displayName?: string, + }, + }, + commands: Command[], + commandAnnotations?: { [key: string]: any }, +|}; +declare export {}; diff --git a/shared-data/flow-types/protocol/types/schemaV4.js.flow b/shared-data/flow-types/protocol/types/schemaV4.js.flow new file mode 100644 index 00000000000..a040e303f4f --- /dev/null +++ b/shared-data/flow-types/protocol/types/schemaV4.js.flow @@ -0,0 +1,140 @@ +/** + * Flowtype definitions for schemaV4 + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { DeckSlotId, ModuleModel } from "../../js/types"; +import type { + ProtocolFile as V3ProtocolFile, + AspDispAirgapParams, + BlowoutParams, + TouchTipParams, + PipetteAccessParams, + MoveToSlotParams, + DelayParams, +} from "./schemaV3"; +declare export { BlowoutParams, FilePipette, FileLabware } from "./schemaV3"; +export type FileModule = {| + slot: DeckSlotId, + model: ModuleModel, +|}; +export type EngageMagnetParams = {| + module: string, + engageHeight: number, +|}; +export type TemperatureParams = {| + module: string, + temperature: number, +|}; +export type AtomicProfileStep = {| + holdTime: number, + temperature: number, +|}; +export type TCProfileParams = {| + module: string, + profile: AtomicProfileStep[], + volume: number, +|}; +export type ModuleOnlyParams = {| + module: string, +|}; +export type ThermocyclerSetTargetBlockTemperatureArgs = {| + module: string, + temperature: number, + volume?: number, +|}; +export type Command = + | { + command: "aspirate" | "dispense" | "airGap", + params: AspDispAirgapParams, + } + | { + command: "blowout", + params: BlowoutParams, + } + | { + command: "touchTip", + params: TouchTipParams, + } + | { + command: "pickUpTip" | "dropTip", + params: PipetteAccessParams, + } + | { + command: "moveToSlot", + params: MoveToSlotParams, + } + | { + command: "delay", + params: DelayParams, + } + | { + command: "magneticModule/engageMagnet", + params: EngageMagnetParams, + } + | { + command: "magneticModule/disengageMagnet", + params: ModuleOnlyParams, + } + | { + command: "temperatureModule/setTargetTemperature", + params: TemperatureParams, + } + | { + command: "temperatureModule/deactivate", + params: ModuleOnlyParams, + } + | { + command: "temperatureModule/awaitTemperature", + params: TemperatureParams, + } + | { + command: "thermocycler/setTargetBlockTemperature", + params: ThermocyclerSetTargetBlockTemperatureArgs, + } + | { + command: "thermocycler/setTargetLidTemperature", + params: TemperatureParams, + } + | { + command: "thermocycler/awaitBlockTemperature", + params: TemperatureParams, + } + | { + command: "thermocycler/awaitLidTemperature", + params: TemperatureParams, + } + | { + command: "thermocycler/openLid", + params: ModuleOnlyParams, + } + | { + command: "thermocycler/closeLid", + params: ModuleOnlyParams, + } + | { + command: "thermocycler/deactivateBlock", + params: ModuleOnlyParams, + } + | { + command: "thermocycler/deactivateLid", + params: ModuleOnlyParams, + } + | { + command: "thermocycler/runProfile", + params: TCProfileParams, + } + | { + command: "thermocycler/awaitProfileComplete", + params: ModuleOnlyParams, + }; +export type ProtocolFile< + DesignerApplicationData +> = V3ProtocolFile & { + $otSharedSchema: "#/protocol/schemas/4", + schemaVersion: 4, + modules: { [key: string]: FileModule }, + commands: Command[], +}; diff --git a/shared-data/flow-types/protocol/types/schemaV5.js.flow b/shared-data/flow-types/protocol/types/schemaV5.js.flow new file mode 100644 index 00000000000..0825b568609 --- /dev/null +++ b/shared-data/flow-types/protocol/types/schemaV5.js.flow @@ -0,0 +1,35 @@ +/** + * Flowtype definitions for schemaV5 + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { ProtocolFile as V3ProtocolFile } from "./schemaV3"; +import type { Command as V4Command, FileModule } from "./schemaV4"; +export type MoveToWellParams = {| + pipette: string, + labware: string, + well: string, + offset?: { + x: number, + y: number, + z: number, + }, + minimumZHeight?: number, + forceDirect?: boolean, +|}; +export type Command = + | V4Command + | { + command: "moveToWell", + params: MoveToWellParams, + }; +export type ProtocolFile< + DesignerApplicationData +> = V3ProtocolFile & { + $otSharedSchema: "#/protocol/schemas/5", + schemaVersion: 5, + modules: { [key: string]: FileModule }, + commands: Command[], +}; diff --git a/shared-data/flow-types/protocol/types/schemaV6.js.flow b/shared-data/flow-types/protocol/types/schemaV6.js.flow new file mode 100644 index 00000000000..21b5b609fed --- /dev/null +++ b/shared-data/flow-types/protocol/types/schemaV6.js.flow @@ -0,0 +1,24 @@ +/** + * Flowtype definitions for schemaV6 + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { ProtocolFile as V3ProtocolFile, AirGapParams } from "./schemaV3"; +import type { FileModule } from "./schemaV4"; +import type { Command as V5Command } from "./schemaV5"; +export type Command = + | V5Command + | { + command: "dispenseAirGap", + params: AirGapParams, + }; +export type ProtocolFile< + DesignerApplicationData +> = V3ProtocolFile & { + $otSharedSchema: "#/protocol/schemas/5", + schemaVersion: 5, + modules: { [key: string]: FileModule }, + commands: Command[], +}; diff --git a/shared-data/js/__tests__/__snapshots__/pipettes.test.js.snap b/shared-data/js/__tests__/__snapshots__/pipettes.test.ts.snap similarity index 100% rename from shared-data/js/__tests__/__snapshots__/pipettes.test.js.snap rename to shared-data/js/__tests__/__snapshots__/pipettes.test.ts.snap diff --git a/shared-data/js/__tests__/deckSchemas.test.js b/shared-data/js/__tests__/deckSchemas.test.ts similarity index 99% rename from shared-data/js/__tests__/deckSchemas.test.js rename to shared-data/js/__tests__/deckSchemas.test.ts index 47a8f1c65a5..63c86301041 100644 --- a/shared-data/js/__tests__/deckSchemas.test.js +++ b/shared-data/js/__tests__/deckSchemas.test.ts @@ -19,11 +19,14 @@ const validateV2Schema = ajv.compile(deckSchemaV2) describe('validate deck defs and fixtures', () => { const v1Fixtures = glob.sync(v1FixtureGlob) + v1Fixtures.forEach(fixturePath => { const fixtureDef = require(fixturePath) + it('fixture validates against v1 schema', () => { const valid = validateV1Schema(fixtureDef) const validationErrors = validateV1Schema.errors + if (validationErrors) { console.log( path.parse(fixturePath).base + @@ -31,16 +34,21 @@ describe('validate deck defs and fixtures', () => { JSON.stringify(validationErrors, null, 4) ) } + expect(validationErrors).toBe(null) expect(valid).toBe(true) }) }) + const v2Fixtures = glob.sync(v2FixtureGlob) + v2Fixtures.forEach(fixturePath => { const fixtureDef = require(fixturePath) + it('fixture validates against v2 schema', () => { const valid = validateV2Schema(fixtureDef) const validationErrors = validateV2Schema.errors + if (validationErrors) { console.log( path.parse(fixturePath).base + @@ -48,16 +56,21 @@ describe('validate deck defs and fixtures', () => { JSON.stringify(validationErrors, null, 4) ) } + expect(validationErrors).toBe(null) expect(valid).toBe(true) }) }) + const v1Defs = glob.sync(v1DefGlob) + v1Defs.forEach(defPath => { const deckDef = require(defPath) + it('deck validates against v1 schema', () => { const valid = validateV1Schema(deckDef) const validationErrors = validateV1Schema.errors + if (validationErrors) { console.log( path.parse(defPath).base + @@ -65,16 +78,21 @@ describe('validate deck defs and fixtures', () => { JSON.stringify(validationErrors, null, 4) ) } + expect(validationErrors).toBe(null) expect(valid).toBe(true) }) }) + const v2Defs = glob.sync(v2DefGlob) + v2Defs.forEach(defPath => { const deckDef = require(defPath) + it('deck validates against v2 schema', () => { const valid = validateV2Schema(deckDef) const validationErrors = validateV2Schema.errors + if (validationErrors) { console.log( path.parse(defPath).base + @@ -82,6 +100,7 @@ describe('validate deck defs and fixtures', () => { JSON.stringify(validationErrors, null, 4) ) } + expect(validationErrors).toBe(null) expect(valid).toBe(true) }) diff --git a/shared-data/js/__tests__/getWellNamePerMultiTip.test.js b/shared-data/js/__tests__/getWellNamePerMultiTip.test.ts similarity index 83% rename from shared-data/js/__tests__/getWellNamePerMultiTip.test.js rename to shared-data/js/__tests__/getWellNamePerMultiTip.test.ts index e5c28002823..fa6a7717ddf 100644 --- a/shared-data/js/__tests__/getWellNamePerMultiTip.test.js +++ b/shared-data/js/__tests__/getWellNamePerMultiTip.test.ts @@ -1,13 +1,21 @@ -// @flow import fixture_trash from '@opentrons/shared-data/labware/fixtures/2/fixture_trash.json' import fixture_96_plate from '@opentrons/shared-data/labware/fixtures/2/fixture_96_plate.json' import fixture_384_plate from '@opentrons/shared-data/labware/fixtures/2/fixture_384_plate.json' import fixture_12_trough from '@opentrons/shared-data/labware/fixtures/2/fixture_12_trough.json' import fixture_24_tuberack from '@opentrons/shared-data/labware/fixtures/2/fixture_24_tuberack.json' + import { getWellNamePerMultiTip } from '../helpers/getWellNamePerMultiTip' +import type { LabwareDefinition2 } from '../types' + +const fixtureTrash = fixture_trash as LabwareDefinition2 +const fixture96Plate = fixture_96_plate as LabwareDefinition2 +const fixture384Plate = fixture_384_plate as LabwareDefinition2 +const fixture12Trough = fixture_12_trough as LabwareDefinition2 +const fixture24Tuberack = fixture_24_tuberack as LabwareDefinition2 + describe('96 plate', () => { - const labware = fixture_96_plate + const labware = fixture96Plate it('A1 => column 1', () => { expect(getWellNamePerMultiTip(labware, 'A1')).toEqual([ @@ -41,7 +49,7 @@ describe('96 plate', () => { }) describe('384 plate', () => { - const labware = fixture_384_plate + const labware = fixture384Plate it('A1 => column 1 ACEGIKMO', () => { expect(getWellNamePerMultiTip(labware, 'A1')).toEqual([ @@ -88,7 +96,7 @@ describe('384 plate', () => { }) describe('Fixed trash', () => { - const labware = fixture_trash + const labware = fixtureTrash it('A1 => all tips in A1', () => { expect(getWellNamePerMultiTip(labware, 'A1')).toEqual([ @@ -109,16 +117,17 @@ describe('Fixed trash', () => { }) describe('tube rack 2mL', () => { - const labware = fixture_24_tuberack + const labware = fixture24Tuberack + it('tube rack 2mL not accessible by 8-channel (return null)', () => { ;['A1', 'A2', 'B1', 'B2'].forEach(well => { - expect(getWellNamePerMultiTip(labware, 'A1')).toEqual(null) + expect(getWellNamePerMultiTip(labware, well)).toEqual(null) }) }) }) describe('12 channel trough', () => { - const labware = fixture_12_trough + const labware = fixture12Trough it('A1 => all tips in A1', () => { expect(getWellNamePerMultiTip(labware, 'A1')).toEqual([ diff --git a/shared-data/js/__tests__/labwareDefQuirks.test.js b/shared-data/js/__tests__/labwareDefQuirks.test.ts similarity index 99% rename from shared-data/js/__tests__/labwareDefQuirks.test.js rename to shared-data/js/__tests__/labwareDefQuirks.test.ts index f41cb70312b..86f14550ac6 100644 --- a/shared-data/js/__tests__/labwareDefQuirks.test.js +++ b/shared-data/js/__tests__/labwareDefQuirks.test.ts @@ -14,14 +14,18 @@ const EXPECTED_VALID_QUIRKS = [ describe('check quirks for all labware defs', () => { const labwarePaths = glob.sync(definitionsGlobPath) + beforeAll(() => { // Make sure definitions path didn't break, which would give you false positives expect(labwarePaths.length).toBeGreaterThan(0) }) + labwarePaths.forEach(labwarePath => { const defname = path.basename(path.dirname(labwarePath)) + it(`${defname} has valid quirks`, () => { const labwareDef = require(labwarePath) + const quirks = labwareDef.parameters.quirks || [] // we want to test that the quirks in the def are a subset of validQuirks, // whereas arrayContaining tests that the expected value is a subset of diff --git a/shared-data/js/__tests__/labwareDefSchemaV1.test.js b/shared-data/js/__tests__/labwareDefSchemaV1.test.ts similarity index 82% rename from shared-data/js/__tests__/labwareDefSchemaV1.test.js rename to shared-data/js/__tests__/labwareDefSchemaV1.test.ts index f4a086846f7..6247e05f06a 100644 --- a/shared-data/js/__tests__/labwareDefSchemaV1.test.js +++ b/shared-data/js/__tests__/labwareDefSchemaV1.test.ts @@ -1,26 +1,21 @@ import path from 'path' import glob from 'glob' import Ajv from 'ajv' + import { labwareSchemaV1 } from '../schema' +import type { LabwareDefinition1 } from '../types' const DEFINITIONS_GLOB_PATTERN = '../../labware/definitions/1/*.json' const GLOB_OPTIONS = { absolute: true, cwd: __dirname } -// JSON Schema defintion & setup - -const ajv = new Ajv({ - allErrors: true, - jsonPointers: true, -}) +// JSON Schema defintion & setup +const ajv = new Ajv({ allErrors: true, jsonPointers: true }) const validate = ajv.compile(labwareSchemaV1) describe('test the schema against a minimalist fixture', () => { it('...', () => { const minimalLabwareDef = { - metadata: { - name: 'test-labware', - format: 'trough', - }, + metadata: { name: 'test-labware', format: 'trough' }, ordering: [['A1']], wells: { A1: { @@ -45,14 +40,15 @@ describe('test the schema against a minimalist fixture', () => { it('fail on bad labware', () => { const badDef = { metadata: { name: 'bad' }, - ordering: ['A1'], // array of strings not array of arrays + ordering: ['A1'], + // array of strings not array of arrays wells: {}, } const valid = validate(badDef) const validationErrors = validate.errors expect( - validationErrors.find(err => err.dataPath === '/ordering/0') + validationErrors?.find(err => err.dataPath === '/ordering/0') ).toMatchObject({ message: 'should be array', }) @@ -63,21 +59,22 @@ describe('test the schema against a minimalist fixture', () => { describe('test schemas of all definitions', () => { const labwarePaths = glob.sync(DEFINITIONS_GLOB_PATTERN, GLOB_OPTIONS) - it('got at least 1 labware definition file', () => { + beforeAll(() => { // Make sure definitions path didn't break, which would give you false positives expect(labwarePaths.length).toBeGreaterThan(0) }) labwarePaths.forEach(labwarePath => { const filename = path.parse(labwarePath).name - const labwareDef = require(labwarePath) + const labwareDef = require(labwarePath) as LabwareDefinition1 + it(filename, () => { const valid = validate(labwareDef) const validationErrors = validate.errors - expect(validationErrors).toBe(null) expect(valid).toBe(true) }) + it(`file name matches metadata.name: ${filename}`, () => { expect(labwareDef.metadata.name).toEqual(filename) }) diff --git a/shared-data/js/__tests__/labwareDefSchemaV2.test.js b/shared-data/js/__tests__/labwareDefSchemaV2.test.ts similarity index 86% rename from shared-data/js/__tests__/labwareDefSchemaV2.test.js rename to shared-data/js/__tests__/labwareDefSchemaV2.test.ts index 29f8f2b4338..7d78755b103 100644 --- a/shared-data/js/__tests__/labwareDefSchemaV2.test.js +++ b/shared-data/js/__tests__/labwareDefSchemaV2.test.ts @@ -1,7 +1,9 @@ import path from 'path' import glob from 'glob' import Ajv from 'ajv' + import schema from '../../labware/schemas/2.json' +import type { LabwareDefinition2 } from '../types' const definitionsGlobPath = path.join( __dirname, @@ -11,23 +13,23 @@ const definitionsGlobPath = path.join( const fixturesGlobPath = path.join(__dirname, '../../labware/fixtures/2/*.json') // JSON Schema defintion & setup - -const ajv = new Ajv({ - allErrors: true, - jsonPointers: true, -}) - +const ajv = new Ajv({ allErrors: true, jsonPointers: true }) const validate = ajv.compile(schema) -const expectGroupsFollowConvention = (labwareDef, filename) => { +const expectGroupsFollowConvention = ( + labwareDef: LabwareDefinition2, + filename: string +): void => { test(`${filename} should not contain "groups.brand.brand" that matches the top-level "brand.brand"`, () => { const topLevelBrand = labwareDef.brand + labwareDef.groups.forEach(group => { if (group.brand) { expect(group.brand.brand).not.toEqual(topLevelBrand) } }) }) + test(`${filename} should not specify certain fields in 'groups' if it is a reservoir or wellPlate`, () => { const { displayCategory } = labwareDef.metadata const noGroupsMetadataAllowed = @@ -43,17 +45,18 @@ const expectGroupsFollowConvention = (labwareDef, filename) => { }) } -describe('fail on bad labware', () => { +test('fail on bad labware', () => { const badDef = { metadata: { name: 'bad' }, - ordering: ['A1'], // array of strings not array of arrays + ordering: ['A1'], + // array of strings not array of arrays wells: {}, } const valid = validate(badDef) const validationErrors = validate.errors expect( - validationErrors.find(err => err.dataPath === '/ordering/0') + validationErrors?.find(err => err.dataPath === '/ordering/0') ).toMatchObject({ message: 'should be array', }) @@ -62,29 +65,33 @@ describe('fail on bad labware', () => { describe('test schemas of all opentrons definitions', () => { const labwarePaths = glob.sync(definitionsGlobPath) - it(`path to definitions OK`, () => { + + beforeAll(() => { // Make sure definitions path didn't break, which would give you false positives expect(labwarePaths.length).toBeGreaterThan(0) }) labwarePaths.forEach(labwarePath => { const filename = path.parse(labwarePath).base - const labwareDef = require(labwarePath) + const labwareDef = require(labwarePath) as LabwareDefinition2 + it(`${filename} validates against schema`, () => { const valid = validate(labwareDef) const validationErrors = validate.errors - expect(validationErrors).toBe(null) expect(valid).toBe(true) }) + it(`file name matches version: ${labwarePath}`, () => { expect(`${labwareDef.version}`).toEqual(path.basename(filename, '.json')) }) + it(`parent dir matches loadName: ${labwarePath}`, () => { expect(labwareDef.parameters.loadName).toEqual( path.basename(path.dirname(labwarePath)) ) }) + it(`namespace is "opentrons": ${labwarePath}`, () => { expect(labwareDef.namespace).toEqual('opentrons') }) @@ -99,26 +106,28 @@ describe('test schemas of all opentrons definitions', () => { describe('test schemas of all v2 labware fixtures', () => { const labwarePaths = glob.sync(fixturesGlobPath) - it(`path to fixtures OK`, () => { + beforeAll(() => { // Make sure fixtures path didn't break, which would give you false positives expect(labwarePaths.length).toBeGreaterThan(0) }) labwarePaths.forEach(labwarePath => { const filename = path.parse(labwarePath).base - const labwareDef = require(labwarePath) + const labwareDef = require(labwarePath) as LabwareDefinition2 + it(`${filename} validates against schema`, () => { const valid = validate(labwareDef) const validationErrors = validate.errors - expect(validationErrors).toBe(null) expect(valid).toBe(true) }) + it(`fixture file name matches loadName: ${labwarePath}`, () => { expect(labwareDef.parameters.loadName).toEqual( path.basename(filename, '.json') ) }) + it(`namespace is "fixture": ${labwarePath}`, () => { expect(labwareDef.namespace).toEqual('fixture') }) diff --git a/shared-data/js/__tests__/moduleAccessors.test.js b/shared-data/js/__tests__/moduleAccessors.test.ts similarity index 98% rename from shared-data/js/__tests__/moduleAccessors.test.js rename to shared-data/js/__tests__/moduleAccessors.test.ts index 3a5c312ebab..ccc7ceaf46a 100644 --- a/shared-data/js/__tests__/moduleAccessors.test.js +++ b/shared-data/js/__tests__/moduleAccessors.test.ts @@ -1,5 +1,3 @@ -// @flow - import { getModuleDef2, getModuleType, @@ -21,14 +19,17 @@ import { describe('all valid models work', () => { MODULE_MODELS.forEach(model => { const loadedDef = getModuleDef2(model) + it('ensure valid models load', () => { expect(loadedDef).not.toBeNull() expect(loadedDef?.model).toEqual(model) }) + it('valid models have valid module types', () => { expect(getModuleType(model)).toEqual(loadedDef.moduleType) expect(MODULE_TYPES).toContain(getModuleType(model)) }) + it('valid modules have display names that match the def', () => { expect(getModuleDisplayName(model)).toEqual(loadedDef.displayName) }) @@ -40,7 +41,8 @@ describe('legacy models work too', () => { [TEMPDECK, TEMPERATURE_MODULE_V1], [MAGDECK, MAGNETIC_MODULE_V1], [THERMOCYCLER, THERMOCYCLER_MODULE_V1], - ] + ] as const + legacyEquivs.forEach(([legacy, modern]) => { const fromLegacy = normalizeModuleModel(legacy) expect(fromLegacy).toEqual(modern) diff --git a/shared-data/js/__tests__/moduleSpecsSchema.test.js b/shared-data/js/__tests__/moduleSpecsSchema.test.ts similarity index 91% rename from shared-data/js/__tests__/moduleSpecsSchema.test.js rename to shared-data/js/__tests__/moduleSpecsSchema.test.ts index a5f2ff23198..460bea127fc 100644 --- a/shared-data/js/__tests__/moduleSpecsSchema.test.js +++ b/shared-data/js/__tests__/moduleSpecsSchema.test.ts @@ -5,11 +5,7 @@ import moduleSpecsSchemaV2 from '../../module/schemas/2.json' import path from 'path' import glob from 'glob' -const ajv = new Ajv({ - allErrors: true, - jsonPointers: true, -}) - +const ajv = new Ajv({ allErrors: true, jsonPointers: true }) const validateModuleSpecsV1 = ajv.compile(moduleSpecsSchemaV1) const validateModuleSpecsV2 = ajv.compile(moduleSpecsSchemaV2) @@ -17,11 +13,11 @@ const V2_DEFS_GLOB_PATTERN = '../../module/definitions/2/*.json' const GLOB_OPTIONS = { cwd: __dirname, absolute: true } const MODULE_PATHS = glob.sync(V2_DEFS_GLOB_PATTERN, GLOB_OPTIONS) -beforeAll(() => { - expect(MODULE_PATHS).not.toHaveLength(0) -}) - describe('validate all module specs with schema', () => { + beforeAll(() => { + expect(MODULE_PATHS).not.toHaveLength(0) + }) + it('ensure V1 module specs match the V1 JSON schema', () => { const valid = validateModuleSpecsV1(moduleSpecsV1) const validationErrors = validateModuleSpecsV1.errors @@ -33,17 +29,21 @@ describe('validate all module specs with schema', () => { MODULE_PATHS.forEach(modulePath => { const filename = path.parse(modulePath).name const moduleDef = require(modulePath) + it(`${filename} validates against schema`, () => { const valid = validateModuleSpecsV2(moduleDef) const validationErrors = validateModuleSpecsV2.errors + expect(validationErrors).toBe(null) expect(valid).toBe(true) }) }) + it('validate each module specs model matches its filename', () => { MODULE_PATHS.forEach(modulePath => { const filename = path.parse(modulePath).name const moduleDef = require(modulePath) + expect(moduleDef.model).toEqual(filename) }) }) diff --git a/shared-data/js/__tests__/pipetteSpecSchemas.test.js b/shared-data/js/__tests__/pipetteSpecSchemas.test.ts similarity index 86% rename from shared-data/js/__tests__/pipetteSpecSchemas.test.js rename to shared-data/js/__tests__/pipetteSpecSchemas.test.ts index c734239cf4f..df096876252 100644 --- a/shared-data/js/__tests__/pipetteSpecSchemas.test.js +++ b/shared-data/js/__tests__/pipetteSpecSchemas.test.ts @@ -4,10 +4,7 @@ import modelSpecsSchema from '../../pipette/schemas/pipetteModelSpecsSchema.json import pipetteNameSpecs from '../../pipette/definitions/pipetteNameSpecs.json' import pipetteModelSpecs from '../../pipette/definitions/pipetteModelSpecs.json' -const ajv = new Ajv({ - allErrors: true, - jsonPointers: true, -}) +const ajv = new Ajv({ allErrors: true, jsonPointers: true }) const validateNameSpecs = ajv.compile(nameSpecsSchema) const validateModelSpecs = ajv.compile(modelSpecsSchema) @@ -40,11 +37,11 @@ describe('validate pipette specs with JSON schemas', () => { describe('model -> name referencing', () => { it('ensure all pipette model specs reference a valid pipette name', () => { - const modelKeys = Object.keys(pipetteModelSpecs.config) + const modelConfigs = Object.values(pipetteModelSpecs.config) const nameKeys = Object.keys(pipetteNameSpecs) - modelKeys.forEach(model => { - const nameForVersion = pipetteModelSpecs.config[model].name + modelConfigs.forEach(config => { + const nameForVersion = config.name expect(nameKeys).toContain(nameForVersion) }) }) diff --git a/shared-data/js/__tests__/pipettes.test.js b/shared-data/js/__tests__/pipettes.test.ts similarity index 98% rename from shared-data/js/__tests__/pipettes.test.js rename to shared-data/js/__tests__/pipettes.test.ts index ac62c27a438..9cf9d42fe02 100644 --- a/shared-data/js/__tests__/pipettes.test.js +++ b/shared-data/js/__tests__/pipettes.test.ts @@ -9,7 +9,7 @@ const PIPETTE_NAMES = [ 'p10_multi', 'p50_multi', 'p300_multi', -] +] as const const PIPETTE_MODELS = [ 'p10_single_v1', @@ -39,7 +39,7 @@ const PIPETTE_MODELS = [ 'p1000_single_v1.3', 'p1000_single_v1.4', 'p1000_single_v1.5', -] +] as const describe('pipette data accessors', () => { describe('getPipetteNameSpecs', () => { diff --git a/shared-data/js/__tests__/protocolSchemaV4.test.js b/shared-data/js/__tests__/protocolSchemaV4.test.ts similarity index 89% rename from shared-data/js/__tests__/protocolSchemaV4.test.js rename to shared-data/js/__tests__/protocolSchemaV4.test.ts index a81f2e2615b..4b4595c53f3 100644 --- a/shared-data/js/__tests__/protocolSchemaV4.test.js +++ b/shared-data/js/__tests__/protocolSchemaV4.test.ts @@ -1,10 +1,10 @@ -// @flow /** Ensure that protocol schema v4 definition itself is functions as intended, * and that all v4 protocol fixtures will validate */ import Ajv from 'ajv' import path from 'path' import glob from 'glob' import omit from 'lodash/omit' + import protocolSchema from '../../protocol/schemas/4.json' import labwareV2Schema from '../../labware/schemas/2.json' import simpleV4Fixture from '../../protocol/fixtures/4/simpleV4.json' @@ -13,12 +13,9 @@ const fixturesGlobPath = path.join( __dirname, '../../protocol/fixtures/4/**/*.json' ) -const protocolFixtures = glob.sync(fixturesGlobPath) -const ajv = new Ajv({ - allErrors: true, - jsonPointers: true, -}) +const protocolFixtures = glob.sync(fixturesGlobPath) +const ajv = new Ajv({ allErrors: true, jsonPointers: true }) // v4 protocol schema contains reference to v2 labware schema, so give AJV access to it ajv.addSchema(labwareV2Schema) @@ -29,12 +26,14 @@ describe('validate v4 protocol fixtures under JSON schema', () => { protocolFixtures.forEach(protocolPath => { it(path.basename(protocolPath), () => { const protocol = require(protocolPath) + const valid = validateProtocol(protocol) const validationErrors = validateProtocol.errors if (validationErrors) { console.log(JSON.stringify(validationErrors, null, 4)) } + expect(valid).toBe(true) expect(validationErrors).toBe(null) }) @@ -74,13 +73,13 @@ describe('ensure bad protocol data fails validation', () => { }, } - Object.keys(badPipettes).forEach((pipetteId: string) => { + Object.entries(badPipettes).forEach(([pipetteId, pipette]) => { expect( validateProtocol({ ...simpleV4Fixture, pipettes: { ...simpleV4Fixture.pipettes, - [pipetteId]: badPipettes[pipetteId], + [pipetteId]: pipette, }, }) ).toBe(false) @@ -98,13 +97,13 @@ describe('ensure bad protocol data fails validation', () => { }, } - Object.keys(badLabware).forEach((labwareId: string) => { + Object.entries(badLabware).forEach(([labwareId, labware]) => { expect( validateProtocol({ ...simpleV4Fixture, labware: { ...simpleV4Fixture.labware, - [labwareId]: badLabware[labwareId], + [labwareId]: labware, }, }) ).toBe(false) @@ -123,13 +122,13 @@ describe('ensure bad protocol data fails validation', () => { }, } - Object.keys(badModules).forEach((moduleId: string) => { + Object.entries(badModules).forEach(([moduleId, module]) => { expect( validateProtocol({ ...simpleV4Fixture, modules: { ...simpleV4Fixture.modules, - [moduleId]: badModules[moduleId], + [moduleId]: module, }, }) ).toBe(false) diff --git a/shared-data/js/__tests__/protocolSchemaV5.test.js b/shared-data/js/__tests__/protocolSchemaV5.test.ts similarity index 89% rename from shared-data/js/__tests__/protocolSchemaV5.test.js rename to shared-data/js/__tests__/protocolSchemaV5.test.ts index 6f4a8504e3a..682209a4ad4 100644 --- a/shared-data/js/__tests__/protocolSchemaV5.test.js +++ b/shared-data/js/__tests__/protocolSchemaV5.test.ts @@ -1,10 +1,10 @@ -// @flow /** Ensure that protocol schema v5 definition itself is functions as intended, * and that all v5 protocol fixtures will validate */ import Ajv from 'ajv' import path from 'path' import glob from 'glob' import omit from 'lodash/omit' + import protocolSchema from '../../protocol/schemas/5.json' import labwareV2Schema from '../../labware/schemas/2.json' import simpleV5Fixture from '../../protocol/fixtures/5/simpleV5.json' @@ -13,12 +13,9 @@ const fixturesGlobPath = path.join( __dirname, '../../protocol/fixtures/5/**/*.json' ) -const protocolFixtures = glob.sync(fixturesGlobPath) -const ajv = new Ajv({ - allErrors: true, - jsonPointers: true, -}) +const protocolFixtures = glob.sync(fixturesGlobPath) +const ajv = new Ajv({ allErrors: true, jsonPointers: true }) // v5 protocol schema contains reference to v2 labware schema, so give AJV access to it ajv.addSchema(labwareV2Schema) @@ -29,12 +26,14 @@ describe('validate v5 protocol fixtures under JSON schema', () => { protocolFixtures.forEach(protocolPath => { it(path.basename(protocolPath), () => { const protocol = require(protocolPath) + const valid = validateProtocol(protocol) const validationErrors = validateProtocol.errors if (validationErrors) { console.log(JSON.stringify(validationErrors, null, 4)) } + expect(valid).toBe(true) expect(validationErrors).toBe(null) }) @@ -74,13 +73,13 @@ describe('ensure bad protocol data fails validation', () => { }, } - Object.keys(badPipettes).forEach((pipetteId: string) => { + Object.entries(badPipettes).forEach(([pipetteId, pipette]) => { expect( validateProtocol({ ...simpleV5Fixture, pipettes: { ...simpleV5Fixture.pipettes, - [pipetteId]: badPipettes[pipetteId], + [pipetteId]: pipette, }, }) ).toBe(false) @@ -98,13 +97,13 @@ describe('ensure bad protocol data fails validation', () => { }, } - Object.keys(badLabware).forEach((labwareId: string) => { + Object.entries(badLabware).forEach(([labwareId, labware]) => { expect( validateProtocol({ ...simpleV5Fixture, labware: { ...simpleV5Fixture.labware, - [labwareId]: badLabware[labwareId], + [labwareId]: labware, }, }) ).toBe(false) @@ -123,13 +122,13 @@ describe('ensure bad protocol data fails validation', () => { }, } - Object.keys(badModules).forEach((moduleId: string) => { + Object.entries(badModules).forEach(([moduleId, module]) => { expect( validateProtocol({ ...simpleV5Fixture, modules: { ...simpleV5Fixture.modules, - [moduleId]: badModules[moduleId], + [moduleId]: module, }, }) ).toBe(false) diff --git a/shared-data/js/__tests__/sortWells.test.js b/shared-data/js/__tests__/sortWells.test.ts similarity index 98% rename from shared-data/js/__tests__/sortWells.test.js rename to shared-data/js/__tests__/sortWells.test.ts index 610854c7d8b..ac2501b25c6 100644 --- a/shared-data/js/__tests__/sortWells.test.js +++ b/shared-data/js/__tests__/sortWells.test.ts @@ -1,4 +1,3 @@ -// @flow import { sortWells } from '../helpers' describe('sortWells', () => { diff --git a/shared-data/js/__tests__/splitWellsOnColumn.test.js b/shared-data/js/__tests__/splitWellsOnColumn.test.ts similarity index 100% rename from shared-data/js/__tests__/splitWellsOnColumn.test.js rename to shared-data/js/__tests__/splitWellsOnColumn.test.ts diff --git a/shared-data/js/constants.js b/shared-data/js/constants.ts similarity index 95% rename from shared-data/js/constants.js rename to shared-data/js/constants.ts index 4fa138691d6..d3fb8d2c605 100644 --- a/shared-data/js/constants.js +++ b/shared-data/js/constants.ts @@ -1,12 +1,11 @@ -// @flow - -import type { ModuleModel } from './types' // constants for dealing with robot coordinate system (eg in labwareTools) export const SLOT_LENGTH_MM = 127.76 // along X axis in robot coordinate system + export const SLOT_WIDTH_MM = 85.48 // along Y axis in robot coordinate system // constants for SVG renders of the deck export const SLOT_RENDER_WIDTH = SLOT_LENGTH_MM // along X axis in SVG coords + export const SLOT_RENDER_HEIGHT = SLOT_WIDTH_MM // along Y axis in SVG coords // taken from opentrons_1_trash_1100ml_fixed v1's dimensions @@ -18,6 +17,7 @@ export const OPENTRONS_LABWARE_NAMESPACE = 'opentrons' export const THERMOCYCLER: 'thermocycler' = 'thermocycler' export const TEMPDECK: 'tempdeck' = 'tempdeck' export const MAGDECK: 'magdeck' = 'magdeck' + // these are the Module Def Schema v2 equivalents of the above. They should match the names of JSON definitions // in shared-data/module/definitions/2. export const MAGNETIC_MODULE_V1: 'magneticModuleV1' = 'magneticModuleV1' @@ -33,6 +33,10 @@ export const THERMOCYCLER_MODULE_V1: 'thermocyclerModuleV1' = export const GEN2: 'GEN2' = 'GEN2' export const GEN1: 'GEN1' = 'GEN1' +// pipette mounts +export const LEFT: 'left' = 'left' +export const RIGHT: 'right' = 'right' + // NOTE: these are NOT module MODELs, they are `moduleType`s. Should match ENUM in module definition file. export const TEMPERATURE_MODULE_TYPE: 'temperatureModuleType' = 'temperatureModuleType' @@ -49,7 +53,7 @@ export const TEMPERATURE_MODULE_MODELS = [ export const THERMOCYCLER_MODULE_MODELS = [THERMOCYCLER_MODULE_V1] -export const MODULE_MODELS: Array = [ +export const MODULE_MODELS = [ ...MAGNETIC_MODULE_MODELS, ...TEMPERATURE_MODULE_MODELS, ...THERMOCYCLER_MODULE_MODELS, diff --git a/shared-data/js/cypressUtils.js b/shared-data/js/cypressUtils.ts similarity index 84% rename from shared-data/js/cypressUtils.js rename to shared-data/js/cypressUtils.ts index 4e21f78a4b0..776c39d6548 100644 --- a/shared-data/js/cypressUtils.js +++ b/shared-data/js/cypressUtils.ts @@ -1,10 +1,9 @@ -// @flow import isEqual from 'lodash/isEqual' import isObject from 'lodash/isObject' import transform from 'lodash/transform' -const difference = (object, base) => { - const changes = (object, base) => { +const difference = (object: any, base: any): any => { + const changes = (object: any, base: any): any => { return transform(object, function (result, value, key) { if (!isEqual(value, base[key])) { result[key] = @@ -14,6 +13,7 @@ const difference = (object, base) => { } }) } + return changes(object, base) } @@ -24,8 +24,9 @@ export const expectDeepEqual = (assert: any, a: any, b: any): void => { assert.deepEqual(a, b) } catch (e) { // visualize undefineds - const replacer = (key, value) => + const replacer = (key: string, value: unknown): unknown => typeof value === 'undefined' ? '__undefined__' : value + throw Error( 'Expected deep equal. Diff is: ' + JSON.stringify(difference(a, b), replacer, 4) diff --git a/shared-data/js/getLabware.js b/shared-data/js/getLabware.ts similarity index 92% rename from shared-data/js/getLabware.js rename to shared-data/js/getLabware.ts index 76e56ff642f..7eb7b2f2e67 100644 --- a/shared-data/js/getLabware.js +++ b/shared-data/js/getLabware.ts @@ -1,13 +1,14 @@ -// @flow import assert from 'assert' import mapValues from 'lodash/mapValues' // TODO: Ian 2019-06-04 remove the shared-data build process for labware v1 import definitions from '../build/labware.json' + import { FIXED_TRASH_RENDER_HEIGHT, OPENTRONS_LABWARE_NAMESPACE, SLOT_RENDER_HEIGHT, } from './constants' + import type { LabwareDefinition1, LabwareDefinition2, @@ -44,8 +45,12 @@ export const PD_DO_NOT_LIST = [ 'opentrons_calibrationblock_short_side_right', ] -export function getLabwareV1Def(labwareName: string): ?LabwareDefinition1 { - const labware: ?LabwareDefinition1 = definitions[labwareName] +export function getLabwareV1Def( + labwareName: string +): LabwareDefinition1 | null | undefined { + const labware: LabwareDefinition1 | null | undefined = + // @ts-expect-error(mc, 2021-04-27): make lookup more strict or remove v1 defs entirely + definitions[labwareName] return labware } @@ -71,6 +76,7 @@ const _SHORT_MM_LABWARE_DEF_LOADNAMES = [ 'nest_96_wellplate_100ul_pcr_full_skirt', 'usascientific_96_wellplate_2.4ml_deep', ] + // offset added to parameters.magneticModuleEngageHeight to convert older labware // definitions from "distance from home switch" to "distance from labware bottom" // Note: this is in actual mm, not "short mm" :) @@ -79,8 +85,9 @@ const ENGAGE_HEIGHT_OFFSET = -4 export function getLabwareDefaultEngageHeight( labwareDef: LabwareDefinition2 ): number | null { - const rawEngageHeight: ?number = + const rawEngageHeight: number | null | undefined = labwareDef.parameters.magneticModuleEngageHeight + if ( labwareDef.namespace === OPENTRONS_LABWARE_NAMESPACE && _SHORT_MM_LABWARE_DEF_LOADNAMES.includes(labwareDef.parameters.loadName) @@ -89,23 +96,22 @@ export function getLabwareDefaultEngageHeight( ? null : rawEngageHeight / 2 + ENGAGE_HEIGHT_OFFSET } + return rawEngageHeight == null ? null : rawEngageHeight } /* Render Helpers */ - // NOTE: this doesn't account for the differing footprints of labware // the fixed trash render height is the first bandaid to partially // mend this, but overall the labware definitions in shared-data are // insufficient to render labware at the resolution we'd like to // achieve going forward. - // TODO: BC 2019-02-28 The height constants used here should be replaced with the heights // in the dimensions field of the corresponding labware in definitions const _getSvgYValueForWell = ( def: LabwareDefinition1, wellDef: WellDefinition -) => { +): number => { const labwareName = def.metadata.name const renderHeight = labwareName === 'fixed-trash' @@ -117,15 +123,15 @@ const _getSvgYValueForWell = ( /** For display. Flips Y axis to match SVG, applies offset to wells */ export function getWellPropsForSVGLabwareV1( def: LabwareDefinition1 -): { [well: string]: WellDefinition, ... } { +): Record { const wellDefs = def && def.wells - // Most labware defs have a weird offset, // but tips are mostly OK. // This is a HACK to make the offset less "off" const isTiprack = getIsLabwareV1Tiprack(def) let xCorrection = 0 let yCorrection = 0 + if (!isTiprack) { xCorrection = 1 yCorrection = -3 diff --git a/shared-data/js/helpers/__tests__/volume.test.js b/shared-data/js/helpers/__tests__/volume.test.ts similarity index 76% rename from shared-data/js/helpers/__tests__/volume.test.js rename to shared-data/js/helpers/__tests__/volume.test.ts index bde5eea4d29..fef3e2e8750 100644 --- a/shared-data/js/helpers/__tests__/volume.test.js +++ b/shared-data/js/helpers/__tests__/volume.test.ts @@ -1,9 +1,26 @@ // volume helpers tests - import * as helpers from '..' +interface BaseSpec any> { + name: string + func: T + input: Parameters + expected: ReturnType +} + +type GetDisplayVolumeSpec = BaseSpec + +type GetAsciiVolumeUnitsSpec = BaseSpec + +type EnsureVolumeUnitsSpec = BaseSpec + +type TestSpec = + | GetDisplayVolumeSpec + | GetAsciiVolumeUnitsSpec + | EnsureVolumeUnitsSpec + describe('volume helpers', () => { - const SPECS = [ + const SPECS: TestSpec[] = [ { name: 'getDisplayVolume outputs µL string by default', func: helpers.getDisplayVolume, @@ -85,6 +102,7 @@ describe('volume helpers', () => { ] SPECS.forEach(s => { - it(s.name, () => expect(s.func(...s.input)).toEqual(s.expected)) + // @ts-expect-error(mc, 2021-04-27): rewrite as regular tests + it(`should ${s.name}`, () => expect(s.func(...s.input)).toEqual(s.expected)) }) }) diff --git a/shared-data/js/helpers/__tests__/wellSets.test.js b/shared-data/js/helpers/__tests__/wellSets.test.ts similarity index 77% rename from shared-data/js/helpers/__tests__/wellSets.test.js rename to shared-data/js/helpers/__tests__/wellSets.test.ts index ea15830124d..68ea47ec823 100644 --- a/shared-data/js/helpers/__tests__/wellSets.test.js +++ b/shared-data/js/helpers/__tests__/wellSets.test.ts @@ -1,15 +1,22 @@ -// @flow -import { - fixtureP10Single, - fixtureP10Multi, -} from '@opentrons/shared-data/pipette/fixtures/name' +import pipetteNameSpecsFixtures from '../../../pipette/fixtures/name/pipetteNameSpecFixtures.json' import fixture_12_trough from '../../../labware/fixtures/2/fixture_12_trough.json' import fixture_96_plate from '../../../labware/fixtures/2/fixture_96_plate.json' import fixture_384_plate from '../../../labware/fixtures/2/fixture_384_plate.json' -import fixture_overlappy_wellplate from '../../../labware/fixtures/2/fixture_overlappy_wellplate' +import fixture_overlappy_wellplate from '../../../labware/fixtures/2/fixture_overlappy_wellplate.json' + import { makeWellSetHelpers } from '../wellSets' import { findWellAt } from '../getWellNamePerMultiTip' +import type { LabwareDefinition2, PipetteNameSpecs } from '../../types' +import type { WellSetHelpers } from '../wellSets' + +const fixtureP10Single = pipetteNameSpecsFixtures.p10_single as PipetteNameSpecs +const fixtureP10Multi = pipetteNameSpecsFixtures.p10_multi as PipetteNameSpecs +const fixture12Trough = fixture_12_trough as LabwareDefinition2 +const fixture96Plate = fixture_96_plate as LabwareDefinition2 +const fixture384Plate = fixture_384_plate as LabwareDefinition2 +const fixtureOverlappyWellplate = fixture_overlappy_wellplate as LabwareDefinition2 + describe('findWellAt', () => { it('should determine if given (x, y) is within a rectangular well', () => { const def: any = { @@ -65,33 +72,40 @@ describe('findWellAt', () => { expect(justOutsideToEast).toBeUndefined() }) }) - describe('canPipetteUseLabware', () => { - let canPipetteUseLabware + let canPipetteUseLabware: WellSetHelpers['canPipetteUseLabware'] + beforeEach(() => { const helpers = makeWellSetHelpers() canPipetteUseLabware = helpers.canPipetteUseLabware }) + it('returns false when wells are too close together for multi channel pipette', () => { - const labwareDef = { ...fixture_overlappy_wellplate } - const pipette = { ...fixtureP10Multi } + const labwareDef = fixtureOverlappyWellplate + const pipette = fixtureP10Multi + expect(canPipetteUseLabware(pipette, labwareDef)).toBe(false) }) + it('returns true when pipette is single channel', () => { - const labwareDef = { ...fixture_overlappy_wellplate } - const pipette = { ...fixtureP10Single } + const labwareDef = fixtureOverlappyWellplate + const pipette = fixtureP10Single + expect(canPipetteUseLabware(pipette, labwareDef)).toBe(true) }) }) describe('getWellSetForMultichannel (integration test)', () => { - let getWellSetForMultichannel + let getWellSetForMultichannel: WellSetHelpers['getWellSetForMultichannel'] + beforeEach(() => { const helpers = makeWellSetHelpers() getWellSetForMultichannel = helpers.getWellSetForMultichannel }) + it('96-flat', () => { - const labwareDef = fixture_96_plate + const labwareDef = fixture96Plate + expect(getWellSetForMultichannel(labwareDef, 'A1')).toEqual([ 'A1', 'B1', @@ -138,12 +152,14 @@ describe('getWellSetForMultichannel (integration test)', () => { }) it('invalid well', () => { - const labwareDef = fixture_96_plate + const labwareDef = fixture96Plate + expect(getWellSetForMultichannel(labwareDef, 'A13')).toBeFalsy() }) it('trough-12row', () => { - const labwareDef = fixture_12_trough + const labwareDef = fixture12Trough + expect(getWellSetForMultichannel(labwareDef, 'A1')).toEqual([ 'A1', 'A1', @@ -168,7 +184,8 @@ describe('getWellSetForMultichannel (integration test)', () => { }) it('384-plate', () => { - const labwareDef = fixture_384_plate + const labwareDef = fixture384Plate + expect(getWellSetForMultichannel(labwareDef, 'C1')).toEqual([ 'A1', 'C1', diff --git a/shared-data/js/helpers/getWellNamePerMultiTip.js b/shared-data/js/helpers/getWellNamePerMultiTip.ts similarity index 90% rename from shared-data/js/helpers/getWellNamePerMultiTip.js rename to shared-data/js/helpers/getWellNamePerMultiTip.ts index 15f7f368f0a..f7b0192a332 100644 --- a/shared-data/js/helpers/getWellNamePerMultiTip.js +++ b/shared-data/js/helpers/getWellNamePerMultiTip.ts @@ -1,17 +1,17 @@ -// @flow import range from 'lodash/range' import { getLabwareHasQuirk, sortWells } from '.' -import type { LabwareDefinition2 } from '@opentrons/shared-data' +import type { LabwareDefinition2 } from '../types' // TODO Ian 2018-03-13 pull pipette offsets/positions from some pipette definitions data const OFFSET_8_CHANNEL = 9 // offset in mm between tips + const MULTICHANNEL_TIP_SPAN = OFFSET_8_CHANNEL * (8 - 1) // length in mm from first to last tip of multichannel export function findWellAt( labwareDef: LabwareDefinition2, x: number, y: number -): ?string { +): string | null | undefined { return Object.keys(labwareDef.wells) .sort(sortWells) .find((wellName: string) => { @@ -37,8 +37,9 @@ export function findWellAt( export function getWellNamePerMultiTip( labwareDef: LabwareDefinition2, topWellName: string -): Array | null { +): string[] | null { const topWell = labwareDef.wells[topWellName] + if (!topWell) { console.warn( `well "${topWellName}" does not exist in labware ${labwareDef?.namespace}/${labwareDef?.parameters?.loadName}, cannot getWellNamePerMultiTip` @@ -47,7 +48,7 @@ export function getWellNamePerMultiTip( } const { x, y } = topWell - let offsetYTipPositions: Array = range(0, 8).map( + let offsetYTipPositions: number[] = range(0, 8).map( tipNo => y - tipNo * OFFSET_8_CHANNEL ) @@ -60,15 +61,16 @@ export function getWellNamePerMultiTip( // Return null for containers with any undefined wells const wellsAccessed = offsetYTipPositions.reduce( - (acc: Array | null, tipPosY) => { + (acc: string[] | null, tipPosY) => { const wellForTip = findWellAt(labwareDef, x, tipPosY) + if (acc === null || !wellForTip) { return null } + return acc.concat(wellForTip) }, [] ) - return wellsAccessed } diff --git a/shared-data/js/helpers/getWellTotalVolume.js b/shared-data/js/helpers/getWellTotalVolume.ts similarity index 92% rename from shared-data/js/helpers/getWellTotalVolume.js rename to shared-data/js/helpers/getWellTotalVolume.ts index 81de50969ac..6e72ce1e110 100644 --- a/shared-data/js/helpers/getWellTotalVolume.js +++ b/shared-data/js/helpers/getWellTotalVolume.ts @@ -1,16 +1,16 @@ -// @flow import type { LabwareDefinition2 } from '../types' - export const getWellTotalVolume = ( labwareDef: LabwareDefinition2, wellName: string -): ?number => { +): number | null | undefined => { const well = labwareDef.wells[wellName] + if (!well) { console.warn( `No well "${wellName}" found for labware ${labwareDef?.namespace}/${labwareDef?.parameters?.loadName}/${labwareDef?.version}"` ) return null } + return well.totalLiquidVolume } diff --git a/shared-data/js/helpers/index.js b/shared-data/js/helpers/index.ts similarity index 94% rename from shared-data/js/helpers/index.js rename to shared-data/js/helpers/index.ts index fb315ed5f36..86708771ecf 100644 --- a/shared-data/js/helpers/index.js +++ b/shared-data/js/helpers/index.ts @@ -1,12 +1,13 @@ -// @flow import assert from 'assert' import uniq from 'lodash/uniq' + import { OPENTRONS_LABWARE_NAMESPACE } from '../constants' import type { LabwareDefinition2 } from '../types' export { getWellNamePerMultiTip } from './getWellNamePerMultiTip' export { getWellTotalVolume } from './getWellTotalVolume' export { wellIsRect } from './wellIsRect' + export * from './volume' export * from './wellSets' @@ -44,6 +45,7 @@ export const getLabwareDisplayName = ( ) { return `(Retired) ${displayName}` } + return displayName } @@ -75,15 +77,14 @@ export const intToAlphabetLetter = ( i: number, lowerCase: boolean = false ): string => String.fromCharCode((lowerCase ? 96 : 65) + i) - // These utils are great candidates for unit tests export const toWellName = ({ rowNum, colNum, -}: {| - rowNum: number, - colNum: number, -|}): string => String.fromCharCode(rowNum + 65) + (colNum + 1) +}: { + rowNum: number + colNum: number +}): string => String.fromCharCode(rowNum + 65) + (colNum + 1) function _parseWell(well: string): [string, number] { const res = well.match(/([A-Z]+)(\d+)/) @@ -108,6 +109,7 @@ function _parseWell(well: string): [string, number] { */ export function sortWells(a: string, b: string): number { const [letterA, numberA] = _parseWell(a) + const [letterB, numberB] = _parseWell(b) if (numberA !== numberB) { @@ -122,15 +124,16 @@ export function sortWells(a: string, b: string): number { return letterA > letterB ? 1 : -1 } -export function splitWellsOnColumn( - sortedArray: Array -): Array> { - return sortedArray.reduce((acc, curr) => { +export function splitWellsOnColumn(sortedArray: string[]): string[][] { + return sortedArray.reduce((acc, curr) => { const lastColumn = acc.slice(-1) + if (lastColumn === undefined || lastColumn.length === 0) { return [[curr]] } + const lastEle = lastColumn[0].slice(-1)[0].slice(1) + if (Number(curr.slice(1)) > Number(lastEle)) { return [...acc, [curr]] } else { @@ -149,9 +152,10 @@ export const getWellDepth = ( // TODO: Ian 2019-07-13 return {[string: well]: offset} to support multi-offset export const getWellsDepth = ( labwareDef: LabwareDefinition2, - wells: Array + wells: string[] ): number => { const offsets = wells.map(well => getWellDepth(labwareDef, well)) + if (uniq(offsets).length !== 1) { console.warn( `expected wells ${JSON.stringify( diff --git a/shared-data/js/helpers/volume.js b/shared-data/js/helpers/volume.ts similarity index 83% rename from shared-data/js/helpers/volume.js rename to shared-data/js/helpers/volume.ts index b85a04395f0..69a4f4e5c74 100644 --- a/shared-data/js/helpers/volume.js +++ b/shared-data/js/helpers/volume.ts @@ -1,6 +1,6 @@ -// @flow // utilities for working with volumes in µL import round from 'lodash/round' + import type { LabwareVolumeUnits } from '../types' const SCALE_BY_UNITS = { @@ -11,7 +11,7 @@ const SCALE_BY_UNITS = { export function getDisplayVolume( volumeInMicroliters: number, - displayUnits?: LabwareVolumeUnits = 'µL', + displayUnits: LabwareVolumeUnits = 'µL', digits?: number ): string { const volume = volumeInMicroliters / SCALE_BY_UNITS[displayUnits] @@ -21,11 +21,15 @@ export function getDisplayVolume( export function getAsciiVolumeUnits(displayUnits: LabwareVolumeUnits): string { if (displayUnits === 'µL') return 'uL' + return displayUnits } -export function ensureVolumeUnits(maybeUnits: ?string): LabwareVolumeUnits { +export function ensureVolumeUnits( + maybeUnits: string | null | undefined +): LabwareVolumeUnits { if (maybeUnits === 'mL' || maybeUnits === 'ml') return 'mL' if (maybeUnits === 'L' || maybeUnits === 'l') return 'L' + return 'µL' } diff --git a/shared-data/js/helpers/wellIsRect.js b/shared-data/js/helpers/wellIsRect.ts similarity index 96% rename from shared-data/js/helpers/wellIsRect.js rename to shared-data/js/helpers/wellIsRect.ts index c9504e0867b..68d80d75704 100644 --- a/shared-data/js/helpers/wellIsRect.js +++ b/shared-data/js/helpers/wellIsRect.ts @@ -1,4 +1,3 @@ -// @flow import type { WellDefinition } from '../types' /** Well is either rect or circle. Depends on whether `diameter` exists */ diff --git a/shared-data/js/helpers/wellSets.js b/shared-data/js/helpers/wellSets.ts similarity index 84% rename from shared-data/js/helpers/wellSets.js rename to shared-data/js/helpers/wellSets.ts index 5d25b4aa496..8460640ade2 100644 --- a/shared-data/js/helpers/wellSets.js +++ b/shared-data/js/helpers/wellSets.ts @@ -1,4 +1,3 @@ -// @flow // A "well set" is an array of wells corresponding to each tip of an 8 channel pipette. // Eg ['A1', 'C1', 'E1', 'G1', 'I1', 'K1', 'M1', 'O1'] is a well set in a 384 plate. // @@ -12,18 +11,20 @@ // A 384 plate has 48 well sets, 2 for each column b/c it has staggered columns. // // If a labware has no possible well sets, then it is not compatible with multi-channel pipettes. -import { getLabwareDefURI } from '@opentrons/shared-data' +import { getLabwareDefURI } from '.' import uniq from 'lodash/uniq' + import { getWellNamePerMultiTip } from './getWellNamePerMultiTip' import type { LabwareDefinition2, PipetteNameSpecs } from '../types' -type WellSetByPrimaryWell = Array> +type WellSetByPrimaryWell = string[][] // Compute all well sets for a labware def (non-memoized) function _getAllWellSetsForLabware( labwareDef: LabwareDefinition2 ): WellSetByPrimaryWell { - const allWells: Array = Object.keys(labwareDef.wells) + const allWells: string[] = Object.keys(labwareDef.wells) + return allWells.reduce( (acc: WellSetByPrimaryWell, well: string): WellSetByPrimaryWell => { const wellSet = getWellNamePerMultiTip(labwareDef, well) @@ -34,54 +35,63 @@ function _getAllWellSetsForLabware( } // creates memoized getAllWellSetsForLabware + getWellSetForMultichannel fns. -export type WellSetHelpers = {| +export interface WellSetHelpers { getAllWellSetsForLabware: ( labwareDef: LabwareDefinition2 - ) => WellSetByPrimaryWell, + ) => WellSetByPrimaryWell + getWellSetForMultichannel: ( labwareDef: LabwareDefinition2, well: string - ) => ?Array, + ) => string[] | null | undefined + canPipetteUseLabware: ( pipetteSpec: PipetteNameSpecs, labwareDef: LabwareDefinition2 - ) => boolean, -|} + ) => boolean +} + export const makeWellSetHelpers = (): WellSetHelpers => { - const cache: { - [labwareDefURI: string]: ?{| - labwareDef: LabwareDefinition2, - wellSetByPrimaryWell: WellSetByPrimaryWell, - |}, - ... - } = {} + const cache: Partial<{ + [labwareDefURI: string]: { + labwareDef: LabwareDefinition2 + wellSetByPrimaryWell: WellSetByPrimaryWell + } | null + }> = {} const getAllWellSetsForLabware = ( labwareDef: LabwareDefinition2 ): WellSetByPrimaryWell => { const labwareDefURI = getLabwareDefURI(labwareDef) const c = cache[labwareDefURI] + // use cached version only if labwareDef is shallowly equal, in case // custom labware defs are changed without giving them a new URI if (c && c.labwareDef === labwareDef) { return c.wellSetByPrimaryWell } + const wellSetByPrimaryWell = _getAllWellSetsForLabware(labwareDef) - cache[labwareDefURI] = { labwareDef, wellSetByPrimaryWell } + + cache[labwareDefURI] = { + labwareDef, + wellSetByPrimaryWell, + } return wellSetByPrimaryWell } const getWellSetForMultichannel = ( labwareDef: LabwareDefinition2, well: string - ): ?Array => { + ): string[] | null | undefined => { /** Given a well for a labware, returns the well set it belongs to (or null) * for 8-channel access. * Ie: C2 for 96-flat => ['A2', 'B2', 'C2', ... 'H2'] * Or A1 for trough => ['A1', 'A1', 'A1', ...] **/ const allWellSets = getAllWellSetsForLabware(labwareDef) - return allWellSets.find((wellSet: Array) => wellSet.includes(well)) + + return allWellSets.find((wellSet: string[]) => wellSet.includes(well)) } const canPipetteUseLabware = ( diff --git a/shared-data/js/index.js b/shared-data/js/index.ts similarity index 95% rename from shared-data/js/index.js rename to shared-data/js/index.ts index 15c9a49a8ee..7fab31cfaf4 100644 --- a/shared-data/js/index.js +++ b/shared-data/js/index.ts @@ -1,5 +1,3 @@ -// @flow - export * from './constants' export * from './getLabware' export * from './helpers' diff --git a/shared-data/js/labwareTools/__tests__/__snapshots__/createIrregularLabware.test.js.snap b/shared-data/js/labwareTools/__tests__/__snapshots__/createIrregularLabware.test.ts.snap similarity index 100% rename from shared-data/js/labwareTools/__tests__/__snapshots__/createIrregularLabware.test.js.snap rename to shared-data/js/labwareTools/__tests__/__snapshots__/createIrregularLabware.test.ts.snap diff --git a/shared-data/js/labwareTools/__tests__/__snapshots__/createLabware.test.js.snap b/shared-data/js/labwareTools/__tests__/__snapshots__/createLabware.test.ts.snap similarity index 100% rename from shared-data/js/labwareTools/__tests__/__snapshots__/createLabware.test.js.snap rename to shared-data/js/labwareTools/__tests__/__snapshots__/createLabware.test.ts.snap diff --git a/shared-data/js/labwareTools/__tests__/createDefaultDisplayName.test.js b/shared-data/js/labwareTools/__tests__/createDefaultDisplayName.test.ts similarity index 84% rename from shared-data/js/labwareTools/__tests__/createDefaultDisplayName.test.js rename to shared-data/js/labwareTools/__tests__/createDefaultDisplayName.test.ts index e1368916f7f..5ec5b62d272 100644 --- a/shared-data/js/labwareTools/__tests__/createDefaultDisplayName.test.js +++ b/shared-data/js/labwareTools/__tests__/createDefaultDisplayName.test.ts @@ -1,9 +1,17 @@ -import { createDefaultDisplayName } from '../index' +import { createDefaultDisplayName } from '..' + +import type { RegularNameProps } from '..' + +interface TestCase { + testName: string + args: RegularNameProps + expected: string +} describe('createDefaultDisplayName', () => { - const testCases = [ + const testCases: TestCase[] = [ { - testName: 'minimal case', + testName: 'handle the minimal case', args: { displayCategory: 'wellPlate', displayVolumeUnits: 'µL', @@ -14,7 +22,7 @@ describe('createDefaultDisplayName', () => { expected: 'Generic 6 Well Plate 123 µL', }, { - testName: 'decimal in volume', + testName: 'handle a decimal in volume', args: { displayCategory: 'wellPlate', displayVolumeUnits: 'µL', @@ -36,7 +44,7 @@ describe('createDefaultDisplayName', () => { expected: 'Generic 80 Well Plate 123 µL', }, { - testName: 'tube rack (example of a different displayCategory)', + testName: 'handle tube rack (example of a different displayCategory)', args: { displayCategory: 'tubeRack', displayVolumeUnits: 'µL', @@ -47,7 +55,7 @@ describe('createDefaultDisplayName', () => { expected: 'Generic 6 Tube Rack 123 µL', }, { - testName: 'should append loadNamePostfix', + testName: 'append loadNamePostfix', args: { displayCategory: 'wellPlate', displayVolumeUnits: 'µL', @@ -96,7 +104,7 @@ describe('createDefaultDisplayName', () => { ] testCases.forEach(({ testName, args, expected }) => { - it(testName, () => { + it(`should ${testName}`, () => { expect(createDefaultDisplayName(args)).toEqual(expected) }) }) diff --git a/shared-data/js/labwareTools/__tests__/createIrregularLabware.test.js b/shared-data/js/labwareTools/__tests__/createIrregularLabware.test.ts similarity index 91% rename from shared-data/js/labwareTools/__tests__/createIrregularLabware.test.js rename to shared-data/js/labwareTools/__tests__/createIrregularLabware.test.ts index 682768ee43a..f0fefe7339c 100644 --- a/shared-data/js/labwareTools/__tests__/createIrregularLabware.test.js +++ b/shared-data/js/labwareTools/__tests__/createIrregularLabware.test.ts @@ -1,15 +1,18 @@ import omit from 'lodash/omit' import range from 'lodash/range' +import { splitWellsOnColumn, sortWells } from '../../helpers/index' +import fixture_irregular_example_1 from '../../../labware/fixtures/2/fixture_irregular_example_1.json' + import { createIrregularLabware, _irregularWellName, _generateIrregularLoadName, _calculateWellCoord, -} from '../index.js' -import { splitWellsOnColumn, sortWells } from '../../helpers/index.js' +} from '..' -import fixture_irregular_example_1 from '../../../labware/fixtures/2/fixture_irregular_example_1.json' +import type { LabwareDefinition2, LabwareWellProperties } from '../../types' +import type { IrregularLabwareProps } from '..' // NOTE: loadName needs to be replaced here b/c fixture has a non-default loadName const exampleLabware1 = { @@ -18,25 +21,30 @@ const exampleLabware1 = { ...fixture_irregular_example_1.parameters, loadName: 'generic_55_tuberack_50x3ml_5x10ml', }, -} +} as LabwareDefinition2 describe('test helper functions', () => { it('Well name generated correctly', () => { const grid = { row: 2, column: 2 } + const gridStart = [ { rowStart: 'A', colStart: '1', rowStride: 1, colStride: 2 }, { rowStart: 'B', colStart: '1', rowStride: 3, colStride: 1 }, ] const expected1 = ['A1', 'B1', 'A3', 'B3'] const expected2 = ['B1', 'E1', 'B2', 'E2'] - let idx = 0 + range(grid.column).forEach(colIdx => { range(grid.row).forEach(rowIdx => { + const idx = colIdx * grid.row + rowIdx + const wellName1 = _irregularWellName(rowIdx, colIdx, gridStart[0]) + expect(expected1[idx]).toEqual(wellName1) + const wellName2 = _irregularWellName(rowIdx, colIdx, gridStart[1]) + expect(expected2[idx]).toEqual(wellName2) - idx += 1 }) }) }) @@ -51,7 +59,8 @@ describe('test helper functions', () => { const well = [ omit(exampleLabware1.wells.A1, ['x', 'y', 'z']), omit(exampleLabware1.wells.B1, ['x', 'y', 'z']), - ] + ] as LabwareWellProperties[] + const expectedX1 = [1, 11, 21, 31, 41] const expectedY1 = [0.5] const expectedX2 = [1, 15, 29, 43, 57] @@ -66,6 +75,7 @@ describe('test helper functions', () => { offset, well[0] ) + expect(well1.x).toBeCloseTo(expectedX1[colIdx], 2) expect(well1.y).toBeCloseTo(expectedY1[rowIdx], 2) expect(well1.z).toBeCloseTo(offset.z - well[0].depth, 2) @@ -77,6 +87,7 @@ describe('test helper functions', () => { offset, well[1] ) + expect(well2.x).toBeCloseTo(expectedX2[colIdx], 2) expect(well2.y).toBeCloseTo(expectedY2[rowIdx], 2) expect(well2.z).toBeCloseTo(offset.z - well[1].depth, 2) @@ -86,8 +97,9 @@ describe('test helper functions', () => { }) describe('test createIrregularLabware function', () => { - let labware1 - let labware1Args + let labware1: LabwareDefinition2 + let labware1Args: IrregularLabwareProps + beforeEach(() => { labware1Args = { namespace: 'fixture', @@ -162,12 +174,12 @@ describe('test createIrregularLabware function', () => { depth: 20, shape: 'circular', totalLiquidVolume: 400, - }, + } as LabwareWellProperties, { depth: 110, shape: 'circular', totalLiquidVolume: 2000, - }, + } as LabwareWellProperties, ], totalWellCount: 10, units: 'µL', @@ -181,7 +193,13 @@ describe('test createIrregularLabware function', () => { it('labware loadName generated correctly for multi-grid labware in other units', () => { const loadName = _generateIrregularLoadName({ grid: [{ row: 3, column: 2 }], - well: [{ depth: 20, shape: 'circular', totalLiquidVolume: 4000 }], + well: [ + { + depth: 20, + shape: 'circular', + totalLiquidVolume: 4000, + } as LabwareWellProperties, + ], totalWellCount: 6, units: 'mL', displayCategory: 'wellPlate', @@ -200,7 +218,6 @@ describe('test createIrregularLabware function', () => { { x: 15, y: -9999, z: 69.48 }, ], } - expect(() => createIrregularLabware(args)).toThrowErrorMatchingSnapshot() expect(() => createIrregularLabware({ ...args, strict: false }) diff --git a/shared-data/js/labwareTools/__tests__/createLabware.test.js b/shared-data/js/labwareTools/__tests__/createLabware.test.ts similarity index 79% rename from shared-data/js/labwareTools/__tests__/createLabware.test.js rename to shared-data/js/labwareTools/__tests__/createLabware.test.ts index a349d4beda1..549c4783dee 100644 --- a/shared-data/js/labwareTools/__tests__/createLabware.test.js +++ b/shared-data/js/labwareTools/__tests__/createLabware.test.ts @@ -1,11 +1,17 @@ import omit from 'lodash/omit' import range from 'lodash/range' - import { createRegularLabware } from '..' - import fixture_regular_example_1 from '../../../labware/fixtures/2/fixture_regular_example_1.json' import fixture_regular_example_2 from '../../../labware/fixtures/2/fixture_regular_example_2.json' +import type { + LabwareDefinition2, + LabwareWellProperties, + LabwareOffset, +} from '../../types' + +import type { RegularLabwareProps } from '..' + // NOTE: loadName needs to be replaced here b/c fixture has a non-default loadName const exampleLabware1 = { ...fixture_regular_example_1, @@ -13,7 +19,7 @@ const exampleLabware1 = { ...fixture_regular_example_1.parameters, loadName: 'opentrons_2_wellplate_100ul', }, -} +} as LabwareDefinition2 const exampleLabware2 = { ...fixture_regular_example_2, @@ -21,20 +27,30 @@ const exampleLabware2 = { ...fixture_regular_example_2.parameters, loadName: 'generic_6_wellplate_1ml', }, -} +} as LabwareDefinition2 describe('createLabware', () => { - let labware1 - let labware2 - let labware2Args - let well1 - let well2 - let offset1 - let offset2 + let labware1: LabwareDefinition2 + let labware2: LabwareDefinition2 + let labware2Args: RegularLabwareProps + let well1: LabwareWellProperties + let well2: LabwareWellProperties + let offset1: LabwareOffset + let offset2: LabwareOffset beforeEach(() => { - well1 = omit(exampleLabware1.wells.A1, ['x', 'y', 'z']) - well2 = omit(exampleLabware2.wells.A1, ['x', 'y', 'z']) + well1 = omit(exampleLabware1.wells.A1, [ + 'x', + 'y', + 'z', + ]) as LabwareWellProperties + + well2 = omit(exampleLabware2.wells.A1, [ + 'x', + 'y', + 'z', + ]) as LabwareWellProperties + offset1 = { x: 10, y: 10, z: 55 } offset2 = { x: 10, y: 10, z: 40 } @@ -49,24 +65,25 @@ describe('createLabware', () => { brand: exampleLabware1.brand, namespace: 'fixture', }) - labware2Args = { metadata: exampleLabware2.metadata, parameters: exampleLabware2.parameters, dimensions: exampleLabware2.dimensions, offset: offset2, - grid: { row: 3, column: 2 }, - spacing: { row: 10, column: 10 }, + grid: { + row: 3, + column: 2, + }, + spacing: { + row: 10, + column: 10, + }, well: well2, namespace: 'fixture', } labware2 = createRegularLabware(labware2Args) }) - afterEach(() => { - jest.clearAllMocks() - }) - it('snapshot tests', () => { expect(labware1).toEqual(exampleLabware1) expect(labware2).toEqual(exampleLabware2) @@ -79,8 +96,8 @@ describe('createLabware', () => { it('well XYZ generates correctly', () => { const spacing = { row: 10, column: 10 } const grid = { row: 3, column: 2 } - const { yDimension } = exampleLabware2.dimensions + const labware3 = createRegularLabware({ metadata: exampleLabware2.metadata, parameters: exampleLabware2.parameters, @@ -106,6 +123,7 @@ describe('createLabware', () => { labware3.ordering.forEach((column, cIndex) => { column.forEach((wellName, rIndex) => { const well = labware3.wells[wellName] + expect(well.x).toBeCloseTo(expectedXByCol[cIndex], 2) expect(well.y).toBeCloseTo(expectedYByRow[rIndex], 2) expect(well.z).toBeCloseTo(offset2.z - well.depth, 2) @@ -117,7 +135,10 @@ describe('createLabware', () => { const args = { ...labware2Args, // this spacing should make negative well `y` value and fail schema validation - spacing: { row: 999, column: 999 }, + spacing: { + row: 999, + column: 999, + }, } expect(() => createRegularLabware(args)).toThrowErrorMatchingSnapshot() diff --git a/shared-data/js/labwareTools/index.js b/shared-data/js/labwareTools/index.ts similarity index 79% rename from shared-data/js/labwareTools/index.js rename to shared-data/js/labwareTools/index.ts index 0e26b906788..82ae2b5b0ba 100644 --- a/shared-data/js/labwareTools/index.js +++ b/shared-data/js/labwareTools/index.ts @@ -1,10 +1,10 @@ -// @flow import Ajv from 'ajv' import flatten from 'lodash/flatten' import range from 'lodash/range' import round from 'lodash/round' import labwareSchema from '../../labware/schemas/2.json' + import { toWellName, sortWells, @@ -30,70 +30,70 @@ import type { // NOTE: leaving this 'beta' to reduce conflicts with future labware cloud namespaces export const DEFAULT_CUSTOM_NAMESPACE = 'custom_beta' + const SCHEMA_VERSION = 2 const DEFAULT_BRAND_NAME = 'generic' -type Cell = {| - row: number, - column: number, -|} +interface Cell { + row: number + column: number +} // This represents creating a "range" of well names with step intervals included // For example, starting at well "A1" with a column stride of 2 would result in // the grid name being ordered as: "A1", "B1"..."A3", "B3"..etc -type GridStart = {| - rowStart: string, - colStart: string, - rowStride: number, - colStride: number, -|} - -type InputParams = $Rest - -type InputWellGroup = $Rest - -export type BaseLabwareProps = {| - metadata: Metadata, - parameters: InputParams, - dimensions: Dimensions, - brand?: Brand, - version?: number, - namespace?: string, - loadNamePostfix?: Array, - strict?: ?boolean, // If true, throws error on failed validation -|} - -export type RegularLabwareProps = {| - ...BaseLabwareProps, - offset: Offset, - grid: Cell, - spacing: Cell, - well: InputWell, - group?: InputWellGroup, -|} - -export type IrregularLabwareProps = {| - ...BaseLabwareProps, - offset: Array, - grid: Array, - spacing: Array, - well: Array, - gridStart: Array, - group?: Array, -|} +interface GridStart { + rowStart: string + colStart: string + rowStride: number + colStride: number +} + +type InputParams = Omit + +type InputWellGroup = Omit + +export interface BaseLabwareProps { + metadata: Metadata + parameters: InputParams + dimensions: Dimensions + brand?: Brand + version?: number + namespace?: string + loadNamePostfix?: string[] + strict?: boolean | null // If true, throws error on failed validation +} + +export interface RegularLabwareProps extends BaseLabwareProps { + offset: Offset + grid: Cell + spacing: Cell + well: InputWell + group?: InputWellGroup +} + +export interface IrregularLabwareProps extends BaseLabwareProps { + offset: Offset[] + grid: Cell[] + spacing: Cell[] + well: InputWell[] + gridStart: GridStart[] + group?: InputWellGroup[] +} const ajv = new Ajv({ allErrors: true, jsonPointers: true }) const validate = ajv.compile(labwareSchema) function validateDefinition( definition: Definition, - strict: ?boolean = true + strict: boolean | null | undefined = true ): Definition { const valid = validate(definition) if (!valid) { console.error('Definition:', definition) console.error('Validation Errors:', validate.errors) + if (strict) { throw new Error( 'Generated labware failed to validate, please check your inputs' @@ -112,7 +112,10 @@ export function _irregularWellName( const rowNum = rowIdx * gridStart.rowStride + gridStart.rowStart.charCodeAt(0) - 65 const colNum = colIdx * gridStart.colStride + parseInt(gridStart.colStart) - 1 - return toWellName({ rowNum, colNum }) + return toWellName({ + rowNum, + colNum, + }) } export function _calculateWellCoord( @@ -129,25 +132,29 @@ export function _calculateWellCoord( } // NOTE: Ian 2019-04-16 this silly "if circular" is to make Flow happy if (well.shape === 'circular') return { ...well, ...coords } - return { - ...well, - ...coords, - } + return { ...well, ...coords } +} + +interface Layout { + wells: WellMap + groups: WellGroup[] } function determineIrregularLayout( - grids: Array, - spacing: Array, - offset: Array, - gridStart: Array, - wells: Array, - group: Array = [] -): { wells: WellMap, groups: Array } { - return grids.reduce( + grids: Cell[], + spacing: Cell[], + offset: Offset[], + gridStart: GridStart[], + wells: InputWell[], + group: InputWellGroup[] = [] +): Layout { + return grids.reduce( (result, gridObj, gridIdx) => { const reverseRowIdx = range(gridObj.row - 1, -1) - const inputGroup = group[gridIdx] || { metadata: {} } - const currentGroup = { ...inputGroup, wells: [] } + const inputGroup = group[gridIdx] || { + metadata: {}, + } + const currentGroup: WellGroup = { ...inputGroup, wells: [] } range(gridObj.column).forEach(colIdx => { range(gridObj.row).forEach(rowIdx => { @@ -156,6 +163,7 @@ function determineIrregularLayout( colIdx, gridStart[gridIdx] ) + currentGroup.wells.push(wellName) result.wells[wellName] = _calculateWellCoord( reverseRowIdx[rowIdx], @@ -166,21 +174,26 @@ function determineIrregularLayout( ) }) }) - - return { wells: result.wells, groups: [...result.groups, currentGroup] } + return { + wells: result.wells, + groups: [...result.groups, currentGroup], + } }, - { wells: {}, groups: [] } + { + wells: {}, + groups: [], + } ) } export function _generateIrregularLoadName(args: { - grid: Array, - well: Array, - totalWellCount: number, - units: VolumeUnits, - brand: string, - displayCategory: string, - loadNamePostfix?: Array, + grid: Cell[] + well: InputWell[] + totalWellCount: number + units: VolumeUnits + brand: string + displayCategory: string + loadNamePostfix?: string[] }): string { const { grid, @@ -195,10 +208,8 @@ export function _generateIrregularLoadName(args: { const wellComboArray = grid.map((gridObj, gridIdx) => { const numWells = gridObj.row * gridObj.column const wellVolume = getDisplayVolume(well[gridIdx].totalLiquidVolume, units) - return `${numWells}x${wellVolume}${loadNameUnits}` }) - return joinLoadName([ brand, totalWellCount, @@ -209,21 +220,22 @@ export function _generateIrregularLoadName(args: { } // Decide order of wells for single grid containers -function determineOrdering(grid: Cell): Array> { +function determineOrdering(grid: Cell): string[][] { const ordering = range(grid.column).map(colNum => - range(grid.row).map(rowNum => toWellName({ rowNum, colNum })) + range(grid.row).map(rowNum => + toWellName({ + rowNum, + colNum, + }) + ) ) - return ordering } // Decide order of wells for multi-grid containers -export function determineIrregularOrdering( - wellsArray: Array -): Array> { +export function determineIrregularOrdering(wellsArray: string[]): string[][] { const sortedArray = wellsArray.sort(sortWells) const ordering = splitWellsOnColumn(sortedArray) - return ordering } @@ -231,13 +243,12 @@ export function determineIrregularOrdering( // Will return a nested object of all well objects for a labware function calculateCoordinates( wellProps: InputWell, - ordering: Array>, + ordering: string[][], spacing: Cell, offset: Offset, dimensions: Dimensions ): WellMap { const { yDimension } = dimensions - return ordering.reduce((wellMap, column, cIndex) => { return column.reduce( (colWellMap, wellName, rIndex) => ({ @@ -255,7 +266,11 @@ function calculateCoordinates( } function ensureBrand(brand?: Brand): Brand { - return brand || { brand: DEFAULT_BRAND_NAME } + return ( + brand || { + brand: DEFAULT_BRAND_NAME, + } + ) } // joins the input array with _ to create a name, making sure to lowercase the @@ -270,14 +285,14 @@ function joinLoadName( .replace(/[^a-z0-9_.]/g, '') } -type RegularNameProps = { - displayCategory: string, - displayVolumeUnits: VolumeUnits, - gridRows: number, - gridColumns: number, - totalLiquidVolume: number, - brandName?: string, - loadNamePostfix?: Array, +export interface RegularNameProps { + displayCategory: string + displayVolumeUnits: VolumeUnits + gridRows: number + gridColumns: number + totalLiquidVolume: number + brandName?: string + loadNamePostfix?: string[] } export function createRegularLoadName(args: RegularNameProps): string { @@ -305,6 +320,7 @@ export function createRegularLoadName(args: RegularNameProps): string { const capitalize = (_s: string): string => { const s = _s.trim() + return `${s.slice(0, 1).toUpperCase()}${s.slice(1)}` } @@ -333,7 +349,6 @@ export function createDefaultDisplayName(args: RegularNameProps): string { .replace(/\s+/g, ' ') .trim() } - // Generator function for labware definitions within a regular grid format // e.g. well plates, regular tuberacks (NOT 15_50ml) etc. // For further info on these parameters look at labware examples in __tests__ @@ -345,7 +360,9 @@ export function createRegularLabware(args: RegularLabwareProps): Definition { const namespace = args.namespace || DEFAULT_CUSTOM_NAMESPACE const ordering = determineOrdering(grid) const brand = ensureBrand(args.brand) - const groupBase = args.group || { metadata: {} } + const groupBase = args.group || { + metadata: {}, + } const metadata = { ...args.metadata, displayVolumeUnits: ensureVolumeUnits(args.metadata.displayVolumeUnits), @@ -359,7 +376,6 @@ export function createRegularLabware(args: RegularLabwareProps): Definition { brandName: brand.brand, loadNamePostfix, }) - return validateDefinition( { ordering, @@ -372,12 +388,15 @@ export function createRegularLabware(args: RegularLabwareProps): Definition { namespace, version, schemaVersion: SCHEMA_VERSION, - cornerOffsetFromSlot: { x: 0, y: 0, z: 0 }, + cornerOffsetFromSlot: { + x: 0, + y: 0, + z: 0, + }, }, strict ) } - // Generator function for labware definitions within an irregular grid format // e.g. crystallization plates, 15_50ml tuberacks and anything with multiple "grids" export function createIrregularLabware( @@ -422,7 +441,11 @@ export function createIrregularLabware( namespace, version, schemaVersion: SCHEMA_VERSION, - cornerOffsetFromSlot: { x: 0, y: 0, z: 0 }, + cornerOffsetFromSlot: { + x: 0, + y: 0, + z: 0, + }, }, strict ) diff --git a/shared-data/js/modules.js b/shared-data/js/modules.ts similarity index 69% rename from shared-data/js/modules.js rename to shared-data/js/modules.ts index 1c80b064b49..4b9a7fe5b64 100644 --- a/shared-data/js/modules.js +++ b/shared-data/js/modules.ts @@ -1,4 +1,3 @@ -// @flow import magneticModuleV1 from '../module/definitions/2/magneticModuleV1.json' import magneticModuleV2 from '../module/definitions/2/magneticModuleV2.json' import temperatureModuleV1 from '../module/definitions/2/temperatureModuleV1.json' @@ -19,49 +18,56 @@ import { import type { ModuleModel, ModuleRealType, ModuleType } from './types' // The module objects in v2 Module Definitions representing a single module model -type Coordinates = {| - x: number, - y: number, - z?: number, -|} +interface Coordinates { + x: number + y: number + z?: number +} + type AffineTransform = [number, number, number] -export type ModuleDef2 = {| - moduleType: ModuleRealType, - model: ModuleModel, - labwareOffset: Coordinates, - dimensions: {| - bareOverallHeight: number, - overLabwareHeight: number, - lidHeight?: number, - |}, - calibrationPoint: Coordinates, - displayName: string, - quirks: Array, - slotTransforms: {| - [deckDef: string]: {| - [slot: string]: {| - [transformName: string]: AffineTransform, - |}, - |}, - |}, - compatibleWith: Array, -|} + +export interface ModuleDef2 { + moduleType: ModuleRealType + model: ModuleModel + labwareOffset: Coordinates + dimensions: { + bareOverallHeight: number + overLabwareHeight: number + lidHeight?: number + } + calibrationPoint: Coordinates + displayName: string + quirks: string[] + slotTransforms: { + [deckDef: string]: { + [slot: string]: { + [transformName: string]: AffineTransform + } + } + } + compatibleWith: ModuleModel[] +} // TODO IMMEDIATELY: Phase out code that uses legacy models export const getModuleDef2 = (moduleModel: ModuleModel): ModuleDef2 => { switch (moduleModel) { case MAGNETIC_MODULE_V1: - return magneticModuleV1 + return magneticModuleV1 as ModuleDef2 + case MAGNETIC_MODULE_V2: - return magneticModuleV2 + return (magneticModuleV2 as unknown) as ModuleDef2 + case TEMPERATURE_MODULE_V1: - return temperatureModuleV1 + return temperatureModuleV1 as ModuleDef2 + case TEMPERATURE_MODULE_V2: - return temperatureModuleV2 + return (temperatureModuleV2 as unknown) as ModuleDef2 + case THERMOCYCLER_MODULE_V1: - return thermocyclerModuleV1 + return thermocyclerModuleV1 as ModuleDef2 + default: - throw new Error(`Invalid module model ${moduleModel}`) + throw new Error(`Invalid module model ${moduleModel as string}`) } } @@ -69,12 +75,15 @@ export function normalizeModuleModel(legacyModule: ModuleType): ModuleModel { switch (legacyModule) { case TEMPDECK: return TEMPERATURE_MODULE_V1 + case MAGDECK: return MAGNETIC_MODULE_V1 + case THERMOCYCLER: return THERMOCYCLER_MODULE_V1 + default: - throw new Error(`Invalid legacy module model ${legacyModule}`) + throw new Error(`Invalid legacy module model ${legacyModule as string}`) } } diff --git a/shared-data/js/pipettes.js b/shared-data/js/pipettes.ts similarity index 54% rename from shared-data/js/pipettes.js rename to shared-data/js/pipettes.ts index a90eef9f134..69e80c0438a 100644 --- a/shared-data/js/pipettes.js +++ b/shared-data/js/pipettes.ts @@ -1,49 +1,56 @@ -// @flow import pipetteNameSpecs from '../pipette/definitions/pipetteNameSpecs.json' import pipetteModelSpecs from '../pipette/definitions/pipetteModelSpecs.json' + import type { PipetteNameSpecs, PipetteModelSpecs } from './types' type SortableProps = 'maxVolume' | 'channels' +// TODO(mc, 2021-04-30): use these types, pulled directly from the JSON, +// to simplify return types in this module and possibly remove some `null`s +export type PipetteName = keyof typeof pipetteNameSpecs +export type PipetteModel = keyof typeof pipetteModelSpecs.config + // models sorted by channels and then volume by default -const ALL_PIPETTE_NAMES: Array = Object.keys(pipetteNameSpecs).sort( - comparePipettes(['channels', 'maxVolume']) -) +const ALL_PIPETTE_NAMES: PipetteName[] = (Object.keys( + pipetteNameSpecs +) as PipetteName[]).sort(comparePipettes(['channels', 'maxVolume'])) // use a name like 'p10_single' to get specs true for all models under that name -export function getPipetteNameSpecs(name: string): PipetteNameSpecs | null { - const config = pipetteNameSpecs[name] +export function getPipetteNameSpecs( + name: PipetteName +): PipetteNameSpecs | null { + const config = pipetteNameSpecs[name] as + | Omit + | undefined return config != null ? { ...config, name } : null } // specify a model, eg 'p10_single_v1.3' to get // both the name specs + model-specific specs // NOTE: this should NEVER be used in PD, which is model-agnostic -export function getPipetteModelSpecs(model: string): ?PipetteModelSpecs { +export function getPipetteModelSpecs( + model: PipetteModel +): PipetteModelSpecs | null | undefined { const modelSpecificFields = pipetteModelSpecs.config[model] const modelFields = - modelSpecificFields && getPipetteNameSpecs(modelSpecificFields.name) - + modelSpecificFields && + getPipetteNameSpecs(modelSpecificFields.name as PipetteName) return modelFields && { ...modelFields, ...modelSpecificFields, model } } -export function getAllPipetteNames( - ...sortBy: Array -): Array { +export function getAllPipetteNames(...sortBy: SortableProps[]): PipetteName[] { const models = [...ALL_PIPETTE_NAMES] - if (sortBy.length) models.sort(comparePipettes(sortBy)) - return models } -function comparePipettes(sortBy: Array) { - return (modelA, modelB) => { +function comparePipettes(sortBy: SortableProps[]) { + return (modelA: PipetteName, modelB: PipetteName) => { // any cast is because we know these pipettes exist - const a: PipetteNameSpecs = (getPipetteNameSpecs(modelA): any) - const b: PipetteNameSpecs = (getPipetteNameSpecs(modelB): any) - + const a = getPipetteNameSpecs(modelA) as PipetteNameSpecs + const b = getPipetteNameSpecs(modelB) as PipetteNameSpecs let i + for (i = 0; i < sortBy.length; i++) { const sortKey = sortBy[i] if (a[sortKey] < b[sortKey]) return -1 diff --git a/shared-data/js/schema.js b/shared-data/js/schema.ts similarity index 66% rename from shared-data/js/schema.js rename to shared-data/js/schema.ts index 6195d027623..91822979497 100644 --- a/shared-data/js/schema.js +++ b/shared-data/js/schema.ts @@ -1,15 +1,11 @@ -// @flow const NumberType = { type: 'number', } - const PositiveNumber = { type: 'number', minimum: 0, } - const LabwareFormats = ['96-standard', '384-standard', 'trough', 'irregular'] - // TODO IMMEDIATELY move to labware/flowTypes/ // TODO(mc, 2020-02-21) move to labware/schemas/1.json? delete? // this only appears to be used in a single test file @@ -23,14 +19,27 @@ export const labwareSchemaV1 = { type: 'object', required: ['name', 'format'], properties: { - name: { type: 'string' }, - format: { enum: LabwareFormats }, - - deprecated: { enum: [true] }, - displayName: { type: 'string' }, - displayCategory: { type: 'string' }, - isValidSource: { enum: [false] }, - isTiprack: { enum: [true] }, + name: { + type: 'string', + }, + format: { + enum: LabwareFormats, + }, + deprecated: { + enum: [true], + }, + displayName: { + type: 'string', + }, + displayCategory: { + type: 'string', + }, + isValidSource: { + enum: [false], + }, + isTiprack: { + enum: [true], + }, tipVolume: PositiveNumber, }, }, @@ -38,7 +47,9 @@ export const labwareSchemaV1 = { type: 'array', items: { type: 'array', - items: { type: 'string' }, + items: { + type: 'string', + }, }, }, wells: { @@ -55,10 +66,11 @@ export const labwareSchemaV1 = { x: NumberType, y: NumberType, z: NumberType, - // Optional - diameter: PositiveNumber, // NOTE: presence of diameter indicates a circular well - depth: PositiveNumber, // TODO Ian 2018-03-12: depth should be required, but is missing in MALDI-plate + diameter: PositiveNumber, + // NOTE: presence of diameter indicates a circular well + depth: PositiveNumber, + // TODO Ian 2018-03-12: depth should be required, but is missing in MALDI-plate 'total-liquid-volume': PositiveNumber, }, }, diff --git a/shared-data/index.d.ts b/shared-data/js/types.ts similarity index 76% rename from shared-data/index.d.ts rename to shared-data/js/types.ts index bdfa92803a2..770e6a5559d 100644 --- a/shared-data/index.d.ts +++ b/shared-data/js/types.ts @@ -1,30 +1,27 @@ -// Temporary defs until shared-data is actually converted - -// TODO(IL, 2021-03-24): I'm any-typing some of these where there are several -// other types involved (eg RegularNameProps), -// when we actually convert shared-data we can get rid of the any's - -export function createRegularLabware(args: any): LabwareDefinition2 - -export const LABWAREV2_DO_NOT_LIST: string[] - -export function getDisplayVolume( - volumeInMicroliters: number, - displayUnits?: LabwareVolumeUnits, - digits?: number -): string -export function getLabwareDefURI(def: LabwareDefinition2): string - -export function createRegularLoadName(args: any): string -export function createDefaultDisplayName(args: any): string - -export const SLOT_LENGTH_MM: number -export const SLOT_WIDTH_MM: number +import { + MAGDECK, + TEMPDECK, + THERMOCYCLER, + MAGNETIC_MODULE_V1, + MAGNETIC_MODULE_V2, + TEMPERATURE_MODULE_V1, + TEMPERATURE_MODULE_V2, + THERMOCYCLER_MODULE_V1, + MAGNETIC_MODULE_TYPE, + TEMPERATURE_MODULE_TYPE, + THERMOCYCLER_MODULE_TYPE, + GEN1, + GEN2, + LEFT, + RIGHT, +} from './constants' // TODO Ian 2019-06-04 split this out into eg ../labware/flowTypes/labwareV1.js export interface WellDefinition { - diameter?: number // NOTE: presence of diameter indicates a circular well - depth?: number // TODO Ian 2018-03-12: depth should be required, but is missing in MALDI-plate + diameter?: number + // NOTE: presence of diameter indicates a circular well + depth?: number + // TODO Ian 2018-03-12: depth should be required, but is missing in MALDI-plate height: number length: number width: number @@ -133,9 +130,7 @@ export type LabwareWell = LabwareWellProperties & { // TODO(mc, 2019-03-21): exact object is tough to use with the initial value in // reduce, so leaving this inexact (e.g. `const a: {||} = {}` errors) -export interface LabwareWellMap { - [wellName: string]: LabwareWell -} +export type LabwareWellMap = Record export interface LabwareWellGroupMetadata { displayName?: string @@ -164,46 +159,24 @@ export interface LabwareDefinition2 { groups: LabwareWellGroup[] } -// from constants.js ===== -export const THERMOCYCLER = 'thermocycler' -export const TEMPDECK = 'tempdeck' -export const MAGDECK = 'magdeck' -// these are the Module Def Schema v2 equivalents of the above. They should match the names of JSON definitions -// in shared-data/module/definitions/2. -export const MAGNETIC_MODULE_V1 = 'magneticModuleV1' -export const MAGNETIC_MODULE_V2 = 'magneticModuleV2' -export const TEMPERATURE_MODULE_V1 = 'temperatureModuleV1' -export const TEMPERATURE_MODULE_V2 = 'temperatureModuleV2' -export const THERMOCYCLER_MODULE_V1 = 'thermocyclerModuleV1' - -// pipette display categories -export const GEN2 = 'GEN2' -export const GEN1 = 'GEN1' - -// NOTE: these are NOT module MODELs, they are `moduleType`s. Should match ENUM in module definition file. -export const TEMPERATURE_MODULE_TYPE = 'temperatureModuleType' -export const MAGNETIC_MODULE_TYPE = 'magneticModuleType' -export const THERMOCYCLER_MODULE_TYPE = 'thermocyclerModuleType' - -// ====== - // Module Type corresponds to `moduleType` key in a module definition. Is NOT model. // TODO: IL 2020-02-20 ModuleType is DEPRECATED. Replace all instances with ModuleRealType // (then finally rename ModuleRealType -> ModuleType) export type ModuleType = typeof MAGDECK | typeof TEMPDECK | typeof THERMOCYCLER + export type ModuleRealType = | typeof MAGNETIC_MODULE_TYPE | typeof TEMPERATURE_MODULE_TYPE | typeof THERMOCYCLER_MODULE_TYPE - // ModuleModel corresponds to top-level keys in shared-data/module/definitions/2 - export type MagneticModuleModel = | typeof MAGNETIC_MODULE_V1 | typeof MAGNETIC_MODULE_V2 + export type TemperatureModuleModel = | typeof TEMPERATURE_MODULE_V1 | typeof TEMPERATURE_MODULE_V2 + export type ThermocyclerModuleModel = typeof THERMOCYCLER_MODULE_V1 export type ModuleModel = @@ -243,6 +216,7 @@ export interface DeckFixture { export type CoordinateTuple = [number, number, number] export type UnitDirection = 1 | -1 + export type UnitVectorTuple = [UnitDirection, UnitDirection, UnitDirection] export type DeckSlotId = string @@ -314,7 +288,7 @@ export type PipetteChannels = 1 | 8 export type PipetteDisplayCategory = typeof GEN1 | typeof GEN2 -export const DEFAULT_CUSTOM_NAMESPACE: string +export type PipetteMount = typeof LEFT | typeof RIGHT export interface FlowRateSpec { value: number @@ -344,5 +318,7 @@ export interface PipetteNameSpecs { export interface PipetteModelSpecs extends PipetteNameSpecs { model: string backCompatNames?: string[] - tipLength: { value: number } + tipLength: { + value: number + } } diff --git a/shared-data/package.json b/shared-data/package.json index 8203762fbf7..cd2b921c28a 100755 --- a/shared-data/package.json +++ b/shared-data/package.json @@ -8,8 +8,9 @@ }, "author": "Opentrons Labworks", "license": "Apache-2.0", - "main": "js/index.js", - "types": "index.d.ts", + "source": "js/index.ts", + "types": "lib/js/index.d.ts", + "flow:main": "flow-types/js/index.js.flow", "dependencies": { "ajv": "6.10.2", "lodash": "4.17.15" diff --git a/shared-data/pipette/fixtures/name/index.js b/shared-data/pipette/fixtures/name/index.js index 52d5e99ec90..f3751d0daf1 100644 --- a/shared-data/pipette/fixtures/name/index.js +++ b/shared-data/pipette/fixtures/name/index.js @@ -1,7 +1,10 @@ // @flow +// TODO(mc, 2021-04-27): remove or rewrite this file when PD tests are in TS import pipetteNameSpecFixtures from './pipetteNameSpecFixtures.json' -import type { PipetteNameSpecs } from '../../../js/types' +// import from @opentrons/shared-data instead of relative to pick up +// generated flow types +import type { PipetteNameSpecs } from '@opentrons/shared-data' export const fixtureP10Single: $Shape = pipetteNameSpecFixtures['p10_single'] diff --git a/shared-data/protocol/README.md b/shared-data/protocol/README.md index 0d412299eee..7df080a73c2 100644 --- a/shared-data/protocol/README.md +++ b/shared-data/protocol/README.md @@ -1,7 +1,7 @@ # Schema Bump Checklist 1. Create new JSON schema in `shared-data/protocol/schemas/${schemaVersion}.json` -2. Create flow types for new schema in `shared-data/protocol/flowTypes` +2. Create TS types for new schema in `shared-data/protocol/types` 3. Create or modify Python types as necessary in both `python/opentrons_shared_data/protocol/dev_types` and in the python api `dev_types` in `api/src/opentrons/protocol_api/dev_types.py` 4. Create new executor in api to handle new schema. Be sure to include unit tests 5. Add new test in `api/tests/opentrons/test_execute.py` for the schema @@ -9,4 +9,4 @@ 7. Add new schema fixture to `shared-data/protocol/fixtures/${schemaVersion}/${schemaVersion}.json` that the above test will read 8. Update `MAX_SUPPORTED_VERSION` in `api/src/opentrons/protocols/parse.py` -**IMPORTANT**: Be sure to clearly communicate the schema bump to the software + qa teams. This is a strict contract that should be honored by both the frontend and robot stack. +**IMPORTANT**: Be sure to clearly communicate the schema bump to the software + QA teams. This is a strict contract that should be honored by both the frontend and robot stack. diff --git a/shared-data/protocol/index.js b/shared-data/protocol/index.js deleted file mode 100644 index 6bc2cde814d..00000000000 --- a/shared-data/protocol/index.js +++ /dev/null @@ -1,13 +0,0 @@ -// @flow -import type { ProtocolFile as ProtocolFileV1 } from './flowTypes/schemaV1' -import type { ProtocolFile as ProtocolFileV3 } from './flowTypes/schemaV3' -import type { ProtocolFile as ProtocolFileV4 } from './flowTypes/schemaV4' -import type { ProtocolFile as ProtocolFileV5 } from './flowTypes/schemaV5' - -export type { ProtocolFileV1, ProtocolFileV3, ProtocolFileV4, ProtocolFileV5 } - -export type JsonProtocolFile = - | $ReadOnly<$Exact>> - | $ReadOnly> - | $ReadOnly> - | $ReadOnly> diff --git a/shared-data/protocol/index.ts b/shared-data/protocol/index.ts new file mode 100644 index 00000000000..cbedf81b3e9 --- /dev/null +++ b/shared-data/protocol/index.ts @@ -0,0 +1,18 @@ +// TODO(mc, 2021-04-27): delete flowTypes directory +import type { ProtocolFile as _ProtocolFileV1 } from './types/schemaV1' +import type { ProtocolFile as _ProtocolFileV3 } from './types/schemaV3' +import type { ProtocolFile as _ProtocolFileV4 } from './types/schemaV4' +import type { ProtocolFile as _ProtocolFileV5 } from './types/schemaV5' + +// TODO(mc, 2021-04-27): these awkward re-exports only for flowgen support +// remove when able +export type ProtocolFileV1 = _ProtocolFileV1 +export type ProtocolFileV3 = _ProtocolFileV3 +export type ProtocolFileV4 = _ProtocolFileV4 +export type ProtocolFileV5 = _ProtocolFileV5 + +export type JsonProtocolFile = + | Readonly> + | Readonly> + | Readonly> + | Readonly> diff --git a/shared-data/protocol/types/schemaV1.ts b/shared-data/protocol/types/schemaV1.ts new file mode 100644 index 00000000000..6751542525f --- /dev/null +++ b/shared-data/protocol/types/schemaV1.ts @@ -0,0 +1,108 @@ +import type { DeckSlotId, PipetteMount as Mount } from '../../js/types' + +// COMMANDS +export interface PipetteLabwareFields { + pipette: string + labware: string + well: string +} + +export interface AspirateDispenseArgs extends PipetteLabwareFields { + volume: number + offsetFromBottomMm?: number | null + 'flow-rate'?: number | null +} + +export type Command = + | { + command: 'aspirate' | 'dispense' + params: AspirateDispenseArgs + } + | { + command: 'pick-up-tip' | 'drop-tip' | 'blowout' + params: PipetteLabwareFields + } + | { + command: 'touch-tip' + params: PipetteLabwareFields & { + offsetFromBottomMm?: number | null + } + } + | { + command: 'delay' + + /** number of seconds to delay (fractional values OK), + or `true` for delay until user input */ + params: { + wait: number | true + message: string | null | undefined + } + } + | { + command: 'air-gap' + params: { + pipette: string + volume: number + } + } + +// File Subtypes +type VersionString = string // eg '1.0.0' + +// NOTE: these are an enum type in the spec, but it's inconvenient to flow-type them. +type PipetteModel = string +type PipetteName = string + +export interface FilePipette { + mount: Mount + model: PipetteModel + name?: PipetteName +} + +export interface FileLabware { + slot: DeckSlotId + model: string + 'display-name'?: string +} + +type FlowRateForPipettes = Record + +// A v1 JSON protocol file +export interface ProtocolFile { + 'protocol-schema': VersionString + metadata: { + 'protocol-name'?: string + author?: string + description?: string + created?: number + 'last-modified'?: number | null + category?: string | null + subcategory?: string | null + tags?: string[] + } + 'default-values': { + 'aspirate-flow-rate': FlowRateForPipettes + 'dispense-flow-rate': FlowRateForPipettes + 'aspirate-mm-from-bottom': number + 'dispense-mm-from-bottom': number + 'touch-tip-mm-from-top'?: number + } + // TODO(mc, 2019-04-17): this key isn't marked required in JSON schema + 'designer-application': { + 'application-name': string + 'application-version': string | null | undefined + data: DesignerApplicationData + } + robot: { + model: 'OT-2 Standard' + } + pipettes: Record + labware: Record + procedure: Array<{ + annotation: { + name: string + description: string + } + subprocedure: Command[] + }> +} diff --git a/shared-data/protocol/types/schemaV3.ts b/shared-data/protocol/types/schemaV3.ts new file mode 100644 index 00000000000..cad3e6313ce --- /dev/null +++ b/shared-data/protocol/types/schemaV3.ts @@ -0,0 +1,128 @@ +import type { + DeckSlotId, + LabwareDefinition2, + PipetteMount as Mount, +} from '../../js/types' + +// NOTE: this is an enum type in the spec, but it's inconvenient to flow-type them. +type PipetteName = string + +export interface FilePipette { + mount: Mount + name: PipetteName +} + +export interface FileLabware { + slot: DeckSlotId + definitionId: string + displayName?: string +} + +interface FlowRateParams { + flowRate: number +} + +export interface PipetteAccessParams { + pipette: string + labware: string + well: string +} + +interface VolumeParams { + volume: number +} + +interface OffsetParams { + offsetFromBottomMm: number +} + +export type AspDispAirgapParams = FlowRateParams & + PipetteAccessParams & + VolumeParams & + OffsetParams + +export type AspirateParams = AspDispAirgapParams +export type DispenseParams = AspDispAirgapParams +export type AirGapParams = AspDispAirgapParams +export type BlowoutParams = FlowRateParams & PipetteAccessParams & OffsetParams +export type TouchTipParams = PipetteAccessParams & OffsetParams +export type PickUpTipParams = PipetteAccessParams +export type DropTipParams = PipetteAccessParams + +export interface MoveToSlotParams { + pipette: string + slot: string + offset?: { + x: number + y: number + z: number + } + minimumZHeight?: number + forceDirect?: boolean +} + +export interface DelayParams { + wait: number | true + message?: string +} + +export type Command = + | { + command: 'aspirate' | 'dispense' | 'airGap' + params: AspDispAirgapParams + } + | { + command: 'blowout' + params: BlowoutParams + } + | { + command: 'touchTip' + params: TouchTipParams + } + | { + command: 'pickUpTip' | 'dropTip' + params: PipetteAccessParams + } + | { + command: 'moveToSlot' + params: MoveToSlotParams + } + | { + command: 'delay' + params: DelayParams + } + +// NOTE: must be kept in sync with '../schemas/3.json' +export interface ProtocolFile { + schemaVersion: 3 + metadata: { + protocolName?: string + author?: string + description?: string | null | undefined + created?: number + lastModified?: number | null | undefined + category?: string | null | undefined + subcategory?: string | null | undefined + tags?: string[] + } + designerApplication?: { + name?: string + version?: string + data?: DesignerApplicationData + } + robot: { + model: 'OT-2 Standard' + } + pipettes: Record + labwareDefinitions: Record + labware: Record< + string, + { + slot: string + definitionId: string + displayName?: string + } + > + commands: Command[] + commandAnnotations?: Record // NOTE: intentionally underspecified b/c we haven't decided on this yet +} diff --git a/shared-data/protocol/types/schemaV4.ts b/shared-data/protocol/types/schemaV4.ts new file mode 100644 index 00000000000..3e93b1f375e --- /dev/null +++ b/shared-data/protocol/types/schemaV4.ts @@ -0,0 +1,144 @@ +import type { DeckSlotId, ModuleModel } from '../../js/types' +import type { + ProtocolFile as V3ProtocolFile, + AspDispAirgapParams, + BlowoutParams, + TouchTipParams, + PipetteAccessParams, + MoveToSlotParams, + DelayParams, +} from './schemaV3' + +export type { BlowoutParams, FilePipette, FileLabware } from './schemaV3' + +export interface FileModule { + slot: DeckSlotId + model: ModuleModel +} + +export interface EngageMagnetParams { + module: string + engageHeight: number +} + +export interface TemperatureParams { + module: string + temperature: number +} + +export interface AtomicProfileStep { + holdTime: number + temperature: number +} + +export interface TCProfileParams { + module: string + profile: AtomicProfileStep[] + volume: number +} + +export interface ModuleOnlyParams { + module: string +} + +export interface ThermocyclerSetTargetBlockTemperatureArgs { + module: string + temperature: number + volume?: number +} + +export type Command = + | { + command: 'aspirate' | 'dispense' | 'airGap' + params: AspDispAirgapParams + } + | { + command: 'blowout' + params: BlowoutParams + } + | { + command: 'touchTip' + params: TouchTipParams + } + | { + command: 'pickUpTip' | 'dropTip' + params: PipetteAccessParams + } + | { + command: 'moveToSlot' + params: MoveToSlotParams + } + | { + command: 'delay' + params: DelayParams + } + | { + command: 'magneticModule/engageMagnet' + params: EngageMagnetParams + } + | { + command: 'magneticModule/disengageMagnet' + params: ModuleOnlyParams + } + | { + command: 'temperatureModule/setTargetTemperature' + params: TemperatureParams + } + | { + command: 'temperatureModule/deactivate' + params: ModuleOnlyParams + } + | { + command: 'temperatureModule/awaitTemperature' + params: TemperatureParams + } + | { + command: 'thermocycler/setTargetBlockTemperature' + params: ThermocyclerSetTargetBlockTemperatureArgs + } + | { + command: 'thermocycler/setTargetLidTemperature' + params: TemperatureParams + } + | { + command: 'thermocycler/awaitBlockTemperature' + params: TemperatureParams + } + | { + command: 'thermocycler/awaitLidTemperature' + params: TemperatureParams + } + | { + command: 'thermocycler/openLid' + params: ModuleOnlyParams + } + | { + command: 'thermocycler/closeLid' + params: ModuleOnlyParams + } + | { + command: 'thermocycler/deactivateBlock' + params: ModuleOnlyParams + } + | { + command: 'thermocycler/deactivateLid' + params: ModuleOnlyParams + } + | { + command: 'thermocycler/runProfile' + params: TCProfileParams + } + | { + command: 'thermocycler/awaitProfileComplete' + params: ModuleOnlyParams + } + +// NOTE: must be kept in sync with '../schemas/4.json' +export type ProtocolFile< + DesignerApplicationData +> = V3ProtocolFile & { + $otSharedSchema: '#/protocol/schemas/4' + schemaVersion: 4 + modules: Record + commands: Command[] +} diff --git a/shared-data/protocol/types/schemaV5.ts b/shared-data/protocol/types/schemaV5.ts new file mode 100644 index 00000000000..f4f0a087c18 --- /dev/null +++ b/shared-data/protocol/types/schemaV5.ts @@ -0,0 +1,32 @@ +import type { ProtocolFile as V3ProtocolFile } from './schemaV3' +import type { Command as V4Command, FileModule } from './schemaV4' + +export interface MoveToWellParams { + pipette: string + labware: string + well: string + offset?: { + x: number + y: number + z: number + } + minimumZHeight?: number + forceDirect?: boolean +} + +export type Command = + | V4Command + | { + command: 'moveToWell' + params: MoveToWellParams + } + +// NOTE: must be kept in sync with '../schemas/5.json' +export type ProtocolFile< + DesignerApplicationData +> = V3ProtocolFile & { + $otSharedSchema: '#/protocol/schemas/5' + schemaVersion: 5 + modules: Record + commands: Command[] +} diff --git a/shared-data/protocol/types/schemaV6.ts b/shared-data/protocol/types/schemaV6.ts new file mode 100644 index 00000000000..aaa15f8ba8d --- /dev/null +++ b/shared-data/protocol/types/schemaV6.ts @@ -0,0 +1,20 @@ +import type { ProtocolFile as V3ProtocolFile, AirGapParams } from './schemaV3' +import type { FileModule } from './schemaV4' +import type { Command as V5Command } from './schemaV5' + +export type Command = + | V5Command + | { + command: 'dispenseAirGap' + params: AirGapParams + } + +// NOTE: must be kept in sync with '../schemas/5.json' +export type ProtocolFile< + DesignerApplicationData +> = V3ProtocolFile & { + $otSharedSchema: '#/protocol/schemas/5' + schemaVersion: 5 + modules: Record + commands: Command[] +} diff --git a/shared-data/tsconfig-data.json b/shared-data/tsconfig-data.json new file mode 100644 index 00000000000..523960290be --- /dev/null +++ b/shared-data/tsconfig-data.json @@ -0,0 +1,19 @@ +{ + "extends": "../tsconfig-base.json", + "references": [], + "compilerOptions": { + "composite": true, + "emitDeclarationOnly": false, + "rootDir": ".", + "outDir": "lib" + }, + "include": [ + "deck/**/*.json", + "labware/**/*.json", + "module/**/*.json", + "pipette/**/*.json", + "protocol/**/*.json", + "build/**/*.json" + ], + "exclude": ["**/*.ts"] +} diff --git a/shared-data/tsconfig.json b/shared-data/tsconfig.json new file mode 100644 index 00000000000..1857e24731d --- /dev/null +++ b/shared-data/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig-base.json", + "references": [{ "path": "./tsconfig-data.json" }], + "compilerOptions": { + "composite": true, + "rootDir": ".", + "outDir": "lib" + }, + "include": ["typings", "js", "protocol"] +} diff --git a/tsconfig.json b/tsconfig.json index 33eeef79cdc..e8e1980b654 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,9 @@ }, { "path": "./labware-library" + }, + { + "path": "./shared-data" } ] }