async def post_serial_update( serial: str = Path(..., description="Serial number of the module"), hardware: ThreadManager = Depends(get_hardware))\ -> V1BasicResponse: """Update module firmware""" attached_modules = hardware.attached_modules # type: ignore matching_module = find_matching_module(serial, attached_modules) if not matching_module: raise V1HandlerError(message=f'Module {serial} not found', status_code=status.HTTP_404_NOT_FOUND) try: if matching_module.bundled_fw: await asyncio.wait_for( modules.update_firmware( matching_module, matching_module.bundled_fw.path, asyncio.get_event_loop()), 100) return V1BasicResponse( message=f'Successfully updated module {serial}' ) else: res = (f'Bundled fw file not found for module of ' f'type: {matching_module.name()}') status_code = status.HTTP_500_INTERNAL_SERVER_ERROR except modules.UpdateError as e: res = f'Update error: {e}' status_code = status.HTTP_500_INTERNAL_SERVER_ERROR except asyncio.TimeoutError: res = 'Module not responding' status_code = status.HTTP_500_INTERNAL_SERVER_ERROR raise V1HandlerError(message=res, status_code=status_code)
async def post_home_robot( robot_home_target: control.RobotHomeTarget, hardware: ThreadManager = Depends(get_hardware), motion_lock: ThreadedAsyncLock = Depends(get_motion_lock)) \ -> V1BasicResponse: """Home the robot or one of the pipettes""" try: async with motion_lock.forbid(): mount = robot_home_target.mount target = robot_home_target.target home = hardware.home # type: ignore home_plunger = hardware.home_plunger # type: ignore if target == control.HomeTarget.pipette and mount: await home([Axis.by_mount(Mount[mount.upper()])]) await home_plunger(Mount[mount.upper()]) message = f"Pipette on {mount} homed successfully" elif target == control.HomeTarget.robot: await home() message = "Homing robot." else: raise V1HandlerError(message=f"{target} is invalid", status_code=status.HTTP_400_BAD_REQUEST) return V1BasicResponse(message=message) except ThreadedAsyncForbidden as e: raise V1HandlerError(status_code=status.HTTP_403_FORBIDDEN, message=str(e))
async def post_serial_command( command: SerialCommand, serial: str = Path(..., description="Serial number of the module"), hardware: HardwareAPILike = Depends(get_hardware)) \ -> SerialCommandResponse: """Send a command on device identified by serial""" attached_modules = hardware.attached_modules # type: ignore if not attached_modules: raise V1HandlerError(message="No connected modules", status_code=status.HTTP_404_NOT_FOUND) # Search for the module matching_mod = find_matching_module(serial, attached_modules) if not matching_mod: raise V1HandlerError(message="Specified module not found", status_code=status.HTTP_404_NOT_FOUND) if hasattr(matching_mod, command.command_type): clean_args = command.args or [] method = getattr(matching_mod, command.command_type) if asyncio.iscoroutinefunction(method): val = await method(*clean_args) else: val = method(*clean_args) return SerialCommandResponse(message='Success', returnValue=val) else: raise V1HandlerError( message=f'Module does not have command: {command.command_type}', status_code=status.HTTP_400_BAD_REQUEST)
async def post_settings(update: AdvancedSettingRequest)\ -> AdvancedSettingsResponse: """Update advanced setting (feature flag)""" try: await advanced_settings.set_adv_setting(update.id, update.value) except ValueError as e: raise V1HandlerError(message=str(e), status_code=status.HTTP_400_BAD_REQUEST) except advanced_settings.SettingException as e: # Severe internal error raise V1HandlerError(message=str(e), status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) return _create_settings_response()
async def post_calibration_deck_start( command: DeckStart = DeckStart(), hardware: ThreadManager = Depends(get_hardware)) \ -> DeckStartResponse: try: res = await dc.create_session(command.force, hardware) return DeckStartResponse(token=UUID(res.token), pipette=PipetteDeckCalibration(**res.pipette)) except dc.SessionForbidden as e: raise V1HandlerError(status_code=status.HTTP_403_FORBIDDEN, message=str(e)) except dc.SessionInProgress as e: raise V1HandlerError(status_code=status.HTTP_409_CONFLICT, message=str(e))
async def post_calibration_deck(operation: DeckCalibrationDispatch) \ -> V1BasicResponse: try: res = await dc.dispatch(token=str(operation.token), command=operation.command, command_data=operation.dict( exclude={'token', 'command'}, exclude_none=True)) if not res.success: raise AssertionError(res.message) return V1BasicResponse(message=res.message) except dc.NoSessionInProgress as e: message = str(e) status_code = 418 except dc.SessionForbidden as e: message = str(e) status_code = status.HTTP_403_FORBIDDEN except AssertionError as e: message = str(e) status_code = status.HTTP_400_BAD_REQUEST except Exception as e: message = f'Exception {type(e)} raised by dispatch of {operation}: {e}' status_code = status.HTTP_500_INTERNAL_SERVER_ERROR raise V1HandlerError(status_code=status_code, message=message)
async def post_log_level_upstream(log_level: LogLevel) -> V1BasicResponse: log_level_value = log_level.log_level log_level_name = None if log_level_value is None else log_level_value.name ok_syslogs = { LogLevels.error.name: "err", LogLevels.warning.name: "warning", LogLevels.info.name: "info", LogLevels.debug.name: "debug" } syslog_level = "emerg" if log_level_name is not None: syslog_level = ok_syslogs[log_level_name] code, stdout, stderr = await log_control.set_syslog_level(syslog_level) if code != 0: msg = f"Could not reload config: {stdout} {stderr}" log.error(msg) raise V1HandlerError(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, message=msg) if log_level_name: result = f"Upstreaming log level changed to {log_level_name}" getattr(log, log_level_name)(result) else: result = "Upstreaming logs disabled" log.info(result) return V1BasicResponse(message=result)
async def delete_wifi_key(key_uuid: str = Path( ..., description="The ID of key to delete, as determined by a previous" " call to GET /wifi/keys")) -> V1BasicResponse: """Delete wifi key handler""" deleted_file = wifi.remove_key(key_uuid) if not deleted_file: raise V1HandlerError(status.HTTP_404_NOT_FOUND, message=f"No such key file {key_uuid}") return V1BasicResponse(message=f'Key file {deleted_file} deleted')
async def get_engaged_motors(hardware: ThreadManager = Depends(get_hardware) ) -> model.EngagedMotors: # type: ignore try: engaged_axes = hardware.engaged_axes # type: ignore axes_dict = {str(k).lower(): model.EngagedMotor(enabled=v) for k, v in engaged_axes.items()} return model.EngagedMotors(**axes_dict) except ValidationError as e: raise V1HandlerError( status.HTTP_500_INTERNAL_SERVER_ERROR, str(e) )
async def post_wifi_configure(configuration: WifiConfiguration)\ -> WifiConfigurationResponse: try: psk = configuration.psk.get_secret_value() if \ configuration.psk else None ok, message = await nmcli.configure( ssid=configuration.ssid, securityType=nmcli.SECURITY_TYPES(configuration.securityType), eapConfig=configuration.eapConfig, hidden=configuration.hidden is True, psk=psk) log.debug(f"Wifi configure result: {message}") except (ValueError, TypeError) as e: # Indicates an unexpected kwarg; check is done here to avoid keeping # the _check_configure_args signature up to date with nmcli.configure raise V1HandlerError(status.HTTP_400_BAD_REQUEST, str(e)) if not ok: raise V1HandlerError(status.HTTP_401_UNAUTHORIZED, message=message) return WifiConfigurationResponse(message=message, ssid=configuration.ssid)
async def post_move_robot( robot_move_target: control.RobotMoveTarget, hardware: ThreadManager = Depends(get_hardware), motion_lock: ThreadedAsyncLock = Depends(get_motion_lock))\ -> V1BasicResponse: """Move the robot""" try: async with motion_lock.forbid(): pos = await _do_move(hardware=hardware, robot_move_target=robot_move_target) return V1BasicResponse( message=f"Move complete. New position: {pos}") except ThreadedAsyncForbidden as e: raise V1HandlerError(status_code=status.HTTP_403_FORBIDDEN, message=str(e))
async def get_module_serial( serial: str = Path(..., description="Serial number of the module"), hardware: ThreadManager = Depends(get_hardware)) \ -> ModuleSerial: res = None attached_modules = hardware.attached_modules # type: ignore matching_module = find_matching_module(serial, attached_modules) if matching_module and hasattr(matching_module, 'live_data'): res = matching_module.live_data if not res: raise V1HandlerError(status_code=status.HTTP_404_NOT_FOUND, message="Module not found") return ModuleSerial(status=res.get('status'), data=res.get('data'))
async def post_log_level_local( log_level: LogLevel, hardware: HardwareAPILike = Depends(get_hardware) ) -> V1BasicResponse: """Update local log level""" level = log_level.log_level if not level: raise V1HandlerError(message="log_level must be set", status_code=status.HTTP_422_UNPROCESSABLE_ENTITY) # Level name is upper case level_name = level.value.upper() # Set the log levels for logger_name in ('opentrons', 'robot_server', 'uvicorn'): logging.getLogger(logger_name).setLevel(level.level_id) # Update and save settings await hardware.update_config(log_level=level_name) # type: ignore robot_configs.save_robot_settings(hardware.config) # type: ignore return V1BasicResponse(message=f'log_level set to {level}')
async def patch_pipette_setting( pipette_id: str, settings_update: PipetteSettingsUpdate) \ -> PipetteSettings: # Convert fields to dict of field name to value fields = settings_update.setting_fields or {} field_values = { k: None if v is None else v.value for k, v in fields.items() } if field_values: try: pipette_config.override(fields=field_values, pipette_id=pipette_id) except ValueError as e: raise V1HandlerError( status_code=status.HTTP_412_PRECONDITION_FAILED, message=str(e)) r = _pipette_settings_from_config(pipette_config, pipette_id) return r
async def get_pipette_setting(pipette_id: str) -> PipetteSettings: if pipette_id not in pipette_config.known_pipettes(): raise V1HandlerError(status_code=status.HTTP_404_NOT_FOUND, message=f'{pipette_id} is not a valid pipette id') r = _pipette_settings_from_config(pipette_config, pipette_id) return r