-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.py
More file actions
131 lines (101 loc) · 4.04 KB
/
Copy pathserver.py
File metadata and controls
131 lines (101 loc) · 4.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import asyncio
from asyncio.queues import Queue
from typing import Any
from starlette.applications import Starlette
from starlette.endpoints import WebSocketEndpoint
from starlette.routing import WebSocketRoute
from starlette.websockets import WebSocket
from olink.core.types import Name
from olink.remote import IObjectSource, RemoteNode
class CounterService:
count = 0
_node: RemoteNode
def increment(self):
self.count += 1
self._node.notify_property_change("demo.Counter/count", self.count)
class CounterWebsocketAdapter(IObjectSource):
# adapts the websocket communication to the remote source
node: RemoteNode = None
def __init__(self, impl):
self.impl = impl
self._methods = {"increment": impl.increment}
self._properties = {"count": lambda v: setattr(impl, "count", v)}
# register the source with the node registry
RemoteNode.register_source(self)
def olink_object_name(self):
# return service name
return "demo.Counter"
def olink_invoke(self, name: str, args: list[Any]) -> Any:
# handle the remote call from client node
path = Name.path_from_name(name)
func = self._methods.get(path)
if func is None:
return None
return func(*args)
def olink_set_property(self, name: str, value: Any):
# set property value on implementation
path = Name.path_from_name(name)
setter = self._properties.get(path)
if setter is not None:
setter(value)
def olink_linked(self, name: str, node: "RemoteNode"):
# called when the source is linked to a client node
self.impl._node = node
def olink_unlinked(self, name: str):
# called when the source is linked to a client node
self.impl._node = None
def olink_collect_properties(self) -> object:
# collect properties from implementation to send back to client node initially
return {k: getattr(self.impl, k) for k in ["count"]}
# create the service implementation
counter = CounterService()
# create the adapter for the implementation
adapter = CounterWebsocketAdapter(counter)
class RemoteEndpoint(WebSocketEndpoint):
# endpoint to handle a client connection
encoding = "text"
def __init__(self, scope, receive, send):
super().__init__(scope, receive, send)
self.node = RemoteNode()
self.queue = Queue()
async def sender(self, ws):
# sender coroutine, messages from queue are send to client
print("start sender")
while True:
msg = await self.queue.get()
print("send", msg)
await ws.send_text(msg)
self.queue.task_done()
async def on_connect(self, ws: WebSocket):
# handle a socket connection
print("on_connect")
# register a sender to the connection
self._sender_task = asyncio.create_task(self.sender(ws))
# a writer function to queue messages
def writer(msg: str):
print("write to queue:", msg)
self.queue.put_nowait(msg)
# register the writer function to the node
self.node.on_write(writer)
# call the super connection handler
await super().on_connect(ws)
async def on_receive(self, ws: WebSocket, data: Any) -> None:
# handle a message from a client socket
print("on_receive", data)
self.node.handle_message(data)
async def on_disconnect(self, websocket: WebSocket, close_code: int) -> None:
# handle a socket disconnect
await super().on_disconnect(websocket, close_code)
# remove the writer from the node
self.node.on_write(None)
# cancel the sender task to avoid deadlock
self._sender_task.cancel()
try:
await self._sender_task
except asyncio.CancelledError:
pass
# see https://www.starlette.io/routing/
routes = [WebSocketRoute("/ws", RemoteEndpoint)]
# call with `uvicorn server:app --port 8282`
# see https://www.starlette.io
app = Starlette(routes=routes)