Skip to content

Commit

Permalink
Merge branch 'edge' into chore_update-cypress
Browse files Browse the repository at this point in the history
  • Loading branch information
shlokamin committed May 6, 2024
2 parents cea1c3e + c867d3f commit d11e5d6
Show file tree
Hide file tree
Showing 403 changed files with 20,574 additions and 18,946 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ module.exports = {
'useLastRunCommandKey',
'useCurrentMaintenanceRun',
'useDeckConfigurationQuery',
'useAllCommandsAsPreSerializedList',
],
message:
'The HTTP hook is deprecated. Utilize the equivalent notification wrapper (useNotifyX) instead.',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,18 +155,10 @@ def upload_calibration_offsets(
parser.add_argument(
"email", metavar="EMAIL", type=str, nargs=1, help="opentrons gmail."
)
parser.add_argument(
"ip_or_all",
metavar="IP_OR_ALL",
type=str,
nargs=1,
help="Enter 'ALL' to read IPs.json or type full IP address of 1 robot.",
)
args = parser.parse_args()
storage_directory = args.storage_directory[0]
folder_name = args.folder_name[0]
google_sheet_name = args.google_sheet_name[0]
ip_or_all = args.ip_or_all[0]
email = args.email[0]
# Connect to google drive.
try:
Expand All @@ -191,6 +183,7 @@ def upload_calibration_offsets(
except FileNotFoundError:
print(f"Add .json file with robot IPs to: {storage_directory}.")
sys.exit()
ip_or_all = input("IP Address or ALL: ")

if ip_or_all == "ALL":
ip_address_list = ip_file["ip_address_list"]
Expand Down
22 changes: 3 additions & 19 deletions abr-testing/abr_testing/tools/abr_scale.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,7 @@
nargs=1,
help="Name of google sheet and local csv to save data to.",
)
parser.add_argument("robot", metavar="ROBOT", type=str, nargs=1, help="Robot name.")
parser.add_argument(
"labware_name",
metavar="LABWARE_NAME",
type=str,
nargs=1,
help="Name of labware.",
)
parser.add_argument(
"protocol_step",
metavar="PROTOCOL_STEP",
type=str,
nargs=1,
help="1 for empty plate, 2 for filled plate, 3 for end of protocol.",
)
args = parser.parse_args()
robot = args.robot[0]
labware = args.labware_name[0]
protocol_step = args.protocol_step[0]
storage_directory = args.storage_directory[0]
file_name = args.file_name[0]
file_name_csv = file_name + ".csv"
Expand Down Expand Up @@ -71,7 +53,9 @@
print("Connected to google sheet.")
except FileNotFoundError:
print("No google sheets credentials. Add credentials to storage notebook.")

robot = input("Robot: ")
labware = input("Labware: ")
protocol_step = input("Measurement Step (1,2,3): ")
# Scale Loop
grams, is_stable = scale.read_mass()
grams, is_stable = scale.read_mass()
Expand Down
22 changes: 22 additions & 0 deletions api-client/src/runs/commands/getCommandsAsPreSerializedList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { GET, request } from '../../request'

import type { ResponsePromise } from '../../request'
import type { HostConfig } from '../../types'
import type {
CommandsAsPreSerializedListData,
GetCommandsParams,
} from './types'

export function getCommandsAsPreSerializedList(
config: HostConfig,
runId: string,
params: GetCommandsParams
): ResponsePromise<CommandsAsPreSerializedListData> {
return request<CommandsAsPreSerializedListData>(
GET,
`/runs/${runId}/commandsAsPreSerializedList`,
null,
config,
params
)
}
6 changes: 6 additions & 0 deletions api-client/src/runs/commands/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ export interface CommandsData {
links: CommandsLinks
}

export interface CommandsAsPreSerializedListData {
data: string[]
meta: GetCommandsParams & { totalLength: number }
links: CommandsLinks
}

export interface CreateCommandParams {
waitUntilComplete?: boolean
timeout?: number
Expand Down
1 change: 1 addition & 0 deletions api-client/src/runs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export { createCommand } from './commands/createCommand'
export { createLiveCommand } from './commands/createLiveCommand'
export { getCommand } from './commands/getCommand'
export { getCommands } from './commands/getCommands'
export { getCommandsAsPreSerializedList } from './commands/getCommandsAsPreSerializedList'
export { createRunAction } from './createRunAction'
export * from './createLabwareOffset'
export * from './createLabwareDefinition'
Expand Down
20 changes: 20 additions & 0 deletions api-client/src/system/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { describe, expect, it } from 'vitest'
import { sanitizeFileName } from '../utils'

describe('sanitizeFileName', () => {
it('returns original alphanumeric file name', () => {
expect(sanitizeFileName('an0ther_otie_logo.png')).toEqual(
'an0ther_otie_logo.png'
)
})

it('sanitizes a file name', () => {
expect(
sanitizeFileName(
`otie's birthday/party - (& the bouncy castle cost ~$100,000).jpeg`
)
).toEqual(
'otie_s_birthday_party_-____the_bouncy_castle_cost___100_000_.jpeg'
)
})
})
6 changes: 4 additions & 2 deletions api-client/src/system/createSplash.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { POST, request } from '../request'
import { sanitizeFileName } from './utils'
import type { ResponsePromise } from '../request'
import type { HostConfig } from '../types'

export function createSplash(
config: HostConfig,
file: File
): ResponsePromise<void> {
// sanitize file name to ensure no spaces
const renamedFile = new File([file], file.name.replace(' ', '_'), {
// sanitize file name to ensure no spaces or special characters
const newFileName = sanitizeFileName(file.name)
const renamedFile = new File([file], newFileName, {
type: 'image/png',
})

Expand Down
1 change: 1 addition & 0 deletions api-client/src/system/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export { createRegistration } from './createRegistration'
export { createSplash } from './createSplash'
export { getConnections } from './getConnections'
export * from './types'
export * from './utils'
3 changes: 3 additions & 0 deletions api-client/src/system/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function sanitizeFileName(fileName: string): string {
return fileName.replace(/[^a-zA-Z0-9-.]/gi, '_')
}
25 changes: 25 additions & 0 deletions api/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,31 @@ log][]. For a list of currently known issues, please see the [Opentrons issue tr

---

## Opentrons Robot Software Changes in 7.3.0

Welcome to the v7.3.0 release of the Opentrons robot software!

### New Features

- Runtime parameters: read, write, and use parameters in Python protocol runs.

### Improved Features

- Automatic tip tracking is now available for all nozzle configurations.
- Flex no longer shows unnecessary pipette calibration warnings.
- Python protocols can once again set labware offsets outside of Labware Position Check.

### Changed Features

- Calling `GET /runs/{id}/commands` for a JSON protocol no longer returns a full list of queued commands. Use protocol analysis to get a full list of commands.

### Bug Fixes

- Fixed an edge case where capitalizing part of a labware load name could cause unexpected behavior or collisions.
- Fixed Python packages installed on the OT-2 with `pip` not being found by `import` statements.

---

## Opentrons Robot Software Changes in 7.2.2

Welcome to the v7.2.2 release of the Opentrons robot software!
Expand Down
7 changes: 5 additions & 2 deletions api/src/opentrons/hardware_control/instruments/ot3/gripper.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
Geometry,
)

RECONFIG_KEYS = {"quirks"}
RECONFIG_KEYS = {"quirks", "grip_force_profile"}

MAX_ACCEPTABLE_JAW_DISPLACEMENT: Final = 20

Expand All @@ -52,6 +52,7 @@ def __init__(
config: GripperDefinition,
gripper_cal_offset: GripperCalibrationOffset,
gripper_id: str,
jaw_max_offset: Optional[float] = None,
) -> None:
self._config = config
self._model = config.model
Expand Down Expand Up @@ -83,7 +84,7 @@ def __init__(
self._log.info(
f"loaded: {self._model}, gripper offset: {self._calibration_offset}"
)
self._jaw_max_offset: Optional[float] = None
self._jaw_max_offset = jaw_max_offset

@property
def grip_force_profile(self) -> GripForceProfile:
Expand Down Expand Up @@ -325,11 +326,13 @@ def _reload_gripper(
changed.add(k)
if changed.intersection(RECONFIG_KEYS):
# Something has changed that requires reconfig
# we shoud recalibrate the jaw as well
return (
Gripper(
new_config,
cal_offset,
attached_instr._gripper_id,
None,
),
False,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def reset_gripper(self) -> None:
og_gripper.config,
load_gripper_calibration_offset(og_gripper.gripper_id),
og_gripper.gripper_id,
og_gripper._jaw_max_offset,
)
self._gripper = new_gripper

Expand Down
14 changes: 11 additions & 3 deletions api/src/opentrons/protocol_api/instrument_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
"""The version after which a partial nozzle configuration became available for the 96 Channel Pipette."""
_PARTIAL_NOZZLE_CONFIGURATION_AUTOMATIC_TIP_TRACKING_IN = APIVersion(2, 18)
"""The version after which automatic tip tracking supported partially configured nozzle layouts."""
_DISPOSAL_LOCATION_OFFSET_ADDED_IN = APIVersion(2, 18)
"""The version after which offsets for deck configured trash containers and changes to alternating tip drop behavior were introduced."""


class InstrumentContext(publisher.CommandPublisher):
Expand Down Expand Up @@ -1086,16 +1088,22 @@ def drop_tip(
well = maybe_well

elif isinstance(location, (TrashBin, WasteChute)):
# In 2.16 and 2.17, we would always automatically use automatic alternate tip drop locations regardless
# of whether you explicitly passed the disposal location as a location or if none was provided. Now, in
# 2.18 and moving forward, passing it in will bypass the automatic behavior and instead go to the set
# offset or the XY center if none is provided.
if self.api_version < _DISPOSAL_LOCATION_OFFSET_ADDED_IN:
alternate_drop_location = True
with publisher.publish_context(
broker=self.broker,
command=cmds.drop_tip_in_disposal_location(
instrument=self, location=location
),
):
# TODO(jbl 2024-02-28) when adding 2.18 api version checks, set alternate_tip_drop
# if below that version for compatability
self._core.drop_tip_in_disposal_location(
location, home_after=home_after
location,
home_after=home_after,
alternate_tip_drop=alternate_drop_location,
)
self._last_tip_picked_up_from = None
return self
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,46 +80,25 @@ async def execute(
ot3_api = ensure_ot3_hardware(
self._hardware_api,
)
# the 96-channel mount is disengaged during gripper calibration and
# must be homed before the gantry position can be called
max_height_z_tip = ot3_api.get_instrument_max_height(Mount.LEFT)
# disengage the gripper z mount if present and enabled
await ot3_api.prepare_for_mount_movement(Mount.LEFT)
current_position_mount = await ot3_api.gantry_position(

await ot3_api.retract(Mount.LEFT)
current_pos = await ot3_api.gantry_position(
Mount.LEFT, critical_point=CriticalPoint.MOUNT
)
max_height_z_mount = ot3_api.get_instrument_max_height(
Mount.LEFT, critical_point=CriticalPoint.MOUNT
await ot3_api.move_to(
mount=Mount.LEFT,
abs_position=Point(x=_ATTACH_POINT.x, y=_ATTACH_POINT.y, z=current_pos.z),
critical_point=CriticalPoint.MOUNT,
)
max_height_z_tip = ot3_api.get_instrument_max_height(Mount.LEFT)
# avoid using motion planning waypoints because we do not need to move the z at this moment
movement_points = [
# move the z to the highest position
Point(
x=current_position_mount.x,
y=current_position_mount.y,
z=max_height_z_mount,
),
# move in x,y without going down the z
Point(x=_ATTACH_POINT.x, y=_ATTACH_POINT.y, z=max_height_z_mount),
]

await ot3_api.prepare_for_mount_movement(Mount.LEFT)
for movement in movement_points:
await ot3_api.move_to(
mount=Mount.LEFT,
abs_position=movement,
critical_point=CriticalPoint.MOUNT,
)

if params.mount != MountType.EXTENSION:

# disengage the gripper z to enable the e-brake, this prevents the gripper
# z from dropping when the right mount carriage gets released from the
# mount during 96-channel detach flow
if ot3_api.has_gripper():
await ot3_api.disengage_axes([Axis.Z_G])

if params.maintenancePosition == MaintenancePosition.ATTACH_INSTRUMENT:
mount_to_axis = Axis.by_mount(params.mount.to_hw_mount())
mount = params.mount.to_hw_mount()
mount_to_axis = Axis.by_mount(mount)
await ot3_api.prepare_for_mount_movement(mount)
await ot3_api.move_axes(
{
mount_to_axis: _INSTRUMENT_ATTACH_Z_POINT,
Expand All @@ -134,6 +113,7 @@ async def execute(
Axis.Z_R: max_motion_range + _RIGHT_MOUNT_Z_MARGIN,
}
)
await ot3_api.disengage_axes([Axis.Z_L, Axis.Z_R])

return MoveToMaintenancePositionResult()

Expand Down
Loading

0 comments on commit d11e5d6

Please sign in to comment.