async def _ws_conn_handler(self, ws): try: if hasattr(ws.remote, 'address'): ip = ws.remote.address else: ip = ws.remote log.info("New ws connection to {} from {}", ws.path, ip) log.debug("Children objects refs: {}", list(self._child_obj.keys())) log.debug("Clients connected: {}", self._subscr) refv = ws.path.split('/')[1] # if anything is in the path if len(refv)>0: async for msg in self._handle_obj_ref(ws, refv): yield msg else: # return link to root element log.info("Sending root {}", ref(self.vars)) yield json.dumps({'root': ref(self.vars)}) except ConnectionClosed as e: code = e.reason.code if code == 1000: log.info('Client {} goes away from {}', ip, ws.path) else: log.warning('Connection from {} to {} closed: {}.', ip, ws.path, e) except Exception as e: log.error("Handling {} error: {}", type(e), e)
def test_simple_var_poll(): responses = Queue() app = App(addr=addr, port=port) client = LeClient('value') try: app.run() time.sleep(.05) class List(list): pass object = List() app.watch_obj(object) app.vars.value = ref(object) _ = client.start(f'ws://{addr}:{port}') for i in range(5): object.append(i) time.sleep(.1) time.sleep(.1) assert client.updates_count() > 0 responses = client.get_all_updates() assert responses[-1] == str(object) finally: app.stop()
def _register_child(self, o): if isinstance(o, Object): def put_updates(name, value): """ Put the updates to temp dictionary """ name, value = o._prepare_send(name, value) # Determines what to send to ws clients self._children_updates[ref(o)].update({name:value}) o.subscribe_set(put_updates) o.subscribe_set(self._register_new) # Determines where to put received updates self._child_obj[ref(o)] = o log.debug("Added child object {} with ref {}", o, ref(o)) # returning none makes mapper # traverse the whole dict tree return None
async def _child_updating_loop(self, ws, child): """ Listen for incomming updates from websocket and update the relevant child This coroutine works N times, where N is number of requested existing objects (from `self._child_obj`) """ log.debug("Listening for updates for {}", ref(child)) while True: msg = await ws.get_message() try: updates = json.loads(msg) log.debug("Updates for {} : {}", ref(child), updates) for key, value in updates.items(): try: child._commit_update(key, value) except Exception as e: log.error(f"{e}. While commiting update to {child}. " f"{key} : {value}") except TypeError: log.error("JSON decode error: {}", msg)
def test_object_poll(): responses = Queue() app = App(addr=addr, port=port) client = LeClient('rapid_updated') try: app.run() time.sleep(.05) _ = client.start(f'ws://{addr}:{port}') class Rollin(Object): def __init__(self): super().__init__() self.x = [] rapid = Rollin() app.watch_obj(rapid) app.vars.rapid_updated = ref(rapid) dt = 1e-4 T = 0.5 # Put data in bursts, use N appends between sleeps N = int(0.005 / dt) app._watch_poll_delay = 0.1 for i in range(int(T // dt)): rapid.x += [i] if i % N == 0: #print('put',i, time.time()) # Don't try to do μs delays, need Real-Time OS for this time.sleep(N * dt) #Wait for finish update time.sleep(app._watch_poll_delay) responses = [json.loads(x) for x in client.get_all_updates()] assert responses xlens = [len(x['x']) for x in responses] assert xlens[-1] == int(T // dt) print("Lengths of rapid updates of growing list", xlens) assert len(responses) >= T // app._watch_poll_delay dlens = [x1 - x0 for x0, x1 in zip(xlens[:-1], xlens[1:])] incrs = app._watch_poll_delay / dt print(f"Lengths increments: should be near {incrs}", dlens) assert all(x <= incrs for x in dlens) finally: app.stop()
def put_updates(name, value): """ Put the updates to temp dictionary """ name, value = o._prepare_send(name, value) # Determines what to send to ws clients self._children_updates[ref(o)].update({name:value})
def watch_obj(self, obj): self._watched_children[ref(obj)] = obj self._child_obj[ref(obj)] = obj