import os import asyncio import base64 import uuid import datetime import sys import json from flask import Flask, request, jsonify, current_app from pymongo import MongoClient from APIs import generate, EdamamAPI from APIs.mqtt import send_command from microwaveCookPlanner import MicrowaveCookPlanner import safety_checker sys.path.insert(0, '..') try: from shared import config except ImportError: from ..shared import config app = Flask(__name__) # --------------------------------------------------------- # Configuration & Setup # --------------------------------------------------------- # Configure MongoDB connection MONGO_URI = os.getenv("MONGO_URI", "mongodb://localhost:27017/") client = MongoClient(MONGO_URI) db = client["microwave_network_db"] cooking_collection = db["cooking_parameters"] telemetry_collection = db["telemetry_data"] # Ensure the camera image storage directory exists when the app starts CAMERA_IMAGE_DIR = "storage/dishPhotos" os.makedirs(CAMERA_IMAGE_DIR, exist_ok=True) # --------------------------------------------------------- # Classes # --------------------------------------------------------- microwave_cook_planner = MicrowaveCookPlanner() # --------------------------------------------------------- # Routes # --------------------------------------------------------- @app.route("/") def hello_world(): gen = generate(prompt="Say Hello, to the user !") print(gen) return f"

{gen}

" @app.route("/cooking-params", methods=["POST"]) async def cooking_params(): try: data = request.get_json() if not data: return jsonify({"error": "Invalid or missing JSON payload"}), 400 # Extract user or device parameters height_cm = float(data.get("dish_height", 4.0)) initial_temp_c = float(data.get("ir_initial_temp", 20.0)) microwave_wattage = int(data.get("microwave_wattage", 900)) defrost_mode = bool(data.get("defrost_mode", False)) # 1. Save Camera Image camera_image_b64 = data.get("camera_image") if not camera_image_b64: return jsonify({"error": "Missing required field 'camera_image'"}), 400 filename = f"dish_{uuid.uuid4().hex}.jpg" filepath = os.path.join(CAMERA_IMAGE_DIR, filename) try: with open(filepath, "wb") as f: f.write(base64.b64decode(camera_image_b64)) data["camera_image"] = filepath except Exception as e: current_app.logger.exception("Failed to save camera image") return jsonify({"error": f"Failed to save camera image: {str(e)}"}), 500 # 2. Run Vision Safety Check & Cook Planner Concurrently try: if getattr(config, "DEBUG", True): print(f"[Debug] Running safety check on predefined image") safety_task = asyncio.to_thread(safety_checker.check_dish_safety, os.path.join(CAMERA_IMAGE_DIR, "dish_0de0ee1dab8949fc8e78796947e24ed3.jpg")) else: safety_task = asyncio.to_thread(safety_checker.check_dish_safety, filepath) planner_task = asyncio.to_thread( microwave_cook_planner.generate_plan, image_path=filepath, height_cm=height_cm, initial_temp_c=initial_temp_c, microwave_wattage=microwave_wattage, defrost_mode=defrost_mode ) # Execute both concurrently and await results safety_result, cook_plan = await asyncio.gather(safety_task, planner_task) except Exception as e: current_app.logger.exception("Exception during safety check or cook planning") return jsonify({"error": f"Task execution failed: {str(e)}"}), 500 # 3. Evaluate Safety Result data["safety_check"] = safety_result if not safety_result.get("is_safe", True): current_app.logger.warning(f"Unsafe dish detected: {safety_result}") return jsonify({ "error": "Safety hazard detected in microwave area", "is_safe": False, "warning": safety_result.get("warning_message", "Unsafe materials detected."), "detected_hazards": safety_result.get("detected_hazards", []) }), 200 # 4. Attach Cooking Plan & Save to MongoDB data["analysis_results"] = cook_plan try: cooking_collection.insert_one(data) data.pop("_id", None) except Exception as e: return jsonify({"error": f"Database error: {str(e)}"}), 500 return jsonify(cook_plan), 201 except Exception as e: current_app.logger.exception("Exception in /cooking-params") @app.route("/telemetry", methods=["POST"]) def telemetry(): data = request.get_json() if data is None: return jsonify({"error": "Invalid or missing JSON payload"}), 400 # If the payload was double-encoded as a string, deserialize it if isinstance(data, str): try: data = json.loads(data) except (json.JSONDecodeError, TypeError): return jsonify({"error": "String payload could not be parsed as JSON"}), 400 if not isinstance(data, dict): return jsonify({"error": "Expected a JSON object/dictionary"}), 400 # Stamp UTC timestamp for Node-RED queries data["received_at"] = datetime.datetime.now(datetime.timezone.utc).isoformat() try: telemetry_collection.insert_one(data) return jsonify({"status": "success", "message": "Telemetry saved"}), 200 except Exception as e: return jsonify({"error": f"Database error: {str(e)}"}), 500 @app.route("/debug/telemetryrequest", methods=["GET"]) def debug(): # Construct external HTTP endpoint dynamically based on incoming request host telemetry_url = f"{request.host_url.rstrip('/')}/telemetry" cmd_payload = { "action": "request_telemetry", "endpoint": telemetry_url } try: send_command(topic="cmd/all", payload=cmd_payload) return jsonify({ "status": "Telemetry command sent to cmd/all", "published_payload": cmd_payload }), 200 except Exception as e: return jsonify({"error": f"Failed to publish MQTT command: {str(e)}"}), 500 @app.route("/debug", methods=["GET"]) def debug(): return safety_checker.check_dish_safety(os.path.join(CAMERA_IMAGE_DIR, "dish_0de0ee1dab8949fc8e78796947e24ed3.jpg")) if __name__ == "__main__": app.run(debug=getattr(config, "DEBUG", True))