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
|
|
|
logger = logging.getLogger(str(__name__)) |
22
|
|
|
#async def socket_server(web_out_queue, address): |
23
|
|
|
#sock = socket(AF_INET, SOCK_STREAM) |
24
|
|
|
#sock.setsockopt(SOL_SOCKET, SO_REUSEADDR,1) |
25
|
|
|
#sock.bind(address) |
26
|
|
|
#sock.listen(5) |
27
|
|
|
#print(f'Server listening at {address}') |
28
|
|
|
#async with sock: |
29
|
|
|
#while True: |
30
|
|
|
#client, addr = await sock.accept() |
31
|
|
|
#wc = WebClient(client, addr, web_out_queue) |
32
|
|
|
#await spawn(wc.run, daemon=True) |
33
|
|
|
|
34
|
|
|
async def bricknil_socket_server(web_out_queue, address): #pragma: no cover |
35
|
|
|
"""Listen for client connections on port 25000 and spawn |
36
|
|
|
`WebClient` instance. |
37
|
|
|
This fuction is spawned as a task during system instantiation |
38
|
|
|
in :func:`bricknil.bricknil._run_all`` |
39
|
|
|
""" |
40
|
|
|
async def web_client_connected(client, addr): |
41
|
|
|
logger.info('connection from', addr) |
42
|
|
|
wc = WebClient(client, addr, web_out_queue) |
43
|
|
|
await wc.run() |
44
|
|
|
|
45
|
|
|
task = await spawn(tcp_server, '', 25000, web_client_connected, daemon=True) |
46
|
|
|
|
47
|
|
|
|
48
|
|
|
class WebClient: #pragma: no cover |
49
|
|
|
""" Represents a client that has connected to BrickNil's server |
50
|
|
|
|
51
|
|
|
Each client has a connection to the global BrickNil `curio.Queue` |
52
|
|
|
that handles broadcast messages about peripherals. Peripherals |
53
|
|
|
insert the messages into the queue, and clients can read from |
54
|
|
|
it (hence why it's called in_queue in this class). |
55
|
|
|
""" |
56
|
|
|
def __init__(self, client, addr, in_queue): |
57
|
|
|
assert in_queue is not None |
58
|
|
|
self.in_queue = in_queue |
59
|
|
|
self.client = client |
60
|
|
|
self.addr = addr |
61
|
|
|
logger.info(f'Web client {client} connected from {addr}') |
62
|
|
|
|
63
|
|
|
|
64
|
|
|
async def run(self): |
65
|
|
|
|
66
|
|
|
async with self.client: |
67
|
|
|
while True: |
68
|
|
|
msg = await self.in_queue.get() |
69
|
|
|
#print(f'Webclient queue got: {msg}') |
70
|
|
|
await self.in_queue.task_done() |
71
|
|
|
await self.client.sendall(msg) |
72
|
|
|
logger.info('connection closed') |
73
|
|
|
|
74
|
|
|
class WebMessage: |
75
|
|
|
"""Handles message conversion into JSON and transmission |
76
|
|
|
""" |
77
|
|
|
|
78
|
|
|
def __init__(self, hub): |
79
|
|
|
self.hub = hub |
80
|
|
|
|
81
|
|
|
async def send(self, peripheral, msg): |
82
|
|
|
obj = { 'hub': self.hub.name, |
83
|
|
|
'peripheral_type': peripheral.__class__.__name__, |
84
|
|
|
'peripheral_name': peripheral.name, |
85
|
|
|
'peripheral_port': peripheral.port, |
86
|
|
|
'message': msg , |
87
|
|
|
} |
88
|
|
|
obj_string = json.dumps(obj) |
89
|
|
|
logger.debug(obj_string) |
90
|
|
|
await self.hub.web_queue_out.put(f'{obj_string}\n'.encode('utf-8')) |