Skip to content

Commit

Permalink
CAP:basic working version with vscode
Browse files Browse the repository at this point in the history
  • Loading branch information
9and3 committed Feb 7, 2024
1 parent 187787c commit e3d433b
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 177 deletions.
125 changes: 74 additions & 51 deletions GH/PyGH/scriptsyncGH_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def component_on_canvas(self):
self._check_if_component_on_canvas()
return self._component_on_canvas


# TODO: clean this class
class ClientThread(GHThread):
"""
A thread to connect to the VSCode server.
Expand All @@ -125,60 +125,73 @@ def __init__(self,
self.vscode_server_port = vscode_server_port
self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.is_connected = False
self.connect_refresh_rate = 2 # seconds
self.connect_refresh_rate = 1 # seconds
self.queue_msg = queue_msg
self.lock_queue_msg = lock_queue_msg
self.event_fire_msg = event_fire_msg

def run(self):
""" Run the thread. Send the message to the vscode server."""
while self.component_on_canvas and self.component_enabled:
if not self.is_connected:
self.clear_queue()
self.connect_to_vscode_server()
self.clear_component()
self.expire_component_solution()
continue

self.event_fire_msg.wait()
self.send_message()

def clear_queue(self):
with self.lock_queue_msg:
while not self.queue_msg.empty():
self.queue_msg.get()
self.queue_msg.task_done()
self.event_fire_msg.set()
self.event_fire_msg.clear()
self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

def send_message(self):
with self.lock_queue_msg:
if self.queue_msg and not self.queue_msg.empty():
msg = self.queue_msg.get()
self.queue_msg.task_done()
self.event_fire_msg.set()
self.event_fire_msg.clear()
self.client_socket.send(msg)
while self.component_on_canvas and self.component_enabled:
try:
if not self.is_connected:
self.connect_to_vscode_server()
self.clear_component()
self.expire_component_solution()
continue

self.event_fire_msg.wait()

with self.lock_queue_msg:
if self.queue_msg is not None:
if not self.queue_msg.empty():
msg = self.queue_msg.get()
self.queue_msg.task_done()
self.event_fire_msg.set()
self.event_fire_msg.clear()
self.client_socket.send(msg)

#FIXME: readjust this catching
except Exception as e:
self.add_runtime_warning(f"script-sync::Error from run: {str(e)}")
self.is_connected = False
self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

self.client_socket.close()
return

def connect_to_vscode_server(self):
""" Connect to the VSCode server. """
while self.component_on_canvas and not self.is_connected:
try:
self.client_socket.send(b"")
self.is_connected = True
except socket.error:
self.handle_connection_error()

def handle_connection_error(self):
try:
self.client_socket.connect((self.vscode_server_ip, self.vscode_server_port))
self.is_connected = True
except (ConnectionRefusedError, ConnectionResetError, socket.error) as e:
self.add_runtime_warning(f"script-sync::Error connecting to the vscode-server: {str(e)}")
self.is_connected = False
if isinstance(e, socket.error) and e.winerror == 10056:
self.is_connected = True
finally:
time.sleep(self.connect_refresh_rate)
try:
self.client_socket.connect((self.vscode_server_ip, self.vscode_server_port))
self.is_connected = True
break
except ConnectionRefusedError:
self.add_runtime_warning("script-sync::Connection refused by the vscode-server")
self.is_connected = False
except ConnectionResetError:
self.add_runtime_warning("script-sync::Connection was forcibly closed by the vscode-server")
self.is_connected = False
except socket.error as e:
if e.winerror == 10056:
self.add_runtime_warning(f"script-sync::A connect request was made on an already connected socket")
self.is_connected = True
break
else:
self.add_runtime_warning(f"script-sync::Error connecting to the vscode-server: {str(e)}")
except Exception as e:
self.add_runtime_warning(f"script-sync::Error connecting to the vscode-server: {str(e)}")
finally:
time.sleep(self.connect_refresh_rate)
# if self.is_connected:
# self.client_socket.send("script-sync:: from GHcomponent:\n\n".encode())


class FileChangedThread(GHThread):
Expand Down Expand Up @@ -223,15 +236,15 @@ def __init__(self):

self.is_success = False

self.client_thread_name = None
self.client_thread_name : str = f"script-sync-client-thread::{ghenv.Component.InstanceGuid}"
self.vscode_server_ip = "127.0.0.1"
self.vscode_server_port = 58260
self.stdout = None
self.queue_msg = queue.Queue()
self.queue_msg_lock = threading.Lock()
self.event_fire_msg = threading.Event()

self.filechanged_thread_name = None
self.filechanged_thread_name : str = f"script-sync-fileChanged-thread::{ghenv.Component.InstanceGuid}"
self.__path_name_table_value = "script-sync::" + "path::" + str(ghenv.Component.InstanceGuid)
self.path_lock = threading.Lock()

Expand All @@ -253,7 +266,7 @@ def RemovedFromDocument(self, doc):
# clear the path from the table view
del self.path

def _add_button(self):
def add_button(self):
"""Add a button to the canvas and wire it to the "script" param."""
# get the "script" param by name
script_param = [param for param in ghenv.Component.Params.Input if param.Name == "script"][0]
Expand Down Expand Up @@ -282,7 +295,7 @@ def _add_button(self):

return True

def _safe_exec(self, path, globals, locals):
def safe_exec(self, path, globals, locals):
"""
Execute Python3 code safely. It redirects the output of the code
to a string buffer 'stdout' to output to the GH component param.
Expand All @@ -303,6 +316,13 @@ def _safe_exec(self, path, globals, locals):
code = compile(f.read(), path, 'exec')
output = io.StringIO()

