|
1
|
|
|
|
|
2
|
|
|
# Copyright 2019 Virantha N. Ekanayake |
|
3
|
|
|
# |
|
4
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
5
|
|
|
# you may not use this file except in compliance with the License. |
|
6
|
|
|
# You may obtain a copy of the License at |
|
7
|
|
|
# |
|
8
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0 |
|
9
|
|
|
# |
|
10
|
|
|
# Unless required by applicable law or agreed to in writing, software |
|
11
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS, |
|
12
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
13
|
|
|
# See the License for the specific language governing permissions and |
|
14
|
|
|
# limitations under the License. |
|
15
|
|
|
"""Experimental socket updates of devices and hubs""" |
|
16
|
|
|
|
|
17
|
|
|
import logging |
|
18
|
|
|
import json |
|
19
|
|
|
from curio import run, spawn, sleep, Queue, tcp_server |
|
20
|
|
|
|
|
21
|
|
|
#async def socket_server(web_out_queue, address): |
|
22
|
|
|
#sock = socket(AF_INET, SOCK_STREAM) |
|
23
|
|
|
#sock.setsockopt(SOL_SOCKET, SO_REUSEADDR,1) |
|
24
|
|
|
#sock.bind(address) |
|
25
|
|
|
#sock.listen(5) |
|
26
|
|
|
#print(f'Server listening at {address}') |
|
27
|
|
|
#async with sock: |
|
28
|
|
|
#while True: |
|
29
|
|
|
#client, addr = await sock.accept() |
|
30
|
|
|
#wc = WebClient(client, addr, web_out_queue) |
|
31
|
|
|
#await spawn(wc.run, daemon=True) |
|
32
|
|
|
|
|
33
|
|
|
async def bricknil_socket_server(web_out_queue, address): |
|
34
|
|
|
"""Listen for client connections on port 25000 and spawn |
|
35
|
|
|
`WebClient` instance. |
|
36
|
|
|
This fuction is spawned as a task during system instantiation |
|
37
|
|
|
in :func:`bricknil.bricknil._run_all`` |
|
38
|
|
|
""" |
|
39
|
|
|
async def web_client_connected(client, addr): |
|
40
|
|
|
print('connection from', addr) |
|
41
|
|
|
wc = WebClient(client, addr, web_out_queue) |
|
42
|
|
|
await wc.run() |
|
43
|
|
|
|
|
44
|
|
|
task = await spawn(tcp_server, '', 25000, web_client_connected, daemon=True) |
|
45
|
|
|
|
|
46
|
|
|
|
|
47
|
|
|
class WebClient: |
|
48
|
|
|
""" Represents a client that has connected to BrickNil's server |
|
49
|
|
|
|
|
50
|
|
|
Each client has a connection to the global BrickNil `curio.Queue` |
|
51
|
|
|
that handles broadcast messages about peripherals. Peripherals |
|
52
|
|
|
insert the messages into the queue, and clients can read from |
|
53
|
|
|
it (hence why it's called in_queue in this class). |
|
54
|
|
|
""" |
|
55
|
|
|
def __init__(self, client, addr, in_queue): |
|
56
|
|
|
assert in_queue is not None |
|
57
|
|
|
self.in_queue = in_queue |
|
58
|
|
|
self.client = client |
|
59
|
|
|
self.addr = addr |
|
60
|
|
|
print(f'Web client {client} connected from {addr}') |
|
61
|
|
|
|
|
62
|
|
|
|
|
63
|
|
|
async def run(self): |
|
64
|
|
|
|
|
65
|
|
|
async with self.client: |
|
66
|
|
|
while True: |
|
67
|
|
|
msg = await self.in_queue.get() |
|
68
|
|
|
#print(f'Webclient queue got: {msg}') |
|
69
|
|
|
await self.in_queue.task_done() |
|
70
|
|
|
await self.client.sendall(msg) |
|
71
|
|
|
print('connection closed') |
|
72
|
|
|
|
|
73
|
|
|
class WebMessage: |
|
74
|
|
|
"""Handles message conversion into JSON and transmission |
|
75
|
|
|
""" |
|
76
|
|
|
|
|
77
|
|
|
def __init__(self, hub): |
|
78
|
|
|
self.hub = hub |
|
79
|
|
|
|
|
80
|
|
|
async def send(self, peripheral, msg): |
|
81
|
|
|
obj = { 'hub': self.hub.name, |
|
82
|
|
|
'peripheral_type': peripheral.__class__.__name__, |
|
83
|
|
|
'peripheral_name': peripheral.name, |
|
84
|
|
|
'peripheral_port': peripheral.port, |
|
85
|
|
|
'message': msg , |
|
86
|
|
|
} |
|
87
|
|
|
obj_string = json.dumps(obj) |
|
88
|
|
|
print(obj_string) |
|
89
|
|
|
await self.hub.web_queue_out.put(f'{obj_string}\n'.encode('utf-8')) |