-
Notifications
You must be signed in to change notification settings - Fork 176
/
run.py
152 lines (134 loc) · 4.93 KB
/
run.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
"""Complete FW updater."""
import logging
import asyncio
from typing import Optional, TextIO, Dict, Tuple, AsyncIterator
from .types import FirmwareUpdateStatus, StatusElement
from opentrons_hardware.drivers.can_bus import CanMessenger
from opentrons_hardware.firmware_bindings import NodeId
from opentrons_hardware.firmware_bindings.messages.message_definitions import (
FirmwareUpdateStartApp,
)
from opentrons_hardware.firmware_update import (
FirmwareUpdateInitiator,
FirmwareUpdateDownloader,
FirmwareUpdateEraser,
HexRecordProcessor,
)
from opentrons_hardware.firmware_update.target import Target
logger = logging.getLogger(__name__)
UpdateDict = Dict[NodeId, TextIO]
class RunUpdate:
"""Class for updating robot microcontroller firmware."""
def __init__(
self,
messenger: CanMessenger,
update_details: UpdateDict,
retry_count: int,
timeout_seconds: float,
erase: Optional[bool] = True,
) -> None:
"""Initialize RunUpdate class.
Args:
messenger: The can messenger to use.
update_details: Dict of nodes to be updated and their firmware files.
retry_count: Number of times to retry.
timeout_seconds: How much to wait for responses.
erase: Whether to erase flash before updating.
Returns:
None
"""
self._messenger = messenger
self._update_details = update_details
self._retry_count = retry_count
self._timeout_seconds = timeout_seconds
self._erase = erase
self._status_dict = {
node_id: (FirmwareUpdateStatus.queued, 0)
for node_id in update_details.keys()
}
self._status_queue: "asyncio.Queue[Tuple[NodeId,StatusElement]]" = (
asyncio.Queue()
)
async def _run_update(
self,
messenger: CanMessenger,
node_id: NodeId,
hex_file: TextIO,
retry_count: int,
timeout_seconds: float,
erase: Optional[bool] = True,
) -> None:
"""Perform a firmware update on a node target."""
hex_processor = HexRecordProcessor.from_file(hex_file)
initiator = FirmwareUpdateInitiator(messenger)
downloader = FirmwareUpdateDownloader(messenger)
target = Target(system_node=node_id)
logger.info(f"Initiating FW Update on {target}.")
await self._status_queue.put((node_id, (FirmwareUpdateStatus.updating, 0)))
await initiator.run(
target=target,
retry_count=retry_count,
ready_wait_time_sec=timeout_seconds,
)
download_start_progress = 0.1
await self._status_queue.put(
(node_id, (FirmwareUpdateStatus.updating, download_start_progress))
)
if erase:
eraser = FirmwareUpdateEraser(messenger)
logger.info(f"Erasing existing FW Update on {target}.")
await eraser.run(
node_id=target.bootloader_node,
timeout_sec=timeout_seconds,
)
download_start_progress = 0.2
await self._status_queue.put(
(node_id, (FirmwareUpdateStatus.updating, download_start_progress))
)
else:
logger.info("Skipping erase step.")
logger.info(f"Downloading FW to {target.bootloader_node}.")
async for download_progress in downloader.run(
node_id=target.bootloader_node,
hex_processor=hex_processor,
ack_wait_seconds=timeout_seconds,
):
await self._status_queue.put(
(
node_id,
(
FirmwareUpdateStatus.updating,
download_start_progress
+ (0.9 - download_start_progress) * download_progress,
),
)
)
logger.info(f"Restarting FW on {target.system_node}.")
await messenger.send(
node_id=target.bootloader_node,
message=FirmwareUpdateStartApp(),
)
await self._status_queue.put((node_id, (FirmwareUpdateStatus.done, 1)))
async def run_updates(
self,
) -> AsyncIterator[Tuple[NodeId, StatusElement]]:
"""Perform a firmware update on multiple node targets."""
tasks = [
self._run_update(
messenger=self._messenger,
node_id=node_id,
hex_file=hex_file,
retry_count=self._retry_count,
timeout_seconds=self._timeout_seconds,
erase=self._erase,
)
for node_id, hex_file in self._update_details.items()
]
task = asyncio.gather(*tasks)
while True:
try:
yield await asyncio.wait_for(self._status_queue.get(), 0.25)
except asyncio.TimeoutError:
pass
if task.done():
break