swarmit 0.1.0__tar.gz → 0.2.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.
- {swarmit-0.1.0 → swarmit-0.2.0}/.gitignore +6 -0
- swarmit-0.2.0/AUTHORS +1 -0
- {swarmit-0.1.0 → swarmit-0.2.0}/PKG-INFO +23 -3
- {swarmit-0.1.0 → swarmit-0.2.0}/README.md +20 -0
- {swarmit-0.1.0 → swarmit-0.2.0}/pyproject.toml +4 -6
- {swarmit-0.1.0 → swarmit-0.2.0}/testbed/cli/main.py +32 -19
- {swarmit-0.1.0 → swarmit-0.2.0}/LICENSE +0 -0
swarmit-0.2.0/AUTHORS
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Alexandre Abadie <alexandre.abadie@inria.fr>
|
@@ -1,11 +1,11 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: swarmit
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.2.0
|
4
4
|
Summary: Run Your Own Robot Swarm Testbed.
|
5
5
|
Project-URL: Homepage, https://github.com/DotBots/swarmit
|
6
6
|
Project-URL: Bug Tracker, https://github.com/DotBots/swarmit/issues
|
7
7
|
Author-email: Alexandre Abadie <alexandre.abadie@inria.fr>
|
8
|
-
License:
|
8
|
+
License-File: AUTHORS
|
9
9
|
License-File: LICENSE
|
10
10
|
Classifier: License :: OSI Approved :: BSD License
|
11
11
|
Classifier: Operating System :: MacOS
|
@@ -15,7 +15,7 @@ Classifier: Programming Language :: Python :: 3
|
|
15
15
|
Requires-Python: >=3.7
|
16
16
|
Requires-Dist: click==8.1.7
|
17
17
|
Requires-Dist: cryptography==43.0.1
|
18
|
-
Requires-Dist: pydotbot==0.
|
18
|
+
Requires-Dist: pydotbot==0.22.0
|
19
19
|
Requires-Dist: pyserial==3.5
|
20
20
|
Requires-Dist: rich==13.8.1
|
21
21
|
Requires-Dist: structlog==24.4.0
|
@@ -50,12 +50,32 @@ and [bootloader.emProject](device/bootloader/bootloader.emProject) in SES
|
|
50
50
|
|
51
51
|
The device is now ready.
|
52
52
|
|
53
|
+
### Gateway
|
54
|
+
|
55
|
+
The communication between the computer and the swarm devices is performed via a
|
56
|
+
gateway board connected via USB to the computer.
|
57
|
+
The gateway board is a Nordic nRF53840DK.
|
58
|
+
|
59
|
+
The firmware to run on the gateway can also be compiled and flash using SES.
|
60
|
+
The SES project to open is located at [gateway.emProject](gateway/gateway.emProject).
|
61
|
+
|
62
|
+
After flashing the gateway firmware, LED1 on the DK should blink fast during 1s.
|
63
|
+
|
53
64
|
### Python CLI script
|
54
65
|
|
55
66
|
The Python CLI script provides commands for flashing, starting and stopping user
|
56
67
|
code on the device, as well as monitoring and checking the status of devices
|
57
68
|
in the swarm.
|
58
69
|
|
70
|
+
The Python CLI script connects via a virtual COM port to the gateway connected to
|
71
|
+
the computer.
|
72
|
+
|
73
|
+
The Python CLI script is available on PyPI. Install it using:
|
74
|
+
|
75
|
+
```
|
76
|
+
pip install swarmit
|
77
|
+
```
|
78
|
+
|
59
79
|
Default usage:
|
60
80
|
|
61
81
|
```
|
@@ -26,12 +26,32 @@ and [bootloader.emProject](device/bootloader/bootloader.emProject) in SES
|
|
26
26
|
|
27
27
|
The device is now ready.
|
28
28
|
|
29
|
+
### Gateway
|
30
|
+
|
31
|
+
The communication between the computer and the swarm devices is performed via a
|
32
|
+
gateway board connected via USB to the computer.
|
33
|
+
The gateway board is a Nordic nRF53840DK.
|
34
|
+
|
35
|
+
The firmware to run on the gateway can also be compiled and flash using SES.
|
36
|
+
The SES project to open is located at [gateway.emProject](gateway/gateway.emProject).
|
37
|
+
|
38
|
+
After flashing the gateway firmware, LED1 on the DK should blink fast during 1s.
|
39
|
+
|
29
40
|
### Python CLI script
|
30
41
|
|
31
42
|
The Python CLI script provides commands for flashing, starting and stopping user
|
32
43
|
code on the device, as well as monitoring and checking the status of devices
|
33
44
|
in the swarm.
|
34
45
|
|
46
|
+
The Python CLI script connects via a virtual COM port to the gateway connected to
|
47
|
+
the computer.
|
48
|
+
|
49
|
+
The Python CLI script is available on PyPI. Install it using:
|
50
|
+
|
51
|
+
```
|
52
|
+
pip install swarmit
|
53
|
+
```
|
54
|
+
|
35
55
|
Default usage:
|
36
56
|
|
37
57
|
```
|
@@ -1,7 +1,5 @@
|
|
1
1
|
[build-system]
|
2
|
-
requires = [
|
3
|
-
"hatchling>=1.4.1",
|
4
|
-
]
|
2
|
+
requires = ["hatchling"]
|
5
3
|
build-backend = "hatchling.build"
|
6
4
|
|
7
5
|
[tool.hatch.build]
|
@@ -15,14 +13,14 @@ exclude = [
|
|
15
13
|
|
16
14
|
[project]
|
17
15
|
name = "swarmit"
|
18
|
-
version = "0.
|
16
|
+
version = "0.2.0"
|
19
17
|
authors = [
|
20
18
|
{ name="Alexandre Abadie", email="alexandre.abadie@inria.fr" },
|
21
19
|
]
|
22
20
|
dependencies = [
|
23
21
|
"click == 8.1.7",
|
24
22
|
"cryptography == 43.0.1",
|
25
|
-
"pydotbot == 0.
|
23
|
+
"pydotbot == 0.22.0",
|
26
24
|
"pyserial == 3.5",
|
27
25
|
"rich == 13.8.1",
|
28
26
|
"structlog == 24.4.0",
|
@@ -30,7 +28,7 @@ dependencies = [
|
|
30
28
|
]
|
31
29
|
description = "Run Your Own Robot Swarm Testbed."
|
32
30
|
readme = "README.md"
|
33
|
-
license =
|
31
|
+
license-file = ["LICENSE"]
|
34
32
|
requires-python = ">=3.7"
|
35
33
|
classifiers = [
|
36
34
|
"Programming Language :: Python :: 3",
|
@@ -19,14 +19,18 @@ from rich.table import Table
|
|
19
19
|
|
20
20
|
from dotbot.logger import LOGGER
|
21
21
|
from dotbot.hdlc import hdlc_encode, HDLCHandler, HDLCState
|
22
|
-
from dotbot.protocol import PROTOCOL_VERSION
|
23
|
-
from dotbot.serial_interface import
|
22
|
+
from dotbot.protocol import PROTOCOL_VERSION, ProtocolHeader
|
23
|
+
from dotbot.serial_interface import (
|
24
|
+
SerialInterface,
|
25
|
+
SerialInterfaceException,
|
26
|
+
get_default_port,
|
27
|
+
)
|
24
28
|
|
25
29
|
|
26
30
|
SERIAL_PORT = get_default_port()
|
27
31
|
BAUDRATE = 1000000
|
28
32
|
CHUNK_SIZE = 128
|
29
|
-
|
33
|
+
HEADER = ProtocolHeader()
|
30
34
|
|
31
35
|
|
32
36
|
class NotificationType(Enum):
|
@@ -66,6 +70,17 @@ class DataChunk:
|
|
66
70
|
data: bytes
|
67
71
|
|
68
72
|
|
73
|
+
def _header():
|
74
|
+
"""Return the header for the protocol."""
|
75
|
+
buffer = bytearray()
|
76
|
+
for field in ProtocolHeader().fields:
|
77
|
+
buffer += int(field.value).to_bytes(
|
78
|
+
length=field.length, byteorder="little", signed=field.signed
|
79
|
+
)
|
80
|
+
buffer += int(16).to_bytes(length=1, byteorder="little")
|
81
|
+
return buffer
|
82
|
+
|
83
|
+
|
69
84
|
class SwarmitFlash:
|
70
85
|
"""Class used to flash a firmware."""
|
71
86
|
|
@@ -79,13 +94,11 @@ class SwarmitFlash:
|
|
79
94
|
self.chunks = []
|
80
95
|
self.fw_hash = None
|
81
96
|
self.acked_ids = []
|
82
|
-
# Just write a single byte to fake a gateway handshake
|
83
|
-
self.serial.write(int(PROTOCOL_VERSION).to_bytes(length=1))
|
84
97
|
|
85
98
|
def on_byte_received(self, byte):
|
86
99
|
self.hdlc_handler.handle_byte(byte)
|
87
100
|
if self.hdlc_handler.state == HDLCState.READY:
|
88
|
-
payload = self.hdlc_handler.payload
|
101
|
+
payload = self.hdlc_handler.payload[25:]
|
89
102
|
if not payload:
|
90
103
|
return
|
91
104
|
deviceid_ack = hex(int.from_bytes(payload[0:8], byteorder="little"))
|
@@ -102,7 +115,7 @@ class SwarmitFlash:
|
|
102
115
|
|
103
116
|
def _send_start_ota(self, device_id):
|
104
117
|
buffer = bytearray()
|
105
|
-
buffer +=
|
118
|
+
buffer += _header()
|
106
119
|
buffer += int(device_id, 16).to_bytes(length=8, byteorder="little")
|
107
120
|
buffer += int(RequestType.SWARMIT_REQ_OTA_START.value).to_bytes(
|
108
121
|
length=1, byteorder="little"
|
@@ -176,7 +189,7 @@ class SwarmitFlash:
|
|
176
189
|
break
|
177
190
|
if send is True:
|
178
191
|
buffer = bytearray()
|
179
|
-
buffer +=
|
192
|
+
buffer += _header()
|
180
193
|
buffer += int(device_id, 16).to_bytes(length=8, byteorder="little")
|
181
194
|
buffer += int(RequestType.SWARMIT_REQ_OTA_CHUNK.value).to_bytes(
|
182
195
|
length=1, byteorder="little"
|
@@ -222,7 +235,7 @@ class SwarmitStart:
|
|
222
235
|
|
223
236
|
def _send_start(self, device_id):
|
224
237
|
buffer = bytearray()
|
225
|
-
buffer +=
|
238
|
+
buffer += _header()
|
226
239
|
buffer += int(device_id, 16).to_bytes(length=8, byteorder="little")
|
227
240
|
buffer += int(RequestType.SWARMIT_REQ_EXPERIMENT_START.value).to_bytes(
|
228
241
|
length=1, byteorder="little"
|
@@ -251,7 +264,7 @@ class SwarmitStop:
|
|
251
264
|
|
252
265
|
def _send_stop(self, device_id):
|
253
266
|
buffer = bytearray()
|
254
|
-
buffer +=
|
267
|
+
buffer += _header()
|
255
268
|
buffer += int(device_id, 16).to_bytes(length=8, byteorder="little")
|
256
269
|
buffer += int(RequestType.SWARMIT_REQ_EXPERIMENT_STOP.value).to_bytes(
|
257
270
|
length=1, byteorder="little"
|
@@ -285,7 +298,7 @@ class SwarmitMonitor:
|
|
285
298
|
return
|
286
299
|
self.hdlc_handler.handle_byte(byte)
|
287
300
|
if self.hdlc_handler.state == HDLCState.READY:
|
288
|
-
payload = self.hdlc_handler.payload
|
301
|
+
payload = self.hdlc_handler.payload[25:]
|
289
302
|
if not payload:
|
290
303
|
return
|
291
304
|
deviceid = int.from_bytes(payload[0:8], byteorder="little")
|
@@ -318,22 +331,22 @@ class SwarmitStatus:
|
|
318
331
|
def __init__(self, port, baudrate):
|
319
332
|
self.logger = LOGGER.bind(context=__name__)
|
320
333
|
self.hdlc_handler = HDLCHandler()
|
321
|
-
self.serial = SerialInterface(port, baudrate, self.on_byte_received)
|
322
334
|
self.last_deviceid_notification = None
|
323
|
-
# Just write a single byte to fake a DotBot gateway handshake
|
324
|
-
self.serial.write(int(PROTOCOL_VERSION).to_bytes(length=1))
|
325
335
|
self.status_data = {}
|
326
336
|
self.resp_ids = []
|
327
337
|
self.table = Table()
|
328
338
|
self.table.add_column("Device ID", style="magenta", no_wrap=True)
|
329
339
|
self.table.add_column("Status", style="green")
|
340
|
+
self.serial = SerialInterface(port, baudrate, self.on_byte_received)
|
341
|
+
# Just write a single byte to fake a DotBot gateway handshake
|
342
|
+
self.serial.write(int(PROTOCOL_VERSION).to_bytes(length=1))
|
330
343
|
|
331
344
|
def on_byte_received(self, byte):
|
332
345
|
if self.hdlc_handler is None:
|
333
346
|
return
|
334
347
|
self.hdlc_handler.handle_byte(byte)
|
335
348
|
if self.hdlc_handler.state == HDLCState.READY:
|
336
|
-
payload = self.hdlc_handler.payload
|
349
|
+
payload = self.hdlc_handler.payload[25:]
|
337
350
|
if not payload:
|
338
351
|
return
|
339
352
|
deviceid_resp = hex(int.from_bytes(payload[0:8], byteorder="little"))
|
@@ -345,19 +358,19 @@ class SwarmitStatus:
|
|
345
358
|
self.status_data[deviceid_resp] = status
|
346
359
|
self.table.add_row(
|
347
360
|
deviceid_resp,
|
348
|
-
f
|
361
|
+
f'{"[bold cyan]" if status == StatusType.Running else "[bold green]"}{status.name}',
|
349
362
|
)
|
350
363
|
|
351
364
|
def status(self, display=True):
|
352
365
|
buffer = bytearray()
|
353
|
-
buffer +=
|
366
|
+
buffer += _header()
|
354
367
|
buffer += int("0", 16).to_bytes(length=8, byteorder="little")
|
355
368
|
buffer += int(RequestType.SWARMIT_REQ_EXPERIMENT_STATUS.value).to_bytes(
|
356
369
|
length=1, byteorder="little"
|
357
370
|
)
|
358
371
|
self.serial.write(hdlc_encode(buffer))
|
359
372
|
timeout = 0 # ms
|
360
|
-
while timeout <
|
373
|
+
while timeout < 2000:
|
361
374
|
timeout += 1
|
362
375
|
time.sleep(0.0001)
|
363
376
|
if self.status_data and display is True:
|
@@ -393,7 +406,7 @@ def swarmit_flash(port, baudrate, firmware, yes, devices, ready_devices):
|
|
393
406
|
console = Console()
|
394
407
|
console.print(
|
395
408
|
"[bold red]Error:[/] some acknowledgment are missing "
|
396
|
-
f
|
409
|
+
f'({", ".join(sorted(set(ready_devices).difference(set(ids))))}). '
|
397
410
|
"Aborting."
|
398
411
|
)
|
399
412
|
return False
|
File without changes
|