import os import socket import gradio as gr import cadquery as cq from cadquery import exporters import trimesh from io import BytesIO import json import atexit import glob # Cleanup function to remove temporary files def cleanup_temp_files(): temp_files = glob.glob("*.stl") + glob.glob("*.inp") for temp_file in temp_files: try: os.remove(temp_file) except OSError: pass atexit.register(cleanup_temp_files) # Define 2D and 3D view generation functions with error handling def generate_2d_view(file): try: model = process_cad_file(file) two_d_image = model.toSvg() return two_d_image except Exception as e: return f"Error generating 2D view: {str(e)}" def generate_3d_view(file): try: # Import CAD file and create a Workplane object model = cq.importers.importStep(file.name) # Extract solids from the Workplane object solids = model.vals() if hasattr(model, 'vals') else model.solids() if not solids: raise ValueError("No solids found in the CAD model.") # Convert solids into trimesh-compatible meshes meshes = [] for solid in solids: vertices = [] faces = [] for face in solid.tessellate(): vertices.extend(face[0]) # Extract vertices (x, y, z) faces.extend([[face[1][i], face[1][i + 1], face[1][i + 2]] for i in range(0, len(face[1]), 3)]) # Triangular faces mesh = trimesh.Trimesh(vertices=vertices, faces=faces) meshes.append(mesh) # Combine all meshes into a single trimesh object if multiple solids exist combined_mesh = trimesh.util.concatenate(meshes) if len(meshes) > 1 else meshes[0] # Visualize the mesh combined_mesh.show() return "3D view generated successfully." except Exception as e: return f"Error generating 3D view: {str(e)}" # Wrapper function to generate views def generate_views(file): try: two_d_view = generate_2d_view(file) three_d_view = generate_3d_view(file) return two_d_view, three_d_view except Exception as e: return f"Error: {str(e)}", None # Function to process different CAD file formats def process_cad_file(file): file_extension = os.path.splitext(file.name)[-1].lower() if file_extension in [".stp", ".step"]: model = cq.importers.importStep(file.name) elif file_extension in [".iges", ".igs"]: model = cq.importers.importStep(file.name) # Same as STEP for cadquery elif file_extension == ".sldprt": raise NotImplementedError("SLDPRT support is not yet implemented.") elif file_extension == ".dwg": raise NotImplementedError("DWG support is not yet implemented.") else: raise ValueError(f"Unsupported file format: {file_extension}") return model # APDL script generation def generate_apdl_script(file, press_force, material_json): try: material = json.loads(material_json) elastic_modulus = material.get("elastic_modulus", 2e11) poisson = material.get("poisson", 0.3) # Use temporary file path for processing step_file_path = file.name model = process_cad_file(file) stl_file = step_file_path.replace(".stp", ".stl") exporters.export(model, stl_file) apdl_script = f"""\n /PREP7 ! Importing Geometry /import, '{step_file_path}', geom, STEP ! Material Properties MP, EX, 1, {elastic_modulus} MP, PRXY, 1, {poisson} ! Load and Boundary Conditions F, NODE, ALL, FX, {press_force} ! Solve /SOLU SOLVE /POST1 *GET, stress, NODE, 0, S, MAX /EXIT, SAVE """ apdl_file_path = step_file_path.replace(".stp", ".inp") with open(apdl_file_path, "w") as apdl_file: apdl_file.write(apdl_script) return apdl_script, apdl_file_path except Exception as e: return f"Error: {str(e)}", None # Function to find a free port in a range def find_free_port(start_port, end_port): for port in range(start_port, end_port + 1): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: if sock.connect_ex(('localhost', port)) != 0: # Port is free return port raise OSError(f"No available ports in range {start_port}-{end_port}") # Gradio App def main(): with gr.Blocks() as app: gr.Markdown("# Press Tool Design and APDL Script Generator") with gr.Row(): step_input = gr.File( label="Upload CAD File", file_types=[".stp", ".step", ".iges", ".igs", ".sldprt", ".dwg"] ) with gr.Row(): two_d_output = gr.Textbox(label="2D View/Error Message", interactive=False) three_d_output = gr.Textbox(label="3D View/Error Message", interactive=False) generate_views_btn = gr.Button("Generate 2D & 3D Views") with gr.Row(): force_input = gr.Number(label="Press Force (N)", value=1000) material_input = gr.Textbox( label="Material Properties (JSON)", value='{"elastic_modulus": 2e11, "poisson": 0.3}' ) with gr.Row(): script_output = gr.Textbox(label="Generated APDL Script", interactive=False) download_button = gr.File(label="Download Script") generate_apdl_btn = gr.Button("Generate APDL Script") # Events for Visualization generate_views_btn.click( fn=generate_views, inputs=step_input, outputs=[two_d_output, three_d_output] ) # Events for APDL Script def handle_download(apdl_script, apdl_file_path): if apdl_file_path and os.path.exists(apdl_file_path): return apdl_file_path return None generate_apdl_btn.click( fn=generate_apdl_script, inputs=[step_input, force_input, material_input], outputs=[script_output, download_button] ) return app if __name__ == "__main__": app = main() try: # Dynamically find a free port in range 7860-7870 port = find_free_port(7860, 7870) print(f"Launching on port: {port}") app.launch(server_port=port, debug=True) except Exception as e: print(f"Failed to launch: {str(e)}")