twinleaf 0.1.0__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.
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: twinleaf
3
+ Version: 0.1.0
4
+ Classifier: Programming Language :: Rust
5
+ Classifier: Programming Language :: Python :: Implementation :: CPython
6
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
7
+ Requires-Python: >=3.8
@@ -0,0 +1,18 @@
1
+ [build-system]
2
+ requires = ["maturin>=1.7,<2.0"]
3
+ build-backend = "maturin"
4
+
5
+ [project]
6
+ name = "twinleaf"
7
+ requires-python = ">=3.8"
8
+ classifiers = [
9
+ "Programming Language :: Rust",
10
+ "Programming Language :: Python :: Implementation :: CPython",
11
+ "Programming Language :: Python :: Implementation :: PyPy",
12
+ ]
13
+ dynamic = ["version"]
14
+
15
+ [tool.maturin]
16
+ features = ["pyo3/extension-module"]
17
+ module-name = "twinleaf._twinleaf"
18
+ data = "twinleaf.data"
@@ -0,0 +1,603 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "autocfg"
7
+ version = "1.4.0"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
10
+
11
+ [[package]]
12
+ name = "bitflags"
13
+ version = "1.3.2"
14
+ source = "registry+https://github.com/rust-lang/crates.io-index"
15
+ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
16
+
17
+ [[package]]
18
+ name = "bitflags"
19
+ version = "2.9.0"
20
+ source = "registry+https://github.com/rust-lang/crates.io-index"
21
+ checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
22
+
23
+ [[package]]
24
+ name = "cfg-if"
25
+ version = "1.0.0"
26
+ source = "registry+https://github.com/rust-lang/crates.io-index"
27
+ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
28
+
29
+ [[package]]
30
+ name = "cfg_aliases"
31
+ version = "0.2.1"
32
+ source = "registry+https://github.com/rust-lang/crates.io-index"
33
+ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
34
+
35
+ [[package]]
36
+ name = "core-foundation"
37
+ version = "0.10.0"
38
+ source = "registry+https://github.com/rust-lang/crates.io-index"
39
+ checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63"
40
+ dependencies = [
41
+ "core-foundation-sys",
42
+ "libc",
43
+ ]
44
+
45
+ [[package]]
46
+ name = "core-foundation-sys"
47
+ version = "0.8.7"
48
+ source = "registry+https://github.com/rust-lang/crates.io-index"
49
+ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
50
+
51
+ [[package]]
52
+ name = "crc"
53
+ version = "3.2.1"
54
+ source = "registry+https://github.com/rust-lang/crates.io-index"
55
+ checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
56
+ dependencies = [
57
+ "crc-catalog",
58
+ ]
59
+
60
+ [[package]]
61
+ name = "crc-catalog"
62
+ version = "2.4.0"
63
+ source = "registry+https://github.com/rust-lang/crates.io-index"
64
+ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
65
+
66
+ [[package]]
67
+ name = "crossbeam"
68
+ version = "0.8.4"
69
+ source = "registry+https://github.com/rust-lang/crates.io-index"
70
+ checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
71
+ dependencies = [
72
+ "crossbeam-channel",
73
+ "crossbeam-deque",
74
+ "crossbeam-epoch",
75
+ "crossbeam-queue",
76
+ "crossbeam-utils",
77
+ ]
78
+
79
+ [[package]]
80
+ name = "crossbeam-channel"
81
+ version = "0.5.15"
82
+ source = "registry+https://github.com/rust-lang/crates.io-index"
83
+ checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
84
+ dependencies = [
85
+ "crossbeam-utils",
86
+ ]
87
+
88
+ [[package]]
89
+ name = "crossbeam-deque"
90
+ version = "0.8.6"
91
+ source = "registry+https://github.com/rust-lang/crates.io-index"
92
+ checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
93
+ dependencies = [
94
+ "crossbeam-epoch",
95
+ "crossbeam-utils",
96
+ ]
97
+
98
+ [[package]]
99
+ name = "crossbeam-epoch"
100
+ version = "0.9.18"
101
+ source = "registry+https://github.com/rust-lang/crates.io-index"
102
+ checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
103
+ dependencies = [
104
+ "crossbeam-utils",
105
+ ]
106
+
107
+ [[package]]
108
+ name = "crossbeam-queue"
109
+ version = "0.3.12"
110
+ source = "registry+https://github.com/rust-lang/crates.io-index"
111
+ checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
112
+ dependencies = [
113
+ "crossbeam-utils",
114
+ ]
115
+
116
+ [[package]]
117
+ name = "crossbeam-utils"
118
+ version = "0.8.21"
119
+ source = "registry+https://github.com/rust-lang/crates.io-index"
120
+ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
121
+
122
+ [[package]]
123
+ name = "equivalent"
124
+ version = "1.0.2"
125
+ source = "registry+https://github.com/rust-lang/crates.io-index"
126
+ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
127
+
128
+ [[package]]
129
+ name = "hashbrown"
130
+ version = "0.15.2"
131
+ source = "registry+https://github.com/rust-lang/crates.io-index"
132
+ checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
133
+
134
+ [[package]]
135
+ name = "heck"
136
+ version = "0.5.0"
137
+ source = "registry+https://github.com/rust-lang/crates.io-index"
138
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
139
+
140
+ [[package]]
141
+ name = "indexmap"
142
+ version = "2.9.0"
143
+ source = "registry+https://github.com/rust-lang/crates.io-index"
144
+ checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
145
+ dependencies = [
146
+ "equivalent",
147
+ "hashbrown",
148
+ ]
149
+
150
+ [[package]]
151
+ name = "indoc"
152
+ version = "2.0.6"
153
+ source = "registry+https://github.com/rust-lang/crates.io-index"
154
+ checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
155
+
156
+ [[package]]
157
+ name = "io-kit-sys"
158
+ version = "0.4.1"
159
+ source = "registry+https://github.com/rust-lang/crates.io-index"
160
+ checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b"
161
+ dependencies = [
162
+ "core-foundation-sys",
163
+ "mach2",
164
+ ]
165
+
166
+ [[package]]
167
+ name = "libc"
168
+ version = "0.2.172"
169
+ source = "registry+https://github.com/rust-lang/crates.io-index"
170
+ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
171
+
172
+ [[package]]
173
+ name = "log"
174
+ version = "0.4.27"
175
+ source = "registry+https://github.com/rust-lang/crates.io-index"
176
+ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
177
+
178
+ [[package]]
179
+ name = "mach2"
180
+ version = "0.4.2"
181
+ source = "registry+https://github.com/rust-lang/crates.io-index"
182
+ checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709"
183
+ dependencies = [
184
+ "libc",
185
+ ]
186
+
187
+ [[package]]
188
+ name = "memchr"
189
+ version = "2.7.4"
190
+ source = "registry+https://github.com/rust-lang/crates.io-index"
191
+ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
192
+
193
+ [[package]]
194
+ name = "memoffset"
195
+ version = "0.9.1"
196
+ source = "registry+https://github.com/rust-lang/crates.io-index"
197
+ checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
198
+ dependencies = [
199
+ "autocfg",
200
+ ]
201
+
202
+ [[package]]
203
+ name = "mio"
204
+ version = "1.0.3"
205
+ source = "registry+https://github.com/rust-lang/crates.io-index"
206
+ checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
207
+ dependencies = [
208
+ "libc",
209
+ "log",
210
+ "wasi",
211
+ "windows-sys",
212
+ ]
213
+
214
+ [[package]]
215
+ name = "mio-serial"
216
+ version = "5.0.6"
217
+ source = "registry+https://github.com/rust-lang/crates.io-index"
218
+ checksum = "029e1f407e261176a983a6599c084efd322d9301028055c87174beac71397ba3"
219
+ dependencies = [
220
+ "log",
221
+ "mio",
222
+ "nix 0.29.0",
223
+ "serialport",
224
+ "winapi",
225
+ ]
226
+
227
+ [[package]]
228
+ name = "nix"
229
+ version = "0.26.4"
230
+ source = "registry+https://github.com/rust-lang/crates.io-index"
231
+ checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
232
+ dependencies = [
233
+ "bitflags 1.3.2",
234
+ "cfg-if",
235
+ "libc",
236
+ ]
237
+
238
+ [[package]]
239
+ name = "nix"
240
+ version = "0.29.0"
241
+ source = "registry+https://github.com/rust-lang/crates.io-index"
242
+ checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
243
+ dependencies = [
244
+ "bitflags 2.9.0",
245
+ "cfg-if",
246
+ "cfg_aliases",
247
+ "libc",
248
+ ]
249
+
250
+ [[package]]
251
+ name = "num_enum"
252
+ version = "0.7.3"
253
+ source = "registry+https://github.com/rust-lang/crates.io-index"
254
+ checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
255
+ dependencies = [
256
+ "num_enum_derive",
257
+ ]
258
+
259
+ [[package]]
260
+ name = "num_enum_derive"
261
+ version = "0.7.3"
262
+ source = "registry+https://github.com/rust-lang/crates.io-index"
263
+ checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
264
+ dependencies = [
265
+ "proc-macro-crate",
266
+ "proc-macro2",
267
+ "quote",
268
+ "syn",
269
+ ]
270
+
271
+ [[package]]
272
+ name = "once_cell"
273
+ version = "1.21.3"
274
+ source = "registry+https://github.com/rust-lang/crates.io-index"
275
+ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
276
+
277
+ [[package]]
278
+ name = "portable-atomic"
279
+ version = "1.11.0"
280
+ source = "registry+https://github.com/rust-lang/crates.io-index"
281
+ checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
282
+
283
+ [[package]]
284
+ name = "proc-macro-crate"
285
+ version = "3.3.0"
286
+ source = "registry+https://github.com/rust-lang/crates.io-index"
287
+ checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35"
288
+ dependencies = [
289
+ "toml_edit",
290
+ ]
291
+
292
+ [[package]]
293
+ name = "proc-macro2"
294
+ version = "1.0.95"
295
+ source = "registry+https://github.com/rust-lang/crates.io-index"
296
+ checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
297
+ dependencies = [
298
+ "unicode-ident",
299
+ ]
300
+
301
+ [[package]]
302
+ name = "pyo3"
303
+ version = "0.24.2"
304
+ source = "registry+https://github.com/rust-lang/crates.io-index"
305
+ checksum = "e5203598f366b11a02b13aa20cab591229ff0a89fd121a308a5df751d5fc9219"
306
+ dependencies = [
307
+ "cfg-if",
308
+ "indoc",
309
+ "libc",
310
+ "memoffset",
311
+ "once_cell",
312
+ "portable-atomic",
313
+ "pyo3-build-config",
314
+ "pyo3-ffi",
315
+ "pyo3-macros",
316
+ "unindent",
317
+ ]
318
+
319
+ [[package]]
320
+ name = "pyo3-build-config"
321
+ version = "0.24.2"
322
+ source = "registry+https://github.com/rust-lang/crates.io-index"
323
+ checksum = "99636d423fa2ca130fa5acde3059308006d46f98caac629418e53f7ebb1e9999"
324
+ dependencies = [
325
+ "once_cell",
326
+ "target-lexicon",
327
+ ]
328
+
329
+ [[package]]
330
+ name = "pyo3-ffi"
331
+ version = "0.24.2"
332
+ source = "registry+https://github.com/rust-lang/crates.io-index"
333
+ checksum = "78f9cf92ba9c409279bc3305b5409d90db2d2c22392d443a87df3a1adad59e33"
334
+ dependencies = [
335
+ "libc",
336
+ "pyo3-build-config",
337
+ ]
338
+
339
+ [[package]]
340
+ name = "pyo3-macros"
341
+ version = "0.24.2"
342
+ source = "registry+https://github.com/rust-lang/crates.io-index"
343
+ checksum = "0b999cb1a6ce21f9a6b147dcf1be9ffedf02e0043aec74dc390f3007047cecd9"
344
+ dependencies = [
345
+ "proc-macro2",
346
+ "pyo3-macros-backend",
347
+ "quote",
348
+ "syn",
349
+ ]
350
+
351
+ [[package]]
352
+ name = "pyo3-macros-backend"
353
+ version = "0.24.2"
354
+ source = "registry+https://github.com/rust-lang/crates.io-index"
355
+ checksum = "822ece1c7e1012745607d5cf0bcb2874769f0f7cb34c4cde03b9358eb9ef911a"
356
+ dependencies = [
357
+ "heck",
358
+ "proc-macro2",
359
+ "pyo3-build-config",
360
+ "quote",
361
+ "syn",
362
+ ]
363
+
364
+ [[package]]
365
+ name = "quote"
366
+ version = "1.0.40"
367
+ source = "registry+https://github.com/rust-lang/crates.io-index"
368
+ checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
369
+ dependencies = [
370
+ "proc-macro2",
371
+ ]
372
+
373
+ [[package]]
374
+ name = "scopeguard"
375
+ version = "1.2.0"
376
+ source = "registry+https://github.com/rust-lang/crates.io-index"
377
+ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
378
+
379
+ [[package]]
380
+ name = "serialport"
381
+ version = "4.7.1"
382
+ source = "registry+https://github.com/rust-lang/crates.io-index"
383
+ checksum = "2daa7abb9b965493e3c8f4184c6f46435484ff2538a332b886788cf6768b927b"
384
+ dependencies = [
385
+ "bitflags 2.9.0",
386
+ "cfg-if",
387
+ "core-foundation",
388
+ "core-foundation-sys",
389
+ "io-kit-sys",
390
+ "mach2",
391
+ "nix 0.26.4",
392
+ "scopeguard",
393
+ "unescaper",
394
+ "winapi",
395
+ ]
396
+
397
+ [[package]]
398
+ name = "syn"
399
+ version = "2.0.101"
400
+ source = "registry+https://github.com/rust-lang/crates.io-index"
401
+ checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
402
+ dependencies = [
403
+ "proc-macro2",
404
+ "quote",
405
+ "unicode-ident",
406
+ ]
407
+
408
+ [[package]]
409
+ name = "target-lexicon"
410
+ version = "0.13.2"
411
+ source = "registry+https://github.com/rust-lang/crates.io-index"
412
+ checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a"
413
+
414
+ [[package]]
415
+ name = "thiserror"
416
+ version = "1.0.69"
417
+ source = "registry+https://github.com/rust-lang/crates.io-index"
418
+ checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
419
+ dependencies = [
420
+ "thiserror-impl",
421
+ ]
422
+
423
+ [[package]]
424
+ name = "thiserror-impl"
425
+ version = "1.0.69"
426
+ source = "registry+https://github.com/rust-lang/crates.io-index"
427
+ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
428
+ dependencies = [
429
+ "proc-macro2",
430
+ "quote",
431
+ "syn",
432
+ ]
433
+
434
+ [[package]]
435
+ name = "toml_datetime"
436
+ version = "0.6.9"
437
+ source = "registry+https://github.com/rust-lang/crates.io-index"
438
+ checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
439
+
440
+ [[package]]
441
+ name = "toml_edit"
442
+ version = "0.22.25"
443
+ source = "registry+https://github.com/rust-lang/crates.io-index"
444
+ checksum = "10558ed0bd2a1562e630926a2d1f0b98c827da99fabd3fe20920a59642504485"
445
+ dependencies = [
446
+ "indexmap",
447
+ "toml_datetime",
448
+ "winnow",
449
+ ]
450
+
451
+ [[package]]
452
+ name = "twinleaf"
453
+ version = "1.3.2"
454
+ source = "registry+https://github.com/rust-lang/crates.io-index"
455
+ checksum = "49fbf6243951b4dc835039b311fbd61b07a645fa1993e353407cebdf25de8662"
456
+ dependencies = [
457
+ "crc",
458
+ "crossbeam",
459
+ "mio",
460
+ "mio-serial",
461
+ "num_enum",
462
+ "winapi",
463
+ ]
464
+
465
+ [[package]]
466
+ name = "twinleaf-py"
467
+ version = "0.1.0"
468
+ dependencies = [
469
+ "crossbeam",
470
+ "pyo3",
471
+ "twinleaf",
472
+ ]
473
+
474
+ [[package]]
475
+ name = "unescaper"
476
+ version = "0.1.5"
477
+ source = "registry+https://github.com/rust-lang/crates.io-index"
478
+ checksum = "c878a167baa8afd137494101a688ef8c67125089ff2249284bd2b5f9bfedb815"
479
+ dependencies = [
480
+ "thiserror",
481
+ ]
482
+
483
+ [[package]]
484
+ name = "unicode-ident"
485
+ version = "1.0.18"
486
+ source = "registry+https://github.com/rust-lang/crates.io-index"
487
+ checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
488
+
489
+ [[package]]
490
+ name = "unindent"
491
+ version = "0.2.4"
492
+ source = "registry+https://github.com/rust-lang/crates.io-index"
493
+ checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
494
+
495
+ [[package]]
496
+ name = "wasi"
497
+ version = "0.11.0+wasi-snapshot-preview1"
498
+ source = "registry+https://github.com/rust-lang/crates.io-index"
499
+ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
500
+
501
+ [[package]]
502
+ name = "winapi"
503
+ version = "0.3.9"
504
+ source = "registry+https://github.com/rust-lang/crates.io-index"
505
+ checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
506
+ dependencies = [
507
+ "winapi-i686-pc-windows-gnu",
508
+ "winapi-x86_64-pc-windows-gnu",
509
+ ]
510
+
511
+ [[package]]
512
+ name = "winapi-i686-pc-windows-gnu"
513
+ version = "0.4.0"
514
+ source = "registry+https://github.com/rust-lang/crates.io-index"
515
+ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
516
+
517
+ [[package]]
518
+ name = "winapi-x86_64-pc-windows-gnu"
519
+ version = "0.4.0"
520
+ source = "registry+https://github.com/rust-lang/crates.io-index"
521
+ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
522
+
523
+ [[package]]
524
+ name = "windows-sys"
525
+ version = "0.52.0"
526
+ source = "registry+https://github.com/rust-lang/crates.io-index"
527
+ checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
528
+ dependencies = [
529
+ "windows-targets",
530
+ ]
531
+
532
+ [[package]]
533
+ name = "windows-targets"
534
+ version = "0.52.6"
535
+ source = "registry+https://github.com/rust-lang/crates.io-index"
536
+ checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
537
+ dependencies = [
538
+ "windows_aarch64_gnullvm",
539
+ "windows_aarch64_msvc",
540
+ "windows_i686_gnu",
541
+ "windows_i686_gnullvm",
542
+ "windows_i686_msvc",
543
+ "windows_x86_64_gnu",
544
+ "windows_x86_64_gnullvm",
545
+ "windows_x86_64_msvc",
546
+ ]
547
+
548
+ [[package]]
549
+ name = "windows_aarch64_gnullvm"
550
+ version = "0.52.6"
551
+ source = "registry+https://github.com/rust-lang/crates.io-index"
552
+ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
553
+
554
+ [[package]]
555
+ name = "windows_aarch64_msvc"
556
+ version = "0.52.6"
557
+ source = "registry+https://github.com/rust-lang/crates.io-index"
558
+ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
559
+
560
+ [[package]]
561
+ name = "windows_i686_gnu"
562
+ version = "0.52.6"
563
+ source = "registry+https://github.com/rust-lang/crates.io-index"
564
+ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
565
+
566
+ [[package]]
567
+ name = "windows_i686_gnullvm"
568
+ version = "0.52.6"
569
+ source = "registry+https://github.com/rust-lang/crates.io-index"
570
+ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
571
+
572
+ [[package]]
573
+ name = "windows_i686_msvc"
574
+ version = "0.52.6"
575
+ source = "registry+https://github.com/rust-lang/crates.io-index"
576
+ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
577
+
578
+ [[package]]
579
+ name = "windows_x86_64_gnu"
580
+ version = "0.52.6"
581
+ source = "registry+https://github.com/rust-lang/crates.io-index"
582
+ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
583
+
584
+ [[package]]
585
+ name = "windows_x86_64_gnullvm"
586
+ version = "0.52.6"
587
+ source = "registry+https://github.com/rust-lang/crates.io-index"
588
+ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
589
+
590
+ [[package]]
591
+ name = "windows_x86_64_msvc"
592
+ version = "0.52.6"
593
+ source = "registry+https://github.com/rust-lang/crates.io-index"
594
+ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
595
+
596
+ [[package]]
597
+ name = "winnow"
598
+ version = "0.7.7"
599
+ source = "registry+https://github.com/rust-lang/crates.io-index"
600
+ checksum = "6cb8234a863ea0e8cd7284fcdd4f145233eb00fee02bbdd9861aec44e6477bc5"
601
+ dependencies = [
602
+ "memchr",
603
+ ]
@@ -0,0 +1,14 @@
1
+ [package]
2
+ name = "twinleaf-py"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+
6
+ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7
+ [lib]
8
+ name = "_twinleaf"
9
+ crate-type = ["cdylib"]
10
+
11
+ [dependencies]
12
+ pyo3 = { version = "0.24.2", features = ["extension-module"] }
13
+ twinleaf = { version = "1.3" }
14
+ crossbeam = "0.8"
@@ -0,0 +1,176 @@
1
+ use ::twinleaf::tio::*;
2
+ use ::twinleaf::*;
3
+ use pyo3::exceptions::PyRuntimeError;
4
+ use pyo3::prelude::*;
5
+ use pyo3::types::{PyBytes, PyDict};
6
+
7
+ #[pyclass(name = "DataIterator", subclass)]
8
+ struct PyIter {
9
+ port: data::Device,
10
+ n: Option<usize>,
11
+ stream: String,
12
+ columns: Vec<String>,
13
+ }
14
+
15
+ #[pymethods]
16
+ impl PyIter {
17
+ fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
18
+ slf
19
+ }
20
+
21
+ fn __next__(mut slf: PyRefMut<'_, Self>) -> PyResult<Option<PyObject>> {
22
+ let dict = PyDict::new(slf.py());
23
+
24
+ if let Some(ctr) = slf.n {
25
+ if ctr == 0 {
26
+ // TODO: drop port
27
+ return Ok(None);
28
+ } else {
29
+ slf.n = Some(ctr - 1);
30
+ }
31
+ }
32
+
33
+ while dict.is_empty() {
34
+ // Check for keyboard interrupt
35
+ slf.py().check_signals()?;
36
+
37
+ let sample = slf.port.next();
38
+
39
+ if !slf.stream.is_empty() && slf.stream != sample.stream.name {
40
+ continue;
41
+ }
42
+
43
+ for sample_column in &sample.columns {
44
+ let sample_column_name = sample_column.desc.name.clone();
45
+ let column_matches = slf.columns.is_empty() || slf.columns.iter().any(|c| {
46
+ if c.ends_with("*") {
47
+ // Remove * and check if sample_column_name starts with prefix
48
+ let prefix = &c[..c.len()-1];
49
+ sample_column_name.starts_with(prefix)
50
+ } else {
51
+ c.eq(&sample_column_name)
52
+ }
53
+ });
54
+ if column_matches {
55
+ let time = sample.timestamp_end().into_pyobject(slf.py())?;
56
+ let stream_id = sample.stream.stream_id.into_pyobject(slf.py())?;
57
+ dict.set_item("stream", stream_id)?;
58
+ dict.set_item("time", time)?;
59
+ match sample_column.value {
60
+ data::ColumnData::Int(x) => {
61
+ dict.set_item(sample_column_name.into_pyobject(slf.py())?, x.into_pyobject(slf.py())?)?
62
+ }
63
+ data::ColumnData::UInt(x) => {
64
+ dict.set_item(sample_column_name.into_pyobject(slf.py())?, x.into_pyobject(slf.py())?)?
65
+ }
66
+ data::ColumnData::Float(x) => {
67
+ dict.set_item(sample_column_name.into_pyobject(slf.py())?, x.into_pyobject(slf.py())?)?
68
+ }
69
+ _ => dict.set_item(sample_column_name.into_pyobject(slf.py())?, "UNKNOWN".into_pyobject(slf.py())?)?,
70
+ };
71
+ }
72
+ }
73
+ }
74
+
75
+ Ok(Some(dict.into()))
76
+ }
77
+ }
78
+
79
+ #[pyclass(name = "Device", subclass)]
80
+ struct PyDevice {
81
+ proxy: proxy::Interface,
82
+ route: proto::DeviceRoute,
83
+ rpc: proxy::Port,
84
+ }
85
+
86
+ #[pymethods]
87
+ impl PyDevice {
88
+ #[new]
89
+ #[pyo3(signature = (root_url=None, route=None))]
90
+ fn new(root_url: Option<String>, route: Option<String>) -> PyResult<PyDevice> {
91
+ let root = if let Some(url) = root_url {
92
+ url
93
+ } else {
94
+ "tcp://localhost".to_string()
95
+ };
96
+ let route = if let Some(path) = route {
97
+ proto::DeviceRoute::from_str(&path).unwrap()
98
+ } else {
99
+ proto::DeviceRoute::root()
100
+ };
101
+ let proxy = proxy::Interface::new(&root);
102
+ let rpc = proxy.device_rpc(route.clone()).unwrap();
103
+ Ok(PyDevice { proxy, route, rpc })
104
+ }
105
+
106
+ fn _rpc<'py>(&self, py: Python<'py>, name: &str, req: &[u8]) -> PyResult<Bound<'py, PyBytes>> {
107
+ match self.rpc.raw_rpc(name, req) {
108
+ Ok(ret) => Ok(PyBytes::new(py, &ret[..])),
109
+ _ => Err(PyRuntimeError::new_err(format!("RPC '{}' failed", name))),
110
+ }
111
+ }
112
+
113
+ #[pyo3(signature = (n=1, stream=None, columns=None))]
114
+ fn _samples<'py>(
115
+ &self,
116
+ _py: Python<'py>,
117
+ n: Option<usize>,
118
+ stream: Option<String>,
119
+ columns: Option<Vec<String>>,
120
+ ) -> PyResult<PyIter> {
121
+ Ok(PyIter {
122
+ port: data::Device::new(self.proxy.device_full(self.route.clone()).unwrap()),
123
+ n: n,
124
+ stream: stream.unwrap_or_default(),
125
+ columns: columns.unwrap_or_default(),
126
+ })
127
+ }
128
+
129
+ fn _get_metadata<'py>(&self, py: Python<'py>) -> PyResult<PyObject> {
130
+ let mut device = data::Device::new(self.proxy.device_full(self.route.clone()).unwrap());
131
+ let meta = device.get_metadata();
132
+
133
+ let dict = PyDict::new(py);
134
+
135
+ // Convert device metadata to dict
136
+ let device_dict = PyDict::new(py);
137
+ device_dict.set_item("serial_number", meta.device.serial_number.to_string())?;
138
+ device_dict.set_item("firmware_hash", meta.device.firmware_hash.to_string())?;
139
+ device_dict.set_item("session_id", meta.device.session_id.to_string())?;
140
+ device_dict.set_item("name", meta.device.name.to_string())?;
141
+ dict.set_item("device", device_dict)?;
142
+
143
+ // Convert streams to dict
144
+ let streams_dict = PyDict::new(py);
145
+ for (_, stream) in meta.streams {
146
+ let stream_dict = PyDict::new(py);
147
+ stream_dict.set_item("stream_id", stream.stream.stream_id.to_string())?;
148
+ // stream_dict.set_item("name", stream.name.to_string())?;
149
+
150
+ let columns_dict = PyDict::new(py);
151
+ for col in stream.columns {
152
+ let col_dict = PyDict::new(py);
153
+ col_dict.set_item("name", col.name.to_string())?;
154
+ col_dict.set_item("description", col.description.to_string())?;
155
+ col_dict.set_item("type", format!("{:?}", col.data_type))?;
156
+ col_dict.set_item("units", col.units.to_string())?;
157
+
158
+ columns_dict.set_item(col.name.to_string(), col_dict)?;
159
+ }
160
+ stream_dict.set_item("columns", columns_dict)?;
161
+ streams_dict.set_item(stream.stream.name.to_string(), stream_dict)?;
162
+ }
163
+ dict.set_item("streams", streams_dict)?;
164
+
165
+ Ok(dict.into())
166
+ }
167
+ }
168
+
169
+ /// A Python module implemented in Rust. The name of this function must match
170
+ /// the `lib.name` setting in the `Cargo.toml`, else Python will not be able to
171
+ /// import the module.
172
+ #[pymodule]
173
+ fn _twinleaf(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
174
+ m.add_class::<PyDevice>()?;
175
+ Ok(())
176
+ }
@@ -0,0 +1,214 @@
1
+ import twinleaf._twinleaf
2
+ import struct
3
+ from types import SimpleNamespace
4
+
5
+ class Device(_twinleaf.Device):
6
+ def __new__(cls, url=None, route=None):
7
+ device = super().__new__(cls, url, route)
8
+ return device
9
+
10
+ def __init__(self, url=None, route=None, instantiate=True):
11
+ super().__init__()
12
+ if instantiate:
13
+ self._instantiate_rpcs()
14
+ self._instantiate_samples()
15
+
16
+ def _rpc_int(self, name: str, size: int, signed: bool, value: int | None = None) -> int:
17
+ # print(name)
18
+ if signed:
19
+ match size:
20
+ case 1:
21
+ fstr = '<b'
22
+ case 2:
23
+ fstr = '<h'
24
+ case 4:
25
+ fstr = '<i'
26
+ else:
27
+ match size:
28
+ case 1:
29
+ fstr = '<B'
30
+ case 2:
31
+ fstr = '<H'
32
+ case 4:
33
+ fstr = '<I'
34
+ payload = b'' if value is None else struct.pack(fstr, value)
35
+ rep = self._rpc(name, payload)
36
+ return struct.unpack(fstr, rep)[0]
37
+
38
+ def _rpc_float(self, name: str, size: int, value: float | None = None) -> float:
39
+ fstr = '<f' if (size == 4) else '<d'
40
+ payload = b'' if value is None else struct.pack(fstr, value)
41
+ rep = self._rpc(name, payload)
42
+ return struct.unpack(fstr, rep)[0]
43
+
44
+ def _get_rpc_obj(self, name: str, meta: int):
45
+ data_type = (meta & 0xF)
46
+ data_size = (meta >> 4) & 0xF
47
+ if (meta & 0x8000) == 0:
48
+ def rpc_method(local_self, arg: bytes = b'') -> bytes:
49
+ return self._rpc(name, arg)
50
+ elif data_size == 0:
51
+ def rpc_method(local_self) -> None:
52
+ return self._rpc(name, b'')
53
+ elif data_type in (0, 1):
54
+ signed = (data_type) == 1
55
+ if (meta & 0x0200) == 0:
56
+ def rpc_method(local_self) -> int:
57
+ return self._rpc_int(name, data_size, signed)
58
+ else:
59
+ def rpc_method(local_self, arg: int | None = None) -> int:
60
+ return self._rpc_int(name, data_size, signed, arg)
61
+ elif data_type == 2:
62
+ if (meta & 0x0200) == 0:
63
+ def rpc_method(local_self) -> float:
64
+ return self._rpc_float(name, data_size)
65
+ else:
66
+ def rpc_method(local_self, arg: float | None = None) -> float:
67
+ return self._rpc_float(name, data_size, arg)
68
+ elif data_type == 3:
69
+ if (meta & 0x0200) == 0:
70
+ def rpc_method(local_self) -> str:
71
+ return self._rpc(name, b'').decode()
72
+ else:
73
+ def rpc_method(local_self, arg: str | None = None) -> str:
74
+ return self._rpc(name, arg.encode()).decode()
75
+ cls = type('rpc',(), {'__name__':name, '__call__':rpc_method, '_data_type':data_type, '_data_size':data_size})
76
+ return cls
77
+
78
+ def _get_obj_survey(self, name: str):
79
+ def survey(local_self):
80
+ survey = {}
81
+ for name, attr in local_self.__dict__.items():
82
+ if callable(attr):
83
+ if hasattr(attr, '_data_type'):
84
+ # don't call actions like reset, stop, etc.
85
+ if attr._data_type > 0 or attr._data_size > 0:
86
+ survey[attr.__name__] = attr()
87
+ else:
88
+ if attr.__class__.__name__ == 'survey':
89
+ subsurvey = attr()
90
+ survey = {**survey, **subsurvey}
91
+ return survey
92
+ cls = type('survey',(), {'__name__':name, '__call__':survey})
93
+ return cls
94
+
95
+ def _instantiate_rpcs(self):
96
+ n = int.from_bytes(self._rpc("rpc.listinfo", b""), "little")
97
+ cls = self._get_obj_survey(self)
98
+ setattr(self, 'settings', cls())
99
+ for i in range(n):
100
+ res = self._rpc("rpc.listinfo", i.to_bytes(2, "little"))
101
+ meta = int.from_bytes(res[0:2], "little")
102
+ name = res[2:].decode()
103
+
104
+ mname, *prefix = reversed(name.split("."))
105
+ parent = self.settings
106
+ survey_prefix = ""
107
+ if prefix and (prefix[-1] == "rpc"):
108
+ prefix[-1] = "_rpc"
109
+ for token in reversed(prefix):
110
+ survey_prefix += "." + token
111
+ if not hasattr(parent, token):
112
+ cls = self._get_obj_survey(token)
113
+ setattr(parent, token, cls())
114
+ parent = getattr(parent, token)
115
+
116
+ cls = self._get_rpc_obj(name, meta)
117
+ setattr(parent, mname, cls())
118
+
119
+ def _samples_list(self, n: int = 1, stream: str = "", columns: list[str] = [], average: bool = False, concatenate: bool = False):
120
+ if n==1:
121
+ # single sample: dict of samples
122
+ sample = list(self._samples(n, stream=stream, columns=columns))[0]
123
+ # sample.pop("stream")
124
+ # sample.pop("time")
125
+ return sample
126
+ else:
127
+ samples = list(self._samples(n, stream=stream, columns=columns))
128
+ # bin into streams
129
+ streams = {}
130
+ for line in samples:
131
+ stream_id = line.pop("stream", None)
132
+ if stream_id not in streams:
133
+ streams[stream_id] = { "stream": stream_id }
134
+ for key, value in line.items():
135
+ if key not in streams[stream_id]:
136
+ streams[stream_id][key] = []
137
+ streams[stream_id][key].append(value)
138
+ if average:
139
+ # average down to dict of samples
140
+ for id in streams.keys():
141
+ for key in streams[id].keys():
142
+ if key != "stream":
143
+ streams[id][key] = sum(streams[id][key]) / len(streams[id][key])
144
+
145
+ if concatenate:
146
+ raise NotImplementedError("Stream concatenation not yet implemented")
147
+
148
+ return streams
149
+
150
+ def _get_obj_samples(self, name: str, stream: str = "", columns: list[str] = [], *args, **kwargs):
151
+ def samples_method(local_self, *args, **kwargs):
152
+ # print(f"Sampling {name} from stream {stream} with columns {columns}")
153
+ return self._samples_list(stream=stream, columns=columns, *args, **kwargs)
154
+ cls = type('samples'+name,(), {'__name__':name, '__call__':samples_method})
155
+ return cls
156
+
157
+ def _instantiate_samples(self):
158
+ metadata = self._get_metadata()
159
+ streams_flattened = []
160
+ for stream, value in metadata['streams'].items():
161
+ for column_name in value['columns'].keys():
162
+ streams_flattened.append(stream+"."+column_name)
163
+
164
+ # All samples
165
+ cls = self._get_obj_samples("samples", stream="", columns=[])
166
+ setattr(self, 'samples', cls())
167
+
168
+ for stream_column in streams_flattened:
169
+ mname, *prefix, stream = reversed(stream_column.split("."))
170
+ parent = self.samples
171
+
172
+ if not hasattr(parent, stream):
173
+ # All samples for this stream
174
+ cls = self._get_obj_samples(stream, stream=stream, columns=[])
175
+ setattr(parent, stream, cls())
176
+ parent = getattr(parent, stream)
177
+
178
+ stream_prefix = ""
179
+ for token in reversed(prefix):
180
+
181
+ stream_prefix += "." + token
182
+ if not hasattr(parent, token):
183
+ #wildcard columns
184
+ cls = self._get_obj_samples(token, stream=stream, columns=[stream_prefix[1:]+".*"])
185
+ setattr(parent, token, cls())
186
+ parent = getattr(parent, token)
187
+
188
+ # specific stream samples
189
+ stream, column_name = stream_column.split(".",1)
190
+
191
+ cls = self._get_obj_samples(mname, stream=stream, columns=[column_name])
192
+ setattr(parent, mname, cls())
193
+
194
+ def _interact(self):
195
+ imported_objects = {}
196
+ imported_objects["tl"] = self
197
+ try:
198
+ import IPython
199
+ IPython.embed(
200
+ user_ns=imported_objects,
201
+ banner1="",
202
+ banner2="", # Use : {self._shortname}.<tab>
203
+ exit_msg="",
204
+ enable_tip=False)
205
+ except ImportError:
206
+ import code
207
+ repl = code.InteractiveConsole(locals=imported_objects)
208
+ repl.interact(
209
+ banner = "",
210
+ exitmsg = "")
211
+
212
+ __doc__ = twinleaf.__doc__
213
+ if hasattr(twinleaf, "__all__"):
214
+ __all__ = twinleaf.__all__