-
Notifications
You must be signed in to change notification settings - Fork 179
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(api): implement loadLiquidClass command in PE #16814
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
720ec9b
Implement LoadLiquidClass.
ddcc4 4bfa13d
Tests.
ddcc4 a091e40
Lint.
ddcc4 1f97012
Update command schema
ddcc4 d0c98c8
Renamed LiquidClassTouchTipParams
ddcc4 b86fc64
Merge remote-tracking branch 'origin' into loadlc
ddcc4 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
137 changes: 137 additions & 0 deletions
137
api/src/opentrons/protocol_engine/commands/load_liquid_class.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
"""LoadLiquidClass stores the liquid class settings used for a transfer into the Protocol Engine.""" | ||
from __future__ import annotations | ||
|
||
from typing import Optional, Type, TYPE_CHECKING | ||
from typing_extensions import Literal | ||
from pydantic import BaseModel, Field | ||
|
||
from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData | ||
from ..errors import LiquidClassDoesNotExistError | ||
from ..errors.error_occurrence import ErrorOccurrence | ||
from ..errors.exceptions import LiquidClassRedefinitionError | ||
from ..state.update_types import LiquidClassLoadedUpdate, StateUpdate | ||
from ..types import LiquidClassRecord | ||
|
||
if TYPE_CHECKING: | ||
from ..state.state import StateView | ||
from ..resources import ModelUtils | ||
|
||
LoadLiquidClassCommandType = Literal["loadLiquidClass"] | ||
|
||
|
||
class LoadLiquidClassParams(BaseModel): | ||
"""The liquid class transfer properties to store.""" | ||
|
||
liquidClassId: Optional[str] = Field( | ||
None, | ||
description="Unique identifier for the liquid class to store. " | ||
"If you do not supply a liquidClassId, we will generate one.", | ||
) | ||
liquidClassRecord: LiquidClassRecord = Field( | ||
..., | ||
description="The liquid class to store.", | ||
) | ||
|
||
|
||
class LoadLiquidClassResult(BaseModel): | ||
"""Result from execution of LoadLiquidClass command.""" | ||
|
||
liquidClassId: str = Field( | ||
..., | ||
description="The ID for the liquid class that was loaded, either the one you " | ||
"supplied or the one we generated.", | ||
) | ||
|
||
|
||
class LoadLiquidClassImplementation( | ||
AbstractCommandImpl[LoadLiquidClassParams, SuccessData[LoadLiquidClassResult]] | ||
): | ||
"""Load Liquid Class command implementation.""" | ||
|
||
def __init__( | ||
self, state_view: StateView, model_utils: ModelUtils, **kwargs: object | ||
) -> None: | ||
self._state_view = state_view | ||
self._model_utils = model_utils | ||
|
||
async def execute( | ||
self, params: LoadLiquidClassParams | ||
) -> SuccessData[LoadLiquidClassResult]: | ||
"""Store the liquid class in the Protocol Engine.""" | ||
liquid_class_id: Optional[str] | ||
already_loaded = False | ||
|
||
if params.liquidClassId: | ||
liquid_class_id = params.liquidClassId | ||
if self._liquid_class_id_already_loaded( | ||
liquid_class_id, params.liquidClassRecord | ||
): | ||
already_loaded = True | ||
else: | ||
liquid_class_id = ( | ||
self._state_view.liquid_classes.get_id_for_liquid_class_record( | ||
params.liquidClassRecord | ||
) # if liquidClassRecord was already loaded, reuse the existing ID | ||
) | ||
if liquid_class_id: | ||
already_loaded = True | ||
else: | ||
liquid_class_id = self._model_utils.generate_id() | ||
|
||
if already_loaded: | ||
state_update = StateUpdate() # liquid class already loaded, do nothing | ||
else: | ||
state_update = StateUpdate( | ||
liquid_class_loaded=LiquidClassLoadedUpdate( | ||
liquid_class_id=liquid_class_id, | ||
liquid_class_record=params.liquidClassRecord, | ||
) | ||
) | ||
|
||
return SuccessData( | ||
public=LoadLiquidClassResult(liquidClassId=liquid_class_id), | ||
state_update=state_update, | ||
) | ||
|
||
def _liquid_class_id_already_loaded( | ||
self, liquid_class_id: str, liquid_class_record: LiquidClassRecord | ||
) -> bool: | ||
"""Check if the liquid_class_id has already been loaded. | ||
|
||
If it has, make sure that liquid_class_record matches the previously loaded definition. | ||
""" | ||
try: | ||
existing_liquid_class_record = self._state_view.liquid_classes.get( | ||
liquid_class_id | ||
) | ||
except LiquidClassDoesNotExistError: | ||
return False | ||
|
||
if liquid_class_record != existing_liquid_class_record: | ||
raise LiquidClassRedefinitionError( | ||
f"Liquid class {liquid_class_id} conflicts with previously loaded definition." | ||
) | ||
return True | ||
|
||
|
||
class LoadLiquidClass( | ||
BaseCommand[LoadLiquidClassParams, LoadLiquidClassResult, ErrorOccurrence] | ||
): | ||
"""Load Liquid Class command resource model.""" | ||
|
||
commandType: LoadLiquidClassCommandType = "loadLiquidClass" | ||
params: LoadLiquidClassParams | ||
result: Optional[LoadLiquidClassResult] | ||
|
||
_ImplementationCls: Type[ | ||
LoadLiquidClassImplementation | ||
] = LoadLiquidClassImplementation | ||
|
||
|
||
class LoadLiquidClassCreate(BaseCommandCreate[LoadLiquidClassParams]): | ||
"""Load Liquid Class command creation request.""" | ||
|
||
commandType: LoadLiquidClassCommandType = "loadLiquidClass" | ||
params: LoadLiquidClassParams | ||
|
||
_CommandCls: Type[LoadLiquidClass] = LoadLiquidClass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely!
Will answer your question here.
StateView.get_summary()
returns a snapshot of the state of a selection of the engine components. The selection is determined mostly by client needs (or any entity that starts a run and needs to monitor its progress) as this state summary is a big part of the 'run data'.Places where run data (and by implication, state summary) is provided-
/runs
,/maintenance_runs
andruns/{runId}
/protocols
endpoints that return an analysis result. The analysis result is simply the 'final run data' of a run with a virtual robot.analyze
cli tool as well as the opentronssimulate
andexecute
cli commandsThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for that info!
I think I want to handle the summary implementation in a separate PR, since this PR is quite large already.
Apparently, the
StateSummary
feeds intoAnalyzeResults
, which says it needs to be kept in sync withrobot-server
's model, etc., so there are a lot of places I'll need to update. I'll ask you later how those classes are related to each other. But I'd like to handle that in a separate PR, and merge this PR as-is.