1+ #!/usr/bin/env python3
2+ """Experiment application for the Protein Design experiment"""
3+
4+ import time
5+ from datetime import datetime
6+ from pathlib import Path
7+
8+ from madsci .common .types .experiment_types import ExperimentDesign
9+ from madsci .client import ExperimentClient , WorkcellClient , LocationClient , ResourceClient
10+ from madsci .common .types .node_types import NodeDefinition
11+ from madsci .common .types .resource_types import Resource
12+ from madsci .experiment_application import (
13+ ExperimentApplication ,
14+ ExperimentApplicationConfig ,
15+ )
16+ from pydantic import AnyUrl
17+
18+ # import helper_functions
19+
20+
21+
22+
23+
24+ class PDApp (ExperimentApplication ):
25+ """PD Experiment Application
26+
27+ # TODO:
28+
29+ """
30+
31+ experiment_design = ExperimentDesign (
32+ experiment_name = "PD_App" ,
33+ )
34+ config = ExperimentApplicationConfig (node_url = AnyUrl ("http://localhost:6000" ))
35+ experiment_client = ExperimentClient ()
36+ workcell_client = WorkcellClient ()
37+ location_client = LocationClient ()
38+ resource_client = ResourceClient ()
39+ experiment_id = None
40+ experiment_label = None
41+
42+
43+ def __init__ (self ) -> None :
44+ """Initializes the PD Experiment App"""
45+
46+ super ().__init__ ()
47+ self .init_assay_plate_resource_template ()
48+
49+ def init_assay_plate_resource_template (self ):
50+ """Initializes assay plate resource template"""
51+
52+ self .resource_client .create_template (
53+ resource = Resource (
54+ resource_description = "NEST PCR plate 200ul" ,
55+ ),
56+ template_name = "opentrons_96_wellplate_200ul_pcr_full_skirt" ,
57+ description = "Template for 200ul PCR plate" ,
58+ tags = ["Plate" , "ANSI/SLAS" , "96 Well" , "PCR" , "Labware" ],
59+ )
60+
61+ self .resource_client .create_template (
62+ resource = Resource (
63+ resource_description = "ot2 20ul tiprack" ,
64+ ),
65+ template_name = "opentrons_96_filtertiprack_20ul" ,
66+ description = "Template for ot2 20ul tiprack" ,
67+ tags = ["Tiprack" , "ANSI/SLAS" , "96 Well" , "Labware" ],
68+ )
69+
70+ self .resource_client .create_template (
71+ resource = Resource (
72+ resource_description = "otflex 50ul tiprack" ,
73+ ),
74+ template_name = "opentrons_flex_96_filtertiprack_50ul" ,
75+ description = "Template for OT-Flex 50ul tiprack" ,
76+ tags = ["Tiprack" , "ANSI/SLAS" , "96 Well" , "Labware" ],
77+ )
78+
79+ self .resource_client .create_template (
80+ resource = Resource (
81+ resource_description = "otflex 200ul tiprack" ,
82+ ),
83+ template_name = "opentrons_flex_96_filtertiprack_200ul" ,
84+ description = "Template for 200ul OT-Flex tiprack" ,
85+ tags = ["Tiprack" , "ANSI/SLAS" , "96 Well" , "Labware" ],
86+ )
87+
88+
89+ def push_new_assay_plate_resource (
90+ self ,
91+ plate_num : int ,
92+ location_name : str ,
93+ experiment_id : int ,
94+ name : str ,
95+ ) -> None | Resource :
96+ """
97+ Pushes a new assay plate resource into the specified location, popping an existing plate in that location if necessary.
98+ """
99+ # get the resource id of the resource associated with the given location
100+ associated_resource_id = self .location_client .get_location_by_name (location_name ).resource_id
101+ print ("Location name: " , location_name )
102+ print ("ASSC Resource id: " , associated_resource_id )
103+ # get the resource object from the resource id
104+ resource_object = self .resource_client .get_resource (associated_resource_id )
105+
106+ # check if the resource object currently has child resources (a plate already at that location)
107+ old_plate = None
108+ if resource_object .child :
109+ # there is already a plate at this location
110+ self .logger .log_info (f"A plate with ID { resource_object .child .resource_id } already exists at location: { location_name } " )
111+
112+ # pop the old plate
113+ popped_plate , updated_parent = self .resource_client .pop (resource = associated_resource_id )
114+ self .logger .log_info (f"Popped plate with ID { resource_object .child .resource_id } from location: { location_name } " )
115+ old_plate = popped_plate
116+
117+
118+ # create a new assay plate resource and push it into the resource object associated with the given location
119+ # if name == "opentrons_96_wellplate_200ul_pcr_full_skirt":
120+
121+ new_plate = self .resource_client .create_resource_from_template (
122+ template_name = name ,
123+ resource_name = f"res_{ experiment_id } _plate{ plate_num } " ,
124+ )
125+
126+ self .logger .log_info (f"Created new plate resource { new_plate .resource_name } , { new_plate .resource_id } " )
127+
128+ self .resource_client .push (
129+ resource = associated_resource_id ,
130+ child = new_plate .resource_id ,
131+ )
132+ self .logger .log_info (f"Pushed new plate resource into location { location_name } " )
133+ return new_plate , old_plate
134+
135+ def run_experiment (self ) -> None :
136+ """main experiment function"""
137+
138+ #TODO: plate starting on ot2 patrick deck 6 on temp block
139+ # new_plate, old_plate = self.push_new_assay_plate_resource(
140+ # location_name="ot2_patrick_nest6_temp_block_wide",
141+ # )
142+
143+ # DEFINE PATHS AND VARIABLES ========
144+ run_robots = False # if False, no robots will run
145+ run_resources = True
146+ test_prints = True # if True, will print out extra info for testing purposes
147+
148+ # Experiment ID and name
149+ experiment_id = self .experiment .experiment_id
150+ experiment_label = "1"
151+
152+ # Directory paths
153+ app_directory = Path (__file__ ).parent .parent # experiment app
154+ wf_directory = app_directory / "workflows" # workflows
155+ run_directory = wf_directory / "run_instrument"
156+ transfers_directory = wf_directory / "transfers"
157+ protocol_directory = app_directory / "protocols" # protocols
158+
159+ # Workflow paths
160+ run_ot2_wf = "run_ot2_wf.yaml"
161+ run_thermo_wf = run_directory / "run_thermo.yaml"
162+
163+ ot2_to_thermocycler = (
164+ transfers_directory / "ot2_to_thermocycler.yaml"
165+ )
166+
167+
168+ # Protocol paths (for OT-2)
169+ golden_gate_protocol = protocol_directory / "pd_golden_gate_ot2_v2.py"
170+
171+ seal_and_thermocycle = (
172+ transfers_directory / "seal_and_thermocycle_pcr.yaml"
173+ )
174+
175+ payload = {}
176+
177+ temp_ot2_file_str = "basic_config_alpha.py"
178+
179+ # combination_data = self.rest_handler.collect_combinations(oracle_id=1008)
180+ # print("combination_data", combination_data)
181+ # print(combination_data["combinations"])
182+ # print(combination_data["use_combinations"])
183+ # print(combination_data["non_combinatorial_sources"])
184+
185+ # payload["combinations"] = combination_data["combinations"]
186+ # payload["use_combinations"] = combination_data["use_combinations"]
187+ # payload["non_combinatorial_sources"] = combination_data["non_combinatorial_sources"]
188+
189+
190+
191+ # EXPERIMENT ACTIONS -------------------------------------------------------
192+
193+ # run ot2 protocol step 1
194+ # ot2_replacement_variables = helper_functions.collect_ot2_replacement_variables(payload)
195+ # temp_ot2_file_str = helper_functions.generate_ot2_protocol(golden_gate_protocol, ot2_replacement_variables)
196+ payload ["current_ot2_protocol" ] = temp_ot2_file_str
197+ workflow = self .workcell_client .submit_workflow (
198+ run_ot2_wf .resolve (),
199+ file_inputs = {
200+ "ot2_protocol" : payload ["current_ot2_protocol" ],
201+ },
202+ )
203+
204+ ###### depending on combinatorial length, will need additional tips
205+ #swap tip boxes
206+
207+ #run ot2 protocol step 2
208+
209+ #swap tip boxes
210+
211+ #run ot2 protocol step 3 with master mix multi dispense
212+
213+ #########################
214+
215+ # transfer destination plate to thermocycler and run
216+ # workflow = self.workcell_client.submit_workflow(
217+ # seal_and_thermocycle.resolve(),
218+ # )
219+
220+ # #run thermocycler
221+ # workflow = self.workcell_client.submit_workflow(
222+ # run_thermo_wf.resolve(),
223+ # )
224+
225+
226+
227+
228+
229+ if __name__ == "__main__" :
230+ exp_app = PDApp ()
231+
232+ current_time = datetime .now ()
233+
234+ # Start experiment run
235+ with exp_app .manage_experiment (
236+ run_name = f"PD_Experiment{ current_time .strftime ('%Y%m%d_%H%M%S' )} " ,
237+ run_description = "PD experiment" ,
238+ ):
239+ exp_app .start_app ()
0 commit comments