import { useState } from "react"; import { supabase } from "@/integrations/supabase/client"; import { UrdfData } from "@/lib/types"; import { toast } from "sonner"; export const useUrdfParser = () => { const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [data, setData] = useState(null); const parseUrdf = async (urdfContent: string) => { const requestId = `urdf-${Date.now()}`; // Generate unique ID for tracking this request console.log(`[${requestId}] 🚀 Urdf Parser: Starting parse request`); console.log( `[${requestId}] 🔍 Content preview:`, urdfContent?.substring(0, 100) + "..." ); // Check if the Urdf content is too large const MAX_SIZE = 4 * 1024 * 1024; // 4MB limit if (urdfContent.length > MAX_SIZE) { const errorMessage = `Urdf content too large (${( urdfContent.length / 1024 / 1024 ).toFixed(2)}MB). Maximum size is ${MAX_SIZE / 1024 / 1024}MB.`; console.error(`[${requestId}] ❌ ${errorMessage}`); setError(errorMessage); toast.error("File too large", { description: errorMessage, duration: 5000, }); return null; } setIsLoading(true); setError(null); const startTime = performance.now(); try { // Make sure the urdfContent is a string if (typeof urdfContent !== "string") { throw new Error("Urdf content must be a string"); } console.log( `[${requestId}] 📡 Calling Supabase edge function "urdf-parser"...` ); const { data: rawResponse, error } = await supabase.functions.invoke( "urdf-parser", { body: { urdfContent }, } ); if (error) { console.error(`[${requestId}] ❌ Supabase function error:`, error); // Handle different error cases if (error.message.includes("non-2xx status code")) { // Server error from edge function const serverErrorMsg = "The Urdf parser encountered a server error. It might be due to the complexity of your Urdf file or temporary server issues."; console.error(`[${requestId}] 🔥 Server error in edge function`); // Check if we got fallback data despite the error if (rawResponse && rawResponse.fallback) { console.log(`[${requestId}] 🛟 Using fallback data from server`); toast.warning("Reduced Urdf Data", { description: "Limited information extracted from your Urdf file due to parsing limitations.", duration: 5000, }); // Use the fallback data setData(rawResponse.fallback); return rawResponse.fallback; } toast.error("Urdf Parser Error", { description: serverErrorMsg, duration: 5000, }); setError(serverErrorMsg); } else if (error.message.includes("timeout")) { // Timeout error const timeoutMsg = "The Urdf parser timed out. Your file might be too complex to process within the allowed time."; console.error(`[${requestId}] ⏱️ Edge function timed out`); toast.error("Processing Timeout", { description: timeoutMsg, duration: 5000, }); setError(timeoutMsg); } else { // Generic error setError(error.message); toast.error("Error Parsing Urdf", { description: error.message, duration: 5000, }); } throw error; } const endTime = performance.now(); console.log( `[${requestId}] ✅ Edge function responded in ${( endTime - startTime ).toFixed(2)}ms` ); console.log( `[${requestId}] 📊 Response data structure:`, Object.keys(rawResponse || {}) ); console.log(`[${requestId}] 📦 Full response:`, rawResponse); // Transform raw data into UrdfData format let transformedData: UrdfData | null = null; if (rawResponse && rawResponse.success && rawResponse.data) { const responseData = rawResponse.data; // Count joint types from raw joints array const jointTypes: { [key: string]: number } = { revolute: 0, prismatic: 0, continuous: 0, fixed: 0, other: 0, }; if (responseData.joints && Array.isArray(responseData.joints)) { responseData.joints.forEach((joint) => { const type = joint.type?.toLowerCase() || "other"; if (type in jointTypes) { jointTypes[type]++; } else { jointTypes.other++; } }); } // Transform links to expected format const transformedLinks = responseData.links?.map((link) => ({ name: link.name, mass: link.mass, })) || []; // Calculate total mass - either use the one from the response or sum up links const totalMass = responseData.total_mass || transformedLinks.reduce((sum, link) => sum + (link.mass || 0), 0); // Create the transformed data object transformedData = { name: responseData.name, description: "A detailed 3D model of a robotic system with articulated joints and components.", mass: totalMass, dofs: responseData.num_joints || responseData.joints?.length || 0, joints: { revolute: jointTypes.revolute, prismatic: jointTypes.prismatic, continuous: jointTypes.continuous, fixed: jointTypes.fixed, other: jointTypes.other, }, links: transformedLinks, // Default materials since we don't have this info from Urdf materials: [{ name: "Metal", percentage: 100 }], }; console.log(`[${requestId}] 🔄 Transformed data:`, transformedData); } setData(transformedData); return transformedData; } catch (err) { const errorMessage = err instanceof Error ? err.message : "Unknown error occurred"; const endTime = performance.now(); console.error( `[${requestId}] ❌ Error parsing Urdf after ${( endTime - startTime ).toFixed(2)}ms:`, err ); console.error(`[${requestId}] 🧩 Error details:`, { message: errorMessage, type: err instanceof Error ? err.constructor.name : typeof err, stack: err instanceof Error ? err.stack : undefined, }); setError(errorMessage); return null; } finally { setIsLoading(false); console.log( `[${requestId}] 🏁 Urdf parsing request completed, isLoading set to false` ); } }; return { parseUrdf, data, isLoading, error, }; };