import logging, sys, asyncio, random, json, requests, os from pathlib import Path from dotenv import load_dotenv # ============================================================ # LOGGER SETUP # ============================================================ logger = logging.getLogger("ai_engine_dummy") logger.setLevel(logging.INFO) # Handler untuk tampil di console handler = logging.StreamHandler(sys.stdout) formatter = logging.Formatter("[%(asctime)s] %(levelname)s → %(message)s", "%H:%M:%S") handler.setFormatter(formatter) logger.addHandler(handler) # ============================================================ # HELPER FUNCTION (opsional untuk simulasi delay) # ============================================================ async def async_sleep_random(min_s=0.2, max_s=0.8): """ Helper untuk simulasi waktu inferensi secara acak. """ durasi = random.uniform(min_s, max_s) await asyncio.sleep(durasi) # ============================================================== # VALIDATION FUNCTION # ============================================================== def validate_input(required_parts_fields, station_id, cameras, parts, webhook_url): errors = [] # Validate station_id if not station_id or not str(station_id).strip(): errors.append("station_id is missing or empty") # Validate parts for field in required_parts_fields: if field not in parts or not str(parts[field]).strip(): errors.append(f"parts.{field} is missing or empty") # Validate cameras list if not isinstance(cameras, list) or len(cameras) == 0: errors.append("cameras must be a non-empty list") else: for index, cam in enumerate(cameras): if "camera_id" not in cam or not str(cam["camera_id"]).strip(): errors.append(f"camera[{index}].camera_id missing or empty") if "image_base64" not in cam or not str(cam["image_base64"]).strip(): errors.append(f"camera[{index}].image_base64 missing or empty") # if "rtsp_url" not in cam or not str(cam["rtsp_url"]).strip(): # errors.append(f"camera[{index}].rtsp_url missing or empty") # Validate webhook if not webhook_url or not webhook_url.startswith("http"): errors.append("webhook_url is invalid or missing") return errors # ============================================================ # HELPER # ============================================================ def _metadata(): """ load file metadata.json into json data """ path = Path("metadata/product.json") if not path.exists(): return {"status": "error", "message": "metadata.json not found"} with open(path, "r") as f: data = json.load(f) return data def _color_map(): path = Path("metadata/defect.json") if not path.exists(): return [] with open(path, "r") as f: return json.load(f) def model_by_id_metadata(part_id): """ part_id = "4" # id part / product get model_path from metadata by part_id """ metadata = _metadata() id_part = part_id item = next((x for x in metadata if x["id"] == id_part), None) model_path = item['model_path'] return model_path def model_by_pin_metadata(pin_api): """ pin_api = "JI4ACL-GCSBYHBK03" # PIN API from part / product get model_path from metadata by pin_api """ metadata = _metadata() pin_api = pin_api item = next((x for x in metadata if x["pin_api"] == pin_api), None) model_path = item['model_path'] return model_path def color_defect(defect_name): """ Return BGR tuple color for OpenCV bounding box. """ defect_name = defect_name.lower() data = _color_map() item = next((x for x in data if x["class"].lower() == defect_name), None) if item is None: return (0, 255, 0) # Default GREEN jika tidak ditemukan hex_color = item["color"].lstrip('#') r = int(hex_color[0:2], 16) g = int(hex_color[2:4], 16) b = int(hex_color[4:6], 16) return (b, g, r) # Convert to BGR def load_json(path): with open(path, "r") as f: return json.load(f) def start_detection(base_url, payload): return requests.post(f"{base_url}/start-detection", json=payload) def load_config(): load_dotenv() return os.getenv("BASE_URL"), os.getenv("WEBHOOK")