import os import asyncio import base64 import uuid import datetime import sys import json from flask import Flask, request, jsonify 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: print(f"[Error] Failed to save camera image: {str(e)}") return jsonify({"error": f"Failed to save camera image: {str(e)}"}), 500 # 2. Run Vision Safety Check & Cook Planner Concurrently try: 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: print(f"[Error] Exception during safety check or cook planning: {str(e)}") 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): print(f"[Safety Warning] 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: print(f"[Error] Exception in /cooking-params: {str(e)}") @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", 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 if __name__ == "__main__": app.run(debug=getattr(config, "DEBUG", True))