feat(rcf): update ui, append data load

This commit is contained in:
Swann Martinez 2019-03-25 14:56:09 +01:00
parent 704ea35129
commit a7f712e824
No known key found for this signature in database
GPG Key ID: 414CCAFD8DA720E1
3 changed files with 128 additions and 155 deletions

View File

@ -1,30 +1,29 @@
# import zmq
import asyncio import asyncio
import logging
from .libs.esper import esper
from .libs import zmq
from .libs import umsgpack
import time
import random
import struct
import collections import collections
import logging
import time
from enum import Enum from enum import Enum
from .libs import umsgpack, zmq
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
CONNECT_TIMEOUT = 2 CONNECT_TIMEOUT = 2
WAITING_TIME = 0.001 WAITING_TIME = 0.001
class RCFStatus(Enum): class RCFStatus(Enum):
IDLE = 1 IDLE = 1
CONNECTING = 2 CONNECTING = 2
CONNECTED = 3 CONNECTED = 3
class RCFFactory(object): class RCFFactory(object):
""" """
Abstract layer used to bridge external and inter Abstract layer used to bridge external and inter
""" """
def init(self, data): def init(self, data):
""" """
set the RCFMessage pointer to local data set the RCFMessage pointer to local data
@ -36,7 +35,6 @@ class RCFFactory(object):
# TODO: Setup local pointer # TODO: Setup local pointer
def load_getter(self, data): def load_getter(self, data):
""" """
local program > rcf local program > rcf
@ -61,6 +59,7 @@ class RCFFactory(object):
""" """
pass pass
class RCFStore(collections.MutableMapping, dict): class RCFStore(collections.MutableMapping, dict):
def __init__(self, custom_factory=RCFFactory()): def __init__(self, custom_factory=RCFFactory()):
@ -75,13 +74,17 @@ class RCFStore(collections.MutableMapping,dict):
def __delitem__(self, key): def __delitem__(self, key):
dict.__delitem__(self, key) dict.__delitem__(self, key)
def __iter__(self): def __iter__(self):
return dict.__iter__(self) return dict.__iter__(self)
def __len__(self): def __len__(self):
return dict.__len__(self) return dict.__len__(self)
def __contains__(self, x): def __contains__(self, x):
return dict.__contains__(self, x) return dict.__contains__(self, x)
class RCFMessage(object): class RCFMessage(object):
""" """
Message is formatted on wire as 2 frames: Message is formatted on wire as 2 frames:
@ -110,7 +113,6 @@ class RCFMessage(object):
elif self.key in dikt: elif self.key in dikt:
del dikt[self.key] del dikt[self.key]
def send(self, socket): def send(self, socket):
"""Send key-value message to socket; any empty frames are sent as such.""" """Send key-value message to socket; any empty frames are sent as such."""
key = ''.encode() if self.key is None else self.key.encode() key = ''.encode() if self.key is None else self.key.encode()
@ -148,7 +150,8 @@ class RCFMessage(object):
data=data, data=data,
)) ))
class Client():
class RCFClient():
def __init__( def __init__(
self, self,
context=zmq.Context(), context=zmq.Context(),
@ -236,8 +239,10 @@ class Client():
logger.info("{} client running".format(id)) logger.info("{} client running".format(id))
self.push_update("net/clients/{}".format(self.id.decode()),"client",self.id) self.push_update(
self.push_update("net/objects/{}".format(self.id.decode()),"client_object","None") "net/clients/{}".format(self.id.decode()), "client", self.id)
self.push_update(
"net/objects/{}".format(self.id.decode()), "client_object", "None")
self.tick_task = asyncio.ensure_future(self.tick()) self.tick_task = asyncio.ensure_future(self.tick())
@ -270,9 +275,12 @@ class Client():
self.push_sock.close() self.push_sock.close()
self.pull_sock.close() self.pull_sock.close()
self.load_task.cancel() self.load_task.cancel()
if self.tick_task:
self.tick_task.cancel() self.tick_task.cancel()
class Server():
class RCFServer():
def __init__(self, context=zmq.Context(), id="admin"): def __init__(self, context=zmq.Context(), id="admin"):
self.context = context self.context = context
@ -285,7 +293,7 @@ class Server():
self.id = id self.id = id
self.bind_ports() self.bind_ports()
# Main client loop registration # Main client loop registration
self.task = asyncio.ensure_future(self.main()) self.task = asyncio.ensure_future(self.tick())
logger.info("{} client initialized".format(id)) logger.info("{} client initialized".format(id))
@ -312,7 +320,7 @@ class Server():
self.poller.register(self.request_sock, zmq.POLLIN) self.poller.register(self.request_sock, zmq.POLLIN)
self.poller.register(self.collector_sock, zmq.POLLIN) self.poller.register(self.collector_sock, zmq.POLLIN)
async def main(self): async def tick(self):
logger.info("{} server launched".format(id)) logger.info("{} server launched".format(id))
while True: while True:
@ -350,7 +358,7 @@ class Server():
msg.store(self.property_map) msg.store(self.property_map)
msg.send(self.pub_sock) msg.send(self.pub_sock)
else: else:
await asyncio.sleep(0.0001) await asyncio.sleep(WAITING_TIME)
def stop(self): def stop(self):
logger.debug("Stopping server") logger.debug("Stopping server")

View File

@ -1,19 +1,19 @@
from bpy_extras import view3d_utils
import bpy
from . import net_components
from . import net_ui
from . import rna_translation
from .libs import dump_anything
import time
import logging import logging
import mathutils
import random import random
import string import string
import time
import bgl import bgl
import blf import blf
import bpy
import gpu import gpu
import mathutils
from bpy_extras import view3d_utils
from gpu_extras.batch import batch_for_shader from gpu_extras.batch import batch_for_shader
from . import net_components, net_ui, rna_translation
from .libs import dump_anything
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
client = None client = None
@ -22,7 +22,15 @@ context = None
COLOR_TABLE = [(1, 0, 0, 1), (0, 1, 0, 1), (0, 0, 1, 1), COLOR_TABLE = [(1, 0, 0, 1), (0, 1, 0, 1), (0, 0, 1, 1),
(0, 0.5, 1, 1), (0.5, 0, 1, 1)] (0, 0.5, 1, 1), (0.5, 0, 1, 1)]
SUPPORTED_DATABLOCKS = ['collections', 'meshes', 'objects', 'materials', 'textures', 'lights', 'cameras', 'actions', 'armatures']
# UTILITY FUNCTIONS
def clean_scene(elements=SUPPORTED_DATABLOCKS):
for datablock in elements:
datablock_ref = getattr(bpy.data, datablock)
for item in datablock_ref:
datablock_ref.remove(item)
def view3d_find(): def view3d_find():
@ -98,13 +106,6 @@ def get_client_2d(coords):
return view3d_utils.location_3d_to_region_2d(region, rv3d, coords) return view3d_utils.location_3d_to_region_2d(region, rv3d, coords)
def on_scene_evalutation(scene):
# TODO: viewer representation
# TODO: Live update only selected object
# TODO: Scene representation
pass
def randomStringDigits(stringLength=6): def randomStringDigits(stringLength=6):
"""Generate a random string of letters and digits """ """Generate a random string of letters and digits """
lettersAndDigits = string.ascii_letters + string.digits lettersAndDigits = string.ascii_letters + string.digits
@ -126,30 +127,6 @@ def resolve_bpy_path(path):
return item return item
def observer(scene):
global client
pass
# if client:
# for key, values in client.property_map.items():
# try:
# 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)
# except:
# pass
return bpy.context.scene.session_settings.update_frequency
def mark_objects_for_update(scene):
for item in dir(bpy.data):
# if item in SUPPORTED_DATABLOCKS:
for datablock in getattr(bpy.data,item):
if bpy.context.object.is_evaluated:
print("EVALUATED: {}:{}".format(item,datablock.name))
def refresh_window(): def refresh_window():
import bpy import bpy
@ -159,9 +136,15 @@ def refresh_window():
def init_scene(): def init_scene():
global client global client
for mesh in bpy.data.meshes:
pass
def load_mesh(target, data): def load_mesh(target, data):
import bmesh import bmesh
# TODO: handle error
mesh_buffer = bmesh.new() mesh_buffer = bmesh.new()
for i in data["vertices"]: for i in data["vertices"]:
@ -180,13 +163,17 @@ def load_mesh(target,data):
for v in data["polygons"][p]["vertices"]: for v in data["polygons"][p]["vertices"]:
verts.append(mesh_buffer.verts[v]) verts.append(mesh_buffer.verts[v])
if len(verts) > 0: if len(verts) > 0:
mesh_buffer.faces.new(verts) mesh_buffer.faces.new(verts)
if not target:
target = bpy.data.meshes.new(data["name"])
mesh_buffer.to_mesh(target) mesh_buffer.to_mesh(target)
def load_object(target,data):
pass
def update_scene(msg): def update_scene(msg):
global client global client
@ -197,39 +184,24 @@ def update_scene(msg):
if bpy.context.scene.session_settings.active_object.name in msg.key: if bpy.context.scene.session_settings.active_object.name in msg.key:
raise ValueError() raise ValueError()
if msg.mtype == 'Mesh' or 'Object': if msg.mtype in SUPPORTED_DATABLOCKS:
item = resolve_bpy_path(msg.key) item = resolve_bpy_path(msg.key)
if item: if item is None:
pass
loader = dump_anything.Loader() loader = dump_anything.Loader()
loader.load(item, msg.body) loader.load(item, msg.body)
if msg.mtype == 'Mesh': if msg.mtype == 'Mesh':
load_mesh(item, msg.body) load_mesh(item, msg.body)
# print(msg.get)
# logger.debug("Updating scene:\n object: {} attribute: {} , value: {}".format(
# obj, attr_name, value))
# setattr(obj, attr_name, value) recv_callbacks = [update_scene]
# except:
# passñ
else:
pass
# logger.debug('no need to update scene on our own')
def update_ui(msg):
"""
Update collaborative UI elements
"""
pass
recv_callbacks = [update_scene, update_ui]
post_init_callbacks = [refresh_window] post_init_callbacks = [refresh_window]
# OPERATORS
class session_join(bpy.types.Operator): class session_join(bpy.types.Operator):
bl_idname = "session.join" bl_idname = "session.join"
@ -245,25 +217,27 @@ class session_join(bpy.types.Operator):
global client global client
net_settings = context.scene.session_settings net_settings = context.scene.session_settings
# Scene setup
if net_settings.session_mode == "CONNECT":
clean_scene()
# Session setup
if net_settings.username == "DefaultUser": if net_settings.username == "DefaultUser":
net_settings.username = "{}_{}".format( net_settings.username = "{}_{}".format(
net_settings.username, randomStringDigits()) net_settings.username, randomStringDigits())
username = str(context.scene.session_settings.username) username = str(context.scene.session_settings.username)
client_factory = rna_translation.RNAFactory() client_factory = rna_translation.RNAFactory()
print("{}".format(client_factory.__class__.__name__))
client = net_components.Client( client = net_components.RCFClient(
id=username, id=username,
on_recv=recv_callbacks, on_recv=recv_callbacks,
on_post_init=post_init_callbacks, on_post_init=post_init_callbacks,
factory=client_factory, factory=client_factory,
address=net_settings.ip) address=net_settings.ip,
# time.sleep(1) is_admin=net_settings.session_mode == "HOST")
bpy.ops.asyncio.loop() bpy.ops.asyncio.loop()
# bpy.app.timers.register(observer)
net_settings.is_running = True net_settings.is_running = True
@ -278,6 +252,7 @@ class session_add_property(bpy.types.Operator):
bl_options = {"REGISTER"} bl_options = {"REGISTER"}
property_path: bpy.props.StringProperty(default="None") property_path: bpy.props.StringProperty(default="None")
depth: bpy.props.IntProperty(default=1)
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
@ -295,7 +270,7 @@ class session_add_property(bpy.types.Operator):
dumper = dump_anything.Dumper() dumper = dump_anything.Dumper()
dumper.type_subset = dumper.match_subset_all dumper.type_subset = dumper.match_subset_all
dumper.depth = 4 dumper.depth = self.depth
data = dumper.dump(item) data = dumper.dump(item)
data_type = item.__class__.__name__ data_type = item.__class__.__name__
@ -342,14 +317,13 @@ class session_create(bpy.types.Operator):
global server global server
global client global client
server = net_components.Server() server = net_components.RCFServer()
time.sleep(0.1) time.sleep(0.1)
bpy.ops.session.join() bpy.ops.session.join()
# init_scene() init_scene()
bpy.app.timers.register(observer)
return {"FINISHED"} return {"FINISHED"}
@ -369,8 +343,6 @@ class session_stop(bpy.types.Operator):
net_settings = context.scene.session_settings net_settings = context.scene.session_settings
# bpy.app.timers.unregister(observer)
if server: if server:
server.stop() server.stop()
del server del server
@ -390,14 +362,13 @@ class session_stop(bpy.types.Operator):
class session_settings(bpy.types.PropertyGroup): class session_settings(bpy.types.PropertyGroup):
username = bpy.props.StringProperty( username = bpy.props.StringProperty(
name="Username", default="user_{}".format(randomStringDigits())) name="Username", default="user_{}".format(randomStringDigits()))
ip = bpy.props.StringProperty(name="localhost") ip = bpy.props.StringProperty(name="ip")
port = bpy.props.IntProperty(name="5555") port = bpy.props.IntProperty(name="5555")
add_property_depth = bpy.props.IntProperty(name="add_property_depth",default=1)
buffer = bpy.props.StringProperty(name="None") buffer = bpy.props.StringProperty(name="None")
is_running = bpy.props.BoolProperty(name="is_running", default=False) is_running = bpy.props.BoolProperty(name="is_running", default=False)
hide_users = bpy.props.BoolProperty(name="is_running", default=False) load_data = bpy.props.BoolProperty(name="load_data", default=True)
hide_settings = bpy.props.BoolProperty(name="hide_settings", default=False)
hide_properties = bpy.props.BoolProperty(
name="hide_properties", default=True)
update_frequency = bpy.props.FloatProperty( update_frequency = bpy.props.FloatProperty(
name="update_frequency", default=0.008) name="update_frequency", default=0.008)
active_object = bpy.props.PointerProperty( active_object = bpy.props.PointerProperty(
@ -622,6 +593,7 @@ class session_snapview(bpy.types.Operator):
pass pass
# TODO: Rename to match official blender convention # TODO: Rename to match official blender convention
classes = ( classes = (
session_join, session_join,
@ -634,6 +606,7 @@ classes = (
session_snapview, session_snapview,
) )
def depsgraph_update(scene): def depsgraph_update(scene):
for c in bpy.context.depsgraph.updates.items(): for c in bpy.context.depsgraph.updates.items():
# print(c[1].id) # print(c[1].id)
@ -653,22 +626,11 @@ def register():
bpy.types.Scene.session_settings = bpy.props.PointerProperty( bpy.types.Scene.session_settings = bpy.props.PointerProperty(
type=session_settings) type=session_settings)
# bpy.app.handlers.depsgraph_update_post.append(depsgraph_update)
# bpy.app.handlers.depsgraph_update_post.append(observer)
def unregister(): def unregister():
try:
bpy.app.handlers.depsgraph_update_post.remove(observer)
except:
pass
global server global server
global client global client
# bpy.app.handlers.depsgraph_update_post.remove(depsgraph_update)
# bpy.app.handlers.depsgraph_update_post.remove(observer)
# bpy.app.handlers.depsgraph_update_post.remove(mark_objects_for_update)
if server: if server:
server.stop() server.stop()
del server del server
@ -682,8 +644,6 @@ def unregister():
for cls in reversed(classes): for cls in reversed(classes):
unregister_class(cls) unregister_class(cls)
del bpy.types.Scene.session_settings del bpy.types.Scene.session_settings

View File

@ -38,8 +38,11 @@ class SessionSettingsPanel(bpy.types.Panel):
if scene.session_settings.session_mode == 'HOST': if scene.session_settings.session_mode == 'HOST':
row.operator("session.create",text="HOST") row.operator("session.create",text="HOST")
else: else:
box = row.box()
row.prop(net_settings,"ip",text="server ip") box.prop(net_settings,"ip",text="server ip")
box = box.row()
box.label(text="load data:")
box.prop(net_settings,"load_data",text="")
row = layout.row() row = layout.row()
row.operator("session.join",text="CONNECT") row.operator("session.join",text="CONNECT")
@ -131,8 +134,10 @@ class SessionPropertiesPanel(bpy.types.Panel):
if net_operators.client: if net_operators.client:
row = layout.row(align=True) row = layout.row(align=True)
row.prop(net_settings, "buffer", text="") row.prop(net_settings, "buffer", text="")
row.prop(net_settings,"add_property_depth",text="")
row.operator("session.add_prop", text="", row.operator("session.add_prop", text="",
icon="ADD").property_path = net_settings.buffer icon="ADD").property_path = net_settings.buffer
row = layout.row() row = layout.row()
# Property area # Property area
area_msg = row.box() area_msg = row.box()