Coverage for farmbot_sidecar_starter_pack/functions/resources.py: 100%
92 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-08-30 13:00 -0700
« prev ^ index » next coverage.py v7.4.4, created at 2024-08-30 13:00 -0700
1"""
2Resources class.
3"""
5# └── functions/resources.py
6# ├── [BROKER] sort_points()
7# ├── [BROKER] sequence()
8# ├── [BROKER] get_seed_tray_cell()
9# ├── [BROKER] detect_weeds()
10# ├── [BROKER] lua()
11# ├── [BROKER] if_statement()
12# └── [BROKER] assertion()
14from .broker import BrokerConnect
15from .information import Information
17ASSERTION_TYPES = ["abort", "recover", "abort_recover", "continue"]
19def validate_assertion_type(assertion_type):
20 """Validate assertion type."""
21 if assertion_type not in ASSERTION_TYPES:
22 raise ValueError(f"Invalid assertion_type: {assertion_type} not in {ASSERTION_TYPES}")
24OPERATORS = ["<", ">", "is", "not", "is_undefined"]
25IF_STATEMENT_VARIABLE_STRINGS = ["x", "y", "z", *[f"pin{str(i)}" for i in range(70)]]
26NAMED_PIN_TYPES = ["Peripheral", "Sensor"]
28def validate_if_statement_args(named_pin_type, variable, operator):
29 """Validate if statement arguments."""
30 if operator not in OPERATORS:
31 raise ValueError(f"Invalid operator: {operator} not in {OPERATORS}")
32 if named_pin_type is None and variable not in IF_STATEMENT_VARIABLE_STRINGS:
33 raise ValueError(f"Invalid variable: {variable} not in {IF_STATEMENT_VARIABLE_STRINGS}")
34 if named_pin_type is not None and named_pin_type not in NAMED_PIN_TYPES:
35 raise ValueError(f"Invalid named_pin_type: {named_pin_type} not in {NAMED_PIN_TYPES}")
37class Resources():
38 """Resources class."""
39 def __init__(self, state):
40 self.broker = BrokerConnect(state)
41 self.info = Information(state)
42 self.state = state
44 # TODO: mark_as()
46 # TODO: sort_points(points, method)
48 def sequence(self, sequence_name):
49 """Executes a predefined sequence."""
50 self.state.print_status(description="Running {sequence_name} sequence.")
52 sequence = self.info.get_resource_by_name("sequences", sequence_name, "name")
53 if sequence is None:
54 return
56 sequence_message = {
57 "kind": "execute",
58 "args": {
59 "sequence_id": sequence["id"],
60 }
61 }
63 self.broker.publish(sequence_message)
66 def get_seed_tray_cell(self, tray_name, tray_cell):
67 """Identifies and returns the location of specified cell in the seed tray."""
68 self.state.print_status(description="Identifying seed tray cell location.")
70 tray_tool = self.info.get_resource_by_name("tools", tray_name, "name")
71 if tray_tool is None:
72 return
73 tray_data = self.info.get_resource_by_name(
74 "points", tray_tool["id"], "tool_id", {"pointer_type": "ToolSlot"})
75 if tray_data is None:
76 self.state.print_status(
77 description=f"{tray_name} must be mounted in a slot.",
78 update_only=True)
79 return
81 cell = tray_cell.upper()
83 seeder_needle_offset = 17.5
84 cell_spacing = 12.5
86 cells = {
87 "A1": {"x": 0, "y": 0},
88 "A2": {"x": 0, "y": 1},
89 "A3": {"x": 0, "y": 2},
90 "A4": {"x": 0, "y": 3},
92 "B1": {"x": -1, "y": 0},
93 "B2": {"x": -1, "y": 1},
94 "B3": {"x": -1, "y": 2},
95 "B4": {"x": -1, "y": 3},
97 "C1": {"x": -2, "y": 0},
98 "C2": {"x": -2, "y": 1},
99 "C3": {"x": -2, "y": 2},
100 "C4": {"x": -2, "y": 3},
102 "D1": {"x": -3, "y": 0},
103 "D2": {"x": -3, "y": 1},
104 "D3": {"x": -3, "y": 2},
105 "D4": {"x": -3, "y": 3}
106 }
108 if cell not in cells:
109 raise ValueError("Seed Tray Cell must be one of **A1** through **D4**")
111 flip = 1
112 if tray_data["pullout_direction"] == 1:
113 flip = 1
114 elif tray_data["pullout_direction"] == 2:
115 flip = -1
116 else:
117 raise ValueError("Seed Tray **SLOT DIRECTION** must be `Positive X` or `Negative X`")
119 a1 = {
120 "x": tray_data["x"] - seeder_needle_offset + (1.5 * cell_spacing * flip),
121 "y": tray_data["y"] - (1.5 * cell_spacing * flip),
122 "z": tray_data["z"]
123 }
125 offset = {
126 "x": cell_spacing * cells[cell]["x"] * flip,
127 "y": cell_spacing * cells[cell]["y"] * flip
128 }
130 cell_xyz = {
131 "x": a1["x"] + offset["x"],
132 "y": a1["y"] + offset["y"],
133 "z": a1["z"],
134 }
136 self.state.print_status(
137 description=f"Cell {tray_cell} is at {cell_xyz}.",
138 update_only=True)
139 return cell_xyz
141 def detect_weeds(self):
142 """Scans the garden to detect weeds."""
143 self.state.print_status(description="Detecting weeds...")
145 detect_weeds_message = {
146 "kind": "execute_script",
147 "args": {
148 "label": "plant-detection"
149 }
150 }
152 self.broker.publish(detect_weeds_message)
154 def lua(self, code_snippet):
155 """Executes custom Lua code snippets to perform complex tasks or automations."""
156 self.state.print_status(description="Running Lua code")
158 lua_message = {
159 "kind": "lua",
160 "args": {
161 "lua": code_snippet.strip()
162 }
163 }
165 self.broker.publish(lua_message)
167 def if_statement(self, variable, operator, value, then_sequence_name=None, else_sequence_name=None, named_pin_type=None):
168 """Performs conditional check and executes actions based on the outcome."""
170 self.state.print_status(description="Executing if statement.")
172 validate_if_statement_args(named_pin_type, variable, operator)
173 if named_pin_type is not None:
174 endpoint = named_pin_type.lower() + "s"
175 resource = self.info.get_resource_by_name(endpoint, variable)
176 if resource is None:
177 return
178 variable = {
179 "kind": "named_pin",
180 "args": {
181 "pin_type": named_pin_type,
182 "pin_id": resource["id"]
183 }
184 }
186 if_statement_message = {
187 "kind": "_if",
188 "args": {
189 "lhs": variable,
190 "op": operator,
191 "rhs": value,
192 "_then": {"kind": "nothing", "args": {}},
193 "_else": {"kind": "nothing", "args": {}},
194 }
195 }
197 sequence_names = {
198 "_then": then_sequence_name,
199 "_else": else_sequence_name,
200 }
201 for key, sequence_name in sequence_names.items():
202 if sequence_name is not None:
203 sequence = self.info.get_resource_by_name("sequences", sequence_name, "name")
204 if sequence is None:
205 return
206 sequence_id = sequence["id"]
207 if_statement_message["args"][key] = {
208 "kind": "execute",
209 "args": {"sequence_id": sequence_id},
210 }
212 self.broker.publish(if_statement_message)
214 def assertion(self, code, assertion_type, recovery_sequence_name=None):
215 """Evaluates an expression."""
216 self.state.print_status(description="Executing assertion.")
218 validate_assertion_type(assertion_type)
220 assertion_message = {
221 "kind": "assertion",
222 "args": {
223 "assertion_type": assertion_type,
224 "lua": code,
225 "_then": {"kind": "nothing", "args": {}},
226 }
227 }
229 if recovery_sequence_name is not None:
230 sequence = self.info.get_resource_by_name("sequences", recovery_sequence_name, "name")
231 if sequence is None:
232 return
233 recovery_sequence_id = sequence["id"]
234 assertion_message["args"]["_then"] = {
235 "kind": "execute",
236 "args": {"sequence_id": recovery_sequence_id},
237 }
239 self.broker.publish(assertion_message)