import path from 'path' ; import startCase from 'lodash/startCase' ; import uniq from 'lodash/uniq' ; import { createSelector } from 'reselect' ; import type { LabwareDefinition2 } from '@opentrons/shared-data' ; import { getPipetteNameSpecs } from '@opentrons/shared-data' ; import { fileIsJson } from './protocol-data' ; import { createLogger } from '../../logger' ; import * as PipetteConstants from '../pipettes/constants' ; import type { State } from '../types' ; import type { ProtocolData , ProtocolType , ProtocolFile , ProtocolPipetteTipRackByMount , } from './types' ; type ProtocolInfoSelector = ( state : State ) => { protocolName : string | null ; lastModified : number | null ; appName : string | null ; appVersion : string | null ; } ; const log = createLogger ( __filename ) ; const stripDirAndExtension = ( f ) : string => path . basename ( f , path . extname ( f ) ) ; export const getProtocolFile = ( state ) : ProtocolFile | null => state . protocol . file ; export const getProtocolContents = ( state ) : string | null => state . protocol . contents ; export const getProtocolData = ( state ) : ProtocolData | null => state . protocol . data ; export const getProtocolFilename = createSelector ( getProtocolFile , file => file ?. name ?? null ) ; interface LabwareDefinitionBySlotMap { [ slot ] : LabwareDefinition2 | null | undefined ; } export const getLabwareDefBySlot = createSelector ( getProtocolData , data => { if ( data && 'labwareDefinitions' in data && 'labware' in data ) { const labwareById = data . labware ; const labwareDefinitions = data . labwareDefinitions ; return Object . keys ( labwareById ) . reduce ( ( defsBySlot , labwareId ) => { const labware = labwareById [ labwareId ] ; let slot = data . modules && labware . slot in data . modules ? data . modules [ labware . slot ] . slot : labware . slot ; if ( slot === 'span7_8_10_11' ) slot = '7' ; if ( slot in defsBySlot ) { log . warn ( `expected 1 labware per slot, slot ${ slot } contains multiple labware` ) ; } defsBySlot [ slot ] = labwareDefinitions [ labware . definitionId ] ; return defsBySlot ; } , { } ) ; } return { } ; } ) ; export const getProtocolDisplayData = createSelector ( getProtocolData , getProtocolFilename , ( _data , name ) => { const basename = name ? stripDirAndExtension ( name ) : null ; if ( ! _data ) { return { protocolName : basename , lastModified : null , appName : null , appVersion : null , } ; } const data = _data ; const metadata = _data . metadata ; const protocolName = metadata ?. protocolName ?? metadata ?. [ 'protocol-name' ] ?? basename ; const lastModified = metadata ?. lastModified ?? metadata ?. [ 'last-modified' ] ?? metadata ?. created ?? null ; const appName = data . designerApplication ?. name ?? data [ 'designer-application' ] ?. [ 'application-name' ] ?? null ; const appVersion = data . designerApplication ?. version ?? data [ 'designer-application' ] ?. [ 'application-version' ] ?? null ; return { protocolName : protocolName , lastModified : lastModified , appName : appName , appVersion : appVersion , } ; } ) ; export const getProtocolName = createSelector ( getProtocolDisplayData , displayData => displayData . protocolName ) ; export const getProtocolAuthor = createSelector ( getProtocolData , data => ( data ?. metadata ?. author as string ) ?? null ) ; export const getProtocolDescription = createSelector ( getProtocolData , data => ( data ?. metadata ?. description as string ) ?? null ) ; export const getProtocolSource = createSelector ( getProtocolData , data => { return data !== null && 'metadata' in data && 'source' in data . metadata && typeof data . metadata . source === 'string' ? data . metadata . source : null ; } ) ; export const getProtocolLastUpdated = createSelector ( getProtocolFile , getProtocolDisplayData , ( file , displayData ) => displayData . lastModified ?? file ?. lastModified ?? null ) ; export const getProtocolType = createSelector ( getProtocolFile , file => file ?. type || null ) ; export const getProtocolCreatorApp = createSelector ( getProtocolDisplayData , displayData => { return { name : displayData . appName , version : displayData . appVersion , } ; } ) ; export const getProtocolApiVersion = ( state ) : string | null => { const level = state . robot . session . apiLevel ; return level != null ? level . join ( '.' ) : null ; } ; const METHOD_OT_API = 'Python Protocol API' ; const METHOD_UNKNOWN = 'Unknown Application' ; export const getProtocolMethod = createSelector ( getProtocolFile , getProtocolContents , getProtocolData , getProtocolCreatorApp , getProtocolApiVersion , ( file , contents , data , app , apiVersion ) => { const isJson = file && fileIsJson ( file ) ; const jsonAppName = app . name ? startCase ( app . name . replace ( /^opentrons\// , '' ) ) : METHOD_UNKNOWN ; const jsonAppVersion = app . version ? ` v${ app . version }` : '' ; const readableJsonName = isJson ? `${ jsonAppName }${ jsonAppVersion }` : null ; if ( ! file || ! contents ) return null ; if ( readableJsonName ) return readableJsonName ; return `${ METHOD_OT_API }${ apiVersion !== null ? ` v${ apiVersion }` : '' }` ; } ) ; export const getProtocolPipetteTipRacks = createSelector ( getProtocolData , protocolData => { if ( protocolData == null || ! ( 'commands' in protocolData ) ) { return { left : null , right : null } ; } const { pipettes , labware , labwareDefinitions , commands } = protocolData ; const tipRackCommands = commands . filter ( commandObject => commandObject . command === 'pickUpTip' ) ; const protocolPipetteValues = Object . values ( pipettes ) ; const protocolPipetteKeys = Object . keys ( pipettes ) ; return PipetteConstants . PIPETTE_MOUNTS . reduce < ProtocolPipetteTipRackByMount > ( ( result , mount ) => { const pipetteOnMount = protocolPipetteValues . find ( pipette => pipette . mount === mount ) ; if ( pipetteOnMount !== undefined ) { const index = protocolPipetteValues . indexOf ( pipetteOnMount ) ; const pipetteKey = protocolPipetteKeys [ index ] ; let tipRackDefs = new Array < LabwareDefinition2 > ( ) ; tipRackCommands . forEach ( command => { if ( 'pipette' in command . params && 'labware' in command . params && pipetteKey === command . params . pipette ) { const tipRack = labware [ command . params . labware ] ; const tipRackDefinition = labwareDefinitions [ tipRack . definitionId ] ; if ( tipRackDefinition !== undefined ) { tipRackDefs . push ( tipRackDefinition ) ; } } tipRackDefs = uniq ( tipRackDefs ) ; } ) ; result [ mount ] = { pipetteSpecs : getPipetteNameSpecs ( pipetteOnMount . name ) , tipRackDefs : tipRackDefs , } ; } else { result [ mount ] = null ; } return result ; } , { left : null , right : null } ) ; } ) ;