indipyclient 0.2.2__tar.gz → 0.2.4__tar.gz
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.
- {indipyclient-0.2.2 → indipyclient-0.2.4}/PKG-INFO +1 -1
- {indipyclient-0.2.2 → indipyclient-0.2.4}/indipyclient/__init__.py +1 -1
- {indipyclient-0.2.2 → indipyclient-0.2.4}/indipyclient/console/consoleclient.py +34 -45
- {indipyclient-0.2.2 → indipyclient-0.2.4}/indipyclient/console/widgets.py +1 -0
- {indipyclient-0.2.2 → indipyclient-0.2.4}/indipyclient/console/windows.py +2 -0
- {indipyclient-0.2.2 → indipyclient-0.2.4}/indipyclient/ipyclient.py +13 -49
- {indipyclient-0.2.2 → indipyclient-0.2.4}/indipyclient/propertymembers.py +8 -5
- {indipyclient-0.2.2 → indipyclient-0.2.4}/pyproject.toml +1 -1
- {indipyclient-0.2.2 → indipyclient-0.2.4}/LICENSE +0 -0
- {indipyclient-0.2.2 → indipyclient-0.2.4}/README.md +0 -0
- {indipyclient-0.2.2 → indipyclient-0.2.4}/indipyclient/__main__.py +0 -0
- {indipyclient-0.2.2 → indipyclient-0.2.4}/indipyclient/console/__init__.py +0 -0
- {indipyclient-0.2.2 → indipyclient-0.2.4}/indipyclient/events.py +0 -0
- {indipyclient-0.2.2 → indipyclient-0.2.4}/indipyclient/propertyvectors.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: indipyclient
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: Pure python package, providing a terminal client and a set of classes which can be used to create scripts or clients to control remote instruments using the INDI protocol.
|
|
5
5
|
Keywords: indi,client,astronomy,instrument
|
|
6
6
|
Author-email: Bernard Czenkusz <bernie@skipole.co.uk>
|
|
@@ -123,7 +123,7 @@ class ConsoleControl:
|
|
|
123
123
|
"If self._shutdown becomes True, shutdown"
|
|
124
124
|
while (not self._shutdown) and (not self.client.stopped) and (not self.stop):
|
|
125
125
|
# no shutdown requested, just continue
|
|
126
|
-
await asyncio.sleep(0)
|
|
126
|
+
await asyncio.sleep(0.1)
|
|
127
127
|
# so either shutdown has been requested or the ipyclient has stopped for some reason
|
|
128
128
|
if not self.client.stopped:
|
|
129
129
|
# client is still running, shut it down
|
|
@@ -147,6 +147,7 @@ class ConsoleControl:
|
|
|
147
147
|
while not self.stop:
|
|
148
148
|
await asyncio.sleep(0)
|
|
149
149
|
if isinstance(self.screen, windows.TooSmall):
|
|
150
|
+
await asyncio.sleep(0.1)
|
|
150
151
|
continue
|
|
151
152
|
if not self.connected:
|
|
152
153
|
if isinstance(self.screen, windows.MessagesScreen):
|
|
@@ -164,6 +165,7 @@ class ConsoleControl:
|
|
|
164
165
|
self.eventque.task_done()
|
|
165
166
|
except asyncio.QueueEmpty:
|
|
166
167
|
# no event received, so do not update screen
|
|
168
|
+
await asyncio.sleep(0.02)
|
|
167
169
|
continue
|
|
168
170
|
if isinstance(event, VectorTimeOut) and (event.devicename == self.screen.devicename):
|
|
169
171
|
if isinstance(self.screen, windows.ChooseVectorScreen):
|
|
@@ -206,41 +208,32 @@ class ConsoleControl:
|
|
|
206
208
|
self.BLOBfiles[(event.devicename, event.vectorname, membername)] = filepath
|
|
207
209
|
if isinstance(self.screen, windows.MessagesScreen):
|
|
208
210
|
self.screen.update(event)
|
|
209
|
-
|
|
210
|
-
if isinstance(self.screen, windows.DevicesScreen):
|
|
211
|
+
elif isinstance(self.screen, windows.DevicesScreen):
|
|
211
212
|
self.screen.update(event)
|
|
212
|
-
|
|
213
|
-
if isinstance(self.screen, windows.EnableBLOBsScreen):
|
|
213
|
+
elif isinstance(self.screen, windows.EnableBLOBsScreen):
|
|
214
214
|
self.screen.update(event)
|
|
215
|
-
|
|
216
|
-
if event.devicename != self.screen.devicename:
|
|
215
|
+
elif event.devicename == self.screen.devicename:
|
|
217
216
|
# the remaining screens are only affected if the event devicename
|
|
218
217
|
# is the device they refer to
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
self.screen.
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
218
|
+
if isinstance(event, delProperty):
|
|
219
|
+
if event.vectorname is None:
|
|
220
|
+
# the whole device is disabled,
|
|
221
|
+
if event.devicename and (event.devicename in self.devicenames):
|
|
222
|
+
self.devicenames.remove(event.devicename)
|
|
223
|
+
# show devicesscreen
|
|
224
|
+
self.screen.close("Devices")
|
|
225
|
+
elif isinstance(self.screen, windows.ChooseVectorScreen):
|
|
226
|
+
# one vector has been disabled, update the ChooseVectorScreen
|
|
227
|
+
self.screen.update(event)
|
|
228
|
+
elif isinstance(self.screen, windows.VectorScreen) and (self.screen.vectorname == event.vectorname):
|
|
229
|
+
# This vector has been disabled, show ChooseVectorScreen
|
|
230
|
+
self.screen.close("Vectors")
|
|
231
|
+
# so its not a delete property
|
|
232
|
+
elif isinstance(self.screen, windows.ChooseVectorScreen):
|
|
233
|
+
self.screen.update(event)
|
|
234
|
+
elif isinstance(self.screen, windows.VectorScreen) and (self.screen.vectorname == event.vectorname):
|
|
235
|
+
# The event refers to this vector
|
|
230
236
|
self.screen.update(event)
|
|
231
|
-
continue
|
|
232
|
-
if isinstance(self.screen, windows.VectorScreen) and (self.screen.vectorname == event.vectorname):
|
|
233
|
-
# This vector has been disabled, show ChooseVectorScreen
|
|
234
|
-
self.screen.close("Vectors")
|
|
235
|
-
continue
|
|
236
|
-
continue
|
|
237
|
-
# so its not a delete property
|
|
238
|
-
if isinstance(self.screen, windows.ChooseVectorScreen):
|
|
239
|
-
self.screen.update(event)
|
|
240
|
-
continue
|
|
241
|
-
if isinstance(self.screen, windows.VectorScreen) and (self.screen.vectorname == event.vectorname):
|
|
242
|
-
# The event refers to this vector
|
|
243
|
-
self.screen.update(event)
|
|
244
237
|
except Exception:
|
|
245
238
|
logger.exception("Exception report from ConsoleControl.updatescreen")
|
|
246
239
|
raise
|
|
@@ -258,7 +251,7 @@ class ConsoleControl:
|
|
|
258
251
|
self.maxrows, self.maxcols = self.stdscr.getmaxyx()
|
|
259
252
|
if self.maxrows < 10 or self.maxcols < 40:
|
|
260
253
|
self.shutdown()
|
|
261
|
-
|
|
254
|
+
break
|
|
262
255
|
if self.maxrows < MINROWS or self.maxcols < MINCOLS:
|
|
263
256
|
self.screen = windows.TooSmall(self.stdscr, self)
|
|
264
257
|
self.screen.show()
|
|
@@ -277,7 +270,7 @@ class ConsoleControl:
|
|
|
277
270
|
self.maxrows, self.maxcols = self.stdscr.getmaxyx()
|
|
278
271
|
if self.maxrows < 16 or self.maxcols < 40:
|
|
279
272
|
self.shutdown()
|
|
280
|
-
|
|
273
|
+
break
|
|
281
274
|
if self.maxrows < MINROWS or self.maxcols < MINCOLS:
|
|
282
275
|
self.screen = windows.TooSmall(self.stdscr, self)
|
|
283
276
|
self.screen.show()
|
|
@@ -302,7 +295,7 @@ class ConsoleControl:
|
|
|
302
295
|
self.maxrows, self.maxcols = self.stdscr.getmaxyx()
|
|
303
296
|
if self.maxrows < 16 or self.maxcols < 40:
|
|
304
297
|
self.shutdown()
|
|
305
|
-
|
|
298
|
+
break
|
|
306
299
|
if self.maxrows < MINROWS or self.maxcols < MINCOLS:
|
|
307
300
|
self.screen = windows.TooSmall(self.stdscr, self)
|
|
308
301
|
self.screen.show()
|
|
@@ -327,7 +320,7 @@ class ConsoleControl:
|
|
|
327
320
|
self.maxrows, self.maxcols = self.stdscr.getmaxyx()
|
|
328
321
|
if self.maxrows < 16 or self.maxcols < 40:
|
|
329
322
|
self.shutdown()
|
|
330
|
-
|
|
323
|
+
break
|
|
331
324
|
if self.maxrows < MINROWS or self.maxcols < MINCOLS:
|
|
332
325
|
self.screen = windows.TooSmall(self.stdscr, self)
|
|
333
326
|
self.screen.show()
|
|
@@ -354,7 +347,7 @@ class ConsoleControl:
|
|
|
354
347
|
self.maxrows, self.maxcols = self.stdscr.getmaxyx()
|
|
355
348
|
if self.maxrows < 16 or self.maxcols < 40:
|
|
356
349
|
self.shutdown()
|
|
357
|
-
|
|
350
|
+
break
|
|
358
351
|
if self.maxrows < MINROWS or self.maxcols < MINCOLS:
|
|
359
352
|
self.screen = windows.TooSmall(self.stdscr, self)
|
|
360
353
|
self.screen.show()
|
|
@@ -384,29 +377,25 @@ class ConsoleControl:
|
|
|
384
377
|
self.maxrows, self.maxcols = self.stdscr.getmaxyx()
|
|
385
378
|
if self.maxrows < 16 or self.maxcols < 40:
|
|
386
379
|
self.shutdown()
|
|
387
|
-
|
|
380
|
+
break
|
|
388
381
|
if self.maxrows < MINROWS or self.maxcols < MINCOLS:
|
|
389
382
|
self.screen = windows.TooSmall(self.stdscr, self)
|
|
390
383
|
self.screen.show()
|
|
391
384
|
continue
|
|
392
385
|
self.screen = windows.VectorScreen(self.stdscr, self, self.screen.devicename, self.screen.vectorname)
|
|
393
386
|
self.screen.show()
|
|
394
|
-
|
|
395
|
-
if result == "Quit":
|
|
387
|
+
elif result == "Quit":
|
|
396
388
|
self.shutdown()
|
|
397
389
|
break
|
|
398
|
-
|
|
390
|
+
elif result == "Messages":
|
|
399
391
|
self.screen = windows.MessagesScreen(self.stdscr, self)
|
|
400
392
|
self.screen.show()
|
|
401
|
-
|
|
402
|
-
if result == "Devices":
|
|
393
|
+
elif result == "Devices":
|
|
403
394
|
self.screen = windows.DevicesScreen(self.stdscr, self)
|
|
404
395
|
self.screen.show()
|
|
405
|
-
|
|
406
|
-
if result == "Vectors":
|
|
396
|
+
elif result == "Vectors":
|
|
407
397
|
self.screen = windows.ChooseVectorScreen(self.stdscr, self, self.screen.devicename, group=self.screen.vector.group)
|
|
408
398
|
self.screen.show()
|
|
409
|
-
continue
|
|
410
399
|
|
|
411
400
|
except Exception:
|
|
412
401
|
logger.exception("Exception report from ConsoleControl.getinput")
|
|
@@ -46,6 +46,7 @@ class ParentScreen:
|
|
|
46
46
|
return self._close
|
|
47
47
|
key = self.stdscr.getch()
|
|
48
48
|
if key == -1:
|
|
49
|
+
await asyncio.sleep(0.02)
|
|
49
50
|
continue
|
|
50
51
|
if key == curses.KEY_RESIZE:
|
|
51
52
|
return "Resize"
|
|
@@ -83,6 +84,7 @@ class ConsoleClientScreen(ParentScreen):
|
|
|
83
84
|
return self._close
|
|
84
85
|
key = self.stdscr.getch()
|
|
85
86
|
if key == -1:
|
|
87
|
+
await asyncio.sleep(0.02)
|
|
86
88
|
continue
|
|
87
89
|
if key == curses.KEY_RESIZE:
|
|
88
90
|
return "Resize"
|
|
@@ -6,8 +6,6 @@ from time import sleep
|
|
|
6
6
|
|
|
7
7
|
from datetime import datetime, timezone
|
|
8
8
|
|
|
9
|
-
from base64 import standard_b64encode
|
|
10
|
-
|
|
11
9
|
import xml.etree.ElementTree as ET
|
|
12
10
|
|
|
13
11
|
import logging
|
|
@@ -61,32 +59,6 @@ def _makestart(element):
|
|
|
61
59
|
return "".join(attriblist)
|
|
62
60
|
|
|
63
61
|
|
|
64
|
-
def blob_xml_bytes(xmldata):
|
|
65
|
-
"""A generator yielding blob xml byte strings
|
|
66
|
-
for a newBLOBVector.
|
|
67
|
-
reads member bytes, b64 encodes the data
|
|
68
|
-
and yields the byte string including tags."""
|
|
69
|
-
|
|
70
|
-
# yield initial newBLOBVector
|
|
71
|
-
newblobvector = _makestart(xmldata)
|
|
72
|
-
yield newblobvector.encode()
|
|
73
|
-
for oneblob in xmldata.iter('oneBLOB'):
|
|
74
|
-
bytescontent = oneblob.text
|
|
75
|
-
size = oneblob.get("size")
|
|
76
|
-
if size == "0":
|
|
77
|
-
oneblob.set("size", str(len(bytescontent)))
|
|
78
|
-
# yield start of oneblob
|
|
79
|
-
start = _makestart(oneblob)
|
|
80
|
-
yield start.encode()
|
|
81
|
-
# yield body, b64 encoded, in chunks
|
|
82
|
-
encoded_data = standard_b64encode(bytescontent)
|
|
83
|
-
chunksize = 1000
|
|
84
|
-
for b in range(0, len(encoded_data), chunksize):
|
|
85
|
-
yield encoded_data[b:b+chunksize]
|
|
86
|
-
yield b"</oneBLOB>"
|
|
87
|
-
yield b"</newBLOBVector>\n"
|
|
88
|
-
|
|
89
|
-
|
|
90
62
|
|
|
91
63
|
class IPyClient(collections.UserDict):
|
|
92
64
|
|
|
@@ -293,7 +265,7 @@ class IPyClient(collections.UserDict):
|
|
|
293
265
|
async def _check_alive(self, writer):
|
|
294
266
|
try:
|
|
295
267
|
while self.connected and (not self._stop):
|
|
296
|
-
await asyncio.sleep(0)
|
|
268
|
+
await asyncio.sleep(0.1)
|
|
297
269
|
if self.tx_timer:
|
|
298
270
|
# data has been sent, waiting for reply
|
|
299
271
|
telapsed = time.time() - self.tx_timer
|
|
@@ -314,7 +286,6 @@ class IPyClient(collections.UserDict):
|
|
|
314
286
|
finally:
|
|
315
287
|
self.connected = False
|
|
316
288
|
|
|
317
|
-
|
|
318
289
|
def _logtx(self, txdata):
|
|
319
290
|
"log tx data with level debug, and detail depends on self._verbose"
|
|
320
291
|
if not self._verbose:
|
|
@@ -324,12 +295,13 @@ class IPyClient(collections.UserDict):
|
|
|
324
295
|
binarydata = ET.tostring(txdata)
|
|
325
296
|
logger.debug(startlog + binarydata.decode())
|
|
326
297
|
elif self._verbose == 2:
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if tag == "newBLOBVector":
|
|
298
|
+
if txdata.tag == "newBLOBVector" or txdata.tag == "setBLOBVector":
|
|
299
|
+
data = copy.deepcopy(txdata)
|
|
300
|
+
for element in data:
|
|
331
301
|
element.text = "NOT LOGGED"
|
|
332
|
-
|
|
302
|
+
binarydata = ET.tostring(data)
|
|
303
|
+
else:
|
|
304
|
+
binarydata = ET.tostring(txdata)
|
|
333
305
|
logger.debug(startlog + binarydata.decode())
|
|
334
306
|
elif self._verbose == 1:
|
|
335
307
|
data = copy.deepcopy(txdata)
|
|
@@ -348,25 +320,17 @@ class IPyClient(collections.UserDict):
|
|
|
348
320
|
try:
|
|
349
321
|
txdata = self.writerque.popleft()
|
|
350
322
|
except IndexError:
|
|
323
|
+
await asyncio.sleep(0.1)
|
|
351
324
|
continue
|
|
352
325
|
if not self.connected:
|
|
353
326
|
break
|
|
354
327
|
if self._stop:
|
|
355
328
|
break
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
writer.write(binarydata)
|
|
362
|
-
await writer.drain()
|
|
363
|
-
else:
|
|
364
|
-
# its straight xml, send it out on the port
|
|
365
|
-
binarydata = ET.tostring(txdata)
|
|
366
|
-
# Send to the port
|
|
367
|
-
writer.write(binarydata)
|
|
368
|
-
await writer.drain()
|
|
369
|
-
|
|
329
|
+
# send it out on the port
|
|
330
|
+
binarydata = ET.tostring(txdata)
|
|
331
|
+
# Send to the port
|
|
332
|
+
writer.write(binarydata)
|
|
333
|
+
await writer.drain()
|
|
370
334
|
if self.timeout_enable:
|
|
371
335
|
# data has been transmitted set timers going, do not set timer
|
|
372
336
|
# for enableBLOB as no answer is expected for that
|
|
@@ -3,6 +3,8 @@ import xml.etree.ElementTree as ET
|
|
|
3
3
|
|
|
4
4
|
import sys, pathlib
|
|
5
5
|
|
|
6
|
+
from base64 import standard_b64encode
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
class ParseException(Exception):
|
|
8
10
|
"Raised if an error occurs when parsing received data"
|
|
@@ -421,13 +423,12 @@ class BLOBMember(ParentBLOBMember):
|
|
|
421
423
|
xmldata = ET.Element('oneBLOB')
|
|
422
424
|
xmldata.set("name", self.name)
|
|
423
425
|
xmldata.set("format", newformat)
|
|
424
|
-
xmldata.set("size", str(newsize))
|
|
425
426
|
# the value set in the xmldata object should be a bytes object
|
|
426
427
|
if isinstance(newvalue, bytes):
|
|
427
|
-
|
|
428
|
+
bytescontent = newvalue
|
|
428
429
|
elif isinstance(newvalue, pathlib.Path):
|
|
429
430
|
try:
|
|
430
|
-
|
|
431
|
+
bytescontent = newvalue.read_bytes()
|
|
431
432
|
except Exception:
|
|
432
433
|
raise ParseException("Unable to read the given file")
|
|
433
434
|
elif hasattr(newvalue, "seek") and hasattr(newvalue, "read") and callable(newvalue.read):
|
|
@@ -440,7 +441,6 @@ class BLOBMember(ParentBLOBMember):
|
|
|
440
441
|
raise ParseException("The read BLOB is not a bytes object")
|
|
441
442
|
if bytescontent == b"":
|
|
442
443
|
raise ParseException("The read BLOB value is empty")
|
|
443
|
-
xmldata.text = bytescontent
|
|
444
444
|
else:
|
|
445
445
|
# could be a path to a file
|
|
446
446
|
try:
|
|
@@ -450,7 +450,10 @@ class BLOBMember(ParentBLOBMember):
|
|
|
450
450
|
raise ParseException("Unable to read the given file")
|
|
451
451
|
if bytescontent == b"":
|
|
452
452
|
raise ParseException("The read BLOB value is empty")
|
|
453
|
-
|
|
453
|
+
if not newsize:
|
|
454
|
+
newsize = len(bytescontent)
|
|
455
|
+
xmldata.set("size", str(newsize))
|
|
456
|
+
xmldata.text = standard_b64encode(bytescontent).decode("utf-8")
|
|
454
457
|
return xmldata
|
|
455
458
|
|
|
456
459
|
|
|
@@ -7,7 +7,7 @@ name = "indipyclient"
|
|
|
7
7
|
authors = [{name = "Bernard Czenkusz", email = "bernie@skipole.co.uk"}]
|
|
8
8
|
license = {file = "LICENSE"}
|
|
9
9
|
classifiers = ["License :: OSI Approved :: MIT License", "Operating System :: POSIX :: Linux","Topic :: Scientific/Engineering :: Astronomy", "Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator"]
|
|
10
|
-
version = "0.2.
|
|
10
|
+
version = "0.2.4"
|
|
11
11
|
description="Pure python package, providing a terminal client and a set of classes which can be used to create scripts or clients to control remote instruments using the INDI protocol."
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
requires-python = ">=3.10"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|