Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 7089e047

Přidáno uživatelem Jakub Šilhavý před více než 2 roky(ů)

re #9422 Generated a documentation using pdoc

Zobrazit rozdíly:

client/doc/pdoc/usb_detector/api_client.html
1
<!doctype html>
2
<html lang="en">
3
<head>
4
<meta charset="utf-8">
5
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
6
<meta name="generator" content="pdoc 0.10.0" />
7
<title>usb_detector.api_client API documentation</title>
8
<meta name="description" content="" />
9
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/sanitize.min.css" integrity="sha256-PK9q560IAAa6WVRRh76LtCaI8pjTJ2z11v0miyNNjrs=" crossorigin>
10
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/typography.min.css" integrity="sha256-7l/o7C8jubJiy74VsKTidCy1yBkRtiUGbVkYBylBqUg=" crossorigin>
11
<link rel="stylesheet preload" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/github.min.css" crossorigin>
12
<style>:root{--highlight-color:#fe9}.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}#sidebar > *:last-child{margin-bottom:2cm}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}h1:target,h2:target,h3:target,h4:target,h5:target,h6:target{background:var(--highlight-color);padding:.2em 0}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{margin-top:.6em;font-weight:bold}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}dt:target .name{background:var(--highlight-color)}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}td{padding:0 .5em}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
13
<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%;height:100vh;overflow:auto;position:sticky;top:0}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style>
14
<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
15
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js" integrity="sha256-Uv3H6lx7dJmRfRvH8TH6kJD1TSK1aFcwgx+mdg3epi8=" crossorigin></script>
16
<script>window.addEventListener('DOMContentLoaded', () => hljs.initHighlighting())</script>
17
</head>
18
<body>
19
<main>
20
<article id="content">
21
<header>
22
<h1 class="title">Module <code>usb_detector.api_client</code></h1>
23
</header>
24
<section id="section-intro">
25
<details class="source">
26
<summary>
27
<span>Expand source code</span>
28
</summary>
29
<pre><code class="python">import json
30
import requests
31
import logging
32
from time import sleep
33
from diskcache import Deque
34
from requests import HTTPError, ConnectionError
35
from requests.exceptions import InvalidSchema
36

  
37

  
38
_uri = None     # server uri (url, port, and endpoint)
39
_cache = None   # cache (failed payloads)
40
_config = None  # instance of Config
41

  
42

  
43
def api_client_set_config(config):
44
    &#34;&#34;&#34;Initializes the client API module.
45

  
46
    This function is meant to be called prior to calling any other function
47
    of the API module. It stores the instance of Config (config manager)
48
    into a private variable. It also initializes the cache for unsuccessful
49
    payloads and constructs a URI (endpoint on the server side).
50

  
51
    :param config: instance of Config which holds all values defined
52
                   in the configuration file.
53
    &#34;&#34;&#34;
54
    # Store the variables globally within the module (file).
55
    global _config, _cache, _uri
56

  
57
    # Store the instance of Config and initialize the cache.
58
    _config = config
59
    _cache = _init_cache()
60

  
61
    # Creates the URI which is made of the server url, port, and path (endpoint).
62
    _uri = config.server_url + &#34;:&#34; + config.server_port + config.server_endpoint
63

  
64

  
65
def _init_cache():
66
    &#34;&#34;&#34; Initializes and returns a disk-based cache.
67

  
68
    The cache holds payloads that the application failed
69
    to send to the server. It periodically attempts to resend
70
    them to the server. All parameters can be seen in the
71
    configuration file.
72

  
73
    :return: instance of a new cache (Deque - FIFO)
74
    &#34;&#34;&#34;
75
    return Deque(directory=_config.cache_dir)
76

  
77

  
78
def send_data(payload: dict):
79
    &#34;&#34;&#34;Sends a payload off to the server.
80

  
81
    This function is called whenever a USB is connected
82
    or disconnected. If there is no internet connection or the
83
    server is not up and running, the payload will be stored
84
    into the disk cache.
85

  
86
    :param payload: payload to be sent to the server
87
    &#34;&#34;&#34;
88
    # Make sure that the URI has been constructed properly.
89
    # It is supposed to be done by calling the api_client_set_config function
90
    # with appropriate parameters.
91
    if _uri is None:
