-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathblender_watcher.py
More file actions
179 lines (156 loc) · 5.69 KB
/
blender_watcher.py
File metadata and controls
179 lines (156 loc) · 5.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# =============================================================================
# Open 2D Studio — Bonsai Live Sync (WebSocket Client)
# =============================================================================
#
# This script connects to Open 2D Studio's WebSocket server and receives
# IFC model updates in real-time.
#
# REQUIREMENTS:
# pip install websocket-client
#
# HOW TO USE:
# 1. Open Blender with the Bonsai add-on enabled
# 2. Paste this script in Scripting workspace → Alt+P
# 3. The script connects to ws://localhost:9876
# 4. IFC updates are received and loaded in Bonsai automatically
#
# =============================================================================
import bpy
import os
import sys
import json
import tempfile
import threading
import time
WS_HOST = "localhost"
WS_PORT = 9876
WS_URL = f"ws://{WS_HOST}:{WS_PORT}"
RECONNECT_DELAY = 3.0
TEMP_IFC_PATH = os.path.join(tempfile.gettempdir(), "open2d_bonsai_sync.ifc")
_ws_client = None
_ws_thread = None
_should_run = True
_reload_count = 0
_pending_ifc_content = None
_pending_lock = threading.Lock()
_connected = False
def _ensure_websocket_installed():
try:
import websocket
return True
except ImportError:
print("[Open2D] Installing websocket-client...")
import subprocess
try:
subprocess.check_call(
[sys.executable, '-m', 'pip', 'install', 'websocket-client'],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
)
print("[Open2D] websocket-client installed.")
return True
except Exception as e:
print(f"[Open2D] Install failed: {e}")
return False
def _ws_thread_func():
global _ws_client, _should_run, _pending_ifc_content, _connected
import websocket
while _should_run:
try:
print(f"[Open2D] Connecting to {WS_URL}...")
ws = websocket.WebSocket()
ws.connect(WS_URL, timeout=5)
_ws_client = ws
_connected = True
print(f"[Open2D] Connected!")
while _should_run:
try:
ws.settimeout(1.0)
data = ws.recv()
if not data:
continue
msg = json.loads(data)
msg_type = msg.get("type", "")
if msg_type == "connected":
print(f"[Open2D] Server: {msg.get('message', '')}")
elif msg_type == "ifc_update":
ifc_content = msg.get("content", "")
if ifc_content:
with _pending_lock:
global _pending_ifc_content
_pending_ifc_content = ifc_content
print(f"[Open2D] IFC update received ({len(ifc_content)} bytes)")
elif msg_type == "pong":
pass
except websocket.WebSocketTimeoutException:
continue
except websocket.WebSocketConnectionClosedException:
print("[Open2D] Connection closed.")
break
except Exception as e:
print(f"[Open2D] Error: {e}")
break
except Exception as e:
print(f"[Open2D] Connection failed: {e}")
_connected = False
_ws_client = None
if _should_run:
print(f"[Open2D] Reconnecting in {RECONNECT_DELAY}s...")
time.sleep(RECONNECT_DELAY)
def _check_pending_reload():
global _pending_ifc_content, _reload_count
content = None
with _pending_lock:
if _pending_ifc_content:
content = _pending_ifc_content
_pending_ifc_content = None
if content:
try:
with open(TEMP_IFC_PATH, 'w', encoding='utf-8') as f:
f.write(content)
_reload_count += 1
print(f"[Open2D] Reloading IFC in Bonsai (#{_reload_count})...")
if not hasattr(bpy.ops, "bim"):
print("[Open2D] Bonsai not installed!")
return 0.5
try:
import blenderbim.tool as tool
ifc_file = tool.Ifc.get()
if ifc_file:
try:
bpy.ops.bim.unload_project()
except:
pass
except:
pass
bpy.ops.bim.load_project(filepath=TEMP_IFC_PATH)
print(f"[Open2D] Reload complete.")
except Exception as e:
print(f"[Open2D] Reload error: {e}")
return 0.5 # Check every 500ms
def open2d_ws_stop():
global _should_run, _ws_client, _ws_thread
_should_run = False
if _ws_client:
try:
_ws_client.close()
except:
pass
if bpy.app.timers.is_registered(_check_pending_reload):
bpy.app.timers.unregister(_check_pending_reload)
print("[Open2D] Stopped.")
def open2d_ws_status():
print(f"[Open2D] Connected: {_connected}")
print(f"[Open2D] Reloads: {_reload_count}")
print(f"[Open2D] URL: {WS_URL}")
# --- Start ---
if not _ensure_websocket_installed():
raise RuntimeError("websocket-client not available")
# Stop previous instance
if bpy.app.timers.is_registered(_check_pending_reload):
bpy.app.timers.unregister(_check_pending_reload)
_should_run = True
_ws_thread = threading.Thread(target=_ws_thread_func, daemon=True)
_ws_thread.start()
bpy.app.timers.register(_check_pending_reload, first_interval=1.0, persistent=True)
print(f"[Open2D] WebSocket client started. Target: {WS_URL}")
print(f"[Open2D] Temp IFC: {TEMP_IFC_PATH}")