diff --git a/core/physical/domains/cooking_engine.py b/core/physical/domains/cooking_engine.py new file mode 100644 index 0000000..1dbe668 --- /dev/null +++ b/core/physical/domains/cooking_engine.py @@ -0,0 +1,98 @@ +import re + +class CookingEngine: + def __init__(self): + # Built-in step templates + self.templates = ["pasta", "stir-fry", "baking", "salad", "omelette"] + self.threshold_temp_f= 400 + self.threshold_temp_c= 200 + + + + + def analyze(self, detections: list, ocr_text: str, hand_results: list): + """ + Analyzes cooking tasks based on object detections, recipe OCR, and hand positions. + """ + detected_items= set(detections) + current_step = 'Unknown step' + criticality_level= 'normal' + criticality_message= ' ' + + + text_lower= ocr_text.lower() + + + if {'knife', 'cutting board'}.issubset(detected_items): + current_step = 'PREP: Cutting ingredients' + + # PASTA + elif {'uncooked pasta','pot', 'stove'}.issubset(detected_items): + current_step= 'PASTA: Adding pasta to water' + elif {'pot', 'stove'}.issubset(detected_items): + current_step= 'PASTA: Boiling water' + elif {'strainer','sink', 'pot'}.issubset(detected_items): + current_step= 'PASTA: Draining water' + elif {'saucepan','ladle', 'stove'}.issubset(detected_items): + current_step= 'PASTA: Simmering and preparing the sauce' + elif {'saucepan','pasta', 'tongs'}.issubset(detected_items): + current_step= 'PASTA: Combining pasta with sauce' + + #STIR-FRY + elif {'pan','spatula', 'stove'}.issubset(detected_items): + current_step= 'STIR-FRY: Frying of protiens and vegetables' + elif {'pan','oil'}.issubset(detected_items): + current_step= 'STIR-FRY: Sauteing in garlic and oil' + elif {'sauce','pan'}.issubset(detected_items): + current_step= 'STIR-FRY: Adding sauce for thickening' + + #SALAD + elif {'dressing','mixing bowl'}.issubset(detected_items): + current_step= 'SALAD: Emulsifying dressing' + elif {'tongs','mixing bowl'}.issubset(detected_items): + current_step= 'SALAD: Tossing ingredients and dressing' + + + + elif {'whisk','mixing bowl'}.issubset(detected_items): + if 'egg' in text_lower or 'omelette' in text_lower: + current_step= 'OMELETTE: Whisking eggs and seasoning' + else: + current_step= 'BAKING: Mixing dough' + + + #BAKING + + elif {'baking tray','parchment paper'}.issubset(detected_items): + current_step= 'BAKING: Greasing and preparing the baking pan' + elif {'baking tray','oven'}.issubset(detected_items): + current_step= 'BAKING: Baking inside oven' + + #OMELETTE + + elif {'pan','spatula', 'stove'}.issubset(detected_items): + current_step= 'OMELETTE: Flipping and folding the omelette' + elif {'pan','stove'}.issubset(detected_items): + current_step= 'OMELETTE: Puring eggs into hot pan' + + detected_temp = 0.0 + temp_match= re.findall(r'(\d+)\s*°?[FfCc]', ocr_text) + for match in temp_match: + detected_temp= float(match) + + + if 'F' in ocr_text.upper() and detected_temp> self.threshold_temp_f: + criticality_level= 'CRITICAL' + criticality_message= f'High temperature detected({detected_temp} °F)' + if 'C' in ocr_text.upper() and detected_temp> self.threshold_temp_c: + criticality_level= 'CRITICAL' + criticality_message= f'High temperature detected({detected_temp} °C)' + + return { + 'current_step': current_step, + 'criticality_level' : criticality_level, + 'criticality_message': criticality_message + } + + + diff --git a/core/physical/physical_pipeline.py b/core/physical/physical_pipeline.py new file mode 100644 index 0000000..82f842d --- /dev/null +++ b/core/physical/physical_pipeline.py @@ -0,0 +1,19 @@ +from core.physical.domains.cooking_engine import CookingEngine + + +class PhysicalPipeline: + def __init__(self): + + self.cooking_engine = CookingEngine() + + def process(self, task_type, detections, ocr_text, hand_results=[]): + + if task_type == "cooking": + return self.cooking_engine.analyze(detections, ocr_text, hand_results) + + + return { + 'current_step': 'Unknown pipeline task', + 'criticality_level': 'normal', + 'criticality_message': '' + } \ No newline at end of file diff --git a/core/physical/task_recognizer.py b/core/physical/task_recognizer.py index 7654227..3178efc 100644 --- a/core/physical/task_recognizer.py +++ b/core/physical/task_recognizer.py @@ -14,7 +14,9 @@ class Detection(Protocol): class TaskRecognizer: """Recognize physical task categories from camera-frame signals.""" - COOKING_OBJECTS = {"knife", "bowl", "stove", "pot", "pan"} + COOKING_OBJECTS = {"knife", "bowl", "stove", "pot", "pan", + "mixing bowl", "whisk", "baking tray", "oven", + "saucepan", "spatula", "tongs", "cutting board", "strainer"} HARDWARE_REPAIR_OBJECTS = {"screwdriver", "wrench", "circuit_board", "wire"} FORM_KEYWORDS = ("name:", "date:", "signature", "___") diff --git a/tests/__pycache__/__init__.cpython-314.pyc b/tests/__pycache__/__init__.cpython-314.pyc index 1ad79aa..ff35ee9 100644 Binary files a/tests/__pycache__/__init__.cpython-314.pyc and b/tests/__pycache__/__init__.cpython-314.pyc differ diff --git a/tests/unit/test_cooking_engine.py b/tests/unit/test_cooking_engine.py new file mode 100644 index 0000000..573a962 --- /dev/null +++ b/tests/unit/test_cooking_engine.py @@ -0,0 +1,63 @@ +import unittest +from core.physical.domains.cooking_engine import CookingEngine + +class TestCookingEngine(unittest.TestCase): + def setUp(self): + self.engine= CookingEngine() + + + def test_prep_cutting_test(self): + + detections= ['knife', 'cutting board', 'tomato'] + ocr_text= "Slice the tomatoes" + + result= self.engine.analyze( detections, ocr_text, []) + self.assertEqual(result['current_step'], 'PREP: Cutting ingredients') + self.assertEqual(result['criticality_level'], 'normal') + + def test_pasta_sequence(self): + + detections = ["uncooked pasta", "pot", "stove"] + ocr_text = "Add the pasta to boiling water." + + result = self.engine.analyze(detections, ocr_text, []) + + self.assertEqual(result['current_step'], 'PASTA: Adding pasta to water') + + def test_mixing_bowl_tie_breaker_baking(self): + detections = ["whisk", "mixing bowl", "flour"] + ocr_text = "Mix the flour and sugar together." + + result = self.engine.analyze(detections, ocr_text, []) + + self.assertEqual(result['current_step'], 'BAKING: Mixing dough') + + def test_mixing_bowl_tie_breaker_omelette(self): + detections = ["whisk", "mixing bowl"] + ocr_text = "Whisk 3 large EGGS thoroughly." # Capitalized to test case-insensitivity + + result = self.engine.analyze(detections, ocr_text, []) + + self.assertEqual(result['current_step'], 'OMELETTE: Whisking eggs and seasoning') + + def test_critical_temperature_alert_fahrenheit(self): + detections = ["baking tray", "oven"] + ocr_text = "Preheat oven to 425°F." + + result = self.engine.analyze(detections, ocr_text, []) + + self.assertEqual(result['criticality_level'], 'CRITICAL') + self.assertIn('High temperature detected', result['criticality_message']) + + def test_safe_temperature_celsius(self): + detections = ["pan", "stove"] + ocr_text = "Keep oil simmered at 150°C." + + result = self.engine.analyze(detections, ocr_text, []) + + self.assertEqual(result['criticality_level'], 'normal') + +if __name__ == '__main__': + unittest.main() + +