feat(rcf): id support
This commit is contained in:
@ -7,7 +7,7 @@ import random
|
|||||||
import struct
|
import struct
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
# TODO: Add message time and author stamp for reliabilty
|
# TODO: Add message time and author stamp for reliabilty
|
||||||
@ -15,18 +15,21 @@ class RCFMessage(object):
|
|||||||
"""
|
"""
|
||||||
Message is formatted on wire as 2 frames:
|
Message is formatted on wire as 2 frames:
|
||||||
frame 0: key (0MQ string) // property path
|
frame 0: key (0MQ string) // property path
|
||||||
frame 1: mtype (0MQ string) // property path
|
frame 1: id (0MQ string) // property path
|
||||||
frame 2: body (blob) // Could be any data
|
frame 2: mtype (0MQ string) // property path
|
||||||
|
frame 3: body (blob) // Could be any data
|
||||||
|
|
||||||
"""
|
"""
|
||||||
key = None # key (string)
|
key = None # key (string)
|
||||||
|
id = None # User (string)
|
||||||
mtype = None # data mtype (string)
|
mtype = None # data mtype (string)
|
||||||
body = None # data blob
|
body = None # data blob
|
||||||
|
|
||||||
def __init__(self, key=None, mtype=None, body=None):
|
def __init__(self, key=None, id=None, mtype=None, body=None):
|
||||||
self.key = key
|
self.key = key
|
||||||
self.mtype = mtype
|
self.mtype = mtype
|
||||||
self.body = body
|
self.body = body
|
||||||
|
self.id = id
|
||||||
|
|
||||||
def store(self, dikt):
|
def store(self, dikt):
|
||||||
"""Store me in a dict if I have anything to store"""
|
"""Store me in a dict if I have anything to store"""
|
||||||
@ -40,17 +43,18 @@ class RCFMessage(object):
|
|||||||
key = '' if self.key is None else self.key.encode()
|
key = '' if self.key is None else self.key.encode()
|
||||||
mtype = '' if self.mtype is None else self.mtype.encode()
|
mtype = '' if self.mtype is None else self.mtype.encode()
|
||||||
body = '' if self.body is None else umsgpack.packb(self.body)
|
body = '' if self.body is None else umsgpack.packb(self.body)
|
||||||
socket.send_multipart([key,mtype, body])
|
socket.send_multipart([key,self.id,mtype, body])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def recv(cls, socket):
|
def recv(cls, socket):
|
||||||
"""Reads key-value message from socket, returns new kvmsg instance."""
|
"""Reads key-value message from socket, returns new kvmsg instance."""
|
||||||
key,mtype, body = socket.recv_multipart(zmq.NOBLOCK)
|
key,id,mtype, body = socket.recv_multipart(zmq.NOBLOCK)
|
||||||
key = key.decode() if key else None
|
key = key.decode() if key else None
|
||||||
|
id = id if id else None
|
||||||
mtype = mtype.decode() if body else None
|
mtype = mtype.decode() if body else None
|
||||||
body = umsgpack.unpackb(body) if body else None
|
body = umsgpack.unpackb(body) if body else None
|
||||||
|
|
||||||
return cls(key=key,mtype=mtype, body=body)
|
return cls(key=key,id=id,mtype=mtype, body=body)
|
||||||
|
|
||||||
def dump(self):
|
def dump(self):
|
||||||
if self.body is None:
|
if self.body is None:
|
||||||
@ -123,18 +127,18 @@ class Client():
|
|||||||
rcfmsg = RCFMessage.recv(self.pull_sock)
|
rcfmsg = RCFMessage.recv(self.pull_sock)
|
||||||
|
|
||||||
rcfmsg.store(self.property_map)
|
rcfmsg.store(self.property_map)
|
||||||
rcfmsg.dump()
|
# rcfmsg.dump()
|
||||||
|
|
||||||
for f in self.recv_callback:
|
for f in self.recv_callback:
|
||||||
f(rcfmsg)
|
f(rcfmsg)
|
||||||
|
|
||||||
def push_update(self, key,mtype,body):
|
def push_update(self, key,mtype,body):
|
||||||
rcfmsg = RCFMessage(key,mtype,body)
|
rcfmsg = RCFMessage(key,self.id,mtype,body)
|
||||||
rcfmsg.send(self.push_sock)
|
rcfmsg.send(self.push_sock)
|
||||||
# self.push_sock.send_multipart()
|
# self.push_sock.send_multipart()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
logger.info("Stopping client")
|
logger.debug("Stopping client")
|
||||||
self.poller.unregister(self.pull_sock)
|
self.poller.unregister(self.pull_sock)
|
||||||
self.req_sock.close()
|
self.req_sock.close()
|
||||||
self.push_sock.close()
|
self.push_sock.close()
|
||||||
@ -182,7 +186,7 @@ class Server():
|
|||||||
|
|
||||||
while True:
|
while True:
|
||||||
# TODO: find a better way
|
# TODO: find a better way
|
||||||
await asyncio.sleep(0.016)
|
await asyncio.sleep(0.0001)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
socks = dict(self.poller.poll(1))
|
socks = dict(self.poller.poll(1))
|
||||||
@ -202,7 +206,7 @@ class Server():
|
|||||||
self.pub_sock.send_multipart(msg)
|
self.pub_sock.send_multipart(msg)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
logger.info("Stopping server")
|
logger.debug("Stopping server")
|
||||||
self.poller.unregister(self.request_sock)
|
self.poller.unregister(self.request_sock)
|
||||||
self.pub_sock.close()
|
self.pub_sock.close()
|
||||||
self.request_sock.close()
|
self.request_sock.close()
|
||||||
|
@ -23,7 +23,7 @@ VECTOR_TYPES = (
|
|||||||
|
|
||||||
# TODO: Less ugly method
|
# TODO: Less ugly method
|
||||||
def from_bpy(value):
|
def from_bpy(value):
|
||||||
print(' casting from bpy')
|
logger.debug(' casting from bpy')
|
||||||
value_type = type(value)
|
value_type = type(value)
|
||||||
value_casted = None
|
value_casted = None
|
||||||
|
|
||||||
@ -59,17 +59,29 @@ def resolve_bpy_path(path):
|
|||||||
|
|
||||||
obj = None
|
obj = None
|
||||||
attribute = path[2]
|
attribute = path[2]
|
||||||
logger.info("resolving {}".format(path))
|
logger.debug("resolving {}".format(path))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
obj = getattr(bpy.data,path[0])[path[1]]
|
obj = getattr(bpy.data,path[0])[path[1]]
|
||||||
attribute = getattr(obj,path[2])
|
attribute = getattr(obj,path[2])
|
||||||
logger.info("done {} : {}".format(obj,attribute))
|
logger.debug("done {} : {}".format(obj,attribute))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
print(" Attribute not found")
|
logger.debug(" Attribute not found")
|
||||||
|
|
||||||
return obj, attribute
|
return obj, attribute
|
||||||
|
|
||||||
|
def observer(scene):
|
||||||
|
global client
|
||||||
|
for key,values in client.property_map.items():
|
||||||
|
# if values.id == client.id:
|
||||||
|
obj, attr = resolve_bpy_path(key)
|
||||||
|
|
||||||
|
if attr != to_bpy(client.property_map[key]):
|
||||||
|
value_type, value = from_bpy(attr)
|
||||||
|
client.push_update(key,value_type,value)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# CLIENT-SERVER
|
# CLIENT-SERVER
|
||||||
def refresh_window(msg):
|
def refresh_window(msg):
|
||||||
import bpy
|
import bpy
|
||||||
@ -77,25 +89,30 @@ def refresh_window(msg):
|
|||||||
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||||
|
|
||||||
def patch_scene(msg):
|
def patch_scene(msg):
|
||||||
value = None
|
global client
|
||||||
|
|
||||||
obj, attr = resolve_bpy_path(msg.key)
|
if msg.id != client.id:
|
||||||
attr_name = msg.key.split('/')[2]
|
value = None
|
||||||
|
|
||||||
value = to_bpy(msg)
|
obj, attr = resolve_bpy_path(msg.key)
|
||||||
print("Updating scene:\n object: {} attribute: {} , value: {}".format(obj, attr_name, value))
|
attr_name = msg.key.split('/')[2]
|
||||||
|
|
||||||
try:
|
|
||||||
setattr(obj,attr_name,value)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
value = to_bpy(msg)
|
||||||
|
|
||||||
|
logger.debug("Updating scene:\n object: {} attribute: {} , value: {}".format(obj, attr_name, value))
|
||||||
|
|
||||||
|
try:
|
||||||
|
setattr(obj,attr_name,value)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
logger.debug('no need to update scene on our own')
|
||||||
|
|
||||||
def vector2array(v):
|
def vector2array(v):
|
||||||
return [v.x,v.y,v.z]
|
return [v.x,v.y,v.z]
|
||||||
|
|
||||||
def array2vector(a):
|
def array2vector(a):
|
||||||
print(mathutils.Vector((a[0],a[1],a[2])))
|
logger.debug(mathutils.Vector((a[0],a[1],a[2])))
|
||||||
return mathutils.Vector((a[0],a[1],a[2]))
|
return mathutils.Vector((a[0],a[1],a[2]))
|
||||||
|
|
||||||
class session_join(bpy.types.Operator):
|
class session_join(bpy.types.Operator):
|
||||||
@ -112,9 +129,9 @@ class session_join(bpy.types.Operator):
|
|||||||
global client
|
global client
|
||||||
|
|
||||||
username = str(context.scene.session_settings.username)
|
username = str(context.scene.session_settings.username)
|
||||||
callbacks=[refresh_window,patch_scene]
|
callbacks=[patch_scene]
|
||||||
|
|
||||||
client = net_components.Client(id=username ,recv_callback=callbacks)
|
client = net_components.Client(id=username,recv_callback=callbacks)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
bpy.ops.asyncio.loop()
|
bpy.ops.asyncio.loop()
|
||||||
@ -162,7 +179,7 @@ class session_create(bpy.types.Operator):
|
|||||||
global client
|
global client
|
||||||
|
|
||||||
username = str(context.scene.session_settings.username)
|
username = str(context.scene.session_settings.username)
|
||||||
callbacks=[refresh_window,patch_scene]
|
callbacks=[patch_scene]
|
||||||
|
|
||||||
server = net_components.Server()
|
server = net_components.Server()
|
||||||
client = net_components.Client(id=username,recv_callback=callbacks)
|
client = net_components.Client(id=username,recv_callback=callbacks)
|
||||||
@ -198,7 +215,7 @@ class session_stop(bpy.types.Operator):
|
|||||||
client = None
|
client = None
|
||||||
bpy.ops.asyncio.stop()
|
bpy.ops.asyncio.stop()
|
||||||
else:
|
else:
|
||||||
logger.info("No server/client running.")
|
logger.debug("No server/client running.")
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@ -225,6 +242,7 @@ def register():
|
|||||||
register_class(cls)
|
register_class(cls)
|
||||||
|
|
||||||
bpy.types.Scene.session_settings = bpy.props.PointerProperty(type=session_settings)
|
bpy.types.Scene.session_settings = bpy.props.PointerProperty(type=session_settings)
|
||||||
|
bpy.app.handlers.depsgraph_update_post.append(observer)
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
from bpy.utils import unregister_class
|
from bpy.utils import unregister_class
|
||||||
|
@ -35,7 +35,7 @@ class SessionPanel(bpy.types.Panel):
|
|||||||
area_msg = row.box()
|
area_msg = row.box()
|
||||||
if len(net_operators.client.property_map) > 0:
|
if len(net_operators.client.property_map) > 0:
|
||||||
for key,values in net_operators.client.property_map.items():
|
for key,values in net_operators.client.property_map.items():
|
||||||
area_msg.label(text="{} ({})".format(key, values.mtype))
|
area_msg.label(text="{} ({}) - ".format(key, values.mtype, values.id.decode()))
|
||||||
else:
|
else:
|
||||||
area_msg.label(text="Empty")
|
area_msg.label(text="Empty")
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user