Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(shared-data): labware schema v3 #16027

Merged
merged 17 commits into from
Aug 26, 2024
Merged
70 changes: 70 additions & 0 deletions shared-data/js/__tests__/labwareDefSchemaV3.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import path from 'path'
import glob from 'glob'
import { describe, expect, it, beforeAll, test } from 'vitest'

import type { LabwareDefinition3 } from '../types'
import Ajv from 'ajv'
import schema from '../../labware/schemas/3.json'

const fixturesDir = path.join(__dirname, '../../labware/fixtures/3')
const globPattern = '**/*.json'

const ajv = new Ajv({ allErrors: true, jsonPointers: true })
const validate = ajv.compile(schema)

const checkGeometryDefinitions = (
labwareDef: LabwareDefinition3,
filename: string
): void => {
test(`all geometryDefinitionIds specified in {filename} should have an accompanying valid entry in innerLabwareGeometry`, () => {
for (const wellName in labwareDef.wells) {
const wellGeometryId = labwareDef.wells[wellName].geometryDefinitionId

if (wellGeometryId === undefined) {
return
}
if (
labwareDef.innerLabwareGeometry === null ||
labwareDef.innerLabwareGeometry === undefined
) {
return
}

expect(wellGeometryId in labwareDef.innerLabwareGeometry).toBe(true)

const wellDepth = labwareDef.wells[wellName].depth
const wellShape = labwareDef.wells[wellName].shape
const topFrustumHeight =
labwareDef.innerLabwareGeometry[wellGeometryId].frusta[0].topHeight
const topFrustumShape =
labwareDef.innerLabwareGeometry[wellGeometryId].frusta[0].geometry.shape
caila-marashaj marked this conversation as resolved.
Show resolved Hide resolved

expect(wellDepth).toEqual(topFrustumHeight)
expect(wellShape).toEqual(topFrustumShape)
}
})
}

describe(`test additions to labware schema in v3`, () => {
const labwarePaths = glob.sync(globPattern, { cwd: fixturesDir })

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 fullLabwarePath = path.join(fixturesDir, labwarePath)
const labwareDef = require(fullLabwarePath) as LabwareDefinition3

checkGeometryDefinitions(labwareDef, labwarePath)

it(`${filename} validates against schema`, () => {
const valid = validate(labwareDef)
const validationErrors = validate.errors
expect(validationErrors).toBe(null)
expect(valid).toBe(true)
})
})
})
62 changes: 58 additions & 4 deletions shared-data/js/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export interface WellDefinition {
y: number
z: number
'total-liquid-volume': number
geometryDefinitionId?: string | null
}

// typedef for labware definitions under v1 labware schema
Expand Down Expand Up @@ -131,19 +132,19 @@ export interface LabwareBrand {
links?: string[]
}

export interface CircularWellShapeProperties {
export interface CircularWellShape {
shape: 'circular'
diameter: number
}
export interface RectangularWellShapeProperties {
export interface RectangularWellShape {
shape: 'rectangular'
xDimension: number
yDimension: number
}

export type LabwareWellShapeProperties =
| CircularWellShapeProperties
| RectangularWellShapeProperties
| CircularWellShape
| RectangularWellShape

// well without x,y,z
export type LabwareWellProperties = LabwareWellShapeProperties & {
Expand All @@ -155,6 +156,41 @@ export type LabwareWell = LabwareWellProperties & {
x: number
y: number
z: number
geometryDefinitionId?: string
}

export interface CircularCrossSection {
shape: 'circular'
diameter: number
}

export interface RectangularCrossSection {
shape: 'rectangular'
xDimension: number
yDimension: number
}

export interface SphericalSegment {
shape: 'spherical'
radiusOfCurvature: number
depth: number
}

export type TopCrossSection = CircularCrossSection | RectangularCrossSection

export type BottomShape =
| CircularCrossSection
| RectangularCrossSection
| SphericalSegment

export interface BoundedSection {
geometry: TopCrossSection
topHeight: number
}

export interface InnerWellGeometry {
frusta: BoundedSection[]
bottomShape: BottomShape
}

// TODO(mc, 2019-03-21): exact object is tough to use with the initial value in
Expand Down Expand Up @@ -193,6 +229,24 @@ export interface LabwareDefinition2 {
stackingOffsetWithModule?: Record<string, LabwareOffset>
}

export interface LabwareDefinition3 {
caila-marashaj marked this conversation as resolved.
Show resolved Hide resolved
version: number
schemaVersion: 3
namespace: string
metadata: LabwareMetadata
dimensions: LabwareDimensions
cornerOffsetFromSlot: LabwareOffset
parameters: LabwareParameters
brand: LabwareBrand
ordering: string[][]
wells: LabwareWellMap
groups: LabwareWellGroup[]
allowedRoles?: LabwareRoles[]
stackingOffsetWithLabware?: Record<string, LabwareOffset>
stackingOffsetWithModule?: Record<string, LabwareOffset>
innerLabwareGeometry?: Record<string, InnerWellGeometry> | null
}

export interface LabwareDefByDefURI {
[defUri: string]: LabwareDefinition2
}
Expand Down
113 changes: 113 additions & 0 deletions shared-data/labware/fixtures/3/fixture_2_plate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
{
"ordering": [["A1"], ["A2"]],
"schemaVersion": 3,
"version": 3,
"namespace": "fixture",
"metadata": {
"displayName": "12 Channel Trough",
"displayVolumeUnits": "mL",
"displayCategory": "reservoir"
},
"dimensions": {
"xDimension": 127.76,
"yDimension": 85.8,
"zDimension": 44.45
},
"parameters": {
"format": "trough",
"isTiprack": false,
"isMagneticModuleCompatible": false,
"loadName": "fixture_12_trough",
"quirks": ["centerMultichannelOnWells", "touchTipDisabled"]
},
"wells": {
"A1": {
"shape": "circular",
"depth": 42.16,
"diameter": 35.0,
"totalLiquidVolume": 22000,
"x": 13.94,
"y": 42.9,
"z": 2.29,
"geometryDefinitionId": "iuweofiuwhfn"
},
"A2": {
"shape": "rectangular",
"depth": 42.16,
"xDimension": 8.33,
"yDimension": 71.88,
"totalLiquidVolume": 22000,
"x": 23.03,
"y": 42.9,
"z": 2.29,
"geometryDefinitionId": "daiwudhadfhiew"
}
},
"brand": {
"brand": "USA Scientific",
"brandId": ["1061-8150"]
},
"groups": [
{
"wells": ["A1", "A2"],
"metadata": {
"wellBottomShape": "v"
}
}
],
"cornerOffsetFromSlot": {
"x": 0,
"y": 0,
"z": 0
},
"innerLabwareGeometry": {
"daiwudhadfhiew": {
"frusta": [
{
"geometry": {
"shape": "rectangular",
"xDimension": 127.76,
"yDimension": 85.8
},
"topHeight": 42.16
},
{
"geometry": {
"shape": "rectangular",
"xDimension": 70.0,
"yDimension": 50.0
},
"topHeight": 20.0
}
],
"bottomShape": {
"shape": "rectangular",
"xDimension": 2.0,
"yDimension": 3.0
}
},
"iuweofiuwhfn": {
"frusta": [
{
"geometry": {
"shape": "circular",
"diameter": 35.0
},
"topHeight": 42.16
},
{
"geometry": {
"shape": "circular",
"diameter": 22.0
},
"topHeight": 20.0
}
],
"bottomShape": {
"shape": "spherical",
"radiusOfCurvature": 20.0,
"depth": 6.0
}
}
}
}
Loading
Loading