92
        logging.warning(f&#34;sending payload = {payload} failed because uri is set to None&#34;)
93
        _cache_failed_payload(payload)
94
        return
95
    try:
96
        logging.info(f&#34;sending payload = {payload} to {_uri}&#34;)
97
        response = requests.post(url=_uri, data=json.dumps(payload))
98
        logging.info(f&#34;response text: {response.text}&#34;)
99
    except (ConnectionError, InvalidSchema):
100
        logging.warning(f&#34;sending payload = {payload} to {_uri} failed&#34;)
101
        _cache_failed_payload(payload)
102
    except HTTPError as error:
103
        logging.error(f&#34;HTTP Error ({_uri}) payload = {payload}, {error}&#34;)
104
        _cache_failed_payload(payload)
105

  
106

  
107
def _cache_failed_payload(payload: dict):
108
    &#34;&#34;&#34; Caches a payload.
109

  
110
    This function is called when the application fails to send a payload
111
    to the server. The payload gets stored into a file-based cache from which
112
    it will be periodically retrieved as the client will attempt to send
113
    it to the server again. All parameters regarding the cache can be found
114
    in the configuration file.
115

  
116
    :param payload: payload to be cached
117
    &#34;&#34;&#34;
118
    # If the cache is &#34;full&#34;, discard the oldest record.
119
    if len(_cache) &gt;= _config.cache_max_entries:
120
        oldest_payload = _cache.pop()
121
        logging.warning(f&#34;cache is full - discarding payload = {oldest_payload}&#34;)
122

  
123
    # Store the payload into the cache.
124
    logging.info(f&#34;adding payload = {payload} into cache&#34;)
125
    _cache.append(payload)
126

  
127

  
128
def _resend_cached_payloads():
129
    &#34;&#34;&#34;Reattempts to send cached payloads to the server (API).
130

  
131
    In the configuration file, there is a predefined number of
132
    payloads that can be sent to the server with each call of this function.
133
    This function is called periodically from api_client_run in order
134
    to resend failed payloads to the server.
135

  
136
    &#34;&#34;&#34;
137
    # Calculate how many payload will be sent to the server
138
    retries = min(_config.cache_max_retries, len(_cache))
139
    logging.info(f&#34;emptying the cache ({retries} records)&#34;)
140

  
141
    # Send the payloads to the server one by one.
142
    for _ in range(0, retries):
143
        payload = _cache.pop()
144
        send_data(payload)
145

  
146

  
147
def api_client_run():
148
    &#34;&#34;&#34; Keeps resending failed payloads to the server.
149

  
150
    This function is instantiated as a thread that periodically
151
    calls the _resend_cached_payloads function in order to empty
152
    the cache (failed payloads). The period can be set in the
153
    configuration file.
154
    &#34;&#34;&#34;
155
    while True:
156
        # Resend a predefined amount of failed payloads to the server.
157
        _resend_cached_payloads()
158

  
159
        # Sleep for a predefined amount of seconds.
160
        sleep(_config.cache_retry_period_seconds)</code></pre>
161
</details>
162
</section>
163
<section>
164
</section>
165
<section>
166
</section>
167
<section>
168
<h2 class="section-title" id="header-functions">Functions</h2>
169
<dl>
170
<dt id="usb_detector.api_client.api_client_run"><code class="name flex">
171
<span>def <span class="ident">api_client_run</span></span>(<span>)</span>
172
</code></dt>
173
<dd>
174
<div class="desc"><p>Keeps resending failed payloads to the server.</p>
175
<p>This function is instantiated as a thread that periodically
176
calls the _resend_cached_payloads function in order to empty
177
the cache (failed payloads). The period can be set in the
178
configuration file.</p></div>
179
<details class="source">
180
<summary>
181
<span>Expand source code</span>
182
</summary>
183
<pre><code class="python">def api_client_run():
184
    &#34;&#34;&#34; Keeps resending failed payloads to the server.
185

  
186
    This function is instantiated as a thread that periodically
187
    calls the _resend_cached_payloads function in order to empty
188
    the cache (failed payloads). The period can be set in the
189
    configuration file.
190
    &#34;&#34;&#34;
191
    while True:
192
        # Resend a predefined amount of failed payloads to the server.
193
        _resend_cached_payloads()
194

  
195
        # Sleep for a predefined amount of seconds.
196
        sleep(_config.cache_retry_period_seconds)</code></pre>
197
</details>
198
</dd>
199
<dt id="usb_detector.api_client.api_client_set_config"><code class="name flex">
200
<span>def <span class="ident">api_client_set_config</span></span>(<span>config)</span>
201
</code></dt>
202
<dd>
203
<div class="desc"><p>Initializes the client API module.</p>
204
<p>This function is meant to be called prior to calling any other function
205
of the API module. It stores the instance of Config (config manager)
206
into a private variable. It also initializes the cache for unsuccessful
207
payloads and constructs a URI (endpoint on the server side).</p>
208
<p>:param config: instance of Config which holds all values defined
209
in the configuration file.</p></div>
210
<details class="source">
211
<summary>
212
<span>Expand source code</span>
213
</summary>
214
<pre><code class="python">def api_client_set_config(config):
215
    &#34;&#34;&#34;Initializes the client API module.
216

  
217
    This function is meant to be called prior to calling any other function
218
    of the API module. It stores the instance of Config (config manager)
219
    into a private variable. It also initializes the cache for unsuccessful
220
    payloads and constructs a URI (endpoint on the server side).
221

  
222
    :param config: instance of Config which holds all values defined
223
                   in the configuration file.
224
    &#34;&#34;&#34;
225
    # Store the variables globally within the module (file).
226
    global _config, _cache, _uri
227

  
228
    # Store the instance of Config and initialize the cache.
229
    _config = config
230
    _cache = _init_cache()
231

  
232
    # Creates the URI which is made of the server url, port, and path (endpoint).
233
    _uri = config.server_url + &#34;:&#34; + config.server_port + config.server_endpoint</code></pre>
234
</details>
235
</dd>
236
<dt id="usb_detector.api_client.send_data"><code class="name flex">
237
<span>def <span class="ident">send_data</span></span>(<span>payload: dict)</span>
238
</code></dt>
239
<dd>
240
<div class="desc"><p>Sends a payload off to the server.</p>
241
<p>This function is called whenever a USB is connected
242
or disconnected. If there is no internet connection or the
243
server is not up and running, the payload will be stored
244
into the disk cache.</p>
245
<p>:param payload: payload to be sent to the server</p></div>
246
<details class="source">
247
<summary>
248
<span>Expand source code</span>
249
</summary>
250
<pre><code class="python">def send_data(payload: dict):
251
    &#34;&#34;&#34;Sends a payload off to the server.
252

  
253
    This function is called whenever a USB is connected
254
    or disconnected. If there is no internet connection or the
255
    server is not up and running, the payload will be stored
256
    into the disk cache.
257

  
258
    :param payload: payload to be sent to the server
259
    &#34;&#34;&#34;
260
    # Make sure that the URI has been constructed properly.
261
    # It is supposed to be done by calling the api_client_set_config function
262
    # with appropriate parameters.
263
    if _uri is None:
264
        logging.warning(f&#34;sending payload = {payload} failed because uri is set to None&#34;)
265
        _cache_failed_payload(payload)
266
        return
267
    try:
268
        logging.info(f&#34;sending payload = {payload} to {_uri}&#34;)
269
        response = requests.post(url=_uri, data=json.dumps(payload))
270
        logging.info(f&#34;response text: {response.text}&#34;)
271
    except (ConnectionError, InvalidSchema):
272
        logging.warning(f&#34;sending payload = {payload} to {_uri} failed&#34;)
273
        _cache_failed_payload(payload)
274
    except HTTPError as error:
275
        logging.error(f&#34;HTTP Error ({_uri}) payload = {payload}, {error}&#34;)
276
        _cache_failed_payload(payload)</code></pre>
277
</details>
278
</dd>
279
</dl>
280
</section>
281
<section>
282
</section>
283
</article>
284
<nav id="sidebar">
285
<h1>Index</h1>
286
<div class="toc">
287
<ul></ul>
288
</div>
289
<ul id="index">
290
<li><h3>Super-module</h3>
291
<ul>
292
<li><code><a title="usb_detector" href="index.html">usb_detector</a></code></li>
293
</ul>
294
</li>
295
<li><h3><a href="#header-functions">Functions</a></h3>
296
<ul class="">
297
<li><code><a title="usb_detector.api_client.api_client_run" href="#usb_detector.api_client.api_client_run">api_client_run</a></code></li>
298
<li><code><a title="usb_detector.api_client.api_client_set_config" href="#usb_detector.api_client.api_client_set_config">api_client_set_config</a></code></li>
299
<li><code><a title="usb_detector.api_client.send_data" href="#usb_detector.api_client.send_data">send_data</a></code></li>
300
</ul>
301
</li>
302
</ul>
303
</nav>
304
</main>
305
<footer id="footer">
306
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
307
</footer>
308
</body>
309
</html>
client/doc/pdoc/usb_detector/detector.html
1
<!doctype html>
2
<html lang="en">
3
<head>
4
<meta charset="utf-8">
5
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
6
<meta name="generator" content="pdoc 0.10.0" />
7
<title>usb_detector.detector API documentation</title>
8
<meta name="description" content="" />
9
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/sanitize.min.css" integrity="sha256-PK9q560IAAa6WVRRh76LtCaI8pjTJ2z11v0miyNNjrs=" crossorigin>
10
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/typography.min.css" integrity="sha256-7l/o7C8jubJiy74VsKTidCy1yBkRtiUGbVkYBylBqUg=" crossorigin>
11
<link rel="stylesheet preload" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/github.min.css" crossorigin>
12
<style>:root{--highlight-color:#fe9}.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}#sidebar > *:last-child{margin-bottom:2cm}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}h1:target,h2:target,h3:target,h4:target,h5:target,h6:target{background:var(--highlight-color);padding:.2em 0}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{margin-top:.6em;font-weight:bold}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}dt:target .name{background:var(--highlight-color)}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}td{padding:0 .5em}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
13
<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%;height:100vh;overflow:auto;position:sticky;top:0}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style>
14
<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
15
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js" integrity="sha256-Uv3H6lx7dJmRfRvH8TH6kJD1TSK1aFcwgx+mdg3epi8=" crossorigin></script>
16
<script>window.addEventListener('DOMContentLoaded', () => hljs.initHighlighting())</script>
17
</head>
18
<body>
19
<main>
20
<article id="content">
21
<header>
22
<h1 class="title">Module <code>usb_detector.detector</code></h1>
23
</header>
24
<section id="section-intro">
25
<details class="source">
26
<summary>
27
<span>Expand source code</span>
28
</summary>
29
<pre><code class="python">import json
30
import logging
31
from time import sleep
32

  
33
from .usb_reader import read_connected_devices
34

  
35

  
36
_listeners_connected = []       # list of listeners (USB devices is connected)
37
_listeners_disconnected = []    # list of listeners (USB devices is disconnected)
38

  
39
_last_connected_devices = []    # list of the lastly connected USB devices
40
_config = None                  # instance of Config (config manager)
41

  
42

  
43
def usb_detector_set_config(config):
44
    &#34;&#34;&#34;Initializes the usb detector module (file).
45

  
46
    This function is meant to be called prior to calling
47
    any other function of the detector module. It stores
48
    an instance of the Config class which is then used
49
    by other functions within this file.
50

  
51
    :param config: instance of Config (config manager)
52
    &#34;&#34;&#34;
53
    # Store the instance into the global variable.
54
    global _config
55
    _config = config
56

  
57

  
58
def register_listener(callback, connected: bool = True):
59
    &#34;&#34;&#34;Registers a new event listener.
60

  
61
    The caller is supposed to pass in a function they
62
    wish to be called whenever an event occurs. What kind
63
    of event will trigger (call) the callback function is
64
    determined by the second parameter.
65

  
66
    :param callback: Function that is called whenever the desired event happens.
67
    :param connected: If the value is set to True, the callback function
68
                      will be called whenever a USB device is connected.
69
                      If it is set to False, it will be triggered whenever a
70
                      USB device is disconnected.
71
    &#34;&#34;&#34;
72
    logging.info(f&#34;Registering callback: {callback}.&#34;)
73

  
74
    if connected is True:
75
        # Register the callback for &#34;connected devices&#34;
76
        _listeners_connected.append(callback)
77
    else:
78
        # Register the callback for &#34;disconnected devices&#34;
79
        _listeners_disconnected.append(callback)
80

  
81

  
82
def _notify_listeners(listeners: list, devices: list):
83
    &#34;&#34;&#34; Notifies (calls) all listeners based on the even that just occurred.
84

  
85
    This function is called whenever a USB device is plugged or unplugged.
86
    Based on the type of the event, the corresponding list of listeners
87
    is passed in along with a list of all devices involved in the event.
88

  
89
    :param listeners: list of listeners registered for the event
90
    :param devices: list of all USB devices involved in the event
91
                    (usually a single device)
92
    &#34;&#34;&#34;
93
    # Make sure both lists are not None.
94
    if listeners is None or devices is None:
95
        return
96

  
97
    # Iterate over the listeners and notify them
98
    # of all USB devices involved in the event.
99
    for callback in listeners:
100
        for device in devices:
101
            callback(device)
102

  
103

  
104
def _store_connected_devices(devices: list):
105
    &#34;&#34;&#34;Stores the list of the currently connected USB devices into a file.
106

  
107
    This function is called whenever a device is connected or disconnected.
108
    Its main purpose is to dump the list of the currently plugged devices
109
    on the disk (so it is not kept in RAM when the computer shuts down). The
110
    list is then loaded upon every start of the application.
111

  
112
    :param devices: list of the devices that are currently connected to the PC
113
    &#34;&#34;&#34;
114
    logging.debug(&#34;storing newly connected devices&#34;)
115

  
116
    # Dump the list into a JSON format.
117
    with open(_config.connected_devices_filename, &#34;w&#34;) as file:
118
        json.dump(devices, file)
119

  
120

  
121
def _load_last_connected_devices() -&gt; list:
122
    &#34;&#34;&#34;Loads the list of the connected devices from the disk.
123

  
124
    This function is called with every start of the application.
125
    It ensures that the application remembers the USB devices
126
    that were connected to the PC before it was turned off
127
    (persistent memory).
128

  
129
    :return: list of the lastly connected USB devices
130
    &#34;&#34;&#34;
131
    logging.debug(&#34;loading last connected devices&#34;)
132
    try:
133
        with open(_config.connected_devices_filename, &#34;r&#34;) as file:
134
            return json.loads(file.read())
135
    except IOError:
136
        logging.error(&#34;loading of last connected devices failed&#34;)
137
        return []
138

  
139

  
140
def _get_connected_devices(detected_devices: list, last_connected_devices: list) -&gt; list:
141
    &#34;&#34;&#34;Returns a list of USB devices that were just plugged into the computer.
142

  
143
    Using the two lists passed in as parameters, it figures out what devices
144
    were just connected to the PC. Essentially, any device in the detected_devices list
145
    that does not apper in the last_connected_devices list must have been just plugged in.
146

  
147
    :param detected_devices: list of the currently plugged USB devices
148
    :param last_connected_devices: list of the lastly connected USB devices
149
    :return: list of all USB devices that were just plugged into the computer
150
    &#34;&#34;&#34;
151
    # If there is no previous record of what USB devices where connected to the PC,
152
    # all newly-connected devices are treated as if they were just plugged in.
153
    if last_connected_devices is None and detected_devices is not None:
154
        return detected_devices
155

  
156
    # Return an empty list if no devices were detected.
157
    if detected_devices is None:
158
        return []
159

  
160
    # Return a list of all devices that were just plugged into the PC.
161
    return [device for device in detected_devices if device not in last_connected_devices]
162

  
163

  
164
def _get_disconnected_devices(detected_devices: list, last_connected_devices: list) -&gt; list:
165
    &#34;&#34;&#34;Returns a list of USB devices that were just disconnected from the computer.
166

  
167
    Using the two lists passed in as parameters, it figures out what devices where just
168
    disconnected from the computer. Basically, any device that was seen connected to the PC
169
    and does not apper in the detected_devices list must have been just unplugged from the PC.
170

  
171
    :param detected_devices: list of the currently plugged USB devices
172
    :param last_connected_devices: list of the lastly connected USB devices
173
    :return: list of all USB devices that were just disconnected from the PC
174
    &#34;&#34;&#34;
175
    # If there is no previous record of what USB devices where connected to the PC,
176
    # no devices were unplugged
177
    if last_connected_devices is None:
178
        return []
179

  
180
    # Return last_connected_devices if no devices were detected.
181
    if detected_devices is None:
182
        return last_connected_devices
183

  
184
    # Return a list of all devices that were just disconnected.
185
    return [device for device in last_connected_devices if device not in detected_devices]
186

  
187

  
188
def _update():
189
    &#34;&#34;&#34;Updates the USB detector.
190

  
191
    This function is periodically called from the usb_detector_run function.
192
    It uses the other functions of this file to figure out if there have
193
    been any changes since the last time this function was called - what
194
    USB devices have been connected/disconnected.
195
    &#34;&#34;&#34;
196
    # Retrieve a list of the currently plugged USB devices
197
    # and store it globally within the file.
198
    global _last_connected_devices
199
    detected_devices = read_connected_devices()
200

  
201
    # Figure out what USB devices were connected to the PC since
202
    # the last time this function was called.
203
    connected_devices = _get_connected_devices(detected_devices, _last_connected_devices)
204

  
205
    # Figure out what USB devices were disconnected from the PC since
206
    # the last time this function was called.
207
    disconnected_devices = _get_disconnected_devices(detected_devices, _last_connected_devices)
208

  
209
    # Notify both kinds of listeners (call the registered callback functions).
210
    _notify_listeners(_listeners_connected, connected_devices)
211
    _notify_listeners(_listeners_disconnected, disconnected_devices)
212

  
213
    # If there have been any changes, update the file on the disk (persistent memory).
214
    if len(connected_devices) &gt; 0 or len(disconnected_devices) &gt; 0:
215
        _store_connected_devices(detected_devices)
216
        _last_connected_devices = detected_devices
217

  
218

  
219
def usb_detector_run():
220
    &#34;&#34;&#34;Keeps detecting what USB devices were plugged/unplugged.
221

  
222
    This function is instantiated as a thread that periodically scans
223
    what USB devices are connected to the PC. The scan period can be
224
    found and modified in the configuration file of the application.
225
    &#34;&#34;&#34;
226
    logging.info(&#34;USB device detector is now running&#34;)
227

  
228
    # Read the list of the lastly connected USB devices from the disk (once).
229
    global _last_connected_devices
230
    _last_connected_devices = _load_last_connected_devices()
231

  
232
    while True:
233
        # Update the USB detector.
234
        _update()
235

  
236
        # Sleep for a predefined amount of seconds.
237
        sleep(_config.scan_period_seconds)</code></pre>
238
</details>
239
</section>
240
<section>
241
</section>
242
<section>
243
</section>
244
<section>
245
<h2 class="section-title" id="header-functions">Functions</h2>
246
<dl>
247
<dt id="usb_detector.detector.register_listener"><code class="name flex">
248
<span>def <span class="ident">register_listener</span></span>(<span>callback, connected: bool = True)</span>
249
</code></dt>
250
<dd>
251
<div class="desc"><p>Registers a new event listener.</p>
252
<p>The caller is supposed to pass in a function they
253
wish to be called whenever an event occurs. What kind
254
of event will trigger (call) the callback function is
255
determined by the second parameter.</p>
256
<p>:param callback: Function that is called whenever the desired event happens.
257
:param connected: If the value is set to True, the callback function
258
will be called whenever a USB device is connected.
259
If it is set to False, it will be triggered whenever a
260
USB device is disconnected.</p></div>
261
<details class="source">
262
<summary>
263
<span>Expand source code</span>
264
</summary>
265
<pre><code class="python">def register_listener(callback, connected: bool = True):
266
    &#34;&#34;&#34;Registers a new event listener.
267

  
268
    The caller is supposed to pass in a function they
269
    wish to be called whenever an event occurs. What kind
270
    of event will trigger (call) the callback function is
271
    determined by the second parameter.
272

  
273
    :param callback: Function that is called whenever the desired event happens.
274
    :param connected: If the value is set to True, the callback function
275
                      will be called whenever a USB device is connected.
276
                      If it is set to False, it will be triggered whenever a
277
                      USB device is disconnected.
278
    &#34;&#34;&#34;
279
    logging.info(f&#34;Registering callback: {callback}.&#34;)
280

  
281
    if connected is True:
282
        # Register the callback for &#34;connected devices&#34;
283
        _listeners_connected.append(callback)
284
    else:
285
        # Register the callback for &#34;disconnected devices&#34;
286
        _listeners_disconnected.append(callback)</code></pre>
287
</details>
288
</dd>
289
<dt id="usb_detector.detector.usb_detector_run"><code class="name flex">
290
<span>def <span class="ident">usb_detector_run</span></span>(<span>)</span>
291
</code></dt>
292
<dd>
293
<div class="desc"><p>Keeps detecting what USB devices were plugged/unplugged.</p>
294
<p>This function is instantiated as a thread that periodically scans
295
what USB devices are connected to the PC. The scan period can be
296
found and modified in the configuration file of the application.</p></div>
297
<details class="source">
298
<summary>
299
<span>Expand source code</span>
300
</summary>
301
<pre><code class="python">def usb_detector_run():
302
    &#34;&#34;&#34;Keeps detecting what USB devices were plugged/unplugged.
303

  
304
    This function is instantiated as a thread that periodically scans
305
    what USB devices are connected to the PC. The scan period can be
306
    found and modified in the configuration file of the application.
307
    &#34;&#34;&#34;
308
    logging.info(&#34;USB device detector is now running&#34;)
309

  
310
    # Read the list of the lastly connected USB devices from the disk (once).
311
    global _last_connected_devices
312
    _last_connected_devices = _load_last_connected_devices()
313

  
314
    while True:
315
        # Update the USB detector.
316
        _update()
317

  
318
        # Sleep for a predefined amount of seconds.
319
        sleep(_config.scan_period_seconds)</code></pre>
320
</details>
321
</dd>
322
<dt id="usb_detector.detector.usb_detector_set_config"><code class="name flex">
323
<span>def <span class="ident">usb_detector_set_config</span></span>(<span>config)</span>
324
</code></dt>
325
<dd>
326
<div class="desc"><p>Initializes the usb detector module (file).</p>
327
<p>This function is meant to be called prior to calling
328
any other function of the detector module. It stores
329
an instance of the Config class which is then used
330
by other functions within this file.</p>
331
<p>:param config: instance of Config (config manager)</p></div>
332
<details class="source">
333
<summary>
334
<span>Expand source code</span>
335
</summary>
336
<pre><code class="python">def usb_detector_set_config(config):
337
    &#34;&#34;&#34;Initializes the usb detector module (file).
338

  
339
    This function is meant to be called prior to calling
340
    any other function of the detector module. It stores
341
    an instance of the Config class which is then used
342
    by other functions within this file.
343

  
344
    :param config: instance of Config (config manager)
345
    &#34;&#34;&#34;
346
    # Store the instance into the global variable.
347
    global _config
348
    _config = config</code></pre>
349
</details>
350
</dd>
351
</dl>
352
</section>
353
<section>
354
</section>
355
</article>
356
<nav id="sidebar">
357
<h1>Index</h1>
358
<div class="toc">
359
<ul></ul>
360
</div>
361
<ul id="index">
362
<li><h3>Super-module</h3>
363
<ul>
364
<li><code><a title="usb_detector" href="index.html">usb_detector</a></code></li>
365
</ul>
366
</li>
367
<li><h3><a href="#header-functions">Functions</a></h3>
368
<ul class="">
369
<li><code><a title="usb_detector.detector.register_listener" href="#usb_detector.detector.register_listener">register_listener</a></code></li>
370
<li><code><a title="usb_detector.detector.usb_detector_run" href="#usb_detector.detector.usb_detector_run">usb_detector_run</a></code></li>
371
<li><code><a title="usb_detector.detector.usb_detector_set_config" href="#usb_detector.detector.usb_detector_set_config">usb_detector_set_config</a></code></li>
372
</ul>
373
</li>
374
</ul>
375
</nav>
376
</main>
377
<footer id="footer">
378
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
379
</footer>
380
</body>
381
</html>
client/doc/pdoc/usb_detector/event_listener.html
1
<!doctype html>
2
<html lang="en">
3
<head>
4
<meta charset="utf-8">
5
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
6
<meta name="generator" content="pdoc 0.10.0" />
7
<title>usb_detector.event_listener API documentation</title>
8
<meta name="description" content="" />
9
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/sanitize.min.css" integrity="sha256-PK9q560IAAa6WVRRh76LtCaI8pjTJ2z11v0miyNNjrs=" crossorigin>
10
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/typography.min.css" integrity="sha256-7l/o7C8jubJiy74VsKTidCy1yBkRtiUGbVkYBylBqUg=" crossorigin>
11
<link rel="stylesheet preload" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/github.min.css" crossorigin>
12
<style>:root{--highlight-color:#fe9}.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}#sidebar > *:last-child{margin-bottom:2cm}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}h1:target,h2:target,h3:target,h4:target,h5:target,h6:target{background:var(--highlight-color);padding:.2em 0}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{margin-top:.6em;font-weight:bold}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}dt:target .name{background:var(--highlight-color)}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}td{padding:0 .5em}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
13
<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%;height:100vh;overflow:auto;position:sticky;top:0}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style>
14
<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
15
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js" integrity="sha256-Uv3H6lx7dJmRfRvH8TH6kJD1TSK1aFcwgx+mdg3epi8=" crossorigin></script>
16
<script>window.addEventListener('DOMContentLoaded', () => hljs.initHighlighting())</script>
17
</head>
18
<body>
19
<main>
20
<article id="content">
21
<header>
22
<h1 class="title">Module <code>usb_detector.event_listener</code></h1>
23
</header>
24
<section id="section-intro">
25
<details class="source">
26
<summary>
27
<span>Expand source code</span>
28
</summary>
29
<pre><code class="python">import platform
30
import logging
31
import getpass
32
from datetime import datetime
33

  
34
from .api_client import send_data
35

  
36

  
37
def _get_metadata() -&gt; dict:
38
    &#34;&#34;&#34;Returns metadata associated with the computer.
39

  
40
    This metadata is sent to the server as a part
41
    of each payload. It includes the username, hostname,
42
    and timestamp.
43

  
44
    :return: metadata associated with the PC
45
    &#34;&#34;&#34;
46
    logging.debug(&#34;getting computer metadata&#34;)
47
    return {
48
        &#34;username&#34;: getpass.getuser(),                  # username
49
        &#34;hostname&#34;: platform.uname().node,              # hostname
50
        &#34;timestamp&#34;: str(datetime.now()).split(&#39;.&#39;)[0]  # timestamp (format: 2022-04-07 12:11:02)
51
    }
52

  
53

  
54
def _send_payload_to_server(device: dict, status: str):
55
    &#34;&#34;&#34; Creates a payload and calls the send_data function to send it to the server.
56

  
57
    Each payload consists of metadata, status (connected/disconnected), and device,
58
    which contains a vendor id, product id, and serial number.
59

  
60
    :param device: USB device that has been detected
61
    :param status: status of the USB device (connected/disconnected)
62
    &#34;&#34;&#34;
63
    logging.debug(&#34;payload send preparation&#34;)
64

  
65
    # Get metadata that will be put into the payload.
66
    payload = _get_metadata()
67

  
68
    # Add information about the USB device.
69
    payload[&#34;device&#34;] = device
70

  
71
    # Add the status of the USB device (connected/disconnected).
72
    payload[&#34;status&#34;] = status
73

  
74
    # Send the payload off to the server.
75
    send_data(payload)
76

  
77

  
78
def usb_connected_callback(device: dict):
79
    &#34;&#34;&#34;Callback function for a connected USB device.
80

  
81
    This function gets called whenever a USB device is
82
    plugged into the computer (it is registered as a listener in
83
    the USB detector). The device consists of a vendor id, product id,
84
    and serial number.
85

  
86
    :param device: USB device that was just plugged into the PC.
87
    &#34;&#34;&#34;
88
    logging.info(f&#34;Device {device} has been connected&#34;)
89

  
90
    # Create a payload and send it to the API (server).
91
    _send_payload_to_server(device, &#34;connected&#34;)
92

  
93

  
94
def usb_disconnected_callback(device: dict):
95
    &#34;&#34;&#34;Callback function for a disconnected USB device.
96

  
97
    This function gets called whenever a USB device is
98
    disconnected from the computer (it is registered as a listener in
99
    the USB detector). The device consists of a vendor id, product id,
100
    and serial number.
101

  
102
    :param device: USB device that was just disconnected from the PC.
103
    &#34;&#34;&#34;
104
    logging.info(f&#34;Device {device} has been disconnected&#34;)
105

  
106
    # Create a payload and send it to the API (server).
107
    _send_payload_to_server(device, &#34;disconnected&#34;)</code></pre>
108
</details>
109
</section>
110
<section>
111
</section>
112
<section>
113
</section>
114
<section>
115
<h2 class="section-title" id="header-functions">Functions</h2>
116
<dl>
117
<dt id="usb_detector.event_listener.usb_connected_callback"><code class="name flex">
118
<span>def <span class="ident">usb_connected_callback</span></span>(<span>device: dict)</span>
119
</code></dt>
120
<dd>
121
<div class="desc"><p>Callback function for a connected USB device.</p>
122
<p>This function gets called whenever a USB device is
123
plugged into the computer (it is registered as a listener in
124
the USB detector). The device consists of a vendor id, product id,
125
and serial number.</p>
126
<p>:param device: USB device that was just plugged into the PC.</p></div>
127
<details class="source">
128
<summary>
129
<span>Expand source code</span>
130
</summary>
131
<pre><code class="python">def usb_connected_callback(device: dict):
132
    &#34;&#34;&#34;Callback function for a connected USB device.
133

  
134
    This function gets called whenever a USB device is
135
    plugged into the computer (it is registered as a listener in
136
    the USB detector). The device consists of a vendor id, product id,
137
    and serial number.
138

  
139
    :param device: USB device that was just plugged into the PC.
140
    &#34;&#34;&#34;
141
    logging.info(f&#34;Device {device} has been connected&#34;)
142

  
143
    # Create a payload and send it to the API (server).
144
    _send_payload_to_server(device, &#34;connected&#34;)</code></pre>
145
</details>
146
</dd>
147
<dt id="usb_detector.event_listener.usb_disconnected_callback"><code class="name flex">
148
<span>def <span class="ident">usb_disconnected_callback</span></span>(<span>device: dict)</span>
149
</code></dt>
150
<dd>
151
<div class="desc"><p>Callback function for a disconnected USB device.</p>
152
<p>This function gets called whenever a USB device is
153
disconnected from the computer (it is registered as a listener in
154
the USB detector). The device consists of a vendor id, product id,
155
and serial number.</p>
156
<p>:param device: USB device that was just disconnected from the PC.</p></div>
157
<details class="source">
158
<summary>
159
<span>Expand source code</span>
160
</summary>
161
<pre><code class="python">def usb_disconnected_callback(device: dict):
162
    &#34;&#34;&#34;Callback function for a disconnected USB device.
163

  
164
    This function gets called whenever a USB device is
165
    disconnected from the computer (it is registered as a listener in
166
    the USB detector). The device consists of a vendor id, product id,
167
    and serial number.
168

  
169
    :param device: USB device that was just disconnected from the PC.
170
    &#34;&#34;&#34;
171
    logging.info(f&#34;Device {device} has been disconnected&#34;)
172

  
173
    # Create a payload and send it to the API (server).
174
    _send_payload_to_server(device, &#34;disconnected&#34;)</code></pre>
175
</details>
176
</dd>
177
</dl>
178
</section>
179
<section>
180
</section>
181
</article>
182
<nav id="sidebar">
183
<h1>Index</h1>
184
<div class="toc">
185
<ul></ul>
186
</div>
187
<ul id="index">
188
<li><h3>Super-module</h3>
189
<ul>
190
<li><code><a title="usb_detector" href="index.html">usb_detector</a></code></li>
191
</ul>
192
</li>
193
<li><h3><a href="#header-functions">Functions</a></h3>
194
<ul class="">
195
<li><code><a title="usb_detector.event_listener.usb_connected_callback" href="#usb_detector.event_listener.usb_connected_callback">usb_connected_callback</a></code></li>
196
<li><code><a title="usb_detector.event_listener.usb_disconnected_callback" href="#usb_detector.event_listener.usb_disconnected_callback">usb_disconnected_callback</a></code></li>
197
</ul>
198
</li>
199
</ul>
200
</nav>
201
</main>
202
<footer id="footer">
203
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
204
</footer>
205
</body>
206
</html>
client/doc/pdoc/usb_detector/index.html
1
<!doctype html>
2
<html lang="en">
3
<head>
4
<meta charset="utf-8">
5
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
6
<meta name="generator" content="pdoc 0.10.0" />
7
<title>usb_detector API documentation</title>
8
<meta name="description" content="" />
9
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/sanitize.min.css" integrity="sha256-PK9q560IAAa6WVRRh76LtCaI8pjTJ2z11v0miyNNjrs=" crossorigin>
10
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/typography.min.css" integrity="sha256-7l/o7C8jubJiy74VsKTidCy1yBkRtiUGbVkYBylBqUg=" crossorigin>
11
<link rel="stylesheet preload" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/github.min.css" crossorigin>
12
<style>:root{--highlight-color:#fe9}.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}#sidebar > *:last-child{margin-bottom:2cm}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}h1:target,h2:target,h3:target,h4:target,h5:target,h6:target{background:var(--highlight-color);padding:.2em 0}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{margin-top:.6em;font-weight:bold}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}dt:target .name{background:var(--highlight-color)}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}td{padding:0 .5em}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
13
<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%;height:100vh;overflow:auto;position:sticky;top:0}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style>
14
<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
15
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js" integrity="sha256-Uv3H6lx7dJmRfRvH8TH6kJD1TSK1aFcwgx+mdg3epi8=" crossorigin></script>
16
<script>window.addEventListener('DOMContentLoaded', () => hljs.initHighlighting())</script>
17
</head>
18
<body>
19
<main>
20
<article id="content">
21
<header>
22
<h1 class="title">Package <code>usb_detector</code></h1>
23
</header>
24
<section id="section-intro">
25
</section>
26
<section>
27
<h2 class="section-title" id="header-submodules">Sub-modules</h2>
28
<dl>
29
<dt><code class="name"><a title="usb_detector.api_client" href="api_client.html">usb_detector.api_client</a></code></dt>
30
<dd>
31
<div class="desc"></div>
32
</dd>
33
<dt><code class="name"><a title="usb_detector.detector" href="detector.html">usb_detector.detector</a></code></dt>
34
<dd>
35
<div class="desc"></div>
36
</dd>
37
<dt><code class="name"><a title="usb_detector.event_listener" href="event_listener.html">usb_detector.event_listener</a></code></dt>
38
<dd>
39
<div class="desc"></div>
40
</dd>
41
<dt><code class="name"><a title="usb_detector.usb_reader" href="usb_reader.html">usb_detector.usb_reader</a></code></dt>
42
<dd>
43
<div class="desc"></div>
44
</dd>
45
</dl>
46
</section>
47
<section>
48
</section>
49
<section>
50
</section>
51
<section>
52
</section>
53
</article>
54
<nav id="sidebar">
55
<h1>Index</h1>
56
<div class="toc">
57
<ul></ul>
58
</div>
59
<ul id="index">
60
<li><h3><a href="#header-submodules">Sub-modules</a></h3>
61
<ul>
62
<li><code><a title="usb_detector.api_client" href="api_client.html">usb_detector.api_client</a></code></li>
63
<li><code><a title="usb_detector.detector" href="detector.html">usb_detector.detector</a></code></li>
64
<li><code><a title="usb_detector.event_listener" href="event_listener.html">usb_detector.event_listener</a></code></li>
65
<li><code><a title="usb_detector.usb_reader" href="usb_reader.html">usb_detector.usb_reader</a></code></li>
66
</ul>
67
</li>
68
</ul>
69
</nav>
70
</main>
71
<footer id="footer">
72
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
73
</footer>
74
</body>
75
</html>
client/doc/pdoc/usb_detector/usb_reader.html
1
<!doctype html>
2
<html lang="en">
3
<head>
4
<meta charset="utf-8">
5
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
6
<meta name="generator" content="pdoc 0.10.0" />
7
<title>usb_detector.usb_reader API documentation</title>
8
<meta name="description" content="" />
9
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/sanitize.min.css" integrity="sha256-PK9q560IAAa6WVRRh76LtCaI8pjTJ2z11v0miyNNjrs=" crossorigin>
10
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/typography.min.css" integrity="sha256-7l/o7C8jubJiy74VsKTidCy1yBkRtiUGbVkYBylBqUg=" crossorigin>
11
<link rel="stylesheet preload" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/github.min.css" crossorigin>
12
<style>:root{--highlight-color:#fe9}.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}#sidebar > *:last-child{margin-bottom:2cm}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}h1:target,h2:target,h3:target,h4:target,h5:target,h6:target{background:var(--highlight-color);padding:.2em 0}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{margin-top:.6em;font-weight:bold}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}dt:target .name{background:var(--highlight-color)}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}td{padding:0 .5em}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
13
<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%;height:100vh;overflow:auto;position:sticky;top:0}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style>
14
<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
15
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js" integrity="sha256-Uv3H6lx7dJmRfRvH8TH6kJD1TSK1aFcwgx+mdg3epi8=" crossorigin></script>
16
<script>window.addEventListener('DOMContentLoaded', () => hljs.initHighlighting())</script>
17
</head>
18
<body>
19
<main>
20
<article id="content">
21
<header>
22
<h1 class="title">Module <code>usb_detector.usb_reader</code></h1>
23
</header>
24
<section id="section-intro">
25
<details class="source">
26
<summary>
27
<span>Expand source code</span>
28
</summary>
29
<pre><code class="python">import logging
30

  
31
import usb.core
32
import usb.util
33

  
34

  
35
# list of devices from which the application
36
# could not retrieve a serial number
37
_invalid_devices = []
38

  
39

  
40
def read_connected_devices():
41
    &#34;&#34;&#34;Reads and returns all USB devices that are currently connected to the computer.
42

  
43
    It iterates over devices connected to individual buses and for each of
44
    them, it tries to retrieve its vendor id, product id, and serial number.
45
    If the application fails to retrieve the serial number of a device, it
46
    will store it into an in-RAM list to prevent &#34;spam&#34; logs. Once the application
47
    manages to read the serial number, the device will be removed from the list.
48

  
49
    :return: list of all USB devices connected to the PC
50
    &#34;&#34;&#34;
51
    logging.debug(&#34;reading all currently connected devices&#34;)
52

  
53
    # Create an empty list of USB devices.
54
    detected_devices = []
55

  
56
    # Get a list of all buses.
57
    busses = usb.busses()
58

  
59
    for bus in busses:
60
        # Get all devices connected to the current bus.
61
        devices = bus.devices
62
        for dev in devices:
63
            # Create a record of the device.
64
            device = {
65
                &#34;vendor_id&#34;: dev.idVendor,
66
                &#34;product_id&#34;: dev.idProduct
67
            }
68

  
69
            # Try to retrieve the serial number of the device.
70
            serial_number = None
71
            device_info = usb.core.find(idProduct=dev.idProduct)
72
            try:
73
                serial_number = usb.util.get_string(device_info, device_info.iSerialNumber)
74
            except ValueError:
75
                # If you fail, store the device into the in-RAM list (if it is not already there).
76
                if device not in _invalid_devices:
77
                    logging.warning(f&#34;Could not retrieve serial number from device {device}&#34;)
78
                    _invalid_devices.append(device)
79

  
80
            if serial_number is not None:
81
                # If you manage to read the serial number of a USB device
82
                # that was previously stored into the list of &#34;failures&#34;, remove it.
83
                if device in _invalid_devices:
84
                    _invalid_devices.remove(device)
85

  
86
                # Add the serial number into to USB device record.
87
                device[&#34;serial_number&#34;] = serial_number
88

  
89
                # Append the record into the list of the connected USB devices.
90
                detected_devices.append(device)
91

  
92
    # Return the list of currently plugged USB devices.
93
    return detected_devices</code></pre>
94
</details>
95
</section>
96
<section>
97
</section>
98
<section>
99
</section>
100
<section>
101
<h2 class="section-title" id="header-functions">Functions</h2>
102
<dl>
103
<dt id="usb_detector.usb_reader.read_connected_devices"><code class="name flex">
104
<span>def <span class="ident">read_connected_devices</span></span>(<span>)</span>
105
</code></dt>
106
<dd>
107
<div class="desc"><p>Reads and returns all USB devices that are currently connected to the computer.</p>
108
<p>It iterates over devices connected to individual buses and for each of
109
them, it tries to retrieve its vendor id, product id, and serial number.
110
If the application fails to retrieve the serial number of a device, it
111
will store it into an in-RAM list to prevent "spam" logs. Once the application
112
manages to read the serial number, the device will be removed from the list.</p>
113
<p>:return: list of all USB devices connected to the PC</p></div>
114
<details class="source">
115
<summary>
116
<span>Expand source code</span>
117
</summary>
118
<pre><code class="python">def read_connected_devices():
119
    &#34;&#34;&#34;Reads and returns all USB devices that are currently connected to the computer.
120

  
121
    It iterates over devices connected to individual buses and for each of
122
    them, it tries to retrieve its vendor id, product id, and serial number.
123
    If the application fails to retrieve the serial number of a device, it
124
    will store it into an in-RAM list to prevent &#34;spam&#34; logs. Once the application
125
    manages to read the serial number, the device will be removed from the list.
126

  
127
    :return: list of all USB devices connected to the PC
128
    &#34;&#34;&#34;
129
    logging.debug(&#34;reading all currently connected devices&#34;)
130

  
131
    # Create an empty list of USB devices.
132
    detected_devices = []
133

  
134
    # Get a list of all buses.
135
    busses = usb.busses()
136

  
137
    for bus in busses:
138
        # Get all devices connected to the current bus.
139
        devices = bus.devices
140
        for dev in devices:
141
            # Create a record of the device.
142
            device = {
143
                &#34;vendor_id&#34;: dev.idVendor,
144
                &#34;product_id&#34;: dev.idProduct
145
            }
146

  
147
            # Try to retrieve the serial number of the device.
148
            serial_number = None
149
            device_info = usb.core.find(idProduct=dev.idProduct)
150
            try:
151
                serial_number = usb.util.get_string(device_info, device_info.iSerialNumber)
152
            except ValueError:
153
                # If you fail, store the device into the in-RAM list (if it is not already there).
154
                if device not in _invalid_devices:
155
                    logging.warning(f&#34;Could not retrieve serial number from device {device}&#34;)
156
                    _invalid_devices.append(device)
157

  
158
            if serial_number is not None:
159
                # If you manage to read the serial number of a USB device
160
                # that was previously stored into the list of &#34;failures&#34;, remove it.
161
                if device in _invalid_devices:
162
                    _invalid_devices.remove(device)
163

  
164
                # Add the serial number into to USB device record.
165
                device[&#34;serial_number&#34;] = serial_number
166

  
167
                # Append the record into the list of the connected USB devices.
168
                detected_devices.append(device)
169

  
170
    # Return the list of currently plugged USB devices.
171
    return detected_devices</code></pre>
172
</details>
173
</dd>
174
</dl>
175
</section>
176
<section>
177
</section>
178
</article>
179
<nav id="sidebar">
180
<h1>Index</h1>
181
<div class="toc">
182
<ul></ul>
183
</div>
184
<ul id="index">
185
<li><h3>Super-module</h3>
186
<ul>
187
<li><code><a title="usb_detector" href="index.html">usb_detector</a></code></li>
188
</ul>
189
</li>
190
<li><h3><a href="#header-functions">Functions</a></h3>
191
<ul class="">
192
<li><code><a title="usb_detector.usb_reader.read_connected_devices" href="#usb_detector.usb_reader.read_connected_devices">read_connected_devices</a></code></li>
193
</ul>
194
</li>
195
</ul>
196
</nav>
197
</main>
198
<footer id="footer">
199
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
200
</footer>
201
</body>
202
</html>

Také k dispozici: Unified diff