-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathserver_setup.py
348 lines (297 loc) · 13.1 KB
/
server_setup.py
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# requirements: flask, requests
from flask import Flask, render_template_string, jsonify, request
import threading
import socket
import time
import json
import os
import tempfile
import requests
# Input:
# html_content: stringa contenente l'HTML della pagina (da html_builder.py)
# restart: bool - Se True, riavvia il server (opzionale)
# Usa la directory temporanea del sistema
JSON_PATH = os.path.join(tempfile.gettempdir(), 'gh_interface_data.json')
INSTANCE_PATH = os.path.join(tempfile.gettempdir(), 'gh_server_instance.json')
HTML_CACHE_PATH = os.path.join(tempfile.gettempdir(), 'gh_html_cache.json')
def init_json_file():
"""Inizializza il file JSON se non esiste"""
try:
current_data = {"controls": {}, "last_update": time.time()}
if os.path.exists(JSON_PATH):
try:
with open(JSON_PATH, 'r') as f:
content = f.read().strip()
if content:
data = json.loads(content)
if isinstance(data, dict):
current_data = data
except:
pass
# Assicurati che la struttura sia corretta
if 'controls' not in current_data:
current_data['controls'] = {}
if 'last_update' not in current_data:
current_data['last_update'] = time.time()
# Salva il file
with open(JSON_PATH, 'w') as f:
json.dump(current_data, f, indent=2)
return current_data
except Exception as e:
print(f"Errore nell'inizializzazione del file JSON: {e}")
return {"controls": {}, "last_update": time.time()}
def get_running_instance():
"""Recupera informazioni sull'istanza corrente del server"""
try:
if os.path.exists(INSTANCE_PATH):
with open(INSTANCE_PATH, 'r') as f:
data = json.load(f)
# Verifica se il server è effettivamente in esecuzione
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(0.1)
result = s.connect_ex(('127.0.0.1', data['port']))
if result == 0:
return data
except:
pass
except:
pass
return None
def save_instance_info(port):
"""Salva le informazioni dell'istanza corrente"""
try:
with open(INSTANCE_PATH, 'w') as f:
json.dump({'port': port, 'timestamp': time.time()}, f)
except:
pass
class WebInterface:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super(WebInterface, cls).__new__(cls)
return cls._instance
def __init__(self):
if not hasattr(self, 'initialized'):
self.initialized = True
self.app = Flask(__name__)
self.server_thread = None
# Controlla se esiste già un'istanza attiva
running_instance = get_running_instance()
if running_instance:
self.port = running_instance['port']
# Prova a fermare il server esistente
try:
requests.get(f'http://127.0.0.1:{self.port}/shutdown', timeout=0.2)
time.sleep(0.5)
except:
pass
else:
self.port = self.find_free_port()
save_instance_info(self.port)
self.setup_routes()
init_json_file()
self.check_server_status()
def find_free_port(self):
"""Trova una porta libera"""
for port in range(5000, 5500):
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(0.1)
result = s.connect_ex(('127.0.0.1', port))
if result != 0: # La porta è libera
return port
except:
continue
# Se non troviamo una porta nel range, lasciamo che il sistema ne assegni una
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(('127.0.0.1', 0))
return s.getsockname()[1]
def setup_routes(self):
@self.app.route('/')
def index():
try:
# Legge l'HTML dal file cache
if os.path.exists(HTML_CACHE_PATH):
with open(HTML_CACHE_PATH, 'r') as f:
data = json.load(f)
if data['port'] == self.port: # Verifica che l'HTML sia per questa istanza
return render_template_string(data['html'])
except Exception as e:
print(f"Errore nel caricamento dell'HTML dalla cache: {e}")
# Fallback all'HTML originale se c'è un errore
return render_template_string(html_content)
@self.app.route('/update', methods=['POST'])
def update():
try:
new_data = request.json
if not isinstance(new_data, dict):
return jsonify({"status": "error", "message": "Invalid data format"}), 400
current_data = {"controls": {}, "last_update": time.time()}
# Leggi i dati esistenti se il file esiste
if os.path.exists(JSON_PATH):
try:
with open(JSON_PATH, 'r') as f:
content = f.read().strip()
if content:
current_data = json.loads(content)
except json.JSONDecodeError as e:
print(f"Errore nella lettura del JSON esistente: {e}")
# Continua con i dati di default se c'è un errore
# Assicurati che la struttura sia corretta
if not isinstance(current_data, dict):
current_data = {"controls": {}, "last_update": time.time()}
if 'controls' not in current_data:
current_data['controls'] = {}
# Aggiorna i controlli
if 'controls' in new_data:
current_data['controls'].update(new_data['controls'])
current_data['last_update'] = time.time()
# Salva i dati aggiornati
with open(JSON_PATH, 'w') as f:
json.dump(current_data, f, indent=2) # Usa indent per una formattazione leggibile
return jsonify({"status": "success"})
except Exception as e:
print(f"Errore nell'aggiornamento dei valori: {e}")
return jsonify({"status": "error", "message": str(e)}), 500
@self.app.route('/get_values')
def get_values():
try:
with open(JSON_PATH, 'r') as f:
return jsonify(json.load(f))
except Exception as e:
return jsonify({"status": "error", "message": str(e)}), 500
@self.app.route('/favicon.ico')
def favicon():
return '', 204 # Restituisce una risposta vuota con status code 204 (No Content)
@self.app.route('/get_geometry')
def get_geometry():
try:
geometry_path = os.path.join(tempfile.gettempdir(), 'gh_geometry_data.json')
if os.path.exists(geometry_path):
with open(geometry_path, 'r') as f:
return jsonify(json.load(f))
return jsonify({"status": "error", "message": "No geometry data"})
except Exception as e:
return jsonify({"status": "error", "message": str(e)})
@self.app.route('/get_initial_values')
def get_initial_values():
try:
if os.path.exists(JSON_PATH):
with open(JSON_PATH, 'r') as f:
content = f.read().strip()
if content:
data = json.loads(content)
if isinstance(data, dict) and 'controls' in data:
return jsonify({"status": "success", "values": data['controls']})
return jsonify({"status": "success", "values": {}})
except Exception as e:
print(f"Errore nel recupero dei valori iniziali: {e}")
return jsonify({"status": "error", "message": str(e)}), 500
@self.app.route('/shutdown', methods=['GET'])
def shutdown():
func = request.environ.get('werkzeug.server.shutdown')
if func is None:
raise RuntimeError('Non in esecuzione con il server Werkzeug')
func()
return 'Server in chiusura...'
def stop_server(self):
"""Ferma il server se è in esecuzione"""
if self.server_thread and self.server_thread.is_alive():
print("\nChiusura server in corso...")
try:
requests.get(f'http://127.0.0.1:{self.port}/shutdown', timeout=0.2)
except:
pass
# Attendiamo che il thread termini
self.server_thread.join(timeout=2)
self.server_thread = None
# Aspettiamo che la porta si liberi
max_attempts = 20 # Aumentiamo il numero di tentativi
for _ in range(max_attempts):
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(0.1)
result = s.connect_ex(('127.0.0.1', self.port))
if result != 0: # La porta è libera
print(f"Server sulla porta {self.port} chiuso.")
return
except:
pass
time.sleep(0.3) # Aumentiamo il tempo di attesa tra i tentativi
# Se non riusciamo a liberare la porta, ne troviamo una nuova
print(f"Impossibile liberare la porta {self.port}, cerco una nuova porta...")
self.port = self.find_free_port()
print(f"Nuova porta assegnata: {self.port}")
def start_server(self):
"""Avvia il server su una porta libera"""
self.stop_server()
# Verifica ulteriore che la porta sia libera
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(0.1)
result = s.connect_ex(('127.0.0.1', self.port))
if result == 0: # La porta è ancora in uso
self.port = self.find_free_port()
print(f"Porta occupata, usando la nuova porta: {self.port}")
except:
pass
if not self.server_thread:
self.server_thread = threading.Thread(target=self._run_server)
self.server_thread.daemon = True
self.server_thread.start()
time.sleep(1)
self.check_server_status()
def _run_server(self):
try:
self.app.run(
host='127.0.0.1',
port=self.port,
debug=False,
use_reloader=False
)
except Exception as e:
print(f"Errore nell'avvio del server: {e}")
def check_server_status(self):
"""Controlla e mostra lo stato del server"""
active_ports = []
for port in range(5000, 5020):
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(0.1)
result = s.connect_ex(('127.0.0.1', port))
if result == 0:
active_ports.append(port)
except:
pass
# Prepara il messaggio di stato
status_msg = f"\n=== STATO SERVER ===\n"
status_msg += f"Porta attuale: {self.port}\n"
if len(active_ports) > 0:
other_ports = [p for p in active_ports if p != self.port]
if other_ports:
status_msg += f"Altre istanze attive trovate sulle porte: {other_ports}\n"
status_msg += "==================="
print(status_msg)
return status_msg
class ServerInstance:
_instance = None
_is_running = False
@classmethod
def get_instance(cls):
if cls._instance is None:
cls._instance = WebInterface()
return cls._instance
@classmethod
def start_if_not_running(cls):
instance = cls.get_instance()
# Forza sempre la chiusura del server esistente
instance.stop_server()
cls._is_running = False
# Avvia una nuova istanza
instance.start_server()
cls._is_running = True
return instance.port
# Output: porta del server corrente
server_instance = ServerInstance.start_if_not_running()
port = server_instance # Assegna la porta restituita alla variabile di output