Skip to content

Commit 3fd73a2

Browse files
feat(app-shell-odd,robot-server): Allow resetting on-device display configuration (#13227)
Co-authored-by: Shlok Amin <[email protected]>
1 parent 3c5753c commit 3fd73a2

File tree

17 files changed

+211
-40
lines changed

17 files changed

+211
-40
lines changed

api/src/opentrons/config/reset.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class ResetOptionId(str, Enum):
3737
gripper_offset = "gripperOffsetCalibrations"
3838
tip_length_calibrations = "tipLengthCalibrations"
3939
runs_history = "runsHistory"
40+
on_device_display = "onDeviceDisplay"
4041

4142

4243
_OT_2_RESET_OPTIONS = [
@@ -51,6 +52,7 @@ class ResetOptionId(str, Enum):
5152
ResetOptionId.pipette_offset,
5253
ResetOptionId.gripper_offset,
5354
ResetOptionId.runs_history,
55+
ResetOptionId.on_device_display,
5456
]
5557

5658
_settings_reset_options = {
@@ -59,7 +61,7 @@ class ResetOptionId(str, Enum):
5961
),
6062
ResetOptionId.deck_calibration: CommonResetOption(
6163
name="Deck Calibration",
62-
description="Clear deck calibration (will also clear pipette " "offset)",
64+
description="Clear deck calibration (will also clear pipette offset)",
6365
),
6466
ResetOptionId.pipette_offset: CommonResetOption(
6567
name="Pipette Offset Calibrations",
@@ -71,16 +73,20 @@ class ResetOptionId(str, Enum):
7173
),
7274
ResetOptionId.tip_length_calibrations: CommonResetOption(
7375
name="Tip Length Calibrations",
74-
description="Clear tip length calibrations (will also clear " "pipette offset)",
76+
description="Clear tip length calibrations (will also clear pipette offset)",
7577
),
76-
# TODO(mm, 2022-05-23): Run and protocol history is a robot-server thing,
77-
# and is not a concept known to this package (the `opentrons` library).
78+
# TODO(mm, 2022-05-23): runs_history and on_device_display are robot-server things,
79+
# and are not concepts known to this package (the `opentrons` library).
7880
# This option is defined here only as a convenience for robot-server.
79-
# Find a way to split thing up and define this in robot-server instead.
81+
# Find a way to split things up and define this in robot-server instead.
8082
ResetOptionId.runs_history: CommonResetOption(
8183
name="Clear Runs History",
8284
description="Erase this device's stored history of protocols and runs.",
8385
),
86+
ResetOptionId.on_device_display: CommonResetOption(
87+
name="On-Device Display Configuration",
88+
description="Clear the configuration of the on-device display (touchscreen)",
89+
),
8490
}
8591

8692

app-shell-odd/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"form-data": "2.5.0",
5252
"fs-extra": "10.0.0",
5353
"get-stream": "5.1.0",
54+
"lodash": "4.17.21",
5455
"merge-options": "1.0.1",
5556
"node-fetch": "2.6.7",
5657
"node-stream-zip": "1.8.2",

app-shell-odd/src/config/index.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// TODO(mc, 2020-01-31): this module is high-importance and needs unit tests
33
import Store from 'electron-store'
44
import get from 'lodash/get'
5+
import forEach from 'lodash/forEach'
56
import mergeOptions from 'merge-options'
67
import yargsParser from 'yargs-parser'
78
import fs from 'fs-extra'
@@ -23,7 +24,7 @@ import type { Config, Overrides } from './types'
2324

2425
export * from './types'
2526

26-
const ODD_DIR = '/data/ODD'
27+
export const ODD_DIR = '/data/ODD'
2728

2829
// Note (kj:03/02/2023) this file path will be updated when the embed team cleans up
2930
const BRIGHTNESS_FILE =
@@ -150,3 +151,11 @@ export function handleConfigChange(
150151
): void {
151152
store().onDidChange(path, changeHandler)
152153
}
154+
export function resetStore(): void {
155+
log().debug('Resetting the store')
156+
store().clear()
157+
const migratedConfig = migrate(DEFAULTS_V12)
158+
forEach(migratedConfig, (configVal, configKey) => {
159+
store().set(configKey, configVal)
160+
})
161+
}

app-shell-odd/src/main.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
// electron main entry point
22
import { app, ipcMain } from 'electron'
3+
import fse from 'fs-extra'
4+
import path from 'path'
35
import { createUi } from './ui'
46
import { createLogger } from './log'
57
import { registerDiscovery } from './discovery'
68
import { registerRobotLogs } from './robot-logs'
79
import { registerUpdate, updateLatestVersion } from './update'
810
import { registerRobotSystemUpdate } from './system-update'
911
import { registerAppRestart } from './restart'
10-
import { getConfig, getStore, getOverrides, registerConfig } from './config'
12+
import {
13+
getConfig,
14+
getStore,
15+
getOverrides,
16+
registerConfig,
17+
resetStore,
18+
ODD_DIR,
19+
} from './config'
1120
import systemd from './systemd'
1221

