connection established! it gets data properly now

This commit is contained in:
ahujasid 2025-03-07 15:48:40 +05:30
parent 4372edf2a7
commit 2efcd9c7f6
5 changed files with 473 additions and 285 deletions

View File

@ -57,7 +57,7 @@ class BlenderMCPServer:
try:
# Set a timeout for receiving data
self.client.settimeout(15.0)
data = self.client.recv(4096)
data = self.client.recv(8192) # Increased buffer size
if not data:
print("Empty data received, client may have disconnected")
break
@ -65,9 +65,19 @@ class BlenderMCPServer:
try:
print(f"Received data: {data.decode('utf-8')}")
command = json.loads(data.decode('utf-8'))
# Process the command
print(f"Processing command: {command.get('type')}")
response = self.execute_command(command)
print(f"Sending response: {json.dumps(response)[:100]}...") # Truncate long responses in log
self.client.sendall(json.dumps(response).encode('utf-8'))
# Send the response - handle large responses by chunking if needed
response_json = json.dumps(response)
print(f"Sending response: {response_json[:100]}...") # Truncate long responses in log
# Send in one go - most responses should be small enough
self.client.sendall(response_json.encode('utf-8'))
print("Response sent successfully")
except json.JSONDecodeError:
print(f"Invalid JSON received: {data.decode('utf-8')}")
self.client.sendall(json.dumps({
@ -89,8 +99,9 @@ class BlenderMCPServer:
print(f"Error receiving data: {str(e)}")
break
self.client.close()
self.client = None
if self.client:
self.client.close()
self.client = None
except socket.timeout:
# This is normal - just continue the loop
continue
@ -133,9 +144,14 @@ class BlenderMCPServer:
handler = handlers.get(cmd_type)
if handler:
try:
print(f"Executing handler for {cmd_type}")
result = handler(**params)
print(f"Handler execution complete")
return {"status": "success", "result": result}
except Exception as e:
print(f"Error in handler: {str(e)}")
import traceback
traceback.print_exc()
return {"status": "error", "message": str(e)}
else:
return {"status": "error", "message": f"Unknown command type: {cmd_type}"}
@ -153,57 +169,30 @@ class BlenderMCPServer:
"""Get information about the current Blender scene"""
try:
print("Getting scene info...")
# Simplify the scene info to reduce data size
scene_info = {
"name": bpy.context.scene.name,
"object_count": len(bpy.context.scene.objects),
"objects": [],
"materials": [],
"camera": {},
"render_settings": {},
"materials_count": len(bpy.data.materials),
}
# Collect object information (limit to first 20 objects to prevent oversized responses)
# Collect minimal object information (limit to first 10 objects)
for i, obj in enumerate(bpy.context.scene.objects):
if i >= 20: # Limit to 20 objects to prevent oversized responses
if i >= 10: # Reduced from 20 to 10
break
obj_info = {
"name": obj.name,
"type": obj.type,
"location": [float(obj.location.x), float(obj.location.y), float(obj.location.z)],
"rotation": [float(obj.rotation_euler.x), float(obj.rotation_euler.y), float(obj.rotation_euler.z)],
"scale": [float(obj.scale.x), float(obj.scale.y), float(obj.scale.z)],
"visible": obj.visible_get(),
# Only include basic location data
"location": [round(float(obj.location.x), 2),
round(float(obj.location.y), 2),
round(float(obj.location.z), 2)],
}
scene_info["objects"].append(obj_info)
# Collect material information (limit to first 10 materials)
for i, mat in enumerate(bpy.data.materials):
if i >= 10:
break
mat_info = {
"name": mat.name,
"use_nodes": bool(mat.use_nodes),
}
scene_info["materials"].append(mat_info)
# Camera information
camera = bpy.context.scene.camera
if camera:
scene_info["camera"] = {
"name": camera.name,
"location": [float(camera.location.x), float(camera.location.y), float(camera.location.z)],
"rotation": [float(camera.rotation_euler.x), float(camera.rotation_euler.y), float(camera.rotation_euler.z)],
}
# Render settings (simplified)
render = bpy.context.scene.render
scene_info["render_settings"] = {
"engine": render.engine,
"resolution_x": int(render.resolution_x),
"resolution_y": int(render.resolution_y),
}
print(f"Scene info collected: {len(scene_info['objects'])} objects, {len(scene_info['materials'])} materials")
print(f"Scene info collected: {len(scene_info['objects'])} objects")
return scene_info
except Exception as e:
print(f"Error in get_scene_info: {str(e)}")

View File

@ -44,42 +44,62 @@ class BlenderConnection:
finally:
self.sock = None
# Replace the single recv call with this chunked approach
def receive_full_response(self, sock, buffer_size=8192):
"""Receive the complete response, potentially in multiple chunks"""
chunks = []
sock.settimeout(10.0)
# Use a consistent timeout value that matches the addon's timeout
sock.settimeout(15.0) # Match the addon's timeout
try:
while True:
chunk = sock.recv(buffer_size)
if not chunk:
break
chunks.append(chunk)
# Check if we've received a complete JSON object
try:
data = b''.join(chunks)
json.loads(data.decode('utf-8'))
# If we get here, it parsed successfully
logger.info(f"Received complete response ({len(data)} bytes)")
return data
except json.JSONDecodeError:
# Incomplete JSON, continue receiving
continue
chunk = sock.recv(buffer_size)
if not chunk:
# If we get an empty chunk, the connection might be closed
if not chunks: # If we haven't received anything yet, this is an error
raise Exception("Connection closed before receiving any data")
break
chunks.append(chunk)
# Check if we've received a complete JSON object
try:
data = b''.join(chunks)
json.loads(data.decode('utf-8'))
# If we get here, it parsed successfully
logger.info(f"Received complete response ({len(data)} bytes)")
return data
except json.JSONDecodeError:
# Incomplete JSON, continue receiving
continue
except socket.timeout:
# If we hit a timeout during receiving, break the loop and try to use what we have
logger.warning("Socket timeout during chunked receive")
break
except (ConnectionError, BrokenPipeError, ConnectionResetError) as e:
logger.error(f"Socket connection error during receive: {str(e)}")
raise # Re-raise to be handled by the caller
except socket.timeout:
logger.warning("Socket timeout during chunked receive")
except Exception as e:
logger.error(f"Error during receive: {str(e)}")
raise
# Return whatever we got
data = b''.join(chunks)
if not data:
raise Exception("Empty response received")
return data
# If we get here, we either timed out or broke out of the loop
# Try to use what we have
if chunks:
data = b''.join(chunks)
logger.info(f"Returning data after receive completion ({len(data)} bytes)")
try:
# Try to parse what we have
json.loads(data.decode('utf-8'))
return data
except json.JSONDecodeError:
# If we can't parse it, it's incomplete
raise Exception("Incomplete JSON response received")
else:
raise Exception("No data received")
# Then in send_command:
# response_data = self.sock.recv(65536)
def send_command(self, command_type: str, params: Dict[str, Any] = None) -> Dict[str, Any]:
"""Send a command to Blender and return the response"""
if not self.sock and not self.connect():
@ -98,11 +118,10 @@ class BlenderConnection:
self.sock.sendall(json.dumps(command).encode('utf-8'))
logger.info(f"Command sent, waiting for response...")
# Set a timeout for receiving
self.sock.settimeout(30.0) # Increased timeout
# Set a timeout for receiving - use the same timeout as in receive_full_response
self.sock.settimeout(15.0) # Match the addon's timeout
# Receive the response
# response_data = self.sock.recv(65536) # Increase buffer size for larger responses
# Receive the response using the improved receive_full_response method
response_data = self.receive_full_response(self.sock)
logger.info(f"Received {len(response_data)} bytes of data")
@ -116,42 +135,55 @@ class BlenderConnection:
return response.get("result", {})
except socket.timeout:
logger.error("Socket timeout while waiting for response from Blender")
# Try to reconnect
self.disconnect()
if self.connect():
logger.info("Reconnected to Blender after timeout")
raise Exception("Timeout waiting for Blender response")
# Don't try to reconnect here - let the get_blender_connection handle reconnection
# Just invalidate the current socket so it will be recreated next time
self.sock = None
raise Exception("Timeout waiting for Blender response - try simplifying your request")
except (ConnectionError, BrokenPipeError, ConnectionResetError) as e:
logger.error(f"Socket connection error: {str(e)}")
self.sock = None
raise Exception(f"Connection to Blender lost: {str(e)}")
except json.JSONDecodeError as e:
logger.error(f"Invalid JSON response from Blender: {str(e)}")
# Try to log what was received
if response_data:
if 'response_data' in locals() and response_data:
logger.error(f"Raw response (first 200 bytes): {response_data[:200]}")
raise Exception(f"Invalid response from Blender: {str(e)}")
except Exception as e:
logger.error(f"Error communicating with Blender: {str(e)}")
# Try to reconnect
self.disconnect()
if self.connect():
logger.info("Reconnected to Blender")
# Don't try to reconnect here - let the get_blender_connection handle reconnection
self.sock = None
raise Exception(f"Communication error with Blender: {str(e)}")
@asynccontextmanager
async def server_lifespan(server: FastMCP) -> AsyncIterator[Dict[str, Any]]:
"""Manage server startup and shutdown lifecycle"""
blender = BlenderConnection(host="localhost", port=9876)
# We don't need to create a connection here since we're using the global connection
# for resources and tools
try:
# Connect to Blender on startup
connected = blender.connect()
if not connected:
logger.warning("Could not connect to Blender on startup. Make sure the Blender addon is running.")
# Just log that we're starting up
logger.info("BlenderMCP server starting up")
# Return the Blender connection in the context
yield {"blender": blender}
# Try to connect to Blender on startup to verify it's available
try:
# This will initialize the global connection if needed
blender = get_blender_connection()
logger.info("Successfully connected to Blender on startup")
except Exception as e:
logger.warning(f"Could not connect to Blender on startup: {str(e)}")
logger.warning("Make sure the Blender addon is running before using Blender resources or tools")
# Return an empty context - we're using the global connection
yield {}
finally:
# Disconnect from Blender on shutdown
blender.disconnect()
logger.info("Disconnected from Blender")
# Clean up the global connection on shutdown
global _blender_connection
if _blender_connection:
logger.info("Disconnecting from Blender on shutdown")
_blender_connection.disconnect()
_blender_connection = None
logger.info("BlenderMCP server shut down")
# Create the MCP server with lifespan support
mcp = FastMCP(
@ -162,68 +194,88 @@ mcp = FastMCP(
# Resource endpoints
# Global connection for resources (workaround since resources can't access context)
# Global connection for resources (since resources can't access context)
_blender_connection = None
def get_blender_connection():
"""Get or create a persistent Blender connection"""
global _blender_connection
# If we have an existing connection, check if it's still valid
if _blender_connection is not None:
# Test if the connection is still alive with a simple ping
try:
# Just try to send a small message to check if the socket is still connected
_blender_connection.sock.sendall(b'')
return _blender_connection
except Exception as e:
# Connection is dead, close it and create a new one
logger.warning(f"Existing connection is no longer valid: {str(e)}")
try:
_blender_connection.disconnect()
except:
pass
_blender_connection = None
# Create a new connection if needed
if _blender_connection is None:
_blender_connection = BlenderConnection(host="localhost", port=9876)
_blender_connection.connect()
if not _blender_connection.connect():
logger.error("Failed to connect to Blender")
_blender_connection = None
raise Exception("Could not connect to Blender. Make sure the Blender addon is running.")
logger.info("Created new persistent connection to Blender")
return _blender_connection
@mcp.resource("blender://ping")
def ping_blender() -> str:
"""Simple ping to test Blender connectivity"""
blender = get_blender_connection()
"""Ping the Blender server to check connectivity"""
try:
blender = get_blender_connection()
result = blender.send_command("ping")
return f"Ping successful: {json.dumps(result)}"
return json.dumps({"status": "success", "result": result})
except Exception as e:
return f"Ping failed: {str(e)}"
logger.error(f"Error pinging Blender: {str(e)}")
return json.dumps({"status": "error", "message": str(e)})
@mcp.resource("blender://simple")
def get_simple_info() -> str:
"""Get simplified information from Blender"""
blender = get_blender_connection()
"""Get basic information about the Blender instance"""
try:
blender = get_blender_connection()
result = blender.send_command("get_simple_info")
return json.dumps(result, indent=2)
return json.dumps({"status": "success", "result": result})
except Exception as e:
return f"Error getting simple info: {str(e)}"
logger.error(f"Error getting simple info from Blender: {str(e)}")
return json.dumps({"status": "error", "message": str(e)})
@mcp.resource("blender://scene")
def get_scene_info() -> str:
"""
Get information about the current Blender scene, including all objects,
materials, camera settings, and render configuration.
"""
blender = get_blender_connection()
"""Get detailed information about the current Blender scene"""
try:
scene_info = blender.send_command("get_scene_info")
return json.dumps(scene_info, indent=2)
blender = get_blender_connection()
result = blender.send_command("get_scene_info")
return json.dumps({"status": "success", "result": result})
except Exception as e:
return f"Error getting scene info: {str(e)}"
logger.error(f"Error getting scene info from Blender: {str(e)}")
return json.dumps({"status": "error", "message": str(e)})
@mcp.resource("blender://object/{object_name}")
def get_object_info(object_name: str) -> str:
"""
Get detailed information about a specific object in the Blender scene.
Parameters:
- object_name: The name of the object to get information about
Args:
object_name: The name of the object to get information about
"""
blender = get_blender_connection()
try:
object_info = blender.send_command("get_object_info", {"name": object_name})
return json.dumps(object_info, indent=2)
blender = get_blender_connection()
result = blender.send_command("get_object_info", {"name": object_name})
return json.dumps({"status": "success", "result": result})
except Exception as e:
return f"Error getting object info: {str(e)}"
logger.error(f"Error getting object info from Blender: {str(e)}")
return json.dumps({"status": "error", "message": str(e)})
# Tool endpoints
@ -246,16 +298,15 @@ def create_object(
- rotation: Optional [x, y, z] rotation in radians
- scale: Optional [x, y, z] scale factors
"""
blender = ctx.request_context.lifespan_context.get("blender")
if not blender:
return "Not connected to Blender"
# Set default values for missing parameters
loc = location or [0, 0, 0]
rot = rotation or [0, 0, 0]
sc = scale or [1, 1, 1]
try:
# Get the global connection
blender = get_blender_connection()
# Set default values for missing parameters
loc = location or [0, 0, 0]
rot = rotation or [0, 0, 0]
sc = scale or [1, 1, 1]
params = {
"type": type,
"location": loc,
@ -269,6 +320,7 @@ def create_object(
result = blender.send_command("create_object", params)
return f"Created {type} object: {result['name']}"
except Exception as e:
logger.error(f"Error creating object: {str(e)}")
return f"Error creating object: {str(e)}"
@mcp.tool()
@ -290,11 +342,10 @@ def modify_object(
- scale: Optional [x, y, z] scale factors
- visible: Optional boolean to set visibility
"""
blender = ctx.request_context.lifespan_context.get("blender")
if not blender:
return "Not connected to Blender"
try:
# Get the global connection
blender = get_blender_connection()
params = {"name": name}
if location is not None:
@ -309,6 +360,7 @@ def modify_object(
result = blender.send_command("modify_object", params)
return f"Modified object: {result['name']}"
except Exception as e:
logger.error(f"Error modifying object: {str(e)}")
return f"Error modifying object: {str(e)}"
@mcp.tool()
@ -319,14 +371,14 @@ def delete_object(ctx: Context, name: str) -> str:
Parameters:
- name: Name of the object to delete
"""
blender = ctx.request_context.lifespan_context.get("blender")
if not blender:
return "Not connected to Blender"
try:
# Get the global connection
blender = get_blender_connection()
result = blender.send_command("delete_object", {"name": name})
return f"Deleted object: {result['deleted']}"
return f"Deleted object: {name}"
except Exception as e:
logger.error(f"Error deleting object: {str(e)}")
return f"Error deleting object: {str(e)}"
@mcp.tool()
@ -340,32 +392,25 @@ def set_material(
Set or create a material for an object.
Parameters:
- object_name: Name of the object to assign the material to
- material_name: Optional name of the material to use/create
- color: Optional [r, g, b] or [r, g, b, a] color values (0.0-1.0)
- object_name: Name of the object to apply the material to
- material_name: Optional name of the material to use or create
- color: Optional [R, G, B] color values (0.0-1.0)
"""
blender = ctx.request_context.lifespan_context.get("blender")
if not blender:
return "Not connected to Blender"
try:
params = {
"object_name": object_name
}
# Get the global connection
blender = get_blender_connection()
params = {"object_name": object_name}
if material_name:
params["material_name"] = material_name
if color:
params["color"] = color
result = blender.send_command("set_material", params)
if "material" in result:
return f"Set material '{result['material']}' on object '{result['object']}'"
else:
return f"Error setting material: {result.get('error', 'Unknown error')}"
return f"Applied material to {object_name}: {result.get('material_name', 'unknown')}"
except Exception as e:
logger.error(f"Error setting material: {str(e)}")
return f"Error setting material: {str(e)}"
@mcp.tool()
@ -376,182 +421,148 @@ def render_scene(
resolution_y: int = None
) -> str:
"""
Render the current Blender scene.
Render the current scene and return the image.
Parameters:
- output_path: Optional path to save the rendered image
- resolution_x: Optional horizontal resolution in pixels
- resolution_y: Optional vertical resolution in pixels
- resolution_x: Optional horizontal resolution
- resolution_y: Optional vertical resolution
"""
blender = ctx.request_context.lifespan_context.get("blender")
if not blender:
return "Not connected to Blender"
try:
params = {}
# Get the global connection
blender = get_blender_connection()
params = {}
if output_path:
params["output_path"] = output_path
if resolution_x is not None:
if resolution_x:
params["resolution_x"] = resolution_x
if resolution_y is not None:
if resolution_y:
params["resolution_y"] = resolution_y
result = blender.send_command("render_scene", params)
if result.get("rendered"):
if output_path:
return f"Scene rendered and saved to {result['output_path']} at resolution {result['resolution'][0]}x{result['resolution'][1]}"
else:
return f"Scene rendered at resolution {result['resolution'][0]}x{result['resolution'][1]}"
if "image_path" in result:
# If we have an image path, we could potentially load and return the image
return f"Scene rendered to {result['image_path']}"
else:
return "Error rendering scene"
return "Scene rendered successfully"
except Exception as e:
logger.error(f"Error rendering scene: {str(e)}")
return f"Error rendering scene: {str(e)}"
@mcp.tool()
def execute_blender_code(ctx: Context, code: str) -> str:
"""
Execute arbitrary Blender Python code.
WARNING: This tool allows executing any Python code in Blender's environment.
Use with caution as it can modify or delete data.
Execute arbitrary Python code in Blender.
Parameters:
- code: The Python code to execute in Blender's context
- code: The Python code to execute
"""
blender = ctx.request_context.lifespan_context.get("blender")
if not blender:
return "Not connected to Blender"
try:
result = blender.send_command("execute_code", {"code": code})
# Get the global connection
blender = get_blender_connection()
if result.get("executed"):
return "Code executed successfully"
else:
return "Error executing code"
result = blender.send_command("execute_code", {"code": code})
return f"Code executed successfully: {result.get('result', '')}"
except Exception as e:
logger.error(f"Error executing code: {str(e)}")
return f"Error executing code: {str(e)}"
@mcp.tool()
def create_3d_scene(ctx: Context, description: str) -> str:
"""
Create a 3D scene based on a natural language description.
This helper function interprets a description and creates the appropriate objects.
Create a 3D scene based on a text description.
This is a higher-level tool that will interpret the description and create
appropriate objects, materials, and lighting.
Parameters:
- description: A natural language description of the 3D scene to create
- description: Text description of the scene to create
"""
blender = ctx.request_context.lifespan_context.get("blender")
if not blender:
return "Not connected to Blender"
# First, get the current scene to see what's there
try:
scene_info = blender.send_command("get_scene_info")
except Exception as e:
return f"Error accessing Blender scene: {str(e)}"
# Parse the description and create a simple scene
# This is a basic implementation - a more sophisticated version would use
# natural language understanding to interpret the description more accurately
response = "Creating scene based on your description:\n\n"
try:
# Split description into parts
parts = description.lower().split()
# Get the global connection
blender = get_blender_connection()
# Look for basic scene elements
if any(word in parts for word in ["ground", "floor", "plane"]):
# Create a ground plane
ground = blender.send_command("create_object", {
# Parse the description and create a scene
# This is a simplified implementation - in a real tool, you would use more
# sophisticated parsing and scene generation logic
# For now, we'll just create a simple scene with a few objects
# Clear existing objects (optional)
try:
blender.send_command("execute_code", {
"code": """
import bpy
# Delete all objects except camera and light
for obj in bpy.data.objects:
if obj.type not in ['CAMERA', 'LIGHT']:
bpy.data.objects.remove(obj)
"""
})
except Exception as e:
logger.warning(f"Error clearing scene: {str(e)}")
# Create a simple scene based on the description
objects_created = []
# Add a ground plane
try:
result = blender.send_command("create_object", {
"type": "PLANE",
"name": "Ground",
"location": [0, 0, 0],
"scale": [5, 5, 1]
})
response += f"✓ Created ground plane '{ground['name']}'\n"
objects_created.append(result["name"])
# Set material to gray
# Set a material for the ground
blender.send_command("set_material", {
"object_name": ground["name"],
"object_name": "Ground",
"material_name": "GroundMaterial",
"color": [0.8, 0.8, 0.8]
})
except Exception as e:
logger.warning(f"Error creating ground: {str(e)}")
# Look for cubes
if any(word in parts for word in ["cube", "box"]):
cube = blender.send_command("create_object", {
"type": "CUBE",
"name": "Cube",
"location": [0, 0, 1],
"scale": [1, 1, 1]
})
response += f"✓ Created cube '{cube['name']}'\n"
# Set material to blue
blender.send_command("set_material", {
"object_name": cube["name"],
"color": [0.2, 0.4, 0.8]
})
# Simple keyword-based object creation
if "cube" in description.lower():
try:
result = blender.send_command("create_object", {
"type": "CUBE",
"name": "Cube",
"location": [0, 0, 1]
})
objects_created.append(result["name"])
except Exception as e:
logger.warning(f"Error creating cube: {str(e)}")
if "sphere" in description.lower():
try:
result = blender.send_command("create_object", {
"type": "SPHERE",
"name": "Sphere",
"location": [2, 0, 1]
})
objects_created.append(result["name"])
except Exception as e:
logger.warning(f"Error creating sphere: {str(e)}")
if "cylinder" in description.lower():
try:
result = blender.send_command("create_object", {
"type": "CYLINDER",
"name": "Cylinder",
"location": [-2, 0, 1]
})
objects_created.append(result["name"])
except Exception as e:
logger.warning(f"Error creating cylinder: {str(e)}")
# Look for spheres
if any(word in parts for word in ["sphere", "ball"]):
sphere = blender.send_command("create_object", {
"type": "SPHERE",
"name": "Sphere",
"location": [2, 2, 1],
"scale": [1, 1, 1]
})
response += f"✓ Created sphere '{sphere['name']}'\n"
# Set material to red
blender.send_command("set_material", {
"object_name": sphere["name"],
"color": [0.8, 0.2, 0.2]
})
# Look for cylinders
if any(word in parts for word in ["cylinder", "pipe", "tube"]):
cylinder = blender.send_command("create_object", {
"type": "CYLINDER",
"name": "Cylinder",
"location": [-2, -2, 1],
"scale": [1, 1, 1]
})
response += f"✓ Created cylinder '{cylinder['name']}'\n"
# Set material to green
blender.send_command("set_material", {
"object_name": cylinder["name"],
"color": [0.2, 0.8, 0.2]
})
# Add a camera if not already in the scene
if not any(obj.get("type") == "CAMERA" for obj in scene_info["objects"]):
camera = blender.send_command("create_object", {
"type": "CAMERA",
"name": "Camera",
"location": [7, -7, 5],
"rotation": [0.9, 0, 2.6] # Pointing at the origin
})
response += f"✓ Added camera '{camera['name']}'\n"
# Add a light if not already in the scene
if not any(obj.get("type") == "LIGHT" for obj in scene_info["objects"]):
light = blender.send_command("create_object", {
"type": "LIGHT",
"name": "Light",
"location": [4, 1, 6]
})
response += f"✓ Added light '{light['name']}'\n"
response += "\nScene created! You can continue to modify it with specific commands."
return response
return f"Created scene with objects: {', '.join(objects_created)}"
except Exception as e:
logger.error(f"Error creating scene: {str(e)}")
return f"Error creating scene: {str(e)}"
# Prompts to help users interact with Blender
@ -583,8 +594,6 @@ def add_material() -> str:
I have a cube in my scene. Can you create a blue metallic material and apply it to the cube?
"""
# Main execution
def main():

61
test_ping.py Normal file
View File

@ -0,0 +1,61 @@
import socket
import json
import time
def test_ping():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
print("Connecting to Blender...")
sock.connect(('localhost', 9876))
print("Connected!")
# Ping command
command = {
"type": "ping",
"params": {}
}
print(f"Sending command: {json.dumps(command)}")
sock.sendall(json.dumps(command).encode('utf-8'))
print(f"Setting socket timeout: 15 seconds")
sock.settimeout(15)
print("Waiting for response...")
try:
# Receive data in chunks
chunks = []
while True:
chunk = sock.recv(8192)
if not chunk:
break
chunks.append(chunk)
# Try to parse the JSON to see if we have a complete response
try:
data = b''.join(chunks)
json.loads(data.decode('utf-8'))
# If we get here, we have a complete response
break
except json.JSONDecodeError:
# Incomplete JSON, continue receiving
continue
data = b''.join(chunks)
print(f"Received {len(data)} bytes")
if data:
response = json.loads(data.decode('utf-8'))
print(f"Response: {response}")
else:
print("Received empty response")
except socket.timeout:
print("Socket timeout while waiting for response")
except Exception as e:
print(f"Error: {type(e).__name__}: {str(e)}")
finally:
sock.close()
if __name__ == "__main__":
test_ping()

68
test_scene_info.py Normal file
View File

@ -0,0 +1,68 @@
import socket
import json
import time
def test_scene_info():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
print("Connecting to Blender...")
sock.connect(('localhost', 9876))
print("Connected!")
# Scene info command
command = {
"type": "get_scene_info",
"params": {}
}
print(f"Sending command: {json.dumps(command)}")
sock.sendall(json.dumps(command).encode('utf-8'))
print(f"Setting socket timeout: 15 seconds")
sock.settimeout(15)
print("Waiting for response...")
try:
# Receive data in chunks
chunks = []
while True:
chunk = sock.recv(8192)
if not chunk:
break
chunks.append(chunk)
# Try to parse the JSON to see if we have a complete response
try:
data = b''.join(chunks)
json.loads(data.decode('utf-8'))
# If we get here, we have a complete response
break
except json.JSONDecodeError:
# Incomplete JSON, continue receiving
continue
data = b''.join(chunks)
print(f"Received {len(data)} bytes")
if data:
response = json.loads(data.decode('utf-8'))
print(f"Response status: {response.get('status')}")
if response.get('status') == 'success':
result = response.get('result', {})
print(f"Scene name: {result.get('name')}")
print(f"Object count: {result.get('object_count')}")
print(f"Objects returned: {len(result.get('objects', []))}")
else:
print(f"Error: {response.get('message')}")
else:
print("Received empty response")
except socket.timeout:
print("Socket timeout while waiting for response")
except Exception as e:
print(f"Error: {type(e).__name__}: {str(e)}")
finally:
sock.close()
if __name__ == "__main__":
test_scene_info()

61
test_simple_info.py Normal file
View File

@ -0,0 +1,61 @@
import socket
import json
import time
def test_simple_info():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
print("Connecting to Blender...")
sock.connect(('localhost', 9876))
print("Connected!")
# Simple info command
command = {
"type": "get_simple_info",
"params": {}
}
print(f"Sending command: {json.dumps(command)}")
sock.sendall(json.dumps(command).encode('utf-8'))
print(f"Setting socket timeout: 15 seconds")
sock.settimeout(15)
print("Waiting for response...")
try:
# Receive data in chunks
chunks = []
while True:
chunk = sock.recv(8192)
if not chunk:
break
chunks.append(chunk)
# Try to parse the JSON to see if we have a complete response
try:
data = b''.join(chunks)
json.loads(data.decode('utf-8'))
# If we get here, we have a complete response
break
except json.JSONDecodeError:
# Incomplete JSON, continue receiving
continue
data = b''.join(chunks)
print(f"Received {len(data)} bytes")
if data:
response = json.loads(data.decode('utf-8'))
print(f"Response: {response}")
else:
print("Received empty response")
except socket.timeout:
print("Socket timeout while waiting for response")
except Exception as e:
print(f"Error: {type(e).__name__}: {str(e)}")
finally:
sock.close()
if __name__ == "__main__":
test_simple_info()