From 15e2b2311d759011cfe8a8e7ab227c40ef0c7605 Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Mon, 19 Apr 2021 13:27:00 -0400 Subject: [PATCH 01/16] refactor(shared-data): port shared-data JS source to TypeScript --- components/tsconfig.json | 6 +- labware-library/tsconfig.json | 3 + shared-data/Makefile | 51 +-- ...eckSchemas.test.js => deckSchemas.test.ts} | 22 +- ...test.js => getWellNamePerMultiTip.test.ts} | 18 - ...uirks.test.js => labwareDefQuirks.test.ts} | 4 +- ...aV1.test.js => labwareDefSchemaV1.test.ts} | 25 +- ...aV2.test.js => labwareDefSchemaV2.test.ts} | 25 +- ...essors.test.js => moduleAccessors.test.ts} | 5 - ...hema.test.js => moduleSpecsSchema.test.ts} | 16 +- ...mas.test.js => pipetteSpecSchemas.test.ts} | 6 - .../{pipettes.test.js => pipettes.test.ts} | 4 - ...emaV4.test.js => protocolSchemaV4.test.ts} | 50 +-- ...emaV5.test.js => protocolSchemaV5.test.ts} | 50 +-- .../{sortWells.test.js => sortWells.test.ts} | 3 - ...umn.test.js => splitWellsOnColumn.test.ts} | 3 - shared-data/js/{constants.js => constants.ts} | 12 +- .../js/{cypressUtils.js => cypressUtils.ts} | 3 +- .../js/{getLabware.js => getLabware.ts} | 25 +- .../{volume.test.js => volume.test.ts} | 3 - .../{wellSets.test.js => wellSets.test.ts} | 20 -- ...rMultiTip.js => getWellNamePerMultiTip.ts} | 10 +- ...llTotalVolume.js => getWellTotalVolume.ts} | 6 +- shared-data/js/helpers/{index.js => index.ts} | 27 +- .../js/helpers/{volume.js => volume.ts} | 12 +- .../helpers/{wellIsRect.js => wellIsRect.ts} | 1 - .../js/helpers/{wellSets.js => wellSets.ts} | 38 ++- shared-data/js/{index.js => index.ts} | 2 - ...st.js => createDefaultDisplayName.test.ts} | 2 - ...test.js => createIrregularLabware.test.ts} | 145 ++++++-- ...eLabware.test.js => createLabware.test.ts} | 65 ++-- .../js/labwareTools/{index.js => index.ts} | 207 ++++++------ shared-data/js/{modules.js => modules.ts} | 66 ++-- shared-data/js/{pipettes.js => pipettes.ts} | 19 +- shared-data/js/{schema.js => schema.ts} | 44 ++- shared-data/js/scripts/{build.js => build.ts} | 9 +- ...romSVG.js => generateDeckLayersFromSVG.ts} | 13 +- shared-data/js/types.js | 316 ------------------ shared-data/js/types.ts | 270 +++++++++++++++ shared-data/package.json | 5 +- shared-data/tsconfig.json | 10 + 41 files changed, 813 insertions(+), 808 deletions(-) rename shared-data/js/__tests__/{deckSchemas.test.js => deckSchemas.test.ts} (97%) rename shared-data/js/__tests__/{getWellNamePerMultiTip.test.js => getWellNamePerMultiTip.test.ts} (99%) rename shared-data/js/__tests__/{labwareDefQuirks.test.js => labwareDefQuirks.test.ts} (99%) rename shared-data/js/__tests__/{labwareDefSchemaV1.test.js => labwareDefSchemaV1.test.ts} (92%) rename shared-data/js/__tests__/{labwareDefSchemaV2.test.js => labwareDefSchemaV2.test.ts} (97%) rename shared-data/js/__tests__/{moduleAccessors.test.js => moduleAccessors.test.ts} (98%) rename shared-data/js/__tests__/{moduleSpecsSchema.test.js => moduleSpecsSchema.test.ts} (96%) rename shared-data/js/__tests__/{pipetteSpecSchemas.test.js => pipetteSpecSchemas.test.ts} (99%) rename shared-data/js/__tests__/{pipettes.test.js => pipettes.test.ts} (99%) rename shared-data/js/__tests__/{protocolSchemaV4.test.js => protocolSchemaV4.test.ts} (87%) rename shared-data/js/__tests__/{protocolSchemaV5.test.js => protocolSchemaV5.test.ts} (87%) rename shared-data/js/__tests__/{sortWells.test.js => sortWells.test.ts} (98%) rename shared-data/js/__tests__/{splitWellsOnColumn.test.js => splitWellsOnColumn.test.ts} (99%) rename shared-data/js/{constants.js => constants.ts} (99%) rename shared-data/js/{cypressUtils.js => cypressUtils.ts} (99%) rename shared-data/js/{getLabware.js => getLabware.ts} (94%) rename shared-data/js/helpers/__tests__/{volume.test.js => volume.test.ts} (99%) rename shared-data/js/helpers/__tests__/{wellSets.test.js => wellSets.test.ts} (99%) rename shared-data/js/helpers/{getWellNamePerMultiTip.js => getWellNamePerMultiTip.ts} (98%) rename shared-data/js/helpers/{getWellTotalVolume.js => getWellTotalVolume.ts} (92%) rename shared-data/js/helpers/{index.js => index.ts} (97%) 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} (89%) rename shared-data/js/{index.js => index.ts} (95%) rename shared-data/js/labwareTools/__tests__/{createDefaultDisplayName.test.js => createDefaultDisplayName.test.ts} (99%) rename shared-data/js/labwareTools/__tests__/{createIrregularLabware.test.js => createIrregularLabware.test.ts} (75%) rename shared-data/js/labwareTools/__tests__/{createLabware.test.js => createLabware.test.ts} (85%) rename shared-data/js/labwareTools/{index.js => index.ts} (81%) rename shared-data/js/{modules.js => modules.ts} (79%) rename shared-data/js/{pipettes.js => pipettes.ts} (88%) rename shared-data/js/{schema.js => schema.ts} (66%) rename shared-data/js/scripts/{build.js => build.ts} (99%) rename shared-data/js/scripts/{generateDeckLayersFromSVG.js => generateDeckLayersFromSVG.ts} (99%) delete mode 100644 shared-data/js/types.js create mode 100644 shared-data/js/types.ts create mode 100644 shared-data/tsconfig.json 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/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/shared-data/Makefile b/shared-data/Makefile index 5943e9e0720..3a62cc883bb 100644 --- a/shared-data/Makefile +++ b/shared-data/Makefile @@ -3,53 +3,58 @@ # 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 - -####################################### +# Top level targets .PHONY: all all: clean dist dist-py +.PHONY: setup +setup: setup-py setup-js + +.PHONY: dist +dist: dist-js dist-py + +.PHONY: clean +clean: clean-js clean-py + +# JavaScript targets + .PHONY: setup-js setup-js: $(MAKE) dist -.PHONY: setup-py -setup-py: - $(MAKE) -C python setup-py - - +.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: setup -setup: setup-py setup-js +.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 $@ -.PHONY: dist -dist: - @shx mkdir -p $(BUILD_DIR) - node js/scripts/build.js $(BUILD_DIR) +# 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 +67,6 @@ lint-py: push-no-restart: $(MAKE) -C python push-no-restart - .PHONY: push push: $(MAKE) -C python push @@ -71,7 +75,6 @@ push: deploy-py: $(MAKE) -C python deploy - .PHONY: test-py test-py: $(MAKE) -C python test diff --git a/shared-data/js/__tests__/deckSchemas.test.js b/shared-data/js/__tests__/deckSchemas.test.ts similarity index 97% rename from shared-data/js/__tests__/deckSchemas.test.js rename to shared-data/js/__tests__/deckSchemas.test.ts index 47a8f1c65a5..e6c81ef2871 100644 --- a/shared-data/js/__tests__/deckSchemas.test.js +++ b/shared-data/js/__tests__/deckSchemas.test.ts @@ -3,27 +3,27 @@ import Ajv from 'ajv' import path from 'path' import glob from 'glob' - import deckSchemaV1 from '../../deck/schemas/1.json' import deckSchemaV2 from '../../deck/schemas/2.json' - const v1FixtureGlob = path.join(__dirname, '../../deck/fixtures/1/*.json') const v2FixtureGlob = path.join(__dirname, '../../deck/fixtures/2/*.json') const v1DefGlob = path.join(__dirname, '../../deck/definitions/1/*.json') const v2DefGlob = path.join(__dirname, '../../deck/definitions/2/*.json') - -const ajv = new Ajv({ allErrors: true, jsonPointers: true }) - +const ajv = new Ajv({ + allErrors: true, + jsonPointers: true, +}) const validateV1Schema = ajv.compile(deckSchemaV1) 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,6 +31,7 @@ describe('validate deck defs and fixtures', () => { JSON.stringify(validationErrors, null, 4) ) } + expect(validationErrors).toBe(null) expect(valid).toBe(true) }) @@ -38,9 +39,11 @@ describe('validate deck defs and fixtures', () => { 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,6 +51,7 @@ describe('validate deck defs and fixtures', () => { JSON.stringify(validationErrors, null, 4) ) } + expect(validationErrors).toBe(null) expect(valid).toBe(true) }) @@ -55,9 +59,11 @@ describe('validate deck defs and fixtures', () => { 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,6 +71,7 @@ describe('validate deck defs and fixtures', () => { JSON.stringify(validationErrors, null, 4) ) } + expect(validationErrors).toBe(null) expect(valid).toBe(true) }) @@ -72,9 +79,11 @@ describe('validate deck defs and fixtures', () => { 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 +91,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 99% rename from shared-data/js/__tests__/getWellNamePerMultiTip.test.js rename to shared-data/js/__tests__/getWellNamePerMultiTip.test.ts index e5c28002823..d615f332a13 100644 --- a/shared-data/js/__tests__/getWellNamePerMultiTip.test.js +++ b/shared-data/js/__tests__/getWellNamePerMultiTip.test.ts @@ -1,14 +1,11 @@ -// @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' - describe('96 plate', () => { const labware = fixture_96_plate - it('A1 => column 1', () => { expect(getWellNamePerMultiTip(labware, 'A1')).toEqual([ 'A1', @@ -21,7 +18,6 @@ describe('96 plate', () => { 'H1', ]) }) - it('A2 => column 2', () => { expect(getWellNamePerMultiTip(labware, 'A2')).toEqual([ 'A2', @@ -34,15 +30,12 @@ describe('96 plate', () => { 'H2', ]) }) - it('B1 => null (cannot access with 8-channel)', () => { expect(getWellNamePerMultiTip(labware, 'B1')).toEqual(null) }) }) - describe('384 plate', () => { const labware = fixture_384_plate - it('A1 => column 1 ACEGIKMO', () => { expect(getWellNamePerMultiTip(labware, 'A1')).toEqual([ 'A1', @@ -55,7 +48,6 @@ describe('384 plate', () => { 'O1', ]) }) - it('A2 => column 2 ACEGIKMO', () => { expect(getWellNamePerMultiTip(labware, 'A2')).toEqual([ 'A2', @@ -68,7 +60,6 @@ describe('384 plate', () => { 'O2', ]) }) - it('B1 => column 1 BDFHJLNP', () => { expect(getWellNamePerMultiTip(labware, 'B1')).toEqual([ 'B1', @@ -81,15 +72,12 @@ describe('384 plate', () => { 'P1', ]) }) - it('C1 => null (cannot access with 8-channel)', () => { expect(getWellNamePerMultiTip(labware, 'C1')).toEqual(null) }) }) - describe('Fixed trash', () => { const labware = fixture_trash - it('A1 => all tips in A1', () => { expect(getWellNamePerMultiTip(labware, 'A1')).toEqual([ 'A1', @@ -102,12 +90,10 @@ describe('Fixed trash', () => { 'A1', ]) }) - it('A2 => null (well does not exist)', () => { expect(getWellNamePerMultiTip(labware, 'A2')).toEqual(null) }) }) - describe('tube rack 2mL', () => { const labware = fixture_24_tuberack it('tube rack 2mL not accessible by 8-channel (return null)', () => { @@ -116,10 +102,8 @@ describe('tube rack 2mL', () => { }) }) }) - describe('12 channel trough', () => { const labware = fixture_12_trough - it('A1 => all tips in A1', () => { expect(getWellNamePerMultiTip(labware, 'A1')).toEqual([ 'A1', @@ -132,7 +116,6 @@ describe('12 channel trough', () => { 'A1', ]) }) - it('A2 => all tips in A2', () => { expect(getWellNamePerMultiTip(labware, 'A2')).toEqual([ 'A2', @@ -145,7 +128,6 @@ describe('12 channel trough', () => { 'A2', ]) }) - it('B1 => null (well does not exist)', () => { expect(getWellNamePerMultiTip(labware, 'B1')).toEqual(null) }) 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..bb49c6d5974 100644 --- a/shared-data/js/__tests__/labwareDefQuirks.test.js +++ b/shared-data/js/__tests__/labwareDefQuirks.test.ts @@ -1,17 +1,14 @@ import path from 'path' import glob from 'glob' - const definitionsGlobPath = path.join( __dirname, '../../labware/definitions/2/**/*.json' ) - const EXPECTED_VALID_QUIRKS = [ 'centerMultichannelOnWells', 'touchTipDisabled', 'fixedTrash', ] - describe('check quirks for all labware defs', () => { const labwarePaths = glob.sync(definitionsGlobPath) beforeAll(() => { @@ -22,6 +19,7 @@ describe('check quirks for all labware defs', () => { 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 92% rename from shared-data/js/__tests__/labwareDefSchemaV1.test.js rename to shared-data/js/__tests__/labwareDefSchemaV1.test.ts index f4a086846f7..f372e837644 100644 --- a/shared-data/js/__tests__/labwareDefSchemaV1.test.js +++ b/shared-data/js/__tests__/labwareDefSchemaV1.test.ts @@ -2,18 +2,17 @@ import path from 'path' import glob from 'glob' import Ajv from 'ajv' import { labwareSchemaV1 } from '../schema' - const DEFINITIONS_GLOB_PATTERN = '../../labware/definitions/1/*.json' -const GLOB_OPTIONS = { absolute: true, cwd: __dirname } +const GLOB_OPTIONS = { + absolute: true, + cwd: __dirname, +} // 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 = { @@ -37,20 +36,20 @@ describe('test the schema against a minimalist fixture', () => { } const valid = validate(minimalLabwareDef) const validationErrors = validate.errors - expect(validationErrors).toBe(null) expect(valid).toBe(true) }) - it('fail on bad labware', () => { const badDef = { - metadata: { name: 'bad' }, - ordering: ['A1'], // array of strings not array of arrays + metadata: { + name: 'bad', + }, + 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') ).toMatchObject({ @@ -59,22 +58,20 @@ describe('test the schema against a minimalist fixture', () => { expect(valid).toBe(false) }) }) - describe('test schemas of all definitions', () => { const labwarePaths = glob.sync(DEFINITIONS_GLOB_PATTERN, GLOB_OPTIONS) - it('got at least 1 labware definition file', () => { // 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) + it(filename, () => { const valid = validate(labwareDef) const validationErrors = validate.errors - expect(validationErrors).toBe(null) expect(valid).toBe(true) }) diff --git a/shared-data/js/__tests__/labwareDefSchemaV2.test.js b/shared-data/js/__tests__/labwareDefSchemaV2.test.ts similarity index 97% rename from shared-data/js/__tests__/labwareDefSchemaV2.test.js rename to shared-data/js/__tests__/labwareDefSchemaV2.test.ts index 29f8f2b4338..4c17f33332a 100644 --- a/shared-data/js/__tests__/labwareDefSchemaV2.test.js +++ b/shared-data/js/__tests__/labwareDefSchemaV2.test.ts @@ -2,21 +2,16 @@ import path from 'path' import glob from 'glob' import Ajv from 'ajv' import schema from '../../labware/schemas/2.json' - const definitionsGlobPath = path.join( __dirname, '../../labware/definitions/2/**/*.json' ) - const fixturesGlobPath = path.join(__dirname, '../../labware/fixtures/2/*.json') - // JSON Schema defintion & setup - const ajv = new Ajv({ allErrors: true, jsonPointers: true, }) - const validate = ajv.compile(schema) const expectGroupsFollowConvention = (labwareDef, filename) => { @@ -45,13 +40,15 @@ const expectGroupsFollowConvention = (labwareDef, filename) => { describe('fail on bad labware', () => { const badDef = { - metadata: { name: 'bad' }, - ordering: ['A1'], // array of strings not array of arrays + metadata: { + name: 'bad', + }, + 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') ).toMatchObject({ @@ -59,21 +56,20 @@ describe('fail on bad labware', () => { }) expect(valid).toBe(false) }) - describe('test schemas of all opentrons definitions', () => { const labwarePaths = glob.sync(definitionsGlobPath) it(`path to definitions OK`, () => { // 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) + it(`${filename} validates against schema`, () => { const valid = validate(labwareDef) const validationErrors = validate.errors - expect(validationErrors).toBe(null) expect(valid).toBe(true) }) @@ -95,22 +91,20 @@ 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`, () => { // 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) + it(`${filename} validates against schema`, () => { const valid = validate(labwareDef) const validationErrors = validate.errors - expect(validationErrors).toBe(null) expect(valid).toBe(true) }) @@ -122,7 +116,6 @@ describe('test schemas of all v2 labware fixtures', () => { it(`namespace is "fixture": ${labwarePath}`, () => { expect(labwareDef.namespace).toEqual('fixture') }) - expectGroupsFollowConvention(labwareDef, filename) }) }) 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..9ed1e32e5a9 100644 --- a/shared-data/js/__tests__/moduleAccessors.test.js +++ b/shared-data/js/__tests__/moduleAccessors.test.ts @@ -1,12 +1,9 @@ -// @flow - import { getModuleDef2, getModuleType, getModuleDisplayName, normalizeModuleModel, } from '../modules' - import { MODULE_MODELS, MODULE_TYPES, @@ -17,7 +14,6 @@ import { TEMPERATURE_MODULE_V1, THERMOCYCLER_MODULE_V1, } from '../constants' - describe('all valid models work', () => { MODULE_MODELS.forEach(model => { const loadedDef = getModuleDef2(model) @@ -34,7 +30,6 @@ describe('all valid models work', () => { }) }) }) - describe('legacy models work too', () => { const legacyEquivs = [ [TEMPDECK, TEMPERATURE_MODULE_V1], diff --git a/shared-data/js/__tests__/moduleSpecsSchema.test.js b/shared-data/js/__tests__/moduleSpecsSchema.test.ts similarity index 96% rename from shared-data/js/__tests__/moduleSpecsSchema.test.js rename to shared-data/js/__tests__/moduleSpecsSchema.test.ts index a5f2ff23198..cd5d387ac49 100644 --- a/shared-data/js/__tests__/moduleSpecsSchema.test.js +++ b/shared-data/js/__tests__/moduleSpecsSchema.test.ts @@ -4,35 +4,33 @@ import moduleSpecsV1 from '../../module/definitions/1.json' import moduleSpecsSchemaV2 from '../../module/schemas/2.json' import path from 'path' import glob from 'glob' - const ajv = new Ajv({ allErrors: true, jsonPointers: true, }) - const validateModuleSpecsV1 = ajv.compile(moduleSpecsSchemaV1) const validateModuleSpecsV2 = ajv.compile(moduleSpecsSchemaV2) - const V2_DEFS_GLOB_PATTERN = '../../module/definitions/2/*.json' -const GLOB_OPTIONS = { cwd: __dirname, absolute: true } +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', () => { it('ensure V1 module specs match the V1 JSON schema', () => { const valid = validateModuleSpecsV1(moduleSpecsV1) const validationErrors = validateModuleSpecsV1.errors - expect(validationErrors).toBe(null) expect(valid).toBe(true) }) - 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 @@ -43,7 +41,9 @@ describe('validate all module specs with schema', () => { 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 99% rename from shared-data/js/__tests__/pipetteSpecSchemas.test.js rename to shared-data/js/__tests__/pipetteSpecSchemas.test.ts index c734239cf4f..3dc4603ae1c 100644 --- a/shared-data/js/__tests__/pipetteSpecSchemas.test.js +++ b/shared-data/js/__tests__/pipetteSpecSchemas.test.ts @@ -3,15 +3,12 @@ import nameSpecsSchema from '../../pipette/schemas/pipetteNameSpecsSchema.json' 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 validateNameSpecs = ajv.compile(nameSpecsSchema) const validateModelSpecs = ajv.compile(modelSpecsSchema) - describe('validate pipette specs with JSON schemas', () => { it('ensure all pipette *NAME* specs match name JSON schema', () => { const valid = validateNameSpecs(pipetteNameSpecs) @@ -24,7 +21,6 @@ describe('validate pipette specs with JSON schemas', () => { expect(validationErrors).toBe(null) expect(valid).toBe(true) }) - it('ensure all pipette *MODEL* specs match model JSON schema', () => { const valid = validateModelSpecs(pipetteModelSpecs) const validationErrors = validateModelSpecs.errors @@ -37,12 +33,10 @@ describe('validate pipette specs with JSON schemas', () => { expect(valid).toBe(true) }) }) - describe('model -> name referencing', () => { it('ensure all pipette model specs reference a valid pipette name', () => { const modelKeys = Object.keys(pipetteModelSpecs.config) const nameKeys = Object.keys(pipetteNameSpecs) - modelKeys.forEach(model => { const nameForVersion = pipetteModelSpecs.config[model].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 99% rename from shared-data/js/__tests__/pipettes.test.js rename to shared-data/js/__tests__/pipettes.test.ts index ac62c27a438..3e3d2150b8f 100644 --- a/shared-data/js/__tests__/pipettes.test.js +++ b/shared-data/js/__tests__/pipettes.test.ts @@ -1,6 +1,5 @@ // tests for pipette info accessors in `shared-data/js/pipettes.js` import { getPipetteNameSpecs, getPipetteModelSpecs } from '../pipettes' - const PIPETTE_NAMES = [ 'p10_single', 'p50_single', @@ -10,7 +9,6 @@ const PIPETTE_NAMES = [ 'p50_multi', 'p300_multi', ] - const PIPETTE_MODELS = [ 'p10_single_v1', 'p10_single_v1.3', @@ -40,7 +38,6 @@ const PIPETTE_MODELS = [ 'p1000_single_v1.4', 'p1000_single_v1.5', ] - describe('pipette data accessors', () => { describe('getPipetteNameSpecs', () => { PIPETTE_NAMES.forEach(name => @@ -48,7 +45,6 @@ describe('pipette data accessors', () => { expect(getPipetteNameSpecs(name)).toMatchSnapshot()) ) }) - describe('getPipetteModelSpecs', () => { PIPETTE_MODELS.forEach(model => it(`model ${model} snapshot`, () => diff --git a/shared-data/js/__tests__/protocolSchemaV4.test.js b/shared-data/js/__tests__/protocolSchemaV4.test.ts similarity index 87% rename from shared-data/js/__tests__/protocolSchemaV4.test.js rename to shared-data/js/__tests__/protocolSchemaV4.test.ts index a81f2e2615b..f3b84210f7d 100644 --- a/shared-data/js/__tests__/protocolSchemaV4.test.js +++ b/shared-data/js/__tests__/protocolSchemaV4.test.ts @@ -1,4 +1,3 @@ -// @flow /** Ensure that protocol schema v4 definition itself is functions as intended, * and that all v4 protocol fixtures will validate */ import Ajv from 'ajv' @@ -8,39 +7,35 @@ 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' - const fixturesGlobPath = path.join( __dirname, '../../protocol/fixtures/4/**/*.json' ) 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) - const validateProtocol = ajv.compile(protocolSchema) - 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) }) }) }) - describe('ensure bad protocol data fails validation', () => { it('$otSharedSchema is required to be "#/protocol/schemas/4"', () => { expect(validateProtocol(omit(simpleV4Fixture, '$otSharedSchema'))).toBe( @@ -53,27 +48,31 @@ describe('ensure bad protocol data fails validation', () => { }) ).toBe(false) }) - it('schemaVersion is required to be 4', () => { expect(validateProtocol(omit(simpleV4Fixture, 'schemaVersion'))).toBe(false) expect(validateProtocol({ ...simpleV4Fixture, schemaVersion: 3 })).toBe( false ) }) - it('reject bad values in "pipettes" objects', () => { const badPipettes = { missingKeys: {}, - missingName: { mount: 'left' }, - missingMount: { name: 'pipetteName' }, - badMount: { mount: 'blah', name: 'pipetteName' }, + missingName: { + mount: 'left', + }, + missingMount: { + name: 'pipetteName', + }, + badMount: { + mount: 'blah', + name: 'pipetteName', + }, hasAdditionalProperties: { mount: 'left', name: 'pipetteName', blah: 'blah', }, } - Object.keys(badPipettes).forEach((pipetteId: string) => { expect( validateProtocol({ @@ -86,18 +85,20 @@ describe('ensure bad protocol data fails validation', () => { ).toBe(false) }) }) - it('reject bad values in "labware" objects', () => { const badLabware = { - noSlot: { definitionId: 'defId' }, - noDefId: { slot: '1' }, + noSlot: { + definitionId: 'defId', + }, + noDefId: { + slot: '1', + }, hasAdditionalProperties: { slot: '1', definitionId: 'defId', blah: 'blah', }, } - Object.keys(badLabware).forEach((labwareId: string) => { expect( validateProtocol({ @@ -110,19 +111,24 @@ describe('ensure bad protocol data fails validation', () => { ).toBe(false) }) }) - it('reject bad values in "modules" objects', () => { const badModules = { - badModuleType: { slot: '1', moduleType: 'fake' }, - noSlot: { moduleType: 'thermocycler' }, - noModuleType: { slot: '1' }, + badModuleType: { + slot: '1', + moduleType: 'fake', + }, + noSlot: { + moduleType: 'thermocycler', + }, + noModuleType: { + slot: '1', + }, hasAdditionalProperties: { slot: '1', moduleType: 'thermocycler', blah: 'blah', }, } - Object.keys(badModules).forEach((moduleId: string) => { expect( validateProtocol({ diff --git a/shared-data/js/__tests__/protocolSchemaV5.test.js b/shared-data/js/__tests__/protocolSchemaV5.test.ts similarity index 87% rename from shared-data/js/__tests__/protocolSchemaV5.test.js rename to shared-data/js/__tests__/protocolSchemaV5.test.ts index 6f4a8504e3a..08f8c14157f 100644 --- a/shared-data/js/__tests__/protocolSchemaV5.test.js +++ b/shared-data/js/__tests__/protocolSchemaV5.test.ts @@ -1,4 +1,3 @@ -// @flow /** Ensure that protocol schema v5 definition itself is functions as intended, * and that all v5 protocol fixtures will validate */ import Ajv from 'ajv' @@ -8,39 +7,35 @@ 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' - const fixturesGlobPath = path.join( __dirname, '../../protocol/fixtures/5/**/*.json' ) 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) - const validateProtocol = ajv.compile(protocolSchema) - 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) }) }) }) - describe('ensure bad protocol data fails validation', () => { it('$otSharedSchema is required to be "#/protocol/schemas/5"', () => { expect(validateProtocol(omit(simpleV5Fixture, '$otSharedSchema'))).toBe( @@ -53,27 +48,31 @@ describe('ensure bad protocol data fails validation', () => { }) ).toBe(false) }) - it('schemaVersion is required to be 5', () => { expect(validateProtocol(omit(simpleV5Fixture, 'schemaVersion'))).toBe(false) expect(validateProtocol({ ...simpleV5Fixture, schemaVersion: 3 })).toBe( false ) }) - it('reject bad values in "pipettes" objects', () => { const badPipettes = { missingKeys: {}, - missingName: { mount: 'left' }, - missingMount: { name: 'pipetteName' }, - badMount: { mount: 'blah', name: 'pipetteName' }, + missingName: { + mount: 'left', + }, + missingMount: { + name: 'pipetteName', + }, + badMount: { + mount: 'blah', + name: 'pipetteName', + }, hasAdditionalProperties: { mount: 'left', name: 'pipetteName', blah: 'blah', }, } - Object.keys(badPipettes).forEach((pipetteId: string) => { expect( validateProtocol({ @@ -86,18 +85,20 @@ describe('ensure bad protocol data fails validation', () => { ).toBe(false) }) }) - it('reject bad values in "labware" objects', () => { const badLabware = { - noSlot: { definitionId: 'defId' }, - noDefId: { slot: '1' }, + noSlot: { + definitionId: 'defId', + }, + noDefId: { + slot: '1', + }, hasAdditionalProperties: { slot: '1', definitionId: 'defId', blah: 'blah', }, } - Object.keys(badLabware).forEach((labwareId: string) => { expect( validateProtocol({ @@ -110,19 +111,24 @@ describe('ensure bad protocol data fails validation', () => { ).toBe(false) }) }) - it('reject bad values in "modules" objects', () => { const badModules = { - badModuleType: { slot: '1', moduleType: 'fake' }, - noSlot: { moduleType: 'thermocycler' }, - noModuleType: { slot: '1' }, + badModuleType: { + slot: '1', + moduleType: 'fake', + }, + noSlot: { + moduleType: 'thermocycler', + }, + noModuleType: { + slot: '1', + }, hasAdditionalProperties: { slot: '1', moduleType: 'thermocycler', blah: 'blah', }, } - Object.keys(badModules).forEach((moduleId: string) => { expect( validateProtocol({ 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..769b10ab887 100644 --- a/shared-data/js/__tests__/sortWells.test.js +++ b/shared-data/js/__tests__/sortWells.test.ts @@ -1,6 +1,4 @@ -// @flow import { sortWells } from '../helpers' - describe('sortWells', () => { it('single letters', () => { const input = ['A12', 'A2', 'B1', 'B12', 'A1'] @@ -8,7 +6,6 @@ describe('sortWells', () => { const result = [...input].sort(sortWells) expect(result).toEqual(expected) }) - // just in case we get a 1536-well plate it('double letters', () => { const input = [ diff --git a/shared-data/js/__tests__/splitWellsOnColumn.test.js b/shared-data/js/__tests__/splitWellsOnColumn.test.ts similarity index 99% rename from shared-data/js/__tests__/splitWellsOnColumn.test.js rename to shared-data/js/__tests__/splitWellsOnColumn.test.ts index 77229116829..d3a86c9564e 100644 --- a/shared-data/js/__tests__/splitWellsOnColumn.test.js +++ b/shared-data/js/__tests__/splitWellsOnColumn.test.ts @@ -1,14 +1,11 @@ import { splitWellsOnColumn } from '../helpers' - describe('test splitWellsOnColumn', () => { it('empty array', () => { expect(splitWellsOnColumn([])).toEqual([]) }) - it('one value', () => { expect(splitWellsOnColumn(['A1'])).toEqual([['A1']]) }) - it('sort multi-digit wels', () => { expect(splitWellsOnColumn(['A1', 'B2', 'C2', 'D3', 'X10', 'X11'])).toEqual([ ['A1'], diff --git a/shared-data/js/constants.js b/shared-data/js/constants.ts similarity index 99% rename from shared-data/js/constants.js rename to shared-data/js/constants.ts index 2f69012d631..ec35ed36c48 100644 --- a/shared-data/js/constants.js +++ b/shared-data/js/constants.ts @@ -1,19 +1,18 @@ -// @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 export const FIXED_TRASH_RENDER_HEIGHT = 165.86 // along Y axis in SVG coords export const OPENTRONS_LABWARE_NAMESPACE = 'opentrons' - // TODO: IL 2020-02-19 These 3 constants are DEPRECATED because they're ambiguous model vs type. export const THERMOCYCLER: 'thermocycler' = 'thermocycler' export const TEMPDECK: 'tempdeck' = 'tempdeck' @@ -28,33 +27,26 @@ export const TEMPERATURE_MODULE_V2: 'temperatureModuleV2' = 'temperatureModuleV2' export const THERMOCYCLER_MODULE_V1: 'thermocyclerModuleV1' = 'thermocyclerModuleV1' - // pipette display categories export const GEN2: 'GEN2' = 'GEN2' export const GEN1: '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' = 'temperatureModuleType' export const MAGNETIC_MODULE_TYPE: 'magneticModuleType' = 'magneticModuleType' export const THERMOCYCLER_MODULE_TYPE: 'thermocyclerModuleType' = 'thermocyclerModuleType' - export const MAGNETIC_MODULE_MODELS = [MAGNETIC_MODULE_V1, MAGNETIC_MODULE_V2] - export const TEMPERATURE_MODULE_MODELS = [ TEMPERATURE_MODULE_V1, TEMPERATURE_MODULE_V2, ] - export const THERMOCYCLER_MODULE_MODELS = [THERMOCYCLER_MODULE_V1] - export const MODULE_MODELS: Array = [ ...MAGNETIC_MODULE_MODELS, ...TEMPERATURE_MODULE_MODELS, ...THERMOCYCLER_MODULE_MODELS, ] - export const MODULE_TYPES = [ TEMPERATURE_MODULE_TYPE, MAGNETIC_MODULE_TYPE, diff --git a/shared-data/js/cypressUtils.js b/shared-data/js/cypressUtils.ts similarity index 99% rename from shared-data/js/cypressUtils.js rename to shared-data/js/cypressUtils.ts index 4e21f78a4b0..328aaa13584 100644 --- a/shared-data/js/cypressUtils.js +++ b/shared-data/js/cypressUtils.ts @@ -1,4 +1,3 @@ -// @flow import isEqual from 'lodash/isEqual' import isObject from 'lodash/isObject' import transform from 'lodash/transform' @@ -14,6 +13,7 @@ const difference = (object, base) => { } }) } + return changes(object, base) } @@ -26,6 +26,7 @@ export const expectDeepEqual = (assert: any, a: any, b: any): void => { // visualize undefineds const replacer = (key, value) => 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 94% rename from shared-data/js/getLabware.js rename to shared-data/js/getLabware.ts index 76e56ff642f..8a873bab4aa 100644 --- a/shared-data/js/getLabware.js +++ b/shared-data/js/getLabware.ts @@ -1,4 +1,3 @@ -// @flow import assert from 'assert' import mapValues from 'lodash/mapValues' // TODO: Ian 2019-06-04 remove the shared-data build process for labware v1 @@ -13,12 +12,10 @@ import type { LabwareDefinition2, WellDefinition, } from './types' - assert( definitions && Object.keys(definitions).length > 0, 'Expected v1 labware defs. Something went wrong with shared-data/build/labware.json' ) - // labware definitions only used for back-compat with legacy v1 defs. // do not list in any "available labware" UI. // TODO(mc, 2019-12-3): how should this correspond to RETIRED_LABWARE? @@ -43,20 +40,19 @@ export const PD_DO_NOT_LIST = [ 'opentrons_calibrationblock_short_side_left', '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 = + definitions[labwareName] return labware } - export function getIsLabwareV1Tiprack(def: LabwareDefinition1): boolean { return Boolean(def?.metadata?.isTiprack) } - export function getIsTiprack(labwareDef: LabwareDefinition2): boolean { return labwareDef.parameters.isTiprack } - // NOTE: these labware definitions in _SHORT_MM_LABWARE_DEF_LOADNAMES // were written in "short mm" = 0.5mm, but // we will write all future definitions in actual mm. @@ -75,12 +71,12 @@ const _SHORT_MM_LABWARE_DEF_LOADNAMES = [ // definitions from "distance from home switch" to "distance from labware bottom" // Note: this is in actual mm, not "short mm" :) 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,17 +85,16 @@ 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 = ( @@ -117,15 +112,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 99% rename from shared-data/js/helpers/__tests__/volume.test.js rename to shared-data/js/helpers/__tests__/volume.test.ts index bde5eea4d29..24f0bebf146 100644 --- a/shared-data/js/helpers/__tests__/volume.test.js +++ b/shared-data/js/helpers/__tests__/volume.test.ts @@ -1,7 +1,5 @@ // volume helpers tests - import * as helpers from '..' - describe('volume helpers', () => { const SPECS = [ { @@ -83,7 +81,6 @@ describe('volume helpers', () => { expected: 'L', }, ] - SPECS.forEach(s => { it(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 99% rename from shared-data/js/helpers/__tests__/wellSets.test.js rename to shared-data/js/helpers/__tests__/wellSets.test.ts index ea15830124d..775eb87d91c 100644 --- a/shared-data/js/helpers/__tests__/wellSets.test.js +++ b/shared-data/js/helpers/__tests__/wellSets.test.ts @@ -1,4 +1,3 @@ -// @flow import { fixtureP10Single, fixtureP10Multi, @@ -9,7 +8,6 @@ import fixture_384_plate from '../../../labware/fixtures/2/fixture_384_plate.jso import fixture_overlappy_wellplate from '../../../labware/fixtures/2/fixture_overlappy_wellplate' import { makeWellSetHelpers } from '../wellSets' import { findWellAt } from '../getWellNamePerMultiTip' - describe('findWellAt', () => { it('should determine if given (x, y) is within a rectangular well', () => { const def: any = { @@ -25,21 +23,16 @@ describe('findWellAt', () => { } const middle = findWellAt(def, 200, 200) expect(middle).toBe('A1') - const insideCornerNE = findWellAt(def, 200 - 4, 200 + 4) expect(insideCornerNE).toEqual('A1') - // exactly at an edge doesn't count const exactlyOnCornerNE = findWellAt(def, 200 - 5, 200 + 5) expect(exactlyOnCornerNE).toBeUndefined() - const exactlyOnWEdge = findWellAt(def, 200, 200 - 5) expect(exactlyOnWEdge).toBeUndefined() - const justOutsideToEast = findWellAt(def, 200 + 5.1, 200) expect(justOutsideToEast).toBeUndefined() }) - it('should determine if given (x, y) is within a circular well', () => { const def: any = { wells: { @@ -53,19 +46,15 @@ describe('findWellAt', () => { } const middle = findWellAt(def, 200, 200) expect(middle).toBe('A1') - const inside = findWellAt(def, 200 - 1, 200 + 1) expect(inside).toEqual('A1') - // exactly at an edge doesn't count const exactlyOnWEdge = findWellAt(def, 200, 200 - 5) expect(exactlyOnWEdge).toBeUndefined() - const justOutsideToEast = findWellAt(def, 200 + 5.1, 200) expect(justOutsideToEast).toBeUndefined() }) }) - describe('canPipetteUseLabware', () => { let canPipetteUseLabware beforeEach(() => { @@ -83,7 +72,6 @@ describe('canPipetteUseLabware', () => { expect(canPipetteUseLabware(pipette, labwareDef)).toBe(true) }) }) - describe('getWellSetForMultichannel (integration test)', () => { let getWellSetForMultichannel beforeEach(() => { @@ -102,7 +90,6 @@ describe('getWellSetForMultichannel (integration test)', () => { 'G1', 'H1', ]) - expect(getWellSetForMultichannel(labwareDef, 'B1')).toEqual([ 'A1', 'B1', @@ -113,7 +100,6 @@ describe('getWellSetForMultichannel (integration test)', () => { 'G1', 'H1', ]) - expect(getWellSetForMultichannel(labwareDef, 'H1')).toEqual([ 'A1', 'B1', @@ -124,7 +110,6 @@ describe('getWellSetForMultichannel (integration test)', () => { 'G1', 'H1', ]) - expect(getWellSetForMultichannel(labwareDef, 'A2')).toEqual([ 'A2', 'B2', @@ -136,12 +121,10 @@ describe('getWellSetForMultichannel (integration test)', () => { 'H2', ]) }) - it('invalid well', () => { const labwareDef = fixture_96_plate expect(getWellSetForMultichannel(labwareDef, 'A13')).toBeFalsy() }) - it('trough-12row', () => { const labwareDef = fixture_12_trough expect(getWellSetForMultichannel(labwareDef, 'A1')).toEqual([ @@ -154,7 +137,6 @@ describe('getWellSetForMultichannel (integration test)', () => { 'A1', 'A1', ]) - expect(getWellSetForMultichannel(labwareDef, 'A2')).toEqual([ 'A2', 'A2', @@ -166,7 +148,6 @@ describe('getWellSetForMultichannel (integration test)', () => { 'A2', ]) }) - it('384-plate', () => { const labwareDef = fixture_384_plate expect(getWellSetForMultichannel(labwareDef, 'C1')).toEqual([ @@ -179,7 +160,6 @@ describe('getWellSetForMultichannel (integration test)', () => { 'M1', 'O1', ]) - expect(getWellSetForMultichannel(labwareDef, 'F2')).toEqual([ 'B2', 'D2', diff --git a/shared-data/js/helpers/getWellNamePerMultiTip.js b/shared-data/js/helpers/getWellNamePerMultiTip.ts similarity index 98% rename from shared-data/js/helpers/getWellNamePerMultiTip.js rename to shared-data/js/helpers/getWellNamePerMultiTip.ts index 15f7f368f0a..8598b5d0ece 100644 --- a/shared-data/js/helpers/getWellNamePerMultiTip.js +++ b/shared-data/js/helpers/getWellNamePerMultiTip.ts @@ -1,17 +1,16 @@ -// @flow import range from 'lodash/range' import { getLabwareHasQuirk, sortWells } from '.' import type { LabwareDefinition2 } from '@opentrons/shared-data' - // 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) => { @@ -32,13 +31,13 @@ export function findWellAt( ) }) } - // "topWellName" means well at the "top" of the column we're accessing: usually A row, or B row for 384-format export function getWellNamePerMultiTip( labwareDef: LabwareDefinition2, topWellName: string ): Array | null { const topWell = labwareDef.wells[topWellName] + if (!topWell) { console.warn( `well "${topWellName}" does not exist in labware ${labwareDef?.namespace}/${labwareDef?.parameters?.loadName}, cannot getWellNamePerMultiTip` @@ -62,13 +61,14 @@ export function getWellNamePerMultiTip( const wellsAccessed = offsetYTipPositions.reduce( (acc: Array | 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 97% rename from shared-data/js/helpers/index.js rename to shared-data/js/helpers/index.ts index fb315ed5f36..500e97ca3b2 100644 --- a/shared-data/js/helpers/index.js +++ b/shared-data/js/helpers/index.ts @@ -1,21 +1,16 @@ -// @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' - export const getLabwareDefIsStandard = (def: LabwareDefinition2): boolean => def?.namespace === OPENTRONS_LABWARE_NAMESPACE - export const getLabwareDefURI = (def: LabwareDefinition2): string => `${def.namespace}/${def.parameters.loadName}/${def.version}` - // Load names of "retired" labware // TODO(mc, 2019-12-3): how should this correspond to LABWAREV2_DO_NOT_LIST? // see shared-data/js/getLabware.js @@ -32,7 +27,6 @@ const RETIRED_LABWARE = [ 'eppendorf_96_tiprack_1000ul_eptips', 'eppendorf_96_tiprack_10ul_eptips', ] - export const getLabwareDisplayName = ( labwareDef: LabwareDefinition2 ): string => { @@ -44,9 +38,9 @@ export const getLabwareDisplayName = ( ) { return `(Retired) ${displayName}` } + return displayName } - export const getTiprackVolume = (labwareDef: LabwareDefinition2): number => { assert( labwareDef.parameters.isTiprack, @@ -62,7 +56,6 @@ export const getTiprackVolume = (labwareDef: LabwareDefinition2): number => { ) return volume } - export function getLabwareHasQuirk( labwareDef: LabwareDefinition2, quirk: string @@ -70,20 +63,18 @@ export function getLabwareHasQuirk( const quirks = labwareDef.parameters.quirks return quirks ? quirks.includes(quirk) : false } - 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 +99,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) { @@ -121,16 +113,18 @@ export function sortWells(a: string, b: string): number { return letterA > letterB ? 1 : -1 } - export function splitWellsOnColumn( sortedArray: Array ): Array> { 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 { @@ -138,12 +132,10 @@ export function splitWellsOnColumn( } }, []) } - export const getWellDepth = ( labwareDef: LabwareDefinition2, well: string ): number => labwareDef.wells[well].depth - // NOTE: this is used in PD for converting "offset from top" to "mm from bottom". // Assumes all wells have same offset because multi-offset not yet supported. // TODO: Ian 2019-07-13 return {[string: well]: offset} to support multi-offset @@ -152,6 +144,7 @@ export const getWellsDepth = ( wells: Array ): 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..8d2b9535209 100644 --- a/shared-data/js/helpers/volume.js +++ b/shared-data/js/helpers/volume.ts @@ -1,30 +1,26 @@ -// @flow // utilities for working with volumes in µL import round from 'lodash/round' import type { LabwareVolumeUnits } from '../types' - const SCALE_BY_UNITS = { µL: 1, mL: 1000, L: 1000000, } - export function getDisplayVolume( volumeInMicroliters: number, - displayUnits?: LabwareVolumeUnits = 'µL', + displayUnits: LabwareVolumeUnits = 'µL', digits?: number ): string { const volume = volumeInMicroliters / SCALE_BY_UNITS[displayUnits] - return `${typeof digits === 'number' ? round(volume, digits) : volume}` } - 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 89% rename from shared-data/js/helpers/wellSets.js rename to shared-data/js/helpers/wellSets.ts index 5d25b4aa496..722a732ca3d 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. // @@ -16,7 +15,6 @@ import { getLabwareDefURI } from '@opentrons/shared-data' import uniq from 'lodash/uniq' import { getWellNamePerMultiTip } from './getWellNamePerMultiTip' import type { LabwareDefinition2, PipetteNameSpecs } from '../types' - type WellSetByPrimaryWell = Array> // Compute all well sets for a labware def (non-memoized) @@ -34,47 +32,55 @@ function _getAllWellSetsForLabware( } // creates memoized getAllWellSetsForLabware + getWellSetForMultichannel fns. -export type WellSetHelpers = {| +export type WellSetHelpers = { getAllWellSetsForLabware: ( labwareDef: LabwareDefinition2 - ) => WellSetByPrimaryWell, + ) => WellSetByPrimaryWell getWellSetForMultichannel: ( labwareDef: LabwareDefinition2, well: string - ) => ?Array, + ) => Array | null | undefined canPipetteUseLabware: ( pipetteSpec: PipetteNameSpecs, labwareDef: LabwareDefinition2 - ) => boolean, -|} + ) => boolean +} export const makeWellSetHelpers = (): WellSetHelpers => { - const cache: { - [labwareDefURI: string]: ?{| - labwareDef: LabwareDefinition2, - wellSetByPrimaryWell: WellSetByPrimaryWell, - |}, - ... - } = {} + const cache: Record< + string, + | { + labwareDef: LabwareDefinition2 + wellSetByPrimaryWell: WellSetByPrimaryWell + } + | null + | undefined + > = {} 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 => { + ): Array | 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'] 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__/createDefaultDisplayName.test.js b/shared-data/js/labwareTools/__tests__/createDefaultDisplayName.test.ts similarity index 99% rename from shared-data/js/labwareTools/__tests__/createDefaultDisplayName.test.js rename to shared-data/js/labwareTools/__tests__/createDefaultDisplayName.test.ts index e1368916f7f..727f401d1ab 100644 --- a/shared-data/js/labwareTools/__tests__/createDefaultDisplayName.test.js +++ b/shared-data/js/labwareTools/__tests__/createDefaultDisplayName.test.ts @@ -1,5 +1,4 @@ import { createDefaultDisplayName } from '../index' - describe('createDefaultDisplayName', () => { const testCases = [ { @@ -94,7 +93,6 @@ describe('createDefaultDisplayName', () => { expected: 'Generic 6 Well Plate 0.123 mL', }, ] - testCases.forEach(({ testName, args, expected }) => { it(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 75% rename from shared-data/js/labwareTools/__tests__/createIrregularLabware.test.js rename to shared-data/js/labwareTools/__tests__/createIrregularLabware.test.ts index 682768ee43a..af17f5b3cb2 100644 --- a/shared-data/js/labwareTools/__tests__/createIrregularLabware.test.js +++ b/shared-data/js/labwareTools/__tests__/createIrregularLabware.test.ts @@ -1,16 +1,13 @@ import omit from 'lodash/omit' import range from 'lodash/range' - import { createIrregularLabware, _irregularWellName, _generateIrregularLoadName, _calculateWellCoord, -} from '../index.js' -import { splitWellsOnColumn, sortWells } from '../../helpers/index.js' - +} from '../index' +import { splitWellsOnColumn, sortWells } from '../../helpers/index' import fixture_irregular_example_1 from '../../../labware/fixtures/2/fixture_irregular_example_1.json' - // NOTE: loadName needs to be replaced here b/c fixture has a non-default loadName const exampleLabware1 = { ...fixture_irregular_example_1, @@ -19,13 +16,25 @@ const exampleLabware1 = { loadName: 'generic_55_tuberack_50x3ml_5x10ml', }, } - describe('test helper functions', () => { it('Well name generated correctly', () => { - const grid = { row: 2, column: 2 } + const grid = { + row: 2, + column: 2, + } const gridStart = [ - { rowStart: 'A', colStart: '1', rowStride: 1, colStride: 2 }, - { rowStart: 'B', colStart: '1', rowStride: 3, colStride: 1 }, + { + 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'] @@ -33,20 +42,35 @@ describe('test helper functions', () => { range(grid.column).forEach(colIdx => { range(grid.row).forEach(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 }) }) }) - it('XYZ generates correctly for each grid', () => { - const grid = { row: 1, column: 5 } - const offset = { x: 1, y: 0.5, z: 55.5 } + const grid = { + row: 1, + column: 5, + } + const offset = { + x: 1, + y: 0.5, + z: 55.5, + } const spacing = [ - { row: 10, column: 10 }, - { row: 5, column: 14 }, + { + row: 10, + column: 10, + }, + { + row: 5, + column: 14, + }, ] const well = [ omit(exampleLabware1.wells.A1, ['x', 'y', 'z']), @@ -56,7 +80,6 @@ describe('test helper functions', () => { const expectedY1 = [0.5] const expectedX2 = [1, 15, 29, 43, 57] const expectedY2 = [0.5] - range(grid.column).forEach(colIdx => { range(grid.row).forEach(rowIdx => { const well1 = _calculateWellCoord( @@ -66,6 +89,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 +101,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) @@ -84,7 +109,6 @@ describe('test helper functions', () => { }) }) }) - describe('test createIrregularLabware function', () => { let labware1 let labware1Args @@ -122,40 +146,73 @@ describe('test createIrregularLabware function', () => { }, ], offset: [ - { x: 10, y: 10, z: 69.48 }, - { x: 15, y: 15, z: 69.48 }, + { + x: 10, + y: 10, + z: 69.48, + }, + { + x: 15, + y: 15, + z: 69.48, + }, ], grid: [ - { row: 5, column: 10 }, - { row: 1, column: 5 }, + { + row: 5, + column: 10, + }, + { + row: 1, + column: 5, + }, ], spacing: [ - { row: 5, column: 10 }, - { row: 5, column: 10 }, + { + row: 5, + column: 10, + }, + { + row: 5, + column: 10, + }, ], gridStart: [ - { rowStart: 'A', colStart: '1', rowStride: 2, colStride: 1 }, - { rowStart: 'B', colStart: '1', rowStride: 1, colStride: 1 }, + { + rowStart: 'A', + colStart: '1', + rowStride: 2, + colStride: 1, + }, + { + rowStart: 'B', + colStart: '1', + rowStride: 1, + colStride: 1, + }, ], } labware1 = createIrregularLabware(labware1Args) }) - it('irregular ordering generates as expected', () => { const keyList = Object.keys(labware1.wells) const generatedOrdering = splitWellsOnColumn(keyList.sort(sortWells)) expect(labware1.ordering).toEqual(generatedOrdering) }) - it('check labware matches fixture', () => { expect(labware1).toEqual(exampleLabware1) }) - it('labware loadName generated correctly for multi-grid labware', () => { const loadName = _generateIrregularLoadName({ grid: [ - { row: 3, column: 2 }, - { row: 1, column: 4 }, + { + row: 3, + column: 2, + }, + { + row: 1, + column: 4, + }, ], well: [ { @@ -177,11 +234,21 @@ describe('test createIrregularLabware function', () => { expect(loadName).toEqual('somebrand_10_wellplate_6x400ul_4x2000ul') }) - 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 }], + grid: [ + { + row: 3, + column: 2, + }, + ], + well: [ + { + depth: 20, + shape: 'circular', + totalLiquidVolume: 4000, + }, + ], totalWellCount: 6, units: 'mL', displayCategory: 'wellPlate', @@ -190,17 +257,23 @@ describe('test createIrregularLabware function', () => { expect(loadName).toEqual('somebrand_6_wellplate_6x4ml') }) - it('failing to validate against labware schema throws w/o "strict"', () => { const args = { ...labware1Args, // negative y offset should fail schema validation by making well `y` negative offset: [ - { x: 10, y: -9999, z: 69.48 }, - { x: 15, y: -9999, z: 69.48 }, + { + x: 10, + y: -9999, + z: 69.48, + }, + { + 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 85% rename from shared-data/js/labwareTools/__tests__/createLabware.test.js rename to shared-data/js/labwareTools/__tests__/createLabware.test.ts index a349d4beda1..2ac8b6e0e57 100644 --- a/shared-data/js/labwareTools/__tests__/createLabware.test.js +++ b/shared-data/js/labwareTools/__tests__/createLabware.test.ts @@ -1,11 +1,8 @@ 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' - // NOTE: loadName needs to be replaced here b/c fixture has a non-default loadName const exampleLabware1 = { ...fixture_regular_example_1, @@ -14,7 +11,6 @@ const exampleLabware1 = { loadName: 'opentrons_2_wellplate_100ul', }, } - const exampleLabware2 = { ...fixture_regular_example_2, parameters: { @@ -22,7 +18,6 @@ const exampleLabware2 = { loadName: 'generic_6_wellplate_1ml', }, } - describe('createLabware', () => { let labware1 let labware2 @@ -31,55 +26,73 @@ describe('createLabware', () => { let well2 let offset1 let offset2 - beforeEach(() => { well1 = omit(exampleLabware1.wells.A1, ['x', 'y', 'z']) well2 = omit(exampleLabware2.wells.A1, ['x', 'y', 'z']) - offset1 = { x: 10, y: 10, z: 55 } - offset2 = { x: 10, y: 10, z: 40 } - + offset1 = { + x: 10, + y: 10, + z: 55, + } + offset2 = { + x: 10, + y: 10, + z: 40, + } labware1 = createRegularLabware({ metadata: exampleLabware1.metadata, parameters: exampleLabware1.parameters, dimensions: exampleLabware1.dimensions, offset: offset1, - grid: { row: 1, column: 2 }, - spacing: { row: 10, column: 10 }, + grid: { + row: 1, + column: 2, + }, + spacing: { + row: 10, + column: 10, + }, well: well1, 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) }) - it('ordering generates as expected', () => { expect(exampleLabware2.ordering).toEqual(labware2.ordering) }) - it('well XYZ generates correctly', () => { - const spacing = { row: 10, column: 10 } - const grid = { row: 3, column: 2 } - + const spacing = { + row: 10, + column: 10, + } + const grid = { + row: 3, + column: 2, + } const { yDimension } = exampleLabware2.dimensions const labware3 = createRegularLabware({ metadata: exampleLabware2.metadata, @@ -90,19 +103,16 @@ describe('createLabware', () => { spacing, well: well2, }) - const expectedXByCol = range( offset2.x, offset2.x + grid.column * spacing.column, spacing.column ) - const expectedYByRow = range( yDimension - offset2.y, yDimension - offset2.y - spacing.row * grid.row, -spacing.row ) - labware3.ordering.forEach((column, cIndex) => { column.forEach((wellName, rIndex) => { const well = labware3.wells[wellName] @@ -112,14 +122,15 @@ describe('createLabware', () => { }) }) }) - it('failing to validate against labware schema throws w/o "strict"', () => { 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() expect(() => createRegularLabware({ ...args, strict: false })).not.toThrow() }) diff --git a/shared-data/js/labwareTools/index.js b/shared-data/js/labwareTools/index.ts similarity index 81% rename from shared-data/js/labwareTools/index.js rename to shared-data/js/labwareTools/index.ts index 0e26b906788..71c6fde80ec 100644 --- a/shared-data/js/labwareTools/index.js +++ b/shared-data/js/labwareTools/index.ts @@ -1,9 +1,8 @@ -// @flow +import { $Diff } from 'utility-types' 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, @@ -13,7 +12,6 @@ import { getAsciiVolumeUnits, ensureVolumeUnits, } from '../helpers/index' - import type { LabwareDefinition2 as Definition, LabwareMetadata as Metadata, @@ -27,73 +25,76 @@ import type { LabwareOffset as Offset, LabwareVolumeUnits as VolumeUnits, } from '../types' - // 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, -|} - +type 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, -|} - -const ajv = new Ajv({ allErrors: true, jsonPointers: true }) +type GridStart = { + rowStart: string + colStart: string + rowStride: number + colStride: number +} +type InputParams = $Diff< + Params, + { + loadName: unknown + } +> +type InputWellGroup = $Diff< + WellGroup, + { + wells: unknown + } +> +export type BaseLabwareProps = { + metadata: Metadata + parameters: InputParams + dimensions: Dimensions + brand?: Brand + version?: number + namespace?: string + loadNamePostfix?: Array + strict?: boolean | null | undefined // 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 +} +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,9 +113,11 @@ 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( rowIdx: number, colIdx: number, @@ -129,10 +132,7 @@ 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 } } function determineIrregularLayout( @@ -142,13 +142,17 @@ function determineIrregularLayout( gridStart: Array, wells: Array, group: Array = [] -): { wells: WellMap, groups: Array } { +): { + wells: WellMap + groups: Array +} { return grids.reduce( (result, gridObj, gridIdx) => { const reverseRowIdx = range(gridObj.row - 1, -1) - const inputGroup = group[gridIdx] || { metadata: {} } + const inputGroup = group[gridIdx] || { + metadata: {}, + } const currentGroup = { ...inputGroup, wells: [] } - range(gridObj.column).forEach(colIdx => { range(gridObj.row).forEach(rowIdx => { const wellName = _irregularWellName( @@ -156,6 +160,7 @@ function determineIrregularLayout( colIdx, gridStart[gridIdx] ) + currentGroup.wells.push(wellName) result.wells[wellName] = _calculateWellCoord( reverseRowIdx[rowIdx], @@ -166,21 +171,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: Array + well: Array + totalWellCount: number + units: VolumeUnits + brand: string + displayCategory: string + loadNamePostfix?: Array }): string { const { grid, @@ -195,10 +205,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, @@ -211,9 +219,13 @@ export function _generateIrregularLoadName(args: { // Decide order of wells for single grid containers function determineOrdering(grid: Cell): Array> { 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 } @@ -223,7 +235,6 @@ export function determineIrregularOrdering( ): Array> { const sortedArray = wellsArray.sort(sortWells) const ordering = splitWellsOnColumn(sortedArray) - return ordering } @@ -237,7 +248,6 @@ function calculateCoordinates( dimensions: Dimensions ): WellMap { const { yDimension } = dimensions - return ordering.reduce((wellMap, column, cIndex) => { return column.reduce( (colWellMap, wellName, rIndex) => ({ @@ -255,7 +265,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 @@ -271,15 +285,14 @@ function joinLoadName( } type RegularNameProps = { - displayCategory: string, - displayVolumeUnits: VolumeUnits, - gridRows: number, - gridColumns: number, - totalLiquidVolume: number, - brandName?: string, - loadNamePostfix?: Array, + displayCategory: string + displayVolumeUnits: VolumeUnits + gridRows: number + gridColumns: number + totalLiquidVolume: number + brandName?: string + loadNamePostfix?: Array } - export function createRegularLoadName(args: RegularNameProps): string { const { gridRows, @@ -305,6 +318,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 +347,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 +358,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 +374,6 @@ export function createRegularLabware(args: RegularLabwareProps): Definition { brandName: brand.brand, loadNamePostfix, }) - return validateDefinition( { ordering, @@ -372,12 +386,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 +439,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 79% rename from shared-data/js/modules.js rename to shared-data/js/modules.ts index 1c80b064b49..cf1802b6b3d 100644 --- a/shared-data/js/modules.js +++ b/shared-data/js/modules.ts @@ -1,10 +1,8 @@ -// @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' import temperatureModuleV2 from '../module/definitions/2/temperatureModuleV2.json' import thermocyclerModuleV1 from '../module/definitions/2/thermocyclerModuleV1.json' - import { MAGDECK, MAGNETIC_MODULE_V1, @@ -15,78 +13,76 @@ import { THERMOCYCLER, THERMOCYCLER_MODULE_V1, } from './constants' - 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, -|} +type 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 type ModuleDef2 = { + moduleType: ModuleRealType + model: ModuleModel + labwareOffset: Coordinates + dimensions: { + bareOverallHeight: number + overLabwareHeight: number + lidHeight?: number + } + calibrationPoint: Coordinates + displayName: string + quirks: Array + slotTransforms: Record< + string, + Record> + > + compatibleWith: Array +} // TODO IMMEDIATELY: Phase out code that uses legacy models export const getModuleDef2 = (moduleModel: ModuleModel): ModuleDef2 => { switch (moduleModel) { case MAGNETIC_MODULE_V1: return magneticModuleV1 + case MAGNETIC_MODULE_V2: return magneticModuleV2 + case TEMPERATURE_MODULE_V1: return temperatureModuleV1 + case TEMPERATURE_MODULE_V2: return temperatureModuleV2 + case THERMOCYCLER_MODULE_V1: return thermocyclerModuleV1 + default: throw new Error(`Invalid module model ${moduleModel}`) } } - 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}`) } } - export function getModuleType(moduleModel: ModuleModel): ModuleRealType { return getModuleDef2(moduleModel).moduleType } - // use module model (not type!) to get model-specific displayName for UI export function getModuleDisplayName(moduleModel: ModuleModel): string { return getModuleDef2(moduleModel).displayName } - export function checkModuleCompatibility( modelA: ModuleModel, modelB: ModuleModel diff --git a/shared-data/js/pipettes.js b/shared-data/js/pipettes.ts similarity index 88% rename from shared-data/js/pipettes.js rename to shared-data/js/pipettes.ts index a90eef9f134..cba8f2043cc 100644 --- a/shared-data/js/pipettes.js +++ b/shared-data/js/pipettes.ts @@ -1,49 +1,42 @@ -// @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' - // models sorted by channels and then volume by default const ALL_PIPETTE_NAMES: Array = Object.keys(pipetteNameSpecs).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] 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: string +): PipetteModelSpecs | null | undefined { const modelSpecificFields = pipetteModelSpecs.config[model] const modelFields = modelSpecificFields && getPipetteNameSpecs(modelSpecificFields.name) - return modelFields && { ...modelFields, ...modelSpecificFields, model } } - export function getAllPipetteNames( ...sortBy: Array ): Array { const models = [...ALL_PIPETTE_NAMES] - if (sortBy.length) models.sort(comparePipettes(sortBy)) - return models } function comparePipettes(sortBy: Array) { return (modelA, modelB) => { // any cast is because we know these pipettes exist - const a: PipetteNameSpecs = (getPipetteNameSpecs(modelA): any) - const b: PipetteNameSpecs = (getPipetteNameSpecs(modelB): any) - + const a: PipetteNameSpecs = getPipetteNameSpecs(modelA) as any + const b: PipetteNameSpecs = getPipetteNameSpecs(modelB) as any 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/js/scripts/build.js b/shared-data/js/scripts/build.ts similarity index 99% rename from shared-data/js/scripts/build.js rename to shared-data/js/scripts/build.ts index be348d96816..c62d79c6967 100644 --- a/shared-data/js/scripts/build.js +++ b/shared-data/js/scripts/build.ts @@ -1,9 +1,10 @@ // This build script is run by `make setup` - // Merge all v1 labware files into a single JSON file, build/labware.json, // with each filename as a key in the final JSON file. const fs = require('fs') + const path = require('path') + const glob = require('glob') const buildDir = process.argv[2] @@ -15,18 +16,14 @@ if (!buildDir) { } const output = {} - const files = glob.sync( path.join(__dirname, '../../labware/definitions/1/*.json') ) - files.forEach(filename => { const contents = require(filename) - const labwareName = path.parse(filename).name + const labwareName = path.parse(filename).name output[labwareName] = contents }) - const jsonOutput = JSON.stringify(output) - fs.writeFileSync(path.join(buildDir, 'labware.json'), jsonOutput) diff --git a/shared-data/js/scripts/generateDeckLayersFromSVG.js b/shared-data/js/scripts/generateDeckLayersFromSVG.ts similarity index 99% rename from shared-data/js/scripts/generateDeckLayersFromSVG.js rename to shared-data/js/scripts/generateDeckLayersFromSVG.ts index 076a680b573..1c00b8ecdf1 100644 --- a/shared-data/js/scripts/generateDeckLayersFromSVG.js +++ b/shared-data/js/scripts/generateDeckLayersFromSVG.ts @@ -1,32 +1,34 @@ const assert = require('assert') + const fs = require('fs') + const path = require('path') + const camelCase = require('lodash/camelCase') + const JSDOM = require('jsdom').JSDOM + const { promisify } = require('util') + const readFile = promisify(fs.readFile) const writeFile = promisify(fs.writeFile) - // NOTE: the expected source SVG is an output SVG from Adobe Illustrator // when using `Save as` or `Save as a Copy` and not `Export` // Furthermore, the .ai file should contain no groups and all layers // will be directly translated to layer groups in the JSON output - const svgPath = process.argv[2] const buildDir = process.argv[3] - const USAGE = 'Expected Params Not Present: (e.g. node ./scripts/generateDeckLayersFromSVG ./path/to/source/svg ./path/to/output/dir)' assert(svgPath && buildDir, USAGE) - readFile(svgPath, 'utf8') .then(data => { const doc = new JSDOM(data, 'text/xml') const groups = Array.from(doc.window.document.querySelectorAll('g')) - let layers = {} groups.forEach(g => { const groupId = g.getAttribute('id') + if (groupId && g.hasChildNodes()) { const features = Array.from(g.querySelectorAll('path')) const paths = features.map(f => ({ @@ -35,7 +37,6 @@ readFile(svgPath, 'utf8') layers = { ...layers, [camelCase(groupId)]: paths } } }) - const jsonOutput = JSON.stringify(layers) return writeFile(path.join(buildDir, 'deckLayers.json'), jsonOutput) }) diff --git a/shared-data/js/types.js b/shared-data/js/types.js deleted file mode 100644 index 2a40252b3d3..00000000000 --- a/shared-data/js/types.js +++ /dev/null @@ -1,316 +0,0 @@ -// @flow -import typeof { - 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, -} 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 - 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 = { - metadata: { - name: string, - format: string, - deprecated?: boolean, - displayName?: string, - displayCategory?: string, - isValidSource?: boolean, - isTiprack?: boolean, - tipVolume?: number, - }, - ordering: Array>, - 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' - -export type LabwareMetadata = {| - displayName: string, - displayCategory: LabwareDisplayCategory, - displayVolumeUnits: LabwareVolumeUnits, - tags?: Array, -|} - -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, - isTiprack: boolean, - tipLength?: number, - isMagneticModuleCompatible: boolean, - magneticModuleEngageHeight?: number, - quirks?: Array, -|} - -export type LabwareBrand = {| - brand: string, - brandId?: Array, - links?: Array, -|} - -export type LabwareWellShapeProperties = - | {| - shape: 'circular', - diameter: number, - |} - | {| - shape: 'rectangular', - xDimension: number, - yDimension: number, - |} - -// well without x,y,z -export type LabwareWellProperties = {| - ...LabwareWellShapeProperties, - depth: number, - totalLiquidVolume: number, -|} - -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 LabwareWellGroupMetadata = {| - displayName?: string, - displayCategory?: LabwareDisplayCategory, - wellBottomShape?: WellBottomShape, -|} - -export type LabwareWellGroup = {| - wells: Array, - metadata: LabwareWellGroupMetadata, - brand?: LabwareBrand, -|} - -// NOTE: must be synced with shared-data/labware/schemas/2.json -export type LabwareDefinition2 = {| - version: number, - schemaVersion: 2, - namespace: string, - metadata: LabwareMetadata, - dimensions: LabwareDimensions, - cornerOffsetFromSlot: LabwareOffset, - parameters: LabwareParameters, - brand: LabwareBrand, - ordering: Array>, - 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 -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 -export type TemperatureModuleModel = - | TEMPERATURE_MODULE_V1 - | TEMPERATURE_MODULE_V2 -export type ThermocyclerModuleModel = THERMOCYCLER_MODULE_V1 - -export type ModuleModel = - | MagneticModuleModel - | TemperatureModuleModel - | ThermocyclerModuleModel - -export type ModuleModelWithLegacy = - | ModuleModel - | THERMOCYCLER - | MAGDECK - | 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 DeckSlot = {| - id: DeckSlotId, - position: CoordinateTuple, - matingSurfaceUnitVector?: UnitVectorTuple, - boundingBox: Dimensions, - displayName: string, - compatibleModules: Array, -|} -export type DeckCalibrationPoint = {| - id: string, - position: CoordinateTuple, - displayName: string, -|} -export type DeckLocations = {| - orderedSlots: Array, - calibrationPoints: Array, - fixtures: Array, -|} - -export type DeckMetadata = {| - displayName: string, - tags: Array, -|} - -export type DeckLayerFeature = {| - footprint: string, -|} - -export type DeckLayer = Array - -export type DeckDefinition = {| - otId: string, - cornerOffsetFromOrigin: CoordinateTuple, - dimensions: CoordinateTuple, - robot: DeckRobot, - locations: DeckLocations, - metadata: DeckMetadata, - layers: { [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 - -export type FlowRateSpec = {| - value: number, - min: number, - max: number, -|} - -export type PipetteNameSpecs = {| - name: string, - displayName: string, - displayCategory: PipetteDisplayCategory, - minVolume: number, - maxVolume: number, - channels: PipetteChannels, - defaultAspirateFlowRate: FlowRateSpec, - defaultDispenseFlowRate: FlowRateSpec, - defaultBlowOutFlowRate: FlowRateSpec, - defaultTipracks: Array, - smoothieConfigs?: {| - stepsPerMM: number, - homePosition: number, - travelDistance: number, - |}, -|} - -// TODO(mc, 2019-10-14): update this type according to the schema -export type PipetteModelSpecs = { - ...PipetteNameSpecs, - model: string, - backCompatNames?: Array, - tipLength: { value: number }, - ... -} diff --git a/shared-data/js/types.ts b/shared-data/js/types.ts new file mode 100644 index 00000000000..131fea17e44 --- /dev/null +++ b/shared-data/js/types.ts @@ -0,0 +1,270 @@ +import type { + 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, +} 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 + 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 = { + metadata: { + name: string + format: string + deprecated?: boolean + displayName?: string + displayCategory?: string + isValidSource?: boolean + isTiprack?: boolean + tipVolume?: number + } + ordering: Array> + wells: Record +} +// 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 = '\xB5L' | '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' +export type LabwareMetadata = { + displayName: string + displayCategory: LabwareDisplayCategory + displayVolumeUnits: LabwareVolumeUnits + tags?: Array +} +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 + isTiprack: boolean + tipLength?: number + isMagneticModuleCompatible: boolean + magneticModuleEngageHeight?: number + quirks?: Array +} +export type LabwareBrand = { + brand: string + brandId?: Array + links?: Array +} +export type LabwareWellShapeProperties = + | { + shape: 'circular' + diameter: number + } + | { + shape: 'rectangular' + xDimension: number + yDimension: number + } +// well without x,y,z +export type LabwareWellProperties = LabwareWellShapeProperties & { + depth: number + totalLiquidVolume: number +} +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 = Record +export type LabwareWellGroupMetadata = { + displayName?: string + displayCategory?: LabwareDisplayCategory + wellBottomShape?: WellBottomShape +} +export type LabwareWellGroup = { + wells: Array + metadata: LabwareWellGroupMetadata + brand?: LabwareBrand +} +// NOTE: must be synced with shared-data/labware/schemas/2.json +export type LabwareDefinition2 = { + version: number + schemaVersion: 2 + namespace: string + metadata: LabwareMetadata + dimensions: LabwareDimensions + cornerOffsetFromSlot: LabwareOffset + parameters: LabwareParameters + brand: LabwareBrand + ordering: Array> + 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 +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 +export type TemperatureModuleModel = + | TEMPERATURE_MODULE_V1 + | TEMPERATURE_MODULE_V2 +export type ThermocyclerModuleModel = THERMOCYCLER_MODULE_V1 +export type ModuleModel = + | MagneticModuleModel + | TemperatureModuleModel + | ThermocyclerModuleModel +export type ModuleModelWithLegacy = + | ModuleModel + | THERMOCYCLER + | MAGDECK + | 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 DeckSlot = { + id: DeckSlotId + position: CoordinateTuple + matingSurfaceUnitVector?: UnitVectorTuple + boundingBox: Dimensions + displayName: string + compatibleModules: Array +} +export type DeckCalibrationPoint = { + id: string + position: CoordinateTuple + displayName: string +} +export type DeckLocations = { + orderedSlots: Array + calibrationPoints: Array + fixtures: Array +} +export type DeckMetadata = { + displayName: string + tags: Array +} +export type DeckLayerFeature = { + footprint: string +} +export type DeckLayer = Array +export type DeckDefinition = { + otId: string + cornerOffsetFromOrigin: CoordinateTuple + dimensions: CoordinateTuple + robot: DeckRobot + locations: DeckLocations + metadata: DeckMetadata + layers: Record +} +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 +export type FlowRateSpec = { + value: number + min: number + max: number +} +export type PipetteNameSpecs = { + name: string + displayName: string + displayCategory: PipetteDisplayCategory + minVolume: number + maxVolume: number + channels: PipetteChannels + defaultAspirateFlowRate: FlowRateSpec + defaultDispenseFlowRate: FlowRateSpec + defaultBlowOutFlowRate: FlowRateSpec + defaultTipracks: Array + smoothieConfigs?: { + stepsPerMM: number + homePosition: number + travelDistance: number + } +} +// TODO(mc, 2019-10-14): update this type according to the schema +export type PipetteModelSpecs = PipetteNameSpecs & { + model: string + backCompatNames?: Array + tipLength: { + value: number + } +} diff --git a/shared-data/package.json b/shared-data/package.json index edf96c437c2..a09568e0d77 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/index.d.ts", + "flow:main": "flow-types/index.js.flow", "dependencies": { "ajv": "6.10.2", "lodash": "4.17.15" diff --git a/shared-data/tsconfig.json b/shared-data/tsconfig.json new file mode 100644 index 00000000000..74043bbb23c --- /dev/null +++ b/shared-data/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig-base.json", + "references": [], + "compilerOptions": { + "composite": true, + "rootDir": "js", + "outDir": "lib" + }, + "include": ["typings", "js"] +} From 14c6e5b357cbb966f7fe71eb7783db4697e50203 Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Tue, 27 Apr 2021 13:45:55 -0400 Subject: [PATCH 02/16] fixup: knock out some linting and typechecking errors --- .eslintrc.js | 1 + shared-data/Makefile | 6 +- shared-data/js/__tests__/deckSchemas.test.ts | 17 +- .../__tests__/createLabware.test.ts | 88 ++++++---- shared-data/js/labwareTools/index.ts | 89 +++++----- shared-data/js/scripts/{build.ts => build.js} | 9 +- ...romSVG.ts => generateDeckLayersFromSVG.js} | 13 +- shared-data/js/types.ts | 158 ++++++++++++------ shared-data/package.json | 2 +- shared-data/tsconfig.json | 11 +- 10 files changed, 238 insertions(+), 156 deletions(-) rename shared-data/js/scripts/{build.ts => build.js} (99%) rename shared-data/js/scripts/{generateDeckLayersFromSVG.ts => generateDeckLayersFromSVG.js} (99%) diff --git a/.eslintrc.js b/.eslintrc.js index 92f5ed6d453..43b63a60e1c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -87,6 +87,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/shared-data/Makefile b/shared-data/Makefile index 3a62cc883bb..6c620e70678 100644 --- a/shared-data/Makefile +++ b/shared-data/Makefile @@ -6,6 +6,10 @@ SHELL := bash # TODO(mc, 2018-10-25): use dist to match other projects BUILD_DIR := build +# type definitions +typedefs := $(shell yarn -s shx find "lib/js/**/*.d.ts") +flow_out := $(patsubst lib/js/%.d.ts,flow-types/%.js.flow,$(typedefs)) + # Top level targets .PHONY: all @@ -38,7 +42,7 @@ clean-js: .PHONY: flow-types flow-types: $(flow_out) -flow-types/%.js.flow: lib/%.d.ts +flow-types/%.js.flow: lib/js/%.d.ts yarn flowgen $< --add-flow-header --interface-records --no-inexact --output-file $@ # Python targets diff --git a/shared-data/js/__tests__/deckSchemas.test.ts b/shared-data/js/__tests__/deckSchemas.test.ts index e6c81ef2871..63c86301041 100644 --- a/shared-data/js/__tests__/deckSchemas.test.ts +++ b/shared-data/js/__tests__/deckSchemas.test.ts @@ -3,20 +3,23 @@ import Ajv from 'ajv' import path from 'path' import glob from 'glob' + import deckSchemaV1 from '../../deck/schemas/1.json' import deckSchemaV2 from '../../deck/schemas/2.json' + const v1FixtureGlob = path.join(__dirname, '../../deck/fixtures/1/*.json') const v2FixtureGlob = path.join(__dirname, '../../deck/fixtures/2/*.json') const v1DefGlob = path.join(__dirname, '../../deck/definitions/1/*.json') const v2DefGlob = path.join(__dirname, '../../deck/definitions/2/*.json') -const ajv = new Ajv({ - allErrors: true, - jsonPointers: true, -}) + +const ajv = new Ajv({ allErrors: true, jsonPointers: true }) + const validateV1Schema = ajv.compile(deckSchemaV1) const validateV2Schema = ajv.compile(deckSchemaV2) + describe('validate deck defs and fixtures', () => { const v1Fixtures = glob.sync(v1FixtureGlob) + v1Fixtures.forEach(fixturePath => { const fixtureDef = require(fixturePath) @@ -36,7 +39,9 @@ describe('validate deck defs and fixtures', () => { expect(valid).toBe(true) }) }) + const v2Fixtures = glob.sync(v2FixtureGlob) + v2Fixtures.forEach(fixturePath => { const fixtureDef = require(fixturePath) @@ -56,7 +61,9 @@ describe('validate deck defs and fixtures', () => { expect(valid).toBe(true) }) }) + const v1Defs = glob.sync(v1DefGlob) + v1Defs.forEach(defPath => { const deckDef = require(defPath) @@ -76,7 +83,9 @@ describe('validate deck defs and fixtures', () => { expect(valid).toBe(true) }) }) + const v2Defs = glob.sync(v2DefGlob) + v2Defs.forEach(defPath => { const deckDef = require(defPath) diff --git a/shared-data/js/labwareTools/__tests__/createLabware.test.ts b/shared-data/js/labwareTools/__tests__/createLabware.test.ts index 2ac8b6e0e57..3f92288289f 100644 --- a/shared-data/js/labwareTools/__tests__/createLabware.test.ts +++ b/shared-data/js/labwareTools/__tests__/createLabware.test.ts @@ -3,6 +3,15 @@ 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, @@ -10,48 +19,48 @@ const exampleLabware1 = { ...fixture_regular_example_1.parameters, loadName: 'opentrons_2_wellplate_100ul', }, -} +} as LabwareDefinition2 + const exampleLabware2 = { ...fixture_regular_example_2, parameters: { ...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']) - offset1 = { - x: 10, - y: 10, - z: 55, - } - offset2 = { - x: 10, - y: 10, - z: 40, - } + 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 } + labware1 = createRegularLabware({ metadata: exampleLabware1.metadata, parameters: exampleLabware1.parameters, dimensions: exampleLabware1.dimensions, offset: offset1, - grid: { - row: 1, - column: 2, - }, - spacing: { - row: 10, - column: 10, - }, + grid: { row: 1, column: 2 }, + spacing: { row: 10, column: 10 }, well: well1, brand: exampleLabware1.brand, namespace: 'fixture', @@ -74,26 +83,25 @@ describe('createLabware', () => { } labware2 = createRegularLabware(labware2Args) }) + afterEach(() => { jest.clearAllMocks() }) + it('snapshot tests', () => { expect(labware1).toEqual(exampleLabware1) expect(labware2).toEqual(exampleLabware2) }) + it('ordering generates as expected', () => { expect(exampleLabware2.ordering).toEqual(labware2.ordering) }) + it('well XYZ generates correctly', () => { - const spacing = { - row: 10, - column: 10, - } - const grid = { - row: 3, - column: 2, - } + 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, @@ -103,25 +111,30 @@ describe('createLabware', () => { spacing, well: well2, }) + const expectedXByCol = range( offset2.x, offset2.x + grid.column * spacing.column, spacing.column ) + const expectedYByRow = range( yDimension - offset2.y, yDimension - offset2.y - spacing.row * grid.row, -spacing.row ) + 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) }) }) }) + it('failing to validate against labware schema throws w/o "strict"', () => { const args = { ...labware2Args, @@ -131,6 +144,7 @@ describe('createLabware', () => { column: 999, }, } + expect(() => createRegularLabware(args)).toThrowErrorMatchingSnapshot() expect(() => createRegularLabware({ ...args, strict: false })).not.toThrow() }) diff --git a/shared-data/js/labwareTools/index.ts b/shared-data/js/labwareTools/index.ts index 71c6fde80ec..2dcb9996e2e 100644 --- a/shared-data/js/labwareTools/index.ts +++ b/shared-data/js/labwareTools/index.ts @@ -1,9 +1,10 @@ -import { $Diff } from 'utility-types' 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, @@ -12,6 +13,7 @@ import { getAsciiVolumeUnits, ensureVolumeUnits, } from '../helpers/index' + import type { LabwareDefinition2 as Definition, LabwareMetadata as Metadata, @@ -25,64 +27,61 @@ import type { LabwareOffset as Offset, LabwareVolumeUnits as VolumeUnits, } from '../types' + // 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 = { + +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 = { +interface GridStart { rowStart: string colStart: string rowStride: number colStride: number } -type InputParams = $Diff< - Params, - { - loadName: unknown - } -> -type InputWellGroup = $Diff< - WellGroup, - { - wells: unknown - } -> -export type BaseLabwareProps = { + +type InputParams = Omit + +type InputWellGroup = Omit + +export interface BaseLabwareProps { metadata: Metadata parameters: InputParams dimensions: Dimensions brand?: Brand version?: number namespace?: string - loadNamePostfix?: Array + loadNamePostfix?: string[] strict?: boolean | null | undefined // If true, throws error on failed validation } -export type RegularLabwareProps = BaseLabwareProps & { + +export interface RegularLabwareProps extends 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 + +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 ajv = new Ajv({ allErrors: true, jsonPointers: true }) const validate = ajv.compile(labwareSchema) function validateDefinition( @@ -136,15 +135,15 @@ export function _calculateWellCoord( } function determineIrregularLayout( - grids: Array, - spacing: Array, - offset: Array, - gridStart: Array, - wells: Array, - group: Array = [] + grids: Cell[], + spacing: Cell[], + offset: Offset[], + gridStart: GridStart[], + wells: InputWell[], + group: InputWellGroup[] = [] ): { wells: WellMap - groups: Array + groups: WellGroup[] } { return grids.reduce( (result, gridObj, gridIdx) => { @@ -184,13 +183,13 @@ function determineIrregularLayout( } export function _generateIrregularLoadName(args: { - grid: Array - well: Array + grid: Cell[] + well: InputWell[] totalWellCount: number units: VolumeUnits brand: string displayCategory: string - loadNamePostfix?: Array + loadNamePostfix?: string[] }): string { const { grid, @@ -217,7 +216,7 @@ 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({ @@ -230,9 +229,7 @@ function determineOrdering(grid: Cell): Array> { } // 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 @@ -242,7 +239,7 @@ 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 @@ -284,14 +281,14 @@ function joinLoadName( .replace(/[^a-z0-9_.]/g, '') } -type RegularNameProps = { +interface RegularNameProps { displayCategory: string displayVolumeUnits: VolumeUnits gridRows: number gridColumns: number totalLiquidVolume: number brandName?: string - loadNamePostfix?: Array + loadNamePostfix?: string[] } export function createRegularLoadName(args: RegularNameProps): string { const { diff --git a/shared-data/js/scripts/build.ts b/shared-data/js/scripts/build.js similarity index 99% rename from shared-data/js/scripts/build.ts rename to shared-data/js/scripts/build.js index c62d79c6967..be348d96816 100644 --- a/shared-data/js/scripts/build.ts +++ b/shared-data/js/scripts/build.js @@ -1,10 +1,9 @@ // This build script is run by `make setup` + // Merge all v1 labware files into a single JSON file, build/labware.json, // with each filename as a key in the final JSON file. const fs = require('fs') - const path = require('path') - const glob = require('glob') const buildDir = process.argv[2] @@ -16,14 +15,18 @@ if (!buildDir) { } const output = {} + const files = glob.sync( path.join(__dirname, '../../labware/definitions/1/*.json') ) + files.forEach(filename => { const contents = require(filename) - const labwareName = path.parse(filename).name + output[labwareName] = contents }) + const jsonOutput = JSON.stringify(output) + fs.writeFileSync(path.join(buildDir, 'labware.json'), jsonOutput) diff --git a/shared-data/js/scripts/generateDeckLayersFromSVG.ts b/shared-data/js/scripts/generateDeckLayersFromSVG.js similarity index 99% rename from shared-data/js/scripts/generateDeckLayersFromSVG.ts rename to shared-data/js/scripts/generateDeckLayersFromSVG.js index 1c00b8ecdf1..076a680b573 100644 --- a/shared-data/js/scripts/generateDeckLayersFromSVG.ts +++ b/shared-data/js/scripts/generateDeckLayersFromSVG.js @@ -1,34 +1,32 @@ const assert = require('assert') - const fs = require('fs') - const path = require('path') - const camelCase = require('lodash/camelCase') - const JSDOM = require('jsdom').JSDOM - const { promisify } = require('util') - const readFile = promisify(fs.readFile) const writeFile = promisify(fs.writeFile) + // NOTE: the expected source SVG is an output SVG from Adobe Illustrator // when using `Save as` or `Save as a Copy` and not `Export` // Furthermore, the .ai file should contain no groups and all layers // will be directly translated to layer groups in the JSON output + const svgPath = process.argv[2] const buildDir = process.argv[3] + const USAGE = 'Expected Params Not Present: (e.g. node ./scripts/generateDeckLayersFromSVG ./path/to/source/svg ./path/to/output/dir)' assert(svgPath && buildDir, USAGE) + readFile(svgPath, 'utf8') .then(data => { const doc = new JSDOM(data, 'text/xml') const groups = Array.from(doc.window.document.querySelectorAll('g')) + let layers = {} groups.forEach(g => { const groupId = g.getAttribute('id') - if (groupId && g.hasChildNodes()) { const features = Array.from(g.querySelectorAll('path')) const paths = features.map(f => ({ @@ -37,6 +35,7 @@ readFile(svgPath, 'utf8') layers = { ...layers, [camelCase(groupId)]: paths } } }) + const jsonOutput = JSON.stringify(layers) return writeFile(path.join(buildDir, 'deckLayers.json'), jsonOutput) }) diff --git a/shared-data/js/types.ts b/shared-data/js/types.ts index 131fea17e44..b188618ed83 100644 --- a/shared-data/js/types.ts +++ b/shared-data/js/types.ts @@ -13,8 +13,9 @@ import type { GEN1, GEN2, } from './constants' + // TODO Ian 2019-06-04 split this out into eg ../labware/flowTypes/labwareV1.js -export type WellDefinition = { +export interface WellDefinition { diameter?: number // NOTE: presence of diameter indicates a circular well depth?: number @@ -27,8 +28,9 @@ export type WellDefinition = { z: number 'total-liquid-volume': number } + // typedef for labware definitions under v1 labware schema -export type LabwareDefinition1 = { +export interface LabwareDefinition1 { metadata: { name: string format: string @@ -39,9 +41,10 @@ export type LabwareDefinition1 = { isTiprack?: boolean tipVolume?: number } - ordering: Array> + ordering: string[][] wells: Record } + // 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 @@ -53,43 +56,51 @@ export type LabwareDisplayCategory = | 'aluminumBlock' | 'trash' | 'other' + export type LabwareVolumeUnits = '\xB5L' | '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' -export type LabwareMetadata = { + +export interface LabwareMetadata { displayName: string displayCategory: LabwareDisplayCategory displayVolumeUnits: LabwareVolumeUnits - tags?: Array + tags?: string[] } -export type LabwareDimensions = { + +export interface LabwareDimensions { xDimension: number yDimension: number zDimension: number } -export type LabwareOffset = { + +export interface 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 = { +export interface LabwareParameters { loadName: string format: string isTiprack: boolean tipLength?: number isMagneticModuleCompatible: boolean magneticModuleEngageHeight?: number - quirks?: Array + quirks?: string[] } -export type LabwareBrand = { + +export interface LabwareBrand { brand: string - brandId?: Array - links?: Array + brandId?: string[] + links?: string[] } + export type LabwareWellShapeProperties = | { shape: 'circular' @@ -100,31 +111,37 @@ export type LabwareWellShapeProperties = xDimension: number yDimension: number } + // well without x,y,z export type LabwareWellProperties = LabwareWellShapeProperties & { depth: number totalLiquidVolume: number } + 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 = Record -export type LabwareWellGroupMetadata = { + +export interface LabwareWellGroupMetadata { displayName?: string displayCategory?: LabwareDisplayCategory wellBottomShape?: WellBottomShape } -export type LabwareWellGroup = { - wells: Array + +export interface LabwareWellGroup { + wells: string[] metadata: LabwareWellGroupMetadata brand?: LabwareBrand } + // NOTE: must be synced with shared-data/labware/schemas/2.json -export type LabwareDefinition2 = { +export interface LabwareDefinition2 { version: number schemaVersion: 2 namespace: string @@ -133,83 +150,106 @@ export type LabwareDefinition2 = { cornerOffsetFromSlot: LabwareOffset parameters: LabwareParameters brand: LabwareBrand - ordering: Array> + ordering: string[][] wells: LabwareWellMap - groups: Array + groups: LabwareWellGroup[] } + // 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 +export type ModuleType = typeof MAGDECK | typeof TEMPDECK | typeof THERMOCYCLER + export type ModuleRealType = - | MAGNETIC_MODULE_TYPE - | TEMPERATURE_MODULE_TYPE - | THERMOCYCLER_MODULE_TYPE + | 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 = MAGNETIC_MODULE_V1 | MAGNETIC_MODULE_V2 +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 + export type ModuleModelWithLegacy = | ModuleModel - | THERMOCYCLER - | MAGDECK - | TEMPDECK -export type DeckOffset = { + | typeof THERMOCYCLER + | typeof MAGDECK + | typeof TEMPDECK + +export interface DeckOffset { x: number y: number z: number } -export type Dimensions = { + +export interface Dimensions { xDimension: number yDimension: number zDimension: number } -export type DeckRobot = { + +export interface DeckRobot { model: string } -export type DeckFixture = { + +export interface 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 DeckSlot = { + +export interface DeckSlot { id: DeckSlotId position: CoordinateTuple matingSurfaceUnitVector?: UnitVectorTuple boundingBox: Dimensions displayName: string - compatibleModules: Array + compatibleModules: ModuleType[] } -export type DeckCalibrationPoint = { + +export interface DeckCalibrationPoint { id: string position: CoordinateTuple displayName: string } -export type DeckLocations = { - orderedSlots: Array - calibrationPoints: Array - fixtures: Array + +export interface DeckLocations { + orderedSlots: DeckSlot[] + calibrationPoints: DeckCalibrationPoint[] + fixtures: DeckFixture[] } -export type DeckMetadata = { + +export interface DeckMetadata { displayName: string - tags: Array + tags: string[] } -export type DeckLayerFeature = { + +export interface DeckLayerFeature { footprint: string } -export type DeckLayer = Array -export type DeckDefinition = { + +export type DeckLayer = DeckLayerFeature[] + +export interface DeckDefinition { otId: string cornerOffsetFromOrigin: CoordinateTuple dimensions: CoordinateTuple @@ -218,32 +258,39 @@ export type DeckDefinition = { metadata: DeckMetadata layers: Record } -export type ModuleDimensions = { + +export interface ModuleDimensions { bareOverallHeight: number overLabwareHeight: number lidHeight: number } -export type ModuleCalibrationPoint = { + +export interface ModuleCalibrationPoint { x: number y: number z?: number } -export type ModuleDefinition = { + +export interface ModuleDefinition { labwareOffset: LabwareOffset dimensions: ModuleDimensions calibrationPoint: ModuleCalibrationPoint displayName: string loadName: string - quirks: Array + quirks: string[] } + export type PipetteChannels = 1 | 8 -export type PipetteDisplayCategory = GEN1 | GEN2 -export type FlowRateSpec = { + +export type PipetteDisplayCategory = typeof GEN1 | typeof GEN2 + +export interface FlowRateSpec { value: number min: number max: number } -export type PipetteNameSpecs = { + +export interface PipetteNameSpecs { name: string displayName: string displayCategory: PipetteDisplayCategory @@ -253,17 +300,18 @@ export type PipetteNameSpecs = { defaultAspirateFlowRate: FlowRateSpec defaultDispenseFlowRate: FlowRateSpec defaultBlowOutFlowRate: FlowRateSpec - defaultTipracks: Array + defaultTipracks: string[] smoothieConfigs?: { stepsPerMM: number homePosition: number travelDistance: number } } + // TODO(mc, 2019-10-14): update this type according to the schema -export type PipetteModelSpecs = PipetteNameSpecs & { +export interface PipetteModelSpecs extends PipetteNameSpecs { model: string - backCompatNames?: Array + backCompatNames?: string[] tipLength: { value: number } diff --git a/shared-data/package.json b/shared-data/package.json index a09568e0d77..c4036cafc37 100755 --- a/shared-data/package.json +++ b/shared-data/package.json @@ -9,7 +9,7 @@ "author": "Opentrons Labworks", "license": "Apache-2.0", "source": "js/index.ts", - "types": "lib/index.d.ts", + "types": "lib/js/index.d.ts", "flow:main": "flow-types/index.js.flow", "dependencies": { "ajv": "6.10.2", diff --git a/shared-data/tsconfig.json b/shared-data/tsconfig.json index 74043bbb23c..93022f2880d 100644 --- a/shared-data/tsconfig.json +++ b/shared-data/tsconfig.json @@ -3,8 +3,15 @@ "references": [], "compilerOptions": { "composite": true, - "rootDir": "js", + "rootDir": ".", "outDir": "lib" }, - "include": ["typings", "js"] + "include": [ + "typings", + "js", + "deck/**/*.json", + "labware/**/*.json", + "module/**/*.json", + "protocol/**/*.json" + ] } From 60952c0f3cb199ebccb55574b4fbe8c1c864d9a6 Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Tue, 27 Apr 2021 14:21:07 -0400 Subject: [PATCH 03/16] fixup: knock out another batch of type errors --- .../js/helpers/__tests__/volume.test.ts | 25 ++- .../js/helpers/__tests__/wellSets.test.ts | 69 ++++++-- shared-data/js/helpers/volume.ts | 8 + shared-data/js/helpers/wellSets.ts | 19 ++- .../createDefaultDisplayName.test.ts | 24 ++- .../__tests__/createIrregularLabware.test.ts | 157 ++++++------------ .../__tests__/createLabware.test.ts | 4 - shared-data/js/labwareTools/index.ts | 15 +- shared-data/js/types.ts | 2 +- shared-data/pipette/fixtures/name/index.js | 2 +- 10 files changed, 177 insertions(+), 148 deletions(-) diff --git a/shared-data/js/helpers/__tests__/volume.test.ts b/shared-data/js/helpers/__tests__/volume.test.ts index 24f0bebf146..fef3e2e8750 100644 --- a/shared-data/js/helpers/__tests__/volume.test.ts +++ b/shared-data/js/helpers/__tests__/volume.test.ts @@ -1,7 +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, @@ -81,7 +100,9 @@ describe('volume helpers', () => { expected: 'L', }, ] + 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.ts b/shared-data/js/helpers/__tests__/wellSets.test.ts index 775eb87d91c..3902a7cd4b1 100644 --- a/shared-data/js/helpers/__tests__/wellSets.test.ts +++ b/shared-data/js/helpers/__tests__/wellSets.test.ts @@ -1,13 +1,26 @@ import { - fixtureP10Single, - fixtureP10Multi, -} from '@opentrons/shared-data/pipette/fixtures/name' + p10_single, + p10_multi, +} from '@opentrons/shared-data/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 = p10_single as PipetteNameSpecs +const fixtureP10Multi = 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 fixtureOverlappyWellplage = fixture_overlappy_wellplate as LabwareDefinition2 + describe('findWellAt', () => { it('should determine if given (x, y) is within a rectangular well', () => { const def: any = { @@ -23,16 +36,21 @@ describe('findWellAt', () => { } const middle = findWellAt(def, 200, 200) expect(middle).toBe('A1') + const insideCornerNE = findWellAt(def, 200 - 4, 200 + 4) expect(insideCornerNE).toEqual('A1') + // exactly at an edge doesn't count const exactlyOnCornerNE = findWellAt(def, 200 - 5, 200 + 5) expect(exactlyOnCornerNE).toBeUndefined() + const exactlyOnWEdge = findWellAt(def, 200, 200 - 5) expect(exactlyOnWEdge).toBeUndefined() + const justOutsideToEast = findWellAt(def, 200 + 5.1, 200) expect(justOutsideToEast).toBeUndefined() }) + it('should determine if given (x, y) is within a circular well', () => { const def: any = { wells: { @@ -46,40 +64,52 @@ describe('findWellAt', () => { } const middle = findWellAt(def, 200, 200) expect(middle).toBe('A1') + const inside = findWellAt(def, 200 - 1, 200 + 1) expect(inside).toEqual('A1') + // exactly at an edge doesn't count const exactlyOnWEdge = findWellAt(def, 200, 200 - 5) expect(exactlyOnWEdge).toBeUndefined() + const justOutsideToEast = findWellAt(def, 200 + 5.1, 200) 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 = fixtureOverlappyWellplage + 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 = fixtureOverlappyWellplage + 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', @@ -90,6 +120,7 @@ describe('getWellSetForMultichannel (integration test)', () => { 'G1', 'H1', ]) + expect(getWellSetForMultichannel(labwareDef, 'B1')).toEqual([ 'A1', 'B1', @@ -100,6 +131,7 @@ describe('getWellSetForMultichannel (integration test)', () => { 'G1', 'H1', ]) + expect(getWellSetForMultichannel(labwareDef, 'H1')).toEqual([ 'A1', 'B1', @@ -110,6 +142,7 @@ describe('getWellSetForMultichannel (integration test)', () => { 'G1', 'H1', ]) + expect(getWellSetForMultichannel(labwareDef, 'A2')).toEqual([ 'A2', 'B2', @@ -121,12 +154,16 @@ describe('getWellSetForMultichannel (integration test)', () => { 'H2', ]) }) + 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', @@ -137,6 +174,7 @@ describe('getWellSetForMultichannel (integration test)', () => { 'A1', 'A1', ]) + expect(getWellSetForMultichannel(labwareDef, 'A2')).toEqual([ 'A2', 'A2', @@ -148,8 +186,10 @@ describe('getWellSetForMultichannel (integration test)', () => { 'A2', ]) }) + it('384-plate', () => { - const labwareDef = fixture_384_plate + const labwareDef = fixture384Plate + expect(getWellSetForMultichannel(labwareDef, 'C1')).toEqual([ 'A1', 'C1', @@ -160,6 +200,7 @@ describe('getWellSetForMultichannel (integration test)', () => { 'M1', 'O1', ]) + expect(getWellSetForMultichannel(labwareDef, 'F2')).toEqual([ 'B2', 'D2', diff --git a/shared-data/js/helpers/volume.ts b/shared-data/js/helpers/volume.ts index 8d2b9535209..69a4f4e5c74 100644 --- a/shared-data/js/helpers/volume.ts +++ b/shared-data/js/helpers/volume.ts @@ -1,27 +1,35 @@ // utilities for working with volumes in µL import round from 'lodash/round' + import type { LabwareVolumeUnits } from '../types' + const SCALE_BY_UNITS = { µL: 1, mL: 1000, L: 1000000, } + export function getDisplayVolume( volumeInMicroliters: number, displayUnits: LabwareVolumeUnits = 'µL', digits?: number ): string { const volume = volumeInMicroliters / SCALE_BY_UNITS[displayUnits] + return `${typeof digits === 'number' ? round(volume, digits) : volume}` } + export function getAsciiVolumeUnits(displayUnits: LabwareVolumeUnits): string { if (displayUnits === 'µL') return 'uL' + return displayUnits } + 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/wellSets.ts b/shared-data/js/helpers/wellSets.ts index 722a732ca3d..c64a491f04c 100644 --- a/shared-data/js/helpers/wellSets.ts +++ b/shared-data/js/helpers/wellSets.ts @@ -13,15 +13,18 @@ // If a labware has no possible well sets, then it is not compatible with multi-channel pipettes. import { getLabwareDefURI } from '@opentrons/shared-data' 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) @@ -32,19 +35,22 @@ function _getAllWellSetsForLabware( } // creates memoized getAllWellSetsForLabware + getWellSetForMultichannel fns. -export type WellSetHelpers = { +export interface WellSetHelpers { getAllWellSetsForLabware: ( labwareDef: LabwareDefinition2 ) => WellSetByPrimaryWell + getWellSetForMultichannel: ( labwareDef: LabwareDefinition2, well: string - ) => Array | null | undefined + ) => string[] | null | undefined + canPipetteUseLabware: ( pipetteSpec: PipetteNameSpecs, labwareDef: LabwareDefinition2 ) => boolean } + export const makeWellSetHelpers = (): WellSetHelpers => { const cache: Record< string, @@ -80,14 +86,15 @@ export const makeWellSetHelpers = (): WellSetHelpers => { const getWellSetForMultichannel = ( labwareDef: LabwareDefinition2, well: string - ): Array | null | undefined => { + ): 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/labwareTools/__tests__/createDefaultDisplayName.test.ts b/shared-data/js/labwareTools/__tests__/createDefaultDisplayName.test.ts index 727f401d1ab..5ec5b62d272 100644 --- a/shared-data/js/labwareTools/__tests__/createDefaultDisplayName.test.ts +++ b/shared-data/js/labwareTools/__tests__/createDefaultDisplayName.test.ts @@ -1,8 +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', @@ -13,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', @@ -35,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', @@ -46,7 +55,7 @@ describe('createDefaultDisplayName', () => { expected: 'Generic 6 Tube Rack 123 µL', }, { - testName: 'should append loadNamePostfix', + testName: 'append loadNamePostfix', args: { displayCategory: 'wellPlate', displayVolumeUnits: 'µL', @@ -93,8 +102,9 @@ describe('createDefaultDisplayName', () => { expected: 'Generic 6 Well Plate 0.123 mL', }, ] + 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.ts b/shared-data/js/labwareTools/__tests__/createIrregularLabware.test.ts index af17f5b3cb2..63c49c58664 100644 --- a/shared-data/js/labwareTools/__tests__/createIrregularLabware.test.ts +++ b/shared-data/js/labwareTools/__tests__/createIrregularLabware.test.ts @@ -1,13 +1,19 @@ 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' -import { splitWellsOnColumn, sortWells } from '../../helpers/index' -import fixture_irregular_example_1 from '../../../labware/fixtures/2/fixture_irregular_example_1.json' +} from '..' + +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 = { ...fixture_irregular_example_1, @@ -15,32 +21,22 @@ 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 grid = { row: 2, column: 2 } + const gridStart = [ - { - rowStart: 'A', - colStart: '1', - rowStride: 1, - colStride: 2, - }, - { - rowStart: 'B', - colStart: '1', - rowStride: 3, - colStride: 1, - }, + { 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 + rowIdx const wellName1 = _irregularWellName(rowIdx, colIdx, gridStart[0]) expect(expected1[idx]).toEqual(wellName1) @@ -48,38 +44,27 @@ describe('test helper functions', () => { const wellName2 = _irregularWellName(rowIdx, colIdx, gridStart[1]) expect(expected2[idx]).toEqual(wellName2) - idx += 1 }) }) }) + it('XYZ generates correctly for each grid', () => { - const grid = { - row: 1, - column: 5, - } - const offset = { - x: 1, - y: 0.5, - z: 55.5, - } + const grid = { row: 1, column: 5 } + const offset = { x: 1, y: 0.5, z: 55.5 } const spacing = [ - { - row: 10, - column: 10, - }, - { - row: 5, - column: 14, - }, + { row: 10, column: 10 }, + { row: 5, column: 14 }, ] 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] const expectedY2 = [0.5] + range(grid.column).forEach(colIdx => { range(grid.row).forEach(rowIdx => { const well1 = _calculateWellCoord( @@ -109,9 +94,11 @@ describe('test helper functions', () => { }) }) }) + describe('test createIrregularLabware function', () => { - let labware1 - let labware1Args + let labware1: LabwareDefinition2 + let labware1Args: IrregularLabwareProps + beforeEach(() => { labware1Args = { namespace: 'fixture', @@ -146,85 +133,52 @@ describe('test createIrregularLabware function', () => { }, ], offset: [ - { - x: 10, - y: 10, - z: 69.48, - }, - { - x: 15, - y: 15, - z: 69.48, - }, + { x: 10, y: 10, z: 69.48 }, + { x: 15, y: 15, z: 69.48 }, ], grid: [ - { - row: 5, - column: 10, - }, - { - row: 1, - column: 5, - }, + { row: 5, column: 10 }, + { row: 1, column: 5 }, ], spacing: [ - { - row: 5, - column: 10, - }, - { - row: 5, - column: 10, - }, + { row: 5, column: 10 }, + { row: 5, column: 10 }, ], gridStart: [ - { - rowStart: 'A', - colStart: '1', - rowStride: 2, - colStride: 1, - }, - { - rowStart: 'B', - colStart: '1', - rowStride: 1, - colStride: 1, - }, + { rowStart: 'A', colStart: '1', rowStride: 2, colStride: 1 }, + { rowStart: 'B', colStart: '1', rowStride: 1, colStride: 1 }, ], } labware1 = createIrregularLabware(labware1Args) }) + it('irregular ordering generates as expected', () => { const keyList = Object.keys(labware1.wells) const generatedOrdering = splitWellsOnColumn(keyList.sort(sortWells)) expect(labware1.ordering).toEqual(generatedOrdering) }) + it('check labware matches fixture', () => { expect(labware1).toEqual(exampleLabware1) }) + it('labware loadName generated correctly for multi-grid labware', () => { const loadName = _generateIrregularLoadName({ grid: [ - { - row: 3, - column: 2, - }, - { - row: 1, - column: 4, - }, + { row: 3, column: 2 }, + { row: 1, column: 4 }, ], well: [ { depth: 20, shape: 'circular', totalLiquidVolume: 400, - }, + } as LabwareWellProperties, { depth: 110, shape: 'circular', totalLiquidVolume: 2000, - }, + } as LabwareWellProperties, ], totalWellCount: 10, units: 'µL', @@ -234,20 +188,16 @@ describe('test createIrregularLabware function', () => { expect(loadName).toEqual('somebrand_10_wellplate_6x400ul_4x2000ul') }) + it('labware loadName generated correctly for multi-grid labware in other units', () => { const loadName = _generateIrregularLoadName({ - grid: [ - { - row: 3, - column: 2, - }, - ], + grid: [{ row: 3, column: 2 }], well: [ { depth: 20, shape: 'circular', totalLiquidVolume: 4000, - }, + } as LabwareWellProperties, ], totalWellCount: 6, units: 'mL', @@ -257,21 +207,14 @@ describe('test createIrregularLabware function', () => { expect(loadName).toEqual('somebrand_6_wellplate_6x4ml') }) + it('failing to validate against labware schema throws w/o "strict"', () => { const args = { ...labware1Args, // negative y offset should fail schema validation by making well `y` negative offset: [ - { - x: 10, - y: -9999, - z: 69.48, - }, - { - x: 15, - y: -9999, - z: 69.48, - }, + { x: 10, y: -9999, z: 69.48 }, + { x: 15, y: -9999, z: 69.48 }, ], } expect(() => createIrregularLabware(args)).toThrowErrorMatchingSnapshot() diff --git a/shared-data/js/labwareTools/__tests__/createLabware.test.ts b/shared-data/js/labwareTools/__tests__/createLabware.test.ts index 3f92288289f..549c4783dee 100644 --- a/shared-data/js/labwareTools/__tests__/createLabware.test.ts +++ b/shared-data/js/labwareTools/__tests__/createLabware.test.ts @@ -84,10 +84,6 @@ describe('createLabware', () => { labware2 = createRegularLabware(labware2Args) }) - afterEach(() => { - jest.clearAllMocks() - }) - it('snapshot tests', () => { expect(labware1).toEqual(exampleLabware1) expect(labware2).toEqual(exampleLabware2) diff --git a/shared-data/js/labwareTools/index.ts b/shared-data/js/labwareTools/index.ts index 2dcb9996e2e..580622e1e75 100644 --- a/shared-data/js/labwareTools/index.ts +++ b/shared-data/js/labwareTools/index.ts @@ -134,6 +134,11 @@ export function _calculateWellCoord( return { ...well, ...coords } } +interface Layout { + wells: WellMap + groups: WellGroup[] +} + function determineIrregularLayout( grids: Cell[], spacing: Cell[], @@ -141,11 +146,8 @@ function determineIrregularLayout( gridStart: GridStart[], wells: InputWell[], group: InputWellGroup[] = [] -): { - wells: WellMap - groups: WellGroup[] -} { - return grids.reduce( +): Layout { + return grids.reduce( (result, gridObj, gridIdx) => { const reverseRowIdx = range(gridObj.row - 1, -1) const inputGroup = group[gridIdx] || { @@ -281,7 +283,7 @@ function joinLoadName( .replace(/[^a-z0-9_.]/g, '') } -interface RegularNameProps { +export interface RegularNameProps { displayCategory: string displayVolumeUnits: VolumeUnits gridRows: number @@ -290,6 +292,7 @@ interface RegularNameProps { brandName?: string loadNamePostfix?: string[] } + export function createRegularLoadName(args: RegularNameProps): string { const { gridRows, diff --git a/shared-data/js/types.ts b/shared-data/js/types.ts index b188618ed83..afbf7da9177 100644 --- a/shared-data/js/types.ts +++ b/shared-data/js/types.ts @@ -57,7 +57,7 @@ export type LabwareDisplayCategory = | 'trash' | 'other' -export type LabwareVolumeUnits = '\xB5L' | 'mL' | 'L' +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 diff --git a/shared-data/pipette/fixtures/name/index.js b/shared-data/pipette/fixtures/name/index.js index 52d5e99ec90..7a266f42136 100644 --- a/shared-data/pipette/fixtures/name/index.js +++ b/shared-data/pipette/fixtures/name/index.js @@ -1,7 +1,7 @@ // @flow import pipetteNameSpecFixtures from './pipetteNameSpecFixtures.json' -import type { PipetteNameSpecs } from '../../../js/types' +import type { PipetteNameSpecs } from '@opentrons/shared-data' export const fixtureP10Single: $Shape = pipetteNameSpecFixtures['p10_single'] From 60ba44ce2f6fff9439c27cfa2345cc75e3520c44 Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Tue, 27 Apr 2021 15:00:58 -0400 Subject: [PATCH 04/16] fixup: knock out another batch of type errors --- .../js/__tests__/protocolSchemaV4.test.ts | 31 ++++++++++++------ .../js/__tests__/protocolSchemaV5.test.ts | 31 ++++++++++++------ shared-data/js/cypressUtils.ts | 6 ++-- shared-data/js/getLabware.ts | 13 +++++++- shared-data/js/helpers/index.ts | 21 +++++++++--- shared-data/js/index.ts | 1 - shared-data/js/labwareTools/index.ts | 4 ++- shared-data/js/modules.ts | 32 ++++++++++++------- shared-data/js/pipettes.ts | 21 +++++++----- shared-data/tsconfig.json | 4 ++- 10 files changed, 113 insertions(+), 51 deletions(-) diff --git a/shared-data/js/__tests__/protocolSchemaV4.test.ts b/shared-data/js/__tests__/protocolSchemaV4.test.ts index f3b84210f7d..b4ac51df071 100644 --- a/shared-data/js/__tests__/protocolSchemaV4.test.ts +++ b/shared-data/js/__tests__/protocolSchemaV4.test.ts @@ -4,21 +4,24 @@ 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' + const fixturesGlobPath = path.join( __dirname, '../../protocol/fixtures/4/**/*.json' ) + const protocolFixtures = glob.sync(fixturesGlobPath) -const ajv = new Ajv({ - allErrors: true, - jsonPointers: true, -}) +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) + const validateProtocol = ajv.compile(protocolSchema) + describe('validate v4 protocol fixtures under JSON schema', () => { protocolFixtures.forEach(protocolPath => { it(path.basename(protocolPath), () => { @@ -36,6 +39,7 @@ describe('validate v4 protocol fixtures under JSON schema', () => { }) }) }) + describe('ensure bad protocol data fails validation', () => { it('$otSharedSchema is required to be "#/protocol/schemas/4"', () => { expect(validateProtocol(omit(simpleV4Fixture, '$otSharedSchema'))).toBe( @@ -48,12 +52,14 @@ describe('ensure bad protocol data fails validation', () => { }) ).toBe(false) }) + it('schemaVersion is required to be 4', () => { expect(validateProtocol(omit(simpleV4Fixture, 'schemaVersion'))).toBe(false) expect(validateProtocol({ ...simpleV4Fixture, schemaVersion: 3 })).toBe( false ) }) + it('reject bad values in "pipettes" objects', () => { const badPipettes = { missingKeys: {}, @@ -73,18 +79,20 @@ describe('ensure bad protocol data fails validation', () => { blah: 'blah', }, } - 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) }) }) + it('reject bad values in "labware" objects', () => { const badLabware = { noSlot: { @@ -99,18 +107,20 @@ describe('ensure bad protocol data fails validation', () => { blah: 'blah', }, } - 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) }) }) + it('reject bad values in "modules" objects', () => { const badModules = { badModuleType: { @@ -129,13 +139,14 @@ describe('ensure bad protocol data fails validation', () => { blah: 'blah', }, } - 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.ts b/shared-data/js/__tests__/protocolSchemaV5.test.ts index 08f8c14157f..96a064cd598 100644 --- a/shared-data/js/__tests__/protocolSchemaV5.test.ts +++ b/shared-data/js/__tests__/protocolSchemaV5.test.ts @@ -4,21 +4,24 @@ 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' + const fixturesGlobPath = path.join( __dirname, '../../protocol/fixtures/5/**/*.json' ) + const protocolFixtures = glob.sync(fixturesGlobPath) -const ajv = new Ajv({ - allErrors: true, - jsonPointers: true, -}) +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) + const validateProtocol = ajv.compile(protocolSchema) + describe('validate v5 protocol fixtures under JSON schema', () => { protocolFixtures.forEach(protocolPath => { it(path.basename(protocolPath), () => { @@ -36,6 +39,7 @@ describe('validate v5 protocol fixtures under JSON schema', () => { }) }) }) + describe('ensure bad protocol data fails validation', () => { it('$otSharedSchema is required to be "#/protocol/schemas/5"', () => { expect(validateProtocol(omit(simpleV5Fixture, '$otSharedSchema'))).toBe( @@ -48,12 +52,14 @@ describe('ensure bad protocol data fails validation', () => { }) ).toBe(false) }) + it('schemaVersion is required to be 5', () => { expect(validateProtocol(omit(simpleV5Fixture, 'schemaVersion'))).toBe(false) expect(validateProtocol({ ...simpleV5Fixture, schemaVersion: 3 })).toBe( false ) }) + it('reject bad values in "pipettes" objects', () => { const badPipettes = { missingKeys: {}, @@ -73,18 +79,20 @@ describe('ensure bad protocol data fails validation', () => { blah: 'blah', }, } - 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) }) }) + it('reject bad values in "labware" objects', () => { const badLabware = { noSlot: { @@ -99,18 +107,20 @@ describe('ensure bad protocol data fails validation', () => { blah: 'blah', }, } - 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) }) }) + it('reject bad values in "modules" objects', () => { const badModules = { badModuleType: { @@ -129,13 +139,14 @@ describe('ensure bad protocol data fails validation', () => { blah: 'blah', }, } - 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/cypressUtils.ts b/shared-data/js/cypressUtils.ts index 328aaa13584..776c39d6548 100644 --- a/shared-data/js/cypressUtils.ts +++ b/shared-data/js/cypressUtils.ts @@ -2,8 +2,8 @@ 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] = @@ -24,7 +24,7 @@ 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( diff --git a/shared-data/js/getLabware.ts b/shared-data/js/getLabware.ts index 8a873bab4aa..7eb7b2f2e67 100644 --- a/shared-data/js/getLabware.ts +++ b/shared-data/js/getLabware.ts @@ -2,20 +2,24 @@ 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, WellDefinition, } from './types' + assert( definitions && Object.keys(definitions).length > 0, 'Expected v1 labware defs. Something went wrong with shared-data/build/labware.json' ) + // labware definitions only used for back-compat with legacy v1 defs. // do not list in any "available labware" UI. // TODO(mc, 2019-12-3): how should this correspond to RETIRED_LABWARE? @@ -40,19 +44,24 @@ export const PD_DO_NOT_LIST = [ 'opentrons_calibrationblock_short_side_left', 'opentrons_calibrationblock_short_side_right', ] + 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 } + export function getIsLabwareV1Tiprack(def: LabwareDefinition1): boolean { return Boolean(def?.metadata?.isTiprack) } + export function getIsTiprack(labwareDef: LabwareDefinition2): boolean { return labwareDef.parameters.isTiprack } + // NOTE: these labware definitions in _SHORT_MM_LABWARE_DEF_LOADNAMES // were written in "short mm" = 0.5mm, but // we will write all future definitions in actual mm. @@ -67,10 +76,12 @@ 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" :) const ENGAGE_HEIGHT_OFFSET = -4 + export function getLabwareDefaultEngageHeight( labwareDef: LabwareDefinition2 ): number | null { @@ -100,7 +111,7 @@ export function getLabwareDefaultEngageHeight( const _getSvgYValueForWell = ( def: LabwareDefinition1, wellDef: WellDefinition -) => { +): number => { const labwareName = def.metadata.name const renderHeight = labwareName === 'fixed-trash' diff --git a/shared-data/js/helpers/index.ts b/shared-data/js/helpers/index.ts index 500e97ca3b2..86708771ecf 100644 --- a/shared-data/js/helpers/index.ts +++ b/shared-data/js/helpers/index.ts @@ -1,16 +1,22 @@ 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' + export const getLabwareDefIsStandard = (def: LabwareDefinition2): boolean => def?.namespace === OPENTRONS_LABWARE_NAMESPACE + export const getLabwareDefURI = (def: LabwareDefinition2): string => `${def.namespace}/${def.parameters.loadName}/${def.version}` + // Load names of "retired" labware // TODO(mc, 2019-12-3): how should this correspond to LABWAREV2_DO_NOT_LIST? // see shared-data/js/getLabware.js @@ -27,6 +33,7 @@ const RETIRED_LABWARE = [ 'eppendorf_96_tiprack_1000ul_eptips', 'eppendorf_96_tiprack_10ul_eptips', ] + export const getLabwareDisplayName = ( labwareDef: LabwareDefinition2 ): string => { @@ -41,6 +48,7 @@ export const getLabwareDisplayName = ( return displayName } + export const getTiprackVolume = (labwareDef: LabwareDefinition2): number => { assert( labwareDef.parameters.isTiprack, @@ -56,6 +64,7 @@ export const getTiprackVolume = (labwareDef: LabwareDefinition2): number => { ) return volume } + export function getLabwareHasQuirk( labwareDef: LabwareDefinition2, quirk: string @@ -63,6 +72,7 @@ export function getLabwareHasQuirk( const quirks = labwareDef.parameters.quirks return quirks ? quirks.includes(quirk) : false } + export const intToAlphabetLetter = ( i: number, lowerCase: boolean = false @@ -113,10 +123,9 @@ 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) { @@ -132,16 +141,18 @@ export function splitWellsOnColumn( } }, []) } + export const getWellDepth = ( labwareDef: LabwareDefinition2, well: string ): number => labwareDef.wells[well].depth + // NOTE: this is used in PD for converting "offset from top" to "mm from bottom". // Assumes all wells have same offset because multi-offset not yet supported. // 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)) diff --git a/shared-data/js/index.ts b/shared-data/js/index.ts index 7fab31cfaf4..2185791eb58 100644 --- a/shared-data/js/index.ts +++ b/shared-data/js/index.ts @@ -5,4 +5,3 @@ export * from './pipettes' export * from './types' export * from './labwareTools' export * from './modules' -export * from '../protocol' diff --git a/shared-data/js/labwareTools/index.ts b/shared-data/js/labwareTools/index.ts index 580622e1e75..4c3f826f650 100644 --- a/shared-data/js/labwareTools/index.ts +++ b/shared-data/js/labwareTools/index.ts @@ -117,6 +117,7 @@ export function _irregularWellName( colNum, }) } + export function _calculateWellCoord( rowIdx: number, colIdx: number, @@ -153,7 +154,8 @@ function determineIrregularLayout( const inputGroup = group[gridIdx] || { metadata: {}, } - const currentGroup = { ...inputGroup, wells: [] } + const currentGroup: WellGroup = { ...inputGroup, wells: [] } + range(gridObj.column).forEach(colIdx => { range(gridObj.row).forEach(rowIdx => { const wellName = _irregularWellName( diff --git a/shared-data/js/modules.ts b/shared-data/js/modules.ts index cf1802b6b3d..a3640020a49 100644 --- a/shared-data/js/modules.ts +++ b/shared-data/js/modules.ts @@ -3,6 +3,7 @@ import magneticModuleV2 from '../module/definitions/2/magneticModuleV2.json' import temperatureModuleV1 from '../module/definitions/2/temperatureModuleV1.json' import temperatureModuleV2 from '../module/definitions/2/temperatureModuleV2.json' import thermocyclerModuleV1 from '../module/definitions/2/thermocyclerModuleV1.json' + import { MAGDECK, MAGNETIC_MODULE_V1, @@ -13,15 +14,19 @@ import { THERMOCYCLER, THERMOCYCLER_MODULE_V1, } from './constants' + import type { ModuleModel, ModuleRealType, ModuleType } from './types' + // The module objects in v2 Module Definitions representing a single module model -type Coordinates = { +interface Coordinates { x: number y: number z?: number } + type AffineTransform = [number, number, number] -export type ModuleDef2 = { + +export interface ModuleDef2 { moduleType: ModuleRealType model: ModuleModel labwareOffset: Coordinates @@ -32,35 +37,37 @@ export type ModuleDef2 = { } calibrationPoint: Coordinates displayName: string - quirks: Array + quirks: string[] slotTransforms: Record< string, Record> > - compatibleWith: Array + 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}`) } } + export function normalizeModuleModel(legacyModule: ModuleType): ModuleModel { switch (legacyModule) { case TEMPDECK: @@ -73,16 +80,19 @@ export function normalizeModuleModel(legacyModule: ModuleType): ModuleModel { return THERMOCYCLER_MODULE_V1 default: - throw new Error(`Invalid legacy module model ${legacyModule}`) + throw new Error(`Invalid legacy module model ${legacyModule as string}`) } } + export function getModuleType(moduleModel: ModuleModel): ModuleRealType { return getModuleDef2(moduleModel).moduleType } + // use module model (not type!) to get model-specific displayName for UI export function getModuleDisplayName(moduleModel: ModuleModel): string { return getModuleDef2(moduleModel).displayName } + export function checkModuleCompatibility( modelA: ModuleModel, modelB: ModuleModel diff --git a/shared-data/js/pipettes.ts b/shared-data/js/pipettes.ts index cba8f2043cc..00797be3ce1 100644 --- a/shared-data/js/pipettes.ts +++ b/shared-data/js/pipettes.ts @@ -1,40 +1,45 @@ import pipetteNameSpecs from '../pipette/definitions/pipetteNameSpecs.json' import pipetteModelSpecs from '../pipette/definitions/pipetteModelSpecs.json' + import type { PipetteNameSpecs, PipetteModelSpecs } from './types' + type SortableProps = 'maxVolume' | 'channels' + // models sorted by channels and then volume by default -const ALL_PIPETTE_NAMES: Array = Object.keys(pipetteNameSpecs).sort( +const ALL_PIPETTE_NAMES: string[] = Object.keys(pipetteNameSpecs).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 { + // @ts-expect-error(mc, 2021-04-27): use "safer" accessor to make TS happy const config = pipetteNameSpecs[name] 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 | null | undefined { + // @ts-expect-error(mc, 2021-04-27): use "safer" accessor to make TS happy const modelSpecificFields = pipetteModelSpecs.config[model] const modelFields = modelSpecificFields && getPipetteNameSpecs(modelSpecificFields.name) return modelFields && { ...modelFields, ...modelSpecificFields, model } } -export function getAllPipetteNames( - ...sortBy: Array -): Array { + +export function getAllPipetteNames(...sortBy: SortableProps[]): string[] { 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: string, modelB: string) => { // any cast is because we know these pipettes exist - const a: PipetteNameSpecs = getPipetteNameSpecs(modelA) as any - const b: PipetteNameSpecs = getPipetteNameSpecs(modelB) as any + const a = getPipetteNameSpecs(modelA) as PipetteNameSpecs + const b = getPipetteNameSpecs(modelB) as PipetteNameSpecs let i for (i = 0; i < sortBy.length; i++) { diff --git a/shared-data/tsconfig.json b/shared-data/tsconfig.json index 93022f2880d..f9886424e52 100644 --- a/shared-data/tsconfig.json +++ b/shared-data/tsconfig.json @@ -12,6 +12,8 @@ "deck/**/*.json", "labware/**/*.json", "module/**/*.json", - "protocol/**/*.json" + "pipette/**/*.json", + "protocol/**/*.json", + "build/**/*.json" ] } From d0e09849e9554290a3eb691c3eab3ef9b5ec7a42 Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Tue, 27 Apr 2021 17:24:44 -0400 Subject: [PATCH 05/16] fixup: get TS and linting passing --- .../src/labware/__tests__/validation.test.ts | 23 +- app-shell/tsconfig.json | 3 + app-shell/typings/opentrons__shared-data.d.ts | 6 - .../deck/labwareInternals/StaticLabware.tsx | 2 + .../src/instrument/InstrumentDiagram.tsx | 10 +- components/src/instrument/InstrumentInfo.tsx | 11 +- components/src/instrument/PipetteSelect.tsx | 4 +- .../__tests__/PipetteSelect.test.tsx | 4 +- .../typings/opentrons__shared-data.d.ts | 135 - .../src/labware-creator/fieldsToLabware.ts | 3 +- shared-data/.gitignore | 1 + shared-data/index.d.ts | 348 - .../__snapshots__/pipettes.test.js.snap | 5969 ----------------- .../__tests__/getWellNamePerMultiTip.test.ts | 39 +- .../js/__tests__/labwareDefQuirks.test.ts | 6 + .../js/__tests__/labwareDefSchemaV1.test.ts | 2 +- .../js/__tests__/labwareDefSchemaV2.test.ts | 44 +- .../js/__tests__/moduleAccessors.test.ts | 9 +- .../js/__tests__/moduleSpecsSchema.test.ts | 26 +- .../js/__tests__/pipetteSpecSchemas.test.ts | 17 +- shared-data/js/__tests__/sortWells.test.ts | 2 + .../js/__tests__/splitWellsOnColumn.test.ts | 3 + shared-data/js/constants.ts | 12 +- .../js/helpers/getWellNamePerMultiTip.ts | 10 +- shared-data/js/helpers/wellSets.ts | 2 +- ...ap => createIrregularLabware.test.ts.snap} | 0 ...est.js.snap => createLabware.test.ts.snap} | 0 .../__tests__/createIrregularLabware.test.ts | 3 +- tsconfig.json | 3 + 29 files changed, 165 insertions(+), 6532 deletions(-) delete mode 100644 app-shell/typings/opentrons__shared-data.d.ts delete mode 100644 components/typings/opentrons__shared-data.d.ts create mode 100644 shared-data/.gitignore delete mode 100644 shared-data/index.d.ts delete mode 100644 shared-data/js/__tests__/__snapshots__/pipettes.test.js.snap 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%) 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/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..3d02628a233 100644 --- a/components/src/instrument/InstrumentInfo.tsx +++ b/components/src/instrument/InstrumentInfo.tsx @@ -1,16 +1,13 @@ 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 @@ -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 | undefined /** 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/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/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/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/index.d.ts b/shared-data/index.d.ts deleted file mode 100644 index bdfa92803a2..00000000000 --- a/shared-data/index.d.ts +++ /dev/null @@ -1,348 +0,0 @@ -// 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 - -// 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 - 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 interface LabwareDefinition1 { - metadata: { - name: string - format: string - deprecated?: boolean - displayName?: string - displayCategory?: string - isValidSource?: boolean - isTiprack?: boolean - tipVolume?: number - } - 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' - -export interface LabwareMetadata { - displayName: string - displayCategory: LabwareDisplayCategory - displayVolumeUnits: LabwareVolumeUnits - tags?: string[] -} - -export interface LabwareDimensions { - xDimension: number - yDimension: number - zDimension: number -} - -export interface 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 interface LabwareParameters { - loadName: string - format: string - isTiprack: boolean - tipLength?: number - isMagneticModuleCompatible: boolean - magneticModuleEngageHeight?: number - quirks?: string[] -} - -export interface LabwareBrand { - brand: string - brandId?: string[] - links?: string[] -} - -export type LabwareWellShapeProperties = - | { - shape: 'circular' - diameter: number - } - | { - shape: 'rectangular' - xDimension: number - yDimension: number - } - -// well without x,y,z -export type LabwareWellProperties = LabwareWellShapeProperties & { - depth: number - totalLiquidVolume: number -} - -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 interface LabwareWellMap { - [wellName: string]: LabwareWell -} - -export interface LabwareWellGroupMetadata { - displayName?: string - displayCategory?: LabwareDisplayCategory - wellBottomShape?: WellBottomShape -} - -export interface LabwareWellGroup { - wells: string[] - metadata: LabwareWellGroupMetadata - brand?: LabwareBrand -} - -// NOTE: must be synced with shared-data/labware/schemas/2.json -export interface LabwareDefinition2 { - version: number - schemaVersion: 2 - namespace: string - metadata: LabwareMetadata - dimensions: LabwareDimensions - cornerOffsetFromSlot: LabwareOffset - parameters: LabwareParameters - brand: LabwareBrand - ordering: string[][] - wells: LabwareWellMap - 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 = - | MagneticModuleModel - | TemperatureModuleModel - | ThermocyclerModuleModel - -export type ModuleModelWithLegacy = - | ModuleModel - | typeof THERMOCYCLER - | typeof MAGDECK - | typeof TEMPDECK - -export interface DeckOffset { - x: number - y: number - z: number -} - -export interface Dimensions { - xDimension: number - yDimension: number - zDimension: number -} - -export interface DeckRobot { - model: string -} - -export interface 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 interface DeckSlot { - id: DeckSlotId - position: CoordinateTuple - matingSurfaceUnitVector?: UnitVectorTuple - boundingBox: Dimensions - displayName: string - compatibleModules: ModuleType[] -} - -export interface DeckCalibrationPoint { - id: string - position: CoordinateTuple - 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 -} - -export interface ModuleDimensions { - bareOverallHeight: number - overLabwareHeight: number - lidHeight: number -} - -export interface ModuleCalibrationPoint { - x: number - y: number - z?: number -} - -export interface ModuleDefinition { - labwareOffset: LabwareOffset - dimensions: ModuleDimensions - calibrationPoint: ModuleCalibrationPoint - displayName: string - loadName: string - quirks: string[] -} - -export type PipetteChannels = 1 | 8 - -export type PipetteDisplayCategory = typeof GEN1 | typeof GEN2 - -export const DEFAULT_CUSTOM_NAMESPACE: string - -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 - } -} - -// TODO(mc, 2019-10-14): update this type according to the schema -export interface PipetteModelSpecs extends PipetteNameSpecs { - model: string - backCompatNames?: string[] - tipLength: { value: number } -} diff --git a/shared-data/js/__tests__/__snapshots__/pipettes.test.js.snap b/shared-data/js/__tests__/__snapshots__/pipettes.test.js.snap deleted file mode 100644 index 759bfd6de3d..00000000000 --- a/shared-data/js/__tests__/__snapshots__/pipettes.test.js.snap +++ /dev/null @@ -1,5969 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`pipette data accessors getPipetteModelSpecs model p10_multi_v1 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": -1, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 2, - }, - "channels": 8, - "defaultAspirateFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 5, - "valuesByApiLevel": Object { - "2.0": 5, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 10, - "valuesByApiLevel": Object { - "2.0": 10, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_10ul/1", - "opentrons/opentrons_96_filtertiprack_10ul/1", - "opentrons/geb_96_tiprack_10ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P10 8-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -4, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 10, - "minVolume": 1, - "model": "p10_multi_v1", - "modelOffset": Array [ - 0, - 31.5, - -25.8, - ], - "name": "p10_multi", - "nozzleOffset": Array [ - 0, - 31.5, - 0.8, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.4, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 33, - }, - "tipOverlap": Object { - "default": 3.29, - "opentrons/eppendorf_96_tiprack_10ul_eptips/1": 1, - "opentrons/geb_96_tiprack_10ul/1": 6.2, - "opentrons/opentrons_96_filtertiprack_10ul/1": 3.29, - "opentrons/opentrons_96_tiprack_10ul/1": 3.29, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 1.893415617, - -1.1069, - 3.042593193, - ], - Array [ - 2.497849452, - -0.1888, - 1.30410391, - ], - Array [ - 5.649462387, - -0.0081, - 0.8528667891, - ], - Array [ - 12.74444519, - -0.0018, - 0.8170558891, - ], - ], - "dispense": Array [ - Array [ - 12.74444519, - 0, - 0.8058688085, - ], - ], - }, - Object { - "aspirate": Array [ - Array [ - 1.438649211, - 0.01931415115, - 0.691538317, - ], - Array [ - 1.836824579, - 0.03868955123, - 0.6636639129, - ], - Array [ - 2.960052684, - 0.00470371018, - 0.7260899411, - ], - Array [ - 4.487508789, - 0.005175245625, - 0.7246941713, - ], - Array [ - 10.59661421, - 0.001470408978, - 0.7413196584, - ], - ], - "dispense": Array [ - Array [ - 12.74444519, - 0, - 0.8058688085, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p10_multi_v1.3 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": -2.5, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 0.5, - }, - "channels": 8, - "defaultAspirateFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 5, - "valuesByApiLevel": Object { - "2.0": 5, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 10, - "valuesByApiLevel": Object { - "2.0": 10, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_10ul/1", - "opentrons/opentrons_96_filtertiprack_10ul/1", - "opentrons/geb_96_tiprack_10ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P10 8-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -5.5, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 10, - "minVolume": 1, - "model": "p10_multi_v1.3", - "modelOffset": Array [ - 0, - 31.5, - -25.8, - ], - "name": "p10_multi", - "nozzleOffset": Array [ - 0, - 31.5, - 0.8, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.4, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 33, - }, - "tipOverlap": Object { - "default": 3.29, - "opentrons/eppendorf_96_tiprack_10ul_eptips/1": 1, - "opentrons/geb_96_tiprack_10ul/1": 6.2, - "opentrons/opentrons_96_filtertiprack_10ul/1": 3.29, - "opentrons/opentrons_96_tiprack_10ul/1": 3.29, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 1.893415617, - -1.1069, - 3.042593193, - ], - Array [ - 2.497849452, - -0.1888, - 1.30410391, - ], - Array [ - 5.649462387, - -0.0081, - 0.8528667891, - ], - Array [ - 12.74444519, - -0.0018, - 0.8170558891, - ], - ], - "dispense": Array [ - Array [ - 12.74444519, - 0, - 0.8058688085, - ], - ], - }, - Object { - "aspirate": Array [ - Array [ - 1.438649211, - 0.01931415115, - 0.691538317, - ], - Array [ - 1.836824579, - 0.03868955123, - 0.6636639129, - ], - Array [ - 2.960052684, - 0.00470371018, - 0.7260899411, - ], - Array [ - 4.487508789, - 0.005175245625, - 0.7246941713, - ], - Array [ - 10.59661421, - 0.001470408978, - 0.7413196584, - ], - ], - "dispense": Array [ - Array [ - 12.74444519, - 0, - 0.8058688085, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p10_multi_v1.4 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": -1, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 2, - }, - "channels": 8, - "defaultAspirateFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 5, - "valuesByApiLevel": Object { - "2.0": 5, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 10, - "valuesByApiLevel": Object { - "2.0": 10, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_10ul/1", - "opentrons/opentrons_96_filtertiprack_10ul/1", - "opentrons/geb_96_tiprack_10ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P10 8-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -4.5, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 10, - "minVolume": 1, - "model": "p10_multi_v1.4", - "modelOffset": Array [ - 0, - 31.5, - -25.8, - ], - "name": "p10_multi", - "nozzleOffset": Array [ - 0, - 31.5, - 0.8, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.4, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 33, - }, - "tipOverlap": Object { - "default": 3.29, - "opentrons/eppendorf_96_tiprack_10ul_eptips/1": 1, - "opentrons/geb_96_tiprack_10ul/1": 6.2, - "opentrons/opentrons_96_filtertiprack_10ul/1": 3.29, - "opentrons/opentrons_96_tiprack_10ul/1": 3.29, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 1.893415617, - -1.1069, - 3.042593193, - ], - Array [ - 2.497849452, - -0.1888, - 1.30410391, - ], - Array [ - 5.649462387, - -0.0081, - 0.8528667891, - ], - Array [ - 12.74444519, - -0.0018, - 0.8170558891, - ], - ], - "dispense": Array [ - Array [ - 12.74444519, - 0, - 0.8058688085, - ], - ], - }, - Object { - "aspirate": Array [ - Array [ - 1.438649211, - 0.01931415115, - 0.691538317, - ], - Array [ - 1.836824579, - 0.03868955123, - 0.6636639129, - ], - Array [ - 2.960052684, - 0.00470371018, - 0.7260899411, - ], - Array [ - 4.487508789, - 0.005175245625, - 0.7246941713, - ], - Array [ - 10.59661421, - 0.001470408978, - 0.7413196584, - ], - ], - "dispense": Array [ - Array [ - 12.74444519, - 0, - 0.8058688085, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p10_multi_v1.5 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": -1, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 2, - }, - "channels": 8, - "defaultAspirateFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 5, - "valuesByApiLevel": Object { - "2.0": 5, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 10, - "valuesByApiLevel": Object { - "2.0": 10, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_10ul/1", - "opentrons/opentrons_96_filtertiprack_10ul/1", - "opentrons/geb_96_tiprack_10ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P10 8-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -4.5, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 10, - "minVolume": 1, - "model": "p10_multi_v1.5", - "modelOffset": Array [ - 0, - 31.5, - -25.8, - ], - "name": "p10_multi", - "nozzleOffset": Array [ - 0, - 31.5, - 0.8, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.55, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 3, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "quirks": Array [ - "dropTipShake", - "doubleDropTip", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 33, - }, - "tipOverlap": Object { - "default": 3.29, - "opentrons/eppendorf_96_tiprack_10ul_eptips/1": 1, - "opentrons/geb_96_tiprack_10ul/1": 6.2, - "opentrons/opentrons_96_filtertiprack_10ul/1": 3.29, - "opentrons/opentrons_96_tiprack_10ul/1": 3.29, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 1.774444444, - -0.1917910448, - 1.2026, - ], - Array [ - 2.151481481, - -0.0706286837, - 1.0125, - ], - Array [ - 2.898518519, - -0.04343083788, - 0.954, - ], - Array [ - 6.373333333, - -0.00905990194, - 0.8544, - ], - Array [ - 11.00259259, - -0.002325900358, - 0.8115, - ], - ], - "dispense": Array [ - Array [ - 12.74444519, - 0, - 0.8058688085, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p10_single_v1 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": -1, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 2, - }, - "channels": 1, - "defaultAspirateFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 5, - "valuesByApiLevel": Object { - "2.0": 5, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 10, - "valuesByApiLevel": Object { - "2.0": 10, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_10ul/1", - "opentrons/opentrons_96_filtertiprack_10ul/1", - "opentrons/geb_96_tiprack_10ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P10 Single-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -4.5, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 10, - "minVolume": 1, - "model": "p10_single_v1", - "modelOffset": Array [ - 0, - 0, - -13, - ], - "name": "p10_single", - "nozzleOffset": Array [ - 0, - 0, - 12, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.1, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.3, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 33, - }, - "tipOverlap": Object { - "default": 3.29, - "opentrons/eppendorf_96_tiprack_10ul_eptips/1": 1, - "opentrons/geb_96_tiprack_10ul/1": 6.2, - "opentrons/opentrons_96_filtertiprack_10ul/1": 3.29, - "opentrons/opentrons_96_tiprack_10ul/1": 3.29, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 1.8263, - -0.0958, - 1.088, - ], - Array [ - 2.5222, - -0.104, - 1.1031, - ], - Array [ - 3.2354, - -0.0447, - 0.9536, - ], - Array [ - 3.9984, - -0.012, - 0.8477, - ], - Array [ - 12.5135, - -0.0021, - 0.8079, - ], - ], - "dispense": Array [ - Array [ - 12.5135, - 0, - 0.7945, - ], - ], - }, - Object { - "aspirate": Array [ - Array [ - 1.438649211, - 0.01931415115, - 0.691538317, - ], - Array [ - 1.836824579, - 0.03868955123, - 0.6636639129, - ], - Array [ - 2.960052684, - 0.00470371018, - 0.7260899411, - ], - Array [ - 4.487508789, - 0.005175245625, - 0.7246941713, - ], - Array [ - 10.59661421, - 0.001470408978, - 0.7413196584, - ], - ], - "dispense": Array [ - Array [ - 12.5135, - 0, - 0.7945, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p10_single_v1.3 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": -2.5, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 0.5, - }, - "channels": 1, - "defaultAspirateFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 5, - "valuesByApiLevel": Object { - "2.0": 5, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 10, - "valuesByApiLevel": Object { - "2.0": 10, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_10ul/1", - "opentrons/opentrons_96_filtertiprack_10ul/1", - "opentrons/geb_96_tiprack_10ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P10 Single-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -6, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 10, - "minVolume": 1, - "model": "p10_single_v1.3", - "modelOffset": Array [ - 0, - 0, - -13, - ], - "name": "p10_single", - "nozzleOffset": Array [ - 0, - 0, - 12, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.1, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.3, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 33, - }, - "tipOverlap": Object { - "default": 3.29, - "opentrons/eppendorf_96_tiprack_10ul_eptips/1": 1, - "opentrons/geb_96_tiprack_10ul/1": 6.2, - "opentrons/opentrons_96_filtertiprack_10ul/1": 3.29, - "opentrons/opentrons_96_tiprack_10ul/1": 3.29, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 1.8263, - -0.0958, - 1.088, - ], - Array [ - 2.5222, - -0.104, - 1.1031, - ], - Array [ - 3.2354, - -0.0447, - 0.9536, - ], - Array [ - 3.9984, - -0.012, - 0.8477, - ], - Array [ - 12.5135, - -0.0021, - 0.8079, - ], - ], - "dispense": Array [ - Array [ - 12.5135, - 0, - 0.7945, - ], - ], - }, - Object { - "aspirate": Array [ - Array [ - 1.438649211, - 0.01931415115, - 0.691538317, - ], - Array [ - 1.836824579, - 0.03868955123, - 0.6636639129, - ], - Array [ - 2.960052684, - 0.00470371018, - 0.7260899411, - ], - Array [ - 4.487508789, - 0.005175245625, - 0.7246941713, - ], - Array [ - 10.59661421, - 0.001470408978, - 0.7413196584, - ], - ], - "dispense": Array [ - Array [ - 12.5135, - 0, - 0.7945, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p10_single_v1.4 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": -0.5, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 2.5, - }, - "channels": 1, - "defaultAspirateFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 5, - "valuesByApiLevel": Object { - "2.0": 5, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 10, - "valuesByApiLevel": Object { - "2.0": 10, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_10ul/1", - "opentrons/opentrons_96_filtertiprack_10ul/1", - "opentrons/geb_96_tiprack_10ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P10 Single-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -5.2, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 10, - "minVolume": 1, - "model": "p10_single_v1.4", - "modelOffset": Array [ - 0, - 0, - -13, - ], - "name": "p10_single", - "nozzleOffset": Array [ - 0, - 0, - 12, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.1, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.3, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 33, - }, - "tipOverlap": Object { - "default": 3.29, - "opentrons/eppendorf_96_tiprack_10ul_eptips/1": 1, - "opentrons/geb_96_tiprack_10ul/1": 6.2, - "opentrons/opentrons_96_filtertiprack_10ul/1": 3.29, - "opentrons/opentrons_96_tiprack_10ul/1": 3.29, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 1.8263, - -0.0958, - 1.088, - ], - Array [ - 2.5222, - -0.104, - 1.1031, - ], - Array [ - 3.2354, - -0.0447, - 0.9536, - ], - Array [ - 3.9984, - -0.012, - 0.8477, - ], - Array [ - 12.5135, - -0.0021, - 0.8079, - ], - ], - "dispense": Array [ - Array [ - 12.5135, - 0, - 0.7945, - ], - ], - }, - Object { - "aspirate": Array [ - Array [ - 1.438649211, - 0.01931415115, - 0.691538317, - ], - Array [ - 1.836824579, - 0.03868955123, - 0.6636639129, - ], - Array [ - 2.960052684, - 0.00470371018, - 0.7260899411, - ], - Array [ - 4.487508789, - 0.005175245625, - 0.7246941713, - ], - Array [ - 10.59661421, - 0.001470408978, - 0.7413196584, - ], - ], - "dispense": Array [ - Array [ - 12.5135, - 0, - 0.7945, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p10_single_v1.5 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": -0.5, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 2.5, - }, - "channels": 1, - "defaultAspirateFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 5, - "valuesByApiLevel": Object { - "2.0": 5, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 10, - "valuesByApiLevel": Object { - "2.0": 10, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_10ul/1", - "opentrons/opentrons_96_filtertiprack_10ul/1", - "opentrons/geb_96_tiprack_10ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P10 Single-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -5.2, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 10, - "minVolume": 1, - "model": "p10_single_v1.5", - "modelOffset": Array [ - 0, - 0, - -13, - ], - "name": "p10_single", - "nozzleOffset": Array [ - 0, - 0, - 12, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.1, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.3, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 33, - }, - "tipOverlap": Object { - "default": 3.29, - "opentrons/eppendorf_96_tiprack_10ul_eptips/1": 1, - "opentrons/geb_96_tiprack_10ul/1": 6.2, - "opentrons/opentrons_96_filtertiprack_10ul/1": 3.29, - "opentrons/opentrons_96_tiprack_10ul/1": 3.29, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 1.553425807, - -0.03427618068, - 0.83, - ], - Array [ - 1.934976526, - -0.007134812859, - 0.7878, - ], - Array [ - 2.689843897, - -0.007238069768, - 0.788, - ], - Array [ - 6.161165493, - 0.0004663523509, - 0.7673, - ], - Array [ - 10.7963169, - 0.0002200157553, - 0.7688, - ], - ], - "dispense": Array [ - Array [ - 12.5135, - 0, - 0.7945, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p50_multi_v1 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": 2, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 2.5, - }, - "channels": 8, - "defaultAspirateFlowRate": Object { - "max": 100, - "min": 0.001, - "value": 25, - "valuesByApiLevel": Object { - "2.0": 25, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 100, - "min": 0.001, - "value": 50, - "valuesByApiLevel": Object { - "2.0": 50, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_300ul/1", - "opentrons/opentrons_96_filtertiprack_200ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P50 8-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -3.5, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 50, - "minVolume": 5, - "model": "p50_multi_v1", - "modelOffset": Array [ - 0, - 31.5, - -25.8, - ], - "name": "p50_multi", - "nozzleOffset": Array [ - 0, - 31.5, - 0.8, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.6, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 51.7, - }, - "tipOverlap": Object { - "default": 7.47, - "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, - "opentrons/opentrons_96_tiprack_300ul/1": 7.47, - "opentrons/tipone_96_tiprack_200ul/1": 6.1, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 12.29687531, - -0.0049, - 3.134703694, - ], - Array [ - 50, - -0.0002, - 3.077116024, - ], - ], - "dispense": Array [ - Array [ - 50, - 0, - 3.06368702, - ], - ], - }, - Object { - "aspirate": Array [ - Array [ - 5.5768667, - 0.076142366, - 2.363797525, - ], - Array [ - 7.0999333, - 0.0338396036, - 2.599714392, - ], - Array [ - 11.5943825, - 0.0130432679, - 2.747366988, - ], - Array [ - 17.6461325, - 0.007010609879, - 2.817311933, - ], - Array [ - 50, - 0.002620115513, - 2.894787178, - ], - ], - "dispense": Array [ - Array [ - 50, - 0, - 3.06368702, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p50_multi_v1.3 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": 0.5, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 2, - }, - "channels": 8, - "defaultAspirateFlowRate": Object { - "max": 100, - "min": 0.001, - "value": 25, - "valuesByApiLevel": Object { - "2.0": 25, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 100, - "min": 0.001, - "value": 50, - "valuesByApiLevel": Object { - "2.0": 50, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_300ul/1", - "opentrons/opentrons_96_filtertiprack_200ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P50 8-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -5, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 50, - "minVolume": 5, - "model": "p50_multi_v1.3", - "modelOffset": Array [ - 0, - 31.5, - -25.8, - ], - "name": "p50_multi", - "nozzleOffset": Array [ - 0, - 31.5, - 0.8, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.6, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 51.7, - }, - "tipOverlap": Object { - "default": 7.47, - "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, - "opentrons/opentrons_96_tiprack_300ul/1": 7.47, - "opentrons/tipone_96_tiprack_200ul/1": 6.1, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 12.29687531, - -0.0049, - 3.134703694, - ], - Array [ - 50, - -0.0002, - 3.077116024, - ], - ], - "dispense": Array [ - Array [ - 50, - 0, - 3.06368702, - ], - ], - }, - Object { - "aspirate": Array [ - Array [ - 5.5768667, - 0.076142366, - 2.363797525, - ], - Array [ - 7.0999333, - 0.0338396036, - 2.599714392, - ], - Array [ - 11.5943825, - 0.0130432679, - 2.747366988, - ], - Array [ - 17.6461325, - 0.007010609879, - 2.817311933, - ], - Array [ - 50, - 0.002620115513, - 2.894787178, - ], - ], - "dispense": Array [ - Array [ - 50, - 0, - 3.06368702, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p50_multi_v1.4 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": 0.5, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 2, - }, - "channels": 8, - "defaultAspirateFlowRate": Object { - "max": 100, - "min": 0.001, - "value": 25, - "valuesByApiLevel": Object { - "2.0": 25, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 100, - "min": 0.001, - "value": 50, - "valuesByApiLevel": Object { - "2.0": 50, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_300ul/1", - "opentrons/opentrons_96_filtertiprack_200ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P50 8-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -4, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 50, - "minVolume": 5, - "model": "p50_multi_v1.4", - "modelOffset": Array [ - 0, - 31.5, - -25.8, - ], - "name": "p50_multi", - "nozzleOffset": Array [ - 0, - 31.5, - 0.8, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.6, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 51.7, - }, - "tipOverlap": Object { - "default": 7.47, - "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, - "opentrons/opentrons_96_tiprack_300ul/1": 7.47, - "opentrons/tipone_96_tiprack_200ul/1": 6.1, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 12.29687531, - -0.0049, - 3.134703694, - ], - Array [ - 50, - -0.0002, - 3.077116024, - ], - ], - "dispense": Array [ - Array [ - 50, - 0, - 3.06368702, - ], - ], - }, - Object { - "aspirate": Array [ - Array [ - 5.5768667, - 0.076142366, - 2.363797525, - ], - Array [ - 7.0999333, - 0.0338396036, - 2.599714392, - ], - Array [ - 11.5943825, - 0.0130432679, - 2.747366988, - ], - Array [ - 17.6461325, - 0.007010609879, - 2.817311933, - ], - Array [ - 50, - 0.002620115513, - 2.894787178, - ], - ], - "dispense": Array [ - Array [ - 50, - 0, - 3.06368702, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p50_multi_v1.5 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": 0.5, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 2, - }, - "channels": 8, - "defaultAspirateFlowRate": Object { - "max": 100, - "min": 0.001, - "value": 25, - "valuesByApiLevel": Object { - "2.0": 25, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 100, - "min": 0.001, - "value": 50, - "valuesByApiLevel": Object { - "2.0": 50, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_300ul/1", - "opentrons/opentrons_96_filtertiprack_200ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P50 8-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -4, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 50, - "minVolume": 5, - "model": "p50_multi_v1.5", - "modelOffset": Array [ - 0, - 31.5, - -25.8, - ], - "name": "p50_multi", - "nozzleOffset": Array [ - 0, - 31.5, - 0.8, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.8, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 3, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "quirks": Array [ - "dropTipShake", - "doubleDropTip", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 51.7, - }, - "tipOverlap": Object { - "default": 7.47, - "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, - "opentrons/opentrons_96_tiprack_300ul/1": 7.47, - "opentrons/tipone_96_tiprack_200ul/1": 6.1, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 6.190392157, - -0.01092438778, - 3.1628, - ], - Array [ - 7.639705882, - -0.02712575255, - 3.2631, - ], - Array [ - 10.69666667, - 0.0001007939816, - 3.0551, - ], - Array [ - 24.49343137, - 0.0003978066956, - 3.0519, - ], - Array [ - 50, - -0.00001501363238, - 3.062, - ], - ], - "dispense": Array [ - Array [ - 50, - 0, - 3.06368702, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p50_single_v1 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": 2, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 2.01, - }, - "channels": 1, - "defaultAspirateFlowRate": Object { - "max": 100, - "min": 0.001, - "value": 25, - "valuesByApiLevel": Object { - "2.0": 25, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 100, - "min": 0.001, - "value": 50, - "valuesByApiLevel": Object { - "2.0": 50, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_300ul/1", - "opentrons/opentrons_96_filtertiprack_200ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P50 Single-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -4.5, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 50, - "minVolume": 5, - "model": "p50_single_v1", - "modelOffset": Array [ - 0, - 0, - 0, - ], - "name": "p50_single", - "nozzleOffset": Array [ - 0, - 0, - 25, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.1, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.3, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 51.7, - }, - "tipOverlap": Object { - "default": 7.47, - "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, - "opentrons/opentrons_96_tiprack_300ul/1": 7.47, - "opentrons/tipone_96_tiprack_200ul/1": 6.1, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 11.79687499, - -0.0098, - 3.064988953, - ], - Array [ - 50, - -0.0004, - 2.954068131, - ], - ], - "dispense": Array [ - Array [ - 50, - 0, - 2.931601299, - ], - ], - }, - Object { - "aspirate": Array [ - Array [ - 5.538952382, - 0.04994568474, - 2.492829422, - ], - Array [ - 7.050333334, - 0.0335171238, - 2.583826438, - ], - Array [ - 11.5397619, - 0.01443549911, - 2.718358253, - ], - Array [ - 17.55071427, - 0.006684226987, - 2.807806088, - ], - Array [ - 50, - 0.001789563193, - 2.893710933, - ], - ], - "dispense": Array [ - Array [ - 50, - 0, - 2.931601299, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p50_single_v1.3 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": 0.5, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 2, - }, - "channels": 1, - "defaultAspirateFlowRate": Object { - "max": 100, - "min": 0.001, - "value": 25, - "valuesByApiLevel": Object { - "2.0": 25, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 100, - "min": 0.001, - "value": 50, - "valuesByApiLevel": Object { - "2.0": 50, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_300ul/1", - "opentrons/opentrons_96_filtertiprack_200ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P50 Single-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -6, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 50, - "minVolume": 5, - "model": "p50_single_v1.3", - "modelOffset": Array [ - 0, - 0, - 0, - ], - "name": "p50_single", - "nozzleOffset": Array [ - 0, - 0, - 25, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.1, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.3, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 51.7, - }, - "tipOverlap": Object { - "default": 7.47, - "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, - "opentrons/opentrons_96_tiprack_300ul/1": 7.47, - "opentrons/tipone_96_tiprack_200ul/1": 6.1, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 11.79687499, - -0.0098, - 3.064988953, - ], - Array [ - 50, - -0.0004, - 2.954068131, - ], - ], - "dispense": Array [ - Array [ - 50, - 0, - 2.931601299, - ], - ], - }, - Object { - "aspirate": Array [ - Array [ - 5.538952382, - 0.04994568474, - 2.492829422, - ], - Array [ - 7.050333334, - 0.0335171238, - 2.583826438, - ], - Array [ - 11.5397619, - 0.01443549911, - 2.718358253, - ], - Array [ - 17.55071427, - 0.006684226987, - 2.807806088, - ], - Array [ - 50, - 0.001789563193, - 2.893710933, - ], - ], - "dispense": Array [ - Array [ - 50, - 0, - 2.931601299, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p50_single_v1.4 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": 0.5, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 2, - }, - "channels": 1, - "defaultAspirateFlowRate": Object { - "max": 100, - "min": 0.001, - "value": 25, - "valuesByApiLevel": Object { - "2.0": 25, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 100, - "min": 0.001, - "value": 50, - "valuesByApiLevel": Object { - "2.0": 50, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_300ul/1", - "opentrons/opentrons_96_filtertiprack_200ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P50 Single-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -5, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 50, - "minVolume": 5, - "model": "p50_single_v1.4", - "modelOffset": Array [ - 0, - 0, - 0, - ], - "name": "p50_single", - "nozzleOffset": Array [ - 0, - 0, - 25, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.1, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.3, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 51.7, - }, - "tipOverlap": Object { - "default": 7.47, - "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, - "opentrons/opentrons_96_tiprack_300ul/1": 7.47, - "opentrons/tipone_96_tiprack_200ul/1": 6.1, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 11.79687499, - -0.0098, - 3.064988953, - ], - Array [ - 50, - -0.0004, - 2.954068131, - ], - ], - "dispense": Array [ - Array [ - 50, - 0, - 2.931601299, - ], - ], - }, - Object { - "aspirate": Array [ - Array [ - 5.538952382, - 0.04994568474, - 2.492829422, - ], - Array [ - 7.050333334, - 0.0335171238, - 2.583826438, - ], - Array [ - 11.5397619, - 0.01443549911, - 2.718358253, - ], - Array [ - 17.55071427, - 0.006684226987, - 2.807806088, - ], - Array [ - 50, - 0.001789563193, - 2.893710933, - ], - ], - "dispense": Array [ - Array [ - 50, - 0, - 2.931601299, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p300_multi_v1 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": 3, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 3.5, - }, - "channels": 8, - "defaultAspirateFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 150, - "valuesByApiLevel": Object { - "2.0": 150, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 300, - "valuesByApiLevel": Object { - "2.0": 300, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_300ul/1", - "opentrons/opentrons_96_filtertiprack_200ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P300 8-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -2, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 300, - "minVolume": 30, - "model": "p300_multi_v1", - "modelOffset": Array [ - 0, - 31.5, - -25.8, - ], - "name": "p300_multi", - "nozzleOffset": Array [ - 0, - 31.5, - 0.8, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.6, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 51.7, - }, - "tipOverlap": Object { - "default": 7.47, - "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, - "opentrons/opentrons_96_tiprack_300ul/1": 7.47, - "opentrons/tipone_96_tiprack_200ul/1": 6.1, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 57.25698968, - 0.017, - 18.132, - ], - Array [ - 309.2612689, - 0.001, - 19.03, - ], - ], - "dispense": Array [ - Array [ - 309.2612689, - 0, - 19.29389273, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p300_multi_v1.3 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": 1.5, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 3.5, - }, - "channels": 8, - "defaultAspirateFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 150, - "valuesByApiLevel": Object { - "2.0": 150, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 300, - "valuesByApiLevel": Object { - "2.0": 300, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_300ul/1", - "opentrons/opentrons_96_filtertiprack_200ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P300 8-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -3.5, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 300, - "minVolume": 30, - "model": "p300_multi_v1.3", - "modelOffset": Array [ - 0, - 31.5, - -25.8, - ], - "name": "p300_multi", - "nozzleOffset": Array [ - 0, - 31.5, - 0.8, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.6, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 51.7, - }, - "tipOverlap": Object { - "default": 7.47, - "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, - "opentrons/opentrons_96_tiprack_300ul/1": 7.47, - "opentrons/tipone_96_tiprack_200ul/1": 6.1, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 57.25698968, - 0.017, - 18.132, - ], - Array [ - 309.2612689, - 0.001, - 19.03, - ], - ], - "dispense": Array [ - Array [ - 309.2612689, - 0, - 19.29389273, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p300_multi_v1.4 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": 1.5, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 3.5, - }, - "channels": 8, - "defaultAspirateFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 150, - "valuesByApiLevel": Object { - "2.0": 150, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 300, - "valuesByApiLevel": Object { - "2.0": 300, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_300ul/1", - "opentrons/opentrons_96_filtertiprack_200ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P300 8-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -3.5, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm", - "value": 5, - }, - "maxVolume": 300, - "minVolume": 30, - "model": "p300_multi_v1.4", - "modelOffset": Array [ - 0, - 31.5, - -25.8, - ], - "name": "p300_multi", - "nozzleOffset": Array [ - 0, - 31.5, - 0.8, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.6, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 51.7, - }, - "tipOverlap": Object { - "default": 7.47, - "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, - "opentrons/opentrons_96_tiprack_300ul/1": 7.47, - "opentrons/tipone_96_tiprack_200ul/1": 6.1, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 57.25698968, - 0.017, - 18.132, - ], - Array [ - 309.2612689, - 0.001, - 19.03, - ], - ], - "dispense": Array [ - Array [ - 309.2612689, - 0, - 19.29389273, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p300_multi_v1.5 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": 1.5, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 3.5, - }, - "channels": 8, - "defaultAspirateFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 150, - "valuesByApiLevel": Object { - "2.0": 150, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 300, - "valuesByApiLevel": Object { - "2.0": 300, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_300ul/1", - "opentrons/opentrons_96_filtertiprack_200ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P300 8-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -3.5, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm", - "value": 5, - }, - "maxVolume": 300, - "minVolume": 30, - "model": "p300_multi_v1.5", - "modelOffset": Array [ - 0, - 31.5, - -25.8, - ], - "name": "p300_multi", - "nozzleOffset": Array [ - 0, - 31.5, - 0.8, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.9, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 3, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "quirks": Array [ - "dropTipShake", - "doubleDropTip", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 51.7, - }, - "tipOverlap": Object { - "default": 7.47, - "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, - "opentrons/opentrons_96_tiprack_300ul/1": 7.47, - "opentrons/tipone_96_tiprack_200ul/1": 6.1, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 57.25698968, - 0.017, - 18.132, - ], - Array [ - 309.2612689, - 0.001, - 19.03, - ], - ], - "dispense": Array [ - Array [ - 309.2612689, - 0, - 19.29389273, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p300_single_v1 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": 0, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 1.5, - }, - "channels": 1, - "defaultAspirateFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 150, - "valuesByApiLevel": Object { - "2.0": 150, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 300, - "valuesByApiLevel": Object { - "2.0": 300, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_300ul/1", - "opentrons/opentrons_96_filtertiprack_200ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P300 Single-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -4, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 300, - "minVolume": 30, - "model": "p300_single_v1", - "modelOffset": Array [ - 0, - 0, - 0, - ], - "name": "p300_single", - "nozzleOffset": Array [ - 0, - 0, - 25, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.1, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.3, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 51.7, - }, - "tipOverlap": Object { - "default": 7.47, - "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, - "opentrons/opentrons_96_tiprack_300ul/1": 7.47, - "opentrons/tipone_96_tiprack_200ul/1": 6.1, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 36.19844973, - 0.043, - 16.548, - ], - Array [ - 54.98518519, - 0.012, - 17.658, - ], - Array [ - 73.90077516, - 0.008, - 17.902, - ], - Array [ - 111.8437953, - 0.004, - 18.153, - ], - Array [ - 302.3895337, - 0.001, - 18.23, - ], - ], - "dispense": Array [ - Array [ - 302.3895337, - 0, - 18.83156277, - ], - ], - }, - Object { - "aspirate": Array [ - Array [ - 53.958, - 0.0252, - 16.6268, - ], - Array [ - 73.0217, - 0.0141, - 17.2234, - ], - Array [ - 82.6834, - 0.0123, - 17.3586, - ], - Array [ - 120.7877, - 0.0055, - 17.9214, - ], - Array [ - 197.3909, - 0.0028, - 18.2415, - ], - Array [ - 300, - 0.0014, - 18.5235, - ], - ], - "dispense": Array [ - Array [ - 302.3895337, - 0, - 18.83156277, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p300_single_v1.3 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": -1.5, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 1.5, - }, - "channels": 1, - "defaultAspirateFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 150, - "valuesByApiLevel": Object { - "2.0": 150, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 300, - "valuesByApiLevel": Object { - "2.0": 300, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_300ul/1", - "opentrons/opentrons_96_filtertiprack_200ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P300 Single-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -5.5, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 300, - "minVolume": 30, - "model": "p300_single_v1.3", - "modelOffset": Array [ - 0, - 0, - 0, - ], - "name": "p300_single", - "nozzleOffset": Array [ - 0, - 0, - 25, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.1, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.3, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 51.7, - }, - "tipOverlap": Object { - "default": 7.47, - "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, - "opentrons/opentrons_96_tiprack_300ul/1": 7.47, - "opentrons/tipone_96_tiprack_200ul/1": 6.1, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 36.19844973, - 0.043, - 16.548, - ], - Array [ - 54.98518519, - 0.012, - 17.658, - ], - Array [ - 73.90077516, - 0.008, - 17.902, - ], - Array [ - 111.8437953, - 0.004, - 18.153, - ], - Array [ - 302.3895337, - 0.001, - 18.23, - ], - ], - "dispense": Array [ - Array [ - 302.3895337, - 0, - 18.83156277, - ], - ], - }, - Object { - "aspirate": Array [ - Array [ - 53.958, - 0.0252, - 16.6268, - ], - Array [ - 73.0217, - 0.0141, - 17.2234, - ], - Array [ - 82.6834, - 0.0123, - 17.3586, - ], - Array [ - 120.7877, - 0.0055, - 17.9214, - ], - Array [ - 197.3909, - 0.0028, - 18.2415, - ], - Array [ - 300, - 0.0014, - 18.5235, - ], - ], - "dispense": Array [ - Array [ - 302.3895337, - 0, - 18.83156277, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p300_single_v1.4 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": 0, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 3, - }, - "channels": 1, - "defaultAspirateFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 150, - "valuesByApiLevel": Object { - "2.0": 150, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 300, - "valuesByApiLevel": Object { - "2.0": 300, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_300ul/1", - "opentrons/opentrons_96_filtertiprack_200ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P300 Single-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -4.5, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 300, - "minVolume": 30, - "model": "p300_single_v1.4", - "modelOffset": Array [ - 0, - 0, - 0, - ], - "name": "p300_single", - "nozzleOffset": Array [ - 0, - 0, - 25, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.1, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.3, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 51.7, - }, - "tipOverlap": Object { - "default": 7.47, - "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, - "opentrons/opentrons_96_tiprack_300ul/1": 7.47, - "opentrons/tipone_96_tiprack_200ul/1": 6.1, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 36.19844973, - 0.043, - 16.548, - ], - Array [ - 54.98518519, - 0.012, - 17.658, - ], - Array [ - 73.90077516, - 0.008, - 17.902, - ], - Array [ - 111.8437953, - 0.004, - 18.153, - ], - Array [ - 302.3895337, - 0.001, - 18.23, - ], - ], - "dispense": Array [ - Array [ - 302.3895337, - 0, - 18.83156277, - ], - ], - }, - Object { - "aspirate": Array [ - Array [ - 53.958, - 0.0252, - 16.6268, - ], - Array [ - 73.0217, - 0.0141, - 17.2234, - ], - Array [ - 82.6834, - 0.0123, - 17.3586, - ], - Array [ - 120.7877, - 0.0055, - 17.9214, - ], - Array [ - 197.3909, - 0.0028, - 18.2415, - ], - Array [ - 300, - 0.0014, - 18.5235, - ], - ], - "dispense": Array [ - Array [ - 302.3895337, - 0, - 18.83156277, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p300_single_v1.5 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": 0, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 3, - }, - "channels": 1, - "defaultAspirateFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 150, - "valuesByApiLevel": Object { - "2.0": 150, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 300, - "valuesByApiLevel": Object { - "2.0": 300, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_300ul/1", - "opentrons/opentrons_96_filtertiprack_200ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P300 Single-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -4.5, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 300, - "minVolume": 30, - "model": "p300_single_v1.5", - "modelOffset": Array [ - 0, - 0, - 0, - ], - "name": "p300_single", - "nozzleOffset": Array [ - 0, - 0, - 25, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.1, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 10, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.3, - }, - "quirks": Array [ - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 51.7, - }, - "tipOverlap": Object { - "default": 7.47, - "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, - "opentrons/opentrons_96_tiprack_300ul/1": 7.47, - "opentrons/tipone_96_tiprack_200ul/1": 6.1, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 35.11266179, - 0.03721315938, - 16.2497, - ], - Array [ - 44.37338506, - 0.02084320206, - 16.8245, - ], - Array [ - 63.12001468, - 0.01519931266, - 17.0749, - ], - Array [ - 148.3020792, - 0.005910516464, - 17.6612, - ], - Array [ - 224.5387262, - 0.00227975152, - 18.1997, - ], - Array [ - 301.049323, - 0.001359578667, - 18.4063, - ], - ], - "dispense": Array [ - Array [ - 302.3895337, - 0, - 18.83156277, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p1000_single_v1 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": 1, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 3, - }, - "channels": 1, - "defaultAspirateFlowRate": Object { - "max": 2000, - "min": 50, - "value": 500, - "valuesByApiLevel": Object { - "2.0": 500, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 2000, - "min": 50, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_1000ul/1", - "opentrons/opentrons_96_filtertiprack_1000ul/1", - "opentrons/geb_96_tiprack_1000ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P1000 Single-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -2.2, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 1000, - "minVolume": 100, - "model": "p1000_single_v1", - "modelOffset": Array [ - 0, - 0, - 20, - ], - "name": "p1000_single", - "nozzleOffset": Array [ - 0, - 0, - 45, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.1, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 15, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "quirks": Array [ - "pickupTipShake", - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 76.7, - }, - "tipOverlap": Object { - "default": 7.95, - "opentrons/eppendorf_96_tiprack_1000ul_eptips/1": 0, - "opentrons/geb_96_tiprack_1000ul/1": 11.2, - "opentrons/opentrons_96_filtertiprack_1000ul/1": 7.95, - "opentrons/opentrons_96_tiprack_1000ul/1": 7.95, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 148.9157, - 0.0213, - 56.3986, - ], - Array [ - 210.8237, - 0.0108, - 57.9568, - ], - Array [ - 241.2405, - 0.0025, - 59.717, - ], - Array [ - 365.2719, - 0.0046, - 59.2043, - ], - Array [ - 614.4871, - 0.0023, - 60.0431, - ], - Array [ - 1000, - 0.001, - 60.8209, - ], - ], - "dispense": Array [ - Array [ - 1000, - 0, - 61.3275, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p1000_single_v1.3 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": 0.5, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 2.5, - }, - "channels": 1, - "defaultAspirateFlowRate": Object { - "max": 2000, - "min": 50, - "value": 500, - "valuesByApiLevel": Object { - "2.0": 500, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 2000, - "min": 50, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_1000ul/1", - "opentrons/opentrons_96_filtertiprack_1000ul/1", - "opentrons/geb_96_tiprack_1000ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P1000 Single-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -4, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.7, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 1000, - "minVolume": 100, - "model": "p1000_single_v1.3", - "modelOffset": Array [ - 0, - 0, - 20, - ], - "name": "p1000_single", - "nozzleOffset": Array [ - 0, - 0, - 45, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.1, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 15, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "quirks": Array [ - "pickupTipShake", - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 76.7, - }, - "tipOverlap": Object { - "default": 7.95, - "opentrons/eppendorf_96_tiprack_1000ul_eptips/1": 0, - "opentrons/geb_96_tiprack_1000ul/1": 11.2, - "opentrons/opentrons_96_filtertiprack_1000ul/1": 7.95, - "opentrons/opentrons_96_tiprack_1000ul/1": 7.95, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 148.9157, - 0.0213, - 56.3986, - ], - Array [ - 210.8237, - 0.0108, - 57.9568, - ], - Array [ - 241.2405, - 0.0025, - 59.717, - ], - Array [ - 365.2719, - 0.0046, - 59.2043, - ], - Array [ - 614.4871, - 0.0023, - 60.0431, - ], - Array [ - 1000, - 0.001, - 60.8209, - ], - ], - "dispense": Array [ - Array [ - 1000, - 0, - 61.3275, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p1000_single_v1.4 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": 0.5, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 2.5, - }, - "channels": 1, - "defaultAspirateFlowRate": Object { - "max": 2000, - "min": 50, - "value": 500, - "valuesByApiLevel": Object { - "2.0": 500, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 2000, - "min": 50, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_1000ul/1", - "opentrons/opentrons_96_filtertiprack_1000ul/1", - "opentrons/geb_96_tiprack_1000ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P1000 Single-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -4, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.7, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 1000, - "minVolume": 100, - "model": "p1000_single_v1.4", - "modelOffset": Array [ - 0, - 0, - 20, - ], - "name": "p1000_single", - "nozzleOffset": Array [ - 0, - 0, - 45, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.1, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 15, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "quirks": Array [ - "pickupTipShake", - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 76.7, - }, - "tipOverlap": Object { - "default": 7.95, - "opentrons/eppendorf_96_tiprack_1000ul_eptips/1": 0, - "opentrons/geb_96_tiprack_1000ul/1": 11.2, - "opentrons/opentrons_96_filtertiprack_1000ul/1": 7.95, - "opentrons/opentrons_96_tiprack_1000ul/1": 7.95, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 148.9157, - 0.0213, - 56.3986, - ], - Array [ - 210.8237, - 0.0108, - 57.9568, - ], - Array [ - 241.2405, - 0.0025, - 59.717, - ], - Array [ - 365.2719, - 0.0046, - 59.2043, - ], - Array [ - 614.4871, - 0.0023, - 60.0431, - ], - Array [ - 1000, - 0.001, - 60.8209, - ], - ], - "dispense": Array [ - Array [ - 1000, - 0, - 61.3275, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteModelSpecs model p1000_single_v1.5 snapshot 1`] = ` -Object { - "blowout": Object { - "max": 10, - "min": -4, - "type": "float", - "units": "mm", - "value": 0.5, - }, - "bottom": Object { - "max": 19, - "min": -2, - "type": "float", - "units": "mm", - "value": 2.5, - }, - "channels": 1, - "defaultAspirateFlowRate": Object { - "max": 2000, - "min": 50, - "value": 500, - "valuesByApiLevel": Object { - "2.0": 500, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 2000, - "min": 50, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_1000ul/1", - "opentrons/opentrons_96_filtertiprack_1000ul/1", - "opentrons/geb_96_tiprack_1000ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P1000 Single-Channel GEN1", - "dropTip": Object { - "max": 2, - "min": -6, - "type": "float", - "units": "mm", - "value": -4, - }, - "dropTipCurrent": Object { - "max": 0.8, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.7, - }, - "dropTipSpeed": Object { - "max": 30, - "min": 0.001, - "type": "float", - "units": "mm/sec", - "value": 5, - }, - "maxVolume": 1000, - "minVolume": 100, - "model": "p1000_single_v1.5", - "modelOffset": Array [ - 0, - 0, - 20, - ], - "name": "p1000_single", - "nozzleOffset": Array [ - 0, - 0, - 45, - ], - "pickUpCurrent": Object { - "max": 2, - "min": 0.05, - "type": "float", - "units": "amps", - "value": 0.15, - }, - "pickUpDistance": Object { - "max": 30, - "min": 1, - "type": "float", - "units": "mm", - "value": 15, - }, - "pickUpIncrement": Object { - "max": 10, - "min": 0, - "type": "float", - "units": "mm", - "value": 1, - }, - "pickUpPresses": Object { - "max": 10, - "min": 0, - "type": "int", - "units": "presses", - "value": 3, - }, - "pickUpSpeed": Object { - "max": 100, - "min": 1, - "type": "float", - "units": "mm/s", - "value": 30, - }, - "plungerCurrent": Object { - "max": 0.5, - "min": 0.1, - "type": "float", - "units": "amps", - "value": 0.5, - }, - "quirks": Array [ - "pickupTipShake", - "dropTipShake", - ], - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, - "tipLength": Object { - "max": 100, - "min": 0, - "type": "float", - "units": "mm", - "value": 76.7, - }, - "tipOverlap": Object { - "default": 7.95, - "opentrons/eppendorf_96_tiprack_1000ul_eptips/1": 0, - "opentrons/geb_96_tiprack_1000ul/1": 11.2, - "opentrons/opentrons_96_filtertiprack_1000ul/1": 7.95, - "opentrons/opentrons_96_tiprack_1000ul/1": 7.95, - }, - "top": Object { - "max": 19.5, - "min": 5, - "type": "float", - "units": "mm", - "value": 19.5, - }, - "ulPerMm": Array [ - Object { - "aspirate": Array [ - Array [ - 148.9157, - 0.0213, - 56.3986, - ], - Array [ - 210.8237, - 0.0108, - 57.9568, - ], - Array [ - 241.2405, - 0.0025, - 59.717, - ], - Array [ - 365.2719, - 0.0046, - 59.2043, - ], - Array [ - 614.4871, - 0.0023, - 60.0431, - ], - Array [ - 1000, - 0.001, - 60.8209, - ], - ], - "dispense": Array [ - Array [ - 1000, - 0, - 61.3275, - ], - ], - }, - ], -} -`; - -exports[`pipette data accessors getPipetteNameSpecs name p10_multi snapshot 1`] = ` -Object { - "channels": 8, - "defaultAspirateFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 5, - "valuesByApiLevel": Object { - "2.0": 5, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 10, - "valuesByApiLevel": Object { - "2.0": 10, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_10ul/1", - "opentrons/opentrons_96_filtertiprack_10ul/1", - "opentrons/geb_96_tiprack_10ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P10 8-Channel GEN1", - "maxVolume": 10, - "minVolume": 1, - "name": "p10_multi", - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, -} -`; - -exports[`pipette data accessors getPipetteNameSpecs name p10_single snapshot 1`] = ` -Object { - "channels": 1, - "defaultAspirateFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 5, - "valuesByApiLevel": Object { - "2.0": 5, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 50, - "min": 0.001, - "value": 10, - "valuesByApiLevel": Object { - "2.0": 10, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_10ul/1", - "opentrons/opentrons_96_filtertiprack_10ul/1", - "opentrons/geb_96_tiprack_10ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P10 Single-Channel GEN1", - "maxVolume": 10, - "minVolume": 1, - "name": "p10_single", - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, -} -`; - -exports[`pipette data accessors getPipetteNameSpecs name p50_multi snapshot 1`] = ` -Object { - "channels": 8, - "defaultAspirateFlowRate": Object { - "max": 100, - "min": 0.001, - "value": 25, - "valuesByApiLevel": Object { - "2.0": 25, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 100, - "min": 0.001, - "value": 50, - "valuesByApiLevel": Object { - "2.0": 50, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_300ul/1", - "opentrons/opentrons_96_filtertiprack_200ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P50 8-Channel GEN1", - "maxVolume": 50, - "minVolume": 5, - "name": "p50_multi", - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, -} -`; - -exports[`pipette data accessors getPipetteNameSpecs name p50_single snapshot 1`] = ` -Object { - "channels": 1, - "defaultAspirateFlowRate": Object { - "max": 100, - "min": 0.001, - "value": 25, - "valuesByApiLevel": Object { - "2.0": 25, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 100, - "min": 0.001, - "value": 50, - "valuesByApiLevel": Object { - "2.0": 50, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_300ul/1", - "opentrons/opentrons_96_filtertiprack_200ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P50 Single-Channel GEN1", - "maxVolume": 50, - "minVolume": 5, - "name": "p50_single", - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, -} -`; - -exports[`pipette data accessors getPipetteNameSpecs name p300_multi snapshot 1`] = ` -Object { - "channels": 8, - "defaultAspirateFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 150, - "valuesByApiLevel": Object { - "2.0": 150, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 300, - "valuesByApiLevel": Object { - "2.0": 300, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_300ul/1", - "opentrons/opentrons_96_filtertiprack_200ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P300 8-Channel GEN1", - "maxVolume": 300, - "minVolume": 30, - "name": "p300_multi", - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, -} -`; - -exports[`pipette data accessors getPipetteNameSpecs name p300_single snapshot 1`] = ` -Object { - "channels": 1, - "defaultAspirateFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 150, - "valuesByApiLevel": Object { - "2.0": 150, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 600, - "min": 0.001, - "value": 300, - "valuesByApiLevel": Object { - "2.0": 300, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_300ul/1", - "opentrons/opentrons_96_filtertiprack_200ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P300 Single-Channel GEN1", - "maxVolume": 300, - "minVolume": 30, - "name": "p300_single", - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, -} -`; - -exports[`pipette data accessors getPipetteNameSpecs name p1000_single snapshot 1`] = ` -Object { - "channels": 1, - "defaultAspirateFlowRate": Object { - "max": 2000, - "min": 50, - "value": 500, - "valuesByApiLevel": Object { - "2.0": 500, - }, - }, - "defaultBlowOutFlowRate": Object { - "max": 1000, - "min": 5, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultDispenseFlowRate": Object { - "max": 2000, - "min": 50, - "value": 1000, - "valuesByApiLevel": Object { - "2.0": 1000, - }, - }, - "defaultTipracks": Array [ - "opentrons/opentrons_96_tiprack_1000ul/1", - "opentrons/opentrons_96_filtertiprack_1000ul/1", - "opentrons/geb_96_tiprack_1000ul/1", - ], - "displayCategory": "GEN1", - "displayName": "P1000 Single-Channel GEN1", - "maxVolume": 1000, - "minVolume": 100, - "name": "p1000_single", - "smoothieConfigs": Object { - "homePosition": 220, - "stepsPerMM": 768, - "travelDistance": 30, - }, -} -`; diff --git a/shared-data/js/__tests__/getWellNamePerMultiTip.test.ts b/shared-data/js/__tests__/getWellNamePerMultiTip.test.ts index d615f332a13..fa6a7717ddf 100644 --- a/shared-data/js/__tests__/getWellNamePerMultiTip.test.ts +++ b/shared-data/js/__tests__/getWellNamePerMultiTip.test.ts @@ -3,9 +3,20 @@ import fixture_96_plate from '@opentrons/shared-data/labware/fixtures/2/fixture_ 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([ 'A1', @@ -18,6 +29,7 @@ describe('96 plate', () => { 'H1', ]) }) + it('A2 => column 2', () => { expect(getWellNamePerMultiTip(labware, 'A2')).toEqual([ 'A2', @@ -30,12 +42,15 @@ describe('96 plate', () => { 'H2', ]) }) + it('B1 => null (cannot access with 8-channel)', () => { expect(getWellNamePerMultiTip(labware, 'B1')).toEqual(null) }) }) + describe('384 plate', () => { - const labware = fixture_384_plate + const labware = fixture384Plate + it('A1 => column 1 ACEGIKMO', () => { expect(getWellNamePerMultiTip(labware, 'A1')).toEqual([ 'A1', @@ -48,6 +63,7 @@ describe('384 plate', () => { 'O1', ]) }) + it('A2 => column 2 ACEGIKMO', () => { expect(getWellNamePerMultiTip(labware, 'A2')).toEqual([ 'A2', @@ -60,6 +76,7 @@ describe('384 plate', () => { 'O2', ]) }) + it('B1 => column 1 BDFHJLNP', () => { expect(getWellNamePerMultiTip(labware, 'B1')).toEqual([ 'B1', @@ -72,12 +89,15 @@ describe('384 plate', () => { 'P1', ]) }) + it('C1 => null (cannot access with 8-channel)', () => { expect(getWellNamePerMultiTip(labware, 'C1')).toEqual(null) }) }) + describe('Fixed trash', () => { - const labware = fixture_trash + const labware = fixtureTrash + it('A1 => all tips in A1', () => { expect(getWellNamePerMultiTip(labware, 'A1')).toEqual([ 'A1', @@ -90,20 +110,25 @@ describe('Fixed trash', () => { 'A1', ]) }) + it('A2 => null (well does not exist)', () => { expect(getWellNamePerMultiTip(labware, 'A2')).toEqual(null) }) }) + 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([ 'A1', @@ -116,6 +141,7 @@ describe('12 channel trough', () => { 'A1', ]) }) + it('A2 => all tips in A2', () => { expect(getWellNamePerMultiTip(labware, 'A2')).toEqual([ 'A2', @@ -128,6 +154,7 @@ describe('12 channel trough', () => { 'A2', ]) }) + it('B1 => null (well does not exist)', () => { expect(getWellNamePerMultiTip(labware, 'B1')).toEqual(null) }) diff --git a/shared-data/js/__tests__/labwareDefQuirks.test.ts b/shared-data/js/__tests__/labwareDefQuirks.test.ts index bb49c6d5974..86f14550ac6 100644 --- a/shared-data/js/__tests__/labwareDefQuirks.test.ts +++ b/shared-data/js/__tests__/labwareDefQuirks.test.ts @@ -1,22 +1,28 @@ import path from 'path' import glob from 'glob' + const definitionsGlobPath = path.join( __dirname, '../../labware/definitions/2/**/*.json' ) + const EXPECTED_VALID_QUIRKS = [ 'centerMultichannelOnWells', 'touchTipDisabled', 'fixedTrash', ] + 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) diff --git a/shared-data/js/__tests__/labwareDefSchemaV1.test.ts b/shared-data/js/__tests__/labwareDefSchemaV1.test.ts index f372e837644..2c6385df916 100644 --- a/shared-data/js/__tests__/labwareDefSchemaV1.test.ts +++ b/shared-data/js/__tests__/labwareDefSchemaV1.test.ts @@ -51,7 +51,7 @@ describe('test the schema against a minimalist fixture', () => { 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', }) diff --git a/shared-data/js/__tests__/labwareDefSchemaV2.test.ts b/shared-data/js/__tests__/labwareDefSchemaV2.test.ts index 4c17f33332a..61c0f9fc06d 100644 --- a/shared-data/js/__tests__/labwareDefSchemaV2.test.ts +++ b/shared-data/js/__tests__/labwareDefSchemaV2.test.ts @@ -1,28 +1,35 @@ 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, '../../labware/definitions/2/**/*.json' ) + 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 = @@ -38,7 +45,7 @@ const expectGroupsFollowConvention = (labwareDef, filename) => { }) } -describe('fail on bad labware', () => { +test('fail on bad labware', () => { const badDef = { metadata: { name: 'bad', @@ -49,23 +56,26 @@ describe('fail on bad labware', () => { } 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', }) expect(valid).toBe(false) }) + 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) @@ -73,14 +83,17 @@ describe('test schemas of all opentrons definitions', () => { 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') }) @@ -91,16 +104,18 @@ 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) @@ -108,14 +123,17 @@ describe('test schemas of all v2 labware fixtures', () => { 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') }) + expectGroupsFollowConvention(labwareDef, filename) }) }) diff --git a/shared-data/js/__tests__/moduleAccessors.test.ts b/shared-data/js/__tests__/moduleAccessors.test.ts index 9ed1e32e5a9..ccc7ceaf46a 100644 --- a/shared-data/js/__tests__/moduleAccessors.test.ts +++ b/shared-data/js/__tests__/moduleAccessors.test.ts @@ -4,6 +4,7 @@ import { getModuleDisplayName, normalizeModuleModel, } from '../modules' + import { MODULE_MODELS, MODULE_TYPES, @@ -14,28 +15,34 @@ import { TEMPERATURE_MODULE_V1, THERMOCYCLER_MODULE_V1, } from '../constants' + 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) }) }) }) + describe('legacy models work too', () => { const legacyEquivs = [ [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.ts b/shared-data/js/__tests__/moduleSpecsSchema.test.ts index cd5d387ac49..460bea127fc 100644 --- a/shared-data/js/__tests__/moduleSpecsSchema.test.ts +++ b/shared-data/js/__tests__/moduleSpecsSchema.test.ts @@ -4,44 +4,44 @@ import moduleSpecsV1 from '../../module/definitions/1.json' 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) + const V2_DEFS_GLOB_PATTERN = '../../module/definitions/2/*.json' -const GLOB_OPTIONS = { - cwd: __dirname, - absolute: true, -} +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 + expect(validationErrors).toBe(null) expect(valid).toBe(true) }) + 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.ts b/shared-data/js/__tests__/pipetteSpecSchemas.test.ts index 3dc4603ae1c..df096876252 100644 --- a/shared-data/js/__tests__/pipetteSpecSchemas.test.ts +++ b/shared-data/js/__tests__/pipetteSpecSchemas.test.ts @@ -3,12 +3,12 @@ import nameSpecsSchema from '../../pipette/schemas/pipetteNameSpecsSchema.json' 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) + describe('validate pipette specs with JSON schemas', () => { it('ensure all pipette *NAME* specs match name JSON schema', () => { const valid = validateNameSpecs(pipetteNameSpecs) @@ -21,6 +21,7 @@ describe('validate pipette specs with JSON schemas', () => { expect(validationErrors).toBe(null) expect(valid).toBe(true) }) + it('ensure all pipette *MODEL* specs match model JSON schema', () => { const valid = validateModelSpecs(pipetteModelSpecs) const validationErrors = validateModelSpecs.errors @@ -33,12 +34,14 @@ describe('validate pipette specs with JSON schemas', () => { expect(valid).toBe(true) }) }) + 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__/sortWells.test.ts b/shared-data/js/__tests__/sortWells.test.ts index 769b10ab887..ac2501b25c6 100644 --- a/shared-data/js/__tests__/sortWells.test.ts +++ b/shared-data/js/__tests__/sortWells.test.ts @@ -1,4 +1,5 @@ import { sortWells } from '../helpers' + describe('sortWells', () => { it('single letters', () => { const input = ['A12', 'A2', 'B1', 'B12', 'A1'] @@ -6,6 +7,7 @@ describe('sortWells', () => { const result = [...input].sort(sortWells) expect(result).toEqual(expected) }) + // just in case we get a 1536-well plate it('double letters', () => { const input = [ diff --git a/shared-data/js/__tests__/splitWellsOnColumn.test.ts b/shared-data/js/__tests__/splitWellsOnColumn.test.ts index d3a86c9564e..77229116829 100644 --- a/shared-data/js/__tests__/splitWellsOnColumn.test.ts +++ b/shared-data/js/__tests__/splitWellsOnColumn.test.ts @@ -1,11 +1,14 @@ import { splitWellsOnColumn } from '../helpers' + describe('test splitWellsOnColumn', () => { it('empty array', () => { expect(splitWellsOnColumn([])).toEqual([]) }) + it('one value', () => { expect(splitWellsOnColumn(['A1'])).toEqual([['A1']]) }) + it('sort multi-digit wels', () => { expect(splitWellsOnColumn(['A1', 'B2', 'C2', 'D3', 'X10', 'X11'])).toEqual([ ['A1'], diff --git a/shared-data/js/constants.ts b/shared-data/js/constants.ts index ec35ed36c48..0b5642f2d29 100644 --- a/shared-data/js/constants.ts +++ b/shared-data/js/constants.ts @@ -1,4 +1,5 @@ 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 @@ -13,10 +14,12 @@ export const SLOT_RENDER_HEIGHT = SLOT_WIDTH_MM // along Y axis in SVG coords export const FIXED_TRASH_RENDER_HEIGHT = 165.86 // along Y axis in SVG coords export const OPENTRONS_LABWARE_NAMESPACE = 'opentrons' + // TODO: IL 2020-02-19 These 3 constants are DEPRECATED because they're ambiguous model vs type. 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' @@ -27,26 +30,33 @@ export const TEMPERATURE_MODULE_V2: 'temperatureModuleV2' = 'temperatureModuleV2' export const THERMOCYCLER_MODULE_V1: 'thermocyclerModuleV1' = 'thermocyclerModuleV1' + // pipette display categories export const GEN2: 'GEN2' = 'GEN2' export const GEN1: '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' = 'temperatureModuleType' export const MAGNETIC_MODULE_TYPE: 'magneticModuleType' = 'magneticModuleType' export const THERMOCYCLER_MODULE_TYPE: 'thermocyclerModuleType' = 'thermocyclerModuleType' + export const MAGNETIC_MODULE_MODELS = [MAGNETIC_MODULE_V1, MAGNETIC_MODULE_V2] + export const TEMPERATURE_MODULE_MODELS = [ TEMPERATURE_MODULE_V1, TEMPERATURE_MODULE_V2, ] + export const THERMOCYCLER_MODULE_MODELS = [THERMOCYCLER_MODULE_V1] -export const MODULE_MODELS: Array = [ + +export const MODULE_MODELS: ModuleModel[] = [ ...MAGNETIC_MODULE_MODELS, ...TEMPERATURE_MODULE_MODELS, ...THERMOCYCLER_MODULE_MODELS, ] + export const MODULE_TYPES = [ TEMPERATURE_MODULE_TYPE, MAGNETIC_MODULE_TYPE, diff --git a/shared-data/js/helpers/getWellNamePerMultiTip.ts b/shared-data/js/helpers/getWellNamePerMultiTip.ts index 8598b5d0ece..f7b0192a332 100644 --- a/shared-data/js/helpers/getWellNamePerMultiTip.ts +++ b/shared-data/js/helpers/getWellNamePerMultiTip.ts @@ -1,6 +1,7 @@ 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 @@ -31,11 +32,12 @@ export function findWellAt( ) }) } + // "topWellName" means well at the "top" of the column we're accessing: usually A row, or B row for 384-format export function getWellNamePerMultiTip( labwareDef: LabwareDefinition2, topWellName: string -): Array | null { +): string[] | null { const topWell = labwareDef.wells[topWellName] if (!topWell) { @@ -46,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 ) @@ -59,7 +61,7 @@ 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) { diff --git a/shared-data/js/helpers/wellSets.ts b/shared-data/js/helpers/wellSets.ts index c64a491f04c..6154dd56a94 100644 --- a/shared-data/js/helpers/wellSets.ts +++ b/shared-data/js/helpers/wellSets.ts @@ -11,7 +11,7 @@ // 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' 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__/createIrregularLabware.test.ts b/shared-data/js/labwareTools/__tests__/createIrregularLabware.test.ts index 63c49c58664..f0fefe7339c 100644 --- a/shared-data/js/labwareTools/__tests__/createIrregularLabware.test.ts +++ b/shared-data/js/labwareTools/__tests__/createIrregularLabware.test.ts @@ -36,7 +36,8 @@ describe('test helper functions', () => { range(grid.column).forEach(colIdx => { range(grid.row).forEach(rowIdx => { - const idx = colIdx + rowIdx + const idx = colIdx * grid.row + rowIdx + const wellName1 = _irregularWellName(rowIdx, colIdx, gridStart[0]) expect(expected1[idx]).toEqual(wellName1) 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" } ] } From b412bc7fa93fcc73b4b5f29ae7a4e67bdf88b4fe Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Tue, 27 Apr 2021 20:45:14 -0400 Subject: [PATCH 06/16] fixup: add last type and lint fixes --- .eslintignore | 2 + .flowconfig | 1 + 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 +- components/src/robot-types.ts | 7 +- discovery-client/.gitignore | 3 +- .../index.js.flow} | 0 discovery-client/package.json | 2 +- protocol-designer/src/labware-defs/actions.js | 2 +- shared-data/Makefile | 6 +- .../__snapshots__/pipettes.test.ts.snap | 5969 +++++++++++++++++ shared-data/js/constants.ts | 8 +- shared-data/js/index.ts | 1 + shared-data/js/types.ts | 6 +- shared-data/package.json | 2 +- 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.json | 1 + 30 files changed, 6467 insertions(+), 53 deletions(-) rename discovery-client/{lib/index.flow.js => flow-types/index.js.flow} (100%) create mode 100644 shared-data/js/__tests__/__snapshots__/pipettes.test.ts.snap 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 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/.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/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/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/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 58acfca009a..f956ebbb345 100644 --- a/discovery-client/package.json +++ b/discovery-client/package.json @@ -3,7 +3,7 @@ "version": "4.2.1", "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/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/Makefile b/shared-data/Makefile index 6c620e70678..0bebe9519b8 100644 --- a/shared-data/Makefile +++ b/shared-data/Makefile @@ -7,8 +7,8 @@ SHELL := bash BUILD_DIR := build # type definitions -typedefs := $(shell yarn -s shx find "lib/js/**/*.d.ts") -flow_out := $(patsubst lib/js/%.d.ts,flow-types/%.js.flow,$(typedefs)) +typedefs := $(shell yarn -s shx find "lib/**/*.d.ts") +flow_out := $(patsubst lib/%.d.ts,flow-types/%.js.flow,$(typedefs)) # Top level targets @@ -42,7 +42,7 @@ clean-js: .PHONY: flow-types flow-types: $(flow_out) -flow-types/%.js.flow: lib/js/%.d.ts +flow-types/%.js.flow: lib/%.d.ts yarn flowgen $< --add-flow-header --interface-records --no-inexact --output-file $@ # Python targets diff --git a/shared-data/js/__tests__/__snapshots__/pipettes.test.ts.snap b/shared-data/js/__tests__/__snapshots__/pipettes.test.ts.snap new file mode 100644 index 00000000000..759bfd6de3d --- /dev/null +++ b/shared-data/js/__tests__/__snapshots__/pipettes.test.ts.snap @@ -0,0 +1,5969 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`pipette data accessors getPipetteModelSpecs model p10_multi_v1 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": -1, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 2, + }, + "channels": 8, + "defaultAspirateFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 5, + "valuesByApiLevel": Object { + "2.0": 5, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 10, + "valuesByApiLevel": Object { + "2.0": 10, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_10ul/1", + "opentrons/opentrons_96_filtertiprack_10ul/1", + "opentrons/geb_96_tiprack_10ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P10 8-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -4, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 10, + "minVolume": 1, + "model": "p10_multi_v1", + "modelOffset": Array [ + 0, + 31.5, + -25.8, + ], + "name": "p10_multi", + "nozzleOffset": Array [ + 0, + 31.5, + 0.8, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.4, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 33, + }, + "tipOverlap": Object { + "default": 3.29, + "opentrons/eppendorf_96_tiprack_10ul_eptips/1": 1, + "opentrons/geb_96_tiprack_10ul/1": 6.2, + "opentrons/opentrons_96_filtertiprack_10ul/1": 3.29, + "opentrons/opentrons_96_tiprack_10ul/1": 3.29, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 1.893415617, + -1.1069, + 3.042593193, + ], + Array [ + 2.497849452, + -0.1888, + 1.30410391, + ], + Array [ + 5.649462387, + -0.0081, + 0.8528667891, + ], + Array [ + 12.74444519, + -0.0018, + 0.8170558891, + ], + ], + "dispense": Array [ + Array [ + 12.74444519, + 0, + 0.8058688085, + ], + ], + }, + Object { + "aspirate": Array [ + Array [ + 1.438649211, + 0.01931415115, + 0.691538317, + ], + Array [ + 1.836824579, + 0.03868955123, + 0.6636639129, + ], + Array [ + 2.960052684, + 0.00470371018, + 0.7260899411, + ], + Array [ + 4.487508789, + 0.005175245625, + 0.7246941713, + ], + Array [ + 10.59661421, + 0.001470408978, + 0.7413196584, + ], + ], + "dispense": Array [ + Array [ + 12.74444519, + 0, + 0.8058688085, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p10_multi_v1.3 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": -2.5, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 0.5, + }, + "channels": 8, + "defaultAspirateFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 5, + "valuesByApiLevel": Object { + "2.0": 5, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 10, + "valuesByApiLevel": Object { + "2.0": 10, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_10ul/1", + "opentrons/opentrons_96_filtertiprack_10ul/1", + "opentrons/geb_96_tiprack_10ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P10 8-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -5.5, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 10, + "minVolume": 1, + "model": "p10_multi_v1.3", + "modelOffset": Array [ + 0, + 31.5, + -25.8, + ], + "name": "p10_multi", + "nozzleOffset": Array [ + 0, + 31.5, + 0.8, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.4, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 33, + }, + "tipOverlap": Object { + "default": 3.29, + "opentrons/eppendorf_96_tiprack_10ul_eptips/1": 1, + "opentrons/geb_96_tiprack_10ul/1": 6.2, + "opentrons/opentrons_96_filtertiprack_10ul/1": 3.29, + "opentrons/opentrons_96_tiprack_10ul/1": 3.29, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 1.893415617, + -1.1069, + 3.042593193, + ], + Array [ + 2.497849452, + -0.1888, + 1.30410391, + ], + Array [ + 5.649462387, + -0.0081, + 0.8528667891, + ], + Array [ + 12.74444519, + -0.0018, + 0.8170558891, + ], + ], + "dispense": Array [ + Array [ + 12.74444519, + 0, + 0.8058688085, + ], + ], + }, + Object { + "aspirate": Array [ + Array [ + 1.438649211, + 0.01931415115, + 0.691538317, + ], + Array [ + 1.836824579, + 0.03868955123, + 0.6636639129, + ], + Array [ + 2.960052684, + 0.00470371018, + 0.7260899411, + ], + Array [ + 4.487508789, + 0.005175245625, + 0.7246941713, + ], + Array [ + 10.59661421, + 0.001470408978, + 0.7413196584, + ], + ], + "dispense": Array [ + Array [ + 12.74444519, + 0, + 0.8058688085, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p10_multi_v1.4 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": -1, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 2, + }, + "channels": 8, + "defaultAspirateFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 5, + "valuesByApiLevel": Object { + "2.0": 5, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 10, + "valuesByApiLevel": Object { + "2.0": 10, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_10ul/1", + "opentrons/opentrons_96_filtertiprack_10ul/1", + "opentrons/geb_96_tiprack_10ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P10 8-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -4.5, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 10, + "minVolume": 1, + "model": "p10_multi_v1.4", + "modelOffset": Array [ + 0, + 31.5, + -25.8, + ], + "name": "p10_multi", + "nozzleOffset": Array [ + 0, + 31.5, + 0.8, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.4, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 33, + }, + "tipOverlap": Object { + "default": 3.29, + "opentrons/eppendorf_96_tiprack_10ul_eptips/1": 1, + "opentrons/geb_96_tiprack_10ul/1": 6.2, + "opentrons/opentrons_96_filtertiprack_10ul/1": 3.29, + "opentrons/opentrons_96_tiprack_10ul/1": 3.29, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 1.893415617, + -1.1069, + 3.042593193, + ], + Array [ + 2.497849452, + -0.1888, + 1.30410391, + ], + Array [ + 5.649462387, + -0.0081, + 0.8528667891, + ], + Array [ + 12.74444519, + -0.0018, + 0.8170558891, + ], + ], + "dispense": Array [ + Array [ + 12.74444519, + 0, + 0.8058688085, + ], + ], + }, + Object { + "aspirate": Array [ + Array [ + 1.438649211, + 0.01931415115, + 0.691538317, + ], + Array [ + 1.836824579, + 0.03868955123, + 0.6636639129, + ], + Array [ + 2.960052684, + 0.00470371018, + 0.7260899411, + ], + Array [ + 4.487508789, + 0.005175245625, + 0.7246941713, + ], + Array [ + 10.59661421, + 0.001470408978, + 0.7413196584, + ], + ], + "dispense": Array [ + Array [ + 12.74444519, + 0, + 0.8058688085, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p10_multi_v1.5 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": -1, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 2, + }, + "channels": 8, + "defaultAspirateFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 5, + "valuesByApiLevel": Object { + "2.0": 5, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 10, + "valuesByApiLevel": Object { + "2.0": 10, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_10ul/1", + "opentrons/opentrons_96_filtertiprack_10ul/1", + "opentrons/geb_96_tiprack_10ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P10 8-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -4.5, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 10, + "minVolume": 1, + "model": "p10_multi_v1.5", + "modelOffset": Array [ + 0, + 31.5, + -25.8, + ], + "name": "p10_multi", + "nozzleOffset": Array [ + 0, + 31.5, + 0.8, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.55, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 3, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "quirks": Array [ + "dropTipShake", + "doubleDropTip", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 33, + }, + "tipOverlap": Object { + "default": 3.29, + "opentrons/eppendorf_96_tiprack_10ul_eptips/1": 1, + "opentrons/geb_96_tiprack_10ul/1": 6.2, + "opentrons/opentrons_96_filtertiprack_10ul/1": 3.29, + "opentrons/opentrons_96_tiprack_10ul/1": 3.29, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 1.774444444, + -0.1917910448, + 1.2026, + ], + Array [ + 2.151481481, + -0.0706286837, + 1.0125, + ], + Array [ + 2.898518519, + -0.04343083788, + 0.954, + ], + Array [ + 6.373333333, + -0.00905990194, + 0.8544, + ], + Array [ + 11.00259259, + -0.002325900358, + 0.8115, + ], + ], + "dispense": Array [ + Array [ + 12.74444519, + 0, + 0.8058688085, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p10_single_v1 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": -1, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 2, + }, + "channels": 1, + "defaultAspirateFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 5, + "valuesByApiLevel": Object { + "2.0": 5, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 10, + "valuesByApiLevel": Object { + "2.0": 10, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_10ul/1", + "opentrons/opentrons_96_filtertiprack_10ul/1", + "opentrons/geb_96_tiprack_10ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P10 Single-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -4.5, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 10, + "minVolume": 1, + "model": "p10_single_v1", + "modelOffset": Array [ + 0, + 0, + -13, + ], + "name": "p10_single", + "nozzleOffset": Array [ + 0, + 0, + 12, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.1, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.3, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 33, + }, + "tipOverlap": Object { + "default": 3.29, + "opentrons/eppendorf_96_tiprack_10ul_eptips/1": 1, + "opentrons/geb_96_tiprack_10ul/1": 6.2, + "opentrons/opentrons_96_filtertiprack_10ul/1": 3.29, + "opentrons/opentrons_96_tiprack_10ul/1": 3.29, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 1.8263, + -0.0958, + 1.088, + ], + Array [ + 2.5222, + -0.104, + 1.1031, + ], + Array [ + 3.2354, + -0.0447, + 0.9536, + ], + Array [ + 3.9984, + -0.012, + 0.8477, + ], + Array [ + 12.5135, + -0.0021, + 0.8079, + ], + ], + "dispense": Array [ + Array [ + 12.5135, + 0, + 0.7945, + ], + ], + }, + Object { + "aspirate": Array [ + Array [ + 1.438649211, + 0.01931415115, + 0.691538317, + ], + Array [ + 1.836824579, + 0.03868955123, + 0.6636639129, + ], + Array [ + 2.960052684, + 0.00470371018, + 0.7260899411, + ], + Array [ + 4.487508789, + 0.005175245625, + 0.7246941713, + ], + Array [ + 10.59661421, + 0.001470408978, + 0.7413196584, + ], + ], + "dispense": Array [ + Array [ + 12.5135, + 0, + 0.7945, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p10_single_v1.3 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": -2.5, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 0.5, + }, + "channels": 1, + "defaultAspirateFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 5, + "valuesByApiLevel": Object { + "2.0": 5, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 10, + "valuesByApiLevel": Object { + "2.0": 10, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_10ul/1", + "opentrons/opentrons_96_filtertiprack_10ul/1", + "opentrons/geb_96_tiprack_10ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P10 Single-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -6, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 10, + "minVolume": 1, + "model": "p10_single_v1.3", + "modelOffset": Array [ + 0, + 0, + -13, + ], + "name": "p10_single", + "nozzleOffset": Array [ + 0, + 0, + 12, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.1, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.3, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 33, + }, + "tipOverlap": Object { + "default": 3.29, + "opentrons/eppendorf_96_tiprack_10ul_eptips/1": 1, + "opentrons/geb_96_tiprack_10ul/1": 6.2, + "opentrons/opentrons_96_filtertiprack_10ul/1": 3.29, + "opentrons/opentrons_96_tiprack_10ul/1": 3.29, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 1.8263, + -0.0958, + 1.088, + ], + Array [ + 2.5222, + -0.104, + 1.1031, + ], + Array [ + 3.2354, + -0.0447, + 0.9536, + ], + Array [ + 3.9984, + -0.012, + 0.8477, + ], + Array [ + 12.5135, + -0.0021, + 0.8079, + ], + ], + "dispense": Array [ + Array [ + 12.5135, + 0, + 0.7945, + ], + ], + }, + Object { + "aspirate": Array [ + Array [ + 1.438649211, + 0.01931415115, + 0.691538317, + ], + Array [ + 1.836824579, + 0.03868955123, + 0.6636639129, + ], + Array [ + 2.960052684, + 0.00470371018, + 0.7260899411, + ], + Array [ + 4.487508789, + 0.005175245625, + 0.7246941713, + ], + Array [ + 10.59661421, + 0.001470408978, + 0.7413196584, + ], + ], + "dispense": Array [ + Array [ + 12.5135, + 0, + 0.7945, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p10_single_v1.4 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": -0.5, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 2.5, + }, + "channels": 1, + "defaultAspirateFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 5, + "valuesByApiLevel": Object { + "2.0": 5, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 10, + "valuesByApiLevel": Object { + "2.0": 10, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_10ul/1", + "opentrons/opentrons_96_filtertiprack_10ul/1", + "opentrons/geb_96_tiprack_10ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P10 Single-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -5.2, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 10, + "minVolume": 1, + "model": "p10_single_v1.4", + "modelOffset": Array [ + 0, + 0, + -13, + ], + "name": "p10_single", + "nozzleOffset": Array [ + 0, + 0, + 12, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.1, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.3, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 33, + }, + "tipOverlap": Object { + "default": 3.29, + "opentrons/eppendorf_96_tiprack_10ul_eptips/1": 1, + "opentrons/geb_96_tiprack_10ul/1": 6.2, + "opentrons/opentrons_96_filtertiprack_10ul/1": 3.29, + "opentrons/opentrons_96_tiprack_10ul/1": 3.29, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 1.8263, + -0.0958, + 1.088, + ], + Array [ + 2.5222, + -0.104, + 1.1031, + ], + Array [ + 3.2354, + -0.0447, + 0.9536, + ], + Array [ + 3.9984, + -0.012, + 0.8477, + ], + Array [ + 12.5135, + -0.0021, + 0.8079, + ], + ], + "dispense": Array [ + Array [ + 12.5135, + 0, + 0.7945, + ], + ], + }, + Object { + "aspirate": Array [ + Array [ + 1.438649211, + 0.01931415115, + 0.691538317, + ], + Array [ + 1.836824579, + 0.03868955123, + 0.6636639129, + ], + Array [ + 2.960052684, + 0.00470371018, + 0.7260899411, + ], + Array [ + 4.487508789, + 0.005175245625, + 0.7246941713, + ], + Array [ + 10.59661421, + 0.001470408978, + 0.7413196584, + ], + ], + "dispense": Array [ + Array [ + 12.5135, + 0, + 0.7945, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p10_single_v1.5 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": -0.5, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 2.5, + }, + "channels": 1, + "defaultAspirateFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 5, + "valuesByApiLevel": Object { + "2.0": 5, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 10, + "valuesByApiLevel": Object { + "2.0": 10, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_10ul/1", + "opentrons/opentrons_96_filtertiprack_10ul/1", + "opentrons/geb_96_tiprack_10ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P10 Single-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -5.2, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 10, + "minVolume": 1, + "model": "p10_single_v1.5", + "modelOffset": Array [ + 0, + 0, + -13, + ], + "name": "p10_single", + "nozzleOffset": Array [ + 0, + 0, + 12, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.1, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.3, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 33, + }, + "tipOverlap": Object { + "default": 3.29, + "opentrons/eppendorf_96_tiprack_10ul_eptips/1": 1, + "opentrons/geb_96_tiprack_10ul/1": 6.2, + "opentrons/opentrons_96_filtertiprack_10ul/1": 3.29, + "opentrons/opentrons_96_tiprack_10ul/1": 3.29, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 1.553425807, + -0.03427618068, + 0.83, + ], + Array [ + 1.934976526, + -0.007134812859, + 0.7878, + ], + Array [ + 2.689843897, + -0.007238069768, + 0.788, + ], + Array [ + 6.161165493, + 0.0004663523509, + 0.7673, + ], + Array [ + 10.7963169, + 0.0002200157553, + 0.7688, + ], + ], + "dispense": Array [ + Array [ + 12.5135, + 0, + 0.7945, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p50_multi_v1 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": 2, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 2.5, + }, + "channels": 8, + "defaultAspirateFlowRate": Object { + "max": 100, + "min": 0.001, + "value": 25, + "valuesByApiLevel": Object { + "2.0": 25, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 100, + "min": 0.001, + "value": 50, + "valuesByApiLevel": Object { + "2.0": 50, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_300ul/1", + "opentrons/opentrons_96_filtertiprack_200ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P50 8-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -3.5, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 50, + "minVolume": 5, + "model": "p50_multi_v1", + "modelOffset": Array [ + 0, + 31.5, + -25.8, + ], + "name": "p50_multi", + "nozzleOffset": Array [ + 0, + 31.5, + 0.8, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.6, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 51.7, + }, + "tipOverlap": Object { + "default": 7.47, + "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, + "opentrons/opentrons_96_tiprack_300ul/1": 7.47, + "opentrons/tipone_96_tiprack_200ul/1": 6.1, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 12.29687531, + -0.0049, + 3.134703694, + ], + Array [ + 50, + -0.0002, + 3.077116024, + ], + ], + "dispense": Array [ + Array [ + 50, + 0, + 3.06368702, + ], + ], + }, + Object { + "aspirate": Array [ + Array [ + 5.5768667, + 0.076142366, + 2.363797525, + ], + Array [ + 7.0999333, + 0.0338396036, + 2.599714392, + ], + Array [ + 11.5943825, + 0.0130432679, + 2.747366988, + ], + Array [ + 17.6461325, + 0.007010609879, + 2.817311933, + ], + Array [ + 50, + 0.002620115513, + 2.894787178, + ], + ], + "dispense": Array [ + Array [ + 50, + 0, + 3.06368702, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p50_multi_v1.3 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": 0.5, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 2, + }, + "channels": 8, + "defaultAspirateFlowRate": Object { + "max": 100, + "min": 0.001, + "value": 25, + "valuesByApiLevel": Object { + "2.0": 25, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 100, + "min": 0.001, + "value": 50, + "valuesByApiLevel": Object { + "2.0": 50, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_300ul/1", + "opentrons/opentrons_96_filtertiprack_200ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P50 8-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -5, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 50, + "minVolume": 5, + "model": "p50_multi_v1.3", + "modelOffset": Array [ + 0, + 31.5, + -25.8, + ], + "name": "p50_multi", + "nozzleOffset": Array [ + 0, + 31.5, + 0.8, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.6, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 51.7, + }, + "tipOverlap": Object { + "default": 7.47, + "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, + "opentrons/opentrons_96_tiprack_300ul/1": 7.47, + "opentrons/tipone_96_tiprack_200ul/1": 6.1, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 12.29687531, + -0.0049, + 3.134703694, + ], + Array [ + 50, + -0.0002, + 3.077116024, + ], + ], + "dispense": Array [ + Array [ + 50, + 0, + 3.06368702, + ], + ], + }, + Object { + "aspirate": Array [ + Array [ + 5.5768667, + 0.076142366, + 2.363797525, + ], + Array [ + 7.0999333, + 0.0338396036, + 2.599714392, + ], + Array [ + 11.5943825, + 0.0130432679, + 2.747366988, + ], + Array [ + 17.6461325, + 0.007010609879, + 2.817311933, + ], + Array [ + 50, + 0.002620115513, + 2.894787178, + ], + ], + "dispense": Array [ + Array [ + 50, + 0, + 3.06368702, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p50_multi_v1.4 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": 0.5, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 2, + }, + "channels": 8, + "defaultAspirateFlowRate": Object { + "max": 100, + "min": 0.001, + "value": 25, + "valuesByApiLevel": Object { + "2.0": 25, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 100, + "min": 0.001, + "value": 50, + "valuesByApiLevel": Object { + "2.0": 50, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_300ul/1", + "opentrons/opentrons_96_filtertiprack_200ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P50 8-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -4, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 50, + "minVolume": 5, + "model": "p50_multi_v1.4", + "modelOffset": Array [ + 0, + 31.5, + -25.8, + ], + "name": "p50_multi", + "nozzleOffset": Array [ + 0, + 31.5, + 0.8, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.6, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 51.7, + }, + "tipOverlap": Object { + "default": 7.47, + "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, + "opentrons/opentrons_96_tiprack_300ul/1": 7.47, + "opentrons/tipone_96_tiprack_200ul/1": 6.1, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 12.29687531, + -0.0049, + 3.134703694, + ], + Array [ + 50, + -0.0002, + 3.077116024, + ], + ], + "dispense": Array [ + Array [ + 50, + 0, + 3.06368702, + ], + ], + }, + Object { + "aspirate": Array [ + Array [ + 5.5768667, + 0.076142366, + 2.363797525, + ], + Array [ + 7.0999333, + 0.0338396036, + 2.599714392, + ], + Array [ + 11.5943825, + 0.0130432679, + 2.747366988, + ], + Array [ + 17.6461325, + 0.007010609879, + 2.817311933, + ], + Array [ + 50, + 0.002620115513, + 2.894787178, + ], + ], + "dispense": Array [ + Array [ + 50, + 0, + 3.06368702, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p50_multi_v1.5 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": 0.5, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 2, + }, + "channels": 8, + "defaultAspirateFlowRate": Object { + "max": 100, + "min": 0.001, + "value": 25, + "valuesByApiLevel": Object { + "2.0": 25, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 100, + "min": 0.001, + "value": 50, + "valuesByApiLevel": Object { + "2.0": 50, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_300ul/1", + "opentrons/opentrons_96_filtertiprack_200ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P50 8-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -4, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 50, + "minVolume": 5, + "model": "p50_multi_v1.5", + "modelOffset": Array [ + 0, + 31.5, + -25.8, + ], + "name": "p50_multi", + "nozzleOffset": Array [ + 0, + 31.5, + 0.8, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.8, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 3, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "quirks": Array [ + "dropTipShake", + "doubleDropTip", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 51.7, + }, + "tipOverlap": Object { + "default": 7.47, + "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, + "opentrons/opentrons_96_tiprack_300ul/1": 7.47, + "opentrons/tipone_96_tiprack_200ul/1": 6.1, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 6.190392157, + -0.01092438778, + 3.1628, + ], + Array [ + 7.639705882, + -0.02712575255, + 3.2631, + ], + Array [ + 10.69666667, + 0.0001007939816, + 3.0551, + ], + Array [ + 24.49343137, + 0.0003978066956, + 3.0519, + ], + Array [ + 50, + -0.00001501363238, + 3.062, + ], + ], + "dispense": Array [ + Array [ + 50, + 0, + 3.06368702, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p50_single_v1 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": 2, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 2.01, + }, + "channels": 1, + "defaultAspirateFlowRate": Object { + "max": 100, + "min": 0.001, + "value": 25, + "valuesByApiLevel": Object { + "2.0": 25, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 100, + "min": 0.001, + "value": 50, + "valuesByApiLevel": Object { + "2.0": 50, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_300ul/1", + "opentrons/opentrons_96_filtertiprack_200ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P50 Single-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -4.5, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 50, + "minVolume": 5, + "model": "p50_single_v1", + "modelOffset": Array [ + 0, + 0, + 0, + ], + "name": "p50_single", + "nozzleOffset": Array [ + 0, + 0, + 25, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.1, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.3, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 51.7, + }, + "tipOverlap": Object { + "default": 7.47, + "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, + "opentrons/opentrons_96_tiprack_300ul/1": 7.47, + "opentrons/tipone_96_tiprack_200ul/1": 6.1, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 11.79687499, + -0.0098, + 3.064988953, + ], + Array [ + 50, + -0.0004, + 2.954068131, + ], + ], + "dispense": Array [ + Array [ + 50, + 0, + 2.931601299, + ], + ], + }, + Object { + "aspirate": Array [ + Array [ + 5.538952382, + 0.04994568474, + 2.492829422, + ], + Array [ + 7.050333334, + 0.0335171238, + 2.583826438, + ], + Array [ + 11.5397619, + 0.01443549911, + 2.718358253, + ], + Array [ + 17.55071427, + 0.006684226987, + 2.807806088, + ], + Array [ + 50, + 0.001789563193, + 2.893710933, + ], + ], + "dispense": Array [ + Array [ + 50, + 0, + 2.931601299, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p50_single_v1.3 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": 0.5, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 2, + }, + "channels": 1, + "defaultAspirateFlowRate": Object { + "max": 100, + "min": 0.001, + "value": 25, + "valuesByApiLevel": Object { + "2.0": 25, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 100, + "min": 0.001, + "value": 50, + "valuesByApiLevel": Object { + "2.0": 50, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_300ul/1", + "opentrons/opentrons_96_filtertiprack_200ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P50 Single-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -6, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 50, + "minVolume": 5, + "model": "p50_single_v1.3", + "modelOffset": Array [ + 0, + 0, + 0, + ], + "name": "p50_single", + "nozzleOffset": Array [ + 0, + 0, + 25, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.1, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.3, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 51.7, + }, + "tipOverlap": Object { + "default": 7.47, + "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, + "opentrons/opentrons_96_tiprack_300ul/1": 7.47, + "opentrons/tipone_96_tiprack_200ul/1": 6.1, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 11.79687499, + -0.0098, + 3.064988953, + ], + Array [ + 50, + -0.0004, + 2.954068131, + ], + ], + "dispense": Array [ + Array [ + 50, + 0, + 2.931601299, + ], + ], + }, + Object { + "aspirate": Array [ + Array [ + 5.538952382, + 0.04994568474, + 2.492829422, + ], + Array [ + 7.050333334, + 0.0335171238, + 2.583826438, + ], + Array [ + 11.5397619, + 0.01443549911, + 2.718358253, + ], + Array [ + 17.55071427, + 0.006684226987, + 2.807806088, + ], + Array [ + 50, + 0.001789563193, + 2.893710933, + ], + ], + "dispense": Array [ + Array [ + 50, + 0, + 2.931601299, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p50_single_v1.4 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": 0.5, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 2, + }, + "channels": 1, + "defaultAspirateFlowRate": Object { + "max": 100, + "min": 0.001, + "value": 25, + "valuesByApiLevel": Object { + "2.0": 25, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 100, + "min": 0.001, + "value": 50, + "valuesByApiLevel": Object { + "2.0": 50, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_300ul/1", + "opentrons/opentrons_96_filtertiprack_200ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P50 Single-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -5, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 50, + "minVolume": 5, + "model": "p50_single_v1.4", + "modelOffset": Array [ + 0, + 0, + 0, + ], + "name": "p50_single", + "nozzleOffset": Array [ + 0, + 0, + 25, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.1, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.3, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 51.7, + }, + "tipOverlap": Object { + "default": 7.47, + "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, + "opentrons/opentrons_96_tiprack_300ul/1": 7.47, + "opentrons/tipone_96_tiprack_200ul/1": 6.1, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 11.79687499, + -0.0098, + 3.064988953, + ], + Array [ + 50, + -0.0004, + 2.954068131, + ], + ], + "dispense": Array [ + Array [ + 50, + 0, + 2.931601299, + ], + ], + }, + Object { + "aspirate": Array [ + Array [ + 5.538952382, + 0.04994568474, + 2.492829422, + ], + Array [ + 7.050333334, + 0.0335171238, + 2.583826438, + ], + Array [ + 11.5397619, + 0.01443549911, + 2.718358253, + ], + Array [ + 17.55071427, + 0.006684226987, + 2.807806088, + ], + Array [ + 50, + 0.001789563193, + 2.893710933, + ], + ], + "dispense": Array [ + Array [ + 50, + 0, + 2.931601299, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p300_multi_v1 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": 3, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 3.5, + }, + "channels": 8, + "defaultAspirateFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 150, + "valuesByApiLevel": Object { + "2.0": 150, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 300, + "valuesByApiLevel": Object { + "2.0": 300, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_300ul/1", + "opentrons/opentrons_96_filtertiprack_200ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P300 8-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -2, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 300, + "minVolume": 30, + "model": "p300_multi_v1", + "modelOffset": Array [ + 0, + 31.5, + -25.8, + ], + "name": "p300_multi", + "nozzleOffset": Array [ + 0, + 31.5, + 0.8, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.6, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 51.7, + }, + "tipOverlap": Object { + "default": 7.47, + "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, + "opentrons/opentrons_96_tiprack_300ul/1": 7.47, + "opentrons/tipone_96_tiprack_200ul/1": 6.1, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 57.25698968, + 0.017, + 18.132, + ], + Array [ + 309.2612689, + 0.001, + 19.03, + ], + ], + "dispense": Array [ + Array [ + 309.2612689, + 0, + 19.29389273, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p300_multi_v1.3 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": 1.5, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 3.5, + }, + "channels": 8, + "defaultAspirateFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 150, + "valuesByApiLevel": Object { + "2.0": 150, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 300, + "valuesByApiLevel": Object { + "2.0": 300, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_300ul/1", + "opentrons/opentrons_96_filtertiprack_200ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P300 8-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -3.5, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 300, + "minVolume": 30, + "model": "p300_multi_v1.3", + "modelOffset": Array [ + 0, + 31.5, + -25.8, + ], + "name": "p300_multi", + "nozzleOffset": Array [ + 0, + 31.5, + 0.8, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.6, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 51.7, + }, + "tipOverlap": Object { + "default": 7.47, + "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, + "opentrons/opentrons_96_tiprack_300ul/1": 7.47, + "opentrons/tipone_96_tiprack_200ul/1": 6.1, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 57.25698968, + 0.017, + 18.132, + ], + Array [ + 309.2612689, + 0.001, + 19.03, + ], + ], + "dispense": Array [ + Array [ + 309.2612689, + 0, + 19.29389273, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p300_multi_v1.4 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": 1.5, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 3.5, + }, + "channels": 8, + "defaultAspirateFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 150, + "valuesByApiLevel": Object { + "2.0": 150, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 300, + "valuesByApiLevel": Object { + "2.0": 300, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_300ul/1", + "opentrons/opentrons_96_filtertiprack_200ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P300 8-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -3.5, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm", + "value": 5, + }, + "maxVolume": 300, + "minVolume": 30, + "model": "p300_multi_v1.4", + "modelOffset": Array [ + 0, + 31.5, + -25.8, + ], + "name": "p300_multi", + "nozzleOffset": Array [ + 0, + 31.5, + 0.8, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.6, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 51.7, + }, + "tipOverlap": Object { + "default": 7.47, + "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, + "opentrons/opentrons_96_tiprack_300ul/1": 7.47, + "opentrons/tipone_96_tiprack_200ul/1": 6.1, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 57.25698968, + 0.017, + 18.132, + ], + Array [ + 309.2612689, + 0.001, + 19.03, + ], + ], + "dispense": Array [ + Array [ + 309.2612689, + 0, + 19.29389273, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p300_multi_v1.5 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": 1.5, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 3.5, + }, + "channels": 8, + "defaultAspirateFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 150, + "valuesByApiLevel": Object { + "2.0": 150, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 300, + "valuesByApiLevel": Object { + "2.0": 300, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_300ul/1", + "opentrons/opentrons_96_filtertiprack_200ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P300 8-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -3.5, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm", + "value": 5, + }, + "maxVolume": 300, + "minVolume": 30, + "model": "p300_multi_v1.5", + "modelOffset": Array [ + 0, + 31.5, + -25.8, + ], + "name": "p300_multi", + "nozzleOffset": Array [ + 0, + 31.5, + 0.8, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.9, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 3, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "quirks": Array [ + "dropTipShake", + "doubleDropTip", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 51.7, + }, + "tipOverlap": Object { + "default": 7.47, + "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, + "opentrons/opentrons_96_tiprack_300ul/1": 7.47, + "opentrons/tipone_96_tiprack_200ul/1": 6.1, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 57.25698968, + 0.017, + 18.132, + ], + Array [ + 309.2612689, + 0.001, + 19.03, + ], + ], + "dispense": Array [ + Array [ + 309.2612689, + 0, + 19.29389273, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p300_single_v1 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": 0, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 1.5, + }, + "channels": 1, + "defaultAspirateFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 150, + "valuesByApiLevel": Object { + "2.0": 150, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 300, + "valuesByApiLevel": Object { + "2.0": 300, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_300ul/1", + "opentrons/opentrons_96_filtertiprack_200ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P300 Single-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -4, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 300, + "minVolume": 30, + "model": "p300_single_v1", + "modelOffset": Array [ + 0, + 0, + 0, + ], + "name": "p300_single", + "nozzleOffset": Array [ + 0, + 0, + 25, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.1, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.3, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 51.7, + }, + "tipOverlap": Object { + "default": 7.47, + "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, + "opentrons/opentrons_96_tiprack_300ul/1": 7.47, + "opentrons/tipone_96_tiprack_200ul/1": 6.1, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 36.19844973, + 0.043, + 16.548, + ], + Array [ + 54.98518519, + 0.012, + 17.658, + ], + Array [ + 73.90077516, + 0.008, + 17.902, + ], + Array [ + 111.8437953, + 0.004, + 18.153, + ], + Array [ + 302.3895337, + 0.001, + 18.23, + ], + ], + "dispense": Array [ + Array [ + 302.3895337, + 0, + 18.83156277, + ], + ], + }, + Object { + "aspirate": Array [ + Array [ + 53.958, + 0.0252, + 16.6268, + ], + Array [ + 73.0217, + 0.0141, + 17.2234, + ], + Array [ + 82.6834, + 0.0123, + 17.3586, + ], + Array [ + 120.7877, + 0.0055, + 17.9214, + ], + Array [ + 197.3909, + 0.0028, + 18.2415, + ], + Array [ + 300, + 0.0014, + 18.5235, + ], + ], + "dispense": Array [ + Array [ + 302.3895337, + 0, + 18.83156277, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p300_single_v1.3 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": -1.5, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 1.5, + }, + "channels": 1, + "defaultAspirateFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 150, + "valuesByApiLevel": Object { + "2.0": 150, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 300, + "valuesByApiLevel": Object { + "2.0": 300, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_300ul/1", + "opentrons/opentrons_96_filtertiprack_200ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P300 Single-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -5.5, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 300, + "minVolume": 30, + "model": "p300_single_v1.3", + "modelOffset": Array [ + 0, + 0, + 0, + ], + "name": "p300_single", + "nozzleOffset": Array [ + 0, + 0, + 25, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.1, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.3, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 51.7, + }, + "tipOverlap": Object { + "default": 7.47, + "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, + "opentrons/opentrons_96_tiprack_300ul/1": 7.47, + "opentrons/tipone_96_tiprack_200ul/1": 6.1, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 36.19844973, + 0.043, + 16.548, + ], + Array [ + 54.98518519, + 0.012, + 17.658, + ], + Array [ + 73.90077516, + 0.008, + 17.902, + ], + Array [ + 111.8437953, + 0.004, + 18.153, + ], + Array [ + 302.3895337, + 0.001, + 18.23, + ], + ], + "dispense": Array [ + Array [ + 302.3895337, + 0, + 18.83156277, + ], + ], + }, + Object { + "aspirate": Array [ + Array [ + 53.958, + 0.0252, + 16.6268, + ], + Array [ + 73.0217, + 0.0141, + 17.2234, + ], + Array [ + 82.6834, + 0.0123, + 17.3586, + ], + Array [ + 120.7877, + 0.0055, + 17.9214, + ], + Array [ + 197.3909, + 0.0028, + 18.2415, + ], + Array [ + 300, + 0.0014, + 18.5235, + ], + ], + "dispense": Array [ + Array [ + 302.3895337, + 0, + 18.83156277, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p300_single_v1.4 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": 0, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 3, + }, + "channels": 1, + "defaultAspirateFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 150, + "valuesByApiLevel": Object { + "2.0": 150, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 300, + "valuesByApiLevel": Object { + "2.0": 300, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_300ul/1", + "opentrons/opentrons_96_filtertiprack_200ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P300 Single-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -4.5, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 300, + "minVolume": 30, + "model": "p300_single_v1.4", + "modelOffset": Array [ + 0, + 0, + 0, + ], + "name": "p300_single", + "nozzleOffset": Array [ + 0, + 0, + 25, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.1, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.3, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 51.7, + }, + "tipOverlap": Object { + "default": 7.47, + "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, + "opentrons/opentrons_96_tiprack_300ul/1": 7.47, + "opentrons/tipone_96_tiprack_200ul/1": 6.1, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 36.19844973, + 0.043, + 16.548, + ], + Array [ + 54.98518519, + 0.012, + 17.658, + ], + Array [ + 73.90077516, + 0.008, + 17.902, + ], + Array [ + 111.8437953, + 0.004, + 18.153, + ], + Array [ + 302.3895337, + 0.001, + 18.23, + ], + ], + "dispense": Array [ + Array [ + 302.3895337, + 0, + 18.83156277, + ], + ], + }, + Object { + "aspirate": Array [ + Array [ + 53.958, + 0.0252, + 16.6268, + ], + Array [ + 73.0217, + 0.0141, + 17.2234, + ], + Array [ + 82.6834, + 0.0123, + 17.3586, + ], + Array [ + 120.7877, + 0.0055, + 17.9214, + ], + Array [ + 197.3909, + 0.0028, + 18.2415, + ], + Array [ + 300, + 0.0014, + 18.5235, + ], + ], + "dispense": Array [ + Array [ + 302.3895337, + 0, + 18.83156277, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p300_single_v1.5 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": 0, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 3, + }, + "channels": 1, + "defaultAspirateFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 150, + "valuesByApiLevel": Object { + "2.0": 150, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 300, + "valuesByApiLevel": Object { + "2.0": 300, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_300ul/1", + "opentrons/opentrons_96_filtertiprack_200ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P300 Single-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -4.5, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 300, + "minVolume": 30, + "model": "p300_single_v1.5", + "modelOffset": Array [ + 0, + 0, + 0, + ], + "name": "p300_single", + "nozzleOffset": Array [ + 0, + 0, + 25, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.1, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 10, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.3, + }, + "quirks": Array [ + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 51.7, + }, + "tipOverlap": Object { + "default": 7.47, + "opentrons/opentrons_96_filtertiprack_200ul/1": 7.47, + "opentrons/opentrons_96_tiprack_300ul/1": 7.47, + "opentrons/tipone_96_tiprack_200ul/1": 6.1, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 35.11266179, + 0.03721315938, + 16.2497, + ], + Array [ + 44.37338506, + 0.02084320206, + 16.8245, + ], + Array [ + 63.12001468, + 0.01519931266, + 17.0749, + ], + Array [ + 148.3020792, + 0.005910516464, + 17.6612, + ], + Array [ + 224.5387262, + 0.00227975152, + 18.1997, + ], + Array [ + 301.049323, + 0.001359578667, + 18.4063, + ], + ], + "dispense": Array [ + Array [ + 302.3895337, + 0, + 18.83156277, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p1000_single_v1 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": 1, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 3, + }, + "channels": 1, + "defaultAspirateFlowRate": Object { + "max": 2000, + "min": 50, + "value": 500, + "valuesByApiLevel": Object { + "2.0": 500, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 2000, + "min": 50, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_1000ul/1", + "opentrons/opentrons_96_filtertiprack_1000ul/1", + "opentrons/geb_96_tiprack_1000ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P1000 Single-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -2.2, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 1000, + "minVolume": 100, + "model": "p1000_single_v1", + "modelOffset": Array [ + 0, + 0, + 20, + ], + "name": "p1000_single", + "nozzleOffset": Array [ + 0, + 0, + 45, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.1, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 15, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "quirks": Array [ + "pickupTipShake", + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 76.7, + }, + "tipOverlap": Object { + "default": 7.95, + "opentrons/eppendorf_96_tiprack_1000ul_eptips/1": 0, + "opentrons/geb_96_tiprack_1000ul/1": 11.2, + "opentrons/opentrons_96_filtertiprack_1000ul/1": 7.95, + "opentrons/opentrons_96_tiprack_1000ul/1": 7.95, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 148.9157, + 0.0213, + 56.3986, + ], + Array [ + 210.8237, + 0.0108, + 57.9568, + ], + Array [ + 241.2405, + 0.0025, + 59.717, + ], + Array [ + 365.2719, + 0.0046, + 59.2043, + ], + Array [ + 614.4871, + 0.0023, + 60.0431, + ], + Array [ + 1000, + 0.001, + 60.8209, + ], + ], + "dispense": Array [ + Array [ + 1000, + 0, + 61.3275, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p1000_single_v1.3 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": 0.5, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 2.5, + }, + "channels": 1, + "defaultAspirateFlowRate": Object { + "max": 2000, + "min": 50, + "value": 500, + "valuesByApiLevel": Object { + "2.0": 500, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 2000, + "min": 50, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_1000ul/1", + "opentrons/opentrons_96_filtertiprack_1000ul/1", + "opentrons/geb_96_tiprack_1000ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P1000 Single-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -4, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.7, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 1000, + "minVolume": 100, + "model": "p1000_single_v1.3", + "modelOffset": Array [ + 0, + 0, + 20, + ], + "name": "p1000_single", + "nozzleOffset": Array [ + 0, + 0, + 45, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.1, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 15, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "quirks": Array [ + "pickupTipShake", + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 76.7, + }, + "tipOverlap": Object { + "default": 7.95, + "opentrons/eppendorf_96_tiprack_1000ul_eptips/1": 0, + "opentrons/geb_96_tiprack_1000ul/1": 11.2, + "opentrons/opentrons_96_filtertiprack_1000ul/1": 7.95, + "opentrons/opentrons_96_tiprack_1000ul/1": 7.95, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 148.9157, + 0.0213, + 56.3986, + ], + Array [ + 210.8237, + 0.0108, + 57.9568, + ], + Array [ + 241.2405, + 0.0025, + 59.717, + ], + Array [ + 365.2719, + 0.0046, + 59.2043, + ], + Array [ + 614.4871, + 0.0023, + 60.0431, + ], + Array [ + 1000, + 0.001, + 60.8209, + ], + ], + "dispense": Array [ + Array [ + 1000, + 0, + 61.3275, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p1000_single_v1.4 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": 0.5, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 2.5, + }, + "channels": 1, + "defaultAspirateFlowRate": Object { + "max": 2000, + "min": 50, + "value": 500, + "valuesByApiLevel": Object { + "2.0": 500, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 2000, + "min": 50, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_1000ul/1", + "opentrons/opentrons_96_filtertiprack_1000ul/1", + "opentrons/geb_96_tiprack_1000ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P1000 Single-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -4, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.7, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 1000, + "minVolume": 100, + "model": "p1000_single_v1.4", + "modelOffset": Array [ + 0, + 0, + 20, + ], + "name": "p1000_single", + "nozzleOffset": Array [ + 0, + 0, + 45, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.1, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 15, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "quirks": Array [ + "pickupTipShake", + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 76.7, + }, + "tipOverlap": Object { + "default": 7.95, + "opentrons/eppendorf_96_tiprack_1000ul_eptips/1": 0, + "opentrons/geb_96_tiprack_1000ul/1": 11.2, + "opentrons/opentrons_96_filtertiprack_1000ul/1": 7.95, + "opentrons/opentrons_96_tiprack_1000ul/1": 7.95, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 148.9157, + 0.0213, + 56.3986, + ], + Array [ + 210.8237, + 0.0108, + 57.9568, + ], + Array [ + 241.2405, + 0.0025, + 59.717, + ], + Array [ + 365.2719, + 0.0046, + 59.2043, + ], + Array [ + 614.4871, + 0.0023, + 60.0431, + ], + Array [ + 1000, + 0.001, + 60.8209, + ], + ], + "dispense": Array [ + Array [ + 1000, + 0, + 61.3275, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteModelSpecs model p1000_single_v1.5 snapshot 1`] = ` +Object { + "blowout": Object { + "max": 10, + "min": -4, + "type": "float", + "units": "mm", + "value": 0.5, + }, + "bottom": Object { + "max": 19, + "min": -2, + "type": "float", + "units": "mm", + "value": 2.5, + }, + "channels": 1, + "defaultAspirateFlowRate": Object { + "max": 2000, + "min": 50, + "value": 500, + "valuesByApiLevel": Object { + "2.0": 500, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 2000, + "min": 50, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_1000ul/1", + "opentrons/opentrons_96_filtertiprack_1000ul/1", + "opentrons/geb_96_tiprack_1000ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P1000 Single-Channel GEN1", + "dropTip": Object { + "max": 2, + "min": -6, + "type": "float", + "units": "mm", + "value": -4, + }, + "dropTipCurrent": Object { + "max": 0.8, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.7, + }, + "dropTipSpeed": Object { + "max": 30, + "min": 0.001, + "type": "float", + "units": "mm/sec", + "value": 5, + }, + "maxVolume": 1000, + "minVolume": 100, + "model": "p1000_single_v1.5", + "modelOffset": Array [ + 0, + 0, + 20, + ], + "name": "p1000_single", + "nozzleOffset": Array [ + 0, + 0, + 45, + ], + "pickUpCurrent": Object { + "max": 2, + "min": 0.05, + "type": "float", + "units": "amps", + "value": 0.15, + }, + "pickUpDistance": Object { + "max": 30, + "min": 1, + "type": "float", + "units": "mm", + "value": 15, + }, + "pickUpIncrement": Object { + "max": 10, + "min": 0, + "type": "float", + "units": "mm", + "value": 1, + }, + "pickUpPresses": Object { + "max": 10, + "min": 0, + "type": "int", + "units": "presses", + "value": 3, + }, + "pickUpSpeed": Object { + "max": 100, + "min": 1, + "type": "float", + "units": "mm/s", + "value": 30, + }, + "plungerCurrent": Object { + "max": 0.5, + "min": 0.1, + "type": "float", + "units": "amps", + "value": 0.5, + }, + "quirks": Array [ + "pickupTipShake", + "dropTipShake", + ], + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, + "tipLength": Object { + "max": 100, + "min": 0, + "type": "float", + "units": "mm", + "value": 76.7, + }, + "tipOverlap": Object { + "default": 7.95, + "opentrons/eppendorf_96_tiprack_1000ul_eptips/1": 0, + "opentrons/geb_96_tiprack_1000ul/1": 11.2, + "opentrons/opentrons_96_filtertiprack_1000ul/1": 7.95, + "opentrons/opentrons_96_tiprack_1000ul/1": 7.95, + }, + "top": Object { + "max": 19.5, + "min": 5, + "type": "float", + "units": "mm", + "value": 19.5, + }, + "ulPerMm": Array [ + Object { + "aspirate": Array [ + Array [ + 148.9157, + 0.0213, + 56.3986, + ], + Array [ + 210.8237, + 0.0108, + 57.9568, + ], + Array [ + 241.2405, + 0.0025, + 59.717, + ], + Array [ + 365.2719, + 0.0046, + 59.2043, + ], + Array [ + 614.4871, + 0.0023, + 60.0431, + ], + Array [ + 1000, + 0.001, + 60.8209, + ], + ], + "dispense": Array [ + Array [ + 1000, + 0, + 61.3275, + ], + ], + }, + ], +} +`; + +exports[`pipette data accessors getPipetteNameSpecs name p10_multi snapshot 1`] = ` +Object { + "channels": 8, + "defaultAspirateFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 5, + "valuesByApiLevel": Object { + "2.0": 5, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 10, + "valuesByApiLevel": Object { + "2.0": 10, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_10ul/1", + "opentrons/opentrons_96_filtertiprack_10ul/1", + "opentrons/geb_96_tiprack_10ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P10 8-Channel GEN1", + "maxVolume": 10, + "minVolume": 1, + "name": "p10_multi", + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, +} +`; + +exports[`pipette data accessors getPipetteNameSpecs name p10_single snapshot 1`] = ` +Object { + "channels": 1, + "defaultAspirateFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 5, + "valuesByApiLevel": Object { + "2.0": 5, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 50, + "min": 0.001, + "value": 10, + "valuesByApiLevel": Object { + "2.0": 10, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_10ul/1", + "opentrons/opentrons_96_filtertiprack_10ul/1", + "opentrons/geb_96_tiprack_10ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P10 Single-Channel GEN1", + "maxVolume": 10, + "minVolume": 1, + "name": "p10_single", + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, +} +`; + +exports[`pipette data accessors getPipetteNameSpecs name p50_multi snapshot 1`] = ` +Object { + "channels": 8, + "defaultAspirateFlowRate": Object { + "max": 100, + "min": 0.001, + "value": 25, + "valuesByApiLevel": Object { + "2.0": 25, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 100, + "min": 0.001, + "value": 50, + "valuesByApiLevel": Object { + "2.0": 50, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_300ul/1", + "opentrons/opentrons_96_filtertiprack_200ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P50 8-Channel GEN1", + "maxVolume": 50, + "minVolume": 5, + "name": "p50_multi", + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, +} +`; + +exports[`pipette data accessors getPipetteNameSpecs name p50_single snapshot 1`] = ` +Object { + "channels": 1, + "defaultAspirateFlowRate": Object { + "max": 100, + "min": 0.001, + "value": 25, + "valuesByApiLevel": Object { + "2.0": 25, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 100, + "min": 0.001, + "value": 50, + "valuesByApiLevel": Object { + "2.0": 50, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_300ul/1", + "opentrons/opentrons_96_filtertiprack_200ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P50 Single-Channel GEN1", + "maxVolume": 50, + "minVolume": 5, + "name": "p50_single", + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, +} +`; + +exports[`pipette data accessors getPipetteNameSpecs name p300_multi snapshot 1`] = ` +Object { + "channels": 8, + "defaultAspirateFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 150, + "valuesByApiLevel": Object { + "2.0": 150, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 300, + "valuesByApiLevel": Object { + "2.0": 300, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_300ul/1", + "opentrons/opentrons_96_filtertiprack_200ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P300 8-Channel GEN1", + "maxVolume": 300, + "minVolume": 30, + "name": "p300_multi", + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, +} +`; + +exports[`pipette data accessors getPipetteNameSpecs name p300_single snapshot 1`] = ` +Object { + "channels": 1, + "defaultAspirateFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 150, + "valuesByApiLevel": Object { + "2.0": 150, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 600, + "min": 0.001, + "value": 300, + "valuesByApiLevel": Object { + "2.0": 300, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_300ul/1", + "opentrons/opentrons_96_filtertiprack_200ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P300 Single-Channel GEN1", + "maxVolume": 300, + "minVolume": 30, + "name": "p300_single", + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, +} +`; + +exports[`pipette data accessors getPipetteNameSpecs name p1000_single snapshot 1`] = ` +Object { + "channels": 1, + "defaultAspirateFlowRate": Object { + "max": 2000, + "min": 50, + "value": 500, + "valuesByApiLevel": Object { + "2.0": 500, + }, + }, + "defaultBlowOutFlowRate": Object { + "max": 1000, + "min": 5, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultDispenseFlowRate": Object { + "max": 2000, + "min": 50, + "value": 1000, + "valuesByApiLevel": Object { + "2.0": 1000, + }, + }, + "defaultTipracks": Array [ + "opentrons/opentrons_96_tiprack_1000ul/1", + "opentrons/opentrons_96_filtertiprack_1000ul/1", + "opentrons/geb_96_tiprack_1000ul/1", + ], + "displayCategory": "GEN1", + "displayName": "P1000 Single-Channel GEN1", + "maxVolume": 1000, + "minVolume": 100, + "name": "p1000_single", + "smoothieConfigs": Object { + "homePosition": 220, + "stepsPerMM": 768, + "travelDistance": 30, + }, +} +`; diff --git a/shared-data/js/constants.ts b/shared-data/js/constants.ts index 0b5642f2d29..7d09c1f6a72 100644 --- a/shared-data/js/constants.ts +++ b/shared-data/js/constants.ts @@ -1,5 +1,3 @@ -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 @@ -35,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' @@ -51,7 +53,7 @@ export const TEMPERATURE_MODULE_MODELS = [ export const THERMOCYCLER_MODULE_MODELS = [THERMOCYCLER_MODULE_V1] -export const MODULE_MODELS: ModuleModel[] = [ +export const MODULE_MODELS = [ ...MAGNETIC_MODULE_MODELS, ...TEMPERATURE_MODULE_MODELS, ...THERMOCYCLER_MODULE_MODELS, diff --git a/shared-data/js/index.ts b/shared-data/js/index.ts index 2185791eb58..7fab31cfaf4 100644 --- a/shared-data/js/index.ts +++ b/shared-data/js/index.ts @@ -5,3 +5,4 @@ export * from './pipettes' export * from './types' export * from './labwareTools' export * from './modules' +export * from '../protocol' diff --git a/shared-data/js/types.ts b/shared-data/js/types.ts index afbf7da9177..4d3b27bb89b 100644 --- a/shared-data/js/types.ts +++ b/shared-data/js/types.ts @@ -1,4 +1,4 @@ -import type { +import { MAGDECK, TEMPDECK, THERMOCYCLER, @@ -12,6 +12,8 @@ import type { THERMOCYCLER_MODULE_TYPE, GEN1, GEN2, + LEFT, + RIGHT, } from './constants' // TODO Ian 2019-06-04 split this out into eg ../labware/flowTypes/labwareV1.js @@ -284,6 +286,8 @@ export type PipetteChannels = 1 | 8 export type PipetteDisplayCategory = typeof GEN1 | typeof GEN2 +export type PipetteMount = typeof LEFT | typeof RIGHT + export interface FlowRateSpec { value: number min: number diff --git a/shared-data/package.json b/shared-data/package.json index c4036cafc37..dfef90c6fc6 100755 --- a/shared-data/package.json +++ b/shared-data/package.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "source": "js/index.ts", "types": "lib/js/index.d.ts", - "flow:main": "flow-types/index.js.flow", + "flow:main": "flow-types/js/index.js.flow", "dependencies": { "ajv": "6.10.2", "lodash": "4.17.15" 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..ec8f446a40b --- /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 | undefined + 'flow-rate'?: number | null | undefined +} + +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 | undefined + } + } + | { + 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.json b/shared-data/tsconfig.json index f9886424e52..9e6f118e4a1 100644 --- a/shared-data/tsconfig.json +++ b/shared-data/tsconfig.json @@ -9,6 +9,7 @@ "include": [ "typings", "js", + "protocol", "deck/**/*.json", "labware/**/*.json", "module/**/*.json", From 66b6cf317c6fee535bd856a5c3696a2169fd7de9 Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Tue, 27 Apr 2021 20:45:47 -0400 Subject: [PATCH 07/16] fixup: add shared-data flow types generated from TS --- .../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 | 49 ++++ .../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 | 42 +++ shared-data/flow-types/js/pipettes.js.flow | 20 ++ shared-data/flow-types/js/schema.js.flow | 97 +++++++ shared-data/flow-types/js/types.js.flow | 263 ++++++++++++++++++ .../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 ++ 44 files changed, 1775 insertions(+) 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 create mode 100644 shared-data/flow-types/js/types.js.flow 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 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..82d8afa5155 --- /dev/null +++ b/shared-data/flow-types/js/constants.js.flow @@ -0,0 +1,49 @@ +/** + * 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" +)[]; 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..c776cffd80a --- /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 | void, +|}; +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..6f854307220 --- /dev/null +++ b/shared-data/flow-types/js/modules.js.flow @@ -0,0 +1,42 @@ +/** + * 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: { + [key: string]: { [key: string]: { [key: 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..2a17f292c6e --- /dev/null +++ b/shared-data/flow-types/js/pipettes.js.flow @@ -0,0 +1,20 @@ +/** + * Flowtype definitions for pipettes + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +import type { PipetteNameSpecs, PipetteModelSpecs } from "./types"; +declare type SortableProps = "maxVolume" | "channels"; +declare export function getPipetteNameSpecs( + name: string +): PipetteNameSpecs | null; +declare export function getPipetteModelSpecs( + model: string +): PipetteModelSpecs | null | void; +declare export function getAllPipetteNames( + ...sortBy: SortableProps[] +): string[]; +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/flow-types/js/types.js.flow b/shared-data/flow-types/js/types.js.flow new file mode 100644 index 00000000000..6ba06efc521 --- /dev/null +++ b/shared-data/flow-types/js/types.js.flow @@ -0,0 +1,263 @@ +/** + * Flowtype definitions for types + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.13.0 + * @flow + */ + +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"; +export type WellDefinition = {| + diameter?: number, + depth?: number, + height: number, + length: number, + width: number, + x: number, + y: number, + z: number, + "total-liquid-volume": number, +|}; +export type LabwareDefinition1 = {| + metadata: { + name: string, + format: string, + deprecated?: boolean, + displayName?: string, + displayCategory?: string, + isValidSource?: boolean, + isTiprack?: boolean, + tipVolume?: number, + }, + ordering: string[][], + wells: { [key: string]: WellDefinition }, +|}; +export type LabwareDisplayCategory = + | "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?: string[], +|}; +export type LabwareDimensions = {| + xDimension: number, + yDimension: number, + zDimension: number, +|}; +export type LabwareOffset = {| + x: number, + y: number, + z: number, +|}; +export type LabwareParameters = {| + loadName: string, + format: string, + isTiprack: boolean, + tipLength?: number, + isMagneticModuleCompatible: boolean, + magneticModuleEngageHeight?: number, + quirks?: string[], +|}; +export type LabwareBrand = {| + brand: string, + brandId?: string[], + links?: string[], +|}; +export type LabwareWellShapeProperties = + | { + shape: "circular", + diameter: number, + } + | { + shape: "rectangular", + xDimension: number, + yDimension: number, + }; +export type LabwareWellProperties = LabwareWellShapeProperties & { + depth: number, + totalLiquidVolume: number, +}; +export type LabwareWell = LabwareWellProperties & { + x: number, + y: number, + z: number, +}; +export type LabwareWellMap = { [key: string]: LabwareWell }; +export type LabwareWellGroupMetadata = {| + displayName?: string, + displayCategory?: LabwareDisplayCategory, + wellBottomShape?: WellBottomShape, +|}; +export type LabwareWellGroup = {| + wells: string[], + metadata: LabwareWellGroupMetadata, + brand?: LabwareBrand, +|}; +export type LabwareDefinition2 = {| + version: number, + schemaVersion: 2, + namespace: string, + metadata: LabwareMetadata, + dimensions: LabwareDimensions, + cornerOffsetFromSlot: LabwareOffset, + parameters: LabwareParameters, + brand: LabwareBrand, + ordering: string[][], + wells: LabwareWellMap, + groups: LabwareWellGroup[], +|}; +export type ModuleType = typeof MAGDECK | typeof TEMPDECK | typeof THERMOCYCLER; +export type ModuleRealType = + | 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 = + | typeof TEMPERATURE_MODULE_V1 + | typeof TEMPERATURE_MODULE_V2; +export type ThermocyclerModuleModel = typeof THERMOCYCLER_MODULE_V1; +export type ModuleModel = + | MagneticModuleModel + | TemperatureModuleModel + | ThermocyclerModuleModel; +export type ModuleModelWithLegacy = + | ModuleModel + | 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 DeckSlot = {| + id: DeckSlotId, + position: CoordinateTuple, + matingSurfaceUnitVector?: UnitVectorTuple, + boundingBox: Dimensions, + displayName: string, + compatibleModules: ModuleType[], +|}; +export type DeckCalibrationPoint = {| + id: string, + position: CoordinateTuple, + displayName: string, +|}; +export type DeckLocations = {| + orderedSlots: DeckSlot[], + calibrationPoints: DeckCalibrationPoint[], + fixtures: DeckFixture[], +|}; +export type DeckMetadata = {| + displayName: string, + tags: string[], +|}; +export type DeckLayerFeature = {| + footprint: string, +|}; +export type DeckLayer = DeckLayerFeature[]; +export type DeckDefinition = {| + otId: string, + cornerOffsetFromOrigin: CoordinateTuple, + dimensions: CoordinateTuple, + robot: DeckRobot, + locations: DeckLocations, + metadata: DeckMetadata, + 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: 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, + 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 = {| + ...$Exact, + + model: string, + 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..4703a08abcc --- /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 | 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/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[], +}; From 10812dcc72d321311656afc2edda34d8625ae08a Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Tue, 27 Apr 2021 21:00:29 -0400 Subject: [PATCH 08/16] fixup: regenerate flow types after edge merge --- shared-data/flow-types/js/constants.js.flow | 1 + 1 file changed, 1 insertion(+) diff --git a/shared-data/flow-types/js/constants.js.flow b/shared-data/flow-types/js/constants.js.flow index 82d8afa5155..635c28e84bf 100644 --- a/shared-data/flow-types/js/constants.js.flow +++ b/shared-data/flow-types/js/constants.js.flow @@ -47,3 +47,4 @@ declare export var MODULE_TYPES: ( | "magneticModuleType" | "thermocyclerModuleType" )[]; +declare export var GEN_ONE_MULTI_PIPETTES: string[]; From c2e6202b0f6d510fe1801182499544db6fe176de Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Tue, 27 Apr 2021 21:18:12 -0400 Subject: [PATCH 09/16] fixup: add back some missing whitespace --- .../js/__tests__/labwareDefSchemaV1.test.ts | 36 +++++++++---------- .../js/__tests__/labwareDefSchemaV2.test.ts | 4 +-- shared-data/js/__tests__/pipettes.test.ts | 4 +++ .../js/__tests__/protocolSchemaV4.test.ts | 34 +++++------------- .../js/__tests__/protocolSchemaV5.test.ts | 34 +++++------------- 5 files changed, 39 insertions(+), 73 deletions(-) diff --git a/shared-data/js/__tests__/labwareDefSchemaV1.test.ts b/shared-data/js/__tests__/labwareDefSchemaV1.test.ts index 2c6385df916..6247e05f06a 100644 --- a/shared-data/js/__tests__/labwareDefSchemaV1.test.ts +++ b/shared-data/js/__tests__/labwareDefSchemaV1.test.ts @@ -1,25 +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, -} +const GLOB_OPTIONS = { absolute: true, cwd: __dirname } + // JSON Schema defintion & setup -const ajv = new Ajv({ - allErrors: true, - jsonPointers: true, -}) +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: { @@ -36,20 +32,21 @@ describe('test the schema against a minimalist fixture', () => { } const valid = validate(minimalLabwareDef) const validationErrors = validate.errors + expect(validationErrors).toBe(null) expect(valid).toBe(true) }) + it('fail on bad labware', () => { const badDef = { - metadata: { - name: 'bad', - }, + metadata: { name: 'bad' }, 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') ).toMatchObject({ @@ -58,16 +55,18 @@ describe('test the schema against a minimalist fixture', () => { expect(valid).toBe(false) }) }) + 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) @@ -75,6 +74,7 @@ describe('test schemas of all definitions', () => { 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.ts b/shared-data/js/__tests__/labwareDefSchemaV2.test.ts index 61c0f9fc06d..7d78755b103 100644 --- a/shared-data/js/__tests__/labwareDefSchemaV2.test.ts +++ b/shared-data/js/__tests__/labwareDefSchemaV2.test.ts @@ -47,9 +47,7 @@ const expectGroupsFollowConvention = ( test('fail on bad labware', () => { const badDef = { - metadata: { - name: 'bad', - }, + metadata: { name: 'bad' }, ordering: ['A1'], // array of strings not array of arrays wells: {}, diff --git a/shared-data/js/__tests__/pipettes.test.ts b/shared-data/js/__tests__/pipettes.test.ts index 3e3d2150b8f..ac62c27a438 100644 --- a/shared-data/js/__tests__/pipettes.test.ts +++ b/shared-data/js/__tests__/pipettes.test.ts @@ -1,5 +1,6 @@ // tests for pipette info accessors in `shared-data/js/pipettes.js` import { getPipetteNameSpecs, getPipetteModelSpecs } from '../pipettes' + const PIPETTE_NAMES = [ 'p10_single', 'p50_single', @@ -9,6 +10,7 @@ const PIPETTE_NAMES = [ 'p50_multi', 'p300_multi', ] + const PIPETTE_MODELS = [ 'p10_single_v1', 'p10_single_v1.3', @@ -38,6 +40,7 @@ const PIPETTE_MODELS = [ 'p1000_single_v1.4', 'p1000_single_v1.5', ] + describe('pipette data accessors', () => { describe('getPipetteNameSpecs', () => { PIPETTE_NAMES.forEach(name => @@ -45,6 +48,7 @@ describe('pipette data accessors', () => { expect(getPipetteNameSpecs(name)).toMatchSnapshot()) ) }) + describe('getPipetteModelSpecs', () => { PIPETTE_MODELS.forEach(model => it(`model ${model} snapshot`, () => diff --git a/shared-data/js/__tests__/protocolSchemaV4.test.ts b/shared-data/js/__tests__/protocolSchemaV4.test.ts index b4ac51df071..4b4595c53f3 100644 --- a/shared-data/js/__tests__/protocolSchemaV4.test.ts +++ b/shared-data/js/__tests__/protocolSchemaV4.test.ts @@ -63,16 +63,9 @@ describe('ensure bad protocol data fails validation', () => { it('reject bad values in "pipettes" objects', () => { const badPipettes = { missingKeys: {}, - missingName: { - mount: 'left', - }, - missingMount: { - name: 'pipetteName', - }, - badMount: { - mount: 'blah', - name: 'pipetteName', - }, + missingName: { mount: 'left' }, + missingMount: { name: 'pipetteName' }, + badMount: { mount: 'blah', name: 'pipetteName' }, hasAdditionalProperties: { mount: 'left', name: 'pipetteName', @@ -95,12 +88,8 @@ describe('ensure bad protocol data fails validation', () => { it('reject bad values in "labware" objects', () => { const badLabware = { - noSlot: { - definitionId: 'defId', - }, - noDefId: { - slot: '1', - }, + noSlot: { definitionId: 'defId' }, + noDefId: { slot: '1' }, hasAdditionalProperties: { slot: '1', definitionId: 'defId', @@ -123,16 +112,9 @@ describe('ensure bad protocol data fails validation', () => { it('reject bad values in "modules" objects', () => { const badModules = { - badModuleType: { - slot: '1', - moduleType: 'fake', - }, - noSlot: { - moduleType: 'thermocycler', - }, - noModuleType: { - slot: '1', - }, + badModuleType: { slot: '1', moduleType: 'fake' }, + noSlot: { moduleType: 'thermocycler' }, + noModuleType: { slot: '1' }, hasAdditionalProperties: { slot: '1', moduleType: 'thermocycler', diff --git a/shared-data/js/__tests__/protocolSchemaV5.test.ts b/shared-data/js/__tests__/protocolSchemaV5.test.ts index 96a064cd598..682209a4ad4 100644 --- a/shared-data/js/__tests__/protocolSchemaV5.test.ts +++ b/shared-data/js/__tests__/protocolSchemaV5.test.ts @@ -63,16 +63,9 @@ describe('ensure bad protocol data fails validation', () => { it('reject bad values in "pipettes" objects', () => { const badPipettes = { missingKeys: {}, - missingName: { - mount: 'left', - }, - missingMount: { - name: 'pipetteName', - }, - badMount: { - mount: 'blah', - name: 'pipetteName', - }, + missingName: { mount: 'left' }, + missingMount: { name: 'pipetteName' }, + badMount: { mount: 'blah', name: 'pipetteName' }, hasAdditionalProperties: { mount: 'left', name: 'pipetteName', @@ -95,12 +88,8 @@ describe('ensure bad protocol data fails validation', () => { it('reject bad values in "labware" objects', () => { const badLabware = { - noSlot: { - definitionId: 'defId', - }, - noDefId: { - slot: '1', - }, + noSlot: { definitionId: 'defId' }, + noDefId: { slot: '1' }, hasAdditionalProperties: { slot: '1', definitionId: 'defId', @@ -123,16 +112,9 @@ describe('ensure bad protocol data fails validation', () => { it('reject bad values in "modules" objects', () => { const badModules = { - badModuleType: { - slot: '1', - moduleType: 'fake', - }, - noSlot: { - moduleType: 'thermocycler', - }, - noModuleType: { - slot: '1', - }, + badModuleType: { slot: '1', moduleType: 'fake' }, + noSlot: { moduleType: 'thermocycler' }, + noModuleType: { slot: '1' }, hasAdditionalProperties: { slot: '1', moduleType: 'thermocycler', From 1eae776e2e9fb898de786b54290fb14a8481007e Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Tue, 27 Apr 2021 21:29:35 -0400 Subject: [PATCH 10/16] fixup: add comments about pipette name specs fixtures --- shared-data/js/helpers/__tests__/wellSets.test.ts | 10 +++------- shared-data/pipette/fixtures/name/index.js | 3 +++ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/shared-data/js/helpers/__tests__/wellSets.test.ts b/shared-data/js/helpers/__tests__/wellSets.test.ts index 3902a7cd4b1..6e55cef3cc6 100644 --- a/shared-data/js/helpers/__tests__/wellSets.test.ts +++ b/shared-data/js/helpers/__tests__/wellSets.test.ts @@ -1,8 +1,4 @@ -import { - p10_single, - p10_multi, -} from '@opentrons/shared-data/pipette/fixtures/name/pipetteNameSpecFixtures.json' - +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' @@ -14,8 +10,8 @@ import { findWellAt } from '../getWellNamePerMultiTip' import type { LabwareDefinition2, PipetteNameSpecs } from '../../types' import type { WellSetHelpers } from '../wellSets' -const fixtureP10Single = p10_single as PipetteNameSpecs -const fixtureP10Multi = p10_multi as PipetteNameSpecs +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 diff --git a/shared-data/pipette/fixtures/name/index.js b/shared-data/pipette/fixtures/name/index.js index 7a266f42136..f3751d0daf1 100644 --- a/shared-data/pipette/fixtures/name/index.js +++ b/shared-data/pipette/fixtures/name/index.js @@ -1,6 +1,9 @@ // @flow +// TODO(mc, 2021-04-27): remove or rewrite this file when PD tests are in TS import pipetteNameSpecFixtures from './pipetteNameSpecFixtures.json' +// 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 = From c3cb73cff5071a7829adf93fe5f2fe488c5f035b Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Tue, 27 Apr 2021 21:36:54 -0400 Subject: [PATCH 11/16] fixup: fix shared-data makefile --- shared-data/Makefile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/shared-data/Makefile b/shared-data/Makefile index 0bebe9519b8..c3b3b55b331 100644 --- a/shared-data/Makefile +++ b/shared-data/Makefile @@ -13,7 +13,7 @@ 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 setup: setup-py setup-js @@ -27,8 +27,7 @@ clean: clean-js clean-py # JavaScript targets .PHONY: setup-js -setup-js: - $(MAKE) dist +setup-js: dist-js .PHONY: dist-js dist-js: From 2bda4d1abe3cacfd85a2298eae1945f5a7fe5254 Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Tue, 27 Apr 2021 21:44:37 -0400 Subject: [PATCH 12/16] fixup: warn to fix lint failures in labware-library --- .eslintrc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/.eslintrc.js b/.eslintrc.js index 43b63a60e1c..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', }, }, { From ebf45f2a8bdf4dc2e6a61b13854811d850700a40 Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Wed, 28 Apr 2021 12:46:13 -0400 Subject: [PATCH 13/16] fixup: fix and refine some conversions and typos --- shared-data/flow-types/js/modules.js.flow | 6 +++++- shared-data/flow-types/js/types.js.flow | 4 +++- shared-data/js/helpers/__tests__/wellSets.test.ts | 6 +++--- shared-data/js/helpers/wellSets.ts | 15 ++++++--------- shared-data/js/modules.ts | 11 +++++++---- shared-data/js/types.ts | 4 +++- 6 files changed, 27 insertions(+), 19 deletions(-) diff --git a/shared-data/flow-types/js/modules.js.flow b/shared-data/flow-types/js/modules.js.flow index 6f854307220..7f715aca25b 100644 --- a/shared-data/flow-types/js/modules.js.flow +++ b/shared-data/flow-types/js/modules.js.flow @@ -25,7 +25,11 @@ export type ModuleDef2 = {| displayName: string, quirks: string[], slotTransforms: { - [key: string]: { [key: string]: { [key: string]: AffineTransform } }, + [deckDef: string]: { + [slot: string]: { + [transformName: string]: AffineTransform, + }, + }, }, compatibleWith: ModuleModel[], |}; diff --git a/shared-data/flow-types/js/types.js.flow b/shared-data/flow-types/js/types.js.flow index 6ba06efc521..7ab436f3a0c 100644 --- a/shared-data/flow-types/js/types.js.flow +++ b/shared-data/flow-types/js/types.js.flow @@ -45,7 +45,9 @@ export type LabwareDefinition1 = {| tipVolume?: number, }, ordering: string[][], - wells: { [key: string]: WellDefinition }, + wells: { + [well: string]: WellDefinition, + }, |}; export type LabwareDisplayCategory = | "wellPlate" diff --git a/shared-data/js/helpers/__tests__/wellSets.test.ts b/shared-data/js/helpers/__tests__/wellSets.test.ts index 6e55cef3cc6..68ea47ec823 100644 --- a/shared-data/js/helpers/__tests__/wellSets.test.ts +++ b/shared-data/js/helpers/__tests__/wellSets.test.ts @@ -15,7 +15,7 @@ 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 fixtureOverlappyWellplage = fixture_overlappy_wellplate as LabwareDefinition2 +const fixtureOverlappyWellplate = fixture_overlappy_wellplate as LabwareDefinition2 describe('findWellAt', () => { it('should determine if given (x, y) is within a rectangular well', () => { @@ -81,14 +81,14 @@ describe('canPipetteUseLabware', () => { }) it('returns false when wells are too close together for multi channel pipette', () => { - const labwareDef = fixtureOverlappyWellplage + const labwareDef = fixtureOverlappyWellplate const pipette = fixtureP10Multi expect(canPipetteUseLabware(pipette, labwareDef)).toBe(false) }) it('returns true when pipette is single channel', () => { - const labwareDef = fixtureOverlappyWellplage + const labwareDef = fixtureOverlappyWellplate const pipette = fixtureP10Single expect(canPipetteUseLabware(pipette, labwareDef)).toBe(true) diff --git a/shared-data/js/helpers/wellSets.ts b/shared-data/js/helpers/wellSets.ts index 6154dd56a94..8460640ade2 100644 --- a/shared-data/js/helpers/wellSets.ts +++ b/shared-data/js/helpers/wellSets.ts @@ -52,15 +52,12 @@ export interface WellSetHelpers { } export const makeWellSetHelpers = (): WellSetHelpers => { - const cache: Record< - string, - | { - labwareDef: LabwareDefinition2 - wellSetByPrimaryWell: WellSetByPrimaryWell - } - | null - | undefined - > = {} + const cache: Partial<{ + [labwareDefURI: string]: { + labwareDef: LabwareDefinition2 + wellSetByPrimaryWell: WellSetByPrimaryWell + } | null + }> = {} const getAllWellSetsForLabware = ( labwareDef: LabwareDefinition2 diff --git a/shared-data/js/modules.ts b/shared-data/js/modules.ts index a3640020a49..4b9a7fe5b64 100644 --- a/shared-data/js/modules.ts +++ b/shared-data/js/modules.ts @@ -38,10 +38,13 @@ export interface ModuleDef2 { calibrationPoint: Coordinates displayName: string quirks: string[] - slotTransforms: Record< - string, - Record> - > + slotTransforms: { + [deckDef: string]: { + [slot: string]: { + [transformName: string]: AffineTransform + } + } + } compatibleWith: ModuleModel[] } diff --git a/shared-data/js/types.ts b/shared-data/js/types.ts index 4d3b27bb89b..770e6a5559d 100644 --- a/shared-data/js/types.ts +++ b/shared-data/js/types.ts @@ -44,7 +44,9 @@ export interface LabwareDefinition1 { tipVolume?: number } ordering: string[][] - wells: Record + wells: { + [well: string]: WellDefinition + } } // TODO(mc, 2019-05-29): Remove this enum in favor of string + exported From b42dde0f25193365a57a08aa851208353d7245fa Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Fri, 30 Apr 2021 13:36:15 -0400 Subject: [PATCH 14/16] fixup: tweak tsconfig to allow PipetteName/Model types from JSON --- shared-data/flow-types/js/pipettes.js.flow | 10 ++++--- shared-data/js/__tests__/pipettes.test.ts | 4 +-- shared-data/js/pipettes.ts | 31 ++++++++++++++-------- shared-data/tsconfig-data.json | 19 +++++++++++++ shared-data/tsconfig.json | 14 ++-------- 5 files changed, 50 insertions(+), 28 deletions(-) create mode 100644 shared-data/tsconfig-data.json diff --git a/shared-data/flow-types/js/pipettes.js.flow b/shared-data/flow-types/js/pipettes.js.flow index 2a17f292c6e..1bbc2d12757 100644 --- a/shared-data/flow-types/js/pipettes.js.flow +++ b/shared-data/flow-types/js/pipettes.js.flow @@ -5,16 +5,20 @@ * @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: string + name: PipetteName ): PipetteNameSpecs | null; declare export function getPipetteModelSpecs( - model: string + model: PipetteModel ): PipetteModelSpecs | null | void; declare export function getAllPipetteNames( ...sortBy: SortableProps[] -): string[]; +): PipetteName[]; declare export function shouldLevel(specs: PipetteNameSpecs): boolean; declare export {}; diff --git a/shared-data/js/__tests__/pipettes.test.ts b/shared-data/js/__tests__/pipettes.test.ts index ac62c27a438..9cf9d42fe02 100644 --- a/shared-data/js/__tests__/pipettes.test.ts +++ 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/pipettes.ts b/shared-data/js/pipettes.ts index 00797be3ce1..69e80c0438a 100644 --- a/shared-data/js/pipettes.ts +++ b/shared-data/js/pipettes.ts @@ -5,14 +5,23 @@ 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: string[] = 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 { - // @ts-expect-error(mc, 2021-04-27): use "safer" accessor to make TS happy - const config = pipetteNameSpecs[name] +export function getPipetteNameSpecs( + name: PipetteName +): PipetteNameSpecs | null { + const config = pipetteNameSpecs[name] as + | Omit + | undefined return config != null ? { ...config, name } : null } @@ -20,23 +29,23 @@ export function getPipetteNameSpecs(name: string): PipetteNameSpecs | null { // both the name specs + model-specific specs // NOTE: this should NEVER be used in PD, which is model-agnostic export function getPipetteModelSpecs( - model: string + model: PipetteModel ): PipetteModelSpecs | null | undefined { - // @ts-expect-error(mc, 2021-04-27): use "safer" accessor to make TS happy 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: SortableProps[]): string[] { +export function getAllPipetteNames(...sortBy: SortableProps[]): PipetteName[] { const models = [...ALL_PIPETTE_NAMES] if (sortBy.length) models.sort(comparePipettes(sortBy)) return models } function comparePipettes(sortBy: SortableProps[]) { - return (modelA: string, modelB: string) => { + return (modelA: PipetteName, modelB: PipetteName) => { // any cast is because we know these pipettes exist const a = getPipetteNameSpecs(modelA) as PipetteNameSpecs const b = getPipetteNameSpecs(modelB) as PipetteNameSpecs 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 index 9e6f118e4a1..1857e24731d 100644 --- a/shared-data/tsconfig.json +++ b/shared-data/tsconfig.json @@ -1,20 +1,10 @@ { "extends": "../tsconfig-base.json", - "references": [], + "references": [{ "path": "./tsconfig-data.json" }], "compilerOptions": { "composite": true, "rootDir": ".", "outDir": "lib" }, - "include": [ - "typings", - "js", - "protocol", - "deck/**/*.json", - "labware/**/*.json", - "module/**/*.json", - "pipette/**/*.json", - "protocol/**/*.json", - "build/**/*.json" - ] + "include": ["typings", "js", "protocol"] } From fce15e0d6bc41aa70eea50683d114203ba362368 Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Fri, 30 Apr 2021 13:45:49 -0400 Subject: [PATCH 15/16] fixup: knock out some redundant '| undefined's --- components/src/instrument/InstrumentInfo.tsx | 4 ++-- shared-data/js/labwareTools/index.ts | 2 +- shared-data/protocol/types/schemaV1.ts | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/src/instrument/InstrumentInfo.tsx b/components/src/instrument/InstrumentInfo.tsx index 3d02628a233..bd9bccd2f68 100644 --- a/components/src/instrument/InstrumentInfo.tsx +++ b/components/src/instrument/InstrumentInfo.tsx @@ -12,7 +12,7 @@ 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 */ @@ -20,7 +20,7 @@ export interface InstrumentInfoProps { /** if disabled, pipette & its info are grayed out */ isDisabled: boolean /** specs of mounted pipette */ - pipetteSpecs?: InstrumentDiagramProps['pipetteSpecs'] | null | undefined + pipetteSpecs?: InstrumentDiagramProps['pipetteSpecs'] | null /** classes to apply */ className?: string /** classes to apply to the info group child */ diff --git a/shared-data/js/labwareTools/index.ts b/shared-data/js/labwareTools/index.ts index 4c3f826f650..82ae2b5b0ba 100644 --- a/shared-data/js/labwareTools/index.ts +++ b/shared-data/js/labwareTools/index.ts @@ -61,7 +61,7 @@ export interface BaseLabwareProps { version?: number namespace?: string loadNamePostfix?: string[] - strict?: boolean | null | undefined // If true, throws error on failed validation + strict?: boolean | null // If true, throws error on failed validation } export interface RegularLabwareProps extends BaseLabwareProps { diff --git a/shared-data/protocol/types/schemaV1.ts b/shared-data/protocol/types/schemaV1.ts index ec8f446a40b..6751542525f 100644 --- a/shared-data/protocol/types/schemaV1.ts +++ b/shared-data/protocol/types/schemaV1.ts @@ -9,8 +9,8 @@ export interface PipetteLabwareFields { export interface AspirateDispenseArgs extends PipetteLabwareFields { volume: number - offsetFromBottomMm?: number | null | undefined - 'flow-rate'?: number | null | undefined + offsetFromBottomMm?: number | null + 'flow-rate'?: number | null } export type Command = @@ -25,7 +25,7 @@ export type Command = | { command: 'touch-tip' params: PipetteLabwareFields & { - offsetFromBottomMm?: number | null | undefined + offsetFromBottomMm?: number | null } } | { From c8e3961a771e867a8824942eb1d74279a9075156 Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Fri, 30 Apr 2021 13:52:49 -0400 Subject: [PATCH 16/16] fixup: update flow types --- shared-data/flow-types/js/labwareTools/index.js.flow | 2 +- shared-data/flow-types/protocol/types/schemaV1.js.flow | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shared-data/flow-types/js/labwareTools/index.js.flow b/shared-data/flow-types/js/labwareTools/index.js.flow index c776cffd80a..7e94917ec02 100644 --- a/shared-data/flow-types/js/labwareTools/index.js.flow +++ b/shared-data/flow-types/js/labwareTools/index.js.flow @@ -38,7 +38,7 @@ export type BaseLabwareProps = {| version?: number, namespace?: string, loadNamePostfix?: string[], - strict?: boolean | null | void, + strict?: boolean | null, |}; export type RegularLabwareProps = {| ...$Exact, diff --git a/shared-data/flow-types/protocol/types/schemaV1.js.flow b/shared-data/flow-types/protocol/types/schemaV1.js.flow index 4703a08abcc..a46c929e5c1 100644 --- a/shared-data/flow-types/protocol/types/schemaV1.js.flow +++ b/shared-data/flow-types/protocol/types/schemaV1.js.flow @@ -15,8 +15,8 @@ export type AspirateDispenseArgs = {| ...$Exact, volume: number, - offsetFromBottomMm?: number | null | void, - "flow-rate"?: number | null | void, + offsetFromBottomMm?: number | null, + "flow-rate"?: number | null, |}; export type Command = | { @@ -30,7 +30,7 @@ export type Command = | { command: "touch-tip", params: PipetteLabwareFields & { - offsetFromBottomMm?: number | null | void, + offsetFromBottomMm?: number | null, }, } | {