1322
import type { BrowserWindow } from 'electron'
@@ -41,6 +50,15 @@ app.once('window-all-closed', () => {
4150

4251
function startUp(): void {
4352
log.info('Starting App')
53+
console.log('Starting App')
54+
const storeNeedsReset = fse.existsSync(
55+
path.join(ODD_DIR, `_CONFIG_TO_BE_DELETED_ON_REBOOT`)
56+
)
57+
if (storeNeedsReset) {
58+
log.debug('store marked to be reset, resetting store')
59+
resetStore()
60+
fse.removeSync(path.join(ODD_DIR, `_CONFIG_TO_BE_DELETED_ON_REBOOT`))
61+
}
4462
systemd.sendStatus('loading app')
4563
process.on('uncaughtException', error => log.error('Uncaught: ', { error }))
4664
process.on('unhandledRejection', reason =>

app-shell/build/release-notes-internal.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This is still pretty early in the process, so some things are known not to work,
1414
- All instrument flows should display errors properly now
1515
- Update robot flows don't say OT-2s anymore
1616
- There should be fewer surprise scroll bars on Windows
17+
- The configuration of the on-device display can be factory-reset, which lets you go back to the first-time setup flow
1718

1819

1920

app/src/assets/localization/en/device_settings.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"checking_for_updates": "Checking for updates",
3030
"choose_reset_settings": "Choose reset settings",
3131
"choose": "Choose...",
32-
"clear_all_stored_data_description": "Clears instrument calibrations and protocols. Keeps robot name and network settings.",
32+
"clear_all_data": "Clear all data",
33+
"clear_all_stored_data_description": "Resets all settings. You’ll have to redo initial setup before using the robot again.",
3334
"clear_all_stored_data": "Clear all stored data",
3435
"clear_data_and_restart_robot": "Clear data and restart robot",
3536
"clear_individual_data": "Clear individual data",
@@ -227,6 +228,7 @@
227228
"security_type": "Security Type",
228229
"select_a_network": "Select a network",
229230
"select_a_security_type": "Select a security type",
231+
"select_all_settings": "Select all settings",
230232
"select_authentication_method": "Select authentication method for your selected network.",
231233
"sending_software": "Sending software...",
232234
"share_logs_with_opentrons_description": "Help Opentrons improve its products and services by automatically sending anonymous robot logs. Opentrons uses these logs to troubleshoot robot issues and spot error trends.",

app/src/organisms/Devices/RobotSettings/AdvancedTab/AdvancedTabSlideouts/DeviceResetModal.tsx

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ import {
2323
SUCCESS,
2424
PENDING,
2525
} from '../../../../../redux/robot-api'
26-
import { resetConfig } from '../../../../../redux/robot-admin'
26+
import {
27+
getResetConfigOptions,
28+
resetConfig,
29+
} from '../../../../../redux/robot-admin'
30+
import { useIsOT3 } from '../../../hooks'
2731

2832
import type { State } from '../../../../../redux/types'
2933
import type { ResetConfigRequest } from '../../../../../redux/robot-admin/types'
@@ -44,13 +48,36 @@ export function DeviceResetModal({
4448
const { t } = useTranslation(['device_settings', 'shared'])
4549
const history = useHistory()
4650
const [dispatchRequest, requestIds] = useDispatchApiRequest()
51+
const isOT3 = useIsOT3(robotName)
4752
const resetRequestStatus = useSelector((state: State) => {
4853
const lastId = last(requestIds)
4954
return lastId != null ? getRequestById(state, lastId) : null
5055
})?.status
5156

57+
const serverResetOptions = useSelector((state: State) =>
58+
getResetConfigOptions(state, robotName)
59+
)
60+
5261
const triggerReset = (): void => {
5362
if (resetOptions != null) {
63+
if (isOT3) {
64+
const totalOptionsSelected = Object.values(resetOptions).filter(
65+
selected => selected === true
66+
).length
67+
68+
const isEveryOptionSelected =
69+
totalOptionsSelected > 0 &&
70+
totalOptionsSelected ===
71+
// filtering out ODD setting because this gets implicitly cleared if all settings are selected
72+
serverResetOptions.filter(o => o.id !== 'onDeviceDisplay').length
73+
74+
if (isEveryOptionSelected) {
75+
resetOptions = {
76+
...resetOptions,
77+
onDeviceDisplay: true,
78+
}
79+
}
80+
}
5481
dispatchRequest(resetConfig(robotName, resetOptions))
5582
history.push(`/devices/`)
5683
}

app/src/organisms/Devices/RobotSettings/AdvancedTab/AdvancedTabSlideouts/DeviceResetSlideout.tsx

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,17 @@ export function DeviceResetSlideout({
131131
onCloseClick()
132132
}
133133

134+
const totalOptionsSelected = Object.values(resetOptions).filter(
135+
selected => selected === true
136+
).length
137+
138+
// filtering out ODD setting because this gets implicitly cleared if all settings are selected
139+
const allOptionsWithoutODD = options.filter(o => o.id !== 'onDeviceDisplay')
140+
141+
const isEveryOptionSelected =
142+
totalOptionsSelected > 0 &&
143+
totalOptionsSelected === allOptionsWithoutODD.length
144+
134145
return (
135146
<Slideout
136147
title={t('device_reset')}
@@ -164,25 +175,35 @@ export function DeviceResetSlideout({
164175
/>
165176
<StyledText as="p">{t('resets_cannot_be_undone')}</StyledText>
166177
</Flex>
167-
{/* Note: (kj:06/07/2023) this part will be updated when be is ready */}
168178
{isOT3 ? (
169179
<>
170180
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing20}>
171181
<Flex flexDirection={DIRECTION_COLUMN}>
172182
<StyledText as="p" fontWeight={TYPOGRAPHY.fontWeightSemiBold}>
173-
{t('factory_reset')}
174-
</StyledText>
175-
<StyledText as="p" marginTop={SPACING.spacing8}>
176-
{t('factory_reset_description')}
177-
</StyledText>
178-
</Flex>
179-
<Flex flexDirection={DIRECTION_COLUMN}>
180-
<StyledText as="p" fontWeight={TYPOGRAPHY.fontWeightSemiBold}>
181-
{t('clear_all_stored_data')}
183+
{t('clear_all_data')}
182184
</StyledText>
183-
<StyledText as="p" marginTop={SPACING.spacing8}>
185+
<StyledText as="p" marginY={SPACING.spacing8}>
184186
{t('clear_all_stored_data_description')}
185187
</StyledText>
188+
<CheckboxField
189+
onChange={() => {
190+
setResetOptions(
191+
isEveryOptionSelected
192+
? {}
193+
: allOptionsWithoutODD.reduce((acc, val) => {
194+
return {
195+
...acc,
196+
[val.id]: true,
197+
}
198+
}, {})
199+
)
200+
}}
201+
value={isEveryOptionSelected}
202+
label={t(`select_all_settings`)}
203+
isIndeterminate={
204+
!isEveryOptionSelected && totalOptionsSelected > 0
205+
}
206+
/>
186207
</Flex>
187208
</Flex>
188209
<Divider marginY={SPACING.spacing16} />

app/src/organisms/Devices/RobotSettings/AdvancedTab/AdvancedTabSlideouts/__tests__/DeviceResetModal.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import { renderWithProviders } from '@opentrons/components'
55
import { i18n } from '../../../../../../i18n'
66
import { resetConfig } from '../../../../../../redux/robot-admin'
77
import { useDispatchApiRequest } from '../../../../../../redux/robot-api'
8-
98
import { DeviceResetModal } from '../DeviceResetModal'
109

1110
import type { DispatchApiRequestType } from '../../../../../../redux/robot-api'
1211

12+
jest.mock('../../../../hooks')
1313
jest.mock('../../../../../../redux/robot-admin')
1414
jest.mock('../../../../../../redux/robot-api')
1515

app/src/organisms/Devices/RobotSettings/AdvancedTab/AdvancedTabSlideouts/__tests__/DeviceResetSlideout.test.tsx

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -106,20 +106,10 @@ describe('RobotSettings DeviceResetSlideout', () => {
106106
it('should change some options and text for Flex', () => {
107107
mockUseIsOT3.mockReturnValue(true)
108108
const [{ getByText, getByRole, queryByRole, queryByText }] = render()
109-
getByText('Factory Reset')
109+
getByText('Clear all data')
110110
getByText(
111111
'Resets all settings. You’ll have to redo initial setup before using the robot again.'
112112
)
113-
getByText('Clear all stored data')
114-
getByText(
115-
'Clears instrument calibrations and protocols. Keeps robot name and network settings.'
116-
)
117-
118-
expect(
119-
queryByText(
120-
'Resetting Deck and/or Tip Length Calibration data will also clear Pipette Offset Calibration data.'
121-
)
122-
).toBeNull()
123113
expect(queryByText('Clear deck calibration')).toBeNull()
124114
getByText('Clear pipette calibration(s)')
125115
expect(queryByText('Clear tip length calibrations')).toBeNull()

0 commit comments

Comments
 (0)