# empty the queue and event
with self.queue_msg_lock:
while not self.queue_msg.empty():
self.queue_msg.get()
self.queue_msg.task_done()
self.event_fire_msg.clear()

# execute the code
with contextlib.redirect_stdout(output):
exec(code, globals, locals)
Expand Down Expand Up @@ -330,8 +350,12 @@ def _safe_exec(self, path, globals, locals):

except Exception as e:

# send the error to the vscode server
self.queue_msg.put(str(e))
# send the error message to the vscode server
err_json = json.dumps({"script_path": self.path,
"guid": str(ghenv.Component.InstanceGuid),
"msg": "err:" + str(e)})
err_json = err_json.encode('utf-8')
self.queue_msg.put(err_json)
self.event_fire_msg.set()

sys.stdout = sys.__stdout__
Expand All @@ -344,7 +368,7 @@ def BeforeRunScript(self):
This method is called as soon as the component has been
placed on the canvas and before the script is run.
"""
self._add_button()
self.add_button()

def RunScript(self,
script : bool,
Expand All @@ -371,12 +395,11 @@ def RunScript(self,
raise Exception("script-sync::File does not exist")

# file change listener thread
self.filechanged_thread_name : str = f"script-sync-fileChanged-thread::{ghenv.Component.InstanceGuid}"

if self.filechanged_thread_name not in [t.name for t in threading.enumerate()]:
FileChangedThread(self.path, self.path_lock, self.filechanged_thread_name).start()

# set up the tcp client to connect to the vscode server
self.client_thread_name : str = f"script-sync-client-thread::{ghenv.Component.InstanceGuid}"
_ = [print(t.name) for t in threading.enumerate()]
if self.client_thread_name not in [t.name for t in threading.enumerate()]:
ClientThread(self.vscode_server_ip,
Expand All @@ -388,7 +411,7 @@ def RunScript(self,
).start()

# run the script
res = self._safe_exec(self.path, globals(), locals())
res = self.safe_exec(self.path, globals(), locals())
self.is_success = True
return

Expand Down
60 changes: 13 additions & 47 deletions GH/PyGH/server_tst.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,6 @@
import os
import json

def handle_client(client_socket):
try:
while True:
# Receive data from the client
message = client_socket.recv(1024).decode('utf-8')
if not message:
break
# obj = json.loads(message)
print(f"{message}")

except Exception as e:
print(f"An error occurred: {e}")
finally:
# Close the connection with the client
client_socket.close()

def start_server():
# Create a socket object
Expand All @@ -29,44 +14,25 @@ def start_server():
# Bind to the port
server.bind(('127.0.0.1', port))

# Put the socket into listening mode
server.listen(5)
print('Server is listening')

# Set a timeout for the accept() method
server.settimeout(1)

try:
while True:
try:
# Accept a new client connection
client_socket, addr = server.accept()
# print(f"Accepted connection from: {addr[0]}:{addr[1]}")
# data_str = client_socket.recv(1024).decode('utf-8')
# print(f"Received data string: {data_str}")
# wait untill the first message is received than close the server
server.listen(1)

# # if the data_str is empty, continue to the next iteration
# if not data_str or data_str == "":
# continue
# Establish connection with client.
client, addr = server.accept()
print('Got connection from', addr)

# try:
# obj = json.loads(data_str)
# print(f"Received data: {obj}")
# except Exception as e:
# print(f"An error occurred: {e}")
# Receive the data from the client
data = client.recv(1024)
print('Server received', data.decode())

# Decode the data and convert it to a dictionary
data = json.loads(data.decode())

# Get the function name and the arguments
msg = data['msg']

print(f"Server received: {msg}")

# Start a new thread to handle this client connection
client_handler = threading.Thread(target=handle_client, args=(client_socket,))
client_handler.start()
except socket.timeout:
continue
except KeyboardInterrupt:
print("\nServer is stopping due to keyboard interruption...")
server.close()
os._exit(1)

# Start the server
start_server()
26 changes: 22 additions & 4 deletions GH/PyGH/test/runner_script.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,33 @@
# #! python3

# def main(x):
# y = 456
# a = x - y
# c = x + y
# b = 123456
# print("runner_script.py::main() function called")
# print(f"runner_script.py::b value: {b}")
# print(f"runner_script.py::c value: {c}")

# return a

# if __name__ == '__main__':
# a = main(x)

#! python3

def main(x):
y = 456
def main():
x = 123
y = 45
a = x - y
c = x + y
b = 123456
b = 12345
print("runner_script.py::main() function called")
print(f"runner_script.py::a value: {a}")
print(f"runner_script.py::b value: {b}")
print(f"runner_script.py::c value: {c}")

return a

if __name__ == '__main__':
a = main(x)
a = main()
26 changes: 26 additions & 0 deletions GH/PyGH/test/runner_script_2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# #! python3

# def main(x):
# y = 456
# a = x - y
# c = x + y
# b = 123456
# print("runner_script.py::main() function called")
# print(f"runner_script.py::b value: {b}")
# print(f"runner_script.py::c value: {c}")

# return a

# if __name__ == '__main__':
# a = main(x)

#! python3

def main():
k = 1234444
print(f"runner_script_2.py::k value: {k}")

return a

if __name__ == '__main__':
a = main()
Loading

0 comments on commit e3d433b

Please sign in to comment.