Skip to content

Commit

Permalink
Merge branch 'edge' into fix_app-desktop-styling-2
Browse files Browse the repository at this point in the history
  • Loading branch information
ncdiehl11 authored Nov 6, 2023
2 parents 98742ba + 3c4964e commit cb9bb3e
Show file tree
Hide file tree
Showing 23 changed files with 219 additions and 99 deletions.
9 changes: 9 additions & 0 deletions api/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ log][]. For a list of currently known issues, please see the [Opentrons issue tr
[technical change log]: https://github.com/Opentrons/opentrons/releases
[opentrons issue tracker]: https://github.com/Opentrons/opentrons/issues?q=is%3Aopen+is%3Aissue+label%3Abug


## Opentrons Robot Software Changes in 7.0.2

The 7.0.2 hotfix release does not contain any changes to the robot software

### Known Issues

JSON protocols created or modified with Protocol Designer v6.0.0 or higher can't be simulated with the `opentrons_simulate` command-line tool

---

## Opentrons Robot Software Changes in 7.0.1
Expand Down
11 changes: 11 additions & 0 deletions app-shell/build/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ log][]. For a list of currently known issues, please see the [Opentrons issue tr

---

## Opentrons App Changes in 7.0.2

Welcome to the v7.0.2 release of the Opentrons App!

### Bug Fixes

- Fixes an issue where robot system updates over USB were stalling
- Fixes an issue where app protocol analysis would fail if you had Python 3.10 installed on your computer and installed the opentrons package there

---

## Opentrons App Changes in 7.0.1

Welcome to the v7.0.1 release of the Opentrons App! This release builds on the major release that added support for Opentrons Flex.
Expand Down
2 changes: 1 addition & 1 deletion app-shell/src/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,10 @@ function createReadStreamWithSize(
}

readStream.once('error', handleError)
readStream.on('data', onData)

function handleSuccess(): void {
resolve(readStream)
readStream.removeListener('error', handleError)
}

function handleError(error: Error): void {
Expand Down
1 change: 1 addition & 0 deletions app-shell/src/protocol-analysis/executeAnalyzeCli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export function executeAnalyzeCli(
sourcePaths: string[]
): Promise<void> {
return execa(pythonPath, [
'-I',
'-m',
'opentrons.cli',
'analyze',
Expand Down
45 changes: 45 additions & 0 deletions app-shell/src/usb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import type { Action, Dispatch } from './types'

let usbHttpAgent: SerialPortHttpAgent | undefined
const usbLog = createLogger('usb')
let usbFetchInterval: NodeJS.Timeout

export function getSerialPortHttpAgent(): SerialPortHttpAgent | undefined {
return usbHttpAgent
Expand All @@ -43,6 +44,7 @@ export function createSerialPortHttpAgent(path: string): void {
keepAliveMsecs: 10000,
path,
logger: usbLog,
timeout: 100000,
})

usbHttpAgent = serialPortHttpAgent
Expand Down Expand Up @@ -110,6 +112,48 @@ async function usbListener(
}
}

function pollSerialPortAndCreateAgent(dispatch: Dispatch): void {
// usb poll already initialized
if (usbFetchInterval != null) {
return
}
usbFetchInterval = setInterval(() => {
// already connected to an Opentrons robot via USB
if (getSerialPortHttpAgent() != null) {
return
}
usbLog.debug('fetching serialport list')
fetchSerialPortList()
.then((list: PortInfo[]) => {
const ot3UsbSerialPort = list.find(
port =>
port.productId?.localeCompare(DEFAULT_PRODUCT_ID, 'en-US', {
sensitivity: 'base',
}) === 0 &&
port.vendorId?.localeCompare(DEFAULT_VENDOR_ID, 'en-US', {
sensitivity: 'base',
}) === 0
)

if (ot3UsbSerialPort == null) {
usbLog.debug('no OT-3 serial port found')
return
}

createSerialPortHttpAgent(ot3UsbSerialPort.path)
// remove any existing handler
ipcMain.removeHandler('usb:request')
ipcMain.handle('usb:request', usbListener)

dispatch(usbRequestsStart())
})
.catch(e =>
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
usbLog.debug(`fetchSerialPortList error ${e?.message ?? 'unknown'}`)
)
}, 10000)
}

function startUsbHttpRequests(dispatch: Dispatch): void {
fetchSerialPortList()
.then((list: PortInfo[]) => {
Expand Down Expand Up @@ -150,6 +194,7 @@ export function registerUsb(dispatch: Dispatch): (action: Action) => unknown {
if (action.payload.usbDevices.find(isUsbDeviceOt3) != null) {
startUsbHttpRequests(dispatch)
}
pollSerialPortAndCreateAgent(dispatch)
break
case USB_DEVICE_ADDED:
if (isUsbDeviceOt3(action.payload.usbDevice)) {
Expand Down
1 change: 1 addition & 0 deletions app/src/assets/localization/en/device_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"are_you_sure_you_want_to_disconnect": "Are you sure you want to disconnect from {{ssid}}?",
"attach_a_pipette_before_calibrating": "Attach a pipette in order to perform calibration",
"boot_scripts": "Boot scripts",
"both": "Both",
"browse_file_system": "Browse file system",
"bug_fixes": "Bug Fixes",
"calibrate_deck": "Calibrate deck",
Expand Down
4 changes: 2 additions & 2 deletions app/src/assets/localization/en/module_wizard_flows.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
"module_too_hot": "Module is too hot to proceed to module calibration",
"move_gantry_to_front": "Move gantry to front",
"next": "Next",
"pipette_probe": "Pipette Probe",
"place_adapter": "Place calibration adapter in {{module}}",
"pipette_probe": "Pipette probe",
"install_adapter": "Place calibration adapter in {{module}}",
"place_flush": "Place the adapter flush on top of the module.",
"prepping_module": "Prepping {{module}} for module calibration",
"recalibrate": "Recalibrate",
Expand Down
10 changes: 9 additions & 1 deletion app/src/organisms/ChooseRobotToRunProtocolSlideout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
} from '@opentrons/components'

import { getRobotUpdateDisplayInfo } from '../../redux/robot-update'
import { OPENTRONS_USB } from '../../redux/discovery'
import { appShellRequestor } from '../../redux/shell/remote'
import { useTrackCreateProtocolRunEvent } from '../Devices/hooks'
import { ApplyHistoricOffsets } from '../ApplyHistoricOffsets'
import { useOffsetCandidatesForAnalysis } from '../ApplyHistoricOffsets/hooks/useOffsetCandidatesForAnalysis'
Expand Down Expand Up @@ -82,7 +84,13 @@ export function ChooseRobotToRunProtocolSlideoutComponent(
})
},
},
selectedRobot != null ? { hostname: selectedRobot.ip } : null,
selectedRobot != null
? {
hostname: selectedRobot.ip,
requestor:
selectedRobot?.ip === OPENTRONS_USB ? appShellRequestor : undefined,
}
: null,
shouldApplyOffsets
? offsetCandidates.map(({ vector, location, definitionUri }) => ({
vector,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ export function UpdateRobotSoftware({
dispatchStartRobotUpdate(robotName, files[0].path)
onUpdateStart()
}
// this is to reset the state of the file picker so users can reselect the same
// system image if the upload fails
if (inputRef.current?.value != null) {
inputRef.current.value = ''
}
}

const handleClick: React.MouseEventHandler<HTMLButtonElement> = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
import { useDispatch } from 'react-redux'
import { css } from 'styled-components'

import {
Flex,
Icon,
Link,
NewPrimaryBtn,
NewSecondaryBtn,
JUSTIFY_FLEX_END,
ALIGN_CENTER,
COLORS,
Expand All @@ -25,14 +23,11 @@ import { FOOTER_BUTTON_STYLE } from './UpdateRobotModal'
import {
startRobotUpdate,
clearRobotUpdateSession,
getRobotSessionIsManualFile,
} from '../../../../redux/robot-update'
import { useDispatchStartRobotUpdate } from '../../../../redux/robot-update/hooks'
import { useRobotUpdateInfo } from './useRobotUpdateInfo'
import successIcon from '../../../../assets/images/icon_success.png'

import type { SetStatusBarCreateCommand } from '@opentrons/shared-data'
import type { State } from '../../../../redux/types'
import type { RobotUpdateSession } from '../../../../redux/robot-update/types'
import type { UpdateStep } from './useRobotUpdateInfo'

Expand All @@ -47,10 +42,6 @@ const UPDATE_TEXT_STYLE = css`
color: ${COLORS.darkGreyEnabled};
font-size: 0.8rem;
`
const TRY_RESTART_STYLE = css`
color: ${COLORS.blueEnabled};
font-size: 0.8rem;
`
const HIDDEN_CSS = css`
position: fixed;
clip: rect(1px 1px 1px 1px);
Expand All @@ -71,17 +62,10 @@ export function RobotUpdateProgressModal({
const { t } = useTranslation('device_settings')
const [showFileSelect, setShowFileSelect] = React.useState<boolean>(false)
const installFromFileRef = React.useRef<HTMLInputElement>(null)
const dispatchStartRobotUpdate = useDispatchStartRobotUpdate()
const manualFileUsedForUpdate = useSelector((state: State) =>
getRobotSessionIsManualFile(state)
)

const completeRobotUpdateHandler = (): void => {
if (closeUpdateBuildroot != null) closeUpdateBuildroot()
}
const reinstallUpdate = React.useCallback(() => {
dispatchStartRobotUpdate(robotName)
}, [robotName])

const { error } = session || { error: null }
const { updateStep, progressPercent } = useRobotUpdateInfo(session)
useStatusBarAnimation(error != null)
Expand Down Expand Up @@ -125,9 +109,6 @@ export function RobotUpdateProgressModal({
footer={
hasStoppedUpdating ? (
<RobotUpdateProgressFooter
robotName={robotName}
installRobotUpdate={dispatchStartRobotUpdate}
errorMessage={error}
closeUpdateBuildroot={completeRobotUpdateHandler}
/>
) : null
Expand All @@ -151,17 +132,7 @@ export function RobotUpdateProgressModal({
<StyledText css={UPDATE_TEXT_STYLE}>
{letUserExitUpdate && updateStep !== 'restart' ? (
<>
{t('problem_during_update')}{' '}
<Link
css={TRY_RESTART_STYLE}
onClick={
!manualFileUsedForUpdate
? reinstallUpdate
: () => setShowFileSelect(true)
}
>
{t('try_restarting_the_update')}
</Link>
{t('problem_during_update')} {t('try_restarting_the_update')}
{showFileSelect && (
<input
ref={installFromFileRef}
Expand All @@ -182,35 +153,16 @@ export function RobotUpdateProgressModal({
}

interface RobotUpdateProgressFooterProps {
robotName: string
installRobotUpdate: (robotName: string) => void
errorMessage?: string | null
closeUpdateBuildroot?: () => void
}

function RobotUpdateProgressFooter({
robotName,
installRobotUpdate,
errorMessage,
closeUpdateBuildroot,
}: RobotUpdateProgressFooterProps): JSX.Element {
const { t } = useTranslation('device_settings')
const installUpdate = React.useCallback(() => {
installRobotUpdate(robotName)
}, [robotName])

return (
<Flex alignItems={ALIGN_CENTER} justifyContent={JUSTIFY_FLEX_END}>
{errorMessage && (
<NewSecondaryBtn
onClick={installUpdate}
marginRight={SPACING.spacing8}
css={FOOTER_BUTTON_STYLE}
border="none"
>
{t('try_again')}
</NewSecondaryBtn>
)}
<NewPrimaryBtn
onClick={closeUpdateBuildroot}
marginRight={SPACING.spacing12}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,6 @@ describe('DownloadUpdateModal', () => {

expect(getByText('test error')).toBeInTheDocument()
fireEvent.click(exitButton)
expect(getByText('Try again')).toBeInTheDocument()
expect(props.closeUpdateBuildroot).toHaveBeenCalled()

expect(mockUseCreateLiveCommandMutation).toBeCalledWith()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ describe('useRobotUpdateInfo', () => {
)

expect(result.current.updateStep).toBe('install')
expect(Math.round(result.current.progressPercent)).toBe(67)
expect(Math.round(result.current.progressPercent)).toBe(75)

rerender({
...mockRobotUpdateSession,
Expand All @@ -70,15 +70,15 @@ describe('useRobotUpdateInfo', () => {
)

expect(result.current.updateStep).toBe('install')
expect(Math.round(result.current.progressPercent)).toBe(67)
expect(Math.round(result.current.progressPercent)).toBe(75)

rerender({
...mockRobotUpdateSession,
error: 'Something went wrong',
})

expect(result.current.updateStep).toBe('error')
expect(Math.round(result.current.progressPercent)).toBe(67)
expect(Math.round(result.current.progressPercent)).toBe(75)
})

it('should calculate correct progressPercent when the update is not manual', () => {
Expand All @@ -94,7 +94,7 @@ describe('useRobotUpdateInfo', () => {
})

expect(result.current.updateStep).toBe('install')
expect(Math.round(result.current.progressPercent)).toBe(67)
expect(Math.round(result.current.progressPercent)).toBe(75)
})

it('should ignore progressPercent reported by a step marked as ignored', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,12 @@ function useFindProgressPercentFrom(

const stepAndStage = `${sessionStep}-${sessionStage}`
// Ignored because 0-100 is too fast to be worth recording.
const IGNORED_STEPS_AND_STAGES = ['processFile-awaiting-file']
const IGNORED_STEPS_AND_STAGES = [
'processFile-awaiting-file',
'uploadFile-awaiting-file',
]
// Each stepAndStage is an equal fraction of the total steps.
const TOTAL_STEPS_WITH_PROGRESS = 3
const TOTAL_STEPS_WITH_PROGRESS = 2

const isNewStateWithProgress =
prevSeenUpdateStep.current !== stepAndStage &&
Expand Down
3 changes: 1 addition & 2 deletions app/src/organisms/ModuleWizardFlows/PlaceAdapter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,7 @@ export const PlaceAdapter = (props: PlaceAdapterProps): JSX.Element | null => {
)
return (
<GenericWizardTile
header={t('place_adapter', { module: moduleDisplayName })}
// TODO: swap this out with the right animation
header={t('install_adapter', { module: moduleDisplayName })}
rightHandBody={placeAdapterVid}
bodyText={bodyText}
proceedButtonText={t('confirm_placement')}
Expand Down
7 changes: 5 additions & 2 deletions app/src/organisms/PipetteWizardFlows/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,11 @@ export const PipetteWizardFlows = (

const attachedPipettes = useAttachedPipettesFromInstrumentsQuery()
const memoizedPipetteInfo = React.useMemo(() => props.pipetteInfo ?? null, [])
const isGantryEmpty =
attachedPipettes[LEFT] == null && attachedPipettes[RIGHT] == null
const isGantryEmpty = React.useMemo(
() => attachedPipettes[LEFT] == null && attachedPipettes[RIGHT] == null,
[]
)

const pipetteWizardSteps = React.useMemo(
() =>
memoizedPipetteInfo == null
Expand Down
Loading

0 comments on commit cb9bb3e

Please sign in to comment.