diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..92a2fa2 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,70 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Current File (Integrated Terminal)", + "type": "python", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal" + }, + { + "name": "Python: Remote Attach", + "type": "python", + "request": "attach", + "port": 5678, + "host": "localhost", + "pathMappings": [ + { + "localRoot": "${workspaceFolder}", + "remoteRoot": "." + } + ] + }, + { + "name": "Python: Module", + "type": "python", + "request": "launch", + "module": "enter-your-module-name-here", + "console": "integratedTerminal" + }, + { + "name": "Python: Django", + "type": "python", + "request": "launch", + "program": "${workspaceFolder}/manage.py", + "console": "integratedTerminal", + "args": [ + "runserver", + "--noreload", + "--nothreading" + ], + "django": true + }, + { + "name": "Python: Flask", + "type": "python", + "request": "launch", + "module": "flask", + "env": { + "FLASK_APP": "app.py" + }, + "args": [ + "run", + "--no-debugger", + "--no-reload" + ], + "jinja": true + }, + { + "name": "Python: Current File (External Terminal)", + "type": "python", + "request": "launch", + "program": "${file}", + "console": "externalTerminal" + } + ] +} \ No newline at end of file diff --git a/__init__.py b/__init__.py index c9cb4d2..419c151 100644 --- a/__init__.py +++ b/__init__.py @@ -28,6 +28,7 @@ import bpy def register(): bpy.types.Scene.message = bpy.props.StringProperty(default="Hi") + bsyncio.register() net_operators.register() net_ui.register() diff --git a/net_components.py b/net_components.py index 728917a..7d494c2 100644 --- a/net_components.py +++ b/net_components.py @@ -1,60 +1,81 @@ import zmq import asyncio import logging - +from .libs import umsgpack logger = logging.getLogger(__name__) logging.basicConfig(level=logging.DEBUG) - class Session(): def __init__(self, host='127.0.0.1', port=5555, is_hosting=False): self.host = host self.port = port - + self.is_running = False # init zmq context self.context = zmq.Context() + self.socket = None - # init socket interface - if is_hosting: - self.socket = self.context.socket(zmq.REP) - self.socket.bind("tcp://*:5555") - else: - self.socket = self.context.socket(zmq.REQ) - self.socket.connect("tcp://127.0.0.1:5555") - - self.listen = asyncio.ensure_future(self.listen()) self.msg = [] - - def is_running(self): - try: - return not self.listen.done - except: - return False + + #self.listen.add_done_callback(self.close_success()) # TODO: Add a kill signal to destroy clients session # TODO: Add a join method # TODO: Add a create session method + def join(self): + logger.info("joinning {}:{}".format(self.host, self.port)) + try: + self.socket = self.context.socket(zmq.REQ) + self.socket.connect("tcp://localhost:5555") + self.listen = asyncio.ensure_future(self.listen()) + return True + + except zmq.ZMQError: + logger.error("Error while joining {}:{}".format( + self.host, self.port)) + + return False + + # TODO: Find better names + def create(self): + logger.info("Creating session") + try: + self.socket = self.context.socket(zmq.REP) + self.socket.bind("tcp://*:5555") + + self.listen = asyncio.ensure_future(self.listen()) + return True + except zmq.ZMQError: + logger.error("Error while creating session: ",zmq.ZMQError) + + return False async def listen(self): logger.info("Listening on {}:{}".format(self.host, self.port)) + self.is_running = True while True: # Ungly blender workaround to prevent blocking... await asyncio.sleep(0.016) try: - message = self.socket.recv_multipart(zmq.NOBLOCK) - self.msg.append(message) - logger.info(message) + msg = self.socket.recv(zmq.NOBLOCK) + # self.msg.append(umsgpack.unpackb(message)) + print(msg) + logger.info(msg) except zmq.ZMQError: pass def send(self, msg): logger.info("Sending {} to {}:{} ".format(msg, self.host, self.port)) + bin = umsgpack.packb(msg) + self.socket.send(bin,zmq.NOBLOCK) - self.socket.send(b"msg") + async def close_success(self): + self.is_running = False def close(self): logger.info("Closing session") + self.socket.close() self.listen.cancel() - + del self.listen + self.is_running = False diff --git a/net_operators.py b/net_operators.py index f2d8758..c5ccd44 100644 --- a/net_operators.py +++ b/net_operators.py @@ -3,12 +3,32 @@ from . import net_components session = None + class join(bpy.types.Operator): bl_idname = "session.join" - bl_label = "connect to net session" + bl_label = "join" bl_description = "Connect to a net session" bl_options = {"REGISTER"} + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + # global session + + if session.join(): + bpy.ops.asyncio.loop() + else: + print('fail to create session, avorting loop') + return {"FINISHED"} + + +class create(bpy.types.Operator): + bl_idname = "session.create" + bl_label = "create" + bl_description = "create to a net session" + bl_options = {"REGISTER"} @classmethod def poll(cls, context): @@ -17,31 +37,17 @@ class join(bpy.types.Operator): def execute(self, context): global session - session = net_components.Session() - bpy.ops.asyncio.loop() + if session.create(): + bpy.ops.asyncio.loop() + else: + print('fail to create session, avorting loop') + return {"FINISHED"} -class host(bpy.types.Operator): - bl_idname = "session.host" - bl_label = "host a net session" - bl_description = "Connect to a net session" - bl_options = {"REGISTER"} - - - @classmethod - def poll(cls, context): - return True - - def execute(self, context): - global session - - session = net_components.Session(is_hosting=True) - bpy.ops.asyncio.loop() - return {"FINISHED"} class send(bpy.types.Operator): bl_idname = "session.send" - bl_label = "Send a message throught the network" + bl_label = "Send" bl_description = "Connect to a net session" bl_options = {"REGISTER"} @@ -54,12 +60,13 @@ class send(bpy.types.Operator): def execute(self, context): global session - session.send(b"") + session.send(self.message) return {"FINISHED"} + class close(bpy.types.Operator): bl_idname = "session.close" - bl_label = "Send a message throught the network" + bl_label = "Close session" bl_description = "Connect to a net session" bl_options = {"REGISTER"} @@ -70,18 +77,33 @@ class close(bpy.types.Operator): def execute(self, context): global session - bpy.ops.asyncio.stop() session.close() - + bpy.ops.asyncio.stop() return {"FINISHED"} + classes = ( join, - host, - send + create, + close, + send, ) -register, unregister = bpy.utils.register_classes_factory(classes) + +def register(): + global session + session = net_components.Session() + + from bpy.utils import register_class + for cls in classes: + register_class(cls) + + +def unregister(): + from bpy.utils import unregister_class + for cls in reversed(classes): + unregister_class(cls) + if __name__ == "__main__": - register() \ No newline at end of file + register() diff --git a/net_ui.py b/net_ui.py index b6a3c36..287a4bc 100644 --- a/net_ui.py +++ b/net_ui.py @@ -15,21 +15,37 @@ class SessionPanel(bpy.types.Panel): def draw(self, context): layout = self.layout - global session - + scene = context.scene # Create a simple row. row = layout.row() - if not session: - row.operator("session.join") - row.operator("session.host") - else: + if net_operators.session.is_running: row.operator("session.close") + row = layout.row() + + row = layout.row(align=True) + row.prop(scene,"message",text="") + row.operator("session.send").message = scene.message + row = layout.row() + # Debug area + + row.label(text="Debug") + + row = layout.row() + area_msg = row.box() + if len(net_operators.session.msg) > 0: + for msg in net_operators.session.msg: + area_msg.label(text=str(msg)) + else: + area_msg.label(text="Empty") + else: + row.operator("session.join") + row.operator("session.create") - # row.operator("session.send").message = bpy.scene.message - # row.prop(scene,"message") + + classes = ( SessionPanel,