indipyterm 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
indipyterm/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+
2
+
3
+ version = "0.0.1"
indipyterm/__main__.py ADDED
@@ -0,0 +1,32 @@
1
+
2
+
3
+ import sys, argparse
4
+
5
+ from . import version
6
+ from .indipyterm import IPyTerm
7
+ from .connections import make_connection
8
+
9
+
10
+
11
+ def main():
12
+ """The commandline entry point to run the terminal client."""
13
+
14
+ parser = argparse.ArgumentParser(usage="indipyterm [options]",
15
+ description="Terminal client to communicate to an INDI service.")
16
+ parser.add_argument("--port", type=int, default=7624, help="Port of the INDI server (default 7624).")
17
+ parser.add_argument("--host", default="localhost", help="Hostname/IP of the INDI server (default localhost).")
18
+ parser.add_argument("--version", action="version", version=version)
19
+ args = parser.parse_args()
20
+
21
+ # Create the initial server connection
22
+ make_connection(host=args.host, port=args.port)
23
+
24
+ # run the IPyTerm app
25
+ app = IPyTerm()
26
+ app.run()
27
+
28
+ return 0
29
+
30
+
31
+ if __name__ == "__main__":
32
+ sys.exit(main())
@@ -0,0 +1,660 @@
1
+
2
+
3
+ import asyncio, queue, threading, pathlib, logging
4
+
5
+ from datetime import datetime, timezone
6
+
7
+ from textual.widgets import Static, Button
8
+
9
+ from indipyclient.queclient import QueClient
10
+
11
+ logger = logging.getLogger()
12
+ logger.addHandler(logging.NullHandler())
13
+
14
+
15
+ #########################################################################
16
+ #
17
+ # Global variable _DEVICENAME will be the name of the device
18
+ # currently being displayed
19
+ #
20
+ #########################################################################
21
+
22
+ _DEVICENAME = ''
23
+
24
+ ##########################################################################
25
+ #
26
+ # Global variable _CONNECTION will be an instance of the _Connection class
27
+ #
28
+ ##########################################################################
29
+
30
+ _CONNECTION = None
31
+
32
+
33
+
34
+ def get_devicename():
35
+ return _DEVICENAME
36
+
37
+ def set_devicename(devicename):
38
+ global _DEVICENAME
39
+ _DEVICENAME = devicename
40
+
41
+
42
+ def clear_devicesc():
43
+ global _CONNECTION
44
+ _CONNECTION.devicesc = None
45
+
46
+
47
+ def get_devicemessages(devicename=None):
48
+ "Returns a list of messages for the device"
49
+ if devicename is None:
50
+ devicename = get_devicename()
51
+ if not devicename:
52
+ return
53
+ connection = get_connection()
54
+ snapshot = connection.snapshot
55
+ if not snapshot:
56
+ return
57
+ if devicename not in snapshot:
58
+ return
59
+ messages = snapshot[devicename].messages
60
+ if not messages:
61
+ return
62
+ return reversed([ localtimestring(t) + " " + m for t,m in messages])
63
+
64
+
65
+ def get_devicegroups(devicename=None):
66
+ "Returns a list of groups for the device"
67
+ if devicename is None:
68
+ devicename = get_devicename()
69
+ if not devicename:
70
+ return
71
+ connection = get_connection()
72
+ snapshot = connection.snapshot
73
+ if not snapshot:
74
+ return
75
+ if devicename not in snapshot:
76
+ return
77
+ device = snapshot[devicename]
78
+ groupset = set(vector.group for vector in device.values() if vector.enable)
79
+ if not groupset:
80
+ return
81
+ grouplist = list(groupset)
82
+ grouplist.sort()
83
+ return grouplist
84
+
85
+
86
+
87
+ def make_connection(host='localhost', port=7624):
88
+ "Creates a singleton _Connection object"
89
+ global _CONNECTION
90
+ if _CONNECTION is None:
91
+ _CONNECTION = _Connection(host=host, port=port, blobfolder=None)
92
+
93
+ def get_connection():
94
+ return _CONNECTION
95
+
96
+
97
+ def sendvector(vectorname, memberdict):
98
+ global _CONNECTION
99
+ global _DEVICENAME
100
+ if _CONNECTION is None:
101
+ return
102
+ if not _DEVICENAME:
103
+ return
104
+ if _CONNECTION.is_alive():
105
+ _CONNECTION.txque.put((_DEVICENAME, vectorname, memberdict))
106
+
107
+
108
+ def sendBLOBfile(vectorname, membername, path):
109
+ """create memberdict of {membername:(value, blobsize, blobformat)}"""
110
+ global _CONNECTION
111
+ global _DEVICENAME
112
+ if _CONNECTION is None:
113
+ return
114
+ if not _DEVICENAME:
115
+ return
116
+ if not _CONNECTION.is_alive():
117
+ return
118
+ _CONNECTION.txque.put((_DEVICENAME, vectorname, {membername:(path, 0, "")}))
119
+ set_last_filename(vectorname, membername, path.name)
120
+
121
+
122
+
123
+ # These two functions are used for storing a BLOB filename sent to the server
124
+
125
+ def get_last_filename(vectorname, membername):
126
+ "Gets a filename previously stored in the client member user_string"
127
+ global _CONNECTION
128
+ global _DEVICENAME
129
+ if _CONNECTION is None:
130
+ return
131
+ if not _DEVICENAME:
132
+ return
133
+ if _CONNECTION.is_alive():
134
+ return _CONNECTION.queclient.get_user_string(_DEVICENAME, vectorname, membername)
135
+
136
+
137
+ def set_last_filename(vectorname, membername, filename):
138
+ "Sets a filename into the client member user_string"
139
+ global _CONNECTION
140
+ global _DEVICENAME
141
+ if _CONNECTION is None:
142
+ return
143
+ if not _DEVICENAME:
144
+ return
145
+ if _CONNECTION.is_alive():
146
+ _CONNECTION.queclient.set_user_string(_DEVICENAME, vectorname, membername, user_string = filename)
147
+
148
+
149
+ class _ItemID():
150
+
151
+ def __init__(self):
152
+ self._itemdict = {}
153
+ self._groupdict = {}
154
+ # Every device, vector, widget will be given an id
155
+ # starting with characters 'id' followed by a string number
156
+ # created by incrementing this self._itemid
157
+ self._itemid = 0
158
+
159
+
160
+ def set_group(self, devicename, groupname):
161
+ "Sets an id for a group"
162
+ if not devicename:
163
+ raise KeyError("A devicename must be given to set a group id")
164
+ if not groupname:
165
+ raise KeyError("A group name must be given to set a group id")
166
+ self._itemid += 1
167
+ self._groupdict[devicename, groupname] = self._itemid
168
+ return self._itemid
169
+
170
+ def get_group(self, devicename, groupname):
171
+ "Gets a group id, or None if one not set"
172
+ if not devicename:
173
+ raise KeyError("A devicename must be given to get a group id")
174
+ if not groupname:
175
+ raise KeyError("A group name must be given to get a group id")
176
+ return self._groupdict.get((devicename, groupname))
177
+
178
+ def unset_group(self, devicename, groupname):
179
+ if not devicename:
180
+ raise KeyError("A devicename must be given to unset a group id")
181
+ if not groupname:
182
+ raise KeyError("A group name must be given to unset a group id")
183
+ self._groupdict[devicename, groupname] = None
184
+
185
+ def set(self, devicename, vectorname=None, membername=None):
186
+ if not vectorname:
187
+ vectorname = None
188
+ if not membername:
189
+ membername = None
190
+ if not devicename:
191
+ raise KeyError("A devicename must be given to set an id")
192
+ if membername and (not vectorname):
193
+ raise KeyError("If a membername is specified, a vectorname must also be given")
194
+ self._itemid += 1
195
+ self._itemdict[devicename, vectorname, membername] = self._itemid
196
+ return self._itemid
197
+
198
+ def unset(self, devicename, vectorname=None, membername=None):
199
+ if not vectorname:
200
+ vectorname = None
201
+ if not membername:
202
+ membername = None
203
+ if not devicename:
204
+ raise KeyError("A devicename must be given to unset an id")
205
+ if membername and (not vectorname):
206
+ raise KeyError("If a membername is specified, a vectorname must also be given")
207
+ self._itemdict[devicename, vectorname, membername] = None
208
+
209
+ def get(self, devicename, vectorname=None, membername=None):
210
+ if not vectorname:
211
+ vectorname = None
212
+ if not membername:
213
+ membername = None
214
+ if not devicename:
215
+ raise KeyError("A devicename must be given to get an id")
216
+ if membername and (not vectorname):
217
+ raise KeyError("If a membername is specified, a vectorname must also be given")
218
+ return self._itemdict.get((devicename, vectorname, membername))
219
+
220
+ def get_devicename(self, deviceid):
221
+ "Given an id, get the devicename, or return None if it does not exist"
222
+ idnumber = int(deviceid.strip("id"))
223
+ for key,value in self._itemdict.items():
224
+ if value == idnumber:
225
+ return key[0]
226
+
227
+ def clear(self):
228
+ self._itemdict.clear()
229
+ self._groupdict.clear()
230
+ self._itemid = 0
231
+
232
+
233
+ ##################################
234
+ #
235
+ # Create a global _ItemID
236
+ #
237
+ ###################################
238
+
239
+ _ITEMID = _ItemID()
240
+
241
+ def get_id(devicename, vectorname=None, membername=None):
242
+ "This is imported into the gui to create ids for widgets"
243
+ global _ITEMID
244
+ idnumber = _ITEMID.get(devicename, vectorname, membername)
245
+ if idnumber is None:
246
+ return
247
+ return "id"+str(idnumber)
248
+
249
+ def set_id(devicename, vectorname=None, membername=None):
250
+ "This create ids for widgets, and returns the id"
251
+ global _ITEMID
252
+ idnumber = _ITEMID.get(devicename, vectorname, membername)
253
+ if idnumber is None:
254
+ idnumber = _ITEMID.set(devicename, vectorname, membername)
255
+ return "id"+str(idnumber)
256
+
257
+ def devicename_from_id(deviceid):
258
+ global _ITEMID
259
+ return _ITEMID.get_devicename(deviceid)
260
+
261
+
262
+ def get_group_id(groupname):
263
+ global _DEVICENAME, _ITEMID
264
+ if _DEVICENAME is None:
265
+ return
266
+ idnumber = _ITEMID.get_group(_DEVICENAME, groupname)
267
+ if idnumber is None:
268
+ return
269
+ return "gid"+str(idnumber)
270
+
271
+
272
+ def set_group_id(groupname):
273
+ global _DEVICENAME, _ITEMID
274
+ if _DEVICENAME is None:
275
+ return
276
+ idnumber = _ITEMID.get_group(_DEVICENAME, groupname)
277
+ if idnumber is None:
278
+ idnumber = _ITEMID.set_group(_DEVICENAME, groupname)
279
+ return "gid"+str(idnumber)
280
+
281
+
282
+
283
+ def localtimestring(t):
284
+ "Return a string of the local time (not date)"
285
+ localtime = t.astimezone(tz=None)
286
+ # convert microsecond to integer between 0 and 100
287
+ ms = localtime.microsecond//10000
288
+ return f"{localtime.strftime('%H:%M:%S')}.{ms:0>2d}"
289
+
290
+
291
+ class _Connection:
292
+
293
+ def __init__(self, host=None, port=None, blobfolder=None):
294
+
295
+ # create two queues
296
+ # txque to transmit data
297
+ self.txque = queue.Queue(maxsize=4)
298
+ # rxque giving received data
299
+ self.rxque = queue.Queue(maxsize=4)
300
+
301
+ self.snapshot = None
302
+ self.queclient = None
303
+ self.clientthread = None
304
+ self.blobfolderpath = None
305
+
306
+ if (host == None) or (port == None):
307
+ self.host = None
308
+ self.port = None
309
+ self.hostport = ""
310
+ else:
311
+ self.hostport = f"{host}:{port}"
312
+ self.make_connection(host, port, blobfolder)
313
+
314
+ # these are filled in as the app is mounted
315
+ self.app = None
316
+ self.startsc = None
317
+ self.devicesc = None
318
+
319
+
320
+ def checkhostport(self, hostport):
321
+ """Given a hostport string, Checks it and sets self.hostport
322
+ Returns a string"""
323
+ hostport = hostport.strip()
324
+ if not hostport:
325
+ self.hostport = "localhost:7624"
326
+ return self.hostport
327
+
328
+ hostportlist = hostport.split(":")
329
+ if len(hostportlist) != 2:
330
+ host = hostportlist[0].strip()
331
+ if host:
332
+ self.hostport = host +":7624"
333
+ else:
334
+ self.hostport = "localhost:7624"
335
+ return self.hostport
336
+ host = hostportlist[0].strip()
337
+ port = hostportlist[1].strip()
338
+ if not host:
339
+ host = "localhost"
340
+ if not port.isdigit():
341
+ port = "7624"
342
+ self.hostport = f"{host}:{port}"
343
+ return self.hostport
344
+
345
+
346
+ def checkblobfolder(self, blobfolder):
347
+ """Given a folder, checks it, sets blobfolder, and returns the folder string
348
+ If the given folder is empty, removes the blobfolder
349
+ If the given folder is not a directory, removes the blobfolder and returns
350
+ Invalid Folder"""
351
+ if not blobfolder:
352
+ self.set_BLOBfolder(None)
353
+ return ""
354
+ blobfolder = pathlib.Path(blobfolder).expanduser().resolve()
355
+ if not blobfolder.is_dir():
356
+ self.set_BLOBfolder(None)
357
+ return "Invalid Folder"
358
+ self.set_BLOBfolder(blobfolder)
359
+ return str(blobfolder)
360
+
361
+
362
+ def set_BLOBfolder(self, blobfolder):
363
+ "Sets blofolder into queclient"
364
+ self.blobfolderpath = blobfolder
365
+ connection = get_connection()
366
+ if not connection.is_alive():
367
+ return
368
+ self.queclient.BLOBfolder = self.blobfolderpath
369
+
370
+
371
+
372
+ def check_rxque(self) -> None:
373
+ """Method to handle received data."""
374
+ global _ITEMID
375
+
376
+ if self.devicesc is None:
377
+ # there is no devicesc, so ensure startsc is active before continuing
378
+ if not self.startsc.is_active:
379
+ return
380
+ else:
381
+ # a devicesc has been set, ensure it is active before continuing
382
+ if not self.devicesc.is_active:
383
+ return
384
+
385
+ try:
386
+ item = self.rxque.get_nowait()
387
+ except queue.Empty:
388
+ return
389
+ self.rxque.task_done()
390
+
391
+ if not (self.host) or (not self.port):
392
+ self.snapshot = None
393
+ return
394
+
395
+ self.snapshot = item.snapshot
396
+ snapshot = self.snapshot
397
+
398
+ # system messages
399
+ if (item.eventtype == "Message") and (not item.devicename) and (not item.vectorname):
400
+ log = self.startsc.query_one("#system-messages")
401
+ log.clear()
402
+ messages = snapshot.messages
403
+ mlist = reversed([ localtimestring(t) + " " + m for t,m in messages ])
404
+ log.write_lines(mlist)
405
+
406
+ if not snapshot.connected:
407
+ # the connection is disconnected
408
+ self.clear_devices()
409
+ if not self.startsc.is_active:
410
+ self.app.push_screen('startsc')
411
+ return
412
+
413
+ if not item.devicename:
414
+ # possible getProperties or system message which is handled above, just return
415
+ return
416
+
417
+ # get currently displayed device
418
+ devicename = get_devicename()
419
+
420
+
421
+ if item.eventtype == "Delete":
422
+ if item.vectorname and snapshot[item.devicename].enable:
423
+ # vector is to be deleted, but the device is still enabled, so there is at least one other
424
+ # vector existing, and the device does not have to be deleted, just the specified vector
425
+ if self.devicesc and devicename and (devicename == item.devicename):
426
+ # This device is being displayed, remove the vector
427
+ vectorid = get_id(item.devicename, item.vectorname)
428
+ if vectorid:
429
+ vectorwidget = self.devicesc.query_one(f"#{vectorid}")
430
+ vectorwidget.remove()
431
+ # the delete event could include a device message
432
+ device = snapshot[devicename]
433
+ messages = device.messages
434
+ if messages:
435
+ log = self.devicesc.query_one("#device-messages")
436
+ log.clear()
437
+ mlist = reversed([ localtimestring(t) + " " + m for t,m in messages ])
438
+ log.write_lines(mlist)
439
+
440
+ # vector removed, does its group need to be removed?
441
+ groupset = set(vector.group for vector in device.values() if vector.enable)
442
+ # get the group of the deleted vector
443
+ grp = device[item.vectorname].group
444
+ if grp not in groupset:
445
+ # the grp no longer has enabled contents, and must be removed
446
+ grpid = get_group_id(grp)
447
+ tabbedcontent = self.devicesc.query_one("#dev_groups")
448
+ tabbedcontent.remove_pane(grpid)
449
+ _ITEMID.unset_group(item.devicename, grp)
450
+ # delete the vector id
451
+ _ITEMID.unset(item.devicename, item.vectorname)
452
+ # give every member an empty id
453
+ membernamelist = list(snapshot[item.devicename][item.vectorname].keys())
454
+ for membername in membernamelist:
455
+ _ITEMID.unset(item.devicename, item.vectorname, membername)
456
+ else:
457
+ # either no vectorname, or last vector is deleted, so delete entire device
458
+ # when a device is deleted, the associated event message, if given, is added
459
+ # to the client messages
460
+ messages = snapshot.messages
461
+ if messages:
462
+ log = self.startsc.query_one("#system-messages")
463
+ log.clear()
464
+ mlist = reversed([ localtimestring(t) + " " + m for t,m in messages ])
465
+ log.write_lines(mlist)
466
+ # remove the device from the startsc device-pane
467
+ deviceid = get_id(item.devicename)
468
+ if deviceid:
469
+ device_pane = self.startsc.query_one("#device-pane")
470
+ device_pane.remove_children(f"#{deviceid}")
471
+ # clear the device, vectors and members from _ITEMID
472
+ _ITEMID.unset(item.devicename)
473
+ vectornamelist = list(snapshot[item.devicename].keys())
474
+ for vectorname in vectornamelist:
475
+ _ITEMID.unset(item.devicename, vectorname)
476
+ membernamelist = list(snapshot[item.devicename][vectorname].keys())
477
+ for membername in membernamelist:
478
+ _ITEMID.unset(item.devicename, vectorname, membername)
479
+ # If the device is currently being displayed, remove its screen and
480
+ # drop to startsc
481
+ if devicename == item.devicename:
482
+ if not self.startsc.is_active:
483
+ self.app.push_screen('startsc')
484
+ return
485
+
486
+ # add device to startsc on receiving a definition
487
+ if (item.eventtype == "Define" or item.eventtype == "DefineBLOB"):
488
+ # does this device have an id
489
+ if not get_id(item.devicename):
490
+ deviceid = set_id(item.devicename)
491
+ device_pane = self.startsc.query_one("#device-pane")
492
+ device_pane.remove_children("#no-devices")
493
+ device_pane.mount(Button(item.devicename, variant="primary", classes="devices", id=deviceid))
494
+ # give the vector an id
495
+ set_id(item.devicename, item.vectorname)
496
+ # give every member an id
497
+ membernamelist = list(snapshot[item.devicename][item.vectorname].keys())
498
+ for membername in membernamelist:
499
+ set_id(item.devicename, item.vectorname, membername)
500
+ elif self.devicesc and devicename and (devicename == item.devicename) and (not get_id(item.devicename, item.vectorname)):
501
+ # known device, which is currently being displayed, but new vector, give the vector an id
502
+ set_id(item.devicename, item.vectorname)
503
+ # give every member an id
504
+ membernamelist = list(snapshot[item.devicename][item.vectorname].keys())
505
+ for membername in membernamelist:
506
+ set_id(item.devicename, item.vectorname, membername)
507
+ if item.devicename == devicename:
508
+ # add the vector to the tab
509
+ vector = snapshot[item.devicename][item.vectorname]
510
+ grpid = get_group_id(vector.group) # if grpid None, a new group has to be created
511
+ if grpid:
512
+ tabpane = self.devicesc.query_one(f"#{grpid}")
513
+ tabpane.add_vector(vector)
514
+ else:
515
+ #set_group_id(vector.group)
516
+ grouppane = self.devicesc.query_one("#dev-group-pane")
517
+ grouppane.add_group(vector.group)
518
+
519
+ return
520
+
521
+
522
+ if (self.devicesc is None) or self.startsc.is_active:
523
+ # no devicesc shown so return
524
+ return
525
+
526
+ if item.devicename != devicename:
527
+ # This device is not currently being shown
528
+ # no need to update any widgets
529
+ return
530
+
531
+ # so device which is currently on self.devicesc has been updated
532
+ # update devicesc
533
+
534
+ # device messages
535
+ if item.eventtype == "Message" and (not item.vectorname):
536
+ messages = snapshot[devicename].messages
537
+ if messages:
538
+ log = self.devicesc.query_one("#device-messages")
539
+ log.clear()
540
+ mlist = reversed([ localtimestring(t) + " " + m for t,m in messages ])
541
+ log.write_lines(mlist)
542
+
543
+ if not item.vectorname:
544
+ return
545
+
546
+ vector = snapshot[item.devicename][item.vectorname]
547
+
548
+ # display the vector timestamp and state
549
+ vectorpane = self.devicesc.query_one(f"#{get_id(devicename, item.vectorname)}")
550
+ vectorpane.vtime = localtimestring(vector.timestamp)
551
+
552
+ if item.eventtype == "TimeOut":
553
+ buttonstatus = self.devicesc.query_one(f"#{get_id(devicename, item.vectorname)}_submitmessage")
554
+ buttonstatus.update("A Timeout Error has occurred")
555
+ vectorpane.vstate = "Alert"
556
+ return
557
+
558
+ vectorpane.vstate = vector.state
559
+
560
+ if item.eventtype == "State":
561
+ # Only the state has changed, and that's dealt with
562
+ return
563
+
564
+ # Display vector message
565
+ if vector.message:
566
+ vectorpane.vmessage = localtimestring(vector.message_timestamp) + " " + vector.message
567
+
568
+ # For every member in the vector, display its value
569
+
570
+ for membername, membervalue in vector.items():
571
+ memberpane = self.devicesc.query_one(f"#{get_id(devicename, vector.name, membername)}")
572
+ if vector.vectortype == "NumberVector":
573
+ membervalue = vector.getformattedvalue(membername)
574
+ if vector.vectortype == "TextVector":
575
+ if not membervalue:
576
+ memberpane.clear_text_value()
577
+ if vector.vectortype == "BLOBVector":
578
+ memberpane.mvalue = vector.member(membername).filename
579
+ else:
580
+ memberpane.mvalue = membervalue
581
+
582
+
583
+ def connect(self):
584
+ host,port = self.hostport.split(":")
585
+ self.make_connection(host, port, self.blobfolderpath)
586
+
587
+ def disconnect(self):
588
+ global _ITEMID
589
+ connection = get_connection()
590
+ if connection.is_alive():
591
+ connection.txque.put(None)
592
+ connection.clientthread.join()
593
+ self.queclient = None
594
+ self.clientthread = None
595
+ self.snapshot = None
596
+ self.host = None
597
+ self.port = None
598
+ log = self.startsc.query_one("#system-messages")
599
+ log.clear()
600
+ t = datetime.now(tz=timezone.utc)
601
+ log.write(localtimestring(t) + " " + "DISCONNECTED")
602
+ self.clear_devices()
603
+
604
+
605
+ def clear_devices(self):
606
+ device_pane = self.startsc.query_one("#device-pane")
607
+ if device_pane.query(".devices"):
608
+ device_pane.remove_children(".devices")
609
+ device_pane.mount(Static("No Devices found", id="no-devices"))
610
+ _ITEMID.clear()
611
+
612
+
613
+
614
+ def make_connection(self, host, port, blobfolder=None):
615
+ if (self.clientthread is not None) and self.clientthread.is_alive():
616
+ raise RuntimeError("Connection has to terminate before another one can be added")
617
+ # The calling app sets the snapshot here
618
+ self.snapshot = None
619
+ self.queclient = None
620
+ self.clientthread = None
621
+ self.host = host
622
+ self.port = port
623
+
624
+ if blobfolder:
625
+ # if no blobfolder given, self.blobfolderpath remains unchanged
626
+ if isinstance(blobfolder, pathlib.Path):
627
+ self.blobfolderpath = blobfolder
628
+ else:
629
+ self.blobfolderpath = pathlib.Path(blobfolder).expanduser().resolve()
630
+ if not self.blobfolderpath.is_dir():
631
+ raise KeyError("If given, the BLOB's folder should be an existing directory")
632
+
633
+
634
+ if self.host and self.port:
635
+ # empty queues
636
+ while not self.txque.empty():
637
+ try:
638
+ item = self.txque.get_nowait()
639
+ except queue.Empty:
640
+ break
641
+ while not self.rxque.empty():
642
+ try:
643
+ item = self.rxque.get_nowait()
644
+ except queue.Empty:
645
+ break
646
+ # create a QueClient object
647
+ self.queclient = QueClient(self.txque, self.rxque, self.host, self.port, self.blobfolderpath)
648
+ # create a thread to run self.queclient.asyncrun()
649
+ self.clientthread = threading.Thread(target=asyncio.run, args=(self.queclient.asyncrun(),))
650
+ self.clientthread.start()
651
+
652
+
653
+ def is_alive(self):
654
+ if self.clientthread is None:
655
+ return False
656
+ if self.clientthread.is_alive():
657
+ return True
658
+ self.queclient = None
659
+ self.clientthread = None
660
+ self.snapshot = None